如何在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
相关产品推荐
相关产品推荐

