|
@@ -36,7 +36,7 @@
|
|
|
<!-- 输入框 -->
|
|
|
<div class="page-foot">
|
|
|
<div class="flex-box">
|
|
|
- <svg-icon class="page-icon" name="voice" @mousedown="startAudio" />
|
|
|
+ <svg-icon class="page-icon" name="voice" @mousedown="startAudio" @mouseup="sendAudioMessage" />
|
|
|
<van-field
|
|
|
rows="1"
|
|
|
type="textarea"
|
|
@@ -101,7 +101,8 @@ const isMobile = Capacitor.getPlatform() !== "web";
|
|
|
|
|
|
// 底部高度动态计算
|
|
|
// 语音
|
|
|
-const mediaRecorder = ref(null);
|
|
|
+const mediaRecorder = ref(null); // 录音对象
|
|
|
+const audioChunks = ref([]) // 录音数据
|
|
|
|
|
|
// 计算当前底部总高度
|
|
|
const currentBottomHeight = computed(() => {
|
|
@@ -110,58 +111,112 @@ const currentBottomHeight = computed(() => {
|
|
|
return 0;
|
|
|
});
|
|
|
|
|
|
-// 输入框聚焦
|
|
|
+// 切换表情/工具面板
|
|
|
+const toggleAppBox = async (type) => {
|
|
|
+ if (isMobile) await Keyboard.hide();
|
|
|
+ keyboardHeight.value = 0;
|
|
|
+ if (type === 1) {
|
|
|
+ showEmoji.value = !showEmoji.value;
|
|
|
+ showTools.value = false;
|
|
|
+ } else {
|
|
|
+ showTools.value = !showTools.value;
|
|
|
+ showEmoji.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
const onFocus = () => {
|
|
|
+ // 隐藏所有面板
|
|
|
showEmoji.value = false;
|
|
|
showTools.value = false;
|
|
|
- if (isMobile) setupKeyboardListeners();
|
|
|
+ // 判断是不是web
|
|
|
+ if (Capacitor.getPlatform() != "web") {
|
|
|
+ setupKeyboardListeners();
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
-// 监听键盘高度
|
|
|
-const setupKeyboardListeners = () => {
|
|
|
+// 键盘监听
|
|
|
+const setupKeyboardListeners = async () => {
|
|
|
Keyboard.addListener("keyboardWillShow", (info) => {
|
|
|
keyboardHeight.value = info.keyboardHeight;
|
|
|
});
|
|
|
+
|
|
|
Keyboard.addListener("keyboardWillHide", () => {
|
|
|
keyboardHeight.value = 0;
|
|
|
});
|
|
|
};
|
|
|
|
|
|
-// 切换表情/工具面板
|
|
|
-const toggleAppBox = async (type) => {
|
|
|
- if (isMobile) await Keyboard.hide();
|
|
|
- keyboardHeight.value = 0;
|
|
|
- if (type === 1) {
|
|
|
- showEmoji.value = !showEmoji.value;
|
|
|
- showTools.value = false;
|
|
|
- } else {
|
|
|
- showTools.value = !showTools.value;
|
|
|
- showEmoji.value = false;
|
|
|
- }
|
|
|
-};
|
|
|
+// 录音
|
|
|
const startAudio = async () => {
|
|
|
try {
|
|
|
// 请求麦克风权限
|
|
|
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
|
-
|
|
|
+
|
|
|
// 创建 MediaRecorder 实例
|
|
|
- mediaRecorder.value = new MediaRecorder(stream);
|
|
|
+ mediaRecorder.value = new MediaRecorder(stream, { mimeType: 'audio/webm; codecs=opus' });
|
|
|
|
|
|
// 收集音频数据
|
|
|
mediaRecorder.value.ondataavailable = (e) => {
|
|
|
- audioChunks.push(e.data);
|
|
|
+ audioChunks.value.push(e.data);
|
|
|
};
|
|
|
|
|
|
- mediaRecorder.value.onerror = (e) => {
|
|
|
- console.error('Recording error:', e.error);
|
|
|
- };
|
|
|
-
|
|
|
- mediaRecorder.value.start();
|
|
|
+ mediaRecorder.value.start(1000); // 每1秒收集一次数据
|
|
|
console.log('Recording started');
|
|
|
} catch (error) {
|
|
|
console.error('Error accessing microphone:', error);
|
|
|
}
|
|
|
}
|
|
|
+// 停止录音
|
|
|
+const stopRecording = async () => {
|
|
|
+ return new Promise<Uint8Array>(async (resolve) => {
|
|
|
+ if (!mediaRecorder.value) {
|
|
|
+ resolve(new Uint8Array());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 停止录音
|
|
|
+ mediaRecorder.value.stop();
|
|
|
+ mediaRecorder.value.stream.getTracks().forEach(track => track.stop());
|
|
|
+
|
|
|
+ // 等待最后的数据可用
|
|
|
+ mediaRecorder.value.onstop = async () => {
|
|
|
+ // 合并所有音频片段
|
|
|
+ const audioBlob = new Blob(audioChunks.value, { type: 'audio/webm' });
|
|
|
+
|
|
|
+ // 转换为 Uint8Array
|
|
|
+ const arrayBuffer = await audioBlob.arrayBuffer();
|
|
|
+ const audioData = new Uint8Array(arrayBuffer);
|
|
|
+
|
|
|
+ resolve(audioData);
|
|
|
+ };
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+const sendAudioMessage = async () => {
|
|
|
+ try {
|
|
|
+ // 1. 停止录音并获取音频数据
|
|
|
+ const audioData = await stopRecording();
|
|
|
+
|
|
|
+ // 2. 准备消息体
|
|
|
+ const message = {
|
|
|
+ content: text.value, // 如果有文本内容
|
|
|
+ contentType: MSG_TYPE.AUDIO, // 音频消息类型
|
|
|
+ messageType: MESSAGE_TYPE_USER, // 单聊消息
|
|
|
+ to: route.query.uuid, // 接收方ID
|
|
|
+ fileSuffix: "webm", // 使用webm后缀更准确
|
|
|
+ file: Array.from(audioData) // 将Uint8Array转为普通数组
|
|
|
+ };
|
|
|
+
|
|
|
+ // 3. 通过WebSocket发送
|
|
|
+ wsStore.sendMessage(message);
|
|
|
+
|
|
|
+ // 4. 重置状态
|
|
|
+ mediaRecorder.value = null;
|
|
|
+ audioChunks.value = [];
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Error sending audio message:', error);
|
|
|
+ }
|
|
|
+}
|
|
|
// 发送消息
|
|
|
const sendMessage = () => {
|
|
|
if (!text.value.trim()) return;
|