mirror of
http://112.124.100.131/ebiz-ai/ebiz-base-ai.git
synced 2025-12-09 10:56:50 +08:00
feat(AI): 为 chat组件添加打字机动画效果
This commit is contained in:
@@ -90,33 +90,32 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
// isThink: {
|
|
||||||
// type: Boolean,
|
|
||||||
// default: false,
|
|
||||||
// },
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isThink: false,
|
isThink: false,
|
||||||
newMessage: '',
|
newMessage: '',
|
||||||
// 录音相关状态
|
|
||||||
isRecording: false,
|
isRecording: false,
|
||||||
mediaRecorder: null,
|
mediaRecorder: null,
|
||||||
audioChunks: [],
|
audioChunks: [],
|
||||||
isRecognizing: false,
|
isRecognizing: false,
|
||||||
// 语音模式开关
|
|
||||||
isVoiceMode: false,
|
isVoiceMode: false,
|
||||||
answerMap: '',
|
answerMap: '',
|
||||||
currentMessage: null,
|
currentMessage: null,
|
||||||
|
|
||||||
|
// 打字机相关
|
||||||
|
typingText: '',
|
||||||
|
typingQueue: [],
|
||||||
|
isTyping: false,
|
||||||
|
typingSpeed: 50,
|
||||||
|
typingTimeout: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
deepInternet() {
|
deepInternet() {
|
||||||
// this.isDeep = !this.isDeep
|
|
||||||
this.$emit('update:isDeep', !this.isDeep)
|
this.$emit('update:isDeep', !this.isDeep)
|
||||||
},
|
},
|
||||||
searchInternet() {
|
searchInternet() {
|
||||||
// this.isSearching = !this.isSearching
|
|
||||||
this.$emit('update:isSearching', !this.isSearching)
|
this.$emit('update:isSearching', !this.isSearching)
|
||||||
},
|
},
|
||||||
startNewConversation() {
|
startNewConversation() {
|
||||||
@@ -124,7 +123,6 @@ export default {
|
|||||||
this.$emit('update:conversationId', '')
|
this.$emit('update:conversationId', '')
|
||||||
this.$emit('update:productName', '')
|
this.$emit('update:productName', '')
|
||||||
},
|
},
|
||||||
|
|
||||||
async startRecording() {
|
async startRecording() {
|
||||||
if (this.messageStatus === 'send') return
|
if (this.messageStatus === 'send') return
|
||||||
if (this.isRecognizing) return
|
if (this.isRecognizing) return
|
||||||
@@ -173,9 +171,7 @@ export default {
|
|||||||
const text = await this.callVoiceRecognitionAPI(blob)
|
const text = await this.callVoiceRecognitionAPI(blob)
|
||||||
if (text) {
|
if (text) {
|
||||||
this.newMessage = text
|
this.newMessage = text
|
||||||
// this.messageStatus = 'stop'
|
|
||||||
this.$emit('update:messageStatus', 'stop')
|
this.$emit('update:messageStatus', 'stop')
|
||||||
// this.sendMessage()
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('语音识别失败:', error)
|
console.error('语音识别失败:', error)
|
||||||
@@ -223,11 +219,9 @@ export default {
|
|||||||
conversationId: this.conversationId,
|
conversationId: this.conversationId,
|
||||||
productName: this.productName,
|
productName: this.productName,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.$route.query.compareId) {
|
if (this.$route.query.compareId) {
|
||||||
params.compareResult = JSON.parse(sessionStorage.getItem('results'))
|
params.compareResult = JSON.parse(sessionStorage.getItem('results'))
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(chat(), {
|
fetch(chat(), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
@@ -272,7 +266,6 @@ export default {
|
|||||||
const cleanLine = line.replace(/^data:\s*/, '')
|
const cleanLine = line.replace(/^data:\s*/, '')
|
||||||
if (!cleanLine) return null
|
if (!cleanLine) return null
|
||||||
const data = JSON.parse(cleanLine)
|
const data = JSON.parse(cleanLine)
|
||||||
// console.log(data)
|
|
||||||
if (data.answer) {
|
if (data.answer) {
|
||||||
this.answerMap += data.answer
|
this.answerMap += data.answer
|
||||||
}
|
}
|
||||||
@@ -284,52 +277,62 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateConversationState(data) {
|
updateConversationState(data) {
|
||||||
// this.conversationId =
|
this.$emit('update:conversationId', data.conversation_id || '')
|
||||||
this.$emit('update:conversationId', data.conversation_id || conversationId.value)
|
|
||||||
if (data.answer && data.answer.indexOf('<think>') !== -1) {
|
if (data.answer && data.answer.indexOf('<think>') !== -1) {
|
||||||
this.isThink = true
|
this.isThink = true
|
||||||
this.$emit('getIsThink', true)
|
this.$emit('getIsThink', true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.answer && data.answer.indexOf('</think>') !== -1) {
|
if (data.answer && data.answer.indexOf('</think>') !== -1) {
|
||||||
this.isThink = false
|
this.isThink = false
|
||||||
this.$emit('getIsThink', false)
|
this.$emit('getIsThink', false)
|
||||||
// this.$emit('update:isThink', false)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateMessageContent({ answer, event }) {
|
updateMessageContent({ answer, event }) {
|
||||||
if (event === 'message_end') {
|
if (event === 'message_end') {
|
||||||
// this.messageStatus = 'stop'
|
|
||||||
this.$emit('update:messageStatus', 'stop')
|
this.$emit('update:messageStatus', 'stop')
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log(answer)
|
|
||||||
// console.log(this.currentMessage)
|
|
||||||
if (!this.currentMessage || !answer) return
|
if (!this.currentMessage || !answer) return
|
||||||
const mode = this.isThink ? 'think' : 'text'
|
const mode = this.isThink ? 'think' : 'text'
|
||||||
this.currentMessage[mode] += answer
|
const chars = answer.split('')
|
||||||
|
this.typingQueue.push(...chars)
|
||||||
|
if (!this.isTyping) {
|
||||||
|
this.startTypingAnimation(mode)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
startTypingAnimation(mode) {
|
||||||
|
this.isTyping = true
|
||||||
|
|
||||||
|
const typeNextChar = () => {
|
||||||
|
if (this.typingQueue.length === 0) {
|
||||||
|
this.isTyping = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const char = this.typingQueue.shift()
|
||||||
|
this.currentMessage[mode] += char
|
||||||
|
|
||||||
|
this.typingTimeout = setTimeout(typeNextChar, this.getTypingDelay(char))
|
||||||
|
}
|
||||||
|
|
||||||
|
typeNextChar()
|
||||||
|
},
|
||||||
|
getTypingDelay(char) {
|
||||||
|
if (['。', '!', '?', ',', '\n'].includes(char)) {
|
||||||
|
return this.typingSpeed * 3
|
||||||
|
}
|
||||||
|
return this.typingSpeed
|
||||||
},
|
},
|
||||||
sendMessage() {
|
sendMessage() {
|
||||||
if (this.messageStatus === 'send') return
|
if (this.messageStatus === 'send') return
|
||||||
if (this.newMessage.trim() === '') return
|
if (this.newMessage.trim() === '') return
|
||||||
// 防止xss
|
|
||||||
this.newMessage = this.newMessage.replace(/<[^>]+>/g, '')
|
this.newMessage = this.newMessage.replace(/<[^>]+>/g, '')
|
||||||
this.messages.push({ type: 'user', text: this.newMessage })
|
this.messages.push({ type: 'user', text: this.newMessage })
|
||||||
this.$emit('update:messageStatus', 'send')
|
this.$emit('update:messageStatus', 'send')
|
||||||
// this.messageStatus = 'send'
|
|
||||||
if (this.newMessage.includes('工具箱')) {
|
if (this.newMessage.includes('工具箱')) {
|
||||||
this.hasTreasureBox()
|
this.hasTreasureBox()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (this.newMessage.includes('产品对比')) {
|
|
||||||
// // this.hasTreasureBox()
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// this.autoScrollEnabled = true
|
|
||||||
this.$emit('update:autoScrollEnabled', true)
|
this.$emit('update:autoScrollEnabled', true)
|
||||||
|
|
||||||
this.axiosGetAiChat()
|
this.axiosGetAiChat()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user