npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

word-editor

v1.0.1

Published

基于原生 JavaScript 开发的 A4 网页富文本与排版编辑器

Readme

Word Editor

基于原生 JavaScript 开发的 A4 网页排版富文本编辑器与电子合同签署组件。无外部运行时依赖,支持与主流前端框架集成。

npm version license bundle size dependencies typescript


核心特性

  • 原生无外部依赖:基于标准原生 JavaScript 开发,不依赖任何第三方富文本内核或 UI 框架。
  • A4 规格分页排版:遵循国际标准 A4 规格(210mm × 297mm),支持毫米级页边距配置、页眉页脚、动态页码、内容跨页自动分割与重流。
  • 内核与视图解耦:排版内核与界面独立,内置 Ribbon 工具栏、页面属性栏与右键快捷菜单;亦可按需隐藏默认界面,结合 Element Plus、Ant Design 等组件库自定义操作面板。
  • 跨端合同签署(ContractSigner):支持桌面端与移动端签署交互,提供待签位导览、手写签名画板、企业印章配置与签署存证数据导出。
  • 领域链式 API:提供 editor.doc、editor.format、editor.table、editor.signer 等模块化调用接口。
  • 排版表格与无边框模式:支持表格行列增删与边框隐藏功能,编辑态显示辅助对齐虚线,导出与打印时边框完全隐藏,便于实现多方信息并排排版。
  • HTML 导出与安全防护:支持导出带内联样式的独立 HTML 文档,内置 URL 协议白名单,拦截恶意伪协议。
  • 完整的 TypeScript 支持:提供 index.d.ts 与 signer.d.ts 类型声明文件,支持开发环境的代码补全与类型检查。

安装

npm install word-editor

或使用 yarn / pnpm:

yarn add word-editor
# 或
pnpm add word-editor

快速上手

1. 原生 HTML / JavaScript

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="node_modules/word-editor/dist/word-editor.css">
  <style>
    #editor-container { width: 100%; height: 100vh; }
  </style>
</head>
<body>
  <div id="toolbar-container"></div>
  <div id="editor-container"></div>

  <script type="module">
    import { WordEditor } from 'word-editor';

    const editor = new WordEditor('#editor-container', {
      toolbarContainer: '#toolbar-container',
      margins: { top: 25.4, bottom: 25.4, left: 25.4, right: 25.4 }, // 单位: 毫米 (mm)
      headerText: '电子服务采购合同',
      footerText: '第 [page] 页 / 共 [total] 页'
    });

    editor.on('change', (docData) => {
      console.log('文档数据更新:', docData);
    });
  </script>
</body>
</html>

2. 在 Vue 3 中使用

<template>
  <div class="editor-shell">
    <div ref="toolbarRef"></div>
    <div ref="editorRef" class="editor-mount"></div>
  </div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { WordEditor } from 'word-editor';
import 'word-editor/css';

const editorRef = ref(null);
const toolbarRef = ref(null);
let editor = null;

onMounted(() => {
  editor = new WordEditor(editorRef.value, {
    toolbarContainer: toolbarRef.value,
    margins: { top: 25.4, bottom: 25.4, left: 25.4, right: 25.4 },
    headerText: '技术开发合作框架协议',
    footerText: '第 [page] 页 / 共 [total] 页'
  });

  editor.on('change', (docJson) => {
    // 监听文档内容变更
  });
});

onBeforeUnmount(() => {
  if (editor) {
    editor.destroy();
    editor = null;
  }
});
</script>

<style scoped>
.editor-shell {
  display: flex;
  flex-direction: column;
  height: 100vh;
}
.editor-mount {
  flex: 1;
  overflow: hidden;
}
</style>

3. 在 React 中使用

import React, { useEffect, useRef } from 'react';
import { WordEditor } from 'word-editor';
import 'word-editor/css';

export default function DocumentEditor() {
  const editorMountRef = useRef(null);
  const toolbarMountRef = useRef(null);
  const editorInstanceRef = useRef(null);

  useEffect(() => {
    if (editorMountRef.current && !editorInstanceRef.current) {
      editorInstanceRef.current = new WordEditor(editorMountRef.current, {
        toolbarContainer: toolbarMountRef.current,
        margins: { top: 25.4, bottom: 25.4, left: 25.4, right: 25.4 }
      });
    }

    return () => {
      if (editorInstanceRef.current) {
        editorInstanceRef.current.destroy();
        editorInstanceRef.current = null;
      }
    };
  }, []);

  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100vh' }}>
      <div ref={toolbarMountRef} />
      <div ref={editorMountRef} style={{ flex: 1, overflow: 'hidden' }} />
    </div>
  );
}

4. 移动端合同签署(ContractSigner)

import { ContractSigner } from 'word-editor/signer';
import 'word-editor/signer/css';

const signer = new ContractSigner('#signer-container', {
  contractData: contractJson,  // 编辑器导出的文档 JSON 数据
  currentRole: 'party_b',      // 当前签署方角色 ('party_a' | 'party_b' | 'party_c' | 'custom')
  title: '技术服务在线合同签署',
  autoJumpToNext: true,        // 签署完当前项后自动滚动至下一个待签项
  onSignComplete: (evidence) => {
    console.log('签署存证数据:', evidence);
    // 包含时间戳、角色、签名/印章 Base64 数据、变量值与设备环境指纹
  }
});

模块分包与按需引入

根据不同业务场景,提供三种入口以适配体积与界面需求:

| 分包名称 | 模块入口 | 适用场景 | 样式导入语句 | | :--- | :--- | :--- | :--- | | 完整版 | 'word-editor' | 桌面端富文本排版,包含内置 Ribbon 工具栏与页面属性栏 | import 'word-editor/css'; | | 核心版 | 'word-editor/core' | 自定义 UI 界面或轻量排版场景,不包含默认工具栏与侧边栏 | import 'word-editor/core/css'; | | 签署版 | 'word-editor/signer' | 移动端或桌面端在线合同查看、签署与存证归档 | import 'word-editor/signer/css'; |


链式 API 调用范例

编辑器实例挂载了细分领域的 API 模块,支持链式与模块化调用:

// 1. 文档数据与导出 (DocAPI)
const docJson = editor.doc.getJson();
const cleanHtml = editor.doc.getHtml();         // 获取包含内联样式的正文 HTML 片段
const standaloneHtml = editor.doc.getFullHtml();// 获取包含 DOCTYPE 的完整单页 HTML
const plainText = editor.doc.getText();         // 获取正文纯文本
const totalWords = editor.doc.getWordCount();   // 获取当前字数
editor.doc.setHtml('<p>插入富文本内容</p>');

// 2. 富文本排版 (FormatAPI)
editor.format
  .bold()
  .color('#185abd')
  .fontSize('18px')
  .lineHeight('1.75')
  .align('center');

// 3. 表格操作 (TableAPI)
editor.table.insert(3, 4);              // 插入 3 行 4 列标准表格
editor.table.insertRow('after');        // 在当前行后插入新行
editor.table.deleteRow();               // 删除当前行
editor.table.deleteTable();             // 删除当前表格
editor.table.toggleBorder();            // 切换表格边框显示/隐藏
editor.table.setBorderless(null, true); // 设置表格为无边框排版模式

// 4. 印章与签名项管理 (SignerAPI)
editor.signer.addSign({ role: 'party_a', label: '甲方经办人签名' });
editor.signer.addSeal({ role: 'party_b', label: '乙方企业公章' });
editor.signer.remove('layer_id');

// 5. 历史记录 (HistoryAPI)
if (editor.historyApi.canUndo()) {
  editor.historyApi.undo();
}

自定义界面与第三方组件库集成

编辑器采用核心引擎与视图分离结构,默认界面均可配置或替换:

1. 禁用内置面板

const editor = new WordEditor('#editor-mount', {
  toolbarContainer: '#custom-toolbar', // 使用自定义容器挂载默认工具栏
  sidebarContainer: null,              // 传入 null 禁用默认右侧属性栏
  contractPropsContainer: null,        // 传入 null 禁用默认印章抽屉
  enableContextMenu: true              // 启用右键菜单
});

2. 结合 Element Plus / Ant Design 实现自定义面板

<template>
  <div class="workspace">
    <div ref="editorMount"></div>

    <!-- 自定义 Element Plus 印签属性抽屉 -->
    <el-drawer v-model="drawerVisible" title="配置签署项" :size="320">
      <el-form v-if="activeLayer" label-position="top">
        <el-form-item label="标签名称">
          <el-input v-model="activeLayer.label" @input="syncLayer" />
        </el-form-item>
        <el-form-item label="签署角色">
          <el-select v-model="activeLayer.role" @change="syncLayer">
            <el-option label="甲方" value="party_a" />
            <el-option label="乙方" value="party_b" />
          </el-select>
        </el-form-item>
        <el-button type="danger" @click="deleteLayer">删除项</el-button>
      </el-form>
    </el-drawer>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { WordEditor } from 'word-editor';
import 'word-editor/css';

const editorMount = ref(null);
const drawerVisible = ref(false);
const activeLayer = ref(null);
let editor = null;

onMounted(() => {
  editor = new WordEditor(editorMount.value, {
    contractPropsContainer: null // 禁用默认面板
  });

  // 监听印签图层选中事件
  editor.on('signseal:selected', (layer) => {
    activeLayer.value = { ...layer };
    drawerVisible.value = true;
  });

  // 监听取消选择事件
  editor.on('signseal:deselected', () => {
    drawerVisible.value = false;
    activeLayer.value = null;
  });
});

function syncLayer() {
  if (activeLayer.value?.id) {
    editor.updateSignSealLayer(activeLayer.value.id, activeLayer.value);
  }
}

function deleteLayer() {
  if (activeLayer.value?.id) {
    editor.removeSignSealLayer(activeLayer.value.id);
    drawerVisible.value = false;
  }
}
</script>

API 参考

WordEditor

初始化参数 (WordEditorOptions)

| 参数 | 类型 | 默认值 | 说明 | | :--- | :--- | :--- | :--- | | container | string \| HTMLElement | 必填 | 编辑器画布挂载容器 DOM 或 CSS 选择器 | | pageSize | string | 'A4' | 纸张规格(当前支持标准 A4) | | margins | Margins | 25.4mm 各边 | 初始页边距配置 { top, bottom, left, right } (单位: mm) | | headerText | string | "" | 默认页眉文本内容 | | footerText | string | "" | 默认页脚文本,支持 [page] 和 [total] 动态占位符 | | showHeader | boolean | true | 是否渲染页眉 | | showFooter | boolean | true | 是否渲染页脚 | | toolbarContainer | string \| HTMLElement | null | 顶部 Ribbon 工具栏挂载节点,传入 null 时不渲染 | | sidebarContainer | string \| HTMLElement | null | 右侧页面属性栏挂载节点,传入 null 时不渲染 | | contractPropsContainer | string \| HTMLElement | null | 印签属性抽屉挂载节点,传入 null 时不渲染 | | enableContextMenu | boolean | true | 是否开启右键上下文菜单 | | variables | Object | {} | 预设合同动态文本变量键值字典 (例如: { party_a: "某某公司" }) |

常用实例方法

| 方法 | 返回值 | 说明 | | :--- | :--- | :--- | | getData() / exportJson() | Object | 导出完整文档结构化 JSON(正文 AST、排版边距、印签图层、变量字典) | | setData(json) / loadJson(json) | void | 加载并恢复结构化 JSON 数据 | | exportHtml(options?) | string | 导出带有内联样式的 HTML 片段或完整单页文档 | | getHtml() | string | 获取正文富文本 HTML 片段 | | loadHtml(html) | void | 导入并解析外部 HTML 富文本片段 | | openPreview(options?) | void | 打开全屏 A4 打印预览视口 | | closePreview() | void | 关闭打印预览弹窗 | | undo() / redo() | void | 撤销 / 重做操作 | | saveState(options?) | boolean | 将当前文档快照压入撤销历史栈 | | getPageElement() | HTMLElement | 获取页面根 DOM 节点 | | getContentElement() | HTMLElement | 获取正文内容编辑区 DOM 节点 (.page-content) | | getBlocks() | HTMLElement[] | 获取所有一级块级段落 DOM 元素数组 | | getWordCount() | number | 获取当前文档纯正文字符总数 | | insertVariable(key, defaultVal?) | HTMLElement | 在当前光标处插入动态文本变量标记 (例如 {{party_name}}) | | addSignSealLayer(options) | Object | 添加印签图层(别名 insertSignSealLayer) | | updateSignSealLayer(id, data) | void | 更新指定印签图层属性 | | removeSignSealLayer(id) | void | 删除指定印签图层 | | on(event, handler) | this | 订阅内部事件 (change, wordCount, signseal:selected 等) | | off(event, handler) | this | 取消订阅内部事件 | | destroy() | void | 销毁实例,解绑全部 DOM 事件与定时器,释放内存 |


ContractSigner

初始化参数 (ContractSignerOptions)

| 参数 | 类型 | 默认值 | 说明 | | :--- | :--- | :--- | :--- | | container | string \| HTMLElement | 必填 | 签署器挂载容器 DOM 或 CSS 选择器 | | contractData | Object | {} | 由编辑器导出的完整合同 JSON 数据 | | currentRole | string | 'party_b' | 当前签署主体身份代码 ('party_a', 'party_b', 'party_c', 'custom') | | title | string | '电子合同在线签署' | 顶部标题栏展示文案 | | autoJumpToNext | boolean | true | 单项签署完成后是否自动平滑滚动并聚焦至下一个未签项 | | onSignComplete | Function | null | 全部签署完毕并提交时的存证回调函数 (evidence) => {} | | onLayerSigned | Function | null | 单个待签位签署完成时的回调函数 (layerItem) => {} |

常用实例方法

| 方法 | 返回值 | 说明 | | :--- | :--- | :--- | | load(contractData) | void | 重新载入合同数据并重新排版渲染 | | setRole(newRole) | void | 切换当前签署角色并刷新待签项高亮状态 | | jumpToNextSign() | boolean | 滚动至下一个待签位并触发提示效果 | | getProgress() | SigningProgress | 获取签署进度 { totalMyCount, signedCount, remainingCount, isAllSigned } | | getEvidenceData() | SignEvidenceData | 获取当前合同签署存证数据 (包含时间戳、签名数据、变量与设备指纹) | | exportSignedData() | Object | 导出签署完成后的合同全量结构化数据 | | setVariable(key, value) | void | 设置变量值并同步更新至所有页面 | | destroy() | void | 销毁签署器,释放全部事件与观察器 |


辅助函数与排版常量

项目公开导出了底层排版算法函数与度量常量:

import {
  docToJson,         // 文档数据序列化函数
  jsonToDom,         // 将结构化 JSON 转为块级 DOM 元素数组
  paginateContent,   // A4 核心分页重流计算函数
  createPageElement, // 创建单个 A4 纸张页面 DOM 节点
  estimatePages,     // 估算标准 A4 规格下的总物理页数
  openPreviewModal,  // 打开全屏打印预览模态框
  closePreviewModal, // 关闭打印预览模态框
  MM_TO_PX,          // 毫米转像素基准系数 (3.78 @ 96 DPI)
  PX_TO_MM,          // 像素转毫米基准系数 (0.264583 @ 96 DPI)
  PAGE_WIDTH_PX,     // 标准 A4 像素宽度 (794px)
  PAGE_HEIGHT_PX,    // 标准 A4 像素高度 (1123px)
  DEFAULT_MARGINS,   // 默认页边距配置
  DEFAULT_SETTINGS   // 默认编辑器排版参数
} from 'word-editor';

数据存证与安全

  1. 自包含存证数据:手写签名笔迹与印章图像统一编码为 Base64 DataURL 保存在图层数据中,避免对外部文件存储的强依赖。
  2. 协议过滤:内置 URL 白名单机制,拦截 javascript:、vbscript:、data:text/html 等脚本伪协议。
  3. 环境指纹与存证:签署完成时记录客户端 User-Agent、屏幕分辨率、签署时间戳及文档内容指纹,便于后续配合服务端签名进行验证。

许可证

本项目采用 MIT 许可证。