应用页规范(bsch)重构: - 6 个列表页迁规范骨架: page-wrapper/tabs-wrapper + search-wrap 搜索区(关键字 200px 即输即搜, 去查询按钮) + link-table table-bottom-box 定高表格 + page-panel 分页栏 - 分页列表统一接 TablePageAlovaNew(页长 [10,20,30,40]/customPageCount 记忆/增删智能翻页); dataApi 须返回 Method, 调 .send() 会导致表格静默空数据 - 操作列统一图标式 operatingHandle(Tooltip+fixed right 居中); 各列表补序号列; 危险操作统一 MyConfirm 二次确认 - 弹窗表单必填红星 + 统一提示「请填写红色星号内容!」 - TablePageAlovaNew 补页长记忆与重载后清空勾选; 新增 src/types OperatorEnum 工作台与流程轨迹优化: - 已办列表去重: 每办件一行, doneCount>1 显示「共办理 N 次」标签 - 新增 TraceTimeline 轨迹时间流(时间升序/kind/轮次, 按人员/科室切换写回 URL), 接入详情/办理页 - 新增流程源码只读抽屉 SourceDrawer, 接入定义列表与图示页 - 办理人映射/流程定义新增「启用」回路(/admin/map/enable, /admin/def/enable) - 图示视图口径统一 dept/user 两档, 缺省人员视图; 状态列 Tag 色映射统一导出
This commit is contained in:
@@ -29,6 +29,13 @@ export interface Pos {
|
||||
|
||||
/** 拓扑分层布局:入参 {nodes,edges},返回 nodeId → 位置 */
|
||||
export function layout(data: any): Record<string, Pos> {
|
||||
const pos = layered(data);
|
||||
centerParallelGroups(data, pos);
|
||||
return pos;
|
||||
}
|
||||
|
||||
/** Kahn 分层 + 同列按定义顺序纵排(基础网格,保证无重叠) */
|
||||
function layered(data: any): Record<string, Pos> {
|
||||
const nodes: any[] = data?.nodes || [];
|
||||
const edges: any[] = data?.edges || [];
|
||||
if (!nodes.length) return {};
|
||||
@@ -66,3 +73,92 @@ export function layout(data: any): Record<string, Pos> {
|
||||
});
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* 并行分支组垂直居中:分支不再从 SPLIT 行向下堆叠,而是以 SPLIT 中心线为轴上下对称展开
|
||||
* (与钉钉/参考稿一致)。SPLIT↔JOIN 配对沿用编辑器口径(各出边可达集求交,取 BFS 序最早的
|
||||
* PARALLEL_JOIN),组员 = SPLIT 到 JOIN 之间可达的全部节点;JOIN 中线吸附回 SPLIT 中线。
|
||||
* 内层组先于外层组处理(嵌套并行整体平移不破坏内层居中);结束统一把最顶行抬回 PAD。
|
||||
*/
|
||||
function centerParallelGroups(data: any, pos: Record<string, Pos>): void {
|
||||
const nodes: any[] = data?.nodes || [];
|
||||
const edges: any[] = data?.edges || [];
|
||||
const out: Record<string, string[]> = {};
|
||||
edges.forEach((e) => {
|
||||
if (pos[e.from] && pos[e.to]) (out[e.from] = out[e.from] || []).push(e.to);
|
||||
});
|
||||
const byId: Record<string, any> = {};
|
||||
nodes.forEach((n) => { byId[n.id] = n; });
|
||||
|
||||
const groups = nodes
|
||||
.filter((n) => n.type === "PARALLEL_SPLIT" && pos[n.id])
|
||||
.map((split) => {
|
||||
// BFS 序 + 各分支可达集,用于定位配对 JOIN
|
||||
const order = new Map<string, number>([[split.id, 0]]);
|
||||
const queue: string[] = [split.id];
|
||||
while (queue.length) {
|
||||
const cur = queue.shift()!;
|
||||
(out[cur] || []).forEach((to) => {
|
||||
if (!order.has(to)) {
|
||||
order.set(to, order.size);
|
||||
queue.push(to);
|
||||
}
|
||||
});
|
||||
}
|
||||
const reachSets = (out[split.id] || []).map((head) => {
|
||||
const set = new Set<string>();
|
||||
const q: string[] = [head];
|
||||
while (q.length) {
|
||||
const cur = q.shift()!;
|
||||
if (set.has(cur)) continue;
|
||||
set.add(cur);
|
||||
(out[cur] || []).forEach((to) => q.push(to));
|
||||
}
|
||||
return set;
|
||||
});
|
||||
let common = reachSets[0] || new Set<string>();
|
||||
reachSets.forEach((set) => { common = new Set([...common].filter((x) => set.has(x))); });
|
||||
const join = nodes
|
||||
.filter((n) => n.type === "PARALLEL_JOIN" && pos[n.id] && common.has(n.id))
|
||||
.sort((a, b) => (order.get(a.id) ?? 0) - (order.get(b.id) ?? 0))[0];
|
||||
if (!join) return null;
|
||||
// 组员:SPLIT 起步、不穿过 JOIN 的可达集(含嵌套 SPLIT/JOIN)
|
||||
const members: string[] = [];
|
||||
const seen = new Set<string>([join.id]);
|
||||
const q: string[] = [split.id];
|
||||
while (q.length) {
|
||||
const cur = q.shift()!;
|
||||
(out[cur] || []).forEach((to) => {
|
||||
if (seen.has(to)) return;
|
||||
seen.add(to);
|
||||
members.push(to);
|
||||
q.push(to);
|
||||
});
|
||||
}
|
||||
return { split: split.id, join: join.id, members };
|
||||
})
|
||||
.filter(Boolean)
|
||||
.sort((a: any, b: any) => Math.max(...b.members.map((m: string) => pos[m].depth))
|
||||
- Math.max(...a.members.map((m: string) => pos[m].depth)));
|
||||
|
||||
groups.forEach((g: any) => {
|
||||
const axisY = pos[g.split].y + pos[g.split].h / 2;
|
||||
let top = Infinity;
|
||||
let bottom = -Infinity;
|
||||
g.members.forEach((id: string) => {
|
||||
top = Math.min(top, pos[id].y);
|
||||
bottom = Math.max(bottom, pos[id].y + pos[id].h);
|
||||
});
|
||||
const delta = axisY - (top + bottom) / 2;
|
||||
g.members.forEach((id: string) => { pos[id].y += delta; });
|
||||
pos[g.join].y += axisY - (pos[g.join].y + pos[g.join].h / 2);
|
||||
});
|
||||
|
||||
// 上移出界的组把整图平移回 PAD(同 delta 平移不破坏各组居中)
|
||||
let minY = Infinity;
|
||||
Object.values(pos).forEach((p) => { minY = Math.min(minY, p.y); });
|
||||
if (minY < PAD) {
|
||||
const lift = PAD - minY;
|
||||
Object.values(pos).forEach((p) => { p.y += lift; });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user