更新代码
This commit is contained in:
@@ -0,0 +1,548 @@
|
||||
# 字典系统使用指南
|
||||
|
||||
## 概述
|
||||
|
||||
字典(Dictionary)系统是一个用于管理应用中常用的枚举值和标签化数据的功能。通过字典系统,你可以在后台统一管理这些数据,而无需修改代码和重新编译部署。
|
||||
|
||||
### 字典的核心概念
|
||||
|
||||
- **字典类型(Dict Type)**:用来分类管理字典项,例如 `user_status`(用户状态)、`gender`(性别)等
|
||||
- **字典编码(Dict Code)**:字典类型的唯一标识,用于前端查询,例如 `user_status`、`gender`
|
||||
- **字典项(Dict Item)**:具体的字典值和标签,包括:
|
||||
- `dict_value`:存储在数据库中的实际值(例如 `1`、`0`)
|
||||
- `dict_label`:显示给用户的标签文本(例如 "启用"、"禁用")
|
||||
|
||||
### 数据库表结构
|
||||
|
||||
```sql
|
||||
-- 字典类型表
|
||||
CREATE TABLE sys_dict_type (
|
||||
id BIGINT,
|
||||
dict_code VARCHAR(50) -- 字典编码(唯一)
|
||||
dict_name VARCHAR(100) -- 字典名称
|
||||
status TINYINT -- 是否启用
|
||||
...
|
||||
)
|
||||
|
||||
-- 字典项表
|
||||
CREATE TABLE sys_dict_item (
|
||||
id BIGINT,
|
||||
dict_type_id BIGINT -- 关联的字典类型ID
|
||||
dict_label VARCHAR(100) -- 显示标签(如 "启用")
|
||||
dict_value VARCHAR(100) -- 存储值(如 "1")
|
||||
status TINYINT -- 是否启用
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 后端 API 接口
|
||||
|
||||
### 1. 获取字典项(最常用)
|
||||
|
||||
**根据字典编码获取字典项列表:**
|
||||
|
||||
```http
|
||||
GET /api/dict/items/code/:code?include_disabled=0
|
||||
```
|
||||
|
||||
**请求示例:**
|
||||
```bash
|
||||
curl -X GET "http://localhost:8080/api/dict/items/code/user_status?include_disabled=0"
|
||||
```
|
||||
|
||||
**响应示例:**
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"dict_type_id": 101,
|
||||
"dict_label": "启用",
|
||||
"dict_value": "1",
|
||||
"status": 1,
|
||||
"sort": 1,
|
||||
...
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"dict_type_id": 101,
|
||||
"dict_label": "禁用",
|
||||
"dict_value": "0",
|
||||
"status": 1,
|
||||
"sort": 2,
|
||||
...
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明:**
|
||||
- `code` (string, 必需):字典编码,例如 `user_status`
|
||||
- `include_disabled` (int, 可选):是否包含禁用项,0 = 不包含(默认),1 = 包含
|
||||
|
||||
---
|
||||
|
||||
### 2. 字典管理接口
|
||||
|
||||
#### 获取字典类型列表
|
||||
```http
|
||||
GET /api/dict/types?parentId=&status=
|
||||
```
|
||||
|
||||
#### 添加字典类型
|
||||
```http
|
||||
POST /api/dict/types
|
||||
{
|
||||
"dict_code": "user_status",
|
||||
"dict_name": "用户状态",
|
||||
"status": 1
|
||||
}
|
||||
```
|
||||
|
||||
#### 添加字典项
|
||||
```http
|
||||
POST /api/dict/items
|
||||
{
|
||||
"dict_type_id": 101,
|
||||
"dict_label": "启用",
|
||||
"dict_value": "1",
|
||||
"status": 1,
|
||||
"sort": 1
|
||||
}
|
||||
```
|
||||
|
||||
#### 更新/删除字典项
|
||||
```http
|
||||
PUT /api/dict/items/:id
|
||||
DELETE /api/dict/items/:id
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 前端 API(Vue/JavaScript)
|
||||
|
||||
### 前端 API 文件位置
|
||||
|
||||
```
|
||||
pc/src/api/dict.js
|
||||
```
|
||||
|
||||
### 可用函数
|
||||
|
||||
#### getDictItemsByCode(code, includeDisabled = false)
|
||||
|
||||
**最常用的函数**,根据字典编码获取字典项列表。
|
||||
|
||||
**参数:**
|
||||
- `code` (string):字典编码,例如 `'user_status'`
|
||||
- `includeDisabled` (boolean):是否包含禁用项,默认 `false`
|
||||
|
||||
**返回:** Promise,返回字典项数组
|
||||
|
||||
**示例:**
|
||||
```javascript
|
||||
import { getDictItemsByCode } from '@/api/dict'
|
||||
|
||||
// 获取用户状态字典
|
||||
const statusItems = await getDictItemsByCode('user_status')
|
||||
console.log(statusItems)
|
||||
// 输出:
|
||||
// [
|
||||
// { dict_label: '启用', dict_value: '1', ... },
|
||||
// { dict_label: '禁用', dict_value: '0', ... }
|
||||
// ]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 其他可用函数
|
||||
|
||||
```javascript
|
||||
// 获取字典类型列表
|
||||
getDictTypes(params)
|
||||
|
||||
// 根据ID获取字典类型
|
||||
getDictTypeById(id)
|
||||
|
||||
// 添加字典类型
|
||||
addDictType(data)
|
||||
|
||||
// 更新字典类型
|
||||
updateDictType(id, data)
|
||||
|
||||
// 删除字典类型
|
||||
deleteDictType(id)
|
||||
|
||||
// 获取字典项列表(需传入参数过滤)
|
||||
getDictItems(params)
|
||||
|
||||
// 根据ID获取字典项
|
||||
getDictItemById(id)
|
||||
|
||||
// 添加字典项
|
||||
addDictItem(data)
|
||||
|
||||
// 更新字典项
|
||||
updateDictItem(id, data)
|
||||
|
||||
// 删除字典项
|
||||
deleteDictItem(id)
|
||||
|
||||
// 批量更新字典项排序
|
||||
batchUpdateDictItemSort(data)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 前端组件中的使用示例
|
||||
|
||||
### 示例 1:在用户管理页面显示用户状态
|
||||
|
||||
**场景**:在用户列表中,根据用户的 `status` 字段显示对应的状态标签。
|
||||
|
||||
**文件**:`pc/src/views/system/users/index.vue`
|
||||
|
||||
**实现步骤**:
|
||||
|
||||
#### 1. 导入字典 API
|
||||
```javascript
|
||||
import { getDictItemsByCode } from '@/api/dict'
|
||||
```
|
||||
|
||||
#### 2. 定义状态字典数据和加载函数
|
||||
```javascript
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
// 状态字典
|
||||
const statusDict = ref<any[]>([])
|
||||
|
||||
// 加载字典数据
|
||||
const fetchStatusDict = async () => {
|
||||
try {
|
||||
const res = await getDictItemsByCode('user_status')
|
||||
let items: any[] = []
|
||||
|
||||
// 兼容不同的返回结构
|
||||
if (res?.data && Array.isArray(res.data)) {
|
||||
items = res.data
|
||||
} else if (Array.isArray(res)) {
|
||||
items = res
|
||||
}
|
||||
|
||||
statusDict.value = items
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch status dict:', err)
|
||||
statusDict.value = []
|
||||
}
|
||||
}
|
||||
|
||||
// 在组件挂载时加载字典
|
||||
onMounted(async () => {
|
||||
await fetchStatusDict()
|
||||
// ... 其他初始化逻辑
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
#### 3. 定义辅助函数
|
||||
```javascript
|
||||
// 根据状态值获取字典标签
|
||||
const getStatusLabel = (status: any) => {
|
||||
const sval = status !== undefined && status !== null ? String(status) : ''
|
||||
|
||||
// 查找匹配的字典项
|
||||
const item = statusDict.value.find(
|
||||
(d: any) => String(d.dict_value) === sval || d.dict_value === status
|
||||
)
|
||||
|
||||
if (item && item.dict_label) {
|
||||
return item.dict_label
|
||||
}
|
||||
|
||||
// 兼容旧逻辑:如果没有匹配的字典项
|
||||
if (status === 1 || sval === '1' || sval === 'active') return '启用'
|
||||
return '禁用'
|
||||
}
|
||||
|
||||
// 根据标签确定 el-tag 的样式类型
|
||||
const getStatusTagType = (status: any) => {
|
||||
const label = getStatusLabel(status)
|
||||
if (!label) return 'info'
|
||||
|
||||
const l = label.toString()
|
||||
if (l.includes('启用') || l.includes('正常') || l.includes('active')) return 'success'
|
||||
if (l.includes('禁用') || l.includes('停用') || l.includes('inactive')) return 'danger'
|
||||
|
||||
return 'info'
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. 在模板中使用
|
||||
```vue
|
||||
<template>
|
||||
<el-table :data="users">
|
||||
<!-- 其他列 -->
|
||||
|
||||
<!-- 状态列 -->
|
||||
<el-table-column prop="status" label="状态" width="120">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusTagType(scope.row.status)">
|
||||
{{ getStatusLabel(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 示例 2:在下拉选择中使用字典
|
||||
|
||||
**场景**:在"添加/编辑用户"对话框中,用字典项填充状态下拉选择。
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<el-dialog title="编辑用户">
|
||||
<el-form :model="form">
|
||||
<!-- 其他字段 -->
|
||||
|
||||
<!-- 状态下拉 -->
|
||||
<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>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const statusDict = ref<any[]>([])
|
||||
|
||||
const fetchStatusDict = async () => {
|
||||
try {
|
||||
const res = await getDictItemsByCode('user_status')
|
||||
const items = res?.data || res || []
|
||||
statusDict.value = Array.isArray(items) ? items : []
|
||||
} catch (err) {
|
||||
statusDict.value = []
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await fetchStatusDict()
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 示例 3:多个字典项的场景
|
||||
|
||||
**场景**:同时加载多个字典(用户状态、性别、部门类型等)。
|
||||
|
||||
```javascript
|
||||
<script setup lang="ts">
|
||||
import { getDictItemsByCode } from '@/api/dict'
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
// 定义多个字典
|
||||
const statusDict = ref<any[]>([])
|
||||
const genderDict = ref<any[]>([])
|
||||
const deptTypeDict = ref<any[]>([])
|
||||
|
||||
// 统一加载函数
|
||||
const fetchAllDicts = async () => {
|
||||
try {
|
||||
const [status, gender, deptType] = await Promise.all([
|
||||
getDictItemsByCode('user_status'),
|
||||
getDictItemsByCode('gender'),
|
||||
getDictItemsByCode('dept_type'),
|
||||
])
|
||||
|
||||
statusDict.value = status?.data || status || []
|
||||
genderDict.value = gender?.data || gender || []
|
||||
deptTypeDict.value = deptType?.data || deptType || []
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch dicts:', err)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await fetchAllDicts()
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 缓存字典数据
|
||||
|
||||
避免在每个组件中都调用字典 API。建议在全局 store 中缓存字典数据:
|
||||
|
||||
**文件**:`pc/src/stores/dict.ts`
|
||||
|
||||
```typescript
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { getDictItemsByCode } from '@/api/dict'
|
||||
|
||||
export const useDictStore = defineStore('dict', () => {
|
||||
const dicts = ref<Record<string, any[]>>({})
|
||||
|
||||
const fetchDict = async (code: string) => {
|
||||
if (dicts.value[code]) {
|
||||
return dicts.value[code]
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await getDictItemsByCode(code)
|
||||
const items = res?.data || res || []
|
||||
dicts.value[code] = Array.isArray(items) ? items : []
|
||||
return dicts.value[code]
|
||||
} catch (err) {
|
||||
console.error(`Failed to fetch dict ${code}:`, err)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
return { dicts, fetchDict }
|
||||
})
|
||||
```
|
||||
|
||||
**在组件中使用:**
|
||||
|
||||
```javascript
|
||||
import { useDictStore } from '@/stores/dict'
|
||||
|
||||
const dictStore = useDictStore()
|
||||
const statusDict = await dictStore.fetchDict('user_status')
|
||||
```
|
||||
|
||||
### 2. 创建字典枚举文件
|
||||
|
||||
建议为常用字典创建枚举文件,便于维护:
|
||||
|
||||
**文件**:`pc/src/constants/dicts.ts`
|
||||
|
||||
```typescript
|
||||
// 字典编码常量
|
||||
export const DICT_CODES = {
|
||||
USER_STATUS: 'user_status',
|
||||
GENDER: 'gender',
|
||||
DEPT_TYPE: 'dept_type',
|
||||
POSITION_LEVEL: 'position_level',
|
||||
} as const
|
||||
|
||||
// 用于 TypeScript 类型
|
||||
export type DictCode = typeof DICT_CODES[keyof typeof DICT_CODES]
|
||||
```
|
||||
|
||||
**在组件中使用:**
|
||||
|
||||
```javascript
|
||||
import { DICT_CODES } from '@/constants/dicts'
|
||||
import { useDictStore } from '@/stores/dict'
|
||||
|
||||
const dictStore = useDictStore()
|
||||
const statusDict = await dictStore.fetchDict(DICT_CODES.USER_STATUS)
|
||||
```
|
||||
|
||||
### 3. 处理不同的数据值类型
|
||||
|
||||
字典值可能是数字、字符串或其他类型。确保比较时进行正确的类型转换:
|
||||
|
||||
```javascript
|
||||
const getStatusLabel = (status: any) => {
|
||||
// 转换为字符串便于比较
|
||||
const statusStr = String(status)
|
||||
|
||||
const item = statusDict.value.find((d: any) => {
|
||||
// 支持多种比较方式
|
||||
return (
|
||||
String(d.dict_value) === statusStr ||
|
||||
d.dict_value === status ||
|
||||
d.dict_value == status // 宽松比较
|
||||
)
|
||||
})
|
||||
|
||||
return item?.dict_label || '未知'
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 错误处理
|
||||
|
||||
始终为字典加载添加适当的错误处理和回退机制:
|
||||
|
||||
```javascript
|
||||
const fetchStatusDict = async () => {
|
||||
try {
|
||||
const res = await getDictItemsByCode('user_status')
|
||||
statusDict.value = res?.data || []
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch status dict:', err)
|
||||
// 使用默认的回退数据
|
||||
statusDict.value = [
|
||||
{ dict_label: '启用', dict_value: '1' },
|
||||
{ dict_label: '禁用', dict_value: '0' },
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1: 字典数据在页面刷新后丢失了?
|
||||
|
||||
**A:** 这是正常的,字典数据只在组件的生命周期内存在。建议使用全局 store(Pinia)来缓存字典数据,或在每个需要的组件中通过 `onMounted` 加载。
|
||||
|
||||
### Q2: 后端添加了新的字典项,前端不显示新数据?
|
||||
|
||||
**A:** 前端缓存了字典数据。有两种解决方案:
|
||||
1. 刷新浏览器页面,重新加载字典
|
||||
2. 在后端修改字典后,调用缓存清除接口(如果有的话),或手动清除前端 store 中的字典缓存
|
||||
|
||||
### Q3: 字典编码对应的字典类型不存在?
|
||||
|
||||
**A:** 确保:
|
||||
1. 后端已在 `sys_dict_type` 表中添加了对应的字典类型记录
|
||||
2. 字典类型的状态(`status`)为启用(通常为 1)
|
||||
3. 至少添加了一项字典项(`sys_dict_item` 表中有数据)
|
||||
4. 字典编码(`dict_code`)完全匹配(区分大小写)
|
||||
|
||||
### Q4: 如何在后台管理系统中管理字典?
|
||||
|
||||
**A:** 通常在系统设置或配置模块中有"字典管理"功能,可以:
|
||||
- 添加/编辑/删除字典类型
|
||||
- 管理字典项(标签、值、排序、启用/禁用等)
|
||||
|
||||
具体路径取决于你的后台管理系统设计。
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
使用字典系统的核心步骤:
|
||||
|
||||
1. **后端准备**:在 `sys_dict_type` 和 `sys_dict_item` 表中添加字典数据
|
||||
2. **前端导入**:`import { getDictItemsByCode } from '@/api/dict'`
|
||||
3. **加载字典**:在 `onMounted` 或其他合适位置调用 API 加载
|
||||
4. **使用字典**:通过 `dict_value` 和 `dict_label` 来显示和存储数据
|
||||
5. **最优化**:使用 Pinia store 缓存字典,避免重复请求
|
||||
|
||||
通过字典系统,你可以灵活地管理应用中的枚举值和标签数据,而无需修改代码。
|
||||
Reference in New Issue
Block a user