bug: md container 卡顿处理

This commit is contained in:
huangzhe
2025-08-04 17:40:17 +08:00
parent e390edf77e
commit c55c46bfab
4 changed files with 55 additions and 45 deletions

View File

@@ -208,11 +208,8 @@ export default {
axiosGetAiChat() { axiosGetAiChat() {
const abortController = new AbortController() const abortController = new AbortController()
this.requestSingle = abortController this.requestSingle = abortController
this.messageInfo.information = this.genMessage.information || this.newMessage this.messageInfo.information = (this.genMessage && this.genMessage.information) || this.newMessage
// 重置 answerMap
this.answerMap = '' this.answerMap = ''
// this.typingQueue = []
this.currentMessage = JSON.parse(JSON.stringify(this.messageInfo)) this.currentMessage = JSON.parse(JSON.stringify(this.messageInfo))
const params = { const params = {
appType: "gwcsHelper2", appType: "gwcsHelper2",
@@ -232,11 +229,12 @@ export default {
isDisLike: false, isDisLike: false,
} }
if (this.genMessage) { if (this.genMessage) {
this.$set(this.messages, this.messages.length - 1, this.genMessage = { ...this.currentMessage }) // this.$set(this.messages, this.messages.length - 1, this.genMessage = { ...this.currentMessage })
this.messages.push({ ...this.currentMessage })
} else { } else {
this.messages.push(this.currentMessage) this.messages.push(this.currentMessage)
} }
// 如果有自定义参数 // 自定义参数
if (this.chatData) { if (this.chatData) {
for (let k in this.chatData) { for (let k in this.chatData) {
params[k] = this.chatData[k] params[k] = this.chatData[k]
@@ -291,8 +289,6 @@ export default {
this.$emit('update:conversationId', data.conversation_id) this.$emit('update:conversationId', data.conversation_id)
if (data.answer) { if (data.answer) {
this.answerMap += data.answer
let completeInformation = {} let completeInformation = {}
let textContent = '' let textContent = ''
@@ -309,7 +305,7 @@ export default {
textContent = text[1].trim() textContent = text[1].trim()
} }
console.log(`completeInformation: ${JSON.stringify(completeInformation)}, textContent: ${textContent}`); // console.log(`completeInformation: ${JSON.stringify(completeInformation)}, textContent: ${textContent}`);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
@@ -318,10 +314,12 @@ export default {
if (!completeInformation && !textContent) { if (!completeInformation && !textContent) {
return null return null
} }
console.log(`textContent: ${textContent}`);
this.answerMap += textContent
const { information, is_complete } = completeInformation const { information, is_complete } = completeInformation
this.messageInfo.information = information ? information : this.newMessage this.messageInfo.information = information
this.messageInfo.is_complete = is_complete && is_complete.toString() this.messageInfo.is_complete = is_complete && is_complete.toString()
if (is_complete) { if (is_complete) {
@@ -329,9 +327,8 @@ export default {
this.requestIndex++ this.requestIndex++
return null return null
} }
}
return this.updateConversationState(data) return this.updateConversationState(data)
}
} catch (error) { } catch (error) {
console.error('流数据解析失败:', error) console.error('流数据解析失败:', error)
return null return null
@@ -360,7 +357,7 @@ export default {
}, },
updateMessageContent(parse, requestIndex) { updateMessageContent(parse, requestIndex) {
let { event, answer, isThink, message_id } = parse let { event, answer, isThink, message_id } = parse
this.currentMessageID = message_id // this.currentMessageID = message_id
if (!this.currentMessage || !answer) return if (!this.currentMessage || !answer) return
if (event !== 'message') return if (event !== 'message') return
@@ -387,20 +384,23 @@ export default {
// 取出一个完整文本块 // 取出一个完整文本块
const chunk = this.typingQueue.shift() const chunk = this.typingQueue.shift()
const chars = Array.from(chunk.answer) const chars = Array.from(chunk.answer)
// const isThink = chunk.isThink
const isThink = chunk.isThink
// 内部递归函数,用于逐字输出当前块 // 内部递归函数,用于逐字输出当前块
const outputChar = () => { const outputChar = () => {
if (chars.length === 0) { if (chars.length === 0) {
// 当前块输出完毕,继续处理下一个 // 当前块输出完毕,继续处理下一个
setTimeout(typeNextChar, 10) setTimeout(typeNextChar, 100)
return return
} }
const char = chars.shift() || '' const char = chars.shift() || ''
// console.log(`char`, char);
if (requestIndex === 2) { if (requestIndex === 2) {
this.$set(this.genMessage, 'text', this.genMessage.text + char) this.$set(this.genMessage, 'text', this.genMessage.text + char)
// this.$forceUpdate()
} if (requestIndex === 1) { } if (requestIndex === 1) {
this.$set(this.currentMessage, 'text', this.currentMessage.text + char) this.$set(this.currentMessage, 'text', this.currentMessage.text + char)
// this.$forceUpdate()
} }
const delay = this.getTypingDelay(char) const delay = this.getTypingDelay(char)
setTimeout(outputChar, delay) setTimeout(outputChar, delay)

View File

@@ -3,9 +3,6 @@ import Vue from "vue";
import KnowledgeCard from "../card/index.vue" import KnowledgeCard from "../card/index.vue"
export function markdownContainer(markdownIt) { export function markdownContainer(markdownIt) {
// console.log(markdownIt);
// console.log(container);
container(markdownIt, "card", { container(markdownIt, "card", {
validate: function (params) { validate: function (params) {
// console.log(params.trim(), "title line"); // console.log(params.trim(), "title line");
@@ -14,9 +11,12 @@ export function markdownContainer(markdownIt) {
}, },
render: function (tokens, idx) { render: function (tokens, idx) {
// console.log(`tokens, idx`, tokens, idx); // console.log(`tokens, idx`, tokens, idx);
return tokens[idx].content // return tokens[idx].content
// return "<br/>--------<br/>"
}, },
content: function (tokens, idx, options, env, self) { content: function (tokens, idx, options, env, self) {
console.log(tokens[idx]);
// console.log(`args`, tokens, idx, options, env, self); // console.log(`args`, tokens, idx, options, env, self);
@@ -25,8 +25,8 @@ export function markdownContainer(markdownIt) {
// const { markup } = tokens[idx] // const { markup } = tokens[idx]
// console.log(`markup`, markup); // console.log(`markup`, markup);
setTimeout(() => { // setTimeout(() => {
const dom = document.getElementById("cardTest"); // const dom = document.getElementById("cardTest");
// if (dom) return // if (dom) return
// const vm = new Vue({ // const vm = new Vue({
// el: "#cardTest", // el: "#cardTest",
@@ -37,10 +37,12 @@ export function markdownContainer(markdownIt) {
// }) // })
// }) // })
// console.log(`vue instance`, vm); // console.log(`vue instance`, vm);
}) // })
// return tokens[idx] // return tokens[idx]
// return "<div id='cardTest'></div>" // return "<div id='cardTest'></div>"
// console.log(tokens[idx].markup);
return tokens[idx].markup return tokens[idx].markup
} }
}) })

View File

@@ -4,7 +4,7 @@ import { markdownContainer } from './markdown-it-container'
export const md = new MarkdownIt({ export const md = new MarkdownIt({
html: true, html: true,
breaks: true,
linkify: true, linkify: true,
typographer: true typographer: true
}).use(markdownItKatex).use(markdownContainer) }).use(markdownItKatex).use(markdownContainer)

View File

@@ -48,7 +48,10 @@
// icon // icon
import { Icon } from 'vant' import { Icon } from 'vant'
import TreasureBox from '@/views/AI/components/treasureBox.vue' import TreasureBox from '@/views/AI/components/treasureBox.vue'
import { md } from './js/markdown-it' // import { md } from './js/markdown-it'
import MarkdownIt from 'markdown-it'
import markdownItKatex from 'markdown-it-katex'
import { markdownContainer } from './js/markdown-it-container'
export default { export default {
@@ -70,16 +73,21 @@ export default {
}, },
data() { data() {
return { return {
md,
primaryColor: '#57a6fc' primaryColor: '#57a6fc'
} }
}, },
methods: { methods: {
render(message) { render(message) {
const text = this.filterVisible(message)
// console.log(`text`, text);
return md.render(text) const md = new MarkdownIt({
html: true,
breaks: true,
linkify: true,
typographer: true
}).use(markdownItKatex).use(markdownContainer)
return md.render(message.text)
}, },
setProductName(e) { setProductName(e) {
this.$emit('setProductName', e) this.$emit('setProductName', e)
@@ -89,15 +97,15 @@ export default {
// 如果开头是中文,直接返回 // 如果开头是中文,直接返回
// if (new RegExp('^[\u4e00-\u9fa5]+', 'g').test(text)) return text // if (new RegExp('^[\u4e00-\u9fa5]+', 'g').test(text)) return text
text = text.replace(/<information>([^<]*)(?:<\/information>)?/g, '').trim() // text = text.replace(/<information>([^<]*)(?:<\/information>)?/g, '').trim()
text = text.replace(/<is_complete>([^<]*)(?:<\/is_complete>)?/g, '').trim() // text = text.replace(/<is_complete>([^<]*)(?:<\/is_complete>)?/g, '').trim()
// 捕获 不包含 < 的后置标签 span> /span> a</span> // 捕获 不包含 < 的后置标签 span> /span> a</span>
text = text.replace(/^[/]?[a-zA-z0-9]+[</\w>]+/g, '').trim() // text = text.replace(/^[/]?[a-zA-z0-9]+[</\w>]+/g, '').trim()
// // 尝试匹配 </abc> 标签 // // 尝试匹配 </abc> 标签
// text = text.replace(/^<\w+>/g, '').trim() // text = text.replace(/^<\w+>/g, '').trim()
// text = text.replace(/^\w+/, "").trim() // text = text.replace(/^\w+/, "").trim()
text = text.replace(/<\/?([\w\s='"]+)?(?!>)$/gi, '').trim() // text = text.replace(/<\/?([\w\s='"]+)?(?!>)$/gi, '').trim()
// console.log(`text`, text[text.length - 1]); // console.log(`text`, text[text.length - 1]);
return text return text