# 将 storage-cells-summary 组件的 message 弹窗改为 wd-popup 底部弹出 ## 当前状态分析 ### 组件位置 `src/components/storage-cells-summary/index.vue` ### 当前弹窗使用情况 组件使用 `useMessage()` 调用 `wot-design-uni` 的 `MessageBox` 组件进行弹窗交互: 1. **存入流程** (`handleDepositFlow`): - `message.alert()`: 显示生成的密码(第135-140行) - `message.prompt()`: 密码验证输入(第143-153行) 2. **取出流程** (`handleRetrieveFlow`): - `message.prompt()`: 输入取出密码(第204-210行) ### 现有 wd-popup 使用模式 从代码库中找到的示例: - `src/components/position-edit/index.vue`: 底部弹出 + 安全区配置 - `src/pages/index/components/product-container.vue`: 简单底部弹出 **关键配置**: ```vue ``` ## 需求总结 1. 将 `message.alert` 和 `message.prompt` 调用改为自定义 `wd-popup` 组件 2. 使用底部弹出位置 (`position="bottom"`) 3. 启用底部安全区 (`safe-area-inset-bottom="true"`) 4. 自定义弹窗内容展示 5. 保持现有业务逻辑不变 ## 设计方案 ### 方案一:创建独立弹窗子组件 **优点**: 代码结构清晰,易于维护 **缺点**: 需要创建新文件,增加组件复杂度 ### 方案二:在当前组件内实现多个弹窗状态 **优点**: 简单直接,无需新增文件 **缺点**: 组件内部状态增多,模板复杂度增加 **推荐方案二**,因为弹窗逻辑相对简单,且与当前组件紧密耦合。 ## 状态机设计 根据用户要求采用状态机模式管理弹窗流程。设计以下状态: ### 状态定义 ```typescript type PopupState = | { type: 'idle' } // 空闲状态 | { type: 'password-show', password: string } // 显示密码 | { type: 'password-verify', correctPassword: string } // 密码验证输入 | { type: 'retrieve-input' } // 取出密码输入 | { type: 'processing', action: 'deposit' | 'retrieve' } // 处理中 ``` ### 状态转换 1. **存入流程**: - `idle` → `password-show` (显示生成的密码) - `password-show` → `password-verify` (用户点击"已记住") - `password-verify` → `processing` (验证通过,打开格口) - `processing` → `idle` (完成) 2. **取出流程**: - `idle` → `retrieve-input` (输入取出密码) - `retrieve-input` → `processing` (输入完成,打开格口) - `processing` → `idle` (完成) ### 状态管理实现 使用 `reactive` 或 `ref` 管理当前状态,通过状态转换函数实现流程控制。 ## 详细实现计划 ### 1. 移除 useMessage 导入和声明 - 删除第5行: `import { useMessage } from 'wot-design-uni'` - 删除第45行: `const message = useMessage()` - 注意:其他API导入保持不变 - wd-password-input 组件通常已全局注册,无需额外导入 ### 2. 实现状态机 ```typescript // 状态定义 type PopupState = | { type: 'idle' } | { type: 'password-show', password: string } | { type: 'password-verify', correctPassword: string } | { type: 'retrieve-input' } | { type: 'processing', action: 'deposit' | 'retrieve' } // 当前状态 const currentState = ref({ type: 'idle' }) // 弹窗输入值 const popupInputValue = ref('') const passwordLength = 4 // 状态转换函数 function transitionTo(state: PopupState) { currentState.value = state // 重置输入值当进入需要输入的弹窗 if (state.type === 'password-verify' || state.type === 'retrieve-input') { popupInputValue.value = '' } } // 根据状态计算弹窗显示属性 const popupVisible = computed(() => currentState.value.type !== 'idle') const popupTitle = computed(() => { const state = currentState.value switch (state.type) { case 'password-show': return '密码已生成' case 'password-verify': return '密码验证' case 'retrieve-input': return '输入密码' default: return '' } }) const popupMessage = computed(() => { const state = currentState.value switch (state.type) { case 'password-show': return `请牢记你的暂存密码为:${state.password}\n请确认已打开的柜子,放置物品后将柜子关闭。` case 'password-verify': return '请输入刚才显示的密码进行验证' case 'retrieve-input': return '请输入格口密码' default: return '' } }) const popupConfirmText = computed(() => { const state = currentState.value switch (state.type) { case 'password-show': return '已记住' case 'password-verify': return '验证' case 'retrieve-input': return '确认' default: return '确认' } }) const showCancelButton = computed(() => { const state = currentState.value return state.type === 'password-verify' || state.type === 'retrieve-input' }) ``` ### 3. 重构业务流程 ```typescript // 存入流程(状态机驱动) async function handleDepositFlow() { try { depositLoading.value = true // 1. 分配格口 const response = await storeItemApi({ shopId: props.shopId, cellType: selectedCellType.value }) const password = response.data?.password || '' if (!password) { throw new Error('格口分配失败,未获取到密码') } // 2. 显示密码(状态转换) transitionTo({ type: 'password-show', password }) // 注意:原流程在这里等待用户点击"已记住" // 现在改为在弹窗确认事件中处理 } catch (error) { // 错误处理 console.error('存入流程失败:', error) uni.showToast({ title: (error as any)?.message || '操作失败', icon: 'error', duration: 3000 }) transitionTo({ type: 'idle' }) } finally { depositLoading.value = false } } // 取出流程(状态机驱动) async function handleRetrieveFlow() { // 直接进入密码输入状态 transitionTo({ type: 'retrieve-input' }) } // 处理弹窗确认事件 async function handlePopupConfirm() { const state = currentState.value switch (state.type) { case 'password-show': // 点击"已记住",进入密码验证 transitionTo({ type: 'password-verify', correctPassword: state.password }) break case 'password-verify': // 验证密码 if (popupInputValue.value !== state.correctPassword) { uni.showToast({ title: '密码不正确', icon: 'error' }) return } // 打开格口 await performOpenByPassword(popupInputValue.value, 'deposit') break case 'retrieve-input': // 打开格口 await performOpenByPassword(popupInputValue.value, 'retrieve') break } } // 处理弹窗取消/关闭事件 function handlePopupCancel() { transitionTo({ type: 'idle' }) } // 统一的打开格口函数 async function performOpenByPassword(password: string, action: 'deposit' | 'retrieve') { transitionTo({ type: 'processing', action }) try { await openByPassword({ shopId: props.shopId, password: String(password) }) uni.showToast({ title: '格口已打开', icon: 'success' }) // 刷新数据 await refresh() } catch (error) { console.error('打开格口失败:', error) uni.showToast({ title: (error as any)?.message || '操作失败', icon: 'error', duration: 3000 }) } finally { transitionTo({ type: 'idle' }) } } ``` ### 4. 实现 wd-popup 模板 ```vue {{ popupTitle }} {{ popupMessage }} {{ currentState.action === 'deposit' ? '正在存入...' : '正在取出...' }} 取消 {{ popupConfirmText }} ``` ### 5. 更新样式 在现有 SCSS 中添加弹窗相关样式: ```scss .popup-content { display: flex; flex-direction: column; gap: 32rpx; .popup-title { font-size: 36rpx; font-weight: 600; color: #333; text-align: center; } .popup-message { font-size: 28rpx; color: #666; line-height: 1.5; white-space: pre-line; text-align: center; } .popup-password-input { margin: 32rpx 0; } .processing-state { display: flex; flex-direction: column; align-items: center; gap: 24rpx; padding: 48rpx 0; .processing-text { font-size: 28rpx; color: #666; } } .popup-actions { display: flex; gap: 16rpx; margin-top: 32rpx; } } ``` ## 实施步骤 1. **准备阶段** - 备份当前文件 - 分析现有弹窗调用的所有参数和返回值 2. **状态变量添加** - 在 script setup 中添加弹窗状态变量 - 创建弹窗控制函数 3. **模板修改** - 添加 wd-popup 组件模板 - 设计弹窗内容布局 - 添加输入框和按钮 4. **业务流程重构** - 替换 `message.alert` 调用 - 替换 `message.prompt` 调用 - 更新异步流程处理 5. **样式适配** - 添加弹窗内容样式 - 调整响应式布局 6. **测试验证** - 测试存入流程弹窗 - 测试取出流程弹窗 - 验证底部安全区效果 ## 注意事项 1. **向后兼容**: 保持现有的 `emit('deposit')` 等事件发射 2. **错误处理**: 保留现有的错误处理逻辑 3. **用户体验**: 保持相似的交互流程和提示信息 4. **性能考虑**: 使用 `lazy-render`(默认启用)避免不必要的渲染 ## 风险与缓解 1. **流程中断**: 仔细测试每个弹窗转换点,确保业务流程完整 2. **样式不一致**: 参考现有弹窗样式,保持视觉统一 3. **输入验证**: 确保密码验证逻辑正确移植 ## 预期结果 - 所有弹窗改为底部弹出的 `wd-popup` - 启用底部安全区适配 - 业务功能完全正常 - 用户体验保持一致