index.tsx 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. import type { ProTableProps } from '@jetlinks/pro-table';
  2. import ProTable from '@jetlinks/pro-table';
  3. import type { ParamsType } from '@ant-design/pro-provider';
  4. import React, { useCallback, useEffect, useState } from 'react';
  5. import { isFunction } from 'lodash';
  6. import { Empty, Pagination, Space } from 'antd';
  7. import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons';
  8. import classNames from 'classnames';
  9. import LoadingComponent from '@ant-design/pro-layout/es/PageLoading';
  10. import './index.less';
  11. import { getDomFullHeight } from '@/utils/util';
  12. enum ModelEnum {
  13. TABLE = 'TABLE',
  14. CARD = 'CARD',
  15. }
  16. const Default_Size = 6;
  17. type ModelType = keyof typeof ModelEnum;
  18. interface ProTableCardProps<T> {
  19. cardRender?: (data: T) => JSX.Element | React.ReactNode;
  20. gridColumn?: number;
  21. }
  22. const ProTableCard = <
  23. T extends Record<string, any>,
  24. U extends ParamsType = ParamsType,
  25. ValueType = 'text',
  26. >(
  27. props: ProTableCardProps<T> & ProTableProps<T, U, ValueType>,
  28. ) => {
  29. const { cardRender, toolBarRender, request, ...extraProps } = props;
  30. const [model, setModel] = useState<ModelType>(ModelEnum.CARD);
  31. const [total, setTotal] = useState<number | undefined>(0);
  32. const [current, setCurrent] = useState(1); // 当前页
  33. const [pageIndex, setPageIndex] = useState(0);
  34. const [pageSize, setPageSize] = useState(Default_Size * 2); // 每页条数
  35. const [column, setColumn] = useState(props.gridColumn || 4);
  36. const [loading, setLoading] = useState(false);
  37. const [dataLength, setDataLength] = useState<number>(0);
  38. const [minHeight, setMinHeight] = useState(100);
  39. /**
  40. * 处理 Card
  41. * @param dataSource
  42. */
  43. const handleCard = useCallback(
  44. (dataSource: readonly T[] | undefined, rowSelection?: any): JSX.Element => {
  45. setDataLength(dataSource ? dataSource.length : 0);
  46. const Item = (dom: React.ReactNode) => {
  47. if (!rowSelection || (rowSelection && !rowSelection.selectedRowKeys)) {
  48. return dom;
  49. }
  50. const { selectedRowKeys, onChange } = rowSelection;
  51. // @ts-ignore
  52. const id = dom.props.id;
  53. // @ts-ignore
  54. return React.cloneElement(dom, {
  55. // @ts-ignore
  56. className: classNames(dom.props.className, {
  57. 'item-active': selectedRowKeys && selectedRowKeys.includes(id),
  58. }),
  59. key: id,
  60. onClick: (e) => {
  61. e.stopPropagation();
  62. if (onChange) {
  63. const isSelect = selectedRowKeys.includes(id);
  64. if (isSelect) {
  65. const nowRowKeys = selectedRowKeys.filter((key: string) => key !== id);
  66. onChange(
  67. nowRowKeys,
  68. dataSource!.filter((item) => nowRowKeys.includes(item.id)),
  69. );
  70. } else {
  71. const nowRowKeys = [...selectedRowKeys, id];
  72. onChange(
  73. nowRowKeys,
  74. dataSource!.filter((item) => nowRowKeys.includes(item.id)),
  75. );
  76. }
  77. }
  78. },
  79. });
  80. };
  81. return (
  82. <>
  83. {dataSource && dataSource.length ? (
  84. <div
  85. className={'pro-table-card-items'}
  86. style={{ gridTemplateColumns: `repeat(${column}, 1fr)` }}
  87. >
  88. {dataSource.map((item) =>
  89. cardRender && isFunction(cardRender) ? Item(cardRender(item)) : null,
  90. )}
  91. </div>
  92. ) : (
  93. <div
  94. style={{
  95. display: 'flex',
  96. justifyContent: 'center',
  97. alignItems: 'center',
  98. minHeight: minHeight - 150,
  99. }}
  100. >
  101. <Empty />
  102. </div>
  103. )}
  104. </>
  105. );
  106. },
  107. [minHeight],
  108. );
  109. const windowChange = () => {
  110. if (window.innerWidth <= 1440) {
  111. setColumn(props.gridColumn && props.gridColumn < 2 ? props.gridColumn : 2);
  112. } else if (window.innerWidth > 1440 && window.innerWidth <= 1600) {
  113. setColumn(props.gridColumn && props.gridColumn < 3 ? props.gridColumn : 3);
  114. } else if (window.innerWidth > 1600) {
  115. setColumn(props.gridColumn && props.gridColumn < 4 ? props.gridColumn : 4);
  116. }
  117. };
  118. useEffect(() => {
  119. window.addEventListener('resize', windowChange);
  120. windowChange();
  121. return () => {
  122. window.removeEventListener('resize', windowChange);
  123. };
  124. }, []);
  125. const pageSizeOptions = [Default_Size * 2, Default_Size * 4, Default_Size * 8, Default_Size * 16];
  126. useEffect(() => {
  127. setCurrent(1);
  128. setPageIndex(0);
  129. }, [props.params]);
  130. return (
  131. <div
  132. className={'pro-table-card'}
  133. style={{ minHeight: minHeight }}
  134. ref={(ref) => {
  135. if (ref) {
  136. setMinHeight(getDomFullHeight('pro-table-card'));
  137. }
  138. }}
  139. >
  140. <ProTable<T, U, ValueType>
  141. {...extraProps}
  142. params={
  143. {
  144. ...props.params,
  145. current: current,
  146. pageIndex: pageIndex,
  147. pageSize,
  148. } as any
  149. }
  150. className={'pro-table-card-body'}
  151. options={model === ModelEnum.CARD ? false : props.options}
  152. request={async (param, sort, filter) => {
  153. if (request) {
  154. const resp = await request(param, sort, filter);
  155. setTotal(resp.result ? resp.result.total : 0);
  156. return {
  157. code: resp.message,
  158. result: {
  159. data: resp.result ? resp.result.data : [],
  160. pageIndex: resp.result ? resp.result.pageIndex : 0,
  161. pageSize: resp.result ? resp.result.pageSize : 0,
  162. total: resp.result ? resp.result.total : 0,
  163. },
  164. status: resp.status,
  165. };
  166. }
  167. return {};
  168. }}
  169. onLoadingChange={(l) => {
  170. setLoading(!!l);
  171. }}
  172. pagination={{
  173. onChange: (page, size) => {
  174. setCurrent(page);
  175. setPageIndex(page - 1);
  176. setPageSize(size);
  177. },
  178. pageSize: pageSize,
  179. current: current,
  180. pageSizeOptions: pageSizeOptions,
  181. }}
  182. toolBarRender={(action, row) => {
  183. const oldBar = toolBarRender ? toolBarRender(action, row) : [];
  184. return [
  185. ...oldBar,
  186. <Space
  187. align="center"
  188. key={ModelEnum.TABLE}
  189. size={12}
  190. className={classNames(`pro-table-card-setting-item`, {
  191. active: model === ModelEnum.TABLE,
  192. })}
  193. onClick={() => {
  194. setModel(ModelEnum.TABLE);
  195. }}
  196. >
  197. <BarsOutlined />
  198. </Space>,
  199. <Space
  200. align="center"
  201. size={12}
  202. key={ModelEnum.CARD}
  203. className={classNames(`pro-table-card-setting-item`, {
  204. active: model === ModelEnum.CARD,
  205. })}
  206. onClick={() => {
  207. setModel(ModelEnum.CARD);
  208. }}
  209. >
  210. <AppstoreOutlined />
  211. </Space>,
  212. ];
  213. }}
  214. tableViewRender={
  215. model === ModelEnum.CARD
  216. ? (tableProps) => {
  217. return handleCard(tableProps.dataSource, extraProps?.rowSelection);
  218. }
  219. : undefined
  220. }
  221. />
  222. {model === ModelEnum.CARD && (
  223. <>
  224. <div className={classNames('mask-loading', { show: loading })}>
  225. <LoadingComponent />
  226. </div>
  227. {!!dataLength && (
  228. <Pagination
  229. showSizeChanger
  230. size="small"
  231. className={'pro-table-card-pagination'}
  232. total={total}
  233. current={current}
  234. onChange={(page, size) => {
  235. setCurrent(page);
  236. setPageIndex(page - 1);
  237. setPageSize(size);
  238. }}
  239. pageSizeOptions={pageSizeOptions}
  240. pageSize={pageSize}
  241. showTotal={(num) => {
  242. const minSize = pageIndex * pageSize + 1;
  243. const MaxSize = (pageIndex + 1) * pageSize;
  244. return `第 ${minSize} - ${MaxSize > num ? num : MaxSize} 条/总共 ${num} 条`;
  245. }}
  246. />
  247. )}
  248. </>
  249. )}
  250. </div>
  251. );
  252. };
  253. export default ProTableCard;