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/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..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, @@ -89,14 +92,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 +119,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 +473,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( 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), ), ); },