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
};
}