Skyroc Admin React
专题能力

图表与 Dashboard

使用 home dashboard、useEcharts、主题暗色模式、尺寸监听和菜单徽标构建管理端数据看板

这一页说明 apps/admin 当前 Dashboard 的组成方式。重点不是讲 ECharts 全部 API,而是说明项目里已经封装好的 useEcharts()、首页模块和菜单 badge 怎么协作。

当前首页是一个真实可运行的 dashboard 示例,但其中数据仍以静态或示例数据为主。接真实后端时,应把数据请求放在服务模块和页面模块之间,不要把请求、图表初始化、主题切换和 resize 全部揉在一个组件里。

适用场景

场景重点位置
新增首页图表模块apps/admin/src/pages/(admin)/home/modules
接入 ECharts 并跟随暗色模式apps/admin/src/hooks/use-echarts.ts
做响应式图表尺寸useSize(domRef)chart.resize()
更新图表数据updateOptions()、服务模块 query hooks
给首页菜单显示动态徽标useAdminMenuBadges()staticData.menu.badge.valueKey

当前实现位置

文件职责
apps/admin/src/pages/(admin)/home/index.tsx首页页面组合,注册 HOME_MENU_BADGE_KEY,挂载卡片、折线图、饼图和项目动态模块。
apps/admin/src/pages/(admin)/home/modules/LineChart.tsx折线图模块示例。
apps/admin/src/pages/(admin)/home/modules/PieChart.tsx饼图模块示例。
apps/admin/src/hooks/use-echarts.tsECharts 注册、初始化、主题切换、尺寸变化、销毁和 options 更新封装。
packages/web/admin-theme/src/hooks/use-theme.ts图表读取的主题状态来源。
packages/web/admin-layouts/src/modules/menus菜单 badge 的运行时状态来源。

核心概念

首页负责组合,不负责所有细节

home/index.tsx 当前只做三件事:

职责当前行为
菜单 badge调用 setMenuBadgeValue('home.updates', 25)
页面布局用 Ant Design SpaceRowCol 组合模块。
模块挂载挂载 HeaderBannerCardDataLineChartPieChartProjectNewsCreativityBanner

图表 options、卡片内容和模块样式都放在 modules 目录里。新增模块时继续保持这个边界,首页不要变成一个大文件。

useEcharts 管生命周期

useEcharts() 当前负责:

能力实现方式
注册 ECharts 能力在 hook 文件顶部调用 echarts.use(),只注册当前项目需要的 chart 和 component。
初始化实例只有在容器有尺寸后才 echarts.init(domRef.current, chartTheme)
暗色模式读取 useSettingsTheme()darkMode,模式变化时销毁并重建实例。
loading 样式默认 onRender 会按主题设置 loading 文本色、遮罩色和主题色。
数据更新updateOptions() 合并当前 options,清空旧图表后重新 setOption()
销毁useUnmount() 中 dispose 实例。

因此,页面模块不需要自己写 echarts.init()window.resize 监听或 unmount dispose。

最小图表模块示例

下面示例展示一个图表模块应如何使用 useEcharts()。真实数据可以从 React Query hook 里取,然后在数据变化时调用 updateOptions()

import type { ECOption } from '@/hooks/use-echarts';
import { useEcharts } from '@/hooks/use-echarts';

interface OrderTrendChartProps {
  /** 图表容器高度,保证 ECharts 初始化时有稳定尺寸。 */
  height?: number;
}

const OrderTrendChart = (props: OrderTrendChartProps) => {
  const { height = 320 } = props;

  const { domRef, updateOptions } = useEcharts<ECOption>(() => ({
    grid: {
      bottom: 24,
      left: 32,
      right: 16,
      top: 24
    },
    series: [
      {
        data: [120, 180, 160, 220, 260, 310, 280],
        smooth: true,
        type: 'line'
      }
    ],
    tooltip: {
      trigger: 'axis'
    },
    xAxis: {
      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
      type: 'category'
    },
    yAxis: {
      type: 'value'
    }
  }));

  function refreshData() {
    updateOptions(() => ({
      series: [
        {
          data: [180, 210, 190, 260, 300, 330, 360],
          smooth: true,
          type: 'line'
        }
      ]
    }));
  }

  return (
    <ACard title="订单趋势">
      <AButton onClick={refreshData}>刷新</AButton>
      <div ref={domRef} style={{ height }} />
    </ACard>
  );
};

关键点是容器必须有稳定高度,否则 useSize() 拿不到有效尺寸,图表不会按预期初始化。

菜单 badge

首页菜单 badge 由两部分组成:

const HOME_MENU_BADGE_KEY = 'home.updates';
staticData: {
  menu: {
    badge: {
      type: 'normal',
      valueKey: HOME_MENU_BADGE_KEY
    }
  }
}

运行时通过 useAdminMenuBadges() 写入同一个 key:

function syncHomeBadge() {
  setMenuBadgeValue(HOME_MENU_BADGE_KEY, 25);
}

staticData.menu.badge.valueKey 是菜单和运行时状态之间的连接点。不要把动态数量直接写死在 staticData 里,否则后续接接口时无法统一刷新。

接真实数据

接真实 dashboard 数据时,推荐拆成三层:

示例位置职责
服务模块apps/admin/src/service/api/dashboard定义 urlsapihookskeystypes
页面模块home/modules/OrderTrendChart.tsx调用 query hook,把返回数据转成 ECharts options。
页面组合home/index.tsx决定模块顺序、布局和菜单 badge 更新。

这样图表模块只关心展示和数据映射,不直接拼接 URL,也不维护全局菜单状态。

常见误区

误区正确做法
在每个图表组件里直接 echarts.init()使用 useEcharts() 统一处理主题、尺寸和销毁。
容器没有高度就挂图表给图表容器稳定高度或响应式高度。
暗色模式变化后只改 CSSuseEcharts() 重建 ECharts 实例,使用对应 chart theme。
把 dashboard 接口写在 home/index.tsx建服务模块,再把数据交给子模块。
动态 badge 写死在 staticDatastaticData 只放 valueKey,运行时用 setMenuBadgeValue() 更新值。

相关链接

On this page