|
@@ -2,14 +2,13 @@
|
|
|
<div
|
|
|
tabindex="0"
|
|
|
class="chat-online relative flex size-full flex-col items-center justify-center overflow-hidden rounded-lg bg-[url('@/assets/images/knowledge/knowledge-back-test.png')] bg-[length:100%_100%] bg-no-repeat"
|
|
|
- @focus="onFocus"
|
|
|
- @blur="onBlur"
|
|
|
- @mousedown.left="onMousedown"
|
|
|
- @mouseup.left="onMouseup"
|
|
|
- @keydown.space="onKeydown"
|
|
|
- @keyup.space="onKeyup"
|
|
|
+ @focus="state.isFocus = true"
|
|
|
+ @blur="state.isFocus = false"
|
|
|
+ @mousedown.left="onSpeakStart"
|
|
|
+ @mouseup.left="onSpeakStop"
|
|
|
+ @keydown.space="onSpaceSpeakStart"
|
|
|
+ @keyup.space="onSpaceSpeakStop"
|
|
|
>
|
|
|
- <div class="absolute top-0 left-0">焦点:{{ state.isFocus }}</div>
|
|
|
<div
|
|
|
class="absolute top-1/10 flex flex-col items-center gap-3 text-2xl font-bold text-[#303133]"
|
|
|
>
|
|
@@ -19,11 +18,11 @@
|
|
|
<template v-if="!state.isWaiting">
|
|
|
<div class="flex flex-col items-center">
|
|
|
<div>
|
|
|
- <soundWave :volume="volume" :num="30" />
|
|
|
+ <soundWave :volume="state.isAsk ? volume : 0" :num="30" />
|
|
|
</div>
|
|
|
- <div class="mt-4 text-lg font-bold text-[var(--czr-main-color)]">
|
|
|
- <template v-if="state.isAsk">正在聆听</template>
|
|
|
- <template v-else>正在回答</template>
|
|
|
+ <div class="mt-4 h-7 text-lg font-bold text-[var(--czr-main-color)]">
|
|
|
+ <template v-if="state.isAsk">正在聆听 {{ durationCpt }}</template>
|
|
|
+ <template v-else-if="state.isAnswer">正在回答</template>
|
|
|
</div>
|
|
|
<div class="mt-3 flex items-center text-sm text-[#909399]">
|
|
|
<SvgIcon
|
|
@@ -32,30 +31,23 @@
|
|
|
size="20"
|
|
|
class="mr-1"
|
|
|
/>
|
|
|
- <template v-if="state.isAsk">松开鼠标左键/Space以提问</template>
|
|
|
- <template v-else>按住鼠标左键/Space以打断</template>
|
|
|
+ <template v-if="state.isAsk"> 松开鼠标左键/Space以提问 </template>
|
|
|
+ <template v-else-if="state.isAnswer"> 鼠标左键/Space以打断 </template>
|
|
|
+ <template v-else> 按住鼠标左键/Space开始提问 </template>
|
|
|
</div>
|
|
|
- <!-- <div>-->
|
|
|
- <!-- <template v-if="state.isSpeak">-->
|
|
|
- <!-- <el-button @click="stop">停止录音</el-button>-->
|
|
|
- <!-- </template>-->
|
|
|
- <!-- <template v-else>-->
|
|
|
- <!-- <el-button @click="speak">开始录音</el-button>-->
|
|
|
- <!-- </template>-->
|
|
|
- <!-- </div>-->
|
|
|
</div>
|
|
|
</template>
|
|
|
<div class="absolute bottom-1/15 flex w-full justify-center gap-[20%]">
|
|
|
<template v-if="state.isWaiting">
|
|
|
<div
|
|
|
class="__hover flex flex-col items-center gap-2 text-[#0064F9]"
|
|
|
- @click="onListen"
|
|
|
+ @click.stop="onCall"
|
|
|
>
|
|
|
<div class="relative flex size-38 items-center justify-center">
|
|
|
<img src="@/assets/images/chat/chat-call.png" class="size-full" />
|
|
|
<div
|
|
|
- class="absolute z-0 size-full scale-70 animate-ping rounded-full bg-[#0064F9]/10"
|
|
|
- style="animation-duration: 3s"
|
|
|
+ class="absolute z-0 size-full scale-70 animate-ping rounded-full bg-[#0064F9]/20"
|
|
|
+ style="animation-duration: 1.5s"
|
|
|
></div>
|
|
|
</div>
|
|
|
<div class="text-sm font-bold">接听</div>
|
|
@@ -64,7 +56,7 @@
|
|
|
</template>
|
|
|
<div
|
|
|
class="__hover flex flex-col items-center gap-2 text-[#FF5454]"
|
|
|
- @click="$emit('hangUp')"
|
|
|
+ @click.stop="$emit('hangUp')"
|
|
|
>
|
|
|
<div class="relative flex size-38 items-center justify-center">
|
|
|
<img src="@/assets/images/chat/chat-hang.png" class="size-full" />
|
|
@@ -77,54 +69,82 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { reactive } from 'vue'
|
|
|
+import { computed, reactive } from 'vue'
|
|
|
import soundWave from './audio/sound-wave.vue'
|
|
|
import useSpeechToAudio from '@/utils/useSpeechToAudio'
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
|
|
|
const emit = defineEmits(['hangUp'])
|
|
|
const state: any = reactive({
|
|
|
- isWaiting: true,
|
|
|
- isAsk: false,
|
|
|
- isSpeak: false,
|
|
|
- isFocus: false,
|
|
|
+ isWaiting: true, // 等待接听
|
|
|
+ isAsk: false, // 提问录音中
|
|
|
+ isAnswer: false, // 聆听回答中
|
|
|
+ isFocus: false, // 语音聊天是否聚焦
|
|
|
+ duration: 0, // 提问录音时长
|
|
|
+ timer: null, // 开始提问防抖
|
|
|
+ clickSpace: false, // 按下空格会一直触发,添加个判断
|
|
|
})
|
|
|
-const onListen = () => {
|
|
|
- state.isWaiting = false
|
|
|
- state.isAsk = false
|
|
|
+const durationCpt = computed(() => {
|
|
|
+ const minutes = Math.floor(state.duration / 60)
|
|
|
+ const seconds = Math.floor(state.duration % 60)
|
|
|
+ return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`
|
|
|
+})
|
|
|
+const onSpeak = ({ duration }) => {
|
|
|
+ state.isAsk = true
|
|
|
+ state.duration = duration
|
|
|
}
|
|
|
const onEnd = (audio) => {
|
|
|
- state.isSpeak = false
|
|
|
+ state.isAsk = false
|
|
|
+ state.isAnswer = true
|
|
|
+ if (state.duration < 2) {
|
|
|
+ ElMessage.warning('提问时长过短,请持续提问!')
|
|
|
+ return
|
|
|
+ }
|
|
|
console.log(audio)
|
|
|
}
|
|
|
-const onSpeak = ({ duration }) => {
|
|
|
- state.isSpeak = true
|
|
|
- // console.log('duration', duration)
|
|
|
-}
|
|
|
const { speak, stop, volume } = useSpeechToAudio({
|
|
|
onEnd,
|
|
|
onSpeak,
|
|
|
timeout: 0,
|
|
|
})
|
|
|
-const onFocus = () => {
|
|
|
- console.log('focus')
|
|
|
- state.isFocus = true
|
|
|
+const onCall = () => {
|
|
|
+ state.isWaiting = false
|
|
|
+ state.isAsk = false
|
|
|
+ state.isAnswer = false
|
|
|
}
|
|
|
-const onBlur = () => {
|
|
|
- console.log('blur')
|
|
|
- state.isFocus = false
|
|
|
+const onSpeakStart = () => {
|
|
|
+ if (state.isAnswer) {
|
|
|
+ onStopAnswer()
|
|
|
+ }
|
|
|
+ if (!state.isWaiting && !state.isAsk && !state.clickSpace) {
|
|
|
+ state.timer = setTimeout(() => {
|
|
|
+ speak()
|
|
|
+ }, 300)
|
|
|
+ }
|
|
|
}
|
|
|
-const onMousedown = () => {
|
|
|
- console.log('mousedown')
|
|
|
+const onSpeakStop = () => {
|
|
|
+ clearTimeout(state.timer)
|
|
|
+ if (!state.isWaiting && state.isAsk) {
|
|
|
+ stop()
|
|
|
+ }
|
|
|
}
|
|
|
-const onMouseup = () => {
|
|
|
- console.log('mouseup')
|
|
|
+const onSpaceSpeakStart = () => {
|
|
|
+ onSpeakStart()
|
|
|
+ state.clickSpace = true
|
|
|
}
|
|
|
-const onKeydown = () => {
|
|
|
- console.log('keydown')
|
|
|
+const onSpaceSpeakStop = () => {
|
|
|
+ onSpeakStop()
|
|
|
+ state.clickSpace = false
|
|
|
}
|
|
|
-const onKeyup = () => {
|
|
|
- console.log('keyup')
|
|
|
+const onStopAnswer = () => {
|
|
|
+ state.isAnswer = false
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
-<style lang="scss" scoped></style>
|
|
|
+<style lang="scss" scoped>
|
|
|
+.chat-online {
|
|
|
+ * {
|
|
|
+ outline: none;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|