feat: 优化打字机效果和消息展示

This commit is contained in:
huangzhe
2025-07-30 18:09:23 +08:00
parent 1e16ba0497
commit 1f6bf9a865
2 changed files with 42 additions and 35 deletions

View File

@@ -108,6 +108,7 @@ export default {
is_complete: false.toString(),
information: '',
},
currentMessageID: "",
// 打字机相关
typingText: '',
typingQueue: [],
@@ -115,7 +116,6 @@ export default {
isTyping: false,
typingSpeed: 30,
typingTimeout: null,
conversationId: ''
}
},
watch: {
@@ -138,6 +138,8 @@ export default {
// this.$emit('update:messages', [])
this.$emit('update:conversationId', '')
this.$emit('update:productName', '')
this.$emit('update:messageStatus', 'stop')
this.typingQueue = []
},
async startRecording() {
if (this.messageStatus === 'send') return
@@ -220,7 +222,7 @@ export default {
// 重置 answerMap
this.answerMap = ''
this.typingQueue = []
// this.typingQueue = []
this.currentMessage = JSON.parse(JSON.stringify(this.messageInfo))
let params = {
appType: "gwcsHelper",
@@ -242,8 +244,8 @@ export default {
}
if (this.single) {
this.messages.length -= 1
this.messages.push(this.currentMessage)
this.$set(this.messages, this.messages.length - 1, this.currentMessage)
} else {
this.messages.push(this.currentMessage)
}
@@ -306,28 +308,24 @@ export default {
if (!cleanLine) return null
const data = JSON.parse(cleanLine)
// debugger/
this.conversationId = data.conversation_id
// console.log(data)
// console.log(data)
// this.conversationId = data.conversation_id
this.$emit('update:conversationId', data.conversation_id)
if (data.answer) {
this.answerMap += data.answer
const is_complete = /<is_complete>([^<]*)(?:<\/is_complete>)?/.exec(this.answerMap)
const information = /<information>([^<]*)(?:<\/information>)?/.exec(this.answerMap)
const text = /<text>([^<]*)(?:<\/text>)?/.exec(this.answerMap)
// console.log(this.answerMap);
this.messageInfo.information = information ? information[1].trim() : this.newMessage
this.messageInfo.is_complete = is_complete ? is_complete[1].trim() : 'false'
if (is_complete && is_complete[1] === 'true' && text && text[1].trim() === '') {
this.requestSingle.abort()
// setTimeout(() => {
// debugger
// this.$emit('update:messageStatus', 'send')
this.single = true
// debugger
this.axiosGetAiChat()
// }, 1000)
// this.typingQueue = []
return null
}
}
@@ -359,7 +357,8 @@ export default {
return data
},
updateMessageContent(parse) {
let { event, answer, isThink } = parse
let { event, answer, isThink, message_id } = parse
this.currentMessageID = message_id
// 会导致发送按钮提前高亮展示
// if (event === 'message_end') {
// this.$emit('update:messageStatus', 'stop')
@@ -371,6 +370,7 @@ export default {
const chars = {
answer: answer,
isThink: isThink,
message_id
}
// debugger
this.typingQueue.push(chars)
@@ -388,6 +388,11 @@ export default {
// 取出一个完整文本块
const chunk = this.typingQueue.shift()
if (chunk.message_id !== this.currentMessageID) {
console.log('message_id !== this.currentMessageID');
typeNextChar()
return
}
// console.log(this.messages);
const chars = Array.from(chunk.answer)

View File

@@ -1,6 +1,5 @@
<template>
<section>
<!-- {{ messagesList }} -->
<div v-for="(message, index) in messagesList" :key="index" class="message-item">
<!-- {{ message }} -->
<!--用户消息-->
@@ -21,6 +20,8 @@
<p v-html="md.render(message.think)" v-if="message.think && message.showThink" class="thinkText" />
</span>
<div style="width: 100%">
<!-- {{ (message) }} -->
<!-- <hr> -->
<p v-html="render(message)" class="render-container"></p>
<span class="speakLoadingToast pv10" v-if="!filterVisible(message)">
<van-loading type="spinner" :color="primaryColor" size="20px" />
@@ -79,33 +80,34 @@ export default {
},
methods: {
render(message) {
return md.render(this.filterVisible(message))
const text = this.filterVisible(message)
return md.render(text)
},
setProductName(e) {
this.$emit('setProductName', e)
},
filterVisible(message) {
if (!message.text.startsWith('<')) {
let text = message.text
// 如果开头是中文,直接返回
if (new RegExp('^[\u4e00-\u9fa5]+', 'g').test(text)) return text
// if (!message.text.startsWith('<')) {
let text = message.text
// 如果开头是中文,直接返回
if (new RegExp('^[\u4e00-\u9fa5]+', 'g').test(text)) return text
// 捕获 不包含 < 的后置标签 span> /span> a</span>
text = text.replace(/^[/]?[a-zA-z0-9]+[</\w>]+/g, '')
// 尝试匹配 </abc> 标签
text = text.replace(/^<\w+>/g, '')
// 尝试把 </ 标签去除
// text = text.replace(/[</]+$/g, '')
// <\/?([\w\s]+)?(?!>)$
text = text.replace(/<\/?([\w\s='"]+)?(?!>)$/g, '')
return text
}
// 只把 text 标签内容渲染
let match = /<text>([^<]*)(?:<\/text>)?/.exec(message.text)
let text = match ? match[1] : ''
// 捕获 不包含 < 的后置标签 span> /span> a</span>
text = text.replace(/^[/]?[a-zA-z0-9]+[</\w>]+/g, '')
// 尝试匹配 </abc> 标签
text = text.replace(/^<\w+>/g, '')
// <\/?([\w\s]+)?(?!>)$
text = text.replace(/<\/?([\w\s='"]+)?(?!>)$/g, '')
text = text.replace(/<information>([^<]*)(?:<\/information>)?/g, '')
text = text.replace(/<is_complete>([^<]*)(?:<\/is_complete>)?/g, '')
text = text.replace(/^\w+/, "")
return text
// }
// 只把 text 标签内容渲染
// let match = /<text>([^<]*)(?:<\/text>)?/.exec(message.text)
// let text = match ? match[1] : ''
// text = text.replace(/<\/?([\w\s='"]+)?(?!>)$/g, '')
// return text
},
showThink(message) {
this.$set(message, 'showThink', !message.showThink)