- def-editor 新增画布模式(默认):bpmn-js 内存画布(精简 palette/汉化/撤销重做/缩放/出口命名/导出 BPMN), toBpmn/fromBpmn 双向序列化,contentJson 仍为持久格式(+layout 坐标);卡片模式保留回退 - 新增 utils/flowLayout.ts 分层布局(设计器与查看器共用) - TaskHandle:多出口节点出口选择下拉;退回/移交按钮与弹窗;轨迹显示已退回/已移交及所选出口 - FlowDiagram:优先使用保存坐标;边画出口名标签;办件状态着色/当前节点高亮 - api/flow:approve 带 edgeName,新增 returnBack/transfer
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* 流程图自动布局(从 FlowDiagram.vue 提取的设计器/查看器共用实现):
|
||||
* Kahn 拓扑分层 depth(to)=max(depth(from))+1,从左到右布列、同列按定义顺序纵排。
|
||||
* 设计器(contentJson 无 layout 坐标时算初始坐标)与查看器(无坐标回退)共用同一口径。
|
||||
*/
|
||||
|
||||
export const SLOT_W = 200; // 节点槽位宽
|
||||
export const SLOT_H = 88; // 节点槽位高
|
||||
export const COL_GAP = 72; // 列间距(留给正交折线走线)
|
||||
export const ROW_GAP = 24; // 行间距
|
||||
export const PAD = 24;
|
||||
|
||||
export const NODE_BOX: Record<string, { w: number; h: number }> = {
|
||||
START: { w: 104, h: 40 },
|
||||
END: { w: 104, h: 40 },
|
||||
APPROVAL: { w: 184, h: 64 },
|
||||
PARALLEL_SPLIT: { w: 128, h: 72 },
|
||||
PARALLEL_JOIN: { w: 128, h: 72 },
|
||||
};
|
||||
|
||||
export interface Pos {
|
||||
x: number;
|
||||
y: number;
|
||||
w: number;
|
||||
h: number;
|
||||
depth: number;
|
||||
row: number;
|
||||
}
|
||||
|
||||
/** 拓扑分层布局:入参 {nodes,edges},返回 nodeId → 位置 */
|
||||
export function layout(data: any): Record<string, Pos> {
|
||||
const nodes: any[] = data?.nodes || [];
|
||||
const edges: any[] = data?.edges || [];
|
||||
if (!nodes.length) return {};
|
||||
const inDeg: Record<string, number> = {};
|
||||
nodes.forEach((n) => { inDeg[n.id] = 0; });
|
||||
edges.forEach((e) => { if (inDeg[e.to] !== undefined) inDeg[e.to]++; });
|
||||
const depth: Record<string, number> = {};
|
||||
const queue = nodes.filter((n) => !inDeg[n.id]).map((n) => n.id);
|
||||
queue.forEach((id) => { depth[id] = 0; });
|
||||
const order = [...queue];
|
||||
while (order.length) {
|
||||
const id = order.shift()!;
|
||||
edges.forEach((e) => {
|
||||
if (e.from !== id || inDeg[e.to] === undefined) return;
|
||||
depth[e.to] = Math.max(depth[e.to] ?? 0, (depth[id] ?? 0) + 1);
|
||||
if (--inDeg[e.to] === 0) order.push(e.to);
|
||||
});
|
||||
}
|
||||
const rowsIn: Record<number, number> = {};
|
||||
const pos: Record<string, Pos> = {};
|
||||
nodes.forEach((n) => {
|
||||
const d = depth[n.id] ?? 0;
|
||||
depth[n.id] = d;
|
||||
const row = rowsIn[d] ?? 0;
|
||||
rowsIn[d] = row + 1;
|
||||
const box = NODE_BOX[n.type] || NODE_BOX.APPROVAL;
|
||||
pos[n.id] = {
|
||||
x: PAD + d * (SLOT_W + COL_GAP) + (SLOT_W - box.w) / 2,
|
||||
y: PAD + row * (SLOT_H + ROW_GAP) + (SLOT_H - box.h) / 2,
|
||||
w: box.w,
|
||||
h: box.h,
|
||||
depth: d,
|
||||
row,
|
||||
};
|
||||
});
|
||||
return pos;
|
||||
}
|
||||
Reference in New Issue
Block a user