@wubaoqi/guancopilot 0.0.0-stage → 0.2.0

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 (239) hide show
  1. package/NOTICE.md +17 -0
  2. package/README.md +146 -2
  3. package/assets/coreutils.wasm +0 -0
  4. package/assets/find.wasm +0 -0
  5. package/assets/guanbi.wasm +4 -0
  6. package/assets/guancli.wasm +0 -0
  7. package/assets/guanhelm.wasm +0 -0
  8. package/assets/jq.wasm +0 -0
  9. package/assets/openskills.wasm +0 -0
  10. package/assets/rg.wasm +0 -0
  11. package/assets/sed.wasm +0 -0
  12. package/bin/guancopilot +0 -0
  13. package/bin/guanvm +0 -0
  14. package/bin/platforms.cjs +6 -0
  15. package/bin/run.cjs +39 -0
  16. package/bin/runtime.cjs +44 -0
  17. package/licenses/Go-LICENSE +27 -0
  18. package/licenses/browser-_speed-highlight_core-LICENSE +121 -0
  19. package/licenses/browser-_speed-highlight_core-package.json +14 -0
  20. package/licenses/browser-dompurify-LICENSE +202 -0
  21. package/licenses/browser-dompurify-LICENSE-MPL +373 -0
  22. package/licenses/browser-dompurify-package.json +10 -0
  23. package/licenses/browser-marked-LICENSE +44 -0
  24. package/licenses/browser-marked-package.json +10 -0
  25. package/licenses/coreutils-LICENSE +18 -0
  26. package/licenses/findutils-LICENSE +18 -0
  27. package/licenses/github.com_BurntSushi_toml_v1.6.0-COPYING +21 -0
  28. package/licenses/github.com_dustin_go-humanize_v1.0.1-LICENSE +21 -0
  29. package/licenses/github.com_google_jsonschema-go_v0.4.3-LICENSE +21 -0
  30. package/licenses/github.com_google_uuid_v1.6.0-LICENSE +27 -0
  31. package/licenses/github.com_jackc_pgpassfile_v1.0.0-LICENSE +22 -0
  32. package/licenses/github.com_jackc_pgservicefile_v0.0.0-20240606120523-5a60cdf6a761-LICENSE +22 -0
  33. package/licenses/github.com_jackc_pgx_v5_v5.11.0-LICENSE +22 -0
  34. package/licenses/github.com_jackc_puddle_v2_v2.2.2-LICENSE +22 -0
  35. package/licenses/github.com_mattn_go-isatty_v0.0.20-LICENSE +9 -0
  36. package/licenses/github.com_modelcontextprotocol_go-sdk_v1.8.0-LICENSE +216 -0
  37. package/licenses/github.com_ncruces_go-strftime_v1.0.0-LICENSE +21 -0
  38. package/licenses/github.com_remyoudompheng_bigfft_v0.0.0-20230129092748-24d4a6f8daec-LICENSE +27 -0
  39. package/licenses/github.com_santhosh-tekuri_jsonschema_v6_v6.0.3-LICENSE +175 -0
  40. package/licenses/github.com_segmentio_asm_v1.1.3-LICENSE +21 -0
  41. package/licenses/github.com_segmentio_encoding_v0.5.4-LICENSE +21 -0
  42. package/licenses/github.com_tetratelabs_wazero_v1.12.0-LICENSE +201 -0
  43. package/licenses/github.com_tetratelabs_wazero_v1.12.0-NOTICE +2 -0
  44. package/licenses/github.com_yosida95_uritemplate_v3_v3.0.2-LICENSE +25 -0
  45. package/licenses/golang.org_x_image_v0.44.0-LICENSE +27 -0
  46. package/licenses/golang.org_x_net_v0.58.0-LICENSE +27 -0
  47. package/licenses/golang.org_x_oauth2_v0.36.0-LICENSE +27 -0
  48. package/licenses/golang.org_x_sync_v0.22.0-LICENSE +27 -0
  49. package/licenses/golang.org_x_sys_v0.47.0-LICENSE +27 -0
  50. package/licenses/golang.org_x_text_v0.41.0-LICENSE +27 -0
  51. package/licenses/golang.org_x_time_v0.15.0-LICENSE +27 -0
  52. package/licenses/guanbi-skills-LICENSE +133 -0
  53. package/licenses/guanbi-skills-LICENSE.README.md +28 -0
  54. package/licenses/guanbi-skills-LICENSE.zh-CN +82 -0
  55. package/licenses/jq-COPYING +176 -0
  56. package/licenses/modernc.org_libc_v1.73.4-LICENSE +27 -0
  57. package/licenses/modernc.org_libc_v1.73.4-LICENSE-3RD-PARTY.md +305 -0
  58. package/licenses/modernc.org_mathutil_v1.7.1-LICENSE +27 -0
  59. package/licenses/modernc.org_memory_v1.11.0-LICENSE +27 -0
  60. package/licenses/modernc.org_memory_v1.11.0-LICENSE-GO +27 -0
  61. package/licenses/modernc.org_memory_v1.11.0-LICENSE-LOGO +1 -0
  62. package/licenses/modernc.org_memory_v1.11.0-LICENSE-MMAP-GO +25 -0
  63. package/licenses/modernc.org_sqlite_v1.53.0-LICENSE +26 -0
  64. package/licenses/mvdan.cc_sh_v3_v3.13.1-LICENSE +27 -0
  65. package/licenses/ripgrep-COPYING +3 -0
  66. package/licenses/ripgrep-LICENSE-MIT +21 -0
  67. package/licenses/sed-LICENSE +21 -0
  68. package/manifest.json +972 -0
  69. package/package.json +30 -4
  70. package/plugin/guancopilot.js +193 -0
  71. package/skills-bundle/.guanvm-skills-bundle +0 -0
  72. package/skills-bundle/_recipes/dashboard/gates/page_check.js +16 -0
  73. package/skills-bundle/_recipes/dashboard/gates/report_check.js +4 -0
  74. package/skills-bundle/_recipes/dashboard/knowledge/01.md +1 -0
  75. package/skills-bundle/_recipes/dashboard/knowledge/02.md +1 -0
  76. package/skills-bundle/_recipes/dashboard/knowledge/03.md +1 -0
  77. package/skills-bundle/_recipes/dashboard/knowledge/04.md +1 -0
  78. package/skills-bundle/_recipes/dashboard/knowledge/05.md +1 -0
  79. package/skills-bundle/_recipes/dashboard/knowledge/06.md +1 -0
  80. package/skills-bundle/_recipes/dashboard/template.json +41 -0
  81. package/skills-bundle/_recipes/etl/gates/etl_live_check.js +10 -0
  82. package/skills-bundle/_recipes/etl/gates/etl_report_check.js +3 -0
  83. package/skills-bundle/_recipes/etl/knowledge/01.md +1 -0
  84. package/skills-bundle/_recipes/etl/knowledge/02.md +9 -0
  85. package/skills-bundle/_recipes/etl/knowledge/03.md +1 -0
  86. package/skills-bundle/_recipes/etl/knowledge/04.md +1 -0
  87. package/skills-bundle/_recipes/etl/knowledge/05.md +1 -0
  88. package/skills-bundle/_recipes/etl/template.json +39 -0
  89. package/skills-bundle/_recipes/hints.json +46 -0
  90. package/skills-bundle/guancli/.commandface.json +155 -0
  91. package/skills-bundle/guancli/SKILL.md +342 -0
  92. package/skills-bundle/guancli/evals/evals.json +156 -0
  93. package/skills-bundle/guancli/evals/fixtures/metric-candidate-empty-search.json +13 -0
  94. package/skills-bundle/guancli/evals/fixtures/metric-candidate-prior-request.json +13 -0
  95. package/skills-bundle/guancli/evals/fixtures/metric-consumption-columns.json +46 -0
  96. package/skills-bundle/guancli/evals/fixtures/metric-consumption-metadata.json +36 -0
  97. package/skills-bundle/guancli/evals/fixtures/metric-consumption-query.json +18 -0
  98. package/skills-bundle/guancli/references/analysis-primitives.md +95 -0
  99. package/skills-bundle/guancli/references/auth-and-config.md +131 -0
  100. package/skills-bundle/guancli/references/examples/metric-date-in.json +7 -0
  101. package/skills-bundle/guancli/references/examples/metric-query-rows.py +83 -0
  102. package/skills-bundle/guancli/references/examples/metric-query.sh +6 -0
  103. package/skills-bundle/guancli/references/form-chatbi.md +199 -0
  104. package/skills-bundle/guancli/references/gateway.md +148 -0
  105. package/skills-bundle/guancli/references/metric-batch-query.md +82 -0
  106. package/skills-bundle/guancli/references/metric-commands.md +562 -0
  107. package/skills-bundle/guancli/references/metric-generic-query.md +215 -0
  108. package/skills-bundle/guancli/references/metric-json-migration.md +18 -0
  109. package/skills-bundle/guancli/references/metric-query-recipes.md +358 -0
  110. package/skills-bundle/guancli/references/preview-and-output.md +192 -0
  111. package/skills-bundle/guancli/references/resource-query.md +101 -0
  112. package/skills-bundle/guands/SKILL.md +281 -0
  113. package/skills-bundle/guands/references/calc-fields-boundaries-and-pitfalls.md +160 -0
  114. package/skills-bundle/guands/references/command-reference.md +267 -0
  115. package/skills-bundle/guands/references/incremental-and-schedule.md +157 -0
  116. package/skills-bundle/guands/references/workflows.md +256 -0
  117. package/skills-bundle/guanetl/SKILL.md +391 -0
  118. package/skills-bundle/guanetl/evals/evals.json +41 -0
  119. package/skills-bundle/guanetl/references/ETL_AI_DEVELOP.md +456 -0
  120. package/skills-bundle/guanmetric/SKILL.md +144 -0
  121. package/skills-bundle/guanmetric/evals/evals.json +117 -0
  122. package/skills-bundle/guanmetric/evals/fixtures/atomic-detail-read-model.json +18 -0
  123. package/skills-bundle/guanmetric/evals/fixtures/confirmed-template-summary.json +12 -0
  124. package/skills-bundle/guanmetric/evals/fixtures/create-result-unknown-state.json +13 -0
  125. package/skills-bundle/guanmetric/evals/forward-eval-2026-09-21.json +62 -0
  126. package/skills-bundle/guanmetric/references/metric-api-contract-examples.json +690 -0
  127. package/skills-bundle/guanmetric/references/metric-api-contract.schema.json +349 -0
  128. package/skills-bundle/guanmetric/references/metric-api-contracts.md +436 -0
  129. package/skills-bundle/guanmetric/references/metric-atomic-mutation.md +238 -0
  130. package/skills-bundle/guanmetric/references/metric-batch-status.md +71 -0
  131. package/skills-bundle/guanmetric/references/metric-business-dictionary-api-contract-examples.json +53 -0
  132. package/skills-bundle/guanmetric/references/metric-business-dictionary-api-contract.schema.json +26 -0
  133. package/skills-bundle/guanmetric/references/metric-business-dictionary.md +76 -0
  134. package/skills-bundle/guanmetric/references/metric-common-fields.md +246 -0
  135. package/skills-bundle/guanmetric/references/metric-composite-mutation.md +165 -0
  136. package/skills-bundle/guanmetric/references/metric-delete-mutation.md +70 -0
  137. package/skills-bundle/guanmetric/references/metric-derived-mutation.md +453 -0
  138. package/skills-bundle/guanmetric/references/metric-excel-template.md +365 -0
  139. package/skills-bundle/guanmetric/references/metric-expression-rules.md +146 -0
  140. package/skills-bundle/guanmetric/references/metric-flow-generate-example.json +79 -0
  141. package/skills-bundle/guanmetric/references/metric-flow-generate.schema.json +93 -0
  142. package/skills-bundle/guanmetric/references/metric-flow.md +218 -0
  143. package/skills-bundle/guanmetric/references/metric-hierarchy-api-contract-examples.json +171 -0
  144. package/skills-bundle/guanmetric/references/metric-hierarchy-api-contract.schema.json +167 -0
  145. package/skills-bundle/guanmetric/references/metric-hierarchy-api-contracts.md +193 -0
  146. package/skills-bundle/guanmetric/references/metric-mutation.md +155 -0
  147. package/skills-bundle/guanmetric/references/metric-public-dimension-api-contract-examples.json +62 -0
  148. package/skills-bundle/guanmetric/references/metric-public-dimension-api-contract.schema.json +47 -0
  149. package/skills-bundle/guanmetric/references/metric-public-dimension.md +155 -0
  150. package/skills-bundle/guanmetric/references/metric-tree-api-contract-examples.json +277 -0
  151. package/skills-bundle/guanmetric/references/metric-tree-api-contract.schema.json +263 -0
  152. package/skills-bundle/guanmetric/references/metric-tree-api-contracts.md +53 -0
  153. package/skills-bundle/guanmetric/references/metric-tree-mutation.md +500 -0
  154. package/skills-bundle/guanmetric/references/metric-validation-ownership.md +74 -0
  155. package/skills-bundle/guanvis/SKILL.md +333 -0
  156. package/skills-bundle/guanvis/evals/china_map/card_01_basic_map.js +13 -0
  157. package/skills-bundle/guanvis/evals/china_map/card_02_world_map.js +10 -0
  158. package/skills-bundle/guanvis/evals/china_map/card_03_point_map.js +12 -0
  159. package/skills-bundle/guanvis/evals/china_map/page.js +10 -0
  160. package/skills-bundle/guanvis/evals/china_map/schema.js +10 -0
  161. package/skills-bundle/guanvis/evals/comparative_mvp_roundtrip/README.md +30 -0
  162. package/skills-bundle/guanvis/evals/comparative_mvp_roundtrip/card_01_comparative_mvp.js +72 -0
  163. package/skills-bundle/guanvis/evals/comparative_mvp_roundtrip/page.js +6 -0
  164. package/skills-bundle/guanvis/evals/comparative_mvp_roundtrip/schema.js +22 -0
  165. package/skills-bundle/guanvis/evals/comparative_mvp_roundtrip/verify_default_roundtrip.sh +92 -0
  166. package/skills-bundle/guanvis/evals/complex_report_pro/card_01_monthly.js +63 -0
  167. package/skills-bundle/guanvis/evals/complex_report_pro/page.js +6 -0
  168. package/skills-bundle/guanvis/evals/complex_report_pro/schema.js +6 -0
  169. package/skills-bundle/guanvis/evals/custom_chart_echarts/card_01_bar.js +13 -0
  170. package/skills-bundle/guanvis/evals/custom_chart_echarts/card_02_pie.js +13 -0
  171. package/skills-bundle/guanvis/evals/custom_chart_echarts/charts/bar.js +11 -0
  172. package/skills-bundle/guanvis/evals/custom_chart_echarts/charts/pie.js +15 -0
  173. package/skills-bundle/guanvis/evals/custom_chart_echarts/page.js +5 -0
  174. package/skills-bundle/guanvis/evals/custom_chart_echarts/schema.js +9 -0
  175. package/skills-bundle/guanvis/evals/custom_chart_sdk/card_01_level_nodes.js +19 -0
  176. package/skills-bundle/guanvis/evals/custom_chart_sdk/charts/level_nodes.css +51 -0
  177. package/skills-bundle/guanvis/evals/custom_chart_sdk/charts/level_nodes.html +1 -0
  178. package/skills-bundle/guanvis/evals/custom_chart_sdk/charts/level_nodes.js +28 -0
  179. package/skills-bundle/guanvis/evals/custom_chart_sdk/page.js +5 -0
  180. package/skills-bundle/guanvis/evals/custom_chart_sdk/schema.js +7 -0
  181. package/skills-bundle/guanvis/evals/dynamic_fields/card_01_dynamic_dataset.js +20 -0
  182. package/skills-bundle/guanvis/evals/dynamic_fields/card_02_dynamic_metric_chart.js +16 -0
  183. package/skills-bundle/guanvis/evals/dynamic_fields/metrics.js +19 -0
  184. package/skills-bundle/guanvis/evals/dynamic_fields/page.js +9 -0
  185. package/skills-bundle/guanvis/evals/dynamic_fields/schema.js +7 -0
  186. package/skills-bundle/guanvis/evals/dynamic_fields/verify_default_roundtrip.sh +137 -0
  187. package/skills-bundle/guanvis/evals/percentage_advcalc_roundtrip/README.md +26 -0
  188. package/skills-bundle/guanvis/evals/percentage_advcalc_roundtrip/card_01_percentage_advcalc.js +44 -0
  189. package/skills-bundle/guanvis/evals/percentage_advcalc_roundtrip/card_02_scroll_percentage.js +12 -0
  190. package/skills-bundle/guanvis/evals/percentage_advcalc_roundtrip/page.js +6 -0
  191. package/skills-bundle/guanvis/evals/percentage_advcalc_roundtrip/schema.js +22 -0
  192. package/skills-bundle/guanvis/evals/rank_advcalc_roundtrip/README.md +28 -0
  193. package/skills-bundle/guanvis/evals/rank_advcalc_roundtrip/card_01_rank_advcalc.js +59 -0
  194. package/skills-bundle/guanvis/evals/rank_advcalc_roundtrip/page.js +6 -0
  195. package/skills-bundle/guanvis/evals/rank_advcalc_roundtrip/schema.js +22 -0
  196. package/skills-bundle/guanvis/evals/rank_advcalc_roundtrip/verify_default_roundtrip.sh +135 -0
  197. package/skills-bundle/guanvis/evals/running_total_advcalc_roundtrip/README.md +15 -0
  198. package/skills-bundle/guanvis/evals/running_total_advcalc_roundtrip/card_01_running_total_advcalc.js +74 -0
  199. package/skills-bundle/guanvis/evals/running_total_advcalc_roundtrip/page.js +6 -0
  200. package/skills-bundle/guanvis/evals/running_total_advcalc_roundtrip/schema.js +22 -0
  201. package/skills-bundle/guanvis/evals/sales_dashboard/card_01_revenue_column.js +10 -0
  202. package/skills-bundle/guanvis/evals/sales_dashboard/card_02_trend_line.js +9 -0
  203. package/skills-bundle/guanvis/evals/sales_dashboard/card_03_kpi.js +6 -0
  204. package/skills-bundle/guanvis/evals/sales_dashboard/card_04_pie.js +9 -0
  205. package/skills-bundle/guanvis/evals/sales_dashboard/card_05_calc_field_test.js +12 -0
  206. package/skills-bundle/guanvis/evals/sales_dashboard/page.js +11 -0
  207. package/skills-bundle/guanvis/evals/sales_dashboard/schema.js +14 -0
  208. package/skills-bundle/guanvis/evals/sales_dashboard/selector_01_region.js +10 -0
  209. package/skills-bundle/guanvis/evals/split_charts/card_01_column_split.js +10 -0
  210. package/skills-bundle/guanvis/evals/split_charts/card_02_line_split.js +10 -0
  211. package/skills-bundle/guanvis/evals/split_charts/card_03_combo_split.js +11 -0
  212. package/skills-bundle/guanvis/evals/split_charts/page.js +10 -0
  213. package/skills-bundle/guanvis/evals/split_charts/schema.js +14 -0
  214. package/skills-bundle/guanvis/evals/tab_layout/card_00_total_revenue.js +6 -0
  215. package/skills-bundle/guanvis/evals/tab_layout/card_01_revenue.js +10 -0
  216. package/skills-bundle/guanvis/evals/tab_layout/card_02_trend.js +9 -0
  217. package/skills-bundle/guanvis/evals/tab_layout/card_03_story.js +11 -0
  218. package/skills-bundle/guanvis/evals/tab_layout/page.js +23 -0
  219. package/skills-bundle/guanvis/evals/tab_layout/schema.js +11 -0
  220. package/skills-bundle/guanvis/evals/tab_layout/selector_01_region.js +8 -0
  221. package/skills-bundle/guanvis/references/api-reference.md +476 -0
  222. package/skills-bundle/guanvis/references/builder-reference.md +1676 -0
  223. package/skills-bundle/guanvis/references/chart-properties.md +2101 -0
  224. package/skills-bundle/guanvis/references/checkout-editing.md +53 -0
  225. package/skills-bundle/guanvis/references/complex-report-pro-patterns.md +398 -0
  226. package/skills-bundle/guanvis/references/dir-and-page-management.md +48 -0
  227. package/skills-bundle/guanvis/references/metric-chart-reference.md +258 -0
  228. package/skills-bundle/guanvis/references/publish-and-constraints.md +60 -0
  229. package/skills-bundle/guanvis/references/selector-reference.md +431 -0
  230. package/skills-bundle/guanvis/references/tableau-migration.md +73 -0
  231. package/skills-bundle/guanvis/references/theme.md +123 -0
  232. package/skills-bundle/guanvis/references/troubleshooting.md +25 -0
  233. package/skills-bundle/guanvis/references/validation-and-chart-patterns.md +205 -0
  234. package/skills-bundle/guanwf/SKILL.md +543 -0
  235. package/skills-bundle/guanwf/references/ETL_AI_DEVELOP.md +456 -0
  236. package/skills-bundle/guanwf/references/TASK_NODE_CONTRACTS.md +64 -0
  237. package/skills-bundle/guanwf/references/WORKFLOW_DSL.md +536 -0
  238. package/skills-bundle/guanwf/references/WORKFLOW_NODES.md +166 -0
  239. package/skills-bundle/js/SKILL.md +145 -0
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
5
+ EVAL_DIR="$ROOT_DIR/evals/comparative_mvp_roundtrip"
6
+ OUT_DIR="$EVAL_DIR/.roundtrip"
7
+
8
+ mkdir -p "$OUT_DIR"
9
+
10
+ cd "$ROOT_DIR"
11
+
12
+ go run ./cmd/guanvis preview "$EVAL_DIR" > "$OUT_DIR/preview_payload.json"
13
+ go run ./cmd/guanvis publish "$EVAL_DIR" > "$OUT_DIR/publish.log"
14
+
15
+ guancli card get p3bc51ab664e095ab2dff402 --profile default --raw > "$OUT_DIR/raw_normal.json"
16
+ guancli card get o360e50afb73bcc998353c13 --profile default --raw > "$OUT_DIR/raw_filter_based.json"
17
+ guancli card preview p3bc51ab664e095ab2dff402 --profile default --limit 50 -f json > "$OUT_DIR/preview_normal.json"
18
+ guancli card preview o360e50afb73bcc998353c13 --profile default --limit 50 -f json > "$OUT_DIR/preview_filter_based.json"
19
+
20
+ python3 - "$OUT_DIR" <<'PY'
21
+ import json
22
+ import sys
23
+ from pathlib import Path
24
+
25
+ out = Path(sys.argv[1])
26
+
27
+ cases = [
28
+ {
29
+ "kind": "normal",
30
+ "raw": out / "raw_normal.json",
31
+ "preview": out / "preview_normal.json",
32
+ "expected_aliases": [
33
+ "月同比对比值",
34
+ "季同比增长值",
35
+ "年同比增长率",
36
+ "上月末增长率",
37
+ "上季度末增长值",
38
+ "上年末对比值",
39
+ ],
40
+ },
41
+ {
42
+ "kind": "filter_based",
43
+ "raw": out / "raw_filter_based.json",
44
+ "preview": out / "preview_filter_based.json",
45
+ "expected_aliases": [
46
+ "日环比增长值",
47
+ "周同比增长率",
48
+ "默认年同比增长率",
49
+ "年同比按周对比值",
50
+ "自定义后移增长率",
51
+ ],
52
+ },
53
+ ]
54
+
55
+ summary = {"cards": []}
56
+
57
+ for case in cases:
58
+ raw_obj = json.loads(case["raw"].read_text())
59
+ response = raw_obj.get("response") or raw_obj.get("data") or raw_obj
60
+ zone_data = response["content"]["meta"]["chartMain"]["zoneData"]
61
+ metrics = zone_data.get("metric", [])
62
+ by_alias = {m.get("alias"): m for m in metrics if m.get("alias")}
63
+ rows = json.loads(case["preview"].read_text())
64
+
65
+ card_summary = {
66
+ "kind": case["kind"],
67
+ "cardId": response.get("cdId") or response.get("id"),
68
+ "name": response.get("name"),
69
+ "previewRows": len(rows),
70
+ "aliases": {},
71
+ }
72
+
73
+ for alias in case["expected_aliases"]:
74
+ metric = by_alias.get(alias)
75
+ if metric is None:
76
+ raise SystemExit(f"{case['kind']}: missing metric alias {alias}")
77
+ if "formula" in metric:
78
+ raise SystemExit(f"{case['kind']}: {alias} degraded to formula")
79
+ adv_calc = metric.get("advCalc")
80
+ if not adv_calc:
81
+ raise SystemExit(f"{case['kind']}: {alias} missing advCalc")
82
+ if adv_calc.get("advType") != "COMPARATIVE":
83
+ raise SystemExit(f"{case['kind']}: {alias} advType is not COMPARATIVE")
84
+ if not any(isinstance(row, dict) and row.get(alias) not in ("", None) for row in rows):
85
+ raise SystemExit(f"{case['kind']}: {alias} has no non-empty preview value")
86
+ card_summary["aliases"][alias] = adv_calc["advValue"]
87
+
88
+ summary["cards"].append(card_summary)
89
+
90
+ (out / "summary.json").write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8")
91
+ print(json.dumps(summary, ensure_ascii=False, indent=2))
92
+ PY
@@ -0,0 +1,63 @@
1
+ var salesView = createCard(ChartType.DATA_GRID, "sales")
2
+ .setId("crpchildsales00000000001")
3
+ .bindDataset(DS)
4
+ .addRow(f("区域"))
5
+ .addRow(f("门店"))
6
+ .addMetric(f("销售额", { aggrType: AggrType.SUM }))
7
+ .addMetric(f("利润", { aggrType: AggrType.SUM }));
8
+
9
+ var workbook = createReportWorkbook()
10
+ .addSheet("经营月报", function(sheet) {
11
+ sheet.setColumnWidth("A", 18)
12
+ .setColumnWidth("B", 18)
13
+ .setColumnWidth("C", 16)
14
+ .setColumnWidth("D", 16)
15
+ .setRowHeight(1, 28)
16
+ .merge("A1:D1")
17
+ .setValue("A1", "门店经营月报", {
18
+ fontSize: 16,
19
+ bold: true,
20
+ horizontalAlignment: "center",
21
+ verticalAlignment: "center",
22
+ backgroundColor: "#D9EAF7",
23
+ borderColor: "#8AA8BF"
24
+ })
25
+ .setValue("A2", "区域", { bold: true, backgroundColor: "#EEF5FA", borderColor: "#8AA8BF" })
26
+ .setValue("B2", "门店", { bold: true, backgroundColor: "#EEF5FA", borderColor: "#8AA8BF" })
27
+ .setValue("C2", "销售额", { bold: true, backgroundColor: "#EEF5FA", borderColor: "#8AA8BF" })
28
+ .setValue("D2", "利润", { bold: true, backgroundColor: "#EEF5FA", borderColor: "#8AA8BF" })
29
+ .bindDimension("A3", reportField("sales", "区域"), {
30
+ expansion: "vertical",
31
+ group: "merge",
32
+ style: { borderColor: "#D0D7DE" }
33
+ })
34
+ .bindDimension("B3", reportField("sales", "门店"), {
35
+ expansion: "vertical",
36
+ context: "A3",
37
+ style: { borderColor: "#D0D7DE" }
38
+ })
39
+ .bindMetric("C3", reportField("sales", "销售额"), {
40
+ aggregate: "SUM",
41
+ expansion: "vertical",
42
+ context: "B3",
43
+ numberFormat: "#,##0.00",
44
+ style: { borderColor: "#D0D7DE" }
45
+ })
46
+ .bindMetric("D3", reportField("sales", "利润"), {
47
+ aggregate: "SUM",
48
+ expansion: "vertical",
49
+ context: "B3",
50
+ numberFormat: "#,##0.00",
51
+ style: { borderColor: "#D0D7DE" }
52
+ });
53
+ });
54
+
55
+ var report = createComplexReportPro("门店经营月报 Pro")
56
+ .setId("crpparentmonthly00000001")
57
+ .setDescription("GuanVis 结构化工作簿 DSL 生成的复杂报表 Pro")
58
+ .setWorkbook(workbook)
59
+ .addDataView("sales", salesView)
60
+ .setPagination(false)
61
+ .setColumnWidthStretch(true);
62
+
63
+ registerComplexReportPro(report.build());
@@ -0,0 +1,6 @@
1
+ var page = createPage("复杂报表 Pro DSL 示例")
2
+ .setId("crppagemonthly0000000001")
3
+ .setDescription("结构化 xlsx、字段绑定、样式和复杂报表 Pro 原生附件协议")
4
+ .addFullWidthCard(0, 12);
5
+
6
+ registerPage(page.build());
@@ -0,0 +1,6 @@
1
+ defineDataset("complex_report_sales", [
2
+ { fdId: "region_fd", name: "区域", fdType: "STRING", metaType: "DIM" },
3
+ { fdId: "store_fd", name: "门店", fdType: "STRING", metaType: "DIM" },
4
+ { fdId: "sales_fd", name: "销售额", fdType: "DOUBLE", metaType: "METRIC" },
5
+ { fdId: "profit_fd", name: "利润", fdType: "DOUBLE", metaType: "METRIC" }
6
+ ], { displayType: "CSV" });
@@ -0,0 +1,13 @@
1
+ var dataView = createCard(ChartType.DATA_GRID, "区域营收数据")
2
+ .setId("a000000000000000000child")
3
+ .bindDataset(DS)
4
+ .addRow(f("区域"))
5
+ .addMetric(f("营收", { aggrType: AggrType.SUM }));
6
+
7
+ var chart = createCustomChart("区域营收柱状图")
8
+ .setId("a000000000000000000chart")
9
+ .setSubType(CustomChartSubType.ECHARTS_LITE)
10
+ .loadContent("charts/bar")
11
+ .addDataView(dataView);
12
+
13
+ registerCustomChart(chart.build());
@@ -0,0 +1,13 @@
1
+ var dataView = createCard(ChartType.DATA_GRID, "产品成本数据")
2
+ .setId("b000000000000000000child")
3
+ .bindDataset(DS)
4
+ .addRow(f("产品"))
5
+ .addMetric(f("成本", { aggrType: AggrType.SUM }));
6
+
7
+ var chart = createCustomChart("产品成本饼图")
8
+ .setId("b000000000000000000chart")
9
+ .setSubType(CustomChartSubType.ECHARTS_LITE)
10
+ .loadContent("charts/pie")
11
+ .addDataView(dataView);
12
+
13
+ registerCustomChart(chart.build());
@@ -0,0 +1,11 @@
1
+ var cols = data[0] || [];
2
+ var categories = (cols[0] || {}).data || [];
3
+ var values = (cols[1] || {}).data || [];
4
+
5
+ option = {
6
+ tooltip: { trigger: "axis" },
7
+ xAxis: { type: "category", data: categories },
8
+ yAxis: { type: "value" },
9
+ series: [{ type: "bar", data: values }],
10
+ color: config.colors
11
+ };
@@ -0,0 +1,15 @@
1
+ var cols = data[0] || [];
2
+ var names = (cols[0] || {}).data || [];
3
+ var values = (cols[1] || {}).data || [];
4
+
5
+ option = {
6
+ tooltip: { trigger: "item" },
7
+ legend: { bottom: 10 },
8
+ series: [{
9
+ type: "pie",
10
+ radius: ["30%", "60%"],
11
+ data: names.map(function(name, i) { return { name: name, value: values[i] }; }),
12
+ label: { formatter: "{b}: {d}%" }
13
+ }],
14
+ color: config.colors
15
+ };
@@ -0,0 +1,5 @@
1
+ var page = createPage("自定义图表示例")
2
+ .setId("na865a38893e52efa3a47c1d")
3
+ .addHalfWidthCards(0, 1);
4
+
5
+ registerPage(page.build());
@@ -0,0 +1,9 @@
1
+ // Auto-generated by guanvis schema command
2
+ // DO NOT EDIT — regenerate with: guanvis schema s9ded2338f43807b095fbb4f
3
+
4
+ defineDataset("s9ded2338f43807b095fbb4f", [
5
+ { fdId: "jb16f22f6c1c21c3f6e8e293", name: "区域", fdType: "STRING", metaType: "DIM" },
6
+ { fdId: "o7093aaf2b2f32c9e964c41a", name: "产品", fdType: "STRING", metaType: "DIM" },
7
+ { fdId: "lb1c31b4dc86511a27049a98", name: "营收", fdType: "DOUBLE", metaType: "METRIC" },
8
+ { fdId: "aece10cd6806966be56f52ff", name: "成本", fdType: "DOUBLE", metaType: "METRIC" }
9
+ ], { displayType: "CSV" });
@@ -0,0 +1,19 @@
1
+ // 自定义图表 SDK 模式最小示例:横向等级进度节点图(自定义 HTML/CSS + GDPlugin)。
2
+ // 适用场景:内置图表做不出来的自定义 DOM 布局(节点图/进度轴/卡片流等);
3
+ // 纯 ECharts 需求改用 CustomChartSubType.ECHARTS_LITE(见 evals/custom_chart_echarts/)。
4
+ var dataView = createCard(ChartType.DATA_GRID, "等级数据视图")
5
+ .setId("a000000000000000sdkchild")
6
+ .bindDataset(DS)
7
+ .addRow(f("等级"))
8
+ .addMetric(f("占比", { aggrType: AggrType.SUM }))
9
+ // 节点顺序必须显式排序,否则依赖查询返回顺序;数据集有序号字段时优先按序号排
10
+ .addSort(f("等级", { sortType: SortOrder.ASC }));
11
+
12
+ var chart = createCustomChart("等级进度节点图")
13
+ .setId("a000000000000000sdkchart")
14
+ .setSubType(CustomChartSubType.SDK)
15
+ .loadContent("charts/level_nodes")
16
+ .addDataView(dataView);
17
+
18
+ // 自定义图表必须用 registerCustomChart;registerCard 会在 preview 时报类型错配。
19
+ registerCustomChart(chart.build());
@@ -0,0 +1,51 @@
1
+ /* 横向节点+连接线布局范式:
2
+ * 圆点和连接线放进同一个固定高度的 .circle-area,连接线绝对定位、top 对准圆心。
3
+ * 把连接线做成与节点同级的独立 flex 项无法与圆心垂直对齐(客户实测踩坑)。 */
4
+ #container {
5
+ width: 100%;
6
+ height: 100%;
7
+ display: flex;
8
+ align-items: center;
9
+ justify-content: center;
10
+ font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
11
+ }
12
+ .node {
13
+ flex: 1;
14
+ max-width: 160px;
15
+ text-align: center;
16
+ }
17
+ .node .name {
18
+ font-size: 14px;
19
+ font-weight: 600;
20
+ margin-bottom: 8px;
21
+ }
22
+ .node .circle-area {
23
+ position: relative;
24
+ height: 16px; /* 与圆点直径一致,连接线以此垂直居中 */
25
+ }
26
+ .node .dot {
27
+ width: 16px;
28
+ height: 16px;
29
+ border-radius: 50%;
30
+ background: #4575b4;
31
+ margin: 0 auto;
32
+ position: relative;
33
+ z-index: 1;
34
+ }
35
+ /* 连接线:从本节点圆心向右延伸到下一节点圆心 */
36
+ .node .connector {
37
+ position: absolute;
38
+ top: 7px; /* (circle-area 高 16 - 线高 2) / 2 */
39
+ left: 50%;
40
+ width: 100%;
41
+ height: 2px;
42
+ background: #c3d4e8;
43
+ }
44
+ .node:last-child .connector {
45
+ display: none;
46
+ }
47
+ .node .value {
48
+ font-size: 13px;
49
+ color: #666;
50
+ margin-top: 8px;
51
+ }
@@ -0,0 +1 @@
1
+ <div id="container"></div>
@@ -0,0 +1,28 @@
1
+ // SDK 模式渲染代码:实现 renderChart 并用 new GDPlugin().init(renderChart) 挂载。
2
+ // data[i] 对应第 i 个数据视图的列数据: [{name:"字段名", data:[...]}, ...]
3
+ // config.colors 是主题色数组。联动回传(仅在 dataView 配置了 .linkTo() 时需要):
4
+ // clickFunc({ clickedItems: [{ idx: [viewIndex, columnIndex], colName: "列名", value: ["选中值"] }] })
5
+ function renderChart(data, clickFunc, config) {
6
+ var cols = data[0] || [];
7
+ var names = (cols[0] || {}).data || [];
8
+ var values = (cols[1] || {}).data || [];
9
+ var color = (config && config.colors && config.colors[0]) || "#4575b4";
10
+
11
+ var container = document.getElementById("container");
12
+ container.innerHTML = "";
13
+ for (var i = 0; i < names.length; i++) {
14
+ var node = document.createElement("div");
15
+ node.className = "node";
16
+ // 量纲以 guancli ds preview 看到的真实数据为准:0~1 小数才需要 ×100
17
+ var pct = (Number(values[i]) * 100).toFixed(1) + "%";
18
+ node.innerHTML =
19
+ '<div class="name"></div>' +
20
+ '<div class="circle-area"><div class="connector"></div><div class="dot"></div></div>' +
21
+ '<div class="value"></div>';
22
+ node.querySelector(".name").textContent = String(names[i]);
23
+ node.querySelector(".dot").style.background = color;
24
+ node.querySelector(".value").textContent = pct;
25
+ container.appendChild(node);
26
+ }
27
+ }
28
+ new GDPlugin().init(renderChart);
@@ -0,0 +1,5 @@
1
+ var page = createPage("自定义图表 SDK 示例")
2
+ .setId("a000000000000000sdkpage0")
3
+ .addFullWidthCard(0, 8);
4
+
5
+ registerPage(page.build());
@@ -0,0 +1,7 @@
1
+ // Auto-generated by guanvis schema command
2
+ // DO NOT EDIT — regenerate with: guanvis schema <dsId>
3
+
4
+ defineDataset("s0000000000000000000demo", [
5
+ { fdId: "f0000000000000000000lvl1", name: "等级", fdType: "STRING", metaType: "DIM" },
6
+ { fdId: "f0000000000000000000rat2", name: "占比", fdType: "DOUBLE", metaType: "METRIC" }
7
+ ], { displayType: "CSV" });
@@ -0,0 +1,20 @@
1
+ var region = f("区域");
2
+ var city = f("城市");
3
+ var sales = f("销售额", { aggrType: AggrType.SUM });
4
+ var profit = f("利润", { aggrType: AggrType.SUM });
5
+
6
+ var card = createCard(ChartType.GROUPED_COLUMN, "动态维度与动态数值")
7
+ .setId("dfcarddataset00000000000")
8
+ .bindDataset(DS)
9
+ .addDynamicRow("分析维度", [region, city], {
10
+ defaultValue: [region],
11
+ multiSelect: false
12
+ })
13
+ .addDynamicMetric("分析数值", [sales, profit], {
14
+ defaultValue: [sales],
15
+ multiSelect: true,
16
+ orderType: DynamicFieldOrder.CLICK
17
+ })
18
+ .setShowLegend(true, "bottom");
19
+
20
+ registerCard(card.build());
@@ -0,0 +1,16 @@
1
+ var region = metricDim("销售额", "区域");
2
+ var city = metricDim("销售额", "城市");
3
+ var sales = metric("销售额");
4
+ var profit = metric("利润");
5
+
6
+ var card = createMetricChart(ChartType.PIVOT_TABLE, "指标平台动态维度与动态指标")
7
+ .setId("dfmetriccard000000000000")
8
+ .addDynamicRow("分析维度", [region, city], {
9
+ defaultValue: [region]
10
+ })
11
+ .addDynamicMetric("分析指标", [sales, profit], {
12
+ defaultValue: [sales],
13
+ multiSelect: true
14
+ });
15
+
16
+ registerMetricChart(card.build());
@@ -0,0 +1,19 @@
1
+ defineMetric({
2
+ id: "metric_sales_eval_000001",
3
+ name: "销售额",
4
+ subType: "ATOMIC",
5
+ applicableDims: [
6
+ { fdId: "region_fd", dsId: "ds_dynamic_fields_eval", name: "区域", fdType: "STRING", metaType: "DIM" },
7
+ { fdId: "city_fd", dsId: "ds_dynamic_fields_eval", name: "城市", fdType: "STRING", metaType: "DIM" }
8
+ ]
9
+ });
10
+
11
+ defineMetric({
12
+ id: "metric_profit_eval_00001",
13
+ name: "利润",
14
+ subType: "ATOMIC",
15
+ applicableDims: [
16
+ { fdId: "region_fd", dsId: "ds_dynamic_fields_eval", name: "区域", fdType: "STRING", metaType: "DIM" },
17
+ { fdId: "city_fd", dsId: "ds_dynamic_fields_eval", name: "城市", fdType: "STRING", metaType: "DIM" }
18
+ ]
19
+ });
@@ -0,0 +1,9 @@
1
+ var page = createPage("动态字段示例")
2
+ .setId("dfpage000000000000000000")
3
+ .setDescription("验证动态维度、动态数值和指标平台动态指标的资源包结构。")
4
+ .setBackgroundColor("#f5f5f5")
5
+ .setCardMargin(8)
6
+ .addFullWidthCard(0, 7)
7
+ .addFullWidthCard(1, 7);
8
+
9
+ registerPage(page.build());
@@ -0,0 +1,7 @@
1
+ defineDataset("ds_dynamic_fields_eval", [
2
+ { fdId: "region_fd", name: "区域", fdType: "STRING", metaType: "DIM" },
3
+ { fdId: "city_fd", name: "城市", fdType: "STRING", metaType: "DIM" },
4
+ { fdId: "month_fd", name: "月份", fdType: "DATE", metaType: "DIM" },
5
+ { fdId: "sales_fd", name: "销售额", fdType: "DOUBLE", metaType: "METRIC" },
6
+ { fdId: "profit_fd", name: "利润", fdType: "DOUBLE", metaType: "METRIC" }
7
+ ], { displayType: "CSV" });
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
5
+ EVAL_DIR="$ROOT_DIR/evals/dynamic_fields"
6
+ OUT_DIR="$EVAL_DIR/.roundtrip"
7
+
8
+ mkdir -p "$OUT_DIR"
9
+
10
+ cd "$ROOT_DIR"
11
+
12
+ go run ./cmd/guanvis preview "$EVAL_DIR" > "$OUT_DIR/preview_payload.json"
13
+ go run ./cmd/guanvis pack "$EVAL_DIR" -o "$OUT_DIR/dynamic_fields_package.zip" > "$OUT_DIR/pack.log"
14
+
15
+ if [[ "${PUBLISH:-0}" == "1" ]]; then
16
+ go run ./cmd/guanvis publish "$EVAL_DIR" > "$OUT_DIR/publish.log"
17
+ guancli card get dfcarddataset00000000000 --raw > "$OUT_DIR/raw_dataset_card.json"
18
+ guancli card get dfmetriccard000000000000 --raw > "$OUT_DIR/raw_metric_card.json"
19
+ fi
20
+
21
+ python3 - "$OUT_DIR" <<'PY'
22
+ import json
23
+ import sys
24
+ from pathlib import Path
25
+
26
+ out = Path(sys.argv[1])
27
+
28
+ expected = {
29
+ "dfcarddataset00000000000": {
30
+ "name": "动态维度与动态数值",
31
+ "mappings": {
32
+ "分析维度": {"zoneId": "row", "metaType": "DIM", "multiSelect": False},
33
+ "分析数值": {"zoneId": "metric", "metaType": "METRIC", "multiSelect": True},
34
+ },
35
+ },
36
+ "dfmetriccard000000000000": {
37
+ "name": "指标平台动态维度与动态指标",
38
+ "mappings": {
39
+ "分析维度": {"zoneId": "row", "metaType": "DIM", "multiSelect": False},
40
+ "分析指标": {"zoneId": "metric", "metaType": "METRIC", "multiSelect": True},
41
+ },
42
+ },
43
+ }
44
+
45
+ def load_json(path):
46
+ text = path.read_text(encoding="utf-8")
47
+ start = text.find("{")
48
+ if start > 0:
49
+ text = text[start:]
50
+ return json.loads(text)
51
+
52
+ def normalize_card(raw):
53
+ obj = raw.get("response") or raw.get("data") or raw
54
+ if "card" in obj:
55
+ card = obj["card"]
56
+ meta = json.loads(card["meta"]) if isinstance(card.get("meta"), str) else card.get("meta")
57
+ return {"id": card.get("cdId"), "name": card.get("name"), "meta": meta}
58
+ content = obj.get("content") or obj
59
+ return {"id": obj.get("cdId") or obj.get("id"), "name": obj.get("name"), "meta": content.get("meta")}
60
+
61
+ def preview_cards():
62
+ payload = load_json(out / "preview_payload.json")
63
+ cards = []
64
+ for item in payload.get("cards", []):
65
+ card = item.get("card", {})
66
+ meta = json.loads(card["meta"]) if isinstance(card.get("meta"), str) else card.get("meta")
67
+ cards.append({"id": card.get("cdId"), "name": card.get("name"), "meta": meta})
68
+ return cards
69
+
70
+ def assert_dynamic_card(card):
71
+ card_id = card["id"]
72
+ if card_id not in expected:
73
+ return None
74
+ spec = expected[card_id]
75
+ chart_main = (card.get("meta") or {}).get("chartMain") or {}
76
+ zone_data = chart_main.get("zoneData") or {}
77
+ dz_info = chart_main.get("dynamicZoneInfo") or {}
78
+ if dz_info.get("hidden") is not False:
79
+ raise SystemExit(f"{card_id}: dynamicZoneInfo.hidden={dz_info.get('hidden')!r}, want false")
80
+ mappings = dz_info.get("dzMappings") or []
81
+ by_name = {m.get("name"): m for m in mappings}
82
+ if set(by_name) != set(spec["mappings"]):
83
+ raise SystemExit(f"{card_id}: dzMappings names={sorted(by_name)}, want {sorted(spec['mappings'])}")
84
+
85
+ summary = {"cardId": card_id, "name": card.get("name"), "mappings": {}}
86
+ for name, want in spec["mappings"].items():
87
+ mapping = by_name[name]
88
+ for key, want_value in want.items():
89
+ got = mapping.get(key)
90
+ if got != want_value:
91
+ raise SystemExit(f"{card_id}: mapping {name} {key}={got!r}, want {want_value!r}")
92
+ if mapping.get("selectType") != "SEARCHBOX":
93
+ raise SystemExit(f"{card_id}: mapping {name} selectType={mapping.get('selectType')!r}, want SEARCHBOX")
94
+ if mapping.get("orderType") not in ("PRESET", "CLICK"):
95
+ raise SystemExit(f"{card_id}: mapping {name} invalid orderType={mapping.get('orderType')!r}")
96
+ dz_id = mapping.get("dzId")
97
+ defaults = mapping.get("defaultValue") or []
98
+ if not dz_id:
99
+ raise SystemExit(f"{card_id}: mapping {name} missing dzId")
100
+ if not defaults:
101
+ raise SystemExit(f"{card_id}: mapping {name} defaultValue is empty")
102
+ zone_fields = zone_data.get(mapping["zoneId"]) or []
103
+ candidate_keys = {f.get("key") for f in zone_fields if f.get("dzId") == dz_id}
104
+ if not candidate_keys:
105
+ raise SystemExit(f"{card_id}: mapping {name} has no zoneData candidates for dzId={dz_id}")
106
+ missing = [key for key in defaults if key not in candidate_keys]
107
+ if missing:
108
+ raise SystemExit(f"{card_id}: mapping {name} defaultValue keys {missing} not found in zoneData.{mapping['zoneId']}")
109
+ summary["mappings"][name] = {
110
+ "dzId": dz_id,
111
+ "zoneId": mapping["zoneId"],
112
+ "metaType": mapping["metaType"],
113
+ "multiSelect": mapping["multiSelect"],
114
+ "defaultValue": defaults,
115
+ "candidateCount": len(candidate_keys),
116
+ }
117
+ return summary
118
+
119
+ summaries = []
120
+ for card in preview_cards():
121
+ result = assert_dynamic_card(card)
122
+ if result:
123
+ summaries.append(result)
124
+
125
+ if len(summaries) != len(expected):
126
+ got = sorted(item["cardId"] for item in summaries)
127
+ raise SystemExit(f"preview dynamic card count={len(summaries)}, got {got}, want {sorted(expected)}")
128
+
129
+ if (out / "raw_dataset_card.json").exists() and (out / "raw_metric_card.json").exists():
130
+ for raw_name in ("raw_dataset_card.json", "raw_metric_card.json"):
131
+ raw_summary = assert_dynamic_card(normalize_card(load_json(out / raw_name)))
132
+ summaries.append({**raw_summary, "source": raw_name})
133
+
134
+ summary = {"cards": summaries}
135
+ (out / "summary.json").write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8")
136
+ print(json.dumps(summary, ensure_ascii=False, indent=2))
137
+ PY
@@ -0,0 +1,26 @@
1
+ # Percentage Advanced Calc Round-Trip
2
+
3
+ This eval verifies the L3 native BI `LEVEL_PERCENTAGE` advanced calculation builder against a real `default` BI environment.
4
+
5
+ It covers:
6
+
7
+ - 按列:`SUM`
8
+ - 按行:`SUM` with a column dimension, matching the UI-created path
9
+ - 指定行维度:`SUM`
10
+ - 指定列维度:`SUM`
11
+ - 去重计数:`CNT_DISTINCT`
12
+ - 平均值:`AVG`, covering the backend SUM/CNT auxiliary denominator path
13
+
14
+ Local payload preview from `guanvis/`:
15
+
16
+ ```bash
17
+ guanvis preview ./evals/percentage_advcalc_roundtrip
18
+ ```
19
+
20
+ Default-environment publish/readback commands intentionally live in the project golden-answer note rather than this PR, because they publish fixed resource IDs to the shared `default` environment. The manual verification checks:
21
+
22
+ - `advCalc` is preserved in `zoneData.metric[*].advCalc`.
23
+ - No metric silently degrades to a formula.
24
+ - Each expected `measureOnDimRow` / `tfdId` / `auto` field round-trips.
25
+ - Percentage number format is present with two decimal places.
26
+ - `card preview` succeeds and percentage aliases have non-empty values.
@@ -0,0 +1,44 @@
1
+ var region = f("地区");
2
+ var province = f("省/自治区");
3
+ var category = f("类别");
4
+ var subcategory = f("子类别");
5
+
6
+ var percentageCard = createCard(ChartType.PIVOT_TABLE, "L3 占比高级计算")
7
+ .setId("pct01a02b03c04d05e06f07g")
8
+ .bindDataset(DS)
9
+ .addRow(region)
10
+ .addRow(province)
11
+ .addColumn(category)
12
+ .addColumn(subcategory)
13
+ .addMetric(f("销售额", {
14
+ alias: "按列SUM占比",
15
+ aggrType: AggrType.SUM,
16
+ advCalc: percentage.byColumn()
17
+ }))
18
+ .addMetric(f("销售额", {
19
+ alias: "按行SUM占比",
20
+ aggrType: AggrType.SUM,
21
+ advCalc: percentage.byRow()
22
+ }))
23
+ .addMetric(f("销售额", {
24
+ alias: "地区层级SUM占比",
25
+ aggrType: AggrType.SUM,
26
+ advCalc: percentage.byField(region)
27
+ }))
28
+ .addMetric(f("销售额", {
29
+ alias: "类别层级SUM占比",
30
+ aggrType: AggrType.SUM,
31
+ advCalc: percentage.byField(category)
32
+ }))
33
+ .addMetric(f("客户 ID", {
34
+ alias: "客户去重按列占比",
35
+ aggrType: AggrType.COUNT_DISTINCT,
36
+ advCalc: percentage.byColumn()
37
+ }))
38
+ .addMetric(f("折扣", {
39
+ alias: "折扣AVG地区占比",
40
+ aggrType: AggrType.AVG,
41
+ advCalc: percentage.byField(region)
42
+ }));
43
+
44
+ registerCard(percentageCard.build());