This commit is contained in:
shengchanzhe 2023-10-14 12:00:10 +08:00
parent 8ab770305c
commit d50341711e
3 changed files with 149 additions and 70 deletions

View File

@ -0,0 +1,57 @@
<template>
<div style="position: relative;">
<div class="div" />
<p class="text">
AI播放中
</p>
</div>
</template>
<style scoped>
.text{
display: block;
width: 100%;
font-size: 1.5em;
text-align: center;
position: absolute;
left: 0;
bottom: -6em;
}
.div {
display: flex;
width: 3.5em;
height: 3.5em;
border: 3px solid transparent;
border-top-color: #3cefff;
border-bottom-color: #3cefff;
border-radius: 50%;
animation: spin 1.5s linear infinite;
}
div:before {
content: '';
display: block;
margin: auto;
width: 0.75em;
height: 0.75em;
border: 3px solid #3cefff;
border-radius: 50%;
animation: pulse 1s alternate ease-in-out infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
@keyframes pulse {
from {
transform: scale(0.5);
}
to {
transform: scale(1);
}
}
</style>

View File

@ -1,6 +1,7 @@
<script setup lang='ts'> <script setup lang='ts'>
import { computed, reactive, ref, watch, toRef, nextTick } from 'vue' import { computed, reactive, ref, watch } from 'vue'
import { NDropdown, useMessage } from 'naive-ui' import { NDropdown, useMessage } from 'naive-ui'
import axios from 'axios'
import AvatarComponent from './Avatar.vue' import AvatarComponent from './Avatar.vue'
import TextComponent from './Text.vue' import TextComponent from './Text.vue'
import { SvgIcon } from '@/components/common' import { SvgIcon } from '@/components/common'
@ -93,47 +94,70 @@ async function handleCopy() {
} }
} }
import axios from "axios" const http = (text: any) => {
return new Promise((resolve, reject) => {
const http = (text: any)=>{ axios.post('https://chat.lihaink.cn/index/tts', { data: text })
return new Promise((resolve, reject)=>{ .then((response) => {
axios.post('https://chat.lihaink.cn/index/tts',{data: text})
.then(response => {
// //
// console.log('',response.data); // console.log('',response.data);
resolve(response.data) resolve(response.data)
}) })
.catch(error => { .catch((error) => {
// //
console.error('请求失败',error); console.error('请求失败', error)
reject(error) reject(error)
}); })
}) })
} }
let ttsList = [];
async function loadTTS(){ //
let tts = props.tts?JSON.parse(props.tts):[] const audioElements = reactive([])
if(tts.length>ttsList.length){
for(let i=ttsList.length;i<tts.length;i++){ let ttslength = 0
let res = await http(tts[i]) let ttsLoadFlag = 0
let lengthFlag = 0
let tts = props.tts ? JSON.parse(props.tts) : []
async function loadTTS() {
lengthFlag = tts.length
if (tts.length > ttslength && audioElements.length == ttslength && ttsLoadFlag == 0) {
for (let i = ttslength; i < lengthFlag; i++) {
console.log(`加载了${i}`, tts[i])
ttsLoadFlag = 1
if (tts[i] == '')
break
try {
const res = await http(tts[i])
const a = new Audio(res.data.mp3) const a = new Audio(res.data.mp3)
a.addEventListener('ended', () => { a.addEventListener('ended', () => {
onAudioEnd() onAudioEnd()
}) })
audioElements.push(a) audioElements.push(a)
if (ttsList.length == 0) if (ttslength == 0)
playAudio() playAudio()
if (i == tts.length - 1) {
ttslength = tts.length
ttsLoadFlag = 0
}
}
catch (error) {
console.log(error)
}
} }
ttsList = JSON.parse(JSON.stringify(tts));
} }
} }
// //
watch(()=>props.tts, async (n:any, o:any)=>{ watch(() => props.tts, async (n: any, o: any) => {
loadTTS(); if (props.tts == '') {
},{immediate:true,deep:true}) for (let i = 0; i < audioElements.length; i++)
audioElements[i].pause()
audioElements.length = 0
}
tts = props.tts ? JSON.parse(props.tts) : []
loadTTS()
}, { immediate: true, deep: true })
console.log( '组件渲染 ', props.text, props.tts); console.log('组件渲染 ', props.text, props.tts)
const playStatus = ref('end') const playStatus = ref('end')
let playing = 0 let playing = 0
@ -158,12 +182,11 @@ async function radioPlay() {
await promise() await promise()
// WebSocket // WebSocket
socket.onclose = (event: any) => { socket.onclose = (event: any) => {
sok = false sok = false
console.log('连接已关闭: ', event, sok) console.log('连接已关闭: ', event, sok)
console.log('sok状态', sok); console.log('sok状态', sok)
} }
// socket.send(JSON.stringify({ // socket.send(JSON.stringify({
@ -178,9 +201,9 @@ async function radioPlay() {
socket.onmessage = (event: any) => { socket.onmessage = (event: any) => {
const msg = JSON.parse(event.data) const msg = JSON.parse(event.data)
console.log(i, msg.mp3) console.log(i, msg.mp3)
if(msg.key=='false'){ if (msg.key == 'false')
return socket.close(); return socket.close()
}
const a = new Audio(msg.mp3) const a = new Audio(msg.mp3)
a.addEventListener('ended', () => { a.addEventListener('ended', () => {
onAudioEnd() onAudioEnd()
@ -213,12 +236,10 @@ const onAudioEnd = (index: any) => {
emit('changeNowStatus', 'playing') emit('changeNowStatus', 'playing')
} }
else { else {
playStatus.value = 'end'; playStatus.value = 'end'
emit('changeNowStatus', 'input') emit('changeNowStatus', 'input')
} }
} }
//
const audioElements = reactive([])
// //
const playAudio = (playIndex: any) => { const playAudio = (playIndex: any) => {
// for (let i = 0; i < audioElements.length; i++) // for (let i = 0; i < audioElements.length; i++)

View File

@ -25,6 +25,7 @@ import indexBG from '@/components/animation/indexBG.vue'
import recording from '@/components/animation/recording.vue' import recording from '@/components/animation/recording.vue'
import inputing from '@/components/animation/inputing.vue' import inputing from '@/components/animation/inputing.vue'
import lodingSpin from '@/components/animation/lodingSpin.vue' import lodingSpin from '@/components/animation/lodingSpin.vue'
import playing from '@/components/animation/playing.vue'
// import IatRecorder from '@/utils/larRcorder.js' // import IatRecorder from '@/utils/larRcorder.js'
@ -108,7 +109,7 @@ const conversationList = computed(() =>
const prompt = ref<string>('') const prompt = ref<string>('')
const loading = ref<boolean>(false) const loading = ref<boolean>(false)
const inputRef = ref<Ref | null>(null) const inputRef = ref<Ref | null>(null)
const nowStatus = ref('input') // , input, loding, record, const nowStatus = ref('playing') // , input, loding, record,playing
const nowStart = ref(true) // , const nowStart = ref(true) // ,
// PromptStore // PromptStore
@ -197,20 +198,19 @@ async function onConversation() {
}) })
scrollToBottom() scrollToBottom()
let infoList = JSON.parse(JSON.stringify(dataSources.value)); let infoList = JSON.parse(JSON.stringify(dataSources.value))
infoList = infoList.map((item:any, index:any)=>{ infoList = infoList.map((item: any, index: any) => {
return { return {
role: index%2==0?'user':'assistant', role: index % 2 == 0 ? 'user' : 'assistant',
content: item.text content: item.text,
} }
}) })
console.log(dataSources.value); console.log(dataSources.value)
try { try {
let lastText = '' let lastText = ''
const tts:any = []; const tts: any = []
console.log('发送消息', message) console.log('发送消息', message)
const fetchChatAPIOnce = async () => { const fetchChatAPIOnce = async () => {
@ -234,8 +234,8 @@ async function onConversation() {
// console.log(`: `, msg.payload.choices.text[0].content); // console.log(`: `, msg.payload.choices.text[0].content);
// console.log(`: `, dataSources.value[dataSources.value.length - 1].text); // console.log(`: `, dataSources.value[dataSources.value.length - 1].text);
lastText += msg.payload.choices.text[0].content lastText += msg.payload.choices.text[0].content
console.log('返回的tts:', msg.payload.choices.tts_text); console.log('返回的tts:', msg.payload.choices.tts_text)
msg.payload.choices.tts_text != '' ? tts.push(msg.payload.choices.tts_text): null; msg.payload.choices.tts_text != '' ? tts.push(msg.payload.choices.tts_text) : null
const loading = false const loading = false
nowStart.value = false nowStart.value = false
updateChat( updateChat(
@ -613,7 +613,7 @@ const changeNowStatus = (e: any) => {
console.log('收到数据', e) console.log('收到数据', e)
if (e != nowStatus.value) { if (e != nowStatus.value) {
switch (e) { switch (e) {
case 'playing': nowStatus.value = 'record'; break case 'playing': nowStatus.value = 'playing'; break
case 'loding': nowStatus.value = 'loding'; break case 'loding': nowStatus.value = 'loding'; break
default: nowStatus.value = 'input'; break default: nowStatus.value = 'input'; break
} }
@ -651,6 +651,7 @@ setTimeout(() => {
<recording v-else-if="nowStatus == 'record'" key="record" /> <recording v-else-if="nowStatus == 'record'" key="record" />
<!-- <loding v-else-if="nowStatus=='loding'" key="loding"></loding> --> <!-- <loding v-else-if="nowStatus=='loding'" key="loding"></loding> -->
<lodingSpin v-else-if="nowStatus == 'loding'" key="loding" /> <lodingSpin v-else-if="nowStatus == 'loding'" key="loding" />
<playing v-else-if="nowStatus == 'playing'" key="playing" />
<!-- <lodingCir v-else-if="nowStatus=='loding'" key="loding"></lodingCir> --> <!-- <lodingCir v-else-if="nowStatus=='loding'" key="loding"></lodingCir> -->
</transition> </transition>
</indexBG> </indexBG>