VariableSizeList不固定尺寸的列表
固定列思维:新增一个容器,采用 absolute绝对定位的方法渲染

  1. <VariableSizeList
  2. itemSize={index => index}
  3. />

VirtualTable

  1. import React, {memo, useState} from 'react';
  2. import PropTypes from 'prop-types';
  3. import {VariableSizeList as List} from 'react-window';
  4. import AutoSizer from 'react-virtualized-auto-sizer';
  5. import {Icon} from 'antd';
  6. import classNames from 'classnames';
  7. VirtualTable.propTypes = {
  8. dataSource: PropTypes.array.isRequired,
  9. onChange: PropTypes.func.isRequired,
  10. };
  11. function VirtualTable({dataSource, onChange}) {
  12. const [sortIcon, setSortIcon] = useState('caret-up');
  13. const children = childrenColumns([{label: '综合', value: 0}, {label: '评价', value: 1}]);
  14. const {length} = dataSource
  15. // 数组第一个 values遍历生成表头
  16. const {values} = dataSource[0] || {};
  17. const columns = getColumns({values, children});
  18. if (dataSource.length) {
  19. columns.unshift(...mapColumns(['学科', '成绩']));
  20. }
  21. function onSort({index, colIndex}) {
  22. setSortIcon(prev => {
  23. const icon = prev === 'caret-up' ? 'caret-down' : 'caret-up';
  24. dataSource.sort((a, b) => {
  25. const current = a.values[colIndex-2][index]
  26. const next = b.values[colIndex-2][index]
  27. return (icon === 'caret-up') ? next - current : current - next;
  28. })
  29. return icon;
  30. });
  31. }
  32. function RenderThead({item, index}) {
  33. if (!item.children) {
  34. const style = {height: 81, lineHeight: '80px', padding: 8}
  35. return (
  36. <div className='tr' style={style}>
  37. {item.name}
  38. </div>
  39. )
  40. }
  41. function onClick({e, ...value}) {
  42. onSort(value)
  43. e.currentTarget.classList.add('on')
  44. }
  45. return (
  46. <div className='tr'>
  47. <div className='tr-first'>{String(item?.name).substr(0, 5)}</div>
  48. <div className="flex">
  49. {
  50. item.children.map((child, i) => {
  51. if (!child.name) return null
  52. return (
  53. <div className='th flex-1' key={`${index}-${i}-thead`}>
  54. {child.name}
  55. <Icon
  56. type={sortIcon}
  57. className='sort-icon'
  58. onClick={(e) => onClick({e, index: i, child, colIndex: index})}
  59. />
  60. </div>
  61. )
  62. })
  63. }
  64. </div>
  65. </div>
  66. )
  67. }
  68. function RenderTbody({index}) {
  69. return dataSource.map((item, i) => {
  70. const {keys, values} = item;
  71. const {length = 0} = keys;
  72. const oddClass = classNames({
  73. tr: true,
  74. flex: true,
  75. odd: i % 2 === 0
  76. });
  77. if(index < length) {
  78. return (
  79. <div className={oddClass}>
  80. <div className='flex-1'>{keys[index]}</div>
  81. </div>
  82. )
  83. }
  84. const colItem = values[index - length]
  85. if (!colItem) return null;
  86. const [time, ...args] = colItem;
  87. return (
  88. <div className={oddClass}>
  89. {args.map((item, i) => {
  90. return (
  91. <div
  92. key={`${index}-${i}`}
  93. className='flex-1'
  94. onClick={() => onChange({index: i, time})}
  95. >
  96. {item === null ? '-' : item}
  97. </div>
  98. )
  99. })}
  100. </div>
  101. );
  102. })
  103. }
  104. function Column(value) {
  105. const {index, style} = value;
  106. const item = columns[index];
  107. if (!item || index < 2) return null;
  108. return (
  109. <div style={style}>
  110. <RenderThead index={index} item={item}/>
  111. <RenderTbody index={index}/>
  112. </div>
  113. )
  114. }
  115. return (
  116. <AutoSizer>
  117. {({width, height}) => {
  118. console.log('wh', width, height)
  119. return (
  120. <>
  121. <div className="_list flex lock" style={{height: (length + 2) * 41}}>
  122. <div style={{flex: 1}}>
  123. <RenderThead index={0} item={columns[0]}/>
  124. <RenderTbody index={0}/>
  125. </div>
  126. <div style={{flex: 1}}>
  127. <RenderThead index={1} item={columns[1]}/>
  128. <RenderTbody index={1}/>
  129. </div>
  130. </div>
  131. <List
  132. className='_list'
  133. width={width}
  134. height={(length + 2) * 41 + 15}
  135. itemCount={columns.length}
  136. itemSize={index => (index < 2) ? 90 : 160}
  137. layout="horizontal"
  138. >
  139. {Column}
  140. </List>
  141. </>
  142. );
  143. }}
  144. </AutoSizer>
  145. );
  146. }
  147. export default memo(VirtualTable);
  148. // 固定左侧表头
  149. function mapColumns(dataSource) {
  150. return dataSource?.map((key, index) => ({
  151. name: key,
  152. key: 'keys',
  153. index,
  154. })) || [];
  155. }
  156. // children 表头
  157. function childrenColumns(dataSource) {
  158. return dataSource.map(({label}, index) => ({
  159. name: label,
  160. index,
  161. }));
  162. }
  163. // 数组第一个 values遍历生成表头
  164. function getColumns({values, children}) {
  165. return values.map(({time}, index) => {
  166. return {
  167. name: time,
  168. children,
  169. index,
  170. };
  171. });
  172. }