feat: 修改ai学习助手接口

This commit is contained in:
王天骄
2026-06-24 10:50:58 +08:00
parent 89f6d06710
commit 92eba5adc8
30 changed files with 207 additions and 47 deletions
+115
View File
@@ -5,6 +5,18 @@ export type LearningAssistantSession = {
title?: string title?: string
} }
type WorkflowRunResponse = {
workflow_run_id?: string
task_id?: string
data?: {
status?: string
outputs?: unknown
error?: string
[key: string]: unknown
}
[key: string]: unknown
}
type ChatStreamPayload = { type ChatStreamPayload = {
question: string question: string
top_k?: number top_k?: number
@@ -16,6 +28,12 @@ type StreamCallbacks = {
onDone?: (meta: Record<string, unknown>) => void onDone?: (meta: Record<string, unknown>) => void
} }
const CLINICAL_BOOK_WORKFLOW_URL = import.meta.env.DEV
? '/dify/v1/workflows/run'
: 'http://8.160.178.88:8088/v1/workflows/run'
const CLINICAL_BOOK_WORKFLOW_API_KEY = 'app-9BapxGXgF2qQi9HHkq4R95Y7'
const CLINICAL_BOOK_WORKFLOW_TRIGGER = '临床思维训练书籍'
export async function createLearningAssistantSession(title?: string) { export async function createLearningAssistantSession(title?: string) {
const body = title?.trim() const body = title?.trim()
? { ? {
@@ -49,6 +67,41 @@ export async function createLearningAssistantSession(title?: string) {
} as LearningAssistantSession } as LearningAssistantSession
} }
export async function runClinicalThinkingBooksWorkflow(signal?: AbortSignal) {
const response = await fetch(CLINICAL_BOOK_WORKFLOW_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Bearer ${CLINICAL_BOOK_WORKFLOW_API_KEY}`
},
body: JSON.stringify({
inputs: {
input: CLINICAL_BOOK_WORKFLOW_TRIGGER
},
response_mode: 'blocking',
user: readWorkflowUserId()
}),
signal
})
if (!response.ok) {
throw new Error(await readWorkflowError(response))
}
const result = (await response.json()) as WorkflowRunResponse
if (result.data?.status && result.data.status !== 'succeeded') {
throw new Error(result.data.error || '临床思维训练书籍生成失败')
}
const output = readWorkflowOutput(result)
if (!output) {
throw new Error('临床思维训练书籍内容为空,请稍后重试')
}
return output
}
export async function streamLearningAssistantChat( export async function streamLearningAssistantChat(
assistantSessionId: number | string, assistantSessionId: number | string,
payload: ChatStreamPayload, payload: ChatStreamPayload,
@@ -94,6 +147,68 @@ function readAssistantSessionId(data: Record<string, unknown>) {
return '' return ''
} }
function readWorkflowUserId() {
const storageKey = 'clinical-thinking-workflow-user-id'
const stored = uni.getStorageSync(storageKey)
if (typeof stored === 'string' && stored.trim()) return stored
const generated = `clinical-thinking-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
uni.setStorageSync(storageKey, generated)
return generated
}
async function readWorkflowError(response: Response) {
const text = await response.text().catch(() => '')
if (!text) return `临床思维训练书籍请求失败(${response.status}`
try {
const payload = JSON.parse(text) as Record<string, unknown>
const message = payload.message || payload.detail || payload.error
if (typeof message === 'string' && message.trim()) return message
} catch {}
return text
}
function readWorkflowOutput(result: WorkflowRunResponse) {
const data = result.data
if (data && typeof data === 'object') {
const output = readOutputText(data.outputs)
|| readOutputText(data.output)
|| readOutputText(data.answer)
|| readOutputText(data.text)
|| readOutputText(data.result)
if (output) return output
}
return readOutputText(result.output)
|| readOutputText(result.answer)
|| readOutputText(result.text)
|| readOutputText(result.result)
}
function readOutputText(value: unknown): string {
if (typeof value === 'string') return value.trim()
if (Array.isArray(value)) {
return value.map(readOutputText).filter(Boolean).join('\n')
}
if (!value || typeof value !== 'object') return ''
const payload = value as Record<string, unknown>
const preferredKeys = ['answer', 'text', 'result', 'output', 'content', 'markdown', 'data']
for (const key of preferredKeys) {
const text = readOutputText(payload[key])
if (text) return text
}
const entries = Object.entries(payload)
.map(([key, entry]) => {
const text = readOutputText(entry)
return text ? `${key}${text}` : ''
})
.filter(Boolean)
return entries.join('\n')
}
function handleStreamBlock(block: string, callbacks: StreamCallbacks) { function handleStreamBlock(block: string, callbacks: StreamCallbacks) {
const event = block.match(/^event:\s*(.+)$/m)?.[1] || '' const event = block.match(/^event:\s*(.+)$/m)?.[1] || ''
const rawLines = block const rawLines = block
@@ -1 +1 @@
import{C as t,D as e}from"./index-CqsCatmH.js";const n="/server/api";class r extends Error{constructor(t,e,n){super(t),this.name="ApiRequestError",this.code=e,this.statusCode=n}}function o(t=!1){const n={"Content-Type":"application/json"};if(!t)return n;const r=function(){try{return e("clinical-thinking-access-token")||""}catch{return""}}();return r&&(n.Authorization=`Bearer ${r}`),n}function s(e,n,s="POST",i=!1){return new Promise(((u,a)=>{t({url:`/server/api${e}`,method:s,timeout:1e4,header:o(i),data:n,success:t=>{if(t.statusCode>=200&&t.statusCode<300)return void u(t.data);const e=t.data,n="string"==typeof(null==e?void 0:e.code)?e.code:void 0;a(new r(function(t,e){if(t&&"object"==typeof t){const e=t,n=e.message||e.detail||e.error;if("string"==typeof n&&n.trim())return n}return e}(t.data,`请求失败(${t.statusCode}`),n,t.statusCode))},fail:t=>{a(new r(t.errMsg||"无法连接服务"))}})}))}function i(t,e="login"){return s("/user/auth/send-code/",{phone:t,scene:e})}function u(){return s("/user/institution_list/",null,"GET")}function a(){return s("/user/institution_info/",null,"GET",!0)}function c(){return s("/user/my_departments/",null,"GET",!0)}function f(t){return s("/user/auth/login-code/",t).then((t=>{if(function(t){if(!t||"object"!=typeof t)return!1;const e=t,n=e.tokens;return Boolean(e.tokens&&"string"==typeof(null==n?void 0:n.access)&&"string"==typeof(null==n?void 0:n.refresh))}(t))return t;throw new Error("登录接口返回数据格式异常")}))}export{r as A,n as a,c as b,a as c,u as f,f as l,i as s}; import{C as t,D as e}from"./index-DVBIT0nD.js";const n="/server/api";class r extends Error{constructor(t,e,n){super(t),this.name="ApiRequestError",this.code=e,this.statusCode=n}}function o(t=!1){const n={"Content-Type":"application/json"};if(!t)return n;const r=function(){try{return e("clinical-thinking-access-token")||""}catch{return""}}();return r&&(n.Authorization=`Bearer ${r}`),n}function s(e,n,s="POST",i=!1){return new Promise(((u,a)=>{t({url:`/server/api${e}`,method:s,timeout:1e4,header:o(i),data:n,success:t=>{if(t.statusCode>=200&&t.statusCode<300)return void u(t.data);const e=t.data,n="string"==typeof(null==e?void 0:e.code)?e.code:void 0;a(new r(function(t,e){if(t&&"object"==typeof t){const e=t,n=e.message||e.detail||e.error;if("string"==typeof n&&n.trim())return n}return e}(t.data,`请求失败(${t.statusCode}`),n,t.statusCode))},fail:t=>{a(new r(t.errMsg||"无法连接服务"))}})}))}function i(t,e="login"){return s("/user/auth/send-code/",{phone:t,scene:e})}function u(){return s("/user/institution_list/",null,"GET")}function a(){return s("/user/institution_info/",null,"GET",!0)}function c(){return s("/user/my_departments/",null,"GET",!0)}function f(t){return s("/user/auth/login-code/",t).then((t=>{if(function(t){if(!t||"object"!=typeof t)return!1;const e=t,n=e.tokens;return Boolean(e.tokens&&"string"==typeof(null==n?void 0:n.access)&&"string"==typeof(null==n?void 0:n.refresh))}(t))return t;throw new Error("登录接口返回数据格式异常")}))}export{r as A,n as a,c as b,a as c,u as f,f as l,i as s};
@@ -1 +1 @@
import{D as e,s as t,P as n,C as r}from"./index-CqsCatmH.js";import{a as i,A as c}from"./auth.DAQZ1Xvg.js";const a={recommended:"/case/mobile/recommended/",specialty:"/case/mobile/specialty/",weak:"/case/mobile/weak/",teaching:"/case/mobile/teaching/","teacher-task":"/case/mobile/teacher-task/"},o=["blue","teal","pink","orange","purple","green"];async function s(e="recommended",t={}){const n=a[e]||a.recommended,s=await function(e,t){return new Promise(((n,a)=>{r({url:`${i}${y(e,t)}`,method:"GET",timeout:1e4,header:f(),success:e=>{if(e.statusCode>=200&&e.statusCode<300)return void n(e.data);const t=e.data,r="string"==typeof(null==t?void 0:t.code)?t.code:void 0;a(new c(function(e,t){if(e&&"object"==typeof e){const t=e,n=t.message||t.detail||t.error;if("string"==typeof n&&n.trim())return n}return t}(e.data,`病例列表加载失败(${e.statusCode}`),r,e.statusCode))},fail:e=>{a(new c(e.errMsg||"无法连接服务"))}})}))}(n,t),u=function(e,t){const n=Array.isArray(e.results)?e.results:[];return{count:b(e.count,n.length),next:"string"==typeof e.next?e.next:null,previous:"string"==typeof e.previous?e.previous:null,results:n.map(((e,n)=>function(e,t,n){const r=h(e.id,`case-${n+1}`),i=h(e.title,"未命名病例"),c=(p=e.patient_gender,"female"===p||"女"===p?"女":"男"),a=h(e.department_name,"未配置科室"),s=h(e.case_type),u=h(e.case_type_display,s||"练习病例"),l=h(e.chief_complaint,i),m="teaching"===t||"teaching"===s?"teaching":"training";var p;return{id:r,title:i,patientName:"女"===c?"患者女士":"患者先生",gender:c,age:b(e.patient_age,0),department:a,departmentId:_(e.department),scene:u,caseNo:r,tone:o[n%o.length],mode:m,caseType:s,caseTypeDisplay:u,difficulty:h(e.difficulty),difficultyScore:_(e.difficulty_score),chiefComplaint:l,description:h(e.description),tags:h(e.tags),competencyTags:Array.isArray(e.competency_tags)?e.competency_tags.map(String):[],estimatedMinutes:_(e.estimated_minutes),osceEnabled:Boolean(e.osce_enabled),myBestScore:"number"==typeof e.my_best_score?e.my_best_score:null,myTrainCount:"number"==typeof e.my_train_count?e.my_train_count:null,createdAt:h(e.created_at),source:t}}(e,t,n)))}}(s,e);return u}function u(e){t("clinical-thinking-prefetched-case-list",e)}function l(){n("clinical-thinking-prefetched-case-list")}function m(t,n){const r=e("clinical-thinking-prefetched-case-list");if(!function(e){if(!e||"object"!=typeof e)return!1;const t=e;return function(e){return"recommended"===e||"specialty"===e||"weak"===e||"teaching"===e||"teacher-task"===e}(t.source)&&"object"==typeof t.query&&null!==t.query&&!!t.page&&"object"==typeof t.page&&Array.isArray(t.page.results)&&"number"==typeof t.cachedAt}(r))return null;return Date.now()-r.cachedAt<=6e4?r.source===t&&(i=r.query,c=n,["search","case_type","difficulty","department","page","page_size"].every((e=>g(i[e])===g(c[e]))))?(l(),r.page):null:(l(),null);var i,c}function p(){const t=e("clinical-thinking-selected-case");return t&&"object"==typeof t?t:null}function d(e){if(!e)return 0;const t=Number(e.id);if(Number.isInteger(t)&&t>0)return t;const n=Number(e.caseNo);if(Number.isInteger(n)&&n>0)return n;const r=String(e.id).match(/\d+/),i=r?Number(r[0]):0;return Number.isInteger(i)&&i>0?i:0}function f(){const t={"Content-Type":"application/json",Accept:"application/json"},n=function(){try{const t=e("clinical-thinking-access-token");return"string"==typeof t?t.trim():""}catch{return""}}();return n&&(t.Authorization=`Bearer ${n}`),t}function y(e,t){const n=Object.entries(t).filter((([,e])=>null!=e&&""!==e)).map((([e,t])=>`${encodeURIComponent(e)}=${encodeURIComponent(String(t))}`)).join("&");return n?`${e}?${n}`:e}function g(e){return null==e||""===e?"":String(e)}function h(e,t=""){return"string"==typeof e?e.trim()||t:"number"==typeof e?String(e):t}function b(e,t=0){const n=Number(e);return Number.isFinite(n)?n:t}function _(e){const t=Number(e);return Number.isFinite(t)?t:void 0}export{d as a,l as c,s as f,p as r,u as s,m as t}; import{D as e,s as t,P as n,C as r}from"./index-DVBIT0nD.js";import{a as i,A as c}from"./auth.BCevMxOR.js";const a={recommended:"/case/mobile/recommended/",specialty:"/case/mobile/specialty/",weak:"/case/mobile/weak/",teaching:"/case/mobile/teaching/","teacher-task":"/case/mobile/teacher-task/"},o=["blue","teal","pink","orange","purple","green"];async function s(e="recommended",t={}){const n=a[e]||a.recommended,s=await function(e,t){return new Promise(((n,a)=>{r({url:`${i}${y(e,t)}`,method:"GET",timeout:1e4,header:f(),success:e=>{if(e.statusCode>=200&&e.statusCode<300)return void n(e.data);const t=e.data,r="string"==typeof(null==t?void 0:t.code)?t.code:void 0;a(new c(function(e,t){if(e&&"object"==typeof e){const t=e,n=t.message||t.detail||t.error;if("string"==typeof n&&n.trim())return n}return t}(e.data,`病例列表加载失败(${e.statusCode}`),r,e.statusCode))},fail:e=>{a(new c(e.errMsg||"无法连接服务"))}})}))}(n,t),u=function(e,t){const n=Array.isArray(e.results)?e.results:[];return{count:b(e.count,n.length),next:"string"==typeof e.next?e.next:null,previous:"string"==typeof e.previous?e.previous:null,results:n.map(((e,n)=>function(e,t,n){const r=h(e.id,`case-${n+1}`),i=h(e.title,"未命名病例"),c=(p=e.patient_gender,"female"===p||"女"===p?"女":"男"),a=h(e.department_name,"未配置科室"),s=h(e.case_type),u=h(e.case_type_display,s||"练习病例"),l=h(e.chief_complaint,i),m="teaching"===t||"teaching"===s?"teaching":"training";var p;return{id:r,title:i,patientName:"女"===c?"患者女士":"患者先生",gender:c,age:b(e.patient_age,0),department:a,departmentId:_(e.department),scene:u,caseNo:r,tone:o[n%o.length],mode:m,caseType:s,caseTypeDisplay:u,difficulty:h(e.difficulty),difficultyScore:_(e.difficulty_score),chiefComplaint:l,description:h(e.description),tags:h(e.tags),competencyTags:Array.isArray(e.competency_tags)?e.competency_tags.map(String):[],estimatedMinutes:_(e.estimated_minutes),osceEnabled:Boolean(e.osce_enabled),myBestScore:"number"==typeof e.my_best_score?e.my_best_score:null,myTrainCount:"number"==typeof e.my_train_count?e.my_train_count:null,createdAt:h(e.created_at),source:t}}(e,t,n)))}}(s,e);return u}function u(e){t("clinical-thinking-prefetched-case-list",e)}function l(){n("clinical-thinking-prefetched-case-list")}function m(t,n){const r=e("clinical-thinking-prefetched-case-list");if(!function(e){if(!e||"object"!=typeof e)return!1;const t=e;return function(e){return"recommended"===e||"specialty"===e||"weak"===e||"teaching"===e||"teacher-task"===e}(t.source)&&"object"==typeof t.query&&null!==t.query&&!!t.page&&"object"==typeof t.page&&Array.isArray(t.page.results)&&"number"==typeof t.cachedAt}(r))return null;return Date.now()-r.cachedAt<=6e4?r.source===t&&(i=r.query,c=n,["search","case_type","difficulty","department","page","page_size"].every((e=>g(i[e])===g(c[e]))))?(l(),r.page):null:(l(),null);var i,c}function p(){const t=e("clinical-thinking-selected-case");return t&&"object"==typeof t?t:null}function d(e){if(!e)return 0;const t=Number(e.id);if(Number.isInteger(t)&&t>0)return t;const n=Number(e.caseNo);if(Number.isInteger(n)&&n>0)return n;const r=String(e.id).match(/\d+/),i=r?Number(r[0]):0;return Number.isInteger(i)&&i>0?i:0}function f(){const t={"Content-Type":"application/json",Accept:"application/json"},n=function(){try{const t=e("clinical-thinking-access-token");return"string"==typeof t?t.trim():""}catch{return""}}();return n&&(t.Authorization=`Bearer ${n}`),t}function y(e,t){const n=Object.entries(t).filter((([,e])=>null!=e&&""!==e)).map((([e,t])=>`${encodeURIComponent(e)}=${encodeURIComponent(String(t))}`)).join("&");return n?`${e}?${n}`:e}function g(e){return null==e||""===e?"":String(e)}function h(e,t=""){return"string"==typeof e?e.trim()||t:"number"==typeof e?String(e):t}function b(e,t=0){const n=Number(e);return Number.isFinite(n)?n:t}function _(e){const t=Number(e);return Number.isFinite(t)?t:void 0}export{d as a,l as c,s as f,p as r,u as s,m as t};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{G as o,q as n,Q as e}from"./index-CqsCatmH.js";function i(n){var i;const l=e(),r=Boolean(null==(i=null==l?void 0:l.vnode.props)?void 0:i.onOpenProfile);return function(){r?n("open-profile"):o({url:"/pages/profile/profile"})}}function l(n){var i;const l=e(),r=Boolean(null==(i=null==l?void 0:l.vnode.props)?void 0:i.onOpenSettings);return function(){r?n("open-settings"):o({url:"/pages/config/config"})}}function r(o){var i;const l=e(),r=Boolean(null==(i=null==l?void 0:l.vnode.props)?void 0:i.onGoHome);return function(){r?o("go-home"):n({url:"/pages/home/home"})}}export{l as a,r as b,i as c}; import{G as o,q as n,Q as e}from"./index-DVBIT0nD.js";function i(n){var i;const l=e(),r=Boolean(null==(i=null==l?void 0:l.vnode.props)?void 0:i.onOpenProfile);return function(){r?n("open-profile"):o({url:"/pages/profile/profile"})}}function l(n){var i;const l=e(),r=Boolean(null==(i=null==l?void 0:l.vnode.props)?void 0:i.onOpenSettings);return function(){r?n("open-settings"):o({url:"/pages/config/config"})}}function r(o){var i;const l=e(),r=Boolean(null==(i=null==l?void 0:l.vnode.props)?void 0:i.onGoHome);return function(){r?o("go-home"):n({url:"/pages/home/home"})}}export{l as a,r as b,i as c};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{d as s,a,r as e,o as l,b as t,e as o,f as i,w as c,i as n,E as u,j as r,g as d,t as m,l as f,m as g,F as _,n as b,s as p,G as h,z as v,A as k,y,x as C}from"./index-CqsCatmH.js";import{_ as j}from"./config-doctor.TgARj_nM.js";import{c as w,a as I}from"./navigation.C2MLAOdv.js";import{_ as x}from"./_plugin-vue_export-helper.BCo6x5W8.js";const T=x(s({__name:"home",emits:["open-settings","open-profile"],setup(s,{emit:x}){const T=x,A=w(T),M=I(T),D=a({greeting:"下午好,医生。",highlight:"让我们继续提升您的临床思维能力吧。",remainingModules:3,doctorName:"王主任"}),E=[{title:"精准补强·薄弱环节训练",icon:"trend-icon",source:"weak"},{title:"实战进阶·科室专项训练",icon:"notes-icon",source:"specialty"},{title:"新手入门·教学互动模式训练",icon:"school-icon",source:"teaching"},{title:"精益管理·老师针对性任务训练",icon:"admin-icon",source:"teacher-task"}],F=e(!1),N=e(""),O=e(!1);let P=null;function S(){F.value||(F.value=!0,Promise.resolve({sessionId:`mock-session-${Date.now()}`,startedAt:(new Date).toISOString()}).then((s=>{p("clinical-thinking-session",s),h({url:"/pages/matching/matching?source=recommended"})})).catch((s=>{!function(s){P&&clearTimeout(P);N.value=s,O.value=!0,P=setTimeout((()=>{O.value=!1}),2200)}(s instanceof Error?s.message:"进入训练失败")})).finally((()=>{setTimeout((()=>{F.value=!1}),300)})))}function $(){h({url:"/pages/learning-assistant/learning-assistant"})}return l((function(){Promise.resolve({greeting:"下午好,医生。",highlight:"让我们继续提升您的临床思维能力吧。",remainingModules:3,doctorName:"王主任"}).then((s=>{Object.assign(D,s)}))})),t((()=>{P&&clearTimeout(P)})),(s,a)=>{const e=v,l=k,t=y,p=C;return o(),i(e,{class:"home-page"},{default:c((()=>[n(e,{class:"home-shell"},{default:c((()=>[n(e,{class:"top-bar"},{default:c((()=>[n(l,{class:"icon-button","aria-label":"配置",onClick:u(M)},{default:c((()=>[n(e,{class:"settings-icon"})])),_:1},8,["onClick"]),n(e,{class:"top-spacer"}),n(l,{class:"icon-button","aria-label":"个人中心",onClick:u(A)},{default:c((()=>[n(e,{class:"account-icon"})])),_:1},8,["onClick"])])),_:1}),n(e,{class:"home-main"},{default:c((()=>[n(e,{class:"speech-bubble"},{default:c((()=>[n(t,{class:"bubble-copy"},{default:c((()=>[r("下午好,医生。准备好开始今天的")])),_:1}),n(t,{class:"bubble-strong"},{default:c((()=>[r("带教模拟")])),_:1}),n(t,{class:"bubble-copy"},{default:c((()=>[r(",精进")])),_:1}),n(t,{class:"bubble-highlight"},{default:c((()=>[r("临床思维")])),_:1}),n(t,{class:"bubble-copy"},{default:c((()=>[r("了吗?")])),_:1})])),_:1}),n(e,{class:"doctor-stage"},{default:c((()=>[n(e,{class:"doctor-shadow"}),n(p,{class:"director-image",src:j,mode:"aspectFit"})])),_:1}),n(e,{class:"training-panel"},{default:c((()=>[n(e,{class:"primary-action"},{default:c((()=>[n(l,{class:"start-button",disabled:F.value,onClick:S},{default:c((()=>[F.value?(o(),i(e,{key:0,class:"spinner"})):d("",!0),n(t,null,{default:c((()=>[r(m(F.value?"正在进入...":"开始训练"),1)])),_:1})])),_:1},8,["disabled"]),n(t,{class:"remaining"},{default:c((()=>[r("今日剩余:"+m(D.remainingModules)+"个模块",1)])),_:1})])),_:1}),n(e,{class:"module-grid"},{default:c((()=>[(o(),f(_,null,g(E,(s=>n(l,{key:s.title,class:"module-card",onClick:a=>{return e=s.source,void h({url:`/pages/cases/cases?source=${encodeURIComponent(e)}`});var e}},{default:c((()=>[n(e,{class:b(["module-icon",s.icon])},null,8,["class"]),n(t,{class:"module-title"},{default:c((()=>[r(m(s.title),1)])),_:2},1024)])),_:2},1032,["onClick"]))),64))])),_:1}),n(e,{class:"assistant-actions"},{default:c((()=>[n(l,{class:"assistant-button",onClick:$},{default:c((()=>[n(e,{class:"assistant-icon chat-icon"}),n(t,null,{default:c((()=>[r("AI 学习助手(医院知识库)")])),_:1})])),_:1})])),_:1})])),_:1})])),_:1})])),_:1}),n(e,{class:b(["toast",{visible:O.value}])},{default:c((()=>[r(m(N.value),1)])),_:1},8,["class"])])),_:1})}}}),[["__scopeId","data-v-f45eae37"]]);export{T as default}; import{d as s,a,r as e,o as l,b as t,e as o,f as i,w as c,i as n,E as u,j as r,g as d,t as m,l as f,m as g,F as _,n as b,s as p,G as h,z as v,A as k,y,x as C}from"./index-DVBIT0nD.js";import{_ as j}from"./config-doctor.TgARj_nM.js";import{c as w,a as I}from"./navigation.RN4abuPi.js";import{_ as x}from"./_plugin-vue_export-helper.BCo6x5W8.js";const T=x(s({__name:"home",emits:["open-settings","open-profile"],setup(s,{emit:x}){const T=x,A=w(T),M=I(T),D=a({greeting:"下午好,医生。",highlight:"让我们继续提升您的临床思维能力吧。",remainingModules:3,doctorName:"王主任"}),E=[{title:"精准补强·薄弱环节训练",icon:"trend-icon",source:"weak"},{title:"实战进阶·科室专项训练",icon:"notes-icon",source:"specialty"},{title:"新手入门·教学互动模式训练",icon:"school-icon",source:"teaching"},{title:"精益管理·老师针对性任务训练",icon:"admin-icon",source:"teacher-task"}],F=e(!1),N=e(""),O=e(!1);let P=null;function S(){F.value||(F.value=!0,Promise.resolve({sessionId:`mock-session-${Date.now()}`,startedAt:(new Date).toISOString()}).then((s=>{p("clinical-thinking-session",s),h({url:"/pages/matching/matching?source=recommended"})})).catch((s=>{!function(s){P&&clearTimeout(P);N.value=s,O.value=!0,P=setTimeout((()=>{O.value=!1}),2200)}(s instanceof Error?s.message:"进入训练失败")})).finally((()=>{setTimeout((()=>{F.value=!1}),300)})))}function $(){h({url:"/pages/learning-assistant/learning-assistant"})}return l((function(){Promise.resolve({greeting:"下午好,医生。",highlight:"让我们继续提升您的临床思维能力吧。",remainingModules:3,doctorName:"王主任"}).then((s=>{Object.assign(D,s)}))})),t((()=>{P&&clearTimeout(P)})),(s,a)=>{const e=v,l=k,t=y,p=C;return o(),i(e,{class:"home-page"},{default:c((()=>[n(e,{class:"home-shell"},{default:c((()=>[n(e,{class:"top-bar"},{default:c((()=>[n(l,{class:"icon-button","aria-label":"配置",onClick:u(M)},{default:c((()=>[n(e,{class:"settings-icon"})])),_:1},8,["onClick"]),n(e,{class:"top-spacer"}),n(l,{class:"icon-button","aria-label":"个人中心",onClick:u(A)},{default:c((()=>[n(e,{class:"account-icon"})])),_:1},8,["onClick"])])),_:1}),n(e,{class:"home-main"},{default:c((()=>[n(e,{class:"speech-bubble"},{default:c((()=>[n(t,{class:"bubble-copy"},{default:c((()=>[r("下午好,医生。准备好开始今天的")])),_:1}),n(t,{class:"bubble-strong"},{default:c((()=>[r("带教模拟")])),_:1}),n(t,{class:"bubble-copy"},{default:c((()=>[r(",精进")])),_:1}),n(t,{class:"bubble-highlight"},{default:c((()=>[r("临床思维")])),_:1}),n(t,{class:"bubble-copy"},{default:c((()=>[r("了吗?")])),_:1})])),_:1}),n(e,{class:"doctor-stage"},{default:c((()=>[n(e,{class:"doctor-shadow"}),n(p,{class:"director-image",src:j,mode:"aspectFit"})])),_:1}),n(e,{class:"training-panel"},{default:c((()=>[n(e,{class:"primary-action"},{default:c((()=>[n(l,{class:"start-button",disabled:F.value,onClick:S},{default:c((()=>[F.value?(o(),i(e,{key:0,class:"spinner"})):d("",!0),n(t,null,{default:c((()=>[r(m(F.value?"正在进入...":"开始训练"),1)])),_:1})])),_:1},8,["disabled"]),n(t,{class:"remaining"},{default:c((()=>[r("今日剩余:"+m(D.remainingModules)+"个模块",1)])),_:1})])),_:1}),n(e,{class:"module-grid"},{default:c((()=>[(o(),f(_,null,g(E,(s=>n(l,{key:s.title,class:"module-card",onClick:a=>{return e=s.source,void h({url:`/pages/cases/cases?source=${encodeURIComponent(e)}`});var e}},{default:c((()=>[n(e,{class:b(["module-icon",s.icon])},null,8,["class"]),n(t,{class:"module-title"},{default:c((()=>[r(m(s.title),1)])),_:2},1024)])),_:2},1032,["onClick"]))),64))])),_:1}),n(e,{class:"assistant-actions"},{default:c((()=>[n(l,{class:"assistant-button",onClick:$},{default:c((()=>[n(e,{class:"assistant-icon chat-icon"}),n(t,null,{default:c((()=>[r("AI 学习助手(医院知识库)")])),_:1})])),_:1})])),_:1})])),_:1})])),_:1})])),_:1}),n(e,{class:b(["toast",{visible:O.value}])},{default:c((()=>[r(m(N.value),1)])),_:1},8,["class"])])),_:1})}}}),[["__scopeId","data-v-f45eae37"]]);export{T as default};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{d as a,a as e,r as s,o as l,b as t,e as n,f as r,w as o,i as c,l as i,m as u,F as d,j as m,t as g,H as p,p as f,z as h,y as v,x as _,n as b}from"./index-CqsCatmH.js";import{_ as y}from"./config-doctor.TgARj_nM.js";import{o as w}from"./uni-app.es.DLc1gqFp.js";import{c as M,f as j,s as x}from"./cases.J0v2hr1Y.js";import{_ as $}from"./_plugin-vue_export-helper.BCo6x5W8.js";import"./auth.DAQZ1Xvg.js";const I=$(a({__name:"matching",setup(a){const $=e({message:"王主任正在为您智能匹配病例",subtitle:"正在通过大模型计算最适合您的临床案例库...",progressTarget:92,tags:[]}),I=s([]),T=s(0),k=s("recommended");let D=0,z=null,F=null,P=null,q=!1,A=!1,C=!1,H=!1;const O={page:1,page_size:10};function R(){Promise.resolve({message:"王主任正在为您智能匹配病例",subtitle:"正在通过大模型计算最适合您的临床案例库...",progressTarget:92,tags:[{label:"薄弱环节",tone:"secondary"},{label:"主治医级别",tone:"primary"},{label:"高匹配度",tone:"tertiary"},{label:"基于历史偏好",tone:"neutral"}]}).then((a=>{Object.assign($,a)}))}function U(){const a=D++,e=256*Math.random(),s=256*Math.random(),l=150*(Math.random()-.5),t=150*(Math.random()-.5),n=2+3*Math.random();I.value.push({id:a,style:{left:`${e}px`,top:`${s}px`,"--particle-x":`${l}px`,"--particle-y":`${t}px`,animationDuration:`${n}s`}}),setTimeout((()=>{I.value=I.value.filter((e=>e.id!==a))}),1e3*n)}async function B(){var a;M(),q=!1,A=!1,C=!1,function(){F&&clearInterval(F),T.value=0;const a=Date.now();F=setInterval((()=>{const e=Date.now()-a,s=Math.min(1,e/3e3),l=e>3e3?Math.min(7,Math.floor((e-3e3)/1e3)):0;T.value=Math.min(99,Math.round($.progressTarget*s+l))}),100)}(),(a=3e3,new Promise((e=>{setTimeout(e,a)}))).then((()=>{q=!0,E()}));try{const a=await j(k.value,O);if(!H)return;x({source:k.value,query:O,page:a,cachedAt:Date.now()})}catch{}finally{A=!0,E()}}function E(){H&&!C&&q&&A&&(C=!0,T.value=100,F&&clearInterval(F),F=null,P=setTimeout((()=>{f({url:`/pages/cases/cases?source=${encodeURIComponent(k.value)}`})}),120))}return w((a=>{const e=null==a?void 0:a.source;var s;("recommended"===(s=e)||"specialty"===s||"weak"===s||"teaching"===s||"teacher-task"===s)&&(k.value=e)})),l((()=>{H=!0,R(),function(){for(let a=0;a<12;a+=1)U();z=setInterval(U,300)}(),B()})),t((()=>{H=!1,z&&clearInterval(z),F&&clearInterval(F),P&&clearTimeout(P)})),(a,e)=>{const s=h,l=v,t=_;return n(),r(s,{class:"matching-page"},{default:o((()=>[c(s,{class:"matching-shell"},{default:o((()=>[c(s,{class:"top-visual"},{default:o((()=>[c(s,{class:"network"},{default:o((()=>[c(s,{class:"ring ring-large"}),c(s,{class:"ring ring-middle"}),c(s,{class:"ring ring-small"}),c(s,{class:"node node-top"}),c(s,{class:"node node-left"}),c(s,{class:"node node-right"}),(n(!0),i(d,null,u(I.value,(a=>(n(),r(s,{key:a.id,class:"particle",style:p(a.style)},null,8,["style"])))),128))])),_:1})])),_:1}),c(s,{class:"middle-visual"},{default:o((()=>[c(s,{class:"match-bubble"},{default:o((()=>[c(l,null,{default:o((()=>[m(g($.message),1)])),_:1}),c(l,{class:"typing-dots"}),c(s,{class:"bubble-tail"})])),_:1}),c(s,{class:"director-card"},{default:o((()=>[c(t,{class:"director-image",src:y,mode:"aspectFit"})])),_:1}),c(s,{class:"intelligence-area"},{default:o((()=>[c(s,{class:"scan-circle"},{default:o((()=>[c(s,{class:"pulse-ring ring-one"}),c(s,{class:"pulse-ring ring-two"}),c(s,{class:"brain-core"},{default:o((()=>[c(s,{class:"scan-bar"}),c(s,{class:"brain-icon"})])),_:1})])),_:1}),(n(!0),i(d,null,u($.tags,((a,e)=>(n(),r(s,{key:a.label,class:b(["float-tag",[`tag-${a.tone}`,`tag-pos-${e}`]])},{default:o((()=>[c(l,null,{default:o((()=>[m(g(a.label),1)])),_:2},1024)])),_:2},1032,["class"])))),128))])),_:1})])),_:1}),c(s,{class:"bottom-progress"},{default:o((()=>[c(s,{class:"progress-track"},{default:o((()=>[c(s,{class:"progress-fill",style:p({width:`${T.value}%`})},null,8,["style"])])),_:1}),c(l,{class:"progress-subtitle"},{default:o((()=>[m(g($.subtitle),1)])),_:1}),c(s,{class:"security-icon"})])),_:1})])),_:1})])),_:1})}}}),[["__scopeId","data-v-c9c34e1f"]]);export{I as default}; import{d as a,a as e,r as s,o as l,b as t,e as n,f as r,w as o,i as c,l as i,m as u,F as d,j as m,t as g,H as p,p as f,z as h,y as v,x as _,n as b}from"./index-DVBIT0nD.js";import{_ as y}from"./config-doctor.TgARj_nM.js";import{o as w}from"./uni-app.es.DwwurLL-.js";import{c as M,f as j,s as x}from"./cases.BkUcJRcR.js";import{_ as $}from"./_plugin-vue_export-helper.BCo6x5W8.js";import"./auth.BCevMxOR.js";const I=$(a({__name:"matching",setup(a){const $=e({message:"王主任正在为您智能匹配病例",subtitle:"正在通过大模型计算最适合您的临床案例库...",progressTarget:92,tags:[]}),I=s([]),T=s(0),k=s("recommended");let D=0,z=null,F=null,P=null,q=!1,A=!1,C=!1,H=!1;const O={page:1,page_size:10};function R(){Promise.resolve({message:"王主任正在为您智能匹配病例",subtitle:"正在通过大模型计算最适合您的临床案例库...",progressTarget:92,tags:[{label:"薄弱环节",tone:"secondary"},{label:"主治医级别",tone:"primary"},{label:"高匹配度",tone:"tertiary"},{label:"基于历史偏好",tone:"neutral"}]}).then((a=>{Object.assign($,a)}))}function U(){const a=D++,e=256*Math.random(),s=256*Math.random(),l=150*(Math.random()-.5),t=150*(Math.random()-.5),n=2+3*Math.random();I.value.push({id:a,style:{left:`${e}px`,top:`${s}px`,"--particle-x":`${l}px`,"--particle-y":`${t}px`,animationDuration:`${n}s`}}),setTimeout((()=>{I.value=I.value.filter((e=>e.id!==a))}),1e3*n)}async function B(){var a;M(),q=!1,A=!1,C=!1,function(){F&&clearInterval(F),T.value=0;const a=Date.now();F=setInterval((()=>{const e=Date.now()-a,s=Math.min(1,e/3e3),l=e>3e3?Math.min(7,Math.floor((e-3e3)/1e3)):0;T.value=Math.min(99,Math.round($.progressTarget*s+l))}),100)}(),(a=3e3,new Promise((e=>{setTimeout(e,a)}))).then((()=>{q=!0,E()}));try{const a=await j(k.value,O);if(!H)return;x({source:k.value,query:O,page:a,cachedAt:Date.now()})}catch{}finally{A=!0,E()}}function E(){H&&!C&&q&&A&&(C=!0,T.value=100,F&&clearInterval(F),F=null,P=setTimeout((()=>{f({url:`/pages/cases/cases?source=${encodeURIComponent(k.value)}`})}),120))}return w((a=>{const e=null==a?void 0:a.source;var s;("recommended"===(s=e)||"specialty"===s||"weak"===s||"teaching"===s||"teacher-task"===s)&&(k.value=e)})),l((()=>{H=!0,R(),function(){for(let a=0;a<12;a+=1)U();z=setInterval(U,300)}(),B()})),t((()=>{H=!1,z&&clearInterval(z),F&&clearInterval(F),P&&clearTimeout(P)})),(a,e)=>{const s=h,l=v,t=_;return n(),r(s,{class:"matching-page"},{default:o((()=>[c(s,{class:"matching-shell"},{default:o((()=>[c(s,{class:"top-visual"},{default:o((()=>[c(s,{class:"network"},{default:o((()=>[c(s,{class:"ring ring-large"}),c(s,{class:"ring ring-middle"}),c(s,{class:"ring ring-small"}),c(s,{class:"node node-top"}),c(s,{class:"node node-left"}),c(s,{class:"node node-right"}),(n(!0),i(d,null,u(I.value,(a=>(n(),r(s,{key:a.id,class:"particle",style:p(a.style)},null,8,["style"])))),128))])),_:1})])),_:1}),c(s,{class:"middle-visual"},{default:o((()=>[c(s,{class:"match-bubble"},{default:o((()=>[c(l,null,{default:o((()=>[m(g($.message),1)])),_:1}),c(l,{class:"typing-dots"}),c(s,{class:"bubble-tail"})])),_:1}),c(s,{class:"director-card"},{default:o((()=>[c(t,{class:"director-image",src:y,mode:"aspectFit"})])),_:1}),c(s,{class:"intelligence-area"},{default:o((()=>[c(s,{class:"scan-circle"},{default:o((()=>[c(s,{class:"pulse-ring ring-one"}),c(s,{class:"pulse-ring ring-two"}),c(s,{class:"brain-core"},{default:o((()=>[c(s,{class:"scan-bar"}),c(s,{class:"brain-icon"})])),_:1})])),_:1}),(n(!0),i(d,null,u($.tags,((a,e)=>(n(),r(s,{key:a.label,class:b(["float-tag",[`tag-${a.tone}`,`tag-pos-${e}`]])},{default:o((()=>[c(l,null,{default:o((()=>[m(g(a.label),1)])),_:2},1024)])),_:2},1032,["class"])))),128))])),_:1})])),_:1}),c(s,{class:"bottom-progress"},{default:o((()=>[c(s,{class:"progress-track"},{default:o((()=>[c(s,{class:"progress-fill",style:p({width:`${T.value}%`})},null,8,["style"])])),_:1}),c(l,{class:"progress-subtitle"},{default:o((()=>[m(g($.subtitle),1)])),_:1}),c(s,{class:"security-icon"})])),_:1})])),_:1})])),_:1})}}}),[["__scopeId","data-v-c9c34e1f"]]);export{I as default};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{C as e,D as t}from"./index-CqsCatmH.js";import{a as n,A as o}from"./auth.DAQZ1Xvg.js";import{F as r}from"./session.Bnzr01cD.js";function i(){return u("/user/profile/")}function s(e={}){return u("/user/training-records/",{search:e.search,page:e.page})}function a(){return u("/user/analysis/")}function c(){return u("/user/competency-metrics/")}async function d(t){var n,i;const s=function(e){if(e&&"object"==typeof e&&"data"in e){const t=e;if(t.code&&"OK"!==t.code&&"ok"!==t.code)throw new o(t.message||"请求失败",t.code);if(void 0!==t.data)return t.data}return e}(await(n=`/evaluations/${encodeURIComponent(String(t))}`,new Promise(((t,s)=>{e({url:`${r}${f(n,i)}`,method:"GET",timeout:1e4,header:m(),success:e=>{if(e.statusCode>=200&&e.statusCode<300)return void t(e.data);const n=e.data,r="string"==typeof(null==n?void 0:n.code)?n.code:void 0;s(new o(p(e.data,`请求失败(${e.statusCode}`),r,e.statusCode))},fail:e=>{s(new o(e.errMsg||"无法连接服务"))}})}))));return function(e,t){const n=A(e.case)?e.case:{},o=v(e.total_score??e.score??e.current_score,0);return{evaluation_id:v(e.evaluation_id??e.id??e.record_id,t),session_id:v(e.session_id,0),case_id:v(e.case_id??n.id,0),case_title:g(e.case_title??e.caseTitle??n.title,"未命名病例"),score_type:y(e.score_type),total_score:o,dimension_scores:l(e.dimension_scores??e.radar),score_details:_(e.score_details),overall_comment:g(e.overall_comment??e.comment,"本次评价暂无详细点评。"),pdf_file_path:h(e.pdf_file_path??e.pdfFilePath),created_at:h(e.created_at??e.trained_at)}}(s,t)}function u(t,r){return new Promise(((i,s)=>{e({url:`${n}${f(t,r)}`,method:"GET",timeout:1e4,header:m(),success:e=>{if(e.statusCode>=200&&e.statusCode<300)return void i(e.data);const t=e.data,n="string"==typeof(null==t?void 0:t.code)?t.code:void 0;s(new o(p(e.data,`请求失败(${e.statusCode}`),n,e.statusCode))},fail:e=>{s(new o(e.errMsg||"无法连接服务"))}})}))}function m(){const e={"Content-Type":"application/json",Accept:"application/json"},n=function(){try{const e=t("clinical-thinking-access-token");return"string"==typeof e?e.trim():""}catch{return""}}();return n&&(e.Authorization=`Bearer ${n}`),e}function f(e,t){if(!t)return e;const n=Object.entries(t).filter((([,e])=>null!=e&&""!==e)).map((([e,t])=>`${encodeURIComponent(e)}=${encodeURIComponent(String(t))}`)).join("&");return n?`${e}?${n}`:e}function p(e,t){if(e&&"object"==typeof e){const t=e,n=t.message||t.detail||t.error;if("string"==typeof n&&n.trim())return n}return t}function l(e){return Array.isArray(e)?e.map((e=>{const t=A(e)?e:{};return{dimension:g(t.dimension??t.label,"未命名维度"),score:v(t.score,0),max_score:v(t.max_score,100),comment:h(t.comment),evidence:Array.isArray(t.evidence)?t.evidence.map(String):void 0,deductions:Array.isArray(t.deductions)?t.deductions.map(String):void 0,improvement:h(t.improvement)}})):[]}function _(e){return Array.isArray(e)?e.map((e=>{const t=A(e)?e:{};return{dimension:g(t.dimension,"未命名维度"),score:v(t.score,0),deducted_reason:h(t.deducted_reason),ai_confidence:void 0===t.ai_confidence?void 0:v(t.ai_confidence,0),comment:h(t.comment)}})):[]}function y(e){return"five_point"===e?"five_point":"percentage"}function v(e,t=0){const n=Number(e);return Number.isFinite(n)?n:t}function g(e,t=""){return"string"==typeof e?e.trim()||t:"number"==typeof e?String(e):t}function h(e){return g(e)||void 0}function A(e){return Boolean(e&&"object"==typeof e&&!Array.isArray(e))}export{s as a,d as b,i as c,c as d,a as f}; import{C as e,D as t}from"./index-DVBIT0nD.js";import{a as n,A as o}from"./auth.BCevMxOR.js";import{F as r}from"./session.DeeNJsIT.js";function i(){return u("/user/profile/")}function s(e={}){return u("/user/training-records/",{search:e.search,page:e.page})}function a(){return u("/user/analysis/")}function c(){return u("/user/competency-metrics/")}async function d(t){var n,i;const s=function(e){if(e&&"object"==typeof e&&"data"in e){const t=e;if(t.code&&"OK"!==t.code&&"ok"!==t.code)throw new o(t.message||"请求失败",t.code);if(void 0!==t.data)return t.data}return e}(await(n=`/evaluations/${encodeURIComponent(String(t))}`,new Promise(((t,s)=>{e({url:`${r}${f(n,i)}`,method:"GET",timeout:1e4,header:m(),success:e=>{if(e.statusCode>=200&&e.statusCode<300)return void t(e.data);const n=e.data,r="string"==typeof(null==n?void 0:n.code)?n.code:void 0;s(new o(p(e.data,`请求失败(${e.statusCode}`),r,e.statusCode))},fail:e=>{s(new o(e.errMsg||"无法连接服务"))}})}))));return function(e,t){const n=A(e.case)?e.case:{},o=v(e.total_score??e.score??e.current_score,0);return{evaluation_id:v(e.evaluation_id??e.id??e.record_id,t),session_id:v(e.session_id,0),case_id:v(e.case_id??n.id,0),case_title:g(e.case_title??e.caseTitle??n.title,"未命名病例"),score_type:y(e.score_type),total_score:o,dimension_scores:l(e.dimension_scores??e.radar),score_details:_(e.score_details),overall_comment:g(e.overall_comment??e.comment,"本次评价暂无详细点评。"),pdf_file_path:h(e.pdf_file_path??e.pdfFilePath),created_at:h(e.created_at??e.trained_at)}}(s,t)}function u(t,r){return new Promise(((i,s)=>{e({url:`${n}${f(t,r)}`,method:"GET",timeout:1e4,header:m(),success:e=>{if(e.statusCode>=200&&e.statusCode<300)return void i(e.data);const t=e.data,n="string"==typeof(null==t?void 0:t.code)?t.code:void 0;s(new o(p(e.data,`请求失败(${e.statusCode}`),n,e.statusCode))},fail:e=>{s(new o(e.errMsg||"无法连接服务"))}})}))}function m(){const e={"Content-Type":"application/json",Accept:"application/json"},n=function(){try{const e=t("clinical-thinking-access-token");return"string"==typeof e?e.trim():""}catch{return""}}();return n&&(e.Authorization=`Bearer ${n}`),e}function f(e,t){if(!t)return e;const n=Object.entries(t).filter((([,e])=>null!=e&&""!==e)).map((([e,t])=>`${encodeURIComponent(e)}=${encodeURIComponent(String(t))}`)).join("&");return n?`${e}?${n}`:e}function p(e,t){if(e&&"object"==typeof e){const t=e,n=t.message||t.detail||t.error;if("string"==typeof n&&n.trim())return n}return t}function l(e){return Array.isArray(e)?e.map((e=>{const t=A(e)?e:{};return{dimension:g(t.dimension??t.label,"未命名维度"),score:v(t.score,0),max_score:v(t.max_score,100),comment:h(t.comment),evidence:Array.isArray(t.evidence)?t.evidence.map(String):void 0,deductions:Array.isArray(t.deductions)?t.deductions.map(String):void 0,improvement:h(t.improvement)}})):[]}function _(e){return Array.isArray(e)?e.map((e=>{const t=A(e)?e:{};return{dimension:g(t.dimension,"未命名维度"),score:v(t.score,0),deducted_reason:h(t.deducted_reason),ai_confidence:void 0===t.ai_confidence?void 0:v(t.ai_confidence,0),comment:h(t.comment)}})):[]}function y(e){return"five_point"===e?"five_point":"percentage"}function v(e,t=0){const n=Number(e);return Number.isFinite(n)?n:t}function g(e,t=""){return"string"==typeof e?e.trim()||t:"number"==typeof e?String(e):t}function h(e){return g(e)||void 0}function A(e){return Boolean(e&&"object"==typeof e&&!Array.isArray(e))}export{s as a,d as b,i as c,c as d,a as f};
@@ -1 +1 @@
import{D as e,s as t}from"./index-CqsCatmH.js";const n="/fastapi/api/v1";function s(){const t=e("clinical-thinking-access-token");if("string"!=typeof t||!t.trim())throw new Error("登录已过期,请重新登录");return t}function o(e="application/json"){return{"Content-Type":"application/json",Accept:e,Authorization:`Bearer ${s()}`,"X-Entry-Scene":"vue_frontend"}}async function a(e){const t=await e.text().catch((()=>""));if(!t)return`请求失败(${e.status}`;try{const e=JSON.parse(t),n=e.message||e.detail||e.error;if("string"==typeof n&&n.trim())return n}catch{}return t}function r(){const t=e("clinical-thinking-scenario");return t&&"object"==typeof t?t:null}function i(){var e,t;const n=null==(t=null==(e=r())?void 0:e.session)?void 0:t.session_id;if("number"==typeof n&&Number.isInteger(n)&&n>0)return n;throw new Error("未找到当前会话,请先生成模拟场景")}function c(e){const n=r();(null==n?void 0:n.session)&&t("clinical-thinking-scenario",{...n,session:{...n.session,status:e}})}async function f(e){var t;const n=await fetch("/fastapi/api/v1/sessions",{method:"POST",headers:o(),body:JSON.stringify(e)});if(!n.ok)throw new Error(await a(n));const s=await n.json();if("OK"!==s.code||!(null==(t=s.data)?void 0:t.session_id))throw new Error(s.message||"新建会话失败");return s.data}async function d(e){var t;const n=await fetch(`/fastapi/api/v1/sessions/${e}/complete-inquiry`,{method:"POST",headers:o()});if(!n.ok)throw new Error(await a(n));const s=await n.json();if("OK"!==s.code||!(null==(t=s.data)?void 0:t.session_id))throw new Error(s.message||"完成采集失败");return s.data}async function l(e,t,n,s){var r,i,c;const f=await fetch(`/fastapi/api/v1/sessions/${e}/chat/stream`,{method:"POST",headers:o("text/event-stream"),body:JSON.stringify({message:t}),signal:s});if(!f.ok||!f.body)throw new Error(await a(f));const d=f.body.getReader(),l=new TextDecoder;let u="",w=!1;for(;;){const{value:e,done:t}=await d.read();if(t)break;u+=l.decode(e,{stream:!0});const s=u.split("\n\n");u=s.pop()||"";for(const o of s){const e=null==(r=o.match(/^event:\s*(.+)$/m))?void 0:r[1],t=null==(i=o.match(/^data:\s*(.+)$/m))?void 0:i[1];if(!e||!t)continue;const s=JSON.parse(t);if("message_delta"===e){const e=s.delta;"string"==typeof e&&n.onDelta(e)}else if("message_done"===e)w=!0,null==(c=n.onDone)||c.call(n,s);else if("error"===e)throw new Error("string"==typeof s.message?s.message:"AI 流式回复异常")}}if(!w)throw new Error("AI 流式回复未正常结束,请重试")}async function u(e,t,n,s){var r,i,c;const f={last_user_message:t,scope:"current_conversation"},d=await fetch(`/fastapi/api/v1/sessions/${e}/hints/stream`,{method:"POST",headers:o("text/event-stream"),body:JSON.stringify(f),signal:s});if(!d.ok||!d.body)throw new Error(await a(d));const l=d.body.getReader(),u=new TextDecoder;let w="",h=!1;for(;;){const{value:e,done:t}=await l.read();if(t)break;w+=u.decode(e,{stream:!0});const s=w.split("\n\n");w=s.pop()||"";for(const o of s){const e=null==(r=o.match(/^event:\s*(.+)$/m))?void 0:r[1],t=null==(i=o.match(/^data:\s*(.+)$/m))?void 0:i[1];if(!e||!t)continue;const s=JSON.parse(t);if("hint_delta"===e){const e=s.delta;"string"==typeof e&&n.onDelta(e)}else if("hint_done"===e)h=!0,null==(c=n.onDone)||c.call(n,s);else if("error"===e)throw new Error("string"==typeof s.message?s.message:"练习提示生成失败,请稍后重试")}}if(!h)throw new Error("练习提示未正常结束,请重试")}export{n as F,o as a,r as b,f as c,d,u as e,i as f,a as r,l as s,c as u}; import{D as e,s as t}from"./index-DVBIT0nD.js";const n="/fastapi/api/v1";function s(){const t=e("clinical-thinking-access-token");if("string"!=typeof t||!t.trim())throw new Error("登录已过期,请重新登录");return t}function o(e="application/json"){return{"Content-Type":"application/json",Accept:e,Authorization:`Bearer ${s()}`,"X-Entry-Scene":"vue_frontend"}}async function a(e){const t=await e.text().catch((()=>""));if(!t)return`请求失败(${e.status}`;try{const e=JSON.parse(t),n=e.message||e.detail||e.error;if("string"==typeof n&&n.trim())return n}catch{}return t}function r(){const t=e("clinical-thinking-scenario");return t&&"object"==typeof t?t:null}function i(){var e,t;const n=null==(t=null==(e=r())?void 0:e.session)?void 0:t.session_id;if("number"==typeof n&&Number.isInteger(n)&&n>0)return n;throw new Error("未找到当前会话,请先生成模拟场景")}function c(e){const n=r();(null==n?void 0:n.session)&&t("clinical-thinking-scenario",{...n,session:{...n.session,status:e}})}async function f(e){var t;const n=await fetch("/fastapi/api/v1/sessions",{method:"POST",headers:o(),body:JSON.stringify(e)});if(!n.ok)throw new Error(await a(n));const s=await n.json();if("OK"!==s.code||!(null==(t=s.data)?void 0:t.session_id))throw new Error(s.message||"新建会话失败");return s.data}async function d(e){var t;const n=await fetch(`/fastapi/api/v1/sessions/${e}/complete-inquiry`,{method:"POST",headers:o()});if(!n.ok)throw new Error(await a(n));const s=await n.json();if("OK"!==s.code||!(null==(t=s.data)?void 0:t.session_id))throw new Error(s.message||"完成采集失败");return s.data}async function l(e,t,n,s){var r,i,c;const f=await fetch(`/fastapi/api/v1/sessions/${e}/chat/stream`,{method:"POST",headers:o("text/event-stream"),body:JSON.stringify({message:t}),signal:s});if(!f.ok||!f.body)throw new Error(await a(f));const d=f.body.getReader(),l=new TextDecoder;let u="",w=!1;for(;;){const{value:e,done:t}=await d.read();if(t)break;u+=l.decode(e,{stream:!0});const s=u.split("\n\n");u=s.pop()||"";for(const o of s){const e=null==(r=o.match(/^event:\s*(.+)$/m))?void 0:r[1],t=null==(i=o.match(/^data:\s*(.+)$/m))?void 0:i[1];if(!e||!t)continue;const s=JSON.parse(t);if("message_delta"===e){const e=s.delta;"string"==typeof e&&n.onDelta(e)}else if("message_done"===e)w=!0,null==(c=n.onDone)||c.call(n,s);else if("error"===e)throw new Error("string"==typeof s.message?s.message:"AI 流式回复异常")}}if(!w)throw new Error("AI 流式回复未正常结束,请重试")}async function u(e,t,n,s){var r,i,c;const f={last_user_message:t,scope:"current_conversation"},d=await fetch(`/fastapi/api/v1/sessions/${e}/hints/stream`,{method:"POST",headers:o("text/event-stream"),body:JSON.stringify(f),signal:s});if(!d.ok||!d.body)throw new Error(await a(d));const l=d.body.getReader(),u=new TextDecoder;let w="",h=!1;for(;;){const{value:e,done:t}=await l.read();if(t)break;w+=u.decode(e,{stream:!0});const s=w.split("\n\n");w=s.pop()||"";for(const o of s){const e=null==(r=o.match(/^event:\s*(.+)$/m))?void 0:r[1],t=null==(i=o.match(/^data:\s*(.+)$/m))?void 0:i[1];if(!e||!t)continue;const s=JSON.parse(t);if("hint_delta"===e){const e=s.delta;"string"==typeof e&&n.onDelta(e)}else if("hint_done"===e)h=!0,null==(c=n.onDone)||c.call(n,s);else if("error"===e)throw new Error("string"==typeof s.message?s.message:"练习提示生成失败,请稍后重试")}}if(!h)throw new Error("练习提示未正常结束,请重试")}export{n as F,o as a,r as b,f as c,d,u as e,i as f,a as r,l as s,c as u};
-1
View File
@@ -1 +0,0 @@
import{T as s,U as a,Q as o,V as r}from"./index-CqsCatmH.js";const t=((r,t=0)=>(t,e=o())=>{!s&&a(r,t,e)})(r,2);export{t as o};
+1
View File
@@ -0,0 +1 @@
import{T as s,U as a,Q as o,V as r}from"./index-DVBIT0nD.js";const t=((r,t=0)=>(t,e=o())=>{!s&&a(r,t,e)})(r,2);export{t as o};
+1 -1
View File
@@ -14,7 +14,7 @@
<title>AI思维临床训练</title> <title>AI思维临床训练</title>
<!--preload-links--> <!--preload-links-->
<!--app-context--> <!--app-context-->
<script type="module" crossorigin src="./assets/index-CqsCatmH.js"></script> <script type="module" crossorigin src="./assets/index-DVBIT0nD.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-C7PPi8dw.css"> <link rel="stylesheet" crossorigin href="./assets/index-C7PPi8dw.css">
</head> </head>
<body> <body>
+62 -22
View File
@@ -89,12 +89,12 @@
<scroll-view class="quick-actions" scroll-x> <scroll-view class="quick-actions" scroll-x>
<view class="quick-row"> <view class="quick-row">
<button <button
v-for="action in quickActions"
:key="action"
class="quick-chip" class="quick-chip"
@click="useQuickAction(action)" :class="{ disabled: sending }"
:disabled="sending"
@click="handleClinicalBooks"
> >
{{ action }} 临床思维训练书籍
</button> </button>
</view> </view>
</scroll-view> </scroll-view>
@@ -175,6 +175,7 @@
import { nextTick, onMounted, onUnmounted, ref } from 'vue' import { nextTick, onMounted, onUnmounted, ref } from 'vue'
import { import {
createLearningAssistantSession, createLearningAssistantSession,
runClinicalThinkingBooksWorkflow,
streamLearningAssistantChat, streamLearningAssistantChat,
type LearningAssistantSession type LearningAssistantSession
} from '../../api/learning-assistant' } from '../../api/learning-assistant'
@@ -195,7 +196,7 @@ const emit = defineEmits<{
const goHome = createHomeNavigator(emit) const goHome = createHomeNavigator(emit)
const quickActions = ['更新指南', '风险评估量表', '药理学详情', '病例研讨'] const clinicalBooksAction = '临床思维训练书籍'
const pathwaySteps = [ const pathwaySteps = [
{ index: '1', title: '早期识别', description: '10分钟内完成12导联心电图。' }, { index: '1', title: '早期识别', description: '10分钟内完成12导联心电图。' },
@@ -216,15 +217,41 @@ const sending = ref(false)
let toastTimer: ReturnType<typeof setTimeout> | null = null let toastTimer: ReturnType<typeof setTimeout> | null = null
let streamAbortController: AbortController | null = null let streamAbortController: AbortController | null = null
let workflowAbortController: AbortController | null = null
function useQuickAction(action: string) { async function handleClinicalBooks() {
const prompts: Record<string, string> = { if (sending.value) return
更新指南: '请帮我梳理 ACS 最新指南中需要重点关注的更新。',
风险评估量表: '请列出 ACS 常用风险评估量表及适用场景。', messages.value.push({
药理学详情: '请说明 ACS 常用抗血小板药物的适应证和注意事项。', id: `user-${Date.now()}`,
病例研讨: '请用病例研讨形式带我复盘一例胸痛患者。' role: 'user',
content: clinicalBooksAction
})
typingVisible.value = true
sending.value = true
scrollToBottom()
const assistantMessageIndex = messages.value.length
messages.value.push({
id: `assistant-${Date.now()}`,
role: 'assistant',
variant: 'simple',
content: ''
})
try {
workflowAbortController?.abort()
workflowAbortController = new AbortController()
const output = await runClinicalThinkingBooksWorkflow(workflowAbortController.signal)
messages.value[assistantMessageIndex].content = output
} catch (error) {
messages.value[assistantMessageIndex].content = error instanceof Error ? error.message : '临床思维训练书籍生成失败'
showToast(messages.value[assistantMessageIndex].content)
} finally {
typingVisible.value = false
sending.value = false
scrollToBottom()
} }
draft.value = prompts[action] || action
} }
async function handleSend() { async function handleSend() {
@@ -324,6 +351,7 @@ onMounted(() => {
onUnmounted(() => { onUnmounted(() => {
streamAbortController?.abort() streamAbortController?.abort()
workflowAbortController?.abort()
if (toastTimer) clearTimeout(toastTimer) if (toastTimer) clearTimeout(toastTimer)
}) })
</script> </script>
@@ -712,27 +740,39 @@ page {
.quick-row { .quick-row {
display: flex; display: flex;
justify-content: flex-start;
align-items: center;
gap: 8px; gap: 8px;
padding-bottom: 4px; padding-bottom: 4px;
} }
.quick-chip { .quick-chip {
box-sizing: border-box; box-sizing: border-box;
min-height: 32px; width: auto;
padding: 6px 16px; flex: 0 0 auto;
border: 1px solid #c2c6d4; display: inline-flex;
border-radius: 12px; align-items: center;
background: #ecedf6; justify-content: center;
color: #424752; margin: 0;
font-size: 12px; min-height: 36px;
line-height: 16px; padding: 8px 18px;
font-weight: 500; border: 1px solid rgba(0, 71, 141, 0.28);
border-radius: 8px;
background: #eef5ff;
color: #00478d;
font-size: 14px;
line-height: 20px;
font-weight: 600;
letter-spacing: 0; letter-spacing: 0;
white-space: nowrap; white-space: nowrap;
} }
.quick-chip.disabled {
opacity: 0.55;
}
.quick-chip:active { .quick-chip:active {
background: #e7e8f0; background: #dcecff;
} }
.composer { .composer {
+5
View File
@@ -14,6 +14,11 @@ export default defineConfig({
target: 'http://8.160.178.88', target: 'http://8.160.178.88',
changeOrigin: true changeOrigin: true
}, },
'/dify': {
target: 'http://8.160.178.88:8088',
changeOrigin: true,
rewrite: path => path.replace(/^\/dify/, '')
},
'/app': { '/app': {
target: 'http://8.160.178.88', target: 'http://8.160.178.88',
changeOrigin: true changeOrigin: true