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 '../utils/scrollbarSize'; export default class AirTable extends Component { private indexCount: any; config: any; memoizeLeftCount: Function; memoizeData: Function; static getDerivedStateFromProps(nextProps: TableProps, prevState: TableState) { const { prevProps } = prevState; let nextState: TableState = { ...prevState, prevProps: nextProps, }; if (JSON.stringify(prevProps.dataSource) !== JSON.stringify(nextProps.dataSource)) { nextState.dataSource = nextProps.dataSource; } if (JSON.stringify(prevProps.columns) !== JSON.stringify(nextProps.columns)) { nextState.columns = nextProps.columns; } if (JSON.stringify(prevProps.checked) !== JSON.stringify(nextProps.checked)) { nextState.checked = nextProps.checked; } nextState.height = document.body.clientHeight - 193; return nextState; } constructor(props: TableProps) { super(props); const { columns, dataSource, tableId, checked = [] } = props; this.state = { columns, dataSource, checked, tableId, prevProps: props, height: document.body.clientHeight - 193, }; 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); } //重置窗口高度 resize = () => { this.setState({ height: document.body.clientHeight - 193, }); }; //获取左侧固定列数量 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; }; // 复选框选择 changeCheckbox = (record: any, flag: boolean) => { const { changeChecked, hasGroup } = this.props; const { checked, dataSource } = this.state; let temp = checked.slice(); if (flag) { if (hasGroup) { const selected = dataSource.filter((item: any) => { return item.groupId === record.groupId; }); temp = temp.concat(selected); } else { temp.push(record); } } else { if (hasGroup) { temp = temp.filter((item: any) => { return item.groupId !== record.groupId; }); } const index = temp.findIndex((item: any) => { return item.id === record.id; }); temp.splice(index, 1); } if (typeof changeChecked === 'function') { changeChecked({ checked: temp }); } }; // 检测当前分组是否全部选中 getCheckedAll = () => { const { checked, dataSource } = this.state; const temp = checked.slice(); const result = temp.filter((v: any) => { return dataSource.some((item: any) => { return item.id === v.id; }); }); return result.length === dataSource.length && result.length !== 0; }; // 全选/反选 changeCheckboxAll = () => { const { checked, dataSource } = this.state; const { changeChecked } = this.props; const temp = checked.slice(); const flag = this.getCheckedAll(); let result = []; if (flag) { result = temp.concat(dataSource).filter((v: any) => { return ( temp.some((item: any) => { return item.id === v.id; }) && !dataSource.some((item: any) => { return item.id === v.id; }) ); }); } else { result = temp.concat( dataSource.filter((v: any) => { return !temp.some((item: any) => { return item.id === v.id; }); }), ); } if (typeof changeChecked === 'function') { changeChecked({ checked: result }); } }; // 下拉加载 onScrollRow = ({ clientHeight, scrollTop }: any) => { const height = clientHeight + scrollTop || 0; const { dataSource = [] } = this.state; const { rowHeight } = this.config; const { onScroll } = this.props; const offset = 100; if (dataSource.length === 0) return; if (height + offset >= dataSource.length * rowHeight && 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] || {}; if (columnIndex === 0) { return (
); } return (
); }; // 渲染首列复选框 renderCheckbox = (record: any, rowIndex: number) => { const { checked } = this.state; const { hasGroup } = this.props; const flag = checked.some((item: any) => { return item.id === record.id; }); let checkbox = flag ? ( ) : ( <>
{record.index || rowIndex + 1}
); if (hasGroup) { if ( this.indexCount[record.groupId][Math.floor((this.indexCount[record.groupId].length - 1) / 2)] !== record.id ) { // 填充透明的复选框使样式保持一致 checkbox = ; } } return checkbox; }; //渲染数据 renderBodyCell = (data: any[], { columnIndex, key, rowIndex, style }: any) => { const { columns, dataSource } = this.state; const { updateData, hasGroup, showForm = () => {}, delData, getData, extraMenu, noAdd, noEdit, noDel, tableId, flush, showHistory = () => {}, } = this.props; if (columns.length === 0 || data.length === 0) { return; } const filterRowData: any = data[rowIndex]; // 过滤掉隐藏项并追加行号的行数据,只在渲染逻辑中使用 const rowData: any = dataSource[rowIndex]; // 原始行数据,真实使用的数据 const columnConfig: any = columns[columnIndex]; // 列属性 const columnData: any = rowData.rowData[columnIndex]; // 列数据 const cellData: any = columnData.cellValueList; // 列数据 let className = styles.indexCell; // 只读列不可编辑样式 if (columnConfig.readOnlyFlag || rowData.isLocked || noEdit) { className += ` ${styles.disabled}`; } let extraIndexCellStyle = {}; let extraCellContentStyle = {}; if (hasGroup) { if (columnConfig.splitFlag) { // 正常列额外样式 extraIndexCellStyle = { borderTop: this.indexCount[rowData.groupId][0] !== rowData.id ? '1px solid #F7F7F7' : '1px solid #e8e8e8', borderBottom: rowIndex + 1 < data.length ? 0 : '1px solid #e8e8e8', }; } else { // 不可拆分列额外样式 extraIndexCellStyle = { borderTop: this.indexCount[rowData.groupId][0] !== rowData.id ? 0 : '1px solid #e8e8e8', borderBottom: rowIndex + 1 < data.length ? 0 : '1px solid #e8e8e8', }; // 不可拆分列数据样式 extraCellContentStyle = { display: this.indexCount[rowData.groupId][ Math.floor((this.indexCount[rowData.groupId].length - 1) / 2) ] !== rowData.id ? 'none' : 'auto', }; } } if (columnIndex === 0) { return (
{this.renderCheckbox(filterRowData, rowIndex)}
edit
0 ? styles.firstCell : styles.empty} columns={columns} columnConfig={columnConfig} rowData={rowData} cellData={cellData} updateData={updateData} showForm={showForm} delData={delData} hasGroup={hasGroup} getData={getData} extraMenu={extraMenu} noAdd={noAdd} noEdit={noEdit} noDel={noDel} tableId={tableId} flush={flush} /> {(tableId === 1 || tableId === 14) && (
{filterRowData.groupHistoryCount && filterRowData.groupHistoryCount > 1 && ( {`(${ filterRowData.groupHistoryCount > 99 ? '99+' : filterRowData.groupHistoryCount })`} )}
)}
{(tableId === 1 || tableId === 14) && (
)}
); } return (
); }; render() { const { columns, dataSource, checked } = this.state; const { changeChecked, operateConfig = {}, hasGroup } = this.props; const { groupConfig } = operateConfig; const { overscanColumnCount, overscanRowCount, columnWidth, rowHeight } = this.config; let { height } = this.state; if (dataSource.length * rowHeight < height) { height = dataSource.length * rowHeight; } //有隐藏列时,修正数据与列头不匹配问题 const data = this.memoizeData(columns, dataSource); //左侧固定列数量 const leftCount = this.memoizeLeftCount(columns); const rowCount = dataSource.length; const columnCount = columns.length; return ( <> {({ onScroll, scrollLeft, scrollTop }: any) => { return (
{ //左侧表头
} { //左侧固定列
}
{({ width }: any) => { // 如果列太少不满一屏时,列头宽度按列计算 const headerWidth: number = columnWidth * columnCount < width ? columnWidth * columnCount : width; return (
{ //右侧表头
} { //右侧内容
) => { onScroll(...arg); this.onScrollRow(arg[0]); }} cellRenderer={this.renderBodyCell.bind(this, data)} columns={columns} checked={checked} hasGroup={hasGroup} />
}
); }}
); }}
); } }