index.tsx 8.8 KB

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