|
@@ -1,8 +1,7 @@
|
|
|
<script setup>
|
|
|
-// const plugin = requirePlugin("WechatSI")
|
|
|
-
|
|
|
import { ref, unref, onMounted } from 'vue';
|
|
|
-import { baseURL } from '@/utils/https';
|
|
|
+
|
|
|
+const plugin = requirePlugin("WechatSI")
|
|
|
|
|
|
const modelInpValue = defineModel();
|
|
|
const emit = defineEmits(['on-submit']);
|
|
@@ -11,17 +10,10 @@ const modelLoading = defineModel('loading');
|
|
|
const isRecording = ref(false);
|
|
|
const recordingTip = ref('松开 发送');
|
|
|
const showVolume = ref(false);
|
|
|
-const volumeLevel = ref(50); // 模拟音量级别
|
|
|
-let recorderManager = null;
|
|
|
-const audioPath = ref(null);
|
|
|
+const isLoad = ref(false);
|
|
|
|
|
|
// 实时语音识别
|
|
|
-// const manager = plugin.getRecordRecognitionManager();
|
|
|
-
|
|
|
-
|
|
|
-console.log( "manager", manager );
|
|
|
-
|
|
|
-const innerAudioContext = wx.createInnerAudioContext();
|
|
|
+const manager = plugin.getRecordRecognitionManager();
|
|
|
|
|
|
// 类型 input|voice
|
|
|
const inpType = ref('input');
|
|
@@ -51,147 +43,84 @@ const onChangeInpType = () => {
|
|
|
inpType.value = inpType.value === 'input' ? 'voice' : 'input';
|
|
|
}
|
|
|
|
|
|
-const uploadAudio = (filePath) => {
|
|
|
- uni.showLoading({
|
|
|
- title: '上传中...'
|
|
|
- });
|
|
|
- uni.uploadFile({
|
|
|
- url: 'https://your-server.com/upload',
|
|
|
- filePath: filePath,
|
|
|
- name: 'audio', // 后端接收文件的字段名
|
|
|
- formData: {
|
|
|
- // 可以附加其他表单数据
|
|
|
- 'userId': '123',
|
|
|
- 'timestamp': new Date().getTime()
|
|
|
- },
|
|
|
- success: (uploadRes) => {
|
|
|
- uni.hideLoading();
|
|
|
- console.log('上传成功', uploadRes);
|
|
|
- // 处理服务器返回的数据
|
|
|
- const data = JSON.parse(uploadRes.data);
|
|
|
- uni.showToast({
|
|
|
- title: '上传成功',
|
|
|
- icon: 'success'
|
|
|
- });
|
|
|
- },
|
|
|
- fail: (error) => {
|
|
|
- uni.hideLoading();
|
|
|
- console.log('上传失败', error);
|
|
|
- uni.showToast({
|
|
|
- title: '上传失败',
|
|
|
- icon: 'none'
|
|
|
- });
|
|
|
+const addManagerEventListener = () => {
|
|
|
+ manager.onStop = (res) => {
|
|
|
+ uni.hideLoading();
|
|
|
+ if (!res.result) {
|
|
|
+ return uni.showToast({ title: '未识别到讲话内容', duration: 3000, icon: 'none' });
|
|
|
}
|
|
|
- });
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-const playRecording = () => {
|
|
|
|
|
|
- if (innerAudioContext) {
|
|
|
- innerAudioContext.src = audioPath.value;
|
|
|
- innerAudioContext.play();
|
|
|
+ modelInpValue.value = res.result;
|
|
|
|
|
|
- innerAudioContext.onPlay(() => {
|
|
|
- console.log('开始播放');
|
|
|
- });
|
|
|
-
|
|
|
- innerAudioContext.onError((err) => {
|
|
|
- console.error('播放错误:', err);
|
|
|
- uni.showToast({ title: '播放失败', icon: 'none' });
|
|
|
- });
|
|
|
+ onSubmit();
|
|
|
+ }
|
|
|
+
|
|
|
+ manager.onStart = (res) => {
|
|
|
+ isRecording.value = true;
|
|
|
+ showVolume.value = true;
|
|
|
+ console.log("成功开始录音识别", res)
|
|
|
+ }
|
|
|
+
|
|
|
+ manager.onError = (res) => {
|
|
|
+ uni.hideLoading();
|
|
|
+
|
|
|
+ isRecording.value = false;
|
|
|
+ showVolume.value = false;
|
|
|
+
|
|
|
+ uni.showToast({ title: '语音识别失败', duration: 3000, icon: 'none' });
|
|
|
+ console.error("error msg", res.msg)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 语音播放相关
|
|
|
-const startRecording = e => {
|
|
|
- isRecording.value = true;
|
|
|
- showVolume.value = true;
|
|
|
-
|
|
|
- recorderManager.start({
|
|
|
- format: 'mp3',
|
|
|
- duration: 60000, // 最长1分钟
|
|
|
- sampleRate: 44100,
|
|
|
- numberOfChannels: 1,
|
|
|
- encodeBitRate: 192000,
|
|
|
- frameSize: 50, // 帧大小,影响onFrameRecorded回调频率
|
|
|
- });
|
|
|
-}
|
|
|
-
|
|
|
-function setupRecorderEvents() {
|
|
|
- if (!recorderManager) return;
|
|
|
-
|
|
|
+// 开始录音
|
|
|
+const startRecording = () => {
|
|
|
+ if ( modelLoading.value ) {
|
|
|
+ return uni.showToast({ title: '当前有会话进行中', duration: 3000, icon: 'none' });
|
|
|
+ };
|
|
|
+
|
|
|
uni.authorize({
|
|
|
scope: 'scope.record',
|
|
|
success() {
|
|
|
- recorderManager.onStart(() => {
|
|
|
- console.log('录音开始');
|
|
|
- });
|
|
|
-
|
|
|
- recorderManager.onPause(() => {
|
|
|
- console.log('录音暂停');
|
|
|
- });
|
|
|
-
|
|
|
- recorderManager.onStop((res) => {
|
|
|
-
|
|
|
- audioPath.value = res.tempFilePath;
|
|
|
-
|
|
|
- // setTimeout(() => {
|
|
|
- // innerAudioContext.src = 'https://dlink.host/musics/aHR0cHM6Ly9vbmVkcnYtbXkuc2hhcmVwb2ludC5jb20vOnU6L2cvcGVyc29uYWwvc3Rvcl9vbmVkcnZfb25taWNyb3NvZnRfY29tL0VjYzBzQUxiWFk5TWdHQl9GUVNkV2pJQm5wRmM0MktDZWpURnhhMjhELUdXeVE.mp3';
|
|
|
- // innerAudioContext.play();
|
|
|
- // }, 1000)
|
|
|
-
|
|
|
- console.log('录音停止', res);
|
|
|
- // 这里可以处理录音结果,如上传或播放
|
|
|
- });
|
|
|
- recorderManager.onFrameRecorded((res) => {
|
|
|
- // 可以在这里获取实时音量等信息
|
|
|
- const volume = res.volumn; // 注意微信可能是volumn而不是volume
|
|
|
- if (volume !== undefined) {
|
|
|
- volumeLevel.value = Math.min(100, volume * 200); // 调整音量显示比例
|
|
|
- }
|
|
|
- });
|
|
|
+ manager.start({ lang: "zh_CN", duration: 60000 })
|
|
|
},
|
|
|
fail() {
|
|
|
- uni.showToast({ title: '用户拒绝授权录音权限', icon: 'none' });
|
|
|
- },
|
|
|
+ uni.showModal({
|
|
|
+ title: '提示',
|
|
|
+ content: '需要麦克风权限'
|
|
|
+ });
|
|
|
+ }
|
|
|
});
|
|
|
-
|
|
|
}
|
|
|
|
|
|
function stopRecording() {
|
|
|
if (!isRecording.value) return;
|
|
|
-
|
|
|
isRecording.value = false;
|
|
|
showVolume.value = false;
|
|
|
-
|
|
|
- recorderManager.stop();
|
|
|
-
|
|
|
- // 停止音量检测
|
|
|
- // stopVolumeDetection();
|
|
|
+ uni.showLoading({
|
|
|
+ title: "识别中...",
|
|
|
+ mask: true
|
|
|
+ })
|
|
|
+ manager.stop();
|
|
|
}
|
|
|
|
|
|
-// 初始化录音管理器
|
|
|
onMounted(() => {
|
|
|
- recorderManager = wx.getRecorderManager();
|
|
|
-
|
|
|
- setupRecorderEvents();
|
|
|
-
|
|
|
-
|
|
|
+ addManagerEventListener();
|
|
|
+ // 后面需要补充一个加载的loading
|
|
|
+ setTimeout(() => {
|
|
|
+ isLoad.value = true;
|
|
|
+ }, 1500)
|
|
|
});
|
|
|
+
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
+ <!-- 后续需要拆分成一个组件 -->
|
|
|
<view class="voice-wrapper" v-show="isRecording">
|
|
|
<view class="voice-inner">
|
|
|
<view class="voice-card">
|
|
|
<view class="voice-tip">
|
|
|
<view class="la-line-scale-pulse-out">
|
|
|
- <view></view>
|
|
|
- <view></view>
|
|
|
- <view></view>
|
|
|
- <view></view>
|
|
|
- <view></view>
|
|
|
+ <view v-for="item in 5" :key="item"></view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
@@ -206,15 +135,23 @@ onMounted(() => {
|
|
|
</view>
|
|
|
|
|
|
<view class="chat-inp-container">
|
|
|
- <!-- <view @click="playRecording">按钮</view> -->
|
|
|
<view class="chat-inp-inner">
|
|
|
<view class="voice-btn" @click="onChangeInpType">
|
|
|
<TheSvgIcon class="icon" src="icon-voice" size="42"></TheSvgIcon>
|
|
|
</view>
|
|
|
<view class="inp-inner">
|
|
|
- <textarea v-model.trim="modelInpValue" :show-confirm-bar="false" :cursor-spacing="30" auto-height
|
|
|
- :maxlength="2000" class="chat-inp" placeholder="输入您的问题或需求" placeholder-style="color:#9A9A9A"
|
|
|
- v-show="inpType == 'input'">
|
|
|
+ <textarea
|
|
|
+ v-model.trim="modelInpValue"
|
|
|
+ :show-confirm-bar="false"
|
|
|
+ :cursor-spacing="30"
|
|
|
+ :maxlength="2000"
|
|
|
+ :style="{ maxHeight: !isLoad ? '56rpx' : '200rpx' }"
|
|
|
+ auto-height
|
|
|
+ class="chat-inp"
|
|
|
+ placeholder="输入您的问题或需求"
|
|
|
+ placeholder-style="color:#9A9A9A"
|
|
|
+ v-show="inpType == 'input'"
|
|
|
+ >
|
|
|
</textarea>
|
|
|
|
|
|
<view class="chat-voice" v-show="inpType == 'voice'" @touchstart="startRecording" @touchend="stopRecording"
|
|
@@ -253,17 +190,16 @@ onMounted(() => {
|
|
|
}
|
|
|
|
|
|
.inp-inner {
|
|
|
- width: 100%;
|
|
|
- min-height: 56rpx;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- // padding-bottom: 10rpx;
|
|
|
+ width: 100%;
|
|
|
+ min-height: 56rpx;
|
|
|
+ padding: 0 16rpx;
|
|
|
|
|
|
.chat-inp {
|
|
|
width: 100%;
|
|
|
- max-height: 200rpx;
|
|
|
height: 100%;
|
|
|
- padding: 0 16rpx;
|
|
|
+ // height: 28rpx;
|
|
|
font-size: 28rpx;
|
|
|
color: #333;
|
|
|
box-sizing: border-box;
|
|
@@ -271,7 +207,10 @@ onMounted(() => {
|
|
|
|
|
|
.chat-voice {
|
|
|
width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ padding: 12rpx 0;
|
|
|
font-size: 28rpx;
|
|
|
+ font-weight: bold;
|
|
|
text-align: center;
|
|
|
}
|
|
|
}
|