ooxml-excel-editor 1.11.0 → 1.14.1
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 +53 -0
- package/README.md +51 -255
- package/dist/chunks/{index-CeTZbV_m.js → index-BYG-8dM_.js} +4977 -3637
- package/dist/chunks/index-SuDQ4pfV.js +13355 -0
- package/dist/chunks/{index.es-CNm6wZK8.js → index.es-BUtta4iJ.js} +1 -1
- package/dist/chunks/{index.es-DF0q70BO.js → index.es-CzHP2YBW.js} +1 -1
- package/dist/chunks/{jspdf.es.min-Bo8KrqZO.js → jspdf.es.min-BOekb9jX.js} +2 -2
- package/dist/chunks/{jspdf.es.min-65KuNx_3.js → jspdf.es.min-DpngVMOz.js} +2 -2
- package/dist/chunks/{toolbar-icons-BjwdJDiN.js → toolbar-icons-Daykg95B.js} +35 -33
- package/dist/components/ExcelViewer.vue.d.ts +8 -3
- package/dist/core/edit/formula-autocomplete.d.ts +20 -0
- package/dist/core/formula/builtin/eval.d.ts +7 -0
- package/dist/core/formula/builtin/functions.d.ts +7 -0
- package/dist/core/formula/builtin/index.d.ts +23 -0
- package/dist/core/formula/builtin/parse.d.ts +54 -0
- package/dist/core/formula/builtin/values.d.ts +35 -0
- package/dist/core/index.d.ts +4 -2
- package/dist/core/plugin.d.ts +10 -0
- package/dist/core/render/canvas-renderer.d.ts +3 -0
- package/dist/core/viewer/controller.d.ts +32 -0
- package/dist/core.js +68 -67
- package/dist/index.d.ts +1 -14
- package/dist/index.js +771 -691
- package/dist/react/ExcelViewer.d.ts +5 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react.js +776 -658
- package/dist/style.css +1 -1
- package/dist/vue2.js +78 -7
- package/package.json +10 -1
- package/dist/chunks/plugin-overlay-C_fL02Qc.js +0 -12128
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,59 @@
|
|
|
2
2
|
|
|
3
3
|
本项目遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/) 与 [语义化版本](https://semver.org/lang/zh-CN/)。
|
|
4
4
|
|
|
5
|
+
## [1.14.1] - 2026-06-15
|
|
6
|
+
|
|
7
|
+
> 文档审计 + 入口出口修正(无运行时行为变更)。一次"保证接入/二开都没问题"的体检。
|
|
8
|
+
|
|
9
|
+
### 修复 — 四入口出口同源(接入)
|
|
10
|
+
|
|
11
|
+
- **入口出口不一致 bug**:公式引擎工厂(`builtinFormulaEngineFactory` / `hyperFormulaEngineFactory` / `FUNCTION_NAMES` / `FormulaEngine` 类型)此前只在 `/core` 出口,**主入口 / `/react` / `/vue2` 都拿不到** —— 但 `:formula-engine` 是主组件上的 prop,文档让注入 `hyperFormulaEngineFactory`,实际 `import { hyperFormulaEngineFactory } from 'ooxml-excel-editor'` 会失败。`/react` 入口更是只导出组件 + hook,`parseWorkbook`/类型/`definePlugin` 全够不着。
|
|
12
|
+
- **修法**:主 / `/react` / `/vue2` 入口统一 `export * from core`(各自再加自己的组件)→ **四入口同源**,任一入口都拿到完整 core 公共 API(解析/读数据/类型/插件/导出/公式引擎工厂…),不再各维护清单致漂移。`CellStyleCtx` / `DataValidationRule` 补进 core 出口。typecheck + build + 419 单测验证。
|
|
13
|
+
|
|
14
|
+
### 文档 — 使用 vs 二开 分离 + 准确性
|
|
15
|
+
|
|
16
|
+
- 抽出 **`EXTENDING.md`(二开 / 扩展 API 手册)**:主题 `:theme` / 数据·渲染钩子 / 自定义编辑器 / 右键菜单 transform / 工具栏自定义 / 分层 UI slots / 命令式 API / 导出·打印高级选项 / 插件 `definePlugin`。README 瘦身成**调用方**文档(装/用/props/编辑/导出),顶部留指路;深度二开看 ARCHITECTURE.md。
|
|
17
|
+
- README **具名导出表**重写(原列 5 项、实际 ~40 项 → 按 解析/读数据/格式/插件/公式引擎/导出/类型 分类 + 标注"四入口同源");**props 表**补 `toolbar` / `plugins` / `openLinks` + 数据验证说明;修跨章节断锚点。
|
|
18
|
+
|
|
19
|
+
## [1.14.0] - 2026-06-15
|
|
20
|
+
|
|
21
|
+
> 新增**内置公式引擎**(MIT,零依赖)设为 recalc 默认引擎(取代 GPL 的 HyperFormula),+ **公式自动补全**。覆盖日常 ~60 个常用函数;需更全覆盖可注入 HyperFormula 或自研引擎。
|
|
22
|
+
|
|
23
|
+
### 新增 — 内置 MIT 公式引擎(默认)
|
|
24
|
+
|
|
25
|
+
- **从零实现**(`formula/builtin/`):词法 + 表达式解析(运算符优先级 / `A1` 绝对相对 / 区域 `A1:B2` / 跨表 `Sheet1!A1` / 函数 / 一元 ± / `%`)→ AST;求值器(错误就近传播 `#DIV/0! #N/A #NAME? #NUM! #REF! #VALUE!`);**依赖图 + 拓扑级联重算 + 循环引用检测**(环上格 → `#REF!`)。
|
|
26
|
+
- **函数 ~60**:聚合(SUM/AVERAGE/MAX/MIN/COUNT/COUNTA/PRODUCT/SUMPRODUCT)、数学(ROUND/ROUNDUP/ROUNDDOWN/ABS/INT/MOD/SQRT/POWER/CEILING/FLOOR…)、逻辑(IF/IFERROR/IFNA/AND/OR/NOT/XOR/IFS)、文本(LEFT/RIGHT/MID/LEN/CONCAT/UPPER/LOWER/TRIM/SUBSTITUTE/FIND/SEARCH…)、查找(VLOOKUP/HLOOKUP/INDEX/MATCH/CHOOSE)、条件聚合(SUMIF/COUNTIF/AVERAGEIF)、信息(ISNUMBER/ISBLANK/ISERROR…)、日期(TODAY/NOW/DATE/YEAR/MONTH/DAY/WEEKDAY/DAYS)。
|
|
27
|
+
- **★ 默认引擎变更**:recalc(`:recalc`,opt-in)的默认引擎由 **HyperFormula → 内置引擎**。好处:**MIT 无 GPL 顾虑、零额外依赖、不再懒加载 ~400KB**。代价:函数集比 HyperFormula 的 ~395 小(日常足够)。
|
|
28
|
+
- **HyperFormula 仍可用**:注入 `:formula-engine="hyperFormulaEngineFactory"`(或自研)获得更全覆盖。`core` 新导出 `builtinFormulaEngineFactory` / `BuiltinFormulaEngine` / `FUNCTION_NAMES` / `hyperFormulaEngineFactory`;`defaultFormulaEngineFactory` 保留为 HyperFormula 别名(向后兼容显式注入它的旧代码)。
|
|
29
|
+
- **解析/显示不受影响**:打开文件仍显示原件缓存的计算结果(与引擎无关);引擎只在编辑后重算时介入。
|
|
30
|
+
### 新增 — 公式自动补全
|
|
31
|
+
|
|
32
|
+
- 在框架无关默认单元格编辑器(`edit/formula-autocomplete.ts`,三壳自动都有)里:输 `=SU` 时下方弹**函数名列表 + 参数提示**(`FUNCTION_SIGNATURES`);↑↓ 选、Enter/Tab 接受(插入 `NAME(` 并把光标移进括号)、Esc 关、点选即填。只在公式(`=` 开头)且光标处于函数名 token 时弹,不影响普通文本编辑。列表来源 = 引擎实际支持的函数(`FUNCTION_NAMES`),所见即所得。
|
|
33
|
+
- 测试:`formula/builtin/__tests__`(parse 12 + eval 11 + engine 7 = 30 例:运算符/函数/级联/循环/跨表);`e2e/formula-autocomplete.e2e.ts`(=SU→弹 SUM→点选插入,三壳);现有 `edit-formula.e2e.ts` 重算 e2e 改由内置引擎驱动仍全过。基线:**419 单测 + 192 e2e**。
|
|
34
|
+
|
|
35
|
+
## [1.13.0] - 2026-06-15
|
|
36
|
+
|
|
37
|
+
> 新增 **不连续多区域选择**(Ctrl/⌘ + 点击)。选区模型从单矩形扩成多矩形;纯框架无关 core 交互,壳只转发鼠标。
|
|
38
|
+
|
|
39
|
+
### 新增 — Ctrl 多区域选择
|
|
40
|
+
|
|
41
|
+
- **Ctrl/⌘ + 点击** 行头 / 列头 / 单元格 → 把当前选区收进多选集再起新区,**加选不相邻**区域(Shift 仍是连续区间,已有);普通点击 / 键盘导航 / `selectCell` / 全选回到单选。
|
|
42
|
+
- 选区模型加 `selRanges[]` + `getSelectionRanges()`(全部矩形,末个为活动区)/ `hasMultiSelection()`;`canvas-renderer` 加 `setExtraSelection` 画所有附加区(填充 + 边框;多选时不画自动填充柄,对齐 Excel)。
|
|
43
|
+
- **复制**:多选时各区按出现顺序**逐行堆叠**成块 → TSV + HTML 表写剪贴板(覆盖最常见的"Ctrl 点多个行头复制非相邻行",粘到 Excel/WPS / app 内都成堆叠块)。
|
|
44
|
+
- **状态栏统计**跨所有选区聚合(count/sum/avg/min/max);新增控制器 `getSelectionStats()`,三壳状态栏改用它。
|
|
45
|
+
- 三壳句柄 + 插件 `ViewerApi` 暴露 `getSelectionRanges` / `hasMultiSelection`。测试:`e2e/multi-select.e2e.ts`(Ctrl+点击两不邻行头 → 2 区域 + 回单选,Vue/React/Vue2)。基线:**389 单测 + 189 e2e**。
|
|
46
|
+
|
|
47
|
+
## [1.12.0] - 2026-06-15
|
|
48
|
+
|
|
49
|
+
> 新增 **格式刷**(Format Painter)。纯框架无关 core 交互(控制器采样 + onMouseUp 刷),壳只加工具栏按钮;需 `editable`。
|
|
50
|
+
|
|
51
|
+
### 新增 — 格式刷
|
|
52
|
+
|
|
53
|
+
- 工具栏 `format-painter` 入口:先选**源格**点按钮采样其完整样式(字体/填充/边框/对齐/换行/数字格式),再**点或拖**目标格/区域即刷上(单次撤销);`Esc` 或再点按钮退出,待刷时光标变 `copy`。
|
|
54
|
+
- 控制器 `startFormatPainter(sticky?)` / `isFormatPainterArmed()` / `cancelFormatPainter()`;刷动作在 `onMouseUp` 选区完成后应用(复用 setStyle)。三壳句柄 + 插件 `ViewerApi` 暴露;三 demo 工具栏加 `format-painter` 入口(工具栏按钮 active 态反映待刷)。
|
|
55
|
+
- 测试:`e2e/format-painter.e2e.ts`(采样红底 → 刷到目标格 + undo,Vue/React/Vue2 三壳)。
|
|
56
|
+
- 顺手:`toolbar` 溢出相关 e2e 宽屏断言 1280→1680(工具栏又加了按钮,宽屏才全部容纳)。基线:**389 单测 + 186 e2e**。
|
|
57
|
+
|
|
5
58
|
## [1.11.0] - 2026-06-15
|
|
6
59
|
|
|
7
60
|
> 三个编辑小件合并:**查找替换补全 + 数字格式编辑器 + 批注编辑**。都复用已有引擎/对话框套路;对话框均为框架无关 DOM(三壳共用一份,UI 天然 1:1)。
|
package/README.md
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
# ooxml-excel-editor
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/ooxml-excel-editor)
|
|
4
|
+
[](https://www.npmjs.com/package/ooxml-excel-editor)
|
|
5
|
+
[](https://github.com/ojaDeveloper/ooxml-excel-editor/actions/workflows/ci.yml)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
|
|
3
8
|
> Vue 3 + **Vue 2** + React 高保真 **.xlsx 预览 / 编辑组件** —— Canvas 渲染,**默认只读预览,可选开启编辑**。从零实现解析与渲染,尽量还原微软 Excel 打开工作簿的观感。**三个壳 UI 1:1 对齐**(Vue 3 SFC 是标准,Vue 2 / React 复刻)。
|
|
4
9
|
|
|
5
|
-
[English](#english) · 中文
|
|
10
|
+
**🔗 在线 demo(直接试用):https://ojadeveloper.github.io/ooxml-excel-editor/** · [English](#english) · 中文
|
|
6
11
|
|
|
7
12
|
## ⚡ 快速开始
|
|
8
13
|
|
|
@@ -48,7 +53,7 @@ const src = ref<File>() // 绑个 <input type="file" @change> 给它即可
|
|
|
48
53
|
- 📋 **从 Excel/WPS 富粘贴**:`Ctrl+V` 解析剪贴板 HTML → 还原字体/颜色/填充/边框/对齐/合并单元格,整块单次撤销。**Excel/WPS 把格式放在 `<style>` 块的 CSS 类里(`<td class="xl65">`),解析时会把类规则合并进每格** —— 不只读内联 `style=`。**`Ctrl+V` 走 `paste` 事件拿原始 HTML**;`navigator.clipboard.read()`(右键菜单粘贴用)会**净化**删掉 `<style>`/注释,所以右键粘贴从 WPS 拿的格式不如 `Ctrl+V` 全。图片走多通道:data-uri `<img>` / **WPS VML `o:gfxdata`**(区域复制的内嵌图藏在 VML 里,是个 zip,解出来落格)/ 单图 blob / 拖文件;**数字格式**(日期/货币)也从 `mso-number-format` 解析还原,不再变成裸序列号。**注**:Excel 某些版本只给 `file:///` 本地路径的 `<img>`(浏览器读不到)而不带 `o:gfxdata`,那种区域图仍救不回。
|
|
49
54
|
- 📋 **应用内 1:1 复制粘贴**:本组件自己 `Ctrl+C` 的内容会把**完整模型快照**嵌进剪贴板(`<table data-ooxml-clip>`),`Ctrl+V` 时识别并 1:1 还原 —— 数字不会退化成文本、边框/数字格式/合并/DISPIMG 图片/**行高**全保留;因为快照随剪贴板走,**Vue3/Vue2/React 三壳之间、跨标签页互相复制结果都一致**。粘到外部应用(Excel/WPS/Word)则读可见 `<table>`(近似)。**列宽以目标现有表头为准、不被源覆盖**(列宽整列共享,改了会动表头;同 Excel 默认粘贴)。
|
|
50
55
|
- 📝 **文本溢出**到相邻空格、**自动行高**
|
|
51
|
-
- 🖱 **交互**:单元格选区(合并感知)、拖选、公式栏、状态栏(计数/求和/均值/最值)、超链接可点、裁切文本悬停看全文、Ctrl+C 复制(**同应用内 1:1 保真**:含数字原始值/数字格式/边框/合并/图片/行高,跨 Vue3/Vue2/React 实例互相复制都一致;**列宽以目标表头为准不覆盖**;另带 TSV/HTML 供贴进 Excel/WPS)、**Ctrl+F 查找替换**(高亮 + 上/下定位 + 计数 + 区分大小写/全字匹配;编辑模式带替换 / 全部替换)、**自动筛选**(点下拉真能筛:去重值多选 + 搜值 + 清除)、**自动填充柄**(编辑模式拖选区右下角填序列:等差/日期/星期月份/文本递增,见 [编辑](#编辑可选默认只读))
|
|
56
|
+
- 🖱 **交互**:单元格选区(合并感知)、拖选、公式栏、状态栏(计数/求和/均值/最值)、超链接可点、裁切文本悬停看全文、Ctrl+C 复制(**同应用内 1:1 保真**:含数字原始值/数字格式/边框/合并/图片/行高,跨 Vue3/Vue2/React 实例互相复制都一致;**列宽以目标表头为准不覆盖**;另带 TSV/HTML 供贴进 Excel/WPS)、**Ctrl+F 查找替换**(高亮 + 上/下定位 + 计数 + 区分大小写/全字匹配;编辑模式带替换 / 全部替换)、**自动筛选**(点下拉真能筛:去重值多选 + 搜值 + 清除)、**自动填充柄**(编辑模式拖选区右下角填序列:等差/日期/星期月份/文本递增,见 [编辑](#编辑可选默认只读))、**不连续多选**(Ctrl/⌘ 点击行头/列头/格 加选不相邻区域,复制堆叠 + 状态栏跨区统计)
|
|
52
57
|
- 🖨 **导出 / 打印**:整表/选区/多表导出 **PNG/JPEG**、**PDF**(位图 + **矢量·文字可选可搜**两种)、**系统打印**(可另存 PDF);默认还原原生 `pageSetup`(纸张/方向/页边距/缩放/打印区域/**打印标题行列每页重复**);宽表**横向跨页**(页矩阵);`beforeRenderPage` 注入页眉/页脚/水印、`configureDoc` 注册字体;内置「导出设置」对话框
|
|
53
58
|
- ⚡ **按需加载**(无图表文件不下载 echarts、不导出 PDF 不下载 jspdf)、**友好错误兜底**(损坏/加密/旧 .xls)、解析失败自动给出可读提示
|
|
54
59
|
|
|
@@ -94,7 +99,7 @@ npm i hyperformula
|
|
|
94
99
|
|
|
95
100
|
> **三壳 UI 1:1**: Vue 3 SFC 是参考实现 (Standard), Vue 2 / React 1:1 复刻视觉与交互 (工具栏 SVG 图标 / 下拉子菜单 / 公式栏 / 状态栏 / dialog / 浮层 / 演示 demo 全部对齐). 详见 [docs/Vue2.md](./docs/Vue2.md) 跟 Vue 3 的差异速查 + [CLAUDE.md](./CLAUDE.md) 第 7 中心原则。
|
|
96
101
|
|
|
97
|
-
>
|
|
102
|
+
> 公式重算的引擎(1.14.0 起):默认是**内置 MIT 引擎**(零依赖,覆盖 ~60 常用函数,无许可证负担)。需要更全函数集时,`formulaEngine` prop 注入 **HyperFormula**(`hyperFormulaEngineFactory`,GPL-3.0/商业双授权,~395 函数)或自研引擎(实现 `FormulaEngine` 接口)。
|
|
98
103
|
|
|
99
104
|
## 使用
|
|
100
105
|
|
|
@@ -302,13 +307,16 @@ viewer.value.getRangeData(viewer.value.getSelection()) // 取"我选中的"区
|
|
|
302
307
|
| `readOnlyCellStyle` | `boolean \| CellStyleOverride \| CellStyleFn` | **只读视觉钩子**(Phase C, 2026-06-08)— 默认 `false` 无视觉差异(老行为);`true` 套内置浅灰底 `#f5f7fa`;对象 = 固定样式给所有只读格;函数 = 按格自定义。仅在该格 `editable=false` 时套用,跟 `editableTargets` 配合一眼看出哪些格可编辑。**鼠标光标**: 编辑模式下悬停只读格自动变 `not-allowed`(内置,不可关)。 |
|
|
303
308
|
| `editor` | `EditorResolver` | 自定义单元格编辑器工厂(返回任意 DOM) |
|
|
304
309
|
| `recalc` | `boolean` | 公式重算(默认 `false`;需 `editable`) |
|
|
305
|
-
| `formulaEngine` | `FormulaEngineFactory` | 自定义/自研公式引擎(默认
|
|
310
|
+
| `formulaEngine` | `FormulaEngineFactory` | 自定义/自研公式引擎(默认 = 内置 MIT 引擎;可注入 `hyperFormulaEngineFactory` 或自研) |
|
|
306
311
|
| `pasteBehavior` | `Partial<PasteBehavior>` | 粘贴行为(默认 = 覆盖式 1:1)。逐项可配:`cellStyle`/`fill`(`overwrite`/`merge`/`skip`)·`rowHeight`(`source`/`keep`)·`colWidth`(`firstRowOnly`/`source`/`keep`)·`sourceMerges`(`apply`/`skip`)·`targetMerges`(`clear`/`keep`,默认清=修旧合并吞列)·`images`(`apply`/`skip`)。缺项回落默认。也可运行时 `viewer.setPasteBehavior(cfg)` / 右键「选择性粘贴」/ 工具栏「⚙ 粘贴配置」面板 |
|
|
307
312
|
| `readOnlyPrompt` | `'dialog' \| 'toast' \| 'none'` | 粘贴撞只读格的内置提醒(默认 `'dialog'`):`dialog` 弹窗**列出具体哪些格**只读 / `toast` 顶部气泡 / `none` 只发 `permission-denied` 事件。逐格精确(编辑模式下也可能有只读格) |
|
|
308
313
|
| `cellImageFit` | `'fill' \| 'contain' \| 'cover'` | WPS 单元格内嵌图贴合方式(默认 `contain` 等比,与 WPS 渲染一致) |
|
|
309
314
|
| `imageLightbox` | `boolean` | 图片点击放大灯箱(默认 `true`;只读单击图放大、编辑右键「查看大图」) |
|
|
315
|
+
| `toolbar` | `false \| Array<string \| ToolbarItem>` | 操作工具栏配置(默认 `['find','filter','sort']`)。内置 id 见 [操作工具栏](EXTENDING.md#操作工具栏可配置--可插件--响应式);`false` 不渲染。可混入自定义项 |
|
|
316
|
+
| `plugins` | `ExcelPlugin[]` | 插件数组(`definePlugin` 打包 theme/cellStyle/transformModel/events/overlay/toolbar/setup);见 [插件](EXTENDING.md#插件-defineplugin) |
|
|
317
|
+
| `openLinks` | `boolean` | 单击超链接是否自动打开(默认 `true`;`false` 只发 `@hyperlink` 事件,自己处理) |
|
|
310
318
|
|
|
311
|
-
> 编辑相关 props 详见下方 [编辑](#编辑可选默认只读)
|
|
319
|
+
> 编辑相关 props 详见下方 [编辑](#编辑可选默认只读) 章节;数据验证(开 `editable` 后**编辑时拦截非法输入** + 列表型下拉选值)无需额外 prop,解析到的规则自动生效。
|
|
312
320
|
|
|
313
321
|
| 事件 | 载荷 | 触发时机 |
|
|
314
322
|
|---|---|---|
|
|
@@ -325,13 +333,21 @@ viewer.value.getRangeData(viewer.value.getSelection()) // 取"我选中的"区
|
|
|
325
333
|
|
|
326
334
|
### 具名导出
|
|
327
335
|
|
|
336
|
+
> **四个入口同源**:`ooxml-excel-editor`(Vue 3)、`/react`、`/vue2`、`/core` 都 **re-export 同一套框架无关 core 公共 API** —— 任一入口都能拿到下表全部(各入口再各自加自己的组件)。所以 React/Vue2 用方不必绕到 `/core`。
|
|
337
|
+
|
|
328
338
|
| 导出 | 说明 |
|
|
329
339
|
|---|---|
|
|
330
|
-
| `ExcelViewer` | 预览/编辑组件 |
|
|
331
|
-
| `
|
|
332
|
-
| `
|
|
333
|
-
| `
|
|
334
|
-
|
|
|
340
|
+
| `ExcelViewer` | 预览/编辑组件(各框架入口各自的) |
|
|
341
|
+
| `default` | Vue 3 / Vue 2 入口默认导出 = Vue 插件(`app.use`) |
|
|
342
|
+
| **解析/加载** | `parseWorkbook(buffer)`(`ArrayBuffer → Promise<WorkbookModel>`,优先 Worker)· `loadArrayBuffer(src)`(多种输入归一) |
|
|
343
|
+
| **读数据 API** | `getCellValue` / `getCellText` / `getCellStyle` / `getSheetData` / `getRangeData` / `sheetToJSON` / `getWorkbookJSON` / `cellDisplayText` |
|
|
344
|
+
| **格式/工具** | `formatValue`(数字格式)· `cellKey` · `colIndexToLetters` |
|
|
345
|
+
| **插件 / 主题** | `definePlugin` · `DEFAULT_THEME` / `mergeTheme` |
|
|
346
|
+
| **公式引擎**(1.14.0) | `builtinFormulaEngineFactory`(默认,MIT)· `hyperFormulaEngineFactory`(HyperFormula,GPL/商业)· `FUNCTION_NAMES`(已支持函数名)· `BuiltinFormulaEngine` |
|
|
347
|
+
| **导出工具** | `workbookToXlsxBlob` · `toCsv` / `toWorkbookJson` · `canvasToBlob` / `canvasToDataURL` / `downloadBlob` |
|
|
348
|
+
| **类型** | `WorkbookModel` / `SheetModel` / `CellModel` / `CellStyle` / `CellStyleOverride` / `MergeRange` / `ConditionalRule` / `DataValidationRule` / `ChartSpec` / `ImageAnchor` / `PivotTableModel` / `CssColor` / `ExcelSource` / `ViewerApi` / `ExcelPlugin` / `FormulaEngine` / `FormulaEngineFactory` / `EditConfig` / 导出选项类型(`PdfExportOptions`/`ImageExportOptions`/…)等 |
|
|
349
|
+
|
|
350
|
+
> 想看完整出口清单见 [`src/core/index.ts`](src/core/index.ts);深度二开(直接用 `ViewerController` / `CanvasRenderer` / `EditController` / 模型 mutations 等内部件)也都从这些入口导出,见 [ARCHITECTURE.md](ARCHITECTURE.md)。
|
|
335
351
|
|
|
336
352
|
## 编辑(可选,默认只读)
|
|
337
353
|
|
|
@@ -371,7 +387,7 @@ viewer.value.getRangeData(viewer.value.getSelection()) // 取"我选中的"区
|
|
|
371
387
|
| 撤销/进编辑 | `undo()` · `redo()` · `canUndo()` · `canRedo()` · `beginEdit(row,col)` · `cancelEdit()` · `isEditing()` · `getEditingCell()` |
|
|
372
388
|
| 公式栏 | `getCellEditString()`(活动格可编辑字符串:公式→`=…`,数值→原始数字串) · `canEditActiveCell()` · `commitActiveCellValue(value, move?)`(顶部 Fx 公式栏可编辑并与单元格联动,底层即用这套) |
|
|
373
389
|
| 查询/状态 | `getCellSnapshot(row,col)` · **`inspectCell(row,col)`**(全息体检:snapshot + 合并区 + 浮动图覆盖 + WPS 内嵌图 + 数据验证 + 条件格式命中 + 链接/批注) · `isDirty()` · `resetToOriginal()` · `isRecalcReady()` · `getVirtualExtent()`(当前虚拟行列范围) |
|
|
374
|
-
| 导出 | `exportXlsx/downloadXlsx` · `exportJson/downloadJson` · `exportCsv/downloadCsv`(见 [导出](#导出--打印)) |
|
|
390
|
+
| 导出 | `exportXlsx/downloadXlsx` · `exportJson/downloadJson` · `exportCsv/downloadCsv`(见 [导出](EXTENDING.md#导出--打印)) |
|
|
375
391
|
|
|
376
392
|
所有写操作(含拖拽改宽高/移图)**统一进撤销栈**、发对应事件、翻**脏标记**;`resetToOriginal()` 一键放弃全部修改、还原到刚加载的原件。
|
|
377
393
|
|
|
@@ -407,6 +423,14 @@ const myEditor: EditorResolver = (cell, pos) => {
|
|
|
407
423
|
- 全部入撤销栈、发 `cell-change`/`image-change`、翻脏标记。(`convertImageToCell(imgIdx,row,col)` 仍保留,用于显式指定目标格。)
|
|
408
424
|
- **导出往返**:`downloadXlsx()` / `exportXlsx()` 导出时,在 ExcelJS 写出后**于 zip 层回注** WPS 私有件(`cellimages.xml` + rels + media + `[Content_Types].xml`/`workbook.xml.rels` 补丁,从模型重建),原有的 + App 内新转的内嵌图导出后用 WPS 打开都正常显示。rebuild / overlay 两种保真模式均覆盖;无字节的 blob-only 图除外。
|
|
409
425
|
|
|
426
|
+
### 不连续多区域选择(1.13.0)
|
|
427
|
+
|
|
428
|
+
**Ctrl/⌘ + 点击** 行头 / 列头 / 单元格 → 加选不相邻区域(Shift 仍是连续区间);普通点击 / 键盘导航回单选。多选时复制把各区**逐行堆叠**成块(TSV + HTML,粘到 Excel/WPS/app 内都成堆叠块),状态栏统计跨所有区聚合。`getSelectionRanges()` / `hasMultiSelection()` 读多选状态。纯框架无关 core 交互,三壳一致。
|
|
429
|
+
|
|
430
|
+
### 格式刷(1.12.0)
|
|
431
|
+
|
|
432
|
+
工具栏 `format-painter` 入口:先选**源格**点按钮采样其完整样式(字体/填充/边框/对齐/换行/数字格式),再**点或拖**目标格/区域即把格式刷上(单次撤销);`Esc` 或再点按钮退出,待刷时光标变 `copy`。也可 `startFormatPainter()` / `isFormatPainterArmed()` / `cancelFormatPainter()` 直调。纯框架无关 core 交互,三壳一致。
|
|
433
|
+
|
|
410
434
|
### 数字格式 / 批注 / 查找替换(1.11.0)
|
|
411
435
|
|
|
412
436
|
- **查找替换**:`Ctrl+F` 打开查找栏,编辑模式下多出替换行 —— 替换输入 + 「替换」(替换当前并跳下一个)/「全部替换」(整体单次撤销),支持区分大小写 / 全字匹配,跳过只读格。
|
|
@@ -429,7 +453,13 @@ const myEditor: EditorResolver = (cell, pos) => {
|
|
|
429
453
|
|
|
430
454
|
### 公式重算(可换引擎)
|
|
431
455
|
|
|
432
|
-
开 `:recalc` 后,编辑公式格或被公式引用的格 → 依赖格**自动级联重算**,每个变动都发 `cell-change`(`source: 'api'|'ui'|'undo'|'redo'`)
|
|
456
|
+
开 `:recalc` 后,编辑公式格或被公式引用的格 → 依赖格**自动级联重算**,每个变动都发 `cell-change`(`source: 'api'|'ui'|'undo'|'redo'`)。
|
|
457
|
+
|
|
458
|
+
**默认引擎(1.14.0 起)= 内置 MIT 引擎**:从零实现的解析 + 求值 + 依赖图 + 拓扑级联 + 循环检测,**零依赖、无 GPL**,覆盖日常 ~60 个常用函数(SUM/AVERAGE/IF/IFERROR/VLOOKUP/INDEX/MATCH/SUMIF/COUNTIF/ROUND/LEFT/MID/CONCAT/DATE/TODAY… 见 `FUNCTION_NAMES` 导出)。函数集比 HyperFormula 小,但日常足够。
|
|
459
|
+
|
|
460
|
+
需要更全覆盖:`:formula-engine="hyperFormulaEngineFactory"` 注入 **HyperFormula**(可选 peer `npm i hyperformula`,GPL-3.0/商业双授权,~395 函数),或注入自研引擎(实现 `FormulaEngine` 接口)。`isRecalcReady()` 查引擎是否就绪。**打开文件的显示值与引擎无关**(读原件缓存结果),引擎只在编辑后重算时介入。
|
|
461
|
+
|
|
462
|
+
**公式自动补全**(1.14.0):在单元格里输 `=SU` 时下方弹**函数名列表 + 参数提示**;↑↓ 选、Enter/Tab 接受(插入 `SUM(` 并把光标移进括号)、Esc 关、点选即填。列表 = 引擎实际支持的函数(所见即所得),框架无关默认编辑器内置,三壳一致。
|
|
433
463
|
|
|
434
464
|
### 事件 = 前后完整快照
|
|
435
465
|
|
|
@@ -438,250 +468,13 @@ const myEditor: EditorResolver = (cell, pos) => {
|
|
|
438
468
|
### v1 已知限制
|
|
439
469
|
|
|
440
470
|
- 增删行列**会自动重写公式引用**(`=A5` 上方插一行 → `=A6`,删被引用行 → `#REF!`,含跨表 `Sheet1!A5`)。
|
|
441
|
-
- 写回 .xlsx 默认从模型重建,丢 VBA/工作表保护/复杂 DrawingML 等;需要更高保真可用 `exportXlsx({ fidelity: 'overlay' })` **重载原件叠加编辑**(见 [导出保真边界](#导出--打印))。
|
|
442
|
-
|
|
443
|
-
## 扩展 API(不改源码定制)
|
|
444
|
-
|
|
445
|
-
组件按"分层可扩展"设计 —— 用内置 props/events/slots/命令式 API 即可定制外观、行为、数据,并在网格上叠自己的 UI。
|
|
471
|
+
- 写回 .xlsx 默认从模型重建,丢 VBA/工作表保护/复杂 DrawingML 等;需要更高保真可用 `exportXlsx({ fidelity: 'overlay' })` **重载原件叠加编辑**(见 [导出保真边界](EXTENDING.md#导出--打印))。
|
|
446
472
|
|
|
447
|
-
|
|
448
|
-
```vue
|
|
449
|
-
<ExcelViewer :src="file" :theme="{ gridLine: '#e8e8e8', selBorder: '#e91e63', selFill: 'rgba(233,30,99,.1)' }" />
|
|
450
|
-
```
|
|
451
|
-
可覆盖:`headerBg / headerText / headerLine / gridLine / selBorder / selFill`(见 `ViewerTheme` / `DEFAULT_THEME` 导出)。
|
|
473
|
+
## 扩展 / 二开
|
|
452
474
|
|
|
453
|
-
|
|
454
|
-
```vue
|
|
455
|
-
<ExcelViewer
|
|
456
|
-
:src="file"
|
|
457
|
-
:transform-model="(wb) => { wb.sheets[0].name = '改过的名字'; return wb }"
|
|
458
|
-
:cell-style="(cell) => typeof cell.raw === 'number' && cell.raw < 0 ? { font: { color: '#d00' } } : undefined"
|
|
459
|
-
/>
|
|
460
|
-
```
|
|
461
|
-
- `transformModel(wb)`:解析后、渲染前改模型(返回新模型或就地改)。
|
|
462
|
-
- `cellStyle(cell, {row,col})`:按条件覆盖单元格样式(`font/fill/borders` 浅合并)。
|
|
475
|
+
不改源码就能定制外观(`:theme`)、数据/渲染钩子(`transformModel`/`cellStyle`)、自定义编辑器(`:editor`)、右键菜单 transform、操作工具栏自定义项、分层 UI slots、命令式 API、导出/打印高级选项、以及**插件**(`definePlugin` 打包多种扩展点、跨框架可用)—— 完整 API 见 **[EXTENDING.md(二开 / 扩展 API 手册)](EXTENDING.md)**。
|
|
463
476
|
|
|
464
|
-
|
|
465
|
-
| 事件 | 载荷 |
|
|
466
|
-
|---|---|
|
|
467
|
-
| `cell-click` / `cell-dblclick` | `{ row, col, text }` |
|
|
468
|
-
| `selection-change` | `{ range, active }` |
|
|
469
|
-
| `sheet-change` | `{ index, name }` |
|
|
470
|
-
| `hyperlink-click` | `{ url, cell }`(配 `:open-links="false"` 接管跳转) |
|
|
471
|
-
| `rendered` / `error` / `progress` | 见上 |
|
|
472
|
-
|
|
473
|
-
### 命令式 API(模板 ref)
|
|
474
|
-
`load(src)` / `getWorkbook()` / `getActiveSheet()` / `setActiveSheet(i)` / `getSelection()` / `setSelection(range)` / `scrollToCell(row,col,{select?})` / `rectOf(row,col)` / `rectOfRange(range)` / `redraw()`,以及下面的导出方法;**编辑命令式 API**(`editCell`/`setStyle`/`createPivotTable`/`openPivotTableDialog`/`createPivotTableFromSelection`/`getConditionalRules`/`addConditionalRule`/`openConditionalFormatDialog`/`setSelectionNumberFormat`/`openNumberFormatDialog`/`getCellComment`/`setCellComment`/`openCommentEditor`/`replaceCurrent`/`replaceAll`/`insertRows`/`undo`/`exportXlsx`…)见 [编辑](#编辑可选默认只读)。
|
|
475
|
-
|
|
476
|
-
```ts
|
|
477
|
-
// 需组件开启 :pivot-table="true"(默认关闭)+ :editable="true"
|
|
478
|
-
viewer.value?.createPivotTable({
|
|
479
|
-
sourceRange: { top: 0, left: 0, bottom: 20, right: 4 },
|
|
480
|
-
output: { kind: 'new-sheet' },
|
|
481
|
-
layout: {
|
|
482
|
-
rows: [0],
|
|
483
|
-
columns: [1],
|
|
484
|
-
filters: [{ field: 2, mode: 'equals', value: '华东' }],
|
|
485
|
-
values: [{ field: 3, summary: 'sum' }],
|
|
486
|
-
},
|
|
487
|
-
})
|
|
488
|
-
```
|
|
489
|
-
|
|
490
|
-
### 导出 / 打印
|
|
491
|
-
内置工具栏右侧有「导出 ▾」菜单(PNG / PDF / 打印 / **导出设置…**)。「导出设置…」打开对话框,可选**范围**(当前选区 / 当前表 / 全部表)、清晰度、是否含行列号/网格线、纸张方向。也可命令式调用(模板 ref / 插件 `viewer`):
|
|
492
|
-
|
|
493
|
-
| 方法 | 说明 |
|
|
494
|
-
|---|---|
|
|
495
|
-
| `exportImage(opts?)` | → `Promise<Blob>`,当前/指定表渲染为图片(png/jpeg/webp) |
|
|
496
|
-
| `downloadImage(opts?)` | 导出图片并触发下载 |
|
|
497
|
-
| `exportPdf(opts?)` | → `Promise<Blob>`,分页 PDF(需可选依赖 `jspdf`) |
|
|
498
|
-
| `downloadPdf(opts?)` | 导出 PDF 并触发下载 |
|
|
499
|
-
| `print(opts?)` | 打开系统打印对话框(可另存为 PDF,零依赖) |
|
|
500
|
-
| `exportXlsx(opts?)` / `downloadXlsx(opts?)` | → `Promise<Blob>` / 下载 **.xlsx**(默认从模型重建;`{fidelity:'overlay'}` 重载原件叠加,保真更高;需可选依赖 `exceljs`) |
|
|
501
|
-
| `exportJson(opts?)` / `downloadJson(opts?)` | → `string` / 下载 **.json**(各表首行作 key 的对象数组,raw 类型值) |
|
|
502
|
-
| `exportCsv(opts?)` / `downloadCsv(opts?)` | → `string` / 下载 **.csv**(格式化显示值,带 UTF-8 BOM;`opts.target` 指定表,默认当前表) |
|
|
503
|
-
|
|
504
|
-
**编辑后导出(.xlsx / JSON / CSV)** —— 三种格式都建在**同一份内存数据层**上(`WorkbookModel`:读 `data-access` + 写 `mutations`),无需为每种格式各写一遍解析,故与渲染所见、彼此之间天然一致。JSON 默认输出 raw 类型值(`{format:true}` 可改显示串);CSV 默认输出格式化显示值(WYSIWYG)。
|
|
505
|
-
|
|
506
|
-
**.xlsx 两种保真模式**:
|
|
507
|
-
|
|
508
|
-
- **`rebuild`(默认)** —— **从编辑后模型完整重建**:遍历 cells/公式/样式(字体/填充/边框/对齐/数字格式)/合并/行高列宽/冻结/图片/**条件格式**(1.9.0 起)/**批注**(1.11.0 起) 重组成 ExcelJS 工作簿。干净、所见即所得,但**丢失**原件里我们不建模的部分(数据验证、VBA 宏、工作表保护、复杂 DrawingML/图表 等)。图片导出区分 oneCell/twoCell 锚点 + 子格 EMU 偏移。
|
|
509
|
-
- **`overlay`(`exportXlsx({ fidelity: 'overlay' })`)** —— **重载原始 .xlsx,只把编辑后的 值/样式/合并/行高列宽/冻结 叠加上去**,**保留** ExcelJS 能往返的其余部分(条件格式 / 数据验证 / 打印设置 / 定义名 / 图表 等)。组件加载时自动留存原件字节供其使用;缺原件时自动回退 `rebuild`。注:overlay 不反映**增删行列 / 图片**编辑(那类用 `rebuild`)。
|
|
510
|
-
|
|
511
|
-
公共选项:`target`(`'active'`(默认)/`'all'`/索引/索引数组)、`range`(限定单元格区域)、`scale`(清晰度,默认 2)、`includeHeaders`、`gridlines`、`background`;PDF/打印另有 `format`(a4/a3/letter/`[宽,高]mm`)、`orientation`、`margin`(mm)、`fitToWidth`。
|
|
512
|
-
|
|
513
|
-
**长任务进度 + 取消 + 内置遮罩(P1 + P1.5)** —— **两层**叠加,默认开箱即用,可逐层覆盖:
|
|
514
|
-
|
|
515
|
-
#### ① Core 层(协议):`onProgress` + `signal`
|
|
516
|
-
所有导出方法(PNG / PDF / XLSX / Print)+ 选区图片批量互转 统一接:
|
|
517
|
-
```ts
|
|
518
|
-
const ctrl = new AbortController()
|
|
519
|
-
try {
|
|
520
|
-
await viewer.value.downloadPdf({
|
|
521
|
-
target: 'all',
|
|
522
|
-
onProgress: (p) => console.log(p.stage, p.ratio, p.label), // 'render'/'compose'/'paginate'/'write'/'zip'/'convert'
|
|
523
|
-
signal: ctrl.signal,
|
|
524
|
-
})
|
|
525
|
-
} catch (e) {
|
|
526
|
-
if ((e as Error).name === 'AbortError') console.log('用户取消')
|
|
527
|
-
else throw e
|
|
528
|
-
}
|
|
529
|
-
ctrl.abort() // 任意时刻取消
|
|
530
|
-
```
|
|
531
|
-
导出全链路在调度点 `await yieldToEvent()` 让出 UI(防假死)+ 调度前 `checkAborted(signal)`(立刻中断)。`ExcelJS.writeBuffer` / `jsPDF` 内部仍是黑盒(`zip`/`write` 阶段),那两段无法细分,但全程都有可视进度。
|
|
532
|
-
|
|
533
|
-
#### ② Shell 层(UI):**内置居中模态**(默认开)
|
|
534
|
-
**不传任何参数**调 `viewer.downloadPdf()` 等异步方法,壳自动建 `AbortController` + 接 `onProgress` → 显示**居中模态**(stage 标签 + 进度条 + 取消按钮)。用户传入的 `onProgress`/`signal` **仍正常链回调**(并存,不冲突)。
|
|
535
|
-
|
|
536
|
-
#### ③ 关闭 / 覆盖
|
|
537
|
-
| 需求 | 做法 |
|
|
538
|
-
|---|---|
|
|
539
|
-
| 完全关掉内置遮罩(纯回调) | `<ExcelViewer :export-progress="false">`(Vue)/ `exportProgress={false}` (React) |
|
|
540
|
-
| 自渲染(用 Element Plus / Ant Design 等自家组件) | **Vue**:`<template #export-progress="{ state, busy, cancel }">…</template>` 插槽;**React**:`renderExportProgress={({state,busy,cancel}) => <YourModal …/>}` |
|
|
541
|
-
| 既要内置又自动注入跟踪 | 默认行为已是 —— 用户传 `{ onProgress, signal }` 仍被链回调,内置 UI 也照常显示 |
|
|
542
|
-
|
|
543
|
-
覆盖矩阵:**导出**(PDF / PNG / XLSX / Print)、**选区图片批量互转**(P2,壳侧 1.2.0 起返 `Promise<number>` 以接遮罩)。**不包含**:文件解析(parsing 有独立的顶栏进度条,与本遮罩分开)、`copySelection` / `setStyle` 等瞬时操作、模板样式 overlay(P3 重设计后是同步纯函数,耗时可忽略)。
|
|
544
|
-
|
|
545
|
-
**默认还原 OOXML 原生页面设置** —— PDF/打印时,未显式指定的 `format`/`orientation`/`margin`/`fitToWidth` 自动取自工作表的 `pageSetup`(纸张、方向、页边距、适应页面/缩放),并应用**打印区域**(默认导出范围)与**打印标题行/列**(每页顶部/左侧重复)。显式传入的选项始终覆盖之。
|
|
546
|
-
|
|
547
|
-
**分页** —— `fitToWidth: true`(默认)把整表缩放到页宽、只竖向跨页;`fitToWidth: false`(或工作表未设"适应页面")按自然尺寸×缩放,**宽表横向跨页 + 高表竖向跨页**(像 Excel 的页矩阵,顺序"先下后右"),此时打印标题列在每张横向页左侧重复。
|
|
548
|
-
|
|
549
|
-
**`beforeRenderPage` 扩展钩子** —— 每页贴图后调用,拿到 `jsPDF` 实例画页眉/页脚/水印/页码:
|
|
550
|
-
```ts
|
|
551
|
-
const viewer = ref() // <ExcelViewer ref="viewer" />
|
|
552
|
-
await viewer.value.downloadPdf({
|
|
553
|
-
target: 'all',
|
|
554
|
-
beforeRenderPage: ({ doc, pageIndex, pageCount, pageWidth, pageHeight, margin, sheetName }) => {
|
|
555
|
-
doc.setFontSize(9); doc.setTextColor(120)
|
|
556
|
-
doc.text(sheetName, margin.left, pageHeight - 5)
|
|
557
|
-
doc.text(`第 ${pageIndex + 1} / ${pageCount} 页`, pageWidth - margin.right, pageHeight - 5, { align: 'right' })
|
|
558
|
-
doc.setFontSize(56); doc.setTextColor(230)
|
|
559
|
-
doc.text('PREVIEW', pageWidth / 2, pageHeight / 2, { align: 'center', angle: 30 }) // 水印
|
|
560
|
-
},
|
|
561
|
-
})
|
|
562
|
-
```
|
|
563
|
-
打印另有 `title` / `headerHtml` / `footerHtml`(每页 HTML 片段)。
|
|
564
|
-
> 图片/图表/形状是 DOM 叠加层,导出时会自动合成到底图;"导出全部表"中非当前表的图表需 `echarts` 可用才能离屏渲染。
|
|
565
|
-
|
|
566
|
-
#### 矢量 PDF(文字可选可搜)
|
|
567
|
-
|
|
568
|
-
两种 PDF 并存,工具栏菜单有「位图 / 矢量」两项,API 用 `vector` 切换:
|
|
569
|
-
```ts
|
|
570
|
-
await viewer.value.downloadPdf({ vector: true })
|
|
571
|
-
```
|
|
572
|
-
- **位图 PDF**(默认):整表贴图,完整还原观感。
|
|
573
|
-
- **矢量 PDF**:逐格用真文字 + 矢量填充/边框绘制 —— 文字**可选中、可搜索、放大清晰、文件更小**。条件格式(背景色/数据条/图标)也走矢量绘制;仅**迷你图、旋转文字、富文本**这几类格会自动从底图**裁小图兜底**(内容不丢)。
|
|
574
|
-
|
|
575
|
-
**中文字体** —— jsPDF 内置字体只认拉丁/数字。用 `configureDoc(doc)` 钩子注册中文 TTF 即可全矢量;不注册时,含中文的单元格自动转为该格小图(清晰但不可选):
|
|
576
|
-
```ts
|
|
577
|
-
await viewer.value.downloadPdf({
|
|
578
|
-
vector: true,
|
|
579
|
-
configureDoc: (doc) => {
|
|
580
|
-
doc.addFileToVFS('NotoSansSC.ttf', base64Ttf) // 你的中文字体(建议子集化)
|
|
581
|
-
doc.addFont('NotoSansSC.ttf', 'NotoSC', 'normal')
|
|
582
|
-
doc.setFont('NotoSC') // 设为默认 → 中文也走矢量
|
|
583
|
-
},
|
|
584
|
-
})
|
|
585
|
-
```
|
|
586
|
-
> 提示:中文表格若不注册字体,矢量模式会产生很多小图、文件偏大且较慢 —— 注册一个子集字体即可全矢量。
|
|
587
|
-
|
|
588
|
-
### 右键菜单(Plan C:三层开放)
|
|
589
|
-
|
|
590
|
-
默认 `editable` 时显示内置菜单(复制/粘贴/插入/删除/合并/拆分/自动换行/清除内容 + WPS 图片互转)。三种覆盖方式可同时使用:
|
|
591
|
-
|
|
592
|
-
**① 加 / 减 / 重排内置项** —— 用 `:contextMenu` 传 transform:
|
|
593
|
-
```vue
|
|
594
|
-
<ExcelViewer
|
|
595
|
-
:context-menu="(ctx, items) => [
|
|
596
|
-
...items,
|
|
597
|
-
{ separator: true },
|
|
598
|
-
{ label: `导出此格 PDF (${ctx.activeCell.row + 1},${ctx.activeCell.col + 1})`, action: () => viewer.downloadPdf() },
|
|
599
|
-
]"
|
|
600
|
-
/>
|
|
601
|
-
```
|
|
602
|
-
- `ctx`: `{ range, single, activeCell, sheet, workbook, editable }` — 当前选区 + 活动格 + 模型句柄
|
|
603
|
-
- `items`: 内置 `MenuItem[]`(`{label, action, disabled, separator}`)—— 加、过滤、重排,返回新数组生效;返 `undefined` / `void` 用原样
|
|
604
|
-
|
|
605
|
-
**② 接管渲染**(用自家 UI 框架的菜单,如 Element Plus / Radix / Headless UI):
|
|
606
|
-
```vue
|
|
607
|
-
<ExcelViewer
|
|
608
|
-
:context-menu="false" <!-- 关闭内置弹层(事件仍触发) -->
|
|
609
|
-
@before-context-menu="(p) => p.preventDefault()"
|
|
610
|
-
@context-menu="(p) => myMenu.show(p.x, p.y, p.items)"
|
|
611
|
-
/>
|
|
612
|
-
```
|
|
613
|
-
- `@before-context-menu` 在内置弹出前触发;调 `payload.preventDefault()` 取消内置;`:contextMenu="false"` 等价于自动 preventDefault
|
|
614
|
-
- `@context-menu` 在内置弹出后(或被 prevent 后)触发,拿到 `{ x, y, ctx, items }` —— **总会触发**,自渲染只需监听这个事件
|
|
615
|
-
- React:`onBeforeContextMenu` / `onContextMenuShow` 接同形 payload
|
|
616
|
-
|
|
617
|
-
**③ 命令式打开 / 关闭**(键盘 Shift+F10、工具栏触发、跨层调用):
|
|
618
|
-
```ts
|
|
619
|
-
viewer.openContextMenu(clickX, clickY) // 按当前选区算内置 items
|
|
620
|
-
viewer.openContextMenu(clickX, clickY, customItems) // 直接喂自定义 items
|
|
621
|
-
viewer.closeContextMenu()
|
|
622
|
-
```
|
|
623
|
-
|
|
624
|
-
**插件贡献**:`definePlugin({ contextMenu: (ctx, items) => [...] })` —— 多插件按数组顺序串行(后者拿前者输出),组件 `:contextMenu` prop 最后覆盖,顺序固定 `内置 → 插件 → prop`。
|
|
625
|
-
|
|
626
|
-
`MenuItem` / `ContextMenuCtx` / `ContextMenuTransform` 全部从 `ooxml-excel-editor/core` 导出(TS 类型完整)。
|
|
627
|
-
|
|
628
|
-
### 操作工具栏(可配置 / 可插件 / 响应式)
|
|
629
|
-
顶栏(文件名/导出/缩放)下方有一行**操作工具栏**,内置 `find`/`filter` 按钮默认显示。用 `:toolbar` 配置:
|
|
630
|
-
```vue
|
|
631
|
-
<ExcelViewer :src="file" /> <!-- 默认: find + filter + sort -->
|
|
632
|
-
<ExcelViewer :toolbar="['find','filter','separator','zoom','export']" /> <!-- 控制项/顺序/分隔 -->
|
|
633
|
-
<ExcelViewer :toolbar="false" /> <!-- 隐藏整条 -->
|
|
634
|
-
```
|
|
635
|
-
- **内置 id**:`find`(查找)、`filter`(切换自动筛选 —— 文件没设也能点出下拉)、`sort`(按活动单元格所在列升序/降序;未开启自动筛选时会先按选区/已用区建立范围)、`clear-filter`(清除筛选,无筛选时禁用)、`copy`(复制选区)、`pivot-table`(透视表入口:选中带表头数据区后选择生成位置,可输出到现有工作表单元格或新建工作表;创建后打开 WPS 风格右侧字段面板,需 `pivotTable` + `editable`,功能未开启时不渲染)、`conditional-format`(条件格式管理入口:列出当前表规则可删/可编辑 + 新建全 6 类规则,需 `conditionalFormat` + `editable`,功能未开启时不渲染)、`number-format`(数字格式编辑入口:分类 + 预览 + 自定义格式代码,需 `editable`)、`wrap-text`(自动换行 toggle,WPS 风格,需 `editable`)、`image-tools`(图片工具 ▾:选区/整表/整列 浮动 ⇄ 嵌入互转,需 `editable`)、`template`(模板 ▾:仅 JSON / 模型数据源下生效;导入 .xlsx 当样式捐赠者;xlsx 数据源下禁用)、`freeze`(冻结/取消)、`zoom`(缩放下拉)、`export`(导出/打印下拉)、`'separator'`/`'|'`(分隔线)。
|
|
636
|
-
- **富项类型**(`ToolbarItem`):`type:'separator'` 分隔线;`items: ToolbarItem[]` 变下拉子菜单;`disabled?(viewer)` 禁用态;`iconSvg`(内联 SVG,优先于 `icon` emoji)/ `icon` / `label` / `title` / `onClick(viewer)` / `active?(viewer)`。
|
|
637
|
-
- **响应式溢出**:宽度不足时,放不下的项自动折叠进「⋯ 更多」下拉。
|
|
638
|
-
- **插件贡献**:`ExcelPlugin.toolbar: ToolbarItem[]`,插件加载即追加(opt-in)。
|
|
639
|
-
- 内置图标用极简线性 **SVG**(跨平台一致);`filter` 按钮让筛选**看得见**,不必依赖文件自带 autofilter。
|
|
640
|
-
|
|
641
|
-
### 分层 UI(slots)
|
|
642
|
-
具名 slot:`header`(顶栏)/ `toolbar`(作用域 `{ items }`,替换整条操作栏)/ `statusbar` / `loading` / `error` / `empty`(缺省用内置)。
|
|
643
|
-
**作用域 `overlay` slot** —— 在格子上叠自己的 Vue 组件,随滚动/缩放跟随:
|
|
644
|
-
```vue
|
|
645
|
-
<ExcelViewer :src="file">
|
|
646
|
-
<template #overlay="{ rectOf, tick }">
|
|
647
|
-
<!-- tick 变化触发重算;rectOf(row,col) 给当前屏幕矩形 -->
|
|
648
|
-
<button v-if="rectOf(2,1)" :style="posStyle(rectOf(2,1), tick)" @click="...">★</button>
|
|
649
|
-
</template>
|
|
650
|
-
</ExcelViewer>
|
|
651
|
-
```
|
|
652
|
-
覆盖层容器 `pointer-events:none`(滚动穿透),子元素自动 `pointer-events:auto`(可点)。
|
|
653
|
-
|
|
654
|
-
### 插件 `definePlugin`
|
|
655
|
-
把上面所有扩展点(主题/数据钩子/渲染钩子/事件/overlay/命令式 API)打包成一个插件,`:plugins` 分发;多个插件按数组顺序合并,组件自身 props 最后覆盖。
|
|
656
|
-
```ts
|
|
657
|
-
import { definePlugin } from 'ooxml-excel-editor'
|
|
658
|
-
|
|
659
|
-
const highlightNegatives = definePlugin({
|
|
660
|
-
name: 'highlight-negatives',
|
|
661
|
-
theme: { selBorder: '#e91e63' },
|
|
662
|
-
cellStyle: (c) => (typeof c.raw === 'number' && c.raw < 0 ? { font: { color: '#d00' } } : undefined),
|
|
663
|
-
events: { 'cell-click': (p) => console.log('clicked', p) },
|
|
664
|
-
overlay: ({ rectOf }) => {
|
|
665
|
-
const r = rectOf(0, 0)
|
|
666
|
-
if (!r) return null
|
|
667
|
-
const el = document.createElement('div') // 返回 DOM(框架无关,Vue/React 通用)
|
|
668
|
-
el.textContent = '⚑'
|
|
669
|
-
Object.assign(el.style, { position: 'absolute', left: r.x + 'px', top: r.y + 'px' })
|
|
670
|
-
return el
|
|
671
|
-
},
|
|
672
|
-
setup: ({ viewer, on }) => {
|
|
673
|
-
on('selection-change', (s) => console.log(s))
|
|
674
|
-
// viewer.setSelection(...) / viewer.getWorkbook() ...
|
|
675
|
-
return () => {/* 清理 */}
|
|
676
|
-
},
|
|
677
|
-
})
|
|
678
|
-
```
|
|
679
|
-
```vue
|
|
680
|
-
<ExcelViewer :src="file" :plugins="[highlightNegatives]" />
|
|
681
|
-
```
|
|
682
|
-
插件字段:`theme` / `transformModel` / `cellStyle` / `events`(事件→处理器) / `overlay`(返回 **DOM 节点**,随滚动跟随) / `toolbar`(贡献操作栏按钮 `ToolbarItem[]`) / `setup(ctx)`(拿 `viewer` 命令式 API、`on()` 订阅事件,返回可选清理函数)。
|
|
683
|
-
|
|
684
|
-
> **跨框架**:插件全字段框架无关,**同一份 `definePlugin` 在 Vue 和 React 壳通用**(`overlay` 返回 DOM 而非 VNode)。React 用法:`<ExcelViewer plugins={[myPlugin]} />`。
|
|
477
|
+
想了解内部结构 / 在哪改代码:见 **[ARCHITECTURE.md](ARCHITECTURE.md)**。
|
|
685
478
|
|
|
686
479
|
## 浏览器支持
|
|
687
480
|
|
|
@@ -717,13 +510,14 @@ npm run build:demo # 构建 demo 站点
|
|
|
717
510
|
|
|
718
511
|
## 文档 / 二开
|
|
719
512
|
|
|
513
|
+
- **[EXTENDING.md](./EXTENDING.md) —— 二开 / 扩展 API 手册**(主题 `:theme` / 数据·渲染钩子 / 自定义编辑器 / 右键菜单 transform / 工具栏自定义项 / 分层 UI slots / 命令式 API / 导出·打印高级选项 / 插件 `definePlugin`)
|
|
720
514
|
- [ARCHITECTURE.md](./ARCHITECTURE.md) —— 包/入口、core 分层、数据流、`ViewerController` 桥接、"加功能改哪"
|
|
721
515
|
- [CONTRIBUTING.md](./CONTRIBUTING.md) —— 本地跑通、改动流程、不可破坏的硬约束
|
|
722
516
|
- [CHANGELOG.md](./CHANGELOG.md) / [RELEASING.md](./RELEASING.md) —— 变更记录 / 发布清单
|
|
723
517
|
- [docs/编辑权限与只读边界.md](./docs/编辑权限与只读边界.md) —— **EditableTarget 白名单 / DimTarget 尺寸多形态 / readOnlyCellStyle 视觉钩子 / permission-denied 事件** 体系化说明(1.2.0)
|
|
724
518
|
- [docs/Vue2.md](./docs/Vue2.md) —— **Vue 2 兼容子入口**完整文档(1.3.0;`ooxml-excel-editor/vue2` 跟 Vue 3 / React 壳 ~100% 功能对齐)
|
|
725
519
|
|
|
726
|
-
> **React props/events** 与 Vue 对齐(事件用 camelCase 回调:`onRendered`/`onError`/`onCellClick`/`onSelectionChange`/`onSheetChange`/`onHyperlinkClick`),命令式句柄 `ExcelViewerHandle` 与 Vue 组件 ref
|
|
520
|
+
> **React props/events** 与 Vue 对齐(事件用 camelCase 回调:`onRendered`/`onError`/`onCellClick`/`onSelectionChange`/`onSheetChange`/`onHyperlinkClick`),命令式句柄 `ExcelViewerHandle` 与 Vue 组件 ref 同名方法一致。[EXTENDING.md](./EXTENDING.md) 里的**插件 `definePlugin`** 目前服务 Vue 壳;React 壳已可用全部 props/命令式 API/事件,插件 overlay 跨框架化在路线图中。
|
|
727
521
|
|
|
728
522
|
## License
|
|
729
523
|
|
|
@@ -734,7 +528,9 @@ MIT
|
|
|
734
528
|
<a name="english"></a>
|
|
735
529
|
## English
|
|
736
530
|
|
|
737
|
-
|
|
531
|
+
**🔗 Live demo: https://ojadeveloper.github.io/ooxml-excel-editor/**
|
|
532
|
+
|
|
533
|
+
A **Vue 3 + Vue 2 + React high-fidelity `.xlsx` preview & edit component** with a from-scratch parser and canvas renderer. Renders cells, number formats, merges, conditional formatting, images, charts (via ECharts), sparklines, comments, data validation, frozen panes, and supports selection / copy / hyperlinks. **Read-only by default**; set `editable` to enable editing — cell values / styles / column-row sizes / floating images / insert-delete rows-cols, with undo-redo, before/after full-snapshot events, dirty tracking + reset-to-original, swappable formula recalc engine, custom cell editors, and **export back to .xlsx / JSON / CSV**. Parsing runs in a Web Worker (with main-thread fallback). `vue` / `react` / `exceljs` are peer dependencies; `echarts` / `jspdf` / `hyperformula` are optional peers (charts / PDF / formula recalc).
|
|
738
534
|
|
|
739
535
|
```bash
|
|
740
536
|
npm i ooxml-excel-editor vue exceljs
|