feat(client): 设备数超限挡板 DeviceLimitScreen(#16 前端)

登录响应带 device_limit 时,顶层路由把用户挡在「移除设备」页,降到上限内才进主界面。

- auth_api.dart:AuthTokens 解析 device_limit;新增 DeviceLimit/DeviceBrief 模型
- auth_provider.dart:deviceLimitProvider(挡板状态);auth_screen 登录后置入
- main.dart:isLoggedIn 后若 deviceLimit!=null → DeviceLimitScreen(挡 HomeShell)
- screens/device_limit_screen.dart(新):设备列表(本机标注不可删)+ 每台移除
  + 一键「移除最久未用」+ 退出登录;降到上限内自动清挡板放行(复用 devicesProvider.remove)
- l10n:deviceLimitTitle/Desc/RemoveOldest(zh/en);顺带修 devicesSub 文案 5→3 台

analyze 干净;client 全量测试 161 通过。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-07-01 19:25:14 +08:00
parent 6bac7fd2f0
commit 34d980d875
8 changed files with 282 additions and 3 deletions
+2
View File
@@ -128,6 +128,8 @@ class _AuthScreenState extends ConsumerState<AuthScreen>
);
await ref.read(tokenStoreProvider).saveLastEmail(_email.text.trim());
await ref.read(authProvider.notifier).saveTokens(tokens);
// 超限信号(非 null 时顶层路由挡到「移除设备」页;null 清除旧状态)。
ref.read(deviceLimitProvider.notifier).state = tokens.deviceLimit;
if (mounted) widget.onDone();
} on AuthApiException catch (e) {
if (mounted) setState(() { _errorZh = e.messageZh; _loading = false; });