docs:添加标签按钮简化与自定义弹窗设计
This commit is contained in:
@@ -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 模式下样式与小程序深色太空主题一致。
|
||||
Reference in New Issue
Block a user