@guandata/guanvis 0.1.20 → 0.1.22
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 +12 -0
- package/README.md +18 -0
- package/bin/run.js +36 -1
- 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 +9 -1
- package/skills/guanvis/SKILL.md +7 -6
- package/skills/guanvis/references/builder-reference.md +141 -7
- package/skills/guanvis/references/publish-and-constraints.md +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## @guandata/guanvis 0.1.22 - 2026-06-03
|
|
4
|
+
|
|
5
|
+
- `install-skill` 增加 WorkBuddy skill 安装路径支持。
|
|
6
|
+
- 补充发布与资源约束说明,强调发布前检查资源 ID、命名和覆盖风险。
|
|
7
|
+
|
|
8
|
+
## @guandata/guanvis 0.1.21 - 2026-05-29
|
|
9
|
+
|
|
10
|
+
- 新增卡片固定钻取路径能力,可在构建时配置图表点击后的固定下钻路径。
|
|
11
|
+
- 新增杜邦分析图表支持,扩展财务分析类图表构建能力。
|
|
12
|
+
- 发布与上传阶段增加线上资源覆盖保护,默认阻止误覆盖同 ID 线上 Card/Page,并可通过 dry-run 检查影响范围。
|
|
13
|
+
- 优化卡片诊断信息和联动/点击动作解析,提升复杂页面发布前的可检查性。
|
|
14
|
+
|
|
3
15
|
## @guandata/guanvis 0.1.20 - 2026-05-28
|
|
4
16
|
|
|
5
17
|
- 新增普通图表卡片点击联动能力,支持通过 `card.linkTo(...)` 配置卡片之间的字段映射联动。
|
package/README.md
CHANGED
|
@@ -30,12 +30,30 @@ guanvis pack ./my_dashboard/
|
|
|
30
30
|
|
|
31
31
|
# 一步到位:构建并上传到 BI
|
|
32
32
|
guanvis publish ./my_dashboard/
|
|
33
|
+
|
|
34
|
+
# 只检查将要覆盖哪些线上资源,不上传
|
|
35
|
+
guanvis publish ./my_dashboard/ --dry-run
|
|
36
|
+
|
|
37
|
+
# 明确要覆盖同 ID 线上 Card/Page 时才加
|
|
38
|
+
guanvis publish ./my_dashboard/ --allow-overwrite
|
|
33
39
|
```
|
|
34
40
|
|
|
35
41
|
说明:npm 包名为 `@guandata/guanvis`,用户侧 CLI 命令统一为 `guanvis`。
|
|
36
42
|
|
|
37
43
|
## 版本更新
|
|
38
44
|
|
|
45
|
+
### @guandata/guanvis 0.1.22
|
|
46
|
+
|
|
47
|
+
- `install-skill` 增加 WorkBuddy skill 安装路径支持。
|
|
48
|
+
- 补充发布与资源约束说明,强调发布前检查资源 ID、命名和覆盖风险。
|
|
49
|
+
|
|
50
|
+
### 0.1.21
|
|
51
|
+
|
|
52
|
+
- 新增卡片固定钻取路径能力,可在构建时配置图表点击后的固定下钻路径。
|
|
53
|
+
- 新增杜邦分析图表支持,扩展财务分析类图表构建能力。
|
|
54
|
+
- 发布与上传阶段增加线上资源覆盖保护,默认阻止误覆盖同 ID 线上 Card/Page,并可通过 dry-run 检查影响范围。
|
|
55
|
+
- 优化卡片诊断信息和联动/点击动作解析,提升复杂页面发布前的可检查性。
|
|
56
|
+
|
|
39
57
|
### 0.1.20
|
|
40
58
|
|
|
41
59
|
- 新增普通图表卡片点击联动能力,支持通过 `card.linkTo(...)` 配置卡片之间的字段映射联动。
|
package/bin/run.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
const { execFileSync, execSync, spawnSync } = require("child_process");
|
|
6
6
|
const path = require("path");
|
|
7
7
|
const fs = require("fs");
|
|
8
|
+
const os = require("os");
|
|
8
9
|
|
|
9
10
|
const PLATFORM_MAP = {
|
|
10
11
|
"darwin-arm64": "guanvis-darwin-arm64",
|
|
@@ -35,6 +36,38 @@ function getBinaryPath() {
|
|
|
35
36
|
return binaryPath;
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
function copyDirectory(src, dest) {
|
|
40
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
41
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
42
|
+
const srcPath = path.join(src, entry.name);
|
|
43
|
+
const destPath = path.join(dest, entry.name);
|
|
44
|
+
if (entry.isDirectory()) {
|
|
45
|
+
copyDirectory(srcPath, destPath);
|
|
46
|
+
} else if (entry.isSymbolicLink()) {
|
|
47
|
+
fs.symlinkSync(fs.readlinkSync(srcPath), destPath);
|
|
48
|
+
} else {
|
|
49
|
+
fs.copyFileSync(srcPath, destPath);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function installCodeBuddySkill(pkgRoot, skill) {
|
|
55
|
+
try {
|
|
56
|
+
const srcDir = path.join(pkgRoot, "skills", skill);
|
|
57
|
+
if (!fs.existsSync(path.join(srcDir, "SKILL.md"))) {
|
|
58
|
+
console.warn(`Warning: skipping CodeBuddy/WorkBuddy install; skill not found: ${srcDir}`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const configDir = process.env.CODEBUDDY_CONFIG_DIR || path.join(os.homedir(), ".codebuddy");
|
|
63
|
+
const destDir = path.join(configDir, "skills", skill);
|
|
64
|
+
copyDirectory(srcDir, destDir);
|
|
65
|
+
console.log(`Installed ${skill} to CodeBuddy/WorkBuddy: ${destDir}`);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
console.warn(`Warning: CodeBuddy/WorkBuddy install failed for ${skill}: ${err.message}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
38
71
|
if (process.argv[2] === "version" && process.argv.length === 3) {
|
|
39
72
|
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8"));
|
|
40
73
|
console.log(pkg.version);
|
|
@@ -57,7 +90,9 @@ if (process.argv[2] === "install-skill") {
|
|
|
57
90
|
console.log("Installing guanvis to AI coding assistants...");
|
|
58
91
|
const result = spawnSync("npx", args, { stdio: "inherit", env: process.env, shell: true });
|
|
59
92
|
if (result.error) throw result.error;
|
|
60
|
-
|
|
93
|
+
const status = result.status || 0;
|
|
94
|
+
if (status === 0) installCodeBuddySkill(pkgRoot, "guanvis");
|
|
95
|
+
process.exit(status);
|
|
61
96
|
}
|
|
62
97
|
|
|
63
98
|
const binary = getBinaryPath();
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guandata/guanvis",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.22",
|
|
4
4
|
"description": "观远 BI Card/Page 生成工具 - 通过 JS DSL 创建图表和仪表板",
|
|
5
5
|
"bin": {
|
|
6
6
|
"guanvis": "bin/run.js"
|
|
7
7
|
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "node scripts/build.js && node scripts/sync-skill.js",
|
|
10
|
+
"test:build-script": "node scripts/build.test.js",
|
|
11
|
+
"changelog": "node ../../scripts/generate-release-changelog.js .",
|
|
12
|
+
"check-changelog": "node ../../scripts/check-release-changelog.js .",
|
|
13
|
+
"prepublishOnly": "npm run test:build-script && npm run check-changelog && npm run build && node scripts/preflight.js"
|
|
14
|
+
},
|
|
8
15
|
"files": [
|
|
9
16
|
"bin/",
|
|
10
17
|
"binaries/",
|
|
@@ -30,6 +37,7 @@
|
|
|
30
37
|
"node": ">=14"
|
|
31
38
|
},
|
|
32
39
|
"publishConfig": {
|
|
40
|
+
"registry": "https://registry.npmjs.org/",
|
|
33
41
|
"access": "public"
|
|
34
42
|
}
|
|
35
43
|
}
|
package/skills/guanvis/SKILL.md
CHANGED
|
@@ -27,8 +27,8 @@ compatibility: "Requires Node.js 14+. Install via npm link (local) or npm instal
|
|
|
27
27
|
|
|
28
28
|
## AI Quick Reference(速查,详细说明见按需参考资料)
|
|
29
29
|
|
|
30
|
-
1. **工厂函数**:只用 `createCard()` / `createSelector()` / `createTextCard()` / `createTab()` / `createPage()`,不要 `new XxxBuilder()`
|
|
31
|
-
2. **注册函数**:`registerCard(card.build())` / `registerSelector(sel.build())` / `registerTextCard(text.build())` / `registerPage(page.build())`
|
|
30
|
+
1. **工厂函数**:只用 `createCard()` / `createSelector()` / `createTextCard()` / `createDuPontChart()` / `createTab()` / `createPage()`,不要 `new XxxBuilder()`
|
|
31
|
+
2. **注册函数**:`registerCard(card.build())` / `registerSelector(sel.build())` / `registerTextCard(text.build())` / `registerDuPontChart(dupont.build())` / `registerPage(page.build())`
|
|
32
32
|
3. **字段引用**:单数据集用 `f("字段名")`,多数据集用 `field(DS, "字段名")`
|
|
33
33
|
4. **zone maxCount**:`BASIC_COLUMN/BAR/LINE` metric=1;`GROUPED_*/STACKED_*` metric=∞;`KPI_CARD` metric=1;组合图 `*_WITH_LINE` row=1
|
|
34
34
|
5. **column vs colorBy**:column 放维度(按类别分组着色),colorBy 放度量(按值渐变着色)
|
|
@@ -36,7 +36,7 @@ compatibility: "Requires Node.js 14+. Install via npm link (local) or npm instal
|
|
|
36
36
|
7. **calcField 命名**:不能与数据集物理字段同名,否则 BI 默认取数据集字段
|
|
37
37
|
8. **calcField 类型**:`aggregation`(默认)公式必须含聚合函数;纯算术用 `{ calculationType: "normal" }`;窗口函数用 `{ calculationType: "window" }`
|
|
38
38
|
9. **明细/滚动表 calcField**:`DETAIL_TABLE` / `SCROLL_TABLE` 只做逐行展示;如需行级计算,必须写 `{ calculationType: "normal" }`,且不要写任何 SQL 聚合函数或窗口函数。汇总需求改用非明细图表 `aggrType` / aggregation calcField,或 ETL 预计算
|
|
39
|
-
10.
|
|
39
|
+
10. **联动/下钻**:筛选器联动必须调用 `.linkToAll()` 或 `.linkTo(cardIndex)`;普通图表卡片联动普通图表用 `card.linkTo(layoutCardIndex, { fields: [{ source, target }] })`;固定路径下钻用 `registerDrillPath(parentCardIndex, [child.build()], { position: DrillPathPosition.BOTTOM })`;详细规则见 `references/builder-reference.md`
|
|
40
40
|
11. **selector 类型选择**:离散值(区域/类别)→ `DS_ELEMENTS`(默认);连续数值(利润率/金额)→ `.setSelectorType(SelectorType.DS_INTERVAL)`;日期 → `CALENDAR`;快捷日期区间(本月/近7天等)→ `.setTimeMacroOptions(options)`
|
|
41
41
|
12. **同环比默认**:用户说同比/环比/同环比/年同比/月环比且未指定输出值时,默认用增长率;未指定模式时默认按日期筛选模式(`ComparativeMode.FILTER_BASED`),普通模式需显式指定 `ComparativeMode.NORMAL`
|
|
42
42
|
13. **placeCard 索引**:按 registerCard → registerTextCard 调用顺序累加,文件按文件名排序加载。**registerSelector 不参与 card index 计数**
|
|
@@ -46,7 +46,8 @@ compatibility: "Requires Node.js 14+. Install via npm link (local) or npm instal
|
|
|
46
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
47
|
18. **设计规则**:`preview`/`pack`/`publish` 会自动应用内置设计规则;需要自定义静态卡片默认规则或主题已开放配置时,在工程目录新建 `design-rule.json`,不要手改 `themes/<themeId>.json`;详见 `references/theme.md`
|
|
48
48
|
19. **图表选型**:创建指标卡时,若只有单指标优先用 `SINGLE_VALUE`;需展示对比指标时用 `KPI_CARD`,并调用 `.addCompare(...)`
|
|
49
|
-
20.
|
|
49
|
+
20. **杜邦分析图**:杜邦不是普通 `ChartType`,用 `createDuPontChart()` 创建 `LAYOUT` 卡片;节点通常放 `KPI_CARD` 子卡片,并通过 `.setRoot()` / `.addChild()` 组织树。页面布局只放杜邦父卡片,不单独放子卡片;筛选器 `linkToAll()` 会覆盖杜邦子卡片。
|
|
50
|
+
21. **tab 布局**:同一主题有多组互斥分析内容时可用 tab;少量卡片或需同时对照时优先平铺。用法见 `references/builder-reference.md`,示例见 `evals/tab_layout/`
|
|
50
51
|
|
|
51
52
|
## 何时使用
|
|
52
53
|
|
|
@@ -289,11 +290,11 @@ guanvis pack ./project
|
|
|
289
290
|
|
|
290
291
|
`publish` 和 `upload` 使用 BI 的 **transfer API**(`/api/manual/template/transfer`),特点:
|
|
291
292
|
- `needIdMapping=false`:保持资源 ID 不变,重复导入会覆盖同 ID 资源
|
|
292
|
-
-
|
|
293
|
+
- 认证方式:随底层 `guancli fetch` 使用 `Cookie: uIdToken=...`
|
|
293
294
|
- 需要 `raw-backend-response: TRUE` header 绕过前端代理层
|
|
294
295
|
- 不需要目标系统开启"一键迁移"开关,所有环境通用
|
|
295
296
|
|
|
296
|
-
**线上仪表板更新策略**:为了保护已经发布过的仪表板和线上仪表板,默认不要复用原 Page/Card/Selector ID 做覆盖更新。需要调整线上看板时,应先生成一组新的 ID,复制并修改 DSL/JS,给 Page 名称追加版本号(例如 `v2`、`v20260430` 或业务约定版本),再 `publish` 到目标目录。旧版本保留用于回滚和对比。仅当用户明确要求“覆盖原仪表板/复用原 ID”时,才允许同 ID
|
|
297
|
+
**线上仪表板更新策略**:为了保护已经发布过的仪表板和线上仪表板,默认不要复用原 Page/Card/Selector ID 做覆盖更新。需要调整线上看板时,应先生成一组新的 ID,复制并修改 DSL/JS,给 Page 名称追加版本号(例如 `v2`、`v20260430` 或业务约定版本),再 `publish` 到目标目录。旧版本保留用于回滚和对比。仅当用户明确要求“覆盖原仪表板/复用原 ID”时,才允许同 ID 发布,并且命令必须显式加 `--allow-overwrite`;发布前可用 `--dry-run` 查看会覆盖哪些线上 Card/Page。
|
|
297
298
|
|
|
298
299
|
## 文件结构约定
|
|
299
300
|
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
| `.setThemeColor(tcId, colors)` | 主题颜色 |
|
|
25
25
|
| `.setLimit(count)` | 数据行数限制 |
|
|
26
26
|
| `.setConditionalFormat(config)` / `.setAuxiliaryLine(config)` | 条件格式/辅助线 |
|
|
27
|
-
| `.linkTo(
|
|
27
|
+
| `.linkTo(target, config)` | 图表卡片点击联动;`target` 可传布局 index 或 cardId,详细规则见本节 Card Linkage |
|
|
28
|
+
| `registerDrillPath(parentCardIndex, drillCards, config?)` | 全局函数,声明固定路径下钻,详细规则见本节 Card Drill |
|
|
28
29
|
| `.setRawSettings(key, value)` | 原始设置 |
|
|
29
30
|
| `.build()` | 构建(触发验证) |
|
|
30
31
|
|
|
@@ -114,7 +115,7 @@
|
|
|
114
115
|
|
|
115
116
|
#### Card Linkage(图表卡片点击联动)
|
|
116
117
|
|
|
117
|
-
`CardBuilder.linkTo(
|
|
118
|
+
`CardBuilder.linkTo(target, config)` 用于让一个普通图表卡片或固定路径下钻子卡,在点击维度值后过滤同一页面内的目标图表卡片。
|
|
118
119
|
通过 `settings.asFilter` 配置联动关系,通过 `settings.interaction`配置默认交互,不是 selector 联动。
|
|
119
120
|
|
|
120
121
|
```javascript
|
|
@@ -133,13 +134,20 @@ var card = createCard(ChartType.GROUPED_COLUMN, "区域销售")
|
|
|
133
134
|
registerCard(card.build());
|
|
134
135
|
```
|
|
135
136
|
|
|
136
|
-
`
|
|
137
|
+
`target` 为数字时,默认和页面布局中的 `card: n` 含义一致,按 `registerCard` / `registerTextCard` / `registerImageCard` / `registerCustomChart` / `registerDuPontChart` 等可布局资源的注册顺序解析;`registerSelector` 不参与该 index。注意它和 `SelectorBuilder.linkTo(cardIndex)` 不同,selector 的 index 仍是历史普通图表注册顺序。
|
|
138
|
+
|
|
139
|
+
`target` 也可以是 cardId 字符串,用于引用不在布局 index 中的固定路径下钻子卡;字符串目标也可以指向普通 layout 图表卡。
|
|
140
|
+
|
|
141
|
+
杜邦子卡片不放页面布局,但可参与联动,数字索引追加在可布局资源之后,顺序为杜邦树中的子卡片构建顺序;也可以使用 cardId 字符串目标直接引用。
|
|
137
142
|
|
|
138
143
|
构建阶段会按当前点击动作重算 `interaction`:只有联动一个动作时默认直接联动;如果已有跳转、下钻等其它点击动作,会切换为菜单选择。
|
|
139
144
|
|
|
140
|
-
|
|
145
|
+
支持范围:
|
|
141
146
|
|
|
142
|
-
-
|
|
147
|
+
- 普通图表卡片可以联动同页普通图表卡片,或其它下钻路径中的子卡。
|
|
148
|
+
- 下钻子卡可以联动同页普通图表卡片。
|
|
149
|
+
- 下钻路径内的父子卡、同路径子卡之间不建立联动;下钻子卡之间也不建立联动。
|
|
150
|
+
- source 卡片不能跨 page 复用,也不能已有 `settings.asFilter`。
|
|
143
151
|
- source 只能用 `.addRow(...)` / `.addColumn(...)` 添加的维度;target 使用目标卡片绑定的数据集字段。
|
|
144
152
|
- 一次 `.linkTo(...)` 声明一组 `{ source, target }`;多个目标多次调用。
|
|
145
153
|
- 日期联动会按 source 日期粒度对齐目标日期字段;source 使用月/季度/年等粒度时,用 `field(..., { granularity })` 表达该粒度。
|
|
@@ -170,6 +178,64 @@ registerCard(trend.build()); // layout card index 0
|
|
|
170
178
|
registerCard(source.build()); // source linkTo(0) targets trend
|
|
171
179
|
```
|
|
172
180
|
|
|
181
|
+
#### Card Drill(固定路径下钻)
|
|
182
|
+
|
|
183
|
+
`registerDrillPath(parentCardIndex, drillCards, config?)` 用于声明固定路径下钻。父卡必须是页面 layout 中的普通图表卡;下钻子卡仍用 `createCard(...).build()` 定义,但不要 `registerCard()`,也不要放进页面布局。
|
|
184
|
+
|
|
185
|
+
```javascript
|
|
186
|
+
var province = createCard(ChartType.BASIC_COLUMN, "省份销售额")
|
|
187
|
+
.setId("aaaaaaaaaaaaaaaaaaaaaaaa")
|
|
188
|
+
.bindDataset(DS)
|
|
189
|
+
.addRow(f("省份"))
|
|
190
|
+
.addMetric(f("销售额", { aggrType: AggrType.SUM }));
|
|
191
|
+
registerCard(province.build());
|
|
192
|
+
|
|
193
|
+
var city = createCard(ChartType.BASIC_BAR, "城市销售额")
|
|
194
|
+
.setId("bbbbbbbbbbbbbbbbbbbbbbbb")
|
|
195
|
+
.bindDataset(DS)
|
|
196
|
+
.addRow(f("城市"))
|
|
197
|
+
.addMetric(f("销售额", { aggrType: AggrType.SUM }));
|
|
198
|
+
|
|
199
|
+
var store = createCard(ChartType.PIVOT_TABLE, "门店明细")
|
|
200
|
+
.setId("cccccccccccccccccccccccc")
|
|
201
|
+
.bindDataset(DS)
|
|
202
|
+
.addRow(f("门店"))
|
|
203
|
+
.addMetric(f("销售额", { aggrType: AggrType.SUM }));
|
|
204
|
+
|
|
205
|
+
registerDrillPath(0, [city.build(), store.build()], {
|
|
206
|
+
position: DrillPathPosition.BOTTOM
|
|
207
|
+
});
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`position` 可传 `DrillPathPosition.TOP` 或 `DrillPathPosition.BOTTOM`,默认在底部展示。下钻链路的点击交互由 guanvis 维护,不要通过 `setRawSettings("interaction", ...)` 手写。
|
|
211
|
+
|
|
212
|
+
`drillWithPageFilters` 只对无维度也支持下钻的指标/进度类父卡生效:`SINGLE_VALUE`、`SOLID_GAUGE`、`PROGRESS_BAR`、`PROGRESS_PIE`、`KPI_CARD`、`LIQUID_GAUGE`。未显式传入时不会写入;传给其他图表类型会输出 warning 并忽略。
|
|
213
|
+
|
|
214
|
+
如果父卡已经通过 `setRawSettings` 写了 `drillPathPosition` 或支持类型上的 `drillWithPageFilters`,建议改为放到 `registerDrillPath` 里统一声明;两边同时写时值必须一致。
|
|
215
|
+
|
|
216
|
+
下钻子卡可以作为联动 source:
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
var city = createCard(ChartType.BASIC_BAR, "城市销售额")
|
|
220
|
+
.setId("bbbbbbbbbbbbbbbbbbbbbbbb")
|
|
221
|
+
.bindDataset(DS)
|
|
222
|
+
.addRow(f("城市"))
|
|
223
|
+
.addMetric(f("销售额", { aggrType: AggrType.SUM }))
|
|
224
|
+
.linkTo(1, {
|
|
225
|
+
fields: [{ source: "城市", target: "城市" }]
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
普通 layout 卡要联动到下钻子卡时,使用 cardId 字符串:
|
|
230
|
+
|
|
231
|
+
```javascript
|
|
232
|
+
overview.linkTo("bbbbbbbbbbbbbbbbbbbbbbbb", {
|
|
233
|
+
fields: [{ source: "区域", target: "城市" }]
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
固定路径下钻支持普通图表卡作为父卡或子卡;不支持自由下钻、指标图表下钻、selector/text/image/custom chart 作为下钻父卡或子卡,也不支持下钻子卡进入页面 layout。
|
|
238
|
+
|
|
173
239
|
### PageBuilder
|
|
174
240
|
|
|
175
241
|
**布局单位**:
|
|
@@ -362,7 +428,7 @@ registerSelector(sel);
|
|
|
362
428
|
- **日期选择**(精确日期范围)→ `SelectorType.CALENDAR`,需要 `bindField` 绑定日期字段。默认会从联动目标卡片推断日期粒度;如需固定月/季度等粒度,可用 `.setGranularity(Granularity.MONTH)` 或 `.setGranularityOptions([...], default)`
|
|
363
429
|
- **快捷日期区间**(本月/上月/近7天等预设区间)→ `.setTimeMacroOptions(options, default)`,不需要 `bindField`,自动匹配目标卡片日期字段联动。`default` 传 `null` 表示无默认值
|
|
364
430
|
|
|
365
|
-
**联动机制**:筛选器通过 `settings.asFilter` 配置联动关系。`linkTo(cardIndex)` 会自动构建 `columnMappings`,将筛选器字段映射到目标卡片的同名字段。`linkToAll()`
|
|
431
|
+
**联动机制**:筛选器通过 `settings.asFilter` 配置联动关系。`linkTo(cardIndex)` 会自动构建 `columnMappings`,将筛选器字段映射到目标卡片的同名字段。`linkToAll()` 会自动匹配所有普通图表卡片和杜邦子卡片中的同名字段。
|
|
366
432
|
|
|
367
433
|
**文件命名**:筛选器脚本建议命名为 `selector_NN_xxx.js`,会在 `card_*.js` 之后、`page.js` 之前执行。
|
|
368
434
|
|
|
@@ -627,6 +693,72 @@ var logoBase64 = __asset_base64("./logo.png")__;
|
|
|
627
693
|
- 路径不允许向上跳出内容目录(安全限制)
|
|
628
694
|
- 占位符仅在 `pack`/`publish` 构建时处理,不影响 AI 脚本的可读性
|
|
629
695
|
|
|
696
|
+
### DuPontBuilder(杜邦分析图)
|
|
697
|
+
|
|
698
|
+
杜邦分析图不是普通 `ChartType`,而是 BI 的 `LAYOUT` 卡片(`cdType=9`),meta 结构为 `{ layoutType: "DU_PONT", layout: ... }`。guanvis 用 `createDuPontChart()` 创建父卡片,用普通 `CardBuilder` 创建每个节点中的指标卡。
|
|
699
|
+
|
|
700
|
+
| 方法 | 说明 |
|
|
701
|
+
|------|------|
|
|
702
|
+
| `createDuPontChart(name)` | 创建杜邦分析图父卡片 |
|
|
703
|
+
| `.setId(cardId)` | **必填**。设置杜邦父卡片 ID |
|
|
704
|
+
| `.setRoot(cardBuilder, options?)` | 设置根节点。`cardBuilder` 必须是已 `.setId()` 的普通图表卡片,通常为 `KPI_CARD` |
|
|
705
|
+
| `.addChild(parentCardOrId, childCardBuilder, options?)` | 给指定父节点添加子节点。`parentCardOrId` 可传父节点的 `CardBuilder` 或 cardId |
|
|
706
|
+
| `.addChildren(parentCardOrId, childCardBuilders, optionsOrOptionsList?)` | 批量添加子节点 |
|
|
707
|
+
| `.setLayout({})` | 仅用于创建空杜邦图占位;非空 raw layout 不支持,请用 `.setRoot()` / `.addChild()` 自动注册子卡 |
|
|
708
|
+
| `.setDescription(desc)` | 描述 |
|
|
709
|
+
| `.setRawSettings(key, value)` | 原始 settings |
|
|
710
|
+
| `.build()` | 构建(触发验证) |
|
|
711
|
+
|
|
712
|
+
`options` 支持:
|
|
713
|
+
|
|
714
|
+
| 属性 | 说明 |
|
|
715
|
+
|------|------|
|
|
716
|
+
| `operation` | 记录在父节点 `operations` 中的运算符,如 `"*"`, `"/"`, `"+"`, `"-"` |
|
|
717
|
+
| `bgColor` | 节点背景色 |
|
|
718
|
+
| `style` | 节点样式覆盖,支持 `titleFont`、`kpiFont`、`bgColor`、`kpiLayout`、`collapse`、`width`、`height` 等 |
|
|
719
|
+
|
|
720
|
+
`operation` 使用 `DuPontOperation.ADD` / `SUBTRACT` / `MULTIPLY` / `DIVIDE` / `NONE`;也兼容 `"*"`、`"/"` 并自动转成 BI 前端使用的 `"×"`、`"÷"`。`kpiLayout` 使用 `DuPontKpiLayout.TILE`(平铺式)或 `DuPontKpiLayout.COMPACT`(紧凑式)。
|
|
721
|
+
|
|
722
|
+
```javascript
|
|
723
|
+
var roe = createCard(ChartType.KPI_CARD, "净资产收益率")
|
|
724
|
+
.setId("aaaaaaaaaaaaaaaaaaaaaaaa")
|
|
725
|
+
.bindDataset(DS)
|
|
726
|
+
.addMetric(f("净利润", { aggrType: AggrType.SUM }));
|
|
727
|
+
|
|
728
|
+
var margin = createCard(ChartType.KPI_CARD, "销售净利率")
|
|
729
|
+
.setId("bbbbbbbbbbbbbbbbbbbbbbbb")
|
|
730
|
+
.bindDataset(DS)
|
|
731
|
+
.addMetric(f("销售收入", { aggrType: AggrType.SUM }));
|
|
732
|
+
|
|
733
|
+
var turnover = createCard(ChartType.KPI_CARD, "资产周转率")
|
|
734
|
+
.setId("cccccccccccccccccccccccc")
|
|
735
|
+
.bindDataset(DS)
|
|
736
|
+
.addMetric(f("资产总额", { aggrType: AggrType.SUM }));
|
|
737
|
+
|
|
738
|
+
var dupont = createDuPontChart("杜邦分析")
|
|
739
|
+
.setId("dddddddddddddddddddddddd")
|
|
740
|
+
.setRoot(roe, { style: { kpiLayout: DuPontKpiLayout.COMPACT } })
|
|
741
|
+
.addChild(roe, margin, { operation: "*" })
|
|
742
|
+
.addChild(roe, turnover, { operation: "*" });
|
|
743
|
+
|
|
744
|
+
registerDuPontChart(dupont.build());
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
页面布局中放杜邦父卡片即可,杜邦子卡片会以 `pId=父卡片ID` 打包,不要再单独放进页面布局:
|
|
748
|
+
|
|
749
|
+
```javascript
|
|
750
|
+
var page = createPage("财务分析")
|
|
751
|
+
.setId("eeeeeeeeeeeeeeeeeeeeeeee")
|
|
752
|
+
.addFullWidthCard("dddddddddddddddddddddddd", 10);
|
|
753
|
+
registerPage(page.build());
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
本地校验会检查:
|
|
757
|
+
- 杜邦父卡片必须是 `LAYOUT` payload,`layoutType` 必须是 `DU_PONT`
|
|
758
|
+
- layout 节点必须有 `id` / `cardId`
|
|
759
|
+
- layout 中引用的 `cardId` 必须是当前杜邦父卡片名下的普通子卡片,不能引用无关顶层卡片、selector、其它 layout 或自定义图表 data view
|
|
760
|
+
- 节点 `id` 不允许重复
|
|
761
|
+
|
|
630
762
|
**内嵌第三方 JS 库**(如 Vega-Lite、D3 等):
|
|
631
763
|
|
|
632
764
|
SDK 模式的 iframe 沙箱**阻止从外部 CDN 加载脚本**(sandbox/CSP 限制)。`addLib()` 添加的 CDN URL 在安全模式下不生效。正确做法是:
|
|
@@ -809,7 +941,9 @@ option = {
|
|
|
809
941
|
| `SelectorType` | `DS_ELEMENTS`(默认), `DS_INTERVAL`, `CALENDAR`, `TIME_MACRO` | 筛选器类型 |
|
|
810
942
|
| `SelectorDisplay` | `SEARCH_LIST`(单选下拉), `SEARCH_BOX`(多选下拉), `CHECKBOX`(复选框), `RADIO`(单选框), `BUTTON_GROUP`(按钮组) | 筛选器展示类型,不设置时根据 multiSelect 自动推断 |
|
|
811
943
|
| `SelectorDefaultType` | `FIRST_PICK`(默认), `FIXED_VALUE`, `ALL`(全部/不筛选) | 筛选器默认值类型 |
|
|
812
|
-
| `CardType` | `CHART`(0), `TEXT`(1), `IFRAME`(2), `PICTURE`(4), `SELECTOR`(6) | 卡片类型(内部使用,通常不需要直接引用) |
|
|
944
|
+
| `CardType` | `CHART`(0), `TEXT`(1), `IFRAME`(2), `PICTURE`(4), `SELECTOR`(6), `LAYOUT`(9) | 卡片类型(内部使用,通常不需要直接引用) |
|
|
813
945
|
| `ImageSourceType` | `OUTSIDE_LINK`(1), `LOCAL_IMAGE`(2) | 图片来源类型 |
|
|
814
946
|
| `ImageRenderType` | `RATIO`(1,原比例), `STRETCH`(2,拉伸填满), `FIT_TO_CONTENT`(3,自适应内容) | 图片渲染模式 |
|
|
815
947
|
| `CustomChartSubType` | `SDK`(iframe,首选), `ECHARTS_LITE`(原生 ECharts) | 自定义图表子类型 |
|
|
948
|
+
| `DuPontKpiLayout` | `TILE`(平铺式), `COMPACT`(紧凑式) | 杜邦节点指标布局 |
|
|
949
|
+
| `DuPontOperation` | `NONE`, `ADD`, `SUBTRACT`, `MULTIPLY`, `DIVIDE` | 杜邦父节点到各子节点的运算符 |
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
`publish` 和 `upload` 命令通过 BI 的 transfer API 直接将资源包上传到目标系统:
|
|
4
4
|
|
|
5
5
|
- **接口**:`POST /api/manual/template/transfer`(标准 multipart/form-data,表单字段名 `new-file`)
|
|
6
|
-
-
|
|
6
|
+
- **认证**:随底层 `guancli fetch` 使用 `Cookie: uIdToken=...`
|
|
7
7
|
- **关键 header**:`raw-backend-response: TRUE`(绕过前端代理层,直达后端)
|
|
8
|
-
- **ID 策略**:`needIdMapping=false`,保持资源 ID 不变。同 ID
|
|
8
|
+
- **ID 策略**:`needIdMapping=false`,保持资源 ID 不变。同 ID 资源会被覆盖更新。`guanvis publish/upload` 默认会先探测目标环境已有 Card/Page ID,检测到覆盖风险时拒绝上传;只有明确加 `--allow-overwrite` 才允许覆盖。
|
|
9
9
|
- **通用性**:不需要目标系统开启"一键迁移"开关,所有客户环境可用
|
|
10
10
|
- **异步执行**:上传成功后返回 `taskId`,后端异步完成导入
|
|
11
11
|
|
|
@@ -43,4 +43,5 @@
|
|
|
43
43
|
- `.setId(id)` 的 ID **必须**为严格 24 位字母数字字符串(正则:`^[a-zA-Z0-9]{24}$`),与后端 `RandUtil.uuid` 格式一致。
|
|
44
44
|
- **生成 ID**:先运行 `guanvis genid <数量>` 生成足够的 ID,在编写脚本时直接填入每个 card/selector/page 的 `.setId()` 调用中。
|
|
45
45
|
- **线上更新默认策略**:已发布过或线上正在使用的仪表板,后续修改默认生成新的 Page/Card/Selector ID,并给 Page 名称追加版本号后发布,保留旧版本不覆盖。
|
|
46
|
-
-
|
|
46
|
+
- **覆盖前检查**:发布前可先运行 `guanvis publish <dir> --dry-run` 或 `guanvis upload <zip> --dry-run`,只构建/解析资源并列出将被覆盖的线上 Card/Page,不提交 transfer 任务。
|
|
47
|
+
- **显式覆盖场景**:只有用户明确要求覆盖原仪表板时,才保持 `.setId()` 不变,并在 `publish/upload` 时加 `--allow-overwrite`;多次同 ID 发布会覆盖资源(因为 transfer API 的 `needIdMapping=false`)。
|