@logicflow/extension 2.1.4 → 2.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,10 +6,18 @@ export type ProximityConnectProps = {
6
6
  distance: number;
7
7
  reverseDirection: boolean;
8
8
  virtualEdgeStyle: Record<string, unknown>;
9
+ /**
10
+ * proximityConnect 类型:
11
+ * - 'node': 节点-节点连接
12
+ * - 'anchor': 锚点-锚点连接
13
+ * - 'default': 节点-锚点连接
14
+ */
15
+ type: 'node' | 'anchor' | 'default';
9
16
  };
10
17
  export declare class ProximityConnect {
11
18
  static pluginName: string;
12
19
  enable: boolean;
20
+ type: 'node' | 'anchor' | 'default';
13
21
  lf: LogicFlow;
14
22
  closestNode?: BaseNodeModel;
15
23
  currentDistance: number;
@@ -15,6 +15,7 @@ var ProximityConnect = /** @class */ (function () {
15
15
  function ProximityConnect(_a) {
16
16
  var lf = _a.lf, options = _a.options;
17
17
  this.enable = true;
18
+ this.type = 'default';
18
19
  this.currentDistance = Infinity; // 当前间距
19
20
  this.thresholdDistance = 100; // 节点-节点连接距离阈值
20
21
  this.reverseDirection = false; // 节点-节点连线方向,默认是拖拽节点连向最近节点
@@ -34,6 +35,8 @@ var ProximityConnect = /** @class */ (function () {
34
35
  // 节点开始拖拽事件
35
36
  this.lf.graphModel.eventCenter.on('node:dragstart', function (_a) {
36
37
  var data = _a.data;
38
+ if (_this.type === 'anchor')
39
+ return;
37
40
  if (!_this.enable)
38
41
  return;
39
42
  var graphModel = _this.lf.graphModel;
@@ -42,12 +45,14 @@ var ProximityConnect = /** @class */ (function () {
42
45
  });
43
46
  // 节点拖拽事件
44
47
  this.lf.graphModel.eventCenter.on('node:drag', function () {
48
+ if (_this.type === 'anchor')
49
+ return;
45
50
  _this.handleNodeDrag();
46
51
  });
47
52
  // 锚点开始拖拽事件
48
53
  this.lf.graphModel.eventCenter.on('anchor:dragstart', function (_a) {
49
54
  var data = _a.data, nodeModel = _a.nodeModel;
50
- if (!_this.enable)
55
+ if (!_this.enable || _this.type === 'node')
51
56
  return;
52
57
  _this.currentNode = nodeModel;
53
58
  _this.currentAnchor = data;
@@ -55,20 +60,20 @@ var ProximityConnect = /** @class */ (function () {
55
60
  // 锚点拖拽事件
56
61
  this.lf.graphModel.eventCenter.on('anchor:drag', function (_a) {
57
62
  var _b = _a.e, clientX = _b.clientX, clientY = _b.clientY;
58
- if (!_this.enable)
63
+ if (!_this.enable || _this.type === 'node')
59
64
  return;
60
65
  _this.handleAnchorDrag(clientX, clientY);
61
66
  });
62
67
  // 节点、锚点拖拽结束事件
63
68
  this.lf.graphModel.eventCenter.on('node:drop', function () {
64
- if (!_this.enable)
69
+ if (!_this.enable || _this.type === 'anchor')
65
70
  return;
66
71
  _this.handleDrop();
67
72
  });
68
73
  // 锚点拖拽需要单独判断一下当前拖拽终点是否在某个锚点上,如果是,就不触发插件的连线,以免出现创建了两条连线的问题,表现见 issue 2140
69
74
  this.lf.graphModel.eventCenter.on('anchor:dragend', function (_a) {
70
75
  var e = _a.e, edgeModel = _a.edgeModel;
71
- if (!_this.enable)
76
+ if (!_this.enable || _this.type === 'node')
72
77
  return;
73
78
  var _b = _this.lf.graphModel.getPointByClient({
74
79
  x: e.clientX,
@@ -32,6 +32,14 @@ export type ToImageOptions = {
32
32
  * - `true`:只导出画面区域内的可见元素
33
33
  */
34
34
  partial?: boolean;
35
+ /**
36
+ * 导出图片时的安全系数,用于确保导出的图片能够容纳所有元素,默认值为 1.1
37
+ */
38
+ safetyFactor?: number;
39
+ /**
40
+ * 导出图片时的安全边距,用于确保导出的图片能够容纳所有元素,默认值为 40
41
+ */
42
+ safetyMargin?: number;
35
43
  };
36
44
  export type SnapshotResponse = {
37
45
  data: Blob | string;
@@ -404,7 +404,7 @@ var Snapshot = /** @class */ (function () {
404
404
  graphModel = this.lf.graphModel;
405
405
  transformModel = graphModel.transformModel;
406
406
  SCALE_X = transformModel.SCALE_X, SCALE_Y = transformModel.SCALE_Y, TRANSLATE_X = transformModel.TRANSLATE_X, TRANSLATE_Y = transformModel.TRANSLATE_Y;
407
- safetyFactor = 1.1 // 安全系数,增加20%的空间
407
+ safetyFactor = toImageOptions.safetyFactor || 1.1 // 安全系数,增加10%的空间
408
408
  ;
409
409
  actualWidth = (bbox.width / SCALE_X) * safetyFactor;
410
410
  actualHeight = (bbox.height / SCALE_Y) * safetyFactor;
@@ -413,7 +413,7 @@ var Snapshot = /** @class */ (function () {
413
413
  canvas = document.createElement('canvas');
414
414
  canvas.style.width = "".concat(bboxWidth, "px");
415
415
  canvas.style.height = "".concat(bboxHeight, "px");
416
- safetyMargin = 40 // 额外的安全边距
416
+ safetyMargin = toImageOptions.safetyMargin || 40 // 额外的安全边距
417
417
  ;
418
418
  _b = this.getCanvasDimensionsByBrowser(), maxCanvasDimension = _b.maxCanvasDimension, otherMaxCanvasDimension = _b.otherMaxCanvasDimension;
419
419
  MAX_CANVAS_DIMENSION = maxCanvasDimension;
@@ -1,3 +1,9 @@
1
+ /**
2
+ * LogicFlow 节点配置(导入/导出过程中使用的中间结构)
3
+ * - id/type/x/y:节点基本信息
4
+ * - text:节点文本的中心坐标与内容(值为未转义的原始字符串)
5
+ * - properties:节点的额外属性(会保留到 BPMN 的扩展字段)
6
+ */
1
7
  type NodeConfig = {
2
8
  id: string;
3
9
  properties?: Record<string, unknown>;
@@ -10,10 +16,20 @@ type NodeConfig = {
10
16
  x: number;
11
17
  y: number;
12
18
  };
19
+ /**
20
+ * 点坐标结构(用于边的路径点)
21
+ */
13
22
  type Point = {
14
23
  x: number;
15
24
  y: number;
16
25
  };
26
+ /**
27
+ * LogicFlow 边配置(导入/导出过程中使用的中间结构)
28
+ * - id/type/sourceNodeId/targetNodeId:边的基本信息
29
+ * - pointsList:边的路径点(用于 BPMN 的 di:waypoint)
30
+ * - text:边文本的位置与内容(值为未转义的原始字符串)
31
+ * - properties:边的扩展属性
32
+ */
17
33
  type EdgeConfig = {
18
34
  id: string;
19
35
  sourceNodeId: string;
@@ -35,11 +51,29 @@ type EdgeConfig = {
35
51
  pointsList?: Point[];
36
52
  properties: Record<string, unknown>;
37
53
  };
54
+ /**
55
+ * 将普通 JSON 转换为 XML 风格 JSON(xmlJson)
56
+ * 输入:任意 JSON 对象;可选的保留属性字段 retainedFields
57
+ * 输出:遵循 XML 属性前缀约定的 xmlJson(属性键以 '-' 开头)
58
+ * 规则:
59
+ * - 原始字符串直接返回;数组逐项转换;对象根据键类型决定是否加 '-' 前缀。
60
+ * - 保留字段(fields)中出现的键以属性形式(带 '-')保留,否则视为子节点。
61
+ */
38
62
  declare function toXmlJson(retainedFields?: string[]): (json: string | any[] | Record<string, any>) => any;
39
63
  /**
40
- * 将xmlJson转换为普通的json,在内部使用。
64
+ * 将 XML 风格 JSON(xmlJson)转换回普通 JSON(内部使用)
65
+ * 输入:遵循 '-' 属性前缀约定的 xmlJson
66
+ * 输出:去除前缀并恢复原有结构的普通 JSON
41
67
  */
42
68
  declare function toNormalJson(xmlJson: any): {};
69
+ /**
70
+ * BpmnAdapter:基础适配器
71
+ *
72
+ * 作用:在 LogicFlow 数据与 BPMN JSON 之间进行转换,并注入 adapterIn/adapterOut 钩子。
73
+ * - processAttributes:导出时 BPMN process 的基础属性(可配置 isExecutable、id 等)。
74
+ * - definitionAttributes:导出时 BPMN definitions 的基础属性与命名空间声明。
75
+ * - shapeConfigMap:不同 BPMN 元素类型的默认宽高,用于坐标/Bounds 计算。
76
+ */
43
77
  declare class BpmnAdapter {
44
78
  static pluginName: string;
45
79
  static shapeConfigMap: Map<any, any>;
@@ -59,6 +93,11 @@ declare class BpmnAdapter {
59
93
  ['-exporterVersion']: string;
60
94
  [key: string]: any;
61
95
  };
96
+ /**
97
+ * 构造函数
98
+ * - 注入 LogicFlow 的 adapterIn/adapterOut(处理 JSON 方向的适配)
99
+ * - 初始化 process 与 definitions 的基础属性
100
+ */
62
101
  constructor({ lf }: {
63
102
  lf: any;
64
103
  });
@@ -68,6 +107,13 @@ declare class BpmnAdapter {
68
107
  * ["properties", "startPoint", "endPoint", "pointsList"]合并,
69
108
  * 这意味着出现在这个数组里的字段当它的值是数组或是对象时不会被视为一个节点而是一个属性。
70
109
  */
110
+ /**
111
+ * adapterOut:将 LogicFlow 图数据转换为 BPMN JSON
112
+ * 输入:
113
+ * - data:LogicFlow GraphData
114
+ * - retainedFields:扩展属性保留字段
115
+ * 输出:BPMN JSON(包含 definitions/process/diagram/plane)
116
+ */
71
117
  adapterOut: (data: any, retainedFields?: string[]) => {
72
118
  'bpmn:definitions': {
73
119
  [key: string]: any;
@@ -82,18 +128,55 @@ declare class BpmnAdapter {
82
128
  "-exporterVersion": string;
83
129
  };
84
130
  };
131
+ /**
132
+ * adapterIn:将 BPMN JSON 转换为 LogicFlow 图数据
133
+ * 输入:bpmnData:BPMN JSON
134
+ * 输出:GraphConfigData(nodes/edges)
135
+ */
85
136
  adapterIn: (bpmnData: any) => {
86
137
  nodes: NodeConfig[];
87
138
  edges: EdgeConfig[];
88
139
  } | undefined;
89
140
  }
141
+ /**
142
+ * BpmnXmlAdapter:XML 适配器(继承 BpmnAdapter)
143
+ *
144
+ * 作用:处理 XML 输入/输出的适配,使用 xml2json/json2xml 完成格式转换。
145
+ * 特殊处理:在 XML 导入前对 name 属性的非法字符进行预处理转义,提升容错。
146
+ */
90
147
  declare class BpmnXmlAdapter extends BpmnAdapter {
91
148
  static pluginName: string;
149
+ /**
150
+ * 构造函数
151
+ * - 覆盖 LogicFlow 的 adapterIn/adapterOut,使其面向 XML 输入与输出。
152
+ */
92
153
  constructor(data: any);
154
+ /**
155
+ * 预处理 XML:仅对 name 属性值进行非法字符转义(<, >, &),避免 DOM 解析失败。
156
+ * 注意:不影响已合法的实体(如 &amp;),仅在属性值中生效,不修改其它内容。
157
+ */
158
+ private sanitizeNameAttributes;
159
+ /**
160
+ * adapterXmlIn:将 BPMN XML 转换为 LogicFlow 图数据
161
+ * 输入:bpmnData:XML 字符串或对象
162
+ * 步骤:
163
+ * 1) 若为字符串,先对 name 属性进行预处理转义;
164
+ * 2) 使用 lfXml2Json 转换为 BPMN JSON;
165
+ * 3) 调用基础 adapterIn 转换为 GraphData。
166
+ */
93
167
  adapterXmlIn: (bpmnData: any) => {
94
168
  nodes: NodeConfig[];
95
169
  edges: EdgeConfig[];
96
170
  } | undefined;
171
+ /**
172
+ * adapterXmlOut:将 LogicFlow 图数据转换为 BPMN XML
173
+ * 输入:
174
+ * - data:GraphData
175
+ * - retainedFields:保留属性字段
176
+ * 步骤:
177
+ * 1) 调用基础 adapterOut 生成 BPMN JSON;
178
+ * 2) 使用 lfJson2Xml 转为合法的 XML 字符串(包含属性与文本的转义)。
179
+ */
97
180
  adapterXmlOut: (data: any, retainedFields?: string[]) => string;
98
181
  }
99
182
  export { BpmnAdapter, BpmnXmlAdapter, toXmlJson, toNormalJson };
@@ -46,7 +46,23 @@ exports.toNormalJson = exports.toXmlJson = exports.BpmnXmlAdapter = exports.Bpmn
46
46
  var bpmnIds_1 = require("./bpmnIds");
47
47
  var json2xml_1 = require("./json2xml");
48
48
  var xml2json_1 = require("./xml2json");
49
+ /**
50
+ * 模块说明(BPMN Adapter)
51
+ *
52
+ * 该模块负责在 LogicFlow 内部图数据(GraphData)与 BPMN XML/JSON 之间进行双向转换:
53
+ * - adapterOut:将 LogicFlow 图数据转换为 BPMN JSON(随后由 json2xml 转为 XML)
54
+ * - adapterIn:将 BPMN JSON 转换为 LogicFlow 图数据(如果是 XML,则先经 xml2json 转为 JSON)
55
+ *
56
+ * 设计要点与特殊处理:
57
+ * - BPMN XML 的属性在 JSON 中以前缀 '-' 表示(如 '-id'、'-name'),本模块严格遵循该约定。
58
+ * - XML 中同名子节点可能出现多次,xml2json 解析后会以数组表示;本模块对数组与单对象场景均做兼容处理。
59
+ * - BPMN 画布坐标以元素左上角为基准,而 LogicFlow 以元素中心为基准;转换时需进行坐标基准转换。
60
+ * - 文本内容在导出时进行 XML 转义,在导入时进行反转义,确保特殊字符(如 <, >, & 等)能被正确保留。
61
+ */
49
62
  var constant_1 = require("../bpmn/constant");
63
+ /**
64
+ * BPMN 元素类型映射(用于在 JSON 中定位具体的 BPMN 节点类型)
65
+ */
50
66
  var BpmnElements;
51
67
  (function (BpmnElements) {
52
68
  BpmnElements["START"] = "bpmn:startEvent";
@@ -56,6 +72,11 @@ var BpmnElements;
56
72
  BpmnElements["SYSTEM"] = "bpmn:serviceTask";
57
73
  BpmnElements["FLOW"] = "bpmn:sequenceFlow";
58
74
  })(BpmnElements || (BpmnElements = {}));
75
+ /**
76
+ * BPMN 过程元素的标准属性键列表
77
+ * - 在解析 `processValue` 时,这些键会被视为标准属性而非扩展属性;
78
+ * - 其余未在列表中的键会进入 LogicFlow 的 `properties` 中,以保留扩展数据。
79
+ */
59
80
  var defaultAttrs = [
60
81
  '-name',
61
82
  '-id',
@@ -74,17 +95,49 @@ var defaultAttrs = [
74
95
  * 这意味着出现在这个数组里的字段当它的值是数组或是对象时不会被视为一个节点而是一个属性
75
96
  * @reference node type reference https://www.w3schools.com/xml/dom_nodetype.asp
76
97
  */
98
+ /**
99
+ * 导出至 BPMN JSON 时,作为属性保留的字段列表
100
+ * - 当这些字段的值为对象或数组时,仍视为属性(在 JSON 中以 '-' 前缀表示),而非子节点。
101
+ */
77
102
  var defaultRetainedFields = [
78
103
  'properties',
79
104
  'startPoint',
80
105
  'endPoint',
81
106
  'pointsList',
82
107
  ];
108
+ /**
109
+ * XML 实体反转义:
110
+ * - 将常见的 XML 实体还原为字符:`&lt;`→`<`、`&gt;`→`>`、`&amp;`→`&`、`&quot;`→`"`、`&apos;`→`'`;保障流程图能正常回填
111
+ * - 使用 `String(text || '')` 规范化输入,避免 `null/undefined` 导致错误;
112
+ * - 注意:此实现为单次替换,若存在嵌套/二次编码(例如 `&amp;lt;`),会先还原为 `&lt;`,
113
+ * 如需完全解码,可在外层循环调用或调整替换顺序策略。
114
+ */
115
+ var unescapeXml = function (text) {
116
+ return String(text || '')
117
+ .replace(/&lt;/g, '<')
118
+ .replace(/&gt;/g, '>')
119
+ .replace(/&amp;/g, '&')
120
+ .replace(/&quot;/g, '"')
121
+ .replace(/&apos;/g, "'");
122
+ };
123
+ /**
124
+ * 将普通 JSON 转换为 XML 风格 JSON(xmlJson)
125
+ * 输入:任意 JSON 对象;可选的保留属性字段 retainedFields
126
+ * 输出:遵循 XML 属性前缀约定的 xmlJson(属性键以 '-' 开头)
127
+ * 规则:
128
+ * - 原始字符串直接返回;数组逐项转换;对象根据键类型决定是否加 '-' 前缀。
129
+ * - 保留字段(fields)中出现的键以属性形式(带 '-')保留,否则视为子节点。
130
+ */
83
131
  function toXmlJson(retainedFields) {
84
132
  var fields = retainedFields
85
133
  ? defaultRetainedFields.concat(retainedFields)
86
134
  : defaultRetainedFields;
87
135
  return function (json) {
136
+ /**
137
+ * 递归转换核心方法
138
+ * @param obj 输入对象/数组/字符串
139
+ * @returns 转换后的 xmlJson
140
+ */
88
141
  function ToXmlJson(obj) {
89
142
  var xmlJson = {};
90
143
  if (typeof obj === 'string') {
@@ -119,7 +172,9 @@ function toXmlJson(retainedFields) {
119
172
  }
120
173
  exports.toXmlJson = toXmlJson;
121
174
  /**
122
- * 将xmlJson转换为普通的json,在内部使用。
175
+ * 将 XML 风格 JSON(xmlJson)转换回普通 JSON(内部使用)
176
+ * 输入:遵循 '-' 属性前缀约定的 xmlJson
177
+ * 输出:去除前缀并恢复原有结构的普通 JSON
123
178
  */
124
179
  function toNormalJson(xmlJson) {
125
180
  var json = {};
@@ -153,6 +208,18 @@ exports.toNormalJson = toNormalJson;
153
208
  * 2)如果只有一个子元素,json中表示为正常属性
154
209
  * 3)如果是多个子元素,json中使用数组存储
155
210
  */
211
+ /**
212
+ * 将 LogicFlow 图数据中的节点与边转换为 BPMN 的 process 数据结构
213
+ * 输入:
214
+ * - bpmnProcessData:输出目标对象(会被填充 '-id'、各 bpmn:* 节点以及 sequenceFlow)
215
+ * - data:LogicFlow 图数据(nodes/edges)
216
+ * - retainedFields:可选保留属性字段,用于控制属性与子节点的映射
217
+ * 输出:直接修改 bpmnProcessData
218
+ * 特殊处理:
219
+ * - 节点文本(node.text.value)作为 BPMN 的 '-name' 属性;
220
+ * - 维护 incoming/outgoing 的顺序,保证解析兼容性;
221
+ * - 多子元素时转为数组结构(XML 约定)。
222
+ */
156
223
  function convertLf2ProcessData(bpmnProcessData, data, retainedFields) {
157
224
  var nodeMap = new Map();
158
225
  data.nodes.forEach(function (node) {
@@ -226,6 +293,16 @@ function convertLf2ProcessData(bpmnProcessData, data, retainedFields) {
226
293
  /**
227
294
  * adapterOut 设置bpmn diagram信息
228
295
  */
296
+ /**
297
+ * 将 LogicFlow 图数据转换为 BPMN 的图形数据(BPMNDiagram/BPMNPlane 下的 Shape 与 Edge)
298
+ * 输入:
299
+ * - bpmnDiagramData:输出目标对象(填充 BPMNShape/BPMNEdge)
300
+ * - data:LogicFlow 图数据(nodes/edges)
301
+ * 输出:直接修改 bpmnDiagramData
302
+ * 特殊处理:
303
+ * - 节点坐标从中心点转换为左上角基准;
304
+ * - 文本的显示边界(Bounds)根据文本长度近似计算,用于在 BPMN 渲染器正确定位标签。
305
+ */
229
306
  function convertLf2DiagramData(bpmnDiagramData, data) {
230
307
  bpmnDiagramData['bpmndi:BPMNEdge'] = data.edges.map(function (edge) {
231
308
  var _a;
@@ -294,21 +371,37 @@ function convertLf2DiagramData(bpmnDiagramData, data) {
294
371
  /**
295
372
  * 将bpmn数据转换为LogicFlow内部能识别数据
296
373
  */
374
+ /**
375
+ * 将 BPMN JSON 转换为 LogicFlow 可识别的图数据
376
+ * 输入:
377
+ * - bpmnData:包含 'bpmn:definitions' 的 BPMN JSON
378
+ * 输出:{ nodes, edges }:LogicFlow 的 GraphConfigData
379
+ * 特殊处理:
380
+ * - 若缺失 process 或 plane,返回空数据以避免渲染错误。
381
+ */
297
382
  function convertBpmn2LfData(bpmnData) {
298
383
  var nodes = [];
299
384
  var edges = [];
300
385
  var definitions = bpmnData['bpmn:definitions'];
301
386
  if (definitions) {
387
+ // 如后续需多图/多流程支持,再扩展为遍历与合并,现在看起来是没有这个场景
302
388
  var process_1 = definitions['bpmn:process'];
389
+ var diagram = definitions['bpmndi:BPMNDiagram'];
390
+ var plane_1 = diagram === null || diagram === void 0 ? void 0 : diagram['bpmndi:BPMNPlane'];
391
+ if (!process_1 || !plane_1) {
392
+ return { nodes: nodes, edges: edges };
393
+ }
303
394
  Object.keys(process_1).forEach(function (key) {
304
395
  if (key.indexOf('bpmn:') === 0) {
305
396
  var value = process_1[key];
306
397
  if (key === BpmnElements.FLOW) {
307
- var bpmnEdges = definitions['bpmndi:BPMNDiagram']['bpmndi:BPMNPlane']['bpmndi:BPMNEdge'];
398
+ var edgesRaw = plane_1['bpmndi:BPMNEdge'];
399
+ var bpmnEdges = Array.isArray(edgesRaw) ? edgesRaw : edgesRaw;
308
400
  edges = getLfEdges(value, bpmnEdges);
309
401
  }
310
402
  else {
311
- var shapes = definitions['bpmndi:BPMNDiagram']['bpmndi:BPMNPlane']['bpmndi:BPMNShape'];
403
+ var shapesRaw = plane_1['bpmndi:BPMNShape'];
404
+ var shapes = Array.isArray(shapesRaw) ? shapesRaw : shapesRaw;
312
405
  nodes = nodes.concat(getLfNodes(value, shapes, key));
313
406
  }
314
407
  }
@@ -319,6 +412,14 @@ function convertBpmn2LfData(bpmnData) {
319
412
  edges: edges,
320
413
  };
321
414
  }
415
+ /**
416
+ * 根据 BPMN 的 process 子节点与 plane 中的 BPMNShape 生成 LogicFlow 节点数组
417
+ * 输入:
418
+ * - value:当前类型(如 bpmn:userTask)的值,可能为对象或数组
419
+ * - shapes:plane['bpmndi:BPMNShape'],可能为对象或数组
420
+ * - key:当前处理的 BPMN 类型键名(如 'bpmn:userTask')
421
+ * 输出:LogicFlow 节点数组
422
+ */
322
423
  function getLfNodes(value, shapes, key) {
323
424
  var nodes = [];
324
425
  if (Array.isArray(value)) {
@@ -348,10 +449,23 @@ function getLfNodes(value, shapes, key) {
348
449
  }
349
450
  return nodes;
350
451
  }
452
+ /**
453
+ * 将单个 BPMNShape 与其对应的 process 节点合成为 LogicFlow 节点配置
454
+ * 输入:
455
+ * - shapeValue:plane 中的 BPMNShape(包含 Bounds 与可选 BPMNLabel)
456
+ * - type:BPMN 节点类型键(如 'bpmn:userTask')
457
+ * - processValue:process 中对应的节点对象(包含 '-id'、'-name' 等)
458
+ * 输出:LogicFlow NodeConfig
459
+ * 特殊处理:
460
+ * - 坐标从左上角转为中心点;
461
+ * - 文本从 '-name' 读取并进行 XML 实体反转义;
462
+ * - 文本位置优先使用 BPMNLabel 的 Bounds。
463
+ */
351
464
  function getNodeConfig(shapeValue, type, processValue) {
352
465
  var x = Number(shapeValue['dc:Bounds']['-x']);
353
466
  var y = Number(shapeValue['dc:Bounds']['-y']);
354
- var name = processValue['-name'];
467
+ // 反转义 XML 实体,确保导入后文本包含特殊字符时能被完整还原
468
+ var name = unescapeXml(processValue['-name']);
355
469
  var shapeConfig = BpmnAdapter.shapeConfigMap.get(type);
356
470
  if (shapeConfig) {
357
471
  x += shapeConfig.width / 2;
@@ -397,6 +511,13 @@ function getNodeConfig(shapeValue, type, processValue) {
397
511
  }
398
512
  return nodeConfig;
399
513
  }
514
+ /**
515
+ * 根据 BPMN 的 sequenceFlow 与 BPMNEdge 生成 LogicFlow 边数组
516
+ * 输入:
517
+ * - value:process['bpmn:sequenceFlow'],对象或数组
518
+ * - bpmnEdges:plane['bpmndi:BPMNEdge'],对象或数组
519
+ * 输出:LogicFlow 边数组
520
+ */
400
521
  function getLfEdges(value, bpmnEdges) {
401
522
  var edges = [];
402
523
  if (Array.isArray(value)) {
@@ -423,11 +544,29 @@ function getLfEdges(value, bpmnEdges) {
423
544
  }
424
545
  return edges;
425
546
  }
547
+ /**
548
+ * 将单个 BPMNEdge 与其对应的 sequenceFlow 合成为 LogicFlow 边配置
549
+ * 输入:
550
+ * - edgeValue:BPMNEdge(包含 di:waypoint 以及可选 BPMNLabel/Bounds)
551
+ * - processValue:sequenceFlow(包含 '-id'、'-sourceRef'、'-targetRef'、'-name' 等)
552
+ * 输出:LogicFlow EdgeConfig
553
+ * 特殊处理:
554
+ * - 文本从 '-name' 读取并进行 XML 实体反转义;
555
+ * - 若缺失 BPMNLabel,则以边的几何中心近似作为文本位置;
556
+ * - pointsList 由 waypoints 转换得到,数值类型统一为 Number。
557
+ */
426
558
  function getEdgeConfig(edgeValue, processValue) {
427
559
  var text;
428
- var textVal = processValue['-name'] ? "".concat(processValue['-name']) : '';
560
+ // 反转义 XML 实体,确保导入后文本包含特殊字符时能被完整还原
561
+ var textVal = processValue['-name']
562
+ ? unescapeXml("".concat(processValue['-name']))
563
+ : '';
429
564
  if (textVal) {
430
- var textBounds = edgeValue['bpmndi:BPMNLabel']['dc:Bounds'];
565
+ var textBounds = void 0;
566
+ if (edgeValue['bpmndi:BPMNLabel'] &&
567
+ edgeValue['bpmndi:BPMNLabel']['dc:Bounds']) {
568
+ textBounds = edgeValue['bpmndi:BPMNLabel']['dc:Bounds'];
569
+ }
431
570
  // 如果边文本换行,则其偏移量应该是最长一行的位置
432
571
  var textLength_1 = 0;
433
572
  textVal.split('\n').forEach(function (textSpan) {
@@ -435,11 +574,26 @@ function getEdgeConfig(edgeValue, processValue) {
435
574
  textLength_1 = textSpan.length;
436
575
  }
437
576
  });
438
- text = {
439
- value: textVal,
440
- x: Number(textBounds['-x']) + (textLength_1 * 10) / 2,
441
- y: Number(textBounds['-y']) + 7,
442
- };
577
+ if (textBounds) {
578
+ text = {
579
+ value: textVal,
580
+ x: Number(textBounds['-x']) + (textLength_1 * 10) / 2,
581
+ y: Number(textBounds['-y']) + 7,
582
+ };
583
+ }
584
+ else {
585
+ // 兼容缺少 BPMNLabel 的图:使用边的几何中心作为文本位置
586
+ var waypoints = edgeValue['di:waypoint'] || [];
587
+ var first = waypoints[0];
588
+ var last = waypoints[waypoints.length - 1] || first;
589
+ var centerX = (Number((first === null || first === void 0 ? void 0 : first['-x']) || 0) + Number((last === null || last === void 0 ? void 0 : last['-x']) || 0)) / 2;
590
+ var centerY = (Number((first === null || first === void 0 ? void 0 : first['-y']) || 0) + Number((last === null || last === void 0 ? void 0 : last['-y']) || 0)) / 2;
591
+ text = {
592
+ value: textVal,
593
+ x: centerX,
594
+ y: centerY,
595
+ };
596
+ }
443
597
  }
444
598
  var properties;
445
599
  // 判断是否存在额外的属性,将额外的属性放到properties中
@@ -470,7 +624,20 @@ function getEdgeConfig(edgeValue, processValue) {
470
624
  }
471
625
  return edge;
472
626
  }
627
+ /**
628
+ * BpmnAdapter:基础适配器
629
+ *
630
+ * 作用:在 LogicFlow 数据与 BPMN JSON 之间进行转换,并注入 adapterIn/adapterOut 钩子。
631
+ * - processAttributes:导出时 BPMN process 的基础属性(可配置 isExecutable、id 等)。
632
+ * - definitionAttributes:导出时 BPMN definitions 的基础属性与命名空间声明。
633
+ * - shapeConfigMap:不同 BPMN 元素类型的默认宽高,用于坐标/Bounds 计算。
634
+ */
473
635
  var BpmnAdapter = /** @class */ (function () {
636
+ /**
637
+ * 构造函数
638
+ * - 注入 LogicFlow 的 adapterIn/adapterOut(处理 JSON 方向的适配)
639
+ * - 初始化 process 与 definitions 的基础属性
640
+ */
474
641
  function BpmnAdapter(_a) {
475
642
  var lf = _a.lf;
476
643
  var _this = this;
@@ -479,6 +646,13 @@ var BpmnAdapter = /** @class */ (function () {
479
646
  * ["properties", "startPoint", "endPoint", "pointsList"]合并,
480
647
  * 这意味着出现在这个数组里的字段当它的值是数组或是对象时不会被视为一个节点而是一个属性。
481
648
  */
649
+ /**
650
+ * adapterOut:将 LogicFlow 图数据转换为 BPMN JSON
651
+ * 输入:
652
+ * - data:LogicFlow GraphData
653
+ * - retainedFields:扩展属性保留字段
654
+ * 输出:BPMN JSON(包含 definitions/process/diagram/plane)
655
+ */
482
656
  this.adapterOut = function (data, retainedFields) {
483
657
  var bpmnProcessData = __assign({}, _this.processAttributes);
484
658
  convertLf2ProcessData(bpmnProcessData, data, retainedFields);
@@ -498,6 +672,11 @@ var BpmnAdapter = /** @class */ (function () {
498
672
  };
499
673
  return bpmnData;
500
674
  };
675
+ /**
676
+ * adapterIn:将 BPMN JSON 转换为 LogicFlow 图数据
677
+ * 输入:bpmnData:BPMN JSON
678
+ * 输出:GraphConfigData(nodes/edges)
679
+ */
501
680
  this.adapterIn = function (bpmnData) {
502
681
  if (bpmnData) {
503
682
  return convertBpmn2LfData(bpmnData);
@@ -551,14 +730,44 @@ BpmnAdapter.shapeConfigMap.set(BpmnElements.USER, {
551
730
  width: constant_1.UserTaskConfig.width,
552
731
  height: constant_1.UserTaskConfig.height,
553
732
  });
733
+ /**
734
+ * BpmnXmlAdapter:XML 适配器(继承 BpmnAdapter)
735
+ *
736
+ * 作用:处理 XML 输入/输出的适配,使用 xml2json/json2xml 完成格式转换。
737
+ * 特殊处理:在 XML 导入前对 name 属性的非法字符进行预处理转义,提升容错。
738
+ */
554
739
  var BpmnXmlAdapter = /** @class */ (function (_super) {
555
740
  __extends(BpmnXmlAdapter, _super);
741
+ /**
742
+ * 构造函数
743
+ * - 覆盖 LogicFlow 的 adapterIn/adapterOut,使其面向 XML 输入与输出。
744
+ */
556
745
  function BpmnXmlAdapter(data) {
557
746
  var _this = _super.call(this, data) || this;
747
+ /**
748
+ * adapterXmlIn:将 BPMN XML 转换为 LogicFlow 图数据
749
+ * 输入:bpmnData:XML 字符串或对象
750
+ * 步骤:
751
+ * 1) 若为字符串,先对 name 属性进行预处理转义;
752
+ * 2) 使用 lfXml2Json 转换为 BPMN JSON;
753
+ * 3) 调用基础 adapterIn 转换为 GraphData。
754
+ */
558
755
  _this.adapterXmlIn = function (bpmnData) {
559
- var json = (0, xml2json_1.lfXml2Json)(bpmnData);
756
+ var xmlStr = typeof bpmnData === 'string'
757
+ ? _this.sanitizeNameAttributes(bpmnData)
758
+ : bpmnData;
759
+ var json = (0, xml2json_1.lfXml2Json)(xmlStr);
560
760
  return _this.adapterIn(json);
561
761
  };
762
+ /**
763
+ * adapterXmlOut:将 LogicFlow 图数据转换为 BPMN XML
764
+ * 输入:
765
+ * - data:GraphData
766
+ * - retainedFields:保留属性字段
767
+ * 步骤:
768
+ * 1) 调用基础 adapterOut 生成 BPMN JSON;
769
+ * 2) 使用 lfJson2Xml 转为合法的 XML 字符串(包含属性与文本的转义)。
770
+ */
562
771
  _this.adapterXmlOut = function (data, retainedFields) {
563
772
  var outData = _this.adapterOut(data, retainedFields);
564
773
  return (0, json2xml_1.lfJson2Xml)(outData);
@@ -568,6 +777,20 @@ var BpmnXmlAdapter = /** @class */ (function (_super) {
568
777
  lf.adapterOut = _this.adapterXmlOut;
569
778
  return _this;
570
779
  }
780
+ // 预处理:修复属性值中非法的XML字符(仅针对 name 属性)
781
+ /**
782
+ * 预处理 XML:仅对 name 属性值进行非法字符转义(<, >, &),避免 DOM 解析失败。
783
+ * 注意:不影响已合法的实体(如 &amp;),仅在属性值中生效,不修改其它内容。
784
+ */
785
+ BpmnXmlAdapter.prototype.sanitizeNameAttributes = function (xml) {
786
+ return xml.replace(/name="([^"]*)"/g, function (_, val) {
787
+ var safe = val
788
+ .replace(/&(?!#?\w+;)/g, '&amp;')
789
+ .replace(/</g, '&lt;')
790
+ .replace(/>/g, '&gt;');
791
+ return "name=\"".concat(safe, "\"");
792
+ });
793
+ };
571
794
  BpmnXmlAdapter.pluginName = 'bpmnXmlAdapter';
572
795
  return BpmnXmlAdapter;
573
796
  }(BpmnAdapter));
@@ -1,8 +1,17 @@
1
1
  declare function handleAttributes(o: any): any;
2
+ /**
3
+ * 将普通文本中的一些特殊字符进行转移,保障文本安全地嵌入 XML:
4
+ * - 空值(`null/undefined`)返回空字符串,避免输出非法字面量;
5
+ * - 按顺序转义 XML 保留字符:`&`, `<`, `>`, `"`, `'`;
6
+ * 注意优先转义 `&`,避免后续生成的实体被再次转义。
7
+ * @param text 原始文本
8
+ * @returns 已完成 XML 转义的字符串
9
+ */
10
+ declare function escapeXml(text: string): string;
2
11
  /**
3
12
  * json 转 xml
4
13
  * @param o object
5
14
  * @returns
6
15
  */
7
16
  declare function lfJson2Xml(o: Object): string;
8
- export { lfJson2Xml, handleAttributes };
17
+ export { lfJson2Xml, handleAttributes, escapeXml };