@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.
- package/CHANGELOG.md +44 -0
- package/README.md +85 -0
- package/bin/run.js +87 -0
- package/binaries/guanvis-darwin-arm64 +0 -0
- package/binaries/guanvis-darwin-x64 +0 -0
- package/binaries/guanvis-linux-arm64 +0 -0
- package/binaries/guanvis-linux-x64 +0 -0
- package/binaries/guanvis-win32-x64.exe +0 -0
- package/package.json +41 -0
- package/skills/guanvis/SKILL.md +334 -0
- package/skills/guanvis/evals/china_map/card_01_basic_map.js +13 -0
- package/skills/guanvis/evals/china_map/card_02_world_map.js +10 -0
- package/skills/guanvis/evals/china_map/card_03_point_map.js +12 -0
- package/skills/guanvis/evals/china_map/page.js +10 -0
- package/skills/guanvis/evals/china_map/schema.js +10 -0
- package/skills/guanvis/evals/comparative_mvp_roundtrip/README.md +30 -0
- package/skills/guanvis/evals/comparative_mvp_roundtrip/card_01_comparative_mvp.js +72 -0
- package/skills/guanvis/evals/comparative_mvp_roundtrip/page.js +6 -0
- package/skills/guanvis/evals/comparative_mvp_roundtrip/schema.js +22 -0
- package/skills/guanvis/evals/comparative_mvp_roundtrip/verify_default_roundtrip.sh +92 -0
- package/skills/guanvis/evals/custom_chart_echarts/card_01_bar.js +13 -0
- package/skills/guanvis/evals/custom_chart_echarts/card_02_pie.js +13 -0
- package/skills/guanvis/evals/custom_chart_echarts/charts/bar.js +11 -0
- package/skills/guanvis/evals/custom_chart_echarts/charts/pie.js +15 -0
- package/skills/guanvis/evals/custom_chart_echarts/page.js +5 -0
- package/skills/guanvis/evals/custom_chart_echarts/schema.js +9 -0
- package/skills/guanvis/evals/percentage_advcalc_roundtrip/README.md +26 -0
- package/skills/guanvis/evals/percentage_advcalc_roundtrip/card_01_percentage_advcalc.js +44 -0
- package/skills/guanvis/evals/percentage_advcalc_roundtrip/card_02_scroll_percentage.js +12 -0
- package/skills/guanvis/evals/percentage_advcalc_roundtrip/page.js +6 -0
- package/skills/guanvis/evals/percentage_advcalc_roundtrip/schema.js +22 -0
- package/skills/guanvis/evals/rank_advcalc_roundtrip/README.md +28 -0
- package/skills/guanvis/evals/rank_advcalc_roundtrip/card_01_rank_advcalc.js +59 -0
- package/skills/guanvis/evals/rank_advcalc_roundtrip/page.js +6 -0
- package/skills/guanvis/evals/rank_advcalc_roundtrip/schema.js +22 -0
- package/skills/guanvis/evals/rank_advcalc_roundtrip/verify_default_roundtrip.sh +135 -0
- package/skills/guanvis/evals/running_total_advcalc_roundtrip/README.md +15 -0
- package/skills/guanvis/evals/running_total_advcalc_roundtrip/card_01_running_total_advcalc.js +74 -0
- package/skills/guanvis/evals/running_total_advcalc_roundtrip/page.js +6 -0
- package/skills/guanvis/evals/running_total_advcalc_roundtrip/schema.js +22 -0
- package/skills/guanvis/evals/sales_dashboard/card_01_revenue_column.js +10 -0
- package/skills/guanvis/evals/sales_dashboard/card_02_trend_line.js +9 -0
- package/skills/guanvis/evals/sales_dashboard/card_03_kpi.js +6 -0
- package/skills/guanvis/evals/sales_dashboard/card_04_pie.js +9 -0
- package/skills/guanvis/evals/sales_dashboard/card_05_calc_field_test.js +12 -0
- package/skills/guanvis/evals/sales_dashboard/page.js +11 -0
- package/skills/guanvis/evals/sales_dashboard/schema.js +14 -0
- package/skills/guanvis/evals/sales_dashboard/selector_01_region.js +10 -0
- package/skills/guanvis/references/api-reference.md +408 -0
- package/skills/guanvis/references/builder-reference.md +690 -0
- package/skills/guanvis/references/publish-and-constraints.md +46 -0
- package/skills/guanvis/references/tableau-migration.md +73 -0
- package/skills/guanvis/references/theme.md +115 -0
- package/skills/guanvis/references/troubleshooting.md +20 -0
- package/skills/guanvis/references/validation-and-chart-patterns.md +205 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
5
|
+
EVAL_DIR="$ROOT_DIR/evals/rank_advcalc_roundtrip"
|
|
6
|
+
OUT_DIR="$EVAL_DIR/.roundtrip"
|
|
7
|
+
|
|
8
|
+
mkdir -p "$OUT_DIR"
|
|
9
|
+
|
|
10
|
+
cd "$ROOT_DIR"
|
|
11
|
+
|
|
12
|
+
export GUANCLI_PROFILE=default
|
|
13
|
+
|
|
14
|
+
go run ./cmd/guanvis preview "$EVAL_DIR" > "$OUT_DIR/preview_payload.json"
|
|
15
|
+
go run ./cmd/guanvis publish "$EVAL_DIR" > "$OUT_DIR/publish.log"
|
|
16
|
+
|
|
17
|
+
guancli card get r3f01a02b03c04d05e06f07a --profile default --raw > "$OUT_DIR/raw_range.json"
|
|
18
|
+
guancli card get r4f01a02b03c04d05e06f08a --profile default --raw > "$OUT_DIR/raw_topn.json"
|
|
19
|
+
guancli card preview r3f01a02b03c04d05e06f07a --profile default --limit 50 -f json > "$OUT_DIR/preview_range.json"
|
|
20
|
+
guancli card preview r4f01a02b03c04d05e06f08a --profile default --limit 50 -f json > "$OUT_DIR/preview_topn.json"
|
|
21
|
+
|
|
22
|
+
python3 - "$OUT_DIR" <<'PY'
|
|
23
|
+
import json
|
|
24
|
+
import sys
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
out = Path(sys.argv[1])
|
|
28
|
+
|
|
29
|
+
cases = [
|
|
30
|
+
{
|
|
31
|
+
"kind": "range",
|
|
32
|
+
"raw": out / "raw_range.json",
|
|
33
|
+
"preview": out / "preview_range.json",
|
|
34
|
+
"expected": {
|
|
35
|
+
"按列RANK降序": {
|
|
36
|
+
"calDimension": "COLUMN",
|
|
37
|
+
"rankType": "RANK",
|
|
38
|
+
"sorting": "desc",
|
|
39
|
+
"tfdId": "",
|
|
40
|
+
"topN": {"topType": "ALL", "count": 10, "calculationType": "COUNT"},
|
|
41
|
+
},
|
|
42
|
+
"按行DENSE升序": {
|
|
43
|
+
"calDimension": "ROW",
|
|
44
|
+
"rankType": "DENSE_RANK",
|
|
45
|
+
"sorting": "asc",
|
|
46
|
+
"tfdId": "",
|
|
47
|
+
"topN": {"topType": "ALL", "count": 10, "calculationType": "COUNT"},
|
|
48
|
+
},
|
|
49
|
+
"地区组内ROW_NUMBER降序": {
|
|
50
|
+
"calDimension": "FIELD",
|
|
51
|
+
"rankType": "ROW_NUMBER",
|
|
52
|
+
"sorting": "desc",
|
|
53
|
+
"tfdId": "xe01f39825ccc428eaf67911",
|
|
54
|
+
"topN": {"topType": "ALL", "count": 10, "calculationType": "COUNT"},
|
|
55
|
+
},
|
|
56
|
+
"类别维度项RANK升序": {
|
|
57
|
+
"calDimension": "DIMENSION_ITEM",
|
|
58
|
+
"rankType": "RANK",
|
|
59
|
+
"sorting": "asc",
|
|
60
|
+
"tfdId": "cece9879e99f545449909a82",
|
|
61
|
+
"topN": {"topType": "ALL", "count": 10, "calculationType": "COUNT"},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"kind": "topn",
|
|
67
|
+
"raw": out / "raw_topn.json",
|
|
68
|
+
"preview": out / "preview_topn.json",
|
|
69
|
+
"expected": {
|
|
70
|
+
"Top3适用小计": {
|
|
71
|
+
"calDimension": "COLUMN",
|
|
72
|
+
"rankType": "RANK",
|
|
73
|
+
"sorting": "desc",
|
|
74
|
+
"tfdId": "",
|
|
75
|
+
"topN": {"topType": "CUSTOM_NUMBER", "count": 3, "calculationType": "COUNT", "applyToSubtotal": True},
|
|
76
|
+
},
|
|
77
|
+
"Top5不适用小计": {
|
|
78
|
+
"calDimension": "DIMENSION_ITEM",
|
|
79
|
+
"rankType": "DENSE_RANK",
|
|
80
|
+
"sorting": "desc",
|
|
81
|
+
"tfdId": "xe01f39825ccc428eaf67911",
|
|
82
|
+
"topN": {"topType": "CUSTOM_NUMBER", "count": 5, "calculationType": "COUNT", "applyToSubtotal": False},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
summary = {"cards": []}
|
|
89
|
+
|
|
90
|
+
for case in cases:
|
|
91
|
+
raw_obj = json.loads(case["raw"].read_text())
|
|
92
|
+
response = raw_obj.get("response") or raw_obj.get("data") or raw_obj
|
|
93
|
+
zone_data = response["content"]["meta"]["chartMain"]["zoneData"]
|
|
94
|
+
metrics = zone_data.get("metric", [])
|
|
95
|
+
by_alias = {m.get("alias"): m for m in metrics if m.get("alias")}
|
|
96
|
+
rows = json.loads(case["preview"].read_text())
|
|
97
|
+
|
|
98
|
+
card_summary = {
|
|
99
|
+
"kind": case["kind"],
|
|
100
|
+
"cardId": response.get("cdId") or response.get("id"),
|
|
101
|
+
"name": response.get("name"),
|
|
102
|
+
"previewRows": len(rows),
|
|
103
|
+
"aliases": {},
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
for alias, expected in case["expected"].items():
|
|
107
|
+
metric = by_alias.get(alias)
|
|
108
|
+
if metric is None:
|
|
109
|
+
raise SystemExit(f"{case['kind']}: missing metric alias {alias}")
|
|
110
|
+
if "formula" in metric:
|
|
111
|
+
raise SystemExit(f"{case['kind']}: {alias} degraded to formula")
|
|
112
|
+
adv_calc = metric.get("advCalc")
|
|
113
|
+
if not adv_calc:
|
|
114
|
+
raise SystemExit(f"{case['kind']}: {alias} missing advCalc")
|
|
115
|
+
if adv_calc.get("advType") != "RANK":
|
|
116
|
+
raise SystemExit(f"{case['kind']}: {alias} advType is not RANK")
|
|
117
|
+
adv_value = adv_calc.get("advValue") or {}
|
|
118
|
+
if adv_value.get("advType") != "RANK":
|
|
119
|
+
raise SystemExit(f"{case['kind']}: {alias} advValue.advType is not RANK")
|
|
120
|
+
for key in ("calDimension", "rankType", "sorting", "tfdId"):
|
|
121
|
+
if adv_value.get(key, "") != expected[key]:
|
|
122
|
+
raise SystemExit(f"{case['kind']}: {alias} {key}={adv_value.get(key)!r}, want {expected[key]!r}")
|
|
123
|
+
top_n = adv_value.get("topN") or {}
|
|
124
|
+
for key, want in expected["topN"].items():
|
|
125
|
+
if top_n.get(key) != want:
|
|
126
|
+
raise SystemExit(f"{case['kind']}: {alias} topN.{key}={top_n.get(key)!r}, want {want!r}")
|
|
127
|
+
if not any(isinstance(row, dict) and row.get(alias) not in ("", None) for row in rows):
|
|
128
|
+
raise SystemExit(f"{case['kind']}: {alias} has no non-empty preview value")
|
|
129
|
+
card_summary["aliases"][alias] = adv_value
|
|
130
|
+
|
|
131
|
+
summary["cards"].append(card_summary)
|
|
132
|
+
|
|
133
|
+
(out / "summary.json").write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
134
|
+
print(json.dumps(summary, ensure_ascii=False, indent=2))
|
|
135
|
+
PY
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
This eval verifies the L3 native BI `RUNNING_TOTAL` advanced calculation builder against a real `default` BI environment.
|
|
2
|
+
|
|
3
|
+
It publishes fixed card/page IDs, reads the created cards back, and verifies:
|
|
4
|
+
|
|
5
|
+
- `COLUMN` accumulation.
|
|
6
|
+
- `ROW` accumulation.
|
|
7
|
+
- `FIELD` accumulation by a non-date dimension.
|
|
8
|
+
- date field accumulation with `DAY`, `WEEK`, `MONTH`, `YEAR`, and `NONE`.
|
|
9
|
+
- sub-date field accumulation with `QUARTER` and `NONE`.
|
|
10
|
+
- `advCalc` is preserved in `zoneData.metric[*].advCalc`.
|
|
11
|
+
- preview returns non-empty accumulated metric values.
|
|
12
|
+
|
|
13
|
+
The default-environment roundtrip runner is intentionally not committed with this eval.
|
|
14
|
+
Its command sequence and assertions are recorded in the knowledge base:
|
|
15
|
+
`01-Projects/L3-running-total-advcalc-golden-answer.md`.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
var orderDate = f("订单日期");
|
|
2
|
+
var orderMonth = f("订单日期", { granularity: Granularity.MONTH });
|
|
3
|
+
var region = f("地区");
|
|
4
|
+
var province = f("省/自治区");
|
|
5
|
+
var category = f("类别");
|
|
6
|
+
|
|
7
|
+
var rangeCard = createCard(ChartType.PIVOT_TABLE, "L3 累计高级计算 范围")
|
|
8
|
+
.setId("t3f01a02b03c04d05e06f07a")
|
|
9
|
+
.bindDataset(DS)
|
|
10
|
+
.addRow(orderDate)
|
|
11
|
+
.addRow(region)
|
|
12
|
+
.addRow(province)
|
|
13
|
+
.addColumn(category)
|
|
14
|
+
.addMetric(f("销售额", {
|
|
15
|
+
alias: "按列累计",
|
|
16
|
+
aggrType: AggrType.SUM,
|
|
17
|
+
advCalc: runningTotal.byColumn()
|
|
18
|
+
}))
|
|
19
|
+
.addMetric(f("销售额", {
|
|
20
|
+
alias: "按行累计",
|
|
21
|
+
aggrType: AggrType.SUM,
|
|
22
|
+
advCalc: runningTotal.byRow()
|
|
23
|
+
}))
|
|
24
|
+
.addMetric(f("销售额", {
|
|
25
|
+
alias: "地区组内累计",
|
|
26
|
+
aggrType: AggrType.SUM,
|
|
27
|
+
advCalc: runningTotal.byField(region)
|
|
28
|
+
}))
|
|
29
|
+
.addMetric(f("销售额", {
|
|
30
|
+
alias: "订单日期日累计",
|
|
31
|
+
aggrType: AggrType.SUM,
|
|
32
|
+
advCalc: runningTotal.byDateField(orderDate, RunningTotalGranularity.DAY)
|
|
33
|
+
}))
|
|
34
|
+
.addMetric(f("销售额", {
|
|
35
|
+
alias: "订单日期周累计",
|
|
36
|
+
aggrType: AggrType.SUM,
|
|
37
|
+
advCalc: runningTotal.byDateField(orderDate, RunningTotalGranularity.WEEK)
|
|
38
|
+
}))
|
|
39
|
+
.addMetric(f("销售额", {
|
|
40
|
+
alias: "订单日期月累计",
|
|
41
|
+
aggrType: AggrType.SUM,
|
|
42
|
+
advCalc: runningTotal.byDateField(orderDate, RunningTotalGranularity.MONTH)
|
|
43
|
+
}))
|
|
44
|
+
.addMetric(f("销售额", {
|
|
45
|
+
alias: "订单日期当年累计",
|
|
46
|
+
aggrType: AggrType.SUM,
|
|
47
|
+
advCalc: runningTotal.byDateField(orderDate, RunningTotalGranularity.YEAR)
|
|
48
|
+
}))
|
|
49
|
+
.addMetric(f("销售额", {
|
|
50
|
+
alias: "订单日期历年累计",
|
|
51
|
+
aggrType: AggrType.SUM,
|
|
52
|
+
advCalc: runningTotal.byDateField(orderDate, RunningTotalGranularity.ALL_YEARS)
|
|
53
|
+
}));
|
|
54
|
+
|
|
55
|
+
registerCard(rangeCard.build());
|
|
56
|
+
|
|
57
|
+
var subDateCard = createCard(ChartType.PIVOT_TABLE, "L3 累计高级计算 子日期")
|
|
58
|
+
.setId("t4f01a02b03c04d05e06f08a")
|
|
59
|
+
.bindDataset(DS)
|
|
60
|
+
.addRow(orderMonth)
|
|
61
|
+
.addRow(region)
|
|
62
|
+
.addColumn(category)
|
|
63
|
+
.addMetric(f("利润", {
|
|
64
|
+
alias: "订单月季度累计",
|
|
65
|
+
aggrType: AggrType.SUM,
|
|
66
|
+
advCalc: runningTotal.byDateField(orderMonth, RunningTotalGranularity.QUARTER)
|
|
67
|
+
}))
|
|
68
|
+
.addMetric(f("利润", {
|
|
69
|
+
alias: "订单月历年累计",
|
|
70
|
+
aggrType: AggrType.SUM,
|
|
71
|
+
advCalc: runningTotal.byDateField(orderMonth, RunningTotalGranularity.ALL_YEARS)
|
|
72
|
+
}));
|
|
73
|
+
|
|
74
|
+
registerCard(subDateCard.build());
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Dataset: 订单表 (default)
|
|
2
|
+
defineDataset("a184fe1b1c27941a9ac977bc", [
|
|
3
|
+
{ fdId: "o0f29e706bf1041d9ab0da11", name: "订单 ID", fdType: "STRING", metaType: "DIM" },
|
|
4
|
+
{ fdId: "r739b62cd3dab49ff9bb7134", name: "订单日期", fdType: "DATE", metaType: "DIM" },
|
|
5
|
+
{ fdId: "o82eaae89ecd046a4b531ce4", name: "发货日期", fdType: "DATE", metaType: "DIM" },
|
|
6
|
+
{ fdId: "k673939fff12744b786d8d44", name: "邮寄方式", fdType: "STRING", metaType: "DIM" },
|
|
7
|
+
{ fdId: "me4783b3bf8984906a32e5ce", name: "客户 ID", fdType: "STRING", metaType: "DIM" },
|
|
8
|
+
{ fdId: "v2a8590c1236e4e58882ee9c", name: "客户名称", fdType: "STRING", metaType: "DIM" },
|
|
9
|
+
{ fdId: "j81f7f09ab6c84168924f9aa", name: "细分", fdType: "STRING", metaType: "DIM" },
|
|
10
|
+
{ fdId: "s99bcae4ac5a045ebae87762", name: "城市", fdType: "STRING", metaType: "DIM" },
|
|
11
|
+
{ fdId: "ufe7632b5afa44dd7a79194e", name: "省/自治区", fdType: "STRING", metaType: "DIM" },
|
|
12
|
+
{ fdId: "e2a051c6ebda04917a10cc14", name: "国家", fdType: "STRING", metaType: "DIM" },
|
|
13
|
+
{ fdId: "xe01f39825ccc428eaf67911", name: "地区", fdType: "STRING", metaType: "DIM" },
|
|
14
|
+
{ fdId: "cd9b3a6c556ef4c53a08df16", name: "产品 ID", fdType: "STRING", metaType: "DIM" },
|
|
15
|
+
{ fdId: "cece9879e99f545449909a82", name: "类别", fdType: "STRING", metaType: "DIM" },
|
|
16
|
+
{ fdId: "k15359167149d4d02bbede2b", name: "子类别", fdType: "STRING", metaType: "DIM" },
|
|
17
|
+
{ fdId: "f247afa584af241dba64821d", name: "产品名称", fdType: "STRING", metaType: "DIM" },
|
|
18
|
+
{ fdId: "g4a34bd7bcb78425aa5485d1", name: "销售额", fdType: "DOUBLE", metaType: "METRIC" },
|
|
19
|
+
{ fdId: "xd21ba1d7fd19433ebc728b6", name: "数量", fdType: "LONG", metaType: "METRIC" },
|
|
20
|
+
{ fdId: "l0dbffcc612e047c9a6dae7b", name: "折扣", fdType: "DOUBLE", metaType: "METRIC" },
|
|
21
|
+
{ fdId: "s1c05aa5e4bdd4a54a60e811", name: "利润", fdType: "DOUBLE", metaType: "METRIC" }
|
|
22
|
+
], { displayType: "DATAFLOW" });
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
var card = createCard(ChartType.GROUPED_COLUMN, "区域营收分析")
|
|
2
|
+
.setId("qe122aaf82e38c1fa369325e")
|
|
3
|
+
.bindDataset(DS)
|
|
4
|
+
.addRow(f("区域"))
|
|
5
|
+
.addMetric(f("营收", { aggrType: AggrType.SUM, numberFormat: NumberFormat.currency("¥", 0) }))
|
|
6
|
+
.addColumn(f("产品"))
|
|
7
|
+
.setShowLegend(true, "right")
|
|
8
|
+
.setDataLabel({ show: true, showNumber: true });
|
|
9
|
+
|
|
10
|
+
registerCard(card.build());
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
var card = createCard(ChartType.MULTI_LINE, "月度趋势")
|
|
2
|
+
.setId("p3e20cf5d750ee644259f7b8")
|
|
3
|
+
.bindDataset(DS)
|
|
4
|
+
.addRow(f("月份"))
|
|
5
|
+
.addMetric(f("营收", { aggrType: AggrType.SUM, numberFormat: NumberFormat.auto("万") }))
|
|
6
|
+
.addMetric(f("成本", { aggrType: AggrType.SUM, numberFormat: NumberFormat.auto("万") }))
|
|
7
|
+
.setShowLegend(true, "bottom");
|
|
8
|
+
|
|
9
|
+
registerCard(card.build());
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
var card = createCard(ChartType.PIE, "产品分布")
|
|
2
|
+
.setId("s1fa6bd1b68a4974f7ad0202")
|
|
3
|
+
.bindDataset(DS)
|
|
4
|
+
.addRow(f("产品"))
|
|
5
|
+
.addMetric(f("营收", { aggrType: AggrType.SUM }))
|
|
6
|
+
.setDataLabel({ show: true, showPercentage: true, showCategory: true })
|
|
7
|
+
.setPieSetting({ innerRadius: 0 });
|
|
8
|
+
|
|
9
|
+
registerCard(card.build());
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
var card = createCard(ChartType.GROUPED_COLUMN, "利润率-计算字段测试")
|
|
2
|
+
.setId("te122aaf82e38c1fa3693250")
|
|
3
|
+
.bindDataset(DS)
|
|
4
|
+
.addRow(f("区域"))
|
|
5
|
+
.addMetric(f("营收", { aggrType: AggrType.SUM }))
|
|
6
|
+
.addMetric(calcField("利润", "(SUM([营收])-SUM([成本]))", { calculationType: "aggregation" }))
|
|
7
|
+
.addMetric(calcField("毛利率", "(SUM([营收])-SUM([成本]))/SUM([营收])", {
|
|
8
|
+
calculationType: "aggregation",
|
|
9
|
+
numberFormat: NumberFormat.percentage(1)
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
registerCard(card.build());
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
var page = createPage("销售分析仪表板")
|
|
2
|
+
.setId("a67a1cddb97678851fe1317f")
|
|
3
|
+
.setDescription("综合销售数据分析看板")
|
|
4
|
+
.setBackgroundColor("#f5f5f5")
|
|
5
|
+
.setCardMargin(8)
|
|
6
|
+
.placeCard(2, 0, 0, 4, 4)
|
|
7
|
+
.placeCard(3, 4, 0, 8, 4)
|
|
8
|
+
.addFullWidthCard(0, 6)
|
|
9
|
+
.addFullWidthCard(1, 6);
|
|
10
|
+
|
|
11
|
+
registerPage(page.build());
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Auto-generated by guanvis schema command
|
|
2
|
+
// DO NOT EDIT — regenerate with: guanvis schema s9ded2338f43807b095fbb4f
|
|
3
|
+
// This file defines dataset schemas for field() references in card scripts.
|
|
4
|
+
|
|
5
|
+
// Dataset: 销售数据 (example, 7 fields)
|
|
6
|
+
defineDataset("s9ded2338f43807b095fbb4f", [
|
|
7
|
+
{ fdId: "jb16f22f6c1c21c3f6e8e293", name: "区域", fdType: "STRING", metaType: "DIM" },
|
|
8
|
+
{ fdId: "o7093aaf2b2f32c9e964c41a", name: "产品", fdType: "STRING", metaType: "DIM" },
|
|
9
|
+
{ fdId: "p2670dd6b74fc78c3e9c57ad", name: "月份", fdType: "DATE", metaType: "DIM" },
|
|
10
|
+
{ fdId: "lb1c31b4dc86511a27049a98", name: "营收", fdType: "DOUBLE", metaType: "METRIC" },
|
|
11
|
+
{ fdId: "aece10cd6806966be56f52ff", name: "成本", fdType: "DOUBLE", metaType: "METRIC" },
|
|
12
|
+
{ fdId: "g1b16b52a16eb10c5e26d4bb", name: "数量", fdType: "INT", metaType: "METRIC" },
|
|
13
|
+
{ fdId: "eae7cae6fbdcde68acdf5d1f", name: "利润率", fdType: "DOUBLE", metaType: "METRIC" }
|
|
14
|
+
]);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Selector: Region filter with auto-link to all cards
|
|
2
|
+
var sel = createSelector("区域筛选")
|
|
3
|
+
.setId("j423ad9cedf3b4bb1336fd6b")
|
|
4
|
+
.bindField(f("区域"))
|
|
5
|
+
.setMultiSelect(true)
|
|
6
|
+
.setDefaultType(SelectorDefaultType.FIRST_PICK)
|
|
7
|
+
.linkToAll()
|
|
8
|
+
.build();
|
|
9
|
+
|
|
10
|
+
registerSelector(sel);
|