feat: 新增 ele 字典管理模块
							parent
							
								
									08cb268c93
								
							
						
					
					
						commit
						73ec26744d
					
				|  | @ -0,0 +1,388 @@ | |||
| import type { VbenFormSchema } from '#/adapter/form'; | ||||
| import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table'; | ||||
| import type { SystemDictDataApi } from '#/api/system/dict/data'; | ||||
| import type { SystemDictTypeApi } from '#/api/system/dict/type'; | ||||
| 
 | ||||
| import { useAccess } from '@vben/access'; | ||||
| 
 | ||||
| import { z } from '#/adapter/form'; | ||||
| import { getSimpleDictTypeList } from '#/api/system/dict/type'; | ||||
| import { CommonStatusEnum, DICT_TYPE, getDictOptions } from '#/utils'; | ||||
| 
 | ||||
| const { hasAccessByCodes } = useAccess(); | ||||
| 
 | ||||
| // ============================== 字典类型 ==============================
 | ||||
| 
 | ||||
| /** 类型新增/修改的表单 */ | ||||
| export function useTypeFormSchema(): VbenFormSchema[] { | ||||
|   return [ | ||||
|     { | ||||
|       fieldName: 'id', | ||||
|       component: 'Input', | ||||
|       dependencies: { | ||||
|         triggerFields: [''], | ||||
|         show: () => false, | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'name', | ||||
|       label: '字典名称', | ||||
|       component: 'Input', | ||||
|       componentProps: { | ||||
|         placeholder: '请输入字典名称', | ||||
|       }, | ||||
|       rules: 'required', | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'type', | ||||
|       label: '字典类型', | ||||
|       component: 'Input', | ||||
|       componentProps: (values) => { | ||||
|         return { | ||||
|           placeholder: '请输入字典类型', | ||||
|           disabled: !!values.id, | ||||
|         }; | ||||
|       }, | ||||
|       rules: 'required', | ||||
|       dependencies: { | ||||
|         triggerFields: [''], | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'status', | ||||
|       label: '状态', | ||||
|       component: 'RadioGroup', | ||||
|       componentProps: { | ||||
|         options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), | ||||
|         buttonStyle: 'solid', | ||||
|         optionType: 'button', | ||||
|       }, | ||||
|       rules: z.number().default(CommonStatusEnum.ENABLE), | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'remark', | ||||
|       label: '备注', | ||||
|       component: 'Textarea', | ||||
|       componentProps: { | ||||
|         placeholder: '请输入备注', | ||||
|       }, | ||||
|     }, | ||||
|   ]; | ||||
| } | ||||
| 
 | ||||
| /** 类型列表的搜索表单 */ | ||||
| export function useTypeGridFormSchema(): VbenFormSchema[] { | ||||
|   return [ | ||||
|     { | ||||
|       fieldName: 'name', | ||||
|       label: '字典名称', | ||||
|       component: 'Input', | ||||
|       componentProps: { | ||||
|         placeholder: '请输入字典名称', | ||||
|         clearable: true, | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'status', | ||||
|       label: '状态', | ||||
|       component: 'Select', | ||||
|       componentProps: { | ||||
|         options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), | ||||
|         placeholder: '请选择状态', | ||||
|         clearable: true, | ||||
|       }, | ||||
|     }, | ||||
|   ]; | ||||
| } | ||||
| 
 | ||||
| /** 类型列表的字段 */ | ||||
| export function useTypeGridColumns<T = SystemDictTypeApi.DictType>( | ||||
|   onActionClick: OnActionClickFn<T>, | ||||
| ): VxeTableGridOptions['columns'] { | ||||
|   return [ | ||||
|     { | ||||
|       field: 'id', | ||||
|       title: '字典编号', | ||||
|       minWidth: 100, | ||||
|     }, | ||||
|     { | ||||
|       field: 'name', | ||||
|       title: '字典名称', | ||||
|       minWidth: 200, | ||||
|     }, | ||||
|     { | ||||
|       field: 'type', | ||||
|       title: '字典类型', | ||||
|       minWidth: 220, | ||||
|     }, | ||||
|     { | ||||
|       field: 'status', | ||||
|       title: '状态', | ||||
|       minWidth: 120, | ||||
|       cellRender: { | ||||
|         name: 'CellDict', | ||||
|         props: { type: DICT_TYPE.COMMON_STATUS }, | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       field: 'remark', | ||||
|       title: '备注', | ||||
|       minWidth: 180, | ||||
|     }, | ||||
|     { | ||||
|       field: 'createTime', | ||||
|       title: '创建时间', | ||||
|       minWidth: 180, | ||||
|       formatter: 'formatDateTime', | ||||
|     }, | ||||
|     { | ||||
|       minWidth: 120, | ||||
|       title: '操作', | ||||
|       field: 'operation', | ||||
|       fixed: 'right', | ||||
|       align: 'center', | ||||
|       cellRender: { | ||||
|         attrs: { | ||||
|           nameField: 'type', | ||||
|           nameTitle: '字典类型', | ||||
|           onClick: onActionClick, | ||||
|         }, | ||||
|         name: 'CellOperation', | ||||
|         options: [ | ||||
|           { | ||||
|             code: 'edit', | ||||
|             show: hasAccessByCodes(['system:dict:update']), | ||||
|           }, | ||||
|           { | ||||
|             code: 'delete', | ||||
|             show: hasAccessByCodes(['system:dict:delete']), | ||||
|           }, | ||||
|         ], | ||||
|       }, | ||||
|     }, | ||||
|   ]; | ||||
| } | ||||
| 
 | ||||
| // ============================== 字典数据 ==============================
 | ||||
| 
 | ||||
| // TODO @芋艿:后续针对 antd,增加
 | ||||
| /** | ||||
|  * 颜色选项 | ||||
|  */ | ||||
| const colorOptions = [ | ||||
|   { value: '', label: '无' }, | ||||
|   { value: 'processing', label: '主要' }, | ||||
|   { value: 'success', label: '成功' }, | ||||
|   { value: 'default', label: '默认' }, | ||||
|   { value: 'warning', label: '警告' }, | ||||
|   { value: 'error', label: '危险' }, | ||||
|   { value: 'pink', label: 'pink' }, | ||||
|   { value: 'red', label: 'red' }, | ||||
|   { value: 'orange', label: 'orange' }, | ||||
|   { value: 'green', label: 'green' }, | ||||
|   { value: 'cyan', label: 'cyan' }, | ||||
|   { value: 'blue', label: 'blue' }, | ||||
|   { value: 'purple', label: 'purple' }, | ||||
| ]; | ||||
| 
 | ||||
| /** 数据新增/修改的表单 */ | ||||
| export function useDataFormSchema(): VbenFormSchema[] { | ||||
|   return [ | ||||
|     { | ||||
|       fieldName: 'id', | ||||
|       component: 'Input', | ||||
|       dependencies: { | ||||
|         triggerFields: [''], | ||||
|         show: () => false, | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'dictType', | ||||
|       label: '字典类型', | ||||
|       component: 'ApiSelect', | ||||
|       componentProps: (values) => { | ||||
|         return { | ||||
|           api: getSimpleDictTypeList, | ||||
|           placeholder: '请输入字典类型', | ||||
|           labelField: 'name', | ||||
|           valueField: 'type', | ||||
|           disabled: !!values.id, | ||||
|         }; | ||||
|       }, | ||||
|       rules: 'required', | ||||
|       dependencies: { | ||||
|         triggerFields: [''], | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'label', | ||||
|       label: '数据标签', | ||||
|       component: 'Input', | ||||
|       componentProps: { | ||||
|         placeholder: '请输入数据标签', | ||||
|       }, | ||||
|       rules: 'required', | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'value', | ||||
|       label: '数据键值', | ||||
|       component: 'Input', | ||||
|       componentProps: { | ||||
|         placeholder: '请输入数据键值', | ||||
|       }, | ||||
|       rules: 'required', | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'sort', | ||||
|       label: '显示排序', | ||||
|       component: 'InputNumber', | ||||
|       componentProps: { | ||||
|         placeholder: '请输入显示排序', | ||||
|       }, | ||||
|       rules: 'required', | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'status', | ||||
|       label: '状态', | ||||
|       component: 'RadioGroup', | ||||
|       componentProps: { | ||||
|         options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), | ||||
|         placeholder: '请选择状态', | ||||
|         buttonStyle: 'solid', | ||||
|         optionType: 'button', | ||||
|       }, | ||||
|       rules: z.number().default(CommonStatusEnum.ENABLE), | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'colorType', | ||||
|       label: '颜色类型', | ||||
|       component: 'Select', | ||||
|       componentProps: { | ||||
|         options: colorOptions, | ||||
|         placeholder: '请选择颜色类型', | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'cssClass', | ||||
|       label: 'CSS Class', | ||||
|       component: 'Input', | ||||
|       componentProps: { | ||||
|         placeholder: '请输入 CSS Class', | ||||
|       }, | ||||
|       help: '输入 hex 模式的颜色, 例如 #108ee9', | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'remark', | ||||
|       label: '备注', | ||||
|       component: 'Textarea', | ||||
|       componentProps: { | ||||
|         placeholder: '请输入备注', | ||||
|       }, | ||||
|     }, | ||||
|   ]; | ||||
| } | ||||
| 
 | ||||
| /** 字典数据列表搜索表单 */ | ||||
| export function useDataGridFormSchema(): VbenFormSchema[] { | ||||
|   return [ | ||||
|     { | ||||
|       fieldName: 'label', | ||||
|       label: '字典标签', | ||||
|       component: 'Input', | ||||
|       componentProps: { | ||||
|         placeholder: '请输入字典标签', | ||||
|         clearable: true, | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'status', | ||||
|       label: '状态', | ||||
|       component: 'Select', | ||||
|       componentProps: { | ||||
|         options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), | ||||
|         placeholder: '请选择状态', | ||||
|         clearable: true, | ||||
|       }, | ||||
|     }, | ||||
|   ]; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 字典数据表格列 | ||||
|  */ | ||||
| export function useDataGridColumns<T = SystemDictDataApi.DictData>( | ||||
|   onActionClick: OnActionClickFn<T>, | ||||
| ): VxeTableGridOptions['columns'] { | ||||
|   return [ | ||||
|     { | ||||
|       field: 'id', | ||||
|       title: '字典编码', | ||||
|       minWidth: 100, | ||||
|     }, | ||||
|     { | ||||
|       field: 'label', | ||||
|       title: '字典标签', | ||||
|       minWidth: 180, | ||||
|     }, | ||||
|     { | ||||
|       field: 'value', | ||||
|       title: '字典键值', | ||||
|       minWidth: 100, | ||||
|     }, | ||||
|     { | ||||
|       field: 'sort', | ||||
|       title: '字典排序', | ||||
|       minWidth: 100, | ||||
|     }, | ||||
|     { | ||||
|       field: 'status', | ||||
|       title: '状态', | ||||
|       minWidth: 100, | ||||
|       cellRender: { | ||||
|         name: 'CellDict', | ||||
|         props: { type: DICT_TYPE.COMMON_STATUS }, | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       field: 'colorType', | ||||
|       title: '颜色类型', | ||||
|       minWidth: 120, | ||||
|     }, | ||||
|     { | ||||
|       field: 'cssClass', | ||||
|       title: 'CSS Class', | ||||
|       minWidth: 120, | ||||
|     }, | ||||
|     { | ||||
|       title: '创建时间', | ||||
|       field: 'createTime', | ||||
|       minWidth: 180, | ||||
|       formatter: 'formatDateTime', | ||||
|     }, | ||||
|     { | ||||
|       minWidth: 120, | ||||
|       title: '操作', | ||||
|       field: 'operation', | ||||
|       fixed: 'right', | ||||
|       align: 'center', | ||||
|       cellRender: { | ||||
|         attrs: { | ||||
|           nameField: 'label', | ||||
|           nameTitle: '字典数据', | ||||
|           onClick: onActionClick, | ||||
|         }, | ||||
|         name: 'CellOperation', | ||||
|         options: [ | ||||
|           { | ||||
|             code: 'edit', | ||||
|             show: hasAccessByCodes(['system:dict:update']), | ||||
|           }, | ||||
|           { | ||||
|             code: 'delete', | ||||
|             show: hasAccessByCodes(['system:dict:delete']), | ||||
|           }, | ||||
|         ], | ||||
|       }, | ||||
|     }, | ||||
|   ]; | ||||
| } | ||||
|  | @ -0,0 +1,35 @@ | |||
| <script lang="ts" setup> | ||||
| import { ref } from 'vue'; | ||||
| 
 | ||||
| import { Page } from '@vben/common-ui'; | ||||
| 
 | ||||
| import { DocAlert } from '#/components/doc-alert'; | ||||
| 
 | ||||
| import DataGrid from './modules/data-grid.vue'; | ||||
| import TypeGrid from './modules/type-grid.vue'; | ||||
| 
 | ||||
| const searchDictType = ref<string>(); // 搜索的字典类型 | ||||
| 
 | ||||
| function onDictTypeSelect(dictType: string) { | ||||
|   searchDictType.value = dictType; | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <Page auto-content-height> | ||||
|     <template #doc> | ||||
|       <DocAlert title="字典管理" url="https://doc.iocoder.cn/system-dict/" /> | ||||
|     </template> | ||||
| 
 | ||||
|     <div class="flex h-full"> | ||||
|       <!-- 左侧字典类型列表 --> | ||||
|       <div class="w-1/2 pr-3"> | ||||
|         <TypeGrid @select="onDictTypeSelect" /> | ||||
|       </div> | ||||
|       <!-- 右侧字典数据列表 --> | ||||
|       <div class="w-1/2"> | ||||
|         <DataGrid :dict-type="searchDictType" /> | ||||
|       </div> | ||||
|     </div> | ||||
|   </Page> | ||||
| </template> | ||||
|  | @ -0,0 +1,98 @@ | |||
| <script lang="ts" setup> | ||||
| import type { SystemDictDataApi } from '#/api/system/dict/data'; | ||||
| 
 | ||||
| import { computed, ref } from 'vue'; | ||||
| 
 | ||||
| import { useVbenModal } from '@vben/common-ui'; | ||||
| 
 | ||||
| import { ElMessage } from 'element-plus'; | ||||
| 
 | ||||
| import { useVbenForm } from '#/adapter/form'; | ||||
| import { | ||||
|   createDictData, | ||||
|   getDictData, | ||||
|   updateDictData, | ||||
| } from '#/api/system/dict/data'; | ||||
| import { $t } from '#/locales'; | ||||
| 
 | ||||
| import { useDataFormSchema } from '../data'; | ||||
| 
 | ||||
| defineOptions({ name: 'SystemDictDataForm' }); | ||||
| 
 | ||||
| const emit = defineEmits(['success']); | ||||
| const formData = ref<SystemDictDataApi.DictData>(); | ||||
| const getTitle = computed(() => { | ||||
|   return formData.value?.id | ||||
|     ? $t('ui.actionTitle.edit', ['字典数据']) | ||||
|     : $t('ui.actionTitle.create', ['字典数据']); | ||||
| }); | ||||
| 
 | ||||
| const [Form, formApi] = useVbenForm({ | ||||
|   commonConfig: { | ||||
|     componentProps: { | ||||
|       class: 'w-full', | ||||
|     }, | ||||
|     formItemClass: 'col-span-2', | ||||
|     labelWidth: 80, | ||||
|   }, | ||||
|   layout: 'horizontal', | ||||
|   schema: useDataFormSchema(), | ||||
|   showDefaultActions: false, | ||||
| }); | ||||
| 
 | ||||
| const [Modal, modalApi] = useVbenModal({ | ||||
|   async onConfirm() { | ||||
|     const { valid } = await formApi.validate(); | ||||
|     if (!valid) { | ||||
|       return; | ||||
|     } | ||||
|     modalApi.lock(); | ||||
|     // 提交表单 | ||||
|     const data = (await formApi.getValues()) as SystemDictDataApi.DictData; | ||||
|     try { | ||||
|       await (formData.value?.id ? updateDictData(data) : createDictData(data)); | ||||
|       // 关闭并提示 | ||||
|       await modalApi.close(); | ||||
|       emit('success'); | ||||
|       ElMessage.success($t('ui.actionMessage.operationSuccess')); | ||||
|     } finally { | ||||
|       modalApi.unlock(); | ||||
|     } | ||||
|   }, | ||||
|   async onOpenChange(isOpen: boolean) { | ||||
|     if (!isOpen) { | ||||
|       formData.value = undefined; | ||||
|       return; | ||||
|     } | ||||
|     // 加载数据 | ||||
|     const data = modalApi.getData< | ||||
|       SystemDictDataApi.DictData | { dictType?: string } | ||||
|     >(); | ||||
| 
 | ||||
|     // 如果有ID,表示是编辑 | ||||
|     if (data && 'id' in data && data.id) { | ||||
|       modalApi.lock(); | ||||
|       try { | ||||
|         formData.value = await getDictData(data.id as number); | ||||
|         // 设置到 values | ||||
|         if (formData.value) { | ||||
|           await formApi.setValues(formData.value); | ||||
|         } | ||||
|       } finally { | ||||
|         modalApi.unlock(); | ||||
|       } | ||||
|     } else if (data && 'dictType' in data && data.dictType) { | ||||
|       // 新增时,如果传入了dictType,则需要设置 | ||||
|       await formApi.setValues({ | ||||
|         dictType: data.dictType, | ||||
|       }); | ||||
|     } | ||||
|   }, | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <Modal :title="getTitle"> | ||||
|     <Form class="mx-4" /> | ||||
|   </Modal> | ||||
| </template> | ||||
|  | @ -0,0 +1,158 @@ | |||
| <script lang="ts" setup> | ||||
| import type { | ||||
|   OnActionClickParams, | ||||
|   VxeTableGridOptions, | ||||
| } from '#/adapter/vxe-table'; | ||||
| import type { SystemDictDataApi } from '#/api/system/dict/data'; | ||||
| 
 | ||||
| import { watch } from 'vue'; | ||||
| 
 | ||||
| import { useVbenModal } from '@vben/common-ui'; | ||||
| import { Download, Plus } from '@vben/icons'; | ||||
| import { downloadFileFromBlobPart } from '@vben/utils'; | ||||
| 
 | ||||
| import { ElButton, ElLoading, ElMessage } from 'element-plus'; | ||||
| 
 | ||||
| import { useVbenVxeGrid } from '#/adapter/vxe-table'; | ||||
| import { | ||||
|   deleteDictData, | ||||
|   exportDictData, | ||||
|   getDictDataPage, | ||||
| } from '#/api/system/dict/data'; | ||||
| import { $t } from '#/locales'; | ||||
| 
 | ||||
| import { useDataGridColumns, useDataGridFormSchema } from '../data'; | ||||
| import DataForm from './data-form.vue'; | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   dictType: { | ||||
|     type: String, | ||||
|     default: undefined, | ||||
|   }, | ||||
| }); | ||||
| 
 | ||||
| const [DataFormModal, dataFormModalApi] = useVbenModal({ | ||||
|   connectedComponent: DataForm, | ||||
|   destroyOnClose: true, | ||||
| }); | ||||
| 
 | ||||
| /** 刷新表格 */ | ||||
| function onRefresh() { | ||||
|   gridApi.query(); | ||||
| } | ||||
| 
 | ||||
| /** 导出表格 */ | ||||
| async function onExport() { | ||||
|   const data = await exportDictData(await gridApi.formApi.getValues()); | ||||
|   downloadFileFromBlobPart({ fileName: '字典数据.xls', source: data }); | ||||
| } | ||||
| 
 | ||||
| /** 创建字典数据 */ | ||||
| function onCreate() { | ||||
|   dataFormModalApi.setData({ dictType: props.dictType }).open(); | ||||
| } | ||||
| 
 | ||||
| /** 编辑字典数据 */ | ||||
| function onEdit(row: any) { | ||||
|   dataFormModalApi.setData(row).open(); | ||||
| } | ||||
| 
 | ||||
| /** 删除字典数据 */ | ||||
| async function onDelete(row: any) { | ||||
|   const loadingInstance = ElLoading.service({ | ||||
|     text: $t('common.processing'), | ||||
|     fullscreen: true, | ||||
|   }); | ||||
|   try { | ||||
|     await deleteDictData(row.id); | ||||
|     ElMessage.success($t('common.operationSuccess')); | ||||
|     onRefresh(); | ||||
|   } catch { | ||||
|     // 异常处理 | ||||
|   } finally { | ||||
|     loadingInstance.close(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 表格操作按钮回调 */ | ||||
| function onActionClick({ code, row }: OnActionClickParams) { | ||||
|   switch (code) { | ||||
|     case 'delete': { | ||||
|       onDelete(row); | ||||
|       break; | ||||
|     } | ||||
|     case 'edit': { | ||||
|       onEdit(row); | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| const [Grid, gridApi] = useVbenVxeGrid({ | ||||
|   formOptions: { | ||||
|     schema: useDataGridFormSchema(), | ||||
|   }, | ||||
|   gridOptions: { | ||||
|     columns: useDataGridColumns(onActionClick), | ||||
|     height: 'auto', | ||||
|     keepSource: true, | ||||
|     proxyConfig: { | ||||
|       ajax: { | ||||
|         query: async ({ page }, formValues) => { | ||||
|           return await getDictDataPage({ | ||||
|             pageNo: page.currentPage, | ||||
|             pageSize: page.pageSize, | ||||
|             dictType: props.dictType, | ||||
|             ...formValues, | ||||
|           }); | ||||
|         }, | ||||
|       }, | ||||
|     }, | ||||
|     rowConfig: { | ||||
|       keyField: 'id', | ||||
|     }, | ||||
|     toolbarConfig: { | ||||
|       refresh: { code: 'query' }, | ||||
|       search: true, | ||||
|     }, | ||||
|   } as VxeTableGridOptions<SystemDictDataApi.DictData>, | ||||
| }); | ||||
| 
 | ||||
| /** 监听 dictType 变化,重新查询 */ | ||||
| watch( | ||||
|   () => props.dictType, | ||||
|   () => { | ||||
|     if (props.dictType) { | ||||
|       onRefresh(); | ||||
|     } | ||||
|   }, | ||||
| ); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div class="flex h-full flex-col"> | ||||
|     <DataFormModal @success="onRefresh" /> | ||||
| 
 | ||||
|     <Grid table-title="字典数据列表"> | ||||
|       <template #toolbar-tools> | ||||
|         <ElButton | ||||
|           type="primary" | ||||
|           @click="onCreate" | ||||
|           v-access:code="['system:dict:create']" | ||||
|         > | ||||
|           <Plus class="size-5" /> | ||||
|           {{ $t('ui.actionTitle.create', ['字典数据']) }} | ||||
|         </ElButton> | ||||
|         <ElButton | ||||
|           type="primary" | ||||
|           class="ml-2" | ||||
|           @click="onExport" | ||||
|           v-access:code="['system:dict:export']" | ||||
|         > | ||||
|           <Download class="size-5" /> | ||||
|           {{ $t('ui.actionTitle.export') }} | ||||
|         </ElButton> | ||||
|       </template> | ||||
|     </Grid> | ||||
|   </div> | ||||
| </template> | ||||
|  | @ -0,0 +1,88 @@ | |||
| <script lang="ts" setup> | ||||
| import type { SystemDictTypeApi } from '#/api/system/dict/type'; | ||||
| 
 | ||||
| import { computed, ref } from 'vue'; | ||||
| 
 | ||||
| import { useVbenModal } from '@vben/common-ui'; | ||||
| 
 | ||||
| import { ElMessage } from 'element-plus'; | ||||
| 
 | ||||
| import { useVbenForm } from '#/adapter/form'; | ||||
| import { | ||||
|   createDictType, | ||||
|   getDictType, | ||||
|   updateDictType, | ||||
| } from '#/api/system/dict/type'; | ||||
| import { $t } from '#/locales'; | ||||
| 
 | ||||
| import { useTypeFormSchema } from '../data'; | ||||
| 
 | ||||
| const emit = defineEmits(['success']); | ||||
| const formData = ref<SystemDictTypeApi.DictType>(); | ||||
| const getTitle = computed(() => { | ||||
|   return formData.value?.id | ||||
|     ? $t('ui.actionTitle.edit', ['字典类型']) | ||||
|     : $t('ui.actionTitle.create', ['字典类型']); | ||||
| }); | ||||
| 
 | ||||
| const [Form, formApi] = useVbenForm({ | ||||
|   commonConfig: { | ||||
|     componentProps: { | ||||
|       class: 'w-full', | ||||
|     }, | ||||
|     formItemClass: 'col-span-2', | ||||
|     labelWidth: 80, | ||||
|   }, | ||||
|   layout: 'horizontal', | ||||
|   schema: useTypeFormSchema(), | ||||
|   showDefaultActions: false, | ||||
| }); | ||||
| 
 | ||||
| const [Modal, modalApi] = useVbenModal({ | ||||
|   async onConfirm() { | ||||
|     const { valid } = await formApi.validate(); | ||||
|     if (!valid) { | ||||
|       return; | ||||
|     } | ||||
|     modalApi.lock(); | ||||
|     // 提交表单 | ||||
|     const data = (await formApi.getValues()) as SystemDictTypeApi.DictType; | ||||
|     try { | ||||
|       await (formData.value?.id ? updateDictType(data) : createDictType(data)); | ||||
|       // 关闭并提示 | ||||
|       await modalApi.close(); | ||||
|       emit('success'); | ||||
|       ElMessage.success($t('ui.actionMessage.operationSuccess')); | ||||
|     } finally { | ||||
|       modalApi.unlock(); | ||||
|     } | ||||
|   }, | ||||
|   async onOpenChange(isOpen: boolean) { | ||||
|     if (!isOpen) { | ||||
|       formData.value = undefined; | ||||
|       return; | ||||
|     } | ||||
|     // 加载数据 | ||||
|     const data = modalApi.getData<SystemDictTypeApi.DictType>(); | ||||
|     if (!data || !data.id) { | ||||
|       return; | ||||
|     } | ||||
|     modalApi.lock(); | ||||
|     try { | ||||
|       formData.value = await getDictType(data.id as number); | ||||
|       // 设置到 values | ||||
|       if (formData.value) { | ||||
|         await formApi.setValues(formData.value); | ||||
|       } | ||||
|     } finally { | ||||
|       modalApi.unlock(); | ||||
|     } | ||||
|   }, | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <Modal :title="getTitle"> | ||||
|     <Form class="mx-4" /> | ||||
|   </Modal> | ||||
| </template> | ||||
|  | @ -0,0 +1,153 @@ | |||
| <script lang="ts" setup> | ||||
| import type { | ||||
|   OnActionClickParams, | ||||
|   VxeGridListeners, | ||||
|   VxeTableGridOptions, | ||||
| } from '#/adapter/vxe-table'; | ||||
| import type { SystemDictTypeApi } from '#/api/system/dict/type'; | ||||
| 
 | ||||
| import { useVbenModal } from '@vben/common-ui'; | ||||
| import { Download, Plus } from '@vben/icons'; | ||||
| import { downloadFileFromBlobPart } from '@vben/utils'; | ||||
| 
 | ||||
| import { ElButton, ElLoading, ElMessage } from 'element-plus'; | ||||
| 
 | ||||
| import { useVbenVxeGrid } from '#/adapter/vxe-table'; | ||||
| import { | ||||
|   deleteDictType, | ||||
|   exportDictType, | ||||
|   getDictTypePage, | ||||
| } from '#/api/system/dict/type'; | ||||
| import { $t } from '#/locales'; | ||||
| 
 | ||||
| import { useTypeGridColumns, useTypeGridFormSchema } from '../data'; | ||||
| import TypeForm from './type-form.vue'; | ||||
| 
 | ||||
| const emit = defineEmits(['select']); | ||||
| 
 | ||||
| const [TypeFormModal, typeFormModalApi] = useVbenModal({ | ||||
|   connectedComponent: TypeForm, | ||||
|   destroyOnClose: true, | ||||
| }); | ||||
| 
 | ||||
| /** 刷新表格 */ | ||||
| function onRefresh() { | ||||
|   gridApi.query(); | ||||
| } | ||||
| 
 | ||||
| /** 导出表格 */ | ||||
| async function onExport() { | ||||
|   const data = await exportDictType(await gridApi.formApi.getValues()); | ||||
|   downloadFileFromBlobPart({ fileName: '字典类型.xls', source: data }); | ||||
| } | ||||
| 
 | ||||
| /** 创建字典类型 */ | ||||
| function onCreate() { | ||||
|   typeFormModalApi.setData(null).open(); | ||||
| } | ||||
| 
 | ||||
| /** 编辑字典类型 */ | ||||
| function onEdit(row: any) { | ||||
|   typeFormModalApi.setData(row).open(); | ||||
| } | ||||
| 
 | ||||
| /** 删除字典类型 */ | ||||
| async function onDelete(row: SystemDictTypeApi.DictType) { | ||||
|   const loadingInstance = ElLoading.service({ | ||||
|     text: $t('common.processing'), | ||||
|     fullscreen: true, | ||||
|   }); | ||||
|   try { | ||||
|     await deleteDictType(row.id as number); | ||||
|     ElMessage.success($t('common.operationSuccess')); | ||||
|     onRefresh(); | ||||
|   } catch { | ||||
|     // 异常处理 | ||||
|   } finally { | ||||
|     loadingInstance.close(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 表格操作按钮回调 */ | ||||
| function onActionClick({ | ||||
|   code, | ||||
|   row, | ||||
| }: OnActionClickParams<SystemDictTypeApi.DictType>) { | ||||
|   switch (code) { | ||||
|     case 'delete': { | ||||
|       onDelete(row); | ||||
|       break; | ||||
|     } | ||||
|     case 'edit': { | ||||
|       onEdit(row); | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 表格事件 */ | ||||
| const gridEvents: VxeGridListeners<SystemDictTypeApi.DictType> = { | ||||
|   cellClick: ({ row }) => { | ||||
|     emit('select', row.type); | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| const [Grid, gridApi] = useVbenVxeGrid({ | ||||
|   formOptions: { | ||||
|     schema: useTypeGridFormSchema(), | ||||
|   }, | ||||
|   gridOptions: { | ||||
|     columns: useTypeGridColumns(onActionClick), | ||||
|     height: 'auto', | ||||
|     keepSource: true, | ||||
|     proxyConfig: { | ||||
|       ajax: { | ||||
|         query: async ({ page }, formValues) => { | ||||
|           return await getDictTypePage({ | ||||
|             pageNo: page.currentPage, | ||||
|             pageSize: page.pageSize, | ||||
|             ...formValues, | ||||
|           }); | ||||
|         }, | ||||
|       }, | ||||
|     }, | ||||
|     rowConfig: { | ||||
|       keyField: 'id', | ||||
|       isCurrent: true, | ||||
|     }, | ||||
|     toolbarConfig: { | ||||
|       refresh: { code: 'query' }, | ||||
|       search: true, | ||||
|     }, | ||||
|   } as VxeTableGridOptions<SystemDictTypeApi.DictType>, | ||||
|   gridEvents, | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div class="h-full"> | ||||
|     <TypeFormModal @success="onRefresh" /> | ||||
| 
 | ||||
|     <Grid table-title="字典类型列表"> | ||||
|       <template #toolbar-tools> | ||||
|         <ElButton | ||||
|           type="primary" | ||||
|           @click="onCreate" | ||||
|           v-access:code="['system:dict:create']" | ||||
|         > | ||||
|           <Plus class="size-5" /> | ||||
|           {{ $t('ui.actionTitle.create', ['字典类型']) }} | ||||
|         </ElButton> | ||||
|         <ElButton | ||||
|           type="primary" | ||||
|           class="ml-2" | ||||
|           @click="onExport" | ||||
|           v-access:code="['system:dict:export']" | ||||
|         > | ||||
|           <Download class="size-5" /> | ||||
|           {{ $t('ui.actionTitle.export') }} | ||||
|         </ElButton> | ||||
|       </template> | ||||
|     </Grid> | ||||
|   </div> | ||||
| </template> | ||||
		Loading…
	
		Reference in New Issue
	
	 puhui999
						puhui999