All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 16s
Docker Build & Deploy / Deploy to Production (push) Successful in 6s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s
Docker Build & Deploy / WeChat Notification (push) Successful in 1s
8.6 KiB
8.6 KiB
Context
当前项目中存在两个弹窗组件:
- PopupContainer.vue (V1): 使用 Vant 主题变量,支持 subtitle、header-actions 插槽、确认/取消按钮等丰富功能,默认高度 80%,被 18 个组件使用
- PopupContainerV2.vue (V2): 采用 Inter 字体和现代化设计风格(纯色背景、16px 圆角),API 更简洁(只提供 title、footer 插槽),默认高度 auto(最大 85%),已被 TransactionDetailSheet 使用
V1 和 V2 的 API 差异较大,V1 提供了更多开箱即用的功能(如内置按钮、subtitle),而 V2 追求灵活性和视觉一致性。此次迁移需要在保持功能不变的前提下,统一使用 V2 的现代化设计。
约束条件:
- 不能改变现有页面的交互逻辑和用户体验
- 需要保持暗色模式的正确支持
- 必须通过 ESLint 和现有的代码规范
Goals / Non-Goals
Goals:
- 统一弹窗组件为 PopupContainerV2,删除 V1 版本
- 迁移 18 个使用 V1 的组件,保持功能等价性
- 适配 API 差异(props → 插槽、样式调整)
- 确保迁移后视觉效果和交互逻辑一致
Non-Goals:
- 不重新设计弹窗的交互流程或视觉风格(完全按照 V2 现有设计)
- 不优化或重构业务逻辑(仅做组件替换和 API 适配)
- 不处理与弹窗无关的代码问题
Decisions
Decision 1: 迁移策略 - 逐文件手动迁移 vs 自动化脚本
选择: 手动逐文件迁移
理由:
- V1 和 V2 的 API 差异大(props → 插槽),无法通过简单的查找替换完成
- 每个组件对 subtitle、header-actions、确认/取消按钮的使用方式不同,需要根据业务语义定制迁移方案
- 自动化脚本的开发成本高于手动迁移 18 个文件的时间成本
- 手动迁移可以确保每个文件的视觉和逻辑正确性
备选方案:
- AST 转换工具(如 jscodeshift):复杂度高,难以处理插槽和样式的语义转换
Decision 2: subtitle 功能的迁移方式
选择: 根据业务语义分类处理
- 统计信息类 subtitle(如 "共 10 笔交易")→ 移至默认插槽顶部,使用自定义样式组件
- 纯文本副标题 → 移至默认插槽,或合并到 title 中(如 "分类详情 - 餐饮")
理由:
- V2 没有 subtitle prop,必须通过插槽实现
- 统计信息通常有特定的业务含义,应作为内容区域的一部分而非标题的附属
- 纯文本副标题可以简化为一级标题的扩展
备选方案:
- 扩展 V2 组件增加 subtitle prop:违背 V2 简化 API 的设计原则,不采纳
Decision 3: 确认/取消按钮的迁移方式
选择: 转换为 footer 插槽 + 手动事件绑定
实现模式:
<!-- V1 -->
<PopupContainer
show-confirm-button
show-cancel-button
confirm-text="确定"
@confirm="handleConfirm"
@cancel="handleCancel"
/>
<!-- V2 -->
<PopupContainerV2>
<template #footer>
<div class="footer-buttons">
<van-button plain @click="handleCancel">取消</van-button>
<van-button type="primary" @click="handleConfirm">确定</van-button>
</div>
</template>
</PopupContainerV2>
<style scoped>
.footer-buttons {
display: flex;
gap: 12px;
}
.footer-buttons .van-button {
flex: 1;
}
</style>
理由:
- V2 的 footer 插槽提供了足够的灵活性
- Vant Button 的样式与 V1 内置按钮一致,迁移成本低
- 手动绑定事件可以保持原有的业务逻辑不变
Decision 4: header-actions 插槽的处理
选择: 移至默认插槽顶部或改用自定义布局
理由:
- V2 没有 header-actions 插槽,标题区域只有标题文本和关闭按钮
- 根据现有代码(如 BudgetCard.vue、SavingsBudgetContent.vue),header-actions 通常是"编辑"、"删除"等操作按钮
- 这些按钮更适合放在内容区域顶部或 footer 中,符合 V2 的极简标题设计
实现模式:
<!-- V2 -->
<PopupContainerV2>
<div class="content-with-actions">
<div class="action-bar">
<van-button size="small" @click="handleEdit">编辑</van-button>
</div>
<!-- 原内容区域 -->
</div>
</PopupContainerV2>
Decision 5: 高度属性的处理
选择: 显式指定 :height="'80%'" 保持视觉一致性
理由:
- V1 默认
height="80%",V2 默认height="auto"(最大 85%) - 直接使用 V2 的 auto 可能导致内容过少时弹窗过小,影响用户体验
- 显式设置 80% 可以确保迁移前后视觉效果一致
- 如果某些组件的内容确实很少,可以在迁移时根据实际情况调整为 auto
Decision 6: 样式和暗色模式的适配
选择: 信任 V2 的内置暗色模式支持,不额外修改
理由:
- V2 已在组件内部通过
@media (prefers-color-scheme: dark)实现暗色模式 - V1 使用 Vant 的 CSS 变量,V2 使用硬编码颜色,但两者在暗色模式下都能正确切换
- 业务组件只需确保内容区域的样式兼容暗色模式即可
风险: 如果业务组件的内容区域使用了与 V2 不兼容的颜色,需要单独调整(通过人工检查)
Risks / Trade-offs
Risk 1: 迁移后视觉效果差异
风险: V1 和 V2 的字体、颜色、圆角不同,可能导致用户感知到不一致 缓解措施:
- 在开发环境逐个测试迁移后的页面
- 重点检查弹窗的标题、内容、按钮的对齐和间距
- 如果某个页面的差异过大,考虑调整 V2 的样式或在该页面单独处理
Risk 2: subtitle 和 header-actions 迁移语义变化
风险: 将 subtitle 移至内容区域可能改变信息层级,header-actions 移至内容顶部可能影响交互流畅性 缓解措施:
- 迁移时保持原有的语义和视觉层级(如 subtitle 仍然显示在顶部且样式相似)
- 通过 CSS 模拟 V1 的 Grid 布局,确保按钮位置不变
Risk 3: 高度变化导致滚动问题
风险: V1 的 80% 固定高度和 V2 的 auto 可能导致滚动行为不同(如内容过多时 V2 可能超出最大高度) 缓解措施:
- 统一使用
:height="'80%'"作为默认值 - 对于内容特别少的弹窗(如确认对话框),可以单独设置为 auto
Risk 4: 事件处理器遗漏
风险: 手动迁移确认/取消按钮时,可能遗漏原有的 @confirm、@cancel 事件逻辑
缓解措施:
- 迁移前通过搜索确认每个组件是否使用了这些事件
- 迁移后通过功能测试验证按钮点击是否正确触发
Risk 5: ESLint 和代码规范问题
风险: 手动创建的 footer 插槽可能不符合项目的 ESLint 规则(如缩进、引号) 缓解措施:
- 迁移完成后运行
pnpm lint并修复所有错误 - 参考现有 V2 的使用示例(TransactionDetailSheet.vue)保持风格一致
Migration Plan
Phase 1: 准备阶段
- 审查 18 个待迁移文件,分析每个文件使用的 V1 功能(subtitle、buttons、header-actions)
- 为每个文件制定迁移清单(需要修改的部分)
Phase 2: 迁移阶段
逐文件迁移,按以下步骤:
- 更新 import 路径和组件名
- 替换基础 props(保留 v-model:show、title,显式设置 height)
- 迁移 subtitle(根据语义选择方案)
- 迁移 header-actions(移至内容区域或 footer)
- 迁移确认/取消按钮(创建 footer 插槽)
- 调整内容区域的 padding(V2 无默认 padding)
- 测试功能和视觉效果
Phase 3: 验证阶段
- 运行
pnpm lint确保代码规范 - 手动测试每个迁移的页面,验证弹窗的打开/关闭、内容展示、按钮交互
- 检查暗色模式下的显示效果
Phase 4: 清理阶段
- 确认所有迁移完成且测试通过
- 删除
Web/src/components/PopupContainer.vue - 全局搜索
PopupContainer确认无残留引用
Rollback 策略
- 如果迁移后发现重大问题(如性能下降、功能缺失),可以通过 Git 回滚到迁移前的版本
- V1 和 V2 是独立文件,迁移失败不会影响现有功能(除非删除了 V1)
- 建议在完成所有迁移并验证通过后再删除 V1 文件
Open Questions
-
是否需要对 V2 组件进行增强?
- 例如增加 subtitle prop 简化迁移
- 暂定答案: 不修改 V2,保持其简洁性,通过插槽实现所有功能
-
是否需要统一 footer 按钮的样式?
- 目前每个文件需要手动创建
.footer-buttons样式 - 暂定答案: 可以提取为全局样式或在 V2 中提供默认样式(后续优化项)
- 目前每个文件需要手动创建
-
是否需要通知用户 UI 风格变化?
- V1 到 V2 的视觉差异可能被用户感知
- 暂定答案: 作为内部优化,不需要用户通知