今天我们来讲MES系统的字典管理。字典管理看上去功能可能很简单,实际上他包含的内容还是比较多,麻雀虽小五脏俱全。其中包括:字典管理、缓存管理、枚举类生成、字典解析。
1. 数据库设计
1.1 ER图
1.2 表明细
系统字典
| 字段名 | 类型 | 是否为空 | 默认值 | 描述 | 主键 |
|---|---|---|---|---|---|
| id | BIGINT | NOT NULL | 主键ID | 是 | |
| dict_name | VARCHAR(128) | NOT NULL | 字典名称 | ||
| dict_code | VARCHAR(128) | NOT NULL | 字典编码 | ||
| description | VARCHAR(128) | 描述 | |||
| status | INT | NOT NULL | 状态 | ||
| create_by | BIGINT | ||||
| create_time | DATETIME | ||||
| update_by | BIGINT | ||||
| update_time | DATETIME | ||||
| 字典项 |
| 字段名 | 类型 | 是否为空 | 默认值 | 描述 | 主键 |
|---|---|---|---|---|---|
| id | BIGINT | NOT NULL | 主键ID | 是 | |
| dict_id | BIGINT | NOT NULL | 字典ID | ||
| dict_code | VARCHAR(128) | NOT NULL | 字典编码 | ||
| item_code | VARCHAR(128) | NOT NULL | 字典项编码 | ||
| item_text | VARCHAR(128) | NOT NULL | 字典项文本 | ||
| item_value | VARCHAR(128) | NOT NULL | 字典项键值 | ||
| color | VARCHAR(128) | 标签颜色 | |||
| description | VARCHAR(128) | 描述 | |||
| sort | INT | 排序 | |||
| status | INT | 状态 | |||
| create_time | DATETIME | ||||
| update_time | DATETIME | ||||
| create_by | BIGINT | ||||
| update_by | BIGINT |
2. 功能讲解
2.1 增删查改
点击新增,添加字典信息;字典编码、字典名称是必填项
点击列表操作列中的【字典项】添加字典项
注: 这里的标签颜色,会在使用到字典的对应字段以Tag的形式展示;如下面的客户类型和是否启用。
补充: 列表中有一个“刷新缓存”的按钮,虽然字典保存和更新都做了自动刷新,但是有时候也可能出现一些异常情况没有刷新到缓存,所以我在这里做了一个手动刷新的功能。
2.2 生成Java枚举类
很多时候我们创建了字典,但是在后端java代码也需要创建对应的枚举类,这里提供了直接在界面上生成Java枚举类代码。 操作如下: 选择要生产枚举类的字典,点击操作栏中字典项,在弹出的抽屉上点击“生成枚举类按钮”。
这里实现是通过自定义的Freemarker模版,把字典信息传入生产枚举类。
2.3 后端启动设置字典缓存
应用启动后执行有很多种方法,这里我选择了比较简单的做法:使用@PostContrut注解
//src/main/java/com/hgyc/mom/system/service/impl/SysDictItemServiceImpl.java
/**
* 项目启动时,初始化字典到缓存
*/
@PostConstruct
public void init()
{
log.info("加载字典缓存数据!");
loadingDictCache();
}
/**
* 加载字典缓存数据
*/
@Override
public void loadingDictCache()
{
LambdaQueryWrapper<SysDictItem> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysDictItem::getStatus, CommonStatus.ENABLE.getValue());
List<SysDictItem> itemList = this.list(queryWrapper);
if (!CollectionUtils.isEmpty(itemList)) {
Map<String, List<SysDictItem>> dictDataMap = itemList.stream().collect(Collectors.groupingBy(SysDictItem::getDictCode));
for (Map.Entry<String, List<SysDictItem>> entry : dictDataMap.entrySet())
{
DictUtils.setDictCache(entry.getKey(), entry.getValue().stream().sorted(Comparator.comparing(SysDictItem::getSort)).collect(Collectors.toList()));
}
}
}
2.4 字典工具类
/**
* 字典工具类
*
* @author fwj
*/@Slf4j
public class DictUtils
{
/**
* 分隔符
*/
public static final String SEPARATOR = ",";
/**
* 设置字典缓存
*
* @param key 参数键
* @param dictDatas 字典数据列表
*/
public static void setDictCache(String key, List<SysDictItem> dictDatas)
{
SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas);
}
/**
* 获取字典缓存
*
* @param key 参数键
* @return dictDatas 字典数据列表
*/
public static List<SysDictItem> getDictCache(String key)
{
return SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
}
/**
* 根据字典类型和字典值获取字典标签
*
* @param dictType 字典类型
* @param dictValue 字典值
* @return 字典标签
*/
public static String getDictLabel(String dictType, String dictValue)
{
if (StringUtils.isEmpty(dictValue))
{
return StringUtils.EMPTY;
}
return getDictLabel(dictType, dictValue, SEPARATOR);
}
/**
* 根据字典类型和字典标签获取字典值
*
* @param dictType 字典类型
* @param dictLabel 字典标签
* @return 字典值
*/
public static String getDictValue(String dictType, String dictLabel)
{
if (StringUtils.isEmpty(dictLabel))
{
return StringUtils.EMPTY;
}
return getDictValue(dictType, dictLabel, SEPARATOR);
}
/**
* 根据字典类型和字典值获取字典标签
*
* @param dictType 字典类型
* @param dictValue 字典值
* @param separator 分隔符
* @return 字典标签
*/
public static String getDictLabel(String dictType, String dictValue, String separator)
{
StringBuilder propertyString = new StringBuilder();
List<SysDictItem> datas = getDictCache(dictType);
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
if (StringUtils.containsAny(separator, dictValue))
{
for (SysDictItem dict : datas)
{
for (String value : dictValue.split(separator))
{
if (value.equals(dict.getItemValue()))
{
propertyString.append(dict.getItemText()).append(separator);
break;
}
}
}
}
else
{
for (SysDictItem dict : datas)
{
if (dictValue.equals(dict.getItemValue()))
{
return dict.getItemText();
}
}
}
return StringUtils.stripEnd(propertyString.toString(), separator);
}
/**
* 根据字典类型和字典标签获取字典值
*
* @param dictType 字典类型
* @param dictLabel 字典标签
* @param separator 分隔符
* @return 字典值
*/
public static String getDictValue(String dictType, String dictLabel, String separator)
{
StringBuilder propertyString = new StringBuilder();
List<SysDictItem> datas = getDictCache(dictType);
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
if (StringUtils.containsAny(separator, dictLabel))
{
for (SysDictItem dict : datas)
{
for (String label : dictLabel.split(separator))
{
if (label.equals(dict.getItemText()))
{
propertyString.append(dict.getItemValue()).append(separator);
break;
}
}
}
}
else
{
for (SysDictItem dict : datas)
{
if (dictLabel.equals(dict.getItemText()))
{
return dict.getItemValue();
}
}
}
return StringUtils.stripEnd(propertyString.toString(), separator);
}
/**
* 根据字典类型获取字典所有值
*
* @param dictType 字典类型
* @return 字典值
*/
public static String getDictValues(String dictType)
{
StringBuilder propertyString = new StringBuilder();
List<SysDictItem> datas = getDictCache(dictType);
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
for (SysDictItem dict : datas)
{
propertyString.append(dict.getItemValue()).append(SEPARATOR);
}
return StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
}
/**
* 根据字典类型获取字典所有标签
*
* @param dictType 字典类型
* @return 字典值
*/
public static String getDictLabels(String dictType)
{
StringBuilder propertyString = new StringBuilder();
List<SysDictItem> datas = getDictCache(dictType);
if (StringUtils.isNull(datas))
{
return StringUtils.EMPTY;
}
for (SysDictItem dict : datas)
{
propertyString.append(dict.getItemText()).append(SEPARATOR);
}
return StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
}
/**
* 删除指定字典缓存
*
* @param key 字典键
*/
public static void removeDictCache(String key)
{
SpringUtils.getBean(RedisCache.class).deleteObject(getCacheKey(key));
}
/**
* 清空字典缓存
*/
public static void clearDictCache()
{
Collection<String> keys = SpringUtils.getBean(RedisCache.class).keys(CacheConstants.SYS_DICT_KEY + "*");
SpringUtils.getBean(RedisCache.class).deleteObject(keys);
}
/**
* 设置cache key
** @param configKey 参数键
* @return 缓存键key
*/ public static String getCacheKey(String configKey)
{
return CacheConstants.SYS_DICT_KEY + configKey;
}
2.5 前端表格自动翻译字典
使用自定义组件 <TableDictTag>
import TableDictTag from "@/components/grid/TableDictTag";
···
const columns: ColumnsType<Client> = [
{
title: "客户类型",
width: 120,
key: "clientType",
dataIndex: "clientType",
render: (_, record) => (
<TableDictTag dictCode="client_type" value={record.clientType ??''}/>)
},
]
参数说明: dictCode : 字典编码 value : 字典值
效果如下:
2.6 下拉组件使用字典
使用自定义Hooks :useDictionary
使用如下:
import useDictionary from "@/hooks/system/useDictionary";
...
const statusOptions = useDictionary("system_status");
...
<Select options={statusOptions} allowClear/>
说明: 这里除了使用了自定义hooks外还使用了zustand做了一个本地的store缓存,详细请看源码:
- src/store/useDictionaryStore.ts
- src/hooks/system/useDictionary.ts
开源项目地址:
欢迎在评论区分享你的技术选型经验,或对本文方案的改进建议!