784 lines
16 KiB
Vue
784 lines
16 KiB
Vue
<template>
|
|
<view class="health-charts-container">
|
|
<!-- 顶部操作栏 -->
|
|
<view class="top-action-bar">
|
|
<view class="action-item" @click="showMoreActions = true">
|
|
<u-icon name="more-circle" size="20" color="#666666"></u-icon>
|
|
<text class="action-text">更多</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 宠物基本信息卡片 -->
|
|
<view class="pet-info-card">
|
|
<u-avatar :src="petInfo.avatar || '/static/default-pet.png'" size="60" shape="circle"></u-avatar>
|
|
<view class="pet-info">
|
|
<text class="pet-name">{{ petInfo.name }}</text>
|
|
<text class="pet-details">{{ petInfo.breed }} · {{ petInfo.age }}岁</text>
|
|
<view class="health-status" :class="petInfo.healthStatus || 'healthy'">
|
|
<u-icon :name="getHealthIcon(petInfo.healthStatus)" size="12" color="#ffffff"></u-icon>
|
|
<text class="status-text">{{ getHealthText(petInfo.healthStatus) }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 健康指标切换 -->
|
|
<view class="chart-tabs">
|
|
<u-tabs
|
|
:list="chartTabs"
|
|
:current="currentTab"
|
|
@change="onTabChange"
|
|
line-color="#FF8A80"
|
|
active-color="#FF8A80"
|
|
inactive-color="#999999"
|
|
></u-tabs>
|
|
</view>
|
|
|
|
<!-- 图表内容区域 -->
|
|
<view class="chart-content">
|
|
<!-- 体重趋势图 -->
|
|
<view class="chart-section" v-if="currentTab === 0">
|
|
<view class="chart-header">
|
|
<text class="chart-title">体重趋势</text>
|
|
<text class="chart-subtitle">最近6个月</text>
|
|
</view>
|
|
<view class="chart-container">
|
|
<canvas
|
|
canvas-id="weightChart"
|
|
id="weightChart"
|
|
class="chart-canvas"
|
|
@touchstart="touchChart"
|
|
@touchmove="touchChart"
|
|
@touchend="touchChart"
|
|
></canvas>
|
|
</view>
|
|
<view class="chart-stats">
|
|
<view class="stat-item">
|
|
<text class="stat-label">当前体重</text>
|
|
<text class="stat-value">{{ currentWeight }}kg</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="stat-label">体重变化</text>
|
|
<text class="stat-value" :class="weightChange >= 0 ? 'positive' : 'negative'">
|
|
{{ weightChange >= 0 ? '+' : '' }}{{ weightChange }}kg
|
|
</text>
|
|
</view>
|
|
<view class="stat-item">
|
|
<text class="stat-label">理想体重</text>
|
|
<text class="stat-value">{{ idealWeight }}kg</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 健康指标雷达图 -->
|
|
<view class="chart-section" v-if="currentTab === 1">
|
|
<view class="chart-header">
|
|
<text class="chart-title">健康指标</text>
|
|
<text class="chart-subtitle">综合评估</text>
|
|
</view>
|
|
<view class="chart-container">
|
|
<canvas
|
|
canvas-id="healthRadar"
|
|
id="healthRadar"
|
|
class="chart-canvas"
|
|
></canvas>
|
|
</view>
|
|
<view class="health-indicators">
|
|
<view class="indicator-item" v-for="indicator in healthIndicators" :key="indicator.name">
|
|
<view class="indicator-info">
|
|
<text class="indicator-name">{{ indicator.name }}</text>
|
|
<text class="indicator-score">{{ indicator.score }}/10</text>
|
|
</view>
|
|
<view class="indicator-bar">
|
|
<view class="indicator-progress" :style="{ width: indicator.score * 10 + '%' }"></view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 疫苗接种时间轴 -->
|
|
<view class="chart-section" v-if="currentTab === 2">
|
|
<view class="chart-header">
|
|
<text class="chart-title">疫苗接种</text>
|
|
<text class="chart-subtitle">接种记录与计划</text>
|
|
</view>
|
|
<view class="vaccine-timeline">
|
|
<view class="timeline-item" v-for="vaccine in vaccineRecords" :key="vaccine.id">
|
|
<view class="timeline-dot" :class="vaccine.status"></view>
|
|
<view class="timeline-content">
|
|
<text class="vaccine-name">{{ vaccine.name }}</text>
|
|
<text class="vaccine-date">{{ vaccine.date }}</text>
|
|
<text class="vaccine-status">{{ vaccine.statusText }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 体检记录 -->
|
|
<view class="chart-section" v-if="currentTab === 3">
|
|
<view class="chart-header">
|
|
<text class="chart-title">体检记录</text>
|
|
<text class="chart-subtitle">健康检查历史</text>
|
|
</view>
|
|
<view class="checkup-list">
|
|
<view class="checkup-item" v-for="checkup in checkupRecords" :key="checkup.id">
|
|
<view class="checkup-date">
|
|
<text class="date-text">{{ checkup.date }}</text>
|
|
</view>
|
|
<view class="checkup-info">
|
|
<text class="checkup-type">{{ checkup.type }}</text>
|
|
<text class="checkup-result">{{ checkup.result }}</text>
|
|
<text class="checkup-note" v-if="checkup.note">{{ checkup.note }}</text>
|
|
</view>
|
|
<view class="checkup-status" :class="checkup.status">
|
|
<u-icon :name="getCheckupIcon(checkup.status)" size="16" color="#ffffff"></u-icon>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 添加记录按钮 -->
|
|
<view class="add-record-btn" @click="addHealthRecord">
|
|
<u-icon name="plus" size="20" color="#ffffff"></u-icon>
|
|
</view>
|
|
|
|
<!-- 更多操作弹窗 -->
|
|
<u-popup v-model="showMoreActions" mode="bottom" border-radius="20">
|
|
<view class="more-actions-popup">
|
|
<view class="popup-header">
|
|
<text class="popup-title">更多操作</text>
|
|
<u-icon name="close" size="20" @click="showMoreActions = false"></u-icon>
|
|
</view>
|
|
<view class="action-list">
|
|
<view class="action-item" @click="exportHealthData">
|
|
<u-icon name="share" size="24" color="#64B5F6"></u-icon>
|
|
<text class="action-text">导出健康数据</text>
|
|
</view>
|
|
<view class="action-item" @click="setHealthReminder">
|
|
<u-icon name="bell" size="24" color="#FFB74D"></u-icon>
|
|
<text class="action-text">设置健康提醒</text>
|
|
</view>
|
|
<view class="action-item" @click="consultVet">
|
|
<u-icon name="phone" size="24" color="#81C784"></u-icon>
|
|
<text class="action-text">咨询兽医</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</u-popup>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
// 引入uCharts图表库
|
|
import uCharts from '@/uni_modules/qiun-data-charts/js_sdk/u-charts/u-charts.js'
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
petId: '',
|
|
petInfo: {},
|
|
currentTab: 0,
|
|
showMoreActions: false,
|
|
chartTabs: [
|
|
{ name: '体重趋势' },
|
|
{ name: '健康指标' },
|
|
{ name: '疫苗接种' },
|
|
{ name: '体检记录' }
|
|
],
|
|
// 体重相关数据
|
|
currentWeight: 4.5,
|
|
weightChange: 0.2,
|
|
idealWeight: 4.3,
|
|
weightData: [],
|
|
// 健康指标数据
|
|
healthIndicators: [
|
|
{ name: '食欲', score: 9 },
|
|
{ name: '精神状态', score: 8 },
|
|
{ name: '毛发光泽', score: 7 },
|
|
{ name: '活动量', score: 8 },
|
|
{ name: '睡眠质量', score: 9 }
|
|
],
|
|
// 疫苗记录
|
|
vaccineRecords: [
|
|
{
|
|
id: 1,
|
|
name: '三联疫苗',
|
|
date: '2024-01-15',
|
|
status: 'completed',
|
|
statusText: '已接种'
|
|
},
|
|
{
|
|
id: 2,
|
|
name: '狂犬疫苗',
|
|
date: '2024-02-15',
|
|
status: 'completed',
|
|
statusText: '已接种'
|
|
},
|
|
{
|
|
id: 3,
|
|
name: '三联疫苗加强',
|
|
date: '2024-07-15',
|
|
status: 'upcoming',
|
|
statusText: '即将到期'
|
|
}
|
|
],
|
|
// 体检记录
|
|
checkupRecords: [
|
|
{
|
|
id: 1,
|
|
date: '2024-01-10',
|
|
type: '常规体检',
|
|
result: '健康状况良好',
|
|
note: '建议定期驱虫',
|
|
status: 'normal'
|
|
},
|
|
{
|
|
id: 2,
|
|
date: '2023-12-05',
|
|
type: '血液检查',
|
|
result: '各项指标正常',
|
|
status: 'normal'
|
|
}
|
|
]
|
|
}
|
|
},
|
|
onLoad(options) {
|
|
this.petId = options.petId || '1'
|
|
this.loadPetInfo()
|
|
this.loadHealthData()
|
|
},
|
|
onReady() {
|
|
this.$nextTick(() => {
|
|
this.initWeightChart()
|
|
})
|
|
},
|
|
methods: {
|
|
loadPetInfo() {
|
|
// 模拟加载宠物信息
|
|
const mockPets = [
|
|
{
|
|
id: '1',
|
|
name: '小橘',
|
|
breed: '橘猫',
|
|
age: 2,
|
|
avatar: '/static/default-pet.png',
|
|
healthStatus: 'healthy'
|
|
}
|
|
]
|
|
this.petInfo = mockPets.find(pet => pet.id === this.petId) || mockPets[0]
|
|
},
|
|
|
|
loadHealthData() {
|
|
// 模拟加载健康数据
|
|
this.weightData = [
|
|
{ date: '2023-08', weight: 4.1 },
|
|
{ date: '2023-09', weight: 4.2 },
|
|
{ date: '2023-10', weight: 4.3 },
|
|
{ date: '2023-11', weight: 4.4 },
|
|
{ date: '2023-12', weight: 4.3 },
|
|
{ date: '2024-01', weight: 4.5 }
|
|
]
|
|
},
|
|
|
|
initWeightChart() {
|
|
// 初始化体重趋势图
|
|
const ctx = uni.createCanvasContext('weightChart', this)
|
|
// 这里应该使用uCharts库绘制图表
|
|
// 由于篇幅限制,这里只是示例代码
|
|
},
|
|
|
|
onTabChange(index) {
|
|
this.currentTab = index
|
|
this.$nextTick(() => {
|
|
if (index === 0) {
|
|
this.initWeightChart()
|
|
} else if (index === 1) {
|
|
this.initHealthRadar()
|
|
}
|
|
})
|
|
},
|
|
|
|
initHealthRadar() {
|
|
// 初始化健康指标雷达图
|
|
const ctx = uni.createCanvasContext('healthRadar', this)
|
|
// 雷达图绘制逻辑
|
|
},
|
|
|
|
getHealthIcon(status) {
|
|
const iconMap = {
|
|
healthy: 'checkmark',
|
|
warning: 'warning',
|
|
sick: 'close'
|
|
}
|
|
return iconMap[status] || 'checkmark'
|
|
},
|
|
|
|
getHealthText(status) {
|
|
const textMap = {
|
|
healthy: '健康',
|
|
warning: '注意',
|
|
sick: '生病'
|
|
}
|
|
return textMap[status] || '健康'
|
|
},
|
|
|
|
getCheckupIcon(status) {
|
|
const iconMap = {
|
|
normal: 'checkmark',
|
|
warning: 'warning',
|
|
abnormal: 'close'
|
|
}
|
|
return iconMap[status] || 'checkmark'
|
|
},
|
|
|
|
addHealthRecord() {
|
|
uni.navigateTo({
|
|
url: `/pages/pets/add-health-record?petId=${this.petId}`
|
|
})
|
|
},
|
|
|
|
exportHealthData() {
|
|
this.showMoreActions = false
|
|
uni.showToast({
|
|
title: '导出功能开发中',
|
|
icon: 'none'
|
|
})
|
|
},
|
|
|
|
setHealthReminder() {
|
|
this.showMoreActions = false
|
|
uni.navigateTo({
|
|
url: `/pages/pets/health-reminder?petId=${this.petId}`
|
|
})
|
|
},
|
|
|
|
consultVet() {
|
|
this.showMoreActions = false
|
|
uni.navigateTo({
|
|
url: '/pages/assistant/assistant'
|
|
})
|
|
},
|
|
|
|
touchChart(e) {
|
|
// 处理图表触摸事件
|
|
},
|
|
|
|
goBack() {
|
|
uni.navigateBack()
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.health-charts-container {
|
|
background: linear-gradient(135deg, #FF8A80 0%, #FFB6C1 25%, #FECFEF 50%, #F8BBD9 100%);
|
|
min-height: 100vh;
|
|
}
|
|
|
|
/* 顶部操作栏 */
|
|
.top-action-bar {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
padding: 20rpx 30rpx;
|
|
|
|
.action-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8rpx;
|
|
padding: 12rpx 20rpx;
|
|
border-radius: 20rpx;
|
|
background: rgba(255, 255, 255, 0.8);
|
|
|
|
.action-text {
|
|
font-size: 24rpx;
|
|
color: #666666;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 宠物信息卡片 */
|
|
.pet-info-card {
|
|
background: #ffffff;
|
|
margin: 20rpx;
|
|
padding: 30rpx;
|
|
border-radius: 20rpx;
|
|
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.08);
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
.pet-info {
|
|
margin-left: 24rpx;
|
|
flex: 1;
|
|
|
|
.pet-name {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #333333;
|
|
display: block;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.pet-details {
|
|
font-size: 24rpx;
|
|
color: #666666;
|
|
display: block;
|
|
margin-bottom: 12rpx;
|
|
}
|
|
|
|
.health-status {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8rpx;
|
|
padding: 8rpx 16rpx;
|
|
border-radius: 20rpx;
|
|
width: fit-content;
|
|
|
|
&.healthy {
|
|
background: #81C784;
|
|
}
|
|
|
|
&.warning {
|
|
background: #FFB74D;
|
|
}
|
|
|
|
&.sick {
|
|
background: #FF8A80;
|
|
}
|
|
|
|
.status-text {
|
|
font-size: 22rpx;
|
|
color: #ffffff;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 图表切换标签 */
|
|
.chart-tabs {
|
|
background: #ffffff;
|
|
margin: 0 20rpx 20rpx;
|
|
border-radius: 20rpx;
|
|
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.08);
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* 图表内容区域 */
|
|
.chart-content {
|
|
padding: 0 20rpx;
|
|
}
|
|
|
|
.chart-section {
|
|
background: #ffffff;
|
|
border-radius: 20rpx;
|
|
padding: 30rpx;
|
|
margin-bottom: 20rpx;
|
|
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.08);
|
|
|
|
.chart-header {
|
|
margin-bottom: 30rpx;
|
|
|
|
.chart-title {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #333333;
|
|
display: block;
|
|
}
|
|
|
|
.chart-subtitle {
|
|
font-size: 24rpx;
|
|
color: #999999;
|
|
display: block;
|
|
margin-top: 8rpx;
|
|
}
|
|
}
|
|
|
|
.chart-container {
|
|
height: 400rpx;
|
|
position: relative;
|
|
margin-bottom: 30rpx;
|
|
|
|
.chart-canvas {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
}
|
|
|
|
.chart-stats {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
padding-top: 20rpx;
|
|
border-top: 2rpx solid #F5F5F5;
|
|
|
|
.stat-item {
|
|
text-align: center;
|
|
|
|
.stat-label {
|
|
font-size: 24rpx;
|
|
color: #999999;
|
|
display: block;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.stat-value {
|
|
font-size: 28rpx;
|
|
font-weight: bold;
|
|
color: #333333;
|
|
display: block;
|
|
|
|
&.positive {
|
|
color: #81C784;
|
|
}
|
|
|
|
&.negative {
|
|
color: #FF8A80;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 健康指标 */
|
|
.health-indicators {
|
|
.indicator-item {
|
|
margin-bottom: 24rpx;
|
|
|
|
.indicator-info {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 12rpx;
|
|
|
|
.indicator-name {
|
|
font-size: 28rpx;
|
|
color: #333333;
|
|
}
|
|
|
|
.indicator-score {
|
|
font-size: 24rpx;
|
|
color: #FF8A80;
|
|
font-weight: bold;
|
|
}
|
|
}
|
|
|
|
.indicator-bar {
|
|
height: 8rpx;
|
|
background: #F5F5F5;
|
|
border-radius: 4rpx;
|
|
overflow: hidden;
|
|
|
|
.indicator-progress {
|
|
height: 100%;
|
|
background: linear-gradient(90deg, #FF8A80, #81C784);
|
|
border-radius: 4rpx;
|
|
transition: width 0.3s ease;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 疫苗时间轴 */
|
|
.vaccine-timeline {
|
|
position: relative;
|
|
|
|
&::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: 20rpx;
|
|
top: 0;
|
|
bottom: 0;
|
|
width: 4rpx;
|
|
background: #E0E0E0;
|
|
}
|
|
|
|
.timeline-item {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
margin-bottom: 40rpx;
|
|
position: relative;
|
|
|
|
.timeline-dot {
|
|
width: 40rpx;
|
|
height: 40rpx;
|
|
border-radius: 20rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-right: 24rpx;
|
|
position: relative;
|
|
z-index: 1;
|
|
|
|
&.completed {
|
|
background: #81C784;
|
|
}
|
|
|
|
&.upcoming {
|
|
background: #FFB74D;
|
|
}
|
|
|
|
&.overdue {
|
|
background: #FF8A80;
|
|
}
|
|
}
|
|
|
|
.timeline-content {
|
|
flex: 1;
|
|
|
|
.vaccine-name {
|
|
font-size: 28rpx;
|
|
font-weight: bold;
|
|
color: #333333;
|
|
display: block;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.vaccine-date {
|
|
font-size: 24rpx;
|
|
color: #666666;
|
|
display: block;
|
|
margin-bottom: 4rpx;
|
|
}
|
|
|
|
.vaccine-status {
|
|
font-size: 22rpx;
|
|
color: #999999;
|
|
display: block;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 体检记录 */
|
|
.checkup-list {
|
|
.checkup-item {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 24rpx 0;
|
|
border-bottom: 2rpx solid #F5F5F5;
|
|
|
|
&:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.checkup-date {
|
|
width: 120rpx;
|
|
margin-right: 24rpx;
|
|
|
|
.date-text {
|
|
font-size: 24rpx;
|
|
color: #999999;
|
|
}
|
|
}
|
|
|
|
.checkup-info {
|
|
flex: 1;
|
|
|
|
.checkup-type {
|
|
font-size: 28rpx;
|
|
font-weight: bold;
|
|
color: #333333;
|
|
display: block;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.checkup-result {
|
|
font-size: 24rpx;
|
|
color: #666666;
|
|
display: block;
|
|
margin-bottom: 4rpx;
|
|
}
|
|
|
|
.checkup-note {
|
|
font-size: 22rpx;
|
|
color: #999999;
|
|
display: block;
|
|
}
|
|
}
|
|
|
|
.checkup-status {
|
|
width: 40rpx;
|
|
height: 40rpx;
|
|
border-radius: 20rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
&.normal {
|
|
background: #81C784;
|
|
}
|
|
|
|
&.warning {
|
|
background: #FFB74D;
|
|
}
|
|
|
|
&.abnormal {
|
|
background: #FF8A80;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 添加记录按钮 */
|
|
.add-record-btn {
|
|
position: fixed;
|
|
right: 30rpx;
|
|
bottom: 120rpx;
|
|
width: 120rpx;
|
|
height: 120rpx;
|
|
border-radius: 60rpx;
|
|
background: linear-gradient(135deg, #FF8A80, #FFB74D);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.4);
|
|
z-index: 100;
|
|
|
|
&:active {
|
|
transform: scale(0.95);
|
|
}
|
|
}
|
|
|
|
/* 更多操作弹窗 */
|
|
.more-actions-popup {
|
|
background: #ffffff;
|
|
border-radius: 20rpx 20rpx 0 0;
|
|
padding: 40rpx 30rpx 60rpx;
|
|
|
|
.popup-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 40rpx;
|
|
|
|
.popup-title {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #333333;
|
|
}
|
|
}
|
|
|
|
.action-list {
|
|
.action-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 24rpx;
|
|
padding: 24rpx 0;
|
|
border-bottom: 2rpx solid #F5F5F5;
|
|
|
|
&:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
&:active {
|
|
background: #F8F9FA;
|
|
}
|
|
|
|
.action-text {
|
|
font-size: 28rpx;
|
|
color: #333333;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|