React - 甘特图时间轴

本文介绍了一个React组件——TimeComponents,用于展示时间轴。组件接收开始时间、结束时间、时间轴算法等参数,动态计算并渲染时间轴数据。时间轴包括日期和小时信息,支持显示当前时间点,并能根据不同的时间轴算法显示分钟间隔。同时,文章还展示了Less样式以定制组件的视觉效果。
// 组件调用
<TimeComponents
	// 维度数
	ganttSliderValue={ganttSliderValue}
	// 维度length
	ganttAlgorithm={ganttAlgorithm}
	// 开始时间
	startTime={ganttStartTime && ganttStartTime.format('YYYY-MM-DD HH:mm')}
	// 结束时间
	endTime={ganttEndTime && ganttEndTime.format('YYYY-MM-DD HH:mm')}
	// 是否显示日期
	dataTimeShow
	// 当前时间position定位
	currentTimePosition={currentTimePosition}
/>
// 时间轴封装
// index.jsx
import React, { Component } from 'react'
import classnames from 'classnames/bind'
import styles from './index.less'
import moment from 'moment'

const cx = classnames.bind(styles)
class TimeComponents extends Component {
  constructor (props) {
    super(props)
    this.state = {
      startTime: props.startTime,
      endTime: props.endTime,
      timeArr: []
    }
  }

  componentDidMount = () => {
    let { startTime, endTime } = this.props
    this.getTimeline(startTime, endTime)
  }

  componentWillReceiveProps = (nextProps) => {
    if (nextProps.startTime !== this.props.startTime || nextProps.endTime !== this.props.endTime) {
      // this.setState({
      //   startTime: nextProps.startTime,
      //   endTime: nextProps.endTime
      // })
      this.getTimeline(nextProps.startTime, nextProps.endTime)
    }
  }

  // 获取时间轴数据
  getTimeline = (startTime, endTime) => {
    // 结束时间与开始时间相差几天
    const diffDays = moment.duration(moment(endTime) - moment(startTime), 'ms').days() + 1
    if (diffDays < 0) {
      return
    }
    const startHours = moment(startTime).get('hours')
    const endHours = moment(endTime).get('hours')
    let TimeLineArr = []
    let diffStartHourArr = []
    let diffEndHourArr = []
    if (diffDays === 1) {
      // 结束时间与开始时间相差几个小时
      const hours = moment.duration(moment(startTime) - moment(endTime), 'ms').hours()
      // 开始时间到 0 点的下标
      let num
      for (let j = 0; j <= hours + 1; j++) {
        if (j + startHours < 24) {
          diffStartHourArr.push(j + startHours)
          num = j
        } else {
          diffStartHourArr.push(j - num - 1)
        }
      }
      let ganttDate = moment(startTime).format('YYYY-MM-DD')
      TimeLineArr.push({
        timeValues: diffStartHourArr,
        timeData: ganttDate
      })
    } else {
      for (let j = 0; j < 24 - startHours + 1; j++) {
        diffStartHourArr.push(j + startHours)
      }
      for (let k = 1; k < endHours + 1; k++) {
        diffEndHourArr.push(k)
      }
      for (let i = 0; i < diffDays; i++) {
        let ganttDate
        if (i === 0) {
          ganttDate = moment(startTime).format('YYYY-MM-DD')
          TimeLineArr.push({
            timeValues: diffStartHourArr,
            timeData: ganttDate
          })
        } else if (i === diffDays - 1) {
          ganttDate = moment(endTime).format('YYYY-MM-DD')
          TimeLineArr.push({
            timeValues: diffEndHourArr,
            timeData: ganttDate
          })
        } else {
          ganttDate = moment(startTime).add(i, 'd').format('YYYY-MM-DD')
          TimeLineArr.push({
            timeValues: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24],
            timeData: ganttDate
          })
        }
      }
    }
    this.setState({
      timeArr: TimeLineArr
    })
  }

  render () {
    const { timeArr } = this.state
    const { ganttAlgorithm, ganttSliderValue, dataTimeShow, currentTimePosition = 0 } = this.props
    let minutesArr = []
    const calcWidth = 60 * (30 / ganttSliderValue)
    switch (ganttAlgorithm) {
      case 1:
        minutesArr = [10, 20, 30, 40, 50]
        break
      case 2:
        minutesArr = [20, 40]
        break
      case 3:
        minutesArr = [30]
        break
      case 4:
        minutesArr = []
        break
    }
    return (
      <div className={cx('gantt-time-content')}>
        <span className='current-time-spot' style={{left: `${currentTimePosition}px`, bottom: ganttSliderValue === 60 && '-20px'}}>{moment().format('HH:mm')}</span>
        {
          timeArr.length > 0 && timeArr.map((item, index) => {
            return <div key={index} className={cx('time-dom')}>
              {
                dataTimeShow && <div className={cx('time-date')} style={{width: `calc(100% - ${calcWidth}px)`}} >{item.timeValues.length > 0 && item.timeData}</div>
              }
              {
                item.timeValues.length > 0 && item.timeValues.map((timeValueItme, timeValueIndex) => {
                  return <div key={timeValueIndex} className={cx('time-dom')}>
                    <h3 className={cx('time-dom-hours')}>
                      <span>
                        {
                          timeValueItme !== 24
                            ? timeValueItme < 10 ? `0${timeValueItme}` : timeValueItme
                            : '00'
                        }
                      </span>{ganttAlgorithm !== 4 && ':00'}
                    </h3>
                    <div className={cx('minutes-content')}>
                      {
                        minutesArr.map((minutesItem, minutesIndex) => {
                          return <div key={minutesIndex} className={cx({'minutes-list': true, 'paddingRight': minutesIndex === 4})}>
                            <div className={cx('minutes-list-icon')}>|</div>
                            <div>{minutesItem}</div>
                          </div>
                        })
                      }
                    </div>
                  </div>
                })
              }
            </div>
          })
        }
      </div>
    )
  }
}

export default TimeComponents
// 时间轴样式
// less
.gantt-time-content {
  position: relative;
  padding-top: 10px;
  white-space: nowrap;
  height: 100%;
  .current-time-spot {
    position: absolute;
    z-index: 9;
    bottom: 0;
    display: block;
    width: 40px;
    font-size: 12px;
    text-align: center;
    color: #22a7fd;
    transform: translateX(-20px);
  }
  .time-dom {
    display: inline-block;
    text-align: center;
    .time-date {
      margin-bottom: 8px;
      font-size: 14px;
    }
    .time-dom-hours {
      display: inline-block;
      margin: 0;
      width: 40px;
      margin-left: -10px;
      padding-left: 10px;
      text-align: center;
      font-size: 14px;
      font-weight: normal;
      vertical-align: top;
      font-family: PingFangSC-Regular;
      font-size: 14px;
      color: #FFFFFF;
      letter-spacing: 0;
    }
    .minutes-content {
      display: inline-block;
      margin-top: 20px;
      .minutes-list {
        width: 30px;
        height: 35px;
        display: inline-block;
        font-size: 10px;
        text-align: center;
        color: #9B9B9B;
        .minutes-list-icon {
          height: 6px;
          overflow: hidden;
        }
      }
    }
  }
}
### 安装与引入 要使用 `react-gantt-timeline` 实现甘特图,首先需要通过 npm 或 yarn 安装该库。执行以下命令进行安装: ```bash npm install react-gantt-timeline ``` 安装完成后,在 React 项目中引入 `react-gantt-timeline` 组件,并在需要显示甘特图的组件中导入相关样式文件: ```javascript import React from 'react'; import ReactGanttTimeline from 'react-gantt-timeline'; import 'react-gantt-timeline/lib/Timeline.css'; ``` ### 数据格式定义 `react-gantt-timeline` 需要特定的数据结构来渲染甘特图。通常,数据格式应包括任务 ID、任务名称、开始时间、结束时间以及可能的依赖关系等字段。以下是一个简单的数据示例: ```javascript const data = [ { id: 'task1', name: '任务 1', start: new Date('2023-10-01'), end: new Date('2023-10-05'), }, { id: 'task2', name: '任务 2', start: new Date('2023-10-06'), end: new Date('2023-10-10'), dependencies: ['task1'], }, ]; ``` ### 渲染甘特图 在组件中,通过将数据传递给 `ReactGanttTimeline` 组件来渲染甘特图: ```javascript function GanttChart() { const data = [ { id: 'task1', name: '任务 1', start: new Date('2023-10-01'), end: new Date('2023-10-05'), }, { id: 'task2', name: '任务 2', start: new Date('2023-10-06'), end: new Date('2023-10-10'), dependencies: ['task1'], }, ]; return ( <div style={{ height: '500px' }}> <ReactGanttTimeline data={data} /> </div> ); } ``` ### 自定义配置 `react-gantt-timeline` 提供了多种配置选项,例如时间轴的缩放级别、任务条的样式、是否显示依赖关系等。以下是一些常见的自定义配置: - **设置时间范围**:通过 `start` 和 `end` 属性定义甘特图的时间范围。 - **调整缩放级别**:通过 `zoom` 属性控制时间轴的缩放级别,例如 `day`, `week`, `month`。 - **显示依赖关系**:通过 `showDependencies` 属性控制是否显示任务之间的依赖关系线。 ```javascript <ReactGanttTimeline data={data} start={new Date('2023-10-01')} end={new Date('2023-10-31')} zoom="week" showDependencies={true} /> ``` ### 样式与交互 `react-gantt-timeline` 提供了丰富的样式和交互功能。可以通过 CSS 自定义任务条的外观,也可以通过事件处理实现点击任务条时的交互逻辑。例如,可以通过 `onTaskClick` 属性定义点击任务时的回调函数: ```javascript <ReactGanttTimeline data={data} onTaskClick={(task) => { console.log('点击的任务:', task); }} /> ``` ### 示例代码总结 以下是一个完整的示例代码,展示了如何使用 `react-gantt-timeline` 实现甘特图: ```javascript import React from 'react'; import ReactGanttTimeline from 'react-gantt-timeline'; import 'react-gantt-timeline/lib/Timeline.css'; function GanttChart() { const data = [ { id: 'task1', name: '任务 1', start: new Date('2023-10-01'), end: new Date('2023-10-05'), }, { id: 'task2', name: '任务 2', start: new Date('2023-10-06'), end: new Date('2023-10-10'), dependencies: ['task1'], }, ]; return ( <div style={{ height: '500px' }}> <ReactGanttTimeline data={data} start={new Date('2023-10-01')} end={new Date('2023-10-31')} zoom="week" showDependencies={true} onTaskClick={(task) => { console.log('点击的任务:', task); }} /> </div> ); } export default GanttChart; ``` ### 甘特图的优势 `react-gantt-timeline` 提供了直观的时间线视图,帮助开发人员快速构建项目管理工具或时间追踪界面。通过简单的配置,可以实现复杂的甘特图功能,如任务依赖关系、时间范围控制以及交互事件处理。[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值