dynamic.jsx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. import React from 'react';
  2. import { Table, Switch, ButtonGroup, Button, Avatar } from '@douyinfe/semi-ui';
  3. import * as dateFns from 'date-fns';
  4. const DAY = 24 * 60 * 60 * 1000;
  5. const figmaIconUrl = 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/figma-icon.png';
  6. class App extends React.Component {
  7. constructor(props) {
  8. super(props);
  9. const dataTotalSize = 46;
  10. const columns = [
  11. {
  12. title: '标题',
  13. dataIndex: 'name',
  14. width: 400,
  15. render: (text, record, index) => {
  16. return (
  17. <div>
  18. <Avatar size="small" shape="square" src={figmaIconUrl} style={{ marginRight: 12 }}></Avatar>
  19. {text}
  20. </div>
  21. );
  22. },
  23. filters: [
  24. {
  25. text: 'Semi Design 设计稿',
  26. value: 'Semi Design 设计稿',
  27. },
  28. {
  29. text: 'Semi Pro 设计稿',
  30. value: 'Semi Pro 设计稿',
  31. },
  32. ],
  33. onFilter: (value, record) => record.name.includes(value),
  34. },
  35. {
  36. title: '大小',
  37. dataIndex: 'size',
  38. sorter: (a, b) => a.size - b.size > 0 ? 1 : -1,
  39. render: (text) => `${text} KB`
  40. },
  41. {
  42. title: '所有者',
  43. dataIndex: 'owner',
  44. render: (text, record, index) => {
  45. return (
  46. <div>
  47. <Avatar size="small" color={record.avatarBg} style={{ marginRight: 4 }}>{typeof text === 'string' && text.slice(0, 1)}</Avatar>
  48. {text}
  49. </div>
  50. );
  51. }
  52. },
  53. {
  54. title: '更新日期',
  55. dataIndex: 'updateTime',
  56. sorter: (a, b) => a.updateTime - b.updateTime > 0 ? 1 : -1,
  57. render: (value) => {
  58. return dateFns.format(new Date(value), 'yyyy-MM-dd');
  59. }
  60. }
  61. ];
  62. this.getData = () => {
  63. const data = [];
  64. for (let i = 0; i < dataTotalSize; i++) {
  65. const isSemiDesign = i % 2 === 0;
  66. const randomNumber = (i * 1000) % 199;
  67. data.push({
  68. key: '' + i,
  69. name: isSemiDesign ? `Semi Design 设计稿${i}.fig` : `Semi Pro 设计稿${i}.fig`,
  70. owner: isSemiDesign ? '姜鹏志' : '郝宣',
  71. size: randomNumber,
  72. updateTime: new Date().valueOf() + randomNumber * DAY,
  73. avatarBg: isSemiDesign ? 'grey' : 'red'
  74. });
  75. }
  76. return data;
  77. };
  78. const data = this.getData();
  79. this.data = data;
  80. this.mergeColumns = (column, columns, keys = ['dataIndex']) => {
  81. columns = [...columns];
  82. columns.forEach((curColumn, index) => {
  83. let isTarget = !!(keys && keys.length);
  84. for (let key of keys) {
  85. if (column[key] !== curColumn[key]) {
  86. isTarget = false;
  87. break;
  88. }
  89. }
  90. if (isTarget) {
  91. columns[index] = { ...curColumn, ...column };
  92. }
  93. });
  94. return columns;
  95. };
  96. this.filterData = (filters, dataSource) => {
  97. dataSource = [...dataSource];
  98. filters.forEach(filter => {
  99. let filteredValue = filter.filteredValue;
  100. let dataIndex = filter.dataIndex;
  101. if (Array.isArray(filteredValue) && filteredValue.length && dataIndex) {
  102. dataSource = dataSource.filter(
  103. data => filteredValue.filter(value => String(data[dataIndex]).indexOf(value) > -1).length
  104. );
  105. }
  106. });
  107. return dataSource;
  108. };
  109. this.getSelfSorterColumn = columns => {
  110. columns = columns || this.state.columns;
  111. return columns.filter(column => !!column.sorter)[0];
  112. };
  113. this.getSelfFilterColumns = columns => {
  114. columns = columns || this.state.columns;
  115. return columns.filter(column => Array.isArray(column.filteredValue) && column.filteredValue.length);
  116. };
  117. this.sortData = (sortObj, dataSource) => {
  118. let { sorter, sortOrder, dataIndex } = sortObj;
  119. if (sorter && sortOrder && typeof sorter !== 'function') {
  120. sorter = (a, b) => (a[dataIndex] > b[dataIndex] ? 1 : -1);
  121. }
  122. if (typeof sorter === 'function') {
  123. dataSource = [...dataSource].sort(sorter);
  124. if (sortOrder === 'descend') {
  125. dataSource = dataSource.reverse();
  126. }
  127. }
  128. return dataSource;
  129. };
  130. this.fetchData = (currentPage = 1, sorter = {}, filters = []) => {
  131. // console.log(`FetchData currentPage: `, currentPage);
  132. let pagination = { ...this.state.pagination, currentPage };
  133. return new Promise((res, rej) => {
  134. setTimeout(() => {
  135. let data = [...this.data];
  136. data = this.sortData(sorter, data);
  137. data = this.filterData(filters, data);
  138. let dataSource = data.slice(
  139. (currentPage - 1) * pagination.pageSize,
  140. currentPage * pagination.pageSize
  141. );
  142. pagination.total = data.length;
  143. res({
  144. dataSource,
  145. pagination,
  146. sorter,
  147. filters,
  148. });
  149. }, 1500);
  150. });
  151. };
  152. this.setPage = (currentPage, sorter, filters) => {
  153. if (this.state.loading) {
  154. return;
  155. }
  156. if (typeof currentPage !== 'number') {
  157. currentPage = (this.state.pagination && this.state.pagination.currentPage) || 1;
  158. }
  159. sorter = sorter || this.getSelfSorterColumn();
  160. filters = filters || this.getSelfFilterColumns();
  161. this.setState({ loading: true });
  162. this.fetchData(currentPage, sorter, filters)
  163. .then(({ dataSource, pagination, sorter, filters }) => {
  164. let columns = [...this.state.columns];
  165. columns = this.mergeColumns(sorter, columns);
  166. for (let filterObj of filters) {
  167. columns = this.mergeColumns(filterObj, columns);
  168. }
  169. this.setState({
  170. loading: false,
  171. pagination,
  172. dataSource,
  173. columns,
  174. });
  175. })
  176. .catch(err => {
  177. console.error(err);
  178. this.setState({ loading: false });
  179. });
  180. };
  181. this.toggleFixHeader = checked => {
  182. let scroll = { ...this.state.scroll };
  183. if (checked) {
  184. scroll.y = 300;
  185. } else {
  186. scroll.y = null;
  187. }
  188. this.setState({ scroll });
  189. };
  190. this.toggleFixColumns = checked => {
  191. let columns = [...this.state.columns];
  192. let scroll = { ...this.state.scroll };
  193. let expandCellFixed = this.state.expandCellFixed;
  194. let rowSelection = this.state.rowSelection;
  195. if (checked) {
  196. columns[0].fixed = true;
  197. if (rowSelection) {
  198. rowSelection = { ...rowSelection, fixed: true };
  199. }
  200. if (columns.length > 1) {
  201. columns[columns.length - 1].fixed = 'right';
  202. }
  203. scroll.x = '150%';
  204. expandCellFixed = true;
  205. } else {
  206. columns.forEach(column => {
  207. column.fixed = false;
  208. });
  209. scroll.x = null;
  210. expandCellFixed = false;
  211. if (rowSelection) {
  212. rowSelection = { ...rowSelection, fixed: false };
  213. }
  214. }
  215. this.setState({
  216. rowSelection,
  217. expandCellFixed,
  218. columns,
  219. scroll,
  220. });
  221. };
  222. this.toggleRowSelection = checked => {
  223. let rowSelection = this.state.rowSelection;
  224. // const anyColumnFixed = this.state.columns.some(column => !!column.fixed);
  225. if (checked) {
  226. rowSelection = {
  227. width: 48,
  228. fixed: true,
  229. onChange: (selectedRowKeys, selectedRows) =>
  230. console.log(
  231. 'Selection changed, selectedRowKeys: ',
  232. selectedRowKeys,
  233. 'selectedRows: ',
  234. selectedRows
  235. ),
  236. };
  237. } else {
  238. rowSelection = null;
  239. }
  240. this.setState({ rowSelection });
  241. };
  242. this.toggleLoading = checked => {
  243. let loading = this.state.loading;
  244. if (checked) {
  245. loading = true;
  246. } else {
  247. loading = false;
  248. }
  249. this.setState({ loading });
  250. };
  251. this.toggleExpandedRowRender = checked => {
  252. let expandedRowRender = this.state.expandedRowRender;
  253. if (checked) {
  254. expandedRowRender = record => {
  255. return {
  256. children: <p>{record.description}</p>,
  257. fixed: 'left',
  258. };
  259. };
  260. } else {
  261. expandedRowRender = null;
  262. }
  263. this.setState({ expandedRowRender });
  264. };
  265. this.toggleShowSorter = checked => {
  266. let columns = [...this.state.columns];
  267. if (checked) {
  268. columns.forEach(column => column.dataIndex === 'age' && (column.sorter = true));
  269. } else {
  270. columns.forEach(column => (column.sorter = null));
  271. }
  272. this.setState({ columns });
  273. };
  274. this.toggleShowFilter = checked => {
  275. let columns = [...this.state.columns];
  276. if (checked) {
  277. columns.forEach(column => {
  278. if (column.dataIndex === 'name') {
  279. column.filters = [
  280. {
  281. text: '姓名中包含 1',
  282. value: '1',
  283. },
  284. {
  285. text: '姓名中包含 2',
  286. value: '2',
  287. },
  288. {
  289. text: '姓名中包含 3',
  290. value: '3',
  291. },
  292. ];
  293. column.filteredValue = [];
  294. }
  295. });
  296. } else {
  297. columns.forEach(column => {
  298. column.filters = null;
  299. column.filteredValue = null;
  300. });
  301. }
  302. this.setState({ columns });
  303. if (!checked) {
  304. this.setPage(null, null, []);
  305. }
  306. };
  307. this.onChange = (data = {}) => {
  308. console.log('Table changed: ', data);
  309. let { pagination, sorter, filters } = data;
  310. this.setPage(pagination.currentPage, sorter, filters);
  311. };
  312. this.onExpandedRowsChange = rows => {
  313. console.log('Expanded rows changed to: ', rows);
  314. const expandedRowKeys = (Array.isArray(rows) && rows.map(row => row.key)) || [];
  315. this.setState({ expandedRowKeys });
  316. };
  317. this.toggleExpandedRowKeys = checked => {
  318. let expandedRowKeys = [];
  319. if (checked) {
  320. let dataSource = [...this.state.dataSource];
  321. expandedRowKeys.push(
  322. ...dataSource.reduce((arr, data) => {
  323. if (data.key) {
  324. arr.push(data.key);
  325. }
  326. return arr;
  327. }, [])
  328. );
  329. this.toggleExpandedRowRender(true);
  330. }
  331. this.setState({ expandedRowKeys });
  332. };
  333. this.toggleBordered = checked => {
  334. let bordered = false;
  335. if (checked) {
  336. bordered = true;
  337. }
  338. this.setState({ bordered });
  339. };
  340. this.toggleResizable = checked => {
  341. let resizable = !!checked || false;
  342. this.setState({ resizable, bordered: resizable });
  343. };
  344. this.toggleHideHeader = checked => {
  345. let showHeader = true;
  346. if (checked) {
  347. showHeader = false;
  348. }
  349. this.setState({ showHeader });
  350. };
  351. this.toggleFooter = checked => {
  352. const footer = checked ? dataSource => <p style={{ margin: 0 }}>This is footer.</p> : null;
  353. this.setState({ footer });
  354. };
  355. this.toggleTitle = checked => {
  356. const title = checked ? 'This is title.' : null;
  357. this.setState({ title });
  358. };
  359. this.toggleHidePagination = checked => {
  360. let pagination = checked
  361. ? false
  362. : {
  363. currentPage: 1,
  364. pageSize: 8,
  365. total: data.length,
  366. onPageChange: page => this.setPage(page),
  367. };
  368. this.setState({ pagination });
  369. };
  370. this.toggleDataSource = checked => {
  371. if (checked) {
  372. this.setState({ dataSource: [] });
  373. } else {
  374. this.setPage();
  375. }
  376. };
  377. this.switchPagination = position => {
  378. let pagination = this.state.pagination;
  379. const defaultPagination = {
  380. currentPage: 1,
  381. pageSize: 8,
  382. total: data.length,
  383. onPageChange: page => this.setPage(page),
  384. };
  385. const positions = ['bottom', 'top', 'both'];
  386. if (position === true || position === false) {
  387. pagination = position ? { ...defaultPagination, ...pagination } : false;
  388. } else if (positions.includes(position)) {
  389. pagination = { ...defaultPagination, ...pagination, position };
  390. }
  391. this.setState({ pagination });
  392. };
  393. this.state = {
  394. loading: false,
  395. columns,
  396. scroll: {},
  397. rowSelection: null,
  398. expandedRowRender: null,
  399. expandCellFixed: false,
  400. defaultExpandedRowKeys: [],
  401. title: null,
  402. footer: null,
  403. expandedRowKeys: [],
  404. showHeader: true,
  405. resizable: false,
  406. pagination: {
  407. currentPage: 1,
  408. pageSize: 8,
  409. total: data.length,
  410. onPageChange: page => this.setPage(page),
  411. },
  412. dataSource: [],
  413. };
  414. this.TableSwitch = function TableSwitch({
  415. text,
  416. children,
  417. checked,
  418. onChange,
  419. style = { display: 'inline-flex', alignItems: 'center', margin: 5 },
  420. }) {
  421. const switchProps = { onChange };
  422. if (checked != null) {
  423. switchProps.checked = !!checked;
  424. }
  425. return (
  426. <span style={style}>
  427. <span>{text}</span>
  428. {children != null ? children : <Switch size="small" {...switchProps} />}
  429. </span>
  430. );
  431. };
  432. }
  433. componentDidMount() {
  434. this.setPage(1);
  435. }
  436. render() {
  437. let {
  438. columns,
  439. dataSource,
  440. pagination,
  441. loading,
  442. scroll,
  443. rowSelection,
  444. expandedRowRender,
  445. expandCellFixed,
  446. expandedRowKeys,
  447. bordered,
  448. resizable,
  449. title,
  450. footer,
  451. showHeader,
  452. defaultExpandedRowKeys,
  453. } = this.state;
  454. const wrapStyle = { marginBottom: 15, display: 'flex', justifyContent: 'space-around', flexWrap: 'wrap' };
  455. const TableSwitch = this.TableSwitch;
  456. return (
  457. <div>
  458. <div style={wrapStyle}>
  459. <TableSwitch text="固定表头:" checked={scroll && scroll.y} onChange={this.toggleFixHeader} />
  460. <TableSwitch text="隐藏表头:" onChange={this.toggleHideHeader} />
  461. <TableSwitch text="显示标题:" onChange={this.toggleTitle} />
  462. <TableSwitch text="显示底部:" onChange={this.toggleFooter} />
  463. <TableSwitch text="固定列:" onChange={this.toggleFixColumns} />
  464. <TableSwitch text="显示选择列:" onChange={this.toggleRowSelection} />
  465. <TableSwitch text="显示加载状态:" onChange={this.toggleLoading} checked={loading} />
  466. <TableSwitch
  467. text="无数据:"
  468. onChange={this.toggleDataSource}
  469. checked={!dataSource || !dataSource.length}
  470. />
  471. <TableSwitch text="开启排序功能:" onChange={this.toggleShowSorter} />
  472. <TableSwitch text="开启过滤功能:" onChange={this.toggleShowFilter} />
  473. <TableSwitch
  474. text="开启行展开功能:"
  475. onChange={this.toggleExpandedRowRender}
  476. checked={typeof expandedRowRender === 'function'}
  477. />
  478. <TableSwitch text="展开当前所有行:" onChange={this.toggleExpandedRowKeys} />
  479. <TableSwitch text="显示边框:" onChange={this.toggleBordered} checked={bordered} />
  480. <TableSwitch text="开启列伸缩功能:" onChange={this.toggleResizable} />
  481. <TableSwitch text="分页控件:">
  482. <ButtonGroup>
  483. <Button onClick={() => this.switchPagination('bottom')}>Bottom</Button>
  484. <Button onClick={() => this.switchPagination('top')}>Top</Button>
  485. <Button onClick={() => this.switchPagination('both')}>Both</Button>
  486. <Button onClick={() => this.switchPagination(false)}>None</Button>
  487. </ButtonGroup>
  488. </TableSwitch>
  489. </div>
  490. <Table
  491. defaultExpandedRowKeys={defaultExpandedRowKeys}
  492. onExpandedRowsChange={this.onExpandedRowsChange}
  493. title={title}
  494. footer={footer}
  495. showHeader={showHeader}
  496. bordered={bordered}
  497. onChange={this.onChange}
  498. expandCellFixed={expandCellFixed}
  499. expandedRowRender={expandedRowRender}
  500. expandedRowKeys={expandedRowKeys}
  501. rowSelection={rowSelection}
  502. scroll={scroll}
  503. columns={columns}
  504. dataSource={dataSource}
  505. pagination={pagination}
  506. loading={loading}
  507. resizable={resizable}
  508. />
  509. </div>
  510. );
  511. }
  512. }
  513. render(App);