소스 검색

字典管理

zouzs 3 주 전
부모
커밋
4f80160f99

+ 20 - 0
src/api/system/dept.ts

@@ -7,6 +7,10 @@ export interface BasicResponseModel<T = any> {
   data: T;
 }
 
+/**
+ * 获取部门列表
+ * @param query
+ */
 export const getSystemDeptList = (query?: object) => {
   return http.request<BasicResponseModel>(
     "get",
@@ -15,18 +19,30 @@ export const getSystemDeptList = (query?: object) => {
   );
 };
 
+/**
+ * 添加部门
+ * @param query
+ */
 export const addDept = (query?: object) => {
   return http.request<BasicResponseModel>("post", baseUrlApi("system/dept"), {
     data: query
   });
 };
 
+/**
+ * 修改部门
+ * @param query
+ */
 export const updateDept = (query?: object) => {
   return http.request<BasicResponseModel>("put", baseUrlApi("system/dept"), {
     data: query
   });
 };
 
+/**
+ * 删除部门
+ * @param id
+ */
 export const deleteDept = (id: number) => {
   return http.request<BasicResponseModel>(
     "delete",
@@ -34,6 +50,10 @@ export const deleteDept = (id: number) => {
   );
 };
 
+/**
+ * 根据ID获取部门详细信息
+ * @param id
+ */
 export const getDept = (id: number) => {
   return http.request<BasicResponseModel>(
     "get",

+ 137 - 0
src/api/system/dict.ts

@@ -0,0 +1,137 @@
+import { http } from "@/utils/http";
+import { baseUrlApi } from "../utils";
+
+export interface DictList<T = any> {
+  code: number;
+  msg: string;
+  rows: T;
+  total?: number;
+}
+
+export interface BasicResponseModel<T = any> {
+  code: number;
+  msg: string;
+  data: T;
+}
+
+/**
+ * 获取字典类型列表
+ * @param query
+ */
+export const getSystemDictList = (query?: object) => {
+  return http.request<DictList>("get", baseUrlApi("system/dict/type/list"), {
+    params: query
+  });
+};
+
+/**
+ * 添加字典类型
+ * @param data
+ */
+export const addSystemDictType = (data: object) => {
+  return http.request<BasicResponseModel>(
+    "post",
+    baseUrlApi("system/dict/type"),
+    { data }
+  );
+};
+
+/**
+ * 修改字典类型
+ * @param data
+ */
+export const updateSystemDictType = (data: object) => {
+  return http.request<BasicResponseModel>(
+    "put",
+    baseUrlApi("system/dict/type"),
+    { data }
+  );
+};
+
+/**
+ * 删除字典类型
+ * @param id
+ */
+export const deleteSystemDictType = (id: string) => {
+  return http.request<BasicResponseModel>(
+    "delete",
+    baseUrlApi(`system/dict/type/${id}`)
+  );
+};
+
+/**
+ * 根据ID获取字典类型详细信息
+ * @param id
+ */
+export const getSystemDictById = (id: string) => {
+  return http.request<BasicResponseModel>(
+    "get",
+    baseUrlApi(`system/dict/type/${id}`)
+  );
+};
+
+/**
+ * 获取字典名称下拉框
+ */
+export const getDictTypeOptionSelect = () => {
+  return http.request<BasicResponseModel>(
+    "get",
+    baseUrlApi("system/dict/type/optionselect")
+  );
+};
+
+/**
+ * 获取字典数据列表
+ * @param query
+ */
+export const getSystemDictDataList = (query?: object) => {
+  return http.request<DictList>("get", baseUrlApi("system/dict/data/list"), {
+    params: query
+  });
+};
+
+/**
+ * 添加字典数据
+ * @param data
+ */
+export const addSystemDictData = (data: object) => {
+  return http.request<BasicResponseModel>(
+    "post",
+    baseUrlApi("system/dict/data"),
+    { data }
+  );
+};
+
+/**
+ * 修改字典数据
+ * @param data
+ */
+export const updateSystemDictData = (data: object) => {
+  return http.request<BasicResponseModel>(
+    "put",
+    baseUrlApi("system/dict/data"),
+    { data }
+  );
+};
+
+/**
+ * 删除字典数据
+ * @param id
+ */
+export const deleteSystemDictData = (id: string) => {
+  return http.request<BasicResponseModel>(
+    "delete",
+    baseUrlApi(`system/dict/data/${id}`)
+  );
+};
+
+/**
+ * 根据ID获取字典数据详细信息
+ * @param id
+ */
+export const getSystemDictDataById = (id: string) => {
+  return http.request<BasicResponseModel>(
+    "get",
+    baseUrlApi(`system/dict/data/${id}`)
+  );
+};

+ 33 - 0
src/api/system/menu.ts

@@ -7,6 +7,10 @@ export interface BasicResponseModel<T = any> {
   data: T;
 }
 
+/**
+ * 获取菜单列表
+ * @param query
+ */
 export const getSystemMenuList = (query?: object) => {
   return http.request<BasicResponseModel>(
     "get",
@@ -15,6 +19,10 @@ export const getSystemMenuList = (query?: object) => {
   );
 };
 
+/**
+ * 根据ID获取菜单详细信息
+ * @param id
+ */
 export const getSystemMenuDetailById = (id: number) => {
   return http.request<BasicResponseModel>(
     "get",
@@ -22,16 +30,28 @@ export const getSystemMenuDetailById = (id: number) => {
   );
 };
 
+/**
+ * 添加菜单
+ * @param data
+ */
 export const addMenu = (data?: object) => {
   return http.post<BasicResponseModel, any>(baseUrlApi(`system/menu`), {
     data
   });
 };
 
+/**
+ * 修改菜单
+ * @param data
+ */
 export const updateMenu = (data?: object) => {
   return http.put<BasicResponseModel, any>(baseUrlApi(`system/menu`), { data });
 };
 
+/**
+ * 删除菜单
+ * @param id
+ */
 export const deleteMenu = (id: number) => {
   return http.request<BasicResponseModel>(
     "delete",
@@ -39,6 +59,9 @@ export const deleteMenu = (id: number) => {
   );
 };
 
+/**
+ * 获取业务系统列表
+ */
 export const getSystemList = () => {
   return http.request<BasicResponseModel>(
     "get",
@@ -46,6 +69,9 @@ export const getSystemList = () => {
   );
 };
 
+/**
+ * 获取部门树结构
+ */
 export interface DeptTreeModel<T = any> {
   code: number;
   msg: string;
@@ -53,6 +79,9 @@ export interface DeptTreeModel<T = any> {
   checkedKeys: number[];
 }
 
+/**
+ * 获取菜单树结构
+ */
 export const getMenuListTree = () => {
   return http.request<BasicResponseModel>(
     "get",
@@ -60,6 +89,10 @@ export const getMenuListTree = () => {
   );
 };
 
+/**
+ * 获取角色对应菜单树结构
+ * @param id
+ */
 export const getMenuListTreeSelect = (id?: string) => {
   return http.request<DeptTreeModel>(
     "get",

+ 36 - 0
src/api/system/role.ts

@@ -21,6 +21,10 @@ export interface DeptTreeModel<T = any> {
   checkedKeys: number[];
 }
 
+/**
+ * 获取角色列表
+ * @param query
+ */
 export const getSystemRoleList = (query?: object) => {
   return http.request<BasicPageResponseModel>(
     "post",
@@ -29,6 +33,11 @@ export const getSystemRoleList = (query?: object) => {
   );
 };
 
+/**
+ * 修改角色状态
+ * @param roleId
+ * @param status
+ */
 export const changeRoleStatus = (roleId: number, status: string) => {
   const data = {
     roleId,
@@ -41,6 +50,10 @@ export const changeRoleStatus = (roleId: number, status: string) => {
   );
 };
 
+/**
+ * 获取角色部门树
+ * @param id
+ */
 export const getSystemRoleDeptTree = (id?: string) => {
   return http.request<DeptTreeModel>(
     "get",
@@ -48,18 +61,30 @@ export const getSystemRoleDeptTree = (id?: string) => {
   );
 };
 
+/**
+ * 添加角色
+ * @param data
+ */
 export const addSystemRole = (data: object) => {
   return http.request<BasicResponseModel>("post", baseUrlApi("system/role"), {
     data
   });
 };
 
+/**
+ * 修改角色
+ * @param data
+ */
 export const updateSystemRole = (data: object) => {
   return http.request<BasicResponseModel>("put", baseUrlApi("system/role"), {
     data
   });
 };
 
+/**
+ * 删除角色
+ * @param id
+ */
 export const deleteSystemRole = (id: string) => {
   return http.request<BasicResponseModel>(
     "delete",
@@ -67,6 +92,10 @@ export const deleteSystemRole = (id: string) => {
   );
 };
 
+/**
+ * 根据ID获取角色详细信息
+ * @param id
+ */
 export const getSystemRoleById = (id?: string) => {
   return http.request<BasicResponseModel>(
     "get",
@@ -74,6 +103,10 @@ export const getSystemRoleById = (id?: string) => {
   );
 };
 
+/**
+ * 修改角色数据权限
+ * @param data
+ */
 export const putSystemRoleDataScope = (data: object) => {
   return http.request<BasicResponseModel>(
     "put",
@@ -82,6 +115,9 @@ export const putSystemRoleDataScope = (data: object) => {
   );
 };
 
+/**
+ * 获取角色选择框列表
+ */
 export const getSystemRoleOptionselect = () => {
   return http.request<BasicResponseModel>(
     "get",

+ 20 - 0
src/api/system/user.ts

@@ -9,28 +9,48 @@ export interface BasicResponseModel<T = any> {
   total: number;
 }
 
+/**
+ * 获取用户列表
+ * @param query
+ */
 export const getSystemUserList = (query?: object) => {
   return http.post<BasicResponseModel, any>(baseUrlApi("system/user/list"), {
     data: query
   });
 };
 
+/**
+ * 根据ID获取用户详细信息
+ * @param id
+ */
 export const getUserInfoById = (id?: string) => {
   return http.get<BasicResponseModel, any>(baseUrlApi(`system/user/${id}`));
 };
 
+/**
+ * 修改用户
+ * @param data
+ */
 export const editUser = (data?: object) => {
   return http.post<BasicResponseModel, any>(baseUrlApi("system/user/edit"), {
     data
   });
 };
 
+/**
+ * 添加用户
+ * @param data
+ */
 export const addUser = (data?: object) => {
   return http.post<BasicResponseModel, any>(baseUrlApi("system/user/add"), {
     data
   });
 };
 
+/**
+ * 删除用户
+ * @param id
+ */
 export const deleteUser = (id?: string) => {
   return http.post<BasicResponseModel, any>(baseUrlApi("system/user/delete"), {
     data: [id]

+ 19 - 0
src/router/modules/system.ts

@@ -0,0 +1,19 @@
+// 最简代码,也就是这些字段必须有
+export default {
+  path: "/system1",
+  meta: {
+    title: "系统管理",
+    showLink: false
+  },
+  children: [
+    {
+      path: "/system/dict-data/index",
+      name: "DictData",
+      component: () => import("@/views/system/dict/dictData.vue"),
+      meta: {
+        title: "字典数据",
+        showParent: true
+      }
+    }
+  ]
+} as RouteConfigsTable;

+ 411 - 0
src/views/system/dict/dictData.vue

@@ -0,0 +1,411 @@
+<script setup lang="ts">
+import {
+  type PlusColumn,
+  PlusDialogForm,
+  PlusPage,
+  type PlusPageInstance,
+  useTable
+} from "plus-pro-components";
+import {
+  addSystemDictData,
+  deleteSystemDictData,
+  getDictTypeOptionSelect,
+  getSystemDictDataById,
+  getSystemDictDataList,
+  updateSystemDictData
+} from "@/api/system/dict";
+import { computed, onMounted, reactive, ref, toRefs } from "vue";
+import { useRoute, useRouter } from "vue-router";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { cloneDeep } from "lodash-es";
+import { isString } from "@pureadmin/utils";
+import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
+
+defineOptions({
+  name: "DictData"
+});
+
+const route = useRoute();
+const router = useRouter();
+
+const defaultValues = {
+  dictType: isString(route.query.dictType)
+    ? (route.query.dictType as string)
+    : ""
+};
+
+onMounted(() => {});
+
+const plusPageInstance = ref<PlusPageInstance | null>(null);
+
+const getList = async (query: Record<string, any>) => {
+  let res = await getSystemDictDataList(query);
+  return {
+    data: res.rows,
+    total: res.total
+  };
+};
+
+const multipleSelection = ref([]);
+
+const handleSelectionChange = (val: string[]) => {
+  multipleSelection.value = val;
+  console.log(multipleSelection.value);
+};
+
+const handleDelete = () => {
+  // 删除前确认
+  ElMessageBox.confirm("是否确认删除选中数据?", "删除确认", {
+    confirmButtonText: "确认",
+    cancelButtonText: "取消",
+    type: "warning",
+    draggable: true
+  })
+    .then(async () => {
+      try {
+        let dictCodes = multipleSelection.value.map(
+          item => item.dictCode
+        ) as string[];
+        let res = await deleteSystemDictData(dictCodes.join(","));
+        if (res.code === 200) {
+          ElMessage.success("删除成功");
+          refresh();
+          multipleSelection.value = [];
+        } else {
+          ElMessage.error(res.msg);
+        }
+      } catch (e) {
+        ElMessage.error("删除失败");
+      }
+    })
+    .catch(() => {
+      // 取消删除
+    });
+};
+
+// 重新请求列表接口
+const refresh = () => {
+  plusPageInstance.value?.getList();
+};
+
+const dialogTitle = computed(() => (state.isCreate ? "新增" : "编辑"));
+
+const dictNameList = ref<Array<{ label: string; value: string }>>([]);
+
+onMounted(async () => {
+  // 获取字典名称下拉选项
+  let res = await getDictTypeOptionSelect();
+  dictNameList.value = res.data;
+});
+
+const tableConfig: PlusColumn[] = [
+  {
+    label: "字典编码",
+    prop: "dictCode",
+    valueType: "input",
+    width: 120,
+    hideInSearch: true
+  },
+  {
+    label: "字典名称",
+    prop: "dictType",
+    valueType: "select",
+    width: 150,
+    options: computed(() => dictNameList.value),
+    optionsMap: {
+      label: "dictName",
+      value: "dictType"
+    },
+    fieldProps: {
+      clearable: false
+    },
+    hideInTable: true
+  },
+  {
+    label: "字典标签",
+    prop: "dictLabel",
+    valueType: "input"
+  },
+  {
+    label: "字典键值",
+    prop: "dictValue",
+    hideInSearch: true
+  },
+  {
+    label: "字典排序",
+    prop: "dictSort",
+    valueType: "input",
+    fieldProps: {
+      type: "number"
+    },
+    hideInSearch: true
+  },
+  {
+    label: "状态",
+    prop: "status",
+    valueType: "tag",
+    fieldProps: value => ({
+      type: value === "0" ? "primary" : "danger"
+    }),
+    fieldSlots: {
+      default: ({ value }) => (value === "0" ? "正常" : "停用")
+    },
+    hideInSearch: true
+  },
+  {
+    label: "状态",
+    prop: "status",
+    valueType: "select",
+    options: [
+      { label: "正常", value: "0" },
+      { label: "停用", value: "1" }
+    ],
+    hideInTable: true,
+    fieldProps: {
+      clearable: true
+    }
+  },
+  {
+    label: "备注",
+    prop: "remark",
+    valueType: "input",
+    hideInSearch: true
+  },
+  {
+    label: "创建时间",
+    prop: "createTime",
+    valueType: "date-picker",
+    hideInSearch: true,
+    width: 180
+  }
+];
+
+const { buttons } = useTable();
+
+buttons.value = [
+  {
+    // 修改
+    text: "修改",
+    code: "edit",
+    // props v0.1.16 版本新增函数类型
+    props: {
+      type: "primary"
+    },
+    onClick(params) {
+      getSystemDictDataById(params.row.dictCode).then(res => {
+        form.value = res.data;
+        state.isCreate = false;
+        state.dialogVisible = true;
+      });
+    }
+  },
+  {
+    // 删除
+    text: "删除",
+    code: "delete",
+    // props v0.1.16 版本新增计算属性支持
+    props: computed(() => ({ type: "danger" })),
+    confirm: {
+      options: {
+        draggable: true,
+        message: "确定删除此数据吗?"
+      }
+    },
+    onConfirm: async params => {
+      try {
+        let res = await deleteSystemDictData(params.row.dictCode);
+        if (res.code === 200) {
+          ElMessage.success("删除成功");
+          refresh();
+        } else {
+          ElMessage.error(res.msg);
+        }
+      } catch (e) {
+        ElMessage.error("删除失败");
+      }
+    }
+  }
+];
+
+const handleCreate = () => {
+  console.log("handleCreate");
+  form.value = {
+    dictType: defaultValues.dictType,
+    status: "0"
+  };
+  state.isCreate = true;
+  state.dialogVisible = true;
+};
+
+const handleCloseTag = () => {
+  const path = router.currentRoute.value.path;
+  useMultiTagsStoreHook().handleTags("splice", path);
+  router.back();
+};
+
+interface State {
+  dialogVisible: boolean;
+  detailsVisible: boolean;
+  confirmLoading: boolean;
+  selectedIds: number[];
+  isCreate: boolean;
+  form: Record<string, any>;
+  rules: Record<string, any>;
+}
+
+const state = reactive<State>({
+  dialogVisible: false,
+  detailsVisible: false,
+  confirmLoading: false,
+  selectedIds: [],
+  isCreate: true,
+  form: {
+    status: "0"
+  },
+  rules: {
+    dictName: [{ required: true, message: "请输入字典名称", trigger: "blur" }],
+    dictType: [{ required: true, message: "请输入字典类型", trigger: "blur" }]
+  }
+});
+
+const columns: PlusColumn[] = [
+  {
+    label: "字典类型",
+    prop: "dictType",
+    valueType: "input",
+    fieldProps: {
+      disabled: true
+    }
+  },
+  {
+    label: "数据标签",
+    prop: "dictLabel",
+    valueType: "input"
+  },
+  {
+    label: "数据键值",
+    prop: "dictValue",
+    valueType: "input"
+  },
+  {
+    label: "样式属性",
+    prop: "cssClass",
+    valueType: "input"
+  },
+  {
+    label: "显示排序",
+    prop: "dictSort",
+    valueType: "input-number",
+    fieldProps: {
+      min: 0
+    }
+  },
+  {
+    label: "状态",
+    prop: "status",
+    valueType: "radio",
+    fieldProps: {
+      options: [
+        { label: "正常", value: "0" },
+        { label: "停用", value: "1" }
+      ]
+    }
+  },
+  {
+    label: "备注",
+    prop: "remark",
+    valueType: "textarea"
+  }
+];
+
+const handleSubmit = async () => {
+  state.confirmLoading = true;
+  try {
+    let payload = cloneDeep(form.value);
+    let res: any;
+    if (state.isCreate) {
+      res = await addSystemDictData(payload);
+    } else {
+      res = await updateSystemDictData(payload);
+    }
+    if (res.code === 200) {
+      ElMessage.success(state.isCreate ? "新增成功" : "修改成功");
+      state.dialogVisible = false;
+      refresh();
+    } else {
+      ElMessage.error(res.msg);
+    }
+  } catch (e) {
+    ElMessage.error(state.isCreate ? "新增失败" : "修改失败");
+  } finally {
+    state.confirmLoading = false;
+  }
+};
+
+const { form, confirmLoading, rules, dialogVisible } = toRefs(state);
+</script>
+
+<template>
+  <div>
+    <PlusPage
+      ref="plusPageInstance"
+      :columns="tableConfig"
+      :request="getList"
+      :pageInfoMap="{ page: 'pageNum', pageSize: 'pageSize' }"
+      :table="{
+        actionBar: { buttons, type: 'link', width: 140 },
+        adaptive: { offsetBottom: 50 },
+        isSelection: true,
+        rowKey: 'dictCode',
+        onSelectionChange: handleSelectionChange
+      }"
+      :search="{
+        defaultValues: defaultValues,
+        showNumber: 3
+      }"
+    >
+      <template #table-title>
+        <el-row class="button-row">
+          <el-button size="default" type="success" @click="handleCreate">
+            新增
+          </el-button>
+          <el-button
+            size="default"
+            type="danger"
+            :disabled="multipleSelection.length === 0"
+            @click="handleDelete"
+          >
+            删除
+          </el-button>
+          <el-button
+            size="default"
+            type="warning"
+            plain
+            @click="handleCloseTag"
+          >
+            关闭
+          </el-button>
+        </el-row>
+      </template>
+    </PlusPage>
+    <!-- 弹窗编辑 -->
+    <PlusDialogForm
+      ref="dialogForm"
+      v-model="form"
+      v-model:visible="dialogVisible"
+      :form="{
+        columns,
+        labelPosition: 'right',
+        labelWidth: 100,
+        rules,
+        rowProps: { gutter: 20 },
+        colProps: { span: 12 },
+        defaultValues: defaultValues
+      }"
+      :dialog="{ title: dialogTitle + '字典数据', width: 600, confirmLoading }"
+      @confirm="handleSubmit"
+    />
+  </div>
+</template>
+
+<style scoped lang="scss"></style>

+ 328 - 0
src/views/system/dict/index.vue

@@ -0,0 +1,328 @@
+<script setup lang="ts">
+import { isString } from "@pureadmin/utils";
+import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
+import { useRouter, useRoute } from "vue-router";
+import {
+  type PlusColumn,
+  PlusDialogForm,
+  PlusPage,
+  PlusPageInstance,
+  useTable
+} from "plus-pro-components";
+import {
+  addSystemDictType,
+  deleteSystemDictType,
+  getSystemDictById,
+  getSystemDictList,
+  updateSystemDictType
+} from "@/api/system/dict";
+import { computed, reactive, ref, toRefs } from "vue";
+import { ElMessage } from "element-plus";
+import { cloneDeep } from "lodash-es";
+
+defineOptions({
+  name: "Dict"
+});
+
+const router = useRouter();
+
+const plusPageInstance = ref<PlusPageInstance | null>(null);
+
+const getList = async (query: Record<string, any>) => {
+  let res = await getSystemDictList(query);
+  return {
+    data: res.rows,
+    total: res.total
+  };
+};
+
+// 重新请求列表接口
+const refresh = () => {
+  plusPageInstance.value?.getList();
+};
+
+const dialogTitle = computed(() => (state.isCreate ? "新增" : "编辑"));
+
+const tableConfig: PlusColumn[] = [
+  {
+    label: "字典主键",
+    prop: "dictId",
+    valueType: "input",
+    width: 100,
+    hideInSearch: true
+  },
+  {
+    label: "字典名称",
+    prop: "dictName",
+    valueType: "input"
+  },
+  {
+    label: "字典类型",
+    prop: "dictType",
+    fieldProps: {
+      showOverflowTooltip: true
+    }
+  },
+  {
+    label: "状态",
+    prop: "status",
+    valueType: "tag",
+    fieldProps: value => ({
+      type: value === "0" ? "primary" : "danger"
+    }),
+    fieldSlots: {
+      default: ({ value }) => (value === "0" ? "正常" : "停用")
+    },
+    hideInSearch: true
+  },
+  {
+    label: "状态",
+    prop: "status",
+    valueType: "select",
+    fieldProps: {
+      options: [
+        { label: "正常", value: "0" },
+        { label: "停用", value: "1" }
+      ],
+      clearable: true
+    },
+    hideInTable: true
+  },
+  {
+    label: "备注",
+    prop: "remark",
+    valueType: "input",
+    hideInSearch: true
+  },
+  {
+    label: "创建时间",
+    prop: "createTime",
+    valueType: "date-picker",
+    fieldProps: {
+      type: "daterange",
+      "range-separator": "至",
+      "start-placeholder": "开始日期",
+      "end-placeholder": "结束日期",
+      valueFormat: "YYYY-MM-DD",
+      style: { width: "100%" }
+    }
+  }
+];
+
+const { buttons } = useTable();
+
+buttons.value = [
+  {
+    // 修改
+    text: "修改",
+    code: "edit",
+    // props v0.1.16 版本新增函数类型
+    props: {
+      type: "primary"
+    },
+    onClick(params) {
+      getSystemDictById(params.row.dictId).then(res => {
+        form.value = res.data;
+        state.isCreate = false;
+        state.dialogVisible = true;
+      });
+    }
+  },
+  {
+    // 删除
+    text: "删除",
+    code: "delete",
+    // props v0.1.16 版本新增计算属性支持
+    props: computed(() => ({ type: "danger" })),
+    show: (row: any) => row.parentId !== 0,
+    confirm: {
+      options: {
+        draggable: true,
+        message: "确定删除此数据吗?"
+      }
+    },
+    onConfirm: async params => {
+      try {
+        let res = await deleteSystemDictType(params.row.dictId);
+        if (res.code === 200) {
+          ElMessage.success("删除成功");
+          refresh();
+        } else {
+          ElMessage.error(res.msg);
+        }
+      } catch (e) {
+        ElMessage.error("删除失败");
+      }
+    }
+  }
+];
+
+const handleClick = (val: number) => {
+  console.log("handleClick", val);
+  let params = {
+    dictType: val
+  };
+  Object.keys(params).forEach(param => {
+    if (!isString(params[param])) {
+      params[param] = params[param].toString();
+    }
+  });
+  // 保存信息到标签页
+  useMultiTagsStoreHook().handleTags("push", {
+    path: `/system/dict-data/index`,
+    name: "DictData",
+    query: params,
+    meta: {
+      title: `字典数据`,
+      // 如果使用的是非国际化精简版title可以像下面这么写
+      // title: `No.${index} - 详情信息`,
+      // 最大打开标签数
+      dynamicLevel: 1
+    }
+  });
+  // 路由跳转
+  router.push({ name: "DictData", query: params });
+};
+
+const handleCreate = () => {
+  console.log("handleCreate");
+  form.value = {
+    status: "0"
+  };
+  state.isCreate = true;
+  state.dialogVisible = true;
+};
+
+interface State {
+  dialogVisible: boolean;
+  detailsVisible: boolean;
+  confirmLoading: boolean;
+  selectedIds: number[];
+  isCreate: boolean;
+  form: Record<string, any>;
+  rules: Record<string, any>;
+}
+
+const state = reactive<State>({
+  dialogVisible: false,
+  detailsVisible: false,
+  confirmLoading: false,
+  selectedIds: [],
+  isCreate: true,
+  form: {
+    status: "0"
+  },
+  rules: {
+    dictName: [{ required: true, message: "请输入字典名称", trigger: "blur" }],
+    dictType: [{ required: true, message: "请输入字典类型", trigger: "blur" }]
+  }
+});
+
+const columns: PlusColumn[] = [
+  {
+    label: "字典名称",
+    prop: "dictName",
+    valueType: "input",
+    placeholder: "请输入字典名称"
+  },
+  {
+    label: "字典类型",
+    prop: "dictType",
+    valueType: "input",
+    placeholder: "请输入字典类型"
+  },
+  {
+    label: "状态",
+    prop: "status",
+    valueType: "radio",
+    fieldProps: {
+      options: [
+        { label: "正常", value: "0" },
+        { label: "停用", value: "1" }
+      ]
+    }
+  },
+  {
+    label: "备注",
+    prop: "remark",
+    valueType: "textarea",
+    placeholder: "请输入备注信息",
+    colProps: { span: 24 }
+  }
+];
+
+const handleSubmit = async () => {
+  state.confirmLoading = true;
+  try {
+    let payload = cloneDeep(form.value);
+    let res: any;
+    if (state.isCreate) {
+      res = await addSystemDictType(payload);
+    } else {
+      res = await updateSystemDictType(payload);
+    }
+    if (res.code === 200) {
+      ElMessage.success(state.isCreate ? "新增成功" : "修改成功");
+      state.dialogVisible = false;
+      refresh();
+    } else {
+      ElMessage.error(res.msg);
+    }
+  } catch (e) {
+    ElMessage.error(state.isCreate ? "新增失败" : "修改失败");
+  } finally {
+    state.confirmLoading = false;
+  }
+};
+
+const { form, confirmLoading, rules, dialogVisible } = toRefs(state);
+</script>
+
+<template>
+  <div>
+    <PlusPage
+      ref="plusPageInstance"
+      :columns="tableConfig"
+      :request="getList"
+      :pageInfoMap="{ page: 'pageNum', pageSize: 'pageSize' }"
+      :table="{
+        actionBar: { buttons, type: 'link', width: 140 },
+        adaptive: { offsetBottom: 50 }
+      }"
+      :search="{
+        showNumber: 3
+      }"
+    >
+      <template #plus-cell-dictType="scoped">
+        <el-button type="text" @click="handleClick(scoped.row.dictType)">{{
+          scoped.row.dictType
+        }}</el-button>
+      </template>
+      <template #table-title>
+        <el-row class="button-row">
+          <el-button size="default" type="success" @click="handleCreate">
+            新增
+          </el-button>
+        </el-row>
+      </template>
+    </PlusPage>
+    <!-- 弹窗编辑 -->
+    <PlusDialogForm
+      ref="dialogForm"
+      v-model="form"
+      v-model:visible="dialogVisible"
+      :form="{
+        columns,
+        labelPosition: 'right',
+        labelWidth: 100,
+        rules,
+        rowProps: { gutter: 20 },
+        colProps: { span: 12 }
+      }"
+      :dialog="{ title: dialogTitle + '字典', width: 800, confirmLoading }"
+      @confirm="handleSubmit"
+    />
+  </div>
+</template>
+
+<style scoped lang="scss"></style>

+ 1 - 9
src/views/system/menu/index.vue

@@ -81,14 +81,6 @@ defineOptions({
   name: "Menu"
 });
 
-interface TableRow {
-  id: number;
-  name: string;
-  status: string;
-  tag: string;
-  time: Date;
-}
-
 onMounted(async () => {
   await getSystem();
 });
@@ -143,7 +135,7 @@ const handleBeforeSearch = (values: any) => {
 
 const dialogTitle = computed(() => (state.isCreate ? "新增" : "编辑"));
 
-const { buttons } = useTable<TableRow[]>();
+const { buttons } = useTable();
 
 const systemList = ref([]);