diff --git a/package.json b/package.json index 5051b8b..46b168b 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "exceljs": "^4.3.0", "file-saver": "^2.0.5", "html2canvas": "^1.4.1", + "markdown-it": "^14.1.0", "moment": "^2.29.4", "node-polyfill-webpack-plugin": "^2.0.1", "print-js": "^1.6.0", diff --git a/src/components/doctorCheck/ButtonList.vue b/src/components/doctorCheck/ButtonList.vue index 1b04fab..c5d659d 100644 --- a/src/components/doctorCheck/ButtonList.vue +++ b/src/components/doctorCheck/ButtonList.vue @@ -185,9 +185,10 @@ style="font-size: 24px;color: red;cursor:pointer;"> - - + --> +
关闭 AI重新诊断 @@ -210,7 +211,8 @@ import SumHistory from "../../components/sumDoctorCheck/SumHistory.vue"; import OccDisease from "../../components/occDisease/OccDisease.vue" import ImageTextReport from "../../components/occDisease/ImageTextReport.vue"; import moment from 'moment'; - +import MarkdownIt from "markdown-it"; +const md = new MarkdownIt(); export default { components: { PatientRegisterEdit, @@ -308,6 +310,10 @@ export default { max: true, visible: false, diagnosis: 'AI诊断信息', + rawText: "", // 原始 markdown 内容(流式累加) + html: "", // 渲染后的 HTML(逐字更新) + typingIndex: 0, + typingTimer: null }, }; @@ -769,20 +775,61 @@ export default { } }); - postapi('/api/app/AIMessage/GetAIMessageResult', { message }) + // postapi('/api/app/AIMessage/GetAIMessageResult', { message }) + // .then(res => { + // if (res.code > -1) { + // this.AI.visible = true + // this.AI.diagnosis = res.data.result + // this.btnAImax(false) + // } else { + // this.$message.error({ showClose: true, message: res.message }) + // } + // }) + + postapi('/api/app/AiMessageWs/GetAIMessageResult', { message }) .then(res => { - if (res.code > -1) { - this.AI.visible = true - this.AI.diagnosis = res.data.result - this.btnAImax(false) - } else { - this.$message.error({ showClose: true, message: res.message }) - } + if (!res) return; + // 移除流结束标志并去掉每行的 'data: ' 前缀 + let cleaned = String(res) + .replace(/data:\s*\[DONE\]/g, '') + .replace(/^data:\s*/gm, '') + .trim(); + if (!cleaned) return; + this.AI.typingIndex = 0; + this.AI.visible = true; + this.AI.rawText = cleaned; + // 确保 typingIndex 在合理范围 + if (!this.AI.typingIndex || this.AI.typingIndex < 0) this.AI.typingIndex = 0; + if (this.AI.typingIndex > this.AI.rawText.length) this.AI.typingIndex = 0; + this.startTyping(); + this.btnAImax(false); }) - }, - + startTyping() { + if (this.AI.typingTimer) clearInterval(this.AI.typingTimer); + + this.AI.typingTimer = setInterval(() => { + if (this.AI.typingIndex < this.AI.rawText.length) { + const current = this.AI.rawText.slice(0, this.AI.typingIndex); + this.AI.html = md.render(current); + this.AI.typingIndex++; + // 每次更新后滚动到容器底部,确保最新输出可见 + this.$nextTick(() => { + try { + const el = this.$refs.aiContent; + if (el) { + el.scrollTop = el.scrollHeight; + } + } catch (e) { + // ignore + } + }); + } else { + clearInterval(this.AI.typingTimer); + } + }, 30); // 每 30ms 打一个字 + }, btnAImax(max) { this.AI.max = !max if (this.AI.max) { diff --git a/src/components/sumDoctorCheck/ButtonList.vue b/src/components/sumDoctorCheck/ButtonList.vue index 1d0eb56..42d1725 100644 --- a/src/components/sumDoctorCheck/ButtonList.vue +++ b/src/components/sumDoctorCheck/ButtonList.vue @@ -90,9 +90,10 @@ style="font-size: 24px;color: red;cursor:pointer;">
- - + --> +
关闭 AI重新诊断 @@ -136,6 +137,8 @@ import SumDiagnosis from "./SumDiagnosis.vue"; import OccDisease from "../../components/occDisease/OccDisease.vue" import moment from "moment"; +import MarkdownIt from "markdown-it"; +const md = new MarkdownIt(); export default { components: { PatientRegisterList, @@ -163,7 +166,11 @@ export default { max: true, visible: false, diagnosis: 'AI诊断信息', - } + rawText: "", // 原始 markdown 内容(流式累加) + html: "", // 渲染后的 HTML(逐字更新) + typingIndex: 0, + typingTimer: null + }, }; }, @@ -458,15 +465,34 @@ export default { message = '性别:' + this.doctorCheck.prBase.sexName + ',年龄:' + this.doctorCheck.prBase.age + '岁,检查结果:' + message - postapi('/api/app/AIMessage/GetAIMessageResult', { message }) + // postapi('/api/app/AIMessage/GetAIMessageResult', { message }) + // .then(res => { + // if (res.code > -1) { + // this.AI.visible = true + // this.AI.diagnosis = res.data.result + // this.btnAImax(false) + // } else { + // this.$message.error({ showClose: true, message: res.message }) + // } + // }) + + postapi('/api/app/AiMessageWs/GetAIMessageResult', { message }) .then(res => { - if (res.code > -1) { - this.AI.visible = true - this.AI.diagnosis = res.data.result - this.btnAImax(false) - } else { - this.$message.error({ showClose: true, message: res.message }) - } + if (!res) return; + // 移除流结束标志并去掉每行的 'data: ' 前缀 + let cleaned = String(res) + .replace(/data:\s*\[DONE\]/g, '') + .replace(/^data:\s*/gm, '') + .trim(); + if (!cleaned) return; + this.AI.typingIndex = 0; + this.AI.visible = true; + this.AI.rawText = cleaned; + // 确保 typingIndex 在合理范围 + if (!this.AI.typingIndex || this.AI.typingIndex < 0) this.AI.typingIndex = 0; + if (this.AI.typingIndex > this.AI.rawText.length) this.AI.typingIndex = 0; + this.startTyping(); + this.btnAImax(false); }) }, @@ -481,7 +507,30 @@ export default { this.AI.height = 24 } }, - + startTyping() { + if (this.AI.typingTimer) clearInterval(this.AI.typingTimer); + + this.AI.typingTimer = setInterval(() => { + if (this.AI.typingIndex < this.AI.rawText.length) { + const current = this.AI.rawText.slice(0, this.AI.typingIndex); + this.AI.html = md.render(current); + this.AI.typingIndex++; + // 每次更新后滚动到容器底部,确保最新输出可见 + this.$nextTick(() => { + try { + const el = this.$refs.aiContent; + if (el) { + el.scrollTop = el.scrollHeight; + } + } catch (e) { + // ignore + } + }); + } else { + clearInterval(this.AI.typingTimer); + } + }, 30); // 每 30ms 打一个字 + }, //审核 audit() { // dataTransOpts.tableS.patient_register.summaryDoctorId