| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- <template>
- <div class="detail-section" v-if="selectedElderly">
- <div class="detail-header">
- <h2>{{ selectedElderly.name }}的详细信息</h2>
- <div class="header-actions">
- <el-button
- :type="showLocationMap ? 'default' : 'primary'"
- size="small"
- @click="toggleLocationMap"
- >
- <Icon :icon="showLocationMap ? 'mdi:format-list-bulleted' : 'mdi:map-marker'" />
- <span style="margin-left: 6px">{{ showLocationMap ? '返回详情' : '长者位置' }}</span>
- </el-button>
- </div>
- </div>
- <!-- 地图模式:替换下方全部内容 -->
- <div v-if="showLocationMap" class="map-full-wrapper">
- <ElderLocationMap :elder-id="selectedElderly.id" />
- </div>
- <!-- 详情模式:健康指标 + 设备监控 -->
- <template v-else>
- <!-- 健康指标 -->
- <div class="health-metrics">
- <h3>健康指标</h3>
- <div
- class="metrics-grid"
- v-if="selectedElderly.healthList && selectedElderly.healthList?.length"
- >
- <div
- class="metric-card"
- v-for="(metric, index) in selectedElderly.healthList"
- :key="index"
- >
- <div class="metric-icon">
- <Icon :icon="getHealthIcon(metric)" />
- </div>
- <div class="metric-info">
- <div class="metric-value">{{ metric.value }}{{ metric.unit }}</div>
- <div class="metric-name">{{ metric.name }}</div>
- </div>
- <div class="metric-trend" :class="metric.status.includes('警') ? 'warning' : 'normal'">
- {{ metric.status }}
- </div>
- </div>
- </div>
- <el-empty v-else description="暂无健康指标" :image-size="40" />
- </div>
- <!-- 设备监控 -->
- <div class="devices-section-large">
- <div class="devices-header">
- <h3>设备监控</h3>
- <el-button type="primary" size="large" @click="addDevice">
- <Icon icon="ep:plus" />
- <span>添加设备</span>
- </el-button>
- </div>
- <div
- class="devices-grid-large"
- v-if="selectedElderly.deviceList && selectedElderly.deviceList?.length"
- >
- <DeviceCard
- v-for="(device, index) in selectedElderly.deviceList"
- :key="index"
- :device="device"
- :selectedElderly="selectedElderly"
- :device-type-options="deviceTypeOptions"
- @show-detail="showDeviceDetail"
- @remove="removeDevice"
- />
- </div>
- <el-empty v-else description="暂无设备" :image-size="40" />
- </div>
- </template>
- </div>
- <div class="detail-placeholder" v-else>
- <div class="placeholder-content">
- <div class="placeholder-icon">
- <Icon icon="mdi:gesture-tap" />
- </div>
- <h3>请选择一位老人查看详细信息</h3>
- <p>点击左侧老人卡片查看健康数据和设备状态</p>
- </div>
- </div>
- </template>
- <script lang="ts" setup>
- import { defineAsyncComponent, ref } from 'vue'
- const DeviceCard = defineAsyncComponent(() => import('./DeviceCard.vue'))
- const ElderLocationMap = defineAsyncComponent(() => import('./ElderLocationMap.vue'))
- const showLocationMap = ref(false)
- const toggleLocationMap = () => {
- showLocationMap.value = !showLocationMap.value
- }
- interface HealthVO {
- name: string
- value: string
- status: string
- unit: string
- }
- interface DetailDevice {
- deviceType: string
- installPosition: string
- status: string
- indicatorText: string
- deviceCode: string
- }
- interface SelectElderly {
- id: number
- healthList: HealthVO[]
- name: string
- deviceList: DetailDevice[]
- }
- interface DeviceTypeVO {
- deviceType: string
- deviceTypeName: string
- displayOrder: number
- }
- interface Props {
- selectedElderly: SelectElderly | null
- deviceTypeOptions?: DeviceTypeVO[]
- }
- const props = defineProps<Props>()
- const emit = defineEmits<{
- (e: 'addDevice', elderly: SelectElderly): void
- (e: 'showDeviceDetail', device: DetailDevice): void
- (e: 'removeDevice', elderly: SelectElderly, device: DetailDevice): void
- }>()
- const healthIconMap: Record<string, string> = {
- 血氧: 'mdi:oxygen-tank',
- 心率: 'mdi:heart-pulse',
- 血压: 'mdi:blood-bag',
- 体温: 'mdi:thermometer',
- 血糖: 'mdi:needle',
- 血脂: 'mdi:blood-bag',
- 呼吸频率: 'mdi:lungs',
- 步数: 'mdi:walk',
- 睡眠: 'mdi:sleep',
- 体重: 'mdi:scale',
- 身高: 'mdi:human-male-height',
- BMI: 'mdi:human-male-height-variant',
- 运动量: 'mdi:run',
- 卡路里: 'mdi:fire',
- 水分摄入: 'mdi:cup-water',
- 血氧饱和度: 'mdi:oxygen-tank',
- 心电图: 'mdi:heart-flash',
- 肺功能: 'mdi:lungs',
- 骨密度: 'mdi:bone',
- 视力: 'mdi:eye',
- 听力: 'mdi:ear-hearing',
- 胆固醇: 'mdi:blood-bag',
- 尿酸: 'mdi:flask',
- 肝功能: 'mdi:liver',
- 肾功能: 'mdi:kidney',
- 血糖波动: 'mdi:chart-line',
- 血压波动: 'mdi:chart-areaspline',
- 心率变异性: 'mdi:chart-bell-curve',
- 睡眠时长: 'mdi:clock-sleep',
- 深睡时长: 'mdi:sleep',
- 浅睡时长: 'mdi:sleep',
- REM睡眠: 'mdi:sleep',
- 入睡时间: 'mdi:clock-start',
- 醒来时间: 'mdi:clock-end',
- 夜间醒来次数: 'mdi:alert-circle',
- 日间活动量: 'mdi:walk',
- 静息心率: 'mdi:heart',
- 最大心率: 'mdi:heart',
- 最低心率: 'mdi:heart',
- 收缩压: 'mdi:blood-bag',
- 舒张压: 'mdi:blood-bag',
- 平均血压: 'mdi:blood-bag',
- 血糖餐前: 'mdi:food',
- 血糖餐后: 'mdi:food',
- 血糖空腹: 'mdi:food-off',
- 血氧夜间: 'mdi:weather-night',
- 血氧日间: 'mdi:weather-sunny',
- 呼吸暂停: 'mdi:alert',
- 打鼾指数: 'mdi:volume-high',
- 体温晨起: 'mdi:weather-sunset-up',
- 体温晚间: 'mdi:weather-sunset-down',
- 体重变化: 'mdi:chart-line',
- BMI趋势: 'mdi:trending-up',
- 水分平衡: 'mdi:water-percent',
- 运动强度: 'mdi:run-fast',
- 卡路里消耗: 'mdi:fire',
- 睡眠效率: 'mdi:percent',
- 睡眠评分: 'mdi:star',
- 健康评分: 'mdi:star',
- 压力指数: 'mdi:alert',
- 情绪状态: ' mdi:emoticon-happy',
- 认知功能: 'mdi:brain',
- 平衡能力: 'mdi:scale-balance',
- 握力: 'mdi:hand-back-right',
- 步行速度: 'mdi:speedometer',
- 日常活动: 'mdi:home',
- 用药依从性: 'mdi:pill',
- 复诊提醒: 'mdi:calendar',
- 紧急呼叫: 'mdi:alert-circle'
- }
- const getHealthIcon = (metric: HealthVO) => {
- return healthIconMap[metric.name] || 'mdi:help-circle-outline'
- }
- const addDevice = () => {
- if (!props.selectedElderly) return
- emit('addDevice', props.selectedElderly)
- }
- const showDeviceDetail = (device: DetailDevice) => {
- emit('showDeviceDetail', device)
- }
- const removeDevice = (elderly: SelectElderly, device: DetailDevice) => {
- emit('removeDevice', elderly, device)
- }
- </script>
- <style lang="scss" scoped>
- $primary-color: #1a73e8;
- $text-light: #fff;
- $text-gray: #8a8f98;
- $success-color: #26de81;
- $warning-color: #fd9644;
- .detail-section {
- display: flex;
- padding: 25px;
- overflow-y: auto;
- background: rgb(26 31 46 / 85%);
- border: 1px solid rgb(255 255 255 / 12%);
- border-radius: 16px;
- flex-direction: column;
- gap: 25px;
- }
- .detail-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding-bottom: 15px;
- border-bottom: 1px solid rgb(255 255 255 / 10%);
- h2 {
- font-size: 28px;
- }
- }
- .map-full-wrapper {
- width: 100%;
- height: 600px;
- border-radius: 12px;
- overflow: hidden;
- }
- .health-metrics h3,
- .devices-section-large h3 {
- margin-bottom: 15px;
- font-size: 22px;
- }
- .devices-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 15px;
- }
- .metrics-grid {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- gap: 15px;
- margin-bottom: 20px;
- }
- .metric-card {
- display: flex;
- align-items: center;
- padding: 20px;
- background: rgb(255 255 255 / 5%);
- border-radius: 12px;
- gap: 15px;
- }
- .metric-icon {
- :deep(svg),
- :deep(span) {
- width: 32px !important;
- height: 32px !important;
- }
- }
- .metric-info {
- flex: 1;
- .metric-value {
- margin-bottom: 5px;
- font-size: 24px;
- font-weight: 600;
- }
- .metric-name {
- color: $text-gray;
- }
- }
- .metric-trend {
- padding: 5px 10px;
- font-size: 14px;
- border-radius: 20px;
- &.normal {
- color: $success-color;
- background: rgb(38 222 129 / 20%);
- }
- &.warning {
- color: $warning-color;
- background: rgb(253 150 68 / 20%);
- }
- }
- .devices-grid-large {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- gap: 20px;
- }
- .detail-placeholder {
- display: flex;
- align-items: center;
- justify-content: center;
- background: rgb(26 31 46 / 85%);
- border: 1px solid rgb(255 255 255 / 12%);
- border-radius: 16px;
- .placeholder-content {
- text-align: center;
- .placeholder-icon {
- margin-bottom: 20px;
- :deep(svg),
- :deep(span) {
- width: 32px !important;
- height: 32px !important;
- }
- }
- h3 {
- margin-bottom: 10px;
- font-size: 24px;
- }
- p {
- color: $text-gray;
- }
- }
- }
- .detail-section::-webkit-scrollbar {
- width: 8px;
- }
- .detail-section::-webkit-scrollbar-track {
- background: rgb(255 255 255 / 5%);
- border-radius: 4px;
- }
- .detail-section::-webkit-scrollbar-thumb {
- background: rgb(26 115 232 / 50%);
- border-radius: 4px;
- }
- </style>
|