Grids.jsx源码
/root/workspace/actionview/av-github-source-code/actionview-fe/app/components/gantt/Grids.jsx

import React, { PropTypes, Component } from 'react';
import _ from 'lodash';
const $ = require('$');
const GridItem = require('./GridItem');
const GridEmptyItem = require('./GridEmptyItem');
export default class Grids extends Component {
  constructor(props) {
    super(props);
  }
  static propTypes = {
    cellWidth: PropTypes.number.isRequired,
    collection: PropTypes.array.isRequired,
    dates: PropTypes.object.isRequired,
    foldIssues: PropTypes.array.isRequired,
    markedIssue: PropTypes.object.isRequired,
    today: PropTypes.string.isRequired
  }
  render() {
    const { 
      cellWidth,
      collection, 
      dates, 
      foldIssues, 
      markedIssue, 
      today=''
    } = this.props;
    let offset = 0;
    let clientWidth = document.body.clientWidth;
    let scrollTop = 0;
    let clientHeight = document.body.clientHeight;
    if ($('div.ganttview-slide-container').length > 0) {
      offset = $('div.ganttview-slide-container').scrollLeft();
      clientWidth = $('div.ganttview-slide-container').get(0).clientWidth;
      scrollTop = $('div.ganttview-slide-container').scrollTop();
      clientHeight = $('div.ganttview-slide-container').get(0).clientHeight;
    }
    const dates2 = _.flatten(_.values(dates));
    const newCollection = _.reject(collection, (v) => v.parent && foldIssues.indexOf(v.parent.id) != -1);
    const topInd = _.max([ _.floor(scrollTop / 31) - 10, 0 ]);
    const bottomInd = _.min([ _.floor((scrollTop + clientHeight) / 31) + 10, newCollection.length - 1 ]);
    return (
      <div
        className='ganttview-grid'
        style={ { width: dates2.length * cellWidth + 'px' } }>
      { _.map(newCollection, (v, key) => {
        if (key < topInd || key > bottomInd) {
          return (
            <GridEmptyItem
              key={ v.id }
              cellWidth={ cellWidth }
              dates={ dates2 } />
            );
        } else {
          return (
            <GridItem
              key={ v.id }
              offset={ offset }
              clientWidth={ clientWidth }
              cellWidth={ cellWidth }
              dates={ dates2 }
              issue={ v }
              markedIssue={ markedIssue }
              today={ today } />
            );
        }
      } ) }
      </div>);
  }
}源码解读
这段代码定义了一个名为 Grids 的 React 组件,用于渲染甘特图中的网格行。这个组件负责根据传入的属性动态生成网格行,并根据折叠状态决定哪些行需要渲染详细信息,哪些行仅渲染空白占位符。
代码分析
导入模块
import React, { PropTypes, Component } from 'react';
import _ from 'lodash';
const $ = require('$');
const GridItem = require('./GridItem');
const GridEmptyItem = require('./GridEmptyItem');- 
React:React 的核心库。
- 
PropTypes:用于定义组件的 prop 类型检查。
- 
lodash:一个实用工具库,提供了很多常用的函数,如_.flatten和_.reject。
- 
$:jQuery 库,用于 DOM 操作。
- 
GridItem:一个子组件,用于渲染包含具体任务信息的网格行。
- 
GridEmptyItem:一个子组件,用于渲染空白占位符的网格行。
定义组件类
export default class Grids extends Component {
  constructor(props) {
    super(props);
  }- 
Grids类继承自Component,表示这是一个 React 组件。
- 
constructor构造函数初始化组件,并调用父类构造函数super(props)来初始化父类属性。
定义 prop 类型
static propTypes = {
    cellWidth: PropTypes.number.isRequired,
    collection: PropTypes.array.isRequired,
    dates: PropTypes.object.isRequired,
    foldIssues: PropTypes.array.isRequired,
    markedIssue: PropTypes.object.isRequired,
    today: PropTypes.string.isRequired
  }- propTypes是一个静态属性,用于定义组件接受的 prop 类型。
- 
cellWidth:每个单元格的宽度。
- 
collection:一个数组,包含所有任务信息。
- 
dates:一个对象,其键为月份,值为该月每一天的信息。
- 
foldIssues:一个数组,包含需要折叠的任务 ID。
- 
markedIssue:一个对象,表示标记的任务。
- 
today:今天的日期字符串。
渲染方法
render() {
    const { 
      cellWidth,
      collection, 
      dates, 
      foldIssues, 
      markedIssue, 
      today=''
    } = this.props;
    let offset = 0;
    let clientWidth = document.body.clientWidth;
    let scrollTop = 0;
    let clientHeight = document.body.clientHeight;
    if ($('div.ganttview-slide-container').length > 0) {
      offset = $('div.ganttview-slide-container').scrollLeft();
      clientWidth = $('div.ganttview-slide-container').get(0).clientWidth;
      scrollTop = $('div.ganttview-slide-container').scrollTop();
      clientHeight = $('div.ganttview-slide-container').get(0).clientHeight;
    }
    const dates2 = _.flatten(_.values(dates));
    const newCollection = _.reject(collection, (v) => v.parent && foldIssues.indexOf(v.parent.id) != -1);
    const topInd = _.max([ _.floor(scrollTop / 31) - 10, 0 ]);
    const bottomInd = _.min([ _.floor((scrollTop + clientHeight) / 31) + 10, newCollection.length - 1 ]);
    return (
      <div
        className='ganttview-grid'
        style={ { width: dates2.length * cellWidth + 'px' } }>
      { _.map(newCollection, (v, key) => {
        if (key < topInd || key > bottomInd) {
          return (
            <GridEmptyItem
              key={ v.id }
              cellWidth={ cellWidth }
              dates={ dates2 } />
            );
        } else {
          return (
            <GridItem
              key={ v.id }
              offset={ offset }
              clientWidth={ clientWidth }
              cellWidth={ cellWidth }
              dates={ dates2 }
              issue={ v }
              markedIssue={ markedIssue }
              today={ today } />
            );
        }
      } ) }
      </div>);
  }
}- render方法返回组件的 JSX 结构。
- 解构赋值 const { ... } = this.props来提取传入的 props。
- 获取当前滚动位置和可视区域尺寸:
- 
offset:水平滚动偏移量。
- 
clientWidth:可视区域的宽度。
- 
scrollTop:垂直滚动位置。
- 
clientHeight:可视区域的高度。
- 如果存在 .ganttview-slide-container元素,则从该元素获取滚动和尺寸信息。
- 计算扁平化的日期数组 dates2。
- 计算过滤后的任务集合 newCollection,排除需要折叠的任务。
- 计算顶部和底部索引 topInd和bottomInd,用于确定哪些任务需要渲染。
- 渲染一个 <div>容器,作为网格的基础。
- 使用 _.map循环遍历newCollection。
- 如果索引小于 topInd或大于bottomInd,则渲染GridEmptyItem,表示该行仅需要空白占位符。
- 否则,渲染 GridItem,表示该行需要详细信息。
- 每个任务行都有唯一的 key属性,以便 React 可以高效地管理 DOM 更新。
总结
这个 Grids 组件负责渲染甘特图中的网格行,根据传入的 props 动态生成网格行,并根据折叠状态决定哪些行需要渲染详细信息,哪些行仅渲染空白占位符。组件还考虑了滚动位置和可视区域尺寸,以优化渲染性能。
通过这种方式,组件能够灵活地适应不同的数据和布局需求,为用户提供清晰的任务进度视图,并且在滚动时只渲染可视区域的任务行,提高渲染效率。
                


