mcpField
O
officecli-data-dashboard

officecli-data-dashboard

github.com/iofficeai数据与数据库32.7k101 次安装今天更新
代码仓库

关于 officecli-data-dashboard

officecli-data-dashboard 是基于 officecli-xlsx 的技能,用于指导构建以 Dashboard 工作表为入口的电子表格仪表板。它强调公式驱动的 KPI、引用单元格范围的图表,以及可视化格式规则。

主要功能

  1. 01创建公式驱动的 KPI 卡片
  2. 02用单元格范围连接图表数据
  3. 03在 Dashboard 工作表组织图表与迷你图
  4. 04按数据规模调整仪表板复杂度
  5. 05应用条件格式辅助数据识读
  6. 06验证工作簿并检查渲染结果

适用场景

  1. 01将销售 CSV 汇总为管理层仪表板
  2. 02制作包含 KPI 和趋势图的运营看板
  3. 03展示随源数据更新的指标与图表
  4. 04准备适合打印的仪表板工作簿

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/iofficeai/officecli --skill officecli-data-dashboard

安装后在 Claude Code 中输入/officecli-data-dashboard确认已加载。

详细介绍

数据仪表盘(基于 officecli-xlsx 的场景层)

仪表盘并不是"带图表的电子表格"。它是一种组合:用户打开后直接看到的一个 Dashboard 工作表,其中包含由公式驱动的 KPI 卡片、与单元格区域联动的图表、迷你图,以及语义化的条件格式。其他所有内容(原始数据、汇总计算)都属于上游基础设施,用户无需打开。本 Skill 讲解的是这种组合模式。关于 xlsx 引擎的一切(单元格、公式、批量 JSON、Shell 转义、validate、HTML 预览)均来自 officecli-xlsx,此处不再重复讲解。

安装

如果系统中没有 officecli:

  • macOS / Linux:curl -fsSL https://d.officecli.ai/install.sh | bash
  • Windows(PowerShell):irm https://d.officecli.ai/install.ps1 | iex

运行 officecli --version 进行验证(如果 PATH 尚未生效,请打开新的终端)。如果安装失败,请从 https://github.com/iOfficeAI/OfficeCLI/releases 下载二进制文件。

⚠️ 先查帮助原则

当属性名、枚举值或别名不确定时,先查阅帮助,不要凭猜测使用。

officecli help xlsx                          # element list
officecli help xlsx chart                    # full schema for charts
officecli help xlsx sparkline                # sparklines
officecli help xlsx conditionalformatting    # all CF rule types

帮助信息反映的是已安装的 CLI 版本。当本 Skill 与帮助信息不一致时,以帮助为准。延迟添加的键(combosplit、holesize)仅适用于 add 命令,详见参考部分。

思维模型与继承关系

本 Skill 继承 officecli-xlsx 的全部 xlsx 硬性规则,包括 Shell 转义、零公式错误、视觉交付底线、批量 JSON 结构({"command":"set"|"add","path":...,"props":{...}},键名是 command,而不是 action)、批量 JSON 的点号名称规则、图表数据源形式、批量与常驻模式的限制,以及 validate 的使用规范。请先阅读 officecli-xlsx,并遵守这些规则,此处不再重复讲解。

反向分流——以下情况请勿使用本 Skill:

  • 需求是单表的 CSV 带格式跟踪表(没有 Dashboard 工作表、没有 KPI 卡片、图表不超过 1 个)→ 请改用 officecli-xlsx。
  • 需求是带有蓝色输入、黑色公式和跨表驱动的三表联动 / DCF / LBO 财务模型 → 请使用 officecli-financial-model。
  • 需求是周度状态报告,只有一个 SUMIF 汇总和一个少于 10 行数据的图表 → 请使用 officecli-xlsx。

本 Skill 仅适用于:"用户首先打开的 Dashboard 工作表、多个 KPI 卡片、多个图表,以及部分条件格式或迷你图"的需求。

Shell 与执行规范

→ 基础规范(转义、用于 ! 的 heredoc、增量执行)请参见 officecli-xlsx 的 Shell 与执行规范部分。

仪表盘特有的两条补充规则:

  • 图表 add 命令超过 180 个字符时,必须用行尾 \ 分行书写;切勿把图表命令压缩在一行中。命令越长,Shell 转义错误隐藏其中的可能性就越高。
  • 统计多个实例的数量时,使用 query --json | jq length,而不是 raw-get | grep -c。 例如,要统计"我有多少个图表?",可使用 officecli query "$FILE" chart --json | jq '.data.results | length'。

核心原则

五条不可妥协的原则。只要违反其中任何一条,输出的就不是仪表板,而只是一张碰巧带图表的电子表格。

  1. KPI 由公式驱动。 仪表板工作表上的每个 KPI 值都是公式(SUM、AVERAGE、IFERROR((...-...)/...,0) 等),引用数据/汇总工作表中的单元格。切勿硬编码计算得出的数值。明天底层数据变化时,KPI 会在打开文件时自动更新。

  2. 图表使用单元格区域引用。 每个图表系列都从单元格区域读取数据,例如 series1.values="Sheet1!B2:B13"。内联的 data="Revenue:100,200,300" 只适用于五分钟的演示,不适用于交付的仪表板。唯一例外是数据需要 Excel 无法表达的聚合运算(罕见情况),此时需在注释单元格中说明该例外。

  3. 仪表板优先的架构。 KPI 标签单元格、KPI 值单元格、图表和迷你图都位于仪表板工作表中,这是用户打开文件后看到的唯一工作表。原始导入数据和 SUMIFS 汇总位于数据/汇总工作表中,处于仪表板的上游。用户应无需切换标签页即可找到答案。

  4. 图表数据源只使用可见单元格。 LibreOffice 在渲染时不会计算隐藏列或隐藏工作表中的公式。若图表的 series1.values 指向隐藏列中的 SUMIFS,则渲染结果为空白。推荐做法:将数据汇总到可见的汇总工作表中,图表指向汇总表的单元格,仅隐藏不作为图表数据源的辅助列。

  5. 根据数据规模控制复杂度。 10 行的数据集不应配备 5 个 KPI 和 4 张图表,200 行的数据集也不应只配备 1 个 KPI 和 1 张图表。应根据输入规模调整组成(参见设计思路中的表格)。过度设计与设计不足同样不可取。

要求

所有 officecli-xlsx 要求均适用(→ 参见 officecli-xlsx 输出要求部分)。仪表板还需满足以下要求:

  • 打开文件时仪表板为活动标签页。 在设置 activeTab="N" 之前,先通过 officecli query "$FILE" sheet 确认从 0 开始的工作表索引。切勿凭猜测填写索引。
  • 设置 calc.fullCalcOnLoad=true。 通过 officecli set "$FILE" / --prop calc.fullCalcOnLoad=true 设置。请勿使用 raw-set 写入 <calcPr>,否则会产生重复元素,导致校验失败。
  • 每次修改上游单元格后,刷新下游的 cachedValue。 fullCalcOnLoad=true 只会安排运行时重新计算,并不会刷新构建时的 cachedValue。执行 set B=100 → set E==B+D → fix B=150 后,E 的缓存值已过时,除非重新写入 E 的公式(或关闭后重新打开文件)。过时的缓存会导致向管理层展示“净变化 = 0”。
  • 每张图表都需有描述性标题,每个系列都需有名称。 图例中出现 "Series1" 说明工作尚未完成。
  • 每个 KPI 值单元格都必须有公式。 可通过以下命令验证:officecli query "$FILE" 'Dashboard!:has(formula)' --json | jq '.data.results | length',其结果应等于计划中的 KPI 数量。
  • 所有数据工作表都要填充表头行。 数据工作表、汇总工作表以及任何次要数据工作表的第 1 行都需填充颜色(例如 fill=1F3864 + font.color=FFFFFF + font.bold=true)。
  • 数据工作表达到 10 行以上时,至少对一个数值列设置 1 条条件格式规则。 20 行的表格若没有任何视觉辅助扫描,属于质量缺陷。
  • 仪表板数值列的宽度应根据预期的最宽 cachedValue 设置,而非固定为 22。 经验公式(24pt 粗体加货币数字格式):宽度 ≈ ceil((可见字符数 + 2) × 1.3)。例如,显示为 ¥1,958,414,250(含货币符号和逗号共 14 个可见字符)的 KPI 需要 宽度 ≥ 28;即使是 4 位数的 KPI,最小宽度也需为 22。若为 10 位以上数字的 KPI 硬编码宽度 22,用户看到的就会是 ###。
  • 迷你图所在行的行高不低于 20。 默认 15pt 行高下迷你图会显得像一条扁平的曲线,应设置 /Dashboard/row[N] height=22(若与 24pt 的 KPI 值单元格位于同一行,则设为 24)。
  • 打印交付物需设置作用域为仪表板的 _xlnm.Print_Area,隐藏非仪表板工作表,并添加 <pageSetup fitToPage/>。 三者缺一不可,否则打印流程会输出所有工作表,仪表板将落在第 2 页或之后。具体命令参见“打印就绪交付”部分。

快速开始

最小可用仪表板:12 个月营收 CSV → 4 个 KPI + 1 个折线图 + activeTab + fullCalcOnLoad。请根据实际数字调整,不要盲目复制粘贴。按阶段拆分,便于快速定位失败的阶段。

阶段 1 — 数据表:创建、导入、格式化。

FILE=my_dashboard.xlsx
officecli create "$FILE"
officecli import "$FILE" /Sheet1 --file sales.csv --header
officecli set "$FILE" '/Sheet1/col[A]' --prop width=12
officecli set "$FILE" '/Sheet1/col[B]' --prop width=15
officecli set "$FILE" '/Sheet1/B2:B13' --prop numFmt='$#,##0'
officecli set "$FILE" '/Sheet1/A1:B1' --prop fill=1F3864 --prop font.color=FFFFFF --prop font.bold=true

阶段 2 — 仪表板表 + 一个 KPI 卡片。

officecli add "$FILE" / --type sheet --prop name=Dashboard
officecli set "$FILE" '/Dashboard/col[A]' --prop width=22
officecli set "$FILE" '/Dashboard/col[B]' --prop width=12
officecli set "$FILE" /Dashboard/A1 --prop value="Total Revenue" --prop font.size=9 --prop font.color=666666 --prop bold=true
officecli set "$FILE" /Dashboard/A2 --prop 'formula==SUM(Sheet1!B2:B13)' --prop numFmt='$#,##0' --prop font.size=24 --prop bold=true --prop font.color=2E7D32

阶段 3 — 迷你图 + 图表。

officecli add "$FILE" /Dashboard --type sparkline --prop cell=B2 --prop range='Sheet1!B2:B13' --prop type=line --prop color=4472C4 --prop highPoint=true --prop highMarkerColor=FF0000
officecli add "$FILE" /Dashboard --type chart \
  --prop chartType=line \
  --prop title="Revenue Trend" \
  --prop series1.name="Revenue" \
  --prop series1.values='Sheet1!B2:B13' \
  --prop series1.categories='Sheet1!A2:A13' \
  --prop preset=dashboard --prop axisNumFmt='$#,##0' \
  --prop x=0 --prop y=5 --prop width=10 --prop height=15

阶段 4 — fullCalcOnLoad → activeTab(最后设置)→ 关闭 → 校验。

officecli set "$FILE" / --prop calc.fullCalcOnLoad=true

# Resolve Dashboard's 0-based index from the actual sheet list — never hardcode.
DASH_IDX=$(officecli query "$FILE" sheet --json \
  | jq '[.data.results[].path] | index("/Dashboard")')
officecli raw-set "$FILE" /workbook --xpath "//x:sheets" --action insertbefore \
  --xml "<bookViews xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><workbookView activeTab=\"$DASH_IDX\" /></bookViews>"
officecli close "$FILE"
officecli validate "$FILE"

已在 12 行营收 CSV 上完成端到端验证:validate 未报告任何错误,仪表板表首先打开,Dashboard/A2.cachedValue 可正确解析(测试数据为 2,075,000),图表渲染时数值已正确链接。

设计思路

以下仅为可选方案,并非固定模板。具体选择应由用户的数据和受众决定。

布局模式(选一种,并保持一致)

模式 1 — 高管摘要(董事会材料):KPI 条位于 A1:H4,图表从第 6 行开始纵向排列。

┌ KPI1 │ KPI2 │ KPI3 │ KPI4 ┐  rows 1-4
├──────┴──────┴──────┴──────┤
│     Chart 1 (wide)        │  rows 6-18
├───────────────┬───────────┤
│   Chart 2     │  Chart 3  │  rows 20-32

模式 2 — 运营控制台(实时运营):KPI 纵向排列于 A:B 列,图表填满 C:L 列。

│ KPI1 │                   │
│ KPI2 │    Chart 1        │  rows 1-12
│ KPI3 │                   │
│ KPI4 ├───────────────────┤
│ KPI5 │    Chart 2        │  rows 14-26

模式 3 — 记分卡(≥ 6 个 KPI,无主导图表):2×3 的卡片网格(标签 / 数值 / 迷你图)。

│ KPI1 │ KPI2 │ KPI3 │  rows 1-4
│ KPI4 │ KPI5 │ KPI6 │  rows 5-8
按数据量调整复杂度
行数KPI图表迷你图条件格式规则预设
< 101–21跳过0–1minimal
10–502–32仅在有连续时间序列时使用1–2dashboard
50–2003–52–3仅在有连续时间序列时使用2–3dashboard
200+3–53仅在有连续时间序列时使用3–4dashboard
图表类型选择
数据模式图表类型说明
单一序列的时间趋势line在波动较大的序列上添加 trendline=linear 以显示趋势方向
多个组成部分的时间趋势line(多序列)或 columnStacked当各组成部分相加具有实际意义的总和时使用堆叠
按时间顺序的类别比较column不要用 bar——横向条形图会破坏从左到右的时间阅读顺序
部分与整体的构成doughnut优先于 pie:chartType=pie 存在已知的 LibreOffice 空白渲染问题
预算与实际对比combo 搭配 combosplit=1第一个序列为柱形,其余为折线
相关性scatterX 轴通过 categories / series1.categories 设置——series1.xValues 不受支持
预设选项

每个图表都需使用 --prop preset=<name>。可选值:minimal、dashboard、corporate、magazine、colorful、monochrome、dark。在同一个仪表板表中选定一个并在所有图表中保持一致——混用预设会显得像是意外。

条件格式——语义化颜色

共有四种条件格式规则类型;在 add 时使用 --type <简写> 指定:

用途--type常用属性
数值条形(销售额、支出)databarsqref=B2:B13 color=4472C4 — 建议显式设置 min=0 max=<合理上限> 以获得可预测的缩放效果,但省略也有效(默认使用数据的最小值/最大值)
热力图(比率、增长率)colorscalesqref=D2:D13 mincolor=FFCDD2 midcolor=FFFFFF maxcolor=C8E6C9
状态指示iconsetsqref=E2:E13 iconset=3Arrows — 完整枚举值请参阅帮助
自定义业务规则formulacfsqref=B2:B13 'formula=$B2>=100000' fill=C8E6C9 font.color=2E7D32 — font.bold 同样适用于条件格式

在同一仪表板内保持一致的语义颜色:

  • 良好 / 正向:填充 C8E6C9,字体 2E7D32
  • 不良 / 负向:填充 FFCDD2,字体 C62828
  • 中性:填充 F5F5F5,字体 666666
KPI 卡片结构

卡片由一个标签单元格和一个数值单元格组成。标签为小号灰色字体(font.size=9,font.color=666666,加粗);数值为大号加粗字体(font.size=24,bold=true,numFmt,font.color 体现色调)。在卡片区域整行使用浅色填充(例如 F0F4FF),无需搭建合并单元格框架即可呈现卡片效果。数值列宽必须根据最大的 cachedValue 设定——不得窄于 22,对于 8 位及以上的货币数值通常应为 26–32(参见要求部分)。

按标题长度规划图表宽度

在 dashboard 预设的默认标题字体下,图表绘图区宽度(以列为单位)必须大于标题字符串所需宽度,否则标题会在单词中间被截断。经验法则:chart.width ≥ ceil(title.length × 0.18)。一个 35 个字符的标题("Department: Year-End Headcount vs Attrition Rate")需要 width ≥ 7;为保险起见可使用 10–12。如果无法加宽锚点区域,则将标题缩短至 25 个字符以内——在可直接用于董事会的交付物中出现截断标题是不可接受的。

officecli get chart[N] 会在 .data.results[0].format 中返回数值型 width(例如 width=480pt)以及 anchor(例如 "A6:K21")。两者都可用于第 2 道闸门检查;需要以列为单位计算预算时,可根据锚点字母推算列跨度(A→K = 10 列)。

可直接打印的交付(董事会材料 / 投资人发送 / 单页摘要)

触发条件:需求中包含"print"/"一页"/"董事会"/"投资人"。在 Dashboard 工作表上生成四类产物;隐藏所有非 Dashboard 工作表,使打印流程只输出一页。

# 1. Print_Area scoped to Dashboard (xlnm convention).
officecli add "$FILE" / --type namedrange --prop name=_xlnm.Print_Area --prop scope=Dashboard --prop 'refersTo=Dashboard!$A$1:$H$36'
# 2. fit-to-page on Dashboard.
officecli raw-set "$FILE" /Dashboard --xpath "//x:worksheet" --action prepend --xml '<sheetPr xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><pageSetUpPr fitToPage="1"/></sheetPr>'
# 3. Landscape page setup.
officecli raw-set "$FILE" /Dashboard --xpath "//x:sheetData" --action insertafter --xml '<pageSetup orientation="landscape" paperSize="9" fitToWidth="1" fitToHeight="1"/>'
# 4. Hide non-Dashboard sheets — Print_Area scope alone does NOT stop the print pipeline from emitting every visible sheet.
for S in Sheet1 Summary; do
  officecli raw-set "$FILE" /workbook --xpath "//x:sheet[@name='$S']" --action setattr --xml "state=hidden" || true
done

删除在 Data / Summary 工作表上设置的任何 Print_Area——相互冲突的打印范围会导致输出多页。

质量检查(必需 — 交付闸门)

假定存在问题,你的任务就是找出它们。 图表已渲染并不代表图表有意义。"validate 通过"不等于可以交付;"Dashboard 工作表读起来像是懂业务的人做出来的"才算可以交付。

"完成"前的最小循环

继承 xlsx 基线(view issues、公式错误查询、validate、HTML 预览扫描):→ 参见 officecli-xlsx §QA 最小循环。

然后运行仪表盘专属的交付门禁。每个门禁都使用带 .data.* 包装的 COUNT-then-if 模式,切勿串联 && echo OK || echo FAIL。

门禁 1 — KPI 公式覆盖。 每个计划中的 KPI 单元格都必须包含公式。请根据你的计划调整 -lt 2(4 个 KPI → -lt 4)。

KPI_FORMULAS=$(officecli query "$FILE" 'Dashboard!:has(formula)' --json | jq '.data.results | length')
[ "$KPI_FORMULAS" -lt 2 ] && { echo "REJECT Gate 1: $KPI_FORMULAS formula cells on Dashboard"; exit 1; }

门禁 2 — 图表数量与计划一致,每张图表都有数据且标题宽度合理。

CHART_COUNT=$(officecli query "$FILE" chart --json | jq '.data.results | length')
[ "$CHART_COUNT" -lt 1 ] && { echo "REJECT Gate 2: zero charts"; exit 1; }
col_num () { local c=$1 n=0; for ((k=0;k<${#c};k++)); do n=$((n*26+$(printf '%d' "'${c:$k:1}")-64)); done; echo "$n"; }
for i in $(seq 1 "$CHART_COUNT"); do
  JSON=$(officecli get "$FILE" "/Dashboard/chart[$i]" --json)
  SC=$(echo "$JSON" | jq -r '.data.results[0].format.seriesCount // 0')
  TITLE=$(echo "$JSON" | jq -r '.data.results[0].format.title // ""')
  ANCHOR=$(echo "$JSON" | jq -r '.data.results[0].format.anchor // ""')
  [ "$SC" = "0" ] || [ -z "$TITLE" ] && { echo "REJECT Gate 2: chart[$i] seriesCount=$SC title='$TITLE'"; exit 1; }
  [ -z "$ANCHOR" ] && continue
  LCOL=$(echo "${ANCHOR%%:*}" | sed 's/[0-9]*$//'); RCOL=$(echo "${ANCHOR##*:}" | sed 's/[0-9]*$//')
  SPAN=$(( $(col_num "$RCOL") - $(col_num "$LCOL") + 1 ))
  MIN=$(( (${#TITLE} * 18 + 99) / 100 ))
  [ "$SPAN" -lt "$MIN" ] && { echo "REJECT Gate 2: chart[$i] title=${#TITLE} chars needs width ≥ $MIN, anchor spans $SPAN"; exit 1; }
done

在预设 minimal / magazine 下,较窄的标题可能会在低于 0.18 系数时就被裁切,请抽查。

门禁 3 — 图表系列名称已填充(图例中不出现 "Series1")。

for i in $(seq 1 "$CHART_COUNT"); do
  BAD=$(officecli get "$FILE" "/Dashboard/chart[$i]" --json | jq '[.data.results[0].children[]? | select(.type == "series") | select((.format.name // "") | test("^Series[0-9]+$"; "i"))] | length')
  [ "$BAD" -gt 0 ] && { echo "REJECT Gate 3: chart[$i] has $BAD auto-named series"; exit 1; }
done

门禁 4 — Data 工作表上的条件格式规则(10 行以上)。

CF_COUNT=$(officecli query "$FILE" conditionalformatting --json | jq '.data.results | length')
[ "$CF_COUNT" -lt 1 ] && { echo "REJECT Gate 4: zero CF rules on 10+ row data sheet"; exit 1; }

注意:query conditionalformatting 是规范的元素名称;query cf 返回 0(不是别名)。

门禁 5 — activeTab 与 fullCalcOnLoad 已设置。 请与真实的 Dashboard 索引进行比对(Dashboard 位于索引 0 即为真正的通过)。

DASH_IDX=$(officecli query "$FILE" sheet --json | jq '[.data.results[].path] | index("/Dashboard")')
ACTIVE=$(officecli get "$FILE" /workbook --json | jq '.data.results[0].format.activeTab // -1')
FULLCALC=$(officecli get "$FILE" /workbook --json | jq -r '.data.results[0].format["calc.fullCalcOnLoad"] // false')
[ "$ACTIVE" != "$DASH_IDX" ] && { echo "REJECT Gate 5: activeTab=$ACTIVE Dashboard=$DASH_IDX"; exit 1; }
[ "$FULLCALC" != "true" ] && { echo "REJECT Gate 5: calc.fullCalcOnLoad=$FULLCALC — stale caches will ship"; exit 1; }

门禁 6 — 占位符扫描。 渲染输出中不得出现构建期令牌。

LEAKS=$(officecli view "$FILE" text 2>/dev/null | grep -niE '\{\{|\$fy\$|<TODO>|xxxx|TBD' | wc -l | tr -d ' ')
[ "$LEAKS" -gt 0 ] && { echo "REJECT Gate 6: $LEAKS placeholder tokens"; exit 1; }

门禁 7 — 视觉交付底线(从 xlsx 移植)。 运行 officecli view "$FILE" html,并读取返回的 HTML 路径。确认:

  • 任何 Dashboard 或 Data 单元格中都没有 ###(列宽过窄)。
  • KPI 标签、工作表标签名或图表标题都没有被截断。
  • 没有以文本形式渲染的占位符令牌($fy$24、{var}、<TODO>、xxxx)。
  • 饼图 / 环形图的扇区以不同的填充色呈现(如果在 LibreOffice 中被压缩,在宣布其损坏之前,请先在用户的目标查看器中验证 → 参见 officecli-xlsx §已知问题/渲染器注意事项)。
  • 没有空的图表锚点——每张图表都有可见且合理的绘图区。
  • Dashboard 工作表首先打开(标签页高亮,可见区域滚动到顶部)。

如果 view html 被阻止(渲染器冲突、无头模式、端口占用),门禁 7 仍然必须执行——运行所有备用检查:

# a) Token / ### sweep.
officecli view "$FILE" text 2>/dev/null | grep -nE '###|\{\{|<TODO>|\$fy\$|xxxx' && { echo "REJECT Gate 7: tokens or ### present"; exit 1; }
# b) Per-KPI: cachedValue length × coef must fit col width. coef=0.55 fit-to-page, 0.85 otherwise.
for CELL in A2 C2 E2 G2; do
  CV=$(officecli get "$FILE" "/Dashboard/$CELL" --json | jq -r '.data.results[0].format.cachedValue // .data.results[0].text // ""')
  W=$(officecli get "$FILE" "/Dashboard/col[${CELL%%[0-9]*}]" --json | jq -r '.data.results[0].format.width // 0')
  CAP=$(echo "$W * 0.55" | bc -l | awk '{print int($1)}')
  [ "${#CV}" -gt "$CAP" ] && { echo "REJECT Gate 7: $CELL '$CV' (${#CV} chars) > cap $CAP"; exit 1; }
done
# c) Rerun Gate 2 title × 0.18 ≤ anchor span.  d) Log which fallback was used and why.

门禁 7 绝不能跳过——跳过就等于把 ### 交给用户。

如果场景关键词包含 print / 一页 / board / 投资人 / 董事会,则需在门禁 7 中加入结构化的打印范围检查:

if echo "$USER_REQ" | grep -qiE 'print|一页|投资人|董事会|board'; then
  # Every non-Dashboard sheet must be hidden or veryHidden.
  # query sheet --json carries the name in .preview (no .name/.state); visibility lives in
  # each sheet's own get .format.hidden (absent => visible), so iterate + probe per sheet.
  LEAKING=""
  while IFS=$'\t' read -r SPATH SNAME; do
    [ "$SNAME" = "Dashboard" ] && continue
    HIDDEN=$(officecli get "$FILE" "$SPATH" --json | jq -r '.data.results[0].format.hidden // false')
    [ "$HIDDEN" != "true" ] && LEAKING="$LEAKING $SNAME"
  done < <(officecli query "$FILE" 'sheet' --json | jq -r '.data.results[] | [.path, .preview] | @tsv')
  [ -n "$LEAKING" ] && { echo "REJECT Gate 7 print-scope: visible non-Dashboard sheet(s):$LEAKING — hide before delivery"; exit 1; }
  # Dashboard must carry an explicit Print_Area named range.
  PA=$(officecli query "$FILE" 'namedrange[name="_xlnm.Print_Area"]' --json | jq '.data.results | length')
  [ "$PA" -ge 1 ] || { echo "REJECT Gate 7 print-scope: no _xlnm.Print_Area set"; exit 1; }
fi

用户需在其目标查看器(Office / WPS / Numbers)中打开文件进行最终的打印预览——本技能不渲染导出产物。

门禁 8 — 公式合理性(cachedValue 真实,非过期或错误)。 fullCalcOnLoad=true 在运行时刷新,而不是在构建时的缓存——因此每个公式单元格此刻都必须带有非空、非零、非错误的 cachedValue。

for CELL in A2 C2 E2 G2; do
  JSON=$(officecli get "$FILE" "/Dashboard/$CELL" --json)
  [ -z "$(echo "$JSON" | jq -r '.data.results[0].format.formula // ""')" ] && continue
  CV=$(echo "$JSON" | jq -r '.data.results[0].format.cachedValue // ""')
  case "$CV" in
    "" | "0" | "#DIV/0!" | "#REF!" | "#N/A" | "#VALUE!" | "#NAME?" | "null")
      echo "REJECT Gate 8: $CELL cachedValue='$CV' — re-issue formula or close+reopen"; exit 1 ;;
  esac
done

如果某个 KPI 确实为零(例如"本季度离职人数"= 0),请在循环中将其列入白名单并加以记录——默认假设是"零即故障"。

如有任何失败,从源头修复,然后重新运行完整循环。

实际限制

散点图不接受 series1.xValues(不支持)——请通过 categories / series1.categories 提供 x 轴数据。LibreOffice 的图表颜色偏移 / 饼图扇区压缩 / 复选框双框属于查看器特有现象——请先在 Office / WPS / Numbers 中抽查。

参考

  • add 时的简写 --type: chart、sparkline、databar、colorscale、iconset、formulacf。条件格式规则对应 help xlsx conditionalformatting;路径后缀为 /Sheet/cf[N]。
  • 完整 schema 见帮助文档: officecli help xlsx chart / sparkline / conditionalformatting。本技能不镜像这些内容。
  • DeferredAddKeys(仅限 add): combosplit、holesize。参见 D-1。(preset、trendline、referenceline、axisNumFmt 现在也可用于 set——帮助中显示为 [add/set]。)
  • 构建顺序: 先图表 + 迷你图 + 条件格式 + tabColors → 通过高级 set 设置 calc.fullCalcOnLoad=true → 最后执行 raw-set activeTab(在所有工作表都已存在之后)。

已知问题与陷阱

仪表盘专属
#问题解决方法
D-1combosplit 是 DeferredAddKey,仅在 add 时生效。(preset、referenceline、trendline、axisNumFmt 现在也可以在 set 时生效,帮助信息中显示为 [add/set]。)在 add 时设置 combosplit;创建后无法补加,需要移除后重新添加。其他四项可在创建后通过 set 应用或修改。
D-2referenceline 的格式为 value:color:label:dash(颜色在标签之前)。"0:Break-Even:FF0000:dash" 会报错 Invalid color value。顺序为 value、color、label、dash。
D-3散点图不接受 series1.xValues(不受支持)。请通过 categories / series1.categories 提供 x 轴数据。--prop series1.categories="Sheet1!A2:A13"(或 --prop categories="Sheet1!A2:A13")
D-4formulacf 支持 font.bold / font.italic(写入 dxf 字体并可在回读时查看),与 fill 和 font.color 一起使用。使用 fill / font.color / font.bold / font.italic 中的任意一项来标识条件格式规则。
D-5仪表板列宽默认为 8.43,24pt 加粗的 KPI 数值会显示为 ###根据 cachedValue 位数调整:4–6 位 → 22–24;7–9 位(百万)→ 26–30;10 位及以上(亿 / billion)→ 32–36;百亿 / 10 位加货币符号且横向适页 → 40–44。公式 ceil((visible_chars+2)*1.3) 可作为起点,但务必通过 Gate 7 的备选方案 b) 进行验证。迷你图所在列:12。
D-6raw-set activeTab 必须是最后一次修改。在所有工作表存在之前插入会导致索引偏移。先完成所有工作表 / 图表 / 条件格式 / 迷你图 / 标签颜色的设置,再执行 raw-set。
D-7通过 raw-set 设置 calc.fullCalcOnLoad 会生成重复的 <calcPr>,导致校验失败使用 officecli set "$FILE" / --prop calc.fullCalcOnLoad=true。
D-8LibreOffice 渲染时不计算隐藏列中的公式,因此引用隐藏单元格的图表会显示为空白汇总到可见的 Summary 工作表中,图表从 Summary 读取数据。仅隐藏不作为图表数据源的列。
D-9chartType=pie 在 LibreOffice 中渲染为空白对于部分与整体的构成分析,使用 doughnut(圆环图)作为安全替代。
D-10若条件为字符串,则 SUMIFS / AVERAGEIFS 使用日期条件时会静默失败用 DATE() 或 DATEVALUE() 包裹:=SUMIFS(B2:B13,A2:A13,DATE(2025,1,5))。
D-11若未设置 numFmt,Summary 工作表中的百分比公式会以原始小数(0.098)显示在设置公式的同一次 set 调用中设置 numFmt="0.0%"。
D-12import --header 会设置冻结窗格和自动筛选,但不会设置列宽。在 col[] 上设置列宽。现在对 col[] 路径设置 numFmt 会应用列级样式(<col s=...>,符合 schema,回读时显示为 numberformat);它会格式化该列中的空白单元格。已有自身样式的单元格仍需通过按单元格区域的 numFmt 进行设置。
D-13迷你图的 highpoint 是布尔值(开启 / 关闭高亮),而不是颜色。--prop highpoint=FF0000 会报错 Invalid boolean value使用 --prop highPoint=true --prop highMarkerColor=FF0000。lowPoint / firstPoint / lastPoint 及其 *MarkerColor 采用相同写法。
D-14迷你图用于横截面数据没有意义(地区或部门之间没有顺序)除非行数据是按顺序排列的时间序列(日期、月份、季度),否则不要使用迷你图。
D-15在 CLI 层会拒绝空图表的 add(报错 Chart requires a 'data' property);过去依赖静默接受的旧版技能在此会失败在添加图表时始终提供 series1.values= / dataRange= / 内联 data=。将 Gate 2 的 seriesCount 检查视为双重保险验证。
D-16fullCalcOnLoad=true 保证最终用户打开文件时进行运行时重新计算;但它不会刷新 XML 中构建时的 cachedValue。构建顺序 set B=100 → set E==B+D → fix B=150 会使 E.cachedValue 过期(董事会会看到“净变化 = 0”)。所有上游编辑最终确定后,重新提交每个下游公式(officecli set "$FILE" /Sheet/E2 --prop formula==B2+D2),或者 close 后重新打开文件。Gate 8 会进行验证。
D-17内置计算引擎不会计算数组条件形式的 SUMPRODUCT,即 SUMPRODUCT((A2:A97=X)*C2:C97*D2:D97) —— cachedValue 保持为 0/null,Gate 8 会拒绝。运行时的 Excel / WPS 可以正确计算,但交付给董事会的 XLSX 若缓存过期仍会输出 0。改写为辅助列加 SUMIF:在源工作表中设置 F2==C2*D2,然后使用 =SUMIF(B:B, "Region X", F:F)。或者在 Summary 工作表中预先汇总,并从那里生成图表。
继承(仅作指引)

跨工作表 ! 陷阱,以及图表创建后 anchor / 系列不可变的问题 → 参见 officecli-xlsx §Known Issues。

评论

支持 Markdown