docs:添加标签按钮简化与自定义弹窗设计

This commit is contained in:
2026-06-24 22:31:54 +08:00
parent 11f1638eef
commit 1900ba6efc
@@ -0,0 +1,289 @@
---
author: Peanut
created_at: 2026-06-24
purpose: 明确小程序编辑资料页"添加"按钮文字简化、字数限制收紧到4字、标签不换行、弹窗替换为深色太空主题自定义组件的改动设计。
---
# 小程序添加标签按钮简化与自定义弹窗设计
## 背景
当前小程序编辑资料页(`mini-program/src/pages/onboarding/index.vue`)和管理标签页(`mini-program/src/pages/onboarding/tag-manage.vue`)的"添加"按钮和弹窗存在以下问题:
- 编辑页性格标签的添加按钮文字是"+ 添加标签",兴趣爱好是"+ 添加兴趣",文字偏长,与紧凑的标签网格不协调。
- 添加标签的字数上限是 8 个字符,偏宽松,容易生成过长的标签破坏网格布局。
- 标签 `.tag-choice` / `.tag-text` 没有设置 `white-space: nowrap`,长标签会自动换行撑高单元格。
- 添加和删除确认都使用 `uni.showModal`,在 H5 模式下是 uni-app 默认的白底蓝按钮样式,与小程序整体的深色太空主题(深紫底、毛玻璃、紫色渐变、金色强调)严重不一致。
## 目标
1. 编辑页两个添加按钮文字统一简化为"+ 添加"。
2. 添加标签字数限制收紧到 4 个字。
3. 标签文字过长时不换行,用省略号截断。
4. 添加和删除确认弹窗替换为自定义弹窗组件,样式与深色太空主题保持一致。
## 非目标
- 不改标签的选择/取消选中逻辑、最多 5 个的限制。
- 不改后端接口或数据结构。
- 不改管理页的左滑删除手势。
- 不引入第三方 UI 组件库。
## 关键设计决策
| 决策点 | 选择 | 理由 |
|---|---|---|
| 添加按钮文字 | " 添加"(去掉"标签"/"兴趣"后缀) | 用户明确要求只保留"添加"两字 |
| 弹窗 title | 保持 `添加性格标签` / `添加兴趣爱好` | 按钮简、弹窗清,分开处理 |
| 字数限制 | 4 个字 | 用户明确要求 |
| toast 文案 | "内容不能超过4个字" | 用户指定 |
| 不换行实现 | `white-space: nowrap` + `overflow: hidden` + `text-overflow: ellipsis` | 标准三件套,标签固定高度不撑高 |
| 弹窗实现 | 新建自定义组件 `TagDialog.vue`,两种模式(input / confirm | 替换 `uni.showModal`,样式可控、深色主题统一 |
## 新增组件
### `mini-program/src/components/TagDialog.vue`
**职责**:统一的弹窗组件,支持输入模式和确认模式,样式与深色太空主题一致。
**Props**
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
| `visible` | `Boolean` | `false` | 是否显示弹窗 |
| `mode` | `String` | `'input'` | `'input'``'confirm'` |
| `title` | `String` | `''` | 弹窗标题 |
| `content` | `String` | `''` | confirm 模式的提示文案 |
| `placeholder` | `String` | `''` | input 模式的输入框占位符 |
| `modelValue` | `String` | `''` | input 模式的输入值(支持 v-model |
**Emits**
| 事件 | 参数 | 说明 |
|---|---|---|
| `confirm` | input 模式:当前输入值;confirm 模式:无 | 点击确定按钮 |
| `cancel` | 无 | 点击取消按钮或遮罩层 |
| `update:modelValue` | 新值 | 输入框内容变化 |
| `update:visible` | `false` | 关闭弹窗 |
**模板结构**
```
┌────────────────────────────────┐
│ 遮罩层(半透明深色) │
│ ┌──────────────────────────┐ │
│ │ ✦ 添加性格标签 │ │ ← 标题 + 金色 ✦
│ │ ┌────────────────────┐ │ │
│ │ │ 输入框(深色底紫边)│ │ │ ← input 模式才有
│ │ └────────────────────┘ │ │
│ │ 确定删除「xxx」吗?... │ │ ← confirm 模式才有
│ │ │ │
│ │ [ 取消 ] [ 确定 ] │ │ ← 按钮
│ └──────────────────────────┘ │
└────────────────────────────────┘
```
**样式**(与小程序深色太空主题一致):
- 遮罩层:`rgba(3, 2, 13, 0.72)` 全屏覆盖
- 弹窗卡片:`glass-card` 毛玻璃 + `rgba(155, 110, 255, 0.14)` 紫色边框 + `22rpx` 圆角
- 标题:白色 `#fff` 加粗,配金色 `✦``#ffd58c`
- 输入框:深色底 `rgba(10, 12, 40, 0.66)` + 紫色边框 `rgba(151, 111, 255, 0.22)`
- 取消按钮:透明底 + 紫色边框
- 确定按钮:紫色渐变 `linear-gradient(180deg, #a855ff, #6b29ce)`
- 所有尺寸用 `rpx`
## 编辑资料页改动(`mini-program/src/pages/onboarding/index.vue`
### 模板
- 性格标签 panel 末尾占位:` 添加标签`` 添加`
- 兴趣爱好 panel 末尾占位:` 添加兴趣`` 添加`
-`<scroll-view>` 之外、`</view>` 之前挂载 `<TagDialog>` 组件:
```html
<TagDialog
v-model="dialogInput"
v-model:visible="dialogVisible"
:mode="dialogMode"
:title="dialogTitle"
:placeholder="dialogPlaceholder"
:content="dialogContent"
@confirm="onDialogConfirm"
@cancel="onDialogCancel"
/>
```
### script setup
- 引入组件:`import TagDialog from '../../components/TagDialog.vue'`
- 新增响应式状态:
```javascript
const dialogVisible = ref(false)
const dialogMode = ref('input') // 'input' 或 'confirm'
const dialogTitle = ref('')
const dialogPlaceholder = ref('')
const dialogContent = ref('')
const dialogInput = ref('')
const dialogAction = ref(null) // 记录当前弹窗对应的动作:{ kind: 'add'|'delete', type: 'personality'|'hobby', tag?: string }
```
- 重构 `addCustomTag`:改为打开弹窗(input 模式),不再调 `uni.showModal`
```javascript
const addCustomTag = (type) => {
const isPersonality = type === 'personality'
dialogMode.value = 'input'
dialogTitle.value = isPersonality ? '添加性格标签' : '添加兴趣爱好'
dialogPlaceholder.value = '请输入标签(最多4个字)'
dialogInput.value = ''
dialogAction.value = { kind: 'add', type }
dialogVisible.value = true
}
```
- 新增 `onDialogConfirm`:处理弹窗确认(add / delete 两种动作):
```javascript
const onDialogConfirm = (value) => {
const action = dialogAction.value
if (!action) return
if (action.kind === 'add') {
handleAddTag(action.type, value)
} else if (action.kind === 'delete') {
handleDeleteTag(action.type, action.tag)
}
dialogVisible.value = false
}
const onDialogCancel = () => {
dialogAction.value = null
dialogVisible.value = false
}
```
- 抽出 `handleAddTag`(原 `addCustomTag` 的 success 回调逻辑,字数限制改为 4):
```javascript
const handleAddTag = (type, rawValue) => {
const value = String(rawValue || '').trim()
const isPersonality = type === 'personality'
const library = isPersonality ? form.personalityTagLibrary : form.hobbyLibrary
const selected = isPersonality ? form.personalityTags : form.hobbies
if (!value) {
uni.showToast({ title: '请输入标签内容', icon: 'none' })
return
}
if (value.length > 4) {
uni.showToast({ title: '内容不能超过4个字', icon: 'none' })
return
}
if (library.includes(value)) {
uni.showToast({ title: '标签已存在', icon: 'none' })
return
}
library.unshift(value)
if (selected.length >= 5) {
uni.showToast({ title: '已达上限,仅加入标签库', icon: 'none' })
return
}
selected.push(value)
}
```
### 样式
`.tag-choice` 增加:
```css
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
```
## 管理页改动(`mini-program/src/pages/onboarding/tag-manage.vue`
### 模板
挂载 `<TagDialog>` 组件(confirm 模式):
```html
<TagDialog
v-model:visible="dialogVisible"
:mode="'confirm'"
:title="dialogTitle"
:content="dialogContent"
@confirm="onDialogConfirm"
@cancel="onDialogCancel"
/>
```
### script setup
- 引入组件:`import TagDialog from '../../components/TagDialog.vue'`
- 新增响应式状态:
```javascript
const dialogVisible = ref(false)
const dialogTitle = ref('确认删除')
const dialogContent = ref('')
const pendingTag = ref(null)
```
- 重构 `confirmDelete`:改为打开弹窗(confirm 模式):
```javascript
const confirmDelete = (tag) => {
pendingTag.value = tag
dialogContent.value = `确定删除「${tag}」吗?删除后不可恢复`
dialogVisible.value = true
}
const onDialogConfirm = () => {
if (pendingTag.value) {
deleteTag(pendingTag.value)
pendingTag.value = null
}
dialogVisible.value = false
}
const onDialogCancel = () => {
pendingTag.value = null
dialogVisible.value = false
}
```
- `deleteTag` 函数保持不变(已经在 Task 6 实现)。
### 样式
`.tag-text` 增加:
```css
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
```
## 文件清单
### 新增文件
- `mini-program/src/components/TagDialog.vue`:自定义弹窗组件。
### 修改文件
- `mini-program/src/pages/onboarding/index.vue`:按钮文字简化、字数限制 4、不换行样式、引入 TagDialog、重构 addCustomTag。
- `mini-program/src/pages/onboarding/tag-manage.vue`:引入 TagDialog、重构 confirmDelete、不换行样式。
## 验收标准
1. 编辑页性格标签和兴趣爱好的添加按钮只显示"+ 添加"。
2. 点击"+ 添加"弹出深色太空主题弹窗(毛玻璃卡片、紫色渐变、金色 ✦),不再是白底蓝按钮。
3. 输入 5 个字时 toast 提示"内容不能超过4个字"。
4. 输入 4 个字以内的新标签,确认后标签出现在列表最前面。
5. 标签文字过长时(如 4 字标签在窄屏),单元格内省略号截断,不换行撑高。
6. 管理页点 × 或左滑删除时,弹出深色主题确认弹窗,文案"确定删除「xxx」吗?删除后不可恢复"。
7. 弹窗的取消按钮和遮罩层点击都能关闭弹窗,不触发任何动作。
8. 弹窗在 H5 模式下样式与小程序深色太空主题一致。