import 'package:common_utils/common_utils.dart'; import 'package:flutter/material.dart'; 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 'ai_bottom_sheet.dart'; import 'generate_bottom_sheet.dart'; import 'speaker_all_bottom_sheet.dart'; 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: [ SingleChildScrollView( padding: EdgeInsets.only(bottom: 80.w), child: Obx( () => Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.symmetric(vertical: 10.w), child: Text( _controller.meetingData.value.title, style: TextStyle( fontSize: 20.sp, fontWeight: FontWeight.bold, ), ), ), Row( children: [ Container( padding: EdgeInsets.symmetric(horizontal: 5.w, vertical: 2.w), margin: EdgeInsets.only(right: 10.w), decoration: BoxDecoration( color: isDarkMode ? Colors.grey[900] : Colors.grey[200], borderRadius: BorderRadius.circular(4.w), ), child: Text( _controller.meetingData.value.type, style: TextStyle( color: isDarkMode ? Colors.grey[400] : Colors.grey[500], fontSize: 10.sp, ), ), ), Icon( Icons.calendar_month, size: 14.w, color: isDarkMode ? Colors.grey[400] : Colors.grey[500], ), 2.horizontalSpace, Text( DateUtil.formatDateMs( _controller.meetingData.value.creationtime, format: DateFormats.y_mo_d_h_m, ), style: TextStyle( color: isDarkMode ? Colors.grey[400] : Colors.grey[500], fontSize: 12.sp, ), ), 10.horizontalSpace, Icon( Icons.access_time, size: 14.w, color: isDarkMode ? Colors.grey[400] : Colors.grey[500], ), 2.horizontalSpace, Text( '${_controller.meetingData.value.seconds}s', style: TextStyle( color: isDarkMode ? Colors.grey[400] : Colors.grey[500], fontSize: 12.sp, ), ), ], ), Padding( padding: EdgeInsets.symmetric(vertical: 10.w), child: Divider( height: 1, color: isDarkMode ? Colors.grey[900] : Colors.grey[300], ), ), Container( padding: EdgeInsets.only(left: 20.w), margin: EdgeInsets.symmetric(vertical: 10.w), decoration: BoxDecoration( border: Border( left: BorderSide( width: 2, color: isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[300]!, ), ), ), 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, ), TextField( controller: _addressController, style: textStyle, minLines: 1, maxLines: 100, decoration: const InputDecoration( isDense: true, fillColor: Colors.transparent, border: InputBorder.none, contentPadding: EdgeInsets.zero, ), ), 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, ), ), ], ), ), ), Obx( () => Positioned( bottom: 20.w, left: 0, right: 0, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ if (_controller.meetingDetails.value.tasktype >= 3) _button( Icons.supervisor_account, '编辑发言者', () { Get.bottomSheet( const SpeakerAllBottomSheet(), isScrollControlled: true, backgroundColor: Colors.grey[100], shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(12.r), ), ), ); }, ), if (_controller.meetingDetails.value.tasktype >= 3) _button( Icons.auto_awesome, 'Ask AI', () { Get.bottomSheet( const AIBottomSheet(), isScrollControlled: true, backgroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(12.r), ), ), ); }, ), if (_controller.meetingDetails.value.tasktype == 0) SizedBox( width: 150.w, child: _button( Icons.auto_awesome, '生成', () { Get.bottomSheet( const GenerateBottomSheet(), isScrollControlled: true, backgroundColor: Colors.grey[100], shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(12.r), ), ), ); }, ), ), ], ), ), ), ], ); } Widget _button(IconData icon, String title, Function() onTap) { return GestureDetector( onTap: onTap, child: Container( height: 42.w, padding: EdgeInsets.symmetric(horizontal: 15.w), margin: EdgeInsets.symmetric(horizontal: 4.w), decoration: BoxDecoration( color: Colors.black, borderRadius: BorderRadius.circular(42.w), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( icon, color: Colors.white, size: 20.sp, ), 5.horizontalSpace, Text( title, style: TextStyle( color: Colors.white, fontSize: 14.sp, ), ), ], ), ), ); } }