@bireturn/word-editor 1.0.3 → 1.0.4
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/README.md +298 -298
- package/package.json +55 -55
- package/lib/components/VirtualTable/index.d.ts +0 -19
- package/lib/components/VirtualTable/src/context-menu.d.ts +0 -16
- package/lib/components/VirtualTable/src/tool.d.ts +0 -15
- package/lib/composables/useDocumentData.d.ts +0 -10
- package/lib/composables/useEditor.d.ts +0 -5
- package/lib/core/command/CommandManager.d.ts +0 -16
- package/lib/core/command/commands/DeleteCommand.d.ts +0 -14
- package/lib/core/command/commands/FormatCommand.d.ts +0 -30
- package/lib/core/command/commands/InsertParagraphCommand.d.ts +0 -12
- package/lib/core/command/commands/InsertTextCommand.d.ts +0 -18
- package/lib/core/editor/Editor.d.ts +0 -46
- package/lib/core/editor/EditorOptions.d.ts +0 -1
- package/lib/core/history/HistoryManager.d.ts +0 -15
- package/lib/core/input/InputHandler.d.ts +0 -20
- package/lib/core/model/DocumentModel.d.ts +0 -38
- package/lib/core/selection/SelectionManager.d.ts +0 -26
- package/lib/index.d.ts +0 -10
- package/lib/types/command.d.ts +0 -57
- package/lib/types/document.d.ts +0 -105
- package/lib/types/index.d.ts +0 -4
- package/lib/types/plugin.d.ts +0 -12
- package/lib/types/selection.d.ts +0 -17
- package/lib/utils/catalog.d.ts +0 -29
- package/lib/utils/nanoid.d.ts +0 -1
- package/lib/utils/titleIndex.d.ts +0 -11
- package/lib/word-editor.css +0 -1
- package/lib/word-editor.es.js +0 -2819
- package/lib/word-editor.umd.js +0 -2
package/README.md
CHANGED
|
@@ -1,298 +1,298 @@
|
|
|
1
|
-
# @bireturn/word-editor
|
|
2
|
-
|
|
3
|
-
类 Word 的 Web 富文本编辑器,目前只实现了只读渲染功能,基于 Vue 3 + Vite + TypeScript。参考 [canvas-editor](https://github.com/Hufe921/canvas-editor) 的数据结构与命令系统设计,但使用 **DOM 渲染**代替 Canvas,并通过虚拟滚动支撑大文档。
|
|
4
|
-
|
|
5
|
-
## 安装
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install @bireturn/word-editor
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
> 需宿主项目提供 `vue ^3.4`(已声明为 `peerDependency`,避免重复打包与多实例)。
|
|
12
|
-
|
|
13
|
-
## 使用
|
|
14
|
-
|
|
15
|
-
数据由使用方通过 `props` 传入,组件本身不发请求:
|
|
16
|
-
|
|
17
|
-
```vue
|
|
18
|
-
<script setup>
|
|
19
|
-
import { EditorContainer } from '@bireturn/word-editor'
|
|
20
|
-
import '@bireturn/word-editor/style.css' // 必须引入样式
|
|
21
|
-
import { ref, onMounted } from 'vue'
|
|
22
|
-
|
|
23
|
-
const docData = ref([]) // IElement[],来源由使用方决定
|
|
24
|
-
const loading = ref(false)
|
|
25
|
-
|
|
26
|
-
onMounted(async () => {
|
|
27
|
-
loading.value = true
|
|
28
|
-
docData.value = await fetchDoc() // 使用方自己的数据源
|
|
29
|
-
loading.value = false
|
|
30
|
-
})
|
|
31
|
-
</script>
|
|
32
|
-
|
|
33
|
-
<template>
|
|
34
|
-
<EditorContainer :data="docData" :loading="loading" />
|
|
35
|
-
</template>
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
也可通过 `app.use()` 全局注册:
|
|
39
|
-
|
|
40
|
-
```ts
|
|
41
|
-
import WordEditor from '@bireturn/word-editor'
|
|
42
|
-
import '@bireturn/word-editor/style.css'
|
|
43
|
-
|
|
44
|
-
app.use(WordEditor) // 注册全局组件 <EditorContainer />
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### Props
|
|
48
|
-
|
|
49
|
-
| 名称 | 类型 | 默认值 | 说明 |
|
|
50
|
-
| --- | --- | --- | --- |
|
|
51
|
-
| `data` | `IElement[]` | `[]` | 文档结构数据,渲染所需的行内元素列表 |
|
|
52
|
-
| `loading` | `boolean` | `false` | 外部数据加载态,为 `true` 时显示加载占位 |
|
|
53
|
-
|
|
54
|
-
## 特性
|
|
55
|
-
|
|
56
|
-
- 数据驱动渲染:文档数据模型是唯一数据源,DOM 仅作视图
|
|
57
|
-
- 命令系统(Command Pattern):所有写操作经 `editor.exec()` 分发,统一支持撤销/重做
|
|
58
|
-
- 虚拟滚动:以段落为最小单元,配合自研 `VirtualTable` 支撑大文档
|
|
59
|
-
- A4 分页排版:按页面内容高度自动分页,支持页眉/页脚、连续图片与表格跨页拆分
|
|
60
|
-
- 富内容:文本、图片、超链接、表格、控件(文本/输入/单选)、上下标、复选框等
|
|
61
|
-
- 插件机制:通过 `editor.use()` 注入自定义命令与能力
|
|
62
|
-
|
|
63
|
-
## 技术栈
|
|
64
|
-
|
|
65
|
-
| 类别 | 选型 |
|
|
66
|
-
| --- | --- |
|
|
67
|
-
| 框架 | Vue 3(Composition API) |
|
|
68
|
-
| 构建 | Vite 5 + TypeScript 5 |
|
|
69
|
-
| 状态 | Pinia |
|
|
70
|
-
| 图标 | lucide-vue-next |
|
|
71
|
-
| 工具 | @vueuse/core、lodash-es |
|
|
72
|
-
| 虚拟滚动 | vue-virtual-scroller + 自研 VirtualTable |
|
|
73
|
-
| Mock | vite-plugin-mock-dev-server |
|
|
74
|
-
|
|
75
|
-
## 快速开始
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
npm install # 安装依赖
|
|
79
|
-
npm run dev # 启动开发服务器(默认 http://localhost:5173)
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
开发态接口由 `mock/` 提供,可完全离线运行,无需后端。
|
|
83
|
-
|
|
84
|
-
## 常用命令
|
|
85
|
-
|
|
86
|
-
```bash
|
|
87
|
-
npm run dev # 启动开发服务器
|
|
88
|
-
npm run build # 构建可部署的应用产物
|
|
89
|
-
npm run build:lib # 构建组件库产物(lib/,含 ESM/UMD + 类型声明)
|
|
90
|
-
npm run preview # 本地预览构建产物
|
|
91
|
-
npm run lint # ESLint 检查并自动修复
|
|
92
|
-
npm run type-check # 仅做 TypeScript 类型检查
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
## 构建与本地联调
|
|
96
|
-
|
|
97
|
-
本项目可同时作为**独立应用**与**组件库**使用。`vite.config.ts` 按 `mode` 区分两种产物:默认模式构建应用,`--mode lib`(即 `npm run build:lib`)构建组件库。
|
|
98
|
-
|
|
99
|
-
### 构建产物
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
npm run build:lib
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
产物输出到 `lib/`:
|
|
106
|
-
|
|
107
|
-
| 文件 | 说明 |
|
|
108
|
-
| --- | --- |
|
|
109
|
-
| `word-editor.es.js` | ESM 格式,供打包工具按需引入 |
|
|
110
|
-
| `word-editor.umd.js` | UMD 格式,供浏览器/旧环境直接引用 |
|
|
111
|
-
| `word-editor.css` | 组件样式,需由使用方手动引入 |
|
|
112
|
-
| `index.d.ts` | TypeScript 类型声明 |
|
|
113
|
-
|
|
114
|
-
`vue` 被声明为 `peerDependencies` 并在构建时外置,由使用方提供,避免重复打包与多实例问题。
|
|
115
|
-
|
|
116
|
-
### 本地联调(不发布到 npm)
|
|
117
|
-
|
|
118
|
-
构建后,在**消费方项目**通过本地路径安装,推荐 `file:` 方式:
|
|
119
|
-
|
|
120
|
-
```jsonc
|
|
121
|
-
// 消费方 package.json
|
|
122
|
-
{
|
|
123
|
-
"dependencies": {
|
|
124
|
-
"@bireturn/word-editor": "file:../word-editor" // 改成实际路径
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
```bash
|
|
130
|
-
npm install
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
改动本库后,重跑 `npm run build:lib` 并重启消费方 dev server 即可生效。也可用 `npm pack` 生成 `.tgz` 后安装(更贴近真实发布),或 `npm link`(改库实时联动)。
|
|
134
|
-
|
|
135
|
-
> ⚠️ 若用 `file:` / `npm link` 出现 Vue 响应式失效或 inject 报错,通常是加载了两份 Vue。在消费方 `vite.config.ts` 加 `resolve.dedupe: ['vue']` 与 `optimizeDeps.include: ['@bireturn/word-editor']` 去重即可。
|
|
136
|
-
|
|
137
|
-
### 发布到 npm
|
|
138
|
-
|
|
139
|
-
```bash
|
|
140
|
-
npm run build:lib # 先构建产物到 lib/
|
|
141
|
-
npm publish --access public # scoped 包首次发布需加 --access public
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
> 仅 `lib/` 会随包发布(由 `package.json` 的 `files` 字段约定)。
|
|
145
|
-
|
|
146
|
-
## Mock 数据
|
|
147
|
-
|
|
148
|
-
开发态接口基于 [vite-plugin-mock-dev-server](https://github.com/pengzhanbo/vite-plugin-mock-dev-server) 实现,配置见 [`mock/api.mock.ts`](mock/api.mock.ts),数据文件存放于 [`mock/json/`](mock/json/),可完全离线运行、无需后端。
|
|
149
|
-
|
|
150
|
-
### 接口约定
|
|
151
|
-
|
|
152
|
-
前端通过 [`src/api/file.ts`](src/api/file.ts) 的 `getStructInfo()` 请求 `/api/info`,期望返回文档结构数组 `IElement[]`(即 `DocumentModel` 渲染所需的行内元素列表)。
|
|
153
|
-
|
|
154
|
-
`mock/api.mock.ts` 中有两类挂载方式:
|
|
155
|
-
|
|
156
|
-
- **约定式**(`apiList`):`/api/xxx` 自动读取 `mock/json/xxx.json`,默认延迟 300ms
|
|
157
|
-
- **自定义式**(`customApis`):显式指定 url 与数据文件,可单独设置延迟。当前 `/api/info` 即在此挂载
|
|
158
|
-
|
|
159
|
-
### 切换测试数据
|
|
160
|
-
|
|
161
|
-
修改 `customApis` 中 `/api/info` 读取的 json 文件即可切换渲染数据,例如:
|
|
162
|
-
|
|
163
|
-
```ts
|
|
164
|
-
const customApis = [
|
|
165
|
-
{
|
|
166
|
-
url: '/api/info',
|
|
167
|
-
delay: 1000,
|
|
168
|
-
body: () =>
|
|
169
|
-
JSON.parse(
|
|
170
|
-
fs.readFileSync(path.resolve(root, `./mock/json/info.json`), 'utf8')
|
|
171
|
-
)
|
|
172
|
-
}
|
|
173
|
-
]
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### 数据文件清单
|
|
177
|
-
|
|
178
|
-
所有文件均为顶层 `IElement[]` 数组,元素以 `type` 区分(`text` / `table` / `image` / `hyperlink` / `control` / `list` 等)。
|
|
179
|
-
|
|
180
|
-
| 文件 | 体积 | 元素数 | 主要内容 | 用途 |
|
|
181
|
-
| --- | --- | --- | --- | --- |
|
|
182
|
-
| `info.json` | ~1.6 MB | 81 | 文本、9 个表格、控件、超链接 | 常规文档,功能演示基线 |
|
|
183
|
-
| `pagetable.json` | ~388 KB | 1 | 单个跨页大表格(77 行) | 表格分页与行渲染调试 |
|
|
184
|
-
| `page1000.json` | ~32 MB | 84 | 84 个表格 | 大量表格的分页/性能压测 |
|
|
185
|
-
| `page69.json` | ~85 MB | 2323 | 文本、198 表格、列表、控件 | 超大文档虚拟滚动压测 |
|
|
186
|
-
| `page69i.json` | ~85 MB | 2344 | 在 `page69` 基础上增加 18 张图片 | 含图片的超大文档压测 |
|
|
187
|
-
|
|
188
|
-
> 说明:`page69*` / `page1000` 体积较大,仅用于性能与边界场景验证,日常开发建议使用 `info.json` 或 `pagetable.json`。
|
|
189
|
-
|
|
190
|
-
### 元素数据结构
|
|
191
|
-
|
|
192
|
-
各 `type` 的字段含义见 [`docs/0.JSON数据类型说明.md`](docs/0.JSON数据类型说明.md) 与 [`docs/1.页面样式属性说明.md`](docs/1.页面样式属性说明.md)。
|
|
193
|
-
|
|
194
|
-
## 核心设计原则
|
|
195
|
-
|
|
196
|
-
1. **数据模型是唯一数据源**,DOM 只是视图,不存储状态
|
|
197
|
-
2. **contenteditable 仅用于输入捕获**,不用于渲染(透明 overlay 模式)
|
|
198
|
-
3. **所有写操作必须通过 `editor.exec()`**,不能直接修改 `docModel`
|
|
199
|
-
4. **大文档通过虚拟滚动**实现,段落是虚拟列表的最小单元
|
|
200
|
-
|
|
201
|
-
## 目录结构
|
|
202
|
-
|
|
203
|
-
```
|
|
204
|
-
src/
|
|
205
|
-
├── core/ # 纯 TypeScript,与 Vue 无关
|
|
206
|
-
│ ├── editor/Editor.ts # 编辑器主类,所有子系统的聚合入口
|
|
207
|
-
│ ├── model/DocumentModel.ts # 文档数据模型,所有原子修改方法在此
|
|
208
|
-
│ ├── command/ # 命令系统
|
|
209
|
-
│ │ ├── CommandManager.ts # exec() 分发,注册/执行命令
|
|
210
|
-
│ │ └── commands/ # InsertText / Delete / Format / InsertParagraph 等
|
|
211
|
-
│ ├── selection/SelectionManager.ts # 模型坐标系的光标/选区
|
|
212
|
-
│ ├── history/HistoryManager.ts # undo/redo 栈
|
|
213
|
-
│ └── input/InputHandler.ts # DOM 事件 → exec() 转换层
|
|
214
|
-
├── composables/
|
|
215
|
-
│ ├── useEditor.ts # provide/inject Editor 实例
|
|
216
|
-
│ └── useDocumentData.ts # 从 docModel 提取响应式数据
|
|
217
|
-
├── components/
|
|
218
|
-
│ ├── Editor/ # 编辑器根组件(含分页、表格、页眉页脚渲染)
|
|
219
|
-
│ └── VirtualTable/ # 自研虚拟表格组件
|
|
220
|
-
├── api/ # 接口请求封装
|
|
221
|
-
└── types/ # 全局类型(document/selection/command/plugin)
|
|
222
|
-
|
|
223
|
-
mock/ # vite mock,离线可用的接口数据
|
|
224
|
-
docs/ # 功能说明与问题分析文档
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
## 架构
|
|
228
|
-
|
|
229
|
-
### 数据流
|
|
230
|
-
|
|
231
|
-
```
|
|
232
|
-
用户输入(键盘/鼠标)
|
|
233
|
-
→ InputHandler(DOM 事件拦截)
|
|
234
|
-
→ editor.exec(commandName, payload)
|
|
235
|
-
→ CommandManager(查找命令工厂)
|
|
236
|
-
→ ICommand.execute()(修改 DocumentModel)
|
|
237
|
-
→ Vue 响应式自动触发重渲染
|
|
238
|
-
→ 组件根据最新 docModel 渲染 DOM
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
### 数据模型
|
|
242
|
-
|
|
243
|
-
```
|
|
244
|
-
DocumentModel
|
|
245
|
-
└── sections[] # 节(分页/分栏单位)
|
|
246
|
-
└── paragraphs[] # 段落(块级,虚拟滚动的最小单元)
|
|
247
|
-
└── children: IElement[] # 行内元素(文本/图片/超链接/表格等)
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
### 坐标系
|
|
251
|
-
|
|
252
|
-
所有位置用 `ModelPoint` 表示:
|
|
253
|
-
|
|
254
|
-
```ts
|
|
255
|
-
{ sectionIndex, paragraphIndex, elementIndex, offset }
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
DOM 元素通过 `data-si / data-pi / data-ei` 属性反向映射回模型坐标(见 `SelectionManager.domToModelPoint`)。
|
|
259
|
-
|
|
260
|
-
## 扩展
|
|
261
|
-
|
|
262
|
-
### 注册命令
|
|
263
|
-
|
|
264
|
-
在 `CommandManager._registerBuiltinCommands()` 中注册,或通过插件注入:
|
|
265
|
-
|
|
266
|
-
```ts
|
|
267
|
-
editor.commands.register('myCommand', (payload) => new MyCommand(...))
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
命令须实现 `ICommand`(`execute` + `undo`),可选实现 `canMergeWith` / `merge` 以支持连续输入合并。
|
|
271
|
-
|
|
272
|
-
### 插件
|
|
273
|
-
|
|
274
|
-
```ts
|
|
275
|
-
editor.use({
|
|
276
|
-
name: 'my-plugin',
|
|
277
|
-
install(editor) {
|
|
278
|
-
editor.commands.register('customCommand', ...)
|
|
279
|
-
}
|
|
280
|
-
})
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
### 新增元素类型
|
|
284
|
-
|
|
285
|
-
需同时更新:
|
|
286
|
-
|
|
287
|
-
- `src/types/document.ts`(类型定义)
|
|
288
|
-
- `src/components/Editor/index.vue`(渲染逻辑)
|
|
289
|
-
|
|
290
|
-
## 关键约束
|
|
291
|
-
|
|
292
|
-
- 禁止在渲染组件中直接修改 `docModel`
|
|
293
|
-
- 禁止在 `InputHandler` 中直接修改 `docModel`(必须通过 `exec`)
|
|
294
|
-
- `DocumentModel` 的原子方法(`insertText` / `deleteAt` 等)只能由 Command 调用
|
|
295
|
-
|
|
296
|
-
## 文档
|
|
297
|
-
|
|
298
|
-
更多功能说明与问题分析见 [`docs/`](docs/) 目录。
|
|
1
|
+
# @bireturn/word-editor
|
|
2
|
+
|
|
3
|
+
类 Word 的 Web 富文本编辑器,目前只实现了只读渲染功能,基于 Vue 3 + Vite + TypeScript。参考 [canvas-editor](https://github.com/Hufe921/canvas-editor) 的数据结构与命令系统设计,但使用 **DOM 渲染**代替 Canvas,并通过虚拟滚动支撑大文档。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @bireturn/word-editor
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
> 需宿主项目提供 `vue ^3.4`(已声明为 `peerDependency`,避免重复打包与多实例)。
|
|
12
|
+
|
|
13
|
+
## 使用
|
|
14
|
+
|
|
15
|
+
数据由使用方通过 `props` 传入,组件本身不发请求:
|
|
16
|
+
|
|
17
|
+
```vue
|
|
18
|
+
<script setup>
|
|
19
|
+
import { EditorContainer } from '@bireturn/word-editor'
|
|
20
|
+
import '@bireturn/word-editor/style.css' // 必须引入样式
|
|
21
|
+
import { ref, onMounted } from 'vue'
|
|
22
|
+
|
|
23
|
+
const docData = ref([]) // IElement[],来源由使用方决定
|
|
24
|
+
const loading = ref(false)
|
|
25
|
+
|
|
26
|
+
onMounted(async () => {
|
|
27
|
+
loading.value = true
|
|
28
|
+
docData.value = await fetchDoc() // 使用方自己的数据源
|
|
29
|
+
loading.value = false
|
|
30
|
+
})
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<EditorContainer :data="docData" :loading="loading" />
|
|
35
|
+
</template>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
也可通过 `app.use()` 全局注册:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import WordEditor from '@bireturn/word-editor'
|
|
42
|
+
import '@bireturn/word-editor/style.css'
|
|
43
|
+
|
|
44
|
+
app.use(WordEditor) // 注册全局组件 <EditorContainer />
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Props
|
|
48
|
+
|
|
49
|
+
| 名称 | 类型 | 默认值 | 说明 |
|
|
50
|
+
| --- | --- | --- | --- |
|
|
51
|
+
| `data` | `IElement[]` | `[]` | 文档结构数据,渲染所需的行内元素列表 |
|
|
52
|
+
| `loading` | `boolean` | `false` | 外部数据加载态,为 `true` 时显示加载占位 |
|
|
53
|
+
|
|
54
|
+
## 特性
|
|
55
|
+
|
|
56
|
+
- 数据驱动渲染:文档数据模型是唯一数据源,DOM 仅作视图
|
|
57
|
+
- 命令系统(Command Pattern):所有写操作经 `editor.exec()` 分发,统一支持撤销/重做
|
|
58
|
+
- 虚拟滚动:以段落为最小单元,配合自研 `VirtualTable` 支撑大文档
|
|
59
|
+
- A4 分页排版:按页面内容高度自动分页,支持页眉/页脚、连续图片与表格跨页拆分
|
|
60
|
+
- 富内容:文本、图片、超链接、表格、控件(文本/输入/单选)、上下标、复选框等
|
|
61
|
+
- 插件机制:通过 `editor.use()` 注入自定义命令与能力
|
|
62
|
+
|
|
63
|
+
## 技术栈
|
|
64
|
+
|
|
65
|
+
| 类别 | 选型 |
|
|
66
|
+
| --- | --- |
|
|
67
|
+
| 框架 | Vue 3(Composition API) |
|
|
68
|
+
| 构建 | Vite 5 + TypeScript 5 |
|
|
69
|
+
| 状态 | Pinia |
|
|
70
|
+
| 图标 | lucide-vue-next |
|
|
71
|
+
| 工具 | @vueuse/core、lodash-es |
|
|
72
|
+
| 虚拟滚动 | vue-virtual-scroller + 自研 VirtualTable |
|
|
73
|
+
| Mock | vite-plugin-mock-dev-server |
|
|
74
|
+
|
|
75
|
+
## 快速开始
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npm install # 安装依赖
|
|
79
|
+
npm run dev # 启动开发服务器(默认 http://localhost:5173)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
开发态接口由 `mock/` 提供,可完全离线运行,无需后端。
|
|
83
|
+
|
|
84
|
+
## 常用命令
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npm run dev # 启动开发服务器
|
|
88
|
+
npm run build # 构建可部署的应用产物
|
|
89
|
+
npm run build:lib # 构建组件库产物(lib/,含 ESM/UMD + 类型声明)
|
|
90
|
+
npm run preview # 本地预览构建产物
|
|
91
|
+
npm run lint # ESLint 检查并自动修复
|
|
92
|
+
npm run type-check # 仅做 TypeScript 类型检查
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## 构建与本地联调
|
|
96
|
+
|
|
97
|
+
本项目可同时作为**独立应用**与**组件库**使用。`vite.config.ts` 按 `mode` 区分两种产物:默认模式构建应用,`--mode lib`(即 `npm run build:lib`)构建组件库。
|
|
98
|
+
|
|
99
|
+
### 构建产物
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
npm run build:lib
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
产物输出到 `lib/`:
|
|
106
|
+
|
|
107
|
+
| 文件 | 说明 |
|
|
108
|
+
| --- | --- |
|
|
109
|
+
| `word-editor.es.js` | ESM 格式,供打包工具按需引入 |
|
|
110
|
+
| `word-editor.umd.js` | UMD 格式,供浏览器/旧环境直接引用 |
|
|
111
|
+
| `word-editor.css` | 组件样式,需由使用方手动引入 |
|
|
112
|
+
| `index.d.ts` | TypeScript 类型声明 |
|
|
113
|
+
|
|
114
|
+
`vue` 被声明为 `peerDependencies` 并在构建时外置,由使用方提供,避免重复打包与多实例问题。
|
|
115
|
+
|
|
116
|
+
### 本地联调(不发布到 npm)
|
|
117
|
+
|
|
118
|
+
构建后,在**消费方项目**通过本地路径安装,推荐 `file:` 方式:
|
|
119
|
+
|
|
120
|
+
```jsonc
|
|
121
|
+
// 消费方 package.json
|
|
122
|
+
{
|
|
123
|
+
"dependencies": {
|
|
124
|
+
"@bireturn/word-editor": "file:../word-editor" // 改成实际路径
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npm install
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
改动本库后,重跑 `npm run build:lib` 并重启消费方 dev server 即可生效。也可用 `npm pack` 生成 `.tgz` 后安装(更贴近真实发布),或 `npm link`(改库实时联动)。
|
|
134
|
+
|
|
135
|
+
> ⚠️ 若用 `file:` / `npm link` 出现 Vue 响应式失效或 inject 报错,通常是加载了两份 Vue。在消费方 `vite.config.ts` 加 `resolve.dedupe: ['vue']` 与 `optimizeDeps.include: ['@bireturn/word-editor']` 去重即可。
|
|
136
|
+
|
|
137
|
+
### 发布到 npm
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npm run build:lib # 先构建产物到 lib/
|
|
141
|
+
npm publish --access public # scoped 包首次发布需加 --access public
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
> 仅 `lib/` 会随包发布(由 `package.json` 的 `files` 字段约定)。
|
|
145
|
+
|
|
146
|
+
## Mock 数据
|
|
147
|
+
|
|
148
|
+
开发态接口基于 [vite-plugin-mock-dev-server](https://github.com/pengzhanbo/vite-plugin-mock-dev-server) 实现,配置见 [`mock/api.mock.ts`](mock/api.mock.ts),数据文件存放于 [`mock/json/`](mock/json/),可完全离线运行、无需后端。
|
|
149
|
+
|
|
150
|
+
### 接口约定
|
|
151
|
+
|
|
152
|
+
前端通过 [`src/api/file.ts`](src/api/file.ts) 的 `getStructInfo()` 请求 `/api/info`,期望返回文档结构数组 `IElement[]`(即 `DocumentModel` 渲染所需的行内元素列表)。
|
|
153
|
+
|
|
154
|
+
`mock/api.mock.ts` 中有两类挂载方式:
|
|
155
|
+
|
|
156
|
+
- **约定式**(`apiList`):`/api/xxx` 自动读取 `mock/json/xxx.json`,默认延迟 300ms
|
|
157
|
+
- **自定义式**(`customApis`):显式指定 url 与数据文件,可单独设置延迟。当前 `/api/info` 即在此挂载
|
|
158
|
+
|
|
159
|
+
### 切换测试数据
|
|
160
|
+
|
|
161
|
+
修改 `customApis` 中 `/api/info` 读取的 json 文件即可切换渲染数据,例如:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
const customApis = [
|
|
165
|
+
{
|
|
166
|
+
url: '/api/info',
|
|
167
|
+
delay: 1000,
|
|
168
|
+
body: () =>
|
|
169
|
+
JSON.parse(
|
|
170
|
+
fs.readFileSync(path.resolve(root, `./mock/json/info.json`), 'utf8')
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
]
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### 数据文件清单
|
|
177
|
+
|
|
178
|
+
所有文件均为顶层 `IElement[]` 数组,元素以 `type` 区分(`text` / `table` / `image` / `hyperlink` / `control` / `list` 等)。
|
|
179
|
+
|
|
180
|
+
| 文件 | 体积 | 元素数 | 主要内容 | 用途 |
|
|
181
|
+
| --- | --- | --- | --- | --- |
|
|
182
|
+
| `info.json` | ~1.6 MB | 81 | 文本、9 个表格、控件、超链接 | 常规文档,功能演示基线 |
|
|
183
|
+
| `pagetable.json` | ~388 KB | 1 | 单个跨页大表格(77 行) | 表格分页与行渲染调试 |
|
|
184
|
+
| `page1000.json` | ~32 MB | 84 | 84 个表格 | 大量表格的分页/性能压测 |
|
|
185
|
+
| `page69.json` | ~85 MB | 2323 | 文本、198 表格、列表、控件 | 超大文档虚拟滚动压测 |
|
|
186
|
+
| `page69i.json` | ~85 MB | 2344 | 在 `page69` 基础上增加 18 张图片 | 含图片的超大文档压测 |
|
|
187
|
+
|
|
188
|
+
> 说明:`page69*` / `page1000` 体积较大,仅用于性能与边界场景验证,日常开发建议使用 `info.json` 或 `pagetable.json`。
|
|
189
|
+
|
|
190
|
+
### 元素数据结构
|
|
191
|
+
|
|
192
|
+
各 `type` 的字段含义见 [`docs/0.JSON数据类型说明.md`](docs/0.JSON数据类型说明.md) 与 [`docs/1.页面样式属性说明.md`](docs/1.页面样式属性说明.md)。
|
|
193
|
+
|
|
194
|
+
## 核心设计原则
|
|
195
|
+
|
|
196
|
+
1. **数据模型是唯一数据源**,DOM 只是视图,不存储状态
|
|
197
|
+
2. **contenteditable 仅用于输入捕获**,不用于渲染(透明 overlay 模式)
|
|
198
|
+
3. **所有写操作必须通过 `editor.exec()`**,不能直接修改 `docModel`
|
|
199
|
+
4. **大文档通过虚拟滚动**实现,段落是虚拟列表的最小单元
|
|
200
|
+
|
|
201
|
+
## 目录结构
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
src/
|
|
205
|
+
├── core/ # 纯 TypeScript,与 Vue 无关
|
|
206
|
+
│ ├── editor/Editor.ts # 编辑器主类,所有子系统的聚合入口
|
|
207
|
+
│ ├── model/DocumentModel.ts # 文档数据模型,所有原子修改方法在此
|
|
208
|
+
│ ├── command/ # 命令系统
|
|
209
|
+
│ │ ├── CommandManager.ts # exec() 分发,注册/执行命令
|
|
210
|
+
│ │ └── commands/ # InsertText / Delete / Format / InsertParagraph 等
|
|
211
|
+
│ ├── selection/SelectionManager.ts # 模型坐标系的光标/选区
|
|
212
|
+
│ ├── history/HistoryManager.ts # undo/redo 栈
|
|
213
|
+
│ └── input/InputHandler.ts # DOM 事件 → exec() 转换层
|
|
214
|
+
├── composables/
|
|
215
|
+
│ ├── useEditor.ts # provide/inject Editor 实例
|
|
216
|
+
│ └── useDocumentData.ts # 从 docModel 提取响应式数据
|
|
217
|
+
├── components/
|
|
218
|
+
│ ├── Editor/ # 编辑器根组件(含分页、表格、页眉页脚渲染)
|
|
219
|
+
│ └── VirtualTable/ # 自研虚拟表格组件
|
|
220
|
+
├── api/ # 接口请求封装
|
|
221
|
+
└── types/ # 全局类型(document/selection/command/plugin)
|
|
222
|
+
|
|
223
|
+
mock/ # vite mock,离线可用的接口数据
|
|
224
|
+
docs/ # 功能说明与问题分析文档
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## 架构
|
|
228
|
+
|
|
229
|
+
### 数据流
|
|
230
|
+
|
|
231
|
+
```
|
|
232
|
+
用户输入(键盘/鼠标)
|
|
233
|
+
→ InputHandler(DOM 事件拦截)
|
|
234
|
+
→ editor.exec(commandName, payload)
|
|
235
|
+
→ CommandManager(查找命令工厂)
|
|
236
|
+
→ ICommand.execute()(修改 DocumentModel)
|
|
237
|
+
→ Vue 响应式自动触发重渲染
|
|
238
|
+
→ 组件根据最新 docModel 渲染 DOM
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### 数据模型
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
DocumentModel
|
|
245
|
+
└── sections[] # 节(分页/分栏单位)
|
|
246
|
+
└── paragraphs[] # 段落(块级,虚拟滚动的最小单元)
|
|
247
|
+
└── children: IElement[] # 行内元素(文本/图片/超链接/表格等)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### 坐标系
|
|
251
|
+
|
|
252
|
+
所有位置用 `ModelPoint` 表示:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
{ sectionIndex, paragraphIndex, elementIndex, offset }
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
DOM 元素通过 `data-si / data-pi / data-ei` 属性反向映射回模型坐标(见 `SelectionManager.domToModelPoint`)。
|
|
259
|
+
|
|
260
|
+
## 扩展
|
|
261
|
+
|
|
262
|
+
### 注册命令
|
|
263
|
+
|
|
264
|
+
在 `CommandManager._registerBuiltinCommands()` 中注册,或通过插件注入:
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
editor.commands.register('myCommand', (payload) => new MyCommand(...))
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
命令须实现 `ICommand`(`execute` + `undo`),可选实现 `canMergeWith` / `merge` 以支持连续输入合并。
|
|
271
|
+
|
|
272
|
+
### 插件
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
editor.use({
|
|
276
|
+
name: 'my-plugin',
|
|
277
|
+
install(editor) {
|
|
278
|
+
editor.commands.register('customCommand', ...)
|
|
279
|
+
}
|
|
280
|
+
})
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### 新增元素类型
|
|
284
|
+
|
|
285
|
+
需同时更新:
|
|
286
|
+
|
|
287
|
+
- `src/types/document.ts`(类型定义)
|
|
288
|
+
- `src/components/Editor/index.vue`(渲染逻辑)
|
|
289
|
+
|
|
290
|
+
## 关键约束
|
|
291
|
+
|
|
292
|
+
- 禁止在渲染组件中直接修改 `docModel`
|
|
293
|
+
- 禁止在 `InputHandler` 中直接修改 `docModel`(必须通过 `exec`)
|
|
294
|
+
- `DocumentModel` 的原子方法(`insertText` / `deleteAt` 等)只能由 Command 调用
|
|
295
|
+
|
|
296
|
+
## 文档
|
|
297
|
+
|
|
298
|
+
更多功能说明与问题分析见 [`docs/`](docs/) 目录。
|