import React, { Component } from 'react'; import classNames from 'classnames'; import memoizeOne from 'memoize-one'; import { Empty, Spin } from 'antd'; import { ScrollSync, Grid, AutoSizer } from 'react-virtualized'; import _ from 'lodash'; import scrollbarSize from 'dom-helpers/scrollbarSize'; import styles from './Table.less'; import { TableProps, TableState, ColumnProps, RowProps } from './interface'; import Column from './Column'; import Cell from './Cell'; import { getMergeClassName, getMergeStyle } from '../utils/utils'; import { Consumer } from './context'; const borderStyle = '1px solid #ececec'; export default class AirTable extends Component { config: any; grid1: any; grid2: any; grid3: any; grid4: any; grid5: any; grid6: any; memoizeData: Function; memoizeLeftColumns: Function; memoizeRightColumns: Function; memoizeColumns: Function; memoizeLeftWidth: Function; memoizeTotalWidth: Function; static getDerivedStateFromProps(nextProps: TableProps, prevState: TableState) { const { columns, dataSource } = prevState; const nextState: TableState = { ...prevState, }; if (JSON.stringify(dataSource) !== JSON.stringify(nextProps.dataSource)) { nextState.dataSource = nextProps.dataSource; } if (JSON.stringify(columns) !== JSON.stringify(nextProps.columns)) { nextState.columns = nextProps.columns; } return nextState; } constructor(props: TableProps) { super(props); const { columns, dataSource, width = 0, height = 0, rowHeight, headerHeight, columnWidth } = props; this.state = { columns, dataSource, tableWidth: width, tableHeight: height, }; this.config = { overscanColumnCount: 5, overscanRowCount: 5, columnWidth: columnWidth || 150, rowHeight: rowHeight || 80, headerHeight: headerHeight || 60, }; this.memoizeLeftColumns = memoizeOne(this.getLeftColumns); this.memoizeRightColumns = memoizeOne(this.getRightColumns); this.memoizeColumns = memoizeOne(this.getShowColumns); this.memoizeLeftWidth = memoizeOne(this.getLeftWidth); this.memoizeTotalWidth = memoizeOne(this.getTotalWidth); this.memoizeData = memoizeOne(this.filterData); } // 获取左侧固定列数量 getLeftColumns = (columns: ColumnProps[]) => { return columns.filter((item) => { return !!item.showStatus && item.fixed && item.fixed !== 'right'; }); }; // 获取右侧固定列数量 getRightColumns = (columns: ColumnProps[]) => { return columns.filter((item) => { return !!item.showStatus && item.fixed === 'right'; }); }; // 获取表格显示列 getShowColumns = (columns: ColumnProps[]) => { return columns.filter((item) => { return !!item.showStatus && (!item.hideScope || item.hideScope.indexOf('LIST') === -1); }); }; // 获取左侧固定列总宽度 getLeftWidth = (leftColumns: ColumnProps[], columns: ColumnProps[]) => { const { tableWidth = 0 } = this.state; const { totalWidth, configWidth, defaultWidthLen } = this.memoizeTotalWidth(columns); const { columnWidth } = this.config; let colWidth = columnWidth; if (totalWidth < tableWidth) { colWidth = _.floor((tableWidth - configWidth) / defaultWidthLen); } let total = 0; leftColumns.map((item) => { total += item.width || colWidth; }); return total; }; // 获取右侧列总宽度 getTotalWidth = (columns: ColumnProps[]) => { const { columnWidth } = this.config; let configWidth = 0; let configWidthLen = 0; let defaultWidth = 0; let defaultWidthLen = 0; columns.map((item: any) => { if (item.width) { configWidth += item.width; configWidthLen += 1; } else { defaultWidth += columnWidth; defaultWidthLen += 1; } }); const totalWidth = configWidth + defaultWidth; return { configWidth, defaultWidth, totalWidth, configWidthLen, defaultWidthLen, }; }; // 获取表格显示数据 filterData = (columns: ColumnProps[], dataSource: RowProps[]) => { if (columns.length === 0 || dataSource.length === 0) { return []; } const cloneData = _.cloneDeep(dataSource); cloneData.map((item: any) => { item.rowData = item.rowData.filter((item2: any) => { return columns.some((temp: any) => { return temp.columnName === item2.colName; }); }); return item; }); return cloneData; }; // 获取每列的宽度 getColumnWidth = (columns: ColumnProps[], { index }: { index: number }) => { const { tableWidth = 0 } = this.state; const { totalWidth, configWidth, defaultWidthLen } = this.memoizeTotalWidth(columns); const { columnWidth } = this.config; let colWidth = columnWidth; if (totalWidth < tableWidth) { colWidth = _.floor((tableWidth - configWidth) / defaultWidthLen); } const columnObj = columns[index]; if (columnObj && columnObj.width) { return columnObj.width; } return colWidth; }; onResize = ({ width, height }: { width: number; height: number }) => { this.setState( { tableWidth: width, tableHeight: height, }, () => { this.grid1 && this.grid1.recomputeGridSize(); this.grid2 && this.grid2.recomputeGridSize(); this.grid3 && this.grid3.recomputeGridSize(); this.grid4 && this.grid4.recomputeGridSize(); this.grid5 && this.grid5.recomputeGridSize(); this.grid6 && this.grid6.recomputeGridSize(); }, ); }; // 下拉加载 onScroll = ({ clientHeight, scrollHeight, scrollTop }: any) => { const height = clientHeight + scrollTop || 0; const { onScroll, distanceToLoad = 0 } = this.props; if (scrollTop > 0 && height >= scrollHeight - distanceToLoad && typeof onScroll === 'function') { onScroll(); } }; // 渲染表头 renderHeaderCell = ( { showColumns, position }: { showColumns: ColumnProps[]; position?: string }, { columnIndex, key, style }: any, ) => { if (showColumns.length === 0) return null; const { sortConfig, showIndex, rowSelection, dataSource } = this.props; const { columnType = 1, columnName, columnChsName = '', columnAttrObj = {}, sortFlag, questionText, icon, } = showColumns[columnIndex]; return (
); }; renderBodyRow = ( { showColumns, showData, position }: { showColumns: ColumnProps[]; showData: RowProps; position?: string }, { columnIndex, key, rowIndex, style }: any, ) => { const { rowClassName, rowStyle, bordered = false } = this.props; // 默认边框覆盖掉传过来的样式 const rowBorderStyle: any = { borderLeft: 'none', borderRight: 'none', borderTop: 'none', borderBottom: borderStyle, }; if (bordered) { rowBorderStyle.borderRight = borderStyle; if (columnIndex === 0) { rowBorderStyle.borderLeft = borderStyle; } } const className = getMergeClassName(styles.bodyCell, rowClassName, { rowIndex }); const rowStyleObject = getMergeStyle(style, rowStyle, { rowIndex }); const mergedRowStyle = { ...rowStyleObject, ...rowBorderStyle, }; return this.renderBodyCellContainer( { showColumns, showData, position }, { columnIndex, key, rowIndex, rowClassName: className, rowStyle: mergedRowStyle, }, ); }; renderBodyCellContainer = ( { showColumns, showData, position }: { showColumns: ColumnProps[]; showData: RowProps; position?: string }, { columnIndex, key, rowIndex, rowClassName, rowStyle }: any, ) => { const columnConfig = showColumns[columnIndex]; const { cellClassName, cellStyle } = columnConfig; const className = getMergeClassName(rowClassName, cellClassName, { columnIndex, columnConfig }); const mergedCellStyle = getMergeStyle(rowStyle, cellStyle, { columnIndex, columnConfig }); return this.renderBodyCell( { showColumns, showData, position }, { columnIndex, key, rowIndex, cellClassName: className, cellStyle: mergedCellStyle, }, ); }; // 渲染数据 renderBodyCell = ( { showColumns, showData, position }: { showColumns: ColumnProps[]; showData: RowProps; position?: string }, { columnIndex, key, rowIndex, cellClassName, cellStyle }: any, ) => { const { columns, dataSource } = this.state; const { emitChangeCell, paginationConfig, showIndex, emptyPlaceholder, cellEditable, rowSelection, contentMenu, } = this.props; if (showColumns.length === 0 || showData.length === 0) { return; } const record: any = dataSource[rowIndex]; // 行数据 const rowData: any = showData[rowIndex]; // 行数据 const columnConfig: any = showColumns[columnIndex]; // 列属性 const columnData: any = rowData.rowData[columnIndex]; // 列数据 const cellData: any = columnData && columnData.cellValueList; // 列数据 return ( ); }; render() { const { loading, noDataPlaceholder, loadComp } = this.props; const { columns, dataSource, tableWidth = 0, tableHeight = 0 } = this.state; const { overscanColumnCount, overscanRowCount, rowHeight, headerHeight } = this.config; const scrollbarWidth = scrollbarSize() || 0; const showColumns = this.memoizeColumns(columns); const leftColumns = this.memoizeLeftColumns(columns); const rightColumns = this.memoizeRightColumns(columns); // 有隐藏列时,修正数据与列头不匹配问题 const showData = this.memoizeData(showColumns, dataSource); const leftData = this.memoizeData(leftColumns, dataSource); const rightData = this.memoizeData(rightColumns, dataSource); // 左侧固定列数量 const leftCount = leftColumns.length; const rightCount = rightColumns.length; const columnCount = showColumns.length; const rowCount = showData.length; const leftWidth = this.memoizeLeftWidth(leftColumns, showColumns); const rightWidth = this.memoizeLeftWidth(rightColumns, showColumns); // let totalHeight: number = rowCount * rowHeight; let totalHeight: number = tableHeight - headerHeight; const { totalWidth } = this.memoizeTotalWidth(showColumns); // if (rowCount > 0 && totalWidth > tableWidth) { // // totalHeight = rowCount * rowHeight + scrollbarWidth; // totalHeight = tableHeight + scrollbarWidth; // } let realWidth = tableWidth; if (rowCount > 0 && rowCount * rowHeight > totalHeight) { realWidth = tableWidth + scrollbarWidth; } return ( {({ locale }) => ( {({ onScroll, scrollLeft, scrollTop }: any) => { return (
{totalWidth > tableWidth && leftCount > 0 && (
{ // 左侧表头
{ this.grid1 = dom; }} className={styles.headerGrid} columnWidth={this.getColumnWidth.bind(this, showColumns)} columnCount={leftCount} width={leftWidth} rowHeight={headerHeight} rowCount={1} height={headerHeight} cellRenderer={this.renderHeaderCell.bind(this, { showColumns: leftColumns, })} />
} { // 左侧固定列
{ this.grid2 = dom; }} className={styles.sideGrid} overscanRowCount={overscanRowCount} cellRenderer={this.renderBodyRow.bind(this, { showColumns: leftColumns, showData: leftData, })} columnWidth={this.getColumnWidth.bind(this, showColumns)} columnCount={leftCount} width={leftWidth} rowHeight={rowHeight} rowCount={rowCount} height={totalHeight - scrollbarWidth} scrollTop={scrollTop} />
}
)}
{({ width, height }: any) => { return (
{ // 中部表头
{ this.grid3 = dom; }} className={styles.headerGrid} overscanColumnCount={overscanColumnCount} columnWidth={this.getColumnWidth.bind( this, showColumns, )} columnCount={columnCount} width={width} rowHeight={headerHeight} rowCount={1} height={headerHeight} scrollLeft={scrollLeft} cellRenderer={this.renderHeaderCell.bind(this, { showColumns, })} />
} { // 中部内容
{rowCount > 0 ? ( { this.grid4 = dom; }} className={styles.centerGrid} overscanColumnCount={overscanColumnCount} overscanRowCount={overscanRowCount} columnWidth={this.getColumnWidth.bind( this, showColumns, )} columnCount={columnCount} width={realWidth} rowHeight={rowHeight} rowCount={rowCount} onScroll={(...arg: Array) => { onScroll(...arg); this.onScroll(arg[0]); }} height={totalHeight} cellRenderer={this.renderBodyRow.bind(this, { showColumns, showData, })} /> ) : ( columnCount > 0 && !loading && ( ) )} } ); }} {totalWidth > tableWidth && rightCount > 0 && (
{ // 右侧表头
{ this.grid5 = dom; }} className={styles.headerGrid} columnWidth={this.getColumnWidth.bind(this, showColumns)} columnCount={rightCount} width={rightWidth} rowHeight={headerHeight} rowCount={1} height={headerHeight} cellRenderer={this.renderHeaderCell.bind(this, { showColumns: rightColumns, position: 'right', })} />
} { // 右侧固定列
{ this.grid6 = dom; }} className={styles.sideGrid} overscanRowCount={overscanRowCount} cellRenderer={this.renderBodyRow.bind(this, { showColumns: rightColumns, showData: rightData, position: 'right', })} columnWidth={this.getColumnWidth.bind(this, showColumns)} columnCount={rightCount} width={rightWidth} rowHeight={rowHeight} rowCount={rowCount} height={totalHeight - scrollbarWidth} scrollTop={scrollTop} />
}
)}
{loading && (loadComp ? loadComp : )}
); }} )} ); } }