From 1cc52075e94b2e125830ad5282599a1e79712d57 Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Sat, 5 Sep 2026 19:08:01 +0800 Subject: [PATCH] =?UTF-8?q?feat(client):=20=E6=96=AD=E5=BC=80=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E5=89=8D=E5=BC=B9=E4=BA=8C=E6=AC=A1=E7=A1=AE=E8=AE=A4?= =?UTF-8?q?=E6=A1=86(=E9=81=BF=E5=85=8D=E8=AF=AF=E8=A7=A6=E7=9E=AC?= =?UTF-8?q?=E6=96=AD)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 连接键在 on 态点击不再直接断开:先弹危险操作确认框(告知连接已加密, 确认才 toggle 断开);off/connecting 态行为不变直接 toggle。 新增 l10n disconnectTitle/Body/Action(en/zh/es/ja/ko/ru 六语)。 Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01FEVUXAbFT6bF1Qw27RHWoD --- client/lib/l10n/app_text.dart | 3 ++ client/lib/l10n/strings_en.dart | 6 ++++ client/lib/l10n/strings_es.dart | 6 ++++ client/lib/l10n/strings_ja.dart | 6 ++++ client/lib/l10n/strings_ko.dart | 6 ++++ client/lib/l10n/strings_ru.dart | 6 ++++ client/lib/l10n/strings_zh.dart | 6 ++++ client/lib/screens/connect_page.dart | 45 ++++++++++++++++++++++++++-- 8 files changed, 82 insertions(+), 2 deletions(-) diff --git a/client/lib/l10n/app_text.dart b/client/lib/l10n/app_text.dart index 5ef69d7..0711dbf 100644 --- a/client/lib/l10n/app_text.dart +++ b/client/lib/l10n/app_text.dart @@ -343,6 +343,9 @@ abstract class AppText { String get capOn; String get connectNow; // 连接键 off 态圆内文字 String get secure; // 连接键圆内「已加密」 + String get disconnectTitle; // 断开确认弹窗标题 + String get disconnectBody; // 断开确认弹窗正文 + String get disconnectAction; // 断开确认弹窗「断开」按钮 // ── 底部 / 侧栏导航 ── String get tabConnect; diff --git a/client/lib/l10n/strings_en.dart b/client/lib/l10n/strings_en.dart index fd4e9c7..fe3b808 100644 --- a/client/lib/l10n/strings_en.dart +++ b/client/lib/l10n/strings_en.dart @@ -27,6 +27,12 @@ class StringsEn extends AppText { @override String get secure => 'SECURE'; @override + String get disconnectTitle => 'Disconnect?'; + @override + String get disconnectBody => 'Your connection is encrypted. Are you sure you want to disconnect?'; + @override + String get disconnectAction => 'Disconnect'; + @override String get tabConnect => 'Connect'; @override String get tabServers => 'Servers'; diff --git a/client/lib/l10n/strings_es.dart b/client/lib/l10n/strings_es.dart index 81820b9..5ffdad3 100644 --- a/client/lib/l10n/strings_es.dart +++ b/client/lib/l10n/strings_es.dart @@ -27,6 +27,12 @@ class StringsEs extends AppText { @override String get secure => 'SEGURO'; @override + String get disconnectTitle => '¿Desconectar?'; + @override + String get disconnectBody => 'Tu conexión está cifrada. ¿Seguro que quieres desconectar?'; + @override + String get disconnectAction => 'Desconectar'; + @override String get tabConnect => 'Conectar'; @override String get tabServers => 'Servidores'; diff --git a/client/lib/l10n/strings_ja.dart b/client/lib/l10n/strings_ja.dart index c35faff..458e7cb 100644 --- a/client/lib/l10n/strings_ja.dart +++ b/client/lib/l10n/strings_ja.dart @@ -27,6 +27,12 @@ class StringsJa extends AppText { @override String get secure => 'セキュア'; @override + String get disconnectTitle => '切断しますか?'; + @override + String get disconnectBody => '接続は暗号化されています。切断してもよろしいですか?'; + @override + String get disconnectAction => '切断'; + @override String get tabConnect => '接続'; @override String get tabServers => 'サーバー'; diff --git a/client/lib/l10n/strings_ko.dart b/client/lib/l10n/strings_ko.dart index 64e5eb9..3c308b5 100644 --- a/client/lib/l10n/strings_ko.dart +++ b/client/lib/l10n/strings_ko.dart @@ -27,6 +27,12 @@ class StringsKo extends AppText { @override String get secure => '보안'; @override + String get disconnectTitle => '연결 해제하시겠습니까?'; + @override + String get disconnectBody => '연결이 암호화되어 있습니다. 정말 연결을 해제하시겠습니까?'; + @override + String get disconnectAction => '연결 해제'; + @override String get tabConnect => '연결'; @override String get tabServers => '서버'; diff --git a/client/lib/l10n/strings_ru.dart b/client/lib/l10n/strings_ru.dart index db82fd6..3243789 100644 --- a/client/lib/l10n/strings_ru.dart +++ b/client/lib/l10n/strings_ru.dart @@ -27,6 +27,12 @@ class StringsRu extends AppText { @override String get secure => 'ЗАЩИЩЕНО'; @override + String get disconnectTitle => 'Отключиться?'; + @override + String get disconnectBody => 'Ваше соединение зашифровано. Действительно отключиться?'; + @override + String get disconnectAction => 'Отключить'; + @override String get tabConnect => 'Подключение'; @override String get tabServers => 'Серверы'; diff --git a/client/lib/l10n/strings_zh.dart b/client/lib/l10n/strings_zh.dart index 5a5f8ab..c7670cc 100644 --- a/client/lib/l10n/strings_zh.dart +++ b/client/lib/l10n/strings_zh.dart @@ -27,6 +27,12 @@ class StringsZh extends AppText { @override String get secure => '已加密'; @override + String get disconnectTitle => '断开连接?'; + @override + String get disconnectBody => '当前连接已加密。确定要断开吗?'; + @override + String get disconnectAction => '断开'; + @override String get tabConnect => '连接'; @override String get tabServers => '节点'; diff --git a/client/lib/screens/connect_page.dart b/client/lib/screens/connect_page.dart index 95ff232..430c564 100644 --- a/client/lib/screens/connect_page.dart +++ b/client/lib/screens/connect_page.dart @@ -21,6 +21,47 @@ class ConnectPage extends ConsumerWidget { final bool isWide; final VoidCallback onOpenNodes; + // 连接键点击:off/connecting 直接 toggle;on 态(已连接)先弹二次确认再断开, + // 避免误触瞬断加密连接。 + Future _handleToggle(BuildContext context, WidgetRef ref, VpnPhase phase, AppText t) async { + final notifier = ref.read(connectionProvider.notifier); + if (phase != VpnPhase.on) { + notifier.toggle(); + return; + } + final c = context.pangolin; + final ok = await showDialog( + context: context, + builder: (ctx) => AlertDialog( + backgroundColor: c.surface, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(PangolinRadius.xl)), + title: Row(children: [ + Container( + width: 34, + height: 34, + decoration: BoxDecoration(color: c.dangerSubtle, shape: BoxShape.circle), + child: Icon(PangolinIcons.alertTriangle, size: 18, color: c.danger)), + const SizedBox(width: 12), + Expanded( + child: Text(t.disconnectTitle, + overflow: TextOverflow.ellipsis, + style: PangolinText.body.copyWith(color: c.fg1, fontWeight: FontWeight.w700))), + ]), + content: Text(t.disconnectBody, style: PangolinText.sm.copyWith(color: c.fg2, height: 1.5)), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx, false), + child: Text(t.devCancel, style: PangolinText.sm.copyWith(color: c.fg2, fontWeight: FontWeight.w600))), + TextButton( + onPressed: () => Navigator.pop(ctx, true), + child: Text(t.disconnectAction, + style: PangolinText.sm.copyWith(color: c.danger, fontWeight: FontWeight.w700))), + ], + ), + ); + if (ok == true) notifier.toggle(); + } + @override Widget build(BuildContext context, WidgetRef ref) { final c = context.pangolin; @@ -53,7 +94,7 @@ class ConnectPage extends ConsumerWidget { elapsed: conn.elapsed, offLabel: t.connectNow, secureLabel: t.secure, - onTap: () => ref.read(connectionProvider.notifier).toggle(), + onTap: () => _handleToggle(context, ref, conn.phase, t), ); final captionWidget = Column(mainAxisSize: MainAxisSize.min, children: [ @@ -94,7 +135,7 @@ class ConnectPage extends ConsumerWidget { offLabel: t.connectNow, secureLabel: t.secure, size: 176, - onTap: () => ref.read(connectionProvider.notifier).toggle(), + onTap: () => _handleToggle(context, ref, conn.phase, t), ), const SizedBox(height: 24), Text(caption,