@logicflow/extension 2.1.12 → 2.1.14

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.
@@ -1,3 +1,4 @@
1
1
  declare function handleAttributes(obj: any): any;
2
+ declare function escapeXml(text: string): string;
2
3
  declare function lfJson2Xml(obj: any): string;
3
- export { lfJson2Xml, handleAttributes };
4
+ export { lfJson2Xml, handleAttributes, escapeXml };
@@ -21,6 +21,17 @@ function handleAttributes(obj) {
21
21
  }
22
22
  return obj;
23
23
  }
24
+ function escapeXml(text) {
25
+ if (text == null)
26
+ return '';
27
+ return text
28
+ .toString()
29
+ .replace(/&/g, '&')
30
+ .replace(/</g, '&lt;')
31
+ .replace(/>/g, '&gt;')
32
+ .replace(/"/g, '&quot;')
33
+ .replace(/'/g, '&apos;');
34
+ }
24
35
  function getAttributes(obj) {
25
36
  var tmp = obj;
26
37
  try {
@@ -31,7 +42,7 @@ function getAttributes(obj) {
31
42
  catch (error) {
32
43
  tmp = JSON.stringify(handleAttributes(obj)).replace(/"/g, "'");
33
44
  }
34
- return tmp;
45
+ return escapeXml(String(tmp));
35
46
  }
36
47
  var tn = '\t\n';
37
48
  // @see issue https://github.com/didi/LogicFlow/issues/718, refactoring of function toXml
@@ -42,7 +53,7 @@ function toXml(obj, name, depth) {
42
53
  if (name === '-json')
43
54
  return '';
44
55
  if (name === '#text') {
45
- return prefix + obj;
56
+ return prefix + escapeXml(String(obj));
46
57
  }
47
58
  if (name === '#cdata-section') {
48
59
  return "".concat(prefix, "<![CDATA[").concat(obj, "]]>");
@@ -50,6 +61,9 @@ function toXml(obj, name, depth) {
50
61
  if (name === '#comment') {
51
62
  return "".concat(prefix, "<!--").concat(obj, "-->");
52
63
  }
64
+ if (obj !== 0 && obj !== false && !obj) {
65
+ return "".concat(prefix, "<").concat(name, " />");
66
+ }
53
67
  if ("".concat(name).charAt(0) === '-') {
54
68
  return " ".concat(name.substring(1), "=\"").concat(getAttributes(obj), "\"");
55
69
  }
@@ -70,7 +84,7 @@ function toXml(obj, name, depth) {
70
84
  attributes_1 + (children_1 !== '' ? ">".concat(children_1).concat(prefix, "</").concat(name, ">") : ' />');
71
85
  }
72
86
  else {
73
- str += "".concat(prefix, "<").concat(name, ">").concat(obj.toString(), "</").concat(name, ">");
87
+ str += "".concat(prefix, "<").concat(name, ">").concat(escapeXml(String(obj)), "</").concat(name, ">");
74
88
  }
75
89
  return str;
76
90
  }
@@ -81,4 +95,4 @@ function lfJson2Xml(obj) {
81
95
  }
82
96
  return xmlStr;
83
97
  }
84
- export { lfJson2Xml, handleAttributes };
98
+ export { lfJson2Xml, handleAttributes, escapeXml };
@@ -5,6 +5,7 @@
5
5
  /* eslint-disable func-names */
6
6
  // @ts-nocheck
7
7
  import { has } from 'lodash-es';
8
+ import { escapeXml } from './json2xml';
8
9
  // ========================================================================
9
10
  // XML.ObjTree -- XML source code from/to JavaScript object like E4X
10
11
  // ========================================================================
@@ -295,13 +296,7 @@ XML.ObjTree.prototype.scalar_to_xml = function (name, text) {
295
296
  return "<".concat(name, ">").concat(this.xml_escape(text), "</").concat(name, ">\n");
296
297
  };
297
298
  // method: xml_escape( text )
298
- XML.ObjTree.prototype.xml_escape = function (text) {
299
- return text
300
- .replace(/&/g, '&')
301
- .replace(/</g, '<')
302
- .replace(/>/g, '>')
303
- .replace(/"/g, '"');
304
- };
299
+ XML.ObjTree.prototype.xml_escape = escapeXml;
305
300
  /*
306
301
  // ========================================================================
307
302
 
@@ -119,14 +119,14 @@ var Control = /** @class */ (function () {
119
119
  text.title = item.title;
120
120
  text.innerText = item.text;
121
121
  itemContainer.append(icon, text);
122
- switch (item.text) {
123
- case '上一步':
122
+ switch (item.key) {
123
+ case 'undo':
124
124
  _this.lf.on('history:change', function (_a) {
125
125
  var undoAble = _a.data.undoAble;
126
126
  itemContainer.className = undoAble ? NORMAL : DISABLED;
127
127
  });
128
128
  break;
129
- case '下一步':
129
+ case 'redo':
130
130
  _this.lf.on('history:change', function (_a) {
131
131
  var redoAble = _a.data.redoAble;
132
132
  itemContainer.className = redoAble ? NORMAL : DISABLED;
@@ -127,38 +127,54 @@ function getMidPoints(cur, key, orientation, radius) {
127
127
  return [];
128
128
  }
129
129
  }
130
- function getPartialPath(prev, cur, next, radius) {
130
+ /**
131
+ * 生成局部路径片段(包含圆角)
132
+ * - 输入为上一个顶点、当前拐点、下一个顶点,计算方向组合并选择圆弧象限
133
+ * - 将圆角半径限制在相邻两段长度的一半以内,避免过度弯曲
134
+ * @param prevPoint 上一个顶点
135
+ * @param cornerPoint 当前拐点(圆角所在拐点)
136
+ * @param nextPoint 下一个顶点
137
+ * @param cornerRadius 圆角半径上限
138
+ * @returns 局部 path 字符串(包含 L/Q 操作)
139
+ */
140
+ function getPartialPath(prevPoint, cornerPoint, nextPoint, cornerRadius) {
131
141
  var _a;
132
- // 定义误差容错变量
133
- var tolerance = 1;
134
- var dir1 = '';
135
- var dir2 = '';
136
- if (Math.abs(prev[0] - cur[0]) <= tolerance) {
137
- // 垂直方向
138
- dir1 = prev[1] > cur[1] ? 't' : 'b';
139
- }
140
- else if (Math.abs(prev[1] - cur[1]) <= tolerance) {
141
- // 水平方向
142
- dir1 = prev[0] > cur[0] ? 'l' : 'r';
143
- }
144
- if (Math.abs(cur[0] - next[0]) <= tolerance) {
145
- dir2 = cur[1] > next[1] ? 't' : 'b';
146
- }
147
- else if (Math.abs(cur[1] - next[1]) <= tolerance) {
148
- dir2 = cur[0] > next[0] ? 'l' : 'r';
149
- }
150
- var r = Math.min(Math.hypot(cur[0] - prev[0], cur[1] - prev[1]) / 2, Math.hypot(next[0] - cur[0], next[1] - cur[1]) / 2, radius) || (1 / 5) * radius;
142
+ // 轴对齐容差(像素),用于消除微小误差
143
+ var epsilon = 1;
144
+ var resolveDir = function (a, b) {
145
+ var dx = b[0] - a[0];
146
+ var dy = b[1] - a[1];
147
+ var adx = Math.abs(dx);
148
+ var ady = Math.abs(dy);
149
+ if (ady <= epsilon && adx > epsilon) {
150
+ return dx < 0 ? 'l' : 'r';
151
+ }
152
+ if (adx <= epsilon && ady > epsilon) {
153
+ return dy < 0 ? 't' : 'b';
154
+ }
155
+ if (adx <= epsilon && ady <= epsilon) {
156
+ return '';
157
+ }
158
+ // 非严格对齐时,选择更接近的轴
159
+ return adx < ady ? (dx < 0 ? 'l' : 'r') : dy < 0 ? 't' : 'b';
160
+ };
161
+ var dir1 = resolveDir(prevPoint, cornerPoint);
162
+ var dir2 = resolveDir(cornerPoint, nextPoint);
163
+ var r = Math.min(Math.hypot(cornerPoint[0] - prevPoint[0], cornerPoint[1] - prevPoint[1]) /
164
+ 2, Math.hypot(nextPoint[0] - cornerPoint[0], nextPoint[1] - cornerPoint[1]) /
165
+ 2, cornerRadius) || (1 / 5) * cornerRadius;
151
166
  var key = "".concat(dir1).concat(dir2);
152
167
  var orientation = directionMap[key] || '-';
153
- var path = "L ".concat(prev[0], " ").concat(prev[1]);
168
+ var path = '';
154
169
  if (orientation === '-') {
155
- path += "L ".concat(cur[0], " ").concat(cur[1], " L ").concat(next[0], " ").concat(next[1]);
170
+ // 仅移动到当前拐点,由下一次迭代决定如何从拐点继续(直线或圆角)
171
+ path += "L ".concat(cornerPoint[0], " ").concat(cornerPoint[1]);
156
172
  }
157
173
  else {
158
- var _b = __read(getMidPoints(cur, key, orientation, r), 2), mid1 = _b[0], mid2 = _b[1];
174
+ var _b = __read(getMidPoints(cornerPoint, key, orientation, r), 2), mid1 = _b[0], mid2 = _b[1];
159
175
  if (mid1 && mid2) {
160
- path += "L ".concat(mid1[0], " ").concat(mid1[1], " Q ").concat(cur[0], " ").concat(cur[1], " ").concat(mid2[0], " ").concat(mid2[1]);
161
- _a = __read(mid2, 2), cur[0] = _a[0], cur[1] = _a[1];
176
+ path += "L ".concat(mid1[0], " ").concat(mid1[1], " Q ").concat(cornerPoint[0], " ").concat(cornerPoint[1], " ").concat(mid2[0], " ").concat(mid2[1]);
177
+ _a = __read(mid2, 2), cornerPoint[0] = _a[0], cornerPoint[1] = _a[1];
162
178
  }
163
179
  }
164
180
  return path;
@@ -1,3 +1,4 @@
1
1
  declare function handleAttributes(obj: any): any;
2
+ declare function escapeXml(text: string): string;
2
3
  declare function lfJson2Xml(obj: any): string;
3
- export { lfJson2Xml, handleAttributes };
4
+ export { lfJson2Xml, handleAttributes, escapeXml };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handleAttributes = exports.lfJson2Xml = void 0;
3
+ exports.escapeXml = exports.handleAttributes = exports.lfJson2Xml = void 0;
4
4
  /* eslint-disable guard-for-in */
5
5
  function type(obj) {
6
6
  return Object.prototype.toString.call(obj);
@@ -25,6 +25,18 @@ function handleAttributes(obj) {
25
25
  return obj;
26
26
  }
27
27
  exports.handleAttributes = handleAttributes;
28
+ function escapeXml(text) {
29
+ if (text == null)
30
+ return '';
31
+ return text
32
+ .toString()
33
+ .replace(/&/g, '&amp;')
34
+ .replace(/</g, '&lt;')
35
+ .replace(/>/g, '&gt;')
36
+ .replace(/"/g, '&quot;')
37
+ .replace(/'/g, '&apos;');
38
+ }
39
+ exports.escapeXml = escapeXml;
28
40
  function getAttributes(obj) {
29
41
  var tmp = obj;
30
42
  try {
@@ -35,7 +47,7 @@ function getAttributes(obj) {
35
47
  catch (error) {
36
48
  tmp = JSON.stringify(handleAttributes(obj)).replace(/"/g, "'");
37
49
  }
38
- return tmp;
50
+ return escapeXml(String(tmp));
39
51
  }
40
52
  var tn = '\t\n';
41
53
  // @see issue https://github.com/didi/LogicFlow/issues/718, refactoring of function toXml
@@ -46,7 +58,7 @@ function toXml(obj, name, depth) {
46
58
  if (name === '-json')
47
59
  return '';
48
60
  if (name === '#text') {
49
- return prefix + obj;
61
+ return prefix + escapeXml(String(obj));
50
62
  }
51
63
  if (name === '#cdata-section') {
52
64
  return "".concat(prefix, "<![CDATA[").concat(obj, "]]>");
@@ -54,6 +66,9 @@ function toXml(obj, name, depth) {
54
66
  if (name === '#comment') {
55
67
  return "".concat(prefix, "<!--").concat(obj, "-->");
56
68
  }
69
+ if (obj !== 0 && obj !== false && !obj) {
70
+ return "".concat(prefix, "<").concat(name, " />");
71
+ }
57
72
  if ("".concat(name).charAt(0) === '-') {
58
73
  return " ".concat(name.substring(1), "=\"").concat(getAttributes(obj), "\"");
59
74
  }
@@ -74,7 +89,7 @@ function toXml(obj, name, depth) {
74
89
  attributes_1 + (children_1 !== '' ? ">".concat(children_1).concat(prefix, "</").concat(name, ">") : ' />');
75
90
  }
76
91
  else {
77
- str += "".concat(prefix, "<").concat(name, ">").concat(obj.toString(), "</").concat(name, ">");
92
+ str += "".concat(prefix, "<").concat(name, ">").concat(escapeXml(String(obj)), "</").concat(name, ">");
78
93
  }
79
94
  return str;
80
95
  }
@@ -8,6 +8,7 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.lfXml2Json = void 0;
10
10
  var lodash_es_1 = require("lodash-es");
11
+ var json2xml_1 = require("./json2xml");
11
12
  // ========================================================================
12
13
  // XML.ObjTree -- XML source code from/to JavaScript object like E4X
13
14
  // ========================================================================
@@ -298,13 +299,7 @@ XML.ObjTree.prototype.scalar_to_xml = function (name, text) {
298
299
  return "<".concat(name, ">").concat(this.xml_escape(text), "</").concat(name, ">\n");
299
300
  };
300
301
  // method: xml_escape( text )
301
- XML.ObjTree.prototype.xml_escape = function (text) {
302
- return text
303
- .replace(/&/g, '&')
304
- .replace(/</g, '<')
305
- .replace(/>/g, '>')
306
- .replace(/"/g, '"');
307
- };
302
+ XML.ObjTree.prototype.xml_escape = json2xml_1.escapeXml;
308
303
  /*
309
304
  // ========================================================================
310
305
 
@@ -122,14 +122,14 @@ var Control = /** @class */ (function () {
122
122
  text.title = item.title;
123
123
  text.innerText = item.text;
124
124
  itemContainer.append(icon, text);
125
- switch (item.text) {
126
- case '上一步':
125
+ switch (item.key) {
126
+ case 'undo':
127
127
  _this.lf.on('history:change', function (_a) {
128
128
  var undoAble = _a.data.undoAble;
129
129
  itemContainer.className = undoAble ? NORMAL : DISABLED;
130
130
  });
131
131
  break;
132
- case '下一步':
132
+ case 'redo':
133
133
  _this.lf.on('history:change', function (_a) {
134
134
  var redoAble = _a.data.redoAble;
135
135
  itemContainer.className = redoAble ? NORMAL : DISABLED;
@@ -130,38 +130,54 @@ function getMidPoints(cur, key, orientation, radius) {
130
130
  return [];
131
131
  }
132
132
  }
133
- function getPartialPath(prev, cur, next, radius) {
133
+ /**
134
+ * 生成局部路径片段(包含圆角)
135
+ * - 输入为上一个顶点、当前拐点、下一个顶点,计算方向组合并选择圆弧象限
136
+ * - 将圆角半径限制在相邻两段长度的一半以内,避免过度弯曲
137
+ * @param prevPoint 上一个顶点
138
+ * @param cornerPoint 当前拐点(圆角所在拐点)
139
+ * @param nextPoint 下一个顶点
140
+ * @param cornerRadius 圆角半径上限
141
+ * @returns 局部 path 字符串(包含 L/Q 操作)
142
+ */
143
+ function getPartialPath(prevPoint, cornerPoint, nextPoint, cornerRadius) {
134
144
  var _a;
135
- // 定义误差容错变量
136
- var tolerance = 1;
137
- var dir1 = '';
138
- var dir2 = '';
139
- if (Math.abs(prev[0] - cur[0]) <= tolerance) {
140
- // 垂直方向
141
- dir1 = prev[1] > cur[1] ? 't' : 'b';
142
- }
143
- else if (Math.abs(prev[1] - cur[1]) <= tolerance) {
144
- // 水平方向
145
- dir1 = prev[0] > cur[0] ? 'l' : 'r';
146
- }
147
- if (Math.abs(cur[0] - next[0]) <= tolerance) {
148
- dir2 = cur[1] > next[1] ? 't' : 'b';
149
- }
150
- else if (Math.abs(cur[1] - next[1]) <= tolerance) {
151
- dir2 = cur[0] > next[0] ? 'l' : 'r';
152
- }
153
- var r = Math.min(Math.hypot(cur[0] - prev[0], cur[1] - prev[1]) / 2, Math.hypot(next[0] - cur[0], next[1] - cur[1]) / 2, radius) || (1 / 5) * radius;
145
+ // 轴对齐容差(像素),用于消除微小误差
146
+ var epsilon = 1;
147
+ var resolveDir = function (a, b) {
148
+ var dx = b[0] - a[0];
149
+ var dy = b[1] - a[1];
150
+ var adx = Math.abs(dx);
151
+ var ady = Math.abs(dy);
152
+ if (ady <= epsilon && adx > epsilon) {
153
+ return dx < 0 ? 'l' : 'r';
154
+ }
155
+ if (adx <= epsilon && ady > epsilon) {
156
+ return dy < 0 ? 't' : 'b';
157
+ }
158
+ if (adx <= epsilon && ady <= epsilon) {
159
+ return '';
160
+ }
161
+ // 非严格对齐时,选择更接近的轴
162
+ return adx < ady ? (dx < 0 ? 'l' : 'r') : dy < 0 ? 't' : 'b';
163
+ };
164
+ var dir1 = resolveDir(prevPoint, cornerPoint);
165
+ var dir2 = resolveDir(cornerPoint, nextPoint);
166
+ var r = Math.min(Math.hypot(cornerPoint[0] - prevPoint[0], cornerPoint[1] - prevPoint[1]) /
167
+ 2, Math.hypot(nextPoint[0] - cornerPoint[0], nextPoint[1] - cornerPoint[1]) /
168
+ 2, cornerRadius) || (1 / 5) * cornerRadius;
154
169
  var key = "".concat(dir1).concat(dir2);
155
170
  var orientation = directionMap[key] || '-';
156
- var path = "L ".concat(prev[0], " ").concat(prev[1]);
171
+ var path = '';
157
172
  if (orientation === '-') {
158
- path += "L ".concat(cur[0], " ").concat(cur[1], " L ").concat(next[0], " ").concat(next[1]);
173
+ // 仅移动到当前拐点,由下一次迭代决定如何从拐点继续(直线或圆角)
174
+ path += "L ".concat(cornerPoint[0], " ").concat(cornerPoint[1]);
159
175
  }
160
176
  else {
161
- var _b = __read(getMidPoints(cur, key, orientation, r), 2), mid1 = _b[0], mid2 = _b[1];
177
+ var _b = __read(getMidPoints(cornerPoint, key, orientation, r), 2), mid1 = _b[0], mid2 = _b[1];
162
178
  if (mid1 && mid2) {
163
- path += "L ".concat(mid1[0], " ").concat(mid1[1], " Q ").concat(cur[0], " ").concat(cur[1], " ").concat(mid2[0], " ").concat(mid2[1]);
164
- _a = __read(mid2, 2), cur[0] = _a[0], cur[1] = _a[1];
179
+ path += "L ".concat(mid1[0], " ").concat(mid1[1], " Q ").concat(cornerPoint[0], " ").concat(cornerPoint[1], " ").concat(mid2[0], " ").concat(mid2[1]);
180
+ _a = __read(mid2, 2), cornerPoint[0] = _a[0], cornerPoint[1] = _a[1];
165
181
  }
166
182
  }
167
183
  return path;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logicflow/extension",
3
- "version": "2.1.12",
3
+ "version": "2.1.14",
4
4
  "description": "LogicFlow Extensions",
5
5
  "main": "lib/index.js",
6
6
  "module": "es/index.js",
@@ -20,8 +20,8 @@
20
20
  "author": "Logicflow-Team",
21
21
  "license": "Apache-2.0",
22
22
  "peerDependencies": {
23
- "@logicflow/vue-node-registry": "1.1.11",
24
- "@logicflow/core": "2.1.10"
23
+ "@logicflow/core": "2.1.11",
24
+ "@logicflow/vue-node-registry": "1.1.12"
25
25
  },
26
26
  "dependencies": {
27
27
  "@antv/hierarchy": "^0.6.11",
@@ -32,8 +32,8 @@
32
32
  "preact": "^10.17.1",
33
33
  "rangy": "^1.3.1",
34
34
  "vanilla-picker": "^2.12.3",
35
- "@logicflow/vue-node-registry": "1.1.11",
36
- "@logicflow/core": "2.1.10"
35
+ "@logicflow/core": "2.1.11",
36
+ "@logicflow/vue-node-registry": "1.1.12"
37
37
  },
38
38
  "devDependencies": {
39
39
  "less": "^4.1.1",
@@ -24,6 +24,17 @@ function handleAttributes(obj: any): any {
24
24
  return obj
25
25
  }
26
26
 
27
+ function escapeXml(text: string) {
28
+ if (text == null) return ''
29
+ return text
30
+ .toString()
31
+ .replace(/&/g, '&amp;')
32
+ .replace(/</g, '&lt;')
33
+ .replace(/>/g, '&gt;')
34
+ .replace(/"/g, '&quot;')
35
+ .replace(/'/g, '&apos;')
36
+ }
37
+
27
38
  function getAttributes(obj: any) {
28
39
  let tmp = obj
29
40
  try {
@@ -33,7 +44,7 @@ function getAttributes(obj: any) {
33
44
  } catch (error) {
34
45
  tmp = JSON.stringify(handleAttributes(obj)).replace(/"/g, "'")
35
46
  }
36
- return tmp
47
+ return escapeXml(String(tmp))
37
48
  }
38
49
 
39
50
  const tn = '\t\n'
@@ -45,7 +56,7 @@ function toXml(obj: any, name: string, depth: number) {
45
56
  const prefix = tn + frontSpace
46
57
  if (name === '-json') return ''
47
58
  if (name === '#text') {
48
- return prefix + obj
59
+ return prefix + escapeXml(String(obj))
49
60
  }
50
61
  if (name === '#cdata-section') {
51
62
  return `${prefix}<![CDATA[${obj}]]>`
@@ -53,6 +64,9 @@ function toXml(obj: any, name: string, depth: number) {
53
64
  if (name === '#comment') {
54
65
  return `${prefix}<!--${obj}-->`
55
66
  }
67
+ if (obj !== 0 && obj !== false && !obj) {
68
+ return `${prefix}<${name} />`
69
+ }
56
70
  if (`${name}`.charAt(0) === '-') {
57
71
  return ` ${name.substring(1)}="${getAttributes(obj)}"`
58
72
  }
@@ -74,7 +88,7 @@ function toXml(obj: any, name: string, depth: number) {
74
88
  str +=
75
89
  attributes + (children !== '' ? `>${children}${prefix}</${name}>` : ' />')
76
90
  } else {
77
- str += `${prefix}<${name}>${obj.toString()}</${name}>`
91
+ str += `${prefix}<${name}>${escapeXml(String(obj))}</${name}>`
78
92
  }
79
93
 
80
94
  return str
@@ -88,4 +102,4 @@ function lfJson2Xml(obj: any) {
88
102
  return xmlStr
89
103
  }
90
104
 
91
- export { lfJson2Xml, handleAttributes }
105
+ export { lfJson2Xml, handleAttributes, escapeXml }
@@ -6,6 +6,7 @@
6
6
  // @ts-nocheck
7
7
 
8
8
  import { has } from 'lodash-es'
9
+ import { escapeXml } from './json2xml'
9
10
  // ========================================================================
10
11
  // XML.ObjTree -- XML source code from/to JavaScript object like E4X
11
12
  // ========================================================================
@@ -287,14 +288,7 @@ XML.ObjTree.prototype.scalar_to_xml = function (name, text) {
287
288
  }
288
289
 
289
290
  // method: xml_escape( text )
290
-
291
- XML.ObjTree.prototype.xml_escape = function (text) {
292
- return text
293
- .replace(/&/g, '&')
294
- .replace(/</g, '<')
295
- .replace(/>/g, '>')
296
- .replace(/"/g, '"')
297
- }
291
+ XML.ObjTree.prototype.xml_escape = escapeXml
298
292
 
299
293
  /*
300
294
  // ========================================================================
@@ -116,13 +116,13 @@ export class Control {
116
116
  text.title = item.title
117
117
  text.innerText = item.text
118
118
  itemContainer.append(icon, text)
119
- switch (item.text) {
120
- case '上一步':
119
+ switch (item.key) {
120
+ case 'undo':
121
121
  this.lf.on('history:change', ({ data: { undoAble } }: any) => {
122
122
  itemContainer.className = undoAble ? NORMAL : DISABLED
123
123
  })
124
124
  break
125
- case '下一步':
125
+ case 'redo':
126
126
  this.lf.on('history:change', ({ data: { redoAble } }: any) => {
127
127
  itemContainer.className = redoAble ? NORMAL : DISABLED
128
128
  })
@@ -97,50 +97,67 @@ function getMidPoints(
97
97
  }
98
98
  }
99
99
 
100
+ /**
101
+ * 生成局部路径片段(包含圆角)
102
+ * - 输入为上一个顶点、当前拐点、下一个顶点,计算方向组合并选择圆弧象限
103
+ * - 将圆角半径限制在相邻两段长度的一半以内,避免过度弯曲
104
+ * @param prevPoint 上一个顶点
105
+ * @param cornerPoint 当前拐点(圆角所在拐点)
106
+ * @param nextPoint 下一个顶点
107
+ * @param cornerRadius 圆角半径上限
108
+ * @returns 局部 path 字符串(包含 L/Q 操作)
109
+ */
100
110
  function getPartialPath(
101
- prev: PointTuple,
102
- cur: PointTuple,
103
- next: PointTuple,
104
- radius: number,
111
+ prevPoint: PointTuple,
112
+ cornerPoint: PointTuple,
113
+ nextPoint: PointTuple,
114
+ cornerRadius: number,
105
115
  ): string {
106
- // 定义误差容错变量
107
- const tolerance = 1
108
-
109
- let dir1: DirectionType = ''
110
- let dir2: DirectionType = ''
111
-
112
- if (Math.abs(prev[0] - cur[0]) <= tolerance) {
113
- // 垂直方向
114
- dir1 = prev[1] > cur[1] ? 't' : 'b'
115
- } else if (Math.abs(prev[1] - cur[1]) <= tolerance) {
116
- // 水平方向
117
- dir1 = prev[0] > cur[0] ? 'l' : 'r'
116
+ // 轴对齐容差(像素),用于消除微小误差
117
+ const epsilon = 1
118
+
119
+ const resolveDir = (a: PointTuple, b: PointTuple): DirectionType => {
120
+ const dx = b[0] - a[0]
121
+ const dy = b[1] - a[1]
122
+ const adx = Math.abs(dx)
123
+ const ady = Math.abs(dy)
124
+ if (ady <= epsilon && adx > epsilon) {
125
+ return dx < 0 ? 'l' : 'r'
126
+ }
127
+ if (adx <= epsilon && ady > epsilon) {
128
+ return dy < 0 ? 't' : 'b'
129
+ }
130
+ if (adx <= epsilon && ady <= epsilon) {
131
+ return ''
132
+ }
133
+ // 非严格对齐时,选择更接近的轴
134
+ return adx < ady ? (dx < 0 ? 'l' : 'r') : dy < 0 ? 't' : 'b'
118
135
  }
119
136
 
120
- if (Math.abs(cur[0] - next[0]) <= tolerance) {
121
- dir2 = cur[1] > next[1] ? 't' : 'b'
122
- } else if (Math.abs(cur[1] - next[1]) <= tolerance) {
123
- dir2 = cur[0] > next[0] ? 'l' : 'r'
124
- }
137
+ const dir1: DirectionType = resolveDir(prevPoint, cornerPoint)
138
+ const dir2: DirectionType = resolveDir(cornerPoint, nextPoint)
125
139
 
126
140
  const r =
127
141
  Math.min(
128
- Math.hypot(cur[0] - prev[0], cur[1] - prev[1]) / 2,
129
- Math.hypot(next[0] - cur[0], next[1] - cur[1]) / 2,
130
- radius,
131
- ) || (1 / 5) * radius
142
+ Math.hypot(cornerPoint[0] - prevPoint[0], cornerPoint[1] - prevPoint[1]) /
143
+ 2,
144
+ Math.hypot(nextPoint[0] - cornerPoint[0], nextPoint[1] - cornerPoint[1]) /
145
+ 2,
146
+ cornerRadius,
147
+ ) || (1 / 5) * cornerRadius
132
148
 
133
149
  const key = `${dir1}${dir2}`
134
150
  const orientation: ArcQuadrantType = directionMap[key] || '-'
135
- let path = `L ${prev[0]} ${prev[1]}`
151
+ let path = ''
136
152
 
137
153
  if (orientation === '-') {
138
- path += `L ${cur[0]} ${cur[1]} L ${next[0]} ${next[1]}`
154
+ // 仅移动到当前拐点,由下一次迭代决定如何从拐点继续(直线或圆角)
155
+ path += `L ${cornerPoint[0]} ${cornerPoint[1]}`
139
156
  } else {
140
- const [mid1, mid2] = getMidPoints(cur, key, orientation, r)
157
+ const [mid1, mid2] = getMidPoints(cornerPoint, key, orientation, r)
141
158
  if (mid1 && mid2) {
142
- path += `L ${mid1[0]} ${mid1[1]} Q ${cur[0]} ${cur[1]} ${mid2[0]} ${mid2[1]}`
143
- ;[cur[0], cur[1]] = mid2
159
+ path += `L ${mid1[0]} ${mid1[1]} Q ${cornerPoint[0]} ${cornerPoint[1]} ${mid2[0]} ${mid2[1]}`
160
+ ;[cornerPoint[0], cornerPoint[1]] = mid2
144
161
  }
145
162
  }
146
163
  return path