Files
vueapp/pages/assessment/assessment.vue
T
2026-06-08 16:39:10 +08:00

780 lines
18 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>
<view class="assessment-page">
<view class="top-app-bar">
<button class="icon-button" aria-label="返回" @click="goBack">
<view class="back-icon"></view>
</button>
<text class="app-title">AI 学习助手</text>
<view class="header-actions">
<button class="icon-button" aria-label="分享" @click="showToast('分享功能即将开放')">
<view class="share-icon"></view>
</button>
<button class="icon-button" aria-label="更多" @click="showToast('更多报告操作即将开放')">
<view class="more-icon"></view>
</button>
</view>
</view>
<scroll-view class="report-content" scroll-y>
<view class="report-head">
<text class="report-title">训练总结与 AI 评估报告</text>
<view class="report-meta">
<text>评估日期{{ report.date }}</text>
<text>模拟编号{{ report.no }}</text>
</view>
</view>
<view class="content-stack">
<view class="overall-card">
<view class="score-ring">
<svg class="ring-svg" viewBox="0 0 100 100">
<circle class="ring-track" cx="50" cy="50" fill="transparent" r="42" stroke-width="10"></circle>
<circle
class="ring-value"
cx="50"
cy="50"
fill="transparent"
r="42"
stroke-dasharray="264"
:stroke-dashoffset="scoreDashOffset"
stroke-linecap="round"
stroke-width="10"
></circle>
</svg>
<view class="score-center">
<text class="score-value">{{ report.score }}</text>
<text class="score-total">/100</text>
</view>
</view>
<view class="overall-copy">
<text class="overall-title">本次考核评价<text class="primary-text">{{ report.level }}</text></text>
<text class="overall-desc">表现已达到临床住院医师中高级水平需在复杂病例鉴别诊断上精进</text>
</view>
</view>
<view class="report-card">
<view class="section-heading">
<view class="hub-icon"></view>
<text>临床胜任力雷达图</text>
</view>
<view class="radar-wrap">
<svg class="radar-svg" viewBox="0 0 400 400">
<circle class="radar-grid" cx="200" cy="200" r="160"></circle>
<circle class="radar-grid" cx="200" cy="200" r="120"></circle>
<circle class="radar-grid" cx="200" cy="200" r="80"></circle>
<circle class="radar-grid" cx="200" cy="200" r="40"></circle>
<line class="radar-grid" x1="200" x2="200" y1="200" y2="40"></line>
<line class="radar-grid" x1="200" x2="352" y1="200" y2="150"></line>
<line class="radar-grid" x1="200" x2="294" y1="200" y2="330"></line>
<line class="radar-grid" x1="200" x2="106" y1="200" y2="330"></line>
<line class="radar-grid" x1="200" x2="48" y1="200" y2="150"></line>
<polygon class="radar-area" points="200,60 340,160 270,300 120,310 60,170"></polygon>
<text class="radar-label" text-anchor="middle" x="200" y="30">病史采集</text>
<text class="radar-label" text-anchor="start" x="355" y="155">体格检查</text>
<text class="radar-label" text-anchor="middle" x="310" y="360">临床思维</text>
<text class="radar-label" text-anchor="middle" x="90" y="360">诊断准确</text>
<text class="radar-label" text-anchor="end" x="40" y="155">治疗方案</text>
</svg>
</view>
</view>
<view class="report-card">
<view class="section-heading breakdown-title">
<view class="analytics-icon"></view>
<text>分项得分与解析</text>
</view>
<view class="breakdown-list">
<view
v-for="item in breakdownItems"
:key="item.label"
class="breakdown-item"
>
<view class="breakdown-head">
<text>{{ item.label }}</text>
<text class="breakdown-score">{{ item.score }}%</text>
</view>
<view class="progress-track">
<view class="progress-fill" :style="{ width: barsReady ? `${item.score}%` : '0%' }"></view>
</view>
<view class="analysis-box">
<text>{{ item.analysis }}</text>
</view>
</view>
</view>
</view>
<view class="mentor-section">
<view class="mentor-head">
<image class="mentor-avatar" src="/static/config-doctor.png" mode="aspectFill"></image>
<view class="mentor-title-group">
<text class="mentor-title">王主任点评</text>
<text class="mentor-subtitle">Director's Mentorship</text>
</view>
</view>
<view class="mentor-bubble">
<view class="mentor-tail"></view>
<text class="mentor-copy">"你在病史采集环节表现出了非常好的逻辑性和条理性。不过,在鉴别诊断方面仍有提升空间。继续保持这种严谨态度。"</text>
<view class="mentor-action-row">
<button class="read-button" @click="showToast('正在为你整理相关知识点')">
<text>去查阅</text>
<view class="arrow-forward-icon"></view>
</button>
</view>
</view>
</view>
</view>
</scroll-view>
<view class="footer-actions">
<button class="download-button" @click="showToast('完整 PDF 报告生成中')">
下载完整 PDF 报告
</button>
<button class="next-button" @click="goHome">
开始下一轮强化训练
</button>
</view>
<view class="toast" :class="{ visible: toastVisible }">{{ toastMessage }}</view>
</view>
</template>
<script setup lang="ts">
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue'
const report = {
date: '2023年10月24日',
no: 'STJ-99283-X',
score: 88,
level: '优良'
}
const breakdownItems = [
{
label: '病史采集',
score: 92,
analysis: '问诊逻辑清晰主诉把握精准成功识别了诱发因素及既往史'
},
{
label: '体格检查',
score: 85,
analysis: '操作规范但触诊顺序有微小疏漏建议更注重痛点的动态观察'
},
{
label: '临床思维',
score: 78,
analysis: '能够建立初步假设但在多系统受累时思维发散性略显不足'
},
{
label: '诊断准确性',
score: 82,
analysis: '核心诊断正确但在鉴别诊断中漏掉了罕见但致死性的并发症'
}
]
const barsReady = ref(false)
const toastMessage = ref('')
const toastVisible = ref(false)
const scoreDashOffset = computed(() => {
return 264 - (264 * report.score) / 100
})
let toastTimer: ReturnType<typeof setTimeout> | null = null
function goBack() {
if (typeof getCurrentPages === 'function' && getCurrentPages().length > 1) {
uni.navigateBack()
return
}
goHome()
}
function goHome() {
uni.reLaunch({
url: '/pages/home/home'
})
}
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)
}
onMounted(() => {
nextTick(() => {
setTimeout(() => {
barsReady.value = true
}, 120)
})
})
onUnmounted(() => {
if (toastTimer) clearTimeout(toastTimer)
})
</script>
<style scoped>
page {
min-height: 100%;
background: #f9f9ff;
}
.assessment-page {
position: relative;
width: 390px;
max-width: 100vw;
min-height: 100vh;
margin: 0 auto;
overflow: hidden;
background: #f9f9ff;
color: #191c21;
font-family: Inter, -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
-webkit-tap-highlight-color: transparent;
}
.assessment-page view,
.assessment-page text,
.assessment-page button,
.assessment-page scroll-view {
box-sizing: border-box;
}
.assessment-page ::-webkit-scrollbar {
width: 0;
height: 0;
background: transparent;
}
.top-app-bar {
position: fixed;
left: 50%;
top: 0;
z-index: 50;
width: 390px;
max-width: 100vw;
height: 56px;
padding: 0 16px;
border-bottom: 1px solid rgba(194, 198, 212, 0.3);
background: rgba(249, 249, 255, 0.82);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
display: flex;
align-items: center;
transform: translateX(-50%);
}
.app-title {
margin-left: 8px;
color: #191c21;
font-size: 20px;
line-height: 28px;
font-weight: 700;
letter-spacing: 0;
}
.header-actions {
margin-left: auto;
display: flex;
align-items: center;
gap: 8px;
}
.icon-button,
.read-button,
.download-button,
.next-button {
padding: 0;
border: 0;
background: transparent;
}
.icon-button::after,
.read-button::after,
.download-button::after,
.next-button::after {
border: 0;
}
.icon-button {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.icon-button:active {
background: rgba(225, 226, 234, 0.5);
}
.back-icon,
.share-icon,
.more-icon,
.hub-icon,
.analytics-icon,
.arrow-forward-icon {
background: #424752;
-webkit-mask-position: center;
-webkit-mask-repeat: no-repeat;
-webkit-mask-size: contain;
mask-position: center;
mask-repeat: no-repeat;
mask-size: contain;
}
.back-icon {
width: 24px;
height: 24px;
background: #191c21;
-webkit-mask-image: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M20%2011H7.83l5.59-5.59L12%204l-8%208%208%208%201.42-1.41L7.83%2013H20v-2z'/%3E%3C/svg%3E");
mask-image: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M20%2011H7.83l5.59-5.59L12%204l-8%208%208%208%201.42-1.41L7.83%2013H20v-2z'/%3E%3C/svg%3E");
}
.share-icon {
width: 22px;
height: 22px;
-webkit-mask-image: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M18%2016.08c-.76%200-1.44.3-1.96.77L8.91%2012.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11A2.99%202.99%200%201%200%2015%205c0%20.24.04.47.09.7L8.04%209.81A3%203%200%201%200%208.04%2014l7.12%204.18c-.05.21-.08.43-.08.65a2.92%202.92%200%201%200%202.92-2.75z'/%3E%3C/svg%3E");
mask-image: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M18%2016.08c-.76%200-1.44.3-1.96.77L8.91%2012.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11A2.99%202.99%200%201%200%2015%205c0%20.24.04.47.09.7L8.04%209.81A3%203%200%201%200%208.04%2014l7.12%204.18c-.05.21-.08.43-.08.65a2.92%202.92%200%201%200%202.92-2.75z'/%3E%3C/svg%3E");
}
.more-icon {
width: 24px;
height: 24px;
-webkit-mask-image: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M12%208a2%202%200%201%200%200-4%202%202%200%200%200%200%204zm0%202a2%202%200%201%200%200%204%202%202%200%200%200%200-4zm0%206a2%202%200%201%200%200%204%202%202%200%200%200%200-4z'/%3E%3C/svg%3E");
mask-image: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M12%208a2%202%200%201%200%200-4%202%202%200%200%200%200%204zm0%202a2%202%200%201%200%200%204%202%202%200%200%200%200-4zm0%206a2%202%200%201%200%200%204%202%202%200%200%200%200-4z'/%3E%3C/svg%3E");
}
.report-content {
width: 100%;
height: calc(100vh - 145px);
margin-top: 56px;
padding: 24px 16px 24px;
position: relative;
z-index: 0;
}
.report-head {
padding: 0 0 24px;
display: flex;
flex-direction: column;
gap: 8px;
}
.report-title {
color: #00478d;
font-size: 24px;
line-height: 32px;
font-weight: 700;
letter-spacing: 0;
}
.report-meta {
display: flex;
flex-direction: column;
gap: 2px;
color: rgba(66, 71, 82, 0.8);
font-size: 14px;
line-height: 20px;
}
.content-stack {
display: flex;
flex-direction: column;
gap: 16px;
}
.overall-card,
.report-card,
.mentor-section {
border: 1px solid rgba(194, 198, 212, 0.3);
border-radius: 8px;
background: #ffffff;
box-shadow: 0 1px 4px rgba(25, 28, 33, 0.04);
}
.overall-card {
padding: 20px;
display: flex;
align-items: center;
gap: 16px;
}
.score-ring {
position: relative;
width: 80px;
height: 80px;
flex: 0 0 auto;
}
.ring-svg {
width: 100%;
height: 100%;
transform: rotate(-90deg);
transform-origin: 50% 50%;
}
.ring-track {
stroke: #e7e8f0;
}
.ring-value {
stroke: #00478d;
transition: stroke-dashoffset 0.8s ease;
}
.score-center {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.score-value {
color: #00478d;
font-size: 20px;
line-height: 22px;
font-weight: 700;
}
.score-total {
color: #424752;
font-size: 10px;
line-height: 12px;
font-weight: 500;
}
.overall-copy {
flex: 1;
display: flex;
flex-direction: column;
gap: 6px;
}
.overall-title {
color: #191c21;
font-size: 14px;
line-height: 20px;
font-weight: 600;
}
.primary-text {
color: #00478d;
font-weight: 700;
}
.overall-desc {
color: #424752;
font-size: 13px;
line-height: 18px;
}
.report-card {
padding: 20px;
}
.section-heading {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 16px;
color: #00478d;
font-size: 14px;
line-height: 20px;
font-weight: 700;
}
.breakdown-title {
margin-bottom: 24px;
}
.hub-icon,
.analytics-icon {
width: 18px;
height: 18px;
background: #00478d;
}
.hub-icon {
-webkit-mask-image: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M12%202a3%203%200%200%201%202%205.24V9h3a3%203%200%201%201-2.83%204H14v3.76A3%203%200%201%201%2012%2016a2.9%202.9%200%200%201%201%20.18V13H9.83A3%203%200%201%201%2010%2011H13V7.24A3%203%200%200%201%2012%202z'/%3E%3C/svg%3E");
mask-image: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M12%202a3%203%200%200%201%202%205.24V9h3a3%203%200%201%201-2.83%204H14v3.76A3%203%200%201%201%2012%2016a2.9%202.9%200%200%201%201%20.18V13H9.83A3%203%200%201%201%2010%2011H13V7.24A3%203%200%200%201%2012%202z'/%3E%3C/svg%3E");
}
.analytics-icon {
-webkit-mask-image: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M3%203h2v18H3V3zm16%207h2v11h-2V10zM8%2013h2v8H8v-8zm5-6h2v14h-2V7z'/%3E%3C/svg%3E");
mask-image: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M3%203h2v18H3V3zm16%207h2v11h-2V10zM8%2013h2v8H8v-8zm5-6h2v14h-2V7z'/%3E%3C/svg%3E");
}
.radar-wrap {
display: flex;
justify-content: center;
padding: 8px 0;
}
.radar-svg {
width: 100%;
max-width: 240px;
aspect-ratio: 1;
}
.radar-grid {
stroke: #e2e8f0;
stroke-width: 1;
fill: none;
}
.radar-area {
fill: rgba(0, 71, 141, 0.15);
stroke: #00478d;
stroke-width: 2;
}
.radar-label {
fill: #424752;
font-size: 16px;
font-weight: 500;
letter-spacing: 0;
}
.breakdown-list {
display: flex;
flex-direction: column;
gap: 24px;
}
.breakdown-item {
display: flex;
flex-direction: column;
gap: 8px;
}
.breakdown-head {
display: flex;
align-items: center;
justify-content: space-between;
color: #191c21;
font-size: 12px;
line-height: 16px;
font-weight: 600;
}
.breakdown-score {
color: #00478d;
font-weight: 700;
}
.progress-track {
width: 100%;
height: 6px;
border-radius: 999px;
background: #e7e8f0;
overflow: hidden;
}
.progress-fill {
height: 100%;
border-radius: 999px;
background: #00478d;
transition: width 1s cubic-bezier(0.4, 0, 0.2, 1);
}
.analysis-box {
padding: 12px;
border: 1px solid rgba(225, 226, 234, 0.35);
border-radius: 8px;
background: rgba(242, 243, 251, 0.5);
color: #424752;
font-size: 13px;
line-height: 20px;
}
.mentor-section {
margin-bottom: 16px;
padding: 20px;
border-color: rgba(0, 71, 141, 0.1);
background: rgba(0, 94, 184, 0.06);
}
.mentor-head {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
}
.mentor-avatar {
width: 48px;
height: 48px;
border: 1px solid #ffffff;
border-radius: 50%;
box-shadow: 0 1px 4px rgba(25, 28, 33, 0.12);
}
.mentor-title-group {
display: flex;
flex-direction: column;
gap: 2px;
}
.mentor-title {
color: #00478d;
font-size: 14px;
line-height: 20px;
font-weight: 700;
}
.mentor-subtitle {
color: rgba(66, 71, 82, 0.7);
font-size: 10px;
line-height: 14px;
font-weight: 500;
letter-spacing: 0;
}
.mentor-bubble {
position: relative;
padding: 16px;
border: 1px solid rgba(0, 71, 141, 0.05);
border-radius: 8px;
background: #ffffff;
box-shadow: 0 1px 4px rgba(25, 28, 33, 0.04);
}
.mentor-tail {
position: absolute;
left: 24px;
top: -7px;
width: 12px;
height: 12px;
border-left: 1px solid rgba(0, 71, 141, 0.05);
border-top: 1px solid rgba(0, 71, 141, 0.05);
background: #ffffff;
transform: rotate(45deg);
}
.mentor-copy {
color: #191c21;
font-size: 14px;
line-height: 22px;
font-style: italic;
}
.mentor-action-row {
margin-top: 16px;
display: flex;
justify-content: flex-end;
}
.read-button {
min-height: 32px;
display: flex;
align-items: center;
gap: 4px;
color: #00478d;
font-size: 14px;
line-height: 20px;
font-weight: 600;
}
.read-button:active {
transform: scale(0.95);
}
.arrow-forward-icon {
width: 18px;
height: 18px;
background: #00478d;
-webkit-mask-image: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M12%204l1.41%201.41L8.83%2010H20v2H8.83l4.58%204.59L12%2018l-7-7%207-7z'/%3E%3C/svg%3E");
mask-image: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M12%204l1.41%201.41L8.83%2010H20v2H8.83l4.58%204.59L12%2018l-7-7%207-7z'/%3E%3C/svg%3E");
transform: rotate(180deg);
}
.footer-actions {
position: fixed;
left: 50%;
bottom: 0;
z-index: 80;
width: 390px;
max-width: 100vw;
padding: 16px;
border-top: 1px solid rgba(194, 198, 212, 0.3);
background: rgba(249, 249, 255, 0.9);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
display: flex;
gap: 12px;
transform: translateX(-50%);
}
.download-button,
.next-button {
flex: 1;
min-height: 48px;
border-radius: 8px;
font-size: 14px;
line-height: 20px;
font-weight: 600;
}
.download-button {
border: 1px solid #00478d;
background: transparent;
color: #00478d;
}
.next-button {
background: #00478d;
box-shadow: 0 2px 8px rgba(0, 71, 141, 0.2);
color: #ffffff;
}
.download-button:active,
.next-button:active {
transform: scale(0.98);
}
.toast {
position: fixed;
left: 50%;
bottom: 92px;
z-index: 100;
max-width: 320px;
padding: 12px 20px;
border-radius: 12px;
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);
}
@media (min-width: 768px) {
.assessment-page {
box-shadow: 0 24px 64px rgba(25, 28, 33, 0.18);
}
}
</style>