feat(AI): 为 chat组件添加打字机动画效果

This commit is contained in:
陈昱达
2025-06-18 16:48:34 +08:00
parent 968a65d8ab
commit f6e60df2d3

View File

@@ -90,33 +90,32 @@ export default {
type: Boolean,
default: false,
},
// isThink: {
// type: Boolean,
// default: false,
// },
},
data() {
return {
isThink: false,
newMessage: '',
// 录音相关状态
isRecording: false,
mediaRecorder: null,
audioChunks: [],
isRecognizing: false,
// 语音模式开关
isVoiceMode: false,
answerMap: '',
currentMessage: null,
// 打字机相关
typingText: '',
typingQueue: [],
isTyping: false,
typingSpeed: 50,
typingTimeout: null,
}
},
methods: {
deepInternet() {
// this.isDeep = !this.isDeep
this.$emit('update:isDeep', !this.isDeep)
},
searchInternet() {
// this.isSearching = !this.isSearching
this.$emit('update:isSearching', !this.isSearching)
},
startNewConversation() {
@@ -124,7 +123,6 @@ export default {
this.$emit('update:conversationId', '')
this.$emit('update:productName', '')
},
async startRecording() {
if (this.messageStatus === 'send') return
if (this.isRecognizing) return
@@ -173,9 +171,7 @@ export default {
const text = await this.callVoiceRecognitionAPI(blob)
if (text) {
this.newMessage = text
// this.messageStatus = 'stop'
this.$emit('update:messageStatus', 'stop')
// this.sendMessage()
}
} catch (error) {
console.error('语音识别失败:', error)
@@ -223,11 +219,9 @@ export default {
conversationId: this.conversationId,
productName: this.productName,
}
if (this.$route.query.compareId) {
params.compareResult = JSON.parse(sessionStorage.getItem('results'))
}
fetch(chat(), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -272,7 +266,6 @@ export default {
const cleanLine = line.replace(/^data:\s*/, '')
if (!cleanLine) return null
const data = JSON.parse(cleanLine)
// console.log(data)
if (data.answer) {
this.answerMap += data.answer
}
@@ -284,52 +277,62 @@ export default {
}
},
updateConversationState(data) {
// this.conversationId =
this.$emit('update:conversationId', data.conversation_id || conversationId.value)
this.$emit('update:conversationId', data.conversation_id || '')
if (data.answer && data.answer.indexOf('<think>') !== -1) {
this.isThink = true
this.$emit('getIsThink', true)
}
if (data.answer && data.answer.indexOf('</think>') !== -1) {
this.isThink = false
this.$emit('getIsThink', false)
// this.$emit('update:isThink', false)
}
},
updateMessageContent({ answer, event }) {
if (event === 'message_end') {
// this.messageStatus = 'stop'
this.$emit('update:messageStatus', 'stop')
}
// console.log(answer)
// console.log(this.currentMessage)
if (!this.currentMessage || !answer) return
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() {
if (this.messageStatus === 'send') return
if (this.newMessage.trim() === '') return
// 防止xss
this.newMessage = this.newMessage.replace(/<[^>]+>/g, '')
this.messages.push({ type: 'user', text: this.newMessage })
this.$emit('update:messageStatus', 'send')
// this.messageStatus = 'send'
if (this.newMessage.includes('工具箱')) {
this.hasTreasureBox()
return
}
// if (this.newMessage.includes('产品对比')) {
// // this.hasTreasureBox()
// return
// }
// this.autoScrollEnabled = true
this.$emit('update:autoScrollEnabled', true)
this.axiosGetAiChat()
},
},