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

如何在React的Chart.js预约图表中避免项目重叠显示

如何在React的Chart.js预约图表中避免项目重叠显示

嘿,我完全懂你现在的困扰!同一个日期的预约项挤在Chart.js的条形图里叠成一团,根本看不清谁是谁对吧?我之前做类似的预约看板也遇到过这个问题,给你分享两个亲测有效的解决思路,结合你现有的React + Chart.js代码调整就行:

问题根源先理清楚

你现在的处理逻辑是把所有同日期的预约项都映射到了完全相同的X轴位置,Chart.js默认会把同位置的条形图重叠绘制,自然就看不到后面的项了。要解决这个问题,核心就是让同日期的每个项在X轴上有独立的显示空间,要么给它们加微小偏移,要么用多数据集分组展示。


方法一:X轴偏移法(适合并行项数量不固定的场景)

这个思路是给同一日期下的每个预约项分配一个带微小偏移的X值,让它们在同个日期标签下并排显示,单个项的日期则正常居中。

步骤1:预处理数据,添加偏移量

先把数据按日期分组,然后给每组里的每个项根据索引加偏移,同时保留原始日期标签用于提示:

import moment from 'moment';

// 按日期标签分组原始预约数据
const groupedByDate = scheduleData.data.reduce((groups, item) => {
  const xLabel = dayNames[Number(moment(item.start).format("d"))] + item.start.getDate();
  if (!groups[xLabel]) groups[xLabel] = [];
  groups[xLabel].push(item);
  return groups;
}, {});

// 处理数据,给同日期的项添加X轴偏移
const uniqueXLabels = Object.keys(groupedByDate);
const processedData = Object.entries(groupedByDate).flatMap(([xLabel, items]) => {
  // 计算偏移基准,让同日期的项整体居中显示
  const maxCount = items.length;
  const baseOffset = -0.2 * (maxCount - 1) / 2;
  
  return items.map((item, index) => {
    const xIndex = uniqueXLabels.indexOf(xLabel);
    const offset = baseOffset + 0.2 * index;
    return {
      x: xIndex + offset, // 带偏移的X值
      y: [moment(item.start).hour(), moment(item.end).hour()],
      title: item.title,
      name: item.name,
      xLabel: xLabel // 保留原始日期标签,用于tooltip提示
    };
  });
});

步骤2:配置Chart.js的X轴和提示框

把X轴设为线性类型,自定义刻度标签显示原始日期,同时调整提示框显示正确的日期:

const chartOptions = {
  scales: {
    x: {
      type: 'linear',
      ticks: {
        // 只在整数索引位置显示日期标签
        callback: function(value) {
          if (Number.isInteger(value) && value >= 0 && value < uniqueXLabels.length) {
            return uniqueXLabels[value];
          }
          return '';
        },
        stepSize: 1,
        autoSkip: false
      },
      grid: {
        drawOnChartArea: false // 隐藏X轴网格线,更清爽
      }
    },
    y: {
      ticks: {
        stepSize: 1,
        callback: (value) => `${value}:00` // 把Y轴刻度显示为"9:00"这样的格式
      }
    }
  },
  plugins: {
    tooltip: {
      // 提示框标题显示原始日期
      callbacks: {
        title: (context) => context[0].raw.xLabel,
        label: (context) => `${context.raw.name}: ${context.raw.title}`
      }
    }
  }
};

方法二:多数据集分组法(适合需要区分并行项的场景)

这个思路是把每个“并行位置”的预约项单独做成一个数据集,比如某日期有2个并行预约,就做2个数据集,Chart.js会自动把同日期的不同数据集条形图并排显示。

步骤1:按并行位置拆分数据集

先统计所有日期里并行预约的最大数量,然后给每个并行位置创建一个数据集:

import moment from 'moment';

// 按日期分组原始数据
const groupedByDate = scheduleData.data.reduce((groups, item) => {
  const xLabel = dayNames[Number(moment(item.start).format("d"))] + item.start.getDate();
  if (!groups[xLabel]) groups[xLabel] = [];
  groups[xLabel].push(item);
  return groups;
}, {});

const uniqueXLabels = Object.keys(groupedByDate);
// 找到所有日期中并行预约的最大数量
const maxParallelCount = Math.max(...Object.values(groupedByDate).map(g => g.length));

// 生成对应数量的数据集
const datasets = Array.from({ length: maxParallelCount }, (_, index) => {
  const datasetData = uniqueXLabels.map(xLabel => {
    const items = groupedByDate[xLabel];
    // 如果当前日期有第index个预约项,就返回数据,否则返回null(不显示)
    if (items[index]) {
      const item = items[index];
      return {
        y: [moment(item.start).hour(), moment(item.end).hour()],
        title: item.title,
        name: item.name
      };
    }
    return null;
  });

  return {
    label: `预约通道 ${index + 1}`, // 可以自定义数据集标签,也可以隐藏
    data: datasetData,
    backgroundColor: `rgba(${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, 0.7)`,
    barPercentage: 0.4, // 调整条形宽度,避免太挤
    categoryPercentage: 0.8
  };
});

步骤2:配置Chart.js的基础参数

这次X轴用类别型,直接绑定我们的日期标签即可:

const chartOptions = {
  scales: {
    x: {
      type: 'category',
      labels: uniqueXLabels,
      autoSkip: false
    },
    y: {
      ticks: {
        stepSize: 1,
        callback: (value) => `${value}:00`
      }
    }
  },
  plugins: {
    legend: {
      display: false // 如果不需要显示数据集标签,可以隐藏
    },
    tooltip: {
      callbacks: {
        label: (context) => `${context.raw.name}: ${context.raw.title}`
      }
    }
  }
};

两种方法的选择建议

  • 如果你的预约并行数量不固定(比如某天2个,某天1个),优先用X轴偏移法,视觉上更统一;
  • 如果需要给不同并行预约分配不同的标识(比如不同颜色代表不同诊室),用多数据集分组法更清晰,还能利用Chart.js的图例功能做筛选。

备注:内容来源于stack exchange,提问作者jyc-coder

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 13:40:27