# 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
<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`

```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：快速使用 Composable Hook

最简单的方式，自动处理加载：

```vue
<template>
  <div>
    <p v-if="loading">加载中...</p>
    <el-select v-else v-model="status">
      <el-option 
        v-for="item in user_statusDict"
        :key="item.dict_value"
        :label="item.dict_label"
        :value="item.dict_value"
      />
    </el-select>
  </div>
</template>

<script setup>
import { useUserStatusDict } from '@/composables/useDict'
import { ref } from 'vue'

const status = ref('active')
const { user_statusDict, loading } = useUserStatusDict()
</script>
```

---

### 场景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` - 导入字典库

