From a2047d064bb4f458f1ae6e97498af9de244ed229 Mon Sep 17 00:00:00 2001 From: Jolie <412895109@qq.com> Date: Thu, 8 Jan 2026 19:58:25 +0800 Subject: [PATCH] =?UTF-8?q?feat(call):=20=E6=9B=B4=E6=96=B0=E9=80=9A?= =?UTF-8?q?=E8=AF=9D=E7=8A=B6=E6=80=81=E5=B9=B6=E4=BC=98=E5=8C=96=E8=A1=A8?= =?UTF-8?q?=E6=83=85=E5=92=8C=E5=BD=95=E9=9F=B3=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在通话控制器中添加 calling 状态更新逻辑 - 调整表情配置将笑脸表情排列到前面 - 在语音输入组件中添加 60 秒最大录音时长限制 - 优化通话页面余额显示逻辑基于通话时长判断 - 修复语音输入时间格式化显示问题 --- lib/config/emoji_config.dart | 18 +++++++++-------- lib/controller/message/call_controller.dart | 11 +++++++++++ lib/pages/message/video_call_page.dart | 11 +++++++---- lib/widget/message/voice_input_view.dart | 22 +++++++++++++++++---- 4 files changed, 46 insertions(+), 16 deletions(-) diff --git a/lib/config/emoji_config.dart b/lib/config/emoji_config.dart index 7de95ff..76e113d 100644 --- a/lib/config/emoji_config.dart +++ b/lib/config/emoji_config.dart @@ -28,27 +28,31 @@ class EmojiItem { class EmojiConfig { static const String basePath = 'assets/images/emoji/'; - /// 所有表情列表 + /// 所有表情列表(笑脸表情已排到前面) static const List allEmojis = [ + // 笑脸表情放在最前面 + EmojiItem(id: '19', name: '开心', path: '${basePath}emoji_19.png'), + EmojiItem(id: '06', name: '吐舌笑', path: '${basePath}emoji_06.png'), + EmojiItem(id: '11', name: '墨镜笑', path: '${basePath}emoji_11.png'), + EmojiItem(id: '14', name: '奸笑', path: '${basePath}emoji_14.png'), + EmojiItem(id: '17', name: '尴尬笑', path: '${basePath}emoji_17.png'), + EmojiItem(id: '47', name: '猫咪笑', path: '${basePath}emoji_47.png'), + EmojiItem(id: '54', name: '笑哭', path: '${basePath}emoji_54.png'), + // 其他表情 EmojiItem(id: '01', name: '不爽', path: '${basePath}emoji_01.png'), EmojiItem(id: '02', name: '不自在', path: '${basePath}emoji_02.png'), EmojiItem(id: '03', name: '兴奋', path: '${basePath}emoji_03.png'), EmojiItem(id: '04', name: '叹气', path: '${basePath}emoji_04.png'), EmojiItem(id: '05', name: '吃惊', path: '${basePath}emoji_05.png'), - EmojiItem(id: '06', name: '吐舌笑', path: '${basePath}emoji_06.png'), EmojiItem(id: '07', name: '吐舌头', path: '${basePath}emoji_07.png'), EmojiItem(id: '08', name: '哭泣', path: '${basePath}emoji_08.png'), EmojiItem(id: '09', name: '困了', path: '${basePath}emoji_09.png'), EmojiItem(id: '10', name: '困惑', path: '${basePath}emoji_10.png'), - EmojiItem(id: '11', name: '墨镜笑', path: '${basePath}emoji_11.png'), EmojiItem(id: '12', name: '大哭', path: '${basePath}emoji_12.png'), EmojiItem(id: '13', name: '失望', path: '${basePath}emoji_13.png'), - EmojiItem(id: '14', name: '奸笑', path: '${basePath}emoji_14.png'), EmojiItem(id: '15', name: '害羞', path: '${basePath}emoji_15.png'), EmojiItem(id: '16', name: '尴尬', path: '${basePath}emoji_16.png'), - EmojiItem(id: '17', name: '尴尬笑', path: '${basePath}emoji_17.png'), EmojiItem(id: '18', name: '尴尬露齿', path: '${basePath}emoji_18.png'), - EmojiItem(id: '19', name: '开心', path: '${basePath}emoji_19.png'), EmojiItem(id: '20', name: '恶心', path: '${basePath}emoji_20.png'), EmojiItem(id: '21', name: '惊恐', path: '${basePath}emoji_21.png'), EmojiItem(id: '22', name: '惊讶', path: '${basePath}emoji_22.png'), @@ -76,14 +80,12 @@ class EmojiConfig { EmojiItem(id: '44', name: '火冒三丈', path: '${basePath}emoji_44.png'), EmojiItem(id: '45', name: '焦虑', path: '${basePath}emoji_45.png'), EmojiItem(id: '46', name: '狡猾', path: '${basePath}emoji_46.png'), - EmojiItem(id: '47', name: '猫咪笑', path: '${basePath}emoji_47.png'), EmojiItem(id: '48', name: '生气', path: '${basePath}emoji_48.png'), EmojiItem(id: '49', name: '生气皱眉', path: '${basePath}emoji_49.png'), EmojiItem(id: '50', name: '疑问', path: '${basePath}emoji_50.png'), EmojiItem(id: '51', name: '眨眼害羞', path: '${basePath}emoji_51.png'), EmojiItem(id: '52', name: '眨眼飞吻', path: '${basePath}emoji_52.png'), EmojiItem(id: '53', name: '禁言', path: '${basePath}emoji_53.png'), - EmojiItem(id: '54', name: '笑哭', path: '${basePath}emoji_54.png'), EmojiItem(id: '55', name: '翻白眼', path: '${basePath}emoji_55.png'), EmojiItem(id: '56', name: '翻白眼叹气', path: '${basePath}emoji_56.png'), EmojiItem(id: '57', name: '翻白眼无奈', path: '${basePath}emoji_57.png'), diff --git a/lib/controller/message/call_controller.dart b/lib/controller/message/call_controller.dart index d2a649c..cb7f7f8 100644 --- a/lib/controller/message/call_controller.dart +++ b/lib/controller/message/call_controller.dart @@ -1005,6 +1005,17 @@ class CallController extends GetxController { _callChannelId = channelId; } + // 更新通话会话状态为 calling(通话中) + // 由于 CallSession 的 status 是 final,需要创建新对象 + final updatedSession = CallSession( + targetUserId: callSession.targetUserId, + callType: callSession.callType, + status: CallStatus.calling, // 更新状态为通话中 + isInitiator: callSession.isInitiator, + startTime: callSession.startTime, + ); + currentCall.value = updatedSession; + // 立即调用一次消费接口,然后启动定时器每隔1分钟调用一次 // 如果定时器还没有启动,则启动它(避免重复启动) if (_consumeTimer == null) { diff --git a/lib/pages/message/video_call_page.dart b/lib/pages/message/video_call_page.dart index 3e9edc0..541f614 100644 --- a/lib/pages/message/video_call_page.dart +++ b/lib/pages/message/video_call_page.dart @@ -576,19 +576,22 @@ class _VideoCallPageState extends State { Obx(() { final callSession = _callController.currentCall.value; final consumeData = _callController.consumeResponse.value; + final callDuration = _callController.callDurationSeconds.value; - // 呼叫中(waitCalling)或免费时不显示 - final isCalling = callSession?.status == CallStatus.waitCalling; + // 判断是否已接通:通过通话时长来判断,如果时长大于0说明已接通 + final isCallConnected = callSession != null && callDuration > 0; + // 呼叫中(未接通)或免费时不显示 final isFree = consumeData?.isFree == true; - if (isCalling || isFree) { + // 只有在已接通且不是免费通话时才显示余额 + if (!isCallConnected || isFree) { return const SizedBox.shrink(); } final availableBalance = consumeData?.availableBalance ?? 0; final unitSellingBalance = consumeData?.unitSellingBalance ?? 0; if (availableBalance <= 0 && unitSellingBalance <= 0) { - return SizedBox(); + return const SizedBox.shrink(); } return Center( child: Text( diff --git a/lib/widget/message/voice_input_view.dart b/lib/widget/message/voice_input_view.dart index f7931a6..79d7496 100644 --- a/lib/widget/message/voice_input_view.dart +++ b/lib/widget/message/voice_input_view.dart @@ -27,6 +27,9 @@ class _VoiceInputViewState extends State { bool _isCanceling = false; Offset _panStartPosition = Offset.zero; String? _recordingPath; + + // 最大录音时长(秒) + static const int maxRecordingDuration = 60; @override void dispose() { @@ -48,8 +51,10 @@ class _VoiceInputViewState extends State { // 格式化时间显示 String _formatTime(int seconds) { - final minutes = seconds ~/ 60; - final secs = seconds % 60; + // 限制最大显示为60秒 + final displaySeconds = seconds.clamp(0, maxRecordingDuration); + final minutes = displaySeconds ~/ 60; + final secs = displaySeconds % 60; return '$minutes:${secs.toString().padLeft(2, '0')}'; } @@ -87,6 +92,15 @@ class _VoiceInputViewState extends State { timer.cancel(); return; } + + // 检查是否达到最大录音时长 + if (_seconds >= maxRecordingDuration) { + // 达到60秒,自动停止录音并发送 + timer.cancel(); + _stopRecording(cancel: false); + return; + } + setState(() { _seconds++; }); @@ -104,8 +118,8 @@ class _VoiceInputViewState extends State { if (_isRecording) { try { - // 在重置状态前保存录音信息 - final finalSeconds = _seconds; + // 在重置状态前保存录音信息,限制最大时长为60秒 + final finalSeconds = _seconds.clamp(0, maxRecordingDuration); final finalPath = _recordingPath; await _audioRecorder.stop();