index.tsx 8.2 KB

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