490 lines
15 KiB
Markdown
490 lines
15 KiB
Markdown
|
|
---
|
|||
|
|
name: docx-cn
|
|||
|
|
description: "基于 docx-js (Node.js) 创建和编辑符合 GB/T 9704-2012 中国公文格式标准的 Word 文档。当用户需要生成公文、研究报告、内部文件等中文 Word 文档时使用。触发词:公文、Word、docx、报告排版、格式规范、GB/T 9704。"
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 中文公文 Word 文档生成(docx-js)
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
基于官方 docx skill 微调,专为中文公文 / 金融研究报告场景优化。
|
|||
|
|
使用 **docx-js**(Node.js)生成文档,配合官方 skill 的 unpack/pack/validate 工具链编辑现有文档。
|
|||
|
|
|
|||
|
|
## 核心工具链
|
|||
|
|
|
|||
|
|
| 任务 | 方法 |
|
|||
|
|
|------|------|
|
|||
|
|
| **新建文档** | `docx-js` — 本文件中的模板 |
|
|||
|
|
| **编辑现有文档** | unpack XML → 编辑 → repack(见「编辑现有文档」节) |
|
|||
|
|
| **验证** | `python .claude/skills/docx/scripts/office/validate.py output.docx` |
|
|||
|
|
| **读取内容** | `pandoc document.docx -o output.md` |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## GB/T 9704-2012 公文格式常量
|
|||
|
|
|
|||
|
|
### 单位换算速查
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
1 inch = 1440 DXA = 72 pt = 25.4 mm
|
|||
|
|
1 mm = 56.693 DXA
|
|||
|
|
1 pt = 20 DXA(spacing/行距用)
|
|||
|
|
font size 单位 = half-point(半磅),如 14pt = 28
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 页面设置(A4)
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
const PAGE = {
|
|||
|
|
WIDTH: 11906, // 210mm
|
|||
|
|
HEIGHT: 16838, // 297mm
|
|||
|
|
MARGIN_TOP: 2098, // 37mm
|
|||
|
|
MARGIN_BOTTOM: 1985, // 35mm
|
|||
|
|
MARGIN_LEFT: 1588, // 28mm
|
|||
|
|
MARGIN_RIGHT: 1474, // 26mm
|
|||
|
|
CONTENT_WIDTH: 8844, // 210mm - 28mm - 26mm
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 字号对照表
|
|||
|
|
|
|||
|
|
| 中文字号 | pt | half-point (docx-js size) | 用途 |
|
|||
|
|
|----------|-----|---------------------------|------|
|
|||
|
|
| 二号 | 22 | 44 | 大标题 |
|
|||
|
|
| 三号 | 16 | 32 | 一级标题 |
|
|||
|
|
| 四号 | 14 | 28 | 正文 / 二级标题 |
|
|||
|
|
| 小四 | 12 | 24 | 表格内容 |
|
|||
|
|
| 五号 | 10.5 | 21 | 页脚、注释 |
|
|||
|
|
|
|||
|
|
### 字体规范
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
// 中文字体必须同时设置 eastAsia,否则 Word 渲染时可能回退到宋体
|
|||
|
|
const FONT = {
|
|||
|
|
HEITI: { ascii: "SimHei", eastAsia: "黑体", hAnsi: "SimHei" }, // 标题
|
|||
|
|
FANGSONG: { ascii: "FangSong", eastAsia: "仿宋", hAnsi: "FangSong" }, // 正文
|
|||
|
|
KAITI: { ascii: "KaiTi", eastAsia: "楷体", hAnsi: "KaiTi" }, // 二级标题
|
|||
|
|
SONGTI: { ascii: "SimSun", eastAsia: "宋体", hAnsi: "SimSun" }, // 备用
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 行距与段落
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
const SPACING = {
|
|||
|
|
// 行距(twips = pt × 20),lineRule: "exact" → 固定值
|
|||
|
|
BODY: { line: 560, lineRule: "exact" }, // 28pt 固定行距
|
|||
|
|
HEADING1: { line: 640, lineRule: "exact" }, // 32pt
|
|||
|
|
HEADING2: { line: 560, lineRule: "exact" }, // 28pt
|
|||
|
|
TABLE: { line: 440, lineRule: "exact" }, // 22pt
|
|||
|
|
|
|||
|
|
// 段前段后(twips)
|
|||
|
|
H1_BEFORE: 240, H1_AFTER: 120, // 12pt / 6pt
|
|||
|
|
H2_BEFORE: 120, H2_AFTER: 60, // 6pt / 3pt
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const INDENT = {
|
|||
|
|
FIRST_LINE: 560, // 首行缩进 2 字符 ≈ 28pt = 560 DXA
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 创建新文档
|
|||
|
|
|
|||
|
|
### 安装
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
npm install docx # 项目内安装即可,无需全局
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 完整模板:公文报告(无封面)
|
|||
|
|
|
|||
|
|
```javascript
|
|||
|
|
const fs = require("fs");
|
|||
|
|
const {
|
|||
|
|
Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
|
|||
|
|
Header, Footer, AlignmentType, HeadingLevel, BorderStyle, WidthType,
|
|||
|
|
ShadingType, PageNumber, PageBreak, LevelFormat, TabStopType, TabStopPosition,
|
|||
|
|
} = require("docx");
|
|||
|
|
|
|||
|
|
// ─── GB/T 9704-2012 格式常量 ─────────────────────────────
|
|||
|
|
const PAGE = {
|
|||
|
|
WIDTH: 11906, HEIGHT: 16838,
|
|||
|
|
MARGIN_TOP: 2098, MARGIN_BOTTOM: 1985,
|
|||
|
|
MARGIN_LEFT: 1588, MARGIN_RIGHT: 1474,
|
|||
|
|
CONTENT_WIDTH: 8844,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const FONT = {
|
|||
|
|
HEITI: { ascii: "SimHei", eastAsia: "黑体", hAnsi: "SimHei" },
|
|||
|
|
FANGSONG: { ascii: "FangSong", eastAsia: "仿宋", hAnsi: "FangSong" },
|
|||
|
|
KAITI: { ascii: "KaiTi", eastAsia: "楷体", hAnsi: "KaiTi" },
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ─── 样式定义 ─────────────────────────────────────────────
|
|||
|
|
const styles = {
|
|||
|
|
default: {
|
|||
|
|
document: {
|
|||
|
|
run: { font: FONT.FANGSONG, size: 28 }, // 默认:仿宋四号
|
|||
|
|
paragraph: {
|
|||
|
|
spacing: { line: 560, lineRule: "exact" },
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
paragraphStyles: [
|
|||
|
|
{
|
|||
|
|
id: "Heading1", name: "Heading 1",
|
|||
|
|
basedOn: "Normal", next: "Normal", quickFormat: true,
|
|||
|
|
run: { font: FONT.HEITI, size: 32, bold: true }, // 黑体三号
|
|||
|
|
paragraph: {
|
|||
|
|
spacing: { line: 640, lineRule: "exact", before: 240, after: 120 },
|
|||
|
|
outlineLevel: 0,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: "Heading2", name: "Heading 2",
|
|||
|
|
basedOn: "Normal", next: "Normal", quickFormat: true,
|
|||
|
|
run: { font: FONT.KAITI, size: 28, bold: true }, // 楷体四号加粗
|
|||
|
|
paragraph: {
|
|||
|
|
spacing: { line: 560, lineRule: "exact", before: 120, after: 60 },
|
|||
|
|
outlineLevel: 1,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ─── 列表定义 ─────────────────────────────────────────────
|
|||
|
|
const numbering = {
|
|||
|
|
config: [
|
|||
|
|
{
|
|||
|
|
reference: "bullets",
|
|||
|
|
levels: [{
|
|||
|
|
level: 0, format: LevelFormat.BULLET, text: "\u2022",
|
|||
|
|
alignment: AlignmentType.LEFT,
|
|||
|
|
style: {
|
|||
|
|
run: { font: FONT.FANGSONG, size: 28 },
|
|||
|
|
paragraph: { indent: { left: 1120, hanging: 560 } }, // 缩进 2 字符 + 悬挂
|
|||
|
|
},
|
|||
|
|
}],
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ─── 辅助函数 ─────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
/** 创建正文段落(仿宋四号,首行缩进 2 字符) */
|
|||
|
|
function bodyParagraph(text, options = {}) {
|
|||
|
|
const { bold = false, alignment = AlignmentType.JUSTIFIED } = options;
|
|||
|
|
return new Paragraph({
|
|||
|
|
alignment,
|
|||
|
|
indent: { firstLine: 560 },
|
|||
|
|
spacing: { line: 560, lineRule: "exact" },
|
|||
|
|
children: [new TextRun({ text, font: FONT.FANGSONG, size: 28, bold })],
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 创建一级标题(黑体三号) */
|
|||
|
|
function heading1(text) {
|
|||
|
|
return new Paragraph({
|
|||
|
|
heading: HeadingLevel.HEADING_1,
|
|||
|
|
children: [new TextRun({ text, font: FONT.HEITI, size: 32, bold: true })],
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 创建二级标题(楷体四号加粗) */
|
|||
|
|
function heading2(text) {
|
|||
|
|
return new Paragraph({
|
|||
|
|
heading: HeadingLevel.HEADING_2,
|
|||
|
|
children: [new TextRun({ text, font: FONT.KAITI, size: 28, bold: true })],
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 创建三线表 */
|
|||
|
|
function threeLineTable(headers, rows) {
|
|||
|
|
const noBorder = { style: BorderStyle.NONE, size: 0 };
|
|||
|
|
const thickBorder = { style: BorderStyle.SINGLE, size: 12, color: "000000" }; // 1.5pt
|
|||
|
|
const thinBorder = { style: BorderStyle.SINGLE, size: 6, color: "000000" }; // 0.75pt
|
|||
|
|
|
|||
|
|
// 表头行边框:顶粗 + 底细
|
|||
|
|
const headerBorders = {
|
|||
|
|
top: thickBorder, bottom: thinBorder,
|
|||
|
|
left: noBorder, right: noBorder,
|
|||
|
|
};
|
|||
|
|
// 数据行边框:无顶底左右(靠 insideH 提供行间线)
|
|||
|
|
const dataBorders = {
|
|||
|
|
top: noBorder, bottom: noBorder,
|
|||
|
|
left: noBorder, right: noBorder,
|
|||
|
|
};
|
|||
|
|
// 最后一行底部:粗线
|
|||
|
|
const lastRowBorders = {
|
|||
|
|
top: noBorder, bottom: thickBorder,
|
|||
|
|
left: noBorder, right: noBorder,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const colWidths = headers.map(() => Math.floor(PAGE.CONTENT_WIDTH / headers.length));
|
|||
|
|
|
|||
|
|
function makeCell(text, isHeader, borders) {
|
|||
|
|
return new TableCell({
|
|||
|
|
borders,
|
|||
|
|
width: { size: colWidths[0], type: WidthType.DXA },
|
|||
|
|
shading: isHeader
|
|||
|
|
? { fill: "F2F2F2", type: ShadingType.CLEAR }
|
|||
|
|
: undefined,
|
|||
|
|
margins: { top: 40, bottom: 40, left: 80, right: 80 },
|
|||
|
|
children: [new Paragraph({
|
|||
|
|
alignment: AlignmentType.CENTER,
|
|||
|
|
spacing: { line: 440, lineRule: "exact" },
|
|||
|
|
children: [new TextRun({
|
|||
|
|
text,
|
|||
|
|
font: isHeader ? FONT.HEITI : FONT.FANGSONG,
|
|||
|
|
size: 24, // 小四 12pt
|
|||
|
|
bold: isHeader,
|
|||
|
|
})],
|
|||
|
|
})],
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const tableRows = [
|
|||
|
|
// 表头
|
|||
|
|
new TableRow({
|
|||
|
|
children: headers.map(h => makeCell(h, true, headerBorders)),
|
|||
|
|
}),
|
|||
|
|
// 数据行
|
|||
|
|
...rows.map((row, rowIdx) =>
|
|||
|
|
new TableRow({
|
|||
|
|
children: row.map(cell =>
|
|||
|
|
makeCell(cell, false, rowIdx === rows.length - 1 ? lastRowBorders : dataBorders)
|
|||
|
|
),
|
|||
|
|
})
|
|||
|
|
),
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
return new Table({
|
|||
|
|
width: { size: PAGE.CONTENT_WIDTH, type: WidthType.DXA },
|
|||
|
|
columnWidths: colWidths,
|
|||
|
|
rows: tableRows,
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 报告标题区(居中,无封面) */
|
|||
|
|
function titleBlock(title, subtitle, date, institution) {
|
|||
|
|
return [
|
|||
|
|
new Paragraph({
|
|||
|
|
alignment: AlignmentType.CENTER,
|
|||
|
|
spacing: { line: 640, lineRule: "exact", after: 0 },
|
|||
|
|
children: [new TextRun({ text: title, font: FONT.HEITI, size: 32, bold: true })],
|
|||
|
|
}),
|
|||
|
|
subtitle ? new Paragraph({
|
|||
|
|
alignment: AlignmentType.CENTER,
|
|||
|
|
spacing: { line: 640, lineRule: "exact", after: 120 },
|
|||
|
|
children: [new TextRun({ text: subtitle, font: FONT.HEITI, size: 32 })],
|
|||
|
|
}) : null,
|
|||
|
|
new Paragraph({
|
|||
|
|
alignment: AlignmentType.CENTER,
|
|||
|
|
spacing: { line: 560, lineRule: "exact", after: 0 },
|
|||
|
|
children: [new TextRun({ text: date, font: FONT.FANGSONG, size: 28 })],
|
|||
|
|
}),
|
|||
|
|
institution ? new Paragraph({
|
|||
|
|
alignment: AlignmentType.CENTER,
|
|||
|
|
spacing: { line: 560, lineRule: "exact", after: 240 },
|
|||
|
|
children: [new TextRun({ text: institution, font: FONT.FANGSONG, size: 28 })],
|
|||
|
|
}) : null,
|
|||
|
|
].filter(Boolean);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ─── 使用示例:构建文档 ──────────────────────────────────
|
|||
|
|
|
|||
|
|
const doc = new Document({
|
|||
|
|
styles,
|
|||
|
|
numbering,
|
|||
|
|
sections: [{
|
|||
|
|
properties: {
|
|||
|
|
page: {
|
|||
|
|
size: { width: PAGE.WIDTH, height: PAGE.HEIGHT },
|
|||
|
|
margin: {
|
|||
|
|
top: PAGE.MARGIN_TOP, bottom: PAGE.MARGIN_BOTTOM,
|
|||
|
|
left: PAGE.MARGIN_LEFT, right: PAGE.MARGIN_RIGHT,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
headers: {
|
|||
|
|
default: new Header({
|
|||
|
|
children: [new Paragraph({
|
|||
|
|
alignment: AlignmentType.CENTER,
|
|||
|
|
children: [new TextRun({
|
|||
|
|
text: "内部资料 注意保密",
|
|||
|
|
font: FONT.FANGSONG, size: 21, color: "808080",
|
|||
|
|
})],
|
|||
|
|
})],
|
|||
|
|
}),
|
|||
|
|
},
|
|||
|
|
footers: {
|
|||
|
|
default: new Footer({
|
|||
|
|
children: [new Paragraph({
|
|||
|
|
alignment: AlignmentType.CENTER,
|
|||
|
|
children: [
|
|||
|
|
new TextRun({ text: "— ", font: FONT.FANGSONG, size: 21 }),
|
|||
|
|
new TextRun({ children: [PageNumber.CURRENT], font: FONT.FANGSONG, size: 21 }),
|
|||
|
|
new TextRun({ text: " —", font: FONT.FANGSONG, size: 21 }),
|
|||
|
|
],
|
|||
|
|
})],
|
|||
|
|
}),
|
|||
|
|
},
|
|||
|
|
children: [
|
|||
|
|
// 标题区
|
|||
|
|
...titleBlock(
|
|||
|
|
"zn04(鲁肆号)年金组合",
|
|||
|
|
"权益投资分析与策略展望报告",
|
|||
|
|
"估值日:2026年3月10日",
|
|||
|
|
"工银瑞信基金管理有限公司"
|
|||
|
|
),
|
|||
|
|
|
|||
|
|
// 正文
|
|||
|
|
heading1("一、投资策略与研判框架"),
|
|||
|
|
bodyParagraph("本组合采用"多维度全局配置"策略,横跨A股和港股市场..."),
|
|||
|
|
|
|||
|
|
heading2("1.1 八大核心研判及验证"),
|
|||
|
|
threeLineTable(
|
|||
|
|
["#", "核心判断", "验证状态", "关键证据"],
|
|||
|
|
[
|
|||
|
|
["1", "恒生科技有超额收益", "1月验证后逆转", "1月+4.2%→3月-12.45%"],
|
|||
|
|
["2", "AI国内参与性价比不高", "已验证", "A股AI概念高位回落"],
|
|||
|
|
]
|
|||
|
|
),
|
|||
|
|
|
|||
|
|
heading1("二、组合现状概览"),
|
|||
|
|
bodyParagraph("截至估值日,组合单位净值1.4430,权益仓位合计占比约23%。"),
|
|||
|
|
],
|
|||
|
|
}],
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 导出
|
|||
|
|
Packer.toBuffer(doc).then(buffer => {
|
|||
|
|
fs.writeFileSync("output.docx", buffer);
|
|||
|
|
console.log("✅ 已生成: output.docx");
|
|||
|
|
});
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 运行
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
node generate_report.js
|
|||
|
|
# 验证(可选,使用官方 docx skill 的验证工具)
|
|||
|
|
python .claude/skills/docx/scripts/office/validate.py output.docx
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 辅助函数速查
|
|||
|
|
|
|||
|
|
| 函数 | 用途 | 格式 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `titleBlock(title, subtitle, date, institution)` | 报告标题区(无封面) | 黑体三号居中 |
|
|||
|
|
| `heading1(text)` | 一级标题 | 黑体三号 16pt |
|
|||
|
|
| `heading2(text)` | 二级标题 | 楷体四号加粗 |
|
|||
|
|
| `bodyParagraph(text, {bold, alignment})` | 正文段落 | 仿宋四号,首行缩进,28pt行距 |
|
|||
|
|
| `threeLineTable(headers, rows)` | 三线表 | 顶底1.5pt,内部0.75pt,无竖线 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 编辑现有文档
|
|||
|
|
|
|||
|
|
当需要修改已有 .docx 文件时,使用官方 docx skill 的 unpack/pack 工作流:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 1. 解包
|
|||
|
|
python .claude/skills/docx/scripts/office/unpack.py document.docx unpacked/
|
|||
|
|
|
|||
|
|
# 2. 编辑 unpacked/word/document.xml(直接用 Edit 工具修改 XML)
|
|||
|
|
|
|||
|
|
# 3. 重新打包
|
|||
|
|
python .claude/skills/docx/scripts/office/pack.py unpacked/ output.docx --original document.docx
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 公文格式关键 XML 片段
|
|||
|
|
|
|||
|
|
#### 页面设置(插入 `<w:sectPr>` 中)
|
|||
|
|
|
|||
|
|
```xml
|
|||
|
|
<w:pgSz w:w="11906" w:h="16838"/>
|
|||
|
|
<w:pgMar w:top="2098" w:right="1474" w:bottom="1985" w:left="1588"
|
|||
|
|
w:header="851" w:footer="851" w:gutter="0"/>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 正文段落格式
|
|||
|
|
|
|||
|
|
```xml
|
|||
|
|
<w:pPr>
|
|||
|
|
<w:spacing w:line="560" w:lineRule="exact"/>
|
|||
|
|
<w:ind w:firstLine="560"/>
|
|||
|
|
<w:jc w:val="both"/>
|
|||
|
|
</w:pPr>
|
|||
|
|
<w:rPr>
|
|||
|
|
<w:rFonts w:ascii="FangSong" w:eastAsia="仿宋" w:hAnsi="FangSong"/>
|
|||
|
|
<w:sz w:val="28"/>
|
|||
|
|
<w:szCs w:val="28"/>
|
|||
|
|
</w:rPr>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 一级标题
|
|||
|
|
|
|||
|
|
```xml
|
|||
|
|
<w:pPr>
|
|||
|
|
<w:pStyle w:val="Heading1"/>
|
|||
|
|
<w:spacing w:line="640" w:lineRule="exact" w:before="240" w:after="120"/>
|
|||
|
|
</w:pPr>
|
|||
|
|
<w:rPr>
|
|||
|
|
<w:rFonts w:ascii="SimHei" w:eastAsia="黑体" w:hAnsi="SimHei"/>
|
|||
|
|
<w:b/>
|
|||
|
|
<w:sz w:val="32"/>
|
|||
|
|
<w:szCs w:val="32"/>
|
|||
|
|
</w:rPr>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 三线表边框(`<w:tblBorders>` 内)
|
|||
|
|
|
|||
|
|
```xml
|
|||
|
|
<w:top w:val="single" w:sz="12" w:space="0" w:color="000000"/>
|
|||
|
|
<w:bottom w:val="single" w:sz="12" w:space="0" w:color="000000"/>
|
|||
|
|
<w:insideH w:val="single" w:sz="6" w:space="0" w:color="000000"/>
|
|||
|
|
<w:left w:val="none" w:sz="0" w:space="0" w:color="auto"/>
|
|||
|
|
<w:right w:val="none" w:sz="0" w:space="0" w:color="auto"/>
|
|||
|
|
<w:insideV w:val="none" w:sz="0" w:space="0" w:color="auto"/>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 从现有文档提取内容并重建
|
|||
|
|
|
|||
|
|
当需要对已有文档「重排版」时,推荐提取内容后用 docx-js 重建:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 提取为 Markdown
|
|||
|
|
pandoc original.docx -o content.md
|
|||
|
|
|
|||
|
|
# 然后读取 content.md,用上面的模板函数构建新文档
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
这比原地修改格式更可靠,避免了样式冲突和继承问题。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 格式检查清单
|
|||
|
|
|
|||
|
|
- [ ] 纸张:A4(11906 × 16838 DXA)
|
|||
|
|
- [ ] 页边距:上37 / 下35 / 左28 / 右26 mm
|
|||
|
|
- [ ] 一级标题:黑体三号(16pt),固定行距 32pt
|
|||
|
|
- [ ] 二级标题:楷体四号加粗(14pt),固定行距 28pt
|
|||
|
|
- [ ] 正文:仿宋四号(14pt),固定行距 28pt,首行缩进 2 字符
|
|||
|
|
- [ ] 表格:三线表,表头黑体小四 + 浅灰底,内容仿宋小四
|
|||
|
|
- [ ] 页脚:居中 "— 页码 —",仿宋五号
|
|||
|
|
- [ ] 字体必须设置 `eastAsia` 属性
|
|||
|
|
|
|||
|
|
## 依赖
|
|||
|
|
|
|||
|
|
- **Node.js**: v18+
|
|||
|
|
- **docx**: `npm install docx`(docx-js 库)
|
|||
|
|
- **pandoc**: 文本提取(可选)
|
|||
|
|
- **官方 docx skill**: 提供 validate.py / unpack.py / pack.py
|