You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ChartJS中实现X轴多级别分类标签并按角色分组

实现ChartJS X轴多级分组标签(含分隔线)

ChartJS原生确实没有内置多级分类X轴的功能,但通过自定义插件可以完全实现你的需求,且兼容chartjs-node-canvas服务端渲染场景。以下是具体实现步骤:

1. 预处理数据:按角色分组

先将你的用户数据按rolTitle分组,整理成「角色-用户列表」的结构,方便后续计算标签位置:

// 假设你的原始数据是这样的数组
const rawData = [
  { rolTitle: "管理员", userName: "张三", value: 12 },
  { rolTitle: "管理员", userName: "李四", value: 8 },
  { rolTitle: "编辑", userName: "王五", value: 15 },
  { rolTitle: "访客", userName: "赵六", value: 5 },
];

// 按rolTitle分组
const groupedByRole = rawData.reduce((acc, item) => {
  if (!acc[item.rolTitle]) {
    acc[item.rolTitle] = [];
  }
  acc[item.rolTitle].push(item);
  return acc;
}, {} as Record<string, typeof rawData>);

// 提取分组后的标签和数据
const xLabels = Object.values(groupedByRole).flatMap(group => group.map(item => item.userName));
const datasetValues = rawData.map(item => item.value);
// 记录每个角色对应的X轴索引范围,用于绘制分隔线和组标签
const roleRanges = Object.entries(groupedByRole).reduce((acc, [role, group], idx, arr) => {
  const start = idx === 0 ? 0 : arr.slice(0, idx).reduce((sum, [_, g]) => sum + g.length, 0);
  const end = start + group.length - 1;
  acc.push({ role, start, end });
  return acc;
}, [] as { role: string; start: number; end: number }[]);

2. 编写自定义X轴分组插件

这个插件会在X轴上方绘制组标签,并在组之间添加分隔线:

const multiLevelXAxisPlugin = {
  id: 'multiLevelXAxis',
  beforeDraw: (chart: any) => {
    const ctx = chart.ctx;
    const xAxis = chart.scales.x;
    const chartTop = chart.chartArea.top;
    const groupLabelY = chartTop - 20; // 组标签的Y坐标,可调整

    // 绘制组标签
    ctx.font = '14px Arial';
    ctx.fillStyle = '#333';
    ctx.textAlign = 'center';
    roleRanges.forEach(range => {
      // 计算组标签的X坐标(组内所有刻度的中间位置)
      const startX = xAxis.getPixelForTick(range.start);
      const endX = xAxis.getPixelForTick(range.end);
      const midX = (startX + endX) / 2;
      ctx.fillText(range.role, midX, groupLabelY);
    });

    // 绘制组分隔线
    ctx.strokeStyle = '#ccc';
    ctx.lineWidth = 1;
    roleRanges.forEach((range, idx) => {
      // 跳过最后一组的右侧分隔线
      if (idx === roleRanges.length - 1) return;
      const lineX = xAxis.getPixelForTick(range.end) + (xAxis.getPixelForTick(range.end + 1) - xAxis.getPixelForTick(range.end)) / 2;
      ctx.beginPath();
      ctx.moveTo(lineX, chartTop);
      ctx.lineTo(lineX, chart.chartArea.bottom);
      ctx.stroke();
    });
  }
};

3. 配置ChartJS实例(兼容chartjs-node-canvas)

在创建图表时,注册自定义插件,并调整布局留出组标签的空间:

import { CanvasRenderService } from 'chartjs-node-canvas';
import ChartDataLabels from 'chartjs-plugin-datalabels';

const width = 800;
const height = 400;

const canvasRenderService = new CanvasRenderService(width, height, (Chart) => {
  // 注册自定义插件和datalabels插件
  Chart.register(multiLevelXAxisPlugin, ChartDataLabels);
});

async function generateChart() {
  const configuration = {
    type: 'bar', // 假设你用的是柱状图,其他类型同理
    data: {
      labels: xLabels,
      datasets: [{
        label: '用户数据',
        data: datasetValues,
        backgroundColor: '#2ecc71'
      }]
    },
    options: {
      layout: {
        padding: {
          top: 40 // 顶部留出空间放组标签
        }
      },
      scales: {
        x: {
          ticks: {
            font: {
              size: 12
            }
          }
        },
        y: {
          beginAtZero: true
        }
      },
      // 保留你原有的datalabels配置
      plugins: {
        datalabels: {
          anchor: 'end',
          align: 'top',
          formatter: (value: number) => value,
          font: {
            weight: 'bold'
          }
        }
      }
    }
  };

  const imageBuffer = await canvasRenderService.renderToBuffer(configuration);
  // 后续将buffer返回给Handlebars模板渲染
  return imageBuffer;
}

关键说明

  • 社区说“无法实现”是指ChartJS原生无此功能,但自定义插件是官方支持的扩展方式,完全可行;
  • 插件中的坐标计算基于ChartJS的scales.x.getPixelForTick方法,能自动适配不同的图表尺寸和刻度位置;
  • 若需要调整组标签样式、分隔线样式,直接修改插件中的ctx.font、ctx.strokeStyle等属性即可;
  • 该方案完全兼容chartjs-node-canvas服务端渲染,无需额外调整。

内容的提问来源于stack exchange,提问作者Izlia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 07:01:17