2 lines
19 KiB
JavaScript
2 lines
19 KiB
JavaScript
import{D as e,d as a,a as l,b as t,c as s,B as n,o,e as i,f as c,C as r,w as u,i as d,j as m,t as f,l as p,m as g,F as h,n as _,k as v,g as b,q as y,y as k,z as w,x,S as $,u as C,I as V,K as D}from"./index-CO9MSRh5.js";import{c as O,_ as F}from"./navigation.CR32hNeX.js";import S from"./pages-diagnosis-diagnosis.66q0uZui.js";import{_ as E}from"./_plugin-vue_export-helper.BCo6x5W8.js";function I(){const a=e("clinical-thinking-access-token");if("string"!=typeof a||!a.trim())throw new Error("登录已过期,请重新登录");return a}function T(e="application/json"){return{"Content-Type":"application/json",Accept:e,Authorization:`Bearer ${I()}`,"X-Entry-Scene":"vue_frontend"}}async function P(e){const a=await e.text().catch((()=>""));if(!a)return`请求失败(${e.status})`;try{const e=JSON.parse(a),l=e.message||e.detail||e.error;if("string"==typeof l&&l.trim())return l}catch{}return a}async function j(e){var a;const l=await fetch("/fastapi/api/v1/sessions",{method:"POST",headers:T(),body:JSON.stringify(e)});if(!l.ok)throw new Error(await P(l));const t=await l.json();if("OK"!==t.code||!(null==(a=t.data)?void 0:a.session_id))throw new Error(t.message||"新建会话失败");return t.data}const A={id:"case-1006004",title:"持续胸痛3小时",patientName:"陈先生",gender:"男",age:60,department:"心血管内科",scene:"住院部",caseNo:"1006004",tone:"orange",mode:"training"};const N=E(a({__name:"chat",props:{caseItem:{}},emits:["open-settings","open-profile","go-home"],setup(a,{emit:E}){const I=a,j=E,N=O(j),U=l({patient:{name:"陈先生",gender:"男",age:60,department:"心血管内科",chiefComplaint:"持续胸痛3小时"},stages:[{key:"history",label:"病史采集",active:!0},{key:"diagnosis",label:"初步诊断",active:!1},{key:"treatment",label:"治疗方案",active:!1}],messages:[]}),J=t(""),q=t(!1),z=t(!1),B=t(0),H=t(""),M=t(!1),R=t(!1),X=t(!1),K=t(!1),G=t(null);let Q=null,L=null,W=null;const Y=[{name:"心电图",result:"检查结果:床边12导联心电图提示窦性心律,II、III、aVF 导联 ST 段抬高,提示下壁急性心肌梗死可能。"},{name:"胸部X线",result:"检查结果:胸部X线未见明显气胸或纵隔明显增宽,心影大小基本正常,不能排除急性冠脉综合征。"},{name:"心脏超声",result:"检查结果:心脏超声提示左室下壁节段性运动减低,未见大量心包积液,需结合心电图及心肌标志物判断。"},{name:"冠脉CTA",result:"检查结果:冠脉CTA提示右冠状动脉近段重度狭窄/闭塞可能,建议结合急诊介入评估。"}],Z=l({temperature:"",pulse:"",respiration:"",bloodPressure:"",spo2:"",complexion:"",examFinding:"",otherFinding:""}),ee=s((()=>U.patient.chiefComplaint.includes("胸痛")?"胸痛":U.patient.chiefComplaint.slice(0,6)));function ae(){K.value=!1,X.value=!0}function le(){X.value=!1,K.value=!0}function te(){const e=[Z.temperature.trim()?`体温 ${Z.temperature.trim()}℃`:"",Z.pulse.trim()?`心率 ${Z.pulse.trim()}次/分`:"",Z.respiration.trim()?`呼吸 ${Z.respiration.trim()}次/分`:"",Z.bloodPressure.trim()?`血压 ${Z.bloodPressure.trim()}mmHg`:"",Z.spo2.trim()?`血氧 ${Z.spo2.trim()}%`:"",Z.complexion.trim()?`意识/面色:${Z.complexion.trim()}`:"",Z.examFinding.trim()?`心肺/腹部查体:${Z.examFinding.trim()}`:"",Z.otherFinding.trim()?`其他发现:${Z.otherFinding.trim()}`:""].filter(Boolean);if(0===e.length)return void re("请至少录入一项体格检查");const a=Date.now(),l=[{id:`doctor-physical-${a}`,role:"doctor",content:`录入体格检查:${e.join(";")}`,label:"我"},{id:`mentor-physical-${a+1}`,role:"mentor",content:se(),label:"AI助手"}];U.messages.push(...l),K.value=!1,Z.temperature="",Z.pulse="",Z.respiration="",Z.bloodPressure="",Z.spo2="",Z.complexion="",Z.examFinding="",Z.otherFinding="",ce()}function se(){const e=Number(Z.pulse),a=Number(Z.respiration),l=Number(Z.spo2),t=Number(Z.temperature),s=[];e>=100&&s.push("心率偏快"),a>=22&&s.push("呼吸频率偏快"),l>0&&l<95&&s.push("血氧偏低"),t>=37.3&&s.push("体温偏高"),(Z.complexion.includes("苍白")||Z.complexion.includes("出汗"))&&s.push("面色/出汗提示急性病容"),(Z.otherFinding.includes("血压差")||Z.otherFinding.includes("双侧"))&&s.push("双侧血压或脉搏差异需警惕主动脉夹层");return`${s.length?`已记录体格检查。当前提示:${s.join("、")}。`:"已记录体格检查,暂未见明确异常体征。"}建议结合胸痛性质、心电图及心肌标志物进一步判断,并持续监测生命体征变化。`}function ne(){const e=J.value.trim();if(e&&!q.value){if(J.value="",G.value)return U.messages.push({id:`doctor-${Date.now()}`,role:"doctor",content:e,label:"我"}),ce(),void oe(e);q.value=!0,function(e){const a=e.trim(),l=a.includes("出冷汗")||a.includes("恶心")?"有,刚才疼得厉害的时候出了一身冷汗,还有点恶心,但没有吐。":a.includes("体格检查")?"患者面色苍白,额部出汗,心率偏快,血压较入院时略低。":a.includes("辅助检查")?"心电图提示下壁导联 ST 段抬高,肌钙蛋白待回报。":"疼痛主要在胸骨后,像压榨一样,休息后也没有明显缓解。";return Promise.resolve([{id:`doctor-${Date.now()}`,role:"doctor",content:a,label:"我"},{id:`patient-${Date.now()+1}`,role:"patient",content:l,label:"患者"},{id:`mentor-${Date.now()+2}`,role:"mentor",content:"很好,继续围绕 OPQRST 思路追问疼痛诱因、部位、性质、放射、持续时间和缓解因素,同时关注危险信号。",label:"王主任"}])}(e).then((e=>{U.messages.push(...e),ce()})).finally((()=>{q.value=!1}))}}async function oe(e){const a=G.value;if(!a)return;q.value=!0,null==L||L.abort(),L=new AbortController;const l=U.messages.length;U.messages.push({id:`patient-stream-${Date.now()}`,role:"patient",content:"",label:"患者"}),ce();try{await async function(e,a,l,t){var s,n,o;const i=await fetch(`/fastapi/api/v1/sessions/${e}/chat/stream`,{method:"POST",headers:T("text/event-stream"),body:JSON.stringify({message:a}),signal:t});if(!i.ok||!i.body)throw new Error(await P(i));const c=i.body.getReader(),r=new TextDecoder;let u="",d=!1;for(;;){const{value:e,done:a}=await c.read();if(a)break;u+=r.decode(e,{stream:!0});const t=u.split("\n\n");u=t.pop()||"";for(const i of t){const e=null==(s=i.match(/^event:\s*(.+)$/m))?void 0:s[1],a=null==(n=i.match(/^data:\s*(.+)$/m))?void 0:n[1];if(!e||!a)continue;const t=JSON.parse(a);if("message_delta"===e){const e=t.delta;"string"==typeof e&&l.onDelta(e)}else if("message_done"===e)d=!0,null==(o=l.onDone)||o.call(l,t);else if("error"===e)throw new Error("string"==typeof t.message?t.message:"AI 流式回复异常")}}if(!d)throw new Error("AI 流式回复未正常结束,请重试")}(a,e,{onDelta(e){U.messages[l].content+=e,ce()}},L.signal)}catch(t){if(t instanceof DOMException&&"AbortError"===t.name)return;U.messages[l].content.trim()||(U.messages[l].content="AI 病人回复失败,请重试。"),re(t instanceof Error?t.message:"AI 流式回复异常")}finally{q.value=!1,L=null,ce()}}async function ie(){const e=G.value;if(!e)return void re("请先生成模拟场景");if(z.value)return;z.value=!0,null==W||W.abort(),W=new AbortController;const a=U.messages.length;U.messages.push({id:`mentor-hint-${Date.now()}`,role:"mentor",content:"王主任正在生成提示...",label:"王主任"}),ce();try{let l=!1;await async function(e,a,l,t){var s,n,o;const i={last_user_message:a,scope:"current_conversation"},c=await fetch(`/fastapi/api/v1/sessions/${e}/hints/stream`,{method:"POST",headers:T("text/event-stream"),body:JSON.stringify(i),signal:t});if(!c.ok||!c.body)throw new Error(await P(c));const r=c.body.getReader(),u=new TextDecoder;let d="",m=!1;for(;;){const{value:e,done:a}=await r.read();if(a)break;d+=u.decode(e,{stream:!0});const t=d.split("\n\n");d=t.pop()||"";for(const i of t){const e=null==(s=i.match(/^event:\s*(.+)$/m))?void 0:s[1],a=null==(n=i.match(/^data:\s*(.+)$/m))?void 0:n[1];if(!e||!a)continue;const t=JSON.parse(a);if("hint_delta"===e){const e=t.delta;"string"==typeof e&&l.onDelta(e)}else if("hint_done"===e)m=!0,null==(o=l.onDone)||o.call(l,t);else if("error"===e)throw new Error("string"==typeof t.message?t.message:"练习提示生成失败,请稍后重试")}}if(!m)throw new Error("练习提示未正常结束,请重试")}(e,function(){const e=[...U.messages].reverse().find((e=>"doctor"===e.role));return(null==e?void 0:e.content)||"开始问诊"}(),{onDelta(e){l||(U.messages[a].content="",l=!0),U.messages[a].content+=e,ce()}},W.signal)}catch(l){if(l instanceof DOMException&&"AbortError"===l.name)return;U.messages[a].content.trim()&&"王主任正在生成提示..."!==U.messages[a].content||(U.messages[a].content="练习提示生成失败,请稍后重试。"),re(l instanceof Error?l.message:"练习提示生成失败,请稍后重试")}finally{z.value=!1,W=null,ce()}}function ce(){setTimeout((()=>{B.value+=1e3}),60)}function re(e){Q&&clearTimeout(Q),H.value=e,M.value=!0,y({title:e,icon:"none"}),Q=setTimeout((()=>{M.value=!1}),2200)}return n((function(){(function(e){const a=e||A,l="毕波涛"===a.patientName?"陈先生":a.patientName;return Promise.resolve({patient:{name:l,gender:a.gender,age:a.age,department:a.department,chiefComplaint:a.title},stages:[{key:"history",label:"病史采集",active:!0},{key:"diagnosis",label:"初步诊断",active:!1},{key:"treatment",label:"治疗方案",active:!1}],messages:[{id:"patient-initial",role:"patient",content:"心血管内科"===a.department?"医生,我心口这儿针扎一样疼了两个小时了,现在感觉喘气都费劲。":`医生,我这次主要是${a.title},有点担心。`,label:"患者"},{id:"mentor-initial",role:"mentor",content:"观察患者的面部表情和生命体征。你的第一个问题应该如何询问,以明确疼痛的性质?",label:"王主任"}]})})(I.caseItem).then((a=>{var l;Object.assign(U.patient,a.patient),U.stages=a.stages;const t=function(){const a=e("clinical-thinking-scenario");return a&&"object"==typeof a?a:null}();if(null==(l=null==t?void 0:t.session)?void 0:l.session_id)return G.value=t.session.session_id,U.messages=t.session.patient_opening?[{id:`patient-opening-${t.session.session_id}`,role:"patient",content:t.session.patient_opening,label:"患者"}]:[],ce(),void oe("开始问诊");U.messages=a.messages,ce()}))})),o((()=>{null==L||L.abort(),null==W||W.abort(),Q&&clearTimeout(Q)})),(e,a)=>{const l=k,t=w,s=x,n=$,o=C,y=V,O=D;return R.value?(i(),c(S,{key:0,"case-item":e.caseItem,onOpenSettings:a[0]||(a[0]=e=>j("open-settings")),onOpenProfile:r(N),onGoHome:a[1]||(a[1]=e=>j("go-home"))},null,8,["case-item","onOpenProfile"])):(i(),c(l,{key:1,class:"chat-page"},{default:u((()=>[d(l,{class:"chat-shell"},{default:u((()=>[d(l,{class:"top-nav"},{default:u((()=>[d(t,{class:"icon-button","aria-label":"设置",onClick:a[2]||(a[2]=e=>j("open-settings"))},{default:u((()=>[d(l,{class:"settings-icon"})])),_:1}),d(t,{class:"icon-button home-button","aria-label":"首页",onClick:a[3]||(a[3]=e=>j("go-home"))},{default:u((()=>[d(l,{class:"home-icon"})])),_:1}),d(l,{class:"nav-spacer"}),d(t,{class:"icon-button","aria-label":"个人中心",onClick:r(N)},{default:u((()=>[d(l,{class:"account-icon"})])),_:1},8,["onClick"])])),_:1}),d(l,{class:"case-header"},{default:u((()=>[d(l,{class:"case-title-row"},{default:u((()=>[d(s,{class:"case-heading"},{default:u((()=>[m("患者:"+f(U.patient.name)+" ("+f(ee.value)+")",1)])),_:1}),d(t,{class:"finish-button",onClick:a[4]||(a[4]=e=>R.value=!0)},{default:u((()=>[d(l,{class:"check-icon"}),d(s,null,{default:u((()=>[m("完成采集")])),_:1})])),_:1})])),_:1}),d(l,{class:"patient-meta"},{default:u((()=>[d(s,null,{default:u((()=>[m("姓名:"+f(U.patient.name),1)])),_:1}),d(s,null,{default:u((()=>[m("性别:"+f(U.patient.gender),1)])),_:1}),d(s,null,{default:u((()=>[m("年龄:"+f(U.patient.age)+"岁",1)])),_:1}),d(s,null,{default:u((()=>[m("科室:"+f(U.patient.department),1)])),_:1})])),_:1})])),_:1}),d(l,{class:"stage-bar"},{default:u((()=>[d(l,{class:"stage-line"}),d(l,{class:"stage-line-active"}),(i(!0),p(h,null,g(U.stages,(e=>(i(),c(l,{key:e.key,class:_(["stage-item",{active:e.active}])},{default:u((()=>[d(l,{class:"stage-dot"},{default:u((()=>[d(l,{class:_(["stage-icon",`stage-icon-${e.key}`])},null,8,["class"])])),_:2},1024),d(s,null,{default:u((()=>[m(f(e.label),1)])),_:2},1024)])),_:2},1032,["class"])))),128))])),_:1}),d(n,{class:"chat-body","scroll-y":"","scroll-top":B.value},{default:u((()=>[d(l,{class:"message-list"},{default:u((()=>[(i(!0),p(h,null,g(U.messages,(e=>(i(),c(l,{key:e.id,class:_(["message-row",[`role-${e.role}`]])},{default:u((()=>["patient"===e.role?(i(),c(l,{key:0,class:"avatar patient-avatar"},{default:u((()=>[d(s,null,{default:u((()=>[m(f(U.patient.name.slice(0,1)),1)])),_:1})])),_:1})):b("",!0),d(l,{class:_(["message-bubble",`${e.role}-bubble`])},{default:u((()=>[d(s,{class:"message-content"},{default:u((()=>[m('"'+f(e.content)+'"',1)])),_:2},1024),d(l,{class:_(["message-label",{mentor:"mentor"===e.role}])},{default:u((()=>["mentor"===e.role?(i(),c(l,{key:0,class:"star-icon"})):b("",!0),d(s,null,{default:u((()=>[m(f(e.label),1)])),_:2},1024)])),_:2},1032,["class"])])),_:2},1032,["class"]),"doctor"===e.role?(i(),c(l,{key:1,class:"avatar doctor-avatar"},{default:u((()=>[d(l,{class:"person-icon"})])),_:1})):b("",!0)])),_:2},1032,["class"])))),128))])),_:1})])),_:1},8,["scroll-top"]),d(l,{class:_(["mentor-float",{loading:z.value}]),onClick:[v(ie,["stop"]),v(ie,["stop"])]},{default:u((()=>[d(s,{class:"mentor-badge"},{default:u((()=>[m("王主任")])),_:1}),d(l,{class:"mentor-avatar",onClick:[v(ie,["stop"]),v(ie,["stop"])]},{default:u((()=>[d(o,{src:F,mode:"aspectFill"})])),_:1})])),_:1},8,["class"]),d(l,{class:"input-panel"},{default:u((()=>[d(n,{class:"quick-actions","scroll-x":""},{default:u((()=>[d(l,{class:"quick-row"},{default:u((()=>[d(t,{class:"quick-chip",onClick:le},{default:u((()=>[m("体格检查")])),_:1}),d(t,{class:"quick-chip",onClick:ae},{default:u((()=>[m("辅助检查")])),_:1})])),_:1})])),_:1}),d(l,{class:"input-row"},{default:u((()=>[d(y,{class:"chat-input",modelValue:J.value,"onUpdate:modelValue":a[5]||(a[5]=e=>J.value=e),type:"text",placeholder:"输入你对患者的提问...","placeholder-class":"input-placeholder",onConfirm:ne},null,8,["modelValue"]),d(t,{class:"send-button",disabled:q.value,onClick:ne},{default:u((()=>[d(l,{class:"send-icon"})])),_:1},8,["disabled"])])),_:1})])),_:1})])),_:1}),K.value?(i(),c(l,{key:0,class:"exam-mask",onClick:a[17]||(a[17]=e=>K.value=!1)},{default:u((()=>[d(l,{class:"physical-panel",onClick:a[16]||(a[16]=v((()=>{}),["stop"]))},{default:u((()=>[d(l,{class:"exam-header"},{default:u((()=>[d(s,{class:"exam-title"},{default:u((()=>[m("录入体格检查")])),_:1}),d(t,{class:"exam-close","aria-label":"关闭",onClick:a[6]||(a[6]=e=>K.value=!1)},{default:u((()=>[d(l,{class:"close-icon"})])),_:1})])),_:1}),d(n,{class:"physical-form","scroll-y":""},{default:u((()=>[d(l,{class:"vital-grid"},{default:u((()=>[d(l,{class:"form-field"},{default:u((()=>[d(s,{class:"field-label"},{default:u((()=>[m("体温")])),_:1}),d(y,{class:"field-input",modelValue:Z.temperature,"onUpdate:modelValue":a[7]||(a[7]=e=>Z.temperature=e),type:"digit",placeholder:"36.8","placeholder-class":"field-placeholder"},null,8,["modelValue"]),d(s,{class:"field-unit"},{default:u((()=>[m("℃")])),_:1})])),_:1}),d(l,{class:"form-field"},{default:u((()=>[d(s,{class:"field-label"},{default:u((()=>[m("心率")])),_:1}),d(y,{class:"field-input",modelValue:Z.pulse,"onUpdate:modelValue":a[8]||(a[8]=e=>Z.pulse=e),type:"number",placeholder:"98","placeholder-class":"field-placeholder"},null,8,["modelValue"]),d(s,{class:"field-unit"},{default:u((()=>[m("次/分")])),_:1})])),_:1}),d(l,{class:"form-field"},{default:u((()=>[d(s,{class:"field-label"},{default:u((()=>[m("呼吸")])),_:1}),d(y,{class:"field-input",modelValue:Z.respiration,"onUpdate:modelValue":a[9]||(a[9]=e=>Z.respiration=e),type:"number",placeholder:"22","placeholder-class":"field-placeholder"},null,8,["modelValue"]),d(s,{class:"field-unit"},{default:u((()=>[m("次/分")])),_:1})])),_:1}),d(l,{class:"form-field"},{default:u((()=>[d(s,{class:"field-label"},{default:u((()=>[m("血氧")])),_:1}),d(y,{class:"field-input",modelValue:Z.spo2,"onUpdate:modelValue":a[10]||(a[10]=e=>Z.spo2=e),type:"number",placeholder:"96","placeholder-class":"field-placeholder"},null,8,["modelValue"]),d(s,{class:"field-unit"},{default:u((()=>[m("%")])),_:1})])),_:1})])),_:1}),d(l,{class:"form-field full"},{default:u((()=>[d(s,{class:"field-label"},{default:u((()=>[m("血压")])),_:1}),d(y,{class:"field-input",modelValue:Z.bloodPressure,"onUpdate:modelValue":a[11]||(a[11]=e=>Z.bloodPressure=e),type:"text",placeholder:"138/86","placeholder-class":"field-placeholder"},null,8,["modelValue"]),d(s,{class:"field-unit"},{default:u((()=>[m("mmHg")])),_:1})])),_:1}),d(l,{class:"form-field full"},{default:u((()=>[d(s,{class:"field-label"},{default:u((()=>[m("意识/面色")])),_:1}),d(y,{class:"field-input",modelValue:Z.complexion,"onUpdate:modelValue":a[12]||(a[12]=e=>Z.complexion=e),type:"text",placeholder:"清醒,面色苍白,出汗","placeholder-class":"field-placeholder"},null,8,["modelValue"])])),_:1}),d(l,{class:"form-field textarea-field"},{default:u((()=>[d(s,{class:"field-label"},{default:u((()=>[m("心肺/腹部查体")])),_:1}),d(O,{class:"field-textarea",modelValue:Z.examFinding,"onUpdate:modelValue":a[13]||(a[13]=e=>Z.examFinding=e),placeholder:"心音、肺部啰音、腹部压痛等","placeholder-class":"field-placeholder"},null,8,["modelValue"])])),_:1}),d(l,{class:"form-field textarea-field"},{default:u((()=>[d(s,{class:"field-label"},{default:u((()=>[m("其他发现")])),_:1}),d(O,{class:"field-textarea",modelValue:Z.otherFinding,"onUpdate:modelValue":a[14]||(a[14]=e=>Z.otherFinding=e),placeholder:"如双侧血压差、下肢水肿、神经系统体征等","placeholder-class":"field-placeholder"},null,8,["modelValue"])])),_:1})])),_:1}),d(l,{class:"physical-actions"},{default:u((()=>[d(t,{class:"physical-cancel",onClick:a[15]||(a[15]=e=>K.value=!1)},{default:u((()=>[m("取消")])),_:1}),d(t,{class:"physical-submit",onClick:te},{default:u((()=>[m("提交检查结果")])),_:1})])),_:1})])),_:1})])),_:1})):b("",!0),X.value?(i(),c(l,{key:1,class:"exam-mask",onClick:a[20]||(a[20]=e=>X.value=!1)},{default:u((()=>[d(l,{class:"exam-panel",onClick:a[19]||(a[19]=v((()=>{}),["stop"]))},{default:u((()=>[d(l,{class:"exam-header"},{default:u((()=>[d(s,{class:"exam-title"},{default:u((()=>[m("选择辅助检查")])),_:1}),d(t,{class:"exam-close","aria-label":"关闭",onClick:a[18]||(a[18]=e=>X.value=!1)},{default:u((()=>[d(l,{class:"close-icon"})])),_:1})])),_:1}),d(l,{class:"exam-list"},{default:u((()=>[(i(),p(h,null,g(Y,(e=>d(t,{key:e.name,class:"exam-item",onClick:a=>function(e){X.value=!1;const a=Date.now(),l=[{id:`doctor-exam-${a}`,role:"doctor",content:`选择辅助检查:${e.name}`,label:"我"},{id:`mentor-exam-${a+1}`,role:"mentor",content:e.result,label:"AI助手"}];U.messages.push(...l),ce()}(e)},{default:u((()=>[d(s,{class:"exam-name"},{default:u((()=>[m(f(e.name),1)])),_:2},1024),d(l,{class:"chevron-icon"})])),_:2},1032,["onClick"]))),64))])),_:1})])),_:1})])),_:1})):b("",!0),d(l,{class:_(["toast",{visible:M.value}])},{default:u((()=>[m(f(H.value),1)])),_:1},8,["class"])])),_:1}))}}}),[["__scopeId","data-v-11043b4d"]]),U=Object.freeze(Object.defineProperty({__proto__:null,default:N},Symbol.toStringTag,{value:"Module"}));export{N as C,U as a,j as c};
|