From 3fd15e56498ddc29dcd16eb2039d6b0138a6dde9 Mon Sep 17 00:00:00 2001 From: Soybean Date: Thu, 21 Mar 2024 19:38:29 +0800 Subject: [PATCH] refactor(projects): finish refactor useTable and apply --- packages/hooks/src/index.ts | 4 +- packages/hooks/src/use-table.ts | 10 +- src/hooks/common/naive-table.ts | 226 ------------- src/hooks/common/table.ts | 320 +++++++++--------- src/service/api/system-manage.ts | 16 +- src/typings/api.d.ts | 3 + src/typings/naive-ui.d.ts | 10 +- src/views/manage/menu/index.vue | 80 ++--- .../menu/modules/menu-operate-drawer.vue | 12 +- src/views/manage/role/index.vue | 105 ++---- .../role/modules/role-operate-drawer.vue | 12 +- src/views/manage/user/index.vue | 107 ++---- src/views/manage/user/index2.vue | 190 ----------- .../user/modules/user-operate-drawer.vue | 12 +- 14 files changed, 264 insertions(+), 843 deletions(-) delete mode 100644 src/hooks/common/naive-table.ts delete mode 100644 src/views/manage/user/index2.vue diff --git a/packages/hooks/src/index.ts b/packages/hooks/src/index.ts index e58ddd5b..2ea9a522 100644 --- a/packages/hooks/src/index.ts +++ b/packages/hooks/src/index.ts @@ -2,8 +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'; +import useHookTable from './use-table'; -export { useBoolean, useLoading, useContext, useSvgIconRender, useTable }; +export { useBoolean, useLoading, useContext, useSvgIconRender, useHookTable }; export * from './use-table'; diff --git a/packages/hooks/src/use-table.ts b/packages/hooks/src/use-table.ts index 9507a4c2..6c2ae07c 100644 --- a/packages/hooks/src/use-table.ts +++ b/packages/hooks/src/use-table.ts @@ -7,18 +7,16 @@ export type MaybePromise = T | Promise; export type ApiFn = (args: any) => Promise; -export type TableData = Record; - -export type TableColumn = Record; - export type TableColumnCheck = { key: string; title: string; checked: boolean; }; +export type TableDataWithIndex = T & { index: number }; + export type TransformedData = { - data: T[]; + data: TableDataWithIndex[]; pageNum: number; pageSize: number; total: number; @@ -61,7 +59,7 @@ export type TableConfig = { immediate?: boolean; }; -export default function useTable(config: TableConfig) { +export default function useHookTable(config: TableConfig) { const { loading, startLoading, endLoading } = useLoading(); const { bool: empty, setBool: setEmpty } = useBoolean(); diff --git a/src/hooks/common/naive-table.ts b/src/hooks/common/naive-table.ts deleted file mode 100644 index 37c30730..00000000 --- a/src/hooks/common/naive-table.ts +++ /dev/null @@ -1,226 +0,0 @@ -import { computed, effectScope, onScopeDispose, reactive, ref, watch } from 'vue'; -import type { Ref } from 'vue'; -import type { PaginationProps } from 'naive-ui'; -import { useBoolean, useTable } from '@sa/hooks'; -import { useAppStore } from '@/store/modules/app'; -import { $t } from '@/locales'; - -type TableData = NaiveUI.TableData; -type GetTableData = NaiveUI.GetTableData; -type TableColumn = NaiveUI.TableColumn; - -export function useNaiveTable(config: NaiveUI.NaiveTableConfig) { - const scope = effectScope(); - const appStore = useAppStore(); - - const { apiFn, apiParams, immediate } = config; - - const SELECTION_KEY = '__selection__'; - - const { - loading, - empty, - data, - columns, - columnChecks, - reloadColumns, - getData, - searchParams, - updateSearchParams, - resetSearchParams - } = useTable, TableColumn>>({ - apiFn, - apiParams, - columns: config.columns, - transformer: res => { - const { records = [], current = 1, size = 10, total = 0 } = res.data || {}; - - return { - data: records, - pageNum: current, - pageSize: size, - total - }; - }, - getColumnChecks: cols => { - const checks: NaiveUI.TableColumnCheck[] = []; - - cols.forEach(column => { - if (isTableColumnHasKey(column)) { - checks.push({ - key: column.key as string, - title: column.title as string, - checked: true - }); - } else if (column.type === 'selection') { - checks.push({ - key: SELECTION_KEY, - title: $t('common.check'), - checked: true - }); - } - }); - - return checks; - }, - getColumns: (cols, checks) => { - const columnMap = new Map>>(); - - cols.forEach(column => { - if (isTableColumnHasKey(column)) { - columnMap.set(column.key as string, column); - } else if (column.type === 'selection') { - columnMap.set(SELECTION_KEY, column); - } - }); - - const filteredColumns = checks - .filter(item => item.checked) - .map(check => columnMap.get(check.key) as TableColumn>); - - return filteredColumns; - }, - onFetched: async transformed => { - const { pageNum, pageSize, total } = transformed; - - updatePagination({ - page: pageNum, - pageSize, - itemCount: total - }); - }, - immediate - }); - - const pagination: PaginationProps = reactive({ - page: 1, - pageSize: 10, - showSizePicker: true, - pageSizes: [10, 15, 20, 25, 30], - onUpdatePage: async (page: number) => { - pagination.page = page; - - updateSearchParams({ - current: page, - size: pagination.pageSize! - }); - - getData(); - }, - onUpdatePageSize: async (pageSize: number) => { - pagination.pageSize = pageSize; - pagination.page = 1; - - updateSearchParams({ - current: pagination.page, - size: pageSize - }); - - getData(); - } - }); - - // this is for mobile, if the system does not support mobile, you can use `pagination` directly - const mobilePagination = computed(() => { - const p: PaginationProps = { - ...pagination, - pageSlot: appStore.isMobile ? 3 : 9 - }; - - return p; - }); - - function updatePagination(update: Partial) { - Object.assign(pagination, update); - } - - scope.run(() => { - watch( - () => appStore.locale, - () => { - reloadColumns(); - } - ); - }); - - onScopeDispose(() => { - scope.stop(); - }); - - return { - loading, - empty, - data, - columns, - columnChecks, - reloadColumns, - pagination, - mobilePagination, - updatePagination, - getData, - searchParams, - updateSearchParams, - resetSearchParams - }; -} - -export function useNaiveTableOperate(data: Ref, getData: () => Promise) { - const { bool: drawerVisible, setTrue: openDrawer, setFalse: closeDrawer } = useBoolean(); - - const operateType = ref('add'); - - function handleAdd() { - operateType.value = 'add'; - openDrawer(); - } - - /** the editing row data */ - const editingData: Ref = ref(null); - - function handleEdit(id: number) { - operateType.value = 'edit'; - editingData.value = data.value.find(item => item.id === id) || null; - - openDrawer(); - } - - const checkedRowKeys = ref([]); - - /** the hook after the batch delete operation is completed */ - async function onBatchDeleted() { - window.$message?.success($t('common.deleteSuccess')); - - checkedRowKeys.value = []; - - await getData(); - } - - /** the hook after the delete operation is completed */ - async function onDeleted() { - window.$message?.success($t('common.deleteSuccess')); - - await getData(); - } - - return { - drawerVisible, - operateType, - handleAdd, - editingData, - handleEdit, - checkedRowKeys, - onBatchDeleted, - onDeleted, - closeDrawer - }; -} - -export function getNaiveTableIndex(pagination: PaginationProps, index: number) { - const { page = 1, pageSize = 10 } = pagination; - - return (page - 1) * pageSize + index + 1; -} - -function isTableColumnHasKey(column: TableColumn): column is NaiveUI.TableColumnWithKey { - return Boolean((column as NaiveUI.TableColumnWithKey).key); -} diff --git a/src/hooks/common/table.ts b/src/hooks/common/table.ts index de9e1738..fe4ac229 100644 --- a/src/hooks/common/table.ts +++ b/src/hooks/common/table.ts @@ -1,153 +1,146 @@ import { computed, effectScope, onScopeDispose, reactive, ref, watch } from 'vue'; import type { Ref } from 'vue'; -import type { DataTableBaseColumn, DataTableExpandColumn, DataTableSelectionColumn, PaginationProps } from 'naive-ui'; -import type { TableColumnGroup } from 'naive-ui/es/data-table/src/interface'; -import { useBoolean, useLoading } from '@sa/hooks'; +import type { PaginationProps } from 'naive-ui'; +import { useBoolean, useHookTable } from '@sa/hooks'; import { useAppStore } from '@/store/modules/app'; import { $t } from '@/locales'; -type BaseData = Record; +type TableData = NaiveUI.TableData; +type GetTableData = NaiveUI.GetTableData; +type TableColumn = NaiveUI.TableColumn; -type ApiFn = (args: any) => Promise; - -export type TableColumn = - | (Omit, 'key'> & { key: keyof T | CustomColumnKey }) - | (Omit, 'key'> & { key: keyof T | CustomColumnKey }) - | DataTableSelectionColumn - | DataTableExpandColumn; - -export type TransformedData = { - data: TableData[]; - pageNum: number; - pageSize: number; - total: number; -}; - -/** transform api response to table data */ -type Transformer> = ( - response: Response -) => TransformedData; - -/** table config */ -export type TableConfig = { - /** api function to get table data */ - apiFn: Fn; - /** api params */ - apiParams?: Parameters[0]; - /** transform api response to table data */ - transformer: Transformer>>; - /** pagination */ - pagination?: PaginationProps; - /** - * callback when pagination changed - * - * @param pagination - */ - onPaginationChanged?: (pagination: PaginationProps) => void | Promise; - /** - * whether to get data immediately - * - * @default true - */ - immediate?: boolean; - /** columns factory */ - columns: () => TableColumn[]; -}; - -/** filter columns */ -export type FilteredColumn = { - key: string; - title: string; - checked: boolean; -}; - -export function useTable( - config: TableConfig -) { +export function useTable(config: NaiveUI.NaiveTableConfig) { const scope = effectScope(); const appStore = useAppStore(); - const { loading, startLoading, endLoading } = useLoading(); - const { bool: empty, setBool: setEmpty } = useBoolean(); + const { apiFn, apiParams, immediate } = config; - const { apiFn, apiParams, transformer, onPaginationChanged, immediate = true } = config; + const SELECTION_KEY = '__selection__'; - const searchParams: NonNullable[0]> = reactive({ ...apiParams }); + const { + loading, + empty, + data, + columns, + columnChecks, + reloadColumns, + getData, + searchParams, + updateSearchParams, + resetSearchParams + } = useHookTable, TableColumn>>>({ + apiFn, + apiParams, + columns: config.columns, + transformer: res => { + const { records = [], current = 1, size = 10, total = 0 } = res.data || {}; - const { columns, filteredColumns, reloadColumns } = useTableColumn(config.columns); + const recordsWithIndex = records.map((item, index) => { + return { + ...item, + index: (current - 1) * size + index + 1 + }; + }); - const data: Ref = ref([]); + return { + data: recordsWithIndex, + pageNum: current, + pageSize: size, + total + }; + }, + getColumnChecks: cols => { + const checks: NaiveUI.TableColumnCheck[] = []; - const pagination = reactive({ + cols.forEach(column => { + if (isTableColumnHasKey(column)) { + checks.push({ + key: column.key as string, + title: column.title as string, + checked: true + }); + } else if (column.type === 'selection') { + checks.push({ + key: SELECTION_KEY, + title: $t('common.check'), + checked: true + }); + } + }); + + return checks; + }, + getColumns: (cols, checks) => { + const columnMap = new Map>>(); + + cols.forEach(column => { + if (isTableColumnHasKey(column)) { + columnMap.set(column.key as string, column); + } else if (column.type === 'selection') { + columnMap.set(SELECTION_KEY, column); + } + }); + + const filteredColumns = checks + .filter(item => item.checked) + .map(check => columnMap.get(check.key) as TableColumn>); + + return filteredColumns; + }, + onFetched: async transformed => { + const { pageNum, pageSize, total } = transformed; + + updatePagination({ + page: pageNum, + pageSize, + itemCount: total + }); + }, + immediate + }); + + const pagination: PaginationProps = reactive({ page: 1, pageSize: 10, showSizePicker: true, pageSizes: [10, 15, 20, 25, 30], - // Fix Naive Pagination's outdated API onUpdatePage: async (page: number) => { pagination.page = page; - await onPaginationChanged?.(pagination); + updateSearchParams({ + current: page, + size: pagination.pageSize! + }); + + getData(); }, onUpdatePageSize: async (pageSize: number) => { pagination.pageSize = pageSize; pagination.page = 1; - await onPaginationChanged?.(pagination); - }, - ...config.pagination - }) as PaginationProps; + updateSearchParams({ + current: pagination.page, + size: pageSize + }); + + getData(); + } + }); + + // this is for mobile, if the system does not support mobile, you can use `pagination` directly + const mobilePagination = computed(() => { + const p: PaginationProps = { + ...pagination, + pageSlot: appStore.isMobile ? 3 : 9 + }; + + return p; + }); function updatePagination(update: Partial) { Object.assign(pagination, update); } - async function getData() { - startLoading(); - - const formattedParams = formatSearchParams(searchParams); - - const response = await apiFn(formattedParams); - - const { data: tableData, pageNum, pageSize, total } = transformer(response as Awaited>); - - data.value = tableData; - - setEmpty(tableData.length === 0); - updatePagination({ page: pageNum, pageSize, itemCount: total }); - 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) { - getData(); - } - scope.run(() => { watch( () => appStore.locale, @@ -166,9 +159,10 @@ export function useTable( - factory: () => TableColumn[] -) { - const SELECTION_KEY = '__selection__'; +export function useTableOperate(data: Ref, getData: () => Promise) { + const { bool: drawerVisible, setTrue: openDrawer, setFalse: closeDrawer } = useBoolean(); - const allColumns = ref(factory()) as Ref[]>; + const operateType = ref('add'); - const filteredColumns: Ref = ref(getFilteredColumns(factory())); - - const columns = computed(() => getColumns()); - - function reloadColumns() { - allColumns.value = factory(); - filteredColumns.value = getFilteredColumns(factory()); + function handleAdd() { + operateType.value = 'add'; + openDrawer(); } - function getFilteredColumns(aColumns: TableColumn[]) { - const cols: FilteredColumn[] = []; + /** the editing row data */ + const editingData: Ref = ref(null); - aColumns.forEach(column => { - if (column.type === undefined) { - cols.push({ - key: column.key as string, - title: column.title as string, - checked: true - }); - } + function handleEdit(id: T['id']) { + operateType.value = 'edit'; + editingData.value = data.value.find(item => item.id === id) || null; - if (column.type === 'selection') { - cols.push({ - key: SELECTION_KEY, - title: $t('common.check'), - checked: true - }); - } - }); - - return cols; + openDrawer(); } - function getColumns() { - const cols = filteredColumns.value - .filter(column => column.checked) - .map(column => { - if (column.key === SELECTION_KEY) { - return allColumns.value.find(col => col.type === 'selection'); - } - return allColumns.value.find(col => (col as DataTableBaseColumn).key === column.key); - }); + /** the checked row keys of table */ + const checkedRowKeys = ref([]); - return cols as TableColumn[]; + /** the hook after the batch delete operation is completed */ + async function onBatchDeleted() { + window.$message?.success($t('common.deleteSuccess')); + + checkedRowKeys.value = []; + + await getData(); + } + + /** the hook after the delete operation is completed */ + async function onDeleted() { + window.$message?.success($t('common.deleteSuccess')); + + await getData(); } return { - columns, - reloadColumns, - filteredColumns + drawerVisible, + operateType, + handleAdd, + editingData, + handleEdit, + checkedRowKeys, + onBatchDeleted, + onDeleted, + closeDrawer }; } + +function isTableColumnHasKey(column: TableColumn): column is NaiveUI.TableColumnWithKey { + return Boolean((column as NaiveUI.TableColumnWithKey).key); +} diff --git a/src/service/api/system-manage.ts b/src/service/api/system-manage.ts index 8a75bf39..11891941 100644 --- a/src/service/api/system-manage.ts +++ b/src/service/api/system-manage.ts @@ -30,10 +30,22 @@ export function fetchGetUserList(params?: Api.SystemManage.UserSearchParams) { }); } -/** get menu list */ -export function fetchGetMenuList() { +/** + * get menu list + * + * @deprecated this will removed in next version 1.1.0 + */ +export function fetchGetMenuListV1() { return request({ url: '/systemManage/getMenuList', method: 'get' }); } + +/** get menu list */ +export function fetchGetMenuList() { + return request({ + url: '/systemManage/getMenuList/v2', + method: 'get' + }); +} diff --git a/src/typings/api.d.ts b/src/typings/api.d.ts index 9293e732..9dc82cd6 100644 --- a/src/typings/api.d.ts +++ b/src/typings/api.d.ts @@ -219,5 +219,8 @@ declare namespace Api { /** children menu */ children?: Menu[]; }>; + + /** menu list */ + type MenuList = Common.PaginatingQueryRecord; } } diff --git a/src/typings/naive-ui.d.ts b/src/typings/naive-ui.d.ts index 32775180..44b77a35 100644 --- a/src/typings/naive-ui.d.ts +++ b/src/typings/naive-ui.d.ts @@ -8,9 +8,15 @@ declare namespace NaiveUI { type TableColumnGroup = import('naive-ui/es/data-table/src/interface').TableColumnGroup; type PaginationProps = import('naive-ui').PaginationProps; type TableColumnCheck = import('@sa/hooks').TableColumnCheck; + type TableDataWithIndex = import('@sa/hooks').TableDataWithIndex; type FlatResponseData = import('@sa/axios').FlatResponseData; - type CustomColumnKey = 'index' | 'operate'; + /** + * the custom column key + * + * if you want to add a custom column, you should add a key to this type + */ + type CustomColumnKey = 'operate'; type SetTableColumnKey = Omit & { key: keyof T | CustomColumnKey }; @@ -35,7 +41,7 @@ declare namespace NaiveUI { type GetTableData = A extends TableApiFn ? T : never; type NaiveTableConfig = Pick< - import('@sa/hooks').TableConfig, TableColumn>>, + import('@sa/hooks').TableConfig, TableColumn>>>, 'apiFn' | 'apiParams' | 'columns' | 'immediate' >; } diff --git a/src/views/manage/menu/index.vue b/src/views/manage/menu/index.vue index 66414013..c96fd2bd 100644 --- a/src/views/manage/menu/index.vue +++ b/src/views/manage/menu/index.vue @@ -1,37 +1,21 @@ @@ -234,7 +208,7 @@ function getRowKey(row: Api.SystemManage.Menu) {