ooxml-excel-editor 1.3.3 → 1.11.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 (51) hide show
  1. package/CHANGELOG.md +283 -0
  2. package/README.md +52 -11
  3. package/dist/chunks/index-CeTZbV_m.js +13673 -0
  4. package/dist/chunks/{index.es-D9BGYyEt.js → index.es-CNm6wZK8.js} +1 -1
  5. package/dist/chunks/{index.es-n6H_ncuE.js → index.es-DF0q70BO.js} +1 -1
  6. package/dist/chunks/{jspdf.es.min-B6-ocR7J.js → jspdf.es.min-65KuNx_3.js} +2 -2
  7. package/dist/chunks/{jspdf.es.min-Dbn0akWf.js → jspdf.es.min-Bo8KrqZO.js} +2 -2
  8. package/dist/chunks/plugin-overlay-C_fL02Qc.js +12128 -0
  9. package/dist/chunks/{toolbar-icons-fOm95ASq.js → toolbar-icons-BjwdJDiN.js} +60 -65
  10. package/dist/components/ExcelViewer.vue.d.ts +46 -1
  11. package/dist/components/FindBar.vue.d.ts +8 -0
  12. package/dist/core/edit/autofill.d.ts +10 -0
  13. package/dist/core/edit/clipboard-html.d.ts +12 -0
  14. package/dist/core/edit/clipboard-snapshot.d.ts +76 -0
  15. package/dist/core/edit/commands.d.ts +9 -1
  16. package/dist/core/edit/context-menu.d.ts +14 -1
  17. package/dist/core/edit/data-validation.d.ts +15 -0
  18. package/dist/core/edit/edit-controller.d.ts +33 -2
  19. package/dist/core/edit/editor-context.d.ts +5 -2
  20. package/dist/core/edit/paste-behavior.d.ts +33 -0
  21. package/dist/core/edit/types.d.ts +26 -0
  22. package/dist/core/export/exporter.d.ts +2 -0
  23. package/dist/core/export/pivot-tables.d.ts +17 -0
  24. package/dist/core/export/xlsx-writer.d.ts +6 -0
  25. package/dist/core/index.d.ts +4 -2
  26. package/dist/core/model/mutations.d.ts +4 -0
  27. package/dist/core/model/types.d.ts +113 -2
  28. package/dist/core/parser/pivot-parser.d.ts +3 -0
  29. package/dist/core/plugin.d.ts +69 -5
  30. package/dist/core/render/canvas-renderer.d.ts +28 -0
  31. package/dist/core/render/pivot-toggle.d.ts +13 -0
  32. package/dist/core/viewer/comment-dialog-host.d.ts +16 -0
  33. package/dist/core/viewer/conditional-format-dialog-host.d.ts +30 -0
  34. package/dist/core/viewer/controller.d.ts +140 -8
  35. package/dist/core/viewer/number-format-dialog-host.d.ts +25 -0
  36. package/dist/core/viewer/overlay-manager.d.ts +1 -0
  37. package/dist/core/viewer/paste-config-host.d.ts +12 -0
  38. package/dist/core/viewer/pivot-dialog-host.d.ts +48 -0
  39. package/dist/core/viewer/readonly-prompt-host.d.ts +23 -0
  40. package/dist/core/viewer/validation-prompt-host.d.ts +25 -0
  41. package/dist/core.js +67 -64
  42. package/dist/index.d.ts +2 -2
  43. package/dist/index.js +1110 -925
  44. package/dist/react/ExcelViewer.d.ts +41 -4
  45. package/dist/react.js +835 -628
  46. package/dist/style.css +1 -1
  47. package/dist/vue2.css +1 -1
  48. package/dist/vue2.js +1 -1
  49. package/package.json +1 -1
  50. package/dist/chunks/index-6q8kSGQg.js +0 -10575
  51. package/dist/chunks/plugin-overlay-BUrPrpT2.js +0 -9146
@@ -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,18 @@ 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;
54
+ /**
55
+ * 条件格式编辑开关(默认 false = 关闭,只读渲染)。开启后(还需 `editable`):工具栏 `conditional-format`
56
+ * 入口可见、`openConditionalFormatDialog` / `addConditionalRule` 等 API 生效、导出 .xlsx 回写条件格式
57
+ * (rebuild 按模型写全类型;overlay 保留原件未编辑规则原样,只增改用户改的)。关闭时与历史版本一致。
58
+ */
59
+ conditionalFormat?: boolean;
47
60
  /** 按格只读判定:返回 true = 该格只读。cell 为空格时传 null。pos 为 0-based 行列。 */
48
61
  cellReadOnly?: (cell: CellModel | null, pos: {
49
62
  row: number;
@@ -80,6 +93,19 @@ export interface EditConfig {
80
93
  recalc?: boolean;
81
94
  /** 自定义/自研公式引擎工厂(可换引擎);不给则用默认 HyperFormula 适配器。 */
82
95
  formulaEngine?: FormulaEngineFactory;
96
+ /**
97
+ * 粘贴行为配置(默认 = 覆盖式 1:1,见 {@link PasteBehavior})。控制 Ctrl+V / 右键粘贴时
98
+ * 源内容各方面如何落到目标(覆盖 / 合并 / 仅值)。不传 = 默认。也可运行时 `viewer.setPasteBehavior(cfg)`,
99
+ * 或右键「选择性粘贴」逐次选预设。缺项回落默认。
100
+ */
101
+ pasteBehavior?: Partial<PasteBehavior>;
102
+ /**
103
+ * 粘贴撞只读格时的**内置提醒**方式(逐格精确,即便编辑模式下也可能有只读格):
104
+ * - `'dialog'`(默认):弹窗列出**具体哪些格**只读,让用户明确知道哪部分没粘上;
105
+ * - `'toast'`:顶部气泡简短提示 + 自动消失;
106
+ * - `'none'`:不弹内置 UI(仍发 `permission-denied` 事件,消费方自处理)。
107
+ */
108
+ readOnlyPrompt?: 'dialog' | 'toast' | 'none';
83
109
  }
84
110
  /** 单元格编辑权限 */
85
111
  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 阶段前后检查) */
@@ -10,7 +10,7 @@ export type { ParseProgress } from './progress';
10
10
  export { cellDisplayText, getCell, getCellValue, getCellStyle, getCellText, getSheetData, getRangeData, sheetToJSON, getWorkbookJSON, } from './model/data-access';
11
11
  export type { CellValue, ReadOptions, SheetToJSONOptions } from './model/data-access';
12
12
  export { cellKey } from './model/types';
13
- export type { WorkbookModel, SheetModel, CellModel, CellStyle, MergeRange, ConditionalRule, ChartSpec, ImageAnchor, ShapeSpec, Sparkline, CssColor, TransformModelFn, CellStyleFn, CellStyleOverride, } from './model/types';
13
+ export type { WorkbookModel, SheetModel, CellModel, CellStyle, MergeRange, ConditionalRule, ChartSpec, ImageAnchor, ShapeSpec, Sparkline, PivotTableModel, PivotButton, PivotTableLayout, PivotFilterRule, PivotValueRule, PivotSummary, PivotFilterMode, CssColor, TransformModelFn, CellStyleFn, CellStyleOverride, } from './model/types';
14
14
  export { CanvasRenderer } from './render/canvas-renderer';
15
15
  export type { ViewState, RendererOptions } from './render/canvas-renderer';
16
16
  export { ViewerController } from './viewer/controller';
@@ -20,6 +20,8 @@ export type { OverlayQuads } from './viewer/overlay-manager';
20
20
  export { PluginOverlayHost } from './viewer/plugin-overlay';
21
21
  export { resolveEditable } from './edit/permissions';
22
22
  export type { EditConfig, EditPermission } from './edit/types';
23
+ export type { PasteBehavior } from './edit/paste-behavior';
24
+ export { DEFAULT_PASTE_BEHAVIOR, PASTE_PRESET_VALUES_ONLY, resolvePasteBehavior } from './edit/paste-behavior';
23
25
  export { EditController } from './edit/edit-controller';
24
26
  export type { EditControllerHost, EditEventName, EditSource, CellChangePayload, DimChangePayload, DirtyChangePayload, ImageChangePayload, StructChangePayload, } from './edit/edit-controller';
25
27
  export { isDimCommand, isImageCommand, isStructCommand } from './edit/commands';
@@ -41,7 +43,7 @@ export type { ShiftSpec, ShiftAxis } from './formula/refs';
41
43
  export { CellEditorHost } from './edit/editor-host';
42
44
  export type { CellEditorContext, CellEditorFactory, EditorResolver, EditorCommitValue } from './edit/editor-context';
43
45
  export { definePlugin } from './plugin';
44
- export type { ExcelPlugin, ExcelPluginContext, OverlayContext, OverlayNode, PluginEvent, Rect, ToolbarItem, ViewerApi, } from './plugin';
46
+ export type { ExcelPlugin, ExcelPluginContext, OverlayContext, OverlayNode, PluginEvent, Rect, ToolbarItem, ViewerApi, CreatePivotTableOptions, PivotOutput, } from './plugin';
45
47
  export { GridMetrics, colIndexToLetters } from './layout/grid-metrics';
46
48
  export { formatValue } from './format/number-format';
47
49
  export { DEFAULT_THEME, mergeTheme } from './render/theme';
@@ -4,6 +4,8 @@ import { CellValue } from './data-access';
4
4
  export declare function setCellValue(sheet: SheetModel, row: number, col: number, value: CellValue): void;
5
5
  /** 清空单元格(删除该格,保留 dimension)。 */
6
6
  export declare function clearCell(sheet: SheetModel, row: number, col: number): void;
7
+ /** 设/清单元格批注(1.11.0):空批注清除;格不存在且有批注 → 建空格挂批注。 */
8
+ export declare function setCellComment(sheet: SheetModel, row: number, col: number, comment: string): void;
7
9
  /** 区域批量设值(2D,左上对齐 range.top/left)。 */
8
10
  export declare function setRangeValues(sheet: SheetModel, range: MergeRange, values: CellValue[][]): void;
9
11
  /**
@@ -23,6 +25,8 @@ export declare function cloneImageAnchor(a: ImageAnchor): ImageAnchor;
23
25
  export declare function addImage(sheet: SheetModel, anchor: ImageAnchor, index?: number): number;
24
26
  /** 删一张图(调用方负责为 undo 捕获前态)。 */
25
27
  export declare function removeImage(sheet: SheetModel, index: number): void;
28
+ /** 整个 CellModel 落格(承载公式/超链/批注/富文本/dispImgId 等 setCellValue 推不出的字段)+ 撑维度。 */
29
+ export declare function setCellModel(sheet: SheetModel, cell: CellModel): void;
26
30
  /**
27
31
  * 浮动图 → WPS 单元格内嵌图(DISPIMG)。
28
32
  * 取 sheet.images[imageIndex] 的字节登记到 wb.cellImages(新 id),目标格设 =DISPIMG 公式 + dispImgId,
@@ -122,14 +122,22 @@ export interface FreezeInfo {
122
122
  }
123
123
  /** 条件格式规则(简化版,覆盖常见 4 类) */
124
124
  export interface ConditionalRule {
125
+ /** 稳定 id(解析:`cf-p<n>`;用户新建:`cf-u<n>`)。编辑/删除/导出对账用。1.9.0 起;老数据缺省 */
126
+ id?: string;
127
+ /** 来源:'parsed' 从文件解析;'user' app 内新建。overlay 导出据此决定原样回写还是按模型写。缺省按 parsed */
128
+ origin?: 'parsed' | 'user';
129
+ /** app 内被编辑过(parsed 规则改过后置 true)。导出:parsed && !dirty → 原样回写 raw;否则按模型写 */
130
+ dirty?: boolean;
125
131
  ranges: MergeRange[];
126
132
  priority: number;
127
133
  type: 'cellIs' | 'colorScale' | 'dataBar' | 'iconSet' | 'expression' | 'top10' | 'unsupported';
128
134
  /** cellIs */
129
135
  operator?: string;
130
136
  formulae?: string[];
131
- /** 命中时套用的样式(cellIs / expression) */
132
- style?: Partial<CellStyle>;
137
+ /** 命中时套用的样式(cellIs / expression / top10)。dxf 各字段都可缺,故 font 也是 Partial */
138
+ style?: Omit<Partial<CellStyle>, 'font'> & {
139
+ font?: Partial<Font>;
140
+ };
133
141
  /** colorScale: 2~3 个色标 */
134
142
  colorScale?: {
135
143
  min: CssColor;
@@ -144,7 +152,20 @@ export interface ConditionalRule {
144
152
  /** iconSet */
145
153
  iconSet?: {
146
154
  name: string;
155
+ reverse?: boolean;
156
+ };
157
+ /** top10: rank 个 / percent 百分比 / bottom 底部 */
158
+ top10?: {
159
+ rank: number;
160
+ percent: boolean;
161
+ bottom: boolean;
147
162
  };
163
+ /**
164
+ * 导出专用:解析时原始 ExcelJS rule 对象(含 cfvo 阈值等我们不全建模的字段)。
165
+ * overlay 导出对"未编辑的 parsed 规则"原样回写保真;编辑色阶/数据条/图标集时尽量改这里的颜色/名称、留住阈值。
166
+ * 框架无关模型刻意只放它作不透明透传载体,渲染/编辑逻辑不读它。
167
+ */
168
+ raw?: unknown;
148
169
  }
149
170
  /** 图片锚定(像素矩形由 layout 阶段最终算出,这里给逻辑锚点) */
150
171
  export interface ImageAnchor {
@@ -195,6 +216,33 @@ export interface ChartSeries {
195
216
  values: (number | null)[];
196
217
  color?: CssColor;
197
218
  }
219
+ /**
220
+ * 数据验证规则(完整版,1.8.0):承载校验语义 —— 编辑时拦截非法输入 + 输入/出错提示。
221
+ * 渲染层只用 list 型画下拉箭头(见 SheetModel.dataValidations / dataValidationLists,从这里派生)。
222
+ * formulae 已尽量解析成字面量:整数/小数/文本长度 → number;日期/时间 → number(序列值)或原始串;
223
+ * list → 选项数组在 options;custom → 公式串(暂不求值,放行)。
224
+ */
225
+ export interface DataValidationRule {
226
+ range: MergeRange;
227
+ type: 'list' | 'whole' | 'decimal' | 'date' | 'time' | 'textLength' | 'custom';
228
+ /** 比较运算符(whole/decimal/date/time/textLength 用;list/custom 不用) */
229
+ operator?: 'between' | 'notBetween' | 'equal' | 'notEqual' | 'greaterThan' | 'lessThan' | 'greaterThanOrEqual' | 'lessThanOrEqual';
230
+ /** 约束操作数(between 用前两个;单目用第一个)。原样保留,校验时按 type 解析 */
231
+ formulae: (string | number)[];
232
+ /** 允许留空(空值不校验) */
233
+ allowBlank: boolean;
234
+ /** list 型可选值(= dataValidationLists 同源) */
235
+ options?: string[];
236
+ /** 出错提示(showErrorMessage 时,非法输入弹) */
237
+ showErrorMessage?: boolean;
238
+ errorStyle?: 'stop' | 'warning' | 'information';
239
+ errorTitle?: string;
240
+ error?: string;
241
+ /** 输入提示(showInputMessage 时,选中该格弹气泡) */
242
+ showInputMessage?: boolean;
243
+ promptTitle?: string;
244
+ prompt?: string;
245
+ }
198
246
  export interface SheetModel {
199
247
  name: string;
200
248
  index: number;
@@ -219,12 +267,21 @@ export interface SheetModel {
219
267
  autoFilterRange?: MergeRange;
220
268
  /** 含"列表"型数据验证的区域(选中时画下拉箭头) */
221
269
  dataValidations: MergeRange[];
270
+ /** 列表型数据验证的可选值(点下拉箭头弹选;range 内任一格命中即用 options)。可选 —— 老数据/无选项时缺省 */
271
+ dataValidationLists?: {
272
+ range: MergeRange;
273
+ options: string[];
274
+ }[];
275
+ /** 完整数据验证规则(校验语义:编辑拦截 + 输入/出错提示)。1.8.0 起;上面两个字段从这里派生 */
276
+ dataValidationRules?: DataValidationRule[];
222
277
  images: ImageAnchor[];
223
278
  charts: ChartSpec[];
224
279
  /** 形状 / 文本框(DrawingML sp) */
225
280
  shapes: ShapeSpec[];
226
281
  /** 迷你图(单元格内嵌折线/柱/盈亏图) */
227
282
  sparklines: Sparkline[];
283
+ /** 透视表只读 UI 元数据:用于叠加字段按钮/下拉箭头;数据仍按普通单元格显示 */
284
+ pivotTables: PivotTableModel[];
228
285
  /** 手动分页符(0-based 边界索引): 在这些行上方/列左侧画分页虚线 */
229
286
  pageBreaks?: {
230
287
  rows: number[];
@@ -284,6 +341,53 @@ export interface Sparkline {
284
341
  color?: CssColor;
285
342
  negativeColor?: CssColor;
286
343
  }
344
+ /** 透视表字段按钮(只读 UI):按钮锚在对应标题/筛选单元格上。 */
345
+ export interface PivotButton {
346
+ row: number;
347
+ col: number;
348
+ label: string;
349
+ kind: 'row' | 'col' | 'page' | 'data' | 'field';
350
+ }
351
+ /** 透视表模型(只读):范围来自 pivotTableDefinition/location,字段来自 cacheFields + axis/dataFields。 */
352
+ export interface PivotTableModel {
353
+ name: string;
354
+ range: MergeRange;
355
+ fields: string[];
356
+ buttons: PivotButton[];
357
+ /** 静态透视表来源:当前模型内的源表 index + 源数据区域。用于运行时重建/后续导出。 */
358
+ source?: {
359
+ sheetIndex: number;
360
+ range: MergeRange;
361
+ };
362
+ /** 运行时透视布局元数据:字段 index 均为源数据区域内的绝对列 index。 */
363
+ layout?: PivotTableLayout;
364
+ /** 已折叠的外层行分组 key(行字段 ≥2 时,外层分组可折叠隐藏明细;空/缺省 = 全展开)。 */
365
+ collapsed?: string[];
366
+ /** 运行时:可折叠的分组表头所在输出行(绝对行号)+ 外层 key;每次重算刷新,供渲染折叠按钮 + 命中测试。 */
367
+ rowGroups?: {
368
+ row: number;
369
+ key: string;
370
+ }[];
371
+ }
372
+ export type PivotSummary = 'sum' | 'count' | 'avg' | 'max' | 'min';
373
+ /** all=全部 / non-empty=非空 / equals=单值等于 / include=多选包含(values 列出保留值,空=不约束)。 */
374
+ export type PivotFilterMode = 'all' | 'non-empty' | 'equals' | 'include';
375
+ export interface PivotFilterRule {
376
+ field: number;
377
+ mode: PivotFilterMode;
378
+ value?: string;
379
+ values?: string[];
380
+ }
381
+ export interface PivotValueRule {
382
+ field: number;
383
+ summary: PivotSummary;
384
+ }
385
+ export interface PivotTableLayout {
386
+ filters: PivotFilterRule[];
387
+ columns: number[];
388
+ rows: number[];
389
+ values: PivotValueRule[];
390
+ }
287
391
  export interface WorkbookModel {
288
392
  sheets: SheetModel[];
289
393
  activeSheet: number;
@@ -297,6 +401,13 @@ export interface WorkbookModel {
297
401
  cellImages?: Map<string, CellImage>;
298
402
  }
299
403
  export declare const cellKey: (row: number, col: number) => string;
404
+ /**
405
+ * 规范的"空白默认样式"——所有 SheetModel.styles[0] 必须是它(中性、无填充、无边框)。
406
+ * 空格 / 新建格 / setCellValue 落的格 / applyStyleOverride 的兜底基样式都回落到 styleId 0,
407
+ * 因此 index 0 绝不能是"恰好第一个被解析到的单元格样式"(否则那个格的底色/边框会冒到所有默认格,
408
+ * 见 parser 把首格 A1 绿底当默认导致粘贴/编辑串色的 bug)。loader-json / parser / clipboard-snapshot 共用此工厂。
409
+ */
410
+ export declare function makeDefaultStyle(): CellStyle;
300
411
  /** 数据钩子: 解析后、渲染前改模型(返回新模型或就地改) */
301
412
  export type TransformModelFn = (workbook: WorkbookModel) => WorkbookModel | void;
302
413
  /** 单元格样式覆盖(各字段可选;font/fill/borders 允许部分,与解析样式浅合并) */
@@ -0,0 +1,3 @@
1
+ import { RawPackage } from './raw-xml';
2
+ import { SheetModel } from '../model/types';
3
+ export declare function attachPivotTables(pkg: RawPackage, sheets: SheetModel[]): void;
@@ -1,10 +1,11 @@
1
- import { CellStyleFn, CellStyleOverride, ImageAnchor, MergeRange, TransformModelFn, WorkbookModel } from './model/types';
1
+ import { CellStyleFn, CellStyleOverride, ConditionalRule, ImageAnchor, MergeRange, PivotTableLayout, TransformModelFn, WorkbookModel } from './model/types';
2
2
  import { CellValue, ReadOptions, SheetToJSONOptions } from './model/data-access';
3
3
  import { CellSnapshot } from './model/snapshot';
4
4
  import { CellInspection } from './model/inspect';
5
5
  import { MenuItem } from './edit/context-menu';
6
6
  import { EditorResolver } from './edit/editor-context';
7
7
  import { EditableTarget } from './edit/types';
8
+ import { PasteBehavior } from './edit/paste-behavior';
8
9
  import { ViewerTheme } from './render/theme';
9
10
  import { ExcelSource } from './loader';
10
11
  import { ImageExportOptions, PdfExportOptions, PrintOptions } from './export/types';
@@ -15,6 +16,24 @@ export interface Rect {
15
16
  w: number;
16
17
  h: number;
17
18
  }
19
+ export type PivotOutput = {
20
+ kind: 'current-sheet';
21
+ cell: string;
22
+ } | {
23
+ kind: 'new-sheet';
24
+ };
25
+ export interface CreatePivotTableOptions {
26
+ /** 源数据区域,第一行作为字段名。缺省时使用当前选区。 */
27
+ sourceRange?: MergeRange;
28
+ /** 源数据所在 sheet index。缺省为当前活动表。 */
29
+ sourceSheetIndex?: number;
30
+ /** 输出位置。缺省为当前表源区域右侧空两列。 */
31
+ output?: PivotOutput;
32
+ /** 透视表布局。缺省:第一个文本字段为行字段,第一个数值字段为值字段。 */
33
+ layout?: Partial<PivotTableLayout>;
34
+ /** 是否打开右侧字段面板。缺省 false;工具栏入口会打开。 */
35
+ showPanel?: boolean;
36
+ }
18
37
  export type PluginEvent = 'cell-click' | 'cell-dblclick' | 'selection-change' | 'sheet-change' | 'hyperlink-click' | 'cell-change' | 'edit-start' | 'edit-commit' | 'dim-change' | 'dirty-change' | 'image-change' | 'struct-change' | 'permission-denied';
19
38
  /**
20
39
  * 权限拒绝事件 payload (Phase A, 2026-06-08):mutation 因 editable / editableTargets /
@@ -24,8 +43,8 @@ export type PluginEvent = 'cell-click' | 'cell-dblclick' | 'selection-change' |
24
43
  * 一次操作只 emit 一次 (避免 N 张图 spam N 次).
25
44
  */
26
45
  export interface PermissionDeniedPayload {
27
- /** 触发的操作类型 */
28
- reason: 'paste' | 'merge' | 'unmerge' | 'image-place' | 'image-convert' | 'dimension' | 'other';
46
+ /** 触发的操作类型('copy' = 复制图片超字节预算,已降级为无图复制,非真正"拒绝") */
47
+ reason: 'paste' | 'merge' | 'unmerge' | 'image-place' | 'image-convert' | 'dimension' | 'copy' | 'other';
29
48
  /** 被拒的目标格 (粘贴 / 合并 / 图片转换 等场景下的具体位置;'dimension' 时可空) */
30
49
  cells: Array<{
31
50
  row: number;
@@ -47,6 +66,10 @@ export interface ViewerApi {
47
66
  setActiveSheet(index: number): void;
48
67
  getSelection(): MergeRange | null;
49
68
  setSelection(range: MergeRange): void;
69
+ /** 滚动到指定单元格;select=true 时同步选中目标格。 */
70
+ scrollToCell(row: number, col: number, opts?: {
71
+ select?: boolean;
72
+ }): boolean;
50
73
  rectOf(row: number, col: number): Rect | null;
51
74
  rectOfRange(range: MergeRange): Rect | null;
52
75
  redraw(): void;
@@ -61,6 +84,40 @@ export interface ViewerApi {
61
84
  setEditableTargets(targets: EditableTarget | EditableTarget[] | undefined): void;
62
85
  /** 当前生效的可编辑白名单. `undefined` 表示未启用白名单. */
63
86
  getEditableTargets(): EditableTarget | EditableTarget[] | undefined;
87
+ /** 按活动单元格所在列排序;未开启自动筛选时会先按选区/已用区建立筛选范围。 */
88
+ sortActiveColumn(dir: 'asc' | 'desc'): boolean;
89
+ /** 通过 API 直接创建静态透视表,不依赖当前页面选区或对话框。需开启 `pivotTable` + `editable` 配置(默认均关)。 */
90
+ createPivotTable(opts: CreatePivotTableOptions): boolean;
91
+ /** 基于当前选区创建静态透视汇总表;未传 opts 时使用默认布局并输出到右侧。需 `pivotTable` + `editable`。 */
92
+ createPivotTableFromSelection(opts?: {
93
+ rowFieldIndex?: number;
94
+ valueFieldIndex?: number;
95
+ output?: PivotOutput;
96
+ }): boolean;
97
+ /** 打开透视表字段选择对话框,再从当前选区创建静态透视汇总表。需 `pivotTable` + `editable`。 */
98
+ openPivotTableDialog(): boolean;
99
+ /** 当前表的条件格式规则集(只读副本)。 */
100
+ getConditionalRules(): ConditionalRule[];
101
+ /** 新增一条规则(未给 id 自动派、origin 默认 'user');返回新 id 或 false。 */
102
+ addConditionalRule(rule: Partial<ConditionalRule> & Pick<ConditionalRule, 'ranges' | 'type'>): string | false;
103
+ /** 按 id 改一条规则(浅合并)。 */
104
+ updateConditionalRule(ruleId: string, patch: Partial<ConditionalRule>): boolean;
105
+ /** 按 id 删一条规则。 */
106
+ removeConditionalRule(ruleId: string): boolean;
107
+ /** 整表替换规则集。 */
108
+ setConditionalRules(rules: ConditionalRule[]): boolean;
109
+ /** 打开条件格式管理对话框(框架无关 DOM,三壳共用)。 */
110
+ openConditionalFormatDialog(): boolean;
111
+ /** 给当前选区设数字格式代码(numFmt)。需 editable。1.11.0 */
112
+ setSelectionNumberFormat(code: string): boolean;
113
+ /** 打开数字格式编辑对话框(框架无关 DOM,三壳共用)。需 editable + 选区。1.11.0 */
114
+ openNumberFormatDialog(): boolean;
115
+ /** 读某格批注(无则 '')。1.11.0 */
116
+ getCellComment(row: number, col: number): string;
117
+ /** 设/清某格批注(空串 = 删除)。需 editable。1.11.0 */
118
+ setCellComment(row: number, col: number, comment: string): boolean;
119
+ /** 打开批注编辑对话框(默认活动格)。需 editable。1.11.0 */
120
+ openCommentEditor(row?: number, col?: number): boolean;
64
121
  /** 导出当前/指定表为图片 Blob(默认 png) */
65
122
  exportImage(opts?: ImageExportOptions): Promise<Blob>;
66
123
  /** 导出为图片并触发下载 */
@@ -147,11 +204,18 @@ export interface ViewerApi {
147
204
  row: number;
148
205
  col: number;
149
206
  }): boolean;
150
- /** 解析 Excel/WPS 复制的剪贴板 HTML → 富粘贴(值+字体/颜色/填充/边框/对齐+合并+data-uri图),整体单次撤销 */
207
+ /** 解析 Excel/WPS 复制的剪贴板 HTML → 富粘贴(值+字体/颜色/填充/边框/对齐+合并+data-uri图),整体单次撤销。
208
+ * behaviorOverride = 逐次粘贴行为预设(右键「选择性粘贴」用;缺省走 setPasteBehavior 设的默认) */
151
209
  pasteRichHtml(html: string, at?: {
152
210
  row: number;
153
211
  col: number;
154
- }): boolean;
212
+ }, behaviorOverride?: Partial<PasteBehavior> | null): boolean;
213
+ /** 读当前粘贴行为配置(完整) */
214
+ getPasteBehavior(): PasteBehavior;
215
+ /** 设粘贴行为默认(缺项回落默认);影响 Ctrl+V / 右键「粘贴」 */
216
+ setPasteBehavior(cfg: Partial<PasteBehavior> | null): void;
217
+ /** 打开「粘贴行为配置」面板(框架无关 DOM,三壳共用);需 editable。返回是否打开 */
218
+ openPasteConfigDialog(): boolean;
155
219
  /** 把一张图片 blob 落到活动格(转内嵌图);剪贴板单图 / 拖文件进网格用 */
156
220
  pasteImageBlob(blob: Blob, at?: {
157
221
  row: number;
@@ -147,6 +147,11 @@ export declare class CanvasRenderer {
147
147
  };
148
148
  private selection;
149
149
  setSelection(sel: MergeRange | null): void;
150
+ /** 自动填充柄可见(= editable;1.10.0)。控制器创建后设置。 */
151
+ showFillHandle: boolean;
152
+ /** 自动填充拖拽预览区(目标范围;拖拽中由控制器设)。 */
153
+ private fillPreview;
154
+ setFillPreview(range: MergeRange | null): void;
150
155
  private findHits;
151
156
  private findCurrent;
152
157
  /** 扫描非空单元格,返回命中的格(按阅读顺序: 先行后列) */
@@ -174,6 +179,11 @@ export declare class CanvasRenderer {
174
179
  };
175
180
  /** 屏幕坐标是否落在某个自动筛选表头的下拉按钮上;是则返回列号 */
176
181
  filterButtonAt(view: ViewState, px: number, py: number): number | null;
182
+ /** 点击是否落在"活动格的数据验证下拉箭头"上(箭头只画在选区左上的列表验证格);是则返回该格。 */
183
+ dataValidationButtonAt(view: ViewState, px: number, py: number): {
184
+ row: number;
185
+ col: number;
186
+ } | null;
177
187
  /** 屏幕坐标 → 单元格(0-based)。落在表头或越界返回 null。 */
178
188
  cellAtScreen(view: ViewState, x: number, y: number): {
179
189
  row: number;
@@ -230,6 +240,13 @@ export declare class CanvasRenderer {
230
240
  * 用 this.{canvas,ctx,metrics,freeze,selection,dpr} 当前值,flags 控制 UI 装饰是否绘制。
231
241
  */
232
242
  private paint;
243
+ /** 画各透视表的行分组折叠/展开按钮(贴在分组表头格最左)。 */
244
+ private drawPivotToggles;
245
+ /** 屏幕坐标是否落在某透视表的折叠按钮上;是则返回 { tableIdx, key }。 */
246
+ pivotToggleAt(view: ViewState, px: number, py: number): {
247
+ tableIdx: number;
248
+ key: string;
249
+ } | null;
233
250
  /** 查找高亮: 所有命中淡黄,当前项橙色描边 */
234
251
  private drawFind;
235
252
  /**
@@ -242,6 +259,17 @@ export declare class CanvasRenderer {
242
259
  private drawPageBreaks;
243
260
  /** 选区高亮: 半透明填充 + 蓝色边框,裁到表头以下的正文区。 */
244
261
  private drawSelection;
262
+ /** 自动填充柄的屏幕矩形(选区右下角的小方块);选区不可见/无选区返 null。 */
263
+ fillHandleRect(view: ViewState): {
264
+ x: number;
265
+ y: number;
266
+ w: number;
267
+ h: number;
268
+ } | null;
269
+ /** 点 (px,py) 是否落在填充柄上(命中区比绘制略大,好点中)。 */
270
+ fillHandleAt(view: ViewState, px: number, py: number): boolean;
271
+ /** 拖拽预览:目标范围的虚线框(超出源选区的部分)。 */
272
+ private drawFillPreview;
245
273
  private drawPane;
246
274
  /** 取某格指定边的边框样式(供相邻共享边取较重者);越界/无格返回 undefined */
247
275
  private borderEdgeOf;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * 透视表行分组折叠/展开按钮(canvas 绘制 + 控制器命中测试),与 autofilter 下拉按钮同款套路:
3
+ * 画在分组表头行最左格内,点击由 ViewerController.onMouseDown 经 CanvasRenderer.pivotToggleAt 命中。
4
+ * 只在多行字段(外层分组可折叠)时出现,导出时不画(导出件靠真 OOXML 透视表自身的展开)。
5
+ */
6
+ /** 折叠按钮方框:左对齐贴在分组表头格内、垂直居中;格太窄(<8px)返回 null 不画。 */
7
+ export declare function pivotToggleBox(cellX: number, cellY: number, cellW: number, cellH: number): {
8
+ x: number;
9
+ y: number;
10
+ size: number;
11
+ } | null;
12
+ /** 画一个折叠按钮:方框 + 横杠(展开态显示 −)/ 横竖杠(折叠态显示 +)。 */
13
+ export declare function drawPivotToggle(ctx: CanvasRenderingContext2D, x: number, y: number, size: number, collapsed: boolean): void;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * 批注编辑对话框(框架无关 DOM,三壳共用一份)。1.11.0 新增。
3
+ * 右键「插入/编辑批注」打开:多行文本框 + 确定/删除/取消。确定回调批注文本(空 = 删除)。
4
+ */
5
+ export interface CommentDialogOptions {
6
+ cellRef: string;
7
+ current: string;
8
+ onApply: (text: string) => void;
9
+ }
10
+ export declare class CommentDialogHost {
11
+ private el;
12
+ private cleanup;
13
+ show(opts: CommentDialogOptions): void;
14
+ close(): void;
15
+ dispose(): void;
16
+ }
@@ -0,0 +1,30 @@
1
+ import { ConditionalRule, MergeRange } from '../model/types';
2
+ export interface ConditionalDialogOptions {
3
+ rules: ConditionalRule[];
4
+ selection: MergeRange | null;
5
+ genId: () => string;
6
+ onApply: (rules: ConditionalRule[]) => void;
7
+ }
8
+ export declare class ConditionalFormatDialogHost {
9
+ private el;
10
+ private cleanup;
11
+ private rules;
12
+ private selection;
13
+ private genId;
14
+ private onApply;
15
+ private editing;
16
+ show(opts: ConditionalDialogOptions): void;
17
+ private renderList;
18
+ private startEdit;
19
+ private renderEditor;
20
+ private defaultForType;
21
+ /** 类型专属字段。 */
22
+ private renderFields;
23
+ /** 通用「格式」字段(填充色 + 字体色 + 加粗),cellIs/expression/top10 用。 */
24
+ private formatFields;
25
+ /** 从 DOM 收集字段写回 draft。 */
26
+ private collectFields;
27
+ private collectFormat;
28
+ close(): void;
29
+ dispose(): void;
30
+ }