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>
288 lines
9.9 KiB
JavaScript
288 lines
9.9 KiB
JavaScript
/**
|
||
* GB/T 9704-2012 公文格式常量
|
||
* 供所有公文文档生成脚本共享
|
||
*/
|
||
|
||
const { AlignmentType, BorderStyle, WidthType, ShadingType, LevelFormat,
|
||
HeadingLevel, Paragraph, TextRun, Table, TableRow, TableCell,
|
||
Header, Footer, PageNumber } = require("docx");
|
||
|
||
// ─── 页面 ─────────────────────────────────────────────────
|
||
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, // 210 - 28 - 26 mm
|
||
};
|
||
|
||
// ─── 字体 ─────────────────────────────────────────────────
|
||
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" },
|
||
};
|
||
|
||
// ─── 字号(half-point) ───────────────────────────────────
|
||
const SIZE = {
|
||
ER_HAO: 44, // 二号 22pt
|
||
SAN_HAO: 32, // 三号 16pt
|
||
SI_HAO: 28, // 四号 14pt
|
||
XIAO_SI: 24, // 小四 12pt
|
||
WU_HAO: 21, // 五号 10.5pt
|
||
};
|
||
|
||
// ─── 行距(twips = pt × 20) ─────────────────────────────
|
||
const LINE = {
|
||
BODY: { line: 560, lineRule: "exact" }, // 28pt
|
||
HEADING1: { line: 640, lineRule: "exact" }, // 32pt
|
||
HEADING2: { line: 560, lineRule: "exact" }, // 28pt
|
||
TABLE: { line: 440, lineRule: "exact" }, // 22pt
|
||
TITLE: { line: 640, lineRule: "exact" }, // 32pt
|
||
COMPACT: { line: 360, lineRule: "exact" }, // 18pt
|
||
};
|
||
|
||
// ─── 缩进(DXA) ─────────────────────────────────────────
|
||
const INDENT = {
|
||
FIRST_LINE: 560, // 首行缩进 2 字符
|
||
BULLET_LEFT: 1120,
|
||
BULLET_HANG: 560,
|
||
};
|
||
|
||
// ─── 边框 ─────────────────────────────────────────────────
|
||
const BORDER = {
|
||
NONE: { style: BorderStyle.NONE, size: 0 },
|
||
THICK: { style: BorderStyle.SINGLE, size: 12, color: "000000" }, // 1.5pt
|
||
THIN: { style: BorderStyle.SINGLE, size: 6, color: "000000" }, // 0.75pt
|
||
};
|
||
|
||
// ─── 样式预设 ─────────────────────────────────────────────
|
||
function getStyles() {
|
||
return {
|
||
default: {
|
||
document: {
|
||
run: { font: FONT.FANGSONG, size: SIZE.SI_HAO },
|
||
paragraph: { spacing: LINE.BODY },
|
||
},
|
||
},
|
||
paragraphStyles: [
|
||
{
|
||
id: "Heading1", name: "Heading 1",
|
||
basedOn: "Normal", next: "Normal", quickFormat: true,
|
||
run: { font: FONT.HEITI, size: SIZE.SAN_HAO, bold: true },
|
||
paragraph: { spacing: { ...LINE.HEADING1, before: 240, after: 120 }, outlineLevel: 0 },
|
||
},
|
||
{
|
||
id: "Heading2", name: "Heading 2",
|
||
basedOn: "Normal", next: "Normal", quickFormat: true,
|
||
run: { font: FONT.KAITI, size: SIZE.SI_HAO, bold: true },
|
||
paragraph: { spacing: { ...LINE.HEADING2, before: 120, after: 60 }, outlineLevel: 1 },
|
||
},
|
||
],
|
||
};
|
||
}
|
||
|
||
function getNumbering() {
|
||
return {
|
||
config: [{
|
||
reference: "bullets",
|
||
levels: [{
|
||
level: 0, format: LevelFormat.BULLET, text: "\u2022",
|
||
alignment: AlignmentType.LEFT,
|
||
style: {
|
||
run: { font: FONT.FANGSONG, size: SIZE.SI_HAO },
|
||
paragraph: { indent: { left: INDENT.BULLET_LEFT, hanging: INDENT.BULLET_HANG } },
|
||
},
|
||
}],
|
||
}],
|
||
};
|
||
}
|
||
|
||
// ─── 辅助函数 ─────────────────────────────────────────────
|
||
|
||
/** 报告标题区(无封面,居中) */
|
||
function titleBlock(title, subtitle, date, institution) {
|
||
const items = [];
|
||
items.push(new Paragraph({
|
||
alignment: AlignmentType.CENTER,
|
||
spacing: { ...LINE.TITLE, after: 0 },
|
||
children: [new TextRun({ text: title, font: FONT.HEITI, size: SIZE.SAN_HAO, bold: true })],
|
||
}));
|
||
if (subtitle) {
|
||
items.push(new Paragraph({
|
||
alignment: AlignmentType.CENTER,
|
||
spacing: { ...LINE.TITLE, after: 120 },
|
||
children: [new TextRun({ text: subtitle, font: FONT.HEITI, size: SIZE.SAN_HAO })],
|
||
}));
|
||
}
|
||
items.push(new Paragraph({
|
||
alignment: AlignmentType.CENTER,
|
||
spacing: { ...LINE.BODY, after: 0 },
|
||
children: [new TextRun({ text: date, font: FONT.FANGSONG, size: SIZE.SI_HAO })],
|
||
}));
|
||
if (institution) {
|
||
items.push(new Paragraph({
|
||
alignment: AlignmentType.CENTER,
|
||
spacing: { ...LINE.BODY, after: 240 },
|
||
children: [new TextRun({ text: institution, font: FONT.FANGSONG, size: SIZE.SI_HAO })],
|
||
}));
|
||
}
|
||
return items;
|
||
}
|
||
|
||
/** 一级标题 */
|
||
function h1(text) {
|
||
return new Paragraph({
|
||
heading: HeadingLevel.HEADING_1,
|
||
spacing: { ...LINE.HEADING1, before: 240, after: 120 },
|
||
children: [new TextRun({ text, font: FONT.HEITI, size: SIZE.SAN_HAO, bold: true })],
|
||
});
|
||
}
|
||
|
||
/** 二级标题 */
|
||
function h2(text) {
|
||
return new Paragraph({
|
||
heading: HeadingLevel.HEADING_2,
|
||
spacing: { ...LINE.HEADING2, before: 120, after: 60 },
|
||
children: [new TextRun({ text, font: FONT.KAITI, size: SIZE.SI_HAO, bold: true })],
|
||
});
|
||
}
|
||
|
||
/** 正文段落 */
|
||
function body(text, opts = {}) {
|
||
const { bold = false, align = AlignmentType.JUSTIFIED, indent = true } = opts;
|
||
return new Paragraph({
|
||
alignment: align,
|
||
indent: indent ? { firstLine: INDENT.FIRST_LINE } : undefined,
|
||
spacing: LINE.BODY,
|
||
children: [new TextRun({ text, font: FONT.FANGSONG, size: SIZE.SI_HAO, bold })],
|
||
});
|
||
}
|
||
|
||
/** 多 run 正文段落(支持混排加粗) */
|
||
function bodyRuns(runs, opts = {}) {
|
||
const { align = AlignmentType.JUSTIFIED, indent = true } = opts;
|
||
return new Paragraph({
|
||
alignment: align,
|
||
indent: indent ? { firstLine: INDENT.FIRST_LINE } : undefined,
|
||
spacing: LINE.BODY,
|
||
children: runs.map(r => {
|
||
if (typeof r === "string") {
|
||
return new TextRun({ text: r, font: FONT.FANGSONG, size: SIZE.SI_HAO });
|
||
}
|
||
return new TextRun({ font: FONT.FANGSONG, size: SIZE.SI_HAO, ...r });
|
||
}),
|
||
});
|
||
}
|
||
|
||
/** 列表项 */
|
||
function bullet(text) {
|
||
return new Paragraph({
|
||
numbering: { reference: "bullets", level: 0 },
|
||
spacing: LINE.BODY,
|
||
children: [new TextRun({ text, font: FONT.FANGSONG, size: SIZE.SI_HAO })],
|
||
});
|
||
}
|
||
|
||
/** 三线表 */
|
||
function threeLineTable(headers, rows, opts = {}) {
|
||
const { colWidths } = opts;
|
||
const widths = colWidths || headers.map(() => Math.floor(PAGE.CONTENT_WIDTH / headers.length));
|
||
|
||
function cell(text, isHeader, isLastRow, colIdx) {
|
||
const borders = {
|
||
top: isHeader ? BORDER.THICK : BORDER.NONE,
|
||
bottom: isHeader ? BORDER.THIN : (isLastRow ? BORDER.THICK : BORDER.NONE),
|
||
left: BORDER.NONE,
|
||
right: BORDER.NONE,
|
||
};
|
||
return new TableCell({
|
||
borders,
|
||
width: { size: widths[colIdx], type: WidthType.DXA },
|
||
shading: isHeader ? { fill: "1F3864", type: ShadingType.CLEAR } : undefined,
|
||
margins: { top: 40, bottom: 40, left: 80, right: 80 },
|
||
children: [new Paragraph({
|
||
alignment: AlignmentType.CENTER,
|
||
spacing: LINE.TABLE,
|
||
children: [new TextRun({
|
||
text: String(text),
|
||
font: isHeader ? FONT.HEITI : FONT.FANGSONG,
|
||
size: SIZE.XIAO_SI,
|
||
bold: isHeader,
|
||
color: isHeader ? "FFFFFF" : undefined,
|
||
})],
|
||
})],
|
||
});
|
||
}
|
||
|
||
// 表级边框全部 NONE,由 cell 级别控制三线效果
|
||
const noBorder = { style: BorderStyle.NONE, size: 0, color: "FFFFFF" };
|
||
return new Table({
|
||
width: { size: PAGE.CONTENT_WIDTH, type: WidthType.DXA },
|
||
columnWidths: widths,
|
||
borders: {
|
||
top: noBorder, bottom: noBorder,
|
||
left: noBorder, right: noBorder,
|
||
insideHorizontal: noBorder, insideVertical: noBorder,
|
||
},
|
||
rows: [
|
||
new TableRow({ children: headers.map((h, i) => cell(h, true, false, i)) }),
|
||
...rows.map((row, ri) =>
|
||
new TableRow({
|
||
children: row.map((c, ci) => cell(c, false, ri === rows.length - 1, ci)),
|
||
})
|
||
),
|
||
],
|
||
});
|
||
}
|
||
|
||
/** 标准页脚(— 页码 —) */
|
||
function pageFooter() {
|
||
return new Footer({
|
||
children: [new Paragraph({
|
||
alignment: AlignmentType.CENTER,
|
||
children: [
|
||
new TextRun({ text: "— ", font: FONT.FANGSONG, size: SIZE.WU_HAO }),
|
||
new TextRun({ children: [PageNumber.CURRENT], font: FONT.FANGSONG, size: SIZE.WU_HAO }),
|
||
new TextRun({ text: " —", font: FONT.FANGSONG, size: SIZE.WU_HAO }),
|
||
],
|
||
})],
|
||
});
|
||
}
|
||
|
||
/** 标准页眉 */
|
||
function pageHeader(text = "内部资料 注意保密") {
|
||
return new Header({
|
||
children: [new Paragraph({
|
||
alignment: AlignmentType.CENTER,
|
||
children: [new TextRun({
|
||
text, font: FONT.FANGSONG, size: SIZE.WU_HAO, color: "808080",
|
||
})],
|
||
})],
|
||
});
|
||
}
|
||
|
||
/** section 属性(页面设置) */
|
||
function sectionProps(opts = {}) {
|
||
return {
|
||
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,
|
||
},
|
||
},
|
||
...opts,
|
||
};
|
||
}
|
||
|
||
module.exports = {
|
||
PAGE, FONT, SIZE, LINE, INDENT, BORDER,
|
||
getStyles, getNumbering, sectionProps,
|
||
titleBlock, h1, h2, body, bodyRuns, bullet,
|
||
threeLineTable, pageFooter, pageHeader,
|
||
};
|