@4399ywkf/editor 0.8.0 → 0.9.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.
Files changed (43) hide show
  1. package/README.md +58 -27
  2. package/dist/SheetView-YFK7BUS5.js +3 -0
  3. package/dist/{SheetView-SDBRSTVS.js.map → SheetView-YFK7BUS5.js.map} +1 -1
  4. package/dist/{chunk-KRP5V7VF.js → chunk-2DI4FJPC.js} +10 -3
  5. package/dist/chunk-2DI4FJPC.js.map +1 -0
  6. package/dist/{chunk-DGUNVCOM.js → chunk-3QTRZBIK.js} +111 -26
  7. package/dist/chunk-3QTRZBIK.js.map +1 -0
  8. package/dist/chunk-JWHQF2CZ.js +257 -0
  9. package/dist/chunk-JWHQF2CZ.js.map +1 -0
  10. package/dist/{chunk-WF7PIIFR.js → chunk-JZ67Q3QI.js} +4 -4
  11. package/dist/chunk-JZ67Q3QI.js.map +1 -0
  12. package/dist/{chunk-SBTKDJAL.js → chunk-SDVGDHC4.js} +45 -3
  13. package/dist/chunk-SDVGDHC4.js.map +1 -0
  14. package/dist/{chunk-HPSPOM2O.js → chunk-XB6FEC4I.js} +3 -3
  15. package/dist/chunk-XB6FEC4I.js.map +1 -0
  16. package/dist/{csv-parse-D0zTBvDB.d.ts → csv-parse-DXlwzoZ6.d.ts} +28 -1
  17. package/dist/csv.d.ts +2 -2
  18. package/dist/csv.js +4 -4
  19. package/dist/import-O7RUERND.js +6 -0
  20. package/dist/{import-GVZDLWA2.js.map → import-O7RUERND.js.map} +1 -1
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.js +1 -1
  23. package/dist/pdf.d.ts +1 -1
  24. package/dist/pptx.d.ts +1 -1
  25. package/dist/{registry-CYKivRDh.d.ts → registry-BQ5T1Yko.d.ts} +9 -16
  26. package/dist/sheet-runtime.d.ts +10 -8
  27. package/dist/sheet-runtime.js +6 -14
  28. package/dist/sheet-runtime.js.map +1 -1
  29. package/dist/sheet.d.ts +4 -3
  30. package/dist/sheet.js +1 -1
  31. package/dist/xlsx.d.ts +12 -11
  32. package/dist/xlsx.js +121 -32
  33. package/dist/xlsx.js.map +1 -1
  34. package/package.json +71 -27
  35. package/dist/SheetView-SDBRSTVS.js +0 -3
  36. package/dist/chunk-DGUNVCOM.js.map +0 -1
  37. package/dist/chunk-HPSPOM2O.js.map +0 -1
  38. package/dist/chunk-KRP5V7VF.js.map +0 -1
  39. package/dist/chunk-SBTKDJAL.js.map +0 -1
  40. package/dist/chunk-SYCK6Y5F.js +0 -89
  41. package/dist/chunk-SYCK6Y5F.js.map +0 -1
  42. package/dist/chunk-WF7PIIFR.js.map +0 -1
  43. package/dist/import-GVZDLWA2.js +0 -6
package/dist/xlsx.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { W as WorkbookData, a as CsvParseOptions } from './csv-parse-D0zTBvDB.js';
2
- export { C as CellData, e as CellRange, f as CellValue, g as ColumnData, b as CsvToSnapshotResult, R as RowData, S as StyleData, c as WorksheetData, d as csvToSnapshot, p as parseCsv } from './csv-parse-D0zTBvDB.js';
1
+ import { W as WorkbookData, a as CsvParseOptions } from './csv-parse-DXlwzoZ6.js';
2
+ export { C as CellData, e as CellRange, f as CellValue, g as ColumnData, b as CsvToSnapshotResult, R as RowData, S as StyleData, c as WorksheetData, d as csvToSnapshot, p as parseCsv } from './csv-parse-DXlwzoZ6.js';
3
3
 
4
4
  /**
5
5
  * Univer 快照 → xlsx 的各个 XML 部件。
@@ -7,9 +7,10 @@ export { C as CellData, e as CellRange, f as CellValue, g as ColumnData, b as Cs
7
7
  * 纯函数、无 DOM、无引擎依赖:Node 里拿一份 JSON 就能出文件,
8
8
  * 不需要把 871KB 的 Univer 渲染引擎拉起来。
9
9
  *
10
- * **能保住的**:多工作表、值与类型、公式(含共享公式与缓存值)、数字格式、
11
- * 字体/颜色/填充/边框/对齐/换行/旋转、合并单元格、行高列宽、隐藏行列、
12
- * 冻结窗格、网格线开关、标签颜色、工作表隐藏。
10
+ * **能保住的**:多工作表、值与类型、公式(含共享公式、数组公式、动态数组与缓存值)、
11
+ * 1904 日期系统、数字格式、字体/颜色/填充/边框/对齐/换行/旋转/缩小字体填充、会计用下划线、
12
+ * 合并单元格、行高列宽、隐藏行列、冻结窗格、网格线开关、标签颜色、工作表隐藏与深度隐藏、
13
+ * 工作表背景图(data URL 形式的)。
13
14
  *
14
15
  * **保不住的**:图片、图表、透视表、条件格式、数据校验、筛选、批注、超链接。
15
16
  * 它们不在 `cellData` 里,而是各插件自己序列化进 `resources` 的私有结构 ——
@@ -113,12 +114,13 @@ declare function importSheetSource(source: string | Blob | File, options?: Impor
113
114
  * (fflate + 原生 DOMParser)和 Node(任意 zip 库 + @xmldom/xmldom)
114
115
  * 共用这一份实现,不存在两份真相 —— 和 `docx/parse.ts` 同一套约定。
115
116
  *
116
- * **能保住的**:多工作表(含顺序与隐藏)、值与类型、公式(普通/共享/数组)、
117
- * 数字格式、字体/颜色/填充/边框/对齐/换行/旋转、合并单元格、行高列宽、
118
- * 隐藏行列、冻结窗格、网格线开关、标签颜色、quotePrefix(强制文本)。
117
+ * **能保住的**:多工作表(含顺序、隐藏与深度隐藏)、值与类型、
118
+ * 公式(普通/共享/数组/动态数组)、1904 日期系统、数字格式、
119
+ * 字体/颜色/填充/边框/对齐/换行/旋转/缩小字体填充、会计用下划线、合并单元格、
120
+ * 行高列宽、隐藏行列、冻结窗格、网格线开关、标签颜色、工作表背景图、quotePrefix(强制文本)。
119
121
  *
120
122
  * **保不住的**:图片、图表、透视表、条件格式、数据校验、筛选、批注、
121
- * 超链接、主题色。它们要么住在独立部件里(drawing / pivot / comments),
123
+ * 超链接、主题色、模拟运算表的定义(只留结果)。它们要么住在独立部件里(drawing / pivot / comments),
122
124
  * 要么需要把 Excel 的主题模型抄一遍(theme + tint)。这里不假装支持,
123
125
  * 而是逐个探测、**如实报告丢了什么**(见 `warnings`)—— 让宿主能把话
124
126
  * 原样告诉用户,而不是打开表格才发现少了东西。
@@ -149,8 +151,7 @@ declare function parseXlsx(entries: XlsxEntries, options: ParseXlsxOptions): Par
149
151
 
150
152
  /**
151
153
  * 参数类型是 `unknown` 而不是 `FUniver`:`@univerjs/presets` 是可选 peer,
152
- * 写成具体类型会让没装它的项目 typecheck 直接红。运行时按 facade 的两个
153
- * 方法名依次试探(`getSnapshot` 是新名字,`save` 是旧的)。
154
+ * 写成具体类型会让没装它的项目 typecheck 直接红。
154
155
  */
155
156
  declare function snapshotFromUniver(univerAPI: unknown): WorkbookData;
156
157
 
package/dist/xlsx.js CHANGED
@@ -1,9 +1,9 @@
1
- export { importCsv, importSheetSource, importXlsx, parseXlsx } from './chunk-DGUNVCOM.js';
2
- import { pxToPoints, cellRef, escapeXml, pxToColumnWidth, columnName, toArgb, XML_DECL, escapeAttr } from './chunk-SBTKDJAL.js';
3
- import { toSnapshot } from './chunk-WF7PIIFR.js';
4
- export { snapshotFromUniver } from './chunk-WF7PIIFR.js';
5
- import { H_ALIGN, V_ALIGN, CELL_TYPE, WRAP } from './chunk-KRP5V7VF.js';
6
- export { csvToSnapshot, parseCsv } from './chunk-KRP5V7VF.js';
1
+ export { importCsv, importSheetSource, importXlsx, parseXlsx } from './chunk-3QTRZBIK.js';
2
+ import { parseRange, cellRef, pxToPoints, escapeXml, pxToColumnWidth, columnName, toArgb, XML_DECL, escapeAttr, imageMimeOf, dataUrlToImage } from './chunk-SDVGDHC4.js';
3
+ import { toSnapshot } from './chunk-JZ67Q3QI.js';
4
+ export { snapshotFromUniver } from './chunk-JZ67Q3QI.js';
5
+ import { H_ALIGN, V_ALIGN, UNDERLINE, FORMULA_TYPE, CELL_TYPE, WRAP } from './chunk-2DI4FJPC.js';
6
+ export { csvToSnapshot, parseCsv } from './chunk-2DI4FJPC.js';
7
7
  import { numericKeys, richTextToPlain } from './chunk-5HMKRNIH.js';
8
8
  import { zipSync } from 'fflate';
9
9
 
@@ -37,6 +37,18 @@ var V_ALIGN_NAME = {
37
37
  [V_ALIGN.MIDDLE]: "center",
38
38
  [V_ALIGN.BOTTOM]: "bottom"
39
39
  };
40
+ var UNDERLINE_NAME = {
41
+ [UNDERLINE.DOUBLE]: "double",
42
+ [UNDERLINE.SINGLE_ACCOUNTING]: "singleAccounting",
43
+ [UNDERLINE.DOUBLE_ACCOUNTING]: "doubleAccounting"
44
+ };
45
+ function underlineOf(s) {
46
+ if (!s.ul?.s) return void 0;
47
+ return UNDERLINE_NAME[s.ul.t ?? UNDERLINE.SINGLE] ?? "single";
48
+ }
49
+ function fontNameOf(s) {
50
+ return s.ff ?? s.eastAsiaFontFamily ?? "Calibri";
51
+ }
40
52
  var FIRST_CUSTOM_NUMFMT_ID = 164;
41
53
  function createStyleTable(defaultStyle) {
42
54
  const numFmts = /* @__PURE__ */ new Map();
@@ -58,11 +70,11 @@ function createStyleTable(defaultStyle) {
58
70
  xfs.set("0|0|0|0||0", 0);
59
71
  function fontKey(s) {
60
72
  return JSON.stringify([
61
- s.ff ?? null,
73
+ fontNameOf(s),
62
74
  s.fs ?? null,
63
75
  s.bl ?? 0,
64
76
  s.it ?? 0,
65
- s.ul?.s ?? 0,
77
+ underlineOf(s) ?? null,
66
78
  s.st?.s ?? 0,
67
79
  s.va ?? 0,
68
80
  toArgb(s.cl) ?? null
@@ -73,13 +85,14 @@ function createStyleTable(defaultStyle) {
73
85
  if (s.bl) parts.push("<b/>");
74
86
  if (s.it) parts.push("<i/>");
75
87
  if (s.st?.s) parts.push("<strike/>");
76
- if (s.ul?.s) parts.push('<u val="single"/>');
88
+ const underline = underlineOf(s);
89
+ if (underline) parts.push(`<u val="${underline}"/>`);
77
90
  if (s.va === 1) parts.push('<vertAlign val="superscript"/>');
78
91
  else if (s.va === 2) parts.push('<vertAlign val="subscript"/>');
79
92
  parts.push(`<sz val="${s.fs ?? 11}"/>`);
80
93
  const color = toArgb(s.cl);
81
94
  if (color) parts.push(`<color rgb="${color}"/>`);
82
- parts.push(`<name val="${escapeAttr(s.ff ?? "Calibri")}"/>`);
95
+ parts.push(`<name val="${escapeAttr(fontNameOf(s))}"/>`);
83
96
  return `<font>${parts.join("")}</font>`;
84
97
  }
85
98
  function fontId(s) {
@@ -142,6 +155,7 @@ function createStyleTable(defaultStyle) {
142
155
  if (s.ht && H_ALIGN_NAME[s.ht]) a.horizontal = H_ALIGN_NAME[s.ht];
143
156
  if (s.vt && V_ALIGN_NAME[s.vt]) a.vertical = V_ALIGN_NAME[s.vt];
144
157
  if (s.tb === WRAP.WRAP) a.wrapText = true;
158
+ if (s.stf === 1) a.shrinkToFit = true;
145
159
  const angle = s.tr?.a;
146
160
  if (s.tr?.v === 1) {
147
161
  a.textRotation = 255;
@@ -174,7 +188,7 @@ function createStyleTable(defaultStyle) {
174
188
  if (border) attrs.push('applyBorder="1"');
175
189
  if (align) attrs.push('applyAlignment="1"');
176
190
  if (quotePrefix) attrs.push('quotePrefix="1"');
177
- const body = align ? `<alignment${align.horizontal ? ` horizontal="${align.horizontal}"` : ""}${align.vertical ? ` vertical="${align.vertical}"` : ""}${align.wrapText ? ' wrapText="1"' : ""}${align.textRotation !== void 0 ? ` textRotation="${align.textRotation}"` : ""}/>` : "";
191
+ const body = align ? `<alignment${align.horizontal ? ` horizontal="${align.horizontal}"` : ""}${align.vertical ? ` vertical="${align.vertical}"` : ""}${align.wrapText ? ' wrapText="1"' : ""}${align.textRotation !== void 0 ? ` textRotation="${align.textRotation}"` : ""}${align.shrinkToFit ? ' shrinkToFit="1"' : ""}/>` : "";
178
192
  const id = xfXml.length;
179
193
  xfXml.push(body ? `<xf ${attrs.join(" ")}>${body}</xf>` : `<xf ${attrs.join(" ")}/>`);
180
194
  xfs.set(key, id);
@@ -203,9 +217,11 @@ var RESOURCE_LABELS = {
203
217
  SHEET_WORKSHEET_PROTECTION_PLUGIN: "工作表保护"
204
218
  };
205
219
  var BAD_SHEET_NAME = /[\\/?*[\]:]/g;
220
+ var SHEET_STATE = { 1: "hidden", 2: "veryHidden" };
206
221
  var RELS_NS = 'xmlns="http://schemas.openxmlformats.org/package/2006/relationships"';
207
222
  var MAIN_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
208
223
  var DOC_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
224
+ var DYNAMIC_ARRAY_METADATA = `${XML_DECL}<metadata xmlns="${MAIN_NS}" xmlns:xda="http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray"><metadataTypes count="1"><metadataType name="XLDAPR" minSupportedVersion="120000" copy="1" pasteAll="1" pasteValues="1" merge="1" splitFirst="1" rowColShift="1" clearFormats="1" clearComments="1" assign="1" coerce="1" cellMeta="1"/></metadataTypes><futureMetadata name="XLDAPR" count="1"><bk><extLst><ext uri="{bdbb8cdc-fa1e-496e-a857-3c3f30c029c3}"><xda:dynamicArrayProperties fDynamic="1" fCollapsed="0"/></ext></extLst></bk></futureMetadata><cellMetadata count="1"><bk><rc t="1" v="0"/></bk></cellMetadata></metadata>`;
209
225
  function serializeXlsx(workbook, options = {}) {
210
226
  const { cachedFormulaValues = true, creator = "@4399ywkf/editor", created = /* @__PURE__ */ new Date() } = options;
211
227
  const warnings = [];
@@ -261,24 +277,50 @@ function serializeXlsx(workbook, options = {}) {
261
277
  }
262
278
  return unique;
263
279
  };
280
+ const imageExts = /* @__PURE__ */ new Set();
281
+ let dynamicArrays = 0;
282
+ let dataTableCells = 0;
264
283
  order.forEach((sheetId, index) => {
265
284
  const sheet = workbook.sheets?.[sheetId];
266
285
  const name = safeName(sheet.name, index);
267
286
  const path = `xl/worksheets/sheet${index + 1}.xml`;
268
- sheets.push({ name, hidden: sheet.hidden === 1, path });
269
- put(path, buildSheetXml(sheet));
287
+ sheets.push({ name, state: SHEET_STATE[sheet.hidden ?? 0], path });
288
+ put(path, buildSheetXml(sheet, index, name));
270
289
  stats.sheets++;
271
290
  });
272
291
  if (!sheets.length) {
273
- sheets.push({ name: "Sheet1", hidden: false, path: "xl/worksheets/sheet1.xml" });
274
- put("xl/worksheets/sheet1.xml", buildSheetXml({}));
292
+ sheets.push({ name: "Sheet1", path: "xl/worksheets/sheet1.xml" });
293
+ put("xl/worksheets/sheet1.xml", buildSheetXml({}, 0, "Sheet1"));
275
294
  stats.sheets++;
276
295
  }
277
- function buildSheetXml(sheet) {
296
+ function backgroundPictureXml(sheet, index, name) {
297
+ const bg = sheet.backgroundImage;
298
+ if (!bg?.source) return "";
299
+ const image = bg.imageSourceType === "BASE64" ? dataUrlToImage(bg.source) : void 0;
300
+ if (!image) {
301
+ warnings.push(
302
+ bg.imageSourceType === "BASE64" ? `工作表「${name}」的背景图格式不支持(只认 png / jpg / gif / bmp),未随 xlsx 导出` : `工作表「${name}」的背景图是${bg.imageSourceType === "URL" ? "外链地址" : "图床里的图片"},导出不联网取图,未随 xlsx 导出`
303
+ );
304
+ return "";
305
+ }
306
+ const file = `background${index + 1}.${image.ext}`;
307
+ files[`xl/media/${file}`] = image.bytes;
308
+ imageExts.add(image.ext);
309
+ put(
310
+ `xl/worksheets/_rels/sheet${index + 1}.xml.rels`,
311
+ `${XML_DECL}<Relationships ${RELS_NS}><Relationship Id="rId1" Type="${DOC_REL}/image" Target="../media/${file}"/></Relationships>`
312
+ );
313
+ stats.backgroundImages = (stats.backgroundImages ?? 0) + 1;
314
+ return '<picture r:id="rId1"/>';
315
+ }
316
+ function buildSheetXml(sheet, index, name) {
278
317
  const cellData = sheet.cellData ?? {};
279
318
  const rowData = sheet.rowData ?? {};
280
319
  const columnData = sheet.columnData ?? {};
281
320
  const sheetDefault = resolveStyle(sheet.defaultStyle);
321
+ const merges = (sheet.mergeData ?? []).filter(
322
+ (m) => m && (m.endRow > m.startRow || m.endColumn > m.startColumn)
323
+ );
282
324
  const rows = [.../* @__PURE__ */ new Set([...numericKeys(cellData), ...numericKeys(rowData)])].sort(
283
325
  (a, b) => a - b
284
326
  );
@@ -317,6 +359,38 @@ function serializeXlsx(workbook, options = {}) {
317
359
  }
318
360
  sharedIndex.set(si, sharedIndex.size);
319
361
  }
362
+ const arrayRefs = /* @__PURE__ */ new Map();
363
+ const arrayConflict = (range, r0, c0) => {
364
+ for (let r = range.startRow; r <= range.endRow; r++) {
365
+ for (const c of numericKeys(cellData[r])) {
366
+ if (c < range.startColumn || c > range.endColumn || r === r0 && c === c0) continue;
367
+ const other = cellData[r]?.[c];
368
+ if (other?.f || other?.si) return `区域里的 ${cellRef(r, c)} 另有公式`;
369
+ }
370
+ }
371
+ const overlapsMerge = merges.some(
372
+ (m) => m.startRow <= range.endRow && m.endRow >= range.startRow && m.startColumn <= range.endColumn && m.endColumn >= range.startColumn
373
+ );
374
+ return overlapsMerge ? "区域和合并单元格相交" : void 0;
375
+ };
376
+ for (const r of numericKeys(cellData)) {
377
+ for (const c of numericKeys(cellData[r])) {
378
+ const cell = cellData[r]?.[c];
379
+ if (!cell?.f || !cell.ref || cell.ft != null && cell.ft !== FORMULA_TYPE.ARRAY) continue;
380
+ const range = parseRange(cell.ref);
381
+ const reason = !range || range.startRow !== r || range.startColumn !== c ? `引用范围 ${cell.ref} 不以它为左上角` : arrayConflict(range, r, c);
382
+ if (reason || !range) {
383
+ warnings.push(
384
+ `${cellRef(r, c)} 是数组公式,${reason},已降级成普通公式(区域内的值按静态值写出)`
385
+ );
386
+ continue;
387
+ }
388
+ arrayRefs.set(
389
+ `${r},${c}`,
390
+ `${cellRef(range.startRow, range.startColumn)}:${cellRef(range.endRow, range.endColumn)}`
391
+ );
392
+ }
393
+ }
320
394
  const rowXml = [];
321
395
  for (const r of rows) {
322
396
  const cols = numericKeys(cellData[r]);
@@ -348,20 +422,28 @@ function serializeXlsx(workbook, options = {}) {
348
422
  const forceString = cell.t === CELL_TYPE.FORCE_STRING;
349
423
  const styleIndex = style || forceString ? styles.xf(style, forceString) : 0;
350
424
  const s = styleIndex ? ` s="${styleIndex}"` : "";
351
- if (cell.ref && cell.f) {
352
- warnings.push(`${ref} 是数组公式,已降级成普通公式(溢出区域的值按静态值写出)`);
353
- }
425
+ if (cell.ft === FORMULA_TYPE.DATA_TABLE) dataTableCells++;
354
426
  const formula = cell.f?.replace(/^=/, "");
355
427
  if (formula || cell.si) {
356
428
  stats.cells++;
357
429
  const si = cell.si ? sharedIndex.get(cell.si) : void 0;
358
430
  const group = cell.si ? groups.get(cell.si) : void 0;
431
+ const arrayRef = arrayRefs.get(`${r},${c}`);
359
432
  let fXml = "";
433
+ let cm = "";
360
434
  if (formula && si !== void 0 && group) {
361
435
  const range = `${cellRef(group.minR, group.minC)}:${cellRef(group.maxR, group.maxC)}`;
362
436
  fXml = `<f t="shared" ref="${range}" si="${si}">${escapeXml(formula)}</f>`;
363
437
  stats.formulas++;
364
438
  stats.sharedFormulas++;
439
+ } else if (formula && arrayRef) {
440
+ fXml = `<f t="array" ref="${arrayRef}">${escapeXml(formula)}</f>`;
441
+ stats.formulas++;
442
+ stats.arrayFormulas = (stats.arrayFormulas ?? 0) + 1;
443
+ if (cell.fd === 1) {
444
+ cm = ' cm="1"';
445
+ dynamicArrays++;
446
+ }
365
447
  } else if (formula) {
366
448
  fXml = `<f>${escapeXml(formula)}</f>`;
367
449
  stats.formulas++;
@@ -371,7 +453,7 @@ function serializeXlsx(workbook, options = {}) {
371
453
  }
372
454
  const cached = cachedFormulaValues ? formulaValue(cell) : "";
373
455
  const t = cachedFormulaValues && typeof cell.v === "string" ? ' t="str"' : "";
374
- return `<c r="${ref}"${s}${t}>${fXml}${cached}</c>`;
456
+ return `<c r="${ref}"${s}${t}${cm}>${fXml}${cached}</c>`;
375
457
  }
376
458
  const rich = richTextToPlain(cell);
377
459
  if (rich !== void 0) {
@@ -397,18 +479,15 @@ function serializeXlsx(workbook, options = {}) {
397
479
  if (typeof v === "number") return Number.isFinite(v) ? `<v>${v}</v>` : "";
398
480
  return `<v>${escapeXml(String(v))}</v>`;
399
481
  }
400
- const merges = (sheet.mergeData ?? []).filter(
401
- (m) => m && (m.endRow > m.startRow || m.endColumn > m.startColumn)
402
- );
403
482
  for (const m of merges) track(m.endRow, m.endColumn);
404
483
  stats.merges += merges.length;
405
484
  const mergeXml = merges.length ? `<mergeCells count="${merges.length}">${merges.map(
406
485
  (m) => `<mergeCell ref="${cellRef(m.startRow, m.startColumn)}:${cellRef(m.endRow, m.endColumn)}"/>`
407
486
  ).join("")}</mergeCells>` : "";
408
- const colsXml = numericKeys(columnData).map((index) => {
409
- const col = columnData[index];
487
+ const colsXml = numericKeys(columnData).map((index2) => {
488
+ const col = columnData[index2];
410
489
  if (!col) return "";
411
- const attrs = [`min="${index + 1}"`, `max="${index + 1}"`];
490
+ const attrs = [`min="${index2 + 1}"`, `max="${index2 + 1}"`];
412
491
  if (col.w) attrs.push(`width="${pxToColumnWidth(col.w)}"`, 'customWidth="1"');
413
492
  if (col.hd === 1) attrs.push('hidden="1"');
414
493
  const colStyle = resolveStyle(col.s);
@@ -430,24 +509,34 @@ function serializeXlsx(workbook, options = {}) {
430
509
  const dimension = maxRow < 0 ? "A1" : `A1:${columnName(Math.max(maxCol, 0))}${maxRow + 1}`;
431
510
  const tabColor = toArgb(sheet.tabColor);
432
511
  const sheetPr = tabColor ? `<sheetPr><tabColor rgb="${tabColor}"/></sheetPr>` : "";
433
- return `${XML_DECL}<worksheet xmlns="${MAIN_NS}" xmlns:r="${DOC_REL}">` + sheetPr + `<dimension ref="${dimension}"/>` + sheetViewXml + `<sheetFormatPr ${formatAttrs.join(" ")}/>` + (colsXml ? `<cols>${colsXml}</cols>` : "") + `<sheetData>${rowXml.join("")}</sheetData>` + mergeXml + "</worksheet>";
512
+ return `${XML_DECL}<worksheet xmlns="${MAIN_NS}" xmlns:r="${DOC_REL}">` + sheetPr + `<dimension ref="${dimension}"/>` + sheetViewXml + `<sheetFormatPr ${formatAttrs.join(" ")}/>` + (colsXml ? `<cols>${colsXml}</cols>` : "") + `<sheetData>${rowXml.join("")}</sheetData>` + mergeXml + backgroundPictureXml(sheet, index, name) + "</worksheet>";
434
513
  }
435
514
  const sheetEntries = sheets.map(
436
- (s, i) => `<sheet name="${escapeAttr(s.name)}" sheetId="${i + 1}"${s.hidden ? ' state="hidden"' : ""} r:id="rId${i + 1}"/>`
515
+ (s, i) => `<sheet name="${escapeAttr(s.name)}" sheetId="${i + 1}"${s.state ? ` state="${s.state}"` : ""} r:id="rId${i + 1}"/>`
437
516
  ).join("");
517
+ if (dataTableCells) {
518
+ warnings.push(
519
+ `${dataTableCells} 个模拟运算表(数据表)单元格按静态值导出:快照里没有模拟运算表的定义`
520
+ );
521
+ }
438
522
  put(
439
523
  "xl/workbook.xml",
440
- `${XML_DECL}<workbook xmlns="${MAIN_NS}" xmlns:r="${DOC_REL}"><workbookPr/><sheets>${sheetEntries}</sheets>` + // 没写缓存值时必须让 Excel 打开就重算,否则整列公式显示成空白。
524
+ `${XML_DECL}<workbook xmlns="${MAIN_NS}" xmlns:r="${DOC_REL}">` + (workbook.dateSystem === "date1904" ? '<workbookPr date1904="1"/>' : "<workbookPr/>") + `<sheets>${sheetEntries}</sheets>` + // 没写缓存值时必须让 Excel 打开就重算,否则整列公式显示成空白。
441
525
  // 写了缓存值就别加:fullCalcOnLoad 会把文件标脏,用户没改动也会被问「是否保存」
442
526
  (cachedFormulaValues ? "" : '<calcPr calcId="0" fullCalcOnLoad="1"/>') + "</workbook>"
443
527
  );
444
528
  const stylesRelId = sheets.length + 1;
445
529
  const sstRelId = sheets.length + 2;
530
+ const metadataRelId = sheets.length + 3;
531
+ if (dynamicArrays) {
532
+ put("xl/metadata.xml", DYNAMIC_ARRAY_METADATA);
533
+ stats.dynamicArrays = dynamicArrays;
534
+ }
446
535
  put(
447
536
  "xl/_rels/workbook.xml.rels",
448
537
  `${XML_DECL}<Relationships ${RELS_NS}>` + sheets.map(
449
538
  (s, i) => `<Relationship Id="rId${i + 1}" Type="${DOC_REL}/worksheet" Target="worksheets/${s.path.split("/").pop()}"/>`
450
- ).join("") + `<Relationship Id="rId${stylesRelId}" Type="${DOC_REL}/styles" Target="styles.xml"/><Relationship Id="rId${sstRelId}" Type="${DOC_REL}/sharedStrings" Target="sharedStrings.xml"/></Relationships>`
539
+ ).join("") + `<Relationship Id="rId${stylesRelId}" Type="${DOC_REL}/styles" Target="styles.xml"/><Relationship Id="rId${sstRelId}" Type="${DOC_REL}/sharedStrings" Target="sharedStrings.xml"/>` + (dynamicArrays ? `<Relationship Id="rId${metadataRelId}" Type="${DOC_REL}/sheetMetadata" Target="metadata.xml"/>` : "") + "</Relationships>"
451
540
  );
452
541
  put(
453
542
  "xl/sharedStrings.xml",
@@ -456,9 +545,9 @@ function serializeXlsx(workbook, options = {}) {
456
545
  put("xl/styles.xml", styles.xml());
457
546
  put(
458
547
  "[Content_Types].xml",
459
- `${XML_DECL}<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>` + sheets.map(
548
+ `${XML_DECL}<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/>` + [...imageExts].map((ext) => `<Default Extension="${ext}" ContentType="${imageMimeOf(`x.${ext}`)}"/>`).join("") + '<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>' + sheets.map(
460
549
  (s) => `<Override PartName="/${s.path}" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>`
461
- ).join("") + '<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/><Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/><Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/></Types>'
550
+ ).join("") + '<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/><Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>' + (dynamicArrays ? '<Override PartName="/xl/metadata.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml"/>' : "") + '<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/><Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/></Types>'
462
551
  );
463
552
  put(
464
553
  "_rels/.rels",