You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
virtual-patient-web/src/views/consultation/components/VoiceInspect/VoiceInquiry.vue

166 lines
4.2 KiB
Vue

<script setup lang="ts">
import { ref, reactive, onBeforeUnmount } from "vue";
import AudioRecorder from "js-audio-recorder";
import { receiveVoiceFile } from "@/api/inquiry";
import tokeGifImg from "@/assets/inquiry/toke.gif";
import tokeImg from "@/assets/inquiry/toke.png";
import { onClickOutside } from "@vueuse/core";
import { onMounted } from "vue";
import { message } from "@/utils/message";
defineOptions({
name: "VoiceInquiry"
});
const defultText = "正在问诊......";
const text = ref(defultText);
const container = ref(null);
let audioRecorder: any = reactive({ undefined });
let intervalId: any = reactive({ undefined });
const recordType = ref("0"); // 0未开始 1 开始录制 2 暂停 3完成
onMounted(() => {
recordType.value = "0";
// 使用js-audio-recorder录制音频
audioRecorder = new AudioRecorder({
sampleBits: 16, // 采样位数,支持 8 或 16默认是16
sampleRate: 16000, // 采样率,支持 11025、16000、22050、24000、44100、48000根据浏览器默认值我的chrome是48000
numChannels: 1 // 声道,支持 1 或 2 默认是1
});
startRecording();
checkSpeaking();
});
function checkSpeaking() {
intervalId = setInterval(() => {
const dataArray = audioRecorder.getRecordAnalyseData();
const isSpeaking = Array.from(dataArray).some(value => value > 128);
if (isSpeaking) {
if (recordType.value === "0") {
recordType.value = "1";
// startRecording();
} else if (recordType.value === "2") {
audioRecorder.resume();
}
} else {
if (recordType.value === "1") {
audioRecorder.pause();
recordType.value = "2";
} else if (recordType.value === "2") {
stopRecording();
}
}
console.log("Is speaking:", isSpeaking);
}, 500); // 每 500 毫秒获取一次音量
}
const startRecording = () => {
audioRecorder.start();
};
async function stopRecording() {
recordType.value = "0";
clearInterval(intervalId);
audioRecorder.stop();
const blob = audioRecorder.getWAVBlob();
const params = new FormData();
params.append("file", blob);
const { data } = await receiveVoiceFile(params);
text.value = data;
recordType.value = "3";
sendVoiceOption();
}
const emit = defineEmits(["changeType", "save"]);
// const reset = () => {
// text.value = defultText;
// recordType.value = "0";
// startRecording();
// checkSpeaking();
// };
onClickOutside(container, () => emit("changeType", 0));
const sendVoiceOption = () => {
if (!text.value || text.value === defultText) {
message("问诊内容不能为空!", { type: "error" });
}
emit("changeType", 0);
emit("save", text.value);
};
onBeforeUnmount(() => {
audioRecorder.destroy();
});
</script>
<template>
<div ref="container" class="voiceInquiry">
<div class="voice_footer_cotent">
<img v-show="recordType === '3'" :src="tokeImg" alt="" />
<img v-show="recordType !== '3'" :src="tokeGifImg" alt="" />
<span>{{ text }}</span>
</div>
<!-- <div v-show="recordType === '3'" class="btn_list">
<span @click="reset"></span>
<el-button
class="btn"
size="large"
@click="sendVoiceOption"
type="primary"
>发送</el-button
>
</div> -->
</div>
</template>
<style lang="scss" scoped>
.voiceInquiry {
position: fixed;
bottom: 66px;
display: flex;
justify-content: center;
width: 500px;
height: 104px;
background-image: url("@/assets/inquiry/voice_bg.png");
background-size: 100% 100%;
box-shadow: 0 0 8px 0 rgb(0 0 0 / 15%);
.voice_footer_cotent {
display: flex;
flex-direction: column;
align-items: center;
img {
width: 300px;
height: 40px;
margin-top: 16px;
cursor: pointer;
}
span {
margin-top: 6px;
font-size: 14px;
font-weight: 400;
color: #364c63;
}
}
.btn_list {
position: absolute;
top: 15px;
right: 16px;
display: flex;
align-items: center;
height: 104px;
span {
margin-right: 16px;
font-size: 16px;
font-weight: 400;
color: #4287ff;
cursor: pointer;
}
img {
width: 24px;
height: 24px;
margin-right: 16px;
cursor: pointer;
}
}
}
</style>