This commit is contained in:
parent
8430b9083a
commit
4e136fb467
|
|
@ -1,27 +1,863 @@
|
||||||
<template>
|
<template>
|
||||||
<view class="health-charts-container">
|
<view class="health-charts-container">
|
||||||
|
<!-- 健康状态总览卡片 -->
|
||||||
|
<view class="health-overview-card">
|
||||||
|
<view class="overview-header">
|
||||||
|
<text class="pet-name">{{ petName }}的健康档案</text>
|
||||||
|
<text class="last-update">最后更新:{{ lastUpdateTime }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="health-summary">
|
||||||
|
<view class="health-score-section">
|
||||||
|
<view class="score-circle">
|
||||||
|
<view class="circle-progress" :style="{ background: `conic-gradient(${getScoreColor()} 0deg, ${getScoreColor()} ${healthScore * 3.6}deg, #E0E0E0 ${healthScore * 3.6}deg)` }">
|
||||||
|
<view class="circle-center">
|
||||||
|
<text class="score-value">{{ healthScore }}</text>
|
||||||
|
<text class="score-label">健康评分</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="score-details">
|
||||||
|
<view class="score-item">
|
||||||
|
<text class="score-name">体重状态</text>
|
||||||
|
<text class="score-points" :style="{ color: getStatusColor(weightData.status) }">{{ weightData.weightScore }}分</text>
|
||||||
|
</view>
|
||||||
|
<view class="score-item">
|
||||||
|
<text class="score-name">疫苗状态</text>
|
||||||
|
<text class="score-points" :style="{ color: getStatusColor(vaccineData.status) }">{{ vaccineData.vaccineScore }}分</text>
|
||||||
|
</view>
|
||||||
|
<view class="score-item">
|
||||||
|
<text class="score-name">医疗记录</text>
|
||||||
|
<text class="score-points" :style="{ color: getStatusColor(medicalData.status) }">{{ medicalData.medicalScore }}分</text>
|
||||||
|
</view>
|
||||||
|
<view class="score-item">
|
||||||
|
<text class="score-name">生长发育</text>
|
||||||
|
<text class="score-points" :style="{ color: getStatusColor(growthData.status) }">{{ growthData.growthScore }}分</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="health-trend">
|
||||||
|
<view class="trend-indicator" :class="healthTrend">
|
||||||
|
<text class="trend-icon">{{ getTrendIcon() }}</text>
|
||||||
|
<text class="trend-text">{{ getTrendText() }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="risk-alerts" v-if="riskFactors.length > 0">
|
||||||
|
<text class="alerts-title">⚠️ 风险提醒</text>
|
||||||
|
<view class="alert-item" v-for="risk in riskFactors" :key="risk.type" :class="risk.level">
|
||||||
|
<text class="alert-text">{{ risk.description }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 时间维度切换 -->
|
||||||
|
<view class="time-selector">
|
||||||
|
<view class="selector-header">
|
||||||
|
<text class="selector-title">📊 数据视图</text>
|
||||||
|
</view>
|
||||||
|
<view class="time-tabs">
|
||||||
|
<view
|
||||||
|
class="time-tab"
|
||||||
|
:class="{ active: activeTimeRange === range.key }"
|
||||||
|
v-for="range in timeRanges"
|
||||||
|
:key="range.key"
|
||||||
|
@click="switchTimeRange(range.key)"
|
||||||
|
>
|
||||||
|
<text class="tab-text">{{ range.label }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 多维度数据展示 -->
|
||||||
|
<view class="data-charts-section">
|
||||||
|
<!-- 体重变化趋势图表 -->
|
||||||
|
<view class="chart-card">
|
||||||
|
<view class="chart-header">
|
||||||
|
<text class="chart-title">📈 体重变化趋势</text>
|
||||||
|
<view class="chart-status" :class="weightData.status">
|
||||||
|
<text class="status-text">{{ getStatusText(weightData.status) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<qiun-data-charts
|
||||||
|
type="line"
|
||||||
|
:opts="weightChartOpts"
|
||||||
|
:chartData="weightChartData"
|
||||||
|
:canvas2d="true"
|
||||||
|
:canvasId="'weightChart'"
|
||||||
|
:canvas-id="'weightChart'"
|
||||||
|
/>
|
||||||
|
<view class="chart-summary">
|
||||||
|
<text class="summary-text">当前体重:{{ weightData.currentWeight }}kg,较上周{{ weightData.weeklyChange.change >= 0 ? '增加' : '减少' }}{{ Math.abs(weightData.weeklyChange.change) }}kg</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 疫苗接种状态时间线 -->
|
||||||
|
<view class="chart-card">
|
||||||
|
<view class="chart-header">
|
||||||
|
<text class="chart-title">💉 疫苗接种状态</text>
|
||||||
|
<view class="chart-status" :class="vaccineData.status">
|
||||||
|
<text class="status-text">{{ getStatusText(vaccineData.status) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="vaccine-timeline">
|
||||||
|
<view class="timeline-summary">
|
||||||
|
<view class="summary-item">
|
||||||
|
<text class="summary-label">完成度</text>
|
||||||
|
<text class="summary-value">{{ vaccineData.statusSummary.completionRate }}%</text>
|
||||||
|
</view>
|
||||||
|
<view class="summary-item">
|
||||||
|
<text class="summary-label">已完成</text>
|
||||||
|
<text class="summary-value" style="color: #4CAF50;">{{ vaccineData.statusSummary.completed }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="summary-item">
|
||||||
|
<text class="summary-label">即将到期</text>
|
||||||
|
<text class="summary-value" style="color: #FF9800;">{{ vaccineData.statusSummary.expiringSoon }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="summary-item">
|
||||||
|
<text class="summary-label">已过期</text>
|
||||||
|
<text class="summary-value" style="color: #F44336;">{{ vaccineData.statusSummary.expired }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 健康记录统计 -->
|
||||||
|
<view class="chart-card">
|
||||||
|
<view class="chart-header">
|
||||||
|
<text class="chart-title">🏥 健康记录统计</text>
|
||||||
|
<view class="chart-status" :class="medicalData.status">
|
||||||
|
<text class="status-text">{{ getStatusText(medicalData.status) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="medical-stats">
|
||||||
|
<view class="stats-grid">
|
||||||
|
<view class="stat-card">
|
||||||
|
<text class="stat-number">{{ medicalData.totalRecords }}</text>
|
||||||
|
<text class="stat-label">总记录数</text>
|
||||||
|
</view>
|
||||||
|
<view class="stat-card">
|
||||||
|
<text class="stat-number">{{ medicalData.medicalVisits }}</text>
|
||||||
|
<text class="stat-label">近期就医</text>
|
||||||
|
</view>
|
||||||
|
<view class="stat-card">
|
||||||
|
<text class="stat-number">{{ medicalData.symptoms }}</text>
|
||||||
|
<text class="stat-label">异常症状</text>
|
||||||
|
</view>
|
||||||
|
<view class="stat-card">
|
||||||
|
<text class="stat-number">{{ medicalData.recentRecords }}</text>
|
||||||
|
<text class="stat-label">近期记录</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 生长发育曲线 -->
|
||||||
|
<view class="chart-card" v-if="showGrowthChart">
|
||||||
|
<view class="chart-header">
|
||||||
|
<text class="chart-title">📏 生长发育曲线</text>
|
||||||
|
<view class="chart-status" :class="growthData.status">
|
||||||
|
<text class="status-text">{{ getStatusText(growthData.status) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="growth-info">
|
||||||
|
<view class="growth-stage">
|
||||||
|
<text class="stage-label">生长阶段:</text>
|
||||||
|
<text class="stage-value">{{ getGrowthStageText() }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="growth-summary">
|
||||||
|
<text class="summary-text">{{ getGrowthSummary() }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- AI健康分析卡片 -->
|
||||||
|
<view class="ai-health-analysis">
|
||||||
|
<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.overallAssessment }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="analysis-section">
|
||||||
|
<text class="section-title">📈 趋势分析</text>
|
||||||
|
<text class="section-content">{{ aiAnalysis.trendAnalysis }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="analysis-section">
|
||||||
|
<text class="section-title">💡 个性化建议</text>
|
||||||
|
<text class="section-content">{{ aiAnalysis.recommendations }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="analysis-section">
|
||||||
|
<text class="section-title">🛡️ 预防措施</text>
|
||||||
|
<text class="section-content">{{ aiAnalysis.preventiveMeasures }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import healthManager from '@/utils/healthManager.js'
|
||||||
|
import weightManager from '@/utils/weightManager.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
}
|
petId: '',
|
||||||
|
petName: '',
|
||||||
|
petInfo: {},
|
||||||
|
lastUpdateTime: '',
|
||||||
|
|
||||||
|
// 健康数据
|
||||||
|
healthScore: 85,
|
||||||
|
healthTrend: 'stable',
|
||||||
|
weightData: {},
|
||||||
|
vaccineData: {},
|
||||||
|
medicalData: {},
|
||||||
|
growthData: {},
|
||||||
|
riskFactors: [],
|
||||||
|
|
||||||
|
// 时间范围
|
||||||
|
activeTimeRange: 'month',
|
||||||
|
timeRanges: [
|
||||||
|
{ key: 'week', label: '近一周' },
|
||||||
|
{ key: 'month', label: '近一月' },
|
||||||
|
{ key: 'year', label: '近一年' },
|
||||||
|
{ key: 'all', label: '全部历史' }
|
||||||
|
],
|
||||||
|
|
||||||
|
// 图表配置
|
||||||
|
weightChartOpts: {
|
||||||
|
color: ["#FF8A80", "#64B5F6"],
|
||||||
|
padding: [15, 15, 0, 15],
|
||||||
|
enableScroll: false,
|
||||||
|
legend: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
disableGrid: true,
|
||||||
|
fontSize: 10,
|
||||||
|
fontColor: "#666666"
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
gridType: "dash",
|
||||||
|
dashLength: 2,
|
||||||
|
fontSize: 10,
|
||||||
|
fontColor: "#666666"
|
||||||
|
},
|
||||||
|
extra: {
|
||||||
|
line: {
|
||||||
|
type: "curve",
|
||||||
|
width: 2,
|
||||||
|
activeType: "hollow"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 图表数据
|
||||||
|
weightChartData: {},
|
||||||
|
|
||||||
|
// AI分析
|
||||||
|
aiAnalysis: {
|
||||||
|
overallAssessment: '',
|
||||||
|
trendAnalysis: '',
|
||||||
|
recommendations: '',
|
||||||
|
preventiveMeasures: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
showGrowthChart() {
|
||||||
|
return this.petInfo.age < 2 // 只为幼宠显示生长曲线
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
|
this.petId = options.petId || ''
|
||||||
|
this.petName = options.petName || '宠物'
|
||||||
|
this.loadPetInfo()
|
||||||
|
this.loadHealthData()
|
||||||
|
this.generateChartData()
|
||||||
},
|
},
|
||||||
onReady() {
|
|
||||||
|
|
||||||
},
|
|
||||||
methods: {
|
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)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
loadHealthData() {
|
||||||
|
// 获取综合健康数据
|
||||||
|
const healthData = healthManager.getComprehensiveHealthData(this.petId, this.petInfo)
|
||||||
|
|
||||||
|
this.healthScore = healthData.healthScore
|
||||||
|
this.healthTrend = healthData.healthTrend
|
||||||
|
this.weightData = healthData.weightData
|
||||||
|
this.vaccineData = healthData.vaccineData
|
||||||
|
this.medicalData = healthData.medicalData
|
||||||
|
this.growthData = healthData.growthData
|
||||||
|
this.riskFactors = healthData.riskFactors
|
||||||
|
|
||||||
|
// 生成AI分析
|
||||||
|
this.aiAnalysis = healthManager.generateAIHealthAnalysis(this.petId, this.petInfo)
|
||||||
|
|
||||||
|
// 更新最后更新时间
|
||||||
|
this.lastUpdateTime = new Date().toLocaleString('zh-CN', {
|
||||||
|
month: 'numeric',
|
||||||
|
day: 'numeric',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
switchTimeRange(timeKey) {
|
||||||
|
this.activeTimeRange = timeKey
|
||||||
|
this.generateChartData()
|
||||||
|
},
|
||||||
|
|
||||||
|
generateChartData() {
|
||||||
|
// 生成体重图表数据
|
||||||
|
this.weightChartData = weightManager.generateChartData(this.petId, this.activeTimeRange)
|
||||||
|
},
|
||||||
|
|
||||||
|
getScoreColor() {
|
||||||
|
if (this.healthScore >= 85) return '#4CAF50'
|
||||||
|
if (this.healthScore >= 70) return '#FF9800'
|
||||||
|
return '#F44336'
|
||||||
|
},
|
||||||
|
|
||||||
|
getStatusColor(status) {
|
||||||
|
const colors = {
|
||||||
|
'good': '#4CAF50',
|
||||||
|
'warning': '#FF9800',
|
||||||
|
'poor': '#F44336'
|
||||||
|
}
|
||||||
|
return colors[status] || '#666666'
|
||||||
|
},
|
||||||
|
|
||||||
|
getStatusText(status) {
|
||||||
|
const texts = {
|
||||||
|
'good': '良好',
|
||||||
|
'warning': '注意',
|
||||||
|
'poor': '需改善'
|
||||||
|
}
|
||||||
|
return texts[status] || '未知'
|
||||||
|
},
|
||||||
|
|
||||||
|
getTrendIcon() {
|
||||||
|
const icons = {
|
||||||
|
'improving': '📈',
|
||||||
|
'stable': '➡️',
|
||||||
|
'declining': '📉'
|
||||||
|
}
|
||||||
|
return icons[this.healthTrend] || '➡️'
|
||||||
|
},
|
||||||
|
|
||||||
|
getTrendText() {
|
||||||
|
const texts = {
|
||||||
|
'improving': '改善中',
|
||||||
|
'stable': '保持稳定',
|
||||||
|
'declining': '需关注'
|
||||||
|
}
|
||||||
|
return texts[this.healthTrend] || '稳定'
|
||||||
|
},
|
||||||
|
|
||||||
|
getGrowthStageText() {
|
||||||
|
const stages = {
|
||||||
|
'kitten': '幼猫期',
|
||||||
|
'young': '青年期',
|
||||||
|
'adult': '成年期',
|
||||||
|
'senior': '老年期'
|
||||||
|
}
|
||||||
|
return stages[this.growthData.growthStage] || '成年期'
|
||||||
|
},
|
||||||
|
|
||||||
|
getGrowthSummary() {
|
||||||
|
const stage = this.growthData.growthStage
|
||||||
|
const weight = this.growthData.currentWeight
|
||||||
|
|
||||||
|
if (stage === 'kitten') {
|
||||||
|
return `幼猫期体重${weight}kg,生长发育${this.growthData.status === 'good' ? '正常' : '需关注'}`
|
||||||
|
} else if (stage === 'senior') {
|
||||||
|
return `老年期体重${weight}kg,建议定期体检关注健康状况`
|
||||||
|
} else {
|
||||||
|
return `${this.getGrowthStageText()}体重${weight}kg,整体发育状况良好`
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.health-charts-container {
|
||||||
|
background: linear-gradient(135deg, #FF8A80 0%, #FFB6C1 25%, #FECFEF 50%, #F8BBD9 100%);
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 健康状态总览卡片 */
|
||||||
|
.health-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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.health-summary {
|
||||||
|
.health-score-section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 32rpx;
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
|
||||||
|
.score-circle {
|
||||||
|
.circle-progress {
|
||||||
|
width: 140rpx;
|
||||||
|
height: 140rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.circle-center {
|
||||||
|
width: 100rpx;
|
||||||
|
height: 100rpx;
|
||||||
|
background: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.score-value {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333333;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-label {
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #999999;
|
||||||
|
margin-top: 6rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-details {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.score-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-name {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score-points {
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.health-trend {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
|
||||||
|
.trend-indicator {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12rpx;
|
||||||
|
padding: 12rpx 24rpx;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
|
||||||
|
&.improving {
|
||||||
|
background: #E8F5E8;
|
||||||
|
color: #4CAF50;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.stable {
|
||||||
|
background: #E3F2FD;
|
||||||
|
color: #2196F3;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.declining {
|
||||||
|
background: #FFEBEE;
|
||||||
|
color: #F44336;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trend-icon {
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trend-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.risk-alerts {
|
||||||
|
.alerts-title {
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #FF9800;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-item {
|
||||||
|
padding: 12rpx 16rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.high {
|
||||||
|
background: #FFEBEE;
|
||||||
|
border-left: 4rpx solid #F44336;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.medium {
|
||||||
|
background: #FFF3E0;
|
||||||
|
border-left: 4rpx solid #FF9800;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.low {
|
||||||
|
background: #E8F5E8;
|
||||||
|
border-left: 4rpx solid #4CAF50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-text {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 时间选择器 */
|
||||||
|
.time-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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 12rpx;
|
||||||
|
|
||||||
|
.time-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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 数据图表区域 */
|
||||||
|
.data-charts-section {
|
||||||
|
.chart-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);
|
||||||
|
|
||||||
|
.chart-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 28rpx;
|
||||||
|
|
||||||
|
.chart-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-status {
|
||||||
|
padding: 8rpx 16rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
font-size: 22rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
&.good {
|
||||||
|
background: #E8F5E8;
|
||||||
|
color: #4CAF50;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
background: #FFF3E0;
|
||||||
|
color: #FF9800;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.poor {
|
||||||
|
background: #FFEBEE;
|
||||||
|
color: #F44336;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-text {
|
||||||
|
font-size: 22rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-summary {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.summary-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 疫苗时间线 */
|
||||||
|
.vaccine-timeline {
|
||||||
|
.timeline-summary {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
background: rgba(255, 138, 128, 0.05);
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 24rpx;
|
||||||
|
|
||||||
|
.summary-item {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.summary-label {
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #999999;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-value {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 医疗统计 */
|
||||||
|
.medical-stats {
|
||||||
|
.stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 16rpx;
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
background: rgba(255, 138, 128, 0.05);
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 20rpx;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.stat-number {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #FF8A80;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #999999;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 生长信息 */
|
||||||
|
.growth-info {
|
||||||
|
.growth-stage {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
|
||||||
|
.stage-label {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stage-value {
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #FF8A80;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.growth-summary {
|
||||||
|
background: rgba(255, 138, 128, 0.05);
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 20rpx;
|
||||||
|
|
||||||
|
.summary-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666666;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AI健康分析 */
|
||||||
|
.ai-health-analysis {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,501 @@
|
||||||
|
/**
|
||||||
|
* 宠物健康数据管理工具类
|
||||||
|
* 负责整合体重、疫苗、健康记录等数据,提供综合健康评估
|
||||||
|
*/
|
||||||
|
|
||||||
|
import weightManager from './weightManager.js'
|
||||||
|
import vaccineManager from './vaccineManager.js'
|
||||||
|
|
||||||
|
class HealthManager {
|
||||||
|
constructor() {
|
||||||
|
this.storageKey = 'pet_health_records'
|
||||||
|
|
||||||
|
// 健康记录类型定义
|
||||||
|
this.recordTypes = {
|
||||||
|
medical: {
|
||||||
|
name: '就医记录',
|
||||||
|
icon: '🏥',
|
||||||
|
color: '#F44336'
|
||||||
|
},
|
||||||
|
checkup: {
|
||||||
|
name: '体检记录',
|
||||||
|
icon: '🔍',
|
||||||
|
color: '#2196F3'
|
||||||
|
},
|
||||||
|
symptom: {
|
||||||
|
name: '异常症状',
|
||||||
|
icon: '⚠️',
|
||||||
|
color: '#FF9800'
|
||||||
|
},
|
||||||
|
medication: {
|
||||||
|
name: '用药记录',
|
||||||
|
icon: '💊',
|
||||||
|
color: '#9C27B0'
|
||||||
|
},
|
||||||
|
surgery: {
|
||||||
|
name: '手术记录',
|
||||||
|
icon: '🔪',
|
||||||
|
color: '#795548'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 健康评分权重配置
|
||||||
|
this.scoreWeights = {
|
||||||
|
weight: 0.3, // 体重状态权重
|
||||||
|
vaccine: 0.3, // 疫苗状态权重
|
||||||
|
medical: 0.2, // 医疗记录权重
|
||||||
|
growth: 0.2 // 生长发育权重
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取宠物的健康记录
|
||||||
|
* @param {string} petId 宠物ID
|
||||||
|
* @returns {Array} 健康记录数组
|
||||||
|
*/
|
||||||
|
getHealthRecords(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,
|
||||||
|
type: 'checkup',
|
||||||
|
title: '年度体检',
|
||||||
|
date: new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000).toISOString(),
|
||||||
|
description: '全面体检,各项指标正常',
|
||||||
|
result: 'normal',
|
||||||
|
hospital: '宠物医院',
|
||||||
|
veterinarian: '张医生',
|
||||||
|
cost: 200,
|
||||||
|
note: '建议半年后复查'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 就医记录
|
||||||
|
testData.push({
|
||||||
|
id: Date.now() + 2,
|
||||||
|
petId: petId,
|
||||||
|
type: 'medical',
|
||||||
|
title: '感冒治疗',
|
||||||
|
date: new Date(now.getTime() - 60 * 24 * 60 * 60 * 1000).toISOString(),
|
||||||
|
description: '轻微感冒症状,开具感冒药',
|
||||||
|
result: 'treated',
|
||||||
|
hospital: '宠物医院',
|
||||||
|
veterinarian: '李医生',
|
||||||
|
cost: 80,
|
||||||
|
note: '已康复'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 异常症状记录
|
||||||
|
testData.push({
|
||||||
|
id: Date.now() + 3,
|
||||||
|
petId: petId,
|
||||||
|
type: 'symptom',
|
||||||
|
title: '食欲不振',
|
||||||
|
date: new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000).toISOString(),
|
||||||
|
description: '连续两天食欲不佳,精神状态一般',
|
||||||
|
result: 'resolved',
|
||||||
|
hospital: '',
|
||||||
|
veterinarian: '',
|
||||||
|
cost: 0,
|
||||||
|
note: '自行恢复'
|
||||||
|
})
|
||||||
|
|
||||||
|
return testData
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加健康记录
|
||||||
|
* @param {string} petId 宠物ID
|
||||||
|
* @param {Object} record 健康记录
|
||||||
|
*/
|
||||||
|
addHealthRecord(petId, record) {
|
||||||
|
try {
|
||||||
|
const allRecords = uni.getStorageSync(this.storageKey) || {}
|
||||||
|
if (!allRecords[petId]) {
|
||||||
|
allRecords[petId] = []
|
||||||
|
}
|
||||||
|
|
||||||
|
const newRecord = {
|
||||||
|
id: Date.now(),
|
||||||
|
petId: petId,
|
||||||
|
type: record.type,
|
||||||
|
title: record.title,
|
||||||
|
date: record.date || new Date().toISOString(),
|
||||||
|
description: record.description || '',
|
||||||
|
result: record.result || 'pending',
|
||||||
|
hospital: record.hospital || '',
|
||||||
|
veterinarian: record.veterinarian || '',
|
||||||
|
cost: record.cost || 0,
|
||||||
|
note: record.note || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
allRecords[petId].push(newRecord)
|
||||||
|
allRecords[petId].sort((a, b) => new Date(b.date) - new Date(a.date))
|
||||||
|
|
||||||
|
uni.setStorageSync(this.storageKey, allRecords)
|
||||||
|
return newRecord
|
||||||
|
} catch (error) {
|
||||||
|
console.error('添加健康记录失败:', error)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取综合健康数据
|
||||||
|
* @param {string} petId 宠物ID
|
||||||
|
* @param {Object} petInfo 宠物信息
|
||||||
|
* @returns {Object} 综合健康数据
|
||||||
|
*/
|
||||||
|
getComprehensiveHealthData(petId, petInfo) {
|
||||||
|
// 获取各类数据
|
||||||
|
const weightData = this.getWeightHealthData(petId)
|
||||||
|
const vaccineData = this.getVaccineHealthData(petId)
|
||||||
|
const medicalData = this.getMedicalHealthData(petId)
|
||||||
|
const growthData = this.getGrowthHealthData(petId, petInfo)
|
||||||
|
|
||||||
|
// 计算综合健康评分
|
||||||
|
const healthScore = this.calculateHealthScore(weightData, vaccineData, medicalData, growthData)
|
||||||
|
|
||||||
|
// 生成健康趋势
|
||||||
|
const healthTrend = this.analyzeHealthTrend(petId)
|
||||||
|
|
||||||
|
// 识别风险因素
|
||||||
|
const riskFactors = this.identifyRiskFactors(weightData, vaccineData, medicalData)
|
||||||
|
|
||||||
|
return {
|
||||||
|
healthScore,
|
||||||
|
healthTrend,
|
||||||
|
riskFactors,
|
||||||
|
weightData,
|
||||||
|
vaccineData,
|
||||||
|
medicalData,
|
||||||
|
growthData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取体重健康数据
|
||||||
|
* @param {string} petId 宠物ID
|
||||||
|
* @returns {Object} 体重健康数据
|
||||||
|
*/
|
||||||
|
getWeightHealthData(petId) {
|
||||||
|
const currentWeight = weightManager.getCurrentWeight(petId)
|
||||||
|
const weeklyChange = weightManager.calculateWeightChange(petId, 7)
|
||||||
|
const monthlyChange = weightManager.calculateWeightChange(petId, 30)
|
||||||
|
|
||||||
|
// 体重状态评分 (0-100)
|
||||||
|
let weightScore = 85 // 默认良好
|
||||||
|
if (Math.abs(weeklyChange.percent) > 10) {
|
||||||
|
weightScore = 60 // 变化过快
|
||||||
|
} else if (Math.abs(weeklyChange.percent) > 5) {
|
||||||
|
weightScore = 75 // 变化较快
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentWeight,
|
||||||
|
weeklyChange,
|
||||||
|
monthlyChange,
|
||||||
|
weightScore,
|
||||||
|
status: weightScore >= 80 ? 'good' : weightScore >= 60 ? 'warning' : 'poor'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取疫苗健康数据
|
||||||
|
* @param {string} petId 宠物ID
|
||||||
|
* @returns {Object} 疫苗健康数据
|
||||||
|
*/
|
||||||
|
getVaccineHealthData(petId) {
|
||||||
|
const statusSummary = vaccineManager.getVaccineStatusSummary(petId)
|
||||||
|
|
||||||
|
// 疫苗状态评分
|
||||||
|
let vaccineScore = statusSummary.completionRate
|
||||||
|
if (statusSummary.expired > 0) {
|
||||||
|
vaccineScore -= 20
|
||||||
|
}
|
||||||
|
if (statusSummary.expiringSoon > 0) {
|
||||||
|
vaccineScore -= 10
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusSummary,
|
||||||
|
vaccineScore: Math.max(0, vaccineScore),
|
||||||
|
status: vaccineScore >= 80 ? 'good' : vaccineScore >= 60 ? 'warning' : 'poor'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取医疗健康数据
|
||||||
|
* @param {string} petId 宠物ID
|
||||||
|
* @returns {Object} 医疗健康数据
|
||||||
|
*/
|
||||||
|
getMedicalHealthData(petId) {
|
||||||
|
const records = this.getHealthRecords(petId)
|
||||||
|
const now = new Date()
|
||||||
|
const threeMonthsAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000)
|
||||||
|
|
||||||
|
// 近三个月的医疗记录
|
||||||
|
const recentRecords = records.filter(record => new Date(record.date) >= threeMonthsAgo)
|
||||||
|
const medicalVisits = recentRecords.filter(record => record.type === 'medical').length
|
||||||
|
const symptoms = recentRecords.filter(record => record.type === 'symptom').length
|
||||||
|
|
||||||
|
// 医疗状态评分
|
||||||
|
let medicalScore = 90 // 默认健康
|
||||||
|
if (medicalVisits > 2) {
|
||||||
|
medicalScore = 60 // 就医频繁
|
||||||
|
} else if (medicalVisits > 0 || symptoms > 1) {
|
||||||
|
medicalScore = 75 // 有健康问题
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalRecords: records.length,
|
||||||
|
recentRecords: recentRecords.length,
|
||||||
|
medicalVisits,
|
||||||
|
symptoms,
|
||||||
|
medicalScore,
|
||||||
|
status: medicalScore >= 80 ? 'good' : medicalScore >= 60 ? 'warning' : 'poor'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取生长发育数据
|
||||||
|
* @param {string} petId 宠物ID
|
||||||
|
* @param {Object} petInfo 宠物信息
|
||||||
|
* @returns {Object} 生长发育数据
|
||||||
|
*/
|
||||||
|
getGrowthHealthData(petId, petInfo) {
|
||||||
|
const age = petInfo.age || 2
|
||||||
|
const currentWeight = weightManager.getCurrentWeight(petId)
|
||||||
|
|
||||||
|
// 根据年龄判断生长阶段
|
||||||
|
let growthStage = 'adult'
|
||||||
|
if (age < 1) {
|
||||||
|
growthStage = 'kitten'
|
||||||
|
} else if (age < 2) {
|
||||||
|
growthStage = 'young'
|
||||||
|
} else if (age > 7) {
|
||||||
|
growthStage = 'senior'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生长发育评分
|
||||||
|
let growthScore = 85
|
||||||
|
if (growthStage === 'kitten' && currentWeight < 2) {
|
||||||
|
growthScore = 70 // 幼猫体重偏轻
|
||||||
|
} else if (growthStage === 'senior' && currentWeight > 6) {
|
||||||
|
growthScore = 75 // 老年猫超重
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
age,
|
||||||
|
growthStage,
|
||||||
|
currentWeight,
|
||||||
|
growthScore,
|
||||||
|
status: growthScore >= 80 ? 'good' : growthScore >= 60 ? 'warning' : 'poor'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算综合健康评分
|
||||||
|
* @param {Object} weightData 体重数据
|
||||||
|
* @param {Object} vaccineData 疫苗数据
|
||||||
|
* @param {Object} medicalData 医疗数据
|
||||||
|
* @param {Object} growthData 生长数据
|
||||||
|
* @returns {number} 健康评分
|
||||||
|
*/
|
||||||
|
calculateHealthScore(weightData, vaccineData, medicalData, growthData) {
|
||||||
|
const score =
|
||||||
|
weightData.weightScore * this.scoreWeights.weight +
|
||||||
|
vaccineData.vaccineScore * this.scoreWeights.vaccine +
|
||||||
|
medicalData.medicalScore * this.scoreWeights.medical +
|
||||||
|
growthData.growthScore * this.scoreWeights.growth
|
||||||
|
|
||||||
|
return Math.round(score)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析健康趋势
|
||||||
|
* @param {string} petId 宠物ID
|
||||||
|
* @returns {string} 趋势分析
|
||||||
|
*/
|
||||||
|
analyzeHealthTrend(petId) {
|
||||||
|
const weightChange = weightManager.calculateWeightChange(petId, 30)
|
||||||
|
const healthRecords = this.getHealthRecords(petId)
|
||||||
|
|
||||||
|
// 简化的趋势分析
|
||||||
|
if (healthRecords.length === 0) {
|
||||||
|
return 'stable'
|
||||||
|
}
|
||||||
|
|
||||||
|
const recentRecords = healthRecords.filter(record => {
|
||||||
|
const recordDate = new Date(record.date)
|
||||||
|
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)
|
||||||
|
return recordDate >= thirtyDaysAgo
|
||||||
|
})
|
||||||
|
|
||||||
|
if (recentRecords.length > 2) {
|
||||||
|
return 'declining'
|
||||||
|
} else if (recentRecords.length === 0 && Math.abs(weightChange.percent) < 3) {
|
||||||
|
return 'improving'
|
||||||
|
} else {
|
||||||
|
return 'stable'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 识别风险因素
|
||||||
|
* @param {Object} weightData 体重数据
|
||||||
|
* @param {Object} vaccineData 疫苗数据
|
||||||
|
* @param {Object} medicalData 医疗数据
|
||||||
|
* @returns {Array} 风险因素列表
|
||||||
|
*/
|
||||||
|
identifyRiskFactors(weightData, vaccineData, medicalData) {
|
||||||
|
const risks = []
|
||||||
|
|
||||||
|
if (weightData.status === 'poor') {
|
||||||
|
risks.push({
|
||||||
|
type: 'weight',
|
||||||
|
level: 'high',
|
||||||
|
description: '体重变化异常,需要关注饮食和运动'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vaccineData.statusSummary.expired > 0) {
|
||||||
|
risks.push({
|
||||||
|
type: 'vaccine',
|
||||||
|
level: 'high',
|
||||||
|
description: '疫苗已过期,免疫保护失效'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (medicalData.medicalVisits > 2) {
|
||||||
|
risks.push({
|
||||||
|
type: 'medical',
|
||||||
|
level: 'medium',
|
||||||
|
description: '近期就医频繁,需要关注健康状况'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return risks
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成AI健康分析
|
||||||
|
* @param {string} petId 宠物ID
|
||||||
|
* @param {Object} petInfo 宠物信息
|
||||||
|
* @returns {Object} AI分析结果
|
||||||
|
*/
|
||||||
|
generateAIHealthAnalysis(petId, petInfo) {
|
||||||
|
const healthData = this.getComprehensiveHealthData(petId, petInfo)
|
||||||
|
|
||||||
|
// 综合评估
|
||||||
|
let overallAssessment = ''
|
||||||
|
if (healthData.healthScore >= 85) {
|
||||||
|
overallAssessment = `${petInfo.name}的整体健康状况优秀,各项指标均在正常范围内。`
|
||||||
|
} else if (healthData.healthScore >= 70) {
|
||||||
|
overallAssessment = `${petInfo.name}的健康状况良好,但有一些需要关注的方面。`
|
||||||
|
} else {
|
||||||
|
overallAssessment = `${petInfo.name}的健康状况需要改善,建议加强健康管理。`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 趋势分析
|
||||||
|
const trendTexts = {
|
||||||
|
'improving': '健康状况呈改善趋势,继续保持良好的护理习惯。',
|
||||||
|
'stable': '健康状况保持稳定,维持当前的护理方式。',
|
||||||
|
'declining': '健康状况有下降趋势,建议加强关注并考虑就医。'
|
||||||
|
}
|
||||||
|
const trendAnalysis = trendTexts[healthData.healthTrend] || '健康趋势需要进一步观察。'
|
||||||
|
|
||||||
|
// 个性化建议
|
||||||
|
const recommendations = this.generateHealthRecommendations(healthData, petInfo)
|
||||||
|
|
||||||
|
// 预防措施
|
||||||
|
const preventiveMeasures = this.generatePreventiveMeasures(healthData, petInfo)
|
||||||
|
|
||||||
|
return {
|
||||||
|
overallAssessment,
|
||||||
|
trendAnalysis,
|
||||||
|
recommendations,
|
||||||
|
preventiveMeasures,
|
||||||
|
riskFactors: healthData.riskFactors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成健康建议
|
||||||
|
* @param {Object} healthData 健康数据
|
||||||
|
* @param {Object} petInfo 宠物信息
|
||||||
|
* @returns {string} 健康建议
|
||||||
|
*/
|
||||||
|
generateHealthRecommendations(healthData, petInfo) {
|
||||||
|
const recommendations = []
|
||||||
|
|
||||||
|
if (healthData.weightData.status !== 'good') {
|
||||||
|
recommendations.push('调整饮食结构,控制食物分量')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (healthData.vaccineData.statusSummary.expiringSoon > 0) {
|
||||||
|
recommendations.push('及时安排疫苗接种,维持免疫保护')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (healthData.medicalData.symptoms > 0) {
|
||||||
|
recommendations.push('密切观察异常症状,必要时及时就医')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据年龄阶段添加建议
|
||||||
|
if (healthData.growthData.growthStage === 'senior') {
|
||||||
|
recommendations.push('增加体检频率,关注老年期常见疾病')
|
||||||
|
} else if (healthData.growthData.growthStage === 'kitten') {
|
||||||
|
recommendations.push('确保营养充足,支持健康成长')
|
||||||
|
}
|
||||||
|
|
||||||
|
return recommendations.length > 0 ? recommendations.join(';') + '。' : '继续保持良好的护理习惯。'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成预防措施
|
||||||
|
* @param {Object} healthData 健康数据
|
||||||
|
* @param {Object} petInfo 宠物信息
|
||||||
|
* @returns {string} 预防措施
|
||||||
|
*/
|
||||||
|
generatePreventiveMeasures(healthData, petInfo) {
|
||||||
|
const measures = []
|
||||||
|
|
||||||
|
measures.push('定期体检,建议每6-12个月进行一次全面检查')
|
||||||
|
measures.push('保持适当运动,每日互动游戏15-30分钟')
|
||||||
|
measures.push('维护口腔健康,定期检查牙齿和牙龈')
|
||||||
|
|
||||||
|
if (petInfo.age > 7) {
|
||||||
|
measures.push('老年宠物需要更频繁的健康监测')
|
||||||
|
}
|
||||||
|
|
||||||
|
return measures.join(';') + '。'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new HealthManager()
|
||||||
Loading…
Reference in New Issue