diff --git a/client/lib/widgets/account_screens.dart b/client/lib/widgets/account_screens.dart index ae9ca66..db444f7 100644 --- a/client/lib/widgets/account_screens.dart +++ b/client/lib/widgets/account_screens.dart @@ -188,7 +188,7 @@ class DevicesScreen extends ConsumerWidget { ), const SizedBox(width: 8), _statusDot(c, d.online), - if (!isLocal) _menu(context, ref, c, d) else const SizedBox(width: 8), + _menu(context, ref, c, d, isLocal), ]), ); } @@ -214,7 +214,8 @@ class DevicesScreen extends ConsumerWidget { ]); } - Widget _menu(BuildContext context, WidgetRef ref, PangolinScheme c, Device d) => PopupMenuButton( + // 本机:只给「重命名」(自我踢下线/清除没意义);其他设备:重命名+强制退出+清除。 + Widget _menu(BuildContext context, WidgetRef ref, PangolinScheme c, Device d, bool isLocal) => PopupMenuButton( icon: Icon(PangolinIcons.moreVertical, size: 18, color: c.fg3), color: c.surface, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(PangolinRadius.md), side: BorderSide(color: c.border)), @@ -223,14 +224,16 @@ class DevicesScreen extends ConsumerWidget { Icon(PangolinIcons.edit, size: 16, color: c.fg2), const SizedBox(width: 10), Text(t.devRename, style: PangolinText.sm.copyWith(color: c.fg1)), ])), - PopupMenuItem(value: 'logout', child: Row(children: [ - Icon(PangolinIcons.logOut, size: 16, color: c.fg2), const SizedBox(width: 10), - Text(t.devForceLogout, style: PangolinText.sm.copyWith(color: c.fg1)), - ])), - PopupMenuItem(value: 'clear', child: Row(children: [ - Icon(PangolinIcons.trash, size: 16, color: c.danger), const SizedBox(width: 10), - Text(t.devClearLogin, style: PangolinText.sm.copyWith(color: c.danger)), - ])), + if (!isLocal) ...[ + PopupMenuItem(value: 'logout', child: Row(children: [ + Icon(PangolinIcons.logOut, size: 16, color: c.fg2), const SizedBox(width: 10), + Text(t.devForceLogout, style: PangolinText.sm.copyWith(color: c.fg1)), + ])), + PopupMenuItem(value: 'clear', child: Row(children: [ + Icon(PangolinIcons.trash, size: 16, color: c.danger), const SizedBox(width: 10), + Text(t.devClearLogin, style: PangolinText.sm.copyWith(color: c.danger)), + ])), + ], ], onSelected: (v) async { if (v == 'rename') { diff --git a/client/pubspec.yaml b/client/pubspec.yaml index d32ad8e..7be200f 100644 --- a/client/pubspec.yaml +++ b/client/pubspec.yaml @@ -1,7 +1,7 @@ name: pangolin_vpn description: 穿山甲 · Pangolin — 极简、稳定、跨平台网络加速客户端。 publish_to: "none" -version: 1.0.13+14 +version: 1.0.14+15 environment: sdk: ^3.5.0 diff --git a/client/test/widget/devices_screen_test.dart b/client/test/widget/devices_screen_test.dart index 1b3b442..97541de 100644 --- a/client/test/widget/devices_screen_test.dart +++ b/client/test/widget/devices_screen_test.dart @@ -92,12 +92,21 @@ void main() { expect(find.text('iPhone 15'), findsOneWidget); }); + testWidgets('本机 ⋯ 菜单只有「重命名」,无强制退出/清除', (tester) async { + await pump(tester); + // 第一个菜单 = 本机(MacBook Pro)。 + await tester.tap(find.byType(PopupMenuButton).first); + await tester.pumpAndSettle(); + expect(find.text(t.devRename), findsOneWidget); + expect(find.text(t.devForceLogout), findsNothing); + expect(find.text(t.devClearLogin), findsNothing); + }); + testWidgets('非本机行 ⋯ 菜单 → 强制退出 → 确认 → 调 logout 接口', (tester) async { await pump(tester); - // 仅非本机(iPhone)有 ⋯ 菜单。 - final menu = find.byType(PopupMenuButton); - expect(menu, findsOneWidget); - await tester.tap(menu); + // 本机+手机各一个菜单;手机(最后一个)才有强制退出/清除。 + expect(find.byType(PopupMenuButton), findsNWidgets(2)); + await tester.tap(find.byType(PopupMenuButton).last); await tester.pumpAndSettle(); expect(find.text(t.devForceLogout), findsOneWidget); expect(find.text(t.devClearLogin), findsOneWidget); @@ -115,7 +124,7 @@ void main() { testWidgets('清除登录信息 → 确认 → 调 DELETE 接口', (tester) async { await pump(tester); - await tester.tap(find.byType(PopupMenuButton)); + await tester.tap(find.byType(PopupMenuButton).last); await tester.pumpAndSettle(); await tester.tap(find.text(t.devClearLogin)); await tester.pumpAndSettle(); @@ -127,7 +136,7 @@ void main() { testWidgets('重命名 → 输入新名 → 保存 → 调 rename 接口', (tester) async { await pump(tester); - await tester.tap(find.byType(PopupMenuButton)); + await tester.tap(find.byType(PopupMenuButton).last); await tester.pumpAndSettle(); await tester.tap(find.text(t.devRename)); await tester.pumpAndSettle(); diff --git a/client/windows/installer/pangolin.iss b/client/windows/installer/pangolin.iss index f5d66f6..797df55 100644 --- a/client/windows/installer/pangolin.iss +++ b/client/windows/installer/pangolin.iss @@ -3,7 +3,7 @@ ; 前置: 先在 client/ 跑 `flutter build windows`(Release),产物在 ; ..\..\build\windows\x64\runner\Release #define MyAppName "穿山甲 Pangolin" -#define MyAppVersion "1.0.13" +#define MyAppVersion "1.0.14" #define MyAppPublisher "Pangolin" #define MyAppExeName "pangolin_vpn.exe" #define BuildDir "..\..\build\windows\x64\runner\Release"