Pretty Tables

基于 docx 库创建跨平台兼容的精美 Word 表格,支持专业排版与样式。

已扫描
适合谁
需要批量生成专业文档的办公人员、自动化报告或行程表制作的开发者
不适合谁
不熟悉 DOCX 格式编程的普通用户、希望一键生成无需代码配置的用户
国内可用性
需网络配置。可能需要网络配置或第三方服务可访问。
安装难度
新手友好(★☆☆)。基于终端操作、依赖、API Key 和本地环境要求的初步判断。

安装与下载

openclaw skills install @happytreees/pretty-tables

Skill 说明

命令、参数、文件名以原文为准

pretty-tables

在 Word 文档中创建美观的表格,确保在 Word 和 Google Docs 中都能一致显示。

⚠️ 5 个关键规则

经过大量测试,以下是确保表格在所有平台正确渲染的必要规范:

1. 双重宽度设置(最重要)

每个表格都需要在表格层级每个单元格层级分别设置宽度:

// 表格层级
new Table({
  width: { size: 9360, type: WidthType.DXA },  // 总宽度
  columnWidths: [1872, 7488],                   // 每列宽度
  rows: [...]
})

// 单元格层级 - 每个单元格都必须设置!
new TableCell({
  width: { size: 1872, type: WidthType.DXA },   // 该单元格的宽度
  children: [...]
})

如果任一位置缺失,某些平台上的表格将显示异常。

2. 仅使用 DXA,不要使用百分比

百分比在 Google Docs 中会失效。始终使用 DXA(twips):

  • 1 英寸 = 1440 DXA
  • A4 或 US Letter(8.5 英寸)带 1 英寸边距时,内容宽度为 9360 DXA
// ❌ 错误 - 在 Google Docs 中失效
width: { size: 100, type: WidthType.PERCENTAGE }

// ✅ 正确
width: { size: 9360, type: WidthType.DXA }

3. 使用 ShadingType.CLEAR,而非 SOLID

这是一个微妙但关键的问题:

const { ShadingType } = require('docx');

// ❌ 错误 - 产生黑色背景
shading: { type: ShadingType.SOLID, fill: "E0F2F1" }

// ✅ 正确 - 正确应用填充颜色
shading: { type: ShadingType.CLEAR, fill: "E0F2F1" }

4. 添加单元格内边距(边距)

防止文字紧贴边框:

const cellMargins = { top: 80, bottom: 80, left: 120, right: 120 };

new TableCell({
  children: [...],
  margins: cellMargins
})

5. 列宽总和必须精确相等

对于 US Letter 带 1 英寸边距的情况:9360 DXA

// 2 列:20% + 80%
columnWidths: [1872, 7488]  // = 9360 ✓

// 3 列:均分
columnWidths: [3120, 3120, 3120]  // = 9360 ✓

// 3 列:25% + 25% + 50%
columnWidths: [2340, 2340, 4680]  // = 9360 ✓

完整可运行示例

const { Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
         WidthType, AlignmentType, BorderStyle, TableLayoutType, ShadingType } = require('docx');
const fs = require('fs');

// 常量
const TOTAL_WIDTH = 9360;  // US Letter 带 1" 边距
const TIME_COL = 1872;     // 20%
const CONTENT_COL = 7488;  // 80%

// 浅灰色边框
const cellBorders = {
  top: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
  bottom: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
  left: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
  right: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" }
};

// 单元格内边距
const cellMargins = { top: 80, bottom: 80, left: 120, right: 120 };

// 表格生成器
function table(columnWidths, rows) {
  return new Table({
    layout: TableLayoutType.FIXED,
    width: { size: columnWidths.reduce((a, b) => a + b, 0), type: WidthType.DXA },
    columnWidths: columnWidths,
    rows: rows
  });
}

// 标题行
function headerRow(text, color) {
  return new TableRow({
    children: [
      new TableCell({
        children: [new Paragraph({
          children: [new TextRun({ text, bold: true, size: 32, color: "FFFFFF" })],
          alignment: AlignmentType.CENTER
        })],
        width: { size: TIME_COL + CONTENT_COL, type: WidthType.DXA },
        columnSpan: 2,
        shading: { type: ShadingType.CLEAR, fill: color },
        borders: cellBorders,
        margins: cellMargins
      })
    ]
  });
}

// 数据行
function dataRow(time, activity, color) {
  return new TableRow({
    children: [
      new TableCell({
        children: [new Paragraph({
          children: [new TextRun({ text: time, bold: true, size: 26, color })],
          alignment: AlignmentType.CENTER
        })],
        width: { size: TIME_COL, type: WidthType.DXA },
        borders: cellBorders,
        margins: cellMargins
      }),
      new TableCell({
        children: [new Paragraph({
          children: [new TextRun({ text: activity, size: 26 })]
        })],
        width: { size: CONTENT_COL, type: WidthType.DXA },
        borders: cellBorders,
        margins: cellMargins
      })
    ]
  });
}

// 构建文档
const doc = new Document({
  sections: [{
    properties: {
      page: { margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 } }
    },
    children: [
      table([TIME_COL, CONTENT_COL], [
        headerRow("SCHEDULE", "1565C0"),
        dataRow("9:00 AM", "到达目的地", "1565C0"),
        dataRow("10:00 AM", "上午活动", "1565C0"),
        dataRow("12:00 PM", "午餐休息", "1565C0"),
      ])
    ]
  }]
});

Packer.toBuffer(doc).then(buffer => {
  fs.writeFileSync('output.docx', buffer);
});

列宽速查表

适用于 US Letter(8.5 英寸)带 1 英寸边距 = 9360 DXA

布局列宽设置总和
2 列(20/80)[1872, 7488]9360
2 列(25/75)[2340, 7020]9360
2 列(30/70)[2808, 6552]9360
2 列(均分)[4680, 4680]9360
3 列(均分)[3120, 3120, 3120]9360
3 列(25/25/50)[2340, 2340, 4680]9360
3 列(20/30/50)[1872, 2808, 4680]9360
4 列(均分)[2340, 2340, 2340, 2340]9360

常见错误

缺少单元格宽度:

new TableCell({ children: [...] })  // 未设置宽度!

使用百分比:

width: { size: 100, type: WidthType.PERCENTAGE }  // 在 Google Docs 中失效

错误的阴影类型:

shading: { type: ShadingType.SOLID, fill: "E0F2F1" }  // 产生黑色背景!

列宽总和不正确:

columnWidths: [2000, 7000]  // 总和为 9000,不是 9360

正确用法:

new TableCell({
  width: { size: 1872, type: WidthType.DXA },
  shading: { type: ShadingType.CLEAR, fill: "E0F2F1" },
  margins: { top: 80, bottom: 80, left: 120, right: 120 },
  borders: cellBorders,
  children: [...]
})

颜色调色板

表头颜色:

颜色十六进制用途
蓝色1565C0默认表头
红色C62828重要信息
绿色2E7D32成功提示
橙色EF6C00警告提示
紫色6A1B9A特殊标记
青绿色00695C信息提示

浅色背景:

颜色十六进制用途
浅蓝色BBDEFB高亮显示
浅绿色C8E6C9成功状态
浅黄色FFF9C4注释说明
浅灰色F5F5F5交替行背景

为何此方案有效

以下组合确保了表格在 Microsoft Word、Google Docs 及其他 DOCX 查看器中正确渲染:

  1. 精确的 DXA 宽度设置,应用于表格及各单元格
  2. 使用 ShadingType.CLEAR 实现正确的颜色填充
  3. 一致的内边距设置,提升可读性
  4. 列宽总和精确匹配,保证布局准确

故障排除

问:表格在 Google Docs 中过窄

  • 确保使用 WidthType.DXA,而非 PERCENTAGE
  • 检查每个 TableCell 是否都设置了 width 属性

问:单元格背景显示为黑色

  • ShadingType.SOLID 改为 ShadingType.CLEAR

问:文本紧贴边框

  • 在每个单元格中添加 margins: { top: 80, bottom: 80, left: 120, right: 120 }

问:列宽不均匀

  • 确认列宽总和精确等于表格总宽度(例如 1 英寸边距时为 9360)
H
@happytreees

已收录 1 个 Skill

相关推荐