diff --git a/api/cases.ts b/api/cases.ts new file mode 100644 index 0000000..5a94d56 --- /dev/null +++ b/api/cases.ts @@ -0,0 +1,82 @@ +export type ClinicalCase = { + id: string + title: string + patientName: string + gender: '男' | '女' + age: number + department: string + scene: string + caseNo: string + tone: 'blue' | 'teal' | 'pink' | 'orange' | 'purple' | 'green' +} + +export function fetchCaseList(): Promise { + return Promise.resolve([ + { + id: 'case-31190016', + title: '间断四肢多关节肿痛5年,加重1个月', + patientName: '郭爱和', + gender: '男', + age: 43, + department: '风湿免疫科', + scene: '门诊部', + caseNo: '31190016', + tone: 'blue' + }, + { + id: 'case-31180002', + title: '右膝关节疼痛8年,腰背部疼痛2年', + patientName: '索航', + gender: '男', + age: 51, + department: '风湿免疫科', + scene: '住院部', + caseNo: '31180002', + tone: 'teal' + }, + { + id: 'case-2238015', + title: '阴道不规则流血4月。', + patientName: '韩爱利', + gender: '女', + age: 52, + department: '妇科', + scene: '住院部', + caseNo: '2238015', + tone: 'pink' + }, + { + id: 'case-1006004', + title: '持续胸痛3小时', + patientName: '毕波涛', + gender: '男', + age: 60, + department: '心血管内科', + scene: '住院部', + caseNo: '1006004', + tone: 'orange' + }, + { + id: 'case-31190042', + title: '咳嗽、咳痰10余年,加重1周', + patientName: '厉明', + gender: '男', + age: 52, + department: '呼吸内科', + scene: '普通门诊', + caseNo: '31190042', + tone: 'purple' + }, + { + id: 'case-2238019', + title: '尿频、尿急、尿痛3天', + patientName: '刘晓元', + gender: '女', + age: 25, + department: '泌尿外科', + scene: '急诊留观', + caseNo: '2238019', + tone: 'green' + } + ]) +} diff --git a/api/chat.ts b/api/chat.ts new file mode 100644 index 0000000..a06733e --- /dev/null +++ b/api/chat.ts @@ -0,0 +1,106 @@ +import type { ClinicalCase } from './cases' + +export type ChatRole = 'patient' | 'mentor' | 'doctor' + +export type ChatMessage = { + id: string + role: ChatRole + content: string + label: string +} + +export type ChatSession = { + patient: { + name: string + gender: string + age: number + department: string + chiefComplaint: string + } + stages: Array<{ + key: string + label: string + active: boolean + }> + messages: ChatMessage[] +} + +const defaultCase: ClinicalCase = { + id: 'case-1006004', + title: '持续胸痛3小时', + patientName: '陈先生', + gender: '男', + age: 60, + department: '心血管内科', + scene: '住院部', + caseNo: '1006004', + tone: 'orange' +} + +export function createMockChatSession(caseItem?: ClinicalCase | null): Promise { + const currentCase = caseItem || defaultCase + const patientName = currentCase.patientName === '毕波涛' ? '陈先生' : currentCase.patientName + + return Promise.resolve({ + patient: { + name: patientName, + gender: currentCase.gender, + age: currentCase.age, + department: currentCase.department, + chiefComplaint: currentCase.title + }, + stages: [ + { key: 'history', label: '病史采集', active: true }, + { key: 'diagnosis', label: '初步诊断', active: false }, + { key: 'treatment', label: '治疗方案', active: false } + ], + messages: [ + { + id: 'patient-initial', + role: 'patient', + content: currentCase.department === '心血管内科' + ? '医生,我心口这儿针扎一样疼了两个小时了,现在感觉喘气都费劲。' + : `医生,我这次主要是${currentCase.title},有点担心。`, + label: '患者' + }, + { + id: 'mentor-initial', + role: 'mentor', + content: '观察患者的面部表情和生命体征。你的第一个问题应该如何询问,以明确疼痛的性质?', + label: '王主任' + } + ] + }) +} + +export function sendMockChatMessage(content: string): Promise { + const normalized = content.trim() + const patientReply = normalized.includes('出冷汗') || normalized.includes('恶心') + ? '有,刚才疼得厉害的时候出了一身冷汗,还有点恶心,但没有吐。' + : normalized.includes('体格检查') + ? '患者面色苍白,额部出汗,心率偏快,血压较入院时略低。' + : normalized.includes('辅助检查') + ? '心电图提示下壁导联 ST 段抬高,肌钙蛋白待回报。' + : '疼痛主要在胸骨后,像压榨一样,休息后也没有明显缓解。' + + return Promise.resolve([ + { + id: `doctor-${Date.now()}`, + role: 'doctor', + content: normalized, + label: '我' + }, + { + id: `patient-${Date.now() + 1}`, + role: 'patient', + content: patientReply, + label: '患者' + }, + { + id: `mentor-${Date.now() + 2}`, + role: 'mentor', + content: '很好,继续围绕 OPQRST 思路追问疼痛诱因、部位、性质、放射、持续时间和缓解因素,同时关注危险信号。', + label: '王主任' + } + ]) +} diff --git a/api/config.ts b/api/config.ts new file mode 100644 index 0000000..a557d9f --- /dev/null +++ b/api/config.ts @@ -0,0 +1,74 @@ +export type ConfigOption = { + value: string + label: string + desc?: string +} + +export type ConfigOptions = { + departments: ConfigOption[] + titles: ConfigOption[] + experiences: ConfigOption[] +} + +export type ClinicalConfigPayload = { + userId: string + phone: string + institutionId: string + department: string + title: string + experience: string + departmentName: string + titleName: string + experienceName: string +} + +export type ClinicalConfigResult = ClinicalConfigPayload & { + id: string + updatedAt: string +} + +export const MOCK_CONFIG_OPTIONS: ConfigOptions = { + departments: [ + { value: 'im', label: '内科', desc: '心内、呼吸、消化、肾内等临床场景' }, + { value: 'sur', label: '外科', desc: '普外、骨科、神外、胸外等临床场景' }, + { value: 'ped', label: '儿科', desc: '儿童常见病、急重症与沟通训练' }, + { value: 'obg', label: '妇产科', desc: '围产、妇科、产科急症训练' }, + { value: 'er', label: '急诊科', desc: '分诊、抢救、危急值处置训练' }, + { value: 'icu', label: '重症医学科', desc: '危重症评估与多学科决策训练' } + ], + titles: [ + { value: 'resident', label: '住院医师', desc: '强化基础诊疗路径与病历思维' }, + { value: 'attending', label: '主治医师', desc: '提升独立诊疗和带教表达' }, + { value: 'associate_chief', label: '副主任医师', desc: '复杂病例决策与团队协作' }, + { value: 'chief', label: '主任医师', desc: '疑难病例、质控和教学管理' } + ], + experiences: [ + { value: '1-3', label: '1-3年', desc: '基础病例训练优先' }, + { value: '3-5', label: '3-5年', desc: '进阶诊疗路径优先' }, + { value: '5-10', label: '5-10年', desc: '复杂病例推演优先' }, + { value: '10+', label: '10年以上', desc: '疑难病例与带教模拟优先' } + ] +} + +export function fetchConfigOptions() { + return Promise.resolve({ + options: MOCK_CONFIG_OPTIONS, + defaults: { + department: 'im', + title: 'resident', + experience: '1-3' + }, + mentor: { + name: '王主任', + message: '欢迎回来!请配置执业信息,开始精准带教模拟。' + } + }) +} + +export function saveClinicalConfig(payload: ClinicalConfigPayload): Promise { + return Promise.resolve({ + id: `mock-config-${Date.now()}`, + ...payload, + updatedAt: new Date().toISOString() + }) +} diff --git a/api/diagnosis.ts b/api/diagnosis.ts new file mode 100644 index 0000000..d8a807f --- /dev/null +++ b/api/diagnosis.ts @@ -0,0 +1,36 @@ +import type { ClinicalCase } from './cases' + +export type DiagnosisDraft = { + primaryDiagnosis: string + differentialDiagnosis: string[] + evidence: string +} + +export type DiagnosisContext = { + mentorAdvice: string + defaultDraft: DiagnosisDraft +} + +export function fetchDiagnosisContext(caseItem?: ClinicalCase | null): Promise { + const isChestPain = caseItem?.title.includes('胸痛') || caseItem?.department === '心血管内科' + + return Promise.resolve({ + mentorAdvice: isChestPain + ? '王主任建议:请结合患者既往高血压史及突发性胸痛的性质,进行准确诊断。注意鉴别心梗与主动脉夹层。' + : '王主任建议:请基于主诉、阳性症状和危险信号提出主要诊断,并列出需要排除的鉴别诊断。', + defaultDraft: { + primaryDiagnosis: '', + differentialDiagnosis: ['', ''], + evidence: '' + } + }) +} + +export function submitDiagnosis(caseId: string, draft: DiagnosisDraft) { + return Promise.resolve({ + id: `mock-diagnosis-${Date.now()}`, + caseId, + ...draft, + submittedAt: new Date().toISOString() + }) +} diff --git a/api/home.ts b/api/home.ts new file mode 100644 index 0000000..fe89cea --- /dev/null +++ b/api/home.ts @@ -0,0 +1,22 @@ +export type HomeSummary = { + greeting: string + highlight: string + remainingModules: number + doctorName: string +} + +export function fetchHomeSummary(): Promise { + return Promise.resolve({ + greeting: '下午好,医生。', + highlight: '让我们继续提升您的临床思维能力吧。', + remainingModules: 3, + doctorName: '王主任' + }) +} + +export function startTrainingSession() { + return Promise.resolve({ + sessionId: `mock-session-${Date.now()}`, + startedAt: new Date().toISOString() + }) +} diff --git a/api/matching.ts b/api/matching.ts new file mode 100644 index 0000000..0c2a74c --- /dev/null +++ b/api/matching.ts @@ -0,0 +1,23 @@ +export type MatchingProfile = { + message: string + subtitle: string + progressTarget: number + tags: Array<{ + label: string + tone: 'secondary' | 'primary' | 'tertiary' | 'neutral' + }> +} + +export function fetchMatchingProfile(): Promise { + return Promise.resolve({ + message: '王主任正在为您智能匹配病例', + subtitle: '正在通过大模型计算最适合您的临床案例库...', + progressTarget: 92, + tags: [ + { label: '薄弱环节', tone: 'secondary' }, + { label: '主治医级别', tone: 'primary' }, + { label: '高匹配度', tone: 'tertiary' }, + { label: '基于历史偏好', tone: 'neutral' } + ] + }) +} diff --git a/api/scenario.ts b/api/scenario.ts new file mode 100644 index 0000000..7fc16ee --- /dev/null +++ b/api/scenario.ts @@ -0,0 +1,96 @@ +export type ScenarioRecommendation = { + id: string + title: string + desc: string + tags: string[] + defaults: ScenarioForm +} + +export type ScenarioOption = { + value: string + label: string +} + +export type ScenarioForm = { + environment: string + ageGroup: string + education: string + personality: string +} + +export type ScenarioOptions = { + environments: ScenarioOption[] + ageGroups: ScenarioOption[] + educations: ScenarioOption[] + personalities: ScenarioOption[] +} + +export type ScenarioConfigPayload = ScenarioForm & { + caseId: string + caseNo: string + recommendationId?: string +} + +export function fetchScenarioOptions() { + return Promise.resolve({ + recommendations: [ + { + id: 'typical-outpatient', + title: '典型门诊病例', + desc: '门诊常规病例:针对初学者设计的标准化沟通流程。', + tags: ['中年', '配合'], + defaults: { + environment: 'outpatient', + ageGroup: 'middle', + education: 'higher', + personality: 'calm' + } + }, + { + id: 'emergency-crisis', + title: '急诊重症危机', + desc: '急诊危重:急性心梗紧急入院。', + tags: ['老年', '急躁'], + defaults: { + environment: 'emergency', + ageGroup: 'elderly', + education: 'secondary', + personality: 'irritable' + } + } + ] as ScenarioRecommendation[], + options: { + environments: [ + { value: 'outpatient', label: '门诊' }, + { value: 'emergency', label: '急诊' }, + { value: 'ward', label: '病房' } + ], + ageGroups: [ + { value: 'child', label: '儿童' }, + { value: 'young', label: '青年' }, + { value: 'middle', label: '中年' }, + { value: 'elderly', label: '老年' } + ], + educations: [ + { value: 'primary', label: '小学及以下' }, + { value: 'secondary', label: '中等教育' }, + { value: 'higher', label: '高等教育' } + ], + personalities: [ + { value: 'calm', label: '平和' }, + { value: 'anxious', label: '焦虑' }, + { value: 'irritable', label: '急躁' }, + { value: 'cooperative', label: '配合' }, + { value: 'suspicious', label: '多疑' } + ] + } as ScenarioOptions + }) +} + +export function createScenarioConfig(payload: ScenarioConfigPayload) { + return Promise.resolve({ + id: `mock-scenario-${Date.now()}`, + ...payload, + createdAt: new Date().toISOString() + }) +} diff --git a/pages.json b/pages.json index d79623c..9e9c099 100644 --- a/pages.json +++ b/pages.json @@ -1,17 +1,88 @@ -{ - "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages - { - "path": "pages/index/index", - "style": { - "navigationBarTitleText": "uni-app" - } - } - ], - "globalStyle": { - "navigationBarTextStyle": "black", - "navigationBarTitleText": "uni-app", - "navigationBarBackgroundColor": "#F8F8F8", - "backgroundColor": "#F8F8F8" - }, - "uniIdRouter": {} +{ + "pages": [ + { + "path": "pages/index/index", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "临床思维训练" + } + }, + { + "path": "pages/config/config", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "信息配置" + } + }, + { + "path": "pages/home/home", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "临床思维训练" + } + }, + { + "path": "pages/matching/matching", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "智能匹配" + } + }, + { + "path": "pages/cases/cases", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "病例列表" + } + }, + { + "path": "pages/scenario/scenario", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "场景配置" + } + }, + { + "path": "pages/chat/chat", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "临床对话" + } + }, + { + "path": "pages/diagnosis/diagnosis", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "临床诊断" + } + }, + { + "path": "pages/treatment/treatment", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "治疗计划" + } + }, + { + "path": "pages/assessment/assessment", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "考核评价" + } + }, + { + "path": "pages/profile/profile", + "style": { + "navigationStyle": "custom", + "navigationBarTitleText": "个人中心" + } + } + ], + "globalStyle": { + "navigationBarTextStyle": "black", + "navigationBarTitleText": "临床思维训练", + "navigationBarBackgroundColor": "#F7F8FF", + "backgroundColor": "#F7F8FF" + }, + "uniIdRouter": {} } diff --git a/pages/assessment/assessment.vue b/pages/assessment/assessment.vue new file mode 100644 index 0000000..ac72dbe --- /dev/null +++ b/pages/assessment/assessment.vue @@ -0,0 +1,563 @@ + + + + + diff --git a/pages/cases/cases.vue b/pages/cases/cases.vue new file mode 100644 index 0000000..b167f34 --- /dev/null +++ b/pages/cases/cases.vue @@ -0,0 +1,446 @@ + + + + + diff --git a/pages/chat/chat.vue b/pages/chat/chat.vue new file mode 100644 index 0000000..4ca8952 --- /dev/null +++ b/pages/chat/chat.vue @@ -0,0 +1,821 @@ + + + + + diff --git a/pages/config/config.vue b/pages/config/config.vue new file mode 100644 index 0000000..e8df469 --- /dev/null +++ b/pages/config/config.vue @@ -0,0 +1,789 @@ + + + + + diff --git a/pages/diagnosis/diagnosis.vue b/pages/diagnosis/diagnosis.vue new file mode 100644 index 0000000..ddf365a --- /dev/null +++ b/pages/diagnosis/diagnosis.vue @@ -0,0 +1,710 @@ + + + + + diff --git a/pages/home/home.vue b/pages/home/home.vue new file mode 100644 index 0000000..7b4ba90 --- /dev/null +++ b/pages/home/home.vue @@ -0,0 +1,393 @@ + + + + + diff --git a/pages/index/index.vue b/pages/index/index.vue index 4ca2b92..49e5715 100644 --- a/pages/index/index.vue +++ b/pages/index/index.vue @@ -1,52 +1,826 @@ - - - - - diff --git a/pages/matching/matching.vue b/pages/matching/matching.vue new file mode 100644 index 0000000..5397d53 --- /dev/null +++ b/pages/matching/matching.vue @@ -0,0 +1,646 @@ + + + + + diff --git a/pages/profile/profile.vue b/pages/profile/profile.vue new file mode 100644 index 0000000..4b3d8af --- /dev/null +++ b/pages/profile/profile.vue @@ -0,0 +1,746 @@ + + + + + diff --git a/pages/scenario/scenario.vue b/pages/scenario/scenario.vue new file mode 100644 index 0000000..c3eb0b7 --- /dev/null +++ b/pages/scenario/scenario.vue @@ -0,0 +1,658 @@ + + + + + diff --git a/pages/treatment/treatment.vue b/pages/treatment/treatment.vue new file mode 100644 index 0000000..381164d --- /dev/null +++ b/pages/treatment/treatment.vue @@ -0,0 +1,678 @@ + + + + + diff --git a/static/config-doctor.png b/static/config-doctor.png new file mode 100644 index 0000000..1c28d0b Binary files /dev/null and b/static/config-doctor.png differ diff --git a/static/config-hospital.png b/static/config-hospital.png new file mode 100644 index 0000000..5776b51 Binary files /dev/null and b/static/config-hospital.png differ