@wanghe1995/docx-editor-ui
v1.0.0
Published
docx editor完整ui编辑器
Maintainers
Readme
docx-editor
基于 Canvas 的类 Word 文档编辑器,提供分页渲染与富文本编辑能力,并支持导入 .docx(基于 docx-preview 解析为编辑器元素)。
- 仓库主页:https://gitee.com/wanghe520/docx-editor
- NPM 包名:
@wanghe1995/docx-editor
功能概览
- 富文本:撤销/重做、字体/字号、加粗/斜体/下划线/删除线、高亮、上下标、对齐、标题、列表等
- 插入:表格、图片、链接、代码块、分页符、公式、日期、图表、音视频、条形码/二维码等
- 文档结构:页眉/页脚、页码、页边距、分页、水印、目录、批注
- 扩展能力:快捷键/右键菜单可注册,自定义插件机制
安装
npm i @wanghe1995/docx-editorUI 方案会使用以下 peer 依赖(由业务项目安装):
vueelement-plus@element-plus/icons-vueprismjs
快速开始
方案 A:作为整套 UI 使用(推荐)
该方案会挂载本仓库 Demo 同款 UI(顶部栏/侧边栏/弹窗/编辑器等),适合业务快速接入。
import { mountDocxEditorUI } from '@wanghe1995/docx-editor/ui'
const { api, unmount } = mountDocxEditorUI('#app')
api.on('ready', () => {
console.log('docx-editor ready')
})
// unmount()指定 Element Plus 语言包:
import { mountDocxEditorUI } from '@wanghe1995/docx-editor/ui'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
mountDocxEditorUI('#app', { locale: zhCn })方案 B:作为核心库使用(更灵活)
适合你有自己的 UI,只需要编辑器内核能力。
import DocxEditor, { EditorMode } from '@wanghe1995/docx-editor'
const container = document.querySelector<HTMLDivElement>('#editor')!
const editor = new DocxEditor(container, { main: [{ value: 'Hello Docx Editor' }] }, {})
editor.command.executeMode(EditorMode.EDIT)销毁:
editor.destroy()UI 模式 API(@wanghe1995/docx-editor/ui)
mountDocxEditorUI
import { mountDocxEditorUI } from '@wanghe1995/docx-editor/ui'签名(概念说明):
mountDocxEditorUI(
target: string | HTMLElement,
options?: {
locale?: Language
onAppCreated?: (app: VueApp) => void
initialDocument?: {
meta: DocumentMeta
url?: string
format?: 'word' | 'canvas'
}
documentApi?: DocumentApi
}
): {
app: VueApp
unmount(): void
executeCommand(command: string, ...args: any[]): void
api: DocxEditorUiExternalApi
}参数说明:
target:挂载点(CSS selector 或 HTMLElement)options.locale:Element Plus 语言包options.onAppCreated:可用于注册插件/注入全局能力options.initialDocument:初始化加载的文档信息(见下文)options.documentApi:后端对接实现(推荐注入;见下文)
返回值说明:
unmount():卸载 UI(SPA 路由切换时使用)executeCommand(...):透传 UI 内部命令(一般不建议作为主集成方式)api:推荐使用的外部 API(事件订阅、保存、快照等)
初始化加载(initialDocument)
UI 挂载时可传入 initialDocument 自动拉取并渲染远程文档。
要点:
initialDocument.meta与后端保存/状态接口使用同一套DocumentMeta- 拉取地址优先
initialDocument.url,否则使用initialDocument.meta.path
import { mountDocxEditorUI } from '@wanghe1995/docx-editor/ui'
mountDocxEditorUI('#app', {
initialDocument: {
meta: {
id: 'doc-10001',
path: 'https://example.com/demo.docx',
status: 'edit',
name: '演示文档',
createdAt: new Date().toISOString(),
submittedAt: ''
},
format: 'word'
}
})UI 外部 API(事件 / 节流 / 保存)
mountDocxEditorUI 返回的 api 用于业务侧订阅变化并对接后端。
api.on / api.off
const unsubscribe = api.on(
'contentChange',
async (payload) => {
console.log(payload)
},
{ debounceMs: 1200, maxWaitMs: 8000 }
)
unsubscribe()事件订阅参数:
debounceMs:防抖间隔(推荐用于保存)maxWaitMs:最长等待(避免持续输入时一直不触发)throttleMs:节流间隔(推荐用于 mode/ability 等高频事件)
建议策略:
- 自动保存:
{ debounceMs: 800~1500, maxWaitMs: 5000~15000 } - 模式/能力同步:
{ throttleMs: 200~500 }
事件列表与 payload
ready
- payload:
undefined
metaChange
- payload:
{ meta: DocumentMeta }
statusChange
- payload:
{ status: 'edit' | 'lock' }
contentChange
- payload:
{ meta: DocumentMeta; content: unknown } - 触发时机:编辑器内容被修改(已内置 1s 防抖;建议业务侧再做“保存节流”)
modeChange
- payload:
{ mode: string; ability?: { focused: boolean; readonly: boolean; disabled: boolean; canInput: boolean } } - 触发时机:调用 UI 的
mode命令切换模式
abilityChange
- payload:
{ focused: boolean; readonly: boolean; disabled: boolean; canInput: boolean } - 触发时机:选区/区域变化导致能力变化(例如进入表单控件区域、只读区域等)
api.document
常用能力:
api.document.getMeta():获取当前文档元信息api.document.setMeta(partial):更新元信息(例如改名)api.document.getSnapshot():获取{ meta, content }快照(用于业务保存)api.document.save({ silent?: boolean }):触发一次保存(走 DocumentApi)
后端对接(保存 / 保护 / 解锁)
DocumentApi(推荐注入)
UI 模式推荐在 mountDocxEditorUI(..., { documentApi }) 注入你的后端实现,编辑器内部会统一调用它完成保存与状态变更。
import type { DocumentApi } from '@wanghe1995/docx-editor'
import { mountDocxEditorUI } from '@wanghe1995/docx-editor/ui'
const documentApi: DocumentApi = {
async saveDocument(payload) {
const resp = await fetch('https://your-domain.com/document/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
})
if (!resp.ok) throw new Error(`saveDocument failed: ${resp.status}`)
return await resp.json()
},
async setStatus(payload) {
const resp = await fetch('https://your-domain.com/document/status', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
})
if (!resp.ok) throw new Error(`setStatus failed: ${resp.status}`)
}
}
mountDocxEditorUI('#app', { documentApi })VITE_DOCUMENT_API_BASE_URL(仅作为默认实现的便捷配置)
当你不注入 documentApi 时,UI 会启用“默认 DocumentApi”:
- 配置了
VITE_DOCUMENT_API_BASE_URL:自动请求你的后端 - 未配置:本地兜底(用于 Demo 体验,不建议生产使用)
示例(运行本仓库 Demo 时使用):
在项目根目录创建 .env.local:
VITE_DOCUMENT_API_BASE_URL=http://localhost:8080默认请求:
POST {VITE_DOCUMENT_API_BASE_URL}/document/savePOST {VITE_DOCUMENT_API_BASE_URL}/document/status
接口约定(建议)
保存文档:
POST /document/save
{
"meta": { "id": "1", "path": "/demo", "status": "edit" },
"content": { "version": "x.y.z", "data": { "main": [{ "value": "..." }] }, "options": {} }
}返回:
{ "submittedAt": "2026-01-01T00:00:00.000Z" }保护/解锁:
POST /document/status
{ "id": "1", "path": "/demo", "status": "lock", "password": "123456" }核心库 API(@wanghe1995/docx-editor)
new DocxEditor(container, data, options) 返回编辑器实例,核心入口:
editor.command:命令入口(编辑能力)editor.listener:监听器入口(单回调槽位)editor.eventBus:事件总线(多订阅者)editor.register:注册入口(快捷键/右键菜单/i18n 等)editor.use(...):插件机制editor.destroy():销毁
常用命令示例:
import DocxEditor, { EditorMode } from '@wanghe1995/docx-editor'
const editor = new DocxEditor(container, { main: [{ value: 'Hello' }] }, {})
const snapshot = editor.command.getValue()
editor.command.executeSetValue({
data: { main: [{ value: '已加载的内容' }] },
options: editor.command.getOptions()
})
editor.command.executeMode(EditorMode.EDIT)
editor.command.executeMode(EditorMode.READONLY)开发
运行 Demo:
npm install
npm run dev构建:
npm run build
npm run build:local
npm run lib质量检查:
npm run lint
npm run type:check