feat(admin): 画布↔卡片数据打通——卡片树为唯一数据源(uid 即 contentJson 节点 id, 画布=contentJson 投影/反解); 多人审批默认或签; 选人弹窗换 /admin/user/teacherTree 教师树; 示意图页三视图(示意图/画布视图/进度图PNG)
build / build (push) Successful in 2m3s
build / build (push) Successful in 2m3s
This commit is contained in:
@@ -22,14 +22,13 @@ import {
|
||||
ParallelNode,
|
||||
createApproval,
|
||||
newUid,
|
||||
resetUidSeq,
|
||||
} from "./model";
|
||||
|
||||
/** 编辑树 → 后端 contentJson(code/name 恒取表单值,规避 contentJson 覆盖行字段的既有坑) */
|
||||
export function toContentJson(model: EditorModel): DefJson {
|
||||
const nodes: DefNodeJson[] = [];
|
||||
const edges: DefEdgeJson[] = [];
|
||||
let seq = 1;
|
||||
const nid = (p: string) => `${p}${seq++}`;
|
||||
|
||||
// 端点 name 仅在既有定义带值时回写(新建流程不生成,保持最小 JSON)
|
||||
const startNode: DefNodeJson = { id: "start", type: "START" };
|
||||
@@ -40,7 +39,7 @@ export function toContentJson(model: EditorModel): DefJson {
|
||||
let cursor = prev;
|
||||
items.forEach((item) => {
|
||||
if (item.kind === "approval") {
|
||||
const id = nid("n");
|
||||
const id = item.uid;
|
||||
const node: DefNodeJson = {
|
||||
id,
|
||||
type: "APPROVAL",
|
||||
@@ -54,8 +53,8 @@ export function toContentJson(model: EditorModel): DefJson {
|
||||
cursor = id;
|
||||
return;
|
||||
}
|
||||
const split = nid("split");
|
||||
const join = nid("join");
|
||||
const split = `${item.uid}s`;
|
||||
const join = `${item.uid}j`;
|
||||
nodes.push({ id: split, type: "PARALLEL_SPLIT" });
|
||||
nodes.push({ id: join, type: "PARALLEL_JOIN", waitFor: "ALL" });
|
||||
edges.push({ from: cursor, to: split });
|
||||
@@ -87,6 +86,11 @@ export function toContentJson(model: EditorModel): DefJson {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* toContentJson 的节点 id 直接取编辑态 uid(u 段 / uid 后缀 +s、+j):id 稳定可回溯,
|
||||
* bpmn 画布与卡片共用同一套 id——画布上选中元素的 id 即卡片 uid,配置抽屉无需二次映射;
|
||||
* id 仅要求"非空且唯一"(DefValidator 口径)。
|
||||
*/
|
||||
function cloneAssignee(a: Assignee | null): Assignee {
|
||||
if (!a) return { type: "DEPT_KEY", deptKey: "" };
|
||||
return a.type === "USERS" ? { type: "USERS", userIds: [...a.userIds] } : { type: "DEPT_KEY", deptKey: a.deptKey };
|
||||
@@ -106,6 +110,8 @@ export interface ParsedDef {
|
||||
* contentJson → 编辑树。抛错即为"不可还原为卡片视图",调用方展示错误并建议源码模式。
|
||||
*/
|
||||
export function fromContentJson(def: any): ParsedDef {
|
||||
UsedIds.set.clear();
|
||||
resetUidSeq();
|
||||
if (!def || !Array.isArray(def.nodes) || !Array.isArray(def.edges)) {
|
||||
throw new Error("JSON 需包含 nodes 与 edges 数组");
|
||||
}
|
||||
@@ -240,8 +246,10 @@ export function fromContentJson(def: any): ParsedDef {
|
||||
|
||||
function toApprovalNode(n: any): ApprovalNode {
|
||||
const countersign: Countersign = n.countersign === "ANY" ? "ANY" : "ALL";
|
||||
// uid 复用 JSON 原节点 id(如 n3):载入→卡片→画布→保存全程同一套 id,卡片树与画布
|
||||
// 节点一一对应(业务配置按 id 继承的通道依赖这一点);缺 id/撞 id 才退回新生成 uid。
|
||||
return {
|
||||
uid: newUid(),
|
||||
uid: UsedIds.take(n.id),
|
||||
kind: "approval",
|
||||
name: n.name || "审批",
|
||||
assignee: n.assignee ? cloneAssignee(n.assignee as Assignee) : null,
|
||||
@@ -250,6 +258,22 @@ function toApprovalNode(n: any): ApprovalNode {
|
||||
};
|
||||
}
|
||||
|
||||
/** 载入会话内已占用的 uid 集(防重复/缺 id 撞车) */
|
||||
const UsedIds = {
|
||||
set: new Set<string>(),
|
||||
take(raw: unknown): string {
|
||||
const s = raw == null ? "" : String(raw).trim();
|
||||
if (s && !this.set.has(s)) {
|
||||
this.set.add(s);
|
||||
return s;
|
||||
}
|
||||
let u = newUid();
|
||||
while (this.set.has(u)) u = newUid();
|
||||
this.set.add(u);
|
||||
return u;
|
||||
},
|
||||
};
|
||||
|
||||
/** 还原时保留真实分支结构(含 1 分支的非常规定义,交给校验提示);仅空分支补占位,避免画布出现空容器 */
|
||||
function makeParallel(branches: EditorItem[][]): ParallelNode {
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user