import { computed, reactive, ref } from 'vue'; import type { Ref } from 'vue'; import useBoolean from './use-boolean'; import useLoading from './use-loading'; export type MaybePromise = T | Promise; export type ApiFn = (args: any) => Promise; export type TableColumnCheck = { key: string; title: string; checked: boolean; }; export type TableDataWithIndex = T & { index: number }; export type TransformedData = { data: TableDataWithIndex[]; pageNum: number; pageSize: number; total: number; }; export type Transformer = (response: Response) => TransformedData; export type TableConfig = { /** api function to get table data */ apiFn: A; /** api params */ apiParams?: Parameters[0]; /** search params */ searchParams?: Parameters[0]; /** transform api response to table data */ transformer: Transformer>>; /** columns factory */ columns: () => C[]; /** * get column checks * * @param columns */ getColumnChecks: (columns: C[]) => TableColumnCheck[]; /** * get columns * * @param columns */ getColumns: (columns: C[], checks: TableColumnCheck[]) => C[]; /** * callback when response fetched * * @param transformed transformed data */ onFetched?: (transformed: TransformedData) => MaybePromise; /** * whether to get data immediately * * @default true */ immediate?: boolean; }; export default function useHookTable(config: TableConfig) { const { loading, startLoading, endLoading } = useLoading(); const { bool: empty, setBool: setEmpty } = useBoolean(); const { apiFn, apiParams, transformer, immediate = true, getColumnChecks, getColumns } = config; const searchParams: NonNullable[0]> = reactive({ ...apiParams }); const allColumns = ref(config.columns()) as Ref; const data: Ref = ref([]); const columnChecks: Ref = ref(getColumnChecks(config.columns())); const columns = computed(() => getColumns(allColumns.value, columnChecks.value)); function reloadColumns() { allColumns.value = config.columns(); const checkMap = new Map(columnChecks.value.map(col => [col.key, col.checked])); const defaultChecks = getColumnChecks(allColumns.value); columnChecks.value = defaultChecks.map(col => ({ ...col, checked: checkMap.get(col.key) ?? col.checked })); } async function getData() { startLoading(); const formattedParams = formatSearchParams(searchParams); const response = await apiFn(formattedParams); const transformed = transformer(response as Awaited>); data.value = transformed.data; setEmpty(transformed.data.length === 0); await config.onFetched?.(transformed); endLoading(); } function formatSearchParams(params: Record) { const formattedParams: Record = {}; Object.entries(params).forEach(([key, value]) => { if (value !== null && value !== undefined) { formattedParams[key] = value; } }); return formattedParams; } /** * update search params * * @param params */ function updateSearchParams(params: Partial[0]>) { Object.assign(searchParams, params); } /** reset search params */ function resetSearchParams() { Object.assign(searchParams, apiParams); } if (immediate) { if (config.searchParams) { updateSearchParams(config.searchParams); } getData(); } return { loading, empty, data, columns, columnChecks, reloadColumns, getData, searchParams, updateSearchParams, resetSearchParams }; }