Skip to content

字典数据

字典数据

全局缓存

用户登录成功后,前端会自动调用接口从后端获取全量的字典数据,缓存在 sessionStorage中。

  • 前端在使用到字典数据时,无需重复请求后端,提升用户体验。
  • 当字典数据发生变化时,需要用户 F5 刷新浏览器,进行重新加载。

image-20250608084536032

文件:src\store\modules\dict.ts 使用 Pinia 和 sessionStorage 实现了一个字典数据的全局缓存管理模块。

  • 定义 Store:通过 defineStore 创建名为 dict 的 store,用于管理字典数据。
  • 状态管理:
    • dictMap: 存储字典数据的 Map,键为 dictType,值为对应的 dictValue 列表。
    • isSetDict: 标记是否已加载字典数据。
  • 获取缓存数据:getDictMap: 从 sessionStorage 中读取缓存的字典数据。
  • 设置字典数据:setDictMap: 若缓存存在则直接使用;否则调用接口获取并构建字典 Map,然后缓存到 sessionStorage。
  • 按类型获取字典:getDictByType: 根据 dictType 获取对应的字典列表,若未加载则自动加载。
  • 重置字典缓存:resetDict: 清除缓存后重新加载字典数据并重新缓存。
  • 无上下文使用 Store:useDictStoreWithOut: 提供一个无需传入 store 即可使用的快捷方式。
ts
export const useDictStore = defineStore('dict', {
  state: (): DictState => ({
    dictMap: new Map<string, any>(),
    isSetDict: false
  }),
  getters: {
    getDictMap(): Recordable {
      const dictMap = wsCache.get(CACHE_KEY.DICT_CACHE)
      if (dictMap) {
        this.dictMap = dictMap
      }
      return this.dictMap
    },
    getIsSetDict(): boolean {
      return this.isSetDict
    }
  },
  actions: {
    async setDictMap() {
      const dictMap = wsCache.get(CACHE_KEY.DICT_CACHE)
      if (dictMap) {
        this.dictMap = dictMap
        this.isSetDict = true
      } else {
        const res = await getSimpleDictDataList()
        // 设置数据
        const dictDataMap = new Map<string, any>()
        res.forEach((dictData: DictDataVO) => {
          // 获得 dictType 层级
          const enumValueObj = dictDataMap[dictData.dictType]
          if (!enumValueObj) {
            dictDataMap[dictData.dictType] = []
          }
          // 处理 dictValue 层级
          dictDataMap[dictData.dictType].push({
            value: dictData.value,
            label: dictData.label,
            colorType: dictData.colorType,
            cssClass: dictData.cssClass
          })
        })
        this.dictMap = dictDataMap
        this.isSetDict = true
        wsCache.set(CACHE_KEY.DICT_CACHE, dictDataMap, { exp: 60 }) // 60 秒 过期
      }
    },
    getDictByType(type: string) {
      if (!this.isSetDict) {
        this.setDictMap()
      }
      return this.dictMap[type]
    },
    async resetDict() {
      wsCache.delete(CACHE_KEY.DICT_CACHE)
      const res = await getSimpleDictDataList()
      // 设置数据
      const dictDataMap = new Map<string, any>()
      res.forEach((dictData: DictDataVO) => {
        // 获得 dictType 层级
        const enumValueObj = dictDataMap[dictData.dictType]
        if (!enumValueObj) {
          dictDataMap[dictData.dictType] = []
        }
        // 处理 dictValue 层级
        dictDataMap[dictData.dictType].push({
          value: dictData.value,
          label: dictData.label,
          colorType: dictData.colorType,
          cssClass: dictData.cssClass
        })
      })
      this.dictMap = dictDataMap
      this.isSetDict = true
      wsCache.set(CACHE_KEY.DICT_CACHE, dictDataMap, { exp: 60 }) // 60 秒 过期
    }
  }
})

字典类型

文件:src\utils\dict.ts,定义了字典的 KEY。后续如果有新的字典 KEY,需要添加到这里。

ts
export enum DICT_TYPE {
  USER_TYPE = 'user_type',
  COMMON_STATUS = 'common_status',
  TERMINAL = 'terminal', // 终端
  DATE_INTERVAL = 'date_interval', // 数据间隔

  // ========== SYSTEM 模块 ==========
  SYSTEM_USER_SEX = 'system_user_sex',
  SYSTEM_MENU_TYPE = 'system_menu_type',
  SYSTEM_ROLE_TYPE = 'system_role_type',
...

字典使用

字典标签

文件:src\components\DictTag\index.ts,定义了字典标签DictTag 组件。根据字典数据渲染一个或多个带颜色标签(ElTag),支持多种类型的输入值,并自动匹配对应的字典选项显示标签。

  • 例如:src\views\system\loginlog\index.vue
vue
  <el-table-column label="登陆结果" align="center" prop="result">
    <template #default="scope">
      <dict-tag :type="DICT_TYPE.SYSTEM_LOGIN_RESULT" :value="scope.row.result" />
    </template>
  </el-table-column>
  • 显示效果

image-20250629093834077

如果使用 CRUD schemas 方式,不需要直接使用 <dict-tag />,而是通过 columnsdictTypedictClass 属性即可。

  • 例如:src\views\system\mail\account\account.data.ts

image-20250629093548112

  • 显示效果

image-20250629093624379

字典工具类

文件:src\utils\dict.ts,定义了字典工具。

  • 获取数据字典:通过 getDictOptions 获取指定类型的数据字典列表;
  • 类型转换获取字典:提供 getIntDictOptions、getStrDictOptions、getBoolDictOptions 方法分别以 number、string、boolean 类型返回值;
  • 获取字典对象和标签:getDictObj 获取匹配的字典对象,getDictLabel 获取对应值的显示文本;
ts
// 获取 dictType 对应的数据字典数组【object】
export const getDictOptions = (dictType: string) => {{ /** 省略代码 */ }

// 获取 dictType 对应的数据字典数组【int】
export const getIntDictOptions = (dictType: string) => { /** 省略代码 */ }

// 获取 dictType 对应的数据字典数组【string】
export const getStrDictOptions = (dictType: string) => { /** 省略代码 */ }

// 获取 dictType 对应的数据字典数组【boolean】
export const getBoolDictOptions = (dictType: string) => { /** 省略代码 */ }

// 获取 dictType 对应的数据字典数组【object】
export const getDictObj = (dictType: string, value: any) => { /** 省略代码 */ }

// 获得字典数据的文本展示
export const getDictLabel = (dictType: string, value: any): string => { /** 省略代码 */ }

使用示例

vue
<template>
  <!-- radio 单选框 -->
  <el-radio 
    v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
    :key="dict.value" 
    :label="parseInt(dict.value)"
  >
    {{dict.label}}
  </el-radio>
    
  <!-- select 下拉框 -->
    <el-select
      v-model="queryParams.storage"
      placeholder="请选择存储器"
      clearable
      class="!w-240px"
    >
      <el-option
        v-for="dict in getIntDictOptions(DICT_TYPE.INFRA_FILE_STORAGE)"
        :key="dict.value"
        :label="dict.label"
        :value="dict.value"
      />
    </el-select>
</template>
<script setup lang="tsx">
import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
</script>

示例文件:src\views\infra\fileConfig\index.vue

image-20250629095225377