Files
vueapp/pages/treatment/treatment.vue
T
2026-05-29 17:40:10 +08:00

679 lines
19 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<AssessmentPage
v-if="showAssessmentPage"
@open-settings="emit('open-settings')"
@open-profile="emit('open-profile')"
@go-home="emit('go-home')"
/>
<view v-else class="treatment-page">
<view class="treatment-shell">
<view class="top-nav">
<button class="icon-button" aria-label="设置" @click="emit('open-settings')">
<view class="settings-icon"></view>
</button>
<button class="icon-button home-button" aria-label="首页" @click="emit('go-home')">
<view class="home-icon"></view>
</button>
<view class="nav-spacer"></view>
<button class="icon-button" aria-label="个人中心" @click="emit('open-profile')">
<view class="account-icon"></view>
</button>
</view>
<view class="case-header">
<text class="case-heading">患者陈先生 (胸痛)</text>
<view class="patient-meta">
<text>姓名陈先生</text>
<text>性别</text>
<text>年龄60</text>
<text>科室心血管内科</text>
</view>
</view>
<view class="treatment-content">
<view class="stepper">
<view class="step-line">
<view class="step-line-active"></view>
</view>
<view class="step done">
<view class="step-dot">
<view class="check-icon"></view>
</view>
<text>问诊</text>
</view>
<view class="step done">
<view class="step-dot">
<view class="check-icon"></view>
</view>
<text>临床诊断</text>
</view>
<view class="step active">
<view class="step-dot">
<view class="pill-icon"></view>
</view>
<text>治疗计划</text>
</view>
</view>
<view class="mentor-card">
<view class="mentor-avatar">
<image src="/static/config-doctor.png" mode="aspectFill"></image>
</view>
<view class="mentor-bubble">
<text>王主任建议请结合患者既往高血压史及突发性胸痛的性质进行准确诊断注意鉴别心梗与主动脉夹层</text>
</view>
</view>
<view class="form-area">
<view class="field-block">
<view class="field-label primary">
<view class="priority-icon"></view>
<text>治疗原则</text>
</view>
<view class="input-wrap">
<input
class="treatment-input"
v-model="form.principle"
type="text"
placeholder="请输入初步诊断..."
placeholder-class="input-placeholder"
/>
</view>
</view>
<view class="field-block">
<view class="field-label">
<view class="checklist-icon"></view>
<text>具体治疗措施</text>
</view>
<view class="measure-list">
<view
v-for="(_, index) in form.measures"
:key="index"
class="measure-row"
>
<text class="measure-index">{{ index + 1 }}</text>
<input
class="measure-input"
v-model="form.measures[index]"
type="text"
:placeholder="`措施 ${index + 1}`"
placeholder-class="input-placeholder"
/>
</view>
</view>
</view>
<view class="field-block">
<view class="field-label">
<view class="description-icon"></view>
<text>医嘱</text>
</view>
<textarea
class="order-input"
v-model="form.orders"
placeholder="请输入医嘱建议..."
placeholder-class="input-placeholder"
></textarea>
</view>
</view>
<button class="next-button" :class="{ submitted }" :disabled="submitting" @click="handleNext">
<view v-if="submitting" class="spinner"></view>
<text>{{ buttonText }}</text>
<view v-if="!submitting && !submitted" class="arrow-icon"></view>
<view v-if="submitted" class="check-small-icon"></view>
</button>
</view>
</view>
<view class="toast" :class="{ visible: toastVisible }">{{ toastMessage }}</view>
</view>
</template>
<script setup lang="ts">
import { computed, onUnmounted, reactive, ref } from 'vue'
import type { ClinicalCase } from '../../api/cases'
import AssessmentPage from '../assessment/assessment.vue'
defineProps<{
caseItem: ClinicalCase | null
}>()
const emit = defineEmits<{
(event: 'open-settings'): void
(event: 'open-profile'): void
(event: 'go-home'): void
}>()
const form = reactive({
principle: '',
measures: ['', ''],
orders: ''
})
const submitting = ref(false)
const submitted = ref(false)
const showAssessmentPage = ref(false)
const toastMessage = ref('')
const toastVisible = ref(false)
let toastTimer: ReturnType<typeof setTimeout> | null = null
const buttonText = computed(() => {
if (submitting.value) return '提交中...'
if (submitted.value) return '已提交'
return '下一步'
})
function handleNext() {
if (submitting.value) return
submitting.value = true
uni.setStorageSync('clinical-thinking-treatment', {
...form,
measures: form.measures.filter(item => item.trim()),
submittedAt: new Date().toISOString()
})
setTimeout(() => {
submitting.value = false
showAssessmentPage.value = true
}, 600)
}
function showToast(message: string) {
if (toastTimer) clearTimeout(toastTimer)
toastMessage.value = message
toastVisible.value = true
uni.showToast({
title: message,
icon: 'none'
})
toastTimer = setTimeout(() => {
toastVisible.value = false
}, 2200)
}
onUnmounted(() => {
if (toastTimer) clearTimeout(toastTimer)
})
</script>
<style scoped>
page {
min-height: 100%;
background: #f9f9ff;
}
.treatment-page {
width: 390px;
max-width: 100vw;
height: 884px;
min-height: 884px;
margin: 0 auto;
overflow-x: hidden;
background: #f9f9ff;
color: #191c21;
font-family: Inter, -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
-webkit-tap-highlight-color: transparent;
}
.treatment-page view,
.treatment-page text,
.treatment-page button,
.treatment-page input,
.treatment-page textarea {
box-sizing: border-box;
}
.treatment-shell {
position: relative;
width: 390px;
max-width: 100vw;
height: 884px;
min-height: 884px;
background: #f9f9ff;
overflow-x: hidden;
}
.top-nav {
position: fixed;
left: 0;
right: 0;
top: 0;
z-index: 50;
width: 100%;
max-width: 100vw;
height: 56px;
padding: 0 20px;
border-bottom: 1px solid rgba(194, 198, 212, 0.3);
background: #ffffff;
box-shadow: 0 2px 8px rgba(25, 28, 33, 0.04);
display: flex;
align-items: center;
}
.nav-spacer {
flex: 1;
}
.icon-button {
width: 40px;
height: 40px;
min-height: 40px;
margin: 0;
padding: 0;
border: 0;
border-radius: 50%;
background: transparent;
line-height: 1;
display: flex;
align-items: center;
justify-content: center;
}
.home-button {
margin-left: 4px;
}
.icon-button::after,
.next-button::after {
border: 0;
}
.settings-icon,
.home-icon,
.account-icon,
.check-icon,
.pill-icon,
.priority-icon,
.checklist-icon,
.description-icon,
.arrow-icon,
.check-small-icon {
background: #424752;
}
.settings-icon {
width: 22px;
height: 22px;
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M19.43%2012.98c.04-.32.07-.65.07-.98s-.02-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.37-.31-.6-.22l-2.49%201c-.52-.4-1.08-.73-1.69-.98L14.5%202.42C14.47%202.18%2014.25%202%2014%202h-4c-.25%200-.46.18-.5.42l-.38%202.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.08-.48%200-.6.22l-2%203.46c-.13.22-.07.49.12.64l2.11%201.65c-.04.32-.08.65-.08.98s.03.66.08.98l-2.11%201.65c-.19.15-.24.42-.12.64l2%203.46c.12.22.37.31.6.22l2.49-1c.52.4%201.08.73%201.69.98l.38%202.65c.04.24.25.42.5.42h4c.25%200%20.46-.18.5-.42l.38-2.65c.61-.25%201.17-.58%201.69-.98l2.49%201c.23.08.48%200%20.6-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12%2015.5A3.5%203.5%200%201%201%2012%208a3.5%203.5%200%200%201%200%207.5z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M19.43%2012.98c.04-.32.07-.65.07-.98s-.02-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.37-.31-.6-.22l-2.49%201c-.52-.4-1.08-.73-1.69-.98L14.5%202.42C14.47%202.18%2014.25%202%2014%202h-4c-.25%200-.46.18-.5.42l-.38%202.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.08-.48%200-.6.22l-2%203.46c-.13.22-.07.49.12.64l2.11%201.65c-.04.32-.08.65-.08.98s.03.66.08.98l-2.11%201.65c-.19.15-.24.42-.12.64l2%203.46c.12.22.37.31.6.22l2.49-1c.52.4%201.08.73%201.69.98l.38%202.65c.04.24.25.42.5.42h4c.25%200%20.46-.18.5-.42l.38-2.65c.61-.25%201.17-.58%201.69-.98l2.49%201c.23.08.48%200%20.6-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12%2015.5A3.5%203.5%200%201%201%2012%208a3.5%203.5%200%200%201%200%207.5z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.home-icon,
.account-icon {
width: 24px;
height: 24px;
}
.home-icon {
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M10%2020v-6h4v6h5v-8h3L12%203%202%2012h3v8h5z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M10%2020v-6h4v6h5v-8h3L12%203%202%2012h3v8h5z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.account-icon {
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M12%202a10%2010%200%201%200%200%2020%2010%2010%200%200%200%200-20zm0%203a3.5%203.5%200%201%201%200%207%203.5%203.5%200%200%201%200-7zm0%2015a8%208%200%200%201-6.4-3.2c1.18-2.02%203.57-3.3%206.4-3.3s5.22%201.28%206.4%203.3A8%208%200%200%201%2012%2020z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M12%202a10%2010%200%201%200%200%2020%2010%2010%200%200%200%200-20zm0%203a3.5%203.5%200%201%201%200%207%203.5%203.5%200%200%201%200-7zm0%2015a8%208%200%200%201-6.4-3.2c1.18-2.02%203.57-3.3%206.4-3.3s5.22%201.28%206.4%203.3A8%208%200%200%201%2012%2020z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.case-header {
margin-top: 56px;
padding: 16px 20px;
background: #f9f9ff;
box-shadow: 0 2px 8px rgba(25, 28, 33, 0.04);
}
.case-heading {
color: #191c21;
font-size: 20px;
line-height: 28px;
font-weight: 600;
}
.patient-meta {
margin-top: 8px;
padding-top: 8px;
border-top: 1px solid rgba(194, 198, 212, 0.3);
display: flex;
flex-wrap: wrap;
column-gap: 24px;
row-gap: 4px;
color: #424752;
font-size: 13px;
line-height: 20px;
}
.treatment-content {
box-sizing: border-box;
padding: 24px 20px 32px;
display: flex;
flex-direction: column;
gap: 24px;
}
.stepper {
position: relative;
padding: 8px 0;
display: flex;
align-items: center;
justify-content: space-between;
}
.step-line {
position: absolute;
left: 40px;
right: 40px;
top: 20px;
height: 4px;
background: #e1e2ea;
z-index: 0;
}
.step-line-active {
width: 100%;
height: 100%;
background: #00478d;
}
.step {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
color: #727783;
font-size: 12px;
line-height: 16px;
font-weight: 500;
}
.step.active {
color: #00478d;
font-weight: 700;
}
.step.done {
color: #006970;
}
.step-dot {
width: 32px;
height: 32px;
border-radius: 50%;
background: #e1e2ea;
display: flex;
align-items: center;
justify-content: center;
}
.step.active .step-dot {
width: 40px;
height: 40px;
border: 4px solid #d6e3ff;
background: #00478d;
box-shadow: 0 0 0 2px #00478d;
}
.step.done .step-dot {
background: #006970;
}
.check-icon,
.pill-icon {
width: 18px;
height: 18px;
background: currentColor;
}
.step.done .check-icon,
.step.active .pill-icon {
background: #ffffff;
}
.check-icon {
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M9%2016.2%204.8%2012l-1.4%201.4L9%2019%2021%207l-1.4-1.4L9%2016.2z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M9%2016.2%204.8%2012l-1.4%201.4L9%2019%2021%207l-1.4-1.4L9%2016.2z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.pill-icon {
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='m4.22%2019.78-.01-.01a5.5%205.5%200%200%201%200-7.78L12%204.22a5.5%205.5%200%200%201%207.78%207.78L12%2019.78a5.5%205.5%200%200%201-7.78%200zM13.41%205.64%2010%209.05%2014.95%2014%2018.36%2010.59a3.5%203.5%200%200%200-4.95-4.95zM5.64%2013.41a3.5%203.5%200%200%200%204.95%204.95L14%2014.95%209.05%2010%205.64%2013.41z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='m4.22%2019.78-.01-.01a5.5%205.5%200%200%201%200-7.78L12%204.22a5.5%205.5%200%200%201%207.78%207.78L12%2019.78a5.5%205.5%200%200%201-7.78%200zM13.41%205.64%2010%209.05%2014.95%2014%2018.36%2010.59a3.5%203.5%200%200%200-4.95-4.95zM5.64%2013.41a3.5%203.5%200%200%200%204.95%204.95L14%2014.95%209.05%2010%205.64%2013.41z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.mentor-card {
padding: 12px;
border: 1px solid rgba(0, 71, 141, 0.1);
border-radius: 12px;
background: rgba(0, 71, 141, 0.05);
display: flex;
align-items: flex-start;
gap: 12px;
}
.mentor-avatar {
flex: 0 0 auto;
width: 48px;
height: 48px;
border: 1px solid rgba(0, 71, 141, 0.2);
border-radius: 50%;
overflow: hidden;
}
.mentor-avatar image {
width: 100%;
height: 100%;
}
.mentor-bubble {
flex: 1;
padding: 12px;
border: 1px solid rgba(194, 198, 212, 0.3);
border-radius: 8px;
border-top-left-radius: 0;
background: #ffffff;
box-shadow: 0 2px 8px rgba(25, 28, 33, 0.04);
color: #191c21;
font-size: 14px;
line-height: 20px;
}
.form-area {
display: flex;
flex-direction: column;
gap: 20px;
}
.field-block {
display: flex;
flex-direction: column;
gap: 8px;
}
.field-label {
display: flex;
align-items: center;
gap: 4px;
color: #424752;
font-size: 14px;
line-height: 20px;
font-weight: 600;
}
.field-label.primary {
color: #00478d;
}
.priority-icon,
.checklist-icon,
.description-icon {
width: 16px;
height: 16px;
background: currentColor;
}
.priority-icon {
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M11%2018h2v-2h-2v2zm0-4h2V6h-2v8zm1%208a10%2010%200%201%201%200-20%2010%2010%200%200%201%200%2020z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M11%2018h2v-2h-2v2zm0-4h2V6h-2v8zm1%208a10%2010%200%201%201%200-20%2010%2010%200%200%201%200%2020z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.checklist-icon {
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M10%2017h10v-2H10v2zm0-4h10v-2H10v2zm0-6v2h10V7H10zM4%207h4v4H4V7zm1%201v2h2V8H5zm-1%205h4v4H4v-4zm1%201v2h2v-2H5z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M10%2017h10v-2H10v2zm0-4h10v-2H10v2zm0-6v2h10V7H10zM4%207h4v4H4V7zm1%201v2h2V8H5zm-1%205h4v4H4v-4zm1%201v2h2v-2H5z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.description-icon {
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M14%202H6a2%202%200%200%200-2%202v16a2%202%200%200%200%202%202h12a2%202%200%200%200%202-2V8l-6-6zm-1%207V3.5L18.5%209H13zm-5%204h8v2H8v-2zm0%204h8v2H8v-2z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M14%202H6a2%202%200%200%200-2%202v16a2%202%200%200%200%202%202h12a2%202%200%200%200%202-2V8l-6-6zm-1%207V3.5L18.5%209H13zm-5%204h8v2H8v-2zm0%204h8v2H8v-2z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.treatment-input,
.measure-input,
.order-input {
box-sizing: border-box;
width: 100%;
border: 1px solid #c2c6d4;
border-radius: 8px;
background: #ffffff;
color: #191c21;
font-size: 16px;
line-height: 24px;
}
.input-wrap {
position: relative;
}
.treatment-input {
height: 56px;
padding: 0 16px;
}
.measure-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.measure-row {
display: flex;
align-items: center;
gap: 8px;
}
.measure-index {
flex: 0 0 auto;
width: 24px;
height: 24px;
border-radius: 4px;
background: #e7e8f0;
color: #424752;
font-size: 12px;
line-height: 24px;
font-weight: 600;
text-align: center;
}
.measure-input {
flex: 1;
height: 40px;
padding: 0 12px;
font-size: 14px;
line-height: 20px;
}
.order-input {
height: 116px;
padding: 14px 16px;
font-size: 14px;
line-height: 20px;
}
.input-placeholder {
color: #c2c6d4;
}
.next-button {
width: 100%;
min-height: 56px;
margin-top: 8px;
padding: 0;
border: 0;
border-radius: 999px;
background: #00478d;
box-shadow: 0 8px 18px rgba(0, 71, 141, 0.22);
color: #ffffff;
font-size: 16px;
line-height: 24px;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.next-button.submitted {
background: #006970;
}
.arrow-icon,
.check-small-icon {
width: 20px;
height: 20px;
background: #ffffff;
}
.arrow-icon {
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='m12%204-1.41%201.41L16.17%2011H4v2h12.17l-5.58%205.59L12%2020l8-8-8-8z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='m12%204-1.41%201.41L16.17%2011H4v2h12.17l-5.58%205.59L12%2020l8-8-8-8z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.check-small-icon {
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M9%2016.2 4.8%2012l-1.4%201.4L9%2019 21%207l-1.4-1.4L9%2016.2z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M9%2016.2 4.8%2012l-1.4%201.4L9%2019 21%207l-1.4-1.4L9%2016.2z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.spinner {
width: 20px;
height: 20px;
border: 2px solid rgba(255, 255, 255, 0.36);
border-top-color: #ffffff;
border-radius: 50%;
box-sizing: border-box;
animation: spin 1s linear infinite;
}
.toast {
position: fixed;
left: 50%;
bottom: 96px;
z-index: 100;
max-width: 320px;
padding: 12px 24px;
border-radius: 999px;
background: #2e3037;
color: #eff0f8;
font-size: 14px;
line-height: 20px;
font-weight: 600;
text-align: center;
pointer-events: none;
opacity: 0;
transform: translate(-50%, 16px);
transition: opacity 0.3s ease, transform 0.3s ease;
}
.toast.visible {
opacity: 1;
transform: translate(-50%, 0);
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
</style>