feat: 新增教学模式

This commit is contained in:
王天骄
2026-06-01 15:35:17 +08:00
parent 3fa96ba8bc
commit e5470f9f62
4 changed files with 947 additions and 10 deletions
+79 -3
View File
@@ -6,6 +6,13 @@
@open-profile="emit('open-profile')"
@go-home="emit('go-home')"
/>
<TeachingPage
v-else-if="showTeachingPage"
:case-item="selectedCase"
@open-settings="emit('open-settings')"
@open-profile="emit('open-profile')"
@go-home="emit('go-home')"
/>
<view v-else class="cases-page">
<view class="case-shell">
<view class="case-header">
@@ -40,6 +47,7 @@
v-for="item in filteredCases"
:key="item.id"
class="case-card"
:class="`mode-${item.mode}`"
@click="selectCase(item)"
>
<view class="case-main">
@@ -55,6 +63,10 @@
</view>
<view class="case-footer">
<text class="case-no">病例编号: {{ item.caseNo }}</text>
<view class="mode-badge" :class="`mode-badge-${item.mode}`">
<view class="mode-icon" :class="`mode-icon-${item.mode}`"></view>
<text>{{ getModeLabel(item.mode) }}</text>
</view>
</view>
</view>
@@ -71,8 +83,10 @@
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { fetchCaseList, type ClinicalCase } from '../../api/cases'
import { onLoad } from '@dcloudio/uni-app'
import { fetchCaseList, type CaseMode, type ClinicalCase } from '../../api/cases'
import ScenarioPage from '../scenario/scenario.vue'
import TeachingPage from '../teaching/teaching.vue'
const emit = defineEmits<{
(event: 'open-settings'): void
@@ -86,14 +100,17 @@ const toastMessage = ref('')
const toastVisible = ref(false)
const selectedCase = ref<ClinicalCase | null>(null)
const showScenarioPage = ref(false)
const showTeachingPage = ref(false)
const modeFilter = ref<CaseMode | ''>('')
let toastTimer: ReturnType<typeof setTimeout> | null = null
const filteredCases = computed(() => {
const value = keyword.value.trim().toLowerCase()
if (!value) return cases.value
const source = modeFilter.value ? cases.value.filter(item => item.mode === modeFilter.value) : cases.value
if (!value) return source
return cases.value.filter(item => {
return source.filter(item => {
return [
item.title,
item.patientName,
@@ -106,6 +123,10 @@ const filteredCases = computed(() => {
})
})
function getModeLabel(mode: CaseMode) {
return mode === 'teaching' ? '教学模式' : '训练模式'
}
function loadCases() {
fetchCaseList().then(result => {
cases.value = result
@@ -114,7 +135,12 @@ function loadCases() {
function selectCase(item: ClinicalCase) {
uni.setStorageSync('clinical-thinking-selected-case', item)
uni.setStorageSync('clinical-thinking-case-mode', item.mode)
selectedCase.value = item
if (item.mode === 'teaching') {
showTeachingPage.value = true
return
}
showScenarioPage.value = true
}
@@ -131,6 +157,13 @@ function showToast(message: string) {
}, 2200)
}
onLoad(query => {
const mode = query?.mode
if (mode === 'teaching' || mode === 'training') {
modeFilter.value = mode
}
})
onMounted(loadCases)
onUnmounted(() => {
@@ -296,6 +329,10 @@ page {
gap: 8px;
}
.case-card.mode-teaching {
border-color: rgba(0, 71, 141, 0.22);
}
.case-card:active {
transform: scale(0.99);
}
@@ -411,6 +448,45 @@ page {
font-weight: 600;
}
.mode-badge {
flex: 0 0 auto;
height: 24px;
padding: 0 8px;
border-radius: 999px;
display: flex;
align-items: center;
gap: 4px;
font-size: 11px;
line-height: 16px;
font-weight: 700;
}
.mode-badge-training {
background: #ecedf6;
color: #424752;
}
.mode-badge-teaching {
background: #d6e3ff;
color: #00478d;
}
.mode-icon {
width: 14px;
height: 14px;
background: currentColor;
}
.mode-icon-training {
-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.2l-3.5-3.5L4%2014.2%209%2019%2020%208l-1.5-1.5L9%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.2l-3.5-3.5L4%2014.2%209%2019%2020%208l-1.5-1.5L9%2016.2z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.mode-icon-teaching {
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M12%203L1%209l11%206%209-4.91V17h2V9L12%203zm0%202.28L18.85%209%2012%2012.72%205.15%209%2012%205.28zM5%2013.18v3.2C6.63%2018.24%209.26%2019%2012%2019s5.37-.76%207-2.62v-3.2l-7%203.82-7-3.82z'/%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%203L1%209l11%206%209-4.91V17h2V9L12%203zm0%202.28L18.85%209%2012%2012.72%205.15%209%2012%205.28zM5%2013.18v3.2C6.63%2018.24%209.26%2019%2012%2019s5.37-.76%207-2.62v-3.2l-7%203.82-7-3.82z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.empty-state {
padding: 40px 0;
text-align: center;