diff --git a/src/hooks/common/table.ts b/src/hooks/common/table.ts index 3544cfd..aba8cdf 100644 --- a/src/hooks/common/table.ts +++ b/src/hooks/common/table.ts @@ -291,3 +291,282 @@ export function useTableOperate(data: Ref, function isTableColumnHasKey(column: TableColumn): column is NaiveUI.TableColumnWithKey { return Boolean((column as NaiveUI.TableColumnWithKey).key); } + +/* 由于后端响应主键为不是id, 而前端框架操作主键为id, 所以需要在此处把值传给id */ +export function useCustomPKTable(pkName: string, config: NaiveUI.NaiveTableConfig) { + const scope = effectScope(); + const appStore = useAppStore(); + + const isMobile = computed(() => appStore.isMobile); + + const { apiFn, apiParams, immediate } = config; + + const showTotal = config.showTotal || true; + + const SELECTION_KEY = '__selection__'; + + const EXPAND_KEY = '__expand__'; + + const { + loading, + empty, + data, + columns, + columnChecks, + reloadColumns, + getData, + searchParams, + updateSearchParams, + resetSearchParams: resetSearchParams0 + } = useHookTable, TableColumn>>>({ + apiFn, + apiParams, + searchParams: config.searchParams, + columns: config.columns, + transformer: res => { + const { data: records = [], page: current = 1, size = 10, total = 0 } = res.data || {}; + + // Ensure that the size is greater than 0, If it is less than 0, it will cause paging calculation errors. + const pageSize = size <= 0 ? 10 : size; + + const recordsWithIndex = records.map((item, index) => { + return { + ...item, + // 这里把pkName赋值给id,用于table监听选框 + id: item[pkName], + index: (current - 1) * pageSize + index + 1 + }; + }); + + return { + data: recordsWithIndex, + pageNum: current, + pageSize, + 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 + }); + } else if (column.type === 'expand') { + checks.push({ + key: EXPAND_KEY, + title: $t('common.expandColumn'), + 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); + } else if (column.type === 'expand') { + columnMap.set(EXPAND_KEY, column); + } + + if (isMobile.value && column.fixed) { + column.fixed = undefined; + } + }); + + 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({ + page, + size: pagination.pageSize! + }); + + getData(); + }, + onUpdatePageSize: async (pageSize: number) => { + pagination.pageSize = pageSize; + pagination.page = 1; + + updateSearchParams({ + page: pagination.page, + size: pageSize + }); + + getData(); + }, + ...(showTotal + ? { + prefix: page => $t('datatable.itemCount', { total: page.itemCount }) + } + : {}) + }); + + // this is for mobile, if the system does not support mobile, you can use `pagination` directly + const mobilePagination = computed(() => { + const p: PaginationProps = { + ...pagination, + pageSlot: isMobile.value ? 3 : 9, + prefix: !isMobile.value && showTotal ? pagination.prefix : undefined + }; + + return p; + }); + + function updatePagination(update: Partial) { + Object.assign(pagination, update); + } + + /** + * get data by page number + * + * @param pageNum the page number. default is 1 + */ + async function getDataByPage(pageNum: number = 1) { + updatePagination({ + page: pageNum + }); + + updateSearchParams({ + page: pageNum, + size: pagination.pageSize! + }); + + await getData(); + } + + scope.run(() => { + watch( + () => appStore.locale, + () => { + reloadColumns(); + } + ); + }); + + onScopeDispose(() => { + scope.stop(); + }); + + const resetSearchParams = () => { + resetSearchParams0(); + getData(); + }; + + return { + loading, + empty, + data, + columns, + columnChecks, + reloadColumns, + pagination, + mobilePagination, + updatePagination, + getData, + getDataByPage, + searchParams, + updateSearchParams, + resetSearchParams + }; +} + +export function useCustomPKTableOperate(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(pkVal: any) { + operateType.value = 'edit'; + const findItem = data.value.find(item => item.id === pkVal) || null; + editingData.value = jsonClone(findItem); + + openDrawer(); + } + + function handleCopy(pkVal: any) { + operateType.value = 'add'; + const findItem = data.value.find(item => item.id === pkVal) || null; + editingData.value = jsonClone(findItem); + delete editingData.value?.id; + + openDrawer(); + } + + /** the checked row keys of table */ + 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, + openDrawer, + closeDrawer, + operateType, + handleAdd, + editingData, + handleEdit, + handleCopy, + checkedRowKeys, + onBatchDeleted, + onDeleted + }; +} diff --git a/src/locales/langs/zh-cn.ts b/src/locales/langs/zh-cn.ts index 1ae526f..021e738 100644 --- a/src/locales/langs/zh-cn.ts +++ b/src/locales/langs/zh-cn.ts @@ -351,7 +351,9 @@ const local: App.I18n.Schema = { workflow_form_add: '新增工作流', job: '数采任务', job_task: '任务管理', - job_batch: '执行批次' + job_batch: '执行批次', + dictionary_type: '字典管理', + dictionary_data: '字典数据' }, page: { common: { @@ -506,15 +508,16 @@ const local: App.I18n.Schema = { addCategory: '新增分类', editCategory: '编辑分类' }, - dictionary: { - title:'字典', - dictionId:'字典编号', + dictionaryType: { + title: '字典管理', + detail: '字典详情', + dictionaryId: '字典编号', dictionaryName: '字典名称', dictionaryType: '字典类型', dictionaryStatus: '状态', - remark:'备注', + remark: '备注', createTime: '创建时间', - form:{ + form: { dictionaryNamePlaceHolder: '请输入字典名称', dictionaryName: '字典名称', dictionaryTypePlaceHolder: '请输入字典名称', @@ -525,7 +528,22 @@ const local: App.I18n.Schema = { }, addDictionary: '新增字典', editDictionary: '编辑字典' - + }, + dictionaryData: { + title: '字典数据', + detail: '字典数据详情', + dictionaryCode: '字典编码', + dictionaryLabel: '字典标签', + dictionaryValue: '字典键值', + createTime: '创建时间', + dictionaryType: '所属类型', + form: { + dictLabel: '字典标签', + dictValue: '字典键值', + dictType: '所属类型' + }, + addDictionary: '新增字典条目', + editDictionary: '编辑字典条目' }, namespace: { title: '命名空间', diff --git a/src/router/elegant/imports.ts b/src/router/elegant/imports.ts index dc6098d..f586371 100644 --- a/src/router/elegant/imports.ts +++ b/src/router/elegant/imports.ts @@ -23,7 +23,8 @@ export const views: Record Promise import("@/views/_builtin/login/index.vue"), about: () => import("@/views/about/index.vue"), category: () => import("@/views/category/index.vue"), - dictionary: () => import("@/views/dictionary/index.vue"), + dictionary_data: () => import("@/views/dictionary/data/index.vue"), + dictionary_type: () => import("@/views/dictionary/type/index.vue"), group: () => import("@/views/group/index.vue"), home: () => import("@/views/home/index.vue"), job_batch: () => import("@/views/job/batch/index.vue"), diff --git a/src/router/elegant/routes.ts b/src/router/elegant/routes.ts index 448e701..5050c38 100644 --- a/src/router/elegant/routes.ts +++ b/src/router/elegant/routes.ts @@ -62,13 +62,36 @@ export const generatedRoutes: GeneratedRoute[] = [ { name: 'dictionary', path: '/dictionary', - component: 'layout.base$view.dictionary', + component: 'layout.base', meta: { title: 'dictionary', i18nKey: 'route.dictionary', icon: 'material-symbols:dictionary-outline', - order: 14 - } + order: 14, + keepAlive: false + }, + children: [ + { + name: 'dictionary_data', + path: '/dictionary/data', + component: 'view.dictionary_data', + meta: { + title: 'dictionary_data', + i18nKey: 'route.dictionary_data', + icon: 'carbon:batch-job' + } + }, + { + name: 'dictionary_type', + path: '/dictionary/type', + component: 'view.dictionary_type', + meta: { + title: 'dictionary_type', + i18nKey: 'route.dictionary_type', + icon: 'octicon:tasklist' + } + } + ] }, { name: 'group', diff --git a/src/service/api/dictionary-data.ts b/src/service/api/dictionary-data.ts new file mode 100644 index 0000000..2038932 --- /dev/null +++ b/src/service/api/dictionary-data.ts @@ -0,0 +1,60 @@ +import { request } from '../request'; + +const prefix = '/dict/data'; + +/** get dictType list */ +export function fetchGetDictionaryDataList(params?: Api.DictionaryData.DictionaryDataSearchParams) { + return request({ + url: `${prefix}/list`, + method: 'get', + params + }); +} + +export function fetchGetDictionaryDataByCode(dictCode: number) { + return request({ + url: `${prefix}/${dictCode}`, + method: 'get' + }); +} + +export function fetchGetDictionaryDataListByDictType(dictType: string) { + return request({ + url: `${prefix}/type/${dictType}`, + method: 'get' + }); +} + +/** add dictType */ +export function fetchAddDictionaryData(data: Api.DictionaryData.DictionaryDataRequestVO) { + return request({ + url: prefix, + method: 'post', + data + }); +} + +/** edit dictType */ +export function fetchEditDictionaryData(data: Api.DictionaryData.DictionaryDataRequestVO) { + return request({ + url: prefix, + method: 'put', + data + }); +} + +/** delete dictType by id */ +export function fetchDeleteDictionaryData(dictCode: number) { + return request({ + url: `${prefix}/${dictCode}`, + method: 'delete' + }); +} + +/** batch delete dictType by id */ +export function fetchBatchDeleteDictionaryData(dictCodes: number[]) { + return request({ + url: `${prefix}/${dictCodes}`, + method: 'delete' + }); +} diff --git a/src/service/api/dictionary-type.ts b/src/service/api/dictionary-type.ts new file mode 100644 index 0000000..186a0ec --- /dev/null +++ b/src/service/api/dictionary-type.ts @@ -0,0 +1,46 @@ +import { request } from '../request'; + +const prefix = '/dict/type'; + +/** get dictType list */ +export function fetchGetDictionaryTypeList(params?: Api.DictionaryType.DictionaryTypeSearchParams) { + return request({ + url: `${prefix}/list`, + method: 'get', + params + }); +} + +/** add dictType */ +export function fetchAddDictionaryType(data: Api.DictionaryType.DictionaryTypeRequestVO) { + return request({ + url: prefix, + method: 'post', + data + }); +} + +/** edit dictType */ +export function fetchEditDictionaryType(data: Api.DictionaryType.DictionaryTypeRequestVO) { + return request({ + url: prefix, + method: 'put', + data + }); +} + +/** delete dictType by id */ +export function fetchDeleteDictionaryType(dictId: number) { + return request({ + url: `${prefix}/${dictId}`, + method: 'delete' + }); +} + +/** batch delete dictType by id */ +export function fetchBatchDeleteDictionaryType(ids: number[]) { + return request({ + url: `${prefix}/${ids}`, + method: 'delete' + }); +} diff --git a/src/service/api/dictionary.ts b/src/service/api/dictionary.ts deleted file mode 100644 index 60fc70c..0000000 --- a/src/service/api/dictionary.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { request } from '../request'; - -/** get dictConfig list */ -export function fetchGetDictionaryConfigList(params?: Api.DictionaryConfig.DictionaryConfigSearchParams) { - return request({ - url: '/dict/list', - method: 'get', - params - }); -} - -export function fetchGetAllDictionaryNameList(params?: Api.DictionaryConfig.DictionaryConfigSearchParams) { - return request({ - url: '/dict/all/dict-name/list', - method: 'get', - params - }); -} - -/** add dictConfig */ -export function fetchAddDictionaryConfig(data: Api.DictionaryConfig.DictionaryConfigRequestVO) { - return request({ - url: '/dict', - method: 'post', - data - }); -} - -/** edit dictConfig */ -export function fetchEditDictionaryConfig(data: Api.DictionaryConfig.DictionaryConfigRequestVO) { - return request({ - url: '/dict', - method: 'put', - data - }); -} - -export function fetchUpdateDictionaryStatus(data: Api.DictionaryConfig.DictionaryConfigRequestVO) { - return request({ - url: '/dict/status', - method: 'put', - data - }); -} - -/** get partition table list */ -// export function fetchGetPartitionTableList() { -// return request({ -// url: '/dict/partition-table/list', -// method: 'get' -// }); -// } - -/** get all dict config list */ -export function fetchGetAllDictionaryConfigList(data: string[]) { - return request({ - url: '/dict/all/dict-config/list', - method: 'post', - data - }); -} - -/** delete dict by id */ -export function fetchDeleteDictionary(dictName: string) { - return request({ - url: `/dict/${dictName}`, - method: 'delete' - }); -} diff --git a/src/service/api/index.ts b/src/service/api/index.ts index a5e9b61..9cdf13a 100644 --- a/src/service/api/index.ts +++ b/src/service/api/index.ts @@ -14,4 +14,5 @@ export * from './job'; export * from './job-batch'; export * from './user'; export * from './category'; -export * from './dictionary'; +export * from './dictionary-type'; +export * from './dictionary-data'; diff --git a/src/typings/api.d.ts b/src/typings/api.d.ts index 41e1e48..5868182 100644 --- a/src/typings/api.d.ts +++ b/src/typings/api.d.ts @@ -1394,30 +1394,75 @@ declare namespace Api { data: RowData[]; } } - namespace DictionaryConfig { - type CommonSearchParams = Pick; - type DictionaryConfig = Common.CommonRecord<{ - dictName: string; - dictType: string; - dictStatus: string; - createTime: string; - }>; - type DictionaryConfigSearchParams = CommonType.RecordNullable< - Pick & - CommonSearchParams - >; - type DictionaryConfigList = { - dictId: number; - dictName: string; - dictType: string; - dictStatus: string; - createTime: string; - }; - type DictionaryConfigRequestVO = { - dictName: string; - dictType: string; - dictStatus: string; - createTime: string; + namespace Common { + type DateRangeParams = { + datetimeRange?: [string, string]; }; } + + // 字典类型 + namespace DictionaryType { + type CommonSearchParams = Pick; + // 此处可以自定义类型,供对象属性使用 + // type DictStatus = 1 | 2 | 3; + // 包含dict对象的所有属性 + type DictionaryType = Common.CommonRecord<{ + dictId?: number; + dictName: string; + dictType: string; + createTime: string; + updateTime: string; + }>; + // 搜索参数,使用Pick从DictionaryType中取出需要的属性,RecordNullable将其设置为可选 + type DictionaryTypeSearchParams = CommonType.RecordNullable< + Pick & CommonSearchParams + >; + // 请求响应对象 + type DictionaryTypeList = Common.PaginatingQueryRecord; + // 请求(新增/修改)参数对象 + type DictionaryTypeRequestVO = { + dictId?: number; + dictName?: string; + dictType?: string; + }; + // 导出json参数对象 + type ExportDictionaryType = CommonType.RecordNullable< + Pick & CommonSearchParams + > & { dictIds: number[] }; + } + + // 字典类型详细数据 + namespace DictionaryData { + type CommonSearchParams = Pick; + // 此处可以自定义类型,供对象属性使用 + // type DictStatus = 1 | 2 | 3; + // 包含dict对象的所有属性 + type DictionaryData = Common.CommonRecord<{ + dictCode: number; + dictLabel: string; + dictValue: string; + dictType: string; + createTime: string; + updateTime: string; + }>; + // 搜索参数,使用Pick从DictionaryData中取出需要的属性,RecordNullable将其设置为可选 + type DictionaryDataSearchParams = CommonType.RecordNullable< + Pick & + CommonSearchParams & + Common.DateRangeParams + >; + // 请求响应对象 + type DictionaryDataList = Common.PaginatingQueryRecord; + // 请求(新增/修改)参数对象 + type DictionaryDataRequestVO = { + dictCode?: number; + dictLabel?: string; + dictValue?: string; + dictType?: string; + }; + // 导出json参数对象 + type ExportDictionaryData = CommonType.RecordNullable< + Pick & CommonSearchParams + > & { dictCodes: number[] }; + } } diff --git a/src/typings/app.d.ts b/src/typings/app.d.ts index adcbdee..e81337c 100644 --- a/src/typings/app.d.ts +++ b/src/typings/app.d.ts @@ -678,14 +678,51 @@ declare namespace App { categoryParentName: string; categoryType: string; createTime: string; - }, + }; type: { website: string; literature: string; - }, + }; addCategory: string; editCategory: string; - }, + }; + dictionaryType: { + title: string; + detail: string; + dictionaryId: string; + dictionaryName: string; + dictionaryType: string; + dictionaryStatus: string; + remark: string; + createTime: string; + form: { + dictionaryNamePlaceHolder: string; + dictionaryName: string; + dictionaryTypePlaceHolder: string; + dictionaryType: string; + dictionaryStatusPlaceHolder: string; + dictionaryStatus: string; + createTime: string; + }; + addDictionary: string; + editDictionary: string; + }; + dictionaryData: { + title: string; + detail: string; + dictionaryCode: string; + dictionaryLabel: string; + dictionaryValue: string; + createTime: string; + dictionaryType: string; + form: { + dictLabel: string; + dictValue: string; + dictType: string; + }; + addDictionary: string; + editDictionary: string; + }; pods: { title: string; nodeType: string; @@ -752,7 +789,7 @@ declare namespace App { government: string; usMilitary: string; usNavy: string; - }, + }; idMode: { idWorker: string; segment: string; diff --git a/src/utils/dict.ts b/src/utils/dict.ts new file mode 100644 index 0000000..f0cddde --- /dev/null +++ b/src/utils/dict.ts @@ -0,0 +1,70 @@ +import { fetchGetDictionaryDataList, fetchGetDictionaryTypeList } from '@/service/api'; +// export dict +// { +// type:{ +// sys_sex:[ +// {label:"男", +// value: "1"}, +// {label:"女", +// value: "2"} +// ] +// } + +// } +type indexAbleDictData = { + label: string; + value: string; +}; +type indexAbleDictType = { + [key: string]: indexAbleDictData[]; +}; + +type indexAbleDict = { + type: indexAbleDictType; +}; + +async function init() { + // const dictTypeList = reactive([]); + // const dict: indexAbleDict = { + // type: { + // fruits: [ + // { label: '苹果', value: 'apple' }, + // { label: '香蕉', value: 'banana' }, + // { label: '橙子', value: 'orange' } + // ], + // colors: [ + // { label: '红色', value: 'red' }, + // { label: '绿色', value: 'green' }, + // { label: '蓝色', value: 'blue' } + // ], + // animals: [ + // { label: '狗', value: 'dog' }, + // { label: '猫', value: 'cat' }, + // { label: '鸟', value: 'bird' } + // ] + // } + // }; + const typeList: string[] = (await fetchGetDictionaryTypeList()).data?.data.map(item => item.dictType) || []; + const dataList: any[] = + (await fetchGetDictionaryDataList()).data?.data.map(item => ({ + dictType: item.dictType, + dictLabel: item.dictLabel, + dictValue: item.dictValue + })) || []; + const dict: indexAbleDict = { + type: Array.from(typeList).reduce((acc, dictType) => { + // 筛选出当前 dictType 对应的数据 + const items = dataList.filter(item => item.dictType === dictType); + // 将数据转换为 indexAbleDictData 类型 + acc[dictType] = items.map(item => ({ + label: item.dictLabel, + value: item.dictValue + })); + return acc; + }, {} as indexAbleDictType) + }; + return dict; +} +const dict = init(); + +export default dict; diff --git a/src/views/dictionary/data/index.vue b/src/views/dictionary/data/index.vue index 42d0a25..6455722 100644 --- a/src/views/dictionary/data/index.vue +++ b/src/views/dictionary/data/index.vue @@ -1,11 +1,11 @@ @@ -149,7 +162,7 @@ function handleExport() { (); @@ -18,22 +18,19 @@ const visible = defineModel('visible', {