# Jimubi Dashboard

> Use when user asks to create/design a dashboard (仪表盘/看板), data kanban, or says "创建仪表盘", "生成仪表盘", "做一个仪表盘", "数据看板", "做一个看板", "创建看板", "数据面板", "统计看板", "运营看板", "create dashboard", "generate dashboard", "design dashboard", "data kanban", "KPI dashboard". Also triggers for QQY/敲敲云 mode dashboards: "敲敲云仪表盘", "低代码应用仪表盘", "应用内仪表盘", "给应用添加图表", mentions appId+tenantId in dashboard context. Also triggers whe…

- **Type:** Skill
- **Install:** `agentstack add skill-jeecgboot-skills-jimubi-dashboard`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [jeecgboot](https://agentstack.voostack.com/s/jeecgboot)
- **Installs:** 0
- **Category:** [Data & Analytics](https://agentstack.voostack.com/c/data-and-analytics)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [jeecgboot](https://github.com/jeecgboot)
- **Source:** https://github.com/jeecgboot/skills/tree/main/jimubi-dashboard
- **Website:** https://jeecg.com

## Install

```sh
agentstack add skill-jeecgboot-skills-jimubi-dashboard
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# JeecgBoot 仪表盘 AI 自动生成器

将自然语言的仪表盘需求转换为 drag page 配置，并通过 API 自动创建。

> **本 skill 专门处理仪表盘（default）模式**：网格布局（24列栅格），亮色主题，带卡片头，适用于日常数据看板。
> 大屏请使用 `jimubi-bigscreen` skill。

## ⚠️ 强制规则：所有仪表盘相关操作必须优先通过本 skill 处理（无任何例外）

**触发范围**：凡涉及仪表盘的任何操作，包括但不限于：
- 创建/删除/修改仪表盘页面
- 添加/编辑/删除组件
- 数据集（SQL/API/文件/WebSocket）的创建与绑定
- 数据源的创建、编辑、测试（包括修改用户名、密码、连接参数等）
- 模板复制、页面配置修改
- 组件联动、钻取、外部链接

**禁止行为**：
- 未调用本 skill，直接读 memory 找凭据自行执行
- 未调用本 skill，自己探索 API 路径后直接调用
- 以"操作太简单不需要 skill"为由跳过
- **用 curl/bash/Agent 子代理探测仪表盘 API 端点**（正确做法：先调用本 skill，再在 skill 上下文内执行）

**正确执行顺序**：
1. 用户提出仪表盘相关需求
2. **第一步必须调用本 skill**（`Skill jimubi-dashboard`）
3. 在 skill 上下文中读取凭据、选择脚本、执行操作

## 按需加载指南

本 skill 采用分层加载：核心规则始终在上下文中，专题文档按需读取。

| 场景 | 读取文件 |
|------|---------|
| **敲敲云（QQY）低代码应用仪表盘** | 核心规则已内联（识别条件/初始化/必填字段/工作流）；完整 config 模板/UI组件配置/批量生成/按钮操作 → 读取 `references/qqy-guide.md` |
| **QQY全组件仪表盘（30统计图表+7UI，一次生成）** | 直接用 `gen_qqy_all_comps.py`（**无需 Write 脚本**）：`SKILL_REFS="$HOME/.claude/skills/jimubi-dashboard/references"; PYTHONIOENCODING=utf-8 PYTHONPATH="$SKILL_REFS:$SKILL_REFS/scripts" py "$SKILL_REFS/scripts/gen_qqy_all_comps.py" API_BASE TOKEN --page-id PAGE_ID --app-id APP_ID --tenant-id TENANT_ID --form-code FORM_CODE [--form-name 表单名称] [--form-type design\|online]` |
| 需要示例/演示数据（用户未提供数据源）| `references/api-dataset-examples.md`（92条公开 mock API，按行业分类，直接用 `dataset_ops.py create-api` 创建） |
| 创建/绑定/修改数据集（SQL/API/文件）| `references/dataset-guide.md`（**仅自定义脚本时需要**；使用预置脚本时**无需读取**） |
| **多文件数据集（FILES）+ 图表** | 直接用 `files_ops.py create-bind`（**无需 Write 脚本**） |
| 创建 WebSocket 数据集 | `references/dataset-guide.md`「创建 WebSocket 数据集」章节 |
| **创建 JSON 数据集 + 图表** | 无需读文档；规则已内联：`dataType:'json'`，数据放 `querySql`（**禁止放 content**），无需 dbSource/queryFieldBySql，直接 `_request POST /add` + `batch-add --specs` 绑定 |
| **多图表+联动批量生成**（≥2个图表且需要联动） | 直接用 `multi_chart_linkage.py` |
| 从模板复制创建仪表盘 | 直接用 `template_ops.py copy` |
| 模板复制遇到问题时 | `references/template-copy-guide.md` |
| 地图组件（JAreaMap 等）| `references/map-guide.md` + 静态数据用 `references/map-static-data.md` |
| 创建数据源 + SQL数据集 + 图表 | `datasource_ops.py create` 创建数据源 → `dataset_ops.py create-sql` 创建数据集 → `comp_ops.py batch-add --specs` 绑定图表（在每个 spec 的 `"config"` 中传入 `dataType:2/dataSetId/dataMapping` 即可，视觉配置由 `default_configs.json` 自动提供）。**仅当 SQL 含 FreeMarker / 需要 queryFieldBySql 回写时，才需写全流程自定义脚本。** |
| 自写 Java API 接口 + API 数据集 + 批量图表 | 参考 `references/pitfalls.md`「完整工作流：自写API接口」章节 |
| **YApi Mock 系统 + API 数据集** | 直接用 `yapi_ops.py create-mock`（固定项目：**proj_id=57，catid=1157，basepath=/claude**） |
| 签名接口 / 数据源管理 / NoSQL 数据源 | `references/signing-datasource-guide.md` |
| 组件联动 / 钻取 | 直接用 `linkage_ops.py` |
| 组件外部链接跳转 | 直接用 `link_ops.py` |
| 字典翻译（jimu_dict） | `references/dict-guide.md` |
| 修改页面配置（背景色/背景图/风格/组件主题）| `references/page-config-guide.md`（**仪表盘无水印功能**，水印仅大屏专有） |
| 遇到奇怪问题时查阅 | `references/pitfalls.md` |
| 组件样式配置路径 | `references/bi-comp-option-config.md`（**仅当 skill.md 中未列出目标组件时才读取**；JStatsSummary/JCapsuleChart/JGauge/JProgress/JScrollBoard/JNumber 已内联在「常用组件配置路径速查」章节） |
| 完整组件类型清单 | `references/bi-component-types.md`（已内联在「图表查询与推荐」章节，一般无需再读取） |
| 新增组件默认尺寸/数据/option | `references/core-configs/component-defaults.md` |
| Online表单/设计器表单生成图表（dataType:4）| `references/online-design-form-chart-guide.md` |
| **bi_utils 初始化 / 字段访问规则** | 已内联在「bi_utils 使用规则（强制）」章节，**无需读取外部文件** |
| **comp_ops.py 参数与数据绑定格式** | 已内联在「快捷操作：comp_ops.py」章节，**无需读取外部文件** |
| **linkage_ops.py 联动/钻取命令** | 已内联在「快捷操作：linkage_ops.py」章节，**无需读取外部文件** |
| **link_ops.py 外部链接 + 自定义JS** | 已内联在「快捷操作：link_ops.py」章节，**无需读取外部文件** |
| 踩坑速查 | 已内联在「核心踩坑速查」章节（~45条），**优先查此处**；极端复杂场景再读 `references/pitfalls.md` |
| 图库（图标/图片）管理 | 已内联在「图库管理」章节，**无需读取外部文件** |

## SQL数据集创建标准流程（强制）

> **触发条件**：用户说"使用SQL数据集"、"增加SQL数据集"、"统计 xxx 表"、"生成图表"等涉及 SQL 数据集的任何场景，必须严格按以下四步执行，**不得跳过第1步**。

### 第1步：确认数据源（必须询问，禁止擅自选择，无任何例外）

> ⚠️ **无论使用任何方式创建 SQL 数据集，都必须先询问数据源，禁止直接执行。**

**执行步骤（强制）：**
1. 先运行以下命令列出所有可用数据源
2. 向用户展示列表，询问"请问使用哪个数据源？"
3. 等待用户选择后，用选定的数据源 ID 继续执行

```bash
SKILL_REFS="$HOME/.claude/skills/jimubi-dashboard/references"
PYTHONIOENCODING=utf-8 PYTHONPATH="$SKILL_REFS:$SKILL_REFS/scripts" py "$SKILL_REFS/scripts/datasource_ops.py" list "" "TOKEN"
```

### 第2步：根据业务场景自行编写SQL
- 用户指定数据源后，根据用户描述的业务场景，自行设计并编写合适的 SQL 语句
- **🚨 comp_ops.py add/batch-add 不支持 `--create-sql`/`--sql-file`/`--ds-name`/`--db-source`**，这些参数不存在，使用会报 `unrecognized arguments`
- **推荐方式（普通SQL，无 FreeMarker 动态条件）**：`dataset_ops.py create-sql` 创建数据集，再用 `comp_ops.py batch-add --specs` 绑定图表；每个 spec 的 `"config"` 字段只需传 `dataType/dataSetId/dataMapping` 等数据绑定字段，视觉配置自动从 `default_configs.json` 取
- **全流程自定义脚本（仅限以下场景）**：SQL 含 FreeMarker 动态参数（``/`${}`）、需要 queryFieldBySql 自动回写字段、或需要在同一脚本内串联复杂逻辑时，才用 Write 工具写入 Python 脚本执行（详见下方"全流程自定义脚本模板"章节）
- **⚠️ "singleFile" 是文件数据集的 dataType 值**（`dataType: 'singleFile'`，上传 Excel/CSV），与 SQL 数据集脚本模式无关，禁止把 SQL 场景的自定义脚本称为"singleFile 脚本"

### 第3步：创建SQL数据集
- 分组必须使用 **"示例数据集"**（`dataset_ops.py create-sql` 已内置 `--group "示例数据集"` 默认值）
```bash
SKILL_REFS="$HOME/.claude/skills/jimubi-dashboard/references"
PYTHONIOENCODING=utf-8 PYTHONPATH="$SKILL_REFS:$SKILL_REFS/scripts" py "$SKILL_REFS/scripts/dataset_ops.py" create-sql $API_BASE $TOKEN \
  --name "数据集名称" --db-source "数据源ID" \
  --sql "SELECT name, value FROM table GROUP BY name" \
  --fields "name:String,value:Integer"
```
- 数据集创建完成后，**必须执行查询解析验证**确认数据正常返回

### 第4步：后续绑定操作（按需）
- 询问用户是否需要将数据集绑定到图表组件
- 如需要，优先使用 `dataset_ops.py create-sql` + `comp_ops.py batch-add --specs` 组合；仅当场景复杂（FreeMarker/需字段回写）时才写全流程自定义脚本

---

## 执行效率规则（强制）

### 简单操作直接执行，禁止多余探索

**对所有仪表盘操作，必须跳过以下步骤直接执行：**
- 禁止启动 Explore 子代理去探索源码
- 禁止启动子代理去读 data.ts 默认配置（skill 文档已包含完整信息）
- 禁止读取 template-copy-guide.md（template_ops.py copy 已实现全部流程）
- 禁止使用预置脚本时读取 dataset-guide.md（`dataset_ops.py`/`comp_ops.py --dataset-name` 已封装全部逻辑）
- 禁止执行预置脚本前先 `--help` 查看用法（skill 文档已包含完整参数说明）
- 禁止展示设计摘要等待确认（除非用户明确要求确认）

**耗时目标：**

| 操作类型 | 目标耗时 | 做法 |
|---------|---------|------|
| 单组件增/删/改/查 | ≤30s | comp_ops.py 一条命令（SKILL_REFS + 全路径执行，1轮完成） |
| 数据集 + 单组件 | ≤60s | singleFile 脚本（7步完整流程：/add → queryFieldBySql → /edit → getAllChartData → config → append → save_page） |
| 复合操作（数据集 + 多组件） | ≤60s | 并行 Bash 调用 |
| 模板复制创建仪表盘 | ≤60s | template_ops.py copy |
| 多图表+联动（≥2图+联动） | ≤10s | `multi_chart_linkage.py` 单脚本 |

### 反模式检查清单（出现任何一条就说明在浪费时间）

- **🚨 用 `py -c` 或 `ls` 探索已知信息**（default_configs.json 的路径、可用键名、组件类型等在 skill.md 和 map-guide.md 中已完整列出，禁止用探索命令去"验证"，直接写脚本执行。探索命令还会引入 `$HOME` Unix 路径格式问题导致 `FileNotFoundError`。）
- **🚨 添加≥2个组件时用单独的 `add` 并行执行**（并行导致乐观锁冲突丢失组件！必须用 `batch-add --specs '[...]'` 一次保存）
- **🚨 `add` 命令后 chartData 为 `[]`**（comp_ops.py 已从 default_configs.json 加载完整默认数据，出现空数据说明 default_configs.json 未被复制到工作目录）
- **🚨 静态 chartData 禁止使用 comp_ops.py 兜底数据**（default_configs.json 为空时 comp_ops.py 回落到内置占位数据：JBar→A/B/C/D/E、JStackBar→收入/支出，这些是虚构数据。必须从前端源码 `data.ts`（位于 `packages/dragEngine/components/jeecgComponents/data.ts`）中读取各组件 `compConfig.chartData` 的真实值，通过每个 spec 加 `"config":{"chartData":[...]}` 字段，或 singleFile 脚本中调用 `comp_ops._build_comp_config(comp_type, title, {"chartData": json.dumps(real_data)})` 覆盖）
- **⚠️ 创建 SQL 数据集时跳过询问数据源直接执行**（无论使用哪种脚本，执行前必须先 `datasource_ops.py list` 列出数据源，询问用户）
- **⚠️ 用户已给出 API 地址时先用 `--dataset-name` 探测或先添加静态组件**（直接 `dataset_ops.py create-api` → `comp_ops.py add --dataset-name`，2 轮完成）
- **⚠️ singleFile 全流程脚本第4步（add_component）前漏写 query_page + 缓存 template**（漏掉两行缓存代码时，`save_page` 将仪表盘**所有已有组件永久清空**）
- **⚠️ singleFile 场景将"建数据集脚本"和"添加图表脚本"拆成两个**（必须一个脚本完成全部流程）
- **⚠️ singleFile 场景用 `comp_ops.py --dataset-name` 绑定图表**（按字段数组顺序自动映射，导致图表显示错误数据）
- **⚠️ 执行 `py script.py` 时不加 `PYTHONIOENCODING=utf-8`**（Windows 默认 GBK 编码，中文必定乱码）
- **⚠️ 自定义脚本用 `cp bi_utils.py` 而非 `sys.path.insert`**（cp 路径硬编码，换机器失效，且需要清理。**必须用 `sys.path.insert(0, os.path.join(os.path.expanduser('~'), '.claude', 'skills', 'jimubi-dashboard', 'references'))`**）
- **⚠️ 预置脚本用 `py comp_ops.py` 短名调用**（PYTHONPATH 只解决 `import bi_utils`，不解决脚本文件查找！`py comp_ops.py` 在当前目录找脚本，必然报"No such file"。**必须用全路径**：`SKILL_REFS="$HOME/.claude/skills/jimubi-dashboard/references"; PYTHONIOENCODING=utf-8 PYTHONPATH="$SKILL_REFS:$SKILL_REFS/scripts" py "$SKILL_REFS/scripts/comp_ops.py" ...`）
- **🚨 create_page 后紧接 save_page**（create_page 内部已保存一次，updateCount 变为1；随即 save_page 携带 updateCount=0 触发乐观锁报"仪表盘内容不是最新"。**save_page 只在添加/修改组件后调用**）
- **⚠️ Write 脚本时写占位符 TOKEN/API_BASE 再单独 Edit 更新**（凭据已在上下文中，必须 Write 时直接填入最终值）
- **⚠️ 多图表+联动场景逐个调用 comp_ops.py + linkage_ops.py**（用 `multi_chart_linkage.py` 单脚本，节省约80%耗时）
- **⚠️ 直接调用 bi_utils.add_xxx() + save_page() 添加组件到已有页面**（会覆盖已有组件！必须先 query_page + 缓存 template）
- **⚠️ 写自定义脚本用 bash heredoc 而非 Write 工具**（heredoc 含单引号必报错）
- **⚠️ 脚本中用拼音/英文替代中文字段名、组件名**（如把"基础柱形图"写成 `JiChu-ZhuXingTu`，用户无法识别）
- **⚠️ 用户未指定数据来源时擅自使用公开 mock API**（必须先执行 Step 0.1 询问数据来源）
- **⚠️ 批量绑定数据集时 dataMapping.filed 写成字段名**（`filed` 是语义槽位标签"维度"/"数值"/"分组"，`mapping` 才是字段名）
- **⚠️ dataMapping 按数组索引顺序映射而非语义映射**（必须按语义显式指定：单系列 `[{维度→name},{数值→value}]`，多系列 `[{分组→type},{维度→name},{数值→value}]`）
- **⚠️ 仪表盘 size 字段用栅格单位**（`config.size.width/height` 必须是像素：`width = w×75, height = h×11`）
- **🚨 QQY全组件仪表盘需要从头 Write 脚本**（直接用 `gen_qqy_all_comps.py` 预置脚本，参数：`--page-id --app-id --tenant-id --form-code`，1轮完成，耗时 **本章节专门处理低代码应用（敲敲云）模式下的仪表盘。** 如果用户只是做普通仪表盘，跳过本章节。

### 识别条件（满足任一即进入 QQY 模式）

- 用户提及"敲敲云"、"低代码应用"、"应用仪表盘"、"应用内仪表盘"
- 用户提供了 `appId`（低代码应用 ID）和 `tenantId`（租户 ID）
- 操作上下文是在低代码应用（`/myapp/{appId}/...` 路由）内的仪表盘
- 用户说"给某应用创建仪表盘"、"在应用里加一个图表"

### QQY 模式 vs 标准仪表盘核心区别

| 特性 | 标准仪表盘 | QQY 仪表盘 |
|------|-----------|-----------|
| `isLowApp` | 前端标识，不存库 | 前端标识，不存库（**禁止写入数据库**，仅前端引擎据此切换至 DragEngineQqyun） |
| 组件库来源 | `menuData` | `qqyMenuData`（不含 JBreakRing 等） |
| 主要数据来源 | SQL/API 数据集（dataType=2） | 设计器/Online 表单（dataType=4） |
| 额外前置条件 | 无 | **appId**（应用ID）+ **tenantId**（租户ID） |
| HTTP 附加头 | 无 | `X-Low-App-ID: {appId}` + `X-Tenant-Id: {tenantId}` |
| 仪表盘归属 | 系统级，无应用关联 | 应用级，`lowAppId` 字段关联到具体应用 |
| 数据查询接口 | `getAllChartData` | `getTotalData`（QQY 统计表单数据）|
| 数据集管理 | 前端可见 | 隐藏，用户不感知 |
| 按钮操作 | 无特殊绑定 | 支持创建记录/打开视图/调用业务流程等 5 种 |

### QQY 模式额外前置条件

用户在标准前置条件基础上，**还必须提供**：
3. **appId**（低代码应用 ID）：从页面 URL `/myapp/{appId}/...` 或应用管理中获取
4. **tenantId**（租户 ID）：从系统设置→租户管理中获取，或询问用户

> 若用户未提供 appId/tenantId，**必须先询问**，不得用占位符代替。

### QQY 模式脚本初始化（强制）

QQY 模式下所有脚本必须在 `init_api` 后立即设置额外请求头，**同时创建页面时必须在 body 中显式传入 `lowAppId`**，确保应用归属正确保存到数据库：

```python
import json, time
import bi_utils

API_BASE = ''
TOKEN = 'your-token'
APP_ID = '应用ID'          # 低代码应用ID（必填）
TENANT_ID = '1'            # 租户ID（必填）
PAGE_ID = '已有页面ID'     # 或稍后调用 create_page 获取

# QQY 模式初始化（必须设置 extra_headers）
bi_utils.init_api(API_BASE, TOKEN, extra_headers={
    'X-Low-App-ID': APP_ID,
    'X-Tenant-Id': str(TENANT_ID),
})
```

> **⚠️ 创建页面时必须在 body 中传 `lowAppId`（强制）：**
> ```python
> page_resp = bi_utils._request('POST', '/drag/page/add', data={
>     'name': '仪表盘名称',
>     'style': 'default',
>     'lowAppId': APP_ID,   # 必须显式传入，确保存库
>     # ❌ 禁止传 isLowApp：这是前端标识，不存数据库
> })
> ```
> **标准仪表盘**创建时不传 `lowAppId`。

> **证据**：`DragEngine.vue` onMounted 中执行 `localStorage.setItem(ConfigEnum.DRAG_APP_ID, props.lowAppId)`，请求拦截器 `request.js` 中：
> `config.headers[ConfigEnum.LOW_APP_ID] = localStorage.getItem(ConfigEnum.DRAG_APP_ID)`
> 后端 `OnlDragPageController.java`：`String lowAppId = TokenUtils.getLowAppIdByRequest(request)` → 写入 `onlDragPage.setLowAppId(lowAppId)`

### QQY 仪表盘列表查询

> **🚨 强制规则：用户未提供 appId 时，必须先询问，禁止自行猜测或遍历已知 appId**
>
> 正确流程：
> 1. 用户说"在某仪表盘中操作"但未给 appId → 先问："请提供该应用的 appId（可从浏览器 URL `/myapp/{appId}/...` 获取）"
> 2. 拿到 appId 后，**优先通过应用菜单接口查找仪表盘**（可按名称精确定位 pageId）
> 3. 确认 pageId 后再执行操作

**✅ 推荐方式：通过应用菜单查找仪表盘（按名称定位 pageId）**

```python
# 查询应用菜单，按名称找到仪表盘的 pageId（menuUrl 字段）
resp = bi_utils._request('GET', '/online/lowAppMenu/list',
    params={'appId': APP_ID, 'pageSize': 100})
records = resp.get('result', {}).get('records', []) or []
for m in records:
    # type='drag' 为仪表盘菜单项，menuUrl 即为 pageId
    print(m['id'], m.get('type'), m.get('menuName'), m.get('menuUrl'))
# 示例输出：
# 2047251681335025666 | drag | 销量分析 | 1207230587321589760
#                                 ↑名称      ↑这就是 PAGE_ID
```

**备用方式：通过 page/list 过滤（结果需二次验证 lowAppId）**

```python
# ⚠️ page/list 接口不精确过滤，返回结果混有其他应用页面，需手动校验 lowAppId
result = bi_utils._request('GET', '/drag/page/list', params={
    'lowAppId': APP_ID,
    'pageNo': 1,
    'pageSize': 50
})
pages = result.get('result', {}).get('records', [])
# 必须二次过滤，排除 lowAppId 不匹配的页面
pages = [p for p in pages if p.get('lowAppId') == APP_ID]
for p in pages:
    print(p['id'], p['name'])
```

### QQY 可用组件（快速参考）

**统计图表（30个）**：JBar, JStackBar, JMultipleBar, JNegativeBar, JHorizontalBar, JRankingList, JTotalProgress, JLine, JArea, JMultipleLine, DoubleLineBar, JWordCloud, JPie, JRing, JRose, JFunnel, JPyramidFunnel, JRadar, JCircleRadar, JColorGauge, JGauge, JAntvGauge, JNumber, JScatter, JBubble, JPivotTable, JAreaMap, JBubbleMap, JHeatMap, JBarMap

❌ 禁止添加：JDynamicBar, JMixLineBar, JSmoothLine, JProgress, JCommonTable, JList, JGrowCard, JFlyLineMap 等（不在 qqyMenuData 中）

**UI/功能组件（7个）**：JCustomButton（按钮）, JText（文本）, JFilterQuery（查询条件）, JCarousel（轮播图，需绑定imgupload字段）, JDragEditor（富文本）, JIframe（嵌入URL）, JCurrentTime（实时日期）

❌ 禁止添加：JTabs, JGrid, JImg, JCalendar, JWaitMatter, JRadioButton 等

### dataType=4 必填字段（QQY 统计图表核心）

每个 QQY 统计图表 config 必须包含：
1. `dataType: 4` + `formType/formId/formName/tableName/appId/appType`
2. `nameFields/valueFields/typeFields/sorts/filter/filterField`（含 `filter.conditionMode:"and"` + `filter.conditionFields:[]`）
3. `compStyleConfig`（含 `summary/showUnit/assist` 完整结构）
4. `analysis`（含 `showData:1, isRawData:True, showMode:1, trendType:'1'`）
5. 笛卡尔坐标图：`option.series:[{type:'bar/line/scatter'}]` + xAxis/yAxis + grid
6. `chart:{category,subclass,isGroup}` + `seriesType:[]`（JPivotTable/地图除外）

> **完整 config 模板、UI组件配置、批量生成流程、按钮操作类型**：见 `references/qqy-guide.md`

### QQY 仪表盘创建完整工作流

```
Step 1: 确认 appId + tenantId（必须询问用户）
Step 2: 确认仪表盘名称
Step 3: 在 bi_utils.init_api 中设置 extra_headers
Step 4: 调用 /drag/page/add 创建页面，body 中必须显式传 lowAppId: APP_ID（不传 isLowApp）
Step 5: 添加每个统计图表前，必须执行【四步询问流程】（见下方）
Step 6: 将仪表盘菜单归入目标分组（见下方「QQY 仪表盘菜单归组」章节）
Step 7: 创建完成后输出仪表盘 ID 和分享地址（格式：{前端域名}:{端口}/drag/share/{appId}/{pageId}）
```

### QQY 仪表盘菜单归组（创建后必须执行）

QQY 仪表盘页面创建后，其对应的应用菜单项 `parentId` 默认为空（不在任何分组下），**必须手动调用接口将其归入目标分组**，否则在低代码应用侧边栏中无法在分组下看到该仪表盘。

**Step 1：查询应用菜单，找到目标分组 ID**

> ⚠️ 必须用 `appId` 参数过滤，用 `lowAppId` 参数无效（会返回所有应用的菜单）

```python
r = requests.get(f'{API_BASE}/online/lowAppMenu/list', headers=HEADERS,
    params={'appId': APP_ID, 'pageSize': 100})
records = r.json().get('result', {}).get('records', []) or []
# 找 type='group' 的分组，以及 type='drag' 的仪表盘菜单项（parentId 为空即是待归组的）
for m in records:
    if m.get('appId') == APP_ID:
        print(m['id'], m['type'], m['menuName'], m.get('parentId'))
```

**Step 2：调用 edit 接口设置 parentId**

```python
body = {
    'id': MENU_ID,            # 仪表盘菜单项 ID（type='drag' 的那条）
    'parentId': GROUP_ID,     # 目标分组 ID（type='group'）
    'menuName': '仪表盘名称',
    'type': 'drag',
    'menuUrl': PAGE_ID,       # 仪表盘页面 ID
    'appId': APP_ID,
    'orderNum': 4,
}
r = requests.put(f'{API_BASE}/online/lowAppMenu/edit', headers=HEADERS, json=body)
# {"success":true,"message":"编辑成功!"} 即为成功
```

> 注意：请求头必须包含 `X-Low-App-ID` 和 `X-Tenant-Id`，否则鉴权失败。

---

### 🚨 QQY 统计图表四步询问流程（强制，每个统计图表都必须执行）

每次在 QQY 仪表盘中添加**任意一个统计图表**（30个范围内），必须严格执行以下四步，**禁止自行假设表单或字段**：

**Step 0：询问使用当前应用还是其他应用的表单**
```
询问用户："请问使用当前应用下的表单，还是其他应用下的表单？"
- 当前应用 → 用当前 APP_ID 继续 Step A
- 其他应用 → 询问"请提供应用名称或应用ID"，等待用户提供后继续
```

**Step A：同时查询普通表单和聚合表，分两组展示 → 询问用户选择**
```python
# 同时调用两个接口（携带 X-Low-App-ID 头）：
GET /desform/api/list/options?appId={APP_ID}          # 普通设计器表单
GET /drag/onlDragTableRelation/list?pageSize=20        # 聚合表

# 向用户分两组展示（对应前端 FormSelectModal 两个 Tab）：
# 【表单（普通）】
# | formCode | 表单名称 | type |
# | ding_dan_guan_li_oaf0 | 订单管理 | design |
#
# 【聚合表】
# | id | 聚合表名称 | 类型标签 |
# | 1207232765004226560 | [聚合] 测试 | aggregation |
# （类型标签判断：relationForms.formType=='aggregation' → '[聚合工厂]'，否则 '[聚合]'）
#
# 询问："请问使用哪个表单？（请指明普通表单 / 聚合表）"
# 等待用户选择后继续
```

**Step B：查询并展示字段 → 询问用户选择维度/数值字段**
```python
# 根据用户选择分两种情况：

# ① 普通表单（type=design）：
GET /desform/api/fields/{formCode}
# result 是 dict，字段列表在 result['fields']
# 跳过 file-upl

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [jeecgboot](https://github.com/jeecgboot)
- **Source:** [jeecgboot/skills](https://github.com/jeecgboot/skills)
- **License:** Apache-2.0
- **Homepage:** https://jeecg.com

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** yes
- **Shell / process execution:** yes
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-jeecgboot-skills-jimubi-dashboard
- Seller: https://agentstack.voostack.com/s/jeecgboot
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
