---
name: jimubi-dashboard
description: "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 when user describes dashboard/kanban requirements like \"做一个运营数据看板\" or mentions grid-layout data display like \"统计系统数据\". Make sure to use this skill for dashboards (仪表盘/看板) — NOT big screens (大屏), which use completely different positioning, styling, and component configurations."
---

# JeecgBoot 仪表盘 AI 自动生成器

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

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

## 仪表盘特征

- **布局**：24 列栅格，坐标和尺寸单位为**栅格单位**（如 x=0, y=0, w=6, h=17）
- **主题**：默认 `default`，白色背景，深色文字
- **无背景图**：仪表盘通常不设背景图
- **卡片头**：仪表盘支持卡片头，但图表组件的 `card.title` 应留空（标题由 ECharts `option.title` 显示），避免标题重复
- **颜色体系**：白底 `#FFFFFF`、深灰标题 `#464646`、浅灰轴标签 `#909198`、浅灰网格 `#F3F3F3`

## 仪表盘栅格布局规则

| 组件类型 | 推荐 w | 推荐 h | 说明 |
|---------|--------|--------|------|
| JNumber | 6 | 17 | 数字卡片，4 个一行正好 24 列 |
| JLine/JBar/JSmoothLine | 12-14 | 28-35 | 图表，通常半宽或更宽 |
| JPie/JRing/JRose | 10-12 | 28-35 | 饼图/环形图 |
| JHorizontalBar | 12 | 28-35 | 横向柱状图 |
| JTable/JCommonTable | 12 | 30-40 | 数据表格 |
| JScrollTable | 12 | 30-40 | 滚动表格 |
| JScrollRankingBoard | 12 | 30-35 | 排行榜 |
| JGauge | 6-8 | 25-30 | 仪表盘表盘 |
| JLiquid | 6 | 25-30 | 水球图 |

**布局原则：**
- 总宽度 24 列，组件 w 之和不要超过 24
- 第一行通常放 4 个 JNumber（w=6×4=24）
- 第二行放图表组合（如 JLine w=14 + JPie w=10 = 24）
- 第三行放表格/排行等

## 前置条件

用户必须提供：
1. **API 地址**：JeecgBoot 后端地址（如 `https://api3.boot.jeecg.com`）
2. **X-Access-Token**：JWT 登录令牌（从浏览器 F12 获取）

## 交互流程

### Step 0: 解析用户需求

| 信息 | 默认值 | 示例 |
|------|--------|------|
| 页面名称 | 用户指定 | "运营数据看板" |
| 主题 | default | default |
| 组件列表 | 从描述中解析 | 用户总数(数字)、增长趋势(折线)、来源分布(饼图) |

### Step 1: 识别组件并选择类型

阅读 `references/bi-component-types.md` 获取完整组件类型清单。

**常用仪表盘组件速查：**

| 用户描述关键词 | 组件 component | 说明 |
|---------------|---------------|------|
| 数字/KPI/指标/总数 | `JNumber` | 数字指标卡（带卡片头） |
| 柱状图 | `JBar` | 基础柱状图 |
| 横向柱状图 | `JHorizontalBar` | 水平柱状图 |
| 折线图/趋势 | `JLine` | 折线图 |
| 曲线图 | `JSmoothLine` | 平滑曲线 |
| 柱线混合 | `JMixLineBar` | 柱状+折线混合 |
| 饼图 | `JPie` | 饼图 |
| 环形图 | `JRing` | 环形图 |
| 玫瑰图 | `JRose` | 南丁格尔玫瑰图 |
| 表盘 | `JGauge` | 仪表盘表盘 |
| 水球图 | `JLiquid` | 水球图 |
| 进度条 | `JProgress` | 进度条 |
| 雷达图 | `JRadar` | 雷达图 |
| 漏斗图 | `JFunnel` | 漏斗图 |
| 地图 | `JAreaMap` | 区域地图 |
| 数据表格 | `JTable` / `JCommonTable` | 数据表格 |
| 滚动表格 | `JScrollTable` | 自动滚动表格 |
| 排行榜 | `JScrollRankingBoard` | 滚动排行榜 |
| 日历 | `JCalendar` | 日历组件 |

### Step 2: 展示设计摘要并确认

**必须展示，等待用户确认后再执行：**

```
## 仪表盘设计摘要

- 页面名称：运营数据看板
- 主题：default

### 组件列表

| 序号 | 组件名称 | 组件类型 | 位置(x,y) | 尺寸(w×h) | 数据源 |
|------|---------|---------|-----------|----------|--------|
| 1 | 总用户数 | JNumber | (0,0) | 6×17 | 静态数据 |
| 2 | 今日活跃 | JNumber | (6,0) | 6×17 | 静态数据 |
| 3 | 用户增长趋势 | JLine | (0,17) | 14×35 | 静态数据 |
| 4 | 用户来源 | JPie | (14,17) | 10×35 | 静态数据 |

确认以上信息正确？(y/n)
```

### Step 3: 调用 API 创建仪表盘

**优先使用共通工具库 `bi_utils.py`**（两个位置均有副本）：
- Skills 目录（权威副本）：`C:\Users\zhang\.claude\skills\jimubi-dashboard\references\bi_utils.py`
- 后端项目根目录（运行副本）：`{后端项目根目录}\bi_utils.py`

> 如果后端项目根目录没有 `bi_utils.py`，先从 skills 目录复制过去再使用。

**执行步骤：**
```
1. 确认后端项目根目录有 bi_utils.py（没有则从 skills 复制）
2. Write 工具 → 写入业务脚本 create_xxx_dashboard.py（项目根目录）
3. Bash 工具 → cd {后端项目根目录} && python create_xxx_dashboard.py
4. Bash 工具 → rm create_xxx_dashboard.py（清理临时脚本）
```

**仪表盘创建示例：**
```python
import sys
sys.path.insert(0, r'{后端项目根目录}')
from bi_utils import *

init_api('https://api3.boot.jeecg.com', 'your-token')

# 创建仪表盘（style='default'，栅格坐标）
page_id = create_page('运营数据看板', style='default', theme='default')

# 第一行：4 个数字卡片（w=6×4=24，h=17）
add_number(page_id, '总用户数', x=0, y=0, w=6, h=17, value=15890, suffix='人')
add_number(page_id, '今日活跃', x=6, y=0, w=6, h=17, value=3256, suffix='人')
add_number(page_id, '今日收入', x=12, y=0, w=6, h=17, value=89600, prefix='¥')
add_number(page_id, '转化率', x=18, y=0, w=6, h=17, value=23.5, suffix='%')

# 第二行：折线图 + 饼图
add_chart(page_id, 'JLine', '用户增长趋势', x=0, y=17, w=14, h=35,
          categories=['周一','周二','周三','周四','周五','周六','周日'],
          series=[{'name':'新增用户', 'data':[120,200,150,80,70,110,130]}])

add_chart(page_id, 'JPie', '用户来源', x=14, y=17, w=10, h=35,
          pie_data=[
              {'name':'微信','value':40},
              {'name':'APP','value':30},
              {'name':'网页','value':20},
              {'name':'其他','value':10},
          ])

save_page(page_id)
print(f'仪表盘创建成功！ID: {page_id}')
```

**仪表盘样式特点（bi_utils.py 自动应用）：**
- 背景：白色 `#FFFFFF`
- 边框：浅灰 `#E8E8E8`
- 标题颜色：深灰 `#464646`
- 轴标签：`#909198`
- 网格线：`#F3F3F3`
- 卡片头：白色背景 + 深灰粗体标题（`headColor: '#FFFFFF'`）
- 图例：深灰色文字

## 仪表盘标题规则（重要）

### 图表组件：card.title 留空，用 option.title 显示

根据真实模板验证，**图表组件**（JBar/JLine/JPie/JRing 等）在仪表盘模式下 `card.title` 应为空字符串，标题通过 ECharts `option.title.text` 显示。如果两者都设置，标题会重复出现（卡片头一次 + 图表内部一次）。

`bi_utils.py` 的 `add_chart()` 已自动处理：调用 `_make_card(mode, '')` 传入空标题。

**JNumber 等非图表组件**可以使用 `card.title` 显示标题。

### 大屏 vs 仪表盘标题对比

| 特征 | 大屏（bigScreen） | 仪表盘（default） |
|------|-------------------|-------------------|
| 图表标题 | `option.title.text`（ECharts 内部） | `option.title.text`（ECharts 内部） |
| card.title（图表） | 必须为空 `''` | 必须为空 `''`（避免重复） |
| card.title（JNumber等） | 为空 `''` | 可填标题 |
| 页面主标题 | JText 组件（fontSize 40+） | 不需要 |

### JText 正确的 config 格式

如果仪表盘中需要使用 JText（少见），config 结构为：
```python
config = {
    'dataType': 1,
    'chartData': {'value': '显示文本'},  # dict 格式，不是字符串
    'option': {
        'body': {
            'color': '#464646',
            'fontSize': 16,
            'fontWeight': 'normal',
            'letterSpacing': 0,
            'text': '',
            'marginTop': 0,
            'marginLeft': 0,
        },
        'textAlign': 'left',
        'card': {'title': '', ...},
    },
}
```

**手动构建组件（用于高级定制，需直接操作 config）：**

当 `add_chart` 等快捷函数无法满足需求时（如需要多系列 chartData、自定义 customColor），可直接构建组件 config：

```python
import json, time, random
import bi_utils

def _key():
    return f'{int(time.time()*1000)}_{random.randint(100000,999999)}'

# 仪表盘亮色主题通用样式
CARD = {
    'size': 'default',
    'headColor': '#FFFFFF',
    'textStyle': {'color': '#464646', 'fontSize': 16, 'fontWeight': 'bold'},
    'extra': '', 'rightHref': ''
}

# 直接构建折线图组件
line_data = [
    {'name': '1月', 'value': 120, 'type': '新增'},
    {'name': '1月', 'value': 80, 'type': '流失'},
    # ...
]
comp = {
    'component': 'JLine',
    'x': 0, 'y': 17, 'w': 14, 'h': 35,
    'i': _key(),
    'config': json.dumps({
        'dataType': 1,
        'chartData': json.dumps(line_data, ensure_ascii=False),
        'background': '#FFFFFF',
        'borderColor': '#E8E8E8',
        'size': {'width': 700, 'height': 375},
        'option': {
            'customColor': [
                {'color': '#1890FF', 'color1': '#1890FF'},
                {'color': '#52C41A', 'color1': '#52C41A'},
            ],
            'title': {'show': True, 'text': '用户变化趋势',
                      'textStyle': {'color': '#464646'}},
            'tooltip': {'show': True},
            'legend': {'show': True, 'textStyle': {'fontSize': 12}},
            'xAxis': {
                'type': 'category',
                'axisLabel': {'color': '#909198'},
                'axisLine': {'lineStyle': {'color': '#F3F3F3'}},
            },
            'yAxis': {
                'axisLabel': {'color': '#909198'},
                'splitLine': {'lineStyle': {'color': '#F3F3F3'}},
            },
            'grid': {'top': 70, 'left': 60, 'right': 30, 'bottom': 40},
            'card': {**CARD, 'title': '用户变化趋势'},
        }
    }, ensure_ascii=False)
}
bi_utils._page_components[page_id].append(comp)
```

### Step 4: 输出结果

```
## 仪表盘创建成功

- 页面ID：{id}
- 页面名称：{name}
- 模式：仪表盘（default）
- 预览地址：{API_BASE}/drag/page/view/{id}
- 组件数量：{count} 个

请在仪表盘设计器中查看：打开 JeecgBoot 后台 → 仪表盘 → 找到该页面
```

---

## 编辑已有仪表盘

```python
from bi_utils import *
init_api('https://api3.boot.jeecg.com', 'your-token')

page = query_page(page_id)
print(page['name'], page['updateCount'])

add_chart(page_id, 'JBar', '新增图表', x=0, y=52, w=12, h=35,
          categories=['A','B','C'], series=[{'name':'值','data':[10,20,30]}])
save_page(page_id)
```

---

## 删除仪表盘

```python
from bi_utils import *
init_api('https://api3.boot.jeecg.com', 'your-token')

delete_page(page_id)                # 软删除
delete_page(page_id, physical=True) # 硬删除
recover_page(page_id)               # 恢复
```

---

## 修改组件样式

阅读 `references/bi-comp-option-config.md` 获取每种组件的完整配置项路径。

**仪表盘样式修改关键规则：**
- 颜色使用色值（`#000000`），不用英文单词
- customColor 格式：`[{color1:'#xxx',color:'#xxx'}]`
- 卡片头样式：`option.card.textStyle.color`、`option.card.headColor`
- 背景色：`config.background`（仪表盘默认 `#FFFFFF`）
- 边框色：`config.borderColor`（仪表盘默认 `#E8E8E8`）

```python
import sys, json
sys.path.insert(0, r'{后端项目根目录}')
from bi_utils import *
import bi_utils

init_api('https://api3.boot.jeecg.com', 'your-token')

page_id = 'xxx'
page = query_page(page_id)
tmpl = page.get('template', [])
if isinstance(tmpl, str):
    tmpl = json.loads(tmpl)

for comp in tmpl:
    config_str = comp.get('config', '{}')
    config = json.loads(config_str) if isinstance(config_str, str) else config_str
    if comp.get('component') == 'JBar':
        option = config.get('option', {})
        option['series'][0]['itemStyle'] = {'color': '#1890FF'}
        config['option'] = option
        comp['config'] = json.dumps(config, ensure_ascii=False)

bi_utils._page_components[page_id] = tmpl
save_page(page_id)
```

---

## 可用的快捷函数

**API 初始化：**
- `init_api(api_base, token)` — 初始化 API 地址和 Token

**页面管理：**
- `create_page(name, style='default', theme='default')` — 创建仪表盘
- `query_page(page_id)` — 查询页面详情
- `list_pages(style='default')` — 列表查询
- `save_page(page_id)` — 保存设计
- `delete_page(page_id, physical)` — 删除
- `recover_page(page_id)` — 恢复
- `copy_page(page_id)` — 复制

**添加组件（栅格坐标）：**
- `add_number(page_id, title, x, y, w, h, value, prefix, suffix)` — 数字指标
- `add_chart(page_id, chart_type, title, x, y, w, h, categories, series, pie_data)` — 图表
- `add_table(page_id, title, x, y, w, h, columns, data)` — 数据表格
- `add_scroll_table(page_id, title, x, y, w, h, columns, data)` — 滚动表格
- `add_ranking(page_id, title, x, y, w, h, data)` — 排行榜
- `add_text(page_id, title, x, y, w, h, content, font_size, color)` — 文本
- `add_image(page_id, title, x, y, w, h, src)` — 图片
- `add_gauge(page_id, title, x, y, w, h, value, max_val, unit, color)` — 仪表盘表盘
- `add_liquid(page_id, title, x, y, w, h, value, color)` — 水球图
- `add_component(page_id, component, title, x, y, w, h, config)` — 通用组件

---

## API 踩坑记录

| 问题 | 说明 |
|------|------|
| `POST /drag/page/add` 返回值 | 返回完整实体含 ID |
| `POST /drag/page/edit` 乐观锁 | 必须传 `updateCount` |
| Windows curl 中文问题 | 必须用 Python urllib/requests |
| 坐标单位 | 仪表盘用**栅格**坐标（24列） |
| 组件 config 分离 | config 存在 onl_drag_page_comp 表 |
| **size 字段必须是像素** | `config.size.width/height` 是像素值，不是栅格单位。仪表盘栅格转像素：`width = w * 75`, `height = h * 11`。如果直接传栅格值，图表会缩成一小团 |
| **chartData 必须是 JSON 字符串** | `config.chartData` 的值必须是 `json.dumps(...)` 后的字符串，不能是原生 list/dict，否则图表不渲染或显示异常 |
| **图表标题去重** | 图表组件（JBar/JLine/JPie 等）的 `option.card.title` 应为空字符串，标题仅通过 `option.title.text` 显示；否则卡片头和图表内部会重复显示标题 |
| **多系列 chartData 格式** | 多系列图表的 chartData 需要 `type` 字段区分系列：`[{"name":"1月","value":10,"type":"系列A"}, {"name":"1月","value":20,"type":"系列B"}]` |
| **HTTPS 连接问题** | api3.boot.jeecg.com 使用 HTTP 协议（非 HTTPS），`init_api` 时用 `http://` 前缀 |

## 错误处理

| 错误 | 解决方案 |
|------|---------|
| Token 过期（401） | 重新获取 X-Access-Token |
| `updateCount` 不匹配 | 重新查询页面获取最新值 |
| 组件不显示 | 检查 dataType、chartData（必须是 JSON 字符串）、option 是否完整 |
| 图表缩成小点 | 检查 `config.size` 是否用了像素值（不是栅格单位），仪表盘需 `w*75` / `h*11` |
| 标题重复显示 | 图表组件的 `option.card.title` 设为空，仅用 `option.title.text` |
| 布局错乱 | 确认使用栅格坐标（不是像素），w 总和 ≤ 24 |
| 中文乱码 | 使用 Python（不要用 curl） |

## 参考文档

- `references/bi-component-types.md` — 完整组件类型清单
- `references/bi-comp-option-config.md` — 组件样式配置路径
- `references/bi_utils.py` — 工具库源码
- `references/templates/default/` — 41 个仪表盘模板 JSON 参考