Procházet zdrojové kódy

feat: 添加字典管理

Gaokun Wang před 4 týdny
rodič
revize
4d6bd96c90

+ 52 - 0
src/api/interface/system/dict.ts

@@ -0,0 +1,52 @@
+/**
+ * 查询参数
+ */
+export interface DictQuery extends PageQuery {
+  dictLabel?: string
+  parentId?: string
+  status?: number
+}
+
+/**
+ * 接收后端返回信息
+ */
+export interface DictVO extends BaseEntity {
+  dictId: number | string
+  parentId: number | string
+  dictLabel: string
+  dictValue: string
+  category: string
+  callbackShowStyle: string
+  orderNum: number
+  remark?: string
+}
+
+/**
+ * 传入后端的表单信息
+ */
+export interface DictBo {
+  dictId: number | string
+  parentId: number | string
+  dictLabel: string
+  dictValue: string
+  category: string
+  callbackShowStyle: string
+  orderNum: number
+  remark?: string
+  status: string
+}
+
+/**
+ * 接收后端返回树信息
+ */
+export interface DictTreeVO extends BaseEntity {
+  dictId: number | string
+  parentId: number | string
+  dictLabel: string
+  dictValue?: string
+  category?: string
+  callbackShowStyle?: string
+  orderNum?: number
+  remark?: string
+  children: DictTreeVO[]
+}

+ 8 - 10
src/api/interface/system/org.ts

@@ -29,20 +29,18 @@ export interface OrgVO extends BaseEntity {
  * 组织树类型
  */
 export interface OrgTreeVO extends BaseEntity {
-  id: number | string
+  id?: number | string
   orgId: number | string
-  parentName: string
+  parentName?: string
   parentId: number | string
   children: OrgTreeVO[]
   name: string
-  orderNum: number
-  leader: string
-  phone: string
-  email: string
-  status: string
-  delFlag: string
-  ancestors: string
-  menuId: string | number
+  orderNum?: number
+  leader?: string
+  phone?: string
+  email?: string
+  ancestors?: string
+  menuId?: string | number
 }
 
 /**

+ 44 - 0
src/api/module/system/dict.ts

@@ -0,0 +1,44 @@
+import http from '@/axios'
+import { DictBo, DictQuery, DictTreeVO, DictVO } from '@/api/interface/system/dict'
+
+class DictApi {
+  /**
+   * @name 查询树结构
+   * @returns returns
+   */
+  static tree = (): Promise<ResultData<any>> => {
+    return http.get<DictTreeVO>({ url: '/system/dict/tree' })
+  }
+
+  /**
+   * @name 查询列表
+   * @returns returns
+   */
+  static page = (params: DictQuery): Promise<ResultData<any>> => {
+    return http.get<DictVO>({ url: '/system/dict/page', params })
+  }
+
+  /**
+   * @name 添加
+   * @returns returns
+   */
+  static add = (data: DictBo): Promise<ResultData<any>> => {
+    return http.post({ url: '/system/dict/add', data })
+  }
+  /**
+   * @name 更新
+   * @returns returns
+   */
+  static edit = (data: DictBo): Promise<ResultData<any>> => {
+    return http.post({ url: '/system/dict/edit', data })
+  }
+
+  /**
+   * @name 更新
+   * @returns returns
+   */
+  static delete = (data: DictBo): Promise<ResultData<any>> => {
+    return http.delete({ url: '/system/dict/delete', data })
+  }
+}
+export default DictApi

+ 1 - 1
src/components/TreeFilter/index.vue

@@ -2,7 +2,7 @@
   <div class="card filter">
     <h4 v-if="title" class="title sle">
       {{ title }}
-      <el-tooltip effect="dark" content="数量仅展示直属组织即当前节点直接下层,不包含其后代" placement="top">
+      <el-tooltip effect="dark" content="展示当前节点直接下层,不包含其后代" placement="top">
         <i :class="'iconfont icon-yiwen'" />
       </el-tooltip>
     </h4>

+ 15 - 0
src/stores/modules/options.ts

@@ -0,0 +1,15 @@
+import { DictVO } from '@/api/interface/system/dict'
+import { pinia } from '../index'
+export const useOptionsStore = defineStore('eco-keep-alive', {
+  state: () => ({
+    isLoaded: false as boolean,
+    dictOptions: {} as Record<string, DictVO[]>
+  }),
+  getters: {},
+  actions: {},
+  persist: true
+})
+
+export const useOptionsStoreWithOut = () => {
+  return useOptionsStore(pinia)
+}

+ 1 - 0
src/types/auto-components.d.ts

@@ -17,6 +17,7 @@ declare module 'vue' {
     AppTools: typeof import('./../layouts/components/AppTools/index.vue')['default']
     AppTransverse: typeof import('./../layouts/container/AppTransverse/index.vue')['default']
     ColSetting: typeof import('./../components/ProTable/ColSetting.vue')['default']
+    DictDrawer: typeof import('./../views/system/dict/components/DictDrawer.vue')['default']
     EcoDialog: typeof import('./../components/EcoDialog/index.vue')['default']
     EcoDialogForm: typeof import('./../components/EcoDialogForm/index.vue')['default']
     EcoDrawer: typeof import('./../components/EcoDrawer/index.vue')['default']

+ 4 - 0
src/types/global.d.ts

@@ -35,9 +35,13 @@ declare interface Result {
 // BaseEntity
 declare interface BaseEntity {
   /** 乐观锁 */
+  status?: string
+  delFlag?: string
   version?: number
+  createName?: string
   createBy?: string
   createTime?: string
+  updateName?: string
   updateBy?: string
   updateTime?: any
 }

+ 187 - 0
src/views/system/dict/components/DictDrawer.vue

@@ -0,0 +1,187 @@
+<template>
+  <ElDrawer ref="elDrawerRef" v-model="drawerVisible" :title="drawerProps.title" v-bind="$attrs" destroy-on-close>
+    <template #header="scope">
+      <slot name="header" v-bind="scope">
+        <div style="display: flex">
+          <slot name="title">
+            <span style="flex: 1">{{ drawerProps.title }}</span>
+          </slot>
+        </div>
+      </slot>
+    </template>
+
+    <el-form
+      ref="ruleFormRef"
+      label-width="100px"
+      label-suffix=" :"
+      label-position="top"
+      :rules="rules"
+      :model="drawerProps.row"
+      @submit.enter.prevent="handleConfirm">
+      <el-form-item label="上级字典" prop="parentId">
+        <el-tree-select
+          v-model="drawerProps.row.parentId"
+          :data="treeData"
+          check-strictly
+          placeholder="请选择上级"
+          :render-after-expand="false"
+          clearable
+          :default-expand-all="true"
+          :props="treeProps" />
+      </el-form-item>
+      <el-form-item label="字典标签" prop="dictLabel">
+        <el-input v-model="drawerProps.row.dictLabel" placeholder="字典标签" clearable />
+      </el-form-item>
+      <el-form-item label="字典值" prop="dictValue">
+        <el-input v-model="drawerProps.row.dictValue" placeholder="字典值" clearable />
+      </el-form-item>
+      <el-form-item label="排序" prop="orderNum">
+        <el-input-number v-model="drawerProps.row.orderNum" :precision="0" :min="1" :max="999999" />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-radio-group v-model="drawerProps.row.status" default-value="1">
+          <el-radio-button label="启用" value="1" />
+          <el-radio-button label="停用" value="0" />
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="回显样式" prop="callbackShowStyle">
+        <el-radio-group v-model="drawerProps.row.callbackShowStyle">
+          <el-radio :value="item.value" v-for="item in tagsTypeOptions" :key="item.label">
+            <el-tag :type="item.value">
+              {{ item.label }}
+            </el-tag>
+          </el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="备注" prop="remark">
+        <el-input v-model="drawerProps.row.remark" placeholder="请填写备注" clearable />
+      </el-form-item>
+    </el-form>
+
+    <template #footer>
+      <slot name="footer">
+        <ElButton @click="drawerVisible = false">取 消</ElButton>
+        <ElButton type="primary" @click="handleConfirm()">确 定</ElButton>
+      </slot>
+    </template>
+  </ElDrawer>
+</template>
+
+<script setup lang="ts" name="OrgForm">
+import { DictBo, DictTreeVO } from '@/api/interface/system/dict'
+import DictApi from '@/api/module/system/dict'
+import { ResultEnum } from '@/enums/HttpEnum'
+export interface Options {
+  id?: number
+  label: string
+  value: string
+}
+/**
+ * 标签类型
+ * @type {[Options,Options]}
+ */
+const tagsTypeOptions: Options[] = [
+  {
+    id: 1,
+    label: 'Default',
+    value: 'primary'
+  },
+  {
+    id: 2,
+    label: 'Success',
+    value: 'success'
+  },
+  {
+    id: 3,
+    label: 'Info',
+    value: 'info'
+  },
+  {
+    id: 4,
+    label: 'Warning',
+    value: 'warning'
+  },
+  {
+    id: 5,
+    label: 'Danger',
+    value: 'danger'
+  }
+]
+import { FormInstance } from 'element-plus'
+const rules = reactive({
+  name: [{ required: true, message: '请填写组织名称' }],
+  parentId: [{ required: true, message: '请选择上级' }],
+  orderNum: [{ required: true, message: '排序不能为空' }],
+  status: [{ required: true, message: '状态不能为空' }]
+})
+
+interface EcoDrawerProps {
+  api?: (params: any) => Promise<any> // 调用接口
+  title: string // 顶部标题
+  isView: boolean
+  row: Partial<DictBo>
+  getTableList?: () => void
+}
+
+const treeData = ref<DictTreeVO[]>([])
+const treeProps = {
+  value: 'dictId',
+  label: 'dictLabel'
+}
+
+const drawerVisible = ref(false)
+const drawerProps = ref<EcoDrawerProps>({
+  isView: false,
+  title: '',
+  row: {}
+})
+
+// emit
+const emit = defineEmits(['submit'])
+
+// 接收父组件传过来的参数
+const acceptParams = (params: EcoDrawerProps) => {
+  drawerProps.value = params
+  drawerVisible.value = true
+  loadTree()
+  console.log(drawerProps.value.row.parentId)
+}
+
+const loadTree = () => {
+  DictApi.tree().then(res => {
+    treeData.value = [
+      {
+        parentId: '-1',
+        dictId: '0',
+        dictLabel: '顶层',
+        children: res.data
+      }
+    ]
+  })
+}
+
+// 提交数据(新增/编辑)
+const ruleFormRef = ref<FormInstance>()
+const handleConfirm = () => {
+  ruleFormRef.value!.validate(async valid => {
+    if (!valid) return
+    try {
+      const { code } = await drawerProps.value.api!(drawerProps.value.row)
+      if (code == ResultEnum.SUCCESS) {
+        ElMessage.success({ message: `${drawerProps.value.title}成功!` })
+        drawerProps.value.getTableList!()
+        emit('submit')
+        drawerVisible.value = false
+      }
+    } catch (error) {
+      console.log(error)
+    }
+  })
+}
+
+defineExpose({
+  acceptParams
+})
+</script>
+
+<style scoped lang="scss"></style>

+ 84 - 1
src/views/system/dict/index.vue

@@ -1,3 +1,86 @@
 <template>
-  <div>字典</div>
+  <div class="main-box">
+    <TreeFilter title="字典类型" ref="treeRef" :request-api="DictApi.tree" label="name" :default-value="initParam.dictId" @change="changeTree" />
+    <div class="table-box">
+      <ProTable
+        ref="proTableRef"
+        title="字典数据"
+        row-key="dictId"
+        :indent="20"
+        :columns="columns"
+        :init-param="initParam"
+        :search-columns="searchColumns"
+        :request-api="getTableList">
+        <!-- 表格 header 按钮 -->
+        <template #tableHeader="scope">
+          <el-button type="primary" icon="CirclePlus" @click="openDrawer('新增')"> 新增 </el-button>
+          <el-button type="danger" icon="Delete" plain :disabled="!scope.isSelected"> 批量删除 </el-button>
+        </template>
+
+        <template #operation="{ row }">
+          <el-button type="primary" link icon="EditPen" @click="openDrawer('编辑', row)"> 编辑 </el-button>
+          <el-button v-if="row.id !== 1" type="primary" link icon="Delete"> 删除 </el-button>
+        </template>
+      </ProTable>
+    </div>
+    <DictDrawer ref="drawerRef" @submit="refreshTree" />
+  </div>
 </template>
+<script lang="tsx" setup name="DIctManage">
+import { RoleInfo, RoleQuery } from '@/api/interface/system/role'
+import DictApi from '@/api/module/system/dict'
+import DictDrawer from './components/DictDrawer.vue'
+import TreeFilter from '@/components/TreeFilter/index.vue'
+import { ColumnProps, ProTableInstance, SearchProps } from '@/components/ProTable/interface'
+import { OrgFormBo } from '@/api/interface/system/org'
+const initParam = reactive({ dictId: '', parentId: '' })
+
+const selectTreeId = ref<string>('')
+const proTableRef = ref<ProTableInstance>()
+const treeRef = ref<InstanceType<typeof TreeFilter>>()
+
+// 树形筛选切换
+const changeTree = (val: string) => {
+  if (val) {
+    selectTreeId.value = val
+    initParam.parentId = val
+    proTableRef.value?.clearSelection()
+  }
+}
+
+// 刷新树表
+const refreshTree = () => {
+  treeRef.value?.refresh()
+}
+
+// 表格配置项
+const columns: ColumnProps<RoleInfo>[] = [
+  { type: 'selection', width: 60 },
+  { prop: 'dictLabel', label: '字典标签' },
+  { prop: 'dictValue', label: '字典值' },
+  { prop: 'orderNum', label: '排序' },
+  { prop: 'remark', label: '备注' },
+  { prop: 'createByName', label: '创建人' },
+  { prop: 'createTime', label: '创建时间' },
+  { prop: 'operation', label: '操作', width: 250, fixed: 'right' }
+]
+
+// 表格配置项
+const searchColumns: SearchProps[] = [{ prop: 'dictLabel', label: '字典标签', el: 'input' }]
+
+// 获取table列表
+const getTableList = (params: RoleQuery) => DictApi.page(params)
+
+// 打开 drawer(新增、查看、编辑)
+const drawerRef = ref<InstanceType<typeof DictDrawer> | null>(null)
+const openDrawer = (title: string, row: Partial<OrgFormBo> = {}) => {
+  const params = {
+    title,
+    row: title === '新增' ? { parentId: selectTreeId.value } : { ...row },
+    isView: title === '查看',
+    api: title === '新增' ? DictApi.add : title === '编辑' ? DictApi.edit : undefined,
+    getTableList: proTableRef.value?.getTableList
+  }
+  drawerRef.value?.acceptParams(params)
+}
+</script>

+ 8 - 1
src/views/system/org/components/OrgDrawer.vue

@@ -97,7 +97,14 @@ const acceptParams = (params: EcoDrawerProps) => {
 
 const loadTree = () => {
   OrgApi.tree().then(res => {
-    treeData.value = res.data
+    treeData.value = [
+      {
+        parentId: '-1',
+        orgId: '0',
+        name: '顶层',
+        children: res.data
+      }
+    ]
   })
 }