@guandata/guanvis 0.1.16

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.
Files changed (55) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +85 -0
  3. package/bin/run.js +87 -0
  4. package/binaries/guanvis-darwin-arm64 +0 -0
  5. package/binaries/guanvis-darwin-x64 +0 -0
  6. package/binaries/guanvis-linux-arm64 +0 -0
  7. package/binaries/guanvis-linux-x64 +0 -0
  8. package/binaries/guanvis-win32-x64.exe +0 -0
  9. package/package.json +41 -0
  10. package/skills/guanvis/SKILL.md +334 -0
  11. package/skills/guanvis/evals/china_map/card_01_basic_map.js +13 -0
  12. package/skills/guanvis/evals/china_map/card_02_world_map.js +10 -0
  13. package/skills/guanvis/evals/china_map/card_03_point_map.js +12 -0
  14. package/skills/guanvis/evals/china_map/page.js +10 -0
  15. package/skills/guanvis/evals/china_map/schema.js +10 -0
  16. package/skills/guanvis/evals/comparative_mvp_roundtrip/README.md +30 -0
  17. package/skills/guanvis/evals/comparative_mvp_roundtrip/card_01_comparative_mvp.js +72 -0
  18. package/skills/guanvis/evals/comparative_mvp_roundtrip/page.js +6 -0
  19. package/skills/guanvis/evals/comparative_mvp_roundtrip/schema.js +22 -0
  20. package/skills/guanvis/evals/comparative_mvp_roundtrip/verify_default_roundtrip.sh +92 -0
  21. package/skills/guanvis/evals/custom_chart_echarts/card_01_bar.js +13 -0
  22. package/skills/guanvis/evals/custom_chart_echarts/card_02_pie.js +13 -0
  23. package/skills/guanvis/evals/custom_chart_echarts/charts/bar.js +11 -0
  24. package/skills/guanvis/evals/custom_chart_echarts/charts/pie.js +15 -0
  25. package/skills/guanvis/evals/custom_chart_echarts/page.js +5 -0
  26. package/skills/guanvis/evals/custom_chart_echarts/schema.js +9 -0
  27. package/skills/guanvis/evals/percentage_advcalc_roundtrip/README.md +26 -0
  28. package/skills/guanvis/evals/percentage_advcalc_roundtrip/card_01_percentage_advcalc.js +44 -0
  29. package/skills/guanvis/evals/percentage_advcalc_roundtrip/card_02_scroll_percentage.js +12 -0
  30. package/skills/guanvis/evals/percentage_advcalc_roundtrip/page.js +6 -0
  31. package/skills/guanvis/evals/percentage_advcalc_roundtrip/schema.js +22 -0
  32. package/skills/guanvis/evals/rank_advcalc_roundtrip/README.md +28 -0
  33. package/skills/guanvis/evals/rank_advcalc_roundtrip/card_01_rank_advcalc.js +59 -0
  34. package/skills/guanvis/evals/rank_advcalc_roundtrip/page.js +6 -0
  35. package/skills/guanvis/evals/rank_advcalc_roundtrip/schema.js +22 -0
  36. package/skills/guanvis/evals/rank_advcalc_roundtrip/verify_default_roundtrip.sh +135 -0
  37. package/skills/guanvis/evals/running_total_advcalc_roundtrip/README.md +15 -0
  38. package/skills/guanvis/evals/running_total_advcalc_roundtrip/card_01_running_total_advcalc.js +74 -0
  39. package/skills/guanvis/evals/running_total_advcalc_roundtrip/page.js +6 -0
  40. package/skills/guanvis/evals/running_total_advcalc_roundtrip/schema.js +22 -0
  41. package/skills/guanvis/evals/sales_dashboard/card_01_revenue_column.js +10 -0
  42. package/skills/guanvis/evals/sales_dashboard/card_02_trend_line.js +9 -0
  43. package/skills/guanvis/evals/sales_dashboard/card_03_kpi.js +6 -0
  44. package/skills/guanvis/evals/sales_dashboard/card_04_pie.js +9 -0
  45. package/skills/guanvis/evals/sales_dashboard/card_05_calc_field_test.js +12 -0
  46. package/skills/guanvis/evals/sales_dashboard/page.js +11 -0
  47. package/skills/guanvis/evals/sales_dashboard/schema.js +14 -0
  48. package/skills/guanvis/evals/sales_dashboard/selector_01_region.js +10 -0
  49. package/skills/guanvis/references/api-reference.md +408 -0
  50. package/skills/guanvis/references/builder-reference.md +690 -0
  51. package/skills/guanvis/references/publish-and-constraints.md +46 -0
  52. package/skills/guanvis/references/tableau-migration.md +73 -0
  53. package/skills/guanvis/references/theme.md +115 -0
  54. package/skills/guanvis/references/troubleshooting.md +20 -0
  55. package/skills/guanvis/references/validation-and-chart-patterns.md +205 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,44 @@
1
+ # Changelog
2
+
3
+ ## @guandata/guanvis 0.1.16 - 2026-05-20
4
+
5
+ - 包名和命令入口统一移除 `-skill` 后缀,改为使用 `@guandata/guanvis` / `guanvis`。
6
+ - 新增 `guanvis` 可视化构建、打包、预览、发布、截图、主题等完整 CLI 与使用文档。
7
+ - 比较卡默认改为基于筛选条件计算占比,改善默认生成结果的业务可用性。
8
+ - `init` 生成的 `schema.js` 现在会包含数据集虚拟字段,便于后续卡片配置直接使用。
9
+ - 改进 npm 包运行与发布兼容性,包括 Windows 控制台 UTF-8 启动和 skill YAML 字段引用兼容。
10
+ ## @guandata/guanvis 0.1.15 - 2026-05-18
11
+
12
+ - 新增服务端页面截图命令,支持更稳定的页面视觉验证流程。
13
+ - 增加主题规则与设计规则能力,优化主题选择、主题回退和 fine mode 页面布局。
14
+ - 拆分并随 npm 包同步参考文档,改善安装后的使用说明完整性。
15
+ - 修复明细表中误用聚合计算字段的问题,降低生成无效图表的风险。
16
+
17
+ ## @guandata/guanvis 0.1.14 - 2026-05-14
18
+
19
+ - 支持仪表板精细网格布局模式,提升页面排版控制能力。
20
+ - 新增仪表板标题显示开关,可按需隐藏或展示页面标题。
21
+ - KPI 卡片支持对比类高级计算配置。
22
+ - 补充 Linux 平台 npm 包适配。
23
+
24
+ ## @guandata/guanvis 0.1.13 - 2026-05-12
25
+
26
+ - 新增百分比高级计算和累计值高级计算支持,扩展图表 DSL 的分析表达能力。
27
+ - 应用主题时会隐藏仪表板标题,使主题化页面展示更贴近最终发布效果。
28
+ - 更新文档,建议优先使用原生聚合字段,减少不必要的自定义计算。
29
+ - 补充高级计算 roundtrip 示例与验证材料,提升打包/发布链路稳定性。
30
+
31
+ ## @guandata/guanvis 0.1.12 - 2026-05-08
32
+
33
+ - 新增排名高级计算能力,支持更丰富的图表指标计算场景。
34
+ - 修复筛选器默认值类型保护逻辑,降低生成筛选器时因默认值格式不匹配导致的错误。
35
+
36
+ ## @guandata/guanvis 0.1.11 - 2026-05-07
37
+
38
+ - 支持从 BI API 解析仪表板主题,并在本地 `themes/` 缓存主题快照。
39
+ - 增加 Harness 化工作法文档,帮助用更可验证的方式构建和迭代看板。
40
+
41
+ ## @guandata/guanvis 0.1.10 - 2026-04-30
42
+
43
+ - 更新已发布仪表板的改版策略文档,默认保留历史版本,避免覆盖线上手工调整。
44
+ - 发布版本同步,将仪表板版本保留策略纳入 npm 包文档。
package/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # guanvis
2
+
3
+ 观远 BI Card/Page 生成工具,通过 JS DSL 创建图表、筛选器和仪表板。
4
+
5
+ ## 本地安装(开发/测试阶段)
6
+
7
+ ```bash
8
+ # 编译所有平台 binary(需要 Go 工具链)
9
+ npm run build
10
+
11
+ # 安装到本地 node global
12
+ npm link
13
+ ```
14
+
15
+ 安装后即可在终端使用:
16
+
17
+ ```bash
18
+ # 生成资源 ID
19
+ guanvis genid 5
20
+
21
+ # 初始化:从 BI 获取数据集结构
22
+ guanvis init <dsId> -d ./my_dashboard/
23
+
24
+ # 预览生成结果(JSON 输出)
25
+ guanvis preview ./my_dashboard/
26
+
27
+ # 打包为 ZIP 资源包
28
+ guanvis pack ./my_dashboard/
29
+
30
+ # 一步到位:构建并上传到 BI
31
+ guanvis publish ./my_dashboard/
32
+ ```
33
+
34
+ 说明:npm 包名为 `@guandata/guanvis`,用户侧 CLI 命令统一为 `guanvis`。
35
+
36
+ Card/Page 的名称、描述和布局都应维护在 JS DSL 源文件中。新建或改版时,通过 preview/pack/publish 从 JS 源文件生成并发布资源;若只是修复已发布 Card/Page 的描述,应保留原资源 ID,使用 `set-description` 直接更新线上描述,并同步更新 JS 中的 `.setDescription(...)`:
37
+
38
+ ```bash
39
+ guanvis card set-description <cdId> --description "卡片业务故事..."
40
+ guanvis page set-description <pgId> --file ./page_story.md --visible=false
41
+ ```
42
+
43
+ ```javascript
44
+ var card = createCard(ChartType.KPI_CARD, "总销售额")
45
+ .setId("abcdefghijklmnopqrstuvwx")
46
+ .bindDataset(DS)
47
+ .setDescription("业务故事:用于跟踪当前总销售额,口径为销售额 SUM。")
48
+ .addMetric(f("销售额", { aggrType: AggrType.SUM }));
49
+
50
+ registerCard(card.build());
51
+
52
+ var page = createPage("销售看板")
53
+ .setId("abcdefghijklmnopqrstuvw1")
54
+ .setDescription("页面故事:面向经营管理层的销售总览。")
55
+ .addFullWidthCard(0, 4);
56
+
57
+ registerPage(page.build());
58
+ ```
59
+
60
+ 也可以为 AI Coding Assistant 安装 Skill:
61
+
62
+ ```bash
63
+ guanvis install-skill
64
+ ```
65
+
66
+ ## 卸载
67
+
68
+ ```bash
69
+ npm unlink -g @guandata/guanvis
70
+ ```
71
+
72
+ ## 支持平台
73
+
74
+ - macOS (Apple Silicon / Intel)
75
+ - Windows (x64)
76
+
77
+ ## 开发
78
+
79
+ ```bash
80
+ # 编译所有平台 binary(需要 Go 工具链)
81
+ npm run build
82
+
83
+ # 发布到内部 Nexus npm 仓库
84
+ npm publish
85
+ ```
package/bin/run.js ADDED
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+
3
+ "use strict";
4
+
5
+ const { execFileSync, execSync, spawnSync } = require("child_process");
6
+ const path = require("path");
7
+ const fs = require("fs");
8
+
9
+ const PLATFORM_MAP = {
10
+ "darwin-arm64": "guanvis-darwin-arm64",
11
+ "darwin-x64": "guanvis-darwin-x64",
12
+ "linux-x64": "guanvis-linux-x64",
13
+ "linux-arm64": "guanvis-linux-arm64",
14
+ "win32-x64": "guanvis-win32-x64.exe",
15
+ };
16
+
17
+ function getBinaryPath() {
18
+ const key = `${process.platform}-${process.arch}`;
19
+ const binaryName = PLATFORM_MAP[key];
20
+ if (!binaryName) {
21
+ console.error(
22
+ `Unsupported platform: ${key}\nSupported: ${Object.keys(PLATFORM_MAP).join(", ")}`
23
+ );
24
+ process.exit(1);
25
+ }
26
+
27
+ const binaryPath = path.join(__dirname, "..", "binaries", binaryName);
28
+ if (!fs.existsSync(binaryPath)) {
29
+ console.error(
30
+ `Binary not found: ${binaryPath}\nRun 'npm run build' to compile binaries.`
31
+ );
32
+ process.exit(1);
33
+ }
34
+
35
+ return binaryPath;
36
+ }
37
+
38
+ if (process.argv[2] === "version" && process.argv.length === 3) {
39
+ const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8"));
40
+ console.log(pkg.version);
41
+ process.exit(0);
42
+ }
43
+
44
+ if (process.argv[2] === "install-skill") {
45
+ const pkgRoot = path.join(__dirname, "..");
46
+ const extraArgs = process.argv.slice(3);
47
+ const args = [
48
+ "skills",
49
+ "add",
50
+ pkgRoot,
51
+ "--skill",
52
+ "guanvis",
53
+ "-g",
54
+ "-y",
55
+ ...extraArgs,
56
+ ];
57
+ console.log("Installing guanvis to AI coding assistants...");
58
+ const result = spawnSync("npx", args, { stdio: "inherit", env: process.env, shell: true });
59
+ if (result.error) throw result.error;
60
+ process.exit(result.status || 0);
61
+ }
62
+
63
+ const binary = getBinaryPath();
64
+
65
+ try {
66
+ if (process.platform !== "win32") {
67
+ fs.chmodSync(binary, 0o755);
68
+ }
69
+ } catch (_) {
70
+ // chmod may fail in read-only environments
71
+ }
72
+
73
+ if (process.platform === "win32") {
74
+ try { execSync("chcp 65001", { stdio: "ignore" }); } catch (_) {}
75
+ }
76
+
77
+ try {
78
+ execFileSync(binary, process.argv.slice(2), {
79
+ stdio: "inherit",
80
+ env: { ...process.env, GUANVIS_PROG_NAME: "guanvis" },
81
+ });
82
+ } catch (err) {
83
+ if (err.status != null) {
84
+ process.exit(err.status);
85
+ }
86
+ throw err;
87
+ }
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@guandata/guanvis",
3
+ "version": "0.1.16",
4
+ "description": "观远 BI Card/Page 生成工具 - 通过 JS DSL 创建图表和仪表板",
5
+ "bin": {
6
+ "guanvis": "bin/run.js"
7
+ },
8
+ "scripts": {
9
+ "build": "node scripts/build.js && node scripts/sync-skill.js",
10
+ "changelog": "node ../../scripts/generate-release-changelog.js .",
11
+ "check-changelog": "node ../../scripts/check-release-changelog.js .",
12
+ "prepublishOnly": "npm run check-changelog && npm run build && node scripts/preflight.js"
13
+ },
14
+ "files": [
15
+ "bin/",
16
+ "binaries/",
17
+ "skills/",
18
+ "CHANGELOG.md",
19
+ "README.md"
20
+ ],
21
+ "keywords": [
22
+ "guandata",
23
+ "bi",
24
+ "card",
25
+ "dashboard",
26
+ "cli",
27
+ "agent-skill"
28
+ ],
29
+ "license": "UNLICENSED",
30
+ "os": [
31
+ "darwin",
32
+ "linux",
33
+ "win32"
34
+ ],
35
+ "engines": {
36
+ "node": ">=14"
37
+ },
38
+ "publishConfig": {
39
+ "registry": "https://app.mayidata.com/nexus/repository/guandata-web/"
40
+ }
41
+ }
@@ -0,0 +1,334 @@
1
+ ---
2
+ name: guanvis
3
+ description: 当用户要新建、修改、组装观远 BI / Guandata 的 Card(图表/报表卡片)、文本卡片、图片卡片、筛选器(selector,含日历/时间宏/区间/离散值)或仪表板(Page),或给出 Card ID、数据集 ID、card.js/page.js、图表类型关键词(柱状/折线/饼/KPI/表格/漏斗/地图/散点等 30+ 种)时,优先使用这个 skill。即使用户只说"做个销售仪表板""加个 KPI 卡片""新建一个区域筛选器联动所有图""帮我改一下这个图的图例""把这几个 card 拼成一个 page""加一个本月/近 7 天的快捷日期筛选",也要主动使用。它通过 AI 编写简洁的 JS 脚本(card_*.js / selector_*.js / page.js)定义卡片、筛选器和页面布局,再 pack/publish 上传到目标 BI。认证复用 guancli 共享配置。只想查现有 Card/Page 内容走 guancli。
4
+ compatibility: "Requires Node.js 14+. Install via npm link (local) or npm install -g @guandata/guanvis (from internal Nexus registry). CLI command: guanvis."
5
+ ---
6
+
7
+ # guanvis
8
+
9
+ 这是一个执行型 skill,用于通过 AI 生成的 JS 脚本创建 BI Card 和 Page 资源。
10
+
11
+ - AI 生成 `card_*.js` 定义各个 Card 图表;`page.js` 将多个 Card 组装成仪表板。
12
+ - `schema.js` 由 `init` 命令自动生成,定义数据集字段信息(不允许 AI 修改)。
13
+ - 框架内置验证规则,在 pack/publish 时检查字段数量、zone 兼容性、必填字段等。
14
+ - 认证通过 `guancli` 共享配置自动获取。
15
+
16
+ ## Harness 化工作法
17
+
18
+ 把可视化生成当作源文件驱动的构建流程:本地 JS DSL 是可编辑事实源,payload、ZIP、线上 Card/Page 都是从源文件生成的派生产物。
19
+
20
+ - `schema.js` 是由 `init` 生成的数据集事实快照,不手改;数据集字段变化时重新运行 `init`。
21
+ - `card_*.js`、`selector_*.js`、`page.js` 是可编辑源文件;`*_package.zip`、preview JSON、publish 后线上资源都是派生产物。
22
+ - Card/Page 描述也是 JS 源文件的一部分:新建看板或改版时,在 `card_*.js` / `page.js` 中写 `.setDescription(...)`,再通过 `preview`/`pack`/`publish` 从 JS 源文件生成并发布资源。
23
+ - 写脚本前先形成 dashboard contract:目标用户、业务问题、使用的数据集、核心指标、维度拆解、筛选器、页面结构和验证方式。
24
+ - 每次生成都针对明确目录或明确子目录;不要把 unrelated 示例、旧包或临时 ZIP 混入同一个发布目标。
25
+ - 先 `preview/pack` 做本地结构验证,再 `publish/upload`;修改已发布资源前先判断变更类型:仅修复 Card/Page 描述时应保留原资源 ID,使用 `card/page set-description` 并同步维护 JS 中的 `.setDescription(...)`;调整图表、布局、筛选器、字段等看板内容时,按线上仪表板更新策略处理。
26
+ - 发布后优先用 `guancli page get/card get` 回读结构与配置。只有在明确需要视觉质量判断、且当前大模型支持图像理解时,才使用 `guanvis screenshot <pageId>` 生成 PNG 并交给模型分析;不要把截图作为默认闭环步骤,因为图像理解会额外消耗 token/费用。
27
+
28
+ ## AI Quick Reference(速查,详细说明见按需参考资料)
29
+
30
+ 1. **工厂函数**:只用 `createCard()` / `createSelector()` / `createTextCard()` / `createPage()`,不要 `new XxxBuilder()`
31
+ 2. **注册函数**:`registerCard(card.build())` / `registerSelector(sel.build())` / `registerTextCard(text.build())` / `registerPage(page.build())`
32
+ 3. **字段引用**:单数据集用 `f("字段名")`,多数据集用 `field(DS, "字段名")`
33
+ 4. **zone maxCount**:`BASIC_COLUMN/BAR/LINE` metric=1;`GROUPED_*/STACKED_*` metric=∞;`KPI_CARD` metric=1;组合图 `*_WITH_LINE` row=1
34
+ 5. **column vs colorBy**:column 放维度(按类别分组着色),colorBy 放度量(按值渐变着色)
35
+ 6. **原生聚合优先**:`SUM`/`AVG`/`COUNT`/`COUNT_DISTINCT`/`MIN`/`MAX` 等单字段聚合用 `f("字段", { aggrType: AggrType.XXX })`,不要用 `calcField()` 手写 SQL 聚合函数;去重计数用 `AggrType.COUNT_DISTINCT`,不要写 `COUNT_DISTINCT([字段])`
36
+ 7. **calcField 命名**:不能与数据集物理字段同名,否则 BI 默认取数据集字段
37
+ 8. **calcField 类型**:`aggregation`(默认)公式必须含聚合函数;纯算术用 `{ calculationType: "normal" }`;窗口函数用 `{ calculationType: "window" }`
38
+ 9. **明细/滚动表 calcField**:`DETAIL_TABLE` / `SCROLL_TABLE` 只做逐行展示;如需行级计算,必须写 `{ calculationType: "normal" }`,且不要写任何 SQL 聚合函数或窗口函数。汇总需求改用非明细图表 `aggrType` / aggregation calcField,或 ETL 预计算
39
+ 10. **selector 联动**:必须调用 `.linkToAll()` 或 `.linkTo(cardIndex)`
40
+ 11. **selector 类型选择**:离散值(区域/类别)→ `DS_ELEMENTS`(默认);连续数值(利润率/金额)→ `.setSelectorType(SelectorType.DS_INTERVAL)`;日期 → `CALENDAR`;快捷日期区间(本月/近7天等)→ `.setTimeMacroOptions(options)`
41
+ 12. **同环比默认**:用户说同比/环比/同环比/年同比/月环比且未指定输出值时,默认用增长率;未指定模式时默认按日期筛选模式(`ComparativeMode.FILTER_BASED`),普通模式需显式指定 `ComparativeMode.NORMAL`
42
+ 13. **placeCard 索引**:按 registerCard → registerTextCard 调用顺序累加,文件按文件名排序加载。**registerSelector 不参与 card index 计数**
43
+ 14. **publish 环境**:认证由底层 CLI 负责——guancli 需先 `guancli auth use <profile>`,guancli-lite 需设置环境变量
44
+ 15. **更新线上仪表板**:已发布过或线上正在使用的仪表板,若调整图表、布局、筛选器、字段等看板内容,默认创建新版本仪表板,不覆盖原仪表板;新版本使用新的 Page/Card/Selector ID,名称追加版本号(如 `销售仪表板 v2` / `销售仪表板 20260430`),保留多个版本。只有用户明确要求覆盖时,才复用原 ID
45
+ 16. **描述维护**:仅修复已发布 Card/Page 的描述时,保留原资源 ID,使用 `guanvis card/page set-description` 更新线上描述;如果本地有对应 JS 工程,也同步更新 `.setDescription(...)`,让源文件与线上描述一致
46
+ 17. **主题切换**:用户描述风格(深色科技风/科技蓝/蓝色简约等)→ 在工程目录里 `guanvis theme preference --keywords "..." --sync`(`[dir]` 可省,默认当前目录);不要选择租户默认的“浅色”/“深色”主题,找不到合适主题时保持/清空偏好,让 preview/pack/publish 自动使用内置“简约”兜底。改版未提风格时 `.applied.json` 会自动继承上次主题,preview/pack/publish 不需要重复指定。**多子目录工程**:每个子目录是独立工程,主题要在子目录里配置 + preview/pack/publish 也必须 `cd` 进对应子目录运行(在根目录直接跑会被命令显式拒绝并给出 cd 提示);详见 `references/theme.md`
47
+ 18. **设计规则**:`preview`/`pack`/`publish` 会自动应用内置设计规则;需要自定义静态卡片默认规则或主题已开放配置时,在工程目录新建 `design-rule.json`,不要手改 `themes/<themeId>.json`;详见 `references/theme.md`
48
+ 19. **图表选型**:创建指标卡时,若只有单指标优先用 `SINGLE_VALUE`;需展示对比指标时用 `KPI_CARD`,并调用 `.addCompare(...)`
49
+
50
+ ## 何时使用
51
+
52
+ 遇到这些任务就用:
53
+
54
+ - 新建观远 BI Card(柱形图、折线图、饼图、表格、KPI、散点图、漏斗、地图等 30+ 种)。
55
+ - 创建文本卡片(含内嵌指标引用)或图片卡片(外链/本地图片)。
56
+ - 创建筛选器并配置联动关系(选择筛选器、日历筛选器等)。
57
+ - 组装多个 Card 和筛选器为一个仪表板/Page,含 grid layout 布局和筛选器面板。
58
+ - 批量生成一组 Card 并上传到 BI 系统。
59
+ - 用户提到 `card.js`、`page.js`、CardPayload、图表类型、chart axes、zone spec、筛选器联动。
60
+
61
+ ## 固定工作方式
62
+
63
+ ### 1. 确认前置条件
64
+
65
+ ```bash
66
+ guancli auth status
67
+ ```
68
+
69
+ - 如果用户指定的资源(数据集、仪表板等)搜索不到,先用 `guancli auth list` 列出所有可用环境,提示用户确认是否需要切换 profile,而不要在当前 profile 上反复重试。
70
+
71
+ ### 2. 生成 schema.js(只做一次或数据集变更时重新生成)
72
+
73
+ ```bash
74
+ # 查找数据集
75
+ guancli ds search <关键词>
76
+
77
+ # 生成 schema.js(传入数据集 ID)
78
+ guanvis init <dsId1> <dsId2> -d ./my_dashboard/
79
+ ```
80
+
81
+ 如果用户给的是业务目录名而不是数据集名,先用 `guancli ds tree` 定位目录,再在目录内按真实数据集名搜索,不要把目录名直接当作数据集名反复 `ds search`。
82
+
83
+ 生成的 `schema.js` 示例:
84
+ ```javascript
85
+ // Auto-generated — DO NOT EDIT
86
+ defineDataset("s9ded2338f43807b095fbb4f", [
87
+ { fdId: "jb16f22f6c1c21c3f6e8e293", name: "区域", fdType: "STRING", metaType: "DIM" },
88
+ { fdId: "lb1c31b4dc86511a27049a98", name: "营收", fdType: "DOUBLE", metaType: "METRIC" },
89
+ // ...
90
+ ], { displayType: "EXCEL" });
91
+ ```
92
+
93
+ ### 3. 设置仪表板主题(按需)
94
+
95
+ 如果用户**明确提到了视觉风格**("深色科技风"、"蓝色简约"、"科技蓝T004" 或具体 themeId 等),在编写 card/page 之前把意图落到 `.preference.json`。不要把租户默认的“浅色”/“深色”当作可选主题;如果候选里没有合适风格,跳过这一步或执行 `theme preference --clear`,让命令自动使用内置“简约”:
96
+
97
+ ```bash
98
+ cd ./my_dashboard
99
+
100
+ # (a) 用户描述了风格关键词 —— 拉一次主题列表,AI 也能离线核对候选
101
+ guanvis theme preference --keywords "深色 科技" --sync
102
+
103
+ # (b) 用户给出明确 themeId
104
+ guanvis theme preference --theme-id custom_blue --sync
105
+
106
+ # (c) 想先看看候选名字
107
+ guanvis theme sync # 拉列表
108
+ guanvis theme list # 看候选 themeId / themeName / themeType
109
+ ```
110
+
111
+ > **多子目录工程**:每个子目录都是独立工程,主题各自隔离。如果不同子目录需要不同主题,**`cd` 进每个子目录分别跑一遍 `theme preference`**(每个子目录会有自己的 `themes/.preference.json` + 主题快照);只要任一子目录配了自己的 `themes/`,preview/pack/publish 就必须在对应子目录里运行(详见 `references/theme.md`)。如果整个工程主题统一,把 `themes/` 配在根目录、所有子目录共享即可。
112
+
113
+ 命令产出的目录形态(与 `schema.js` 同级):
114
+
115
+ ```
116
+ my_dashboard/
117
+ ├── schema.js
118
+ ├── themes/
119
+ │ ├── .preference.json # theme preference 写入的偏好(themeId / keywords)
120
+ │ ├── .index.json # theme sync 拉到的候选主题索引
121
+ │ ├── .sync-meta.json # syncedAt / count
122
+ │ ├── default_7.json # theme sync 落盘的主题快照(每个 themeId 一份)
123
+ │ └── custom_blue.json
124
+ └── ... (card_*.js / page.js 后面再加)
125
+ ```
126
+
127
+ `.applied.json` 会在第一次 `pack`/`publish` 实际使用了线上主题时自动写入;这之前不存在很正常。回退到 skill 自带 `simple.json` 时**故意不写** `.applied.json`,避免污染下次解析。
128
+
129
+ 何时跳过这一步:
130
+
131
+ - **改版已有看板**(工程下已存在 `themes/.applied.json`)→ 跳过,`pack`/`publish` 会自动继承上次主题。
132
+ - **新看板但用户没描述风格** → 跳过,自动落到 skill 自带的 `simple.json`(与原行为一致;离线/弱网都不会失败)。
133
+ - **候选主题只剩默认“浅色”/“深色”或都不贴合需求** → 不要为了命中而随便选,清空/不写偏好并使用内置“简约”兜底。
134
+ - 后续用户改主意要换风格 → 再跑一次 `theme preference` 就行,不需要重做 schema 或 card。
135
+
136
+ 完整决策规则、`themes/` 目录结构、离线 vs 联网约定见 `references/theme.md`。
137
+
138
+ ### 4. 编写 card 脚本
139
+
140
+ card 脚本使用 `field()` 或简写 `f()` 从 schema 引用字段(无需记忆 fdId):
141
+
142
+ ```javascript
143
+ // card_01_revenue.js — 使用 field(DS, ...) 完整写法
144
+ var card = createCard(ChartType.GROUPED_COLUMN, "营收分析")
145
+ .bindDataset(DS)
146
+ .setDescription("业务故事:用于回答各区域营收规模与产品结构差异,目标用户是经营管理层。指标口径:营收按 SUM 聚合。")
147
+ .addRow(field(DS, "区域"))
148
+ .addMetric(field(DS, "营收", {
149
+ aggrType: AggrType.SUM,
150
+ numberFormat: NumberFormat.currency("¥", 0)
151
+ }))
152
+ .addColumn(field(DS, "产品"))
153
+ .setShowLegend(true, "right")
154
+ .setDataLabel({ show: true, showNumber: true });
155
+
156
+ registerCard(card.build());
157
+ ```
158
+
159
+ ```javascript
160
+ // card_02_pivot.js — 使用 f() 简写(单数据集场景)
161
+ var card = createCard(ChartType.PIVOT_TABLE, "区域产品交叉分析")
162
+ .bindDataset(DS)
163
+ .addRow(f("区域"))
164
+ .addColumn(f("产品"))
165
+ .addMetric(f("营收", { aggrType: AggrType.SUM }))
166
+ .addSort(f("营收", { aggrType: AggrType.SUM, sortType: SortOrder.DESC }))
167
+ .setTableSetting({ showRowTotal: true, showColumnTotal: true });
168
+
169
+ registerCard(card.build());
170
+ ```
171
+
172
+ ### 5. 编写 page.js(可选)
173
+
174
+ ```javascript
175
+ var page = createPage("销售仪表板")
176
+ .setId("wb52d11444d6f1b4063d8cf8")
177
+ .setDescription("综合销售分析")
178
+ .setBackgroundColor("#f5f5f5")
179
+ .setCardMargin(8)
180
+ .addRow([{ card: 0, w: 4 }, { card: 1, w: 8 }], 4)
181
+ .addFullWidthCard(2, 6)
182
+ .addHalfWidthCards(3, 4, 6);
183
+
184
+ registerPage(page.build());
185
+ ```
186
+
187
+ **仪表板主题**:项目目录下没配 `themes/` 时,`preview`/`pack`/`publish` 自动使用 skill 自带的简约主题(embed 在二进制里的 `simple.json`,不依赖租户线上主题列表)。如需切换租户自定义主题或带明确风格的默认主题,请参考 `references/theme.md`,通过 `guanvis theme preference` 写入 `themes/.preference.json`;租户默认“浅色”/“深色”没有特殊样式,不作为可选主题。**JS DSL 不再提供主题相关接口**——任何 `setDashboardTheme(...)` 或 `page.setTheme(...)` 调用都会因函数未定义而报错。
188
+
189
+ **多页面支持**:可以在同一个项目中注册多个 page(多次调用 `registerPage`)。两种组织方式:
190
+
191
+ 1. **单目录**:所有 card 和 page 在同一目录,page 通过 card index(注册顺序)引用
192
+ 2. **子目录**:每个子目录有独立的 `schema.js`、`card_*.js`、`page.js`。page 通过 card ID 字符串引用(推荐)
193
+
194
+ 子目录结构示例:
195
+ ```
196
+ project/
197
+ ├── overview/
198
+ │ ├── schema.js
199
+ │ ├── themes/ ← 主题偏好与缓存(按需,仅当跑过 theme preference / sync)
200
+ │ │ ├── .preference.json
201
+ │ │ ├── .applied.json
202
+ │ │ ├── .index.json
203
+ │ │ ├── .sync-meta.json
204
+ │ │ └── <themeId>.json
205
+ │ ├── card_01_kpi.js
206
+ │ ├── card_02_chart.js
207
+ │ ├── page.js ← addFullWidthCard("card_id_string", 6)
208
+ │ └── charts/ ← 自定义图表内容
209
+ ├── detail/
210
+ │ ├── schema.js
211
+ │ ├── themes/ ← 每个子目录是独立工程,主题各自维护
212
+ │ │ └── ...
213
+ │ ├── card_01_table.js
214
+ │ └── page.js
215
+ ```
216
+
217
+ > 子目录模式下每个子目录是独立的看板工程,`themes/` 与 `schema.js` 同级、各自隔离;preview/pack/publish/`theme *` 命令必须 `cd` 进对应子目录单独执行。**在父级根目录直接 `guanvis pack/preview/publish .` 时,若任一子目录里出现了 `themes/`,命令会直接报错并提示需要进入子目录单独执行**——这避免了根目录运行时各子目录主题被静默忽略、全部退回内置 `simple.json` 的隐蔽问题。如果整个工程不需要主题(所有子目录都没有 `themes/`),在根目录一次性 `pack` 也照常工作,行为不变。
218
+
219
+ 子目录模式下,page 布局必须使用 card ID 字符串而非 index:
220
+ ```javascript
221
+ var p = createPage("Detail Page")
222
+ .setId("pgId24chars")
223
+ .addFullWidthCard("cardId24chars", 8); // 用 card ID 而非 index
224
+ registerPage(p.build());
225
+ ```
226
+
227
+ ### 6. 执行命令
228
+
229
+ ```bash
230
+ # 生成资源 ID(用于 .setId() 调用)
231
+ guanvis genid # 生成 1 个
232
+ guanvis genid 5 # 生成 5 个
233
+
234
+ # 预览生成结果(JSON 输出到 stdout,含 payload 验证,用于调试)
235
+ guanvis preview ./my_dashboard/
236
+
237
+ # 打包为 ZIP 资源包
238
+ guanvis pack ./my_dashboard/
239
+ guanvis pack -o output.zip ./my_dashboard/
240
+
241
+ # 一步到位:构建并上传到 BI(在线同步)
242
+ guanvis publish ./my_dashboard/
243
+ guanvis publish ./my_dashboard/ --page-parent-dir <dir_id> # 指定页面目录
244
+
245
+ # 上传已有 ZIP 资源包
246
+ guanvis upload output.zip
247
+
248
+ # 修复已发布资源描述(保留原 Card/Page ID)
249
+ # 如果本地维护对应 JS 工程,也同步更新 card_*.js / page.js 里的 .setDescription(...)。
250
+ guanvis card set-description <cd_id> --description "这张卡用于回答..."
251
+ guanvis card set-description <cd_id> --file ./card_story.md
252
+ guanvis page set-description <pg_id> --description "这张页面用于回答..."
253
+ guanvis page set-description <pg_id> --file ./page_story.md --visible=false
254
+
255
+ # 页面视觉验证(可选,PNG,通过 BI 后端服务端截图,不依赖浏览器)
256
+ # 仅在明确需要视觉质量判断且当前大模型支持图像理解时使用;图像分析会额外消耗 token/费用。
257
+ guanvis screenshot <pageId> # 截图页面 PNG 到 <pageId>.png
258
+ guanvis screenshot <pageId> -o /tmp/dashboard.png # 指定输出路径
259
+ guanvis screenshot <pageId> --orientation horizontal # 横向截图
260
+
261
+ # 仪表板主题(详见 `references/theme.md`,[dir] 缺省为当前目录)
262
+ guanvis theme preference --keywords "深色 科技" --sync # 在当前目录写入偏好并同步主题列表
263
+ guanvis theme preference ./my_dashboard --theme-id custom_blue # 显式指定工程目录
264
+ guanvis theme preference --clear # 清空当前目录的偏好与 applied 快照(彻底回到内置 simple.json)
265
+ guanvis theme list # 列出已落盘的候选主题
266
+ guanvis theme show # 打印当前主题决策(不联网)
267
+ guanvis theme sync # 强制刷新主题列表
268
+ ```
269
+
270
+ **多子目录工程的执行方式**:每个子目录是独立工程,preview/pack/publish 必须在每个子目录里分别执行,每次产出独立的 ZIP 资源包;想要每个子目录用不同主题,就在每个子目录里分别 `theme preference`。在根目录直接运行 preview/pack/publish 时,若任一子目录里有 `themes/`,命令会拒绝执行并打印 `cd <subdir> && guanvis <preview|pack|publish> .` 提示。
271
+
272
+ ```bash
273
+ # 多子目录工程 + 各子目录主题不同 → 在每个子目录分别执行
274
+ for sub in overview detail kpi; do
275
+ cd "./project/$sub"
276
+ guanvis theme preference --keywords "..." --sync
277
+ guanvis publish .
278
+ cd -
279
+ done
280
+
281
+ # 多子目录工程 + 整个工程不需要主题(无任一子目录配 themes/)→ 根目录一次性 pack 仍然可用(与原行为一致)
282
+ guanvis pack ./project
283
+ ```
284
+
285
+ `publish` 和 `upload` 使用 BI 的 **transfer API**(`/api/manual/template/transfer`),特点:
286
+ - `needIdMapping=false`:保持资源 ID 不变,重复导入会覆盖同 ID 资源
287
+ - 认证方式:`X-Auth-Token` header
288
+ - 需要 `raw-backend-response: TRUE` header 绕过前端代理层
289
+ - 不需要目标系统开启"一键迁移"开关,所有环境通用
290
+
291
+ **线上仪表板更新策略**:为了保护已经发布过的仪表板和线上仪表板,默认不要复用原 Page/Card/Selector ID 做覆盖更新。需要调整线上看板时,应先生成一组新的 ID,复制并修改 DSL/JS,给 Page 名称追加版本号(例如 `v2`、`v20260430` 或业务约定版本),再 `publish` 到目标目录。旧版本保留用于回滚和对比。仅当用户明确要求“覆盖原仪表板/复用原 ID”时,才允许同 ID 发布。
292
+
293
+ ## 文件结构约定
294
+
295
+ 目录模式下文件加载顺序:
296
+ 1. `schema.js` — 数据集定义(自动生成,不可修改)
297
+ 2. `card_01_xxx.js` ~ `card_NN_xxx.js` — Card 定义(按文件名排序)
298
+ 3. `selector_01_xxx.js` ~ `selector_NN_xxx.js` — 筛选器定义(在 card 之后执行,因为联动需要引用 card 索引)
299
+ 4. `page.js` — Page/仪表板组装
300
+
301
+ 自定义图表时,图表内容文件(如 ECharts 脚本)放在 **子目录**(如 `charts/`),避免被当作 card 脚本执行。
302
+
303
+ `themes/` 子目录由 `theme *` 子命令维护,与 `schema.js` 同级;preview/pack/publish 会自动读取并应用。点前缀文件(`.preference.json` / `.applied.json` / `.index.json` / `.sync-meta.json`)是元数据/缓存,`<themeId>.json` 是主题快照——所有文件**不要手改**,详见 `references/theme.md`。
304
+
305
+ ## 参考示例
306
+
307
+ | 示例 | 路径 | 说明 |
308
+ |------|------|------|
309
+ | 基础仪表板 | `evals/sales_dashboard/` | 普通卡片(柱状图、折线图、KPI、饼图)+ 筛选器 + 页面布局 |
310
+ | 自定义图表 | `evals/custom_chart_echarts/` | ECharts Lite 自定义图表:柱状图 + 饼图,使用 `loadContent()` 文件模式 |
311
+
312
+ 生成脚本前先查阅对应示例中的文件结构和写法。
313
+
314
+ ## 按需参考资料
315
+
316
+ | 场景 | 路径 | 读取时机 |
317
+ |------|------|----------|
318
+ | 字段、计算字段、NumberFormat、高级计算和卡片筛选器 API | `references/api-reference.md` | 编写字段、指标、公式或筛选条件时 |
319
+ | Card/Page/Selector/Text/Image/CustomChart Builder API 与枚举 | `references/builder-reference.md` | 编写或修改 JS DSL builder 调用时 |
320
+ | zone 校验、图表选型、地图/拆分图、字段对象细节 | `references/validation-and-chart-patterns.md` | pack 报错、选型不确定或需要特殊图表行为时 |
321
+ | 仪表板主题机制与 theme 命令排错 | `references/theme.md` | 用户指定视觉风格、改版继承主题或主题异常时 |
322
+ | 在线同步、认证、ID 管理和硬约束 | `references/publish-and-constraints.md` | publish/upload、更新线上资源或确认生成边界时 |
323
+ | Tableau 迁移清单 | `references/tableau-migration.md` | 从 Tableau 工作簿迁移到观远 BI 时 |
324
+ | 常见错误与修复 | `references/troubleshooting.md` | 命令失败或校验报错时 |
325
+
326
+ ## 最后怎么向用户汇报
327
+
328
+ 至少说明:
329
+
330
+ - 创建了多少个 Card、什么图表类型。
331
+ - 是否有筛选器,联动了哪些卡片。
332
+ - 是否有 Page,布局方式如何。
333
+ - 是否有验证错误及修复建议。
334
+ - `pack`/`publish`/`upload` 哪些步骤已成功。
@@ -0,0 +1,13 @@
1
+ // BASIC_MAP: 中国省级行政地图(内置 Highmaps)
2
+ // row zone 放地理维度(省/自治区名),colorBy zone 放渐变着色指标
3
+ // 后端验证:rowSize == 1 && (colorBySize == 1 | metricSize == 1)
4
+
5
+ var card = createCard(ChartType.BASIC_MAP, "各省利润率")
6
+ .setId("b11111111111111111111110")
7
+ .bindDataset(DS)
8
+ .addRow(f("省/自治区"))
9
+ .addColorBy(calcField("利润率", "SUM([利润])/SUM([销售额])", {
10
+ numberFormat: NumberFormat.percentage(1)
11
+ }));
12
+
13
+ registerCard(card.build());
@@ -0,0 +1,10 @@
1
+ // WORLD_MAP: 世界国家级地图(内置 Highmaps)
2
+ // row zone 放国家名维度,colorBy zone 放渐变着色指标
3
+
4
+ var card = createCard(ChartType.WORLD_MAP, "全球销售分布")
5
+ .setId("b22222222222222222222220")
6
+ .bindDataset(DS)
7
+ .addRow(f("省/自治区"))
8
+ .addColorBy(f("销售额", { aggrType: AggrType.SUM }));
9
+
10
+ registerCard(card.build());
@@ -0,0 +1,12 @@
1
+ // POINT_MAP: 标记地图(内置 Highmaps)
2
+ // row zone 放地名维度(1个),metric zone 放数值指标(可多个)
3
+ // 后端验证:rowSize == 1 && metricSize >= 1
4
+
5
+ var card = createCard(ChartType.POINT_MAP, "各省订单与销售额")
6
+ .setId("b33333333333333333333330")
7
+ .bindDataset(DS)
8
+ .addRow(f("省/自治区"))
9
+ .addMetric(f("订单数", { aggrType: AggrType.SUM }))
10
+ .addMetric(f("销售额", { aggrType: AggrType.SUM }));
11
+
12
+ registerCard(card.build());
@@ -0,0 +1,10 @@
1
+ var page = createPage("中国地图示例")
2
+ .setId("c11111111111111111111110")
3
+ .setDescription("BASIC_MAP / WORLD_MAP / POINT_MAP 内置地图示例")
4
+ .setBackgroundColor("#f5f5f5")
5
+ .setCardMargin(8)
6
+ .addFullWidthCard(0, 8)
7
+ .addFullWidthCard(1, 8)
8
+ .addFullWidthCard(2, 8);
9
+
10
+ registerPage(page.build());