import React, { Component } from 'react'; import { Checkbox } from 'antd'; import memoizeOne from 'memoize-one'; import { ScrollSync, Grid, AutoSizer } from 'react-virtualized'; import 'react-virtualized/styles.css'; import _ from 'lodash'; import styles from './Table.less'; import { TableProps, TableState } from './interface'; import Column from './Column'; import Cell from './Cell'; import extendIcon from '../assets/extend.png'; import IconFont from './base/extra/iconFont'; import scrollbarSize from 'dom-helpers/scrollbarSize'; import { getMergeClassName, getMergeStyle } from '@/apollo-table/utils/utils'; const borderStyle = '1px solid #e8e8e8'; export default class AirTable extends Component { private indexCount: any; config: any; memoizeLeftCount: Function; memoizeData: Function; static getDerivedStateFromProps(nextProps: TableProps, prevState: TableState) { const { columns, dataSource } = prevState; let 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, height } = props; this.state = { columns, dataSource, tableWidth: width || document.body.clientWidth, tableHeight: height || document.body.clientHeight - 40, }; this.config = { overscanColumnCount: 5, overscanRowCount: 5, columnWidth: 200, rowHeight: 40, }; this.memoizeLeftCount = memoizeOne(this.getLeftCount); this.memoizeData = memoizeOne(this.filterData); this.indexCount = {}; } componentDidMount() { window.addEventListener('resize', this.resize, false); } componentWillUnmount() { window.removeEventListener('resize', this.resize, false); } //重置窗口高度 resize = () => { this.setState({ tableHeight: document.body.clientHeight - 40, tableWidth: document.body.clientWidth, }); }; //获取左侧固定列数量 getLeftCount = (columns: []) => { let len = 0; columns.map((item: any) => { if (item.fixed) { len += 1; } }); return len; }; //表头隐藏时过滤数据源 filterData = (columns: [], dataSource: []) => { if (columns.length === 0 || dataSource.length === 0) { return []; } let cloneData = _.cloneDeep(dataSource); let i = 0; this.indexCount = {}; cloneData.map((item1: any) => { const itemData = item1; itemData.rowData = itemData.rowData.filter((item2: any) => columns.some((temp: any) => temp.columnName === item2.colName), ); if (itemData.groupId) { if (this.indexCount[itemData.groupId]) { this.indexCount[itemData.groupId].push(itemData.id); } else { i += 1; this.indexCount[itemData.groupId] = [itemData.id]; } itemData.index = i; } return itemData; }); return cloneData; }; // 下拉加载 onScroll = ({ clientHeight, scrollHeight, scrollTop }: any) => { const height = clientHeight + scrollTop || 0; const { onScroll, distanceToLoad = 100 } = this.props; if (scrollTop > 0 && height >= (scrollHeight - distanceToLoad) && typeof onScroll === 'function') { onScroll(); } }; //渲染表头 renderHeaderCell = ({ columnIndex, key, rowIndex, style }: any) => { const { columns } = this.state; if (columns.length === 0) return null; const { columnType, columnChsName, width, columnAttrObj } = columns[columnIndex] || {}; return (
); }; renderBodyRow = (data: any[], { 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(data, { columnIndex, key, rowIndex, rowClassName: className, rowStyle: mergedRowStyle, }); }; renderBodyCellContainer = (data: any[], { columnIndex, key, rowIndex, rowClassName, rowStyle }: any) => { const { columns } = this.state; const columnConfig = columns[columnIndex]; const { cellClassName, cellStyle } = columnConfig; const className = getMergeClassName(rowClassName, cellClassName, { columnIndex }); const mergedCellStyle = getMergeStyle(rowStyle, cellStyle, { columnIndex }); return this.renderBodyCell(data, { columnIndex, key, rowIndex, cellClassName: className, cellStyle: mergedCellStyle, }); }; //渲染数据 renderBodyCell = (data: any[], { columnIndex, key, rowIndex, cellClassName, cellStyle }: any) => { const { columns, dataSource } = this.state; const { emitChangeCell } = this.props; if (columns.length === 0 || data.length === 0) { return; } const rowData: any = dataSource[rowIndex]; // 原始行数据,真实使用的数据 const columnConfig: any = columns[columnIndex]; // 列属性 const columnData: any = rowData.rowData[columnIndex]; // 列数据 const cellData: any = columnData.cellValueList; // 列数据 return ( ); }; render() { const { columns, dataSource, tableHeight = 0, tableWidth = 0 } = this.state; const { overscanColumnCount, overscanRowCount, columnWidth, rowHeight } = this.config; let totalHeight = tableHeight; let colWidth = columnWidth; const columnCount = columns.length; const rowCount = dataSource.length; if (rowCount > 0 && rowCount * rowHeight < tableHeight) { // 表格行数少于一屏高度时,高度增加横向滚动条高度 totalHeight = rowCount * rowHeight + scrollbarSize(); } if (columnCount > 0 && columnCount * columnWidth < tableWidth) { // 表格宽度不足一屏时,按屏幕宽度平分每列宽度 colWidth = Math.floor(tableWidth / columnCount); } //有隐藏列时,修正数据与列头不匹配问题 const data = this.memoizeData(columns, dataSource); //左侧固定列数量 const leftCount = this.memoizeLeftCount(columns); return ( {({ onScroll, scrollLeft, scrollTop }: any) => { return (
{ //左侧表头
} { //左侧固定列
}
{({ width }: any) => { // 如果列太少不满一屏时,列头宽度按列计算 let headerWidth = width; if (rowCount * rowHeight > tableHeight) { // 表格行数大于一屏的高度时出现竖向滚动条,此时宽度减出滚动条宽度 headerWidth -= scrollbarSize(); } return (
{ //右侧表头
} { //右侧内容
) => { onScroll(...arg); this.onScroll(arg[0]); }} height={totalHeight} cellRenderer={this.renderBodyRow.bind(this, data)} columns={columns} />
}
); }}
); }} ); } }