-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(hooks): @sa/hooks: add useTable
- Loading branch information
1 parent
466bc7a
commit 348b4f6
Showing
2 changed files
with
155 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
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> = T | Promise<T>; | ||
|
||
export type ApiFn = (args: any) => Promise<unknown>; | ||
|
||
export type TableColumnCheck = { | ||
key: string; | ||
title: string; | ||
checked: boolean; | ||
}; | ||
|
||
export type TableDataWithIndex<T> = T & { index: number }; | ||
|
||
export type TransformedData<T> = { | ||
data: TableDataWithIndex<T>[]; | ||
pageNum: number; | ||
pageSize: number; | ||
total: number; | ||
}; | ||
|
||
export type Transformer<T, Response> = (response: Response) => TransformedData<T>; | ||
|
||
export type TableConfig<A extends ApiFn, T, C> = { | ||
/** api function to get table data */ | ||
apiFn: A; | ||
/** api params */ | ||
apiParams?: Parameters<A>[0]; | ||
/** transform api response to table data */ | ||
transformer: Transformer<T, Awaited<ReturnType<A>>>; | ||
/** 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<T>) => MaybePromise<void>; | ||
/** | ||
* whether to get data immediately | ||
* | ||
* @default true | ||
*/ | ||
immediate?: boolean; | ||
}; | ||
|
||
export default function useHookTable<A extends ApiFn, T, C>(config: TableConfig<A, T, C>) { | ||
const { loading, startLoading, endLoading } = useLoading(); | ||
const { bool: empty, setBool: setEmpty } = useBoolean(); | ||
|
||
const { apiFn, apiParams, transformer, immediate = true, getColumnChecks, getColumns } = config; | ||
|
||
const searchParams: NonNullable<Parameters<A>[0]> = reactive({ ...apiParams }); | ||
|
||
const allColumns = ref(config.columns()) as Ref<C[]>; | ||
|
||
const data: Ref<T[]> = ref([]); | ||
|
||
const columnChecks: Ref<TableColumnCheck[]> = 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<ReturnType<A>>); | ||
|
||
data.value = transformed.data; | ||
|
||
setEmpty(transformed.data.length === 0); | ||
|
||
await config.onFetched?.(transformed); | ||
|
||
endLoading(); | ||
} | ||
|
||
function formatSearchParams(params: Record<string, unknown>) { | ||
const formattedParams: Record<string, unknown> = {}; | ||
|
||
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<Parameters<A>[0]>) { | ||
Object.assign(searchParams, params); | ||
} | ||
|
||
/** reset search params */ | ||
function resetSearchParams() { | ||
Object.assign(searchParams, apiParams); | ||
} | ||
|
||
if (immediate) { | ||
getData(); | ||
} | ||
|
||
return { | ||
loading, | ||
empty, | ||
data, | ||
columns, | ||
columnChecks, | ||
reloadColumns, | ||
getData, | ||
searchParams, | ||
updateSearchParams, | ||
resetSearchParams | ||
}; | ||
} |