fix
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

This commit is contained in:
SunCheng
2026-02-20 14:57:19 +08:00
parent 6e95568906
commit 32d5ed62d0
27 changed files with 1520 additions and 1114 deletions

View File

@@ -0,0 +1,216 @@
## 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 插槽 + 手动事件绑定
**实现模式**:
```vue
<!-- 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.vueheader-actions 通常是"编辑"、"删除"等操作按钮
- 这些按钮更适合放在内容区域顶部或 footer 中,符合 V2 的极简标题设计
**实现模式**:
```vue
<!-- 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: 准备阶段
1. 审查 18 个待迁移文件,分析每个文件使用的 V1 功能subtitle、buttons、header-actions
2. 为每个文件制定迁移清单(需要修改的部分)
### Phase 2: 迁移阶段
逐文件迁移,按以下步骤:
1. 更新 import 路径和组件名
2. 替换基础 props保留 v-model:show、title显式设置 height
3. 迁移 subtitle根据语义选择方案
4. 迁移 header-actions移至内容区域或 footer
5. 迁移确认/取消按钮(创建 footer 插槽)
6. 调整内容区域的 paddingV2 无默认 padding
7. 测试功能和视觉效果
### Phase 3: 验证阶段
1. 运行 `pnpm lint` 确保代码规范
2. 手动测试每个迁移的页面,验证弹窗的打开/关闭、内容展示、按钮交互
3. 检查暗色模式下的显示效果
### Phase 4: 清理阶段
1. 确认所有迁移完成且测试通过
2. 删除 `Web/src/components/PopupContainer.vue`
3. 全局搜索 `PopupContainer` 确认无残留引用
### Rollback 策略
- 如果迁移后发现重大问题(如性能下降、功能缺失),可以通过 Git 回滚到迁移前的版本
- V1 和 V2 是独立文件,迁移失败不会影响现有功能(除非删除了 V1
- 建议在完成所有迁移并验证通过后再删除 V1 文件
## Open Questions
1. **是否需要对 V2 组件进行增强?**
- 例如增加 subtitle prop 简化迁移
- **暂定答案**: 不修改 V2保持其简洁性通过插槽实现所有功能
2. **是否需要统一 footer 按钮的样式?**
- 目前每个文件需要手动创建 `.footer-buttons` 样式
- **暂定答案**: 可以提取为全局样式或在 V2 中提供默认样式(后续优化项)
3. **是否需要通知用户 UI 风格变化?**
- V1 到 V2 的视觉差异可能被用户感知
- **暂定答案**: 作为内部优化,不需要用户通知