ooxml-excel-editor 1.3.2 → 1.7.0
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.
- package/CHANGELOG.md +243 -3
- package/README.md +27 -8
- package/dist/chunks/index-BNQIWClg.js +12532 -0
- package/dist/chunks/{index.es-aesXm4JC.js → index.es-BVXJfTmn.js} +1 -1
- package/dist/chunks/{index.es-n6H_ncuE.js → index.es-gb2_kTSZ.js} +1 -1
- package/dist/chunks/{jspdf.es.min-DMfUtFJC.js → jspdf.es.min-C7JL2eZm.js} +2 -2
- package/dist/chunks/{jspdf.es.min-B6-ocR7J.js → jspdf.es.min-CBWDsR7H.js} +2 -2
- package/dist/chunks/plugin-overlay-C_qauTcv.js +11057 -0
- package/dist/chunks/{toolbar-icons-fOm95ASq.js → toolbar-icons-CD7G5Aof.js} +62 -71
- package/dist/components/ExcelViewer.vue.d.ts +29 -1
- package/dist/core/edit/clipboard-html.d.ts +12 -0
- package/dist/core/edit/clipboard-snapshot.d.ts +76 -0
- package/dist/core/edit/context-menu.d.ts +14 -1
- package/dist/core/edit/edit-controller.d.ts +22 -1
- package/dist/core/edit/paste-behavior.d.ts +33 -0
- package/dist/core/edit/types.d.ts +20 -0
- package/dist/core/export/exporter.d.ts +2 -0
- package/dist/core/export/pivot-tables.d.ts +17 -0
- package/dist/core/export/xlsx-writer.d.ts +6 -0
- package/dist/core/index.d.ts +4 -2
- package/dist/core/model/mutations.d.ts +2 -0
- package/dist/core/model/types.d.ts +61 -0
- package/dist/core/parser/pivot-parser.d.ts +3 -0
- package/dist/core/plugin.d.ts +47 -5
- package/dist/core/render/canvas-renderer.d.ts +12 -0
- package/dist/core/render/pivot-toggle.d.ts +13 -0
- package/dist/core/viewer/controller.d.ts +82 -6
- package/dist/core/viewer/overlay-manager.d.ts +1 -0
- package/dist/core/viewer/paste-config-host.d.ts +12 -0
- package/dist/core/viewer/pivot-dialog-host.d.ts +48 -0
- package/dist/core/viewer/readonly-prompt-host.d.ts +23 -0
- package/dist/core.js +67 -64
- package/dist/index.d.ts +2 -2
- package/dist/index.js +948 -873
- package/dist/react/ExcelViewer.d.ts +25 -4
- package/dist/react.js +621 -535
- package/dist/style.css +1 -1
- package/dist/vue2.css +1 -1
- package/dist/vue2.js +1 -1
- package/package.json +1 -1
- package/dist/chunks/index-Cu1G47bB.js +0 -10575
- package/dist/chunks/plugin-overlay-BUrPrpT2.js +0 -9146
|
@@ -1,30 +1,16 @@
|
|
|
1
1
|
var S = Object.defineProperty;
|
|
2
|
-
var
|
|
2
|
+
var z = Object.getOwnPropertySymbols;
|
|
3
3
|
var E = Object.prototype.hasOwnProperty, H = Object.prototype.propertyIsEnumerable;
|
|
4
|
-
var j = (e, r, t) => r in e ? S(e, r, { enumerable: !0, configurable: !0, writable: !0, value: t }) : e[r] = t,
|
|
4
|
+
var j = (e, r, t) => r in e ? S(e, r, { enumerable: !0, configurable: !0, writable: !0, value: t }) : e[r] = t, w = (e, r) => {
|
|
5
5
|
for (var t in r || (r = {}))
|
|
6
6
|
E.call(r, t) && j(e, t, r[t]);
|
|
7
|
-
if (
|
|
8
|
-
for (var t of
|
|
7
|
+
if (z)
|
|
8
|
+
for (var t of z(r))
|
|
9
9
|
H.call(r, t) && j(e, t, r[t]);
|
|
10
10
|
return e;
|
|
11
11
|
};
|
|
12
|
-
import { i as
|
|
13
|
-
const
|
|
14
|
-
function F() {
|
|
15
|
-
return {
|
|
16
|
-
font: { name: "Calibri", size: 11, bold: !1, italic: !1, underline: !1, strike: !1, color: "#000000" },
|
|
17
|
-
fill: { type: "none" },
|
|
18
|
-
borders: {},
|
|
19
|
-
hAlign: "general",
|
|
20
|
-
vAlign: "bottom",
|
|
21
|
-
wrapText: !1,
|
|
22
|
-
shrinkToFit: !1,
|
|
23
|
-
textRotation: 0,
|
|
24
|
-
indent: 0,
|
|
25
|
-
numFmt: "General"
|
|
26
|
-
};
|
|
27
|
-
}
|
|
12
|
+
import { m as N, i as k, p as G } from "./plugin-overlay-C_qauTcv.js";
|
|
13
|
+
const O = Array(17).fill("#000000");
|
|
28
14
|
function V(e, r) {
|
|
29
15
|
return {
|
|
30
16
|
name: e,
|
|
@@ -32,7 +18,7 @@ function V(e, r) {
|
|
|
32
18
|
state: "visible",
|
|
33
19
|
dimension: { rows: 0, cols: 0 },
|
|
34
20
|
cells: /* @__PURE__ */ new Map(),
|
|
35
|
-
styles: [
|
|
21
|
+
styles: [N()],
|
|
36
22
|
merges: [],
|
|
37
23
|
columns: /* @__PURE__ */ new Map(),
|
|
38
24
|
rows: /* @__PURE__ */ new Map(),
|
|
@@ -45,10 +31,11 @@ function V(e, r) {
|
|
|
45
31
|
charts: [],
|
|
46
32
|
shapes: [],
|
|
47
33
|
sparklines: [],
|
|
34
|
+
pivotTables: [],
|
|
48
35
|
showGridLines: !0
|
|
49
36
|
};
|
|
50
37
|
}
|
|
51
|
-
function
|
|
38
|
+
function $(e, r) {
|
|
52
39
|
if (e == null || e === "") return { raw: null, type: "empty" };
|
|
53
40
|
if (typeof e == "number") return { raw: e, type: "number" };
|
|
54
41
|
if (typeof e == "boolean") return { raw: e, type: "boolean" };
|
|
@@ -67,54 +54,54 @@ function O(e, r) {
|
|
|
67
54
|
}
|
|
68
55
|
return { raw: e, type: "string" };
|
|
69
56
|
}
|
|
70
|
-
function
|
|
57
|
+
function D(e, r) {
|
|
71
58
|
return r.map((t) => e[t]);
|
|
72
59
|
}
|
|
73
60
|
function L(e, r, t = {}) {
|
|
74
|
-
var
|
|
75
|
-
const s = (
|
|
76
|
-
let
|
|
61
|
+
var I, x, p, f, M, C;
|
|
62
|
+
const s = (I = t.startRow) != null ? I : 0, c = (x = t.startCol) != null ? x : 0, y = (p = t.autoInfer) != null ? p : !0;
|
|
63
|
+
let l = 0, h = 0, m = !1;
|
|
77
64
|
if (!r.length) return { lastRow: 0, lastCol: 0, wrote: !1 };
|
|
78
|
-
const
|
|
65
|
+
const d = r[0], o = d !== null && typeof d == "object" && !Array.isArray(d), a = o ? (f = t.columns) != null ? f : Object.keys(d) : [];
|
|
79
66
|
let g = s;
|
|
80
|
-
if (o && ((
|
|
81
|
-
for (let
|
|
82
|
-
const i = g, u = c +
|
|
83
|
-
e.cells.set(
|
|
67
|
+
if (o && ((M = t.headerRow) == null || M)) {
|
|
68
|
+
for (let n = 0; n < a.length; n++) {
|
|
69
|
+
const i = g, u = c + n;
|
|
70
|
+
e.cells.set(k(i, u), { row: i, col: u, type: "string", raw: a[n], styleId: 0 }), l = Math.max(l, i), h = Math.max(h, u), m = !0;
|
|
84
71
|
}
|
|
85
72
|
g++;
|
|
86
73
|
}
|
|
87
|
-
for (let
|
|
88
|
-
const i = r[
|
|
89
|
-
for (let
|
|
90
|
-
const
|
|
91
|
-
|
|
74
|
+
for (let n = 0; n < r.length; n++) {
|
|
75
|
+
const i = r[n], u = Array.isArray(i) ? i : D(i, a);
|
|
76
|
+
for (let R = 0; R < u.length; R++) {
|
|
77
|
+
const b = g + n - (o && ((C = t.headerRow) == null || C), 0), A = c + R, T = $(u[R], y);
|
|
78
|
+
T.type !== "empty" && (e.cells.set(k(b, A), { row: b, col: A, type: T.type, raw: T.raw, styleId: 0 }), l = Math.max(l, b), h = Math.max(h, A), m = !0);
|
|
92
79
|
}
|
|
93
80
|
}
|
|
94
|
-
return { lastRow:
|
|
81
|
+
return { lastRow: l, lastCol: h, wrote: m };
|
|
95
82
|
}
|
|
96
|
-
function
|
|
83
|
+
function v(e, r = {}) {
|
|
97
84
|
var o, a;
|
|
98
|
-
const t = r.themeColors && r.themeColors.length === 17 ? r.themeColors :
|
|
85
|
+
const t = r.themeColors && r.themeColors.length === 17 ? r.themeColors : O, s = (o = r.autoInfer) != null ? o : !0, c = (a = r.headerRow) != null ? a : !0;
|
|
99
86
|
if (e && typeof e == "object" && !Array.isArray(e) && Array.isArray(e.sheets))
|
|
100
87
|
return { sheets: e.sheets.map((x, p) => {
|
|
101
88
|
var i;
|
|
102
|
-
const
|
|
103
|
-
return
|
|
89
|
+
const f = V(x.name || `Sheet${p + 1}`, p), { lastRow: M, lastCol: C, wrote: n } = L(f, (i = x.rows) != null ? i : [], { headerRow: c, autoInfer: s });
|
|
90
|
+
return f.dimension = n ? { rows: M + 1, cols: C + 1 } : { rows: 0, cols: 0 }, f;
|
|
104
91
|
}), activeSheet: 0, themeColors: t, date1904: !1 };
|
|
105
|
-
const y = V(r.sheetName || "Sheet1", 0),
|
|
106
|
-
return y.dimension =
|
|
92
|
+
const y = V(r.sheetName || "Sheet1", 0), l = e != null ? e : [], { lastRow: h, lastCol: m, wrote: d } = L(y, l, { headerRow: c, autoInfer: s });
|
|
93
|
+
return y.dimension = d ? { rows: h + 1, cols: m + 1 } : { rows: 0, cols: 0 }, { sheets: [y], activeSheet: 0, themeColors: t, date1904: !1 };
|
|
107
94
|
}
|
|
108
|
-
function
|
|
95
|
+
function B(e) {
|
|
109
96
|
return !!e && typeof e == "object" && Array.isArray(e.sheets) && typeof e.activeSheet == "number" && Array.isArray(e.themeColors);
|
|
110
97
|
}
|
|
111
|
-
function
|
|
112
|
-
var
|
|
98
|
+
function _(e, r) {
|
|
99
|
+
var d;
|
|
113
100
|
const t = r.sheets[0], s = e.sheets[0];
|
|
114
101
|
if (!t) return e;
|
|
115
102
|
if (!s)
|
|
116
103
|
return {
|
|
117
|
-
sheets: [
|
|
104
|
+
sheets: [F(t, t.name)],
|
|
118
105
|
activeSheet: 0,
|
|
119
106
|
themeColors: r.themeColors,
|
|
120
107
|
date1904: e.date1904,
|
|
@@ -126,13 +113,13 @@ function K(e, r) {
|
|
|
126
113
|
state: "visible",
|
|
127
114
|
dimension: { rows: 0, cols: 0 },
|
|
128
115
|
cells: /* @__PURE__ */ new Map(),
|
|
129
|
-
styles: t.styles.map((o) =>
|
|
130
|
-
merges: t.merges.map((o) =>
|
|
116
|
+
styles: t.styles.map((o) => w({}, o)),
|
|
117
|
+
merges: t.merges.map((o) => w({}, o)),
|
|
131
118
|
columns: new Map(t.columns),
|
|
132
119
|
rows: new Map(t.rows),
|
|
133
120
|
defaultColWidth: t.defaultColWidth,
|
|
134
121
|
defaultRowHeight: t.defaultRowHeight,
|
|
135
|
-
freeze:
|
|
122
|
+
freeze: w({}, t.freeze),
|
|
136
123
|
// 条件格式 / 数据验证 不带过来 —— 数据可能不在模板的目标列上, 套规则会误命中
|
|
137
124
|
conditional: [],
|
|
138
125
|
dataValidations: [],
|
|
@@ -141,12 +128,13 @@ function K(e, r) {
|
|
|
141
128
|
charts: [],
|
|
142
129
|
shapes: [],
|
|
143
130
|
sparklines: [],
|
|
131
|
+
pivotTables: [],
|
|
144
132
|
showGridLines: t.showGridLines
|
|
145
133
|
};
|
|
146
|
-
let y = 0,
|
|
134
|
+
let y = 0, l = 0;
|
|
147
135
|
for (const o of s.cells.values()) {
|
|
148
|
-
const a = t.cells.get(
|
|
149
|
-
c.cells.set(
|
|
136
|
+
const a = t.cells.get(k(o.row, o.col));
|
|
137
|
+
c.cells.set(k(o.row, o.col), {
|
|
150
138
|
row: o.row,
|
|
151
139
|
col: o.col,
|
|
152
140
|
type: o.type,
|
|
@@ -155,14 +143,14 @@ function K(e, r) {
|
|
|
155
143
|
formula: o.formula,
|
|
156
144
|
hyperlink: o.hyperlink,
|
|
157
145
|
comment: o.comment,
|
|
158
|
-
styleId: (
|
|
146
|
+
styleId: (d = a == null ? void 0 : a.styleId) != null ? d : 0,
|
|
159
147
|
dispImgId: o.dispImgId
|
|
160
|
-
}), o.row > y && (y = o.row), o.col >
|
|
148
|
+
}), o.row > y && (y = o.row), o.col > l && (l = o.col);
|
|
161
149
|
}
|
|
162
|
-
const h = t.columns.size ? Math.max(...t.columns.keys()) + 1 : 0,
|
|
150
|
+
const h = t.columns.size ? Math.max(...t.columns.keys()) + 1 : 0, m = t.rows.size ? Math.max(...t.rows.keys()) + 1 : 0;
|
|
163
151
|
return c.dimension = {
|
|
164
|
-
rows: Math.max(s.dimension.rows, y + 1,
|
|
165
|
-
cols: Math.max(s.dimension.cols,
|
|
152
|
+
rows: Math.max(s.dimension.rows, y + 1, m),
|
|
153
|
+
cols: Math.max(s.dimension.cols, l + 1, h)
|
|
166
154
|
}, {
|
|
167
155
|
sheets: [c],
|
|
168
156
|
activeSheet: 0,
|
|
@@ -171,33 +159,34 @@ function K(e, r) {
|
|
|
171
159
|
cellImages: e.cellImages
|
|
172
160
|
};
|
|
173
161
|
}
|
|
174
|
-
function
|
|
162
|
+
function F(e, r) {
|
|
175
163
|
return {
|
|
176
164
|
name: r,
|
|
177
165
|
index: 0,
|
|
178
166
|
state: "visible",
|
|
179
167
|
dimension: { rows: 0, cols: 0 },
|
|
180
168
|
cells: /* @__PURE__ */ new Map(),
|
|
181
|
-
styles: e.styles.map((t) =>
|
|
182
|
-
merges: e.merges.map((t) =>
|
|
169
|
+
styles: e.styles.map((t) => w({}, t)),
|
|
170
|
+
merges: e.merges.map((t) => w({}, t)),
|
|
183
171
|
columns: new Map(e.columns),
|
|
184
172
|
rows: new Map(e.rows),
|
|
185
173
|
defaultColWidth: e.defaultColWidth,
|
|
186
174
|
defaultRowHeight: e.defaultRowHeight,
|
|
187
|
-
freeze:
|
|
175
|
+
freeze: w({}, e.freeze),
|
|
188
176
|
conditional: [],
|
|
189
177
|
dataValidations: [],
|
|
190
178
|
images: [],
|
|
191
179
|
charts: [],
|
|
192
180
|
shapes: [],
|
|
193
181
|
sparklines: [],
|
|
182
|
+
pivotTables: [],
|
|
194
183
|
showGridLines: e.showGridLines
|
|
195
184
|
};
|
|
196
185
|
}
|
|
197
|
-
function
|
|
186
|
+
function K(e, r) {
|
|
198
187
|
return G(e, r);
|
|
199
188
|
}
|
|
200
|
-
const
|
|
189
|
+
const Z = {
|
|
201
190
|
// 放大镜
|
|
202
191
|
find: '<circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>',
|
|
203
192
|
// 漏斗
|
|
@@ -206,6 +195,8 @@ const q = {
|
|
|
206
195
|
"clear-filter": '<path d="M3 5h18l-7 8.5V20l-4-2.2V13.5z"/><line x1="3" y1="3" x2="21" y2="21"/>',
|
|
207
196
|
// 上下箭头(排序)
|
|
208
197
|
sort: '<path d="M7 16l3 3 3-3"/><line x1="10" y1="19" x2="10" y2="5"/><path d="M17 8l-3-3-3 3"/><line x1="14" y1="5" x2="14" y2="19"/>',
|
|
198
|
+
// 透视表: 小表格 + 汇总箭头
|
|
199
|
+
"pivot-table": '<rect x="3" y="4" width="13" height="13" rx="1.5"/><line x1="3" y1="8" x2="16" y2="8"/><line x1="7" y1="4" x2="7" y2="17"/><path d="M14 14h7"/><path d="M18 11l3 3-3 3"/>',
|
|
209
200
|
// 下载(导出)
|
|
210
201
|
export: '<line x1="12" y1="3" x2="12" y2="15"/><path d="M8 11l4 4 4-4"/><path d="M4 19h16"/>',
|
|
211
202
|
// 放大镜带 +
|
|
@@ -225,14 +216,14 @@ const q = {
|
|
|
225
216
|
// 下拉小箭头
|
|
226
217
|
caret: '<path d="M6 9l6 6 6-6"/>'
|
|
227
218
|
};
|
|
228
|
-
function
|
|
219
|
+
function q(e) {
|
|
229
220
|
return '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">' + e + "</svg>";
|
|
230
221
|
}
|
|
231
222
|
export {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
223
|
+
Z as T,
|
|
224
|
+
_ as a,
|
|
225
|
+
B as i,
|
|
226
|
+
v as j,
|
|
227
|
+
K as p,
|
|
228
|
+
q as s
|
|
238
229
|
};
|
|
@@ -7,6 +7,7 @@ import { ReadOptions } from '../core/model/data-access';
|
|
|
7
7
|
import { ViewerTheme } from '../core/render/theme';
|
|
8
8
|
import { ContextMenuBeforePayload, ContextMenuShowPayload, ContextMenuTransform } from '../core/viewer/controller';
|
|
9
9
|
import { EditableTarget } from '../core/edit/types';
|
|
10
|
+
import { PasteBehavior } from '../core/edit/paste-behavior';
|
|
10
11
|
import { FormulaEngineFactory } from '../core/formula/engine';
|
|
11
12
|
import { CellChangePayload, DimChangePayload, DirtyChangePayload, ImageChangePayload, StructChangePayload } from '../core/edit/edit-controller';
|
|
12
13
|
import { EditorResolver } from '../core/edit/editor-context';
|
|
@@ -58,6 +59,12 @@ type __VLS_Props = {
|
|
|
58
59
|
toolbar?: boolean | Array<string | ToolbarItem>;
|
|
59
60
|
/** 编辑总开关:默认 false = 只读(行为不变)。开启后才能进入编辑(E0:闸门) */
|
|
60
61
|
editable?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* 透视表功能开关:默认 false = 关闭。开启后(还需 `editable`)工具栏 `pivot-table` 入口可见、
|
|
64
|
+
* `createPivotTable`/`openPivotTableDialog` 等 API 生效、导出 .xlsx 回注真实 OOXML 透视表零件
|
|
65
|
+
* (overlay 模式同时保留原文件透视表)。
|
|
66
|
+
*/
|
|
67
|
+
pivotTable?: boolean;
|
|
61
68
|
/** 按格只读判定:返回 true = 只读(cell 为空格时传 null) */
|
|
62
69
|
cellReadOnly?: (cell: CellModel | null, pos: {
|
|
63
70
|
row: number;
|
|
@@ -95,6 +102,13 @@ type __VLS_Props = {
|
|
|
95
102
|
recalc?: boolean;
|
|
96
103
|
/** 自定义/自研公式引擎工厂(可换引擎);不给则用默认 HyperFormula(需 npm i hyperformula) */
|
|
97
104
|
formulaEngine?: FormulaEngineFactory;
|
|
105
|
+
/**
|
|
106
|
+
* 粘贴行为(默认 = 覆盖式 1:1)。控制 Ctrl+V / 右键粘贴时源各方面如何落目标(覆盖/合并/仅值)。
|
|
107
|
+
* 不传 = 默认;也可 `viewer.setPasteBehavior(cfg)` 运行时改、右键「选择性粘贴」逐次选预设。
|
|
108
|
+
*/
|
|
109
|
+
pasteBehavior?: Partial<PasteBehavior>;
|
|
110
|
+
/** 粘贴撞只读格的内置提醒:'dialog'(默认,弹窗列出哪些格只读)/ 'toast'(气泡)/ 'none'(只发事件) */
|
|
111
|
+
readOnlyPrompt?: 'dialog' | 'toast' | 'none';
|
|
98
112
|
/**
|
|
99
113
|
* 内置导出进度遮罩(P1.5):默认 `true` —— 调 `viewer.downloadPdf` / `downloadImage` / `downloadXlsx` /
|
|
100
114
|
* `print` / 选区图片批量转换 时,壳自动建 `AbortController` + 接 `onProgress` →
|
|
@@ -206,12 +220,23 @@ declare const __VLS_component: import('vue').DefineComponent<__VLS_Props, {
|
|
|
206
220
|
setActiveSheet(index: number): void;
|
|
207
221
|
getSelection(): MergeRange | null;
|
|
208
222
|
setSelection(range: MergeRange): void;
|
|
223
|
+
scrollToCell(row: number, col: number, opts?: {
|
|
224
|
+
select?: boolean;
|
|
225
|
+
}): boolean;
|
|
209
226
|
rectOf(row: number, col: number): import('../core/plugin').Rect | null;
|
|
210
227
|
rectOfRange(range: MergeRange): import('../core/plugin').Rect | null;
|
|
211
228
|
redraw(): void;
|
|
212
229
|
isCellEditable(row: number, col: number): boolean;
|
|
213
230
|
setEditableTargets(targets: EditableTarget | EditableTarget[] | undefined): void;
|
|
214
231
|
getEditableTargets(): EditableTarget | EditableTarget[] | undefined;
|
|
232
|
+
sortActiveColumn(dir: "asc" | "desc"): boolean;
|
|
233
|
+
createPivotTable(opts: import('../core/plugin').CreatePivotTableOptions): boolean;
|
|
234
|
+
createPivotTableFromSelection(opts?: {
|
|
235
|
+
rowFieldIndex?: number;
|
|
236
|
+
valueFieldIndex?: number;
|
|
237
|
+
output?: import('../core/plugin').PivotOutput;
|
|
238
|
+
}): boolean;
|
|
239
|
+
openPivotTableDialog(): boolean;
|
|
215
240
|
exportImage(opts?: ImageExportOptions): Promise<Blob>;
|
|
216
241
|
downloadImage(opts?: ImageExportOptions): Promise<void>;
|
|
217
242
|
exportPdf(opts?: PdfExportOptions): Promise<Blob>;
|
|
@@ -266,7 +291,10 @@ declare const __VLS_component: import('vue').DefineComponent<__VLS_Props, {
|
|
|
266
291
|
pasteRichHtml(html: string, at?: {
|
|
267
292
|
row: number;
|
|
268
293
|
col: number;
|
|
269
|
-
}): boolean;
|
|
294
|
+
}, behaviorOverride?: Partial<PasteBehavior> | null): boolean;
|
|
295
|
+
getPasteBehavior(): PasteBehavior;
|
|
296
|
+
setPasteBehavior(cfg: Partial<PasteBehavior> | null): void;
|
|
297
|
+
openPasteConfigDialog(): boolean;
|
|
270
298
|
pasteImageBlob(blob: Blob, at?: {
|
|
271
299
|
row: number;
|
|
272
300
|
col: number;
|
|
@@ -17,7 +17,19 @@ export interface ParsedClipboard {
|
|
|
17
17
|
col: number;
|
|
18
18
|
dataUrl: string;
|
|
19
19
|
}[];
|
|
20
|
+
/** 列宽(相对列 index → px;来自 <col width>);稀疏 */
|
|
21
|
+
colWidths: number[];
|
|
22
|
+
/** 行高(相对行 index → px;来自 <tr height>);稀疏 */
|
|
23
|
+
rowHeights: number[];
|
|
20
24
|
}
|
|
25
|
+
/** mso-number-format 的值是 CSS 转义的 Excel 格式码(\0022→" \#→# \;→; \\(→\( …),解回真实格式码。 */
|
|
26
|
+
export declare function unescapeMsoNumFmt(v: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* 从合并 CSS 串里解析 mso-number-format → Excel 格式码(值可能含转义的 \; ,故按引号串/到分号匹配)。
|
|
29
|
+
* **取最后一条**:合并串是 `td默认;类;内联`,按 CSS 层叠后写的覆盖——裸 `td` 默认常带 `mso-number-format:General`,
|
|
30
|
+
* 若取第一条会被它的 General 顶掉、丢掉后面类里的真实日期/货币格式码。
|
|
31
|
+
*/
|
|
32
|
+
export declare function parseMsoNumberFormat(css: string): string | undefined;
|
|
21
33
|
/**
|
|
22
34
|
* 解析剪贴板 HTML → {values, styles, merges, images}。非浏览器环境 / 无 <table> 返 null(调用方回退 TSV)。
|
|
23
35
|
*/
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { AnchorCell, CellModel, CellStyle, CellValueType, MergeRange, RichTextRun, SheetModel, WorkbookModel } from '../model/types';
|
|
2
|
+
/** raw 是 Date 时序列化为 {__d: epochMs},反序列化还原。其余原样 JSON。 */
|
|
3
|
+
type ClipRaw = number | string | boolean | null | {
|
|
4
|
+
__d: number;
|
|
5
|
+
};
|
|
6
|
+
export interface ClipCell {
|
|
7
|
+
r: number;
|
|
8
|
+
c: number;
|
|
9
|
+
type: CellValueType;
|
|
10
|
+
raw: ClipRaw;
|
|
11
|
+
formula?: string;
|
|
12
|
+
hyperlink?: string;
|
|
13
|
+
comment?: string;
|
|
14
|
+
rich?: RichTextRun[];
|
|
15
|
+
dispImgId?: string;
|
|
16
|
+
style: CellStyle;
|
|
17
|
+
}
|
|
18
|
+
export interface ClipImage {
|
|
19
|
+
from: AnchorCell;
|
|
20
|
+
to?: AnchorCell;
|
|
21
|
+
extWidthEmu?: number;
|
|
22
|
+
extHeightEmu?: number;
|
|
23
|
+
editAs?: string;
|
|
24
|
+
mime: string;
|
|
25
|
+
b64: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ClipCellImage {
|
|
28
|
+
id: string;
|
|
29
|
+
mime: string;
|
|
30
|
+
b64: string;
|
|
31
|
+
}
|
|
32
|
+
export interface ClipDim {
|
|
33
|
+
i: number;
|
|
34
|
+
height?: number;
|
|
35
|
+
width?: number;
|
|
36
|
+
hidden?: boolean;
|
|
37
|
+
custom?: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface ClipSnapshot {
|
|
40
|
+
v: 1;
|
|
41
|
+
rows: number;
|
|
42
|
+
cols: number;
|
|
43
|
+
cells: ClipCell[];
|
|
44
|
+
merges: MergeRange[];
|
|
45
|
+
images: ClipImage[];
|
|
46
|
+
cellImages: ClipCellImage[];
|
|
47
|
+
rowHeights: ClipDim[];
|
|
48
|
+
colWidths: ClipDim[];
|
|
49
|
+
}
|
|
50
|
+
/** 图片字节预算(原始字节):复制区图片总字节超此值 → 降级为"无图 1:1 复制",避免剪贴板超限/卡顿。 */
|
|
51
|
+
export declare const CLIP_IMAGE_BUDGET_BYTES: number;
|
|
52
|
+
/**
|
|
53
|
+
* 抓一段区域的完整模型快照(相对坐标)。
|
|
54
|
+
* `withImageBytes=false`:图片只记引用(id / 浮动序号),b64 留空 —— 给"图片字节走可见 `<img>`、
|
|
55
|
+
* 快照只引用"的瘦身传输用(避免图片被双重 base64),粘贴时由 parseSnapshotHtml 从 `<img>` 回填字节。
|
|
56
|
+
*/
|
|
57
|
+
export declare function serializeSnapshot(sheet: SheetModel, wb: WorkbookModel, range: MergeRange, opts?: {
|
|
58
|
+
withImageBytes?: boolean;
|
|
59
|
+
}): ClipSnapshot;
|
|
60
|
+
/** ClipRaw → 运行时 raw(还原 Date)。 */
|
|
61
|
+
export declare function reviveClipRaw(raw: ClipRaw): CellModel['raw'];
|
|
62
|
+
export declare function encodeSnapshot(snap: ClipSnapshot): string;
|
|
63
|
+
export declare function decodeSnapshot(s: string | null | undefined): ClipSnapshot | null;
|
|
64
|
+
/**
|
|
65
|
+
* 把图片字节从可见 `<img data-clip-img="key">` 回填进瘦身快照(传输优化:图片只在 `<img>` 存一份)。
|
|
66
|
+
* key 约定:DISPIMG 单元格图 = `c:${id}`,浮动图 = `f:${序号}`(序号同 serializeSnapshot 的 images 顺序)。
|
|
67
|
+
* 已带 b64(完整快照路径)则原样保留。
|
|
68
|
+
*/
|
|
69
|
+
export declare function reattachImages(snap: ClipSnapshot | null, imgB64: Map<string, string>): ClipSnapshot | null;
|
|
70
|
+
/** 降级:去掉所有图片(超字节预算时用)。DISPIMG 格中性化为空格(保样式),避免粘出引用不到的破图。 */
|
|
71
|
+
export declare function withoutImages(snap: ClipSnapshot): ClipSnapshot;
|
|
72
|
+
/** 从剪贴板 HTML 抽出本组件的 1:1 快照(`<table data-ooxml-clip="...">` + 回填 `<img>` 字节);非本组件复制返 null。 */
|
|
73
|
+
export declare function parseSnapshotHtml(html: string): ClipSnapshot | null;
|
|
74
|
+
export declare function bytesToB64(bytes: Uint8Array): string;
|
|
75
|
+
export declare function b64ToBytes(b64: string): Uint8Array;
|
|
76
|
+
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 右键上下文菜单宿主(框架无关 DOM)—— 挂到 document.body 的 fixed 菜单,点项执行 action + 关闭,
|
|
3
|
-
* 点外部 / Esc
|
|
3
|
+
* 点外部 / Esc 关闭,贴边自动翻转。支持一级子菜单(`children`,悬停展开右侧 flyout)。
|
|
4
|
+
* Vue / React 壳只把 contextmenu 事件转给 controller,菜单逻辑全在这。
|
|
4
5
|
*/
|
|
5
6
|
export interface MenuItem {
|
|
6
7
|
label?: string;
|
|
@@ -8,12 +9,24 @@ export interface MenuItem {
|
|
|
8
9
|
disabled?: boolean;
|
|
9
10
|
/** true = 分隔线 */
|
|
10
11
|
separator?: boolean;
|
|
12
|
+
/** 子菜单项(有则该行不执行 action,悬停展开右侧 flyout) */
|
|
13
|
+
children?: MenuItem[];
|
|
11
14
|
}
|
|
12
15
|
export declare class ContextMenuHost {
|
|
13
16
|
private el;
|
|
17
|
+
private submenus;
|
|
18
|
+
private openSubRow;
|
|
19
|
+
private subCloseTimer;
|
|
14
20
|
private cleanup;
|
|
15
21
|
isOpen(): boolean;
|
|
16
22
|
show(x: number, y: number, items: MenuItem[]): void;
|
|
23
|
+
/** 建一级菜单 DOM(不挂载、不定位);子菜单悬停时再建 flyout。 */
|
|
24
|
+
private buildMenu;
|
|
25
|
+
/** 在 row 右侧展开子菜单 flyout(贴边翻转到左侧);悬停 flyout 取消关闭,离开 flyout 延时关。 */
|
|
26
|
+
private openSubmenu;
|
|
27
|
+
private scheduleSubClose;
|
|
28
|
+
private cancelSubClose;
|
|
29
|
+
private closeAllSubmenus;
|
|
17
30
|
close(): void;
|
|
18
31
|
dispose(): void;
|
|
19
32
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { CellStyleOverride, ColumnInfo, ImageAnchor, MergeRange, RowInfo, SheetModel, WorkbookModel } from '../model/types';
|
|
2
2
|
import { CellValue } from '../model/data-access';
|
|
3
3
|
import { CellSnapshot } from '../model/snapshot';
|
|
4
|
+
import { PasteBehavior } from './paste-behavior';
|
|
4
5
|
import { ParsedClipboard } from './clipboard-html';
|
|
6
|
+
import { ClipSnapshot } from './clipboard-snapshot';
|
|
5
7
|
import { CellPos, DimAxis, EditCommand } from './commands';
|
|
6
8
|
import { StructOp } from '../model/structure';
|
|
7
9
|
import { FormulaEngineFactory } from '../formula/engine';
|
|
@@ -72,7 +74,12 @@ export declare class EditController {
|
|
|
72
74
|
private engine;
|
|
73
75
|
private engineFor;
|
|
74
76
|
private warming;
|
|
77
|
+
private pasteBehavior;
|
|
75
78
|
constructor(host: EditControllerHost);
|
|
79
|
+
/** 读当前粘贴行为(完整配置)。 */
|
|
80
|
+
getPasteBehavior(): PasteBehavior;
|
|
81
|
+
/** 设粘贴行为默认(缺项回落默认);影响 Ctrl+V / 右键「粘贴」。 */
|
|
82
|
+
setPasteBehavior(cfg: Partial<PasteBehavior> | null): void;
|
|
76
83
|
/** 异步懒初始化引擎(开重算 + 有工厂 + 尚未为当前簿建好)。返回的 Promise 供测试 await;生产 fire-and-forget。 */
|
|
77
84
|
warmEngine(): Promise<void>;
|
|
78
85
|
/** 释放引擎(切簿/关重算/dispose)。 */
|
|
@@ -94,14 +101,28 @@ export declare class EditController {
|
|
|
94
101
|
editCell(row: number, col: number, value: CellValue): boolean;
|
|
95
102
|
/** 区域批量设值(2D,左上对齐 range.top/left);跳过只读格,返回是否有改动 */
|
|
96
103
|
editRange(range: MergeRange, values: CellValue[][]): boolean;
|
|
104
|
+
/** 合并区 m 是否与矩形 [top,left]-[bottom,right] 有交叠(用于清粘贴区内的目标原有合并)。 */
|
|
105
|
+
private _mergeOverlaps;
|
|
97
106
|
/**
|
|
98
107
|
* 富粘贴(Excel/WPS 复制的 HTML 解析后):值 + 样式 + 合并 + 图片,**整体单次撤销**。
|
|
99
108
|
* start = 落点左上角。跳过只读格。一次 cloneWorkbook 快照 → 应用全部 → 压一条 restore-wb 逆。
|
|
109
|
+
* behaviorOverride = 右键「选择性粘贴」逐次预设(覆盖 this.pasteBehavior;缺省走默认配置)。
|
|
100
110
|
*/
|
|
101
111
|
pasteRich(start: {
|
|
102
112
|
row: number;
|
|
103
113
|
col: number;
|
|
104
|
-
}, parsed: ParsedClipboard): boolean;
|
|
114
|
+
}, parsed: ParsedClipboard, behaviorOverride?: Partial<PasteBehavior> | null): boolean;
|
|
115
|
+
/** 去重后发一次 paste 的 permission-denied(没只读格则不发)。 */
|
|
116
|
+
private _emitPasteDenied;
|
|
117
|
+
/**
|
|
118
|
+
* 1:1 保真粘贴:用复制时序列化进剪贴板的完整模型快照(本组件自己复制的,跨实例 Vue3/Vue2/React 通用)
|
|
119
|
+
* 覆盖式落到目标区 —— 完整还原 值/原始类型/数字格式/全部样式(含边框)/合并/图片(浮动 + DISPIMG)/行高列宽。
|
|
120
|
+
* 跟 pasteRich(外部 HTML 近似)同走命令栈(整体单次撤销)+ 前后快照 cell-change + 只读 permission-denied。
|
|
121
|
+
*/
|
|
122
|
+
pasteSnapshot(start: {
|
|
123
|
+
row: number;
|
|
124
|
+
col: number;
|
|
125
|
+
}, snap: ClipSnapshot, behaviorOverride?: Partial<PasteBehavior> | null): boolean;
|
|
105
126
|
/** 清空区域(跳过只读) */
|
|
106
127
|
clearRange(range: MergeRange): boolean;
|
|
107
128
|
/** 给区域套样式覆盖(E5;跳过只读格)。返回是否有改动。前后 style 不同 → 发 cell-change。 */
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { CellStyle, CellStyleOverride } from '../model/types';
|
|
2
|
+
export interface PasteBehavior {
|
|
3
|
+
/** 字体/对齐/换行/边框/数字格式: 'overwrite' 覆盖式(只留源) | 'merge' 合并式(源没写的留目标) | 'skip' 不动 */
|
|
4
|
+
cellStyle: 'overwrite' | 'merge' | 'skip';
|
|
5
|
+
/** 填充底色: 'overwrite' 覆盖式(源没写→无填充) | 'merge' 合并式(源没写→留目标底色) | 'skip' 不动 */
|
|
6
|
+
fill: 'overwrite' | 'merge' | 'skip';
|
|
7
|
+
/** 行高: 'source' 搬源行高 | 'keep' 不动 */
|
|
8
|
+
rowHeight: 'source' | 'keep';
|
|
9
|
+
/** 列宽: 'source' 总搬源 | 'keep' 不动 | 'firstRowOnly' 仅粘到首行(start.row===0)搬源 */
|
|
10
|
+
colWidth: 'source' | 'keep' | 'firstRowOnly';
|
|
11
|
+
/** 源自带的合并区: 'apply' 应用 | 'skip' 不应用 */
|
|
12
|
+
sourceMerges: 'apply' | 'skip';
|
|
13
|
+
/** 目标原有、落在粘贴区内的合并: 'clear' 清掉(否则旧合并会吞列致数据错位) | 'keep' 保留 */
|
|
14
|
+
targetMerges: 'clear' | 'keep';
|
|
15
|
+
/** 图片(内嵌/浮动): 'apply' 落格 | 'skip' 不粘 */
|
|
16
|
+
images: 'apply' | 'skip';
|
|
17
|
+
}
|
|
18
|
+
/** 默认粘贴行为 = 覆盖式 1:1(贴近源)。不配置即此。 */
|
|
19
|
+
export declare const DEFAULT_PASTE_BEHAVIOR: PasteBehavior;
|
|
20
|
+
/** 右键预设:保留原来的(仅值)—— 只落值,样式/底色/行高列宽/合并/图片全不动,目标结构完全保留。 */
|
|
21
|
+
export declare const PASTE_PRESET_VALUES_ONLY: PasteBehavior;
|
|
22
|
+
/** 补全部分配置为完整 PasteBehavior(缺项回落默认)。 */
|
|
23
|
+
export declare function resolvePasteBehavior(partial?: Partial<PasteBehavior> | null): PasteBehavior;
|
|
24
|
+
/** 列宽该不该搬:source 总搬;firstRowOnly 仅 start.row===0 搬;keep 不搬。 */
|
|
25
|
+
export declare function shouldApplyColWidth(b: PasteBehavior, startRow: number): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* 按 cellStyle / fill 两档模式,从「目标现有样式 target + 源 patch」算出粘贴后该格的完整 CellStyle。
|
|
28
|
+
* 返回 null = 两档都 skip,样式整个不动(调用方跳过)。neutral = 表的中性默认(styles[0])。
|
|
29
|
+
*
|
|
30
|
+
* 非填充部分(font/对齐/换行/边框/数字格式):overwrite 以 neutral 为基套源(只留源);merge 以 target 为基套源
|
|
31
|
+
* (源没写的留目标);skip 保留 target。填充部分(fill):overwrite 源没写→无填充;merge 源没写→留目标;skip 留目标。
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolvePastedCellStyle(target: CellStyle, neutral: CellStyle, patch: CellStyleOverride, styleMode: PasteBehavior['cellStyle'], fillMode: PasteBehavior['fill']): CellStyle | null;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CellModel, MergeRange } from '../model/types';
|
|
2
2
|
import { FormulaEngineFactory } from '../formula/engine';
|
|
3
|
+
import { PasteBehavior } from './paste-behavior';
|
|
3
4
|
/**
|
|
4
5
|
* 行/列维度目标 (Phase B, 2026-06-08) —— 用于尺寸 API (setColumnWidth / setRowHeight /
|
|
5
6
|
* autoFitColumns / resetColumnWidth ...) 的参数. 3 种形状自动识别:
|
|
@@ -44,6 +45,12 @@ export type EditableTarget = {
|
|
|
44
45
|
export interface EditConfig {
|
|
45
46
|
/** 总开关:默认 false = 只读(行为与历史完全一致) */
|
|
46
47
|
editable?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* 透视表功能开关(默认 false = 关闭)。开启后(还需 `editable`):工具栏 `pivot-table` 入口可见、
|
|
50
|
+
* `createPivotTable` / `openPivotTableDialog` 等 API 生效、导出 .xlsx 回注真实 OOXML 透视表零件
|
|
51
|
+
* (含 overlay 模式保留原文件透视表)。关闭时上述行为全部不生效,与历史版本一致。
|
|
52
|
+
*/
|
|
53
|
+
pivotTable?: boolean;
|
|
47
54
|
/** 按格只读判定:返回 true = 该格只读。cell 为空格时传 null。pos 为 0-based 行列。 */
|
|
48
55
|
cellReadOnly?: (cell: CellModel | null, pos: {
|
|
49
56
|
row: number;
|
|
@@ -80,6 +87,19 @@ export interface EditConfig {
|
|
|
80
87
|
recalc?: boolean;
|
|
81
88
|
/** 自定义/自研公式引擎工厂(可换引擎);不给则用默认 HyperFormula 适配器。 */
|
|
82
89
|
formulaEngine?: FormulaEngineFactory;
|
|
90
|
+
/**
|
|
91
|
+
* 粘贴行为配置(默认 = 覆盖式 1:1,见 {@link PasteBehavior})。控制 Ctrl+V / 右键粘贴时
|
|
92
|
+
* 源内容各方面如何落到目标(覆盖 / 合并 / 仅值)。不传 = 默认。也可运行时 `viewer.setPasteBehavior(cfg)`,
|
|
93
|
+
* 或右键「选择性粘贴」逐次选预设。缺项回落默认。
|
|
94
|
+
*/
|
|
95
|
+
pasteBehavior?: Partial<PasteBehavior>;
|
|
96
|
+
/**
|
|
97
|
+
* 粘贴撞只读格时的**内置提醒**方式(逐格精确,即便编辑模式下也可能有只读格):
|
|
98
|
+
* - `'dialog'`(默认):弹窗列出**具体哪些格**只读,让用户明确知道哪部分没粘上;
|
|
99
|
+
* - `'toast'`:顶部气泡简短提示 + 自动消失;
|
|
100
|
+
* - `'none'`:不弹内置 UI(仍发 `permission-denied` 事件,消费方自处理)。
|
|
101
|
+
*/
|
|
102
|
+
readOnlyPrompt?: 'dialog' | 'toast' | 'none';
|
|
83
103
|
}
|
|
84
104
|
/** 单元格编辑权限 */
|
|
85
105
|
export type EditPermission = 'editable' | 'readonly';
|
|
@@ -16,6 +16,8 @@ export interface ExporterHost {
|
|
|
16
16
|
getFileName(): string | undefined;
|
|
17
17
|
/** 原始 .xlsx 字节(高保真 overlay 导出重载原件用;无则返回 null,overlay 回退 rebuild) */
|
|
18
18
|
getSourceBuffer?(): ArrayBuffer | null;
|
|
19
|
+
/** 透视表功能是否开启(EditConfig.pivotTable;决定 xlsx 导出是否回注/保留 pivot 零件) */
|
|
20
|
+
isPivotEnabled?(): boolean;
|
|
19
21
|
}
|
|
20
22
|
export declare class WorkbookExporter {
|
|
21
23
|
private host;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { WorkbookModel } from '../model/types';
|
|
2
|
+
/**
|
|
3
|
+
* 把 App 内创建的透视表回注进 ExcelJS 写出的 zip 字节,返回新 zip 字节。
|
|
4
|
+
* 无可回注透视表 → 原样返回(零开销)。单个透视表失败不影响其余(整体 try/catch 由调用方兜底)。
|
|
5
|
+
*/
|
|
6
|
+
export declare function injectPivotTablesIntoZip(zipBytes: Uint8Array, workbook: WorkbookModel): Uint8Array;
|
|
7
|
+
/**
|
|
8
|
+
* overlay 导出:把**原文件**的透视表零件原样搬运进 ExcelJS 写出的 zip(ExcelJS 不建模 pivot,
|
|
9
|
+
* load→write 会整套丢掉)。与 `injectPivotTablesIntoZip`(重建 App 内新建的)互补,先搬运后重建,
|
|
10
|
+
* 重建侧的零件编号/cacheId 扫描会自动避开搬运进来的。
|
|
11
|
+
*
|
|
12
|
+
* 搬运内容:xl/pivotCache/** + xl/pivotTables/**(零件 + _rels 整目录)、workbook `<pivotCaches>`
|
|
13
|
+
* 注册(cacheId 不变,r:id 在新 rels 里重新分配)、所在 worksheet 的隐式关系(按表名匹配新旧
|
|
14
|
+
* worksheet)、`[Content_Types].xml` Override。源数据被编辑过时,打开后由宿主刷新重算(透视表
|
|
15
|
+
* 自身带 cacheSource 范围)。原件无透视表 → 原样返回(零开销)。
|
|
16
|
+
*/
|
|
17
|
+
export declare function restoreOriginalPivotPartsIntoZip(zipBytes: Uint8Array, sourceBytes: Uint8Array): Uint8Array;
|
|
@@ -15,6 +15,12 @@ export interface XlsxExportOptions {
|
|
|
15
15
|
fidelity?: 'rebuild' | 'overlay';
|
|
16
16
|
/** 原始 .xlsx 字节(overlay 模式用;由 exporter 从 host 注入,用方一般不直接传) */
|
|
17
17
|
sourceBuffer?: ArrayBuffer;
|
|
18
|
+
/**
|
|
19
|
+
* 透视表零件回注开关(默认 false,经 viewer 导出时随 `EditConfig.pivotTable` 自动注入)。
|
|
20
|
+
* 开启时:① App 内创建的透视表重建成真实 OOXML 零件(pivot-tables.ts);② overlay 模式
|
|
21
|
+
* 从 `sourceBuffer` 原样搬运原文件的透视表零件(ExcelJS 不建模 pivot,不搬运就丢)。
|
|
22
|
+
*/
|
|
23
|
+
pivotTables?: boolean;
|
|
18
24
|
/** 长任务进度回调(zip 写出前/后 emit `{stage:'zip'}`;exceljs writeBuffer 黑盒) */
|
|
19
25
|
onProgress?: ExportProgressFn;
|
|
20
26
|
/** 取消信号(zip 阶段前后检查) */
|