@xpert-ai/plugin-live-dashboard 0.3.0 → 0.4.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 (153) hide show
  1. package/.xpertai-plugin/plugin.json +10 -2
  2. package/README.md +70 -0
  3. package/assets/branding/cover-v1.prompt.txt +27 -0
  4. package/assets/branding/cover-v1.webp +0 -0
  5. package/assets/previews/customer-brief.png +0 -0
  6. package/assets/previews/profit-atlas.png +0 -0
  7. package/assets/previews/trend-workspace.png +0 -0
  8. package/dist/index.js +14 -1
  9. package/dist/lib/agent.schema.d.ts +43 -37
  10. package/dist/lib/agent.schema.js +2 -1
  11. package/dist/lib/app-config.d.ts +62 -0
  12. package/dist/lib/app-config.js +38 -0
  13. package/dist/lib/artifact-type.provider.d.ts +15 -0
  14. package/dist/lib/artifact-type.provider.js +78 -0
  15. package/dist/lib/assistant-template.d.ts +2 -0
  16. package/dist/lib/assistant-template.js +7 -0
  17. package/dist/lib/content.provider.js +13 -5
  18. package/dist/lib/dashboard-navigation.d.ts +58 -14
  19. package/dist/lib/dashboard-navigation.js +11 -10
  20. package/dist/lib/dashboard-view.schema.d.ts +197 -46
  21. package/dist/lib/dashboard-view.schema.js +8 -1
  22. package/dist/lib/dashboard.service.d.ts +39 -2
  23. package/dist/lib/dashboard.service.js +52 -28
  24. package/dist/lib/document-libraries.d.ts +1 -1
  25. package/dist/lib/document-libraries.js +3 -2
  26. package/dist/lib/document-network.d.ts +1 -1
  27. package/dist/lib/document-network.js +2 -6
  28. package/dist/lib/editing-feedback.d.ts +5 -0
  29. package/dist/lib/editing-feedback.js +19 -0
  30. package/dist/lib/editing-patch.d.ts +22 -0
  31. package/dist/lib/editing-patch.js +46 -0
  32. package/dist/lib/editing-session.d.ts +167 -0
  33. package/dist/lib/editing-session.js +53 -0
  34. package/dist/lib/editing.d.ts +92 -60
  35. package/dist/lib/editing.js +36 -24
  36. package/dist/lib/html-guidance.d.ts +1 -1
  37. package/dist/lib/html-guidance.js +1 -1
  38. package/dist/lib/middleware.js +48 -20
  39. package/dist/lib/monitor.d.ts +70 -0
  40. package/dist/lib/monitor.js +101 -0
  41. package/dist/lib/plugin.js +5 -1
  42. package/dist/lib/project.d.ts +106 -0
  43. package/dist/lib/project.js +17 -0
  44. package/dist/lib/projects.schema.d.ts +49 -0
  45. package/dist/lib/projects.schema.js +7 -0
  46. package/dist/lib/projects.service.d.ts +23 -0
  47. package/dist/lib/projects.service.js +65 -0
  48. package/dist/lib/prompt-workflows.d.ts +3 -2
  49. package/dist/lib/prompt-workflows.js +17 -3
  50. package/dist/lib/provider-options.controller.d.ts +10 -0
  51. package/dist/lib/provider-options.controller.js +49 -0
  52. package/dist/lib/resource-card.js +1 -1
  53. package/dist/lib/runtime-context.d.ts +3 -0
  54. package/dist/lib/runtime-context.js +13 -0
  55. package/dist/lib/save-evidence.d.ts +1 -1
  56. package/dist/lib/templates.d.ts +2 -0
  57. package/dist/lib/templates.js +55 -0
  58. package/dist/lib/view.provider.d.ts +97 -7
  59. package/dist/lib/view.provider.js +39 -10
  60. package/dist/libraries/TREND-WORKSPACE-ICONS-LICENSE.txt +43 -0
  61. package/dist/libraries/catalog.json +1 -1
  62. package/dist/remote/app.css +1 -1
  63. package/dist/remote/app.js +33 -33
  64. package/dist/remote/build-manifest.json +118 -32
  65. package/dist/sandbox-actions/action.json +1 -1
  66. package/dist/sandbox-actions/bundle/host.js +2 -2
  67. package/dist/sandbox-actions/bundle/resource-cache.mjs +2 -1
  68. package/dist/sandbox-actions/bundle/validate.mjs +1 -1
  69. package/docs/docs.json +7 -0
  70. package/docs/frontend-libraries.mdx +12 -4
  71. package/docs/html-mode.md +3 -3
  72. package/docs/quick-start-acceptance.mdx +50 -0
  73. package/docs/quick-start.mdx +64 -0
  74. package/docs/superstore.mdx +38 -0
  75. package/docs/team-sharing.mdx +6 -2
  76. package/docs/trend-workspace-acceptance.mdx +43 -0
  77. package/docs/trend-workspace.mdx +30 -0
  78. package/docs/verification.mdx +4 -2
  79. package/docs/visual-styles-acceptance.mdx +40 -0
  80. package/docs/visual-styles.mdx +46 -0
  81. package/package.json +25 -18
  82. package/scripts/import-prompts.mjs +108 -0
  83. package/skills/live-dashboard/SKILL.md +27 -3
  84. package/skills/live-dashboard/examples/README.md +25 -0
  85. package/skills/live-dashboard/examples/catalog.json +79 -0
  86. package/skills/live-dashboard/examples/production-quality/README.md +25 -0
  87. package/skills/live-dashboard/examples/production-quality/dashboard.html +129 -0
  88. package/skills/live-dashboard/examples/production-quality/definition.json +67 -0
  89. package/skills/live-dashboard/examples/production-quality/demo.csv +7 -0
  90. package/skills/live-dashboard/examples/production-quality/demo.json +44 -0
  91. package/skills/live-dashboard/examples/production-quality/expected.json +26 -0
  92. package/skills/live-dashboard/examples/production-quality/preview.png +0 -0
  93. package/skills/live-dashboard/examples/production-quality/query.json +53 -0
  94. package/skills/live-dashboard/examples/regional-sales/README.md +25 -0
  95. package/skills/live-dashboard/examples/regional-sales/dashboard-create.json +35 -0
  96. package/skills/live-dashboard/examples/regional-sales/definition.json +35 -0
  97. package/skills/live-dashboard/examples/regional-sales/demo.csv +5 -0
  98. package/skills/live-dashboard/examples/regional-sales/demo.json +18 -0
  99. package/skills/live-dashboard/examples/regional-sales/expected.json +19 -0
  100. package/skills/live-dashboard/examples/regional-sales/preview.png +0 -0
  101. package/skills/live-dashboard/examples/regional-sales/query.json +45 -0
  102. package/skills/live-dashboard/examples/sales-overview/README.md +25 -0
  103. package/skills/live-dashboard/examples/sales-overview/dashboard.html +129 -0
  104. package/skills/live-dashboard/examples/sales-overview/definition.json +57 -0
  105. package/skills/live-dashboard/examples/sales-overview/demo.csv +7 -0
  106. package/skills/live-dashboard/examples/sales-overview/demo.json +38 -0
  107. package/skills/live-dashboard/examples/sales-overview/expected.json +22 -0
  108. package/skills/live-dashboard/examples/sales-overview/preview.png +0 -0
  109. package/skills/live-dashboard/examples/sales-overview/query.json +52 -0
  110. package/skills/live-dashboard/examples/superstore/README.md +64 -0
  111. package/skills/live-dashboard/examples/superstore/THIRD_PARTY_LICENSE.txt +207 -0
  112. package/skills/live-dashboard/examples/superstore/build_data.py +155 -0
  113. package/skills/live-dashboard/examples/superstore/build_pages.py +129 -0
  114. package/skills/live-dashboard/examples/superstore/checks.json +104 -0
  115. package/skills/live-dashboard/examples/superstore/customers-mobile-dark.png +0 -0
  116. package/skills/live-dashboard/examples/superstore/customers-select.png +0 -0
  117. package/skills/live-dashboard/examples/superstore/customers.html +316 -0
  118. package/skills/live-dashboard/examples/superstore/customers.png +0 -0
  119. package/skills/live-dashboard/examples/superstore/customers.queries.json +512 -0
  120. package/skills/live-dashboard/examples/superstore/expected.json +4069 -0
  121. package/skills/live-dashboard/examples/superstore/import.json +200 -0
  122. package/skills/live-dashboard/examples/superstore/orders.csv +10195 -0
  123. package/skills/live-dashboard/examples/superstore/overview-mobile-dark.png +0 -0
  124. package/skills/live-dashboard/examples/superstore/overview-select.png +0 -0
  125. package/skills/live-dashboard/examples/superstore/overview.html +310 -0
  126. package/skills/live-dashboard/examples/superstore/overview.png +0 -0
  127. package/skills/live-dashboard/examples/superstore/overview.queries.json +636 -0
  128. package/skills/live-dashboard/examples/superstore/page.js +109 -0
  129. package/skills/live-dashboard/examples/superstore/profitability-mobile-dark.png +0 -0
  130. package/skills/live-dashboard/examples/superstore/profitability-select.png +0 -0
  131. package/skills/live-dashboard/examples/superstore/profitability.html +307 -0
  132. package/skills/live-dashboard/examples/superstore/profitability.png +0 -0
  133. package/skills/live-dashboard/examples/superstore/profitability.queries.json +334 -0
  134. package/skills/live-dashboard/examples/superstore/provenance.json +24 -0
  135. package/skills/live-dashboard/examples/superstore/semantic-model.json +220 -0
  136. package/skills/live-dashboard/examples/superstore/test_data.py +62 -0
  137. package/skills/live-dashboard/references/html-runtime.md +1 -1
  138. package/skills/live-dashboard/themes/README.md +50 -0
  139. package/skills/live-dashboard/themes/customer-brief/README.md +47 -0
  140. package/skills/live-dashboard/themes/customer-brief/charts.js +17 -0
  141. package/skills/live-dashboard/themes/customer-brief/layout.html +22 -0
  142. package/skills/live-dashboard/themes/customer-brief/theme.css +108 -0
  143. package/skills/live-dashboard/themes/profit-atlas/README.md +43 -0
  144. package/skills/live-dashboard/themes/profit-atlas/charts.js +30 -0
  145. package/skills/live-dashboard/themes/profit-atlas/layout.html +19 -0
  146. package/skills/live-dashboard/themes/profit-atlas/theme.css +75 -0
  147. package/skills/live-dashboard/themes/trend-workspace/ICON_LICENSE.txt +43 -0
  148. package/skills/live-dashboard/themes/trend-workspace/README.md +144 -0
  149. package/skills/live-dashboard/themes/trend-workspace/charts.js +60 -0
  150. package/skills/live-dashboard/themes/trend-workspace/icons.js +11 -0
  151. package/skills/live-dashboard/themes/trend-workspace/layout.html +45 -0
  152. package/skills/live-dashboard/themes/trend-workspace/select.js +127 -0
  153. package/skills/live-dashboard/themes/trend-workspace/theme.css +137 -0
@@ -0,0 +1,53 @@
1
+ {
2
+ "payloadFields": {
3
+ "select": [
4
+ "date",
5
+ "line",
6
+ "produced",
7
+ "good",
8
+ "defects"
9
+ ],
10
+ "filters": [
11
+ {
12
+ "column": "line",
13
+ "operator": "eq",
14
+ "value": {
15
+ "type": "parameter",
16
+ "name": "line"
17
+ },
18
+ "skipWhenNull": true
19
+ }
20
+ ],
21
+ "orderBy": [
22
+ {
23
+ "column": "date",
24
+ "direction": "asc"
25
+ },
26
+ {
27
+ "column": "line",
28
+ "direction": "asc"
29
+ }
30
+ ]
31
+ },
32
+ "parameters": [
33
+ {
34
+ "name": "line",
35
+ "controlId": "line",
36
+ "type": "string",
37
+ "required": false,
38
+ "defaultValue": null
39
+ }
40
+ ],
41
+ "grain": [
42
+ "date",
43
+ "line"
44
+ ],
45
+ "controls": [
46
+ {
47
+ "controlId": "line",
48
+ "label": "产线",
49
+ "type": "string",
50
+ "defaultValue": null
51
+ }
52
+ ]
53
+ }
@@ -0,0 +1,25 @@
1
+ # 区域销售 / regional-sales — v1
2
+
3
+ 这是可复制的制作配方,演示数据是人为构造的,不代表任何客户或业务。`preview.png` 是这份演示数据的页面预览。
4
+
5
+ ## 数据与口径
6
+
7
+ - 粒度:`region`,同一粒度不重复。金额单位为整元 CNY;数量为非负整数。
8
+ - 筛选:`region`,默认 `null` 表示全部;不要用字符串 `"null"`。
9
+ - 金额可按互斥粒度求和;订单数同一粒度不可重复计算。
10
+ - `definition.json` 给出列类型和业务说明;`query.json` 给出查询字段、参数和控件;`expected.json` 给出验收结果。
11
+
12
+ ## 用演示数据制作
13
+
14
+ 1. 读取主技能及 `../README.md`。确认当前 ChatKit 项目已选定,用户明确要求演示数据。
15
+ 2. 读取 `demo.csv`,以 SandboxFile 工具写入当前项目的 `live-dashboard-examples/regional-sales/demo-v1.csv`。如果已存在,先读内容;不同则使用新文件名,不覆盖。`demo.json` 是同一数据的替代输入,使用时 reader 应为 `{"format":"json","rowsPath":[]}`,列 path 保持相同。
16
+ 3. 按共享制作流程注册实际文件并取得当前 source / discovery / bindingPayload;不要把文件路径放进 Binding payload。
17
+ 4. 基于 `query.json`,使用发现结果的 datasetId、revision 和 contractHash 构造查询。预览全部、至少两个筛选值,逐项比对 `expected.json`。输出字段 id 必须与本样例列 key 一致;logicalType/nullable/unit 必须与发现结果一致。
18
+ 5. 读取 `dashboard-create.json`,将 previewIds 替换为本次预览凭证,图表/table 的 datasetId 替换为预览返回值,再调用 dashboard_create。
19
+ 6. 检查成功保存回执,报告看板及草稿修订;打开看板核对筛选、刷新与项目归属。保存失败不得宣布完成,发布仍需人工确认。
20
+
21
+ ## 换成真实数据
22
+
23
+ 先使用已授权来源发现与真实预览,将业务字段映射到本配方列(或同步改写页面字段和控件)。确认粒度、单位、日期和指标口径。缺少必要字段时说明缺口,不插入演示行、不补零、不复制旧 source/preview/Artifact ID。移除演示标识仅限绑定和所有可见数值均来自已验证真实来源之后。
24
+
25
+ 配方更新不迁移或覆盖已创建的看板。编辑现有看板必须先 dashboard_open,保持 Artifact、组件 ID 和预期修订。
@@ -0,0 +1,35 @@
1
+ {
2
+ "title": "区域销售 · 演示数据",
3
+ "previewIds": [
4
+ "<current-preview-id>"
5
+ ],
6
+ "controls": [
7
+ {
8
+ "controlId": "region",
9
+ "label": "区域",
10
+ "type": "string",
11
+ "defaultValue": null
12
+ }
13
+ ],
14
+ "charts": [
15
+ {
16
+ "id": "region-sales",
17
+ "title": "区域销售额 · CNY",
18
+ "type": "bar",
19
+ "datasetId": "<preview-dataset-id>",
20
+ "x": "region",
21
+ "y": "amount"
22
+ }
23
+ ],
24
+ "tables": [
25
+ {
26
+ "id": "sales-details",
27
+ "title": "销售明细 · 演示数据",
28
+ "datasetId": "<preview-dataset-id>",
29
+ "fields": [
30
+ "region",
31
+ "amount"
32
+ ]
33
+ }
34
+ ]
35
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "path": "live-dashboard-examples/regional-sales/demo-v1.csv",
3
+ "name": "区域销售 · 演示数据 v1",
4
+ "description": "Synthetic example data; not business results. Rows have disjoint grain; amount is whole CNY and orders are counts.",
5
+ "reader": {
6
+ "format": "csv",
7
+ "delimiter": ",",
8
+ "headerRow": 1
9
+ },
10
+ "columns": [
11
+ {
12
+ "key": "region",
13
+ "path": [
14
+ "region"
15
+ ],
16
+ "label": "区域",
17
+ "description": "Synthetic demonstration only. Unique daily dimension grain.",
18
+ "logicalType": "string",
19
+ "nullable": false,
20
+ "role": "dimension"
21
+ },
22
+ {
23
+ "key": "amount",
24
+ "path": [
25
+ "amount"
26
+ ],
27
+ "label": "销售额",
28
+ "description": "Synthetic demonstration only. Whole CNY, no fractional currency.",
29
+ "logicalType": "int64",
30
+ "nullable": false,
31
+ "role": "measure",
32
+ "unit": "CNY"
33
+ }
34
+ ]
35
+ }
@@ -0,0 +1,5 @@
1
+ region,amount
2
+ East,12000
3
+ West,18000
4
+ North,9000
5
+ South,11000
@@ -0,0 +1,18 @@
1
+ [
2
+ {
3
+ "region": "East",
4
+ "amount": 12000
5
+ },
6
+ {
7
+ "region": "West",
8
+ "amount": 18000
9
+ },
10
+ {
11
+ "region": "North",
12
+ "amount": 9000
13
+ },
14
+ {
15
+ "region": "South",
16
+ "amount": 11000
17
+ }
18
+ ]
@@ -0,0 +1,19 @@
1
+ {
2
+ "all": {
3
+ "amount": 50000
4
+ },
5
+ "filtered": {
6
+ "East": {
7
+ "amount": 12000
8
+ },
9
+ "West": {
10
+ "amount": 18000
11
+ }
12
+ },
13
+ "checks": [
14
+ "All \u2192 first option \u2192 second option \u2192 all; chart, KPI and detail agree.",
15
+ "Refresh uses the current filter.",
16
+ "Empty results clear charts and show no-data; errors never show old totals as current.",
17
+ "Saved draft remains unpublished and belongs to current Project."
18
+ ]
19
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "payloadFields": {
3
+ "select": [
4
+ "region",
5
+ "amount"
6
+ ],
7
+ "filters": [
8
+ {
9
+ "column": "region",
10
+ "operator": "eq",
11
+ "value": {
12
+ "type": "parameter",
13
+ "name": "region"
14
+ },
15
+ "skipWhenNull": true
16
+ }
17
+ ],
18
+ "orderBy": [
19
+ {
20
+ "column": "region",
21
+ "direction": "asc"
22
+ }
23
+ ]
24
+ },
25
+ "parameters": [
26
+ {
27
+ "name": "region",
28
+ "controlId": "region",
29
+ "type": "string",
30
+ "required": false,
31
+ "defaultValue": null
32
+ }
33
+ ],
34
+ "grain": [
35
+ "region"
36
+ ],
37
+ "controls": [
38
+ {
39
+ "controlId": "region",
40
+ "label": "区域",
41
+ "type": "string",
42
+ "defaultValue": null
43
+ }
44
+ ]
45
+ }
@@ -0,0 +1,25 @@
1
+ # 销售总览 / sales-overview — v1
2
+
3
+ 这是可复制的制作配方,演示数据是人为构造的,不代表任何客户或业务。`preview.png` 是这份演示数据的页面预览。
4
+
5
+ ## 数据与口径
6
+
7
+ - 粒度:`date, region`,同一粒度不重复。金额单位为整元 CNY;数量为非负整数。
8
+ - 筛选:`region`,默认 `null` 表示全部;不要用字符串 `"null"`。
9
+ - 金额可按互斥粒度求和;订单数同一粒度不可重复计算。
10
+ - `definition.json` 给出列类型和业务说明;`query.json` 给出查询字段、参数和控件;`expected.json` 给出验收结果。
11
+
12
+ ## 用演示数据制作
13
+
14
+ 1. 读取主技能及 `../README.md`。确认当前 ChatKit 项目已选定,用户明确要求演示数据。
15
+ 2. 读取 `demo.csv`,以 SandboxFile 工具写入当前项目的 `live-dashboard-examples/sales-overview/demo-v1.csv`。如果已存在,先读内容;不同则使用新文件名,不覆盖。`demo.json` 是同一数据的替代输入,使用时 reader 应为 `{"format":"json","rowsPath":[]}`,列 path 保持相同。
16
+ 3. 按共享制作流程注册实际文件并取得当前 source / discovery / bindingPayload;不要把文件路径放进 Binding payload。
17
+ 4. 基于 `query.json`,使用发现结果的 datasetId、revision 和 contractHash 构造查询。预览全部、至少两个筛选值,逐项比对 `expected.json`。输出字段 id 必须与本样例列 key 一致;logicalType/nullable/unit 必须与发现结果一致。
18
+ 5. 读取 `dashboard.html`,仅使用本次唯一的完整 Dataset Binding。保留注释中的数据与生命周期约束。调用 dashboard_libraries,写入当前项目文件,再使用本次 previewIds、query.json 的 controls 和 libraries:["echarts"] 调用 dashboard_create_html。
19
+ 6. 检查成功保存回执,报告看板及草稿修订,结束制作;不要为了确认保存而再调用 dashboard_open/update。用户通过回执卡片打开看板核对筛选、刷新与项目归属。保存失败不得宣布完成,发布仍需人工确认。
20
+
21
+ ## 换成真实数据
22
+
23
+ 先使用已授权来源发现与真实预览,将业务字段映射到本配方列(或同步改写页面字段和控件)。确认粒度、单位、日期和指标口径。缺少必要字段时说明缺口,不插入演示行、不补零、不复制旧 source/preview/Artifact ID。移除演示标识仅限绑定和所有可见数值均来自已验证真实来源之后。
24
+
25
+ 配方更新不迁移或覆盖已创建的看板。编辑现有看板必须先 dashboard_open,保持 Artifact、组件 ID 和预期修订。
@@ -0,0 +1,129 @@
1
+ <!doctype html>
2
+ <html lang="zh-Hans">
3
+ <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>销售总览 · 演示数据</title>
4
+ <style>
5
+ :root{color-scheme:light;--bg:#f5f7fa;--paper:#fff;--ink:#172d42;--muted:#526579;--line:#d8e0e8;--accent:#22649a;--second:#16847d}
6
+ *{box-sizing:border-box}
7
+ body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.5 system-ui,"PingFang SC",sans-serif}
8
+ body.dark{color-scheme:dark;--bg:#142433;--paper:#1c3042;--ink:#ecf2f8;--muted:#b0c2d1;--line:#3b5266;--accent:#75b7ee;--second:#65cabc}
9
+ main{max-width:1320px;margin:auto;padding:28px}
10
+ header{display:flex;justify-content:space-between;align-items:flex-start;gap:20px;border-bottom:1px solid var(--line);padding-bottom:20px}
11
+ h1{font-size:26px;line-height:1.3;margin:4px 0 8px}
12
+ h2{font-size:17px;margin:0 0 8px}
13
+ p{margin:0}
14
+ .muted{color:var(--muted)}
15
+ .demo{display:inline-block;border:1px solid var(--line);border-radius:4px;padding:2px 7px;font-size:12px}
16
+ .filter{display:grid;gap:6px;min-width:160px}
17
+ select{font:inherit;color:var(--ink);background:var(--paper);border:1px solid var(--line);border-radius:6px;padding:9px}
18
+ select:focus-visible{outline:2px solid var(--accent);outline-offset:3px}
19
+ .metrics{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:24px;padding:25px 0}
20
+ .metric{border-left:3px solid var(--accent);padding-left:16px}
21
+ .metric p{color:var(--muted)}
22
+ .metric output{font-size:32px;font-weight:650;font-variant-numeric:tabular-nums;display:block}
23
+ .plots{display:grid;grid-template-columns:3fr 2fr;gap:20px}
24
+ .panel{min-width:0;background:var(--paper);padding:20px;border:1px solid var(--line);border-radius:8px}
25
+ .chart{height:300px}
26
+ .details{margin-top:20px;overflow:auto}
27
+ table{width:100%;border-collapse:collapse;white-space:nowrap;font-variant-numeric:tabular-nums}
28
+ caption{text-align:left;font-size:17px;font-weight:600;margin-bottom:10px}
29
+ th,td{padding:10px 12px;border-bottom:1px solid var(--line);text-align:right}
30
+ th:first-child,td:first-child{text-align:left}
31
+ th{color:var(--muted);font-weight:500}
32
+ #feedback{min-height:28px;margin-top:16px}
33
+ footer{display:flex;justify-content:space-between;gap:16px;margin-top:20px;font-size:12px;color:var(--muted)}
34
+ @media(max-width:700px){main{padding:16px}
35
+ header{flex-direction:column}
36
+ .filter{width:100%}
37
+ .metrics{gap:12px}
38
+ .metric{padding-left:8px}
39
+ .metric output{font-size:24px}
40
+ .plots{grid-template-columns:1fr}
41
+ .panel{padding:14px}
42
+ footer{flex-direction:column}
43
+ }
44
+ @media(prefers-reduced-motion:reduce){*{scroll-behavior:auto}
45
+ }
46
+
47
+ </style></head>
48
+ <body><main>
49
+ <header><div><span class="demo">演示数据 · 人为构造</span><h1>销售总览</h1><p class="muted">从每日销售趋势追踪区域表现</p></div><label class="filter" for="scope">区域<select id="scope" data-dashboard-control="region" disabled></select></label></header>
50
+ <section class="metrics" aria-label="主要指标"><div class="metric"><p>销售额(CNY)</p><output id="amount">—</output></div><div class="metric"><p>订单数</p><output id="orders">—</output></div><div class="metric"><p>平均订单金额(CNY)</p><output id="average">—</output></div></section>
51
+ <div class="plots"><section class="panel"><h2>每日销售趋势</h2><p class="muted">销售额(CNY)</p><div id="trend" class="chart" role="img" aria-label="每日销售趋势,同一数据详见下表"></div></section><section class="panel"><h2>区域销售对比</h2><p class="muted">销售额(CNY)</p><div id="breakdown" class="chart" role="img" aria-label="区域销售对比,同一数据详见下表"></div></section></div>
52
+ <p id="feedback" role="status">正在查询…</p>
53
+ <section class="panel details"><table><caption>数据明细</caption><thead><tr><th scope="col">日期</th><th scope="col">区域</th><th scope="col">销售额(CNY)</th><th scope="col">订单数</th></tr></thead><tbody id="rows"></tbody></table></section>
54
+ <footer><span>金额为整元 CNY;演示日期为 2026 年 1 月 1 日至 3 日。</span><span id="freshness">尚未取得查询结果</span></footer>
55
+ </main><script>
56
+ // Example v1. One complete Dataset drives every panel. Adapt explicit field IDs after real preview.
57
+ // No business rows, source IDs, query text or credentials belong in this HTML.
58
+ (async () => {
59
+ const live = window.xpertLiveArtifact;
60
+ const filter = document.getElementById('scope');
61
+ const feedback = document.getElementById('feedback');
62
+ const fields = ["date", "region", "amount", "orders"];
63
+ const measures = ["amount", "orders"];
64
+ const fmt = new Intl.NumberFormat('zh-CN', {maximumFractionDigits: 2});
65
+ let trend, breakdown;
66
+ function clear(message) {
67
+ document.querySelectorAll('output').forEach(el => { el.textContent = '—'; });
68
+ document.getElementById('rows').replaceChildren();
69
+ trend?.clear(); breakdown?.clear();
70
+ feedback.textContent = message;
71
+ document.getElementById('freshness').textContent = '当前结果不可用';
72
+ }
73
+ function total(rows, field) {
74
+ const values = rows.map(row => {
75
+ const raw = row[field];
76
+ if (typeof raw !== 'string' && typeof raw !== 'number' || !/^\d+$/.test(String(raw))) throw Error('计数字段或金额单位不符合样例口径');
77
+ const n = Number(raw); if (!Number.isSafeInteger(n)) throw Error('数值超过本样例安全整数范围');
78
+ return n;
79
+ });
80
+ const sum = values.reduce((a,b) => a+b,0);
81
+ if (!Number.isSafeInteger(sum)) throw Error('合计超过本样例安全整数范围');
82
+ return sum;
83
+ }
84
+ function group(rows, dimension, measure) {
85
+ const groups = new Map();
86
+ for (const row of rows) {
87
+ if (typeof row[dimension] !== 'string') throw Error('维度字段缺失');
88
+ const key = row[dimension]; if (!groups.has(key)) groups.set(key,[]); groups.get(key).push(row);
89
+ }
90
+ return [...groups].sort(([a],[b])=>a.localeCompare(b)).map(([key,items])=>[key,total(items,measure)]);
91
+ }
92
+ function render(dataset) {
93
+ try {
94
+ if (!dataset.completeness || dataset.completeness.truncated) { clear('结果不完整,请缩小范围后重试;当前不显示汇总值。'); return; }
95
+ const rows = dataset.rows;
96
+ if (!rows.length) { clear('此筛选下没有数据。'); return; }
97
+ for (const row of rows) for (const field of fields) if (row[field] === null || row[field] === undefined) throw Error('必要字段为空,无法展示完整指标');
98
+ const totals = Object.fromEntries(measures.map(field => [field,total(rows,field)]));
99
+ document.getElementById('amount').textContent=fmt.format(totals.amount);document.getElementById('orders').textContent=fmt.format(totals.orders);document.getElementById('average').textContent=totals.orders ? fmt.format(totals.amount/totals.orders) : '—';
100
+ const daily = group(rows,'date','amount');
101
+ const groups = group(rows,'region','amount');
102
+ const color = getComputedStyle(document.body).getPropertyValue('--ink').trim();
103
+ const line = getComputedStyle(document.body).getPropertyValue('--line').trim();
104
+ const axis = {axisLabel:{color},axisLine:{lineStyle:{color:line}},splitLine:{lineStyle:{color:line}}};
105
+ trend.setOption({animation:false,aria:{enabled:true},color:['#3984bd'],grid:{left:60,right:18,top:28,bottom:35},tooltip:{trigger:'axis'},xAxis:{...axis,type:'category',data:daily.map(([k])=>k)},yAxis:{...axis,type:'value'},series:[{id:'daily',name:'销售额(CNY)',type:'line',symbolSize:7,areaStyle:{opacity:0.08},data:daily.map(([,v])=>v)}]},{notMerge:true});
106
+ breakdown.setOption({animation:false,aria:{enabled:true},color:['#16847d'],grid:{left:65,right:30,top:20,bottom:35},tooltip:{trigger:'axis'},xAxis:{...axis,type:'value'},yAxis:{...axis,type:'category',data:groups.map(([k])=>k)},series:[{id:'breakdown',name:'销售额(CNY)',type:'bar',barMaxWidth:38,label:{show:true,position:'right',color},data:groups.map(([,v])=>v)}]},{notMerge:true});
107
+ document.getElementById('rows').replaceChildren(...rows.map(row => { const tr=document.createElement('tr'); for(const field of fields){const td=document.createElement('td');td.textContent=String(row[field]);tr.append(td);}return tr;}));
108
+ feedback.textContent = '已显示 '+ rows.length +' 行,所有图表与指标使用同一筛选结果。';
109
+ document.getElementById('freshness').textContent = '查询时间:'+(dataset.freshness?.sourceFetchedAt ?? '未提供');
110
+ } catch (error) { clear(error.message || '数据无法展示'); }
111
+ }
112
+ try {
113
+ const context = await live.ready();
114
+ document.body.classList.toggle('dark', context.theme === 'dark');
115
+ if (context.bindingIds.length !== 1) throw Error('本样例要求一个已验证的完整数据集');
116
+ const bindingId = context.bindingIds[0];
117
+ trend=window.echarts.init(document.getElementById('trend'),null,{renderer:'svg'});
118
+ breakdown=window.echarts.init(document.getElementById('breakdown'),null,{renderer:'svg'});
119
+ new ResizeObserver(()=>{trend.resize();breakdown.resize();}).observe(document.querySelector('.plots'));
120
+ // Subscribe before bindSelect/query so toolbar refresh uses this same render function.
121
+ live.subscribe(bindingId,render);
122
+ filter.addEventListener('change',()=>clear('正在查询…'));
123
+ filter.addEventListener('xpert-query-error',()=>clear('查询失败,请检查访问权限或重试。'));
124
+ live.bindSelect(filter,{bindingId,controlId:'region',fieldId:'region',allLabel:'全部'});
125
+ await live.query(bindingId,{...context.defaults});
126
+ filter.disabled=false;
127
+ } catch(error) { clear(error.message || '查询失败,请重试'); }
128
+ })();
129
+ </script></body></html>
@@ -0,0 +1,57 @@
1
+ {
2
+ "path": "live-dashboard-examples/sales-overview/demo-v1.csv",
3
+ "name": "销售总览 · 演示数据 v1",
4
+ "description": "Synthetic example data; not business results. Rows have disjoint grain; amount is whole CNY and orders are counts.",
5
+ "reader": {
6
+ "format": "csv",
7
+ "delimiter": ",",
8
+ "headerRow": 1
9
+ },
10
+ "columns": [
11
+ {
12
+ "key": "date",
13
+ "path": [
14
+ "date"
15
+ ],
16
+ "label": "日期",
17
+ "description": "Synthetic demonstration only. Unique daily dimension grain.",
18
+ "logicalType": "date",
19
+ "nullable": false,
20
+ "role": "time"
21
+ },
22
+ {
23
+ "key": "region",
24
+ "path": [
25
+ "region"
26
+ ],
27
+ "label": "区域",
28
+ "description": "Synthetic demonstration only. Unique daily dimension grain.",
29
+ "logicalType": "string",
30
+ "nullable": false,
31
+ "role": "dimension"
32
+ },
33
+ {
34
+ "key": "amount",
35
+ "path": [
36
+ "amount"
37
+ ],
38
+ "label": "销售额",
39
+ "description": "Synthetic demonstration only. Whole CNY, no fractional currency.",
40
+ "logicalType": "int64",
41
+ "nullable": false,
42
+ "role": "measure",
43
+ "unit": "CNY"
44
+ },
45
+ {
46
+ "key": "orders",
47
+ "path": [
48
+ "orders"
49
+ ],
50
+ "label": "订单数",
51
+ "description": "Synthetic demonstration only. Nonnegative whole-item count.",
52
+ "logicalType": "int64",
53
+ "nullable": false,
54
+ "role": "measure"
55
+ }
56
+ ]
57
+ }
@@ -0,0 +1,7 @@
1
+ date,region,amount,orders
2
+ 2026-01-01,East,12000,60
3
+ 2026-01-01,West,8000,40
4
+ 2026-01-02,East,15000,75
5
+ 2026-01-02,West,11000,55
6
+ 2026-01-03,East,17000,85
7
+ 2026-01-03,West,13000,65
@@ -0,0 +1,38 @@
1
+ [
2
+ {
3
+ "date": "2026-01-01",
4
+ "region": "East",
5
+ "amount": 12000,
6
+ "orders": 60
7
+ },
8
+ {
9
+ "date": "2026-01-01",
10
+ "region": "West",
11
+ "amount": 8000,
12
+ "orders": 40
13
+ },
14
+ {
15
+ "date": "2026-01-02",
16
+ "region": "East",
17
+ "amount": 15000,
18
+ "orders": 75
19
+ },
20
+ {
21
+ "date": "2026-01-02",
22
+ "region": "West",
23
+ "amount": 11000,
24
+ "orders": 55
25
+ },
26
+ {
27
+ "date": "2026-01-03",
28
+ "region": "East",
29
+ "amount": 17000,
30
+ "orders": 85
31
+ },
32
+ {
33
+ "date": "2026-01-03",
34
+ "region": "West",
35
+ "amount": 13000,
36
+ "orders": 65
37
+ }
38
+ ]
@@ -0,0 +1,22 @@
1
+ {
2
+ "all": {
3
+ "amount": 76000,
4
+ "orders": 380
5
+ },
6
+ "filtered": {
7
+ "East": {
8
+ "amount": 44000,
9
+ "orders": 220
10
+ },
11
+ "West": {
12
+ "amount": 32000,
13
+ "orders": 160
14
+ }
15
+ },
16
+ "checks": [
17
+ "All \u2192 first option \u2192 second option \u2192 all; chart, KPI and detail agree.",
18
+ "Refresh uses the current filter.",
19
+ "Empty results clear charts and show no-data; errors never show old totals as current.",
20
+ "Saved draft remains unpublished and belongs to current Project."
21
+ ]
22
+ }
@@ -0,0 +1,52 @@
1
+ {
2
+ "payloadFields": {
3
+ "select": [
4
+ "date",
5
+ "region",
6
+ "amount",
7
+ "orders"
8
+ ],
9
+ "filters": [
10
+ {
11
+ "column": "region",
12
+ "operator": "eq",
13
+ "value": {
14
+ "type": "parameter",
15
+ "name": "region"
16
+ },
17
+ "skipWhenNull": true
18
+ }
19
+ ],
20
+ "orderBy": [
21
+ {
22
+ "column": "date",
23
+ "direction": "asc"
24
+ },
25
+ {
26
+ "column": "region",
27
+ "direction": "asc"
28
+ }
29
+ ]
30
+ },
31
+ "parameters": [
32
+ {
33
+ "name": "region",
34
+ "controlId": "region",
35
+ "type": "string",
36
+ "required": false,
37
+ "defaultValue": null
38
+ }
39
+ ],
40
+ "grain": [
41
+ "date",
42
+ "region"
43
+ ],
44
+ "controls": [
45
+ {
46
+ "controlId": "region",
47
+ "label": "区域",
48
+ "type": "string",
49
+ "defaultValue": null
50
+ }
51
+ ]
52
+ }