feat: 字典加颜色字段 + 产品线主题标签字典化 (PR #1017)
这个提交包含在:
父节点
8dafe1b9cb
当前提交
42ba23bba6
@ -0,0 +1,198 @@
|
||||
# 字典加颜色字段 + 产品线主题标签字典化(前端要改)
|
||||
|
||||
**日期**:2026-04-20
|
||||
**PR**:#1017(Closes Issue #1016)
|
||||
**合并到 dev commit**:0dc2cb8c
|
||||
**后端服务**:hl-user-service(实质改动)+ hl-product-service-v2(仅 VO 注释完善)
|
||||
**影响**:管理端「字典管理」表单 + 「产品线/主题」编辑弹窗
|
||||
|
||||
---
|
||||
|
||||
## 为什么变
|
||||
|
||||
两件事一起做:
|
||||
|
||||
1. **字典数据项加 `color` 字段**:`sys_dict_data` 全局加可选 hex 颜色值,方便所有字典(不只是 product_tag)都能配色
|
||||
2. **产品线(主题)的「标签」从自由输入改为字典选择**:使用 `product_tag` 字典,标签存 dict_value(英文 code),展示用 dict_label(中文)+ color(颜色渲染)
|
||||
|
||||
> 后端「字典管理 CRUD + 产品线 VO 注释」全部就绪,**`product_tag` 字典本身的数据由 wx 在字典管理页录入,本次不预置业务数据**。
|
||||
|
||||
---
|
||||
|
||||
## 接口契约变化
|
||||
|
||||
### 1. 字典 CRUD —— 新增 `color` 字段
|
||||
|
||||
**类型**:`string | null`,hex 格式 `#RGB` / `#RRGGBB` / `#RRGGBBAA`,可为空字符串
|
||||
**校验**:后端正则 `^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$|^$`,传 `red` / `#XYZ` 等非 hex 会被 400 拒绝
|
||||
|
||||
#### POST /admin/dict/data
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"dictType": "product_tag",
|
||||
"dictLabel": "亲子游",
|
||||
"dictValue": "PARENT_CHILD",
|
||||
"icon": "🏖️",
|
||||
"color": "#FF5722", // ⭐ 新增可选
|
||||
"sortOrder": 10,
|
||||
"remark": "亲子主题标签"
|
||||
}
|
||||
```
|
||||
|
||||
#### PUT /admin/dict/data/{id}
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"dictLabel": "亲子游",
|
||||
"color": "#FF5722" // ⭐ 新增可选;不传则不覆盖
|
||||
}
|
||||
```
|
||||
|
||||
> PUT 语义:字段缺失 = 不改;传 `null` = 不改;传空字符串 `""` = 清空
|
||||
|
||||
#### GET 类接口 —— 响应新增 `color`
|
||||
|
||||
| Method | Path | 变化 |
|
||||
|---|---|---|
|
||||
| GET | `/admin/dict/data/{id}` | 响应新增 `color`(NULL 时省略) |
|
||||
| GET | `/admin/dict/data/{dictType}` | 响应每项新增 `color`(NULL 时省略) |
|
||||
| GET | `/admin/dict/all` | 响应每项新增 `color`(NULL 时省略) |
|
||||
| GET | `/dict/all`(公开,无需 token) | `dataList[].color`(NULL 时省略) |
|
||||
|
||||
**重要**:`color` 用了 `@JsonInclude(NON_NULL)`,**老字典 / 没配色的字典项响应里不会有 `color` 这个 key**,前端解构时记得用可选链 / 默认值,不要 `data.color.toUpperCase()` 这种硬解构。
|
||||
|
||||
#### `/dict/all` 公开接口响应样例
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"code": 200,
|
||||
"data": [
|
||||
{
|
||||
"dictType": "product_tag",
|
||||
"dictName": "产品标签",
|
||||
"dataList": [
|
||||
{
|
||||
"dictValue": "PARENT_CHILD",
|
||||
"dictLabel": "亲子游",
|
||||
"icon": "🏖️",
|
||||
"color": "#FF5722", // ⭐ 配了色就有
|
||||
"sort": 1,
|
||||
"remark": null
|
||||
},
|
||||
{
|
||||
"dictValue": "SELF_DRIVE",
|
||||
"dictLabel": "自驾",
|
||||
"icon": null,
|
||||
// ⭐ 没配色就没有 color 字段(NON_NULL)
|
||||
"sort": 2,
|
||||
"remark": null
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 产品线 VO(无字段类型变化,仅注释完善)
|
||||
|
||||
| 文件 | 字段 | 改动 |
|
||||
|---|---|---|
|
||||
| `ProductLineSaveReqVO.tags` | `List<String>` | 注释从「标签」改为「标签(product_tag字典的dict_value英文code列表,无数量上限,可为空)」+ example=`PARENT_CHILD,SELF_DRIVE` |
|
||||
| `ProductLineRespVO.tags` | `List<String>` | 注释从「产品线标签」改为「产品线标签(product_tag字典的dict_value英文code列表,可为空)」+ example=`PARENT_CHILD,SELF_DRIVE` |
|
||||
|
||||
**接口签名 0 变化**:`POST /admin/product/line` / `PUT /admin/product/line/{lineId}` / `GET /admin/product/line/{lineId}` 的 `tags` 字段类型仍是 `string[]`,前端字段类型不需要改,**只是语义换了**:
|
||||
|
||||
- 旧语义:自由文本数组(用户随便输入)
|
||||
- 新语义:`product_tag` 字典的 **dict_value(英文 code)** 数组
|
||||
|
||||
后端**不强制校验** tags 必须在 product_tag 字典中(兼容老数据 + 留前端逐步迁移空间)。
|
||||
|
||||
---
|
||||
|
||||
## 前端要做的事
|
||||
|
||||
### 1. 字典管理页 —— CRUD 表单加「颜色值」输入
|
||||
|
||||
- 在 `dictLabel` / `icon` 旁边加一个「颜色值」输入框
|
||||
- 推荐控件:**ColorPicker**(最佳);或简单 hex 文本框(placeholder:`#FF5722`,可为空)
|
||||
- **校验**:前端可不做(后端会校验),但建议加 hex 正则提示提升体验
|
||||
- 列表展示:在 dictLabel 旁渲染一个小色块(`<span style="background:{color}">`),无 color 时不渲染或灰色占位
|
||||
|
||||
### 2. dict store —— 自动透传,无需改代码
|
||||
|
||||
- `useDictStore` 内部按 `/dict/all` 整个对象塞入,每个 dataItem 多一个 `color` key 自动透传给业务页面
|
||||
- `getDictOptions(dictType)` 返回的每项都会带 `color`(如果配了)
|
||||
- 业务侧使用:`option.color || '#999'`(默认色逻辑由前端决定)
|
||||
|
||||
### 3. 产品线(主题)编辑弹窗 —— 标签控件改造
|
||||
|
||||
**这是本次最大的前端改造点。** 文件位置参考:`hl-ui/src/views/product-v2/line/index.vue:312` 附近。
|
||||
|
||||
**控件替换**:
|
||||
|
||||
- 旧:`n-dynamic-tags`(用户自由输入字符串)
|
||||
- 新:`n-select` 多选模式,options 来自 `useDictStore().getDictOptions('product_tag')`
|
||||
|
||||
```js
|
||||
// 伪代码示意
|
||||
const tagOptions = computed(() =>
|
||||
dictStore.getDictOptions('product_tag').map(item => ({
|
||||
label: item.dictLabel, // 中文展示
|
||||
value: item.dictValue, // 英文 code 入库
|
||||
color: item.color, // 用于自定义渲染
|
||||
}))
|
||||
)
|
||||
```
|
||||
|
||||
**多选无上限**(沿用现有自由度)。
|
||||
|
||||
**自定义渲染**(重点):
|
||||
|
||||
- `n-select` 的 `render-tag` / `render-label` slot 用 `option.color` 给 chip 上色
|
||||
- 卡片展示标签也按 color 渲染(`hl-ui/src/views/product-v2/line/index.vue:125-136` 那块卡片标签 chip)
|
||||
|
||||
```vue
|
||||
<n-tag :color="{ color: option.color || '#999', textColor: '#fff' }">
|
||||
{{ option.label }}
|
||||
</n-tag>
|
||||
```
|
||||
|
||||
### 4. 老数据兼容(关键)
|
||||
|
||||
现有 `product_line.tags` 里很可能存的是老的**自由文本**(中文随手输入),不是字典 code:
|
||||
|
||||
- **不要把 tags 里不在 product_tag 字典里的值过滤掉** —— 这会悄悄丢数据
|
||||
- **建议展示**:老 tag 值在 n-select 里按字符串原样回填(`v-model:value` 数组里塞进去),找不到对应 option 时按文本展示,灰色提示「已下架/未识别标签」
|
||||
- 用户保存时引导用户从 product_tag 字典里重新选,保存后老值就被覆盖
|
||||
|
||||
后端**保存时不校验** tags 必须在字典中,所以前端就算把老中文文本一起传上来也不会被拒。
|
||||
|
||||
---
|
||||
|
||||
## 兼容性 / 不影响的地方
|
||||
|
||||
- **老字典 color 为 null 时**:`/dict/all` 响应里就没有 `color` 这个 key(NON_NULL),前端按"无 color = 默认色(建议 #999 灰)"处理即可
|
||||
- **老 product_line.tags 自由文本仍可显示**:n-select 多选模式可以容纳"不在 options 里的值"(按 NaiveUI 文档 `tag` 模式或回退到字符串展示)
|
||||
- **mp 端产品线展示**:当前 mp 端 `MpProductLineVO` **没有 tags 字段**,本次也不加。如果以后小程序要展示标签 + 颜色,等明确需求再做
|
||||
|
||||
---
|
||||
|
||||
## 不在本次范围
|
||||
|
||||
- ❌ **不预置 product_tag 字典数据**:交付能力,wx 在字典管理页手动录入业务标签项
|
||||
- ❌ **不做老 product_line.tags 数据迁移**:现存自由文本由用户后续手工编辑替换
|
||||
- ❌ **不动 ProductDO 的 tags 字段**(产品本身的 tags 走另一个 product_tag 字典使用,已是单选,见 [2026-04-20_product-tag-single-select.md](./2026-04-20_product-tag-single-select.md))—— 本次只动「产品线/主题」的 tags
|
||||
- ❌ **不动 mp 端**:`/mp/product/line/*` 接口 tags 不返回,零前端改造影响
|
||||
|
||||
---
|
||||
|
||||
## 验证
|
||||
|
||||
- ✅ 测试环境 hl-user-service 已部署(commit 0dc2cb8c)
|
||||
- ✅ POST /admin/dict/data 带 `color` 字段保存成功
|
||||
- ✅ GET /dict/all 配过 color 的项响应带 color,未配的项无 color key
|
||||
- ✅ POST /admin/dict/data 传 `color: "red"` 返回 400 校验错误
|
||||
- ⏳ 前端 dict 管理页加 ColorPicker + 产品线弹窗改 n-select 后,wx 在字典管理页录入 product_tag 字典项
|
||||
正在加载...
x
在新工单中引用
屏蔽一个用户