用 NocoBase 搭建可联动的运营仪表盘

📅 2026/7/11 7:05:16
用 NocoBase 搭建可联动的运营仪表盘
原文链接https://www.nocobase.com/cn/blog/build-interactive-operations-dashboard-with-nocobase本文以“工单系统”的运营仪表盘为例介绍如何组合使用 NocoBase 的图表区块、筛选区块和 JS 区块搭建一个支持筛选联动、图表下钻和自定义样式的数据看板。虽然示例来自工单场景但这些方法也适用于 CRM、设备运维、项目管理、审批流、客户成功等业务系统。 这篇文章想介绍的不是“怎么用 JS 区块写一个大屏”而是如何把 NocoBase 的原生区块能力和 JS 区块组合起来让原生区块负责标准能力让 JS 区块补足个性化体验。 嗨你正在阅读 NocoBase 博客。NocoBase 是一个极易扩展的 AI 无代码/低代码开发平台用于构建企业应用、内部工具和各类系统。它完全支持自托管基于插件架构设计开发者友好。→ 欢迎在 GitHub 上了解我们场景目标我们希望搭建一个 Operations dashboard用来帮助运营或服务团队快速判断当前工作负载当前还有多少未完成工单哪些工单存在 SLA 风险新建工单趋势如何工单状态、优先级分布如何点击某个图表后可以查看对应明细页面大致可以分为四层顶部筛选区时间、服务组、请求类型、优先级、SLA 状态KPI 统计区Open backlog、Unassigned、SLA warning 等图表分析区趋势、状态、SLA、优先级分布下钻明细区点击图表后展示匹配记录先明确一个搭建思路很多人在做数据看板时容易把问题想成二选一要么全部使用 NocoBase 的原生区块配置简单但担心样式和交互不够灵活要么干脆写一个大的 JS 区块把查询、图表、筛选、下钻都自己控制但这样又会失去低代码配置带来的便利性。实际上更推荐的方式是把两者组合起来。在这个 Operations dashboard 里我们没有把整个页面写成一个 JS 大屏而是按职责拆分顶部筛选使用 NocoBase 系统自带的筛选区块趋势图、状态分布、SLA 分布使用原生图表区块KPI 卡片和下钻明细使用 JS 区块筛选区块同时影响图表区块和 JS 区块图表点击后再把下钻条件传给下方 JS 明细区块。这样做的好处是标准统计和筛选仍然保留 NocoBase 的配置能力个性化展示和复杂交互则交给 JS 区块完成。页面既不是“只能配置”也不是“全部代码”而是配置和代码各司其职。一、图表区块如何自定义样式NocoBase 的图表区块可以先用 Query builder 定义统计口径再用自定义 ECharts option 调整样式。以“工单状态统计”为例Query builder 可以配置为数据表tickets指标id count别名 ticketCount维度status关键是自定义样式时不需要重写查询只需要基于ctx.data.objects处理图表展示。constrowsArray.isArray(ctx.data?.objects)?ctx.data.objects:[];这行代码读取图表查询结果。然后定义状态标签和颜色constlabels{new:ctx.t(New),open:ctx.t(Open),pending_customer:ctx.t(Pending customer),resolved:ctx.t(Resolved),closed:ctx.t(Closed),};constcolors{new:#1677ff,open:#22a06b,pending_customer:#f59f00,resolved:#13c2c2,closed:#8c8c8c,};这里建议所有可见文案都使用ctx.t()方便后续支持多语言。生成图表数据时可以把下钻信息挂到每个图表数据点上constdatarows.map((row)({value:Number(row.ticketCount||0),itemStyle:{color:colors[row.status]||#8c8c8c,borderRadius:[6,6,0,0],},ticketingDrilldown:{label:ctx.t(Status): (labels[row.status]||row.status),filter:{status:{$eq:row.status}},},}));这里最关键的是ticketingDrilldown。它不是 ECharts 的标准字段而是我们自己放进去的业务上下文后续点击图表时会用到。最后返回 ECharts optionreturn{grid:{top:28,right:22,bottom:48,left:42},tooltip:{trigger:axis,axisPointer:{type:shadow}},xAxis:{type:category,data:rows.map((row)labels[row.status]||row.status),},yAxis:{type:value,minInterval:1,},series:[{name:ctx.t(Tickets),type:bar,barWidth:36,data,},],};这一部分的核心思路是Query builder 负责统计数据Custom option 负责视觉表达自定义字段负责携带下钻上下文。二、让系统筛选区块成为整个页面的观察口径运营仪表盘里的筛选区不应该只是一个孤立的表单。它代表的是整个页面当前的观察口径。例如用户选择了某个服务组、某个请求类型、某段创建时间那么 KPI、趋势图、状态分布和下钻明细都应该基于同一组条件展示。否则页面上不同区块的数字就会互相打架使用者也很难判断到底哪个数据才是当前范围内的结果。这里我们直接使用 NocoBase 系统自带的筛选区块而不是自己写一个筛选组件。原生筛选区块可以自然绑定到图表区块让 Chart block 继续使用 Query builder、权限、刷新和筛选机制。顶部Dashboard scope可以配置这些筛选项Created atService groupRequest typePrioritySLA status对于 JS 区块只需要在代码里读取同一组筛选条件再转换成查询 filter。这样 KPI 和下钻明细也能和原生图表保持一致。过滤条件组合可以封装成一个小函数functioncombineFilters(...filters){constpartsfilters.filter(Boolean);if(!parts.length)returnundefined;if(parts.length1)returnparts[0];return{$and:parts};}按过滤条件统计数量asyncfunctioncountTickets(filter){constresourcectx.makeResource(MultiRecordResource);resource.setResourceName(tickets);resource.setPageSize(1);if(filter){resource.setFilter(filter);}awaitresource.refresh();constmetaresource.getMeta?.()||{};returnNumber(meta.count||meta.total||0);}这里的关键点是resource.setFilter(filter);awaitresource.refresh();JS 区块通过 resource 查询业务数据而不是直接写 SQL。这样更容易和 NocoBase 的权限、数据源和页面运行时保持一致。三、用 JS 区块展示 KPI 卡片KPI 更适合使用 JS 区块。因为 KPI 通常不是一个单独查询而是多个业务口径的组合未完成、未分派、SLA warning、SLA breached、新建、已解决等。JS 区块可以根据当前筛选范围重新查询数据并渲染成统计卡片。const{Card,Col,Row,Statistic,Tag}ctx.libs.antd;constscopeFiltergetDashboardScopeFilter();constopenBacklogawaitcountTickets(combineFilters(scopeFilter,{status:{$notIn:[resolved,closed,cancelled]},}),);ctx.render(Row gutter{[12,12]}Col span{6}Card sizesmallTag colorblue{ctx.t(Active)}/TagStatistic title{ctx.t(Open backlog)}value{openBacklog}//Card/Col/Row,);JS 区块的关键点是用ctx.makeResource()查询数据用ctx.libs.antd渲染界面用ctx.render()输出内容筛选变化后重新渲染 JS 区块。在真实页面里筛选按钮和重置按钮可以配置事件流让它们在完成原生筛选动作后同时刷新 KPI JS 区块和下钻 JS 区块。这样用户点击一次筛选图表和自定义内容都会基于同一个范围更新。四、图表联动 JS 区块做下钻图表点击下钻是仪表盘里很实用的交互。在工单场景里用户点击 “Status: Open” 柱子下方明细区显示所有 Open 工单点击 “SLA breached”下方显示所有超时工单。实现思路是图表数据点上携带ticketingDrilldown图表事件读取这个下钻信息把下钻信息写入目标 JS 区块上下文触发目标 JS 区块重新渲染。图表事件中的关键代码如下。先找到下钻 JS 区块constDRILLDOWN_TARGET_UIDv7mioopm6rm;functiongetDrilldownTarget(){if(typeofctx.getModelfunction){returnctx.getModel(DRILLDOWN_TARGET_UID);}constenginectx.model?.flowEngine||ctx.model?.context?.flowEngine||ctx.engine;returnengine?.getModel?.(DRILLDOWN_TARGET_UID);}然后把图表点击得到的下钻条件写入目标区块functionapplyDrilldown(drilldown){if(!drilldown?.filter)return;consttargetgetDrilldownTarget();if(!target?.context?.defineProperty)return;target.context.defineProperty(ticketingDashboardDrilldown,{value:drilldown,});target.rerender?.();}最关键的是这两行target.context.defineProperty(ticketingDashboardDrilldown,{value:drilldown});target.rerender?.();第一行把下钻条件交给 JS 区块第二行触发 JS 区块刷新。最后绑定图表点击事件constclickHandler(params){applyDrilldown(params?.data?.ticketingDrilldown);};chart.on(click,clickHandler);return()chart.off(click,clickHandler);这里推荐一定要返回 cleanupreturn()chart.off(click,clickHandler);这样图表重新配置或重新渲染时可以清理旧事件避免重复绑定。上述点击事件相关代码适用于 v2.2.0-beta.10 以上版本旧版本代码参考chart.off(click); chart.on(click, clickHandler);五、下钻 JS 区块如何展示明细下钻 JS 区块读取刚才写入的ticketingDashboardDrilldown然后按其中的 filter 查询数据。constdrilldownctx.model?.context?.ticketingDashboardDrilldown;if(!drilldown){ctx.render(Alert typeinfoshowIcon message{ctx.t(Select a chart segment to inspect matching tickets)}/,);return;}如果用户还没有点击图表就显示一个提示。点击后根据drilldown.filter查询工单constresourcectx.makeResource(MultiRecordResource);resource.setResourceName(tickets);resource.setFilter(drilldown.filter);resource.setPageSize(10);awaitresource.refresh();constrowsresource.getData?.()||[];然后渲染表格const{Table,Typography}ctx.libs.antd;ctx.render(Typography.Title level{5}{ctx.t(Drilldown)}:{drilldown.label}/Typography.TitleTable sizesmallrowKeyiddataSource{rows}pagination{false}columns{[{title:ctx.t(Ticket No),dataIndex:ticketNo},{title:ctx.t(Title),dataIndex:title},{title:ctx.t(Status),dataIndex:status},{title:ctx.t(Priority),dataIndex:priority},]}//,);如果需要清除下钻条件可以参考function clearChartDrilldown() { if (ctx.model?.context?.defineProperty) { ctx.model.context.defineProperty(ticketingDashboardDrilldown, { value: null }); } if (typeof ctx.model?.rerender function) { ctx.model.rerender(); } }这部分的关键点是图表只负责传递 filterJS 区块负责查询和展示明细点击不同图表可以共用同一个下钻区块。实践建议1. 不要急着把复杂页面整体代码化这个页面最重要的经验是不要把原生能力和 JS 能力对立起来。如果一个能力已经是 NocoBase 的原生能力比如筛选、图表查询、表格展示、权限控制就优先用原生区块。这样后续调整字段、筛选条件、图表口径时仍然可以在界面上配置。JS 区块更适合处理那些原生区块不擅长的部分例如多个指标组合成一组 KPI、特殊卡片样式、点击图表后展示一组自定义明细、或者在不同区块之间传递业务上下文。换句话说原生区块负责“可配置的标准能力”JS 区块负责“业务化的个性体验”。这也是这个仪表盘最值得复用的搭建思路。2. 简单统计优先用图表区块 Query builder这样可以保留 NocoBase 的标准查询、权限、筛选和刷新能力。只有当默认图表样式无法表达业务重点时再通过自定义 ECharts option 做视觉优化。3. KPI 卡片优先用 JS 区块KPI 通常需要多个查询、条件组合和自定义布局JS 区块更灵活。尤其是当 KPI 需要响应同一组系统筛选条件时用 JS 区块统一处理会更清晰。4. 图表事件要返回 cleanup推荐写法consthandler(params){// handle click};chart.on(click,handler);return()chart.off(click,handler);不要直接使用chart.off(click)清空所有点击事件这可能会误删图表区块或配置面板自己的监听。让 AI 帮你搭建这类仪表盘非常适合让 AI 辅助生成因为它同时涉及数据模型、统计口径、图表样式和页面交互。你可以把这篇文章的内容交给它并参考下面的提示词进行提问。你可以这样提问我正在用 NocoBase 搭建一个工单系统的运营仪表盘。 请以工单场景为例帮我设计一个 Operations dashboard。 数据表 tickets 包含 ticketNo、title、status、priority、slaStatus、 requestType、serviceGroup、assignee、createdAt、updatedAt。 页面需要 1. 顶部筛选Created at、Service group、Request type、Priority、SLA status。 2. KPI 卡片Open backlog、Unassigned、SLA warning、SLA breached、New tickets、Resolved tickets。 3. 图表Created tickets trend、Ticket status、SLA status、Priority mix。 4. 点击图表后下方 JS 区块展示匹配的 Ticket drilldown 表格。 5. 图表样式要适合运营大盘颜色清晰布局紧凑。 6. 所有 JS 文案使用 ctx.t()。 7. 图表事件使用 chart.on并返回 cleanup 函数。 8. 优先使用 NocoBase 原生筛选区块和图表区块只有 KPI、下钻明细、特殊样式和跨区块交互使用 JS 区块不要把整个页面写成一个大 JS 区块。 请给出每个区块的配置思路并标出关键 JS 代码。如果你已经有页面也可以让 AI 帮你优化这是我现在的 NocoBase 仪表盘设计 顶部是筛选区中间是 4 个图表下面是下钻 JS 区块。 请帮我从运营人员使用体验角度优化 1. KPI 应该展示哪些指标 2. 图表之间是否需要联动 3. 下钻明细应该展示哪些列 4. JS 区块和图表事件应该如何组织 5. 哪些代码应该放在图表 custom option哪些应该放在 JS 区块。这样 AI 生成的内容会更接近真实业务而不是只给出孤立代码。如果选择让 AI 帮你搭建在开始前请用备份管理器先备份项目。参考文档图表配置前端 RunJS筛选表单AI 搭建 - 界面搭建ECharts Options