Kaynağa Gözat

feat: 对接字典接口

dao qin 11 ay önce
ebeveyn
işleme
7be7065eab

+ 14 - 1
src/api/dict/index.ts

@@ -150,7 +150,7 @@ export interface DictDetailRes {
  * @returns
  */
 export function queryDictTypeDetail(dictCode: string) {
-  return http.request<DictDetailRes>({ url: `/admin/dict/queryDictTypeDetail?dictCode=${dictCode}`, method: 'Post' });
+  return http.request<DictDetailRes>({ url: `/admin/dict/queryDictTypeDetail?dictCode=${dictCode}`, method: 'GET' });
 }
 
 /** 删除字典 */
@@ -172,6 +172,7 @@ export interface SaveDictRes {
 
 // Parameter interface
 export interface SaveDictParams {
+  dictId?: number;
   /*字典code */
   dictCode: string;
 
@@ -277,3 +278,15 @@ export interface UpdateDictParams {
 export function updateDict(params: UpdateDictParams): Promise<null> {
   return http.request({ url: `/admin/dict/updateDict`, data: params, method: 'POST' });
 }
+
+export const uploadPresetImageApi = (file: Blob, bizType: string) => {
+  const formData = new FormData();
+  formData.append('bizType', bizType);
+  formData.append('file', file);
+  return http.request({
+    url: `/admin/minio/uploadFile`,
+    method: 'post',
+    data: formData,
+    headers: { 'Content-Type': 'multipart/form-data' },
+  });
+};

+ 349 - 263
src/views/system/dictionary/components/AddDict.vue

@@ -9,81 +9,85 @@
         <el-input v-model="formData.dictCode" />
       </el-form-item>
 
-      <el-form-item label="字典分类" prop="dictCategoryType">
-        <el-select v-model="formData.dictCategoryType" placeholder="请选择">
-          <!-- <el-option label="分类1" value="category1"></el-option> -->
-          <!-- <el-option label="分类2" value="category2"></el-option> -->
+      <el-form-item label="字典分类" prop="dictType">
+        <el-select v-model="formData.dictType" placeholder="请选择">
+          <el-option v-for="(item, index) in dictionaryTypeOptions" :key="index" :label="item.label"
+            :value="item.value" />
         </el-select>
       </el-form-item>
       <div class="subDictWrapper">
         <div class="el-form-item__label" style="width: 100px">字典项</div>
         <div class="subDictList">
           <div>
-            <div v-for="(item, index) in formData.dictItems" :key="index" class="dict-item-group">
+            <div v-for="(item, index) in formData.sysDictDataList" :key="index" class="dict-item-group">
+              <!-- 移动按钮 -->
               <div class="dict-item-header">
-                <el-button v-if="formData.dictItems.length > 1" type="danger" link @click="removeDictItem(index)">
-                  <el-icon><Delete /></el-icon> 删除
-                </el-button>
+                <div class="move-buttons">
+                  <el-button v-if="index > 0" type="primary" link @click="moveDictItem(index, 'up')">
+                    <el-icon>
+                      <Top />
+                    </el-icon>
+                  </el-button>
+                  <el-button v-if="index < formData.sysDictDataList.length - 1" type="primary" link
+                    @click="moveDictItem(index, 'down')">
+                    <el-icon>
+                      <Bottom />
+                    </el-icon>
+                  </el-button>
+                  <el-button v-if="formData.sysDictDataList.length > 1" type="danger" link
+                    @click="removeDictItem(index)">
+                    <el-icon>
+                      <Delete />
+                    </el-icon>删除
+                  </el-button>
+                </div>
               </div>
-              <el-form-item
-                :label="`字典项值`"
-                :prop="`dictItems.${index}.value`"
-                :rules="[{ required: true, message: '请输入字典项值', trigger: 'blur' }]"
-              >
-                <el-input v-model="item.value" placeholder="请输入" />
-              </el-form-item>
-              <el-form-item
-                :label="`字典项编码`"
-                :prop="`dictItems.${index}.code`"
-                :rules="[{ required: true, message: '请输入字典项编码', trigger: 'blur' }]"
-              >
-                <el-input v-model="item.code" placeholder="请输入" />
+
+              <el-form-item :label="`字典项值`" :prop="`sysDictDataList.${index}.itemValue`"
+                :rules="[{ required: true, message: '请输入字典项值', trigger: 'blur' }]">
+                <el-input v-model="item.itemValue" placeholder="请输入" />
               </el-form-item>
-              <el-form-item
-                :label="`显示排序`"
-                :prop="`dictItems.${index}.sortOrder`"
-                :rules="[{ required: true, message: '请输入显示排序', trigger: 'blur' }]"
-              >
-                <el-input-number v-model="item.sortOrder" :min="1" placeholder="请输入" />
+              <el-form-item :label="`字典项编码`" :prop="`sysDictDataList.${index}.itemCode`"
+                :rules="[{ required: true, message: '请输入字典项编码', trigger: 'blur' }]">
+                <el-input v-model="item.itemCode" placeholder="请输入" />
               </el-form-item>
               <el-form-item label="图标">
-                <el-upload
-                  action="#"
-                  list-type="picture-card"
-                  :auto-upload="false"
+                <el-upload 
+                  auto-upload="false"
+                  :action="actionUrl" 
+                  list-type="picture-card" 
                   :limit="1"
-                  :on-preview="(file) => handlePictureCardPreview(file, index)"
-                  :on-remove="() => handleRemove(index)"
+                  :on-preview="(file) => handlePictureCardPreview(file, index)" :on-remove="() => handleRemove(index)"
                   :on-change="(file, fileList) => handleChange(file, fileList, index)"
-                >
-                  <el-icon><Plus /></el-icon>
+
+                  >
+                  <el-icon>
+                    <Plus />
+                  </el-icon>
                 </el-upload>
-              </el-form-item> </div
-          ></div>
+              </el-form-item>
+            </div>
+          </div>
         </div>
       </div>
 
       <el-form-item>
         <el-button type="primary" link @click="addDictItem">
-          <el-icon><CirclePlus /></el-icon> 新增字典分类
+          <el-icon>
+            <CirclePlus />
+          </el-icon> 新增字典分类
         </el-button>
       </el-form-item>
 
       <el-form-item label="字典描述" prop="description">
-        <el-input
-          v-model="formData.description"
-          type="textarea"
-          :rows="3"
-          placeholder="请描述灾害处置过程, 不超过1000字"
-          maxlength="1000"
-          show-word-limit
-        />
+        <el-input v-model="formData.description" type="textarea" :rows="3" placeholder="请描述灾害处置过程, 不超过1000字"
+          maxlength="1000" show-word-limit />
       </el-form-item>
 
       <el-form-item label="状态" prop="status">
         <el-radio-group v-model="formData.status">
-          <el-radio-button label="enabled">启用</el-radio-button>
-          <el-radio-button label="disabled">停用</el-radio-button>
+          <el-radio-button v-for="item in dictionaryStatusOptions" :key="item.value" :value="item.value">{{ item.label
+            }}</el-radio-button>
         </el-radio-group>
       </el-form-item>
 
@@ -95,254 +99,336 @@
   </el-drawer>
 
   <el-dialog v-model="dialogVisible">
-    <img w-full :src="dialogImageUrl" alt="Preview Image" />
+    <div class="dialog-content">
+      <img :src="dialogImageUrl" alt="Preview Image" />
+    </div>
   </el-dialog>
 </template>
 
 <script lang="ts" setup>
-  import { ref, reactive, PropType, watch } from 'vue';
-  import {
-    ElForm,
-    ElFormItem,
-    ElInput,
-    ElSelect,
-    ElOption,
-    ElButton,
-    ElRadioGroup,
-    ElRadioButton,
-    ElInputNumber,
-    ElUpload,
-    ElIcon,
-    ElDialog,
-    ElDivider,
-    FormInstance,
-    FormRules,
-    UploadProps,
-    UploadUserFile,
-  } from 'element-plus';
-  import { Plus, Delete, CirclePlus, Minus, Picture } from '@element-plus/icons-vue';
-
-  interface DictItem {
-    id?: string | number; // 可选,用于编辑时
-    value: string;
-    code: string;
-    sortOrder: number | undefined;
-    iconFile?: UploadUserFile | null; // 用于存储上传的文件对象
-    iconUrl?: string; // 用于显示已上传的图片
-  }
+import { ref, reactive, PropType, watch, computed } from 'vue';
+import {
+  ElForm,
+  ElFormItem,
+  ElInput,
+  ElSelect,
+  ElOption,
+  ElButton,
+  ElRadioGroup,
+  ElRadioButton,
+  ElInputNumber,
+  ElUpload,
+  ElIcon,
+  ElDialog,
+  ElDivider,
+  FormInstance,
+  FormRules,
+  UploadProps,
+  UploadUserFile,
+} from 'element-plus';
+import { Plus, Delete, CirclePlus, Minus, Picture, Top, Bottom } from '@element-plus/icons-vue';
+import { dictionaryTypeOptions, DictionaryStatus, dictionaryStatusOptions } from '../constants';
+import { queryDictTypeDetail, uploadPresetImageApi } from '@/api/dict'
+import { getHeaders } from '@/utils/http/axios';
+import { useGlobSetting } from '@/hooks/setting';
+import urlJoin from 'url-join';
 
-  interface FormData {
-    id?: string | number; // 可选,用于编辑时
-    dictName: string;
-    dictCode: string;
-    dictCategoryType: string;
-    dictItems: DictItem[];
-    description: string;
-    status: 'enabled' | 'disabled';
-  }
+interface SysDictDataItem {
+  id?: string | number; // 可选,用于编辑时
+  dictId: string | number | undefined; // 关联的字典ID
+  dictCode: string;
+  itemValue: string;
+  itemCode: string;
+  itemSort: number | undefined;
+  isDefault: 0 | 1,
+  imageUrl?: string; // 用于显示已上传的图片
+  status: DictionaryStatus.disabled | DictionaryStatus.enabled;
+}
 
-  const props = defineProps({
-    initialData: {
-      type: Object as PropType<Partial<FormData>>,
-      default: () => ({}),
-    },
-    // 可根据需要添加其他 props,例如区分是新增还是编辑模式
-    isEditMode: {
-      type: Boolean,
-      default: false,
-    },
-  });
+interface FormData {
+  dictId?: string | number; // 可选,用于编辑时
+  dictName: string;
+  dictCode: string;
+  dictType: string;
+  sysDictDataList: SysDictDataItem[];
+  description: string;
+  status: DictionaryStatus.disabled | DictionaryStatus.enabled;
+}
 
-  const emit = defineEmits(['submit', 'close']);
+const props = defineProps({
+  dictCode: {
+    type: String,
+    default: '',
+  }
+});
 
-  const handleClose = () => {
-    emit('close');
-  };
+const { urlPrefix } = useGlobSetting();
+const actionUrl = computed(() => {
+  return urlJoin(urlPrefix!, `/admin/minio/uploadFile`);
+});
 
-  const formRef = ref<FormInstance>();
-  const formData = reactive<FormData>({
-    dictName: '',
-    dictCode: '',
-    dictCategoryType: '',
-    dictItems: [
-      {
-        value: '',
-        code: '',
-        sortOrder: 5, // 默认排序值
-        iconFile: null,
-      },
-    ],
-    description: '',
-    status: 'enabled',
-    ...props.initialData, // 使用 initialData 初始化表单
-  });
+const emit = defineEmits(['submit', 'close']);
+const handleClose = () => {
+  emit('close');
+};
 
-  // 监听 initialData 的变化,用于编辑时填充表单
-  watch(
-    () => props.initialData,
-    (newData) => {
-      if (newData) {
-        Object.assign(formData, {
-          ...formData, // 保留现有响应式链接
-          ...newData,
-          dictItems: newData.dictItems
-            ? newData.dictItems.map((item) => ({ ...item }))
-            : [{ value: '', code: '', sortOrder: 5, iconFile: null }],
-        });
-      }
+const formRef = ref<FormInstance>();
+const formData = reactive<FormData>({
+  dictName: '',
+  dictCode: '',
+  dictType: '',
+  sysDictDataList: [
+    {
+      id: undefined,
+      dictId: undefined,
+      dictCode: '',
+      itemCode: '',
+      itemValue: '',
+      itemSort: 1, // 默认排序值
+      // iconFile: null,
+      isDefault: 0,
+      status: DictionaryStatus.disabled,
+      imageUrl: '',
     },
-    { deep: true, immediate: true },
+  ],
+  description: '',
+  status: DictionaryStatus.disabled,
+  ...props.initialData, // 使用 initialData 初始化表单
+});
+
+watch(() => props.dictCode, (newData) => {
+ if (newData) {
+   queryDictTypeDetail(newData).then((res) => {
+    Object.assign(formData, {
+      ...res,
+      // 单独处理字典项数组保持响应式更新
+      sysDictDataList: res.sysDictDataList || []
+    });
+  })
+ }
+}, { deep: true, immediate: true })
+
+const formRules = reactive<FormRules<FormData>>({
+  dictName: [{ required: true, message: '请选择字典名称', trigger: 'change' }],
+  dictCode: [{ required: true, message: '请选择字典编码', trigger: 'change' }],
+  dictType: [{ required: true, message: '请选择字典分类', trigger: 'change' }],
+  description: [{ max: 1000, message: '描述不超过1000字', trigger: 'blur' }],
+  status: [{ required: true, message: '请选择状态', trigger: 'change' }],
+});
+
+const addDictItem = () => {
+  formData.sysDictDataList.push({
+    id: undefined,
+    dictId: formData.dictId,
+    dictCode: formData.dictCode,
+    itemCode: '',
+    itemValue: '',
+    itemSort: 1, // 默认排序值
+    isDefault: 0,
+    status: DictionaryStatus.disabled,
+    imageUrl: '',
+  },
   );
+};
 
-  const formRules = reactive<FormRules<FormData>>({
-    dictName: [{ required: true, message: '请选择字典名称', trigger: 'change' }],
-    dictCode: [{ required: true, message: '请选择字典编码', trigger: 'change' }],
-    dictCategoryType: [{ required: true, message: '请选择字典分类', trigger: 'change' }],
-    description: [{ max: 1000, message: '描述不超过1000字', trigger: 'blur' }],
-    status: [{ required: true, message: '请选择状态', trigger: 'change' }],
-  });
+const removeDictItem = (index: number) => {
+  formData.sysDictDataList.splice(index, 1);
+};
 
-  const addDictItem = () => {
-    formData.dictItems.push({
-      value: '',
-      code: '',
-      sortOrder: 5,
-      iconFile: null,
-    });
-  };
+// 图片上传相关
+const dialogImageUrl = ref('');
+const dialogVisible = ref(false);
+
+const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
+  dialogImageUrl.value = uploadFile.url!;
+  dialogVisible.value = true;
+};
+
+const handleRemove = (itemIndex: number) => {
+  if (formData.sysDictDataList[itemIndex]) {
+    // formData.sysDictDataList[itemIndex].iconFile = null;
+    formData.sysDictDataList[itemIndex].imageUrl = ''; // 如果有单独的 URL 字段
+  }
+};
 
-  const removeDictItem = (index: number) => {
-    formData.dictItems.splice(index, 1);
-  };
+const handleChange = (uploadFile: UploadUserFile, uploadFiles: UploadUserFile[], itemIndex: number) => {
+  // Element Plus 的 onChange 会在文件状态改变时触发多次,通常在 ready 状态时文件已可选
+  // 这里简单地将文件对象存起来,实际上传应在 handleSubmit 中处理
+  if (formData.sysDictDataList[itemIndex]) {
+    // formData.sysDictDataList[itemIndex].iconFile = uploadFile;
+    // console.log('uploadFile:', uploadFile);
+    // uploadPresetImageApi(uploadFile.raw, 'CAMERA_IMAGE').then((res) => {
+    //   // formData.sysDictDataList[itemIndex].imageUrl = res.data;
+    // })
+  }
+};
 
-  // 图片上传相关
-  const dialogImageUrl = ref('');
-  const dialogVisible = ref(false);
+const handleUpload = (res: any) => {
+  console.log('res:', res)
+};
 
-  const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
-    dialogImageUrl.value = uploadFile.url!;
-    dialogVisible.value = true;
-  };
+const handleSubmit = async () => {
+  if (!formRef.value) return;
 
-  const handleRemove = (itemIndex: number) => {
-    if (formData.dictItems[itemIndex]) {
-      formData.dictItems[itemIndex].iconFile = null;
-      formData.dictItems[itemIndex].iconUrl = ''; // 如果有单独的 URL 字段
-    }
-  };
+  await formRef.value.validate((valid) => {
+    if (valid) {
+      // 在这里处理实际的文件上传逻辑,例如使用 FormData
+      // const submissionData = new FormData();
+      // Object.keys(formData).forEach(key => {
+      //   if (key === 'sysDictDataList') {
+      //     formData.sysDictDataList.forEach((item, index) => {
+      //       submissionData.append(`sysDictDataList[${index}][value]`, item.value);
+      //       submissionData.append(`sysDictDataList[${index}][code]`, item.code);
+      //       submissionData.append(`sysDictDataList[${index}][sortOrder]`, String(item.sortOrder));
+      //       if (item.iconFile && item.iconFile.raw) {
+      //         submissionData.append(`sysDictDataList[${index}][icon]`, item.iconFile.raw);
+      //       }
+      //     });
+      //   } else {
+      //     submissionData.append(key, formData[key as keyof Omit<FormData, 'sysDictDataList'>]);
+      //   }
+      // });
+      // emit('submit', submissionData);
+      formData.sysDictDataList.forEach(item => {
+        item.dictCode = formData.dictCode;
+      });
+      
+      emit('submit', JSON.parse(JSON.stringify(formData))); // 暂时发送原始数据,上传需单独处理
+    } else {
 
-  const handleChange = (uploadFile: UploadUserFile, uploadFiles: UploadUserFile[], itemIndex: number) => {
-    // Element Plus 的 onChange 会在文件状态改变时触发多次,通常在 ready 状态时文件已可选
-    // 这里简单地将文件对象存起来,实际上传应在 handleSubmit 中处理
-    if (formData.dictItems[itemIndex]) {
-      formData.dictItems[itemIndex].iconFile = uploadFile;
+      return false;
     }
-  };
-
-  const handleSubmit = async () => {
-    if (!formRef.value) return;
-    await formRef.value.validate((valid) => {
-      if (valid) {
-        // 在这里处理实际的文件上传逻辑,例如使用 FormData
-        // const submissionData = new FormData();
-        // Object.keys(formData).forEach(key => {
-        //   if (key === 'dictItems') {
-        //     formData.dictItems.forEach((item, index) => {
-        //       submissionData.append(`dictItems[${index}][value]`, item.value);
-        //       submissionData.append(`dictItems[${index}][code]`, item.code);
-        //       submissionData.append(`dictItems[${index}][sortOrder]`, String(item.sortOrder));
-        //       if (item.iconFile && item.iconFile.raw) {
-        //         submissionData.append(`dictItems[${index}][icon]`, item.iconFile.raw);
-        //       }
-        //     });
-        //   } else {
-        //     submissionData.append(key, formData[key as keyof Omit<FormData, 'dictItems'>]);
-        //   }
-        // });
-        // emit('submit', submissionData);
-        emit('submit', JSON.parse(JSON.stringify(formData))); // 暂时发送原始数据,上传需单独处理
-      } else {
-        console.log('error submit!');
-        return false;
-      }
-    });
-  };
-
-  const handleCancel = () => {
-    emit('close');
-  };
-
-  // 暴露方法给父组件,例如重置表单
-  defineExpose({
-    resetForm: () => {
-      formRef.value?.resetFields();
-      formData.dictItems = [
-        {
-          value: '',
-          code: '',
-          sortOrder: 5,
-          iconFile: null,
-        },
-      ];
-    },
-    getFormData: () => formData, // 允许父组件获取当前表单数据
   });
+};
+
+// 移动方法
+const moveDictItem = (index: number, direction: 'up' | 'down') => {
+  const items = formData.sysDictDataList;
+  if (direction === 'up' && index > 0) {
+    // 交换位置
+    [items[index - 1], items[index]] = [items[index], items[index - 1]];
+  } else if (direction === 'down' && index < items.length - 1) {
+    // 交换位置
+    [items[index + 1], items[index]] = [items[index], items[index + 1]];
+  }
+  
+  // 根据最新位置重新设置排序值
+  items.forEach((item, idx) => {
+    item.itemSort = idx + 1;
+  });
+};
+              
+const handleCancel = () => {
+  emit('close');
+};
+
+// 暴露方法给父组件,例如重置表单
+defineExpose({
+  resetForm: () => {
+    formRef.value?.resetFields();
+    formData.sysDictDataList = [
+      {
+        id: undefined,
+        dictId: undefined,
+        dictCode: '',
+        itemCode: '',
+        itemValue: '',
+        itemSort: 1, // 默认排序值
+        isDefault: 0,
+        status: DictionaryStatus.disabled,
+        imageUrl: '',
+      },
+    ];
+    
+  },
+  getFormData: () => formData, // 允许父组件获取当前表单数据
+});
 </script>
 
 <style lang="scss" scoped>
-  .add-dict-form {
-    .el-select,
-    .el-input-number {
-      width: 100%;
-    }
+.add-dict-form {
+
+  .el-select,
+  .el-input-number {
+    width: 100%;
   }
+}
+
+.dict-item-group {
+  padding: 15px;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  margin-bottom: 20px;
+  position: relative;
+
+  .dict-item-header {
+    display: flex;
+    justify-content: flex-end;
+    align-items: center;
+    margin-bottom: 10px;
 
-  .dict-item-group {
-    padding: 15px;
-    border: 1px solid #dcdfe6;
-    border-radius: 4px;
-    margin-bottom: 20px;
-    position: relative;
-
-    .dict-item-header {
-      display: flex;
-      justify-content: flex-end;
-      align-items: center;
-      margin-bottom: 10px;
-
-      h4 {
-        margin: 0;
-        font-size: 16px;
-      }
+    h4 {
+      margin: 0;
+      font-size: 16px;
     }
   }
+}
+
+.form-footer {
+  text-align: right;
+  margin-top: 20px;
+}
+
+// 覆盖 el-upload 的样式,使其适应小图标场景
+:deep(.el-upload--picture-card) {
+  width: 100px;
+  height: 100px;
+  line-height: 110px;
+}
 
-  .form-footer {
+:deep(.el-upload-list--picture-card .el-upload-list__item) {
+  width: 100px;
+  height: 100px;
+}
+
+.subDictWrapper {
+  display: flex;
+
+  .dictItemsLabel {
+    width: 30%;
+    font-size: 14px;
+    color: #606266;
     text-align: right;
-    margin-top: 20px;
+    margin-right: 26px;
   }
 
-  // 覆盖 el-upload 的样式,使其适应小图标场景
-  :deep(.el-upload--picture-card) {
-    width: 100px;
-    height: 100px;
-    line-height: 110px;
-  }
-  :deep(.el-upload-list--picture-card .el-upload-list__item) {
-    width: 100px;
-    height: 100px;
+  .subDictList {
+    flex: 1;
   }
-  .subDictWrapper {
-    display: flex;
-    .dictItemsLabel {
-      width: 30%;
-      font-size: 14px;
-      color: #606266;
-      text-align: right;
-      margin-right: 26px;
-    }
-    .subDictList {
-      flex: 1;
+}
+
+.dict-item-header {
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+  margin-bottom: 10px;
+  
+  .move-buttons {
+    margin-right: auto;
+    .el-button {
+      padding: 5px;
+      margin-right: 8px;
     }
   }
+}
+
+.dialog-content {
+  width: 100%;
+  height: 100%;
+
+  img {
+    width: 100%;
+    height: 100%;
+  }
+}
 </style>

+ 38 - 0
src/views/system/dictionary/constants.ts

@@ -0,0 +1,38 @@
+export const dictionaryTypeOptions = [
+  {
+    value: 1,
+    label: '灾害防护'
+  },
+  {
+    value: 2,
+    label: '应急管理'
+  },
+  {
+    value: 3,
+    label: '交通安全'
+  },
+  {
+    value: 4,
+    label: '保卫保密'
+  },
+  {
+    value: 5,
+    label: '生产安全'
+  },
+] 
+
+export enum DictionaryStatus {
+  disabled = 0,
+  enabled = 1,
+}
+
+export const dictionaryStatusOptions = [
+  {
+    value: DictionaryStatus.disabled,
+    label: '禁用'
+  },
+  {
+    value: DictionaryStatus.enabled,
+    label: '启用'
+  },
+]

+ 64 - 49
src/views/system/dictionary/dictionary.vue

@@ -4,13 +4,21 @@
       <el-button type="primary" @click="handleAddDialogShow">新增字典</el-button>
     </div>
 
-    <el-table v-loading="loading" :data="dataSource" style="width: 100%" border>
+    <el-table v-loading="loading" height="calc(100vh - 220px)" :data="dataSource" style="width: 100%" border>
       <!-- <el-table-column prop="id" label="序号" /> -->
       <el-table-column prop="dictName" label="字典名称" />
       <el-table-column prop="dictCode" label="字典编码" />
       <el-table-column prop="description" label="字典描述" show-overflow-tooltip />
-      <el-table-column prop="dictType" label="分类" width="180" />
-      <el-table-column prop="status" label="状态" width="180" />
+      <el-table-column prop="dictType" label="分类" width="180">
+        <template #default="{ row }">
+          {{ typeLabelMap[row.dictType] || '' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="status" label="状态" width="180">
+        <template #default="{ row }">
+          {{ statusLabelMap[row.status] || '' }}
+        </template>
+      </el-table-column>
       <el-table-column label="操作" width="200" fixed="right">
         <template #default="{ row }">
           <el-button type="primary" link @click="handleEdit(row)">编辑</el-button>
@@ -20,28 +28,24 @@
     </el-table>
 
     <div class="pagination-container">
-      <el-pagination
-        v-model:current-page="currentPage"
-        v-model:page-size="pageSize"
-        :page-sizes="[10, 20, 50, 100]"
-        :total="totalPage"
-        layout="total, sizes, prev, pager, next, jumper"
-        @size-change="handleSizeChange"
-        @current-change="handleCurrentChange"
-      />
+      <el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[10, 20, 50, 100]"
+        :total="totalPage" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange"
+        @current-change="handleCurrentChange" />
     </div>
 
-    <AddDict v-if="dialogVisible" @close="closeAddDialog" />
-    <EditDict />
+    <AddDict v-if="dialogVisible" ref="addDictRef" @close="closeAddDialog" @submit="handleSubmit"
+      :dictCode="currentDictCode" />
   </div>
 </template>
 
 <script lang="ts" setup>
-  import { ref, reactive, onMounted } from 'vue';
+  import { ref, reactive, onMounted, computed } from 'vue';
   import { ElMessage, ElMessageBox, FormInstance } from 'element-plus';
   import AddDict from './components/AddDict.vue';
-  import { queryDictPageApi } from '@/api/dict';
+  import { queryDictPageApi, createDictApi, SaveDictParams, updateDict, deleteDict } from '@/api/dict';
   import { useDataSource } from './useDataSource';
+  import { dictionaryStatusOptions, dictionaryTypeOptions } from './constants'
+  
   // 表格数据
   const loading = ref(false);
 
@@ -70,12 +74,8 @@
   // 表单相关
   const dialogVisible = ref(false);
   const dialogTitle = ref('新增字典');
-  const formRef = ref<FormInstance>();
-  const formData = reactive({
-    name: '',
-    code: '',
-    description: '',
-  });
+  const addDictRef = ref<InstanceType<typeof AddDict>>();
+  const currentDictCode = ref('');
 
   const formRules = {
     name: [
@@ -91,20 +91,21 @@
   // 新增字典
   function handleAddDialogShow() {
     dialogVisible.value = true;
+    currentDictCode.value = '';
   }
 
   const closeAddDialog = () => {
     dialogVisible.value = false;
+    if (addDictRef.value) {
+      addDictRef.value.resetForm();
+    }
   };
 
   // 编辑字典
   function handleEdit(row: any) {
     dialogTitle.value = '编辑字典';
     dialogVisible.value = true;
-    // 填充表单数据
-    Object.assign(formData, {
-      ...row,
-    });
+    currentDictCode.value = row.dictCode;
   }
 
   // 删除字典
@@ -113,37 +114,51 @@
       await ElMessageBox.confirm('确定要删除这条字典记录吗?', '提示', {
         type: 'warning',
       });
-      // TODO: 实现删除API调用
-      // await deleteDictionary(row.id)
-      ElMessage.success('删除成功');
-      loadTableData();
+      await deleteDictionary(row.dictId)
     } catch {
       // 用户取消删除
     }
   }
 
   // 提交表单
-  async function handleSubmit() {
-    if (!formRef.value) return;
-
-    await formRef.value.validate(async (valid) => {
-      if (valid) {
-        try {
-          // TODO: 实现保存API调用
-          // if (formData.id) {
-          //   await updateDictionary(formData)
-          // } else {
-          //   await createDictionary(formData)
-          // }
-          ElMessage.success('保存成功');
-          dialogVisible.value = false;
-          loadTableData();
-        } catch (error) {
-          ElMessage.error('保存失败');
-        }
-      }
+  async function handleSubmit(formData: SaveDictParams) {
+    if (!addDictRef.value) return;
+    if (formData.dictId) {
+      await updateDictionary(formData)
+    } else {
+      await createDictionary(formData)
+    }
+    dialogVisible.value = false;
+    getDataSource();
+  }
+
+  const createDictionary = (formData: SaveDictParams) => {
+    createDictApi(formData).then((res) => {
+      ElMessage.success('保存成功');
     });
+  };
+
+  const updateDictionary = (formData: SaveDictParams) => {
+    updateDict(formData).then((res) => {
+      ElMessage.success('编辑成功');
+    })
   }
+
+  const deleteDictionary = (id: number) => {
+    deleteDict(id).then((res) => {
+      ElMessage.success('删除成功');
+      getDataSource();
+    })
+  }
+
+  const typeLabelMap = computed(() => 
+    Object.fromEntries(dictionaryTypeOptions.map(item => [item.value, item.label]))
+  );
+
+  const statusLabelMap = computed(() =>
+    Object.fromEntries(dictionaryStatusOptions.map(item => [item.value, item.label]))
+  );
+    
 </script>
 
 <style lang="scss" scoped>