From 1900ba6efc9feb2f4656c11b6d4dc0fcc133fcbd Mon Sep 17 00:00:00 2001 From: Peanut Date: Wed, 24 Jun 2026 22:31:54 +0800 Subject: [PATCH] =?UTF-8?q?docs=EF=BC=9A=E6=B7=BB=E5=8A=A0=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=E6=8C=89=E9=92=AE=E7=AE=80=E5=8C=96=E4=B8=8E=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E5=BC=B9=E7=AA=97=E8=AE=BE=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...26-06-24-tag-add-button-simplify-design.md | 289 ++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-24-tag-add-button-simplify-design.md diff --git a/docs/superpowers/specs/2026-06-24-tag-add-button-simplify-design.md b/docs/superpowers/specs/2026-06-24-tag-add-button-simplify-design.md new file mode 100644 index 0000000..9834156 --- /dev/null +++ b/docs/superpowers/specs/2026-06-24-tag-add-button-simplify-design.md @@ -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 末尾占位:`+ 添加兴趣` → `+ 添加` +- 在 `` 之外、`` 之前挂载 `` 组件: + +```html + +``` + +### 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`) + +### 模板 + +挂载 `` 组件(confirm 模式): + +```html + +``` + +### 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 模式下样式与小程序深色太空主题一致。