Skip to content

5.11 字典配置与使用 ​

本章概要

数据字典的完整使用流程,从新增字典类型到在代码中使用字典数据的实操指南。字典是系统中管理枚举值的标准方式,替代硬编码,实现"数据与代码分离"。

数据字典结构 ​

数据字典由两层组成:

text
字典类型(think_dict)          字典项(think_dict_item)
├── gender(性别)              ├── 1 → 男
│                               └── 2 → 女
├── user_status(用户状态)     ├── 0 → 禁用
│                               └── 1 → 启用
├── article_status(文章状态)  ├── 0 → 草稿
│                               └── 1 → 已发布
└── ...
层级表说明
字典类型think_dict字典分类(如"性别""用户状态")
字典项think_dict_item字典的具体选项(如"1-男""2-女")

表结构 ​

think_dict(字典主表):

字段类型说明示例
idINT主键1
nameVARCHAR(50)字典名称性别
codeVARCHAR(50)字典编码(唯一)gender
remarkVARCHAR(500)备注性别字典
is_deleteTINYINT(1)软删除0
create_userVARCHAR(50)创建人admin
create_timeDATETIME创建时间
update_userVARCHAR(50)更新人
update_timeDATETIME更新时间

think_dict_item(字典项表):

字段类型说明示例
idINT主键1
dict_idINT字典 ID(外键)1
nameVARCHAR(50)项名称(显示标签)男
valueVARCHAR(50)项值(存储值)1
sortINT排序1
noteVARCHAR(200)备注
is_deleteTINYINT(1)软删除0
create_userVARCHAR(50)创建人
create_timeDATETIME创建时间
update_userVARCHAR(50)更新人
update_timeDATETIME更新时间

系统内置字典 ​

字典编码字典名称字典项
gender性别0=女, 1=男
user_status用户状态0=禁用, 1=启用
article_status文章状态0=草稿, 1=已发布
tenant_status租户状态0=禁用, 1=启用
example_type案例类型0=类型一, 1=类型二
example_status案例状态0=禁用, 1=启用
data_scope数据权限1=全部, 2=本部门, 3=仅本人

实操:新增一个字典类型 ​

场景 ​

需要为"案例类型"模块添加一个下拉选项:1-基础 2-进阶 3-高级。

第 1 步:在后台添加字典类型 ​

登录管理后台,进入「数据管理 → 字典管理」:

  1. 点击「新增」按钮
  2. 填写信息:
字段值
字典名称案例类型
字典编码example_type
状态启用
  1. 保存

第 2 步:添加字典项 ​

在字典类型列表中点击「案例类型」,进入字典项管理:

  1. 添加字典项:
字典值字典标签排序
1基础1
2进阶2
3高级3
  1. 保存

第 3 步:在 Logic 中关联字典 ​

在模块的 Logic 中配置 serializeMaps:

php
class ExampleLogic extends BaseLogic
{
    // ... 其他配置 ...

    /**
     * 枚举显示名映射
     *
     * @var array
     */
    protected array $serializeMaps = [
        'type'   => 'example_type',
        'status' => 'example_status',
    ];
}

配置后,列表和详情接口会自动将 type=1 转换为 typeText='基础'。

第 4 步:在 Validate 中校验枚举值(可选) ​

php
class ExampleValidate extends Validate
{
    protected $rule = [
        'name'   => 'require',
        'type'   => 'in:1,2,3',        // 限制为合法枚举值
        'status' => 'in:0,1',
    ];
    protected $scene = [
        'add'    => ['name', 'type', 'status'],
        'update' => ['name', 'type', 'status'],
    ];
}

在后端代码中使用 ​

方式 1:通过 serializeMaps 自动转换(推荐) ​

php
// Logic 中配置后,查询数据自动补全
protected array $serializeMaps = [
    'status' => 'example_status',
];

// 查询结果自动包含 statusText 字段
// { "id": 1, "name": "测试", "status": 1, "statusText": "启用" }

方式 2:手动调用 DictService ​

php
use app\service\DictService;

// 根据值获取名称
$text = DictService::getText('example_status', 1);  // '启用'

// 根据名称反向获取值(导入时用)
$value = DictService::getValue('example_status', '启用');  // '1'

// 获取下拉框选项
$options = DictService::getOptions('example_status');
// [['label'=>'启用','value'=>'1'], ['label'=>'禁用','value'=>'0']]

// 获取完整字典项(含 id、sort、note)
$items = DictService::getFullItems('example_status');

方式 3:在导入时反向映射 ​

php
// ExampleLogic::import()
foreach ($data as &$row) {
    // Excel 中"基础" → 数据库中 1
    if (!empty($row['type']) && !is_numeric($row['type'])) {
        $row['type'] = DictService::getValue('example_type', $row['type']);
    }
}

在前端使用 ​

获取字典项 ​

typescript
// ui/src/api/common/index.ts
export function getDictItemList(code: string) {
  return http.request({
    url: '/dict/item/getDictItemList/' + code,
    method: 'GET',
  });
}

下拉框使用 ​

vue
<template>
  <el-select v-model="formData.status" placeholder="请选择状态">
    <el-option
      v-for="item in statusOptions"
      :key="item.value"
      :label="item.name"
      :value="item.value"
    />
  </el-select>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { getDictItemList } from '@/api/common/index';

const statusOptions = ref([]);

onMounted(async () => {
  statusOptions.value = await getDictItemList('example_status');
});
</script>

多个字典批量获取 ​

typescript
// 同时获取多个字典选项
const [genderOptions, statusOptions] = await Promise.all([
  getDictItemList('gender'),
  getDictItemList('user_status'),
]);

封装为通用 Hook ​

typescript
// ui/src/hooks/web/useDict.ts
import { ref, onMounted } from 'vue';
import { getDictItemList } from '@/api/common/index';

export function useDict(code: string) {
  const options = ref([]);
  const loading = ref(false);

  const load = async () => {
    loading.value = true;
    try {
      options.value = await getDictItemList(code);
    } finally {
      loading.value = false;
    }
  };

  onMounted(() => load());

  return { options, loading, reload: load };
}

// 使用
const { options: genderOptions } = useDict('gender');
const { options: statusOptions } = useDict('user_status');

字典值显示为 Tag ​

vue
<template>
  <el-tag :type="row.status === 1 ? 'success' : 'danger'">
    {{ row.statusText }}
  </el-tag>
</template>

缓存管理 ​

DictService 采用两级缓存(请求级 + 持久缓存,1小时过期)。修改字典数据后需刷新缓存:

bash
# 调用刷新接口
GET /api/dict/refreshCache

缓存失效时机 ​

时机操作说明
字典项新增/修改/删除自动清除该字典缓存DictLogic 中调用 DictService::clearCache($code)
手动刷新GET /dict/refreshCache清除所有字典缓存
缓存过期自动失效1 小时 TTL

字典数据变更后必须刷新缓存

修改字典项后,如果不刷新缓存,前端显示的可能仍是旧数据。建议在字典管理的增删改接口中自动调用 clearCache()。

字典 vs 硬编码 ​

方式优点缺点
字典(推荐)可在后台动态修改,无需改代码重启需要额外的数据库查询(有缓存)
硬编码简单直接,无额外查询修改需改代码重新部署

何时用字典: 值可能变化(状态、类型、分类)、需要前端下拉框、需要多处复用

何时硬编码: 值几乎不变(性别只有男女)、单次使用、性能极致敏感

命名规范 ​

类型命名规范示例
字典编码小写下划线example_type、user_status
字典值数字字符串1、2、3
字典标签中文名称基础、启用
字段名小写下划线type、status
显示名字段字段名 + TexttypeText、statusText

最佳实践 ​

实践说明
编码统一同一字典编码全局唯一,如 user_status
值用数字字典值使用数字字符串(1、2),便于排序和比较
标签用中文字典标签使用中文(启用、禁用),便于前端直接显示
排序有序通过 sort 字段控制下拉框选项顺序
备注说明在 note 字段记录特殊值的含义,便于维护
Validate 校验在 Validate 中用 in:0,1 限制合法值
导入导出导入时用 getValue 反向映射,导出时用 getText 正向翻译

小蚂蚁云团队 · 提供技术支持