From 995129f371cef3d53f8bf029fc6c3926acc2c55c Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Wed, 21 May 2025 14:33:52 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BC=9A=E8=AE=AE=E5=8A=A9?= =?UTF-8?q?=E6=89=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../meeting_details_controller.dart | 3 + .../views/speaker_all_bottom_sheet.dart | 4 +- lib/modules/meeting/views/speech_tab.dart | 14 +- lib/modules/meeting/views/summary_tab.dart | 212 +++++++++++++++--- 4 files changed, 188 insertions(+), 45 deletions(-) diff --git a/lib/modules/meeting/controllers/meeting_details_controller.dart b/lib/modules/meeting/controllers/meeting_details_controller.dart index 3c367eb2a..4aa2a200a 100644 --- a/lib/modules/meeting/controllers/meeting_details_controller.dart +++ b/lib/modules/meeting/controllers/meeting_details_controller.dart @@ -55,6 +55,9 @@ class MeetingDetailsController extends GetxController super.onInit(); tabController = TabController(length: 4, vsync: this); tabController.addListener(() { + if (currentIndex.value == 2 && tabController.index != 2) { + FocusScope.of(Get.context!).unfocus(); + } currentIndex.value = tabController.index; }); final Map args = Get.arguments ?? {}; diff --git a/lib/modules/meeting/views/speaker_all_bottom_sheet.dart b/lib/modules/meeting/views/speaker_all_bottom_sheet.dart index 48b331c8f..bb3271747 100644 --- a/lib/modules/meeting/views/speaker_all_bottom_sheet.dart +++ b/lib/modules/meeting/views/speaker_all_bottom_sheet.dart @@ -145,7 +145,9 @@ class _SpeakerAllBottomSheetState extends State { ), child: Focus( onFocusChange: (bool hasFocus) { - item['isShow'] = hasFocus; + setState(() { + item['isShow'] = hasFocus; + }); }, child: TextField( controller: item['speakerText'], diff --git a/lib/modules/meeting/views/speech_tab.dart b/lib/modules/meeting/views/speech_tab.dart index f7a4bb0f3..94c7debe2 100644 --- a/lib/modules/meeting/views/speech_tab.dart +++ b/lib/modules/meeting/views/speech_tab.dart @@ -21,7 +21,7 @@ class SpeechTab extends GetView { itemBuilder: (BuildContext context, int index) { Map item = controller.textList[index]; TextStyle textStyle = TextStyle( - fontSize: 16.sp, + fontSize: 15.sp, color: Colors.black87, letterSpacing: 0, height: 1.4, @@ -35,11 +35,11 @@ class SpeechTab extends GetView { children: [ if (item['speaker'].isNotEmpty) Container( - width: 12.w, - height: 12.w, - margin: EdgeInsets.only(right: 2.w), + width: 11.w, + height: 11.w, + margin: EdgeInsets.only(right: 3.w), decoration: BoxDecoration( - borderRadius: BorderRadius.circular(28.r), + borderRadius: BorderRadius.circular(11.r), border: Border.all( color: const Color(0xFFFFC107), width: 3.w, @@ -71,7 +71,7 @@ class SpeechTab extends GetView { item['speaker'], style: TextStyle( color: const Color(0xFFFFC107), - fontSize: 14.sp, + fontSize: 13.sp, ), ), ), @@ -84,7 +84,7 @@ class SpeechTab extends GetView { ), style: TextStyle( color: Colors.grey, - fontSize: 14.sp, + fontSize: 13.sp, ), ), ], diff --git a/lib/modules/meeting/views/summary_tab.dart b/lib/modules/meeting/views/summary_tab.dart index d663ee748..b4eb594d2 100644 --- a/lib/modules/meeting/views/summary_tab.dart +++ b/lib/modules/meeting/views/summary_tab.dart @@ -4,15 +4,117 @@ import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; +import '../../../data/services/db/sqflite_api.dart'; import '../controllers/meeting_details_controller.dart'; import 'speaker_all_bottom_sheet.dart'; -class SummaryTab extends GetView { +class SummaryTab extends StatefulWidget { const SummaryTab({super.key}); + @override + State createState() => _SummaryTabState(); +} + +class _SummaryTabState extends State { + final _controller = Get.find(); + final String _addressPrefix = '地点:'; + String _addressText = ''; + final String _personnelPrefix = '与会人员:'; + String _personnelText = ''; + final TextEditingController _addressController = TextEditingController(); + final TextEditingController _personnelController = TextEditingController(); + + @override + void initState() { + super.initState(); + _addressController.text = _addressPrefix + + (_controller.meetingDetails.value.address.isNotEmpty + ? _controller.meetingDetails.value.address + : '[输入地点]'); + _personnelController.text = _personnelPrefix + + (_controller.meetingDetails.value.personnel.isNotEmpty + ? _controller.meetingDetails.value.personnel + : '[输入与会人员]'); + _addressController.addListener(_handleAddressChanged); + _personnelController.addListener(_handlePersonnelChanged); + } + + void _handleAddressChanged() { + String currentText = _addressController.text; + TextSelection currentSelection = _addressController.selection; + if (!currentText.startsWith(_addressPrefix)) { + _addressController.value = TextEditingValue( + text: _addressPrefix + _addressText, + selection: TextSelection.collapsed( + offset: _addressPrefix.length + _addressText.length, + ), + ); + return; + } else { + _addressText = currentText.substring(_addressPrefix.length); + } + + if (currentSelection.start < _addressPrefix.length || + currentSelection.end < _addressPrefix.length) { + int newStart = currentSelection.start < _addressPrefix.length + ? _addressPrefix.length + : currentSelection.start; + int newEnd = currentSelection.end < _addressPrefix.length + ? _addressPrefix.length + : currentSelection.end; + _addressController.selection = currentSelection.copyWith( + baseOffset: newStart, + extentOffset: newEnd, + ); + } + } + + void _handlePersonnelChanged() { + String currentText = _personnelController.text; + TextSelection currentSelection = _personnelController.selection; + if (!currentText.startsWith(_personnelPrefix)) { + _personnelController.value = TextEditingValue( + text: _personnelPrefix + _personnelText, + selection: TextSelection.collapsed( + offset: _personnelPrefix.length + _personnelText.length, + ), + ); + return; + } else { + _personnelText = currentText.substring(_personnelPrefix.length); + } + + if (currentSelection.start < _personnelPrefix.length || + currentSelection.end < _personnelPrefix.length) { + int newStart = currentSelection.start < _personnelPrefix.length + ? _personnelPrefix.length + : currentSelection.start; + int newEnd = currentSelection.end < _personnelPrefix.length + ? _personnelPrefix.length + : currentSelection.end; + _personnelController.selection = currentSelection.copyWith( + baseOffset: newStart, + extentOffset: newEnd, + ); + } + } + + @override + void dispose() { + _addressController.dispose(); + _personnelController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final isDarkMode = Theme.of(context).brightness == Brightness.dark; + final TextStyle textStyle = TextStyle( + color: isDarkMode ? Colors.grey[400] : Colors.grey[700], + fontSize: 16.sp, + letterSpacing: 0, + height: 1.5, + ); return Stack( children: [ @@ -25,7 +127,7 @@ class SummaryTab extends GetView { Padding( padding: EdgeInsets.symmetric(vertical: 10.w), child: Text( - controller.meetingData.value.title, + _controller.meetingData.value.title, style: TextStyle( fontSize: 20.sp, fontWeight: FontWeight.bold, @@ -43,7 +145,7 @@ class SummaryTab extends GetView { borderRadius: BorderRadius.circular(4.w), ), child: Text( - controller.meetingData.value.type, + _controller.meetingData.value.type, style: TextStyle( color: isDarkMode ? Colors.grey[400] : Colors.grey[500], @@ -59,7 +161,7 @@ class SummaryTab extends GetView { 2.horizontalSpace, Text( DateUtil.formatDateMs( - controller.meetingData.value.creationtime, + _controller.meetingData.value.creationtime, format: DateFormats.y_mo_d_h_m, ), style: TextStyle( @@ -75,7 +177,7 @@ class SummaryTab extends GetView { ), 2.horizontalSpace, Text( - '${controller.meetingData.value.seconds}s', + '${_controller.meetingData.value.seconds}s', style: TextStyle( color: isDarkMode ? Colors.grey[400] : Colors.grey[500], fontSize: 12.sp, @@ -103,43 +205,79 @@ class SummaryTab extends GetView { ), ), ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '日期和时间:${DateUtil.formatDateMs( - controller.meetingData.value.creationtime, - format: DateFormats.y_mo_d_h_m, - )}', - style: TextStyle( - color: - isDarkMode ? Colors.grey[400] : Colors.grey[700], - fontSize: 16.sp, + child: Focus( + onFocusChange: (bool hasFocus) { + if (!hasFocus) { + bool isEdit = false; + if (_addressText.isNotEmpty && + _addressText != + _controller.meetingDetails.value.address) { + _controller.meetingDetails.value.address = + _addressText; + isEdit = true; + } + if (_personnelText.isNotEmpty && + _personnelText != + _controller.meetingDetails.value.personnel) { + _controller.meetingDetails.value.personnel = + _personnelText; + isEdit = true; + } + if (isEdit) { + SqfliteApi.editMeeting( + _controller.id, + { + 'address': + _controller.meetingDetails.value.address, + 'personnel': + _controller.meetingDetails.value.personnel, + }, + ); + } + } + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '日期和时间:${DateUtil.formatDateMs( + _controller.meetingData.value.creationtime, + format: DateFormats.y_mo_d_h_m, + )}', + style: textStyle, ), - ), - Text( - '地点:${controller.meetingDetails.value.address.isNotEmpty ? controller.meetingDetails.value.address : '[输入地点]'}', - style: TextStyle( - color: - isDarkMode ? Colors.grey[400] : Colors.grey[700], - fontSize: 16.sp, + TextField( + controller: _addressController, + style: textStyle, + minLines: 1, + maxLines: 100, + decoration: const InputDecoration( + isDense: true, + fillColor: Colors.transparent, + border: InputBorder.none, + contentPadding: EdgeInsets.zero, + ), ), - ), - Text( - '与会人员:${controller.meetingDetails.value.personnel.isNotEmpty ? controller.meetingDetails.value.personnel : '[输入与会人员]'}', - style: TextStyle( - color: - isDarkMode ? Colors.grey[400] : Colors.grey[700], - fontSize: 16.sp, + TextField( + controller: _personnelController, + style: textStyle, + minLines: 1, + maxLines: 100, + decoration: const InputDecoration( + isDense: true, + fillColor: Colors.transparent, + border: InputBorder.none, + contentPadding: EdgeInsets.zero, + ), ), - ), - ], + ], + ), ), ), Padding( padding: EdgeInsets.only(top: 10.w), child: MarkdownBody( - data: controller.meetingDetails.value.summary, + data: _controller.meetingDetails.value.summary, ), ), ], @@ -154,7 +292,7 @@ class SummaryTab extends GetView { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - if (controller.meetingDetails.value.summary.isNotEmpty) + if (_controller.meetingDetails.value.summary.isNotEmpty) _button( Icons.supervisor_account, '编辑发言者', @@ -174,13 +312,13 @@ class SummaryTab extends GetView { ); }, ), - if (controller.meetingDetails.value.summary.isEmpty) + if (_controller.meetingDetails.value.summary.isEmpty) SizedBox( width: 150.w, child: _button( Icons.auto_awesome, '生成', - controller.summaryCreate, + _controller.summaryCreate, ), ), ],