1. 页面概览
1.1 是什么
可视化仪表盘页面(路由 /foundry/dashboards,对应 TAD-08 可视化仪表盘)是 LightFoundry 把"数据洞察"组装成面板的操作台。它围绕三层实体运转:本体对象类型(数据从哪来)、图表(数据怎么展示)、仪表盘(图表怎么摆放与分享)。用户先在页面里创建绑定到本体对象类型的图表(选择图表类型、绑定对象、映射 X/Y 轴与分组属性),再创建仪表盘并把图表以栅格布局添加到盘中,最后可以把整个仪表盘分享给指定用户或角色。
本页的取数与传统 BI 有本质差异:图表直接绑定到本体对象(object_type_id),数据加载复用 Foundry 的语义对象查询(semantic.QueryRequest),即"字段白名单 + RLS/CLS 只读注入 + 参数化执行"。因此仪表盘上每一张图的数据都天然受到本体权限体系的约束——用户看到的行/列就是语义查询按当前身份裁剪后的结果,不存在绕过权限的裸 SQL。
当前版本(MVP)图表数据以表格形式渲染(列 + 行),柱状图/折线图/饼图等类型的 ECharts 视觉化渲染由共享图表构建器 action/web/src/utils/chartBuilders.js 与按需注册 action/web/src/utils/echartsRegistry.js 支撑,被 Notebook、报表等页面的通用渲染组件(ChartRender.vue)复用;本页在数据层已经按"列 + 行"标准结构返回,后续可直接切换视觉渲染层。
1.2 核心价值
| 价值点 | 说明 |
|---|---|
| 图表即语义 | 图表绑定本体对象类型,字段选择来自对象属性白名单,数据口径与本体建模一致 |
| 权限内生 | 图表取数经语义查询执行,RLS/CLS 只读注入按当前用户生效,分享他人也看到授权范围 |
| 仪表盘组装 | 新建/打开/删除仪表盘,向盘中添加/移除图表,栅格宽度自适应(12 列栅格) |
| 分享协作 | 分享给指定用户(user_id)或角色(role),被分享者列表可见 |
| 类型可扩展 | 图表类型枚举(bar/line/pie/table 等)与 ECharts option 映射集中管理,便于扩展视觉渲染 |
1.3 一句话总结
可视化仪表盘页把"本体对象 → 图表 → 仪表盘 → 分享"串成一条低门槛的可视化链路,让语义查询结果以可分享的面板形态交付给业务用户。
2. 访问入口
2.1 路由与菜单
| 项目 | 值 |
|---|---|
| 路由 path | /foundry/dashboards |
| 路由 name | FoundryDashboards |
| 路由 title | 可视化仪表盘 |
| requiresAuth | true |
| 菜单位置 | Foundry 左侧边栏(FoundryLayout.vue)菜单项「可视化仪表盘」 |
| 前端源码 | action/web/src/views/DashboardPage.vue |
| 路由注册 | action/web/src/router/index.js |
侧边栏菜单注册项为 { path: '/foundry/dashboards', label: '可视化仪表盘' };FoundryLayout 的全局搜索支持 dashboard://foundry/<id> 直达本页。
2.2 认证与权限
- meta
requiresAuth: true,未登录由路由守卫拦到登录页; - 请求经
action/web/src/api/client.js:baseURL/api/v1、超时 30s、Authorization: Bearer <aip_token>、401 清除 Token 并跳/login; - 后端仪表盘路由全部挂在
protected组; - 数据级权限:
GET /dashboards只返回当前用户可访问的仪表盘(本人拥有 + 分享给该用户 + 分享给用户任一角色,见后端ListDashboards的owner_id = userID OR id IN (SELECT dashboard_id FROM dashboard_shares WHERE user_id = userID OR role IN roles));图表数据经语义查询带当前用户 ID 注入 RLS/CLS。
2.3 端口与 API 前缀
Foundry 后端端口 18081,API 前缀 /api(请求路径带 /v1)。开发环境 Vite 代理转发;跨产品共用前端时须按产品前缀分流(/api → 18081)。
3. 界面布局
+---------------------------------------------------------------------+
| 可视化仪表盘 [刷新][新建图表][新建仪表盘|收起] |
+---------------------------------------------------------------------+
| [操作结果提示 alert(可关闭)] |
+---------------------------------------------------------------------+
| [新建仪表盘表单(点「新建仪表盘」展开)] 名称*/布局类型/描述 + 创建 |
+---------------------------------------------------------------------+
| 左侧:我的仪表盘(card,宽 280px) |
| 仪表盘项:名称 + 「布局 xx · 已发布|草稿」 + [打开][分享][删除] |
+---------------------------------------------------------------------+
| 右侧:当前仪表盘(dash-main) |
| - 未打开:提示选择左侧仪表盘打开 |
| - 已打开:仪表盘头部(名称/描述/图表数)+ [分享][添加图表] |
| 图表网格(12 列栅格):每个图表卡片 = 标题/类型徽标/ |
| [↑][↓][宽度▾][编辑][刷新][移除] + 元信息(对象 #id · X/Y 轴) |
| + 数据表格 |
+---------------------------------------------------------------------+
| [图表创建/编辑弹窗] 名称*/描述/图表类型/绑定对象*/X轴/Y轴/分组 + 取消/保存 |
| [添加图表弹窗] 可选图表列表 + 宽度/高度 + 取消/添加 |
| [分享弹窗] 目标(user|role)+目标值 + 分享;已分享列表 + 取消分享 |
+---------------------------------------------------------------------+
各板块职责:
- 页头操作区:「刷新」重新加载仪表盘/图表/对象三类数据;「新建图表」打开图表弹窗;「新建仪表盘」展开/收起创建表单(按钮文字在展开时变为「收起」)。
- 我的仪表盘列表:左侧独立卡片,列出当前用户可访问的仪表盘;每项有打开、分享、删除三个操作;选中项高亮(item-selected)。
- 当前仪表盘:右侧主区,打开后显示仪表盘名、描述(若有)、图表数量;提供「分享」「添加图表」;图表网格按 12 列栅格排布,每张图表卡片自带数据刷新、移除以及布局编辑(↑ 上移 / ↓ 下移 / 宽度▾ 调宽)。
- 三个弹窗:图表创建/编辑、添加图表到仪表盘、分享管理,均以遮罩 + 模态框形式出现,点遮罩空白处可关闭。
4. 交互元素详解
4.1 页头操作区
| 元素 | 位置 | 含义 | 必填与默认值 | 操作效果 | 触发的后端调用 |
|---|---|---|---|---|---|
| 刷新按钮 | 页头右侧 | 重新加载三类主数据 | — | 并行刷新仪表盘、图表、对象类型 | GET /dashboards + GET /charts + GET /ontology/objects |
| 新建图表按钮 | 页头右侧 | 打开图表创建弹窗 | — | 打开弹窗,表单为空白(编辑态则由图表项进入) | 无(保存时才请求) |
| 新建仪表盘/收起按钮 | 页头右侧 | 展开/收起创建表单 | — | 切换 showDashboardForm | 无(提交时请求) |
4.2 新建仪表盘表单
| 元素 | 含义 | 必填与默认值 | 操作效果 | 触发的后端调用 |
|---|---|---|---|---|
| 名称输入框 | 仪表盘名称 | 必填,placeholder「如 销售大盘」 | 决定仪表盘标识 | POST /dashboards |
| 布局类型下拉 | grid(栅格)/ freeform(自由) | 默认 grid | 决定布局体系 | 同上 |
| 描述输入框 | 仪表盘用途说明 | 选填 | 展示于仪表盘头部 | 同上 |
| 创建按钮 | 提交表单 | busy 时禁用 | 成功后提示"仪表盘创建成功(id=...)"并收起表单 | POST /dashboards,body {name, description, layout_type} |
4.3 仪表盘列表项
| 元素 | 含义 | 操作效果 | 触发的后端调用 |
|---|---|---|---|
| 名称/元信息区 | 显示名称与「布局 xx · 已发布/草稿」 | 点击打开仪表盘 | GET /dashboards/:id |
| 打开按钮 | 打开选中仪表盘 | 打开后自动加载全部图表数据(每张图调一次取数) | GET /dashboards/:id + 各图 POST /charts/:id/data |
| 分享按钮 | 打开分享弹窗 | 加载该仪表盘分享列表 | GET /dashboards/:id/shares |
| 删除按钮 | 删除仪表盘 | confirm 二次确认"确认删除该仪表盘?(图表保留,仅删除仪表盘及布局/分享)"后删除 | DELETE /dashboards/:id |
4.4 当前仪表盘区
| 元素 | 含义 | 操作效果 | 触发的后端调用 |
|---|---|---|---|
| 分享按钮 | 分享当前仪表盘 | 打开分享弹窗 | GET /dashboards/:id/shares |
| 添加图表按钮 | 打开添加图表弹窗 | 展示"可添加图表"列表(已有图表排除) | 无(提交时请求) |
| 图表卡片刷新按钮 | 重新加载单图数据 | 该图 loading → 取数 → 表格渲染 | POST /charts/:id/data,body {filters: [], limit: 200} |
| 图表卡片上移按钮(↑) | 把该图在盘中顺序前移一位 | moveChart(item, -1):数组内与前一位置交换后整体提交布局;已在首位时无操作;busy 期间禁用 | PUT /dashboards/:id/layout |
| 图表卡片下移按钮(↓) | 把该图在盘中顺序后移一位 | moveChart(item, 1):同上;已在末位时无操作 | PUT /dashboards/:id/layout |
| 图表卡片宽度下拉(宽度▾) | 调整该图栅格跨度 | 选项 4/6/8/12 列(对应 1/3、1/2、2/3、整行);changeWidth(item, w) 经 clampWidth 夹紧到 [4,12](与卡片 gridColumn 口径一致)后整体提交布局 | PUT /dashboards/:id/layout |
| 图表卡片移除按钮 | 从仪表盘移除图表 | confirm 二次确认"确认从仪表盘移除该图表?(图表定义保留)"后移除 | DELETE /dashboards/:id/charts/:chartId |
| 图表卡片元信息 | 对象 #id · X 轴/Y 轴 | 只读展示 | — |
图表卡片渲染逻辑:item.loading 显示"数据加载中...";item.error 显示红色错误块;item.result 有数据时渲染列 + 行表格(对象/数组单元格 JSON 化),行数为 0 显示"无数据";否则显示"点击"刷新"加载数据"。卡片宽度按 gridColumn: span min(max(width||6,4),12) 占栅格列数(4~12 列)。
4.5 图表创建/编辑弹窗
| 元素 | 含义 | 必填与默认值 | 操作效果 | 触发的后端调用 |
|---|---|---|---|---|
| 名称输入框 | 图表名称 | 必填,placeholder「如 各区域销售额」 | 图表面板标题 | POST /charts / PUT /charts/:id |
| 描述输入框 | 图表说明 | 选填 | 图表卡片副标题 | 同上 |
| 图表类型下拉 | bar(柱状图)/ line(折线图)/ pie(饼图)/ table(表格) | 默认 table | 决定渲染形态与语义查询路径 | 同上 |
| 绑定对象下拉 | 本体对象类型(展示 display_name(name)) | 必填 | 选中后触发加载该对象属性列表 | GET /ontology/objects(选项);GET /ontology/objects/:id(属性) |
| X 轴属性下拉 | X 轴维度属性 | 默认不选 | 语义查询的维度列 | 保存时写入 x_axis |
| Y 轴属性下拉 | Y 轴度量属性 | 默认不选 | 语义查询的度量列 | 保存时写入 y_axis |
| 分组属性下拉 | 分组维度属性 | 默认不选 | 语义查询 GROUP BY | 保存时写入 group_by |
| 保存按钮 | 保存新建/更新图表 | 名称与绑定对象必填 | 新建调 POST,编辑调 PUT;成功后刷新图表列表 | POST /charts / PUT /charts/:id |
| 取消按钮 | 关闭弹窗 | — | 丢弃当前表单 | 无 |
轴字段保存为 AxisSpec(JSON {property, label, aggregation}):前端把空属性置 null 不发送(后端允许轴缺省,回退全部属性),label 缺省取 property;编辑时后端返回的 JSON 字符串经 parseAxis 反序列化回表单。
4.6 添加图表到仪表盘弹窗
| 元素 | 含义 | 必填与默认值 | 操作效果 | 触发的后端调用 |
|---|---|---|---|---|
| 可选图表列表 | 未在该仪表盘上的图表(含类型徽标与对象 #id) | 必须选中一个 | 点击选中(高亮) | 无(提交时请求) |
| 宽度输入框 | 栅格列数 | 默认 6,min 1 max 12 | 决定卡片栅格跨度 | 提交 body width |
| 高度输入框 | 栅格行数 | 默认 4,min 1 | 决定卡片高度 | 提交 body height |
| 添加按钮 | 提交添加 | chart_id 必选 | 成功后重新打开仪表盘并刷新图表列表 | POST /dashboards/:id/charts,body {chart_id, position_x: 0, position_y: 当前图表数, width, height} |
| 取消按钮 | 关闭弹窗 | — | 丢弃 | 无 |
4.7 分享弹窗
| 元素 | 含义 | 必填与默认值 | 操作效果 | 触发的后端调用 |
|---|---|---|---|---|
| 目标类型下拉 | user(用户 user_id)/ role(角色 role) | 默认 user | 决定分享语义与 body 字段 | POST /dashboards/:id/share |
| 目标输入框 | 用户 ID(如 user_2)或角色名(如 analyst) | 必填 | 决定分享对象 | 同上 |
| 分享按钮 | 提交分享 | 目标非空 | 成功后刷新分享列表 | POST /dashboards/:id/share,body {share_type, user_id} 或 {share_type, role} |
| 已分享列表 | 每条:类型徽标 + user_id 或 role + 取消分享按钮 | — | 取消分享有即时反馈 | DELETE /dashboards/:id/share/:shareId |
5. 后端关联
5.1 API 客户端
同页共用一个 apiClient(action/web/src/api/client.js):baseURL /api/v1、timeout 30000、JSON 头、Bearer 注入、401 跳登录。
5.2 端点表
| 方法 | 路径 | 请求参数 | 超时 |
|---|---|---|---|
| GET | /ontology/objects | 无 | 30s |
| GET | /ontology/objects/:id | 无(返回对象属性列表) | 30s |
| GET | /charts | 无 | 30s |
| POST | /charts | body {name, description, chart_type, object_type_id, x_axis, y_axis, group_by, filters: []} | 30s |
| PUT | /charts/:id | body 同上(指针字段可部分更新) | 30s |
| POST | /charts/:id/data | body {filters: [], limit: 200}(filters 为仪表盘全局筛选器,与图表默认筛选合并) | 30s |
| POST | /charts/preview | body {chart, data}(未保存图表预览,本页未直接使用) | 30s |
| GET | /dashboards | 无(后端按当前用户 + 角色过滤) | 30s |
| POST | /dashboards | body {name, description, layout_type} | 30s |
| GET | /dashboards/:id | 无(返回仪表盘 + 全部图表布局) | 30s |
| DELETE | /dashboards/:id | 无(级联清理关联与分享) | 30s |
| POST | /dashboards/:id/charts | body {chart_id, position_x, position_y, width, height} | 30s |
| DELETE | /dashboards/:id/charts/:chartId | 无 | 30s |
| PUT | /dashboards/:id/layout | body [{chart_id, position_x, position_y, width, height}](本页「↑/↓/宽度」布局编辑使用;position_x 归 0、position_y 取图表在盘中的行序) | 30s |
| POST | /dashboards/:id/share | body {share_type: user, user_id} 或 {share_type: role, role} | 30s |
| GET | /dashboards/:id/shares | 无 | 30s |
| DELETE | /dashboards/:id/share/:shareId | 无 | 30s |
5.3 响应结构
统一成功包裹 {"code": 0, "data": ...};错误 {"code", "error"}。
图表数据(POST /charts/:id/data)响应 data:
{
"columns": ["region", "total_amount", "order_count"],
"rows": [["华东", 12345.0, 320], ["华南", 9800.0, 210]],
"chart": { "id": 1, "name": "各区域销售额", "chart_type": "bar", "object_type_id": 3 }
}
仪表盘详情(GET /dashboards/:id)响应 data:
{
"dashboard": { "id": 1, "name": "销售大盘", "description": "月度销售", "layout_type": "grid", "is_published": false, "owner_id": "alice" },
"charts": [
{
"dashboard_chart_id": 11, "chart_id": 3, "position_x": 0, "position_y": 0,
"width": 6, "height": 4,
"chart": { "id": 3, "name": "各区域销售额", "chart_type": "bar", "object_type_id": 3 }
}
]
}
分享列表(GET /dashboards/:id/shares)响应 data:[{id, dashboard_id, share_type: "user", user_id: "user_2"}, ...]。
5.4 关联模块表
| 后端包 | 职责 |
|---|---|
action/products/foundry/server/dashboard_handlers.go | 图表/仪表盘/分享/取数全部 handler |
action/products/foundry/dashboard/service.go | 业务服务:CRUD、布局、分享、GetChartData 语义取数 |
action/products/foundry/dashboard/models.go | Chart/Dashboard/DashboardChart/DashboardShare 模型与图表类型枚举 |
action/products/foundry/semantic/query.go | 语义对象查询:字段白名单、Filter、RLS/CLS 注入、Y 轴聚合 |
action/products/foundry/ontology | 对象类型/属性元数据(绑定对象与轴字段来源) |
action/products/foundry/server/server.go | protected 组路由注册(1058-1077 行)与 dashboardSvc 组装 |
5.5 关键机制
- 图表类型枚举:后端
ValidChartType校验 bar/line/pie/table/scatter/radar/map;前端图表表单提供 bar/line/pie/table 四类。chartBuilders.js的 BUILDERS 注册表支持 17 种构建器(bar/line/area/stacked_bar/horizontal_bar/dual_axis/pie/donut/funnel/sankey/scatter/heatmap/radar/gauge/liquidfill/wordcloud/metric_card),buildChartOption(chartType, data, mapping, options)是统一入口,并对 ECharts 图表自动附加 toolbox 保存图片能力;echarts 核心经echartsRegistry.js按需注册(Bar/Line/Pie/Scatter/Radar/Funnel/Gauge/Heatmap/Sankey + Toolbox/DataZoom/VisualMap 等组件 + CanvasRenderer + UniversalTransition)。 - 字段映射(AxisSpec):x_axis/y_axis/group_by 均为
{property, label, aggregation}。Y 轴声明聚合(aggregation=sum/avg/count/min/max)时走GroupedFields的 AGG + GROUP BY 聚合路径;未声明则按明细列返回。轴属性名必须是绑定对象的属性 api_name(语义查询字段白名单成员)。 - 仪表盘权限口径:
ListDashboards返回"本人拥有 ∪ 分享给本人 ∪ 分享给本人任一角色"三类;分享用dashboard_shares表(user/role 二选一,唯一索引防重复)。 - 图表数据权限:
handleChartData把当前用户 ID 注入req.UserID,经语义查询执行 RLS/CLS 只读路径;图表默认筛选(charts.filters)与请求级全局筛选(仪表盘筛选器)合并后执行。 - 删除级联:删除仪表盘级联清理 dashboard_charts 关联与 dashboard_shares 分享;删除图表级联清理仪表盘关联(图表定义删除不保留布局)。前端删除仪表盘用 confirm 二次确认,且明确"图表保留"。
- 布局更新(
dashboard/service.go:612-641):UpdateDashboardLayout按chart_id逐项覆盖position_x/position_y/width/height(事务内),chart_id=0或某图不在该盘上分别报VALIDATION_ERROR/NOT_FOUND(dashboard/service.go:91-104的LayoutItem形状);handler 在调用前先做 owner/admin 校验(handleUpdateDashboardLayout,dashboard_handlers.go:299-317)。
6. 核心流程详解
6.1 页面加载
onMounted(fetchAll) 并行拉取仪表盘、图表、对象类型三类数据;任一失败各自 alert 提示(不影响其他请求)。
6.2 创建图表主流程
- 点「新建图表」→ 打开弹窗,
chart_type默认 table。 - 填写名称、选择图表类型、绑定对象类型;选对象后
@change触发loadObjectProperties拉取属性填充 X/Y/分组下拉。 - 选择 X/Y/分组属性(可不选)。
- 点「保存」:名称与绑定对象必填校验(否则提示"图表名称与绑定对象必填");新建调
POST /charts、编辑调PUT /charts/:id;成功后提示"图表创建成功(id=...)"/"图表已更新",关闭弹窗并刷新图表列表;若当前正打开某仪表盘,保存后同步重新打开该仪表盘以刷新盘中图表。 - 编辑入口:图表卡片提供「编辑」按钮,点击调
openChartForm(item.chart)回填表单并置editingChartId,提交走PUT /charts/:id更新。
6.3 组装仪表盘主流程
- 点「新建仪表盘」展开表单 → 填名称(必填)/布局类型/描述 → 「创建」→ 成功提示并收起表单。
- 左侧点「打开」或点击仪表盘名称 →
GET /dashboards/:id→ 填充 currentDashboard 与图表列表,并自动为每张图调loadChartData(打开即出数)。 - 点「添加图表」→ 在弹窗选择未入盘图表,设置宽高 → 「添加」→ 重新打开仪表盘刷新出图。
- 图表卡片可单独「刷新」(重新取数)、「移除」(从盘中移除,图表定义保留),或用「↑ / ↓ / 宽度▾」调整盘内顺序与栅格跨度(布局编辑,成功提示「布局已保存」)。
- 发布切换:列表项提供「发布/转草稿」按钮;打开仪表盘后头部有状态徽标与「发布/转草稿」切换,均调
PUT /dashboards/:id(is_published)落库。 - 自动刷新:打开仪表盘后头部「自动刷新」内联设置可填
refresh_interval_sec(秒)并「保存」调PUT /dashboards/:id;设置后按该间隔定时重新取当前仪表盘各图表数据,onBeforeUnmount清除定时器,无泄漏。 - 全局筛选:打开仪表盘后提供「全局筛选」卡片,按 field/op/value 增删筛选行,「应用筛选」后所有图表取数携带该条件。
- 分享:点「分享」→ 弹窗选 user/role 填目标 → 「分享」;已分享列表可逐条「取消分享」。
6.4 图表数据加载机制
loadChartData(item):置 item.loading=true → POST /charts/:chart_id/data,body {filters: <全局筛选数组>, limit: 200} → 成功存 columns/rows,失败记 item.error("数据加载失败:...")。filters 由「全局筛选」卡片构建(buildFilters:field/op/value,in 按逗号拆数组、is_null 不带 value);无筛选时为空数组。limit 200 是前端固定值;后端语义查询有单图表数据点上限(chartDataDefaultLimit = 1000)。
6.5 状态与终态语义
- 仪表盘
is_published:列表与打开后的头部均提供「发布/转草稿」切换,切换即调PUT /dashboards/:id落库并刷新列表。 - 自动刷新:
refresh_interval_sec可在打开仪表盘后的头部内联设置并保存(PUT /dashboards/:id);设置非 0 时启动定时器按秒轮询重新取数,切换仪表盘/离开页面时清除。 - 布局状态:添加图表后
position_y = 当前图表数(追加到尾部),宽度按min(max(width||6,4),12)计算栅格跨度;布局编辑已在本页提供——每张图表卡片右上角「↑ / ↓」调整顺序、「宽度▾」调整栅格跨度(4/6/8/12 列),任一操作把当前全部图表按「数组下标 =position_y、position_x = 0、宽度夹紧 [4,12]」整体提交PUT /dashboards/:id/layout,成功后提示「布局已保存」(不再重取全盘数据);失败的(无权限/网络)以红条提示。仍无自由拖拽(不需引入拖拽库)。 - 数据加载状态机:每张图表卡片独立维护 loading/error/result 三态;打开仪表盘时批量触发,任一张失败不影响其他图表;「全局筛选」条件会随每次取数发送。
7. 权限与安全
- 认证:
aip_tokenBearer,401 跳登录;路由requiresAuth。 - 对象可见性:绑定对象下拉来自
GET /ontology/objects,只列当前用户可见对象类型。 - 数据级安全:图表取数经语义查询执行,handler 注入当前用户 ID,RLS/CLS 只读生效;被分享者对仪表盘数据的可见性同样受语义权限约束(看到的是授权范围)。
- 分享即授权:分享本身只授予"列表可见 + 打开查看",写操作(编辑/删除/改布局)仍需所有者能力(后端按 owner 校验)。
- 写操作防护:删除仪表盘、移除图表均有
window.confirm二次确认;创建/分享等写操作在 busy 期间禁用按钮防重复提交。
8. 常见问题与排错
问题 1:仪表盘列表为空但后台有数据
- 现象:左侧「我的仪表盘」为空,提示"暂无仪表盘,点击右上角'新建仪表盘'创建。",但库里有其他用户创建的仪表盘。
- 原因:
GET /dashboards按当前用户过滤——只返回本人拥有、或分享给本人/本人角色的仪表盘。 - 排查步骤:
- 1. 确认当前登录用户与仪表盘
owner_id是否一致; - 2. 若是他人仪表盘,请对方在分享弹窗中把
share_type选 user(填你的用户 ID)或 role(填你的角色名); - 3. 确认
GET /dashboards响应中确无该记录,再看 Network 是否有 401(Token 失效)。
问题 2:图表数据加载失败/列不对
- 现象:图表卡片显示红色"数据加载失败:...",或列名与预期不符。
- 原因:绑定对象属性名(AxisSpec.property)必须存在于该对象属性白名单;Y 轴未声明聚合时按明细列返回,列集合与 X/Y 轴选择相关。
- 排查步骤:
- 1. 查看
POST /charts/:id/data响应体错误信息(data.error); - 2. 确认图表绑定对象与该对象实际属性名(可在本体工作台查看);
- 3. 若列集合异常,检查 x_axis/y_axis/group_by 的 property 是否拼错;空轴时后端回退全部属性。
问题 3:打开仪表盘后某些图表一直"数据加载中..."
- 现象:部分图表卡片停在"数据加载中...",刷新也无效。
- 原因:打开仪表盘时批量触发全部图表取数,个别请求超时(30s)或后端语义查询异常。
- 排查步骤:
- 1. 等待当前请求结束,看是否转为"数据加载失败:..."并读取错误详情;
- 2. 点该卡片「刷新」单独重试;
- 3. 若单图失败但其他正常,多为该图绑定对象/轴属性配置问题,回图表配置检查。
问题 4:分享后对方看不到仪表盘
- 现象:已添加分享,但目标用户登录后列表为空。
- 原因:分享目标类型或目标值不匹配(user 填了角色名、role 填了用户 ID),或目标用户换了账号。
- 排查步骤:
- 1. 在分享弹窗「已分享给」列表确认记录:
share_type=user时展示user_id,share_type=role时展示role; - 2. 确认目标用户的账号 ID / 角色名与此一致;
- 3. 确认
POST /dashboards/:id/share请求成功(重复分享会因唯一索引报错,提示分享失败)。
问题 5:图表类型选了柱状图但显示表格
- 现象:chart_type 为 bar,但卡片渲染仍是列 + 行表格。
- 原因:本页 MVP 以表格渲染数据("MVP 用表格展示列+行,柱状/折线可后续接 ECharts");ECharts 视觉化由
chartBuilders.js/echartsRegistry.js支撑的通用渲染组件(ChartRender.vue)在 Notebook/报表等页面使用。 - 排查步骤:
- 1. 确认这是设计预期而非故障:本页展示数据列/行结构,图表类型仍会被语义查询记录并在后端校验;
- 2. 若需要 ECharts 渲染,可在数据层已就绪的前提下由前端渲染层接入
buildChartOption(chart_type, {columns, rows}, mapping)。
9. 已知缺陷与边界
| 项 | 说明 |
|---|---|
| MVP 表格渲染 | 本页图表数据以表格呈现,bar/line/pie 的 ECharts 视觉渲染依赖共享图表组件,未在本页内置 |
| 图表编辑 | 图表卡片提供「编辑」入口,回填表单后走 PUT /charts/:id;仅本页字段(名称/类型/绑定对象/轴/分组),无高级轴配置 |
| 发布切换 | 列表与打开的头部均提供「发布/转草稿」,调 PUT /dashboards/:id;无「定时发布」等策略 |
| 布局编辑(已于 2026-09-13 补齐按钮式编辑) | 每张图表卡片提供「↑ 上移 / ↓ 下移」(moveChart)与「宽度▾」(changeWidth,4/6/8/12 列),整体提交 PUT /dashboards/:id/layout(position_x=0、position_y=数组行序),成功提示「布局已保存」。边界:仍无自由拖拽/像素级定位(不引入拖拽库,属有意取舍);position_x 恒提交 0、高度 height 沿用原值不在本页调整;该端点需仪表盘所有者或管理员权限(handleUpdateDashboardLayout 走 requireDashboardAccess(c,id,true),dashboard_handlers.go:299-317),被分享的只读用户操作会收到 403 |
| 自动刷新 | 打开仪表盘后可内联设置 refresh_interval_sec 并按间隔轮询取数;关闭页面/切换仪表盘即停止,无后台常驻 |
| 全局筛选 | 提供 field/op/value 行式筛选并随取数下发;无跨图表联动、无筛选持久化到仪表盘定义 |
| 分享无权限细分 | 分享仅控制可见性,写权限仍需所有者能力,无只读/可编辑细粒度 |