feat: 联调对话功能
This commit is contained in:
+110
-20
@@ -41,7 +41,7 @@
|
||||
|
||||
<view class="overall-copy">
|
||||
<text class="overall-title">本次考核评价:<text class="primary-text">{{ report.level }}</text></text>
|
||||
<text class="overall-desc">表现已达到临床住院医师中高级水平。需在复杂病例鉴别诊断上精进。</text>
|
||||
<text class="overall-desc">{{ report.overallComment }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -84,10 +84,10 @@
|
||||
>
|
||||
<view class="breakdown-head">
|
||||
<text>{{ item.label }}</text>
|
||||
<text class="breakdown-score">{{ item.score }}%</text>
|
||||
<text class="breakdown-score">{{ item.displayScore }}</text>
|
||||
</view>
|
||||
<view class="progress-track">
|
||||
<view class="progress-fill" :style="{ width: barsReady ? `${item.score}%` : '0%' }"></view>
|
||||
<view class="progress-fill" :style="{ width: barsReady ? `${item.percent}%` : '0%' }"></view>
|
||||
</view>
|
||||
<view class="analysis-box">
|
||||
<text>{{ item.analysis }}</text>
|
||||
@@ -107,7 +107,7 @@
|
||||
|
||||
<view class="mentor-bubble">
|
||||
<view class="mentor-tail"></view>
|
||||
<text class="mentor-copy">"你在病史采集环节表现出了非常好的逻辑性和条理性。不过,在鉴别诊断方面仍有提升空间。继续保持这种严谨态度。"</text>
|
||||
<text class="mentor-copy">{{ mentorComment }}</text>
|
||||
<view class="mentor-action-row">
|
||||
<button class="read-button" @click="openLearningAssistant">
|
||||
<text>去查阅</text>
|
||||
@@ -133,46 +133,96 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { computed, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'
|
||||
import { generateEvaluation, type EvaluationResult } from '../../api/assessment'
|
||||
import { readActiveSessionId } from '../../api/session'
|
||||
|
||||
const report = {
|
||||
date: '2023年10月24日',
|
||||
no: 'STJ-99283-X',
|
||||
score: 88,
|
||||
level: '优良'
|
||||
type BreakdownItem = {
|
||||
label: string
|
||||
percent: number
|
||||
displayScore: string
|
||||
analysis: string
|
||||
}
|
||||
|
||||
const breakdownItems = [
|
||||
const fallbackBreakdownItems: BreakdownItem[] = [
|
||||
{
|
||||
label: '病史采集',
|
||||
score: 92,
|
||||
percent: 92,
|
||||
displayScore: '92%',
|
||||
analysis: '问诊逻辑清晰,主诉把握精准。成功识别了诱发因素及既往史。'
|
||||
},
|
||||
{
|
||||
label: '体格检查',
|
||||
score: 85,
|
||||
percent: 85,
|
||||
displayScore: '85%',
|
||||
analysis: '操作规范,但触诊顺序有微小疏漏。建议更注重痛点的动态观察。'
|
||||
},
|
||||
{
|
||||
label: '临床思维',
|
||||
score: 78,
|
||||
percent: 78,
|
||||
displayScore: '78%',
|
||||
analysis: '能够建立初步假设,但在多系统受累时,思维发散性略显不足。'
|
||||
},
|
||||
{
|
||||
label: '诊断准确性',
|
||||
score: 82,
|
||||
percent: 82,
|
||||
displayScore: '82%',
|
||||
analysis: '核心诊断正确。但在鉴别诊断中漏掉了罕见但致死性的并发症。'
|
||||
}
|
||||
]
|
||||
|
||||
const report = reactive({
|
||||
date: formatReportDate(new Date()),
|
||||
no: 'STJ-99283-X',
|
||||
score: 88,
|
||||
level: '优良',
|
||||
overallComment: '表现已达到临床住院医师中高级水平。需在复杂病例鉴别诊断上精进。'
|
||||
})
|
||||
|
||||
const barsReady = ref(false)
|
||||
const toastMessage = ref('')
|
||||
const toastVisible = ref(false)
|
||||
const evaluation = ref<EvaluationResult | null>(null)
|
||||
|
||||
const scoreDashOffset = computed(() => {
|
||||
return 264 - (264 * report.score) / 100
|
||||
})
|
||||
|
||||
const mentorComment = computed(() => report.overallComment || '本次评价已生成,请结合分项得分继续强化训练。')
|
||||
|
||||
const breakdownItems = computed<BreakdownItem[]>(() => {
|
||||
const result = evaluation.value
|
||||
if (!result) return fallbackBreakdownItems
|
||||
|
||||
if (Array.isArray(result.dimension_scores) && result.dimension_scores.length > 0) {
|
||||
return result.dimension_scores.map(item => {
|
||||
const percent = item.max_score > 0 ? clampScore((Number(item.score) / Number(item.max_score)) * 100) : clampScore(Number(item.score))
|
||||
const analysis = [
|
||||
item.comment,
|
||||
item.improvement ? `改进建议:${item.improvement}` : ''
|
||||
].filter(Boolean).join(' ')
|
||||
|
||||
return {
|
||||
label: item.dimension,
|
||||
percent,
|
||||
displayScore: item.max_score > 0 ? `${item.score}/${item.max_score}` : `${item.score}%`,
|
||||
analysis: analysis || '暂无分项解析。'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (Array.isArray(result.score_details) && result.score_details.length > 0) {
|
||||
return result.score_details.map(item => ({
|
||||
label: item.dimension,
|
||||
percent: clampScore(Number(item.score)),
|
||||
displayScore: `${item.score}%`,
|
||||
analysis: item.comment || item.deducted_reason || '暂无分项解析。'
|
||||
}))
|
||||
}
|
||||
|
||||
return fallbackBreakdownItems
|
||||
})
|
||||
|
||||
let toastTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
function goBack() {
|
||||
@@ -200,21 +250,61 @@ 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(() => {
|
||||
function formatReportDate(date: Date) {
|
||||
return `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日`
|
||||
}
|
||||
|
||||
function clampScore(value: number) {
|
||||
if (!Number.isFinite(value)) return 0
|
||||
return Math.max(0, Math.min(100, Math.round(value)))
|
||||
}
|
||||
|
||||
function getLevel(score: number) {
|
||||
if (score >= 90) return '优秀'
|
||||
if (score >= 80) return '优良'
|
||||
if (score >= 70) return '良好'
|
||||
if (score >= 60) return '合格'
|
||||
return '待加强'
|
||||
}
|
||||
|
||||
function applyEvaluation(result: EvaluationResult) {
|
||||
evaluation.value = result
|
||||
report.no = `EV-${result.evaluation_id}`
|
||||
report.score = clampScore(Number(result.total_score))
|
||||
report.level = getLevel(report.score)
|
||||
report.overallComment = result.overall_comment || report.overallComment
|
||||
}
|
||||
|
||||
function animateBars() {
|
||||
barsReady.value = false
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
barsReady.value = true
|
||||
}, 120)
|
||||
})
|
||||
}
|
||||
|
||||
async function loadEvaluation() {
|
||||
try {
|
||||
const sessionId = readActiveSessionId()
|
||||
report.no = `SID-${sessionId}`
|
||||
const result = await generateEvaluation(sessionId, 'percentage')
|
||||
uni.setStorageSync('clinical-thinking-evaluation', result)
|
||||
applyEvaluation(result)
|
||||
} catch (error) {
|
||||
showToast(error instanceof Error ? error.message : '评价生成失败')
|
||||
} finally {
|
||||
animateBars()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
void loadEvaluation()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
|
||||
Reference in New Issue
Block a user