Files
medical-mall/pages/mall/merchant/health-management.uvue
2026-04-10 11:42:02 +08:00

905 lines
20 KiB
Plaintext
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="hm-page">
<!-- #ifdef MP-WEIXIN -->
<view class="detail-navbar">
<view class="detail-navbar-back" @click="uni.navigateBack()">
<text class="back-arrow"></text>
<text class="back-text">返回</text>
</view>
<text class="detail-navbar-title">健康管理</text>
<view style="width: 120rpx;"></view>
</view>
<!-- #endif -->
<scroll-view class="hm-scroll" direction="vertical">
<!-- 顶部用户选择器 -->
<view class="user-selector-card">
<view class="us-inner">
<view class="us-avatar-wrap">
<view class="us-avatar">{{ currentUser.name.substring(0, 1) }}</view>
</view>
<view class="us-info">
<text class="us-name">{{ currentUser.name }}</text>
<text class="us-sub">{{ currentUser.age }}岁 · {{ currentUser.gender }} · {{ currentUser.diagnosis }}</text>
</view>
<view class="us-switch-btn" @click="switchUser">切换</view>
</view>
<view class="us-tags-row">
<view v-for="tag in currentUser.tags" :key="tag" class="us-tag" :class="tag === '高风险' ? 'us-tag-danger' : ''">{{ tag }}</view>
</view>
</view>
<!-- 健康数据面板 -->
<view class="section-card mt16">
<view class="section-card-title">
<text class="sct-icon">📊</text>
<text class="sct-text">今日健康数据</text>
<text class="sct-time">{{ todayDate }}</text>
</view>
<view class="health-data-grid">
<view v-for="item in healthMetrics" :key="item.key" class="hd-item" :class="item.status === 'warning' ? 'hd-item-warning' : item.status === 'danger' ? 'hd-item-danger' : ''">
<text class="hd-icon">{{ item.icon }}</text>
<text class="hd-val">{{ item.value }}</text>
<text class="hd-unit">{{ item.unit }}</text>
<text class="hd-label">{{ item.label }}</text>
<view v-if="item.status !== 'normal'" class="hd-badge" :class="item.status === 'danger' ? 'hd-badge-danger' : 'hd-badge-warning'">{{ item.statusText }}</view>
</view>
</view>
</view>
<!-- 健康档案 -->
<view class="section-card mt16">
<view class="section-card-title">
<text class="sct-icon">📋</text>
<text class="sct-text">健康档案</text>
<text class="sct-action" @click="editRecord">编辑</text>
</view>
<view class="record-row">
<text class="record-label">身高/体重</text>
<text class="record-val">{{ healthRecord.height }}cm / {{ healthRecord.weight }}kg</text>
</view>
<view class="record-row">
<text class="record-label">血型</text>
<text class="record-val">{{ healthRecord.bloodType }}</text>
</view>
<view class="record-row">
<text class="record-label">主要诊断</text>
<text class="record-val">{{ healthRecord.mainDiagnosis }}</text>
</view>
<view class="record-row">
<text class="record-label">过敏史</text>
<text class="record-val">{{ healthRecord.allergy || '无已知过敏' }}</text>
</view>
<view class="record-row">
<text class="record-label">主治医生</text>
<text class="record-val">{{ healthRecord.doctor }}</text>
</view>
<view class="record-row">
<text class="record-label">就诊医院</text>
<text class="record-val">{{ healthRecord.hospital }}</text>
</view>
</view>
<!-- 慢病管理 -->
<view class="section-card mt16">
<view class="section-card-title">
<text class="sct-icon">🏥</text>
<text class="sct-text">慢病管理</text>
</view>
<view v-for="disease in chronicDiseases" :key="disease.name" class="disease-item">
<view class="disease-header">
<text class="disease-name">{{ disease.name }}</text>
<view class="disease-status" :class="disease.controlled ? 'ds-good' : 'ds-bad'">
{{ disease.controlled ? '控制良好' : '需关注' }}
</view>
</view>
<view class="disease-indicators">
<view v-for="ind in disease.indicators" :key="ind.name" class="di-row">
<text class="di-label">{{ ind.name }}</text>
<text class="di-val" :class="ind.abnormal ? 'di-val-warn' : ''">{{ ind.value }}</text>
<text class="di-ref">参考:{{ ind.reference }}</text>
</view>
</view>
</view>
</view>
<!-- 用药提醒 -->
<view class="section-card mt16">
<view class="section-card-title">
<text class="sct-icon">💊</text>
<text class="sct-text">用药提醒</text>
<text class="sct-action" @click="addMedication">+ 添加</text>
</view>
<view v-if="medications.length === 0" class="empty-tip">
<text class="empty-tip-text">暂无用药记录</text>
</view>
<view v-for="med in medications" :key="med.name" class="med-item">
<view class="med-left">
<view class="med-dot" :class="med.taken ? 'med-dot-done' : 'med-dot-pending'"></view>
</view>
<view class="med-info">
<text class="med-name">{{ med.name }}</text>
<text class="med-dose">{{ med.dose }} · {{ med.frequency }}</text>
</view>
<view class="med-time-col">
<text class="med-time">{{ med.nextTime }}</text>
<text class="med-status" :class="med.taken ? 'ms-done' : 'ms-pending'">{{ med.taken ? '已服' : '待服' }}</text>
</view>
</view>
</view>
<!-- 复查预约 -->
<view class="section-card mt16">
<view class="section-card-title">
<text class="sct-icon">📅</text>
<text class="sct-text">复查预约</text>
<text class="sct-action" @click="bookReview">+ 预约</text>
</view>
<view v-if="appointments.length === 0" class="empty-tip">
<text class="empty-tip-text">暂无复查预约</text>
</view>
<view v-for="appt in appointments" :key="appt.id" class="appt-item">
<view class="appt-date-col">
<text class="appt-month">{{ appt.month }}</text>
<text class="appt-day">{{ appt.day }}</text>
</view>
<view class="appt-info">
<text class="appt-title">{{ appt.title }}</text>
<text class="appt-desc">{{ appt.department }} · {{ appt.doctor }}</text>
<text class="appt-location">{{ appt.hospital }}</text>
</view>
<view class="appt-tag" :class="appt.status === 'upcoming' ? 'at-upcoming' : 'at-done'">
{{ appt.status === 'upcoming' ? '待复查' : '已完成' }}
</view>
</view>
</view>
<!-- 健康趋势图(简单柱状图) -->
<view class="section-card mt16">
<view class="section-card-title">
<text class="sct-icon">📈</text>
<text class="sct-text">本周血压趋势</text>
</view>
<view class="chart-area">
<view v-for="(item, idx) in bpTrend" :key="idx" class="bar-group">
<view class="bar-pair">
<view class="bar bar-sys" :style="'height: ' + (item.sys / 200 * 120) + 'rpx;'"></view>
<view class="bar bar-dia" :style="'height: ' + (item.dia / 200 * 120) + 'rpx;'"></view>
</view>
<text class="bar-label">{{ item.day }}</text>
</view>
</view>
<view class="chart-legend">
<view class="legend-item"><view class="legend-dot ld-sys"></view><text class="legend-text">收缩压</text></view>
<view class="legend-item"><view class="legend-dot ld-dia"></view><text class="legend-text">舒张压</text></view>
</view>
</view>
<view style="height: 60rpx;"></view>
</scroll-view>
</view>
</template>
<script lang="uts">
type HealthMetricType = {
key: string
icon: string
value: string
unit: string
label: string
status: string
statusText: string
}
type IndicatorType = {
name: string
value: string
reference: string
abnormal: boolean
}
type DiseaseType = {
name: string
controlled: boolean
indicators: IndicatorType[]
}
type MedicationType = {
name: string
dose: string
frequency: string
nextTime: string
taken: boolean
}
type AppointmentType = {
id: string
month: string
day: string
title: string
department: string
doctor: string
hospital: string
status: string
}
type BpPointType = {
day: string
sys: number
dia: number
}
type UserType = {
name: string
age: number
gender: string
diagnosis: string
tags: string[]
}
export default {
data() {
return {
todayDate: '' as string,
currentUser: {
name: '李奶奶',
age: 78,
gender: '女',
diagnosis: '高血压·糖尿病',
tags: ['慢病用户', '高风险', '长期服务']
} as UserType,
healthMetrics: [
{ key: 'bp', icon: '❤️', value: '148/92', unit: 'mmHg', label: '血压', status: 'warning', statusText: '偏高' },
{ key: 'blood_sugar', icon: '🩸', value: '7.8', unit: 'mmol/L', label: '血糖', status: 'warning', statusText: '偏高' },
{ key: 'heart_rate', icon: '💓', value: '76', unit: 'bpm', label: '心率', status: 'normal', statusText: '' },
{ key: 'spo2', icon: '🫁', value: '97', unit: '%', label: '血氧', status: 'normal', statusText: '' },
{ key: 'temp', icon: '🌡️', value: '36.5', unit: '°C', label: '体温', status: 'normal', statusText: '' },
{ key: 'weight', icon: '⚖️', value: '62.5', unit: 'kg', label: '体重', status: 'normal', statusText: '' }
] as HealthMetricType[],
healthRecord: {
height: 162,
weight: 62.5,
bloodType: 'A型',
mainDiagnosis: '高血压3级、2型糖尿病、骨质疏松',
allergy: '青霉素',
doctor: '王主任',
hospital: '嘉城医院内科'
},
chronicDiseases: [
{
name: '高血压',
controlled: false,
indicators: [
{ name: '收缩压', value: '148 mmHg', reference: '<140', abnormal: true },
{ name: '舒张压', value: '92 mmHg', reference: '<90', abnormal: true }
]
},
{
name: '2型糖尿病',
controlled: false,
indicators: [
{ name: '空腹血糖', value: '7.8 mmol/L', reference: '3.9-7.0', abnormal: true },
{ name: '糖化血红蛋白', value: '7.2%', reference: '<7.0%', abnormal: true }
]
}
] as DiseaseType[],
medications: [
{ name: '硝苯地平控释片', dose: '30mg', frequency: '每日一次', nextTime: '今日 08:00', taken: true },
{ name: '二甲双胍', dose: '500mg', frequency: '每日三次', nextTime: '今日 12:00', taken: false },
{ name: '阿司匹林肠溶片', dose: '100mg', frequency: '每日一次', nextTime: '今日 08:00', taken: true },
{ name: '骨化三醇胶丸', dose: '0.25μg', frequency: '每日两次', nextTime: '今日 20:00', taken: false }
] as MedicationType[],
appointments: [
{ id: '1', month: '04月', day: '20', title: '高血压复查', department: '心内科', doctor: '王主任', hospital: '嘉城医院', status: 'upcoming' },
{ id: '2', month: '04月', day: '28', title: '糖尿病随访', department: '内分泌科', doctor: '刘副主任', hospital: '嘉城医院', status: 'upcoming' }
] as AppointmentType[],
bpTrend: [
{ day: '周一', sys: 145, dia: 88 },
{ day: '周二', sys: 150, dia: 92 },
{ day: '周三', sys: 142, dia: 86 },
{ day: '周四', sys: 148, dia: 90 },
{ day: '周五', sys: 152, dia: 94 },
{ day: '周六', sys: 144, dia: 87 },
{ day: '周日', sys: 148, dia: 92 }
] as BpPointType[]
}
},
onLoad() {
const now = new Date()
const m = (now.getMonth() + 1).toString().padStart(2, '0')
const d = now.getDate().toString().padStart(2, '0')
this.todayDate = `${m}月${d}日`
},
methods: {
switchUser() {
uni.showToast({ title: '切换用户功能开发中', icon: 'none' })
},
editRecord() {
uni.showToast({ title: '编辑档案功能开发中', icon: 'none' })
},
addMedication() {
uni.showToast({ title: '添加用药功能开发中', icon: 'none' })
},
bookReview() {
uni.showToast({ title: '复查预约功能开发中', icon: 'none' })
}
}
}
</script>
<style>
.hm-page {
background-color: #f0f2f7;
min-height: 100vh;
}
.hm-scroll {
flex: 1;
}
.mt16 { margin-top: 16rpx; }
/* ===== 导航栏 ===== */
.detail-navbar {
display: flex;
flex-direction: row;
align-items: flex-end;
background-color: #ffffff;
border-bottom-width: 1rpx;
border-bottom-style: solid;
border-bottom-color: #eeeeee;
box-sizing: border-box;
padding-top: var(--status-bar-height);
height: calc(88rpx + var(--status-bar-height));
}
.detail-navbar-back {
display: flex;
flex-direction: row;
align-items: center;
padding: 0 30rpx;
height: 88rpx;
width: 120rpx;
}
.back-arrow {
font-size: 44rpx;
color: #333333;
line-height: 1;
margin-right: 4rpx;
}
.back-text {
font-size: 28rpx;
color: #333333;
}
.detail-navbar-title {
flex: 1;
text-align: center;
font-size: 32rpx;
font-weight: 600;
color: #1a1a1a;
height: 88rpx;
line-height: 88rpx;
}
/* ===== 用户选择器 ===== */
.user-selector-card {
background: linear-gradient(135deg, #A6F1E4 0%, #69DFC2 100%);
padding: 24rpx 30rpx 20rpx 30rpx;
}
.us-inner {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 16rpx;
}
.us-avatar-wrap {
margin-right: 20rpx;
}
.us-avatar {
width: 80rpx;
height: 80rpx;
border-radius: 40rpx;
background-color: rgba(255,255,255,0.3);
color: #ffffff;
font-size: 34rpx;
font-weight: 700;
line-height: 80rpx;
text-align: center;
}
.us-info {
flex: 1;
display: flex;
flex-direction: column;
}
.us-name {
font-size: 32rpx;
font-weight: 700;
color: #ffffff;
margin-bottom: 6rpx;
}
.us-sub {
font-size: 22rpx;
color: rgba(255,255,255,0.8);
}
.us-switch-btn {
font-size: 24rpx;
color: rgba(255,255,255,0.9);
border-width: 1rpx;
border-style: solid;
border-color: rgba(255,255,255,0.5);
border-radius: 20rpx;
padding: 8rpx 20rpx;
}
.us-tags-row {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.us-tag {
font-size: 20rpx;
color: rgba(255,255,255,0.9);
background-color: rgba(255,255,255,0.15);
border-radius: 4rpx;
padding: 4rpx 14rpx;
margin-right: 10rpx;
}
.us-tag-danger {
background-color: rgba(255,80,80,0.35);
color: #ffcccc;
}
/* ===== 通用卡片 ===== */
.section-card {
background-color: #ffffff;
}
.section-card-title {
display: flex;
flex-direction: row;
align-items: center;
padding: 22rpx 30rpx 18rpx 30rpx;
border-bottom-width: 1rpx;
border-bottom-style: solid;
border-bottom-color: #f0f0f0;
}
.sct-icon { font-size: 30rpx; margin-right: 10rpx; }
.sct-text {
font-size: 28rpx;
font-weight: 600;
color: #1a1a1a;
flex: 1;
}
.sct-time {
font-size: 22rpx;
color: #999;
}
.sct-action {
font-size: 24rpx;
color: #09C39D;
}
/* ===== 健康数据网格 ===== */
.health-data-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
padding: 16rpx 16rpx 8rpx 16rpx;
}
.hd-item {
width: 33.33%;
display: flex;
flex-direction: column;
align-items: center;
padding: 16rpx 10rpx;
position: relative;
}
.hd-item-warning {
background-color: #fffbf0;
}
.hd-item-danger {
background-color: #fff0f0;
}
.hd-icon {
font-size: 36rpx;
margin-bottom: 8rpx;
}
.hd-val {
font-size: 30rpx;
font-weight: 700;
color: #1a1a1a;
line-height: 1;
}
.hd-unit {
font-size: 18rpx;
color: #999;
margin-top: 2rpx;
margin-bottom: 4rpx;
}
.hd-label {
font-size: 22rpx;
color: #666;
}
.hd-badge {
position: absolute;
top: 10rpx;
right: 10rpx;
font-size: 18rpx;
padding: 2rpx 8rpx;
border-radius: 4rpx;
}
.hd-badge-warning {
color: #FF7800;
background-color: #fff3e0;
}
.hd-badge-danger {
color: #E64A19;
background-color: #fde8e0;
}
/* ===== 健康档案 ===== */
.record-row {
display: flex;
flex-direction: row;
align-items: center;
padding: 16rpx 30rpx;
border-bottom-width: 1rpx;
border-bottom-style: solid;
border-bottom-color: #f5f5f5;
}
.record-label {
font-size: 26rpx;
color: #999;
min-width: 150rpx;
flex-shrink: 0;
}
.record-val {
font-size: 26rpx;
color: #333;
flex: 1;
}
/* ===== 慢病管理 ===== */
.disease-item {
padding: 16rpx 30rpx;
border-bottom-width: 1rpx;
border-bottom-style: solid;
border-bottom-color: #f5f5f5;
}
.disease-header {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 12rpx;
}
.disease-name {
font-size: 28rpx;
font-weight: 600;
color: #1a1a1a;
flex: 1;
}
.disease-status {
font-size: 22rpx;
border-radius: 4rpx;
padding: 4rpx 14rpx;
}
.ds-good {
color: #4CAF50;
background-color: #e8f5e9;
}
.ds-bad {
color: #E64A19;
background-color: #fde8e0;
}
.disease-indicators {
background-color: #f9f9f9;
border-radius: 8rpx;
padding: 10rpx 16rpx;
}
.di-row {
display: flex;
flex-direction: row;
align-items: center;
padding: 6rpx 0;
}
.di-label {
font-size: 24rpx;
color: #666;
min-width: 140rpx;
}
.di-val {
font-size: 26rpx;
color: #333;
font-weight: 500;
flex: 1;
}
.di-val-warn {
color: #E64A19;
}
.di-ref {
font-size: 20rpx;
color: #bbb;
}
/* ===== 用药提醒 ===== */
.med-item {
display: flex;
flex-direction: row;
align-items: center;
padding: 20rpx 30rpx;
border-bottom-width: 1rpx;
border-bottom-style: solid;
border-bottom-color: #f5f5f5;
}
.med-left {
width: 36rpx;
display: flex;
align-items: center;
justify-content: center;
}
.med-dot {
width: 18rpx;
height: 18rpx;
border-radius: 9rpx;
}
.med-dot-done {
background-color: #4CAF50;
}
.med-dot-pending {
background-color: #FF7800;
}
.med-info {
flex: 1;
margin-left: 16rpx;
display: flex;
flex-direction: column;
}
.med-name {
font-size: 28rpx;
color: #1a1a1a;
margin-bottom: 6rpx;
}
.med-dose {
font-size: 22rpx;
color: #999;
}
.med-time-col {
display: flex;
flex-direction: column;
align-items: flex-end;
}
.med-time {
font-size: 22rpx;
color: #666;
margin-bottom: 4rpx;
}
.med-status {
font-size: 20rpx;
border-radius: 4rpx;
padding: 2rpx 10rpx;
}
.ms-done {
color: #4CAF50;
background-color: #e8f5e9;
}
.ms-pending {
color: #FF7800;
background-color: #fff3e0;
}
/* ===== 复查预约 ===== */
.appt-item {
display: flex;
flex-direction: row;
align-items: center;
padding: 20rpx 30rpx;
border-bottom-width: 1rpx;
border-bottom-style: solid;
border-bottom-color: #f5f5f5;
}
.appt-date-col {
width: 72rpx;
display: flex;
flex-direction: column;
align-items: center;
margin-right: 20rpx;
background-color: #E3F7ED;
border-radius: 8rpx;
padding: 8rpx 10rpx;
}
.appt-month {
font-size: 18rpx;
color: #09C39D;
}
.appt-day {
font-size: 34rpx;
font-weight: 700;
color: #09C39D;
line-height: 1;
}
.appt-info {
flex: 1;
display: flex;
flex-direction: column;
}
.appt-title {
font-size: 28rpx;
color: #1a1a1a;
font-weight: 500;
margin-bottom: 6rpx;
}
.appt-desc {
font-size: 22rpx;
color: #666;
margin-bottom: 4rpx;
}
.appt-location {
font-size: 20rpx;
color: #999;
}
.appt-tag {
font-size: 22rpx;
border-radius: 4rpx;
padding: 4rpx 14rpx;
}
.at-upcoming {
color: #09C39D;
background-color: #E3F7ED;
}
.at-done {
color: #999;
background-color: #f5f5f5;
}
/* ===== 血压趋势图 ===== */
.chart-area {
display: flex;
flex-direction: row;
align-items: flex-end;
justify-content: space-around;
padding: 20rpx 30rpx 10rpx 30rpx;
height: 180rpx;
}
.bar-group {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
.bar-pair {
display: flex;
flex-direction: row;
align-items: flex-end;
margin-bottom: 8rpx;
}
.bar {
width: 14rpx;
border-radius: 4rpx 4rpx 0 0;
margin: 0 2rpx;
min-height: 6rpx;
}
.bar-sys {
background-color: #09C39D;
}
.bar-dia {
background-color: #84a8f8;
}
.bar-label {
font-size: 18rpx;
color: #999;
}
.chart-legend {
display: flex;
flex-direction: row;
justify-content: center;
padding: 10rpx 30rpx 20rpx 30rpx;
}
.legend-item {
display: flex;
flex-direction: row;
align-items: center;
margin: 0 20rpx;
}
.legend-dot {
width: 16rpx;
height: 16rpx;
border-radius: 3rpx;
margin-right: 8rpx;
}
.ld-sys { background-color: #09C39D; }
.ld-dia { background-color: #84a8f8; }
.legend-text {
font-size: 22rpx;
color: #666;
}
/* ===== 通用 ===== */
.empty-tip {
padding: 40rpx 30rpx;
display: flex;
flex-direction: row;
justify-content: center;
}
.empty-tip-text {
font-size: 26rpx;
color: #bbb;
}
</style>