This commit is contained in:
parent
8c7e7750b5
commit
8430b9083a
|
|
@ -72,6 +72,14 @@
|
|||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/pets/pet-vaccine",
|
||||
"style": {
|
||||
"navigationBarTitleText": "疫苗管理",
|
||||
"navigationBarBackgroundColor": "#FF8A80",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "pages/pets/pet-chat-simple",
|
||||
|
|
|
|||
|
|
@ -1,783 +1,27 @@
|
|||
<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>
|
||||
|
|
|
|||
|
|
@ -76,12 +76,13 @@
|
|||
</view>
|
||||
</view>
|
||||
<view class="detail-row">
|
||||
<view class="detail-item">
|
||||
<view class="detail-item clickable" @click="goToVaccineManagement">
|
||||
<view class="detail-icon">💉</view>
|
||||
<view class="detail-content">
|
||||
<text class="detail-label">疫苗状态</text>
|
||||
<text class="detail-value status-good">已完成</text>
|
||||
</view>
|
||||
<text class="detail-arrow">›</text>
|
||||
</view>
|
||||
<view class="detail-item">
|
||||
<view class="detail-icon">🏥</view>
|
||||
|
|
@ -380,6 +381,12 @@ export default {
|
|||
})
|
||||
},
|
||||
|
||||
goToVaccineManagement() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/pets/pet-vaccine?petId=${this.petId}&petName=${this.petInfo.name}`
|
||||
})
|
||||
},
|
||||
|
||||
shareProfile() {
|
||||
uni.showToast({
|
||||
title: '分享功能开发中',
|
||||
|
|
@ -651,6 +658,26 @@ export default {
|
|||
background: rgba(255, 138, 128, 0.05);
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx;
|
||||
position: relative;
|
||||
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
background: rgba(255, 138, 128, 0.1);
|
||||
}
|
||||
|
||||
.detail-arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 16rpx;
|
||||
transform: translateY(-50%);
|
||||
font-size: 24rpx;
|
||||
color: #CCCCCC;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-icon {
|
||||
font-size: 32rpx;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,928 @@
|
|||
<template>
|
||||
<view class="vaccine-container">
|
||||
<!-- 疫苗状态概览卡片 -->
|
||||
<view class="status-overview-card">
|
||||
<view class="overview-header">
|
||||
<text class="pet-name">{{ petName }}的疫苗管理</text>
|
||||
<text class="last-update">最后更新:{{ lastUpdateTime }}</text>
|
||||
</view>
|
||||
|
||||
<view class="status-display">
|
||||
<view class="completion-circle">
|
||||
<view class="circle-progress" :style="{ background: `conic-gradient(#4CAF50 0deg, #4CAF50 ${completionRate * 3.6}deg, #E0E0E0 ${completionRate * 3.6}deg)` }">
|
||||
<view class="circle-center">
|
||||
<text class="completion-rate">{{ completionRate }}%</text>
|
||||
<text class="completion-label">完成度</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="status-stats">
|
||||
<view class="stat-item">
|
||||
<text class="stat-number" style="color: #4CAF50;">{{ statusSummary.completed }}</text>
|
||||
<text class="stat-label">已完成</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-number" style="color: #FF9800;">{{ statusSummary.expiringSoon }}</text>
|
||||
<text class="stat-label">即将到期</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-number" style="color: #F44336;">{{ statusSummary.expired }}</text>
|
||||
<text class="stat-label">已过期</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-number" style="color: #9E9E9E;">{{ statusSummary.missing }}</text>
|
||||
<text class="stat-label">未接种</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 疫苗分类切换 -->
|
||||
<view class="category-selector">
|
||||
<view class="selector-header">
|
||||
<text class="selector-title">💉 疫苗分类</text>
|
||||
</view>
|
||||
<view class="category-tabs">
|
||||
<view
|
||||
class="category-tab"
|
||||
:class="{ active: activeCategory === 'all' }"
|
||||
@click="switchCategory('all')"
|
||||
>
|
||||
<text class="tab-text">全部</text>
|
||||
</view>
|
||||
<view
|
||||
class="category-tab"
|
||||
:class="{ active: activeCategory === 'core' }"
|
||||
@click="switchCategory('core')"
|
||||
>
|
||||
<text class="tab-text">核心疫苗</text>
|
||||
</view>
|
||||
<view
|
||||
class="category-tab"
|
||||
:class="{ active: activeCategory === 'nonCore' }"
|
||||
@click="switchCategory('nonCore')"
|
||||
>
|
||||
<text class="tab-text">非核心疫苗</text>
|
||||
</view>
|
||||
<view
|
||||
class="category-tab"
|
||||
:class="{ active: activeCategory === 'plan' }"
|
||||
@click="switchCategory('plan')"
|
||||
>
|
||||
<text class="tab-text">接种计划</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 疫苗记录列表 -->
|
||||
<view class="vaccine-records" v-if="activeCategory !== 'plan'">
|
||||
<view class="records-header">
|
||||
<text class="records-title">📋 疫苗记录</text>
|
||||
<view class="add-record-btn" @click="addVaccineRecord">
|
||||
<text class="add-btn-text">+ 添加</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="records-timeline" v-if="filteredRecords.length > 0">
|
||||
<view
|
||||
class="timeline-item"
|
||||
v-for="record in filteredRecords"
|
||||
:key="record.id"
|
||||
>
|
||||
<view class="timeline-dot" :class="getStatusClass(record)"></view>
|
||||
<view class="timeline-content">
|
||||
<view class="vaccine-card" :class="getStatusClass(record)">
|
||||
<view class="vaccine-header">
|
||||
<text class="vaccine-name">{{ record.vaccineName }}</text>
|
||||
<view class="vaccine-status" :class="getStatusClass(record)">
|
||||
<text class="status-text">{{ getStatusText(record) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="vaccine-details">
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">接种日期:</text>
|
||||
<text class="detail-value">{{ formatDate(record.vaccineDate) }}</text>
|
||||
</view>
|
||||
<view class="detail-row">
|
||||
<text class="detail-label">有效期至:</text>
|
||||
<text class="detail-value">{{ formatDate(record.expiryDate) }}</text>
|
||||
</view>
|
||||
<view class="detail-row" v-if="record.hospital">
|
||||
<text class="detail-label">接种医院:</text>
|
||||
<text class="detail-value">{{ record.hospital }}</text>
|
||||
</view>
|
||||
<view class="detail-row" v-if="record.batchNumber">
|
||||
<text class="detail-label">批次号:</text>
|
||||
<text class="detail-value">{{ record.batchNumber }}</text>
|
||||
</view>
|
||||
<view class="detail-row" v-if="record.note">
|
||||
<text class="detail-label">备注:</text>
|
||||
<text class="detail-value">{{ record.note }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="empty-records" v-else>
|
||||
<text class="empty-icon">💉</text>
|
||||
<text class="empty-title">暂无{{ getCategoryName() }}记录</text>
|
||||
<text class="empty-subtitle">点击添加按钮开始记录疫苗接种信息</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 接种计划 -->
|
||||
<view class="vaccine-plan" v-if="activeCategory === 'plan'">
|
||||
<view class="plan-header">
|
||||
<text class="plan-title">📅 接种计划</text>
|
||||
</view>
|
||||
|
||||
<view class="plan-list" v-if="vaccinePlan.length > 0">
|
||||
<view
|
||||
class="plan-item"
|
||||
v-for="plan in vaccinePlan"
|
||||
:key="plan.vaccineType"
|
||||
:class="getPriorityClass(plan.priority)"
|
||||
>
|
||||
<view class="plan-priority" :class="getPriorityClass(plan.priority)">
|
||||
<text class="priority-text">{{ getPriorityText(plan.priority) }}</text>
|
||||
</view>
|
||||
|
||||
<view class="plan-content">
|
||||
<text class="plan-vaccine-name">{{ plan.vaccineName }}</text>
|
||||
<text class="plan-due-date">建议接种时间:{{ formatDate(plan.nextDueDate) }}</text>
|
||||
<text class="plan-reason">{{ plan.reason }}</text>
|
||||
</view>
|
||||
|
||||
<view class="plan-action" @click="scheduleVaccine(plan)">
|
||||
<text class="action-text">安排接种</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- AI分析卡片 -->
|
||||
<view class="ai-analysis-card">
|
||||
<view class="analysis-header">
|
||||
<text class="analysis-title">🤖 AI疫苗分析</text>
|
||||
<view class="analysis-badge">
|
||||
<text class="badge-text">智能分析</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="analysis-content">
|
||||
<view class="analysis-section">
|
||||
<text class="section-title">📊 完整性评估</text>
|
||||
<text class="section-content">{{ aiAnalysis.completenessAnalysis }}</text>
|
||||
</view>
|
||||
|
||||
<view class="analysis-section">
|
||||
<text class="section-title">⏰ 时效性分析</text>
|
||||
<text class="section-content">{{ aiAnalysis.timelinessAnalysis }}</text>
|
||||
</view>
|
||||
|
||||
<view class="analysis-section">
|
||||
<text class="section-title">💡 个性化建议</text>
|
||||
<text class="section-content">{{ aiAnalysis.recommendations }}</text>
|
||||
</view>
|
||||
|
||||
<view class="analysis-section" v-if="aiAnalysis.riskAssessment">
|
||||
<text class="section-title">⚠️ 风险提醒</text>
|
||||
<text class="section-content warning">{{ aiAnalysis.riskAssessment }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import vaccineManager from '@/utils/vaccineManager.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
petId: '',
|
||||
petName: '',
|
||||
petInfo: {},
|
||||
lastUpdateTime: '',
|
||||
|
||||
// 状态统计
|
||||
statusSummary: {
|
||||
completed: 0,
|
||||
expiringSoon: 0,
|
||||
expired: 0,
|
||||
missing: 0,
|
||||
total: 0,
|
||||
completionRate: 0
|
||||
},
|
||||
completionRate: 0,
|
||||
|
||||
// 分类切换
|
||||
activeCategory: 'all',
|
||||
|
||||
// 疫苗记录
|
||||
allRecords: [],
|
||||
filteredRecords: [],
|
||||
|
||||
// 接种计划
|
||||
vaccinePlan: [],
|
||||
|
||||
// AI分析
|
||||
aiAnalysis: {
|
||||
completenessAnalysis: '',
|
||||
timelinessAnalysis: '',
|
||||
recommendations: '',
|
||||
riskAssessment: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
this.petId = options.petId || ''
|
||||
this.petName = options.petName || '宠物'
|
||||
this.loadPetInfo()
|
||||
this.loadVaccineData()
|
||||
},
|
||||
|
||||
methods: {
|
||||
loadPetInfo() {
|
||||
try {
|
||||
const pets = uni.getStorageSync('pets') || []
|
||||
this.petInfo = pets.find(pet => pet.id == this.petId) || {
|
||||
id: this.petId,
|
||||
name: this.petName,
|
||||
breed: '橘猫',
|
||||
age: 2,
|
||||
gender: '公'
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载宠物信息失败:', error)
|
||||
}
|
||||
},
|
||||
|
||||
loadVaccineData() {
|
||||
// 获取疫苗记录
|
||||
this.allRecords = vaccineManager.getVaccineRecords(this.petId)
|
||||
this.filterRecords()
|
||||
|
||||
// 获取状态统计
|
||||
this.statusSummary = vaccineManager.getVaccineStatusSummary(this.petId)
|
||||
this.completionRate = this.statusSummary.completionRate
|
||||
|
||||
// 生成接种计划
|
||||
this.vaccinePlan = vaccineManager.generateVaccinePlan(this.petId, this.petInfo)
|
||||
|
||||
// 生成AI分析
|
||||
this.aiAnalysis = vaccineManager.generateAIAnalysis(this.petId, this.petInfo)
|
||||
|
||||
// 更新最后更新时间
|
||||
if (this.allRecords.length > 0) {
|
||||
const latestRecord = this.allRecords.sort((a, b) => new Date(b.vaccineDate) - new Date(a.vaccineDate))[0]
|
||||
const lastDate = new Date(latestRecord.vaccineDate)
|
||||
this.lastUpdateTime = `${lastDate.getMonth() + 1}-${lastDate.getDate()} ${lastDate.getHours()}:${lastDate.getMinutes().toString().padStart(2, '0')}`
|
||||
} else {
|
||||
this.lastUpdateTime = '暂无记录'
|
||||
}
|
||||
},
|
||||
|
||||
switchCategory(category) {
|
||||
this.activeCategory = category
|
||||
this.filterRecords()
|
||||
},
|
||||
|
||||
filterRecords() {
|
||||
if (this.activeCategory === 'all') {
|
||||
this.filteredRecords = this.allRecords
|
||||
} else {
|
||||
this.filteredRecords = this.allRecords.filter(record => record.category === this.activeCategory)
|
||||
}
|
||||
|
||||
// 按接种日期倒序排列
|
||||
this.filteredRecords.sort((a, b) => new Date(b.vaccineDate) - new Date(a.vaccineDate))
|
||||
},
|
||||
|
||||
getCategoryName() {
|
||||
const categoryNames = {
|
||||
'all': '疫苗',
|
||||
'core': '核心疫苗',
|
||||
'nonCore': '非核心疫苗'
|
||||
}
|
||||
return categoryNames[this.activeCategory] || '疫苗'
|
||||
},
|
||||
|
||||
getStatusClass(record) {
|
||||
const status = vaccineManager.getVaccineStatus(record)
|
||||
return status // 'valid', 'expiring', 'expired'
|
||||
},
|
||||
|
||||
getStatusText(record) {
|
||||
const status = vaccineManager.getVaccineStatus(record)
|
||||
const statusTexts = {
|
||||
'valid': '有效',
|
||||
'expiring': '即将到期',
|
||||
'expired': '已过期'
|
||||
}
|
||||
return statusTexts[status] || '未知'
|
||||
},
|
||||
|
||||
getPriorityClass(priority) {
|
||||
return priority // 'high', 'medium', 'low'
|
||||
},
|
||||
|
||||
getPriorityText(priority) {
|
||||
const priorityTexts = {
|
||||
'high': '紧急',
|
||||
'medium': '建议',
|
||||
'low': '可选'
|
||||
}
|
||||
return priorityTexts[priority] || '未知'
|
||||
},
|
||||
|
||||
formatDate(dateStr) {
|
||||
if (!dateStr) return ''
|
||||
const date = new Date(dateStr)
|
||||
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`
|
||||
},
|
||||
|
||||
addVaccineRecord() {
|
||||
uni.showModal({
|
||||
title: '添加疫苗记录',
|
||||
content: '跳转到添加疫苗记录页面?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 这里可以跳转到添加疫苗记录的页面
|
||||
// 或者显示一个表单弹窗
|
||||
this.showAddVaccineForm()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
showAddVaccineForm() {
|
||||
// 简化版添加功能,实际项目中可以创建专门的表单页面
|
||||
uni.showToast({
|
||||
title: '功能开发中',
|
||||
icon: 'none'
|
||||
})
|
||||
},
|
||||
|
||||
scheduleVaccine(plan) {
|
||||
uni.showModal({
|
||||
title: '安排接种',
|
||||
content: `是否要安排${plan.vaccineName}的接种?`,
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showToast({
|
||||
title: '已添加到日程',
|
||||
icon: 'success'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.vaccine-container {
|
||||
background: linear-gradient(135deg, #FF8A80 0%, #FFB6C1 25%, #FECFEF 50%, #F8BBD9 100%);
|
||||
min-height: 100vh;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
/* 疫苗状态概览卡片 */
|
||||
.status-overview-card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20rpx);
|
||||
border-radius: 28rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.3);
|
||||
|
||||
.overview-header {
|
||||
text-align: center;
|
||||
margin-bottom: 32rpx;
|
||||
|
||||
.pet-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
display: block;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.last-update {
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.status-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32rpx;
|
||||
|
||||
.completion-circle {
|
||||
.circle-progress {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
|
||||
.circle-center {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.completion-rate {
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
color: #4CAF50;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.completion-label {
|
||||
font-size: 18rpx;
|
||||
color: #999999;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status-stats {
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 16rpx;
|
||||
|
||||
.stat-item {
|
||||
text-align: center;
|
||||
|
||||
.stat-number {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 20rpx;
|
||||
color: #999999;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 分类选择器 */
|
||||
.category-selector {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20rpx);
|
||||
border-radius: 28rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.3);
|
||||
|
||||
.selector-header {
|
||||
margin-bottom: 28rpx;
|
||||
|
||||
.selector-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.category-tabs {
|
||||
display: flex;
|
||||
gap: 12rpx;
|
||||
|
||||
.category-tab {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
padding: 16rpx 12rpx;
|
||||
border-radius: 16rpx;
|
||||
background: rgba(255, 138, 128, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&.active {
|
||||
background: #FF8A80;
|
||||
|
||||
.tab-text {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.tab-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 疫苗记录列表 */
|
||||
.vaccine-records {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20rpx);
|
||||
border-radius: 28rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.3);
|
||||
|
||||
.records-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 28rpx;
|
||||
|
||||
.records-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.add-record-btn {
|
||||
background: linear-gradient(135deg, #FF8A80, #FFB6C1);
|
||||
border-radius: 20rpx;
|
||||
padding: 12rpx 24rpx;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.add-btn-text {
|
||||
font-size: 24rpx;
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.records-timeline {
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 24rpx;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 2rpx;
|
||||
background: #E0E0E0;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
position: relative;
|
||||
margin-bottom: 32rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.timeline-dot {
|
||||
position: absolute;
|
||||
left: 16rpx;
|
||||
top: 24rpx;
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 50%;
|
||||
z-index: 1;
|
||||
|
||||
&.valid {
|
||||
background: #4CAF50;
|
||||
}
|
||||
|
||||
&.expiring {
|
||||
background: #FF9800;
|
||||
}
|
||||
|
||||
&.expired {
|
||||
background: #F44336;
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
margin-left: 56rpx;
|
||||
|
||||
.vaccine-card {
|
||||
background: white;
|
||||
border-radius: 20rpx;
|
||||
padding: 24rpx;
|
||||
border-left: 4rpx solid #E0E0E0;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&.valid {
|
||||
border-left-color: #4CAF50;
|
||||
}
|
||||
|
||||
&.expiring {
|
||||
border-left-color: #FF9800;
|
||||
}
|
||||
|
||||
&.expired {
|
||||
border-left-color: #F44336;
|
||||
}
|
||||
|
||||
.vaccine-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.vaccine-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.vaccine-status {
|
||||
padding: 6rpx 12rpx;
|
||||
border-radius: 12rpx;
|
||||
font-size: 20rpx;
|
||||
|
||||
&.valid {
|
||||
background: #E8F5E8;
|
||||
color: #4CAF50;
|
||||
}
|
||||
|
||||
&.expiring {
|
||||
background: #FFF3E0;
|
||||
color: #FF9800;
|
||||
}
|
||||
|
||||
&.expired {
|
||||
background: #FFEBEE;
|
||||
color: #F44336;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.vaccine-details {
|
||||
.detail-row {
|
||||
display: flex;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
font-size: 22rpx;
|
||||
color: #999999;
|
||||
min-width: 120rpx;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
font-size: 22rpx;
|
||||
color: #666666;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-records {
|
||||
text-align: center;
|
||||
padding: 80rpx 40rpx;
|
||||
|
||||
.empty-icon {
|
||||
font-size: 80rpx;
|
||||
margin-bottom: 24rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.empty-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
margin-bottom: 12rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.empty-subtitle {
|
||||
font-size: 26rpx;
|
||||
color: #999999;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 接种计划 */
|
||||
.vaccine-plan {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20rpx);
|
||||
border-radius: 28rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.3);
|
||||
|
||||
.plan-header {
|
||||
margin-bottom: 28rpx;
|
||||
|
||||
.plan-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.plan-list {
|
||||
.plan-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
background: white;
|
||||
border-radius: 20rpx;
|
||||
padding: 20rpx;
|
||||
margin-bottom: 16rpx;
|
||||
border-left: 4rpx solid #E0E0E0;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.high {
|
||||
border-left-color: #F44336;
|
||||
}
|
||||
|
||||
&.medium {
|
||||
border-left-color: #FF9800;
|
||||
}
|
||||
|
||||
&.low {
|
||||
border-left-color: #4CAF50;
|
||||
}
|
||||
|
||||
.plan-priority {
|
||||
padding: 6rpx 12rpx;
|
||||
border-radius: 12rpx;
|
||||
font-size: 20rpx;
|
||||
font-weight: 500;
|
||||
|
||||
&.high {
|
||||
background: #FFEBEE;
|
||||
color: #F44336;
|
||||
}
|
||||
|
||||
&.medium {
|
||||
background: #FFF3E0;
|
||||
color: #FF9800;
|
||||
}
|
||||
|
||||
&.low {
|
||||
background: #E8F5E8;
|
||||
color: #4CAF50;
|
||||
}
|
||||
|
||||
.priority-text {
|
||||
font-size: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.plan-content {
|
||||
flex: 1;
|
||||
|
||||
.plan-vaccine-name {
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
display: block;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.plan-due-date {
|
||||
font-size: 22rpx;
|
||||
color: #666666;
|
||||
display: block;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.plan-reason {
|
||||
font-size: 20rpx;
|
||||
color: #999999;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.plan-action {
|
||||
background: rgba(255, 138, 128, 0.1);
|
||||
border-radius: 16rpx;
|
||||
padding: 12rpx 20rpx;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
background: rgba(255, 138, 128, 0.2);
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.action-text {
|
||||
font-size: 22rpx;
|
||||
color: #FF8A80;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* AI分析卡片 */
|
||||
.ai-analysis-card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(20rpx);
|
||||
border-radius: 28rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 40rpx;
|
||||
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
||||
border: 1rpx solid rgba(255, 255, 255, 0.3);
|
||||
|
||||
.analysis-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 28rpx;
|
||||
|
||||
.analysis-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.analysis-badge {
|
||||
background: linear-gradient(135deg, #64B5F6, #81C784);
|
||||
border-radius: 20rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
|
||||
.badge-text {
|
||||
font-size: 20rpx;
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.analysis-content {
|
||||
.analysis-section {
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
color: #333333;
|
||||
margin-bottom: 12rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.section-content {
|
||||
font-size: 24rpx;
|
||||
color: #666666;
|
||||
line-height: 1.6;
|
||||
display: block;
|
||||
|
||||
&.warning {
|
||||
color: #FF9800;
|
||||
background: #FFF3E0;
|
||||
padding: 16rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,406 @@
|
|||
/**
|
||||
* 宠物疫苗管理工具类
|
||||
* 负责疫苗数据的存储、分析和AI建议生成
|
||||
*/
|
||||
|
||||
class VaccineManager {
|
||||
constructor() {
|
||||
this.storageKey = 'pet_vaccine_records'
|
||||
|
||||
// 疫苗类型定义
|
||||
this.vaccineTypes = {
|
||||
core: {
|
||||
'rabies': {
|
||||
name: '狂犬病疫苗',
|
||||
duration: 365, // 有效期天数
|
||||
interval: 365, // 接种间隔天数
|
||||
required: true,
|
||||
description: '预防狂犬病,法律要求必须接种'
|
||||
},
|
||||
'fvrcp': {
|
||||
name: '猫三联疫苗',
|
||||
duration: 365,
|
||||
interval: 365,
|
||||
required: true,
|
||||
description: '预防猫瘟热、猫杯状病毒、猫鼻气管炎'
|
||||
}
|
||||
},
|
||||
nonCore: {
|
||||
'felv': {
|
||||
name: '猫白血病疫苗',
|
||||
duration: 365,
|
||||
interval: 365,
|
||||
required: false,
|
||||
description: '预防猫白血病病毒感染'
|
||||
},
|
||||
'fip': {
|
||||
name: '猫传腹疫苗',
|
||||
duration: 365,
|
||||
interval: 365,
|
||||
required: false,
|
||||
description: '预防猫传染性腹膜炎'
|
||||
},
|
||||
'chlamydia': {
|
||||
name: '猫衣原体疫苗',
|
||||
duration: 365,
|
||||
interval: 365,
|
||||
required: false,
|
||||
description: '预防猫衣原体感染'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取宠物的疫苗记录
|
||||
* @param {string} petId 宠物ID
|
||||
* @returns {Array} 疫苗记录数组
|
||||
*/
|
||||
getVaccineRecords(petId) {
|
||||
try {
|
||||
const allRecords = uni.getStorageSync(this.storageKey) || {}
|
||||
let records = allRecords[petId] || []
|
||||
|
||||
// 如果没有数据,初始化一些测试数据
|
||||
if (records.length === 0) {
|
||||
records = this.initializeTestData(petId)
|
||||
allRecords[petId] = records
|
||||
uni.setStorageSync(this.storageKey, allRecords)
|
||||
}
|
||||
|
||||
return records
|
||||
} catch (error) {
|
||||
console.error('获取疫苗记录失败:', error)
|
||||
return this.initializeTestData(petId)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化测试数据
|
||||
* @param {string} petId 宠物ID
|
||||
* @returns {Array} 测试数据数组
|
||||
*/
|
||||
initializeTestData(petId) {
|
||||
const now = new Date()
|
||||
const testData = []
|
||||
|
||||
// 狂犬病疫苗记录
|
||||
testData.push({
|
||||
id: Date.now() + 1,
|
||||
petId: petId,
|
||||
vaccineType: 'rabies',
|
||||
vaccineName: '狂犬病疫苗',
|
||||
vaccineDate: new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
expiryDate: new Date(now.getTime() + 335 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
batchNumber: 'RAB2024001',
|
||||
hospital: '宠物医院',
|
||||
veterinarian: '张医生',
|
||||
note: '首次接种',
|
||||
category: 'core',
|
||||
status: 'completed'
|
||||
})
|
||||
|
||||
// 猫三联疫苗记录
|
||||
testData.push({
|
||||
id: Date.now() + 2,
|
||||
petId: petId,
|
||||
vaccineType: 'fvrcp',
|
||||
vaccineName: '猫三联疫苗',
|
||||
vaccineDate: new Date(now.getTime() - 45 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
expiryDate: new Date(now.getTime() + 320 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
batchNumber: 'FVRCP2024001',
|
||||
hospital: '宠物医院',
|
||||
veterinarian: '李医生',
|
||||
note: '年度接种',
|
||||
category: 'core',
|
||||
status: 'completed'
|
||||
})
|
||||
|
||||
// 猫白血病疫苗记录
|
||||
testData.push({
|
||||
id: Date.now() + 3,
|
||||
petId: petId,
|
||||
vaccineType: 'felv',
|
||||
vaccineName: '猫白血病疫苗',
|
||||
vaccineDate: new Date(now.getTime() - 60 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
expiryDate: new Date(now.getTime() + 305 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
batchNumber: 'FELV2024001',
|
||||
hospital: '宠物医院',
|
||||
veterinarian: '王医生',
|
||||
note: '可选接种',
|
||||
category: 'nonCore',
|
||||
status: 'completed'
|
||||
})
|
||||
|
||||
return testData
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加疫苗记录
|
||||
* @param {string} petId 宠物ID
|
||||
* @param {Object} record 疫苗记录
|
||||
*/
|
||||
addVaccineRecord(petId, record) {
|
||||
try {
|
||||
const allRecords = uni.getStorageSync(this.storageKey) || {}
|
||||
if (!allRecords[petId]) {
|
||||
allRecords[petId] = []
|
||||
}
|
||||
|
||||
const vaccineInfo = this.getVaccineInfo(record.vaccineType)
|
||||
const vaccineDate = new Date(record.vaccineDate)
|
||||
const expiryDate = new Date(vaccineDate.getTime() + vaccineInfo.duration * 24 * 60 * 60 * 1000)
|
||||
|
||||
const newRecord = {
|
||||
id: Date.now(),
|
||||
petId: petId,
|
||||
vaccineType: record.vaccineType,
|
||||
vaccineName: vaccineInfo.name,
|
||||
vaccineDate: record.vaccineDate,
|
||||
expiryDate: expiryDate.toISOString(),
|
||||
batchNumber: record.batchNumber || '',
|
||||
hospital: record.hospital || '',
|
||||
veterinarian: record.veterinarian || '',
|
||||
note: record.note || '',
|
||||
category: vaccineInfo.category,
|
||||
status: 'completed'
|
||||
}
|
||||
|
||||
allRecords[petId].push(newRecord)
|
||||
allRecords[petId].sort((a, b) => new Date(b.vaccineDate) - new Date(a.vaccineDate))
|
||||
|
||||
uni.setStorageSync(this.storageKey, allRecords)
|
||||
return newRecord
|
||||
} catch (error) {
|
||||
console.error('添加疫苗记录失败:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取疫苗信息
|
||||
* @param {string} vaccineType 疫苗类型
|
||||
* @returns {Object} 疫苗信息
|
||||
*/
|
||||
getVaccineInfo(vaccineType) {
|
||||
const coreVaccine = this.vaccineTypes.core[vaccineType]
|
||||
if (coreVaccine) {
|
||||
return { ...coreVaccine, category: 'core' }
|
||||
}
|
||||
|
||||
const nonCoreVaccine = this.vaccineTypes.nonCore[vaccineType]
|
||||
if (nonCoreVaccine) {
|
||||
return { ...nonCoreVaccine, category: 'nonCore' }
|
||||
}
|
||||
|
||||
return {
|
||||
name: '未知疫苗',
|
||||
duration: 365,
|
||||
interval: 365,
|
||||
required: false,
|
||||
category: 'unknown'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取疫苗状态统计
|
||||
* @param {string} petId 宠物ID
|
||||
* @returns {Object} 状态统计
|
||||
*/
|
||||
getVaccineStatusSummary(petId) {
|
||||
const records = this.getVaccineRecords(petId)
|
||||
const now = new Date()
|
||||
|
||||
let completed = 0
|
||||
let expiringSoon = 0 // 30天内到期
|
||||
let expired = 0
|
||||
let missing = 0
|
||||
|
||||
// 检查核心疫苗
|
||||
Object.keys(this.vaccineTypes.core).forEach(vaccineType => {
|
||||
const latestRecord = records
|
||||
.filter(r => r.vaccineType === vaccineType)
|
||||
.sort((a, b) => new Date(b.vaccineDate) - new Date(a.vaccineDate))[0]
|
||||
|
||||
if (!latestRecord) {
|
||||
missing++
|
||||
} else {
|
||||
const expiryDate = new Date(latestRecord.expiryDate)
|
||||
const daysUntilExpiry = Math.ceil((expiryDate - now) / (1000 * 60 * 60 * 24))
|
||||
|
||||
if (daysUntilExpiry < 0) {
|
||||
expired++
|
||||
} else if (daysUntilExpiry <= 30) {
|
||||
expiringSoon++
|
||||
} else {
|
||||
completed++
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const total = Object.keys(this.vaccineTypes.core).length
|
||||
const completionRate = Math.round((completed / total) * 100)
|
||||
|
||||
return {
|
||||
completed,
|
||||
expiringSoon,
|
||||
expired,
|
||||
missing,
|
||||
total,
|
||||
completionRate
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取疫苗状态
|
||||
* @param {Object} record 疫苗记录
|
||||
* @returns {string} 状态
|
||||
*/
|
||||
getVaccineStatus(record) {
|
||||
const now = new Date()
|
||||
const expiryDate = new Date(record.expiryDate)
|
||||
const daysUntilExpiry = Math.ceil((expiryDate - now) / (1000 * 60 * 60 * 24))
|
||||
|
||||
if (daysUntilExpiry < 0) {
|
||||
return 'expired'
|
||||
} else if (daysUntilExpiry <= 30) {
|
||||
return 'expiring'
|
||||
} else {
|
||||
return 'valid'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成疫苗接种计划
|
||||
* @param {string} petId 宠物ID
|
||||
* @param {Object} petInfo 宠物信息
|
||||
* @returns {Array} 接种计划
|
||||
*/
|
||||
generateVaccinePlan(petId, petInfo) {
|
||||
const records = this.getVaccineRecords(petId)
|
||||
const now = new Date()
|
||||
const plan = []
|
||||
|
||||
// 检查所有疫苗类型
|
||||
const allVaccineTypes = { ...this.vaccineTypes.core, ...this.vaccineTypes.nonCore }
|
||||
|
||||
Object.entries(allVaccineTypes).forEach(([vaccineType, vaccineInfo]) => {
|
||||
const latestRecord = records
|
||||
.filter(r => r.vaccineType === vaccineType)
|
||||
.sort((a, b) => new Date(b.vaccineDate) - new Date(a.vaccineDate))[0]
|
||||
|
||||
let nextDueDate
|
||||
let priority = 'low'
|
||||
let reason = ''
|
||||
|
||||
if (!latestRecord) {
|
||||
// 从未接种
|
||||
if (vaccineInfo.required) {
|
||||
nextDueDate = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000) // 一周后
|
||||
priority = 'high'
|
||||
reason = '必需疫苗,建议尽快接种'
|
||||
} else {
|
||||
nextDueDate = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000) // 一个月后
|
||||
priority = 'medium'
|
||||
reason = '可选疫苗,建议接种'
|
||||
}
|
||||
} else {
|
||||
// 已接种,计算下次接种时间
|
||||
const lastVaccineDate = new Date(latestRecord.vaccineDate)
|
||||
nextDueDate = new Date(lastVaccineDate.getTime() + vaccineInfo.interval * 24 * 60 * 60 * 1000)
|
||||
|
||||
const daysUntilDue = Math.ceil((nextDueDate - now) / (1000 * 60 * 60 * 24))
|
||||
|
||||
if (daysUntilDue <= 0) {
|
||||
priority = 'high'
|
||||
reason = '疫苗已过期,需要立即接种'
|
||||
} else if (daysUntilDue <= 30) {
|
||||
priority = 'medium'
|
||||
reason = '疫苗即将到期,建议提前接种'
|
||||
} else {
|
||||
priority = 'low'
|
||||
reason = '疫苗有效期内'
|
||||
}
|
||||
}
|
||||
|
||||
plan.push({
|
||||
vaccineType,
|
||||
vaccineName: vaccineInfo.name,
|
||||
category: vaccineType in this.vaccineTypes.core ? 'core' : 'nonCore',
|
||||
nextDueDate: nextDueDate.toISOString(),
|
||||
priority,
|
||||
reason,
|
||||
required: vaccineInfo.required,
|
||||
lastVaccineDate: latestRecord ? latestRecord.vaccineDate : null
|
||||
})
|
||||
})
|
||||
|
||||
// 按优先级和到期时间排序
|
||||
plan.sort((a, b) => {
|
||||
const priorityOrder = { 'high': 3, 'medium': 2, 'low': 1 }
|
||||
if (priorityOrder[a.priority] !== priorityOrder[b.priority]) {
|
||||
return priorityOrder[b.priority] - priorityOrder[a.priority]
|
||||
}
|
||||
return new Date(a.nextDueDate) - new Date(b.nextDueDate)
|
||||
})
|
||||
|
||||
return plan
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成AI分析建议
|
||||
* @param {string} petId 宠物ID
|
||||
* @param {Object} petInfo 宠物信息
|
||||
* @returns {Object} AI分析结果
|
||||
*/
|
||||
generateAIAnalysis(petId, petInfo) {
|
||||
const records = this.getVaccineRecords(petId)
|
||||
const statusSummary = this.getVaccineStatusSummary(petId)
|
||||
const vaccinePlan = this.generateVaccinePlan(petId, petInfo)
|
||||
|
||||
// 疫苗完整性分析
|
||||
let completenessAnalysis = ''
|
||||
if (statusSummary.completionRate >= 100) {
|
||||
completenessAnalysis = `${petInfo.name}的核心疫苗接种完整,免疫保护良好。`
|
||||
} else if (statusSummary.completionRate >= 50) {
|
||||
completenessAnalysis = `${petInfo.name}的疫苗接种基本完整,但还有${statusSummary.missing}种核心疫苗需要接种。`
|
||||
} else {
|
||||
completenessAnalysis = `${petInfo.name}的疫苗接种不完整,缺少${statusSummary.missing}种核心疫苗,存在健康风险。`
|
||||
}
|
||||
|
||||
// 时效性分析
|
||||
let timelinessAnalysis = ''
|
||||
if (statusSummary.expired > 0) {
|
||||
timelinessAnalysis = `有${statusSummary.expired}种疫苗已过期,免疫保护可能失效,建议立即补种。`
|
||||
} else if (statusSummary.expiringSoon > 0) {
|
||||
timelinessAnalysis = `有${statusSummary.expiringSoon}种疫苗即将到期,建议提前安排接种。`
|
||||
} else {
|
||||
timelinessAnalysis = '所有疫苗均在有效期内,免疫保护充分。'
|
||||
}
|
||||
|
||||
// 个性化建议
|
||||
const highPriorityVaccines = vaccinePlan.filter(v => v.priority === 'high')
|
||||
let recommendations = ''
|
||||
if (highPriorityVaccines.length > 0) {
|
||||
const vaccineNames = highPriorityVaccines.map(v => v.vaccineName).join('、')
|
||||
recommendations = `建议优先接种:${vaccineNames}。根据${petInfo.name}的年龄(${petInfo.age}岁)和生活环境,这些疫苗对健康保护至关重要。`
|
||||
} else {
|
||||
recommendations = `当前疫苗状态良好,建议按计划进行常规疫苗接种。定期检查疫苗有效期,确保免疫保护持续有效。`
|
||||
}
|
||||
|
||||
// 风险评估
|
||||
let riskAssessment = ''
|
||||
if (statusSummary.expired > 0 || statusSummary.missing > 0) {
|
||||
riskAssessment = '存在免疫空白期,建议避免接触其他动物,减少外出,直到完成疫苗接种。'
|
||||
}
|
||||
|
||||
return {
|
||||
completenessAnalysis,
|
||||
timelinessAnalysis,
|
||||
recommendations,
|
||||
riskAssessment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new VaccineManager()
|
||||
Loading…
Reference in New Issue