第四十二节:全局字典封装(后端字典,下拉选择复用)

第四十二节:全局字典封装(后端字典,下拉选择复用)

目标:一次封装,所有页面直接使用字典,不用每个页面重复写请求

后端接口规范(若依风格,国内后台最通用)

  1. 获取全部字典类型列表:GET /system/dict/type/list
  2. 根据字典类型编码获取字典项:GET /system/dict/data/{dictType}
    例如:/system/dict/data/sys_user_status
    返回:{ code:200, data: [ {label:'启用', value:'1'}, {label:'禁用', value:'0'} ] }

整体思路

  1. api 层:封装字典请求函数
  2. composables/useDict.js:字典逻辑封装 + 全局缓存(避免重复请求同一个字典)
  3. utils/dictUtil.js:工具函数,根据 value 查找字典 label(表格渲染用)
  4. mock/index.js:配置模拟接口,本地开发调试
  5. 页面使用:一行代码加载字典,下拉框、表格直接渲染

1. api/dict.js 接口层

复制代码
import request from '@/utils/request'

/**
 * 根据字典类型,获取字典数据项
 * @param {string} dictType 字典编码,例如 sys_user_status
 */
export function getDictData(dictType) {
  return request({
    url: `/system/dict/data/${dictType}`,
    method: 'get'
  })
}

2. composables/useDict.js(核心组合式钩子,全局缓存)

复制代码
import { ref } from 'vue'
import { getDictData } from '@/api/dict'

// 全局缓存,整个项目共用,只请求一次同一个字典
const dictCache = new Map()

export function useDict() {
  /**
   * 加载字典,带缓存
   * @param {string} dictType
   * @returns Promise<Array> 字典数组 [{label, value}]
   */
  const loadDict = async (dictType) => {
    // 如果缓存已经存在,直接返回缓存,不再发请求
    if (dictCache.has(dictType)) {
      return dictCache.get(dictType)
    }
    try {
      const res = await getDictData(dictType)
      // ✅ 兜底!防止接口返回 null / undefined,永远返回数组
      const dictList = Array.isArray(res.data) ? res.data : []
      // 存入全局缓存
      dictCache.set(dictType, dictList)
      return dictList
    } catch (err) {
      console.error('字典加载失败:', dictType, err)
      // 接口报错,返回空数组,防止页面崩溃
      return []
    }
  }

  return {
    loadDict
  }
}

3. utils/dictUtil.js 字典工具类(表格根据 value 反查 label)

复制代码
export default {
  /**
   * 根据字典数组 和 value,获取对应的label文本
   * @param {Array} dictList 字典数组 [{label,value}]
   * @param {string|number} value 后端返回的值
   * @returns string label
   */
  getDictLabel(dictList, value) {
    if (!Array.isArray(dictList)) return ''
    const item = dictList.find(i => i.value === value)
    return item ? item.label : ''
  }
}

4. mock/index.js 添加字典模拟接口

复制代码
// 根据字典类型获取字典项
'/system/dict/data/sys_user_status': () => {
  return {
    code: 200,
    data: [
      { label: '启用', value: '1' },
      { label: '禁用', value: '0' }
    ]
  }
},
'/system/dict/data/sys_gender': () => {
  return {
    code: 200,
    data: [
      { label: '男', value: '1' },
      { label: '女', value: '0' }
    ]
  }
},

5. 页面如何使用(你的用户页面示例)

复制代码
<template>
  <PageCardView title="用户">
    <!-- 状态下拉筛选 -->
    <el-select v-model="queryParams.status" placeholder="请选择状态">
      <el-option
        v-for="item in statusDict"
        :key="item.value"
        :label="item.label"
        :value="item.value"
      />
    </el-select>

    <el-table :data="tableData">
      <el-table-column label="状态">
        <template #default="scope">
          {{ dictUtil.getDictLabel(statusDict, scope.row.status) }}
        </template>
      </el-table-column>
    </el-table>
  </PageCardView>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { useDict } from '@/composables/useDict'
import dictUtil from '@/utils/dictUtil'
import PageCardView from '@/components/common/PageCardView.vue'

const { loadDict } = useDict()
const statusDict = ref([])
const queryParams = ref({ status: '' })

onMounted(async () => {
  // 加载 sys_user_status 字典
  statusDict.value = await loadDict('sys_user_status')
})
</script>

6. 整套方案核心特性(重点)

  1. 全局缓存 Map:同一个字典,页面多次打开只请求一次,提升性能
  2. 多层兜底防护 :接口异常、返回 null,全部返回空数组,杜绝 Cannot use 'in' operator 报错
  3. 解耦:api、composable、util、mock 分层,结构清晰
  4. 复用 :任意页面,只需要 const { loadDict } = useDict() 即可加载任意字典
  5. 符合国内后台(若依)开发习惯,后端接口不用改动

7. 扩展:一次性加载多个字典

复制代码
onMounted(async () => {
  // 并行加载多个字典
  const [statusList, genderList] = await Promise.all([
    loadDict('sys_user_status'),
    loadDict('sys_gender')
  ])
  statusDict.value = statusList
  genderDict.value = genderList
})

8. 常见踩坑点(你刚才遇到的坑)

✅ 坑 1:接口地址不匹配 → mock 路由和前端请求 URL 必须一模一样

✅ 坑 2:后端返回 null,没有兜底 → useDict 内部强制转数组

✅ 坑 3:字典数据结构不对,后端返回[{dictType, options}]而不是[{label,value}] → 修改 mock / 后端返回格式

✅ 坑 4:忘记缓存,页面切换反复请求字典 → Map 全局缓存


现在我们可以分步操作

  1. 先把上面 4 段核心文件代码全部替换
  2. mock 添加字典接口
  3. 修改用户页面调用 loadDict('sys_user_status')
  4. 重启项目,测试,报错消失
相关推荐
时间的情敌15 小时前
Cesium 流动管线、管道动画、管线流向仿真,管网、油气、水利孪生高频需求
前端·交互·cesium
yuyuxun115 小时前
协同过滤的校园二手物品智能推荐系统的设计与实现 毕业设计-附源码40082
javascript·vue.js·spring boot·python·数据分析·django·课程设计
another heaven16 小时前
【C/C++ Ranges 是零开销抽象吗?拆开管道、惰性求值和编译期膨胀】
c语言·开发语言·c++
wangxiaoxuan21716 小时前
SLAM 的基本结构:前端、后端、回环与地图优化
前端·机器人·科沃斯八界
SomeOtherTime17 小时前
Rust 闭包语法和原理(AI回答)
开发语言·后端·rust
apcipot_rain17 小时前
C++转Java日记(10)
java·开发语言
总要学点什么18 小时前
第105篇:三维特效-流动图片墙体效果——让箭头沿墙长滚,它俩竟是同一份代码
开发语言·javascript·ecmascript·gis·webgl·材质·cesium
xiaoye-duck18 小时前
《C++ 项目:手写大模型接入 SDK》会话模块整合:内存 SessionManager 与 DataManager 持久化联动
开发语言·数据库·c++·语言模型
Agent世界19 小时前
用 CDP 做发布自动化:四道机器闸门与产物验收
前端·javascript
FungLeo19 小时前
成为全栈·Vue 管理后台篇·编辑器图片上传:从文件选择到文章附件关联
前端·vue.js·编辑器·markdown·文件上传·formdata·附件管理