feat(routing): 设置入口改分流规则下钻行 + 双形态导航

- settings_page.dart: smartRoute 布尔开关行 → 分流规则下钻行(标题 + 当前模式 pill
  + chevron),点击 desktop/tablet 切 navViewProvider 内容区下钻,mobile 全屏 push
  RoutingScreen(与 account_page.dart 的 open() 双形态导航同一范式)。
- navigation_provider.dart: 新增 NavView.routing + kSettingsSubViews 集合(登记
  settings 的下钻子页,供 shell backTarget 判断返回 settings 而非 account)。
- desktop_shell.dart/tablet_shell.dart: 内容区 switch 挂 RoutingScreen(embedded:
  true),backTarget 优先判 kSettingsSubViews → 回 settings。
- settings_ia_test.dart: 追加下钻行断言(标题+模式pill+点击导航到 RoutingScreen)。
- 重录 desktop_settings golden(唯一因本刀布局变化受影响的 golden)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-07-28 09:04:46 +08:00
parent d73b6da110
commit 86e5b6d451
6 changed files with 118 additions and 6 deletions
+47 -1
View File
@@ -7,15 +7,37 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../core/responsive/form_factor.dart';
import '../l10n/app_text.dart';
import '../pangolin_theme.dart';
import '../services/web_launch.dart';
import '../state/app_providers.dart';
import '../state/navigation_provider.dart';
import '../state/routing_provider.dart';
import '../state/settings_provider.dart';
import '../state/update_provider.dart';
import '../widgets/page_body.dart';
import '../widgets/pangolin_icons.dart';
import '../widgets/pangolin_toast.dart';
import '../widgets/routing_screen.dart';
/// 「分流规则」当前模式短标签(与 [RoutingScreen] 段选顺序一致的三态)。
String _routingModeLabel(AppText t, String mode) => switch (mode) {
'global' => t.routingModeGlobal,
'direct' => t.routingModeDirect,
_ => t.routingModeSmart, // 'rule' + 兜底
};
/// 打开「分流规则」子屏:desktop/tablet 在内容区下钻(切 navViewProvider,顶栏带返回到
/// settings);mobile 全屏 push(RoutingScreen 自带返回箭头,同 account_page.dart 的
/// mobile 全屏 push 范式)。
void _openRouting(BuildContext context, WidgetRef ref) {
if (context.formFactor == FormFactor.mobile) {
Navigator.of(context).push(MaterialPageRoute(builder: (_) => const RoutingScreen()));
} else {
ref.read(navViewProvider.notifier).state = NavView.routing;
}
}
/// 「检查更新」手动触发:拉取 `$kApiBaseUrl/version`,失败/无更新走轻提示,
/// 有更新则清掉「已忽略版本」→ 顶部更新 banner 显示 + toast(不弹窗)。
@@ -63,7 +85,15 @@ class SettingsPage extends ConsumerWidget {
if (Platform.isMacOS || Platform.isWindows || Platform.isLinux)
_Row(title: t.autostart, sub: t.autostartSub, right: sw(s.autostart, settings.setAutostart)),
_Row(title: t.autoConnect, sub: t.autoConnectSub, right: sw(s.autoConnect, settings.setAutoConnect)),
_Row(title: t.smartRoute, sub: t.smartRouteSub, right: sw(s.smartRoute, settings.setSmartRoute)),
_Row(
title: t.routingRulesTitle,
right: Row(mainAxisSize: MainAxisSize.min, children: [
_ModePill(label: _routingModeLabel(t, ref.watch(routingProfileProvider).valueOrNull?.mode ?? 'rule')),
const SizedBox(width: 4),
Icon(PangolinIcons.chevronRight, size: 18, color: c.fg3),
]),
onTap: () => _openRouting(context, ref),
),
_Row(title: t.killSwitch, sub: t.killSwitchSub, last: true, right: sw(s.killSwitch, settings.setKillSwitch)),
]),
const SizedBox(height: 20),
@@ -157,6 +187,22 @@ class _Row extends StatelessWidget {
}
}
/// 「分流规则」行右侧的当前模式小药丸(全局代理/智能分流/全部直连)。
class _ModePill extends StatelessWidget {
const _ModePill({required this.label});
final String label;
@override
Widget build(BuildContext context) {
final c = context.pangolin;
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(color: c.accentSubtle, borderRadius: BorderRadius.circular(PangolinRadius.full)),
child: Text(label, style: PangolinText.caption.copyWith(color: c.accent, fontWeight: FontWeight.w700, fontSize: 11)),
);
}
}
class _LangSwitch extends StatelessWidget {
const _LangSwitch({required this.lang, required this.onPick});
final AppLang lang;