From a42730fb05a54411dadcbcfc99ed33062836cfc9 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Tue, 25 Nov 2025 17:43:02 +0800 Subject: [PATCH 1/2] =?UTF-8?q?refactor(views):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E7=A7=AF=E5=88=86=E6=98=BE=E7=A4=BA=E7=BB=84=E4=BB=B6=E4=B8=BA?= =?UTF-8?q?=E7=8B=AC=E7=AB=8B=E8=A1=8C=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将积分统计卡片重构为独立的行组件,提升代码复用性和可维护性 移除冗余的积分卡片代码,统一积分显示样式 --- .../usage/views/aichat_stats_view.dart | 6 + .../usage/views/meeting_stats_view.dart | 121 ++++++---------- .../usage/views/translation_stats_view.dart | 136 +++++++----------- 3 files changed, 100 insertions(+), 163 deletions(-) diff --git a/lib/modules/usage/views/aichat_stats_view.dart b/lib/modules/usage/views/aichat_stats_view.dart index 7ac2d806b..a04fe6c44 100644 --- a/lib/modules/usage/views/aichat_stats_view.dart +++ b/lib/modules/usage/views/aichat_stats_view.dart @@ -40,6 +40,12 @@ class AiChatStatsView extends GetView { } Widget _buildBody(bool isDarkMode) { + return Column( + children: [_buildIntegralRow(isDarkMode)], + ); + } + + Widget _buildIntegralRow(bool isDarkMode) { return Obx(() { final _ = User.refreshTick.value; final u = User.instance; diff --git a/lib/modules/usage/views/meeting_stats_view.dart b/lib/modules/usage/views/meeting_stats_view.dart index de27ba356..63ea4897a 100644 --- a/lib/modules/usage/views/meeting_stats_view.dart +++ b/lib/modules/usage/views/meeting_stats_view.dart @@ -51,14 +51,13 @@ class MeetingStatsView extends GetView { Widget _buildBody(bool isDarkMode) { return Column( children: [ + _buildIntegralRow(isDarkMode), _buildTimeRangeSelector(isDarkMode), Expanded( child: SingleChildScrollView( padding: EdgeInsets.all(16.w), child: Column( children: [ - _buildIntegralCard(isDarkMode), - SizedBox(height: 16.h), _buildOverviewCard(isDarkMode), ], ), @@ -68,6 +67,51 @@ class MeetingStatsView extends GetView { ); } + Widget _buildIntegralRow(bool isDarkMode) { + return Obx(() { + final _ = User.refreshTick.value; + final u = User.instance; + return Padding( + padding: EdgeInsets.all(16.w), + child: Container( + padding: EdgeInsets.all(16.w), + decoration: BoxDecoration( + color: isDarkMode ? AppColors.cardBackground : Colors.white, + borderRadius: BorderRadius.circular(12.r), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.05), + blurRadius: 10, + offset: const Offset(0, 2)), + ], + ), + child: Row( + children: [ + Icon(Icons.people, color: AppColors.primary, size: 20.sp), + SizedBox(width: 8.w), + Expanded( + child: Text( + '会议积分', + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w600, + color: isDarkMode ? Colors.white : Colors.black), + ), + ), + Text( + '${u.meetintegral}/${u.meettotalintegral}', + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w600, + color: isDarkMode ? Colors.white : Colors.black), + ), + ], + ), + ), + ); + }); + } + Widget _buildTimeRangeSelector(bool isDarkMode) { return Container( margin: EdgeInsets.all(16.w), @@ -212,77 +256,4 @@ class MeetingStatsView extends GetView { ], ); } - - Widget _buildIntegralCard(bool isDarkMode) { - return Container( - padding: EdgeInsets.all(16.w), - decoration: BoxDecoration( - color: isDarkMode ? AppColors.cardBackground : Colors.white, - borderRadius: BorderRadius.circular(12.r), - boxShadow: [ - BoxShadow( - color: Colors.black.withValues(alpha: 0.05), - blurRadius: 10, - offset: const Offset(0, 2), - ), - ], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon(Icons.stacked_line_chart, - color: AppColors.primary, size: 20.sp), - SizedBox(width: 8.w), - Text( - '积分统计', - style: TextStyle( - fontSize: 16.sp, - fontWeight: FontWeight.w600, - color: isDarkMode ? Colors.white : Colors.black, - ), - ), - ], - ), - SizedBox(height: 16.h), - Obx(() { - final _ = User.refreshTick.value; - final u = User.instance; - return _buildIntegralRow( - '会议', - '${controller.formatNumber(u.meetintegral)}/${controller.formatNumber(u.meettotalintegral)}', - Icons.people, - isDarkMode, - ); - }), - ], - ), - ); - } - - Widget _buildIntegralRow( - String label, String value, IconData icon, bool isDarkMode) { - return Row( - children: [ - Icon(icon, color: AppColors.primary, size: 18.sp), - SizedBox(width: 12.w), - Expanded( - child: Text( - label, - style: TextStyle( - fontSize: 14.sp, - color: isDarkMode ? Colors.white : Colors.black), - ), - ), - Text( - value, - style: TextStyle( - fontSize: 14.sp, - fontWeight: FontWeight.w600, - color: isDarkMode ? Colors.white : Colors.black), - ), - ], - ); - } } diff --git a/lib/modules/usage/views/translation_stats_view.dart b/lib/modules/usage/views/translation_stats_view.dart index 4c98c9493..a966284c4 100644 --- a/lib/modules/usage/views/translation_stats_view.dart +++ b/lib/modules/usage/views/translation_stats_view.dart @@ -89,14 +89,15 @@ class TranslationStatsView extends GetView { Widget _buildBody(bool isDarkMode) { return Column( children: [ + _buildIntegralRow(isDarkMode), _buildTimeRangeSelector(isDarkMode), Expanded( child: SingleChildScrollView( padding: EdgeInsets.all(16.w), child: Column( children: [ - _buildIntegralCard(isDarkMode), - SizedBox(height: 16.h), + // _buildIntegralCard(isDarkMode), + // SizedBox(height: 16.h), if (controller.selectedTabIndex.value == 0) _buildRealtimeStatsCard(isDarkMode), SizedBox(height: 16.h), @@ -115,6 +116,51 @@ class TranslationStatsView extends GetView { ); } + Widget _buildIntegralRow(bool isDarkMode) { + return Obx(() { + final _ = User.refreshTick.value; + final u = User.instance; + return Padding( + padding: EdgeInsets.all(16.w), + child: Container( + padding: EdgeInsets.all(16.w), + decoration: BoxDecoration( + color: isDarkMode ? AppColors.cardBackground : Colors.white, + borderRadius: BorderRadius.circular(12.r), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.05), + blurRadius: 10, + offset: const Offset(0, 2)), + ], + ), + child: Row( + children: [ + Icon(Icons.translate, color: AppColors.primary, size: 20.sp), + SizedBox(width: 8.w), + Expanded( + child: Text( + '翻译积分', + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w600, + color: isDarkMode ? Colors.white : Colors.black), + ), + ), + Text( + '${u.tradeintegral}/${u.tradetotalintegral}', + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w600, + color: isDarkMode ? Colors.white : Colors.black), + ), + ], + ), + ), + ); + }); + } + Widget _buildTimeRangeSelector(bool isDarkMode) { return Container( margin: EdgeInsets.all(16.w), @@ -424,92 +470,6 @@ class TranslationStatsView extends GetView { ); } - Widget _buildIntegralCard(bool isDarkMode) { - return Container( - padding: EdgeInsets.all(16.w), - decoration: BoxDecoration( - color: isDarkMode ? AppColors.cardBackground : Colors.white, - borderRadius: BorderRadius.circular(12.r), - boxShadow: [ - BoxShadow( - color: Colors.black.withValues(alpha: 0.05), - blurRadius: 10, - offset: const Offset(0, 2), - ), - ], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Icon( - Icons.stacked_line_chart, - color: AppColors.primary, - size: 20.sp, - ), - SizedBox(width: 8.w), - Text( - '积分统计', - style: TextStyle( - fontSize: 16.sp, - fontWeight: FontWeight.w600, - color: isDarkMode ? Colors.white : Colors.black, - ), - ), - ], - ), - SizedBox(height: 16.h), - Obx(() { - final _ = User.refreshTick.value; - final u = User.instance; - return Column( - children: [ - _buildIntegralRow( - '翻译', - '${controller.formatNumber(u.tradeintegral)}/${controller.formatNumber(u.tradetotalintegral)}', - Icons.translate, - isDarkMode, - ), - ], - ); - }), - ], - ), - ); - } - - Widget _buildIntegralRow( - String label, String value, IconData icon, bool isDarkMode) { - return Row( - children: [ - Icon( - icon, - color: AppColors.primary, - size: 18.sp, - ), - SizedBox(width: 12.w), - Expanded( - child: Text( - label, - style: TextStyle( - fontSize: 14.sp, - color: isDarkMode ? Colors.white : Colors.black, - ), - ), - ), - Text( - value, - style: TextStyle( - fontSize: 14.sp, - fontWeight: FontWeight.w600, - color: isDarkMode ? Colors.white : Colors.black, - ), - ), - ], - ); - } - Widget _buildApiCallRow( String label, int count, IconData icon, bool isDarkMode) { return Row( From 01be27d6b26cdb8a119c8f973d2acf4ca5754a22 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Tue, 25 Nov 2025 19:30:00 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat(usage):=20=E6=B7=BB=E5=8A=A0AI?= =?UTF-8?q?=E8=81=8A=E5=A4=A9=E7=BB=9F=E8=AE=A1=E8=A7=86=E5=9B=BE=E7=9A=84?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现通过快速点击翻译统计标题三次来切换显示/隐藏AI聊天统计视图 修改底部导航栏根据显示状态动态调整项目 --- .../translation_stats_controller.dart | 19 +++++++++++ .../usage/controllers/usage_controller.dart | 29 ++++++++++++++--- .../usage/views/translation_stats_view.dart | 15 +++++---- lib/modules/usage/views/usage_view.dart | 32 ++++++++++--------- 4 files changed, 69 insertions(+), 26 deletions(-) diff --git a/lib/modules/usage/controllers/translation_stats_controller.dart b/lib/modules/usage/controllers/translation_stats_controller.dart index 7015537aa..099138941 100644 --- a/lib/modules/usage/controllers/translation_stats_controller.dart +++ b/lib/modules/usage/controllers/translation_stats_controller.dart @@ -2,6 +2,7 @@ import 'package:get/get.dart'; import '../../../data/services/usage_stats_service.dart'; import '../../../data/models/translation_usage_stats.dart'; import '../../../core/utils/logger.dart'; +import 'usage_controller.dart'; class TranslationStatsController extends GetxController { final UsageStatsService _usageService = Get.find(); @@ -297,6 +298,24 @@ class TranslationStatsController extends GetxController { return '¥${cost.toStringAsFixed(2)}'; } + int _titleTapCount = 0; + int _lastTitleTapTs = 0; + + void onTitleTap() { + final now = DateTime.now().millisecondsSinceEpoch; + if (now - _lastTitleTapTs <= 800) { + _titleTapCount++; + } else { + _titleTapCount = 1; + } + _lastTitleTapTs = now; + if (_titleTapCount >= 3) { + _titleTapCount = 0; + final uc = Get.find(); + uc.toggleAiChat(!uc.showAiChat.value); + } + } + String formatNumber(int number) { if (number < 1000) { return number.toString(); diff --git a/lib/modules/usage/controllers/usage_controller.dart b/lib/modules/usage/controllers/usage_controller.dart index d8ecbbecd..7407d5e09 100644 --- a/lib/modules/usage/controllers/usage_controller.dart +++ b/lib/modules/usage/controllers/usage_controller.dart @@ -8,15 +8,34 @@ class UsageController extends GetxController { late PageController pageController; final RxInt pageIndex = 0.obs; - final List pageList = const [ - AiChatStatsView(), - TranslationStatsView(), - MeetingStatsView(), - ]; + final RxBool showAiChat = false.obs; + + List get pageList => showAiChat.value + ? const [ + AiChatStatsView(), + TranslationStatsView(), + MeetingStatsView(), + ] + : const [ + TranslationStatsView(), + MeetingStatsView(), + ]; @override void onInit() { super.onInit(); pageController = PageController(initialPage: 0); } + + void toggleAiChat(bool enable) { + if (showAiChat.value == enable) return; + showAiChat.value = enable; + final int maxIndex = pageList.length - 1; + if (pageIndex.value > maxIndex) { + pageIndex.value = maxIndex; + pageController.jumpToPage(maxIndex); + } else { + pageController.jumpToPage(pageIndex.value); + } + } } diff --git a/lib/modules/usage/views/translation_stats_view.dart b/lib/modules/usage/views/translation_stats_view.dart index a966284c4..6fc3c706c 100644 --- a/lib/modules/usage/views/translation_stats_view.dart +++ b/lib/modules/usage/views/translation_stats_view.dart @@ -33,12 +33,15 @@ class TranslationStatsView extends GetView { ), backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.white, elevation: 0.5, - title: Text( - '翻译统计', - style: TextStyle( - fontSize: 17.sp, - fontWeight: FontWeight.w600, - color: isDarkMode ? Colors.white : Colors.black, + title: GestureDetector( + onTap: controller.onTitleTap, + child: Text( + '翻译统计', + style: TextStyle( + fontSize: 17.sp, + fontWeight: FontWeight.w600, + color: isDarkMode ? Colors.white : Colors.black, + ), ), ), centerTitle: true, diff --git a/lib/modules/usage/views/usage_view.dart b/lib/modules/usage/views/usage_view.dart index 26531868f..814b64ad3 100644 --- a/lib/modules/usage/views/usage_view.dart +++ b/lib/modules/usage/views/usage_view.dart @@ -7,13 +7,6 @@ import '../controllers/usage_controller.dart'; class UsageView extends GetView { const UsageView({super.key}); - // 1. 将导航栏配置提取为常量,提高可维护性 - static const _navItems = [ - _NavItem(Icons.chat_bubble_outline, 'AI chat'), - _NavItem(Icons.translate, 'interpretation'), - _NavItem(Icons.people, 'meeting'), - ]; - // 2. 颜色配置集中管理 Color _getBgColor(bool isDark) => isDark ? Colors.black : const Color(0xFFF9FAFB); @@ -28,11 +21,11 @@ class UsageView extends GetView { return Scaffold( backgroundColor: isDarkMode ? AppColors.darkBackground : Colors.grey[50], - body: PageView( - physics: const NeverScrollableScrollPhysics(), - controller: controller.pageController, - children: controller.pageList, - ), + body: Obx(() => PageView( + physics: const NeverScrollableScrollPhysics(), + controller: controller.pageController, + children: controller.pageList, + )), bottomNavigationBar: Obx( () { final currentIndex = controller.pageIndex.value; @@ -48,7 +41,16 @@ class UsageView extends GetView { // 3. 添加安全区域适配(针对刘海屏) child: BottomNavigationBar( // 4. 使用生成的导航项 - items: _navItems + items: (controller.showAiChat.value + ? const [ + _NavItem(Icons.chat_bubble_outline, 'AI chat'), + _NavItem(Icons.translate, 'interpretation'), + _NavItem(Icons.people, 'meeting'), + ] + : const [ + _NavItem(Icons.translate, 'interpretation'), + _NavItem(Icons.people, 'meeting'), + ]) .map((item) => BottomNavigationBarItem( icon: Icon(item.icon), activeIcon: Icon(item.icon, @@ -70,8 +72,8 @@ class UsageView extends GetView { type: BottomNavigationBarType.fixed, // 7. 增加图标和文字的间距 iconSize: 22.w, // 响应式图标尺寸 - selectedLabelStyle: TextStyle(height: 1.5), // 文字垂直间距 - unselectedLabelStyle: TextStyle(height: 1.5), + selectedLabelStyle: TextStyle(height: 1.5.sp), // 文字垂直间距 + unselectedLabelStyle: TextStyle(height: 1.5.sp), ), ); },