# Pinia 字典管理系统使用指南
## 系统架构
```
┌─────────────────────────────────────┐
│ API 接口 (getDictItemsByCode) │
│ /api/dict/items/code/{code} │
└──────────────┬──────────────────────┘
│
↓
┌─────────────────────────────────────┐
│ Pinia Store (useDictStore) │
│ ✅ 自动缓存字典数据 │
│ ✅ 避免重复请求 │
│ ✅ 支持同步/异步访问 │
└──────────────┬──────────────────────┘
│
┌──────┴──────┐
↓ ↓
┌────────┐ ┌──────────────┐
│组件 │ │Composable │
│直接用 │ │useDict Hook │
└────────┘ └──────────────┘
```
---
## 核心文件说明
### 1. **Store**: `src/stores/dict.js`
字典数据的全局管理器
**主要方法**:
```javascript
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')
```
---
### 2. **常量**: `src/constants/dictCodes.js`
集中管理所有字典编码
**使用示例**:
```javascript
import { DICT_CODES } from '@/constants/dictCodes'
// 好处:避免硬编码,IDE 有自动完成
const items = await dictStore.getDictItems(DICT_CODES.USER_STATUS)
// 所有可用的编码:
DICT_CODES.USER_STATUS // 用户状态
DICT_CODES.USER_GENDER // 用户性别
DICT_CODES.USER_ROLE // 用户角色
DICT_CODES.DEPT_STATUS // 部门状态
DICT_CODES.POSITION_STATUS // 职位状态
// ... 更多编码
```
---
### 3. **Composable**: `src/composables/useDict.js`
简化在组件中使用字典的 Hook
**基础用法**:
```javascript
import { useDictionary, useUserStatusDict } from '@/composables/useDict'
import { DICT_CODES } from '@/constants/dictCodes'
// 方式1:使用常量
const { statusDict, loading } = useDictionary(DICT_CODES.USER_STATUS)
// 方式2:使用字符串
const { dicts, loading } = useDictionary('user_status')
// 方式3:使用特化 Hook(推荐)
const { user_statusDict, loading } = useUserStatusDict()
```
---
## 使用场景
### 场景1:在列表页加载字典
**文件**: `src/views/system/users/index.vue`
```vue
```
---
### 场景2:在编辑对话框使用字典
**文件**: `src/views/system/users/components/UserEdit.vue`
```vue
```
---
### 场景3:快速使用 Composable Hook
最简单的方式,自动处理加载:
```vue
```
---
### 场景4:预加载应用启动时需要的字典
**文件**: `src/main.js`
```javascript
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { useDictStore } from '@/stores/dict'
import { DICT_CODES } from '@/constants/dictCodes'
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
// 在应用启动后预加载常用字典
const dictStore = useDictStore()
await dictStore.preloadDicts([
DICT_CODES.USER_STATUS,
DICT_CODES.COMMON_STATUS,
DICT_CODES.YES_NO,
])
app.mount('#app')
```
---
## 字典数据结构
后端返回的字典数据结构:
```json
[
{
"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
1. **使用常量而不是硬编码字符串**
```javascript
// ✅ 好
dictStore.getDictItems(DICT_CODES.USER_STATUS)
// ❌ 差
dictStore.getDictItems('user_status')
```
2. **在父组件加载,通过 props 传给子组件**
```javascript
// ✅ 父组件负责数据,子组件负责展示
// index.vue
const statusDict = await dictStore.getDictItems(DICT_CODES.USER_STATUS)
// UserEdit.vue
const props = defineProps({ statusDict: Array })
```
3. **用 Composable 简化组件逻辑**
```javascript
// ✅ 一行代码搞定
const { user_statusDict, loading } = useUserStatusDict()
```
4. **预加载常用字典**
```javascript
// ✅ 应用启动时预加载,避免页面初始化时加载
await dictStore.preloadDicts([...])
```
### ❌ DON'T
1. **不要在多个地方重复加载同一个字典**
```javascript
// ❌ 糟糕:重复加载
// 页面A
const dict1 = await dictStore.getDictItems('user_status')
// 页面B(Store 会自动缓存,但代码看起来重复)
const dict2 = await dictStore.getDictItems('user_status')
```
2. **不要忘记处理加载状态**
```javascript
// ❌ 可能展示空白
const { statusDict } = useUserStatusDict()
// ✅ 处理加载状态
const { statusDict, loading } = useUserStatusDict()
if (loading) { /* 显示加载中 */ }
```
3. **不要混用不同的字典访问方式**
```javascript
// ❌ 混乱
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:状态选项为空
**原因**:字典未加载
**解决**:
```javascript
// ❌ 错误:字典还未加载
const statusDict = dictStore.getDictItemsSync('user_status') // 返回 []
// ✅ 正确:等待异步加载完成
const statusDict = await dictStore.getDictItems('user_status')
```
### 问题2:重复加载字典
**原因**:没有使用 Store 的缓存
**解决**:
```javascript
// 所有调用都会自动使用缓存,只请求一次
await dictStore.getDictItems('user_status') // 首次:发送请求
await dictStore.getDictItems('user_status') // 第二次:返回缓存
```
### 问题3:字典显示不对
**原因**:value 类型不匹配(如 1 vs "1")
**解决**:
```javascript
// Store 会自动处理类型匹配
const item = items.find(i =>
String(i.dict_value) === String(value) || i.dict_value === value
)
```
---
## 集成检清表
- [ ] 创建 `src/stores/dict.js` - Store
- [ ] 创建 `src/constants/dictCodes.js` - 常量
- [ ] 创建 `src/composables/useDict.js` - Composable
- [ ] 在 `index.vue` 中导入 `useDictStore`
- [ ] 在 `UserEdit.vue` 中接收 `statusDict` props
- [ ] 测试字典加载和显示
- [ ] 验证缓存功能(打开浏览器 DevTools 检查 Network)
- [ ] 预加载常用字典(可选)
---
## 相关文件修改
已修改的文件:
- ✅ `src/stores/dict.js` - 新建
- ✅ `src/constants/dictCodes.js` - 新建
- ✅ `src/composables/useDict.js` - 新建
- ✅ `src/views/system/users/index.vue` - 使用 `useDictStore`
- ✅ `src/views/system/users/components/UserEdit.vue` - 导入字典库