docs(plan): 账户 IA 重构 Flutter 实现计划(8 任务,TDD,含 Spec ②③占位接口)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
This commit is contained in:
@@ -0,0 +1,416 @@
|
||||
# 账户/设置/联系 信息架构重构 — Flutter 实现计划
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 把已评审的 IA(`docs/account-ia-reorg-design.html`)落成 Flutter:账户/设置/联系去重归位,Account 提为桌面一级项,顶栏加通知铃铛,账户页加邀请入口,并为 Spec ②推广激励 / ③系统通知 留占位屏与接口。
|
||||
|
||||
**Architecture:** 复用现有 Riverpod + shell(desktop/mobile/tablet) + `open(NavView, mobilePage)` 下钻模式。多数是改造现有页面(account_page/settings_page/account_screens)+ 导航(navigation_provider/desktop_shell);新建仅 2 个占位屏(InviteScreen/NotificationsScreen)+ 铃铛 widget。邀请奖励引擎、通知下发机制**不在本计划**(各自 Spec)。
|
||||
|
||||
**Tech Stack:** Flutter / Dart · flutter_riverpod 2.x · 现有 `PangolinIcons`(生成,勿手改) · `AppText` 抽象基类 + 6 语言 strings。
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- **l10n 完整性闸**:`app_text.dart` 每加一个抽象 getter,必须在 6 份 `strings_{zh,en,ja,ko,ru,es}.dart` 全部实现,否则 `node tools/check-l1-sync.mjs` red。默认语言英文。
|
||||
- **图标单源**:`client/lib/widgets/pangolin_icons.dart` 是生成文件**勿手改**;真源 `design/prototype/icons.js`。本计划所需图标(bell/gift/crown/ticket/monitorSmartphone/users/settings/messageCircle)均已在。
|
||||
- **颜色单源**:禁裸 `Color(0x)`/`Colors.x`,走 `context.pangolin` 语义色;例外加 `// ds-ignore: 理由`。
|
||||
- **验收**:每任务后 `cd client && flutter analyze`(0 error) + 相关 `flutter test` 绿;改到有 golden 的屏随功能 `--update-goldens` 重录。
|
||||
- **占位屏边界**:InviteScreen/NotificationsScreen 本计划只做**静态占位 UI + 导航接线**;数据/奖励/下发逻辑属 Spec ②③,用 `// TODO(spec-2)` / `// TODO(spec-3)` 标注接口点。
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 导航枚举加 invite / notifications
|
||||
|
||||
**Files:**
|
||||
- Modify: `client/lib/state/navigation_provider.dart:7,10`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: `NavView.invite`、`NavView.notifications` 枚举值;`kAccountSubViews` 含 `invite`。
|
||||
|
||||
- [ ] **Step 1: 加枚举值 + 子页登记**
|
||||
|
||||
`navigation_provider.dart` 第 7 行 `enum NavView` 末尾加 `, invite, notifications`:
|
||||
```dart
|
||||
enum NavView { connect, servers, stats, account, contact, settings, plans, redeem, devices, purchase, payment, orders, invite, notifications }
|
||||
```
|
||||
第 10 行 `kAccountSubViews` 加 `invite`(notifications 走全屏,不入子页返回集):
|
||||
```dart
|
||||
const Set<NavView> kAccountSubViews = {NavView.plans, NavView.redeem, NavView.devices, NavView.purchase, NavView.payment, NavView.orders, NavView.invite};
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 编译校验**
|
||||
|
||||
Run: `cd client && flutter analyze lib/state/navigation_provider.dart`
|
||||
Expected: 出现「missing case NavView.invite/notifications」类 error(desktop_shell 的穷举 switch 未覆盖)——**预期**,Task 5 修复。仅确认本文件无语法 error。
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add client/lib/state/navigation_provider.dart
|
||||
git commit -m "feat(nav): NavView 加 invite/notifications + kAccountSubViews 收录 invite"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: l10n — 邀请 + 通知 + IA 归组文案
|
||||
|
||||
**Files:**
|
||||
- Modify: `client/lib/l10n/app_text.dart`(抽象 getter)
|
||||
- Modify: `client/lib/l10n/strings_{zh,en,ja,ko,ru,es}.dart`(6 份实现)
|
||||
|
||||
**Interfaces:**
|
||||
- Produces getter(供后续任务用):`inviteEntry, inviteEntrySub, inviteTitle, inviteCodeLabel, inviteLinkLabel, inviteCopyCode, inviteCopyLink, inviteRewardsTitle, inviteRewardRegister, inviteRewardFirstBuy, inviteRewardJoinTg, inviteProgressTitle`;`notifTitle, notifEmpty, notifTypeNews, notifTypeFeature, notifTypeImportant`;`settingsGroupConn, settingsGroupAppearance, settingsGroupAbout`。
|
||||
|
||||
- [ ] **Step 1: app_text.dart 加抽象 getter**
|
||||
|
||||
在 `app_text.dart` 的「购买/支付」段后加一段(照现有注释风格):
|
||||
```dart
|
||||
// ── 邀请好友(Spec ②占位) ──
|
||||
String get inviteEntry; // 邀请好友得会员 / Invite friends, earn membership
|
||||
String get inviteEntrySub; // 好友注册 · 双方都得会员天数 / Friend signs up · both earn days
|
||||
String get inviteTitle; // 邀请好友 / Invite friends
|
||||
String get inviteCodeLabel; // 邀请码 / Invite code
|
||||
String get inviteLinkLabel; // 邀请链接 / Invite link
|
||||
String get inviteCopyCode; // 复制邀请码 / Copy code
|
||||
String get inviteCopyLink; // 复制链接 / Copy link
|
||||
String get inviteRewardsTitle; // 奖励规则 / Rewards
|
||||
String get inviteRewardRegister; // 好友注册 · 双方各得 3 天 / Sign up · +3 days each
|
||||
String get inviteRewardFirstBuy; // 好友首购 · 双方各得 15 天 / First purchase · +15 days each
|
||||
String get inviteRewardJoinTg; // 加入 TG 频道 · 得 3 天 / Join TG · +3 days
|
||||
String get inviteProgressTitle; // 我的奖励 / My rewards
|
||||
// ── 通知(Spec ③占位) ──
|
||||
String get notifTitle; // 通知 / Notifications
|
||||
String get notifEmpty; // 暂无通知 / No notifications
|
||||
String get notifTypeNews; // 新闻 / News
|
||||
String get notifTypeFeature; // 功能 / Feature
|
||||
String get notifTypeImportant; // 重要 / Important
|
||||
// ── 设置分组 ──
|
||||
String get settingsGroupConn; // 连接 / Connection
|
||||
String get settingsGroupAppearance;// 外观 / Appearance
|
||||
String get settingsGroupAbout; // 关于 / About
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 6 份 strings 各实现(带 @override)**
|
||||
|
||||
每份 `strings_<lang>.dart` 在 `qrNotSupported` 之后加对应 20 个 `@override String get X => '<译文>';`。译文(zh 见注释;en 见注释;ja/ko/ru/es 按语义翻译,与已有条目风格一致)。
|
||||
|
||||
- [ ] **Step 3: 跑 l10n 闸**
|
||||
|
||||
Run: `cd /Users/wangjia/code/pangolin/.claude/worktrees/pay-v2-integration && node tools/check-l1-sync.mjs`
|
||||
Expected: `✓ l10n 完整 OK —— NNN 个 getter × 6 语言全实现`(getter 数 +20)。
|
||||
|
||||
- [ ] **Step 4: analyze**
|
||||
|
||||
Run: `cd client && flutter analyze lib/l10n/`
|
||||
Expected: No issues found(仅 annotate_overrides 若漏 @override 会 info,补齐)。
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add client/lib/l10n/
|
||||
git commit -m "feat(l10n): 邀请/通知/设置分组文案 ×6 语言(Spec ②③占位)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: 联系渠道单源 + Redeem 删「去哪买」
|
||||
|
||||
**Files:**
|
||||
- Create: `client/lib/services/contact_channels.dart`
|
||||
- Modify: `client/lib/screens/contact_page.dart:22-26`
|
||||
- Modify: `client/lib/widgets/account_screens.dart:377-381,420-425,461-465`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: `const List<ContactChannel> kContactChannels`(single source);`class ContactChannel { IconData icon; String name; String sub; bool accent; }`。
|
||||
|
||||
- [ ] **Step 1: 建单源常量**
|
||||
|
||||
`contact_channels.dart`:
|
||||
```dart
|
||||
import 'package:flutter/widgets.dart';
|
||||
import '../widgets/pangolin_icons.dart';
|
||||
|
||||
class ContactChannel {
|
||||
const ContactChannel({required this.icon, required this.name, required this.sub, this.accent = false});
|
||||
final IconData icon;
|
||||
final String name; // 展示名(Telegram 用字面量;邮箱/官网用 l10n,在页面侧传入)
|
||||
final String sub; // handle:@x / a@b / domain(launchChannel 据此推导 URL)
|
||||
final bool accent;
|
||||
}
|
||||
|
||||
// name 用 key 占位,页面按 t 映射;Telegram 固定字面量
|
||||
const List<({IconData icon, String nameKey, String sub, bool accent})> kContactChannels = [
|
||||
(icon: PangolinIcons.send, nameKey: 'telegram', sub: '@pangolin_app', accent: true),
|
||||
(icon: PangolinIcons.mail, nameKey: 'email', sub: 'support@yanmeiai.com', accent: false),
|
||||
(icon: PangolinIcons.globe, nameKey: 'website', sub: 'pangolin.yanmeiai.com', accent: false),
|
||||
];
|
||||
```
|
||||
> 说明:`nameKey` 在页面侧映射(`telegram`→字面量 'Telegram',`email`→`t.contactEmail`,`website`→`t.contactWebsite`),避免把 AppText 塞进常量。
|
||||
|
||||
- [ ] **Step 2: contact_page.dart 用单源**
|
||||
|
||||
`contact_page.dart:22-26` 的三元素内联 list 换成 `kContactChannels.map(...)`,name 按 nameKey 映射。
|
||||
|
||||
- [ ] **Step 3: ContactScreen 用单源**
|
||||
|
||||
`account_screens.dart:461-465`(ContactScreen 的 channels)同样换成 `kContactChannels`。
|
||||
|
||||
- [ ] **Step 4: RedeemScreen 删「去哪买」**
|
||||
|
||||
`account_screens.dart`:删 `channels`(377-381)、删 build 里「去哪买」引流卡(`buyTitle`/`buySub` + channel 列表,420-425)、删 `_channel` 方法(430+)。RedeemScreen 只保留兑换码输入(386-419)。
|
||||
|
||||
- [ ] **Step 5: analyze + 手测**
|
||||
|
||||
Run: `cd client && flutter analyze lib/screens/contact_page.dart lib/widgets/account_screens.dart lib/services/contact_channels.dart`
|
||||
Expected: No issues found。
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add client/lib/services/contact_channels.dart client/lib/screens/contact_page.dart client/lib/widgets/account_screens.dart
|
||||
git commit -m "refactor(contact): 渠道抽单源常量 + RedeemScreen 删去哪买引流卡(仅留兑换码)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: 账户页重排 + 邀请入口 + 删偏好卡 + formFactor 门控
|
||||
|
||||
**Files:**
|
||||
- Modify: `client/lib/screens/account_page.dart:52-141,381,423`
|
||||
- Create: `client/lib/screens/invite_page.dart`
|
||||
- Test: `client/test/widget/account_ia_test.dart`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `NavView.invite`(Task 1)、`t.inviteEntry/inviteEntrySub`(Task 2)、`open(NavView, Widget)`(现有)。
|
||||
- Produces: `class InviteScreen extends ConsumerWidget { InviteScreen({required AppText t, VoidCallback? onBack, bool embedded}); }`(占位)。
|
||||
|
||||
- [ ] **Step 1: 建 InviteScreen 占位屏**
|
||||
|
||||
`invite_page.dart`:`_SubScaffold`(复用 account_screens 的骨架不便跨文件,简单自建 Scaffold/embedded)标题 `t.inviteTitle`,内容 = 邀请码卡(可复制,值写死 `PANGOLIN-DEMO`)+ 链接卡 + 奖励规则列表(register/firstBuy/joinTg 三条,用 `t.inviteReward*`)+ 「我的奖励」占位卡。全走 `context.pangolin`/`PangolinIcons.gift`。顶部 `// TODO(spec-2): 邀请码/链接由后端签发,奖励进度来自 /v1/invite`。
|
||||
|
||||
- [ ] **Step 2: 写账户页 IA 结构测试(失败)**
|
||||
|
||||
`account_ia_test.dart`:desktop 态(`debugDefaultTargetPlatformOverride=windows` + 宽 surface + provider override,参考 `test/widget/account_desktop_nav_test.dart`)pump `AccountPage(isWide:true)`,断言:
|
||||
```dart
|
||||
// 邀请入口存在
|
||||
expect(find.text(t.inviteEntry), findsOneWidget);
|
||||
// 语言/主题不在账户页(移到 Settings)
|
||||
expect(find.text(t.language), findsNothing);
|
||||
// 兑换在账户页(购买&订单&兑换块)
|
||||
expect(find.text(t.redeemEntry), findsOneWidget);
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 跑测试确认失败**
|
||||
|
||||
Run: `cd client && flutter test test/widget/account_ia_test.dart`
|
||||
Expected: FAIL(邀请入口不存在 / 语言仍在)。
|
||||
|
||||
- [ ] **Step 4: 改 account_page.dart**
|
||||
|
||||
- 购买&订单&兑换块(现 80-94):把兑换码行(现在设备卡里的 `t.redeemEntry`)并入,成为 购买套餐 / 我的订单 / 兑换码 三行。
|
||||
- 其后加**邀请卡**:`_NavRow(icon: PangolinIcons.gift, title: t.inviteEntry, sub: t.inviteEntrySub, onTap: () => open(NavView.invite, InviteScreen(t: t)))`。
|
||||
- 设备/设置/兑换/联系卡(现 97-105):兑换移走(见上);设备保持下钻行;设置/联系两行包 `if (context.formFactor == FormFactor.mobile)`(桌面侧栏已有,不重复)。
|
||||
- **删语言/主题/协议卡**(108-130)+ `_LangSwitch`(381)+ `_PangolinSwitch`(423)。
|
||||
- import `invite_page.dart` + `core/responsive/form_factor.dart`。
|
||||
|
||||
- [ ] **Step 5: 跑测试确认通过**
|
||||
|
||||
Run: `cd client && flutter test test/widget/account_ia_test.dart`
|
||||
Expected: PASS。
|
||||
|
||||
- [ ] **Step 6: analyze + golden 重录(账户页有 golden 则录)**
|
||||
|
||||
Run: `cd client && flutter analyze lib/screens/account_page.dart lib/screens/invite_page.dart && flutter test --update-goldens 2>&1 | tail -3`
|
||||
|
||||
- [ ] **Step 7: Commit**
|
||||
|
||||
```bash
|
||||
git add client/lib/screens/account_page.dart client/lib/screens/invite_page.dart client/test/widget/account_ia_test.dart client/test/golden/
|
||||
git commit -m "feat(account): IA 重排+邀请入口+删偏好卡+设置/联系 formFactor 门控"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: 桌面侧栏加 Account + 顶栏铃铛 + invite 视图接线
|
||||
|
||||
**Files:**
|
||||
- Modify: `client/lib/shell/desktop_shell.dart:34-40,42-55,59-88,92-96,104`
|
||||
- Modify: `client/lib/shell/tablet_shell.dart:39-46`
|
||||
- Create: `client/lib/widgets/notification_bell.dart`
|
||||
- Modify: `client/lib/widgets/content_top_bar.dart:44-66`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `NavView.invite/notifications`(Task 1)、`InviteScreen`(Task 4)、`NotificationsScreen`(Task 6,先前向引用——本任务铃铛 onTap 先 `go(NavView.notifications)`,Task 6 补屏)。
|
||||
- Produces: `class NotificationBell extends ConsumerWidget { NotificationBell({required VoidCallback onTap, bool hasUnread}); }`。
|
||||
|
||||
- [ ] **Step 1: 建 NotificationBell widget**
|
||||
|
||||
`notification_bell.dart`:`IconButton`(`PangolinIcons.bell`) + 右上未读红点(`Positioned` 小圆 `context.pangolin.danger`,`hasUnread` 才显)。`hasUnread` 本任务先写死 `true`(演示),`// TODO(spec-3): 未读态来自 notices provider`。
|
||||
|
||||
- [ ] **Step 2: 侧栏加 Account 一级项**
|
||||
|
||||
`desktop_shell.dart:34-40` `items` 在 stats 后 settings 前插:
|
||||
```dart
|
||||
(icon: PangolinIcons.user, label: t.tabMe, view: NavView.account),
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 顶栏挂铃铛**
|
||||
|
||||
`content_top_bar.dart` 的 trailing 区(主题切换旁)加 `NotificationBell(hasUnread: true, onTap: onBell)`;`ContentTopBar` 加 `VoidCallback? onBell` 入参。`desktop_shell.dart:104` 传 `onBell: () => go(NavView.notifications)`。
|
||||
|
||||
- [ ] **Step 4: content()/titles 补 invite + notifications**
|
||||
|
||||
`desktop_shell.dart` `titles`(42-55)加 `NavView.invite: t.inviteTitle, NavView.notifications: t.notifTitle`;`content()`(59-88)加 `case NavView.invite: return InviteScreen(t: t, embedded: true);` 和 `case NavView.notifications: return NotificationsScreen(t: t, embedded: true);`(NotificationsScreen 由 Task 6 建,本步先 import 占位——若 Task 6 未完成,临时 `return const SizedBox()` 并 `// TODO`)。`backTarget()`(92-96)靠 `kAccountSubViews` 已含 invite → 自动回 account;notifications 不在集内,返回走顶栏无返回(全屏态)——给 notifications 也返回 account:在 `backTarget` 加 `if (view == NavView.notifications) return () => go(NavView.account);`。
|
||||
|
||||
- [ ] **Step 5: tablet_shell titles 补一条**
|
||||
|
||||
`tablet_shell.dart:39-46` `titles` 加 `NavView.invite: t.inviteTitle, NavView.notifications: t.notifTitle`(内容走 default→AccountPage/push)。
|
||||
|
||||
- [ ] **Step 6: analyze(全绿——穷举 switch 现已覆盖)**
|
||||
|
||||
Run: `cd client && flutter analyze lib/shell/ lib/widgets/notification_bell.dart lib/widgets/content_top_bar.dart`
|
||||
Expected: No issues found。
|
||||
|
||||
- [ ] **Step 7: Commit**
|
||||
|
||||
```bash
|
||||
git add client/lib/shell/desktop_shell.dart client/lib/shell/tablet_shell.dart client/lib/widgets/notification_bell.dart client/lib/widgets/content_top_bar.dart
|
||||
git commit -m "feat(shell): 桌面侧栏加 Account 一级项 + 顶栏通知铃铛 + invite/notifications 视图接线"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 6: 通知占位屏 + 移动顶栏铃铛
|
||||
|
||||
**Files:**
|
||||
- Create: `client/lib/screens/notifications_page.dart`
|
||||
- Modify: `client/lib/widgets/app_top_bar.dart`(trailing 铃铛)
|
||||
- Modify: `client/lib/screens/account_page.dart:146`(AppTopBar 传 trailing)
|
||||
- Modify: `client/lib/shell/desktop_shell.dart`(把 Task 5 的占位 import 换成真 NotificationsScreen)
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `t.notifTitle/notifEmpty/notifType*`(Task 2)、`NotificationBell`(Task 5)、`open(NavView.notifications, ...)`。
|
||||
- Produces: `class NotificationsScreen extends ConsumerWidget { NotificationsScreen({required AppText t, VoidCallback? onBack, bool embedded}); }`。
|
||||
|
||||
- [ ] **Step 1: 建 NotificationsScreen 占位**
|
||||
|
||||
`notifications_page.dart`:标题 `t.notifTitle`,内容 = 3 条示例通知(类型 pill 用 `t.notifType*`,未读点)+ 空态 `t.notifEmpty`(数据为空时)。`// TODO(spec-3): 列表来自 GET /v1/notices,已读态本地持久化`。
|
||||
|
||||
- [ ] **Step 2: 桌面 shell 换真屏**
|
||||
|
||||
`desktop_shell.dart` `content()` 的 notifications case 从占位 `SizedBox` 换成 `NotificationsScreen(t: t, embedded: true)` + import。
|
||||
|
||||
- [ ] **Step 3: 移动顶栏挂铃铛**
|
||||
|
||||
`app_top_bar.dart` 加 `Widget? trailing` 入参并渲染在右侧;`account_page.dart:146` `AppTopBar(brand: t.brand, trailing: NotificationBell(hasUnread: true, onTap: () => open(NavView.notifications, NotificationsScreen(t: t))))`。
|
||||
|
||||
- [ ] **Step 4: analyze**
|
||||
|
||||
Run: `cd client && flutter analyze lib/screens/notifications_page.dart lib/widgets/app_top_bar.dart lib/screens/account_page.dart lib/shell/desktop_shell.dart`
|
||||
Expected: No issues found。
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add client/lib/screens/notifications_page.dart client/lib/widgets/app_top_bar.dart client/lib/screens/account_page.dart client/lib/shell/desktop_shell.dart
|
||||
git commit -m "feat(notif): 通知占位屏 + 移动顶栏铃铛(Spec ③接口占位)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 7: 设置页归三组 + Web 链接下沉
|
||||
|
||||
**Files:**
|
||||
- Modify: `client/lib/screens/settings_page.dart:20-22,54-112`
|
||||
- Test: `client/test/widget/settings_ia_test.dart`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `t.settingsGroupConn/Appearance/About`(Task 2)。
|
||||
|
||||
- [ ] **Step 1: 写设置分组测试(失败)**
|
||||
|
||||
`settings_ia_test.dart`:pump `SettingsPage`(provider override),断言:
|
||||
```dart
|
||||
expect(find.text(t.settingsGroupConn), findsOneWidget);
|
||||
expect(find.text(t.settingsGroupAppearance), findsOneWidget);
|
||||
expect(find.text(t.settingsGroupAbout), findsOneWidget);
|
||||
// 购买入口不在设置页(归 Account)
|
||||
expect(find.text(t.purchaseTitle), findsNothing);
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 跑测试确认失败**
|
||||
|
||||
Run: `cd client && flutter test test/widget/settings_ia_test.dart`
|
||||
Expected: FAIL。
|
||||
|
||||
- [ ] **Step 3: 改 settings_page.dart**
|
||||
|
||||
- 删第一张「购买套餐 + 我的订单」卡(75-89)+ 顶部 `orders_page/payment_page/purchase_page` import(20-22)+ `open()` helper(61-67)。
|
||||
- 三张卡分组:① `_SectionLabel(t.settingsGroupConn)` + 开关组(autostart/autoConnect/smartRoute/killSwitch);② `_SectionLabel(t.settingsGroupAppearance)` + 语言 + 深色;③ `_SectionLabel(t.settingsGroupAbout)` + 协议 + 检查更新 + Version + **底部** webUserCenter 行(降到最后)。
|
||||
|
||||
- [ ] **Step 4: 跑测试确认通过**
|
||||
|
||||
Run: `cd client && flutter test test/widget/settings_ia_test.dart`
|
||||
Expected: PASS。
|
||||
|
||||
- [ ] **Step 5: analyze + golden**
|
||||
|
||||
Run: `cd client && flutter analyze lib/screens/settings_page.dart && flutter test --update-goldens 2>&1 | tail -3`
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add client/lib/screens/settings_page.dart client/test/widget/settings_ia_test.dart client/test/golden/
|
||||
git commit -m "feat(settings): 删购买/订单卡 + 归连接/外观/关于三组 + Web 链接下沉底部"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 8: 全量验收 + 收尾
|
||||
|
||||
**Files:** 无(验收)
|
||||
|
||||
- [ ] **Step 1: 全量 analyze**
|
||||
|
||||
Run: `cd client && flutter analyze`
|
||||
Expected: 0 error(仅 pre-existing 3 条 info)。
|
||||
|
||||
- [ ] **Step 2: 全量测试**
|
||||
|
||||
Run: `cd client && flutter test 2>&1 | tail -3`
|
||||
Expected: All tests passed。
|
||||
|
||||
- [ ] **Step 3: DS 双闸**
|
||||
|
||||
Run: `cd /Users/wangjia/code/pangolin/.claude/worktrees/pay-v2-integration && node tools/check-l1-sync.mjs && node design/prototype/tools/check-ds.mjs`
|
||||
Expected: 图标同集 OK + l10n 完整 OK + check-ds 5 项过。
|
||||
|
||||
- [ ] **Step 4: 更新决策日志**
|
||||
|
||||
`docs/pay-v2-dev-log.md` 追加「IA 重构落地」step。
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add docs/pay-v2-dev-log.md
|
||||
git commit -m "docs(pay-v2): IA 重构落地验收记录"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Self-Review
|
||||
|
||||
**Spec coverage**(对照 `account-ia-reorg-design.html` §5 落地改动清单):
|
||||
- navigation_provider 加 invite ✅ Task 1
|
||||
- desktop_shell 侧栏 Account + 顶栏铃铛 + content 分支 ✅ Task 5
|
||||
- account_page 重排/邀请/删偏好卡/formFactor 门控 ✅ Task 4
|
||||
- settings_page 归组 + Web 下沉 ✅ Task 7
|
||||
- account_screens RedeemScreen 删去哪买 ✅ Task 3
|
||||
- 顶栏铃铛槽(content_top_bar + app_top_bar)✅ Task 5/6
|
||||
- 邀请/通知占位屏 ✅ Task 4/6
|
||||
- l10n ✅ Task 2
|
||||
|
||||
**已就绪不做**(盘点确认):设备⋮菜单、登录/注册、SegSwitch、图标、支付/订单 l10n。
|
||||
|
||||
**留给后续 spec**:邀请奖励引擎/归因/防刷(Spec ②)、通知 model/provider/api/下发/已读持久化(Spec ③)——本计划仅占位屏 + `// TODO(spec-N)` 接口标注。
|
||||
|
||||
**依赖顺序**:1→2→(3,4 可并)→5→6→7→8。Task 5 前向引用 NotificationsScreen(Task 6),已注明临时占位处理。
|
||||
Reference in New Issue
Block a user