Files
rxthinkcmf-wiki/RXThinkCMF_Laravel13_NaiveVue/zh/core/dict-guide.md
T
2026-10-08 17:50:29 +08:00

9.0 KiB
Raw Blame History

5.11 字典配置与使用

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

数据字典结构

数据字典由两层组成:

字典类型(laravel_dict)        字典项(laravel_dict_item)
├── gender(性别)              ├── 1 → 男
│                               ├── 2 → 女
│                               └── 3 → 保密
├── user_status(用户状态)     ├── 1 → 正常
│                               └── 2 → 禁用
├── example_status(案例状态)  ├── 1 → 在用
│                               └── 2 → 停用
└── ...
层级 表 说明
字典类型 laravel_dict 字典分类(如"性别""用户状态")
字典项 laravel_dict_item 字典的具体选项(如"1-男""2-女")

表结构

laravel_dict(字典主表):

字段 类型 说明 示例
id INT 主键 1
name VARCHAR(50) 字典名称 性别
code VARCHAR(50) 字典编码(唯一) gender
remark VARCHAR(500) 备注 性别字典
is_delete TINYINT(1) 软删除 0
create_user VARCHAR(50) 创建人 admin
create_time DATETIME 创建时间
update_user VARCHAR(50) 更新人
update_time DATETIME 更新时间

laravel_dict_item(字典项表):

字段 类型 说明 示例
id INT 主键 1
dict_id INT 字典 ID(外键) 1
name VARCHAR(50) 项名称(显示标签) 男
value VARCHAR(50) 项值(存储值) 1
sort INT 排序 1
note VARCHAR(200) 备注
is_delete + 四个审计字段 公共字段

系统内置字典

字典编码 字典名称 字典项
gender 性别 1=男, 2=女, 3=保密
user_status 用户状态 1=正常, 2=禁用
tenant_status 租户状态 1=启用, 0=禁用
example_type 案例类型 1=类型1, 2=类型2, 3=类型3, 4=类型4
example_status 案例状态 1=在用, 2=停用

(以数据库初始化脚本中的实际字典项为准,可在「数据管理 → 字典管理」中查看。)

实操:新增一个字典类型

场景

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

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

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

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

第 2 步:添加字典项

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

  1. 添加字典项:
字典值 字典标签 排序
1 基础 1
2 进阶 2
3 高级 3
  1. 保存(保存即触发 DictItemObserver,相关缓存自动失效)

第 3 步:在 Service 中关联字典

在模块的 Service 中配置 serializeMaps(需 use HasSerializeMaps):

class ExampleService extends BaseService
{
    use HasCrudOperations, HasQueryScopes, HasSerializeMaps, HasUniqueCheck;

    // ... 其他配置 ...

    /**
     * 字典翻译配置:[字段 => 字典编码]
     *
     * @var array<string, string>
     */
    protected array $serializeMaps = [
        'type'   => 'example_level',
        'status' => 'example_status',
    ];
}

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

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

class ExampleRequest extends BaseRequest
{
    public function rules(): array
    {
        return [
            'name'   => 'required|string|max:255',
            'type'   => 'nullable|integer|in:1,2,3',   // 限制为合法枚举值
            'status' => 'nullable|integer|in:1,2',
        ];
    }
}

在后端代码中使用

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

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

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

方式 2:手动调用 DictCacheService

use App\Services\DictCacheService;

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

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

// 获取下拉框选项
$options = DictCacheService::getOptions('example_status');
// [['label'=>'在用','value'=>'1'], ['label'=>'停用','value'=>'2']]

// 批量获取多个字典映射(列表翻译一次取词)
$maps = DictCacheService::getBatchTextMaps(['example_type', 'example_status']);

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

// ExampleService::import()(来自项目实际代码)
foreach ($data as &$row) {
    // Excel 中"在用" → 数据库中 1
    if (! empty($row['status']) && ! is_numeric($row['status'])) {
        $row['status'] = DictCacheService::getValue('example_status', $row['status']);
    }
}
unset($row);

在前端使用

获取字典项

// ui/src/api/common/index.ts(实际代码)
/**
 * 根据字典编码查询字典项数据
 * @param code 字典编码
 * @returns 返回结果
 */
export function getDictItemByCode(code) {
  return http.request({
    url: '/dict/item/getDictItemList/' + code,
    method: 'GET',
  });
}

下拉框使用

<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 { getDictItemByCode } from '@/api/common/index';

const statusOptions = ref([]);

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

多个字典批量获取

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

字典值显示为 Tag

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

缓存管理

DictCacheService 采用两级缓存(请求级 + 持久缓存)。修改字典数据后的缓存失效:

# 手动刷新接口(认证组内)
GET /api/dict/refreshCache

# 命令行兜底
php artisan dict:clear-cache

缓存失效时机

时机 操作 说明
字典项新增/修改/删除 自动清除 DictItemObserver 监听模型事件
手动刷新 GET /api/dict/refreshCache 清除所有字典缓存(兜底)
命令行清理 php artisan dict:clear-cache 直接改库等绕过模型事件时使用
缓存过期 自动失效 TTL 见 DictCacheService::CACHE_TTL

:::tip 正常走后台接口修改字典会自动失效缓存;只有绕过模型直接操作数据库(如手工 SQL 改字典项)才需要手动刷新。 :::

字典 vs 硬编码

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

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

何时硬编码: 值几乎不变、单次使用、性能极致敏感

命名规范

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

最佳实践

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