From ecec8ceb4a121d8e230e4dc113cfb7bd0aa456f9 Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Wed, 16 Jul 2025 17:02:54 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=83=AD=E5=8A=9B?= =?UTF-8?q?=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/meeting_mine_controller.dart | 68 +++++++++++++++++-- .../bottomSheet/statistics_bottom_sheet.dart | 20 +++++- .../meeting/views/meeting_mine_view.dart | 37 +++++++--- 3 files changed, 108 insertions(+), 17 deletions(-) diff --git a/lib/modules/meeting/controllers/meeting_mine_controller.dart b/lib/modules/meeting/controllers/meeting_mine_controller.dart index 907ae480e..b219cc602 100644 --- a/lib/modules/meeting/controllers/meeting_mine_controller.dart +++ b/lib/modules/meeting/controllers/meeting_mine_controller.dart @@ -1,9 +1,13 @@ +import 'package:common_utils/common_utils.dart'; import 'package:get/get.dart'; -import 'meeting_home_controller.dart'; +import '../../../data/services/db/sqflite_api.dart'; +import '../../profile/controllers/profile_controller.dart'; class MeetingMineController extends GetxController { - final _controller = Get.find(); + final profileCtrl = Get.find(); + + Map statistics = {}; RxInt days = 0.obs; RxInt numberFiles = 0.obs; @@ -12,15 +16,69 @@ class MeetingMineController extends GetxController { RxInt maxNumberFiles = 0.obs; RxDouble maxHourage = 0.0.obs; + int averageNumberFiles = 0; + @override void onInit() { super.onInit(); _getStatistics(); } - void _getStatistics() { - numberFiles.value = _controller.originalDataList.length; + void _getStatistics() async { + List dataList = await SqfliteApi.getMeetingList(); + numberFiles.value = dataList.length; + statistics = {}; + + for (var element in dataList) { + DateTime creationtime = + DateTime.fromMillisecondsSinceEpoch(element['creationtime']); + int dayOfYear = DateUtil.getDayOfYear(creationtime); + + if (!statistics.containsKey(dayOfYear)) { + statistics[dayOfYear] = { + 'creationtime': creationtime, + 'count': 0, + 'seconds': 0, + }; + } + statistics[dayOfYear]['count']++; + statistics[dayOfYear]['seconds'] += element['seconds']; + } + + int currentD = 1; + int maxD = 1; + int previous = 0; + int h = 0; + int maxH = 0; + int maxN = 0; + int minN = 0; + statistics.forEach((key, value) { + h += value['seconds'] as int; + if (key + 1 == previous) { + currentD++; + if (currentD > maxD) { + maxD = currentD; + } + } else { + currentD = 1; + } + previous = key; + if (value['count'] > maxN) { + maxN = value['count']; + } + if (value['count'] < minN || minN == 0) { + minN = value['count']; + } + if (value['seconds'] > maxH) { + maxH = value['seconds']; + } + }); - for (var element in _controller.originalDataList) {} + days.value = statistics.length; + hourage.value = double.parse((h / 60 / 60).toStringAsFixed(2)); + continuousDays.value = maxD; + maxNumberFiles.value = maxN; + maxHourage.value = double.parse((maxH / 60 / 60).toStringAsFixed(2)); + averageNumberFiles = (maxN + minN) ~/ 2; } } diff --git a/lib/modules/meeting/views/bottomSheet/statistics_bottom_sheet.dart b/lib/modules/meeting/views/bottomSheet/statistics_bottom_sheet.dart index 667bf370a..2eb8d1f38 100644 --- a/lib/modules/meeting/views/bottomSheet/statistics_bottom_sheet.dart +++ b/lib/modules/meeting/views/bottomSheet/statistics_bottom_sheet.dart @@ -171,8 +171,26 @@ class _StatisticsBottomSheetState extends State { ), itemCount: 364, itemBuilder: (BuildContext context, int index) { + final col = index % 52; + final row = index ~/ 52; + final vIndex = row + col * 7; + + int type = 0; + if (_mineController.statistics + .containsKey(vIndex + 1)) { + if (_mineController.statistics[vIndex + 1]['count'] <= + _mineController.averageNumberFiles) { + type = 1; + } else { + type = 2; + } + } return Container( - color: Colors.grey[200], + color: type == 0 + ? Colors.grey[200] + : type == 1 + ? Colors.red[100] + : Colors.red, ); }, ), diff --git a/lib/modules/meeting/views/meeting_mine_view.dart b/lib/modules/meeting/views/meeting_mine_view.dart index ae0337965..a2cef03c5 100644 --- a/lib/modules/meeting/views/meeting_mine_view.dart +++ b/lib/modules/meeting/views/meeting_mine_view.dart @@ -3,7 +3,6 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'dart:io'; import '../../../routes/app_routes.dart'; -import '../../profile/controllers/profile_controller.dart'; import '../controllers/meeting_mine_controller.dart'; import 'bottomSheet/statistics_bottom_sheet.dart'; @@ -96,8 +95,6 @@ class MeetingMineView extends GetView { } Widget _head() { - final ProfileController profileCtrl = - Get.find(); // 从GetX中获取ProfileController实例 return Padding( padding: EdgeInsets.symmetric(vertical: 20.w), child: GestureDetector( @@ -114,14 +111,19 @@ class MeetingMineView extends GetView { ), child: CircleAvatar( radius: 25.w, - backgroundImage: profileCtrl.localAvatar.value.isNotEmpty - ? FileImage(File(profileCtrl.localAvatar.value)) - : (profileCtrl.userInfo.value.avatar.isNotEmpty - ? NetworkImage(profileCtrl.userInfo.value.avatar) + backgroundImage: controller + .profileCtrl.localAvatar.value.isNotEmpty + ? FileImage( + File(controller.profileCtrl.localAvatar.value)) + : (controller + .profileCtrl.userInfo.value.avatar.isNotEmpty + ? NetworkImage( + controller.profileCtrl.userInfo.value.avatar) : null) as ImageProvider?, backgroundColor: Colors.grey[200], - child: (profileCtrl.localAvatar.value.isEmpty && - profileCtrl.userInfo.value.avatar.isEmpty) + child: (controller.profileCtrl.localAvatar.value.isEmpty && + controller + .profileCtrl.userInfo.value.avatar.isEmpty) ? Icon( Icons.person, size: 25.r, @@ -133,7 +135,7 @@ class MeetingMineView extends GetView { Expanded( child: Obx( () => Text( - profileCtrl.userInfo.value.name, + controller.profileCtrl.userInfo.value.name, style: TextStyle( color: Colors.black87, fontSize: 20.sp, @@ -203,9 +205,22 @@ class MeetingMineView extends GetView { ), itemCount: 364, itemBuilder: (BuildContext context, int index) { + int type = 0; + if (controller.statistics.containsKey(index + 1)) { + if (controller.statistics[index + 1]['count'] <= + controller.averageNumberFiles) { + type = 1; + } else { + type = 2; + } + } return Container( decoration: BoxDecoration( - color: Colors.grey[200], + color: type == 0 + ? Colors.grey[200] + : type == 1 + ? Colors.red[100] + : Colors.red, borderRadius: BorderRadius.circular(2.w), ), ); From dbd518c0ed713757703c3c3e23ac4e03a1257e7c Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Wed, 16 Jul 2025 17:55:00 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/meeting_mine_controller.dart | 17 ++++++ .../bottomSheet/statistics_bottom_sheet.dart | 28 +++++++-- .../meeting/views/meeting_mine_view.dart | 61 ++++++++++--------- 3 files changed, 70 insertions(+), 36 deletions(-) diff --git a/lib/modules/meeting/controllers/meeting_mine_controller.dart b/lib/modules/meeting/controllers/meeting_mine_controller.dart index b219cc602..9e0fe905d 100644 --- a/lib/modules/meeting/controllers/meeting_mine_controller.dart +++ b/lib/modules/meeting/controllers/meeting_mine_controller.dart @@ -1,4 +1,5 @@ import 'package:common_utils/common_utils.dart'; +import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../../data/services/db/sqflite_api.dart'; @@ -7,6 +8,8 @@ import '../../profile/controllers/profile_controller.dart'; class MeetingMineController extends GetxController { final profileCtrl = Get.find(); + final ScrollController scrollController = ScrollController(); + Map statistics = {}; RxInt days = 0.obs; @@ -21,9 +24,23 @@ class MeetingMineController extends GetxController { @override void onInit() { super.onInit(); + WidgetsBinding.instance.addPostFrameCallback((_) { + if (scrollController.hasClients) { + final now = DateTime.now(); + if (now.month > 6) { + scrollController.jumpTo(scrollController.position.maxScrollExtent); + } + } + }); _getStatistics(); } + @override + void onClose() { + scrollController.dispose(); + super.onClose(); + } + void _getStatistics() async { List dataList = await SqfliteApi.getMeetingList(); numberFiles.value = dataList.length; diff --git a/lib/modules/meeting/views/bottomSheet/statistics_bottom_sheet.dart b/lib/modules/meeting/views/bottomSheet/statistics_bottom_sheet.dart index 2eb8d1f38..e9cd37d2a 100644 --- a/lib/modules/meeting/views/bottomSheet/statistics_bottom_sheet.dart +++ b/lib/modules/meeting/views/bottomSheet/statistics_bottom_sheet.dart @@ -1,7 +1,10 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; +import '../../../profile/controllers/profile_controller.dart'; import '../../controllers/meeting_mine_controller.dart'; class StatisticsBottomSheet extends StatefulWidget { @@ -13,6 +16,7 @@ class StatisticsBottomSheet extends StatefulWidget { class _StatisticsBottomSheetState extends State { final _mineController = Get.find(); + final _profileCtrl = Get.find(); @override Widget build(BuildContext context) { @@ -49,15 +53,27 @@ class _StatisticsBottomSheetState extends State { margin: EdgeInsets.only(bottom: 10.w), decoration: BoxDecoration( borderRadius: BorderRadius.circular(50.w), - image: const DecorationImage( - image: NetworkImage( - 'https://pic8.iqiyipic.com/image/20250403/ed/a7/a_100013977_m_601_m75_318_424.avif'), - fit: BoxFit.cover, - ), + ), + child: CircleAvatar( + radius: 25.w, + backgroundImage: _profileCtrl.localAvatar.value.isNotEmpty + ? FileImage(File(_profileCtrl.localAvatar.value)) + : (_profileCtrl.userInfo.value.avatar.isNotEmpty + ? NetworkImage(_profileCtrl.userInfo.value.avatar) + : null) as ImageProvider?, + backgroundColor: Colors.grey[200], + child: (_profileCtrl.localAvatar.value.isEmpty && + _profileCtrl.userInfo.value.avatar.isEmpty) + ? Icon( + Icons.person, + size: 25.r, + color: Colors.grey[600], + ) + : null, ), ), Text( - 'tianming liu', + _profileCtrl.userInfo.value.name, style: TextStyle( color: Colors.black87, fontSize: 20.sp, diff --git a/lib/modules/meeting/views/meeting_mine_view.dart b/lib/modules/meeting/views/meeting_mine_view.dart index a2cef03c5..153822bb1 100644 --- a/lib/modules/meeting/views/meeting_mine_view.dart +++ b/lib/modules/meeting/views/meeting_mine_view.dart @@ -102,36 +102,36 @@ class MeetingMineView extends GetView { child: Row( children: [ // 统一头像显示逻辑:本地头像 > 网络头像 > 默认图标 - Obx(() => Container( - width: 50.w, - height: 50.w, - margin: EdgeInsets.only(right: 10.w), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(50.w), - ), - child: CircleAvatar( - radius: 25.w, - backgroundImage: controller - .profileCtrl.localAvatar.value.isNotEmpty - ? FileImage( - File(controller.profileCtrl.localAvatar.value)) - : (controller - .profileCtrl.userInfo.value.avatar.isNotEmpty - ? NetworkImage( - controller.profileCtrl.userInfo.value.avatar) - : null) as ImageProvider?, - backgroundColor: Colors.grey[200], - child: (controller.profileCtrl.localAvatar.value.isEmpty && - controller - .profileCtrl.userInfo.value.avatar.isEmpty) - ? Icon( - Icons.person, - size: 25.r, - color: Colors.grey[600], - ) - : null, - ), - )), + Obx( + () => Container( + width: 50.w, + height: 50.w, + margin: EdgeInsets.only(right: 10.w), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(50.w), + ), + child: CircleAvatar( + radius: 25.w, + backgroundImage: controller + .profileCtrl.localAvatar.value.isNotEmpty + ? FileImage( + File(controller.profileCtrl.localAvatar.value)) + : (controller.profileCtrl.userInfo.value.avatar.isNotEmpty + ? NetworkImage( + controller.profileCtrl.userInfo.value.avatar) + : null) as ImageProvider?, + backgroundColor: Colors.grey[200], + child: (controller.profileCtrl.localAvatar.value.isEmpty && + controller.profileCtrl.userInfo.value.avatar.isEmpty) + ? Icon( + Icons.person, + size: 25.r, + color: Colors.grey[600], + ) + : null, + ), + ), + ), Expanded( child: Obx( () => Text( @@ -197,6 +197,7 @@ class MeetingMineView extends GetView { padding: EdgeInsets.symmetric(horizontal: 2.w), margin: EdgeInsets.only(top: 8.w, bottom: 2.w), child: GridView.builder( + controller: controller.scrollController, scrollDirection: Axis.horizontal, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 7, From b53010e18a0c46f97ce9edd076f8ff4aea8c8363 Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Wed, 16 Jul 2025 18:41:33 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../meeting/views/meeting_home_view.dart | 40 ++++++++++----- .../meeting/views/meeting_mine_view.dart | 51 ++++++++++++------- 2 files changed, 59 insertions(+), 32 deletions(-) diff --git a/lib/modules/meeting/views/meeting_home_view.dart b/lib/modules/meeting/views/meeting_home_view.dart index 2d79397e5..519273713 100644 --- a/lib/modules/meeting/views/meeting_home_view.dart +++ b/lib/modules/meeting/views/meeting_home_view.dart @@ -11,15 +11,27 @@ import '../model/meeting_model.dart'; import 'bottomSheet/edit_title_bottom_sheet.dart'; import 'bottomSheet/import_audio_bottom_sheet.dart'; -class MeetingHomeView extends GetView { +class MeetingHomeView extends StatefulWidget { const MeetingHomeView({super.key}); + @override + State createState() => _MeetingHomeViewState(); +} + +class _MeetingHomeViewState extends State + with AutomaticKeepAliveClientMixin { + final _controller = Get.put(MeetingHomeController()); + + @override + bool get wantKeepAlive => true; + @override Widget build(BuildContext context) { + super.build(context); final isDarkMode = Theme.of(context).brightness == Brightness.dark; return PopScope( - onPopInvokedWithResult: controller.onPopInvokedWithResult, + onPopInvokedWithResult: _controller.onPopInvokedWithResult, child: Scaffold( backgroundColor: isDarkMode ? Colors.black : Colors.grey[100], appBar: _buildAppBar(isDarkMode), @@ -61,7 +73,7 @@ class MeetingHomeView extends GetView { Widget _buildFiltrate(bool isDarkMode) { return GestureDetector( onTap: () { - controller.meetingController.scaffoldKey.currentState?.openDrawer(); + _controller.meetingController.scaffoldKey.currentState?.openDrawer(); }, behavior: HitTestBehavior.translucent, child: Row( @@ -95,7 +107,7 @@ class MeetingHomeView extends GetView { 5.horizontalSpace, Obx( () => Text( - '${controller.filtrateType.value.isNotEmpty ? controller.filtrateType : '全部文件'}(${controller.dataList.length})', + '${_controller.filtrateType.value.isNotEmpty ? _controller.filtrateType : '全部文件'}(${_controller.dataList.length})', style: TextStyle( color: isDarkMode ? Colors.white : Colors.black87, fontSize: 16.sp, @@ -194,10 +206,11 @@ class MeetingHomeView extends GetView { Widget _buildBody(BuildContext context, bool isDarkMode) { return Obx( () => ListView.builder( - itemCount: controller.dataList.length, + itemCount: _controller.dataList.length, padding: EdgeInsets.fromLTRB(12.w, 5.w, 12.w, 90.w), itemBuilder: (BuildContext context, int index) { - MeetingModel data = MeetingModel.fromJson(controller.dataList[index]); + MeetingModel data = + MeetingModel.fromJson(_controller.dataList[index]); return _item(context, isDarkMode, data); }, ), @@ -298,7 +311,7 @@ class MeetingHomeView extends GetView { const Spacer(), Obx(() { Map uploadData = - controller.meetingUploadService.uploadList.firstWhere( + _controller.meetingUploadService.uploadList.firstWhere( (element) => element['id'] == data.id, orElse: () => {}, ); @@ -307,13 +320,14 @@ class MeetingHomeView extends GetView { alignment: Alignment.center, children: [ if (uploadData['id'] == - controller.meetingUploadService.meetingId.value) + _controller + .meetingUploadService.meetingId.value) Obx( () => SizedBox( width: 20.w, height: 20.w, child: CircularProgressIndicator( - value: controller.meetingUploadService + value: _controller.meetingUploadService .uploadProgress.value, valueColor: const AlwaysStoppedAnimation( @@ -339,7 +353,7 @@ class MeetingHomeView extends GetView { }), Obx(() { Map taskData = - controller.meetingTaskService.taskFinishList.firstWhere( + _controller.meetingTaskService.taskFinishList.firstWhere( (element) => element['id'] == data.id, orElse: () => {}, ); @@ -401,9 +415,9 @@ class MeetingHomeView extends GetView { isDarkMode ? Colors.white : Colors.black87, () { final dotIndex = data.title.lastIndexOf('.'); - controller.titleController.text = + _controller.titleController.text = dotIndex != -1 ? data.title.substring(0, dotIndex) : data.title; - controller.titleCSuffix = + _controller.titleCSuffix = data.title.substring(dotIndex, data.title.length); Get.bottomSheet( EditTitleBottomSheet(data.id), @@ -426,7 +440,7 @@ class MeetingHomeView extends GetView { 'delete'.tr, // 删除 isDarkMode ? Colors.red[700]! : Colors.red, () { - controller.deleteMeeting(data.id, data.filepath); + _controller.deleteMeeting(data.id, data.filepath); }, ), ], diff --git a/lib/modules/meeting/views/meeting_mine_view.dart b/lib/modules/meeting/views/meeting_mine_view.dart index 153822bb1..62c014d6b 100644 --- a/lib/modules/meeting/views/meeting_mine_view.dart +++ b/lib/modules/meeting/views/meeting_mine_view.dart @@ -7,11 +7,23 @@ import '../../../routes/app_routes.dart'; import '../controllers/meeting_mine_controller.dart'; import 'bottomSheet/statistics_bottom_sheet.dart'; -class MeetingMineView extends GetView { +class MeetingMineView extends StatefulWidget { const MeetingMineView({super.key}); + @override + State createState() => _MeetingMineViewState(); +} + +class _MeetingMineViewState extends State + with AutomaticKeepAliveClientMixin { + final _controller = Get.put(MeetingMineController()); + + @override + bool get wantKeepAlive => true; + @override Widget build(BuildContext context) { + super.build(context); return Scaffold( backgroundColor: Colors.grey[100], body: Container( @@ -112,17 +124,18 @@ class MeetingMineView extends GetView { ), child: CircleAvatar( radius: 25.w, - backgroundImage: controller - .profileCtrl.localAvatar.value.isNotEmpty - ? FileImage( - File(controller.profileCtrl.localAvatar.value)) - : (controller.profileCtrl.userInfo.value.avatar.isNotEmpty - ? NetworkImage( - controller.profileCtrl.userInfo.value.avatar) - : null) as ImageProvider?, + backgroundImage: + _controller.profileCtrl.localAvatar.value.isNotEmpty + ? FileImage( + File(_controller.profileCtrl.localAvatar.value)) + : (_controller + .profileCtrl.userInfo.value.avatar.isNotEmpty + ? NetworkImage( + _controller.profileCtrl.userInfo.value.avatar) + : null) as ImageProvider?, backgroundColor: Colors.grey[200], - child: (controller.profileCtrl.localAvatar.value.isEmpty && - controller.profileCtrl.userInfo.value.avatar.isEmpty) + child: (_controller.profileCtrl.localAvatar.value.isEmpty && + _controller.profileCtrl.userInfo.value.avatar.isEmpty) ? Icon( Icons.person, size: 25.r, @@ -135,7 +148,7 @@ class MeetingMineView extends GetView { Expanded( child: Obx( () => Text( - controller.profileCtrl.userInfo.value.name, + _controller.profileCtrl.userInfo.value.name, style: TextStyle( color: Colors.black87, fontSize: 20.sp, @@ -176,17 +189,17 @@ class MeetingMineView extends GetView { Row( children: [ _statisticsItem( - '${controller.days.value}', + '${_controller.days.value}', '天', Colors.lime, ), _statisticsItem( - '${controller.numberFiles.value}', + '${_controller.numberFiles.value}', '文件', Colors.lightGreen, ), _statisticsItem( - '${controller.hourage.value}', + '${_controller.hourage.value}', '小时', Colors.blueGrey, ), @@ -197,7 +210,7 @@ class MeetingMineView extends GetView { padding: EdgeInsets.symmetric(horizontal: 2.w), margin: EdgeInsets.only(top: 8.w, bottom: 2.w), child: GridView.builder( - controller: controller.scrollController, + controller: _controller.scrollController, scrollDirection: Axis.horizontal, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 7, @@ -207,9 +220,9 @@ class MeetingMineView extends GetView { itemCount: 364, itemBuilder: (BuildContext context, int index) { int type = 0; - if (controller.statistics.containsKey(index + 1)) { - if (controller.statistics[index + 1]['count'] <= - controller.averageNumberFiles) { + if (_controller.statistics.containsKey(index + 1)) { + if (_controller.statistics[index + 1]['count'] <= + _controller.averageNumberFiles) { type = 1; } else { type = 2;