diff --git a/lib/modules/chat/bindings/chat_binding.dart b/lib/modules/chat/bindings/chat_binding.dart index 5618dccaa..6b6ae6683 100644 --- a/lib/modules/chat/bindings/chat_binding.dart +++ b/lib/modules/chat/bindings/chat_binding.dart @@ -1,17 +1,10 @@ import 'package:get/get.dart'; import '../controllers/chat_controller.dart'; -import '../../../data/services/speech_impl/azure_asr_service.dart'; -import '../../../data/services/speech_impl/azure_tts_service.dart'; -import '../../../data/services/asr_service.dart'; -import '../../../data/services/tts_service.dart'; -import '../../../data/services/volcano_ai_service.dart'; -import '../../../data/services/chat_history_service.dart'; class ChatBinding extends Bindings { @override void dependencies() { - // 注册控制器 Get.lazyPut(() => ChatController()); } -} \ No newline at end of file +} diff --git a/lib/modules/chat/controllers/chat_controller.dart b/lib/modules/chat/controllers/chat_controller.dart index 4b62a1a9f..e92c85538 100644 --- a/lib/modules/chat/controllers/chat_controller.dart +++ b/lib/modules/chat/controllers/chat_controller.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:get/get.dart'; import 'package:flutter/widgets.dart'; import 'package:get_storage/get_storage.dart'; +import 'package:just_audio/just_audio.dart'; import '../../../data/services/ai_service.dart'; import '../../../data/services/deapsound_ai_service.dart'; import '../../../data/services/kouzi_ai_service.dart'; @@ -72,6 +73,10 @@ class ChatController extends GetxController { // 记录最后一次活动时间 DateTime _lastActivityTime = DateTime.now(); + final _player = AudioPlayer(); + final RxString audiourl = ''.obs; + final RxBool isPlay = false.obs; + @override Future onInit() async { super.onInit(); @@ -248,6 +253,8 @@ class ChatController extends GetxController { _historyService.saveHistory(agent.id!, messages.toList()); } + _player.dispose(); + super.onClose(); } @@ -345,6 +352,9 @@ class ChatController extends GetxController { if (chunk is Map) { _updateLoadingMessage(data: chunk); + if (chunk['musicaudio'] != null) { + play(chunk['musicaudio']); + } } else { fullResponse += chunk; @@ -427,6 +437,31 @@ class ChatController extends GetxController { } } +// 播放音乐 + void play(Map musicaudio) async { + try { + isPlay.value = false; + if (audiourl.value != musicaudio['audiourl']) { + audiourl.value = musicaudio['audiourl']; + await _player.setUrl(musicaudio['audiourl']); + _player.play(); + } else { + _player.play(); + } + isPlay.value = true; + } catch (e) { + isPlay.value = false; + audiourl.value = ''; + Logger.error('加载异常: $e'); + } + } + + // 暂停播放音乐 + void pause() async { + await _player.pause(); + isPlay.value = false; + } + // 完成响应 void _finalizeResponse(String content) { final index = messages.indexWhere((m) => m.isLoading); diff --git a/lib/modules/chat/views/chat_music_card.dart b/lib/modules/chat/views/chat_music_card.dart new file mode 100644 index 000000000..0ae512a42 --- /dev/null +++ b/lib/modules/chat/views/chat_music_card.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; + +import '../controllers/chat_controller.dart'; + +class ChatMusicCard extends GetView { + final Map? data; + final bool isDarkMode; + + const ChatMusicCard(this.data, this.isDarkMode, {super.key}); + + @override + Widget build(BuildContext context) { + final Map musiccards = data?['musiccards'].first; + final Map musicaudio = data?['musicaudio']; + + return Row( + children: [ + Container( + width: 60.w, + height: 60.w, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6.r), + ), + child: Image.network( + musiccards['image'], + fit: BoxFit.cover, + ), + ), + 10.horizontalSpace, + Expanded( + child: Text( + musiccards['name'], + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: isDarkMode ? Colors.white : Colors.black, + fontSize: 16.sp, + height: 1.5, + ), + ), + ), + 10.horizontalSpace, + Obx( + () => controller.audiourl == musicaudio['audiourl'] && + controller.isPlay.value + ? GestureDetector( + onTap: () { + controller.pause(); + }, + child: Container( + width: 38.w, + height: 38.w, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: + isDarkMode ? Colors.grey[700]! : Colors.grey[300]!, + width: 2, + ), + ), + child: Icon( + Icons.pause_rounded, + color: isDarkMode ? Colors.white : Colors.black, + ), + ), + ) + : GestureDetector( + onTap: () { + controller.play(musicaudio); + }, + child: Container( + width: 38.w, + height: 38.w, + decoration: BoxDecoration( + color: isDarkMode ? Colors.white : Colors.black, + shape: BoxShape.circle, + ), + child: Icon( + Icons.play_arrow_rounded, + color: isDarkMode ? Colors.black : Colors.white, + ), + ), + ), + ), + ], + ); + } +} diff --git a/lib/modules/chat/views/chat_view.dart b/lib/modules/chat/views/chat_view.dart index 4fbc6c261..2c6adee7b 100644 --- a/lib/modules/chat/views/chat_view.dart +++ b/lib/modules/chat/views/chat_view.dart @@ -5,6 +5,7 @@ import 'dart:math'; import '../controllers/chat_controller.dart'; import '../models/message_model.dart'; import '../../../core/theme/app_colors.dart'; +import 'chat_music_card.dart'; class ChatView extends GetView { const ChatView({Key? key}) : super(key: key); @@ -297,19 +298,7 @@ class ChatView extends GetView { crossAxisAlignment: CrossAxisAlignment.start, children: [ // 消息内容 - if (message.content.isNotEmpty) - SelectableText( - '${message.content}'.tr, - style: TextStyle( - color: isUser - ? Colors.white - : isDarkMode - ? Colors.white - : Colors.black, - fontSize: 16.sp, - height: 1.5, - ), - ), + _buildMessageContent(message, isDarkMode, isUser), // 加载中动画 if (message.isLoading) @@ -348,6 +337,30 @@ class ChatView extends GetView { ); } + Widget _buildMessageContent(Message message, bool isDarkMode, bool isUser) { + Widget box = const SizedBox(); + if (message.content.isNotEmpty) { + box = SelectableText( + message.content.tr, + style: TextStyle( + color: isUser + ? Colors.white + : isDarkMode + ? Colors.white + : Colors.black, + fontSize: 16.sp, + height: 1.5, + ), + ); + } + if (message.data != null) { + if (message.data?['musicaudio'] != null) { + box = ChatMusicCard(message.data, isDarkMode); + } + } + return box; + } + // 构建输入区域 Widget _buildInputArea(bool isDarkMode) { final textController = @@ -715,21 +728,22 @@ class ChatView extends GetView { ); } - // 格式化时间 + // 格式化时间 String _formatTime(DateTime time) { final now = DateTime.now(); final today = DateTime(now.year, now.month, now.day); //final yesterday = today.subtract(const Duration(days: 1)); final messageDate = DateTime(time.year, time.month, time.day); - String timeStr = '${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}'; + String timeStr = + '${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}'; if (messageDate == today) { - return timeStr; // 只显示时间,如 "14:30" - } + return timeStr; // 只显示时间,如 "14:30" + } // else if (messageDate == yesterday) { // return 'Yesterday $timeStr'; // 如 "Yesterday 14:30" - // } + // } else { // 国际通用日期格式 "MM-dd HH:mm" return '${time.month.toString().padLeft(2, '0')}-${time.day.toString().padLeft(2, '0')} $timeStr';