业务系统(例如表单加后端服务)只需要按本页的协议组装一份 JSON。版头、红线、标题、层次编号、署名、版记和页码都由
@quirekit/official-document 按 GB/T 9704—2012 排好;生成的文档在 Classic 中和普通文档一样编辑,并可导出 Word、PDF、OFD。
各要素按 GB/T 33476.2 标注公文域,编辑后可以读回同一格式的 JSON 交还业务系统。
@quirekit/official-document 属于商业组件,使用前先取得相应分发与授权,见付费使用。
1. 整体流程
- 业务系统根据表单内容生成公文 JSON(第 2~8 节)。
- 前端校验数据,编译成文档并载入 Classic。
- 用户在线修改、导出;需要时把当前内容读回 JSON(第 10 节),交给业务系统保存。
import { validateOfficialDocumentData } from '@quirekit/official-document'
import { readOfficialDocumentData } from '@quirekit/official-document/classic'
const checked = validateOfficialDocumentData(await response.json())
if (!checked.ok) showIssues(checked.issues) // 每项都有 path 与中文说明
else instance.editor?.commands.composeOfficialDocument(checked.data) // 一次可撤销地替换整篇文档
// 编辑完成后,连同印章、图片一起读回
const extracted = readOfficialDocumentData(instance.editor!)
if (extracted.ok) await saveToBusinessSystem(extracted.data, extracted.warnings)
composeOfficialDocument 由公文扩展提供:装配了 @quirekit/classic-ai 时已随附,否则安装
createClassicOfficialDocumentPlugin()(@quirekit/official-document/classic)。它把数据中的印章、图片先登记到编辑器媒体,
再替换文档;数据不合法时返回 false 且不改动文档。不用命令时,先把 collectOfficialDocumentImages(data) 登记到媒体,
再载入 compileOfficialDocument(data) 的结果;回取用 extractOfficialDocumentData(instance.getJSON(), { images })。
文档的保存、版本与文件转换见保存、打开与文件转换。
也可以不经浏览器,在 Node 服务中编译并用 Classic 的 DOCX 服务导出 Word 文件。Node 中没有浏览器的字体测量, 偶数末页版记是否与正文同页按估算字宽判断,交付前应在 Word 中核对。
2. 完整示例
一份带份号、密级和紧急程度的通知:
{
"version": 1,
"kind": "notice",
"issuer": { "agency": "示例市政务服务管理局" },
"copyNumber": "000123",
"secrecy": { "level": "秘密", "term": "1年" },
"urgency": "加急",
"documentNumber": "示政发〔2026〕12号",
"title": "示例市政务服务管理局关于2027年元旦放假安排的通知",
"recipients": ["各处室", "各直属单位"],
"body": [
{ "type": "paragraph", "text": "根据国务院办公厅通知精神,结合工作实际,现将2027年元旦放假安排通知如下。" },
{ "type": "heading", "level": 1, "title": "放假时间" },
{ "type": "paragraph", "text": "2027年1月1日至3日放假调休,共3天。" },
{ "type": "heading", "level": 1, "title": "工作要求" },
{ "type": "heading", "level": 2, "title": "严格值班值守。", "text": "各单位要安排好节日期间值班。" },
{ "type": "heading", "level": 2, "title": "做好安全检查。", "text": "节前开展一次安全隐患排查。" },
{ "type": "paragraph", "text": "特此通知。" }
],
"attachments": ["值班安排表", "联系人名单"],
"date": "2026-12-20",
"note": "联系人:李某,电话:12345678",
"imprint": { "cc": ["示例市人民政府办公室"], "office": "示例市政务服务管理局办公室", "date": "2026-12-20" }
}
3. 字段
只有 kind、issuer.agency 和 body 必填。没有内容的字段不要输出:可选字段写成 null 或空字符串会被拒绝。
| 字段 | 类型 | 含义与规则 |
|---|---|---|
version | 1 | 协议版本,可省略 |
kind | 文种值 | 见第 5 节 |
format | "letter" | 信函格式(第 6 节),通常用于函;不写时为文件格式 |
issuer.agency | 文本 | 发文机关全称或规范化简称,用于署名;联合行文时为主办机关 |
issuer.joint | 文本数组 | 联合行文的联署机关,按顺序排在主办机关之后 |
issuer.mark | 文本 | 发文机关标志,默认“机关名称+文件”(命令为“+令”,纪要为“+会议纪要”);可用 \n 分行 |
copyNumber | 数字串 | 份号,一般 6 位,如 000123 |
secrecy.level | 秘密、机密、绝密 | 密级 |
secrecy.term | 文本 | 保密期限、解密时间或条件,如 1年;排成“机密★1年” |
urgency | 特急、加急 | 紧急程度;电报另有特提、平急 |
documentNumber | 文本 | 发文字号,年份用六角括号,如 示政发〔2026〕12号;命令写令号 第1号,纪要写期号 第1期 |
signers | 文本数组 | 签发人,只用于上行文(请示、报告),按发文机关顺序每个机关一人 |
title | 文本 | 标题;过长时自动在“关于”前分行,也可以用 \n 指定分行;命令、纪要不写 |
adoption | 文本 | 会议通过说明,如 (2026年10月17日……会议通过),用于决议等 |
recipients | 文本数组 | 主送机关,每项一个机关,不带顿号或冒号;公告、通告、公报不写 |
body | 正文块数组 | 见第 4 节 |
attachments | 文本数组 | 附件名称,末尾不加标点;多个附件自动编号 |
signature | 落款 | 见第 7 节 |
date | YYYY-MM-DD | 成文日期,输出为“2026年12月20日”;不写时留出“年 月 日”待填 |
note | 文本 | 附注,不带括号 |
attendees | 名单数组 | 纪要的出席、请假、列席名单:{ "label": "出席", "names": ["张某", "李某"] } |
imprint.cc | 文本数组 | 抄送机关 |
imprint.office | 文本 | 印发机关,通常是发文机关办公室;信函格式不写 |
imprint.date | YYYY-MM-DD | 印发日期,信函格式不写;不需要版记时整个 imprint 不写 |
4. 正文
body 按阅读顺序排列,有四种块:
| 块 | 写法 | 排版 |
|---|---|---|
| 段落 | { "type": "paragraph", "text": "……" } | 三号仿宋,首行缩进两字;不要以空格开头 |
| 层次标题 | { "type": "heading", "level": 1, "title": "……" } | 1~4 级依次编号为“一、”“(一)”“1.”“(1)”,下级随上级重新计数 |
| 表格 | { "type": "table", "title": "……", "header": [[…]], "rows": [[…]] } | 版心全宽、0.5 pt 单线;表题在上方居中(黑体),表头黑体居中、跨页重复,每行不跨页 |
| 图片 | { "type": "image", "src": "data:image/png;base64,…", "caption": "……" } | 单独成段、居中,图题在下方居中 |
- 标题不要写序号,序号由程序生成;第一个层次标题必须从第 1 级开始。
- 小标题后同一行接正文时,
title以句号结尾,正文写在text:{ "type": "heading", "level": 2, "title": "严格值班值守。", "text": "各单位要安排好节日期间值班。" }。
表格:
{
"type": "table",
"title": "表1 2027年培训安排",
"header": [["时间", "内容", "对象"]],
"rows": [["3月", "公文格式", "各处室"], ["4月", "公文处理\n办文流程", "办公室"]],
"columns": [{ "width": 1, "align": "center" }, { "width": 3 }, { "width": 2, "align": "center" }],
"fontSize": 14
}
| 字段 | 说明 |
|---|---|
title | 表题,可省略 |
header | 表头行,可省略;跨页时每页重复 |
rows | 表体行;每行(含表头)的格数必须等于列数,不支持合并单元格;单元格中的 \n 另起一段 |
columns | 每列一项:width 为相对宽度(缺省等分),align 为 start(缺省)、center 或 end |
fontSize | 表格文字字号:16(三号,缺省)、14(四号)或 12(小四) |
图片:src 为 PNG 或 JPEG 的 data URL;width 为宽度(mm),缺省按图片 96 dpi 的尺寸、不超过版心宽度 156 mm,高度按比例;
caption 为图题,可省略。图片与印章一样保存在编辑器媒体中,见第 7 节。国标 §8 的横排表格暂不支持。
5. 文种
kind | 文种 | 说明 |
|---|---|---|
resolution | 决议 | 一般无署名,常带 adoption |
decision | 决定 | |
order | 命令(令) | 令式版头,无标题、主送;documentNumber 写令号;落款写负责人 |
communique | 公报 | 一般无主送、无署名 |
announcement | 公告 | 一般无主送 |
public-notice | 通告 | 一般无主送 |
opinion | 意见 | |
notice | 通知 | |
circular | 通报 | |
report | 报告 | 上行文,写 signers |
request | 请示 | 上行文,写 signers |
reply | 批复 | |
proposal | 议案 | 落款写负责人 |
letter | 函 | |
minutes | 纪要 | 纪要式版头,无标题、主送;写 attendees,一般无署名 |
命令和纪要不支持联合行文;命令不排份号、密级和紧急程度。
6. 信函格式
写 "format": "letter" 时按 GB/T 9704 §10.1 编排:发文机关标志只用机关名称(不加“文件”),上边缘距上页边 30 mm,
其下 4 mm 为红色双线(上粗下细),距下页边 20 mm 为红色双线(上细下粗),线长 170 mm;份号、密级、紧急程度顶格居左,
发文字号顶格居右,标题在其下二行;首页不显示页码;版记只排抄送(imprint 只写 cc),位于最后一页版心最下方。
联合行文时标志只用主办机关,署名仍依次排列。信函格式不排签发人,命令和纪要不能使用。
{
"kind": "letter",
"format": "letter",
"issuer": { "agency": "示例市政务服务管理局" },
"documentNumber": "示政函〔2026〕5号",
"title": "关于商请协助开展公文规范培训的函",
"recipients": ["示例市档案局"],
"body": [{ "type": "paragraph", "text": "专此函达。" }],
"date": "2026-12-20",
"imprint": { "cc": ["示例市财政局"] }
}
7. 落款与联合行文
signature 不写时为机关署名(不加盖印章的版式),其他写法:
- 加盖印章:
{ "type": "sealed" },见下文“印章与签名章” - 加盖签发人签名章:
{ "type": "stamp", "stamps": [...] },见下文 - 负责人署名(命令、议案):
{ "type": "leader", "position": "市长", "name": "张某" } - 不署名(决议、公报、纪要):
{ "type": "none" }
联合行文在 issuer.joint 列出联署机关:版头各机关名称上下排列、“文件”居中排在右侧,署名按主办机关在前依次向下排。
上行文的签发人每行两个,按机关顺序排列:
{
"kind": "request",
"issuer": { "agency": "示例市政务服务管理局", "joint": ["示例市财政局"] },
"documentNumber": "示政请〔2026〕3号",
"signers": ["张某", "李某"],
"title": "关于联合开展公文规范培训的请示",
"recipients": ["示例市人民政府"],
"body": [
{ "type": "paragraph", "text": "为提高公文办理质量,拟联合开展公文规范培训。现将有关事项请示如下。" },
{ "type": "heading", "level": 1, "title": "培训安排" },
{ "type": "paragraph", "text": "拟于2027年3月组织一次专题培训。" },
{ "type": "paragraph", "text": "妥否,请批示。" }
],
"date": "2026-12-20"
}
印章与签名章
按 GB/T 9704—2012 §7.3.5.1、§7.3.5.3 编排,两种用法任选:
- 接入电子签章系统:不给图片,文档只排好落款并预留盖章位置(不可见、随分页移动),导出后由签章系统按
locateOfficialSeals给出的坐标盖章。 - 没有签章系统:在数据中给出印章图片(透明底 PNG 的 data URL),直接盖在文档中,Word、PDF、OFD 导出都包含。
{ "type": "sealed", "seals": ["data:image/png;base64,iVBORw0..."], "diameter": 42 }
| 字段 | 说明 |
|---|---|
seals | 按发文机关顺序,每个机关一枚;不写时预留全部位置。枚数必须等于机关数 |
diameter | 印章直径(mm),20~60,默认 42 |
加盖印章时成文日期右空四字,机关署名以日期为准居中,印章与署名、日期同心,顶端距正文半行。联合行文时印章按机关顺序成排, 最后一枚压在成文日期上,其余向左排列,枚间与排间留 2 mm;机关名称较长、一排放不下各自署名时,每排一枚、依次向下。
{ "type": "stamp", "stamps": [{ "position": "示例市人民政府市长", "image": "data:image/png;base64,iVBORw0..." }], "height": 15 }
| 字段 | 说明 |
|---|---|
stamps | 按发文机关顺序,每个机关一项:position 为签发人职务全称,image 为签名章图片,可省略以预留位置 |
height | 签名章高度(mm),8~30,默认 15;宽度按图片比例,没有图片时取高度的 2.5 倍 |
签名章在正文下空二行、右空四字,职务在其左侧空二字并上下居中,成文日期在签名章下空一行、右空四字;联合行文时每行一个机关。
浏览器中,盖章位置由 @quirekit/official-document/classic 读出:
import { locateOfficialSeals } from "@quirekit/official-document/classic";
// [{ kind: "seal", number: 1, reserved: true, page: 1, x: 120.68, y: 215.62, width: 42, height: 42 }]
// page 从 1 起;x、y、width、height 为毫米,以页面左上角为原点,与 PDF 导出一致。
const positions = locateOfficialSeals(instance.editor!);
图片保存在编辑器的媒体中,不写入文档 JSON;composeOfficialDocument 会先登记图片再替换文档。
8. 后端校验
发行包内的 schema/official-document.schema.json(JSON Schema 2020-12)描述字段结构,后端可以用所用语言的
JSON Schema 2020-12 校验库先行校验。以下规则 Schema 表达不了,由 validateOfficialDocumentData 检查:
- 除命令、纪要外必须有标题;
- 日期必须是真实存在的日子;
- 第一个层次标题从第 1 级开始;
- 命令不排份号、密级、紧急程度,命令和纪要不支持联合行文;
- 签发人不能多于发文机关;
attendees只用于纪要; - 信函格式不能用于命令、纪要,也不排签发人;非信函格式的版记必须有
office和date; - 印章(
seals)、签名章(stamps)的个数等于发文机关数; - 表格每行(含表头)的格数等于列数。
校验失败时返回全部问题,每项含 JSON Pointer 路径和中文说明,可以直接提示到对应表单项:
[
{ "path": "/issuer", "message": "缺少必填字段:agency。" },
{ "path": "/date", "message": "格式不对,日期请写成 YYYY-MM-DD。" }
]
9. 接入要点
我们提供的接口与语言无关:业务系统用任何语言生成符合本协议的 JSON,通过 HTTP 交给前端即可。组装数据时注意:
- 字段名使用协议的驼峰命名,如
documentNumber、fontSize; - 没填的字段直接不输出,不要输出
null或空字符串(段落的text不能为空); - 日期写成
2026-12-20; - 层次标题不写“一、”“(一)”等序号,由程序编号;
- 表格每行(含表头)的格数等于列数;印章、签名章的个数等于发文机关数;
- 印章、签名章与正文图片写成 data URL:印章、签名章为透明底 PNG,正文图片可用 PNG 或 JPEG。
后端可先用发行包内的 schema/official-document.schema.json 校验结构(第 8 节)。回取得到的 JSON 结构相同,可以用生成时的同一组类型反序列化。
10. 回取
extractOfficialDocumentData(document, options?) 读取编辑器内容(instance.getJSON())或导入的 DOCX,返回
{ ok: true, data, warnings } 或 { ok: false, issues }:
- 只读取公文域内的文字;在域外另起的段落不回取,在正文内回车新增的段落正常回取。
- 待审修订按“全部接受”后的文字读取,不改变文档中的修订。
- 正文中的表格与图片读回为表格块和图片块;含合并单元格的表格等协议容纳不了的内容跳过,并在
warnings中给出位置; 无法识别的日期留空并给出警告。列宽读回为对应的 twips 宽度,比例不变。 - 联合行文的机关以署名为准;版头与署名中的机关名称不一致时给出警告。
- 在 Microsoft Word 中编辑后再导入也能回取(Mac Word 16.109.2 实测)。
- 在线编辑时,“视图”功能区的“公文要素”打开右侧面板,按版头、主体、版记列出文档中的要素,像填表一样修改,改动写入对应的
公文域,可撤销;正文、附件说明只显示摘要并可定位。界面样式需引入
@quirekit/official-document/styles.css。 - 印章与签名章图片在编辑器媒体中,回取时传入
{ images: 媒体解析器 }才能读回;浏览器中用readOfficialDocumentData(editor)(@quirekit/official-document/classic)自动读回。读不到时给出警告,按预留位置回取。
数据未修改时,回取结果再编译得到同一份文档。
11. 当前范围
已支持:上述十五种文种、文件格式与信函格式、份号、密级与保密期限、紧急程度、签发人、联合行文、四级层次、正文表格与图片、附件说明、机关署名、加盖印章(预留位置或图片)、签名章、负责人署名与不署名、附注、纪要名单、 抄送与印发、奇偶页码,以及 Word/PDF/OFD 导出。
尚未支持:横排表格、表格合并单元格。WPS 等其他公文系统能否识别公文域尚未验证; PDF、OFD 导出只含版面内容,不含公文域。