规划页删除愿望与刷新抽象
知与行 / iOS App 开发复盘 ·
删除入口消失、删完黑屏、删除不同步刷新。根因:本地删与云端删状态机分离、乐观更新无回滚、刷新抽象未抽离。
记录日期:2026-05-26
格式参考:docs/开发记录博客/agents.md
1. 问题背景
规划页重构后,用户反馈 删愿望入口消失;修复过程中又出现 删除按钮位置不对、删完黑屏、删后列表不同步;顺带发现 愿望页(growth)缺少与知/行一致的下拉刷新。 根因不是单一 UI bug,而是:规划 Tab 能力迁移不完整、删后路由/缓存/Context 未收口、刷新 hook 有但容器未接 RefreshControl。
2. 问题列表
2.1 规划页没有删除愿望按钮
根因:
- why1:用户要在 规划 Tab header 行最右删愿望,但
planning.tsx只有ZhiWishPickerButton - why2:删除逻辑曾在
vision/[id].tsx详情页,规划 Tab 内嵌WishPlanPanel后 未迁移 - why3:曾误改
SwipeRevealActions(感悟左滑删除),与愿望删除 不是同一路径
方案:
- 短期方案:在
ZhiWishPickerButton增加onDelete - 优点:改动小
- 缺点:删除散落 hook,布局难固定到行尾
- 长远方案:
ZhiWishPlanningContext.deleteWish()+src/lib/zhi-wish-delete.ts - 优点:DB + invalidate + 列表刷新 + Tab 导航一处维护
- 缺点:需抽共享 lib
采纳方案:长远方案(Phase 3 已落地)。规划页 header 右侧独立删除按钮;useWishPlanDetail.handleDeleteWish 只负责 Alert,确认后调 deleteWish(wishId)。
补充用例:
- 规划 Tab → header 行最右可见删除按钮
- 点击删除 → 二次确认 → 进入愿望 Tab,列表不含已删愿望
2.2 删除按钮未固定到 header 行最右
根因:
- why1:删除按钮放在
ZhiWishPickerButton内部,triggerWrap为alignSelf: 'flex-start',宽度随内容收缩 - why2:标题
flex: 1在 shrink 容器内 撑不满整行,删除按钮只能贴在标题旁
方案:
- 短期方案:
triggerWrap设flexDirection: 'row'+ 标题flex: 1 - 优点:快
- 缺点:仍无法贴屏幕/行尾最右
- 长远方案:header 拆 左
headerLeft(flex:1)+ 右删除按钮 - 优点:布局语义清晰,符合「行最右」需求
- 缺点:删除不再内嵌 picker 组件
采纳方案:长远方案。planning.tsx header 使用 justifyContent: 'space-between'。
补充用例:
- 长愿望标题 → 删除按钮仍在 header 行最右侧,不折行到下方
2.3 删除愿望后黑屏 / 列表不同步
根因:
- why1:曾
router.push('/zhi/wishes'),路由表 无 该屏(实际为growth) - why2:中期改为
router.push('/zhi/growth'),消除黑屏,但 未refreshWishes()、未 invalidate trends/vision 缓存 - why3:
selectedWishId仍指向已删 id,growth 趋势可能短暂显示旧数据
方案:
- 短期方案:修正 path 为
/zhi/growth - 优点:消除黑屏
- 缺点:数据与 Tab 状态仍可能不一致
- 长远方案:
deleteUserWish()invalidate +Context.deleteWish()→refreshWishes()+openWishesTab() - 优点:导航走 Tab dispatch,缓存与列表同步
- 缺点:需 Context + lib 分层
采纳方案:长远方案。
落地文件:
- [
src/lib/zhi-wish-delete.ts](../zhiyuxing-app/src/lib/zhi-wish-delete.ts):删库 +invalidateZhiGrowthTrendsCache+invalidateZhiVisionDetail - [
ZhiWishPlanningContext.deleteWish()](../zhiyuxing-app/src/contexts/ZhiWishPlanningContext.tsx) - [
vision/[id].tsx](../zhiyuxing-app/app/(tabs)/zhi/vision/[id].tsx) 同步改用deleteUserWish+router.replace('/zhi/growth')
补充用例:
- 规划页删愿望 → 愿望 Tab,非黑屏
- 规划切换菜单、growth 趋势均不含已删愿望
2.4 愿望页缺少知/行同款下拉刷新
根因:
- why1:
useZhiWishTrends已有refreshing/pullRefresh,growth.tsx用裸ScrollView,未接 RefreshControl - why2:知/行各自内联 RefreshControl,无统一容器,易漏接
- why3:refresh 语义分散,新人难对齐
方案(Phase 2):
useCachedResource统一 loading / refreshing / refresh / pullRefreshAppRefreshScrollView/AppRefreshFlatList统一下拉 UI- [
docs/app-cache-refresh.md](../zhiyuxing-app/docs/app-cache-refresh.md) 写清 invalidate / refresh / pullRefresh
采纳方案:Phase 2 已落地。
| 页面 | UI 容器 | 数据 hook | |------|---------|-----------| | 愿望 growth | AppRefreshScrollView | useZhiWishTrends → useCachedResource | | 知主页 | AppRefreshFlatList(ZhiInsightTimeline) | 仍页面内逻辑(待迁) | | 行时间格 | AppRefreshScrollView | 仍页面内逻辑(待迁) |
补充用例:
- growth 下拉 → 刷新指示器 → 趋势数据更新
- 三 Tab tintColor 分别为
#6366F1/#6B8DD6/#111827
2.5 pullRefresh 无缓存时无 loading 指示(审查发现)
根因:
- why1:
pullRefresh=load({ force: true, silent: true }) - why2:
silent: true跳过setLoading;hadCache === false时也不setRefreshing - why3:空列表下拉时网络在跑但 UI 无任何反馈,与文档语义不一致
方案:
- 在
useCachedResource中:force && !hadCache时同时setRefreshing(true)+setLoading(true)
采纳方案:已修复 [useCachedResource.ts](../zhiyuxing-app/src/hooks/useCachedResource.ts)。
补充用例:
- growth 空列表下拉 → 顶部 RefreshControl + 内容区 ActivityIndicator 可见
3. 小结
本轮从「删愿望按钮没了」出发,串联修复了 入口 / 布局 / 删后导航与缓存 / 刷新抽象 四条线。
已落地:
| 层级 | 产物 | |------|------| | 删愿望 | zhi-wish-delete.ts、Context.deleteWish()、规划 header 右删、vision 页同步 | | 刷新 UI | AppRefreshScrollView、AppRefreshFlatList(知/行/愿望三 Tab 主页面) | | 刷新数据 | useCachedResource;useZhiWishTrends 首个 domain 接入 | | 文档 | [app-cache-refresh.md](../zhiyuxing-app/docs/app-cache-refresh.md)、[AGENTS.md](../zhiyuxing-app/AGENTS.md) 引用 |
仍待(下一期):
- 知主页 / 行时间格 数据层 迁入
useCachedResource - Web 端
SwipeRevealActions滑动操作回退(onActionPress ?? action.onPress写法需分支,避免原生双触发) - 清理调试
console.log
教训:改 bug 前先确认 页面与交互路径(规划 Tab ≠ 感悟左滑 ≠ vision 详情);删数据后走 Context + invalidate + Tab 导航,不要猜 route path。