Browse Source

修改会议记录

newdev_shunjiawei
tanlongsheng 2 years ago
parent
commit
7b36c171dd
  1. 2
      lib/data/services/db/database_helper.dart
  2. 3
      lib/modules/meeting/controllers/meeting_details_controller.dart
  3. 4
      lib/modules/meeting/controllers/meeting_edit_controller.dart
  4. 38
      lib/modules/meeting/controllers/meeting_record_controller.dart
  5. 4
      lib/modules/meeting/model/meeting_details_model.dart
  6. 246
      lib/modules/meeting/views/meeting_record_view.dart
  7. 6
      lib/modules/meeting/views/meeting_view.dart

2
lib/data/services/db/database_helper.dart

@ -40,7 +40,7 @@ class DatabaseHelper {
CREATE TABLE IF NOT EXISTS meetingdetails (
id INTEGER PRIMARY KEY,
meetingid INTEGER,
audio BLOB,
audio TEXT,
content TEXT
)
''');

3
lib/modules/meeting/controllers/meeting_details_controller.dart

@ -3,7 +3,6 @@ import 'package:get/get.dart';
import 'package:just_audio/just_audio.dart';
import '../../../data/services/db/sqflite_api.dart';
import '../../../data/services/speech_impl/volcano_tts_api_service.dart';
import '../model/meeting_details_model.dart';
import '../model/meeting_model.dart';
@ -42,7 +41,7 @@ class MeetingDetailsController extends GetxController
final data = await SqfliteApi.getMeetingDetails(id);
meetingData.value = MeetingModel.fromJson(data);
dataDetails.value = MeetingDetailsModel.fromJson(data['details']);
audioPlayer.setAudioSource(BytesAudioSource(dataDetails.value.audio));
audioPlayer.setAudioSource(AudioSource.file(dataDetails.value.audio));
audioPlayer.playerStateStream.listen((state) async {
if (state.processingState == ProcessingState.completed) {
await audioPlayer.seek(Duration.zero);

4
lib/modules/meeting/controllers/meeting_edit_controller.dart

@ -3,7 +3,6 @@ import 'package:get/get.dart';
import 'package:just_audio/just_audio.dart';
import '../../../data/services/db/sqflite_api.dart';
import '../../../data/services/speech_impl/volcano_tts_api_service.dart';
import 'meeting_controller.dart';
import 'meeting_details_controller.dart';
@ -53,7 +52,7 @@ class MeetingEditController extends GetxController
);
creationTime = meetingDetailsController.meetingData.value.creationtime;
audioPlayer.setAudioSource(
BytesAudioSource(meetingDetailsController.dataDetails.value.audio));
AudioSource.file(meetingDetailsController.dataDetails.value.audio));
audioPlayer.playerStateStream.listen((state) async {
if (state.processingState == ProcessingState.completed) {
await audioPlayer.seek(Duration.zero);
@ -71,7 +70,6 @@ class MeetingEditController extends GetxController
isPlay.value = false;
} else {
lottieController.repeat();
await audioPlayer.seek(Duration.zero);
audioPlayer.play();
isPlay.value = true;
}

38
lib/modules/meeting/controllers/meeting_record_controller.dart

@ -1,9 +1,13 @@
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import '../../../core/utils/logger.dart';
import '../../../data/services/asr_service.dart';
import '../../../data/services/db/sqflite_api.dart';
import '../../../data/services/speech_impl/xunfei_asr_service.dart';
@ -34,6 +38,7 @@ class MeetingRecordController extends GetxController
@override
void onInit() {
super.onInit();
Permission.storage.request();
lottieController = AnimationController(vsync: this);
Get.put(XunfeiAsrService());
_asrService = Get.find<XunfeiAsrService>();
@ -144,6 +149,20 @@ class MeetingRecordController extends GetxController
// 完成
void complete() async {
if (await Permission.storage.request().isGranted) {
_save();
} else {
Get.snackbar(
'error'.tr, // 错误
'请开启文件存储权限',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red.withOpacity(0.1),
colorText: Colors.red,
);
}
}
void _save() async {
String firstContent = finalContent.value.length >= 30
? finalContent.value.substring(0, 30)
: finalContent.value;
@ -155,12 +174,15 @@ class MeetingRecordController extends GetxController
dataLength: audio.length,
);
Uint8List wavData = Uint8List.fromList([...wavHeader, ...audio]);
String audiopPath =
await _saveAudioToFile(wavData, 'meeting_$creationTime.wav');
if (audiopPath.isEmpty) return;
Map<String, Object?> data = {
'title': title,
'firstContent': firstContent.replaceAll("\n", ""),
'seconds': seconds.value,
'creationTime': creationTime,
'audio': wavData,
'audio': audiopPath,
'content': finalContent.value,
};
final result = await SqfliteApi.insertMeeting(data);
@ -176,6 +198,20 @@ class MeetingRecordController extends GetxController
}
}
Future<String> _saveAudioToFile(Uint8List audioData, String fileName) async {
try {
final directory = await getApplicationDocumentsDirectory();
final file = File('${directory.path}/$fileName');
// 写入文件
await file.writeAsBytes(audioData);
return file.path;
} catch (e) {
Logger.error('保存失败: $e');
return '';
}
}
Uint8List generateWavHeader({
required int sampleRate,
required int numberOfChannels,

4
lib/modules/meeting/model/meeting_details_model.dart

@ -1,11 +1,9 @@
import 'dart:typed_data';
class MeetingDetailsModel {
MeetingDetailsModel();
late int id;
late int meetingid;
late Uint8List audio;
late String audio;
late String content;
factory MeetingDetailsModel.fromJson(Map<String, dynamic> map) =>

246
lib/modules/meeting/views/meeting_record_view.dart

@ -90,86 +90,36 @@ class MeetingRecordView extends GetView<MeetingRecordController> {
// 构建主体内容
Widget _buildBody(bool isDarkMode) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 10.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
10.verticalSpace,
Padding(
padding: EdgeInsets.symmetric(horizontal: 12.w),
child: Text(
controller.title,
style: TextStyle(
color: isDarkMode ? Colors.white : Colors.black87,
fontSize: 17.sp,
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 5.w),
child: Text(
DateUtil.formatDateMs(
controller.creationTime,
format: DateFormats.y_mo_d_h_m,
),
style: TextStyle(
color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
fontSize: 14.sp,
),
),
),
_audioWave(isDarkMode),
_recordContent(isDarkMode),
_bottomButton(isDarkMode),
],
),
);
}
// 音波
Widget _audioWave(bool isDarkMode) {
return Container(
width: double.infinity,
height: 70.w,
margin: EdgeInsets.symmetric(vertical: 5.w),
decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white,
borderRadius: BorderRadius.circular(12.w),
boxShadow: [
BoxShadow(
color: isDarkMode
? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
child: Row(
children: [
Expanded(
child: Lottie.asset(
'assets/lottie/audio_wave.json',
controller: controller.lottieController,
fit: BoxFit.fill,
onLoaded: (composition) {
controller.lottieController.duration = composition.duration;
},
),
),
Expanded(
child: Lottie.asset(
'assets/lottie/audio_wave.json',
controller: controller.lottieController,
fit: BoxFit.fill,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 5.w),
child: Text(
DateUtil.formatDateMs(
controller.creationTime,
format: DateFormats.y_mo_d_h_m,
),
),
Expanded(
child: Lottie.asset(
'assets/lottie/audio_wave.json',
controller: controller.lottieController,
fit: BoxFit.fill,
style: TextStyle(
color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
fontSize: 14.sp,
),
),
],
),
),
_recordContent(isDarkMode),
_bottomButton(isDarkMode),
],
);
}
@ -179,7 +129,7 @@ class MeetingRecordView extends GetView<MeetingRecordController> {
child: Container(
width: double.infinity,
padding: EdgeInsets.all(10.w),
margin: EdgeInsets.symmetric(vertical: 5.w),
margin: EdgeInsets.symmetric(horizontal: 12.w, vertical: 5.w),
decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white,
borderRadius: BorderRadius.circular(12.w),
@ -210,51 +160,121 @@ class MeetingRecordView extends GetView<MeetingRecordController> {
// 底部按钮
Widget _bottomButton(bool isDarkMode) {
return Padding(
padding: EdgeInsets.only(top: 40.w, bottom: 30.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: controller.recorderHandle,
child: Container(
width: 130.w,
height: 60.w,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(60.w),
border: Border.all(
color: isDarkMode ? AppColors.primary : Colors.blue,
width: 2.w,
return Container(
margin: EdgeInsets.only(top: 10.h),
decoration: BoxDecoration(
color: isDarkMode ? AppColors.cardBackground : Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 3,
offset: const Offset(0, -1),
),
],
),
child: SafeArea(
child: SizedBox(
height: 80.h,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 12.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// 语音录制按钮
Container(
width: 60.r,
height: 60.r,
decoration: BoxDecoration(
color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.white,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
spreadRadius: 1,
),
],
),
child: Center(
child: Obx(
() => IconButton(
icon: Icon(
controller.isRecording.value
? Icons.mic
: Icons.mic_none,
),
color: controller.isRecording.value
? AppColors.primary
: Colors.grey.shade400,
iconSize: 28.r,
padding: EdgeInsets.zero,
onPressed: controller.recorderHandle,
),
),
),
),
),
child: Obx(
() => Icon(
controller.isRecording.value ? Icons.pause : Icons.play_arrow,
color: isDarkMode ? AppColors.primary : Colors.blue,
size: 40.w,
10.horizontalSpace,
Expanded(
child: Lottie.asset(
'assets/lottie/audio_wave.json',
controller: controller.lottieController,
fit: BoxFit.fill,
onLoaded: (composition) {
controller.lottieController.duration =
composition.duration;
},
),
),
),
),
),
GestureDetector(
onTap: () {
_showAlertDialog(isDarkMode);
},
child: Container(
width: 130.w,
height: 60.w,
decoration: BoxDecoration(
color: isDarkMode ? AppColors.primary : Colors.blue,
borderRadius: BorderRadius.circular(60.w),
),
child: Icon(
Icons.fiber_manual_record_outlined,
color: Colors.white,
size: 40.w,
),
Expanded(
child: Lottie.asset(
'assets/lottie/audio_wave.json',
controller: controller.lottieController,
fit: BoxFit.fill,
onLoaded: (composition) {
controller.lottieController.duration =
composition.duration;
},
),
),
10.horizontalSpace,
// 关闭按钮
Container(
width: 60.r,
height: 60.r,
decoration: BoxDecoration(
color: isDarkMode
? Colors.white.withOpacity(0.1)
: Colors.white,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
spreadRadius: 1,
),
],
),
child: Center(
child: IconButton(
icon: Icon(
Icons.close,
color: isDarkMode ? Colors.white : Colors.grey.shade600,
size: 28.r,
),
padding: EdgeInsets.zero,
onPressed: () {
_showAlertDialog(isDarkMode);
},
),
),
),
],
),
),
],
),
),
);
}

6
lib/modules/meeting/views/meeting_view.dart

@ -82,7 +82,7 @@ class MeetingView extends GetView<MeetingController> {
const Icon(Icons.keyboard_voice, color: Colors.white),
5.horizontalSpace,
Text(
'录音',
'开始会议',
style: TextStyle(color: Colors.white, fontSize: 16.sp),
),
],
@ -264,7 +264,7 @@ class MeetingView extends GetView<MeetingController> {
mainAxisSize: MainAxisSize.min,
children: [
Text(
id != null ? '修改录音标题(非必填)' : '录音标题(非必填)',
id != null ? '修改会议标题(非必填)' : '会议标题(非必填)',
style: TextStyle(
color: isDarkMode ? Colors.white : Colors.black87,
fontSize: 16.sp,
@ -347,7 +347,7 @@ class MeetingView extends GetView<MeetingController> {
} else {
if (title.isEmpty) {
title =
'${DateUtil.formatDateMs(time, format: DateFormats.y_mo_d)} 录音';
'${DateUtil.formatDateMs(time, format: DateFormats.y_mo_d)} 会议';
}
Get.back();
Get.toNamed(Routes.meetingRecord, arguments: {

Loading…
Cancel
Save