@guandata/guanvis 0.1.34 → 0.1.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,88 +6,72 @@ compatibility: "Requires Node.js 14+. Install via npm link (local) or npm instal
6
6
 
7
7
  # guanvis
8
8
 
9
- 这是一个执行型 skill,用于通过 AI 生成的 JS 脚本创建 BI Card 和 Page 资源。
9
+ 执行型 skill:通过 AI 编写的 JS 脚本创建/修改 BI Card 和 Page 资源。
10
10
 
11
- - AI 生成 `card_*.js` 定义各个 Card 图表;`page.js` 将多个 Card 组装成仪表板。
12
- - `schema.js` `init` `checkout` 命令生成,定义数据集字段信息(不允许 AI 修改);checkout 生成 schema 是 best-effort,失败不会阻断 checkout。指标卡片使用 `metric-init` 生成 `metrics.js`。
13
- - 框架内置验证规则,在 pack/publish 时检查字段数量、zone 兼容性、必填字段等。
14
- - 认证通过 `guancli` 共享配置自动获取。
11
+ - AI 生成 `card_*.js` 定义 Card;`page.js` 组装仪表板。`schema.js` `init`/`checkout` 生成(不允许 AI 修改,checkout 生成是 best-effort);指标卡片用 `metric-init` 生成 `metrics.js`。
12
+ - 框架在 preview/pack/publish 时内置验证(字段数量、zone 兼容性、必填字段等);认证复用 `guancli` 共享配置。
15
13
 
16
14
  ## Harness 化工作法
17
15
 
18
16
  把可视化生成当作源文件驱动的构建流程:本地 JS DSL 是可编辑事实源,payload、ZIP、线上 Card/Page 都是从源文件生成的派生产物。
19
17
 
20
18
  - `schema.js` 是由 `init` / `checkout` 生成的数据集事实快照,不手改;数据集字段变化时重新运行对应命令。`metrics.js` 是由 `metric-init` 生成的指标事实快照,不手改;指标口径、适用维度或格式变化时重新运行 `metric-init`。
21
- - 全局参数先用 `guanvis parameter` 创建或复用,再通过 `dynamic-parameters.js` `dpId` 引用;`pack`/`publish` 不会隐式写入参数。创建前必须按名称查询;若系统已有同名有效参数,立即停止并让用户明确选择“更新已有参数”或“换名创建”,不得自动复用或修改。只有用户明确选择更新后,才可按该参数的 `dpId` 执行 `update`。
19
+ - `theme-colors.js` 是项目级图表主题色事实快照,不手改。准备在脚本中使用 `setThemeColor()` 前,先检查项目根目录;不存在时先运行 `guanvis theme-color sync -d <project>`。调用 `setThemeColor()` 切换主题时必须从快照选择真实 `tcId`,不得猜测或编造。已有文件直接复用,主题色列表变化或切换 BI 环境时用 `theme-color sync` 刷新;快照会记录实际选中的 profile、服务地址和 domain,通过环境变量或默认 profile 切换环境后,构建都会拒绝复用旧快照。`preview`/`diff`/`pack`/`publish` 对缺失快照的自动生成只作兜底。
20
+ - 全局参数先用 `guanvis parameter` 创建或复用,再通过 `dynamic-parameters.js` 按 `dpId` 引用;`pack`/`publish` 不会隐式写入参数。创建前必须按名称查询;发现同名有效参数时立即停止,让用户明确选择"更新已有参数"或"换名创建",不得自动复用或修改。
22
21
  - `card_*.js`、`selector_*.js`、`page.js` 是可编辑源文件;`*_package.zip`、`.preview.json`(preview 落盘的全量 payload)、publish 后线上资源都是派生产物,不要手工编辑。
23
- - **Page 归属红线**:任何包含 CardSelector 的资源包都必须同时包含 Page,且包内每个 Card/Selector 都必须被至少一个 Page 引用。禁止发布只有 Card/Selector、没有 Page 的资源包,也禁止留下未放入任何 Page 的 Card/Selector;否则后端会产生 `pg_id` 为空、无法访问且可能阻塞后续页面发布的孤儿卡片。校验失败时在 `page.js` 中放置对应资源,再将 Page 与 Card/Selector 同包 `pack`/`publish`。
24
- - 已有线上仪表板用 `guanvis checkout <pgId> -d <dir>` 拉成可编辑工程:checkout 会尽量生成 `schema.js`,但数据集无权限/已删除/跨环境残留时只 warning 并继续生成可编辑工程;普通 Card 用 `attachCard(cdId, ".guanvis/base/cards/<cdId>.json")` 接受线上 JSON 作为基线;自定义图表(SDK/ECHARTS_LITE)会把脚本/HTML/CSS 反编译到 `charts/<cdId>.js|.html|.css`,超长内联资源(GeoJSON、base64 图片)抽取回 `charts/<cdId>_assets/` + `__asset_text/__asset_base64` 占位符(逐字节往返验证,未编辑时 diff 恒为空),card JS 用 `attachCard(...).loadContent("charts/<cdId>")` 引用,直接编辑内容文件即可改图表;复杂报表 Pro 会额外下载可编辑 xlsx 到 `templates/`,生成 `createComplexReportPro()` 父卡和嵌套的 attached data view,并使用下一模板版本,旧版复杂报表 checkout 直接拒绝;Page 生成 `createPage(...).setBasePath(...).placeCard("cdId", ...)` 脚本,尽量反编译根布局里的 Tab/CardGroup/AreaTitle/SelGroup 和快捷筛选组,并保留 parentDirId。checkout 工程只用于修改指定 Page,不用于复制新 Page;遇到嵌套布局组件等当前 DSL 不能安全表达的结构,checkout 会直接失败,不生成会清结构的工程。只支持普通仪表板(pgType=PAGE),自助取数/数据大屏/清单页等特殊页面会在 checkout 入口直接报错。
25
- - **Checkout 账号要求**:checkout 读到的是"当前账号视角"的卡片定义,publish 会把它整体回写。必须用对页面涉及数据集拥有**完整列权限、无脱敏限制**的账号(推荐资源 owner 或管理员)执行 checkout/publish,否则读接口会按该账号权限裁剪 zone 字段/脱敏字段,回写后这些字段会从线上卡片中永久丢失。租户启用多语言时,操作账号语言应与卡片原始语言一致,避免把翻译后的字段显示名固化回卡片。
26
- - **Checkout JSON 红线**:`.guanvis/raw/**`、`.guanvis/base/**`、`.guanvis/manifest.json` 是 checkout 生成物和只读快照,不是可编辑源文件。Agent 不得修改这些 JSON,也不得复制一份 checkout JSON 后改副本来创建新资源;`attachCard(cardId, jsonPath)` 只能引用 manifest 记录的原始 base-card,且 `cardId` 必须与该 JSON 的资源 ID 一致。现有卡片修改必须通过 `attachCard(...).setName()/updateMetric()/addMetric()/removeMetric()/addLink()` DSL 操作表达;字段整体重建才用 `setRows()` / `setMetrics()`,这些整 zone 替换会触发 warning,默认应优先用 `update*/patch*/add*/remove*/move*`。Page 快捷筛选区修改用 `createPage(...).setBasePath(...).setFilterLayout()/clearFilterLayout()/addFilterLayoutItem()/insertFilterSelector()/removeFilterSelector()/moveFilterSelector()` 表达;筛选器在快捷筛选区和画布之间移动时优先用 `moveFilterSelectorToCanvas()` / `moveCanvasSelectorToFilter()`,筛选器组用 `moveSelectorGroupToCanvas()` / `moveCanvasSelectorGroupToFilter()`。新增卡片必须使用 `createCard()` / `createSelector()` 等工厂函数。
22
+ - **Page 归属红线**:包含 Card/Selector 的资源包必须同时包含 Page,且每个 Card/Selector 都必须被至少一个 Page 引用;否则后端会产生 `pg_id` 为空、无法访问且可能阻塞后续发布的孤儿卡片。校验失败时在 `page.js` 中放置对应资源后同包发布。
23
+ - 已有线上仪表板用 `guanvis checkout <pgId> -d <dir>` 拉成可编辑工程(attachCard 基线、自定义图表 `charts/` 反编译、Pro 模板 `templates/`、Page 布局脚本)。checkout 工程只用于修改指定 Page,不用于复制新 Page;只支持普通仪表板(pgType=PAGE),DSL 不能安全表达的结构会直接失败而不是清结构。**checkout 工程动手前先读 `references/checkout-editing.md`**。
24
+ - **Checkout 账号红线**:checkout 读到的是"当前账号视角"publish 整体回写。必须用对涉及数据集有**完整列权限、无脱敏限制**的账号(推荐 owner 或管理员)执行 checkout/publish,否则被裁剪的字段会在回写后从线上卡片永久丢失;多语言租户操作账号语言须与卡片原始语言一致。
25
+ - **Checkout JSON 红线**:`.guanvis/raw/**`、`.guanvis/base/**`、`.guanvis/manifest.json` 是只读快照。Agent 不得修改这些 JSON,也不得复制 checkout JSON 改副本创建新资源;现有卡片修改必须通过 `attachCard(...)` DSL 操作表达(优先 `update*/patch*/add*/remove*/move*`,整 zone 重建的 `set*/clear*` 会触发 warning),新增卡片必须用 `createCard()` / `createSelector()` 工厂函数;操作清单见 `references/checkout-editing.md` §3。
27
26
  - Card/Page 描述也是 JS 源文件的一部分:新建看板或改版时,在 `card_*.js` / `page.js` 中写 `.setDescription(...)`,再通过 `preview`/`pack`/`publish` 从 JS 源文件生成并发布资源。
28
27
  - 写脚本前先形成 dashboard contract:目标用户、业务问题、使用的数据集、核心指标、维度拆解、筛选器、页面结构和验证方式。
29
28
  - 每次生成都针对明确目录或明确子目录;不要把 unrelated 示例、旧包或临时 ZIP 混入同一个发布目标。
30
- - Card/Page/Selector 的 `.setId(...)` 建议使用 `guanvis genid` 生成(保证字母开头)。数字开头的 24 位 ID 会让 BI 前端把 ID 拼成 CSS selector(如 `#5bef...`)时触发 `querySelector` 语法错误,`preview`/`pack`/`publish` 会对**新建资源**直接报 error 拦截;checkout/attach 的已有线上资源不受此限制(保留原 ID 原地更新)。
31
- - 先 `preview` 做本地结构验证,再 `publish`(publish 自带构建打包上传,**发布前不需要单独 `pack`**;`pack` 只用于生成离线 ZIP 交付物走 `upload`);修改已发布资源前先判断变更类型:仅修复 Card/Page 描述时应保留原资源 ID,使用 `card/page set-description` 并同步维护 JS 中的 `.setDescription(...)`;调整图表、布局、筛选器、字段等看板内容时,按线上仪表板更新策略处理。
32
- - **编辑红线(编辑 ≠ 删除重建)**:用户要求修改/改名已发布 Page 或 Card 时,必须保留原 pgId/cdId 原地覆盖发布,先按工程来源分流。**本地有源工程**(该 Page 就是当前 guanvis 工程创建并发布的,`card_*.js` / `page.js` 还在,且线上没有在发布后被网页端改动过):直接修改本地 JS 源文件(改名即改 `createCard(...)` / `createPage(...)` 标题)重新 publish 同 ID 覆盖。**线上在发布后被网页端修改过、本地没有原始 JS 工程、或不确定线上是否已被改动**:用 `checkout` 拉取线上版本编辑——Page 改 checkout 生成的 `createPage(...)` 标题,已 checkout 的 Card 用 `attachCard(...).setName(...)`,**不得**把 `attachCard` 改写成 `createCard()`(那会绕过 base JSON,未被 DSL 表达的线上配置会在覆盖发布时丢失,`createCard()` 只用于新增卡片)。**禁止**用"新建一个新 Page/Card + 删除或废弃旧的"来模拟编辑——资源 ID 变化会让收藏、分享链接、订阅推送、门户菜单引用和页面权限配置全部失效,这些状态 guanvis 无法迁移。需要"保留旧页面、出一个新版本"时用 `guanvis page save-as`。全局参数编辑同理:只能 `parameter update <dpId>` 原地更新,禁止 delete 后重建同名参数(dpId 变化会让所有引用它的卡片/筛选器断链);`parameter delete` 只用于用户明确要求删除参数本身。
29
+ - Card/Page/Selector 的 `.setId(...)` `guanvis genid` 生成(保证字母开头);数字开头 ID 会触发 BI 前端 CSS selector 语法错误,新建资源校验直接报 error 拦截(checkout/attach 的已有资源保留原 ID 不受限)。
30
+ - 先 `preview` 做本地结构验证,再 `publish`(publish 自带构建打包上传,**发布前不需要单独 `pack`**;`pack` 只用于生成离线 ZIP `upload`)。仅修复描述用 `card/page set-description` 并同步 JS 中的 `.setDescription(...)`;改看板内容按「线上仪表板更新红线」处理。
31
+ - **编辑红线(编辑 ≠ 删除重建)**:修改/改名已发布 Page 或 Card 必须保留原 pgId/cdId 原地覆盖发布。本地源工程还在且线上未被网页端改动 直接改本地 JS 同 ID 重新 publish;否则 `checkout` 后编辑,且**不得**把 `attachCard` 改写成 `createCard()`(绕过 base JSON 会丢线上配置)。**禁止**用"新建 + 删除旧的"模拟编辑——资源 ID 变化会让收藏、分享、订阅、门户引用和页面权限全部失效且无法迁移;"保留旧页面出新版本" `guanvis page save-as`。全局参数同理只能 `parameter update <dpId>` 原地更新,禁止 delete 后重建同名参数(dpId 断链)。分流决策见 `references/checkout-editing.md` §4。
33
32
  - **资源包安全红线**:Agent 只编辑 DSL 源文件;资源包 ZIP 是 `guanvis pack/publish` 的派生产物,不手工生成、解包修改或重打包。`guanvis upload` 只允许上传 `guanvis pack` 原样生成的 ZIP。若用户要求批量重绑资源、迁移已有页面或复用线上页面结构,先停下来说明风险并确认方案,不要直接改 ZIP。
34
- - 发布后优先用 `guancli page get/card get` 回读结构与配置。只有在明确需要视觉质量判断、且当前大模型支持图像理解时,才使用 `guanvis screenshot <pageId>` 生成 PNG 并交给模型分析;不要把截图作为默认闭环步骤,因为图像理解会额外消耗 token/费用。
33
+ - 发布后优先用 `guancli page get/card get` 回读结构与配置;`guanvis screenshot` 仅在明确需要视觉质量判断且模型支持图像理解时使用(额外消耗 token),不作默认闭环步骤。
35
34
 
36
35
  ## AI Quick Reference(速查,详细说明见按需参考资料)
37
36
 
38
- **Checkout/attachCard 速记**:`attachCard(cardId, jsonPath)` 是“base JSON + 链式 DSL 操作 = 目标 JSON”。它不会修改 base JSON,重复执行同一组 JS 操作应产出相同 payload。已有卡片可继续串接常用 `createCard` 后续操作,包括标题/描述、`setRawSettings`、图例/标签/坐标轴/表格/拆分等视觉设置。已有自定义图表**仅限 subType 为 SDK / ECHARTS_LITE** 支持内容编辑:checkout 自动反编译出 `charts/<cdId>.js`(含内嵌资源抽取),card JS 里 `attachCard(...).loadContent("charts/<cdId>")`;也可用 `.setScript()/.setHtml()/.setCss()/.setLibs()/.addLib()` 内联修改,编辑 `charts/` 下源文件即可改图表脚本。COMPLEX_REPORT(走 Pro 流程)、PLUGIN/PLUGIN_LITE(事实源是插件市场资源)、REPORT_FORM(填报模板配置)调用这些内容编辑 API 会直接报错,不要对它们生成此类修改;详见 `references/builder-reference.md` 自定义图表章节。Zone 操作按链式顺序真实执行:`addRow/addMetric/...` 追加字段,`insertMetric(index, field)` 插入字段,`removeMetric(selector)` 删除字段并保守清理其它 zone 中同字段引用,`moveMetric(selector, index)` 调整顺序,`updateMetric(selector, patch)` / `patchMetric(...)` 修改已有字段属性并保留未设置字段配置;`setRows/setMetrics/...` 和 `clearRows/clearMetrics/...` 是整 zone 重建,不会继承被替换字段的格式,并会在验证时提示 warning。已有 selector 额外用 `.addLink(cardIdOrIndex, targetFieldName?)` / `.removeLink(cardIdOrIndex)` / `.clearLinks()` 修改联动。Page 筛选区也按有序操作执行:`setFilterLayout` 整体设置,`clearFilterLayout` 清空,`addFilterLayoutItem` 追加去重,`insert/remove/moveFilterSelector` 局部调整;筛选栏布局使用 `setFilterPanelLayout()`;颜色、字体和背景继续由主题管理。筛选器在筛选栏和画布间移动优先用动作级 API:`moveFilterSelectorToCanvas(selectorId, x, y, w, h)` / `moveCanvasSelectorToFilter(selectorId, index?)`,筛选器组用 `moveSelectorGroupToCanvas(group, x, y, w, h)` / `moveCanvasSelectorGroupToFilter(group, index?)`。checkout 生成的 page 布局默认用 card/selector ID 字符串;发布前可用 `guanvis diff <dir>` 或 `preview` 输出里的 `changeSummary` 查看 base JSON 到最终 payload 的路径级影响面;没有 DSL 操作覆盖的需求,先扩展 DSL,不要改 `.guanvis` JSON。
37
+ **Checkout/attachCard 速记**:`attachCard(cardId, jsonPath)` 是"base JSON + 链式 DSL 操作 = 目标 JSON",不修改 base JSON,重复执行产出相同 payloadzone 修改优先 `update*/patch*/add*/remove*/move*`(整 zone 重建的 `set*/clear*` 会触发 warning);已有 selector `.addLink()/.removeLink()/.clearLinks()` 改联动;自定义图表内容编辑仅限 SDK/ECHARTS_LITE(编辑 `charts/` 下反编译源文件),COMPLEX_REPORT/PLUGIN/PLUGIN_LITE/REPORT_FORM 调内容编辑 API 直接报错。发布前用 `guanvis diff <dir>` 或 preview `changeSummary` 查看影响面;没有 DSL 操作覆盖的需求先扩展 DSL,不改 `.guanvis` JSON。完整操作语义(zone/筛选区/筛选栏画布互移等)见 `references/checkout-editing.md` §3
39
38
 
40
- **字段显示名与 Card 标题**:`createCard()` 第二参数是 Card 标题,与字段显示名相互独立。所有数据图表的图例、轴标题、表头、Tooltip 或指标标签都可能使用字段显示名;需要与物理字段名或 calcField 内部名分开时设置 `alias`,如 `f("营收", { alias: "本月营收" })`、`calcField("整体毛利率_kpi", formula, { alias: "整体毛利率" })`。其中 `SINGLE_VALUE`、`KPI_CARD`、`KPI_TREND` 和仪表盘/进度类图表尤其明显。
39
+ **字段显示名与 Card 标题**:`createCard()` 第二参数是 Card 标题,与字段显示名独立。图例/轴标题/表头/Tooltip/指标标签都用字段显示名,需要与物理字段名或 calcField 内部名分开时设置 `alias`(如 `f("营收", { alias: "本月营收" })`);`SINGLE_VALUE`/`KPI_CARD`/`KPI_TREND` 和仪表盘/进度类尤其明显。
41
40
 
42
- 1. **工厂函数**:数据集图表用 `createCard()`;复杂报表 Pro 用 `createComplexReportPro()` + `createReportWorkbook()` 或 `.setTemplate(xlsx)`;已有线上普通卡片用 `attachCard(cardId, jsonPath)`;指标平台指标卡片用 `createMetricChart()`;筛选器/筛选器组/文本/图片/杜邦/Tab/页面用对应工厂函数,不要 `new XxxBuilder()`。旧版复杂报表拒绝,Pro 父卡也不能用泛化 `attachCard()`
43
- 2. **注册函数**:`registerCard(card.build())` / `registerComplexReportPro(report.build())` / `registerMetricChart(card.build())` / `registerSelector(sel.build())` / `registerTextCard(text.build())` / `registerImageCard(image.build())` / `registerDuPontChart(dupont.build())` / `registerPage(page.build())`
41
+ **图表属性配置**:设置图表属性时,先读 `references/chart-properties.md`,按其中的方法和参数配置。
42
+
43
+ 1. **工厂函数**:数据集图表 `createCard()`;复杂报表 Pro `createComplexReportPro()` + `createReportWorkbook()`;已有线上普通卡片 `attachCard(cardId, jsonPath)`;指标卡片 `createMetricChart()`;筛选器/文本/图片/杜邦/Tab/页面用对应工厂函数,不要 `new XxxBuilder()`。旧版复杂报表拒绝,Pro 父卡不能用泛化 `attachCard()`
44
+ 2. **注册函数**:`registerCard` / `registerComplexReportPro` / `registerMetricChart` / `registerSelector` / `registerTextCard` / `registerImageCard` / `registerDuPontChart` / `registerPage`,入参一律 `xxx.build()`
44
45
  3. **字段引用**:单数据集用 `f("字段名")`,多数据集用 `field(DS, "字段名")`
45
46
  4. **zone maxCount**:`BASIC_COLUMN/BAR/LINE` metric=1;`GROUPED_*/STACKED_*` metric=∞;`KPI_CARD` metric=1;组合图 `*_WITH_LINE` row=1
46
47
  5. **column vs colorBy**:column 放维度(按类别分组着色),colorBy 放度量(按值渐变着色)
47
- 6. **原生聚合优先**:`SUM`/`AVG`/`COUNT`/`COUNT_DISTINCT`/`MIN`/`MAX` 等单字段聚合用 `f("字段", { aggrType: AggrType.XXX })`,不要用 `calcField()` 手写 SQL 聚合函数;去重计数用 `AggrType.COUNT_DISTINCT`,不要写 `COUNT_DISTINCT([字段])`
48
+ 6. **原生聚合优先**:单字段聚合用 `f("字段", { aggrType: AggrType.XXX })`(SUM/AVG/COUNT/COUNT_DISTINCT/MIN/MAX),不要用 `calcField()` 手写 SQL 聚合函数(如 `COUNT_DISTINCT([字段])`)
48
49
  7. **calcField 命名**:不能与数据集物理字段同名,否则 BI 默认取数据集字段
49
50
  8. **calcField 类型**:`aggregation`(默认)公式必须含聚合函数;纯算术用 `{ calculationType: "normal" }`;窗口函数用 `{ calculationType: "window" }`
50
- 9. **明细/滚动表 calcField**:`DETAIL_TABLE` / `SCROLL_TABLE` 只做逐行展示;如需行级计算,必须写 `{ calculationType: "normal" }`,且不要写任何 SQL 聚合函数或窗口函数。汇总需求改用非明细图表 `aggrType` / aggregation calcField,或 ETL 预计算
51
- 10. **联动/下钻**:新建筛选器联动图表必须调用 `.linkToAll()` 或 `.linkTo(cardIndex)`;checkout attach 回来的已有筛选器用 `attachCard(selectorId, jsonPath).addLink(cardIdOrIndex, targetFieldName?)` / `.removeLink(cardIdOrIndex)` / `.clearLinks()` 叠加修改现有 `settings.asFilter`,不要反向改 JSON;筛选器级联筛选器用 `.linkToSelector(selectorId, targetFieldName?)`,目标为 ALL/空默认值时会自动启用 FIRST_PICK + firstPickLink,固定默认值会保留;普通图表卡片联动普通图表用 `card.linkTo(layoutCardIndex, { fields: [{ source, target }] })`;固定路径下钻用 `registerDrillPath(parentCardIndex, [child.build()], { position: DrillPathPosition.BOTTOM })`;详细规则见 `references/builder-reference.md`
52
- 11. **selector 类型选择**:离散值(区域/类别)→ `DS_ELEMENTS`(默认);连续数值(利润率/金额)→ `.setSelectorType(SelectorType.DS_INTERVAL)`;日期 → `CALENDAR`;快捷日期区间(本月/近7天等)→ `.setTimeMacroOptions(options)`
53
- 12. **同环比默认**:用户说同比/环比/同环比/年同比/月环比且未指定输出值时,默认用增长率;未指定模式时默认按日期筛选模式(`ComparativeMode.FILTER_BASED`),普通模式需显式指定 `ComparativeMode.NORMAL`。如果日期字段已经是预聚合周期字段(如 `周开始日期` / `月开始日期`),必须把日期字段声明为 `f("周开始日期", { granularity: Granularity.NONE })`,builder 会生成不带筛选窗口和 `mode` 的同环比,避免按 DAY 筛选窗口计算为空
54
- 13. **placeCard 入参**:优先使用 card/selector ID 字符串,尤其是 checkout 工程和子目录工程,如 `placeCard("cardId", x, y, w, h)`。数字 index 仍可用于新建工程,按 `registerCard` / `registerMetricChart` / `registerTextCard` / `registerImageCard` / `registerCustomChart` / `registerDuPontChart` 等可布局资源的注册顺序累加,文件按文件名排序加载;**registerSelector 不参与 card index 计数**。要把 selector 放进画布或者布局组件时,使用 selector 字符串 ID,如 `placeCard("selectorId", x, y, w, h)` 或 `addFullWidthCard("selectorId", h)`。
55
- 14. **publish 环境**:认证由底层 CLI 负责——guancli 需先 `guancli auth use <profile>`,guancli-lite 需设置环境变量
56
- 15. **更新线上仪表板**:checkout 工程默认表示“修改指定线上 Page”,保留原 Page/Card/Selector ID;发布时如 CLI 检测到同 ID Page,必须先向用户说明将覆盖哪些线上资源并取得明确确认,确认后才可加 `--allow-overwrite`,由现有覆盖备份机制兜底。不要把 checkout JSON 复制后改 ID 来做“新版本”。若用户明确要保留原页面并生成新版本,用 `guanvis page save-as <pgId> --suffix _guanvis`(或 `--name <新名>`)做 BI 原生整页另存为——服务端事务复制全部卡片并重映射联动/下钻/父子/指标关系等 ID,命令自动 release 发布副本(7.2.0 之前无草稿态会自动跳过),再基于副本页面 checkout/edit;不要在 guanvis CLI 里手写复杂 ID map,也不要用浏览器手工复制页面。
57
- 16. **描述维护**:仅修复已发布 Card/Page 的描述时,保留原资源 ID,使用 `guanvis card/page set-description` 更新线上描述;如果本地有对应 JS 工程,也同步更新 `.setDescription(...)`,让源文件与线上描述一致
58
- 17. **主题切换**:用户描述风格(深色科技风/科技蓝/蓝色简约等)→ 在工程目录里 `guanvis theme preference --keywords "..." --sync`(`[dir]` 可省,默认当前目录);不要选择租户默认的“浅色”/“深色”主题,找不到合适主题时保持/清空偏好,让 preview/pack/publish 自动使用内置“简约”兜底。普通数据集图表与指标平台 MetricChart 都会自动应用主题视觉配置;改版未提风格时 `.applied.json` 会自动继承上次主题,preview/pack/publish 不需要重复指定。**多子目录工程**:每个子目录是独立工程,主题要在子目录里配置 + preview/pack/publish 也必须 `cd` 进对应子目录运行(在根目录直接跑会被命令显式拒绝并给出 cd 提示);详见 `references/theme.md`
59
- 18. **设计规则**:`preview`/`pack`/`publish` 会自动应用内置设计规则;需要自定义静态卡片默认规则或主题已开放配置时,在工程目录新建 `design-rule.json`,不要手改 `themes/<themeId>.json`;详见 `references/theme.md`
60
- 19. **图表选型**:用户说“指标卡片”时先区分语义:如果是数据集字段做单值/KPI,用 `SINGLE_VALUE` / `KPI_CARD`;如果是“用指标平台已有指标创建卡片”,必须先 `guanvis metric-init <metricId>`,再用 `createMetricChart()` + `metric()` / `metricDim()`,生成后端 `CARD_TYPE.METRIC_CHART`。复杂指标卡片参数先读 `references/metric-chart-reference.md`
61
- 20. **杜邦分析图**:杜邦不是普通 `ChartType`,用 `createDuPontChart()` 创建 `LAYOUT` 卡片;节点通常放 `KPI_CARD` 子卡片,并通过 `.setRoot()` / `.addChild()` 组织树。页面布局只放杜邦父卡片,不单独放子卡片;筛选器 `linkToAll()` 会覆盖杜邦子卡片。
62
- 21. **布局组件**:支持 `小标题(AreaTitle)` / `卡片组(CardGroup)` / `筛选器组(SelGroup)` / `标签页(Tab)`。布局组件本身只支持放在画布根布局,不支持嵌套组合使用;SelGroup 内只能放 selector;checkout 会反编译根布局组件,无法安全表达的嵌套结构会失败;内部布局能力详见 `references/builder-reference.md`。
63
- 22. **资源包/checkout JSON 禁止手改**:只改 DSL,不改 ZIP 内部文件,也不改 `.guanvis/raw` / `.guanvis/base` / `.guanvis/manifest.json`;`upload` 只用于上传 `guanvis pack` 原样生成的包。批量重绑、迁移页面、补缺失操作符等需求先讨论方案,必要时扩展 DSL 操作,不手工改生成物。
64
- 23. **动态字段**:普通卡支持动态维度/动态数值,指标平台卡支持动态维度/动态指标;用户明确需要字段切换时使用 `.addDynamicRow()` / `.addDynamicMetric()` API,细节见 `references/builder-reference.md`。
65
- 24. **复杂报表 Pro 红线速记**(工作流程见下文「复杂报表 Pro 工作方式」,配方与报错修复先读 `references/complex-report-pro-patterns.md`):
66
- - **Pro 不是默认功能,需要单独授权**:只有用户明确说了"复杂报表 / 复杂报表 Pro",或修改的卡片本身已是 Pro(checkout 保持原类型),才选 Pro;用户只说"表格/报表/透视表"一律用原生卡片(DATA_GRID/PIVOT_TABLE 等),版式做不到时告知用户"Pro 可实现但需授权"由用户决定。
67
- - 只支持 `COMPLEX_REPORT_PRO`;旧版复杂报表拒绝,Pro 父卡不能用泛化 `attachCard()`。从零创建优先 Workbook DSL,拿到现成 xlsx 才用 `.setTemplate()`。
68
- - 同一数据视图只写 `context`,绝不加同源 `filterBy`(嵌套 LP 后端 500);`filterBy` 仅用于跨数据视图父格映射。
69
- - 左父格(纵向)允许在其列或左侧的**任意行**(阶梯布局、"汇总在上"总计/小计行挂下方维度均合法);上父格(横向)必须同列上方;各最多一个,双父格写 `A4*B3`。
70
- - 小计三件套:维度父格 → `setContextText` 标签 → `bindSubtotal`(标签同行左侧);总计 `bindGrandTotal` 禁止 context;小计行公式聚合用 `setDynamicFormula("SUM(D2)", { context: 标签格 })`;不要写老版 `G_SUBTOTAL/G_GRANDTOTAL`。
71
- - 聚合白名单 `SUM/COUNT/AVERAGE/MAX/MIN/PRODUCT/STDDEV/STDDEVP/VAR/VARP`(`AVG`→`AVERAGE`,`COUNTA` 拒绝);`bindDimension` 字段必须 DIM,数值列不能做维度。
72
- - 分页:单 Sheet、全模板禁 `filter/filterBy`、唯一 `countPerPage` 放无 context 顶层维度。段落块用 `sheet.beginBlock()`。多源 JOIN 用 `.setDataSourceRelations()`。
73
- - 模板引用的每个字段都必须进对应子视图查询区;子视图 `.setLimit()` 不限制导出行数。
74
- - 排序放数据视图 `.addSort()`(决定模板展开顺序);行级链接用 `HYPERLINK()` 动态公式(模板格超链接不随扩展复制);涨跌着色可直接用条件数字格式 `[Red]0"↑";[Green]0"↓"`。
75
- - **计算 BI 优先**:行级公式→guands 普通计算字段;比率/均值等非可加指标→聚合计算字段(`--calc-type aggregation`,引用时不写 aggrType),其小计/总计=每个粒度一个数据视图(禁用 bindSubtotal,会把比率按明细求和);GcExcel `setDynamicFormula` 只做排版级算术(序号/HYPERLINK/单格换算)。模板 `aggregate: COUNT` 作用于视图行不是明细行,计数放视图 zone 模板用 SUM 透传。
76
- - 新建 Pro 必须与引用它的 Page 同包发布,ID 用 `guanvis genid` 生成;pack/preview 通过≠语义正确,发布后必须 `guancli card preview <proCardId> -o result.xlsx` 检查无 `{{...}}` 残留。发布失败后重试若持续报"找不到相关卡片",直接 `genid` 换新 ID 重发。
77
- - 编辑已有 Pro 模板:统一走 `.setWorkbook()`。`report decompile` 全量可表达时输出 `createReportWorkbook()` 从零形态;含图片/数据验证等 lossy 部件时输出 `createReportWorkbook("templates/<cdId>.xlsx").editSheet(...)` 基底编辑形态(增量操作,未触及部件原样保留;覆盖模板格需 `{ overwrite: true }`,删除用 `clearCell`/`unmerge`)。publish 成功后 CLI 自动 bump JS 里的 `.setTemplateVersion()`;若提示未找到该调用,下轮改模板前必须手动 +1,否则命中服务端模板缓存静默不生效。
51
+ 9. **明细/滚动表 calcField**:`DETAIL_TABLE`/`SCROLL_TABLE` 只逐行展示,行级计算必须 `{ calculationType: "normal" }` 且禁用聚合/窗口函数;汇总需求改用非明细图表或 ETL 预计算
52
+ 10. **联动/下钻**:新建筛选器联动图表必须 `.linkToAll()` 或 `.linkTo(cardIndex)`;已有筛选器用 `attachCard(...).addLink()/.removeLink()/.clearLinks()`;筛选器级联用 `.linkToSelector(selectorId, targetFieldName?)`;卡片联动卡片用 `card.linkTo(layoutCardIndex, { fields: [{ source, target }] })`;固定路径下钻用 `registerDrillPath(parentCardIndex, [child.build()], { position: DrillPathPosition.BOTTOM })`;详细规则见 `references/builder-reference.md`
53
+ 11. **selector 类型**:离散值 `DS_ELEMENTS`(默认);连续数值 → `SelectorType.DS_INTERVAL`;日期 → `CALENDAR`;快捷日期区间(本月/近7天等)→ `.setTimeMacroOptions(options)`
54
+ 12. **同环比默认**:未指定输出值时默认增长率;未指定模式时默认 `ComparativeMode.FILTER_BASED`(普通模式需显式 `NORMAL`)。日期字段已是预聚合周期字段(如 `月开始日期`)时必须声明 `{ granularity: Granularity.NONE }`,避免按 DAY 筛选窗口计算为空
55
+ 13. **placeCard 入参**:优先用 card/selector ID 字符串(checkout 工程与子目录工程必须用),如 `placeCard("cardId", x, y, w, h)`。数字 index 仅限新建工程,按可布局资源(registerCard/MetricChart/TextCard/ImageCard/CustomChart/DuPontChart)的注册顺序累加,文件按文件名排序加载;**registerSelector 不参与 card index 计数**——selector 进画布/布局组件一律用其 ID 字符串。
56
+ 14. **publish 认证**:由底层 CLI 负责——guancli `guancli auth use <profile>`,guancli-lite 用环境变量
57
+ 15. **更新线上仪表板**:checkout 工程 = 修改指定线上 Page,保留原资源 IDCLI 检测到同 ID Page 时必须先向用户说明覆盖影响并取得明确确认,才可加 `--allow-overwrite`(自带覆盖备份兜底)。要"保留原页面出新版本"用 `guanvis page save-as <pgId> --suffix _guanvis`(BI 原生整页另存为,服务端重映射全部卡片间引用),不要复制 checkout JSON ID;细节见 `references/checkout-editing.md` §5
58
+ 16. **描述维护**:仅修复已发布资源描述时保留原 ID,用 `guanvis card/page set-description`;本地有 JS 工程时同步更新 `.setDescription(...)`
59
+ 17. **主题切换**:用户描述风格 工程目录里 `guanvis theme preference --keywords "..." --sync`;不要选租户默认"浅色"/"深色",没有合适主题就保持/清空偏好用内置"简约"兜底;改版未提风格时 `.applied.json` 自动继承上次主题。多子目录工程主题按子目录独立配置和执行;详见 `references/theme.md`
60
+ 18. **设计规则**:preview/pack/publish 自动应用内置设计规则;自定义时在工程目录新建 `design-rule.json`,不手改 `themes/<themeId>.json`;详见 `references/theme.md`
61
+ 19. **"指标卡片"语义分流**:数据集字段做单值/KPI `SINGLE_VALUE` / `KPI_CARD`;用指标平台已有指标建卡 → 先 `guanvis metric-init <metricId>` `createMetricChart()` + `metric()`/`metricDim()`;复杂指标卡片参数先读 `references/metric-chart-reference.md`
62
+ 20. **杜邦分析图**:用 `createDuPontChart()` 创建 `LAYOUT` 卡片(非普通 ChartType),节点放 `KPI_CARD` 子卡并用 `.setRoot()`/`.addChild()` 组织树;页面只放杜邦父卡片,筛选器 `linkToAll()` 会覆盖杜邦子卡片
63
+ 21. **布局组件**:AreaTitle/CardGroup/SelGroup/Tab 只支持放画布根布局、不支持嵌套组合;SelGroup 内只能放 selector;详见 `references/builder-reference.md`
64
+ 22. **资源包/checkout JSON 禁止手改**:只改 DSL 源文件,不改 ZIP 内部文件与 `.guanvis/**` JSON;批量重绑、迁移页面等需求先讨论方案,必要时扩展 DSL,不手工改生成物。
65
+ 23. **动态字段**:用户明确需要字段切换时用 `.addDynamicRow()` / `.addDynamicMetric()` 等(普通卡动态维度/数值,指标卡动态维度/指标);细节见 `references/builder-reference.md`。
66
+ 24. **复杂报表 Pro**:非默认功能,需单独授权——只有用户明确点名"复杂报表 / 复杂报表 Pro"或被修改卡片已是 Pro 才选 Pro;用户只说"表格/报表/透视表"一律用原生卡片(DATA_GRID/PIVOT_TABLE 等),版式做不到时告知"Pro 可实现但需授权"由用户决定。动手前必读 `references/complex-report-pro-patterns.md`(§0 选型、§2 配方、§3 数据视图规则、§4 报错修复、§5 编辑闭环、§6 验证闭环),全部结构红线与修复表以该文件为准;工作流程见下文「复杂报表 Pro 工作方式」
78
67
 
79
68
  ## 何时使用
80
69
 
81
- 遇到这些任务就用:
82
-
83
- - 新建观远 BI Card(柱形图、折线图、饼图、表格、KPI、散点图、漏斗、地图等 30+ 种)。
84
- - 基于指标平台已有指标创建指标卡片(MetricChart,后端 `cdType=13`),支持指标自身格式和适用维度。
85
- - 创建文本卡片(含内嵌指标引用)或图片卡片(外链/本地图片)。
86
- - 创建或 checkout/edit 复杂报表 Pro——**仅当用户明确点名"复杂报表 / 复杂报表 Pro",或被修改的卡片已是 Pro**(Pro 需单独授权,不是默认功能,泛化的"做个表格/报表"需求用原生卡片);旧版复杂报表则明确拒绝并建议外部迁移后新建 Pro。
87
- - 创建筛选器并配置联动关系(选择筛选器、日历筛选器等)。
88
- - 组装多个 Card 和筛选器为一个仪表板/Page,含 grid layout 布局和筛选器面板。
89
- - 批量生成一组 Card 并上传到 BI 系统。
90
- - 用户提到 `card.js`、`page.js`、CardPayload、图表类型、chart axes、zone spec、筛选器联动。
70
+ - 新建观远 BI Card(柱形/折线/饼/表格/KPI/散点/漏斗/地图等 30+ 种)、文本卡片、图片卡片。
71
+ - 基于指标平台已有指标创建指标卡片(MetricChart,后端 `cdType=13`)。
72
+ - 创建或 checkout/edit 复杂报表 Pro——**仅当用户明确点名"复杂报表 / 复杂报表 Pro"或被修改卡片已是 Pro**(需单独授权;泛化"做个表格/报表"用原生卡片);旧版复杂报表明确拒绝并建议外部迁移后新建 Pro。
73
+ - 创建筛选器并配置联动;组装 Card 和筛选器为仪表板/Page(grid layout + 筛选器面板)。
74
+ - 用户提到 `card.js`、`page.js`、CardPayload、图表类型、zone spec、筛选器联动。
91
75
 
92
76
  ## 固定工作方式
93
77
 
@@ -102,49 +86,30 @@ guancli auth status
102
86
  ### 2. 生成 schema.js(只做一次或数据集变更时重新生成)
103
87
 
104
88
  ```bash
105
- # 查找数据集
106
- guancli ds search <关键词>
107
-
108
- # 生成 schema.js(传入数据集 ID)
109
- guanvis init <dsId1> <dsId2> -d ./my_dashboard/
89
+ guancli ds search <关键词> # 查找数据集 ID
90
+ guanvis init <dsId1> <dsId2> -d ./my_dashboard/ # 生成 schema.js(多数据集加 --alias 命名全局变量)
110
91
  ```
111
92
 
112
- 如果用户给的是业务目录名而不是数据集名,先用 `guancli ds tree` 定位目录,再在目录内按真实数据集名搜索,不要把目录名直接当作数据集名反复 `ds search`。
113
-
114
- 生成的 `schema.js` 示例:
115
- ```javascript
116
- // Auto-generated — DO NOT EDIT
117
- defineDataset("s9ded2338f43807b095fbb4f", [
118
- { fdId: "jb16f22f6c1c21c3f6e8e293", name: "区域", fdType: "STRING", metaType: "DIM" },
119
- { fdId: "lb1c31b4dc86511a27049a98", name: "营收", fdType: "DOUBLE", metaType: "METRIC" },
120
- // ...
121
- ], { displayType: "EXCEL" });
122
- ```
93
+ 用户给的是业务目录名时,先 `guancli ds tree` 定位目录再按真实数据集名搜索,不要把目录名当数据集名反复 `ds search`。生成的 `schema.js` 是 `defineDataset(dsId, [{ fdId, name, fdType, metaType }...])` 形态的事实快照,不可编辑。
123
94
 
124
95
  ### 2b. 生成 metrics.js(创建指标平台指标卡片时)
125
96
 
126
- 当用户说“用指标创建卡片”“指标卡片”且给出的是指标平台 metric ID/指标名时,走这条链路,而不是把指标当作数据集字段:
97
+ 用户给的是指标平台 metric ID/指标名时走这条链路,不要把指标当数据集字段:
127
98
 
128
99
  ```bash
129
- # 先用 guancli 查指标 ID(如果用户只给名称)
130
- guancli metric search '<关键词>'
131
-
132
- # 生成指标事实快照
100
+ guancli metric search '<关键词>' # 用户只给名称时先查 ID
133
101
  guanvis metric-init <metricId1> <metricId2> -d ./my_dashboard/
134
102
  ```
135
103
 
136
- 生成后在 `card_*.js` 中使用:
137
-
138
104
  ```javascript
139
105
  var card = createMetricChart(ChartType.PIVOT_TABLE, "销售指标分析")
140
106
  .setId("cardId24chars")
141
107
  .addRow(metricDim("销售额", "区域"))
142
108
  .addMetric(metric("销售额"));
143
-
144
109
  registerMetricChart(card.build());
145
110
  ```
146
111
 
147
- `metrics.js` 和 `dynamic-parameters.js` 必须在 `card_*.js` 前加载;目录模式会自动按 `schema.js` → `metrics.js` → `dynamic-parameters.js` → `card_*.js` → `selector_*.js` → `page.js` 的顺序执行。
112
+ `metrics.js`、`dynamic-parameters.js` 和 `theme-colors.js` 必须在 `card_*.js` 前加载;目录模式会自动按 `schema.js` → `metrics.js` → `dynamic-parameters.js` → `theme-colors.js` → `card_*.js` → `selector_*.js` → `page.js` 的顺序执行。
148
113
 
149
114
  ### 3. 设置仪表板主题(按需)
150
115
 
@@ -152,75 +117,28 @@ registerMetricChart(card.build());
152
117
 
153
118
  ```bash
154
119
  cd ./my_dashboard
155
-
156
- # (a) 用户描述了风格关键词 —— 拉一次主题列表,AI 也能离线核对候选
157
- guanvis theme preference --keywords "深色 科技" --sync
158
-
159
- # (b) 用户给出明确 themeId
160
- guanvis theme preference --theme-id custom_blue --sync
161
-
162
- # (c) 想先看看候选名字
163
- guanvis theme sync # 拉列表
164
- guanvis theme list # 看候选 themeId / themeName / themeType
120
+ guanvis theme preference --keywords "深色 科技" --sync # (a) 用户描述了风格关键词
121
+ guanvis theme preference --theme-id custom_blue --sync # (b) 用户给出明确 themeId
122
+ guanvis theme sync && guanvis theme list # (c) 先看候选名字再决定
165
123
  ```
166
124
 
167
- > **多子目录工程**:每个子目录都是独立工程,主题各自隔离。如果不同子目录需要不同主题,**`cd` 进每个子目录分别跑一遍 `theme preference`**(每个子目录会有自己的 `themes/.preference.json` + 主题快照);只要任一子目录配了自己的 `themes/`,preview/pack/publish 就必须在对应子目录里运行(详见 `references/theme.md`)。如果整个工程主题统一,把 `themes/` 配在根目录、所有子目录共享即可。
168
-
169
- 命令产出的目录形态(与 `schema.js` 同级):
170
-
171
- ```
172
- my_dashboard/
173
- ├── schema.js
174
- ├── themes/
175
- │ ├── .preference.json # theme preference 写入的偏好(themeId / keywords)
176
- │ ├── .index.json # theme sync 拉到的候选主题索引
177
- │ ├── .sync-meta.json # syncedAt / count
178
- │ ├── default_7.json # theme sync 落盘的主题快照(每个 themeId 一份)
179
- │ └── custom_blue.json
180
- └── ... (card_*.js / page.js 后面再加)
181
- ```
182
-
183
- `.applied.json` 会在第一次 `pack`/`publish` 实际使用了线上主题时自动写入;这之前不存在很正常。回退到 skill 自带 `simple.json` 时**故意不写** `.applied.json`,避免污染下次解析。
184
-
185
- 何时跳过这一步:
186
-
187
- - **改版已有看板**(工程下已存在 `themes/.applied.json`)→ 跳过,`pack`/`publish` 会自动继承上次主题。
188
- - **新看板但用户没描述风格** → 跳过,自动落到 skill 自带的 `simple.json`(与原行为一致;离线/弱网都不会失败)。
189
- - **候选主题只剩默认“浅色”/“深色”或都不贴合需求** → 不要为了命中而随便选,清空/不写偏好并使用内置“简约”兜底。
190
- - 后续用户改主意要换风格 → 再跑一次 `theme preference` 就行,不需要重做 schema 或 card。
191
-
192
- 完整决策规则、`themes/` 目录结构、离线 vs 联网约定见 `references/theme.md`。
125
+ 何时跳过:改版已有看板(`.applied.json` 自动继承上次主题)、新看板但用户没描述风格(自动落到内置 `simple.json`)、候选只剩默认"浅色"/"深色"或都不贴合(不要随便选,清空/不写偏好用内置"简约"兜底)。多子目录工程的主题各子目录独立配置和执行。完整决策规则、`themes/` 目录文件说明、离线 vs 联网约定、排错见 `references/theme.md`。
193
126
 
194
127
  ### 4. 编写 card 脚本
195
128
 
196
- card 脚本使用 `field()` 或简写 `f()` 从 schema 引用字段(无需记忆 fdId):
129
+ card 脚本用 `field(DS, "字段")` 或单数据集简写 `f("字段")` 从 schema 引用字段(无需记忆 fdId):
197
130
 
198
131
  ```javascript
199
- // card_01_revenue.js — 使用 field(DS, ...) 完整写法
132
+ // card_01_revenue.js
200
133
  var card = createCard(ChartType.GROUPED_COLUMN, "营收分析")
201
134
  .bindDataset(DS)
202
- .setDescription("业务故事:用于回答各区域营收规模与产品结构差异,目标用户是经营管理层。指标口径:营收按 SUM 聚合。")
203
- .addRow(field(DS, "区域"))
204
- .addMetric(field(DS, "营收", {
205
- aggrType: AggrType.SUM,
206
- numberFormat: NumberFormat.currency("¥", 0)
207
- }))
208
- .addColumn(field(DS, "产品"))
209
- .setShowLegend(true, "right")
210
- .setDataLabel({ show: true, showNumber: true });
211
-
212
- registerCard(card.build());
213
- ```
214
-
215
- ```javascript
216
- // card_02_pivot.js — 使用 f() 简写(单数据集场景)
217
- var card = createCard(ChartType.PIVOT_TABLE, "区域产品交叉分析")
218
- .bindDataset(DS)
135
+ .setDescription("业务故事:回答各区域营收规模与产品结构差异,面向经营管理层。营收按 SUM 聚合。")
219
136
  .addRow(f("区域"))
220
137
  .addColumn(f("产品"))
221
- .addMetric(f("营收", { aggrType: AggrType.SUM }))
138
+ .addMetric(f("营收", { aggrType: AggrType.SUM, numberFormat: NumberFormat.currency("¥", 0) }))
222
139
  .addSort(f("营收", { aggrType: AggrType.SUM, sortType: SortOrder.DESC }))
223
- .setTableSetting({ showRowTotal: true, showColumnTotal: true });
140
+ .setShowLegend(true, "right")
141
+ .setDataLabel({ show: true, showNumber: true });
224
142
 
225
143
  registerCard(card.build());
226
144
  ```
@@ -240,238 +158,95 @@ var page = createPage("销售仪表板")
240
158
  registerPage(page.build());
241
159
  ```
242
160
 
243
- **仪表板主题**:项目目录下没配 `themes/` 时,`preview`/`pack`/`publish` 自动使用 skill 自带的简约主题(embed 在二进制里的 `simple.json`,不依赖租户线上主题列表)。如需切换租户自定义主题或带明确风格的默认主题,请参考 `references/theme.md`,通过 `guanvis theme preference` 写入 `themes/.preference.json`;租户默认“浅色”/“深色”没有特殊样式,不作为可选主题。**JS DSL 不再提供主题相关接口**——任何 `setDashboardTheme(...)` `page.setTheme(...)` 调用都会因函数未定义而报错。
161
+ **仪表板主题**:没配 `themes/` 时 `preview`/`pack`/`publish` 自动使用内置简约主题(不依赖租户线上主题列表);普通图表与指标平台 MetricChart 都会自动应用主题视觉配置(只写 `settings` `meta.chartMain.props`,不改 `zoneData` 等查询字段)。**JS DSL 不提供主题接口**——`setDashboardTheme(...)` / `page.setTheme(...)` 会因函数未定义而报错,主题一律走 `guanvis theme preference`(见 `references/theme.md`)。
244
162
 
245
- 指标平台 MetricChart 也会在 `preview`/`pack`/`publish` 阶段自动应用主题视觉配置:透视表会补表格/合计样式,柱线饼等会补坐标轴、图例、数据标签和主题色。主题只写 `settings` `meta.chartMain.props`,不改 `zoneData`、`dsInfo`、`defaultView` 等影响指标查询的字段。
163
+ **单张图表主题色**:用 `.setThemeColor(tcId, colors?, options?)` 切换主题色、修改颜色覆盖或选择分类/顺序色板,`options.useSequentialPalette` 用于显式选择色板模式。只修改当前主题时 `tcId` `null`;编写配置前必须检查项目根目录的 `theme-colors.js`,不存在时先运行 `guanvis theme-color sync -d <project>`,非空 `tcId` 必须取自快照。多页面和多子目录共用这一份快照,构建阶段的自动生成只作兜底。完整语义见 `references/chart-properties.md`。
246
164
 
247
- **多页面支持**:可以在同一个项目中注册多个 page(多次调用 `registerPage`)。两种组织方式:
165
+ **多页面支持**:同一项目可多次 `registerPage`。两种组织方式:
248
166
 
249
167
  1. **单目录**:所有 card 和 page 在同一目录,page 通过 card index(注册顺序)引用
250
- 2. **子目录**:每个子目录有独立的 `schema.js`、`card_*.js`、`page.js`。page 通过 card ID 字符串引用(推荐)
168
+ 2. **子目录**(推荐):每个子目录是独立看板工程,有自己的 `schema.js`、`card_*.js`、`page.js`、`themes/`、`charts/`;page 布局必须用 card ID 字符串而非 index,如 `addFullWidthCard("cardId24chars", 8)`
251
169
 
252
- 子目录结构示例:
253
- ```
254
- project/
255
- ├── overview/
256
- │ ├── schema.js
257
- │ ├── dynamic-parameters.js ← 全局参数快照(按需)
258
- │ ├── themes/ ← 主题偏好与缓存(按需,仅当跑过 theme preference / sync)
259
- │ │ ├── .preference.json
260
- │ │ ├── .applied.json
261
- │ │ ├── .index.json
262
- │ │ ├── .sync-meta.json
263
- │ │ └── <themeId>.json
264
- │ ├── card_01_kpi.js
265
- │ ├── card_02_chart.js
266
- │ ├── page.js ← addFullWidthCard("card_id_string", 6)
267
- │ └── charts/ ← 自定义图表内容
268
- ├── detail/
269
- │ ├── schema.js
270
- │ ├── themes/ ← 每个子目录是独立工程,主题各自维护
271
- │ │ └── ...
272
- │ ├── card_01_table.js
273
- │ └── page.js
274
- ```
275
-
276
- > 子目录模式下每个子目录是独立的看板工程,`themes/` 与 `schema.js` 同级、各自隔离;preview/pack/publish/`theme *` 命令必须 `cd` 进对应子目录单独执行。**在父级根目录直接 `guanvis pack/preview/publish .` 时,若任一子目录里出现了 `themes/`,命令会直接报错并提示需要进入子目录单独执行**——这避免了根目录运行时各子目录主题被静默忽略、全部退回内置 `simple.json` 的隐蔽问题。如果整个工程不需要主题(所有子目录都没有 `themes/`),在根目录一次性 `pack` 也照常工作,行为不变。
277
-
278
- 子目录模式下,page 布局必须使用 card ID 字符串而非 index:
279
- ```javascript
280
- var p = createPage("Detail Page")
281
- .setId("pgId24chars")
282
- .addFullWidthCard("cardId24chars", 8); // 用 card ID 而非 index
283
- registerPage(p.build());
284
- ```
170
+ 子目录模式下 preview/pack/publish/`theme *` 必须 `cd` 进对应子目录单独执行;**根目录直接运行时,若任一子目录有 `themes/`,命令会直接报错并提示 cd**(避免子目录主题被静默忽略)。整个工程都没配 `themes/` 时根目录一次性 `pack` 照常工作。
285
171
 
286
172
  ### 6. 执行命令
287
173
 
288
174
  ```bash
289
- # 生成资源 ID(用于 .setId() 调用)
290
- guanvis genid # 生成 1 个
291
- guanvis genid 5 # 生成 5
292
- # genid 保证字母开头;手写数字开头 ID 会被 preview/pack/publish 对新建资源直接报错拦截
293
-
294
- # 生成布局组件 ID
295
- guanvis gen-layout-id tab # 生成 1 个 tab_ + 默认 6 位字母
296
- guanvis gen-layout-id panel 3 --length 8 # 生成 3 个 panel_ + 8 位字母;length 只计算下划线后的随机字母,超出 6~10 时自动收敛
297
- guanvis gen-layout-id areaTitle # 生成 1 个 areaTitle_ + 默认 6 位字母
298
- guanvis gen-layout-id cardGroup # 生成 1 个 cardGroup_ + 默认 6 位字母
299
- guanvis gen-layout-id selGroup # 生成 1 个 selGroup_ + 默认 6 位字母
300
-
301
- # 拉取已有线上仪表板为可编辑工程(只读 BI,不发布;尽量生成 schema.js)
175
+ # 生成资源 ID(用于 .setId();genid 保证字母开头,手写数字开头 ID 会被新建校验报错拦截)
176
+ guanvis genid 5
177
+ guanvis gen-layout-id <tab|panel|areaTitle|cardGroup|selGroup> [数量] [--length N] # 布局组件 ID,如 tab_AbCdEf
178
+
179
+ # 拉取已有线上仪表板为可编辑工程(只读 BI;细节与红线见 references/checkout-editing.md)
302
180
  guanvis checkout <pageId> -d ./existing_dashboard
303
- # checkout 后只编辑 schema 外的 card_*.js / selector_*.js / page.js;不要修改或复制 .guanvis/raw、.guanvis/base、.guanvis/manifest.json
304
- # 自定义图表的脚本/HTML/CSS/内嵌资源会反编译到 charts/ 下(可编辑源文件),改完 pack/publish 自动回填
305
- # 若 schema.js 未生成或缺字段,后续可手动 guanvis init <dsId> -d ./existing_dashboard --force 补齐
306
- # checkout --overwrite 会清理输出目录下所有根级 .js 和旧 .guanvis,避免 schema/selector/metrics/other.js 残留混入运行
181
+ # schema.js 未生成或缺字段时:guanvis init <dsId> -d ./existing_dashboard --force 补齐
182
+ # checkout --overwrite 会清理输出目录下所有根级 .js 和旧 .guanvis,避免残留混入运行
307
183
 
308
- # 预览生成结果(默认输出摘要 JSON:卡片/页面清单 + changeSummary + 验证状态;
309
- # 全量 payload 写入工程目录 .preview.json,需要时用 read_file 按需查看,或加 --full 全量输出)
184
+ # 预览(默认摘要 JSON:卡片/页面清单 + changeSummary + 验证状态;全量 payload 落盘 .preview.json,--full 全量输出)
310
185
  guanvis preview ./my_dashboard/
311
- # checkout/attach 工程的路径级变更摘要(也会出现在 preview JSON 的 changeSummary 中)
312
- guanvis diff ./existing_dashboard/
313
-
314
- # 打包为 ZIP 资源包
315
- guanvis pack ./my_dashboard/
316
- guanvis pack -o output.zip ./my_dashboard/
317
-
318
- # 一步到位:构建并上传到 BI(在线同步)
319
- guanvis publish ./my_dashboard/
320
- guanvis publish ./my_dashboard/ --page-parent-dir <dir_id> # 指定页面目录
321
-
322
- # 上传已有 ZIP 资源包;只允许上传 guanvis pack 原样生成的 ZIP
323
- guanvis upload output.zip
324
-
325
- # 修复已发布资源描述(保留原 Card/Page ID)
326
- # 如果本地维护对应 JS 工程,也同步更新 card_*.js / page.js 里的 .setDescription(...)。
327
- guanvis card set-description <cd_id> --description "这张卡用于回答..."
328
- guanvis card set-description <cd_id> --file ./card_story.md
329
- guanvis page set-description <pg_id> --description "这张页面用于回答..."
330
- guanvis page set-description <pg_id> --file ./page_story.md --visible=false
331
-
332
- # 页面视觉验证(可选,PNG,通过 BI 后端服务端截图,不依赖浏览器)
333
- # 仅在明确需要视觉质量判断且当前大模型支持图像理解时使用;图像分析会额外消耗 token/费用。
334
- guanvis screenshot <pageId> # 截图页面 PNG 到 <pageId>.png
335
- guanvis screenshot <pageId> -o /tmp/dashboard.png # 指定输出路径
336
- guanvis screenshot <pageId> --orientation horizontal # 横向截图
337
-
338
- # 数据集/指标切换后的检查(仅梳理)
339
- guanvis check-dataset-usage . --ds <dsId> # 盘点某个 dsId 在工程中的引用
340
- guanvis check-dataset-switch . --from <oldDs> --to <newDs> --mode full # 整页/整包数据集切换后检查
341
- guanvis check-dataset-switch . --from <oldDs> --to <newDs> --mode linked --changed-cards <cdId> # 局部切数据集后检查一跳关联资源
342
- guanvis check-metric-switch . --from <oldMetric> --to <newMetric> --mode linked --changed-cards <cdId> # 局部切指标后检查一跳关联资源
343
-
344
- # 仪表板主题(详见 `references/theme.md`,[dir] 缺省为当前目录)
345
- guanvis theme preference --keywords "深色 科技" --sync # 在当前目录写入偏好并同步主题列表
346
- guanvis theme preference ./my_dashboard --theme-id custom_blue # 显式指定工程目录
347
- guanvis theme preference --clear # 清空当前目录的偏好与 applied 快照(彻底回到内置 simple.json)
348
- guanvis theme list # 列出已落盘的候选主题
349
- guanvis theme show # 打印当前主题决策(不联网)
350
- guanvis theme sync # 强制刷新主题列表
351
- ```
352
-
353
- **多子目录工程的执行方式**:每个子目录是独立工程,preview/pack/publish 必须在每个子目录里分别执行,每次产出独立的 ZIP 资源包;想要每个子目录用不同主题,就在每个子目录里分别 `theme preference`。在根目录直接运行 preview/pack/publish 时,若任一子目录里有 `themes/`,命令会拒绝执行并打印 `cd <subdir> && guanvis <preview|pack|publish> .` 提示。
354
-
355
- ```bash
356
- # 多子目录工程 + 各子目录主题不同 → 在每个子目录分别执行
357
- for sub in overview detail kpi; do
358
- cd "./project/$sub"
359
- guanvis theme preference --keywords "..." --sync
360
- guanvis publish .
361
- cd -
362
- done
363
-
364
- # 多子目录工程 + 整个工程不需要主题(无任一子目录配 themes/)→ 根目录一次性 pack 仍然可用(与原行为一致)
365
- guanvis pack ./project
366
- ```
367
-
368
- `publish` 和 `upload` 使用 BI 的 **transfer API**(`/api/manual/template/transfer`),特点:
369
- - `needIdMapping=false`:保持资源 ID 不变,重复导入会覆盖同 ID 资源
370
- - 发布前强制校验 Page 归属:资源包包含 Card/Selector 时必须同时包含 Page,且每个 Card/Selector 都必须被至少一个 Page 引用;Card-only 和孤儿 Card/Selector 包会在上传前被拒绝,并提示在 `page.js` 中放置资源后同包发布
371
- - 在线覆盖检查只探测 Page ID,不探测 Card/Selector ID,避免部分 BI 版本在导入前缓存"找不到相关卡片"
372
- - 认证方式:随底层 `guancli fetch` 使用 `Cookie: uIdToken=...`
373
- - 需要 `raw-backend-response: TRUE` header 绕过前端代理层
374
- - 不需要目标系统开启"一键迁移"开关,所有环境通用
375
-
376
- **资源包安全约束**:`upload` 只是上传器,不是制作自定义资源包的入口。除非用户明确批准,否则不得上传手工生成、解包修改、重打包或批量替换内部内容后的 ZIP。需要批量重绑数据集、字段、卡片或页面 ID 时,先讨论方案,不要直接改 ZIP。
377
-
378
- **线上仪表板更新策略**:新建工程发布的是新 Page;checkout 工程发布的是对 checkout 指定 Page 的覆盖式修改,不承担“复制新版本”职责。需要保留原页面并生成新版本时,不要在 guanvis CLI 内手工复制 JSON 或改 ID map;用 `guanvis page save-as <pgId> --suffix _guanvis`(或 `--name <新名>`,可加 `--parent-dir <dirId>`)做 BI 原生整页另存为,服务端会重映射全部卡片间引用并由命令自动 release 发布副本,然后 checkout 副本 Page 继续编辑。命令会回读副本核对卡片数(无指标平台 license 时指标卡会被服务端过滤)并扫描是否残留源页卡片 ID 引用,出现 ⚠ 警告时先排查再继续。对 checkout 工程同 ID 发布时,命令必须显式加 `--allow-overwrite` 允许同 ID Page 覆盖;发布前可用 `--dry-run` 查看会覆盖哪些线上 Page。Card/Selector ID 不做在线覆盖检查。**Agent 禁止在未确认的情况下自行加 `--allow-overwrite`**:当 CLI 提示将覆盖线上 Page 时,必须先停止发布,向用户说明将覆盖的 Page ID、名称和覆盖后可能替换原页面布局,等用户明确确认“覆盖”后才可以重跑并加 `--allow-overwrite`。使用 `--allow-overwrite` 时,CLI 会先为冲突 Page 发起资源包导出备份并等待导出成功;备份包含 Page 及其组成资源,但不会沿血缘额外导出数据集、数据账户等上游资源,避免普通用户因缺少上游资源所有者权限而无法备份。备份未成功则中止覆盖。CLI 只记录备份导出记录和 packageId,不自动下载资源包;需要回滚时,到 BI 资源迁移导出记录中手动下载该资源包后再导入覆盖回去。
379
-
380
- ## 复杂报表 Pro 工作方式
381
-
382
- **进入本节的前提**:用户明确点名了"复杂报表 / 复杂报表 Pro",或 checkout 的目标卡片已是 Pro。Pro 是需要单独授权的非默认功能——未点名的表格/报表需求回到「固定工作方式」用原生卡片实现。
186
+ guanvis diff ./existing_dashboard/ # checkout/attach 工程的路径级变更摘要
383
187
 
384
- 与「固定工作方式」同体系(auth init → 写脚本 → pack publish),差异只在脚本形态和验证闭环。**动手前先读 `references/complex-report-pro-patterns.md`**:§0 选型门槛与判定(平面表/透视表不要用 Pro)、§2 结构模式配方(分组/交叉/小计/分页/块/多源等 10 个套路直接套用)、§4 报错修复表。
188
+ # 发布(publish 自带构建打包上传;pack 只用于生成离线 ZIP upload)
189
+ guanvis publish ./my_dashboard/ [--page-parent-dir <dir_id>]
190
+ guanvis pack [-o output.zip] ./my_dashboard/
191
+ guanvis upload output.zip # 只允许上传 guanvis pack 原样生成的 ZIP
385
192
 
386
- ### Pro-1. 新建(init genid → 数据视图 + workbook → page → pack/publish
193
+ # 修复已发布资源描述(保留原 ID;本地有 JS 工程时同步更新 .setDescription(...)
194
+ guanvis card set-description <cd_id> --description "..." # 或 --file ./story.md
195
+ guanvis page set-description <pg_id> --description "..." # 支持 --visible=false
387
196
 
388
- ```bash
389
- guanvis init <dsId> -d ./pro_report/ # 单数据集
390
- guanvis init <ds1> <ds2> -d ./x/ --alias ORDERS,SALES # 多数据集必须用 --alias 命名全局变量
391
- cd ./pro_report && guanvis genid 3 # 父卡 + 每个数据视图 + page 各一个 ID
392
- ```
197
+ # 页面视觉验证(可选 PNG,服务端截图不依赖浏览器;仅在需要视觉判断且模型支持图像理解时用,消耗额外 token)
198
+ guanvis screenshot <pageId> [-o out.png] [--orientation horizontal]
393
199
 
394
- 多数据集时**不要**手写 `var X = defineDataset(...)`(schema.js 的顶层 var 不会进入其他脚本的作用域),一律用 `--alias`;单数据集裸引用可用 `f("字段")`,多数据集用 `field(SALES, "字段")` 指明归属(`f()` 永远取第一个数据集)。
200
+ # 数据集/指标切换后的检查(仅梳理):check-dataset-usage / check-dataset-switch / check-metric-switch
201
+ guanvis check-dataset-switch . --from <oldDs> --to <newDs> --mode full # 详见 --help;--mode linked 查一跳关联
395
202
 
396
- `card_01_xxx.js` 最小骨架(分组小计形态;其他结构套 patterns §2 配方):
203
+ # 仪表板主题(详见 references/theme.md)
204
+ guanvis theme preference --keywords "..." --sync # 另有 --theme-id / --clear;theme list/show/sync
397
205
 
398
- ```javascript
399
- // 数据视图:模板引用到的字段都必须出现在 row/metric 查询区
400
- var orders = createCard(ChartType.DATA_GRID, "orders")
401
- .setId("<genid>")
402
- .bindDataset(DS)
403
- .addRow(f("区域"))
404
- .addMetric(f("金额", { aggrType: AggrType.SUM }));
405
-
406
- var workbook = createReportWorkbook().addSheet("月报", function (sheet) {
407
- sheet.merge("A1:B1")
408
- .setValue("A1", "销售月报", { fontSize: 16, bold: true, horizontalAlignment: "center", verticalAlignment: "center" })
409
- .setRowHeight(1, 34)
410
- .setValue("A2", "区域", { bold: true }).setValue("B2", "金额", { bold: true })
411
- .bindDimension("A3", reportField("orders", "区域"))
412
- .bindMetric("B3", reportField("orders", "金额"), { aggregate: "SUM", context: "A3", numberFormat: "#,##0.00" })
413
- .setContextText("A4", "小计", { context: "A3" })
414
- .bindSubtotal("B4", reportField("orders", "金额"), { aggregate: "SUM", context: "A4" })
415
- .bindGrandTotal("B5", reportField("orders", "金额"), { aggregate: "SUM" });
416
- });
417
-
418
- registerComplexReportPro(createComplexReportPro("销售月报")
419
- .setId("<genid>")
420
- .setWorkbook(workbook)
421
- .addDataView("orders", orders)
422
- .setColumnWidthStretch(true)
423
- .build());
206
+ # 单张图表主题色
207
+ guanvis theme-color list
208
+ guanvis theme-color sync -d ./my_dashboard
424
209
  ```
425
210
 
426
- `page.js` 正常 `registerPage`(Pro 卡按注册顺序参与 card index,或用 ID 字符串);**所有 Card/Selector 都必须与引用它们的 Page 同包发布**,Pro 也不例外。之后与普通流程一致:`pack` 确认 → `publish`。
211
+ **多子目录工程**:每个子目录是独立工程,preview/pack/publish/`theme *` 必须 `cd` 进各子目录分别执行、各出各的资源包;根目录直接运行时若任一子目录有 `themes/` 会拒绝执行并打印 cd 提示。
427
212
 
428
- ### Pro-2. 编辑已有 Pro(checkout inspect/decompile diff publish
213
+ **发布机制**:`publish`/`upload` BI transfer API,`needIdMapping=false` 同 ID 覆盖、发布前强制校验 Page 归属(Card-only 与孤儿 Card/Selector 包直接拒绝)、覆盖检查只探测 Page ID;接口/认证/header 细节见 `references/publish-and-constraints.md`。
429
214
 
430
- ```bash
431
- guanvis checkout <pageId> -d ./work # 模板下载到 templates/,templateVersion 自增
432
- guanvis report inspect ./work/templates/<cdId>.xlsx # 不开 Excel 看模板格/父格链/环检测
433
- guanvis report decompile ./work/templates/<cdId>.xlsx -o wb.js # 反编译为 Workbook DSL 或 patch 脚手架
434
- ```
435
-
436
- 创建与编辑共用**一套 Workbook DSL**:`createReportWorkbook()` 从零构建(`addSheet`),`createReportWorkbook("templates/<cdId>.xlsx")` 以已有模板为基底做增量编辑(`editSheet`),挂载入口统一是 `.setWorkbook(workbook)`。decompile 有两种产物:模板完全可表达时输出从零构建形态(冻结窗格/条件格式/序号列/阶梯布局都能还原);含 DSL 无法表达部件(内嵌图片、数据验证等)时自动输出基底编辑脚手架 `createReportWorkbook(path).editSheet(...)`——只写要改的操作,未触及部件原样保留,现有模板格以注释列在脚手架里供参考。`editSheet` 内方法与 `addSheet` 完全同名,另有仅编辑模式可用的原语:`clearCell`/`unmerge` 删除,`insertRows`/`removeRows`/`insertCols`/`removeCols` 结构编辑(自动重写模板表达式引用与父格,删除被引用模板格会拒绝;插入后坐标按移位后位置书写),`setCellStyle(cellOrRange, style)` 合并式改样式(只覆盖给出的属性);落点已有模板表达式时须在 options 加 `{ overwrite: true }`。改完 `guanvis diff` 看影响面(基底编辑的指令流会逐条出现在 changeSummary),发布走覆盖确认流程(`--allow-overwrite` 前必须征得用户确认)。
215
+ **线上仪表板更新红线**:checkout 工程发布 = 覆盖式修改指定 Page。CLI 检测到同 ID Page 时,**Agent 禁止未经用户确认自行加 `--allow-overwrite`**——必须先说明将覆盖的 Page ID/名称与影响,用户明确确认后才可重跑加该参数(CLI 会先做覆盖备份,备份失败则中止;`--dry-run` 可预查覆盖对象)。要"保留原页面出新版本"用 `guanvis page save-as`,不要复制 JSON 改 ID。备份/回滚/save-as 细节见 `references/checkout-editing.md` §5。
437
216
 
438
- **templateVersion 生命周期**:checkout 生成线上版本 +1;`publish` 成功后 CLI 自动把工程 JS 里的 `.setTemplateVersion(N)` 改写为 N+1(防止下一轮模板编辑与线上同版本、命中服务端模板缓存静默不生效)。看到 "Bumped .setTemplateVersion" 输出属正常;若提示找不到调用,须手动在 JS 中补 `.setTemplateVersion(N+1)` 再改模板。
217
+ ## 复杂报表 Pro 工作方式
439
218
 
440
- **多轮基底编辑防护**:基底编辑(`createReportWorkbook(templatePath)`)的发布结果 = 本地 templates/ 快照 + JS 全部操作。多轮修改必须累积追加操作;只保留新一轮操作会让上一轮修改从线上静默消失。preview/diff/pack/publish 检测到基底落后(发布过后未刷新)会打印 stale-base warning;此时要么确认操作已累积,要么 `guanvis checkout <pgId> -d <dir> --refresh-base` 刷新基底(不动 card_*.js/page.js),刷新后删除已发布的旧操作再写新增量。
219
+ **进入前提**:用户明确点名"复杂报表 / 复杂报表 Pro",或 checkout 的目标卡片已是 Pro(未点名的表格/报表需求回「固定工作方式」用原生卡片)。只支持 `COMPLEX_REPORT_PRO`,旧版复杂报表拒绝并建议外部迁移后新建 Pro。
441
220
 
442
- ### Pro-3. 验证闭环(发布后必做)
221
+ 与「固定工作方式」同体系(auth → init → 写脚本 → preview/pack → publish),差异只在脚本形态和验证闭环。**动手前先读 `references/complex-report-pro-patterns.md`**:
443
222
 
444
- `pack`/`preview` 只验证结构,**只有 GcExcel 能验证模板语义**:
445
-
446
- ```bash
447
- guancli card preview <proCardId> -o result.xlsx # 1. 真实渲染导出
448
- # 2. 检查 result.xlsx:无 {{...}} 残留、行数/分组符合预期、抽查小计值
449
- guancli card preview <proCardId> --filter "字段 EQ 值" -o f.xlsx # 3. 有筛选联动时验证 childFilters
450
- ```
223
+ - **新建**:§0 选型门槛 → §2.0 最小工程骨架(init/`--alias`/genid + 数据视图/workbook/注册三段式)→ §2 结构配方 → §3 数据视图规则。Pro 卡必须与引用它的 Page 同包发布。
224
+ - **编辑已有 Pro**:checkout 后统一走 `.setWorkbook()`;decompile 产物形态、`editSheet` 原语、templateVersion 生命周期、stale-base 防护全在 §5。
225
+ - **报错修复**:对照 §4 修复表。
226
+ - **验证闭环(发布后必做)**:`pack`/`preview` 只验证结构,发布后必须 `guancli card preview <proCardId> -o result.xlsx` 检查无 `{{...}}` 残留,详见 §6。
451
227
 
452
228
  ## 文件结构约定
453
229
 
454
230
  目录模式下文件加载顺序:
455
- 1. `schema.js` — 数据集定义(init 生成;checkout 尽量生成,不可修改)
231
+ 1. `schema.js` — 数据集定义(init/checkout 生成,不可修改)
456
232
  2. `metrics.js` — 指标定义(仅指标卡片需要,自动生成,不可修改)
457
233
  3. `dynamic-parameters.js` — 项目全局参数快照(按需,由 parameter 命令或 checkout 生成)
458
- 4. `card_01_xxx.js` ~ `card_NN_xxx.js` — Card 定义(按文件名排序)
459
- 5. `selector_01_xxx.js` ~ `selector_NN_xxx.js` — 筛选器定义(在 card 之后执行,因为联动需要引用 card 索引)
460
- 6. `page.js` — Page/仪表板组装
461
-
462
- 自定义图表时,图表内容文件(如 ECharts 脚本)放在 **子目录**(如 `charts/`),避免被当作 card 脚本执行。
234
+ 4. `theme-colors.js` 项目级图表主题色快照(配置主题色前由 `theme-color sync` 生成或刷新,不可修改;多页面/多子目录共用;构建阶段缺失时会兜底生成)
235
+ 5. `card_01_xxx.js` ~ `card_NN_xxx.js` — Card 定义(按文件名排序)
236
+ 6. `selector_01_xxx.js` ~ `selector_NN_xxx.js` 筛选器定义(在 card 之后执行,因为联动需要引用 card 索引)
237
+ 7. `page.js` — Page/仪表板组装
463
238
 
464
- `themes/` 子目录由 `theme *` 子命令维护,与 `schema.js` 同级;preview/pack/publish 会自动读取并应用。点前缀文件(`.preference.json` / `.applied.json` / `.index.json` / `.sync-meta.json`)是元数据/缓存,`<themeId>.json` 是主题快照——所有文件**不要手改**,详见 `references/theme.md`。
239
+ 自定义图表内容文件(如 ECharts 脚本)放子目录(如 `charts/`),避免被当作 card 脚本执行;`themes/` `theme *` 命令维护、全部文件不手改(见 `references/theme.md`)。
465
240
 
466
241
  ## 参考示例
467
242
 
468
243
  | 示例 | 路径 | 说明 |
469
244
  |------|------|------|
470
- | 基础仪表板 | `evals/sales_dashboard/` | 普通卡片(柱状图、折线图、KPI、饼图)+ 筛选器 + 页面布局 |
471
- | 拆分图 | `evals/split_charts/` | 柱形等图表按字段拆分 |
472
- | 自定义图表 | `evals/custom_chart_echarts/` | ECharts Lite 自定义图表:柱状图 + 饼图,使用 `loadContent()` 文件模式 |
473
- | 复杂报表 Pro | `evals/complex_report_pro/` | 最小 Workbook DSL 工程;其余结构形态(小计/交叉/分页/块/多源等)的配方以 `references/complex-report-pro-patterns.md` §2 的代码片段为准 |
474
- | Tab 布局 | `evals/tab_layout/` | 单页面 tab 示例,含根布局指标卡、图表卡片、文本卡片、筛选器和 panel 内卡片布局 |
245
+ | 基础仪表板 | `evals/sales_dashboard/` | 普通卡片 + 筛选器 + 页面布局 |
246
+ | 拆分图 | `evals/split_charts/` | 图表按字段拆分 |
247
+ | 自定义图表 | `evals/custom_chart_echarts/` | ECharts Lite,`loadContent()` 文件模式 |
248
+ | 复杂报表 Pro | `evals/complex_report_pro/` | 最小 Workbook DSL 工程(结构配方以 patterns.md §2 为准) |
249
+ | Tab 布局 | `evals/tab_layout/` | 根布局指标卡 + panel 内卡片 + 筛选器 |
475
250
 
476
251
  生成脚本前先查阅对应示例中的文件结构和写法。
477
252
 
@@ -479,14 +254,15 @@ guancli card preview <proCardId> --filter "字段 EQ 值" -o f.xlsx # 3. 有
479
254
 
480
255
  | 场景 | 路径 | 读取时机 |
481
256
  |------|------|----------|
482
- | 字段、计算字段、NumberFormat、高级计算和卡片筛选器 API | `references/api-reference.md` | 编写字段、指标、公式或筛选条件时 |
483
- | Card/Page/Tab/Selector/Text/Image/CustomChart Builder API 与枚举 | `references/builder-reference.md` | 编写或修改 JS DSL builder 调用时 |
484
- | 复杂报表 Pro 选型/配方/报错修复 | `references/complex-report-pro-patterns.md` | 创建或修改 Pro 前先读;pack/publish 报 Pro 相关错误时查 §4 |
485
- | 复杂报表 Pro Builder API 权威定义 | `references/builder-reference.md` 的 ComplexReportProBuilder | 需要完整方法签名、options 取值或协议细节时 |
486
- | zone 校验、图表选型、地图/拆分图、字段对象细节 | `references/validation-and-chart-patterns.md` | pack 报错、选型不确定或需要特殊图表行为时 |
487
- | 仪表板主题机制与 theme 命令排错 | `references/theme.md` | 用户指定视觉风格、改版继承主题或主题异常时 |
488
- | 在线同步、认证、ID 管理和硬约束 | `references/publish-and-constraints.md` | publish/upload、更新线上资源或确认生成边界时 |
489
- | Tableau 迁移清单 | `references/tableau-migration.md` | Tableau 工作簿迁移到观远 BI 时 |
257
+ | 字段、计算字段、NumberFormat、高级计算、卡片筛选器 API | `references/api-reference.md` | 写字段/指标/公式/筛选条件时 |
258
+ | 各类 Builder API 与枚举(含 ComplexReportProBuilder 权威定义) | `references/builder-reference.md` | 写或改 JS DSL builder 调用时 |
259
+ | 图表属性配置 | `references/chart-properties.md` | 创建或修改图表属性时 |
260
+ | checkout 编辑闭环:生成物、attachCard 操作语义、覆盖发布与备份 | `references/checkout-editing.md` | checkout 工程动手前、改线上仪表板时 |
261
+ | 复杂报表 Pro 选型/骨架/配方/报错/编辑/验证 | `references/complex-report-pro-patterns.md` | 创建或修改 Pro 前必读 |
262
+ | zone 校验、图表选型、地图/拆分图、字段对象细节 | `references/validation-and-chart-patterns.md` | pack 报错、选型不确定时 |
263
+ | 仪表板主题机制与 theme 排错 | `references/theme.md` | 指定视觉风格、主题异常时 |
264
+ | 在线同步、认证、ID 管理和硬约束 | `references/publish-and-constraints.md` | publish/upload、确认生成边界时 |
265
+ | Tableau 迁移清单 | `references/tableau-migration.md` | Tableau 工作簿迁移时 |
490
266
  | 常见错误与修复 | `references/troubleshooting.md` | 命令失败或校验报错时 |
491
267
 
492
268
  ## 最后怎么向用户汇报