8.6 KiB
8.6 KiB
Pinia 字典管理系统使用指南
系统架构
┌─────────────────────────────────────┐
│ API 接口 (getDictItemsByCode) │
│ /api/dict/items/code/{code} │
└──────────────┬──────────────────────┘
│
↓
┌─────────────────────────────────────┐
│ Pinia Store (useDictStore) │
│ ✅ 自动缓存字典数据 │
│ ✅ 避免重复请求 │
│ ✅ 支持同步/异步访问 │
└──────────────┬──────────────────────┘
│
┌──────┴──────┐
↓ ↓
┌────────┐ ┌──────────────┐
│组件 │ │Composable │
│直接用 │ │useDict Hook │
└────────┘ └──────────────┘
核心文件说明
1. Store: src/stores/dict.js
字典数据的全局管理器
主要方法:
import { useDictStore } from '@/stores/dict'
const dictStore = useDictStore()
// ✅ 异步获取字典(推荐)
const items = await dictStore.getDictItems('user_status')
// ✅ 同步获取字典(已缓存时)
const items = dictStore.getDictItemsSync('user_status')
// ✅ 获取字典值对应的标签
const label = dictStore.getDictLabel('user_status', 1)
// ✅ 预加载多个字典
await dictStore.preloadDicts(['user_status', 'user_role'])
// ✅ 清空缓存
dictStore.clearCache('user_status')
使用场景
场景1:在列表页加载字典
文件: src/views/system/users/index.vue
<script setup>
import { useDictStore } from '@/stores/dict'
import { ref, onMounted } from 'vue'
const dictStore = useDictStore()
const statusDict = ref([])
const fetchStatusDict = async () => {
statusDict.value = await dictStore.getDictItems('user_status')
}
onMounted(async () => {
await fetchStatusDict()
})
</script>
<template>
<!-- 传给子组件 -->
<UserEditDialog :status-dict="statusDict" />
</template>
场景2:在编辑对话框使用字典
文件: src/views/system/users/components/UserEdit.vue
<template>
<!-- 状态选择 -->
<el-form-item label="状态">
<el-select v-model="form.status">
<el-option
v-for="item in statusDict"
:key="item.dict_value"
:label="item.dict_label"
:value="item.dict_value"
/>
</el-select>
</el-form-item>
</template>
<script setup>
const props = defineProps({
statusDict: {
type: Array,
default: () => [],
},
})
</script>
场景3:直接使用字典Store
直接使用字典Store获取数据:
<template>
<div>
<p v-if="loading">加载中...</p>
<el-select v-else v-model="status">
<el-option
v-for="item in statusDict"
:key="item.dict_value"
:label="item.dict_label"
:value="item.dict_value"
/>
</el-select>
</div>
</template>
<script setup>
import { useDictStore } from '@/stores/dict'
import { ref, onMounted } from 'vue'
const dictStore = useDictStore()
const status = ref('1')
const statusDict = ref([])
const loading = ref(false)
const fetchStatusDict = async () => {
loading.value = true
try {
const items = await dictStore.getDictItems('user_status')
statusDict.value = items
} catch (error) {
console.error('获取用户状态字典失败:', error)
statusDict.value = []
} finally {
loading.value = false
}
}
onMounted(() => {
fetchStatusDict()
})
</script>
场景4:预加载应用启动时需要的字典
文件: src/main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { useDictStore } from '@/stores/dict'
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
// 在应用启动后预加载常用字典
const dictStore = useDictStore()
await dictStore.preloadDicts([
'user_status',
'common_status',
'yes_no',
])
app.mount('#app')
字典数据结构
后端返回的字典数据结构:
[
{
"dict_id": 1,
"dict_value": "1",
"dict_label": "启用",
"dict_type": "user_status",
"remarks": "用户启用状态",
"remark": "用户启用状态"
},
{
"dict_id": 2,
"dict_value": "0",
"dict_label": "禁用",
"dict_type": "user_status",
"remarks": "用户禁用状态",
"remark": "用户禁用状态"
}
]
关键字段:
dict_value: 字典值(存储在数据库中)dict_label: 字典标签(显示给用户)dict_type: 字典类型编码(如 'user_status')
最佳实践
✅ DO
-
使用字符串而不是硬编码数字
// ✅ 好 dictStore.getDictItems('user_status') // ❌ 差 // 避免直接使用数字,应该使用字符串 -
在父组件加载,通过 props 传给子组件
// ✅ 父组件负责数据,子组件负责展示 // index.vue const statusDict = await dictStore.getDictItems('user_status') // UserEdit.vue const props = defineProps({ statusDict: Array }) -
直接使用字典Store
// ✅ 直接使用Store获取数据 const statusDict = await dictStore.getDictItems('user_status') -
预加载常用字典
// ✅ 应用启动时预加载,避免页面初始化时加载 await dictStore.preloadDicts([...])
❌ DON'T
-
不要在多个地方重复加载同一个字典
// ❌ 糟糕:重复加载 // 页面A const dict1 = await dictStore.getDictItems('user_status') // 页面B(Store 会自动缓存,但代码看起来重复) const dict2 = await dictStore.getDictItems('user_status') -
不要忘记处理加载状态
// ❌ 可能展示空白 const { statusDict } = useUserStatusDict() // ✅ 处理加载状态 const { statusDict, loading } = useUserStatusDict() if (loading) { /* 显示加载中 */ } -
不要混用不同的字典访问方式
// ❌ 混乱 const dict1 = await dictStore.getDictItems('user_status') const dict2 = dictStore.getDictItemsSync('user_role') // ✅ 统一使用 const dict1 = await dictStore.getDictItems('user_status') const dict2 = await dictStore.getDictItems('user_role')
性能优化建议
| 优化项 | 说明 |
|---|---|
| 缓存 | Store 自动缓存,同一个字典只请求一次 |
| 预加载 | 在路由切换前预加载需要的字典 |
| 同步访问 | 已加载的字典可用 getDictItemsSync 同步获取 |
| 避免重复 | 不要在多个组件重复请求同一个字典 |
故障排查
问题1:状态选项为空
原因:字典未加载 解决:
// ❌ 错误:字典还未加载
const statusDict = dictStore.getDictItemsSync('user_status') // 返回 []
// ✅ 正确:等待异步加载完成
const statusDict = await dictStore.getDictItems('user_status')
问题2:重复加载字典
原因:没有使用 Store 的缓存 解决:
// 所有调用都会自动使用缓存,只请求一次
await dictStore.getDictItems('user_status') // 首次:发送请求
await dictStore.getDictItems('user_status') // 第二次:返回缓存
问题3:字典显示不对
原因:value 类型不匹配(如 1 vs "1") 解决:
// Store 会自动处理类型匹配
const item = items.find(i =>
String(i.dict_value) === String(value) || i.dict_value === value
)
集成检清表
- 创建
src/stores/dict.js- Store - 在
index.vue中导入useDictStore - 在
UserEdit.vue中使用useDictStore获取字典数据 - 测试字典加载和显示
- 验证缓存功能(打开浏览器 DevTools 检查 Network)
- 预加载常用字典(可选)
相关文件修改
已修改的文件:
- ✅
src/stores/dict.js- 新建 - ✅
src/views/system/users/index.vue- 使用useDictStore - ✅
src/views/system/users/components/UserEdit.vue- 使用字典功能 - ✅
src/constants/dictCodes.js- 删除(不再需要) - ✅
src/composables/useDict.js- 删除(不再需要)