import { useBoolean, useHookTable } from '@sa/hooks'; import type { TablePaginationConfig, TableProps } from 'antd'; import { Form } from 'antd'; import { parseQuery } from '@/features/router/query'; import { getIsMobile } from '@/layouts/appStore'; type TableData = AntDesign.TableData; type GetTableData = AntDesign.GetTableData; type TableColumn = AntDesign.TableColumn; type Config = AntDesign.AntDesignTableConfig & { isChangeURL?: boolean; }; export function useTable( config: Config, paginationConfig?: Omit ) { const isMobile = useAppSelector(getIsMobile); const { apiFn, apiParams, immediate, isChangeURL = true, rowKey = 'id' } = config; const [form] = Form.useForm['apiParams']>(); const { search } = useLocation(); const query = parseQuery(search) as unknown as Parameters[0]; const { columnChecks, columns, data, empty, loading, pageNum, pageSize, resetSearchParams, searchParams, setColumnChecks, total, updateSearchParams } = useHookTable, TableColumn>>>({ apiFn, apiParams: { ...apiParams, ...query }, columns: config.columns, getColumnChecks: cols => { const checks: AntDesign.TableColumnCheck[] = []; cols.forEach(column => { if (column.key) { checks.push({ checked: true, key: column.key as string, title: column.title as string }); } }); return checks; }, getColumns: (cols, checks) => { const columnMap = new Map>>>(); cols.forEach(column => { if (column.key) { columnMap.set(column.key as string, column); } }); const filteredColumns = checks.filter(item => item.checked).map(check => columnMap.get(check.key)); return filteredColumns as TableColumn>>[]; }, immediate, isChangeURL, transformer: res => { const { current = 1, records = [], size = 10, total: totalNum = 0 } = res.data || {}; const recordsWithIndex = records.map((item, index) => { return { ...item, index: (current - 1) * size + index + 1 }; }); return { data: recordsWithIndex, pageNum: current, pageSize: size, total: totalNum }; } }); // this is for mobile, if the system does not support mobile, you can use `pagination` directly const pagination: TablePaginationConfig = { current: pageNum, onChange: async (current: number, size: number) => { updateSearchParams({ current, size }); }, pageSize, pageSizeOptions: ['10', '15', '20', '25', '30'], showSizeChanger: true, simple: isMobile, total, ...paginationConfig }; function reset() { form.setFieldsValue(apiParams as NonNullable[0]>); resetSearchParams(); } async function run(isResetCurrent: boolean = true) { const res = await form.validateFields(); if (res) { if (isResetCurrent) { const { current = 1, ...rest } = res; updateSearchParams({ current, ...rest }); } else { updateSearchParams(res); } } } return { columnChecks, data, empty, run, searchParams, searchProps: { form, reset, search: run, searchParams: searchParams as NonNullable[0]> }, setColumnChecks, tableProps: { columns, dataSource: data, loading, pagination, rowKey } }; } export function useTableOperate( data: T[], getData: (isResetCurrent?: boolean) => Promise, executeResActions: (res: T, operateType: AntDesign.TableOperateType) => void ) { const { bool: drawerVisible, setFalse: closeDrawer, setTrue: openDrawer } = useBoolean(); const { t } = useTranslation(); const [operateType, setOperateType] = useState('add'); const [form] = Form.useForm(); function handleAdd() { setOperateType('add'); openDrawer(); } /** the editing row data */ const [editingData, setEditingData] = useState(); function handleEdit(idOrData: T['id'] | T) { if (typeof idOrData === 'object') { form.setFieldsValue(idOrData); setEditingData(idOrData); } else { const findItem = data.find(item => item.id === idOrData); if (findItem) { form.setFieldsValue(findItem); setEditingData(findItem); } } setOperateType('edit'); openDrawer(); } /** the checked row keys of table */ const [checkedRowKeys, setCheckedRowKeys] = useState([]); function onSelectChange(keys: React.Key[]) { setCheckedRowKeys(keys); } const rowSelection: TableProps['rowSelection'] = { columnWidth: 48, fixed: true, onChange: onSelectChange, selectedRowKeys: checkedRowKeys, type: 'checkbox' }; function onClose() { closeDrawer(); form.resetFields(); } /** the hook after the batch delete operation is completed */ async function onBatchDeleted() { window.$message?.success(t('common.deleteSuccess')); setCheckedRowKeys([]); await getData(false); } /** the hook after the delete operation is completed */ async function onDeleted() { window.$message?.success(t('common.deleteSuccess')); await getData(false); } async function handleSubmit() { const res = await form.validateFields(); // request await executeResActions(res, operateType); window.$message?.success(t('common.updateSuccess')); onClose(); getData(); } return { checkedRowKeys, closeDrawer, drawerVisible, editingData, generalPopupOperation: { form, handleSubmit, onClose, open: drawerVisible, operateType }, handleAdd, handleEdit, onBatchDeleted, onDeleted, onSelectChange, openDrawer, operateType, rowSelection }; } export function useTableScroll(scrollX: number = 702) { const tableWrapperRef = useRef(null); const size = useSize(tableWrapperRef); function getTableScrollY() { const height = size?.height; if (!height) return undefined; return height - 160; } const scrollConfig = { x: scrollX, y: getTableScrollY() }; return { scrollConfig, tableWrapperRef }; }