skills/docx-cn/SKILL.md
wangyitong a65adcc2e5 Initial commit: merged, deduplicated, and vetted skill collection
Sources: extracted from two upstream archives (skill-repo, skills-main),
merged with the following policy:

- 15 broken symlinks (pointing to /Users/jameslee/.cc-switch/skills or
  ../../.agents/skills on a foreign machine) discarded
- 3 real name collisions with identical content (ai-pair, ifind-http-api,
  zhipu-websearch) kept as one copy
- Functional overlaps deduped keeping the strongest variant:
  - docx family: kept docx (official, full toolchain) + docx-cn
    (GB/T 9704 Chinese official-document constants),
    dropped docx_writer (no scripts, name collided with docx)
  - humanizer family: kept humanizer-zh (6 zh reference docs),
    dropped humanizer (en, redundant for CN workflow)
- Skills that only ran in a foreign environment removed:
  ablemind-ops, app-publish, hlb-design-system, openclaw-adj-skill,
  claude-driver
- alphapai excluded from this public repo because its SKILL.md hard-coded
  live credentials

Result: 25 skills, 572 files, ~7.5 MB.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 14:47:12 +08:00

15 KiB
Raw Permalink Blame History

name description
docx-cn 基于 docx-js (Node.js) 创建和编辑符合 GB/T 9704-2012 中国公文格式标准的 Word 文档。当用户需要生成公文、研究报告、内部文件等中文 Word 文档时使用。触发词公文、Word、docx、报告排版、格式规范、GB/T 9704。

中文公文 Word 文档生成docx-js

概述

基于官方 docx skill 微调,专为中文公文 / 金融研究报告场景优化。 使用 docx-jsNode.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 DXAspacing/行距用)
font size 单位 = half-point半磅如 14pt = 28

页面设置A4

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 页脚、注释

字体规范

// 中文字体必须同时设置 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"   },  // 备用
};

行距与段落

const SPACING = {
  // 行距twips = pt × 20lineRule: "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
};

创建新文档

安装

npm install docx   # 项目内安装即可,无需全局

完整模板:公文报告(无封面)

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");
});

运行

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 工作流:

# 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> 中)

<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"/>

正文段落格式

<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>

一级标题

<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> 内)

<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 重建:

# 提取为 Markdown
pandoc original.docx -o content.md

# 然后读取 content.md用上面的模板函数构建新文档

这比原地修改格式更可靠,避免了样式冲突和继承问题。


格式检查清单

  • 纸张A411906 × 16838 DXA
  • 页边距上37 / 下35 / 左28 / 右26 mm
  • 一级标题黑体三号16pt固定行距 32pt
  • 二级标题楷体四号加粗14pt固定行距 28pt
  • 正文仿宋四号14pt固定行距 28pt首行缩进 2 字符
  • 表格:三线表,表头黑体小四 + 浅灰底,内容仿宋小四
  • 页脚:居中 "— 页码 —",仿宋五号
  • 字体必须设置 eastAsia 属性

依赖

  • Node.js: v18+
  • docx: npm install docxdocx-js 库)
  • pandoc: 文本提取(可选)
  • 官方 docx skill: 提供 validate.py / unpack.py / pack.py