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

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

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

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

  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. 重启项目,测试,报错消失
相关推荐
用户64596598710881 小时前
Nginx + Vue Router 基础:SPA History 部署、反向代理与请求分流
前端
样子20181 小时前
Js 之根据白名单过滤 HTML(防止 XSS 攻击)
android·前端·javascript·html·xss
老王爱玩车1 小时前
第2讲:C语言数据类型和变量
c语言·开发语言
lewis_lk1 小时前
ReactJs状态迷思:从"常量"到"闭包陷阱"的底层逻辑
前端·react.js
尘世中一位迷途小书童1 小时前
在 Cesium 上画全球网格:我们怎么把 GeoSOT 接到瓦片调度上
前端·javascript·前端框架
aixingpan1 小时前
aixingpan.cn API开发文档:api_docs_trichart_natal_progression_ter_transit2接口指南
前端·php
菜鸟~noob2331 小时前
【电子战】第12篇:TDOA 定位——双曲线与等时差线【含matlab代码】
开发语言·matlab
liuyt20221 小时前
【javaweb】day3
前端