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

React中基于D3.js绘制部件操作时间轴图表的技术求助

技术求助:React中用D3.js实现分层甘特图

我需要基于以下数据,用D3.js在React中实现一个图表:

export const graphData = [
  {
    part_number: "N3241Z",
    serial_number: "40615705",
    date: "2022-12-16T00:00:00",
    part_desc: "PANEL ASSY, MODE CONTROL",
    action: "installed",
    description: "",
    service_bulletin: "SB-787-22-0002-03--.pdf",
  },
  {
    part_number: "N3241AZ",
    serial_number: "40615705",
    date: "2022-12-16T00:00:00",
    part_desc: "PANEL ASSY, MODE CONTROL",
    action: "installed",
    description: "",
    service_bulletin: "SB-787-22-0002-03--.pdf",
  },
  {
    part_number: "N3241AZ",
    serial_number: "40615705",
    date: "2022-12-16T00:00:00",
    part_desc: "PANEL ASSY, MODE CONTROL",
    action: "removed",
    description: "",
    service_bulletin: "SB-787-22-0002-03--.pdf",
  },
  {
    part_number: "N3241AZ",
    serial_number: "40615705",
    date: "2022-12-16T00:00:00",
    part_desc: "PANEL ASSY, MODE CONTROL",
    action: "wo_created",
    description: "",
    service_bulletin: "SB-787-22-0002-03--.pdf",
  },
  {
    part_number: "N3241ZX",
    serial_number: "40615705",
    date: "2022-12-16T00:00:00",
    part_desc: "PANEL ASSY, MODE CONTROL",
    action: "installed",
    description: "",
    service_bulletin: "SB-787-22-0002-03--.pdf",
  },
  {
    part_number: "N3241TZ",
    serial_number: "40615705",
    date: "2022-12-16T00:00:00",
    part_desc: "PANEL ASSY, MODE CONTROL",
    action: "wo_created",
    description: "",
    service_bulletin: "SB-787-22-0002-03--.pdf",
  },
  {
    part_number: "N3241TZCD",
    serial_number: "40615705",
    date: "2022-12-16T00:00:00",
    part_desc: "PANEL ASSY, MODE CONTROL",
    action: "wo_created",
    description: "",
    service_bulletin: "SB-787-22-0002-03--.pdf",
  },
];

需求说明

  • Y轴为日期,X轴为part_number
  • 不同的action按层级(zIndex)展示,同一part_number和日期下可以对应多个不同action
  • 我是D3.js新手,期望实现的UI效果是同一单元格内多个action元素分层排列的甘特图样式

以下是我目前的实现代码,请求技术帮助:

import {
  useEffect,
  useState,
  useRef,
  useCallback,
  useLayoutEffect,
} from 'react';
import { Flex } from '@core-ui/components';
// eslint-disable-next-line import/no-extraneous-dependencies
import * as d3 from 'd3';
import { ASSET_ACTIONS } from '../../util/types';

interface GanttChartProps {
  readonly uniqueAssets: any[];
  readonly filteredTableData: any[];
  readonly filteredData: any[];
  readonly themeVariant: string;
  readonly selectedElements: number[];
}

const getCodeChipColor = (code: string) => {
  switch (code) {
    case ASSET_ACTIONS.INSTALL:
      return '#DB9C09';
    case ASSET_ACTIONS.FAULTCODE:
      return '#CC4948';
    case ASSET_ACTIONS.REMOVED:
      return '#4BABBF';
    case ASSET_ACTIONS.WO_CREATED:
      return '#4CAF80';
    default:
      return null;
  }
};

export const GanttChart: React.FC<GanttChartProps> = ({
  uniqueAssets,
  filteredTableData,
  filteredData,
  themeVariant,
  selectedElements,
}) => {
  const svgRef = useRef<any>(null);
  const visibleAssets = selectedElements.map(
    (index: number) => uniqueAssets[index],
  );
  const uniqueDates = filteredTableData.map(d => d.date);
  useLayoutEffect(() => {
    console.log(selectedElements);

    const margin = { top: 20, right: 20, bottom: 30, left: 100 };
    const width = 1000 - margin.left - margin.right;
    const height = 900 - margin.top - margin.bottom;

    const svg = d3
      .select(svgRef.current)
      .attr('width', width + margin.left + margin.right)
      .attr('height', height + margin.top + margin.bottom)
      .append('g')
      .attr('transform', `translate(${margin.left},${margin.top})`);

    const x: any = d3.scaleBand().range([0, width]).padding(0.1);

    const y: any = d3.scaleBand().range([0, height]).padding(0.1);

    const xAxis = d3.axisTop(x).tickSize(0);

    const yAxis = d3.axisLeft(y).tickSize(0);

    // Update scales and axes with new data
    x.domain(visibleAssets);
    y.domain(uniqueDates);

    svg.append('g').call(xAxis).selectAll('.tick text');
    svg.append('g').call(yAxis);

    const bars = svg.selectAll('.bar').data(filteredData, d => d.part_number);

    // Remove old bars
    bars.exit().remove();

    // Update existing bars
    bars
      .attr('x', d => x(d.part_number))
      .attr('y', d => y(d.date))
      .attr('width', x.bandwidth())
      .style('fill', d => getCodeChipColor(d.action));

    // Append new bars
    bars
      .enter()
      .append('rect')
      .attr('class', 'bar')
      .attr('x', d => x(d.part_number))
      .attr('y', d => y(d.date))
      .attr('width', x.bandwidth())
      .attr('height', 20) // You can adjust the height of the bars as needed
      .style('fill', d => getCodeChipColor(d.action));
  }, [filteredData, selectedElements, filteredTableData]);

  return (
    <Flex
      shrink
      align="center"
      justify="center"
      css={{ flexDirection: 'column', width: '100%' }}>
      <svg ref={svgRef} />
    </Flex>
  );
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 05:02:03