image.png
react-window 固定列参考
https://codesandbox.io/s/0mk3qwpl4l?file=/src/index.js:0-1735

antd Table3x https://github.com/ctq123/ant-virtual-table
antd Table4x https://ant.design/components/table/#components-table-demo-virtual-list
https://github.com/wubostc/virtualized-table-for-antd

缺点:

  • 点击排序会自动刷新表格
  • innerElementType就是 sticky的列;固定列要和滚动的列分开 ```jsx import React, {memo, useState, createContext, forwardRef} from ‘react’; import PropTypes from ‘prop-types’; import {VariableSizeList as List} from ‘react-window’; import AutoSizer from ‘react-virtualized-auto-sizer’; import {Icon} from ‘antd’; import classNames from ‘classnames’;

const Context = createContext();

VirtualTable.propTypes = { dataSource: PropTypes.array.isRequired, };

function VirtualTable({dataSource}) { const [sortIcon, setSortIcon] = useState(‘caret-up’); const children = childrenColumns([{label: ‘综合’, value: 0}, {label: ‘评价’, value: 1}]); const {length} = dataSource

// 数组第一个 values遍历生成表头 const {values} = dataSource[0] || {}; const columns = getColumns({values, children, onClick}); if (dataSource.length) { columns.unshift(…mapColumns([‘学科’, ‘成绩’])); }

function onClick(value) { console.log(‘click’, value) }

function onSort({index, colIndex}) { setSortIcon(prev => { return prev === ‘caret-up’ ? ‘caret-down’ : ‘caret-up’; });

  1. setTimeout(() => {
  2. dataSource.sort((a, b) => {
  3. const item = a.values[colIndex][index]
  4. const bitem = b.values[colIndex][index]
  5. return (sortIcon === 'caret-up') ? bitem - item : item - bitem;
  6. })
  7. }, 0)

}

function RenderThead({item, index}) { if (!item.children) { const style = {height: 81, lineHeight: ‘80px’, padding: 8} return (

{item.name}
) }

  1. function onClick({e, ...value}) {
  2. onSort(value)
  3. e.currentTarget.classList.add('on')
  4. }
  5. return (
  6. <div className='tr'>
  7. <div className='tr-first'>{item.name}</div>
  8. <div className="flex">
  9. {
  10. item.children.map((child, i) => {
  11. if (!child.name) return null
  12. return (
  13. <div className='th flex-1' key={`${index}-${i}-thead`}>
  14. {child.name}
  15. <Icon
  16. type={sortIcon}
  17. className='sort-icon'
  18. onClick={(e) => onClick({e, index: i, child, colIndex: index})}
  19. />
  20. </div>
  21. )
  22. })
  23. }
  24. </div>
  25. </div>
  26. )

}

function RenderTbody({index}) { return dataSource.map((item, i) => { const {keys, values} = item; const {length = 0} = keys;

  1. const oddClass = classNames({
  2. tr: true,
  3. flex: true,
  4. odd: i % 2 === 0
  5. });
  6. if (index < length) {
  7. return (
  8. <div className={oddClass}>
  9. <div className='flex-1'>{keys[index]}</div>
  10. </div>
  11. )
  12. }
  13. const colItem = values[index - length]
  14. if (!colItem) return (
  15. <div className={oddClass}>
  16. <div className='flex-1' />
  17. </div>
  18. );
  19. const [time, a, b, c, ...args] = colItem;
  20. return (
  21. <div className={oddClass}>
  22. {args.map((item, i) => {
  23. return (
  24. <div
  25. key={`${index}-${i}`}
  26. className='flex-1'
  27. onClick={() => onClick({index: i, time})}
  28. >
  29. {item === null ? '' : item}
  30. </div>
  31. )
  32. })}
  33. </div>
  34. );
  35. })

}

function Column(value) { const {index, style} = value; const item = columns[index]; if (!item) return null;

  1. return (
  2. <div style={style}>
  3. <RenderThead index={index} item={item}/>
  4. <RenderTbody index={index}/>
  5. </div>
  6. )

}

const ItemWrapper = ({data, index, style}) => { const {ItemRenderer, stickyIndices} = data; // 过滤掉固定的行 if (stickyIndices && stickyIndices.includes(index)) { return null; } return ; };

function StickyList(props) { const {children, stickyIndices, …rest} = props; return ( {ItemWrapper} ); }

// 固定的行 const StickyRow = ({index}) => { console.log(12, index) const item = columns[index]; if (!item) return null;

  1. const _style = {height: 81, lineHeight: '80px', padding: 8}
  2. return (
  3. <div style={{float: 'left', width: '50%'}}>
  4. <div className='tr' style={_style}>
  5. {item.name}
  6. </div>
  7. <RenderTbody index={index}/>
  8. </div>
  9. )

};

const innerElementType = forwardRef(({children, …rest}, ref) => { return ( {({stickyIndices}) => (

{stickyIndices.map(index => ( ))}
{children}
)} ) });

return ( {({width, height}) => { return ( ( { return (index < 2) ? 80 : 160 }} stickyIndices={[0, 1]} // 那几列固定 layout=”horizontal” > {Column} ) ); }} ); }

export default memo(VirtualTable);

// 固定左侧表头 function mapColumns(dataSource) { return dataSource.map((key, i) => ({ name: key, key: ‘keys’, })); }

// children 表头 function childrenColumns(dataSource) { return dataSource.map(({label}, i) => ({ name: label, index: i, key: ‘values’, })); }

// 遍历数组第一个values生成表头 function getColumns({values, children}) { return values.map(([time], i) => { return { name: toHours(time), children, }; }); }

  1. RenderCell
  2. ```jsx
  3. function RenderCell({ className, value = '' }) {
  4. return (
  5. <div className={className}>
  6. <div className='flex-1'>{value}</div>
  7. </div>
  8. );
  9. }

数据格式

  1. [
  2. {
  3. keys: ['标题', '描述'],
  4. values: [{time: 1628148180000, title: '', desc: '', value: 100}]
  5. },
  6. {
  7. keys: ['标题', '描述'],
  8. values: [{time: 1628148190000, title: '', desc: '', value: 100}]
  9. },
  10. ]