@guandata/guanvis 0.1.17 → 0.1.19

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 CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## @guandata/guanvis 0.1.19 - 2026-05-26
4
+
5
+ - 筛选器默认值校验增强,日历筛选器会校验绑定字段类型,避免非日期字段误配日期默认值。
6
+ - 增强来源动态字段、联动筛选器和默认值映射的校验,提前发现无法可靠发布的配置。
7
+ - 补充相关测试覆盖和 builder 参考文档说明,提升复杂筛选器配置的可诊断性。
8
+
9
+ ## @guandata/guanvis 0.1.18 - 2026-05-25
10
+
11
+ - 新增 Tab 布局能力,支持通过 `createTab()` / `addPanel()` 组织同页多组可切换内容。
12
+ - 新增 `gen-layout-id` 命令,用于生成 tab/panel 等布局组件 ID。
13
+ - 增加 Tab 布局示例工程和文档说明,便于快速复用。
14
+ - 增强筛选器默认值校验,提前发现不匹配的筛选值配置。
15
+
3
16
  ## @guandata/guanvis 0.1.17 - 2026-05-21
4
17
 
5
18
  - 构建流程会将 npm 包版本注入到 `guanvis` 二进制,确保运行时版本信息与发布包一致。
package/README.md CHANGED
@@ -15,6 +15,10 @@ guanvis install-skill
15
15
  # 生成资源 ID
16
16
  guanvis genid 5
17
17
 
18
+ # 生成布局组件 ID
19
+ guanvis gen-layout-id tab
20
+ guanvis gen-layout-id panel 3 --length 8
21
+
18
22
  # 初始化:从 BI 获取数据集结构
19
23
  guanvis init <dsId> -d ./my_dashboard/
20
24
 
@@ -32,6 +36,13 @@ guanvis publish ./my_dashboard/
32
36
 
33
37
  ## 版本更新
34
38
 
39
+ ### 0.1.18
40
+
41
+ - 新增 Tab 布局能力,支持通过 `createTab()` / `addPanel()` 组织同页多组可切换内容。
42
+ - 新增 `gen-layout-id` 命令,用于生成 tab/panel 等布局组件 ID。
43
+ - 增加 Tab 布局示例工程和文档说明,便于快速复用。
44
+ - 增强筛选器默认值校验,提前发现不匹配的筛选值配置。
45
+
35
46
  ### 0.1.17
36
47
 
37
48
  - 构建流程会将 npm 包版本注入到 `guanvis` 二进制,确保运行时版本信息与发布包一致。
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guandata/guanvis",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "description": "观远 BI Card/Page 生成工具 - 通过 JS DSL 创建图表和仪表板",
5
5
  "bin": {
6
6
  "guanvis": "bin/run.js"
@@ -27,7 +27,7 @@ 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()` / `createPage()`,不要 `new XxxBuilder()`
30
+ 1. **工厂函数**:只用 `createCard()` / `createSelector()` / `createTextCard()` / `createTab()` / `createPage()`,不要 `new XxxBuilder()`
31
31
  2. **注册函数**:`registerCard(card.build())` / `registerSelector(sel.build())` / `registerTextCard(text.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
@@ -46,6 +46,7 @@ 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. **tab 布局**:同一主题有多组互斥分析内容时可用 tab;少量卡片或需同时对照时优先平铺。用法见 `references/builder-reference.md`,示例见 `evals/tab_layout/`
49
50
 
50
51
  ## 何时使用
51
52
 
@@ -231,6 +232,10 @@ registerPage(p.build());
231
232
  guanvis genid # 生成 1 个
232
233
  guanvis genid 5 # 生成 5 个
233
234
 
235
+ # 生成布局组件 ID
236
+ guanvis gen-layout-id tab # 生成 1 个 tab_ + 默认 6 位字母
237
+ guanvis gen-layout-id panel 3 --length 8 # 生成 3 个 panel_ + 8 位字母;length 只计算下划线后的随机字母,超出 6~10 时自动收敛
238
+
234
239
  # 预览生成结果(JSON 输出到 stdout,含 payload 验证,用于调试)
235
240
  guanvis preview ./my_dashboard/
236
241
 
@@ -308,6 +313,7 @@ guanvis pack ./project
308
313
  |------|------|------|
309
314
  | 基础仪表板 | `evals/sales_dashboard/` | 普通卡片(柱状图、折线图、KPI、饼图)+ 筛选器 + 页面布局 |
310
315
  | 自定义图表 | `evals/custom_chart_echarts/` | ECharts Lite 自定义图表:柱状图 + 饼图,使用 `loadContent()` 文件模式 |
316
+ | Tab 布局 | `evals/tab_layout/` | 单页面 tab 示例,含根布局指标卡、图表卡片、文本卡片、筛选器和 panel 内卡片布局 |
311
317
 
312
318
  生成脚本前先查阅对应示例中的文件结构和写法。
313
319
 
@@ -316,7 +322,7 @@ guanvis pack ./project
316
322
  | 场景 | 路径 | 读取时机 |
317
323
  |------|------|----------|
318
324
  | 字段、计算字段、NumberFormat、高级计算和卡片筛选器 API | `references/api-reference.md` | 编写字段、指标、公式或筛选条件时 |
319
- | Card/Page/Selector/Text/Image/CustomChart Builder API 与枚举 | `references/builder-reference.md` | 编写或修改 JS DSL builder 调用时 |
325
+ | Card/Page/Tab/Selector/Text/Image/CustomChart Builder API 与枚举 | `references/builder-reference.md` | 编写或修改 JS DSL builder 调用时 |
320
326
  | zone 校验、图表选型、地图/拆分图、字段对象细节 | `references/validation-and-chart-patterns.md` | pack 报错、选型不确定或需要特殊图表行为时 |
321
327
  | 仪表板主题机制与 theme 命令排错 | `references/theme.md` | 用户指定视觉风格、改版继承主题或主题异常时 |
322
328
  | 在线同步、认证、ID 管理和硬约束 | `references/publish-and-constraints.md` | publish/upload、更新线上资源或确认生成边界时 |
@@ -0,0 +1,6 @@
1
+ var card = createCard(ChartType.SINGLE_VALUE, "总营收")
2
+ .setId("eeeeeeeeeeeeeeeeeeeeeeee")
3
+ .bindDataset(DS)
4
+ .addMetric(f("营收", { aggrType: AggrType.SUM, numberFormat: NumberFormat.currency("¥", 0) }));
5
+
6
+ registerCard(card.build());
@@ -0,0 +1,10 @@
1
+ var card = createCard(ChartType.GROUPED_COLUMN, "区域产品营收")
2
+ .setId("aaaaaaaaaaaaaaaaaaaaaaaa")
3
+ .bindDataset(DS)
4
+ .addRow(f("区域"))
5
+ .addColumn(f("产品"))
6
+ .addMetric(f("营收", { aggrType: AggrType.SUM, numberFormat: NumberFormat.currency("¥", 0) }))
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("bbbbbbbbbbbbbbbbbbbbbbbb")
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,11 @@
1
+ var text = createTextCard("Tab 使用说明")
2
+ .setId("cccccccccccccccccccccccc")
3
+ .addMarkdown(
4
+ "### Tab 布局示例\n\n" +
5
+ "- `createTab()` 创建容器\n" +
6
+ "- `addPanel(name, callback)` 创建页面上可切换的标签\n" +
7
+ "- panel 内通过 cardIndex 放置图表卡片或文本卡片\n\n" +
8
+ "> 筛选器仍然注册为 selector,自动进入页面筛选器区。"
9
+ );
10
+
11
+ registerTextCard(text.build());
@@ -0,0 +1,23 @@
1
+ var salesTab = createTab("销售分析")
2
+ .setId("tab_AbCdEf")
3
+ .setLabelStyle(TabLabelStyle.CAPSULE)
4
+ .setAlignment(TabAlignment.CENTER)
5
+ .addPanel("区域分析", function(panel) {
6
+ panel
7
+ .setId("panel_GhIjKl")
8
+ .addRow([{ card: 1, w: 6 }, { card: 3, w: 6 }], 6);
9
+ })
10
+ .addPanel("趋势分析", function(panel) {
11
+ panel
12
+ .setId("panel_MnOpQr")
13
+ .addFullWidthCard(2, 7);
14
+ });
15
+
16
+ var page = createPage("Tab Layout 示例")
17
+ .setId("pppppppppppppppppppppppp")
18
+ .setDescription("展示根布局指标卡、tab 容器、panel 内图表/文本卡片,以及 selector 仍进入筛选器区。")
19
+ .setCardMargin(8)
20
+ .addRow([{ card: 0, w: 4 }], 3)
21
+ .addTab(salesTab);
22
+
23
+ registerPage(page.build());
@@ -0,0 +1,11 @@
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: "p2670dd6b74fc78c3e9c57ad", name: "月份", fdType: "DATE", metaType: "DIM" },
8
+ { fdId: "lb1c31b4dc86511a27049a98", name: "营收", fdType: "DOUBLE", metaType: "METRIC" },
9
+ { fdId: "aece10cd6806966be56f52ff", name: "成本", fdType: "DOUBLE", metaType: "METRIC" },
10
+ { fdId: "g1b16b52a16eb10c5e26d4bb", name: "数量", fdType: "INT", metaType: "METRIC" }
11
+ ], { displayType: "EXCEL" });
@@ -0,0 +1,8 @@
1
+ var sel = createSelector("区域筛选")
2
+ .setId("ssssssssssssssssssssssss")
3
+ .bindField(f("区域"))
4
+ .setMultiSelect(true)
5
+ .linkToAll()
6
+ .build();
7
+
8
+ registerSelector(sel);
@@ -115,7 +115,7 @@
115
115
 
116
116
  **布局单位**:
117
117
  1. 默认横向使用 12 列栅格;开启精细模式后使用 60 列栅格。
118
- 2. 推荐通过 `.setFineMode(true)` 设置为精细模式,且必须在任何 `addRow()` / `placeCard()` 等放置卡片的工具方法之前调用。
118
+ 2. 推荐通过 `.setFineMode(true)` 设置为精细模式,且必须在任何 `addRow()` / `placeCard()` / `addTab()` 等布局方法之前调用。
119
119
  3. `addRow()` 及其快捷方法的 `height` 省略或为 0 时使用默认行高(普通 6,精细 18)。
120
120
 
121
121
  | 方法 | 说明 |
@@ -129,6 +129,7 @@
129
129
  | `.addThirdWidthCards(ci1, ci2, ci3, height?)` | 三等分 |
130
130
  | `.addQuarterWidthCards(ci1, ci2, ci3, ci4, height?)` | 四等分 |
131
131
  | `.placeCard(cardIndex, x, y, w, h)` | 精确放置 Card,`w/h` 必须大于 0, `x/y/w/h` 必须显式填写 |
132
+ | `.addTab(tab, height?)` | 添加一个满宽 tab 容器;不传 height 时按第一个 panel 内容自动推导 |
132
133
  | `.setBackgroundColor(color)` | 页面背景色 |
133
134
  | `.setCardMargin(margin)` | 卡片间距 |
134
135
  | `.setFineMode(enabled)` | 开启/关闭精细模式 |
@@ -154,6 +155,55 @@
154
155
 
155
156
  注意:这里的高度建议是非精细模式下的页面生成建议,不覆盖 `addRow()` 在精细模式下省略高度时使用默认行高 `18` 的 API 行为。
156
157
 
158
+ ### Tab 布局
159
+
160
+ tab 用于把页面中的卡片分到多个 panel。适合同一主题下多组互斥查看的分析内容;若卡片较少或需要同时对照,优先直接平铺。关键总览指标通常放在 tab 外,tab 内按实际分析主题组织 panel。支持通过 `page.addTab(tab)` 添加满宽 tab,暂不支持半宽 tab 或 tab 嵌套。
161
+
162
+ | 方法 | 说明 |
163
+ |------|------|
164
+ | `createTab(name)` | 创建 tab 容器;`name` 用于脚本可读性,页面上显示的是各 panel 的 name |
165
+ | `.setId(tabId)` | **必填**。设置 tab ID,必须以 `tab_` 开头且同一页面内唯一。建议用 `guanvis gen-layout-id tab` 生成 |
166
+ | `.addPanel(name, callback)` | 添加 panel;callback 接收 `panel` 配置对象,必须在其中放入至少一张卡片 |
167
+ | `.setLabelStyle(style)` | 设置标签样式:`TabLabelStyle.UNDERLINE`(默认)、`CARD`、`CAPSULE`、`TRAPEZOID` |
168
+ | `.setAlignment(alignment)` | 设置标签对齐:`TabAlignment.LEFT`(默认)、`CENTER`、`RIGHT` |
169
+ | `.setTabSize(size)` | 设置标签宽度:`TabSizeType.MAX_CONTENT`(默认)或 `FLEX` |
170
+ | `panel.setId(panelId)` | **必填**。设置 panel ID,必须以 `panel_` 开头且同一页面内唯一。建议用 `guanvis gen-layout-id panel` 生成 |
171
+ | `panel.addRow(specs, height?)` | 在 panel 内按行放置卡片,写法同 `PageBuilder.addRow()` |
172
+ | `panel.addFullWidthCard(cardIndex, height?)` | 在 panel 内放一张满宽卡片 |
173
+ | `panel.placeCard(cardIndex, x, y, w, h)` | 在 panel 内精确放置卡片 |
174
+
175
+ **使用示例**:
176
+
177
+ ```javascript
178
+ var salesTab = createTab("销售分析")
179
+ .setId("tab_AbCdEf")
180
+ .setLabelStyle(TabLabelStyle.CAPSULE)
181
+ .setAlignment(TabAlignment.CENTER)
182
+ .addPanel("区域分析", function(panel) {
183
+ panel
184
+ .setId("panel_GhIjKl")
185
+ .addRow([{ card: 0, w: 6 }, { card: 1, w: 6 }], 6);
186
+ })
187
+ .addPanel("趋势分析", function(panel) {
188
+ panel
189
+ .setId("panel_MnOpQr")
190
+ .addFullWidthCard(2, 8);
191
+ });
192
+
193
+ var page = createPage("销售仪表板")
194
+ .setId("p184352b7a76776db5f534df")
195
+ .addTab(salesTab);
196
+
197
+ registerPage(page.build());
198
+ ```
199
+
200
+ 注意:
201
+
202
+ - tab 必须有 panel,panel 必须有卡片;不要创建空 tab 或空 panel。
203
+ - panel 内放置非 selector 卡片(图表、文本、图片、自定义图表等)。selector 仍通过 `registerSelector()` 进入页面筛选器区。
204
+ - panel 布局跟随页面精细模式:`page.setFineMode(true)` 后,panel 也使用 60 列栅格和精细模式默认行高。
205
+ - 不需要手工设置 tab 高度;只有明确要固定高度时才给 `page.addTab(tab, height)` 传第二个参数。
206
+
157
207
  ### SelectorBuilder(筛选器)
158
208
 
159
209
  筛选器是特殊的卡片(`cdType=6`),放在页面顶部的筛选器面板中,可联动影响其他图表卡片。
@@ -173,6 +223,7 @@
173
223
  | `.setDefaultType(type)` | 默认值类型:`SelectorDefaultType.FIRST_PICK`(默认)、`FIXED_VALUE` 或 `ALL` |
174
224
  | `.setDefaultAll()` | 设置默认"全部"(不筛选),等价于 `setDefaultType(SelectorDefaultType.ALL)` |
175
225
  | `.setDefaultValue(values, displayValues?)` | 设置固定默认值,自动切换为 FIXED_VALUE 类型 |
226
+ | `.setDefaultDateRange(start, end, displayValues?)` | 设置 `CALENDAR` 日期区间默认值,自动切换为 FIXED_VALUE;比手写数组更不容易漏填区间端点 |
176
227
  | `.setFirstPickLink(bool)` | FIRST_PICK 模式下是否联动刷新(默认 false) |
177
228
  | `.setDisplayType(type)` | 展示类型(仅 `DS_ELEMENTS` 使用):`SelectorDisplay.SEARCH_LIST`(单选下拉)、`SEARCH_BOX`(多选下拉)、`CHECKBOX`(复选框)、`RADIO`(单选框)、`BUTTON_GROUP`(按钮组)。不设置时根据 multiSelect 自动选择。`DS_INTERVAL` 类型不需要此设置 |
178
229
  | `.setShowSelectAll(bool)` | 是否显示"全选"(默认 true) |
@@ -207,6 +258,21 @@ registerSelector(sel);
207
258
  // 也可设置 .setFilterType("EQ") 单值等于,或 "GT"/"LT" 等比较
208
259
  ```
209
260
 
261
+ ```javascript
262
+ // selector_03_date.js — 日期范围(CALENDAR)
263
+ var sel = createSelector("日期筛选")
264
+ .setId("abc123def456abc123def456")
265
+ .setSelectorType(SelectorType.CALENDAR)
266
+ .bindField(f("订单日期"))
267
+ .setDefaultDateRange("2026-01-01", "2026-01-31")
268
+ .linkToAll()
269
+ .build();
270
+ registerSelector(sel);
271
+ // CALENDAR 默认 filterType="BT"(区间),固定默认值必须是 2 个日期值。
272
+ // 支持 "2026-01-15"、"2026-01"、"2026-Q1"、"2026-W03"、"2026" 等格式。
273
+ // 同一个默认值数组必须使用同一粒度;未显式 setGranularity 时会从默认值格式推断粒度。
274
+ ```
275
+
210
276
  ```javascript
211
277
  // selector_03_timemacro.js — 快捷日期(TIME_MACRO)
212
278
  var sel = createSelector("快捷日期")