关于 officecli-data-dashboard
officecli-data-dashboard 是基于 officecli-xlsx 的技能,用于指导构建以 Dashboard 工作表为入口的电子表格仪表板。它强调公式驱动的 KPI、引用单元格范围的图表,以及可视化格式规则。
主要功能
- 01创建公式驱动的 KPI 卡片
- 02用单元格范围连接图表数据
- 03在 Dashboard 工作表组织图表与迷你图
- 04按数据规模调整仪表板复杂度
- 05应用条件格式辅助数据识读
- 06验证工作簿并检查渲染结果
适用场景
- 01将销售 CSV 汇总为管理层仪表板
- 02制作包含 KPI 和趋势图的运营看板
- 03展示随源数据更新的指标与图表
- 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'。
核心原则
五条不可妥协的原则。只要违反其中任何一条,输出的就不是仪表板,而只是一张碰巧带图表的电子表格。
-
KPI 由公式驱动。 仪表板工作表上的每个 KPI 值都是公式(
SUM、AVERAGE、IFERROR((...-...)/...,0)等),引用数据/汇总工作表中的单元格。切勿硬编码计算得出的数值。明天底层数据变化时,KPI 会在打开文件时自动更新。 -
图表使用单元格区域引用。 每个图表系列都从单元格区域读取数据,例如
series1.values="Sheet1!B2:B13"。内联的data="Revenue:100,200,300"只适用于五分钟的演示,不适用于交付的仪表板。唯一例外是数据需要 Excel 无法表达的聚合运算(罕见情况),此时需在注释单元格中说明该例外。 -
仪表板优先的架构。 KPI 标签单元格、KPI 值单元格、图表和迷你图都位于仪表板工作表中,这是用户打开文件后看到的唯一工作表。原始导入数据和
SUMIFS汇总位于数据/汇总工作表中,处于仪表板的上游。用户应无需切换标签页即可找到答案。 -
图表数据源只使用可见单元格。 LibreOffice 在渲染时不会计算隐藏列或隐藏工作表中的公式。若图表的
series1.values指向隐藏列中的SUMIFS,则渲染结果为空白。推荐做法:将数据汇总到可见的汇总工作表中,图表指向汇总表的单元格,仅隐藏不作为图表数据源的辅助列。 -
根据数据规模控制复杂度。 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 | 图表 | 迷你图 | 条件格式规则 | 预设 |
|---|---|---|---|---|---|
| < 10 | 1–2 | 1 | 跳过 | 0–1 | minimal |
| 10–50 | 2–3 | 2 | 仅在有连续时间序列时使用 | 1–2 | dashboard |
| 50–200 | 3–5 | 2–3 | 仅在有连续时间序列时使用 | 2–3 | dashboard |
| 200+ | 3–5 | 3 | 仅在有连续时间序列时使用 | 3–4 | dashboard |
图表类型选择
| 数据模式 | 图表类型 | 说明 |
|---|---|---|
| 单一序列的时间趋势 | line | 在波动较大的序列上添加 trendline=linear 以显示趋势方向 |
| 多个组成部分的时间趋势 | line(多序列)或 columnStacked | 当各组成部分相加具有实际意义的总和时使用堆叠 |
| 按时间顺序的类别比较 | column | 不要用 bar——横向条形图会破坏从左到右的时间阅读顺序 |
| 部分与整体的构成 | doughnut | 优先于 pie:chartType=pie 存在已知的 LibreOffice 空白渲染问题 |
| 预算与实际对比 | combo 搭配 combosplit=1 | 第一个序列为柱形,其余为折线 |
| 相关性 | scatter | X 轴通过 categories / series1.categories 设置——series1.xValues 不受支持 |
预设选项
每个图表都需使用 --prop preset=<name>。可选值:minimal、dashboard、corporate、magazine、colorful、monochrome、dark。在同一个仪表板表中选定一个并在所有图表中保持一致——混用预设会显得像是意外。
条件格式——语义化颜色
共有四种条件格式规则类型;在 add 时使用 --type <简写> 指定:
| 用途 | --type | 常用属性 |
|---|---|---|
| 数值条形(销售额、支出) | databar | sqref=B2:B13 color=4472C4 — 建议显式设置 min=0 max=<合理上限> 以获得可预测的缩放效果,但省略也有效(默认使用数据的最小值/最大值) |
| 热力图(比率、增长率) | colorscale | sqref=D2:D13 mincolor=FFCDD2 midcolor=FFFFFF maxcolor=C8E6C9 |
| 状态指示 | iconset | sqref=E2:E13 iconset=3Arrows — 完整枚举值请参阅帮助 |
| 自定义业务规则 | formulacf | sqref=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-1 | combosplit 是 DeferredAddKey,仅在 add 时生效。(preset、referenceline、trendline、axisNumFmt 现在也可以在 set 时生效,帮助信息中显示为 [add/set]。) | 在 add 时设置 combosplit;创建后无法补加,需要移除后重新添加。其他四项可在创建后通过 set 应用或修改。 |
| D-2 | referenceline 的格式为 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-4 | formulacf 支持 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-6 | raw-set activeTab 必须是最后一次修改。在所有工作表存在之前插入会导致索引偏移。 | 先完成所有工作表 / 图表 / 条件格式 / 迷你图 / 标签颜色的设置,再执行 raw-set。 |
| D-7 | 通过 raw-set 设置 calc.fullCalcOnLoad 会生成重复的 <calcPr>,导致校验失败 | 使用 officecli set "$FILE" / --prop calc.fullCalcOnLoad=true。 |
| D-8 | LibreOffice 渲染时不计算隐藏列中的公式,因此引用隐藏单元格的图表会显示为空白 | 汇总到可见的 Summary 工作表中,图表从 Summary 读取数据。仅隐藏不作为图表数据源的列。 |
| D-9 | chartType=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-12 | import --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-16 | fullCalcOnLoad=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。