Skip to content

Commit

Permalink
refactor(projects): refactor useTable
Browse files Browse the repository at this point in the history
  • Loading branch information
honghuangdc committed Mar 18, 2024
1 parent 51d7758 commit c3efa1b
Show file tree
Hide file tree
Showing 7 changed files with 639 additions and 6 deletions.
5 changes: 4 additions & 1 deletion packages/hooks/src/index.ts
Expand Up @@ -2,5 +2,8 @@ import useBoolean from './use-boolean';
import useLoading from './use-loading';
import useContext from './use-context';
import useSvgIconRender from './use-svg-icon-render';
import useTable from './use-table';

export { useBoolean, useLoading, useContext, useSvgIconRender };
export { useBoolean, useLoading, useContext, useSvgIconRender, useTable };

export * from './use-table';
163 changes: 163 additions & 0 deletions packages/hooks/src/use-table.ts
@@ -0,0 +1,163 @@
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 TableData = Record<string, unknown>;

export type TableColumn = Record<string, any>;

export type TableColumnCheck = {
key: string;
title: string;
checked: boolean;
};

export type TransformedData<T extends TableData = TableData> = {
data: T[];
pageNum: number;
pageSize: number;
total: number;
};

export type Transformer<T extends TableData = TableData, Response = object> = (
response: Response
) => TransformedData<T>;

export type TableConfig<
A extends ApiFn = ApiFn,
T extends TableData = TableData,
C extends TableColumn = TableColumn
> = {
/** 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 useTable<
A extends ApiFn = ApiFn,
T extends TableData = TableData,
C extends TableColumn = TableColumn
>(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
};
}
3 changes: 1 addition & 2 deletions src/components/advanced/table-column-setting.vue
@@ -1,13 +1,12 @@
<script setup lang="ts" generic="T extends Record<string, unknown>, K = never">
import { VueDraggable } from 'vue-draggable-plus';
import type { FilteredColumn } from '@/hooks/common/table';
import { $t } from '@/locales';
defineOptions({
name: 'TableColumnSetting'
});
const columns = defineModel<FilteredColumn[]>('columns', {
const columns = defineModel<NaiveUI.TableColumnCheck[]>('columns', {
required: true
});
</script>
Expand Down
4 changes: 1 addition & 3 deletions src/components/advanced/table-header-operation.vue
@@ -1,6 +1,4 @@
<script setup lang="ts">
import type { FilteredColumn } from '@/hooks/common/table';
defineOptions({
name: 'TableHeaderOperation'
});
Expand All @@ -21,7 +19,7 @@ interface Emits {
const emit = defineEmits<Emits>();
const columns = defineModel<FilteredColumn[]>('columns', {
const columns = defineModel<NaiveUI.TableColumnCheck[]>('columns', {
default: () => []
});
Expand Down

0 comments on commit c3efa1b

Please sign in to comment.