diff --git a/com.actionsoft.apps.coe.method.process.subprocess/lib/com.actionsoft.apps.coe.method.process.subprocess.jar b/com.actionsoft.apps.coe.method.process.subprocess/lib/com.actionsoft.apps.coe.method.process.subprocess.jar index 7cfcaca6..7eb41992 100644 Binary files a/com.actionsoft.apps.coe.method.process.subprocess/lib/com.actionsoft.apps.coe.method.process.subprocess.jar and b/com.actionsoft.apps.coe.method.process.subprocess/lib/com.actionsoft.apps.coe.method.process.subprocess.jar differ diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/SubProcessController.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/SubProcessController.java index 3b0afd57..d3714506 100644 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/SubProcessController.java +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/SubProcessController.java @@ -129,10 +129,30 @@ public class SubProcessController { return ResponseObject.newOkResponse().toString(); } + /** + * 节点展开 + * @param uc + * @param repositoryId + * @param shapeId + * @return define + */ @Mapping("com.actionsoft.apps.coe.method.process.subprocess.shape_expand") public String shapeExpand(UserContext uc, String repositoryId, String shapeId){ SubProcessWeb processWeb = new SubProcessWeb(uc); return processWeb.shapeNodeExpand(repositoryId, shapeId); } + /** + * 节点关闭 + * @param uc + * @param repositoryId + * @param shapeId + * @return define + */ + @Mapping("com.actionsoft.apps.coe.method.process.subprocess.shape_close") + public String shapeClose(UserContext uc, String repositoryId, String shapeId, String endToEndProcessDefineStr){ + SubProcessWeb processWeb = new SubProcessWeb(uc); + return processWeb.shapeNodeClose(repositoryId, shapeId, endToEndProcessDefineStr); + } + } diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphNodeCloseHandle.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphNodeCloseHandle.java new file mode 100644 index 00000000..96a1e65e --- /dev/null +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/graph/GraphNodeCloseHandle.java @@ -0,0 +1,617 @@ +package com.actionsoft.apps.coe.method.process.subprocess.graph; + +import com.actionsoft.apps.coe.method.process.subprocess.constant.LinkerDefConstant; +import com.actionsoft.apps.coe.method.process.subprocess.constant.SubProcessConst; +import com.actionsoft.apps.coe.pal.pal.repository.cache.PALRepositoryCache; +import com.actionsoft.apps.coe.pal.pal.repository.designer.manage.CoeDesignerAPIManager; +import com.actionsoft.apps.coe.pal.pal.repository.designer.model.BaseModel; +import com.actionsoft.apps.coe.pal.pal.repository.designer.relation.cache.DesignerShapeRelationCache; +import com.actionsoft.apps.coe.pal.pal.repository.designer.relation.model.DesignerShapeRelationModel; +import com.actionsoft.apps.coe.pal.pal.repository.designer.util.ShapeUtil; +import com.actionsoft.bpms.util.ConsolePrinter; +import com.actionsoft.bpms.util.UUIDGener; +import com.actionsoft.bpms.util.UtilString; +import com.actionsoft.exception.AWSException; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +import java.util.*; +import java.util.concurrent.locks.ReentrantLock; + +/** + * 图节点关闭处理 + */ +public class GraphNodeCloseHandle { + + private String repositoryId; // 总图模型ID + private String shapeId; // 总图中当前要关闭的子流程图形ID + private CoeDesignerAPIManager apiManager; + private String relationFileId; // 当前要关闭的子流程图形所标识模型文件ID + private JSONObject childProcessDefine; // 要关闭的子流程模型信息 + private JSONObject endToEndProcessDefine; // 总图的模型信息 + private JSONObject scopeLimitationShape; // 范围标注框 + + private final ReentrantLock lock = new ReentrantLock(); + + public GraphNodeCloseHandle(String repositoryId, String shapeId, String endToEndProcessDefineStr) { + this.repositoryId = repositoryId; + this.shapeId = shapeId; + + apiManager = CoeDesignerAPIManager.getInstance(); + + try { + readChildProcessDefine(); + readCurrentProcessDefine(endToEndProcessDefineStr); + } catch (AWSException e) { + throw new AWSException(e); + } + } + + /** + * 读取子流程节点的存储信息 + * @throws AWSException + */ + private void readChildProcessDefine() throws AWSException{ + List childProcessModelList = DesignerShapeRelationCache.getListByAttrId(repositoryId, shapeId, SubProcessConst.CHILD_PROCESS); + DesignerShapeRelationModel relationModel = childProcessModelList.stream().findFirst().orElse(null); + if (relationModel == null) + throw new AWSException("未找到当前节点所标识的子流程文件信息"); + relationFileId = relationModel.getRelationFileId(); + // 先去与总图存储的同级目录下读取 如果为空说明是初次读取 + String childProcessDefineStr = apiManager.getChildProcessDefine(repositoryId, 0, relationFileId); + if (UtilString.isEmpty(childProcessDefineStr)){ // 初次读取 去源文件目录读取 + BaseModel childProcessBaseModel = apiManager.getDefinition(relationFileId, 0); + if (childProcessBaseModel == null) + throw new AWSException("当前子流程节点内部可能没有图形元素,可以去添加后展开"); + childProcessDefineStr = childProcessBaseModel.getDefinition(); + } + childProcessDefine = JSONObject.parseObject(childProcessDefineStr); + } + + /** + * 读取当前总图的存储信息 + * @throws AWSException + */ + private void readCurrentProcessDefine(String endToEndProcessDefineStr) throws AWSException{ + if (UtilString.isEmpty(endToEndProcessDefineStr)) + throw new AWSException("参数异常,模型存储信息未传"); + endToEndProcessDefine = JSONObject.parseObject(endToEndProcessDefineStr); + + scopeLimitationShape= endToEndProcessDefine.getJSONObject("elements").getJSONObject(shapeId); + } + + /** + * 处理节点关闭 + * @return + * @throws AWSException + */ + public String handleNodeClose() throws AWSException { + + + // 1、处理范围选择框及其内部节点 + handleRelationModelNodePosition(); + + // 2、处理总图中的节点与连线 + handleEndToEndGraphNodeAndLinker(); + + return endToEndProcessDefine.toJSONString(); + } + + private void handleEndToEndGraphNodeAndLinker(){ + // 1、创建对应子流程节点 + JSONObject subProcessNode = buildSubProcessNode(shapeId, scopeLimitationShape); + addEndToEndGraphElements(subProcessNode); + // 2、根据现有连线关系创建邻接矩阵 + NodeCloseAdjMatrix closeAdjMatrix = buildEndToEndGraphAdjMatrix(); + closeAdjMatrix.buildAdjMatrix(); + // 3、收集现有元素坐标 + JSONObject elements = readEndToEndGraphElements(); + double[][] vertexPosition = closeAdjMatrix.getVertexPosition(elements); + // 4、删除现有连线 + removeEndToEndGraphOldLinker(); + // 5、构建新的连线 + JSONObject processProperties = endToEndProcessDefine.getJSONObject("processProperties"); + String direction = processProperties.getString("direction"); + NodeCloseLinkerRender linkerRender = new NodeCloseLinkerRender(vertexPosition, closeAdjMatrix, scopeLimitationShape); + JSONArray linkers = linkerRender.toAssembleLinker(direction, shapeId); + for (Object o : linkers) { + JSONObject linker = (JSONObject) o; + addEndToEndGraphElements(linker); + } + + // 6、更新画布的大小 + // 确定画布的宽度与高度 + double w = Arrays.stream(vertexPosition).mapToDouble(position -> position[0]).max().orElse(0.0); + double h = Arrays.stream(vertexPosition).mapToDouble(position -> position[1]).max().orElse(0.0); + + JSONObject page = endToEndProcessDefine.getJSONObject("page"); + page.put("width", w + 300); + page.put("height", h + 300); + } + + private void handleRelationModelNodePosition(){ + removeScopeShapeAndInRangeEle(); + removeEndToEndGraphElements(shapeId); + } + + + /** + * 删除总图中节点展开前的连线 + */ + private void removeEndToEndGraphOldLinker(){ + JSONObject elements = endToEndProcessDefine.getJSONObject("elements"); + Set eleKeys = new HashSet<>(); + for (String key : elements.keySet()) { + JSONObject ele = elements.getJSONObject(key); + if ("linker".equals(ele.getString("name"))){ + eleKeys.add(key); + } + } + for (String eleKey : eleKeys) { + removeEndToEndGraphElements(eleKey); + } + } + + + /** + * 构建节点展开前 端到端总图的邻接矩阵 + * 方便后续节点展开或者闭合进行重新连线 + */ + private NodeCloseAdjMatrix buildEndToEndGraphAdjMatrix(){ + + List nodeIdList = new ArrayList<>(); + List linkerList = new ArrayList<>(); + JSONObject endToEndProcessElements = endToEndProcessDefine.getJSONObject("elements"); + for (String key : endToEndProcessElements.keySet()) { + JSONObject ele = endToEndProcessElements.getJSONObject(key); + if ("linker".equals(ele.getString("name"))) { + linkerList.add(ele); + }else { + nodeIdList.add(key); + } + } + NodeCloseAdjMatrix closeAdjMatrix = new NodeCloseAdjMatrix(nodeIdList, linkerList); + return closeAdjMatrix; + } + + /** + * 构建子流程节点 + * @param shapeId + * @param scopeLimitationShape + * @return + */ + private JSONObject buildSubProcessNode(String shapeId, JSONObject scopeLimitationShape){ + JSONObject subProcessNode = ShapeUtil.getProcessShapeDefinition(SubProcessConst.SUB_PROCESS_METHOD_ID, "子流程"); + subProcessNode.put("id", shapeId); + JSONObject subProcessNodeProps = subProcessNode.getJSONObject("props"); + subProcessNodeProps.put("x", scopeLimitationShape.getJSONObject("props").getDoubleValue("x")); + subProcessNodeProps.put("y", scopeLimitationShape.getJSONObject("props").getDoubleValue("y")); + subProcessNodeProps.put("zindex", scopeLimitationShape.getJSONObject("props").getIntValue("zindex")); + subProcessNode.put("text", PALRepositoryCache.getCache().get(relationFileId).getName()); + return subProcessNode; + } + + /** + * 删除总图中范围选择框以及 框内元素 + */ + private void removeScopeShapeAndInRangeEle(){ + JSONObject childProcessElements = childProcessDefine.getJSONObject("elements"); + for (String key : childProcessElements.keySet()) { + removeEndToEndGraphElements(key); + } + + removeEndToEndGraphElements(shapeId); + } + + /** + * 读取总图中的elements + * @return elements + */ + private JSONObject readEndToEndGraphElements(){ + lock.lock(); + try { + JSONObject elements = endToEndProcessDefine.getJSONObject("elements"); + return elements; + }finally { + lock.unlock(); + } + } + + /** + * 总图中添加元素 + * @param ele + */ + private void addEndToEndGraphElements(JSONObject ele){ + lock.lock(); + try{ + JSONObject elements = readEndToEndGraphElements(); + String id = ele.getString("id"); + elements.put(id, ele); + }finally { + lock.unlock(); + } + } + + /** + * 总图中删除元素 + * @param key + */ + private void removeEndToEndGraphElements(String key){ + lock.lock(); + try{ + JSONObject elements = readEndToEndGraphElements(); + elements.remove(key); + }finally { + lock.unlock(); + } + } +} + +/** + * 节点展开的邻接矩阵 + */ +class NodeCloseAdjMatrix{ + + private int[][] adjMatrix; + private List nodeIds; + private List linkerList; + + public NodeCloseAdjMatrix(List nodeIds, List linkerList) { + this.adjMatrix = new int[nodeIds.size()][nodeIds.size()]; + this.nodeIds = nodeIds; + this.linkerList = linkerList; + } + + /** + * 添加一条从顶点 u 到顶点 v 的有向边。 + */ + public void addEdge(int u, int v) { + adjMatrix[u][v] = 1; // 设置邻接矩阵中相应的位置为 1 + } + + /** + * 获取从顶点 u 出发可以到达的所有顶点。 + */ + public List getNeighbors(int u) { + List neighbors = new ArrayList<>(); + for (int i = 0; i < nodeIds.size(); i++) { + if (adjMatrix[u][i] == 1) { + neighbors.add(i); + } + } + return neighbors; + } + + /** + * 判断从顶点 u 是否可以到达顶点 v。 + */ + public boolean hasEdge(int u, int v) { + return adjMatrix[u][v] == 1; + } + + /** + * 获取邻接矩阵 + * @return + */ + public int[][] getAdjMatrix(){ + return adjMatrix; + } + + public List getNodeIds() { + return nodeIds; + } + + public List getLinkerList() { + return linkerList; + } + + /** + * 构建邻接矩阵 + */ + public void buildAdjMatrix(){ + for (JSONObject linker : linkerList) { + JSONObject from = linker.getJSONObject("from"); + JSONObject to = linker.getJSONObject("to"); + int fromIndex = nodeIds.indexOf(from.getString("id")); + int toIndex = nodeIds.indexOf(to.getString("id")); + addEdge(fromIndex, toIndex); + } + } + + // 输出邻接矩阵 + public void printAdjMatrix() { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < nodeIds.size(); i++) { + for (int j = 0; j < nodeIds.size(); j++) { + sb.append(adjMatrix[i][j]).append(" "); + } + sb.append("\n"); + } + System.out.println(sb.toString()); + } + + /** + * 获取总图中节点展开前的所有节点坐标 + * @param elements + * @return + */ + public double[][] getVertexPosition(JSONObject elements){ + double[][] position = new double[nodeIds.size()][2]; + for (int i = 0; i < nodeIds.size(); i++) { + JSONObject shape = elements.getJSONObject(nodeIds.get(i)); + JSONObject props = shape.getJSONObject("props"); + double x = props.getDoubleValue("x"); + double y = props.getDoubleValue("y"); + position[i][0] = x; + position[i][1] = y; + } + return position; + } +} + +/** + * 节点展开后连线重构 + */ +class NodeCloseLinkerRender{ + + private List nodeIds; // 图形节点ID集合 + private double[][] vertexPosition; // 所有节点的坐标 + private NodeCloseAdjMatrix closeAdjMatrix; // 节点矩阵 + private JSONObject scopeLimitationShape; // 范围标注框 + + public NodeCloseLinkerRender(double[][] vertexPosition, NodeCloseAdjMatrix closeAdjMatrix, JSONObject scopeLimitationShape) { + this.nodeIds = closeAdjMatrix.getNodeIds(); + this.vertexPosition = vertexPosition; + this.closeAdjMatrix = closeAdjMatrix; + this.scopeLimitationShape = scopeLimitationShape; + } + + /** + * 根据连线方向 组装连线 + * @param direction 连线方向 + * @param shapeId 当前待展开节点ID + * @return + */ + public JSONArray toAssembleLinker(String direction, String shapeId){ + JSONArray linkers = new JSONArray(); + int index = nodeIds.indexOf(shapeId); + for (int i = 0; i < vertexPosition.length; i++) { + boolean currentExpandNodeIsStart = false; + if (i == index){ + // currentExpandNodeIsStart = true; // 当前待展开的节点此处应是范围选择框为连线的出发点 + } + double[] fromPoi = vertexPosition[i]; + List nextNodeIndex = closeAdjMatrix.getNeighbors(i); + if (nextNodeIndex.size() > 0){ // 说明当前节点有连线 + for (Integer nodeIndex : nextNodeIndex) { + boolean currentExpandNodeIsEnd = false; + if (nodeIndex.intValue() == index){ + // currentExpandNodeIsEnd = true; // 当前待展开的节点 此处应是范围选择框为连线的终点 + } + double[] toPoi = vertexPosition[nodeIndex]; + double[][] turnPoi = "horizontal".equals(direction) + ? calculationLinkerPointInHorizLayOut(fromPoi, toPoi, currentExpandNodeIsStart, currentExpandNodeIsEnd) + : calculationLinkerPointInVertLayOut(fromPoi, toPoi, currentExpandNodeIsStart, currentExpandNodeIsEnd); + double[] angleArr = calculationLinkerAngle(fromPoi, toPoi, turnPoi[1], turnPoi[turnPoi.length - 2]); + // 构建连线 + JSONObject linkerObj = JSONObject.parseObject(LinkerDefConstant.linker); + linkerObj.put("id", UUIDGener.getObjectId()); + // 折点 + JSONArray points = new JSONArray(); + for (int j = 0; j < turnPoi.length; j++) { + if (j > 0 && j < turnPoi.length - 1){ + JSONObject pointObj = new JSONObject(); + pointObj.put("x", turnPoi[j][0]); + pointObj.put("y", turnPoi[j][1]); + points.add(pointObj); + } + } + linkerObj.put("points", points); + // 起点与终点 + JSONObject fromObj = new JSONObject(); + fromObj.put("x", turnPoi[0][0]); + fromObj.put("y", turnPoi[0][1]); + fromObj.put("angle", angleArr[0]); + fromObj.put("id", nodeIds.get(i)); + linkerObj.put("from", fromObj); + JSONObject toObj = new JSONObject(); + toObj.put("x", turnPoi[turnPoi.length - 1][0]); + toObj.put("y", turnPoi[turnPoi.length - 1][1]); + toObj.put("angle", angleArr[1]); + toObj.put("id", nodeIds.get(nodeIndex.intValue())); + linkerObj.put("to", toObj); + + linkers.add(linkerObj); + } + } + } + return linkers; + } + + private double[][] calculationLinkerPointInVertLayOut(double[] fromPoi, double[] toPoi, boolean currentExpandNodeIsStart, boolean currentExpandNodeIsEnd) { + double fromX = fromPoi[0],fromY = fromPoi[1],toX = toPoi[0],toY = toPoi[1]; + double scopeShapeW = scopeLimitationShape.getJSONObject("props").getDoubleValue("w"), scopeShapeH = scopeLimitationShape.getJSONObject("props").getDoubleValue("h"); + if (fromY == toY){ // 水平 分析可知 水平方向上不会出现 从左到右直连的情况 只有 右边节点右侧锚点出 向上走 左折 连到左侧节点上方锚点 + double[] startPoi = new double[]{fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W), fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}; + double[] turnPoi1 = new double[]{fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}; + double[] turnPoi2 = new double[]{fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}; + double[] turnPoi3 = new double[]{toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}; + double[] endPoi = new double[]{toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY}; + return new double[][]{startPoi, turnPoi1, turnPoi2, turnPoi3, endPoi}; + }else if (fromX == toX){ // 垂直 分析可知 垂直方向上应该不会有 toY < fromY 的情况 鉴于数据不确定性 先写上 + double[] startPoi = fromY < toY + ? new double[]{fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H)} + : new double[]{fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W), fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}; + double[] endPoi = new double[]{toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY}; + return fromY < toY + ? new double[][]{ + startPoi, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) + SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + endPoi} + : new double[][]{ + startPoi, + {fromX + ((currentExpandNodeIsStart || currentExpandNodeIsEnd) ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {fromX + ((currentExpandNodeIsStart || currentExpandNodeIsEnd) ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + endPoi}; + }else { // 分布在四个象限内 + if (fromX > toX && fromY > toY){ // 目标节点在第二象限 + return new double[][]{ + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W), fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY} + }; + }else if (fromX > toX && fromY < toY){ // 目标节点在第三象限 + return toY - fromY == SubProcessConst.SHAPE_VERT_INTERVAL + ? new double[][] + { + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H)}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) + SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY} + } + : new double[][] + { + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H)}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) + SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) + SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY} + }; + }else if (fromX < toX && fromY < toY){ // 目标节点在第四象限 + return new double[][]{ + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H)}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) + SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY} + }; + }else { + // fromX < toX && fromY > toY 目标节点在第一象限 分析可知 纵向排布的情况下 应该不会出现目标节点在第一象限的情况 + } + } + return new double[2][2]; + } + + private double[][] calculationLinkerPointInHorizLayOut(double[] fromPoi, double[] toPoi, boolean currentExpandNodeIsStart, boolean currentExpandNodeIsEnd) { + double fromX = fromPoi[0],fromY = fromPoi[1],toX = toPoi[0],toY = toPoi[1]; + double scopeShapeW = scopeLimitationShape.getJSONObject("props").getDoubleValue("w"), scopeShapeH = scopeLimitationShape.getJSONObject("props").getDoubleValue("h"); + if (fromY == toY) { // 水平 方向上 存在从左向右直连的情况 但不存在从右向左直连的情况 水平方向上 从左向右 应是 右出 向上 左折 向下 + return fromX < toX + ? new double[][] + { + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W), fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {toX - SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, toY + (currentExpandNodeIsEnd ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {toX, toY + (currentExpandNodeIsEnd ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2} + } + : new double[][] + { + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W), fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY} + }; + }else if (fromX == toX) { // 垂直 + // 节点横向分布 连线按照大原则 垂直 不存在 fromY < toY 的情况 也就是不存在 连线从上到下直连的情况 + double[] startPoint = new double[]{fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W), fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}; + double[] endPoint = new double[]{toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY}; + return new double[][]{startPoint, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + endPoint}; + }else { + if (fromX < toX && fromY > toY){ // 目标节点在第一象限 + double[] startPoint = new double[]{fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W), fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}; + double turnPointX = fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2; + if (fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL == toX){ // 相邻节点 存在两个折点 + double[] endPoint = new double[]{toX, toY + (currentExpandNodeIsEnd ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}; + return new double[][]{startPoint,{turnPointX, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2},{turnPointX, toY + (currentExpandNodeIsEnd ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, endPoint}; + }else { // 不相邻节点 存在三个折点 + double[] endPoint = new double[]{toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY}; + return new double[][]{ + startPoint, + {turnPointX, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {turnPointX, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + endPoint + }; + } + }else if (fromX > toX && fromY > toY) { // 目标节点在第二象限 无论节点是否相邻 都按照三个折点走 + double[] startPoint = new double[]{fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W), fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}; + double[] endPoint = new double[]{toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY}; + return new double[][]{ + startPoint, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + endPoint + }; + }else if (fromX < toX && fromY < toY){ // 目标节点在第四象限 + double[] startPoint = new double[]{fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W), fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}; + if (fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL == toX){ // 相邻节点 存在两个折点 + double turnPointX = fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2; + double[] endPoint = new double[]{toX, toY + (currentExpandNodeIsEnd ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}; + return new double[][]{ + startPoint, + {turnPointX, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {turnPointX, toY + (currentExpandNodeIsEnd ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + endPoint + }; + }else { // 不相邻节点 存在三个折点 + double[] endPoint = new double[]{toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY}; + return new double[][]{ + startPoint, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, fromY + (currentExpandNodeIsStart ? scopeShapeH : SubProcessConst.SUB_PROCESS_SHAPE_H) / 2}, + {fromX + (currentExpandNodeIsStart ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) + SubProcessConst.SHAPE_HORIZ_INTERVAL / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + {toX + (currentExpandNodeIsEnd ? scopeShapeW : SubProcessConst.SUB_PROCESS_SHAPE_W) / 2, toY - SubProcessConst.SHAPE_VERT_INTERVAL / 2}, + endPoint + }; + } + }else if (fromX > toX && fromY < toY){ // 目标节点在第三象限 横向布局的情况下 应该不会出现目标节点在第三象限的情况 + ConsolePrinter.warn("[端到端功能][节点展开模块]处理连线时目标节点在[横向布局]的情况下出现在了第三象限"); + } + } + return new double[2][2]; + } + + /** + * 计算angle值 + * @param fromPoi 起始节点坐标 + * @param toPoi 结束节点坐标 + * @param firstTurnPoi 连线第一个折点坐标 + * @param lastTurnPoi 连线最后一个折点坐标 + * @return [0]: from的angle [1]: to的angle + */ + private double[] calculationLinkerAngle(double[] fromPoi, double[] toPoi, double[] firstTurnPoi, double[] lastTurnPoi){ + double fromX = fromPoi[0], fromY = fromPoi[1]; + double toX = toPoi[0], toY = toPoi[1]; + double firstTurnX = firstTurnPoi[0], firstTurnY = firstTurnPoi[1]; + double lastTurnX = lastTurnPoi[0], lastTurnY = lastTurnPoi[1]; + + if (fromY == toY){ // 水平 + return fromX < toX ? new double[]{LinkerDefConstant.ANGLE_LEFT, LinkerDefConstant.ANGLE_RIGHT} : new double[]{LinkerDefConstant.ANGLE_RIGHT, LinkerDefConstant.ANGLE_LEFT}; + }else if (fromX == toX){ // 垂直 + return fromY < toY ? new double[]{LinkerDefConstant.ANGLE_UP, LinkerDefConstant.ANGLE_DOWN} : new double[]{LinkerDefConstant.ANGLE_DOWN, LinkerDefConstant.ANGLE_UP}; + }else { + double fromAngle = 0.0; + if (fromY == firstTurnY){ // 水平 + fromAngle = fromX < firstTurnX ? LinkerDefConstant.ANGLE_LEFT : LinkerDefConstant.ANGLE_RIGHT; + }else if (fromX == firstTurnX){ // 垂直 + fromAngle = fromY < firstTurnY ? LinkerDefConstant.ANGLE_UP : LinkerDefConstant.ANGLE_DOWN; + } + double toAngle = 0.0; + if (toY == lastTurnY){ // 水平 + toAngle = toX < lastTurnX ? LinkerDefConstant.ANGLE_LEFT : LinkerDefConstant.ANGLE_RIGHT; + }else if (toX == lastTurnX){ // 垂直 + toAngle = toY < lastTurnY ? LinkerDefConstant.ANGLE_UP : LinkerDefConstant.ANGLE_DOWN; + } + return new double[]{fromAngle, toAngle}; + } + } +} diff --git a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/web/SubProcessWeb.java b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/web/SubProcessWeb.java index be1e1ace..3242a782 100644 --- a/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/web/SubProcessWeb.java +++ b/com.actionsoft.apps.coe.method.process.subprocess/src/com/actionsoft/apps/coe/method/process/subprocess/web/SubProcessWeb.java @@ -316,7 +316,7 @@ public class SubProcessWeb extends ActionWeb { try { GraphNodeExpandHandle nodeExpandHandle = new GraphNodeExpandHandle(repositoryId, shapeId); String define = nodeExpandHandle.handleNodeExpand(); - ResponseObject ro = ResponseObject.newOkResponse("展开成功"); + ResponseObject ro = ResponseObject.newOkResponse("节点展开成功"); ro.setData(define); return ro.toString(); } catch (Exception e) { @@ -324,4 +324,16 @@ public class SubProcessWeb extends ActionWeb { } } + public String shapeNodeClose(String repositoryId, String shapeId, String endToEndProcessDefineStr){ + try { + GraphNodeCloseHandle closeHandle = new GraphNodeCloseHandle(repositoryId, shapeId, endToEndProcessDefineStr); + String define = closeHandle.handleNodeClose(); + ResponseObject ro = ResponseObject.newOkResponse("节点关闭成功"); + ro.setData(define); + return ro.toString(); + } catch (AWSException e) { + return ResponseObject.newErrResponse(e.getMessage()).toString(); + } + } + } diff --git a/com.actionsoft.apps.coe.method.process.subprocess/web/com.actionsoft.apps.coe.method.process.subprocess/action.xml b/com.actionsoft.apps.coe.method.process.subprocess/web/com.actionsoft.apps.coe.method.process.subprocess/action.xml index fd2d64bd..6b37bcd8 100644 --- a/com.actionsoft.apps.coe.method.process.subprocess/web/com.actionsoft.apps.coe.method.process.subprocess/action.xml +++ b/com.actionsoft.apps.coe.method.process.subprocess/web/com.actionsoft.apps.coe.method.process.subprocess/action.xml @@ -31,4 +31,9 @@ + + + + + \ No newline at end of file diff --git a/com.actionsoft.apps.coe.pal/web/com.actionsoft.apps.coe.pal/lib/designer/scripts/diagraming/designer.method.subprocess.js b/com.actionsoft.apps.coe.pal/web/com.actionsoft.apps.coe.pal/lib/designer/scripts/diagraming/designer.method.subprocess.js index 5989b045..fde93702 100644 --- a/com.actionsoft.apps.coe.pal/web/com.actionsoft.apps.coe.pal/lib/designer/scripts/diagraming/designer.method.subprocess.js +++ b/com.actionsoft.apps.coe.pal/web/com.actionsoft.apps.coe.pal/lib/designer/scripts/diagraming/designer.method.subprocess.js @@ -165,7 +165,29 @@ class SubProcess { shapeClose(event){ console.log('sss'); let param = event.data; + awsui.ajax.request({ + url: './jd', + method: 'POST', + data: { + cmd: 'com.actionsoft.apps.coe.method.process.subprocess.shape_close', + sid: param.sid, + repositoryId: param.repositoryId, + shapeId: param.shapeId, + endToEndProcessDefineStr: definition + }, + ok: function (r) { + definition = JSON.stringify(r.data); + Designer.open(definition); // 节点重新渲染 + // 针对范围标识框渲染 节点关闭按钮 + window.subProcess.shapeIconRender(); + // 提示用户文件已修改 + window.subProcess.fileModifiedTip(); + }, + err: function (r) { + + } + }); } fileModifiedTip(){