This commit is contained in:
parent
4d3a62d185
commit
f071de4ead
27
pages.json
27
pages.json
|
|
@ -64,14 +64,7 @@
|
|||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/pets/add-record",
|
||||
"style": {
|
||||
"navigationBarTitleText": "添加记录",
|
||||
"navigationBarBackgroundColor": "#FF8A80",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "pages/pets/pet-weight",
|
||||
"style": {
|
||||
|
|
@ -105,22 +98,8 @@
|
|||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/pets/add-record-enhanced",
|
||||
"style": {
|
||||
"navigationBarTitleText": "添加记录",
|
||||
"navigationBarBackgroundColor": "#FF8A80",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/pets/add-record-simple",
|
||||
"style": {
|
||||
"navigationBarTitleText": "添加记录",
|
||||
"navigationBarBackgroundColor": "#FF8A80",
|
||||
"navigationBarTextStyle": "white"
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"path": "pages/pets/select-record-type",
|
||||
"style": {
|
||||
|
|
|
|||
|
|
@ -162,7 +162,76 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import adoptionManager from '@/utils/adoptionManager.js'
|
||||
// 简化的领养管理器 - 从 utils/adoptionManager.js 移入
|
||||
const adoptionManager = {
|
||||
storageKey: 'adoption_pets',
|
||||
|
||||
getAdoptionPets() {
|
||||
try {
|
||||
return uni.getStorageSync(this.storageKey) || []
|
||||
} catch (error) {
|
||||
console.error('获取领养宠物数据失败:', error)
|
||||
return []
|
||||
}
|
||||
},
|
||||
|
||||
getPetById(id) {
|
||||
const pets = this.getAdoptionPets()
|
||||
return pets.find(pet => pet.id == id)
|
||||
},
|
||||
|
||||
getPetTypeName(type) {
|
||||
const types = {
|
||||
cat: '猫咪',
|
||||
dog: '狗狗',
|
||||
rabbit: '兔子',
|
||||
other: '其他'
|
||||
}
|
||||
return types[type] || '未知类型'
|
||||
},
|
||||
|
||||
getPetBreedName(type, breed) {
|
||||
const breeds = {
|
||||
cat: {
|
||||
'british-shorthair': '英国短毛猫',
|
||||
'american-shorthair': '美国短毛猫',
|
||||
'persian': '波斯猫',
|
||||
'ragdoll': '布偶猫',
|
||||
'siamese': '暹罗猫',
|
||||
'mixed': '混血猫'
|
||||
},
|
||||
dog: {
|
||||
'golden-retriever': '金毛寻回犬',
|
||||
'labrador': '拉布拉多',
|
||||
'husky': '哈士奇',
|
||||
'corgi': '柯基',
|
||||
'shiba-inu': '柴犬',
|
||||
'mixed': '混血犬'
|
||||
}
|
||||
}
|
||||
return breeds[type]?.[breed] || '未知品种'
|
||||
},
|
||||
|
||||
getLocationName(province, city, district) {
|
||||
const regions = {
|
||||
'beijing': '北京市',
|
||||
'shanghai': '上海市',
|
||||
'guangdong': '广东省',
|
||||
'jiangsu': '江苏省'
|
||||
}
|
||||
return regions[province] || province
|
||||
},
|
||||
|
||||
getStatusInfo(status) {
|
||||
const statusMap = {
|
||||
available: { name: '可领养', color: '#4CAF50', icon: '✅' },
|
||||
reserved: { name: '已预约', color: '#FF9800', icon: '⏰' },
|
||||
adopted: { name: '已领养', color: '#9E9E9E', icon: '❤️' },
|
||||
pending: { name: '审核中', color: '#2196F3', icon: '📋' }
|
||||
}
|
||||
return statusMap[status] || { name: '未知状态', color: '#999999', icon: '❓' }
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
|
|
|||
|
|
@ -285,7 +285,346 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import adoptionManager from '@/utils/adoptionManager.js'
|
||||
// 宠物领养数据管理工具类 - 从 utils/adoptionManager.js 移入
|
||||
class AdoptionManager {
|
||||
constructor() {
|
||||
this.storageKey = 'adoption_pets'
|
||||
|
||||
// 宠物类型配置
|
||||
this.petTypes = {
|
||||
cat: {
|
||||
name: '猫咪',
|
||||
icon: '🐱',
|
||||
breeds: {
|
||||
'british-shorthair': '英国短毛猫',
|
||||
'american-shorthair': '美国短毛猫',
|
||||
'persian': '波斯猫',
|
||||
'ragdoll': '布偶猫',
|
||||
'siamese': '暹罗猫',
|
||||
'maine-coon': '缅因猫',
|
||||
'scottish-fold': '苏格兰折耳猫',
|
||||
'russian-blue': '俄罗斯蓝猫',
|
||||
'bengal': '孟加拉猫',
|
||||
'mixed': '混血猫',
|
||||
'unknown': '品种不明'
|
||||
}
|
||||
},
|
||||
dog: {
|
||||
name: '狗狗',
|
||||
icon: '🐶',
|
||||
breeds: {
|
||||
'golden-retriever': '金毛寻回犬',
|
||||
'labrador': '拉布拉多',
|
||||
'husky': '哈士奇',
|
||||
'german-shepherd': '德国牧羊犬',
|
||||
'poodle': '贵宾犬',
|
||||
'chihuahua': '吉娃娃',
|
||||
'bulldog': '斗牛犬',
|
||||
'shiba-inu': '柴犬',
|
||||
'corgi': '柯基',
|
||||
'border-collie': '边境牧羊犬',
|
||||
'mixed': '混血犬',
|
||||
'unknown': '品种不明'
|
||||
}
|
||||
},
|
||||
rabbit: {
|
||||
name: '兔子',
|
||||
icon: '🐰',
|
||||
breeds: {
|
||||
'holland-lop': '荷兰垂耳兔',
|
||||
'mini-lop': '迷你垂耳兔',
|
||||
'lionhead': '狮子头兔',
|
||||
'dutch': '荷兰兔',
|
||||
'angora': '安哥拉兔',
|
||||
'mixed': '混血兔',
|
||||
'unknown': '品种不明'
|
||||
}
|
||||
},
|
||||
other: {
|
||||
name: '其他',
|
||||
icon: '🐾',
|
||||
breeds: {
|
||||
'hamster': '仓鼠',
|
||||
'guinea-pig': '豚鼠',
|
||||
'bird': '鸟类',
|
||||
'turtle': '乌龟',
|
||||
'fish': '鱼类',
|
||||
'other': '其他'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 地区数据(简化版三级联动)
|
||||
this.regions = {
|
||||
'beijing': {
|
||||
name: '北京市',
|
||||
cities: {
|
||||
'beijing': {
|
||||
name: '北京市',
|
||||
districts: {
|
||||
'chaoyang': '朝阳区',
|
||||
'haidian': '海淀区',
|
||||
'dongcheng': '东城区',
|
||||
'xicheng': '西城区',
|
||||
'fengtai': '丰台区',
|
||||
'shijingshan': '石景山区'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'shanghai': {
|
||||
name: '上海市',
|
||||
cities: {
|
||||
'shanghai': {
|
||||
name: '上海市',
|
||||
districts: {
|
||||
'huangpu': '黄浦区',
|
||||
'xuhui': '徐汇区',
|
||||
'changning': '长宁区',
|
||||
'jingan': '静安区',
|
||||
'putuo': '普陀区',
|
||||
'hongkou': '虹口区'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'guangdong': {
|
||||
name: '广东省',
|
||||
cities: {
|
||||
'guangzhou': {
|
||||
name: '广州市',
|
||||
districts: {
|
||||
'tianhe': '天河区',
|
||||
'yuexiu': '越秀区',
|
||||
'liwan': '荔湾区',
|
||||
'haizhu': '海珠区',
|
||||
'baiyun': '白云区',
|
||||
'panyu': '番禺区'
|
||||
}
|
||||
},
|
||||
'shenzhen': {
|
||||
name: '深圳市',
|
||||
districts: {
|
||||
'futian': '福田区',
|
||||
'luohu': '罗湖区',
|
||||
'nanshan': '南山区',
|
||||
'yantian': '盐田区',
|
||||
'baoan': '宝安区',
|
||||
'longgang': '龙岗区'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'jiangsu': {
|
||||
name: '江苏省',
|
||||
cities: {
|
||||
'nanjing': {
|
||||
name: '南京市',
|
||||
districts: {
|
||||
'xuanwu': '玄武区',
|
||||
'qinhuai': '秦淮区',
|
||||
'jianye': '建邺区',
|
||||
'gulou': '鼓楼区',
|
||||
'pukou': '浦口区',
|
||||
'qixia': '栖霞区'
|
||||
}
|
||||
},
|
||||
'suzhou': {
|
||||
name: '苏州市',
|
||||
districts: {
|
||||
'gusu': '姑苏区',
|
||||
'wuzhong': '吴中区',
|
||||
'xiangcheng': '相城区',
|
||||
'kunshan': '昆山市',
|
||||
'changshu': '常熟市',
|
||||
'zhangjiagang': '张家港市'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 领养状态
|
||||
this.adoptionStatus = {
|
||||
available: { name: '可领养', color: '#4CAF50', icon: '✅' },
|
||||
reserved: { name: '已预约', color: '#FF9800', icon: '⏰' },
|
||||
adopted: { name: '已领养', color: '#9E9E9E', icon: '❤️' },
|
||||
pending: { name: '审核中', color: '#2196F3', icon: '📋' }
|
||||
}
|
||||
}
|
||||
|
||||
// 获取所有领养宠物数据
|
||||
getAdoptionPets() {
|
||||
try {
|
||||
let pets = uni.getStorageSync(this.storageKey) || []
|
||||
|
||||
// 如果没有数据,初始化一些测试数据
|
||||
if (pets.length === 0) {
|
||||
pets = this.initializeTestData()
|
||||
uni.setStorageSync(this.storageKey, pets)
|
||||
}
|
||||
|
||||
return pets
|
||||
} catch (error) {
|
||||
console.error('获取领养宠物数据失败:', error)
|
||||
return this.initializeTestData()
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化测试数据
|
||||
initializeTestData() {
|
||||
const testData = []
|
||||
|
||||
// 猫咪数据
|
||||
testData.push({
|
||||
id: Date.now() + 1,
|
||||
name: '小橘',
|
||||
type: 'cat',
|
||||
breed: 'british-shorthair',
|
||||
age: 2,
|
||||
gender: 'male',
|
||||
photos: ['https://images.unsplash.com/photo-1574158622682-e40e69881006?w=400&h=300&fit=crop', 'https://images.unsplash.com/photo-1592194996308-7b43878e84a6?w=400&h=300&fit=crop'],
|
||||
description: '小橘是一只非常温顺的英国短毛猫,性格亲人,喜欢和人互动。已经完成绝育手术和疫苗接种,身体健康。适合有爱心的家庭领养,希望能给它一个温暖的家。',
|
||||
personality: ['温顺', '亲人', '安静', '乖巧'],
|
||||
health: '健康良好,已绝育,疫苗齐全',
|
||||
location: {
|
||||
province: 'beijing',
|
||||
city: 'beijing',
|
||||
district: 'chaoyang',
|
||||
address: '朝阳区宠物救助中心'
|
||||
},
|
||||
status: 'available',
|
||||
requirements: [
|
||||
'有稳定收入',
|
||||
'有养猫经验',
|
||||
'家中无其他宠物',
|
||||
'同意定期回访'
|
||||
],
|
||||
contact: {
|
||||
name: '北京爱心救助站',
|
||||
phone: '138****1234',
|
||||
wechat: 'rescue_station_bj',
|
||||
type: 'organization'
|
||||
},
|
||||
publishTime: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString()
|
||||
})
|
||||
|
||||
// 更多测试数据...
|
||||
return testData
|
||||
}
|
||||
|
||||
// 搜索宠物
|
||||
searchPets(keyword, pets = null) {
|
||||
if (!pets) {
|
||||
pets = this.getAdoptionPets()
|
||||
}
|
||||
|
||||
if (!keyword) return pets
|
||||
|
||||
const lowerKeyword = keyword.toLowerCase()
|
||||
return pets.filter(pet => {
|
||||
return pet.name.toLowerCase().includes(lowerKeyword) ||
|
||||
pet.description.toLowerCase().includes(lowerKeyword) ||
|
||||
this.getPetTypeName(pet.type).includes(keyword) ||
|
||||
this.getPetBreedName(pet.type, pet.breed).includes(keyword) ||
|
||||
pet.personality.some(trait => trait.includes(keyword))
|
||||
})
|
||||
}
|
||||
|
||||
// 筛选宠物
|
||||
filterPets(filters, pets = null) {
|
||||
if (!pets) {
|
||||
pets = this.getAdoptionPets()
|
||||
}
|
||||
|
||||
return pets.filter(pet => {
|
||||
// 宠物类型筛选
|
||||
if (filters.type && pet.type !== filters.type) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 品种筛选
|
||||
if (filters.breed && pet.breed !== filters.breed) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 地区筛选
|
||||
if (filters.province && pet.location.province !== filters.province) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (filters.city && pet.location.city !== filters.city) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (filters.district && pet.location.district !== filters.district) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
if (filters.status && pet.status !== filters.status) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 性别筛选
|
||||
if (filters.gender && pet.gender !== filters.gender) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 年龄筛选
|
||||
if (filters.ageRange) {
|
||||
const [minAge, maxAge] = filters.ageRange
|
||||
if (pet.age < minAge || pet.age > maxAge) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// 获取宠物类型名称
|
||||
getPetTypeName(type) {
|
||||
return this.petTypes[type]?.name || '未知类型'
|
||||
}
|
||||
|
||||
// 获取宠物品种名称
|
||||
getPetBreedName(type, breed) {
|
||||
return this.petTypes[type]?.breeds[breed] || '未知品种'
|
||||
}
|
||||
|
||||
// 获取地区名称
|
||||
getLocationName(province, city = null, district = null) {
|
||||
let locationName = this.regions[province]?.name || province
|
||||
|
||||
if (city) {
|
||||
const cityName = this.regions[province]?.cities[city]?.name
|
||||
if (cityName) {
|
||||
locationName += ' ' + cityName
|
||||
}
|
||||
}
|
||||
|
||||
if (district) {
|
||||
const districtName = this.regions[province]?.cities[city]?.districts[district]
|
||||
if (districtName) {
|
||||
locationName += ' ' + districtName
|
||||
}
|
||||
}
|
||||
|
||||
return locationName
|
||||
}
|
||||
|
||||
// 获取状态信息
|
||||
getStatusInfo(status) {
|
||||
return this.adoptionStatus[status] || {
|
||||
name: '未知状态',
|
||||
color: '#999999',
|
||||
icon: '❓'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const adoptionManager = new AdoptionManager()
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
|
|
|||
|
|
@ -1,52 +0,0 @@
|
|||
<template>
|
||||
<view class="content page-container">
|
||||
<image class="logo" src="/static/logo.png"></image>
|
||||
<view class="text-area">
|
||||
<text class="title">{{title}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
title: 'Hello'
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 200rpx;
|
||||
width: 200rpx;
|
||||
margin-top: 200rpx;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 50rpx;
|
||||
}
|
||||
|
||||
.text-area {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
color: #8f8f94;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,643 +0,0 @@
|
|||
<template>
|
||||
<view class="add-record-container page-container-with-bg">
|
||||
<u-navbar title="添加记录" left-icon="arrow-left" @left-click="goBack">
|
||||
<template #right>
|
||||
<u-text text="保存" color="#ff6b6b" @click="saveRecord"></u-text>
|
||||
</template>
|
||||
</u-navbar>
|
||||
|
||||
<u-form ref="recordFormRef" :model="recordForm" :rules="rules" label-width="80">
|
||||
<!-- 记录类型选择 -->
|
||||
<u-card title="记录类型" :padding="20" margin="20">
|
||||
<u-radio-group v-model="recordForm.category" @change="onCategoryChange">
|
||||
<view class="category-grid">
|
||||
<view
|
||||
class="category-item"
|
||||
v-for="category in recordCategories"
|
||||
:key="category.value"
|
||||
@click="selectCategory(category.value)"
|
||||
>
|
||||
<view class="category-icon" :class="{ active: recordForm.category === category.value }">
|
||||
<u-icon :name="category.icon" size="24" :color="recordForm.category === category.value ? '#ffffff' : '#666666'"></u-icon>
|
||||
</view>
|
||||
<u-text :text="category.label" size="12" :color="recordForm.category === category.value ? '#ff6b6b' : '#666666'"></u-text>
|
||||
</view>
|
||||
</view>
|
||||
</u-radio-group>
|
||||
</u-card>
|
||||
|
||||
<!-- 基础信息 -->
|
||||
<u-card title="基础信息" :padding="20" margin="20">
|
||||
<u-form-item label="记录时间" prop="recordTime">
|
||||
<u-input
|
||||
v-model="recordForm.recordTime"
|
||||
placeholder="选择记录时间"
|
||||
readonly
|
||||
@click="showDatePicker = true"
|
||||
></u-input>
|
||||
</u-form-item>
|
||||
|
||||
<u-form-item label="记录内容" prop="content">
|
||||
<u-textarea
|
||||
v-model="recordForm.content"
|
||||
placeholder="描述一下具体情况..."
|
||||
maxlength="500"
|
||||
count
|
||||
></u-textarea>
|
||||
</u-form-item>
|
||||
</u-card>
|
||||
|
||||
<!-- 动态表单区域 -->
|
||||
<u-card :title="currentCategoryInfo.title" :padding="20" margin="20" v-if="currentCategoryInfo.fields.length > 0">
|
||||
<view v-for="field in currentCategoryInfo.fields" :key="field.key">
|
||||
<!-- 数字输入 -->
|
||||
<u-form-item :label="field.label" v-if="field.type === 'number'">
|
||||
<u-input
|
||||
v-model="recordForm.details[field.key]"
|
||||
:placeholder="field.placeholder"
|
||||
type="number"
|
||||
>
|
||||
<template #suffix v-if="field.unit">
|
||||
<u-text :text="field.unit" size="14" color="#999"></u-text>
|
||||
</template>
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
|
||||
<!-- 选择器 -->
|
||||
<u-form-item :label="field.label" v-if="field.type === 'select'">
|
||||
<u-input
|
||||
v-model="recordForm.details[field.key]"
|
||||
:placeholder="field.placeholder"
|
||||
readonly
|
||||
@click="showPicker(field)"
|
||||
></u-input>
|
||||
</u-form-item>
|
||||
|
||||
<!-- 多选标签 -->
|
||||
<u-form-item :label="field.label" v-if="field.type === 'tags'">
|
||||
<view class="tags-container">
|
||||
<u-tag
|
||||
v-for="option in field.options"
|
||||
:key="option.value"
|
||||
:text="option.label"
|
||||
:type="isTagSelected(field.key, option.value) ? 'primary' : 'info'"
|
||||
size="mini"
|
||||
@click="toggleTag(field.key, option.value)"
|
||||
></u-tag>
|
||||
</view>
|
||||
</u-form-item>
|
||||
|
||||
<!-- 文本输入 -->
|
||||
<u-form-item :label="field.label" v-if="field.type === 'text'">
|
||||
<u-input
|
||||
v-model="recordForm.details[field.key]"
|
||||
:placeholder="field.placeholder"
|
||||
></u-input>
|
||||
</u-form-item>
|
||||
</view>
|
||||
</u-card>
|
||||
|
||||
<!-- 图片上传 -->
|
||||
<u-card title="添加图片" :padding="20" margin="20">
|
||||
<view class="photo-upload-area">
|
||||
<view class="photo-grid">
|
||||
<view class="photo-item" v-for="(photo, index) in recordForm.photos" :key="index">
|
||||
<u-image :src="photo" width="80px" height="80px" border-radius="8px"></u-image>
|
||||
<view class="photo-delete" @click="removePhoto(index)">
|
||||
<u-icon name="close-circle-fill" size="16" color="#ff6b6b"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="photo-add" @click="choosePhotos" v-if="recordForm.photos.length < 9">
|
||||
<u-icon name="camera-fill" size="24" color="#cccccc"></u-icon>
|
||||
<u-text text="添加图片" size="12" color="#cccccc"></u-text>
|
||||
</view>
|
||||
</view>
|
||||
<u-text :text="`${recordForm.photos.length}/9`" size="12" color="#999" style="margin-top: 10px;"></u-text>
|
||||
</view>
|
||||
</u-card>
|
||||
|
||||
<!-- 分享设置 -->
|
||||
<u-card title="分享设置" :padding="20" margin="20">
|
||||
<u-radio-group v-model="recordForm.shareLevel">
|
||||
<view class="share-options">
|
||||
<view class="share-option" v-for="option in shareOptions" :key="option.value">
|
||||
<u-radio :name="option.value" :disabled="false">
|
||||
<template #icon="{ checked }">
|
||||
<u-icon :name="checked ? 'checkmark-circle-fill' : 'circle'" :color="checked ? '#ff6b6b' : '#c8c9cc'" size="18"></u-icon>
|
||||
</template>
|
||||
</u-radio>
|
||||
<view class="share-info">
|
||||
<u-text :text="option.label" size="14" bold></u-text>
|
||||
<u-text :text="option.desc" size="12" color="#999"></u-text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-radio-group>
|
||||
</u-card>
|
||||
</u-form>
|
||||
|
||||
<!-- 日期选择器 -->
|
||||
<u-datetime-picker
|
||||
:show="showDatePicker"
|
||||
v-model="selectedDate"
|
||||
mode="datetime"
|
||||
@confirm="confirmDate"
|
||||
@cancel="showDatePicker = false"
|
||||
></u-datetime-picker>
|
||||
|
||||
<!-- 选择器弹窗 -->
|
||||
<u-picker
|
||||
:show="showPickerModal"
|
||||
:columns="pickerColumns"
|
||||
@confirm="confirmPicker"
|
||||
@cancel="showPickerModal = false"
|
||||
></u-picker>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'AddRecordEnhanced',
|
||||
setup() {
|
||||
// 响应式数据
|
||||
const state = reactive({
|
||||
petId: '',
|
||||
showDatePicker: false,
|
||||
showPickerModal: false,
|
||||
selectedDate: new Date().getTime(),
|
||||
currentPickerField: null,
|
||||
pickerColumns: [],
|
||||
recordForm: {
|
||||
category: 'daily',
|
||||
recordTime: formatDateTime(new Date()),
|
||||
content: '',
|
||||
details: {},
|
||||
photos: [],
|
||||
shareLevel: 'family'
|
||||
}
|
||||
})
|
||||
|
||||
// 记录分类配置
|
||||
const recordCategories = [
|
||||
{ value: 'daily', label: '随手记', icon: 'edit-pen' },
|
||||
{ value: 'milestone', label: '大事记', icon: 'star' },
|
||||
{ value: 'health', label: '健康记录', icon: 'heart' },
|
||||
{ value: 'grooming', label: '洗护记录', icon: 'flower' },
|
||||
{ value: 'cleaning', label: '清洁记录', icon: 'home' },
|
||||
{ value: 'expense', label: '消费记录', icon: 'rmb-circle' }
|
||||
]
|
||||
|
||||
// 分享选项
|
||||
const shareOptions = [
|
||||
{ value: 'public', label: '公开', desc: '所有用户可见' },
|
||||
{ value: 'family', label: '家人', desc: '仅家庭成员可见' },
|
||||
{ value: 'private', label: '私有', desc: '仅自己可见' }
|
||||
]
|
||||
|
||||
// 记录类型字段配置
|
||||
const categoryFields = {
|
||||
daily: {
|
||||
title: '随手记详情',
|
||||
fields: []
|
||||
},
|
||||
milestone: {
|
||||
title: '大事记详情',
|
||||
fields: [
|
||||
{
|
||||
key: 'milestoneType',
|
||||
label: '里程碑类型',
|
||||
type: 'select',
|
||||
placeholder: '选择里程碑类型',
|
||||
options: [
|
||||
{ label: '绝育', value: 'neuter' },
|
||||
{ label: '第一次吃奶', value: 'first_milk' },
|
||||
{ label: '第一次吃猫粮', value: 'first_food' },
|
||||
{ label: '第一次用猫砂', value: 'first_litter' },
|
||||
{ label: '第一次出门', value: 'first_outside' },
|
||||
{ label: '生日', value: 'birthday' },
|
||||
{ label: '到家纪念日', value: 'adoption_day' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
health: {
|
||||
title: '健康记录详情',
|
||||
fields: [
|
||||
{
|
||||
key: 'healthType',
|
||||
label: '健康类型',
|
||||
type: 'select',
|
||||
placeholder: '选择健康记录类型',
|
||||
options: [
|
||||
{ label: '疫苗接种', value: 'vaccine' },
|
||||
{ label: '驱虫', value: 'deworming' },
|
||||
{ label: '体检', value: 'checkup' },
|
||||
{ label: '看病', value: 'treatment' },
|
||||
{ label: '给药', value: 'medication' },
|
||||
{ label: '手术', value: 'surgery' }
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'weight',
|
||||
label: '体重',
|
||||
type: 'number',
|
||||
placeholder: '输入体重',
|
||||
unit: 'kg'
|
||||
},
|
||||
{
|
||||
key: 'temperature',
|
||||
label: '体温',
|
||||
type: 'number',
|
||||
placeholder: '输入体温',
|
||||
unit: '°C'
|
||||
},
|
||||
{
|
||||
key: 'symptoms',
|
||||
label: '症状',
|
||||
type: 'tags',
|
||||
options: [
|
||||
{ label: '食欲不振', value: 'loss_appetite' },
|
||||
{ label: '精神萎靡', value: 'lethargy' },
|
||||
{ label: '呕吐', value: 'vomiting' },
|
||||
{ label: '腹泻', value: 'diarrhea' },
|
||||
{ label: '发热', value: 'fever' },
|
||||
{ label: '咳嗽', value: 'cough' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
grooming: {
|
||||
title: '洗护记录详情',
|
||||
fields: [
|
||||
{
|
||||
key: 'groomingType',
|
||||
label: '洗护类型',
|
||||
type: 'tags',
|
||||
options: [
|
||||
{ label: '洗澡', value: 'bath' },
|
||||
{ label: '剪指甲', value: 'nail_trim' },
|
||||
{ label: '洗耳朵', value: 'ear_clean' },
|
||||
{ label: '刷牙', value: 'teeth_clean' },
|
||||
{ label: '梳毛', value: 'brush' },
|
||||
{ label: '美容', value: 'grooming' }
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'duration',
|
||||
label: '耗时',
|
||||
type: 'number',
|
||||
placeholder: '输入耗时',
|
||||
unit: '分钟'
|
||||
}
|
||||
]
|
||||
},
|
||||
cleaning: {
|
||||
title: '清洁记录详情',
|
||||
fields: [
|
||||
{
|
||||
key: 'cleaningType',
|
||||
label: '清洁类型',
|
||||
type: 'tags',
|
||||
options: [
|
||||
{ label: '换猫砂', value: 'litter_change' },
|
||||
{ label: '洗猫砂盆', value: 'litter_box_clean' },
|
||||
{ label: '洗食盆', value: 'food_bowl_clean' },
|
||||
{ label: '洗水盆', value: 'water_bowl_clean' },
|
||||
{ label: '清洁笼子', value: 'cage_clean' },
|
||||
{ label: '消毒', value: 'disinfect' }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
expense: {
|
||||
title: '消费记录详情',
|
||||
fields: [
|
||||
{
|
||||
key: 'amount',
|
||||
label: '金额',
|
||||
type: 'number',
|
||||
placeholder: '输入消费金额',
|
||||
unit: '元'
|
||||
},
|
||||
{
|
||||
key: 'expenseType',
|
||||
label: '消费类型',
|
||||
type: 'select',
|
||||
placeholder: '选择消费类型',
|
||||
options: [
|
||||
{ label: '食物', value: 'food' },
|
||||
{ label: '医疗', value: 'medical' },
|
||||
{ label: '用品', value: 'supplies' },
|
||||
{ label: '服务', value: 'service' },
|
||||
{ label: '其他', value: 'other' }
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'store',
|
||||
label: '购买地点',
|
||||
type: 'text',
|
||||
placeholder: '输入购买地点'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// 计算属性
|
||||
const currentCategoryInfo = computed(() => {
|
||||
return categoryFields[state.recordForm.category] || { title: '', fields: [] }
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const rules = {
|
||||
recordTime: [
|
||||
{ required: true, message: '请选择记录时间', trigger: 'blur' }
|
||||
],
|
||||
content: [
|
||||
{ required: true, message: '请输入记录内容', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
initPage()
|
||||
})
|
||||
|
||||
// 初始化页面
|
||||
const initPage = () => {
|
||||
const pages = getCurrentPages()
|
||||
const currentPage = pages[pages.length - 1]
|
||||
state.petId = currentPage.options.petId || '1'
|
||||
}
|
||||
|
||||
// 选择记录类型
|
||||
const selectCategory = (category) => {
|
||||
state.recordForm.category = category
|
||||
state.recordForm.details = {} // 清空详情数据
|
||||
}
|
||||
|
||||
// 记录类型变化
|
||||
const onCategoryChange = (value) => {
|
||||
state.recordForm.details = {} // 清空详情数据
|
||||
}
|
||||
|
||||
// 确认日期
|
||||
const confirmDate = (e) => {
|
||||
const date = new Date(e.value)
|
||||
state.recordForm.recordTime = formatDateTime(date)
|
||||
state.showDatePicker = false
|
||||
}
|
||||
|
||||
// 显示选择器
|
||||
const showPicker = (field) => {
|
||||
state.currentPickerField = field
|
||||
state.pickerColumns = [field.options.map(option => option.label)]
|
||||
state.showPickerModal = true
|
||||
}
|
||||
|
||||
// 确认选择器
|
||||
const confirmPicker = (e) => {
|
||||
if (state.currentPickerField) {
|
||||
const selectedIndex = e.indexs[0]
|
||||
const selectedOption = state.currentPickerField.options[selectedIndex]
|
||||
state.recordForm.details[state.currentPickerField.key] = selectedOption.label
|
||||
}
|
||||
state.showPickerModal = false
|
||||
}
|
||||
|
||||
// 标签选择
|
||||
const isTagSelected = (fieldKey, value) => {
|
||||
const currentValue = state.recordForm.details[fieldKey]
|
||||
if (!currentValue) return false
|
||||
return Array.isArray(currentValue) ? currentValue.includes(value) : currentValue === value
|
||||
}
|
||||
|
||||
const toggleTag = (fieldKey, value) => {
|
||||
if (!state.recordForm.details[fieldKey]) {
|
||||
state.recordForm.details[fieldKey] = []
|
||||
}
|
||||
|
||||
const currentValue = state.recordForm.details[fieldKey]
|
||||
if (Array.isArray(currentValue)) {
|
||||
const index = currentValue.indexOf(value)
|
||||
if (index > -1) {
|
||||
currentValue.splice(index, 1)
|
||||
} else {
|
||||
currentValue.push(value)
|
||||
}
|
||||
} else {
|
||||
state.recordForm.details[fieldKey] = [value]
|
||||
}
|
||||
}
|
||||
|
||||
// 选择图片
|
||||
const choosePhotos = () => {
|
||||
const remainingCount = 9 - state.recordForm.photos.length
|
||||
uni.chooseImage({
|
||||
count: remainingCount,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
state.recordForm.photos.push(...res.tempFilePaths)
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('选择图片失败', err)
|
||||
uni.showToast({
|
||||
title: '选择图片失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 删除图片
|
||||
const removePhoto = (index) => {
|
||||
state.recordForm.photos.splice(index, 1)
|
||||
}
|
||||
|
||||
// 保存记录
|
||||
const saveRecord = () => {
|
||||
// 表单验证
|
||||
if (!state.recordForm.recordTime) {
|
||||
uni.showToast({
|
||||
title: '请选择记录时间',
|
||||
icon: 'error'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!state.recordForm.content.trim()) {
|
||||
uni.showToast({
|
||||
title: '请输入记录内容',
|
||||
icon: 'error'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 构建记录数据
|
||||
const recordData = {
|
||||
id: Date.now().toString(),
|
||||
petId: state.petId,
|
||||
category: state.recordForm.category,
|
||||
recordTime: state.recordForm.recordTime,
|
||||
content: state.recordForm.content,
|
||||
details: state.recordForm.details,
|
||||
photos: state.recordForm.photos,
|
||||
shareLevel: state.recordForm.shareLevel,
|
||||
createTime: new Date().toISOString()
|
||||
}
|
||||
|
||||
// 模拟保存到本地存储
|
||||
try {
|
||||
let records = uni.getStorageSync('petRecords') || []
|
||||
records.unshift(recordData)
|
||||
uni.setStorageSync('petRecords', records)
|
||||
|
||||
uni.showToast({
|
||||
title: '保存成功',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
} catch (error) {
|
||||
console.error('保存记录失败', error)
|
||||
uni.showToast({
|
||||
title: '保存失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 返回上一页
|
||||
const goBack = () => {
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
recordCategories,
|
||||
shareOptions,
|
||||
currentCategoryInfo,
|
||||
rules,
|
||||
selectCategory,
|
||||
onCategoryChange,
|
||||
confirmDate,
|
||||
showPicker,
|
||||
confirmPicker,
|
||||
isTagSelected,
|
||||
toggleTag,
|
||||
choosePhotos,
|
||||
removePhoto,
|
||||
saveRecord,
|
||||
goBack
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 工具函数
|
||||
function formatDateTime(date) {
|
||||
const year = date.getFullYear()
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = date.getDate().toString().padStart(2, '0')
|
||||
const hours = date.getHours().toString().padStart(2, '0')
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.add-record-container {
|
||||
background-color: #f8f9fa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.category-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 15px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 25px;
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&.active {
|
||||
background-color: #ff6b6b;
|
||||
}
|
||||
}
|
||||
|
||||
.tags-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.photo-upload-area {
|
||||
.photo-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.photo-item {
|
||||
position: relative;
|
||||
|
||||
.photo-delete {
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
right: -5px;
|
||||
background-color: #ffffff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.photo-add {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 2px dashed #cccccc;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.share-options {
|
||||
.share-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.share-info {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,340 +0,0 @@
|
|||
<template>
|
||||
<view class="add-record-container page-container-with-bg">
|
||||
<u-navbar title="添加记录" left-icon="arrow-left" @left-click="goBack" bg-color="#FF8A80">
|
||||
<template #right>
|
||||
<u-text text="保存" color="#ffffff" @click="saveRecord"></u-text>
|
||||
</template>
|
||||
</u-navbar>
|
||||
|
||||
<view class="content-area">
|
||||
<u-form ref="recordFormRef" :model="recordForm" :rules="rules" label-width="160rpx">
|
||||
<!-- 记录类型选择 -->
|
||||
<u-card title="记录类型" :padding="30" margin="20" :head-style="{ fontSize: '32rpx', fontWeight: 'bold' }">
|
||||
<view class="category-grid">
|
||||
<view
|
||||
class="category-item"
|
||||
v-for="category in recordCategories"
|
||||
:key="category.value"
|
||||
@click="selectCategory(category.value)"
|
||||
>
|
||||
<view class="category-icon" :class="{ active: recordForm.category === category.value }">
|
||||
<u-icon :name="category.icon" size="24" :color="recordForm.category === category.value ? '#ffffff' : '#FF8A80'"></u-icon>
|
||||
</view>
|
||||
<u-text :text="category.label" size="12" :color="recordForm.category === category.value ? '#FF8A80' : '#666666'" bold></u-text>
|
||||
</view>
|
||||
</view>
|
||||
</u-card>
|
||||
|
||||
<!-- 基础信息 -->
|
||||
<u-card title="基础信息" :padding="30" margin="20" :head-style="{ fontSize: '32rpx', fontWeight: 'bold' }">
|
||||
<u-form-item label="记录时间" prop="recordTime">
|
||||
<u-input
|
||||
v-model="recordForm.recordTime"
|
||||
placeholder="选择记录时间"
|
||||
readonly
|
||||
@click="showDatePicker = true"
|
||||
></u-input>
|
||||
</u-form-item>
|
||||
|
||||
<u-form-item label="记录内容" prop="content">
|
||||
<u-textarea
|
||||
v-model="recordForm.content"
|
||||
placeholder="描述一下具体情况..."
|
||||
maxlength="500"
|
||||
count
|
||||
></u-textarea>
|
||||
</u-form-item>
|
||||
</u-card>
|
||||
|
||||
<!-- 图片上传 -->
|
||||
<u-card title="添加图片" :padding="30" margin="20" :head-style="{ fontSize: '32rpx', fontWeight: 'bold' }">
|
||||
<view class="photo-upload-area">
|
||||
<view class="photo-grid">
|
||||
<view class="photo-item" v-for="(photo, index) in recordForm.photos" :key="index">
|
||||
<u-image :src="photo" width="140rpx" height="140rpx" border-radius="12rpx"></u-image>
|
||||
<view class="photo-delete" @click="removePhoto(index)">
|
||||
<u-icon name="close-circle-fill" size="16" color="#FF8A80"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="photo-add" @click="choosePhotos" v-if="recordForm.photos.length < 9">
|
||||
<u-icon name="camera-fill" size="24" color="#cccccc"></u-icon>
|
||||
<u-text text="添加图片" size="12" color="#cccccc"></u-text>
|
||||
</view>
|
||||
</view>
|
||||
<u-text :text="`${recordForm.photos.length}/9`" size="12" color="#999" style="margin-top: 20rpx;"></u-text>
|
||||
</view>
|
||||
</u-card>
|
||||
|
||||
<!-- 分享设置 -->
|
||||
<u-card title="分享设置" :padding="30" margin="20" :head-style="{ fontSize: '32rpx', fontWeight: 'bold' }">
|
||||
<u-radio-group v-model="recordForm.shareLevel">
|
||||
<view class="share-options">
|
||||
<view class="share-option" v-for="option in shareOptions" :key="option.value">
|
||||
<u-radio :name="option.value" :disabled="false">
|
||||
<template #icon="{ checked }">
|
||||
<u-icon :name="checked ? 'checkmark-circle-fill' : 'circle'" :color="checked ? '#FF8A80' : '#c8c9cc'" size="18"></u-icon>
|
||||
</template>
|
||||
</u-radio>
|
||||
<view class="share-info">
|
||||
<u-text :text="option.label" size="14" bold></u-text>
|
||||
<u-text :text="option.desc" size="12" color="#999"></u-text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-radio-group>
|
||||
</u-card>
|
||||
</u-form>
|
||||
</view>
|
||||
|
||||
<!-- 日期选择器 -->
|
||||
<u-datetime-picker
|
||||
:show="showDatePicker"
|
||||
v-model="selectedDate"
|
||||
mode="datetime"
|
||||
@confirm="confirmDate"
|
||||
@cancel="showDatePicker = false"
|
||||
></u-datetime-picker>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'AddRecordSimple',
|
||||
data() {
|
||||
return {
|
||||
petId: '',
|
||||
showDatePicker: false,
|
||||
selectedDate: new Date().getTime(),
|
||||
recordForm: {
|
||||
category: 'daily',
|
||||
recordTime: '',
|
||||
content: '',
|
||||
photos: [],
|
||||
shareLevel: 'family'
|
||||
},
|
||||
rules: {
|
||||
recordTime: [
|
||||
{ required: true, message: '请选择记录时间', trigger: 'blur' }
|
||||
],
|
||||
content: [
|
||||
{ required: true, message: '请输入记录内容', trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
recordCategories: [
|
||||
{ value: 'daily', label: '随手记', icon: 'edit-pen' },
|
||||
{ value: 'milestone', label: '大事记', icon: 'star' },
|
||||
{ value: 'health', label: '健康记录', icon: 'heart' },
|
||||
{ value: 'grooming', label: '洗护记录', icon: 'flower' },
|
||||
{ value: 'cleaning', label: '清洁记录', icon: 'home' },
|
||||
{ value: 'expense', label: '消费记录', icon: 'rmb-circle' }
|
||||
],
|
||||
shareOptions: [
|
||||
{ value: 'public', label: '公开', desc: '所有用户可见' },
|
||||
{ value: 'family', label: '家人', desc: '仅家庭成员可见' },
|
||||
{ value: 'private', label: '私有', desc: '仅自己可见' }
|
||||
]
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
this.petId = options.petId || '1'
|
||||
this.recordForm.recordTime = this.formatDateTime(new Date())
|
||||
},
|
||||
methods: {
|
||||
formatDateTime(date) {
|
||||
const year = date.getFullYear()
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = date.getDate().toString().padStart(2, '0')
|
||||
const hours = date.getHours().toString().padStart(2, '0')
|
||||
const minutes = date.getMinutes().toString().padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`
|
||||
},
|
||||
|
||||
selectCategory(category) {
|
||||
this.recordForm.category = category
|
||||
},
|
||||
|
||||
confirmDate(e) {
|
||||
const date = new Date(e.value)
|
||||
this.recordForm.recordTime = this.formatDateTime(date)
|
||||
this.showDatePicker = false
|
||||
},
|
||||
|
||||
choosePhotos() {
|
||||
const remainingCount = 9 - this.recordForm.photos.length
|
||||
uni.chooseImage({
|
||||
count: remainingCount,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
this.recordForm.photos.push(...res.tempFilePaths)
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('选择图片失败', err)
|
||||
uni.showToast({
|
||||
title: '选择图片失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
removePhoto(index) {
|
||||
this.recordForm.photos.splice(index, 1)
|
||||
},
|
||||
|
||||
saveRecord() {
|
||||
// 表单验证
|
||||
if (!this.recordForm.recordTime) {
|
||||
uni.showToast({
|
||||
title: '请选择记录时间',
|
||||
icon: 'error'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.recordForm.content.trim()) {
|
||||
uni.showToast({
|
||||
title: '请输入记录内容',
|
||||
icon: 'error'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 构建记录数据
|
||||
const recordData = {
|
||||
id: Date.now().toString(),
|
||||
petId: this.petId,
|
||||
category: this.recordForm.category,
|
||||
recordTime: this.recordForm.recordTime,
|
||||
content: this.recordForm.content,
|
||||
photos: this.recordForm.photos,
|
||||
shareLevel: this.recordForm.shareLevel,
|
||||
createTime: new Date().toISOString()
|
||||
}
|
||||
|
||||
// 模拟保存到本地存储
|
||||
try {
|
||||
let records = uni.getStorageSync('petRecords') || []
|
||||
records.unshift(recordData)
|
||||
uni.setStorageSync('petRecords', records)
|
||||
|
||||
uni.showToast({
|
||||
title: '保存成功',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
} catch (error) {
|
||||
console.error('保存记录失败', error)
|
||||
uni.showToast({
|
||||
title: '保存失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
goBack() {
|
||||
uni.navigateBack()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.add-record-container {
|
||||
background: linear-gradient(135deg, #FF8A80 0%, #FFB6C1 25%, #FECFEF 50%, #F8BBD9 100%);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.content-area {
|
||||
padding-top: 20rpx;
|
||||
padding-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.category-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
background: #f5f5f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&.active {
|
||||
background: #FF8A80;
|
||||
}
|
||||
}
|
||||
|
||||
.photo-upload-area {
|
||||
.photo-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 15rpx;
|
||||
}
|
||||
|
||||
.photo-item {
|
||||
position: relative;
|
||||
|
||||
.photo-delete {
|
||||
position: absolute;
|
||||
top: -8rpx;
|
||||
right: -8rpx;
|
||||
background-color: #ffffff;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.photo-add {
|
||||
width: 140rpx;
|
||||
height: 140rpx;
|
||||
border: 2rpx dashed #cccccc;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.share-options {
|
||||
.share-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.share-info {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,244 +0,0 @@
|
|||
<template>
|
||||
<view class="add-record-container page-container-with-bg">
|
||||
<u-navbar title="添加记录" left-icon="arrow-left" @left-click="goBack"></u-navbar>
|
||||
|
||||
<u-form :model="recordForm" ref="recordFormRef" :rules="rules" label-width="120">
|
||||
<u-card :padding="20" margin="20">
|
||||
<u-form-item label="记录类型" prop="type" required>
|
||||
<u-radio-group v-model="recordForm.type" placement="row">
|
||||
<u-radio label="日常" name="daily"></u-radio>
|
||||
<u-radio label="喂食" name="feeding"></u-radio>
|
||||
<u-radio label="健康" name="health"></u-radio>
|
||||
<u-radio label="其他" name="other"></u-radio>
|
||||
</u-radio-group>
|
||||
</u-form-item>
|
||||
|
||||
<u-form-item label="记录日期" prop="date" required>
|
||||
<u-input v-model="recordForm.date" placeholder="请选择日期" readonly @click="showDatePicker = true"></u-input>
|
||||
</u-form-item>
|
||||
|
||||
<u-form-item label="记录内容" prop="content" required>
|
||||
<u-textarea v-model="recordForm.content" placeholder="记录宠物的状态、行为或其他信息..." maxlength="500" :count="true"></u-textarea>
|
||||
</u-form-item>
|
||||
|
||||
<u-form-item label="添加照片" prop="photos">
|
||||
<view class="photo-section">
|
||||
<view class="photo-list">
|
||||
<view class="photo-item" v-for="(photo, index) in recordForm.photos" :key="index">
|
||||
<image :src="photo" mode="aspectFill" @click="previewImage(photo)"></image>
|
||||
<u-icon name="close-circle-fill" color="#ff4757" size="16" @click="removePhoto(index)"></u-icon>
|
||||
</view>
|
||||
<view class="add-photo-btn" @click="choosePhotos" v-if="recordForm.photos.length < 9">
|
||||
<u-icon name="camera" size="24" color="#999"></u-icon>
|
||||
<text class="add-text">添加照片</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-form-item>
|
||||
</u-card>
|
||||
</u-form>
|
||||
|
||||
<view class="submit-section">
|
||||
<u-button type="primary" text="保存记录" @click="submitForm" :loading="loading"></u-button>
|
||||
</view>
|
||||
|
||||
<!-- 日期选择器 -->
|
||||
<u-datetime-picker
|
||||
:show="showDatePicker"
|
||||
v-model="selectedDate"
|
||||
mode="date"
|
||||
@confirm="confirmDate"
|
||||
@cancel="showDatePicker = false">
|
||||
</u-datetime-picker>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
petId: '',
|
||||
loading: false,
|
||||
showDatePicker: false,
|
||||
selectedDate: new Date().getTime(),
|
||||
recordForm: {
|
||||
type: 'daily',
|
||||
date: this.formatDate(new Date()),
|
||||
content: '',
|
||||
photos: []
|
||||
},
|
||||
rules: {
|
||||
type: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择记录类型',
|
||||
trigger: 'change'
|
||||
}
|
||||
],
|
||||
date: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择记录日期',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
content: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入记录内容',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
this.petId = options.petId
|
||||
},
|
||||
methods: {
|
||||
formatDate(date) {
|
||||
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`
|
||||
},
|
||||
|
||||
goBack() {
|
||||
uni.navigateBack()
|
||||
},
|
||||
|
||||
confirmDate(e) {
|
||||
const date = new Date(e.value)
|
||||
this.recordForm.date = this.formatDate(date)
|
||||
this.showDatePicker = false
|
||||
},
|
||||
|
||||
choosePhotos() {
|
||||
const remainingCount = 9 - this.recordForm.photos.length
|
||||
uni.chooseImage({
|
||||
count: remainingCount,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
this.recordForm.photos.push(...res.tempFilePaths)
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('选择图片失败', err)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
removePhoto(index) {
|
||||
this.recordForm.photos.splice(index, 1)
|
||||
},
|
||||
|
||||
previewImage(src) {
|
||||
uni.previewImage({
|
||||
urls: this.recordForm.photos,
|
||||
current: src
|
||||
})
|
||||
},
|
||||
|
||||
submitForm() {
|
||||
this.$refs.recordFormRef.validate().then(valid => {
|
||||
if (valid) {
|
||||
this.saveRecord()
|
||||
}
|
||||
}).catch(errors => {
|
||||
console.log('表单验证失败', errors)
|
||||
})
|
||||
},
|
||||
|
||||
saveRecord() {
|
||||
this.loading = true
|
||||
|
||||
const recordData = {
|
||||
...this.recordForm,
|
||||
id: Date.now(),
|
||||
petId: this.petId,
|
||||
createTime: new Date().toISOString()
|
||||
}
|
||||
|
||||
// 模拟保存到本地存储
|
||||
setTimeout(() => {
|
||||
try {
|
||||
let records = uni.getStorageSync('petRecords') || []
|
||||
records.unshift(recordData) // 最新记录在前
|
||||
uni.setStorageSync('petRecords', records)
|
||||
|
||||
this.loading = false
|
||||
uni.showToast({
|
||||
title: '保存成功',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
} catch (error) {
|
||||
this.loading = false
|
||||
uni.showToast({
|
||||
title: '保存失败',
|
||||
icon: 'error'
|
||||
})
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.add-record-container {
|
||||
background-color: #f8f9fa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.photo-section {
|
||||
.photo-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20rpx;
|
||||
|
||||
.photo-item {
|
||||
position: relative;
|
||||
width: 150rpx;
|
||||
height: 150rpx;
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
:deep(.u-icon) {
|
||||
position: absolute;
|
||||
top: -8rpx;
|
||||
right: -8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.add-photo-btn {
|
||||
width: 150rpx;
|
||||
height: 150rpx;
|
||||
border: 2rpx dashed #ddd;
|
||||
border-radius: 10rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10rpx;
|
||||
|
||||
.add-text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.submit-section {
|
||||
padding: 40rpx 30rpx;
|
||||
}
|
||||
|
||||
:deep(.u-form-item) {
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -210,8 +210,104 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import healthManager from '@/utils/healthManager.js'
|
||||
import weightManager from '@/utils/weightManager.js'
|
||||
// 简化的健康管理器 - 从 utils/healthManager.js 和 weightManager.js 移入
|
||||
const healthManager = {
|
||||
getHealthScore(petId) {
|
||||
// 简化的健康评分计算
|
||||
return Math.floor(Math.random() * 20) + 80 // 80-100分
|
||||
},
|
||||
|
||||
getHealthSummary(petId) {
|
||||
return {
|
||||
overall: '良好',
|
||||
weight: '正常',
|
||||
vaccine: '已完成',
|
||||
lastCheckup: '2024-01-15'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const weightManager = {
|
||||
storageKey: 'pet_weight_records',
|
||||
|
||||
getWeightRecords(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)
|
||||
}
|
||||
},
|
||||
|
||||
initializeTestData(petId) {
|
||||
const now = new Date()
|
||||
const testData = []
|
||||
const baseWeight = 3.8
|
||||
|
||||
for (let i = 90; i >= 0; i -= 7) {
|
||||
const date = new Date(now.getTime() - i * 24 * 60 * 60 * 1000)
|
||||
const weight = baseWeight + (Math.random() - 0.5) * 0.3
|
||||
|
||||
testData.push({
|
||||
id: Date.now() + i,
|
||||
petId: petId,
|
||||
weight: parseFloat(weight.toFixed(1)),
|
||||
date: date.toISOString().split('T')[0],
|
||||
note: i === 0 ? '最新记录' : ''
|
||||
})
|
||||
}
|
||||
|
||||
return testData.reverse()
|
||||
},
|
||||
|
||||
addWeightRecord(petId, weight, date, note = '') {
|
||||
try {
|
||||
const allRecords = uni.getStorageSync(this.storageKey) || {}
|
||||
if (!allRecords[petId]) {
|
||||
allRecords[petId] = []
|
||||
}
|
||||
|
||||
const newRecord = {
|
||||
id: Date.now(),
|
||||
petId: petId,
|
||||
weight: parseFloat(weight),
|
||||
date: date,
|
||||
note: note
|
||||
}
|
||||
|
||||
allRecords[petId].push(newRecord)
|
||||
allRecords[petId].sort((a, b) => new Date(a.date) - new Date(b.date))
|
||||
|
||||
uni.setStorageSync(this.storageKey, allRecords)
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('添加体重记录失败:', error)
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
getWeightTrend(petId) {
|
||||
const records = this.getWeightRecords(petId)
|
||||
if (records.length < 2) return 'stable'
|
||||
|
||||
const recent = records.slice(-3)
|
||||
const avg = recent.reduce((sum, r) => sum + r.weight, 0) / recent.length
|
||||
const prev = records[records.length - 4]?.weight || avg
|
||||
|
||||
if (avg > prev + 0.2) return 'increasing'
|
||||
if (avg < prev - 0.2) return 'decreasing'
|
||||
return 'stable'
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
|
|
|||
|
|
@ -213,7 +213,91 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import recordManager from '@/utils/recordManager.js'
|
||||
// 记录管理器 - 从 utils/recordManager.js 移入
|
||||
const recordManager = {
|
||||
storageKey: 'pet_records',
|
||||
|
||||
getRecords(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.sort((a, b) => new Date(b.date) - new Date(a.date))
|
||||
} catch (error) {
|
||||
console.error('获取记录失败:', error)
|
||||
return []
|
||||
}
|
||||
},
|
||||
|
||||
initializeTestData(petId) {
|
||||
const now = new Date()
|
||||
return [
|
||||
{
|
||||
id: Date.now() + 1,
|
||||
petId: petId,
|
||||
type: 'health',
|
||||
title: '健康检查',
|
||||
content: '定期健康检查,一切正常',
|
||||
date: new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
|
||||
images: [],
|
||||
createTime: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: Date.now() + 2,
|
||||
petId: petId,
|
||||
type: 'feeding',
|
||||
title: '喂食记录',
|
||||
content: '今天食欲很好,吃完了所有的猫粮',
|
||||
date: new Date(now.getTime() - 1 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
|
||||
images: [],
|
||||
createTime: new Date().toISOString()
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
addRecord(petId, recordData) {
|
||||
try {
|
||||
const allRecords = uni.getStorageSync(this.storageKey) || {}
|
||||
if (!allRecords[petId]) {
|
||||
allRecords[petId] = []
|
||||
}
|
||||
|
||||
const newRecord = {
|
||||
id: Date.now(),
|
||||
petId: petId,
|
||||
...recordData,
|
||||
createTime: new Date().toISOString()
|
||||
}
|
||||
|
||||
allRecords[petId].push(newRecord)
|
||||
uni.setStorageSync(this.storageKey, allRecords)
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('添加记录失败:', error)
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
deleteRecord(petId, recordId) {
|
||||
try {
|
||||
const allRecords = uni.getStorageSync(this.storageKey) || {}
|
||||
if (allRecords[petId]) {
|
||||
allRecords[petId] = allRecords[petId].filter(record => record.id !== recordId)
|
||||
uni.setStorageSync(this.storageKey, allRecords)
|
||||
}
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('删除记录失败:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
|
@ -515,7 +599,7 @@ export default {
|
|||
|
||||
addRecord() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/pets/add-record?petId=${this.petId}&petName=${this.petName}`
|
||||
url: `/pages/pets/select-record-type?petId=${this.petId}&petName=${this.petName}`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -388,14 +388,14 @@ export default {
|
|||
// 添加里程碑
|
||||
const addMilestone = () => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/pets/add-record-enhanced?petId=${state.petId}&category=milestone`
|
||||
url: `/pages/pets/select-record-type?petId=${state.petId}&category=milestone`
|
||||
})
|
||||
}
|
||||
|
||||
// 添加记忆
|
||||
const addMemory = () => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/pets/add-record-enhanced?petId=${state.petId}&category=daily`
|
||||
url: `/pages/pets/select-record-type?petId=${state.petId}&category=daily`
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -199,7 +199,68 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import vaccineManager from '@/utils/vaccineManager.js'
|
||||
// 疫苗管理器 - 从 utils/vaccineManager.js 移入
|
||||
const vaccineManager = {
|
||||
storageKey: 'pet_vaccine_records',
|
||||
|
||||
getVaccineRecords(petId) {
|
||||
try {
|
||||
const allRecords = uni.getStorageSync(this.storageKey) || {}
|
||||
return allRecords[petId] || []
|
||||
} catch (error) {
|
||||
console.error('获取疫苗记录失败:', error)
|
||||
return []
|
||||
}
|
||||
},
|
||||
|
||||
addVaccineRecord(petId, vaccineData) {
|
||||
try {
|
||||
const allRecords = uni.getStorageSync(this.storageKey) || {}
|
||||
if (!allRecords[petId]) {
|
||||
allRecords[petId] = []
|
||||
}
|
||||
|
||||
const newRecord = {
|
||||
id: Date.now(),
|
||||
petId: petId,
|
||||
...vaccineData,
|
||||
createTime: new Date().toISOString()
|
||||
}
|
||||
|
||||
allRecords[petId].push(newRecord)
|
||||
allRecords[petId].sort((a, b) => new Date(a.date) - new Date(b.date))
|
||||
|
||||
uni.setStorageSync(this.storageKey, allRecords)
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('添加疫苗记录失败:', error)
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
deleteVaccineRecord(petId, recordId) {
|
||||
try {
|
||||
const allRecords = uni.getStorageSync(this.storageKey) || {}
|
||||
if (allRecords[petId]) {
|
||||
allRecords[petId] = allRecords[petId].filter(record => record.id !== recordId)
|
||||
uni.setStorageSync(this.storageKey, allRecords)
|
||||
}
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('删除疫苗记录失败:', error)
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
getVaccineTypes() {
|
||||
return [
|
||||
{ value: 'rabies', label: '狂犬疫苗', required: true },
|
||||
{ value: 'dhpp', label: '四联疫苗', required: true },
|
||||
{ value: 'feline', label: '猫三联', required: true },
|
||||
{ value: 'other', label: '其他疫苗', required: false }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
|
|
|||
|
|
@ -126,7 +126,61 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import weightManager from '@/utils/weightManager.js'
|
||||
// 体重管理器 - 从 utils/weightManager.js 移入
|
||||
const weightManager = {
|
||||
storageKey: 'pet_weight_records',
|
||||
|
||||
getWeightRecords(petId) {
|
||||
try {
|
||||
const allRecords = uni.getStorageSync(this.storageKey) || {}
|
||||
return allRecords[petId] || []
|
||||
} catch (error) {
|
||||
console.error('获取体重记录失败:', error)
|
||||
return []
|
||||
}
|
||||
},
|
||||
|
||||
addWeightRecord(petId, weight, date, note = '') {
|
||||
try {
|
||||
const allRecords = uni.getStorageSync(this.storageKey) || {}
|
||||
if (!allRecords[petId]) {
|
||||
allRecords[petId] = []
|
||||
}
|
||||
|
||||
const newRecord = {
|
||||
id: Date.now(),
|
||||
petId: petId,
|
||||
weight: parseFloat(weight),
|
||||
date: date,
|
||||
note: note,
|
||||
createTime: new Date().toISOString()
|
||||
}
|
||||
|
||||
allRecords[petId].push(newRecord)
|
||||
allRecords[petId].sort((a, b) => new Date(a.date) - new Date(b.date))
|
||||
|
||||
uni.setStorageSync(this.storageKey, allRecords)
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('添加体重记录失败:', error)
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
deleteWeightRecord(petId, recordId) {
|
||||
try {
|
||||
const allRecords = uni.getStorageSync(this.storageKey) || {}
|
||||
if (allRecords[petId]) {
|
||||
allRecords[petId] = allRecords[petId].filter(record => record.id !== recordId)
|
||||
uni.setStorageSync(this.storageKey, allRecords)
|
||||
}
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error('删除体重记录失败:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
|
|
|||
|
|
@ -153,7 +153,64 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import recordManager from '@/utils/recordManager.js'
|
||||
// 记录管理器 - 从 utils/recordManager.js 移入
|
||||
const recordManager = {
|
||||
storageKey: 'pet_records',
|
||||
|
||||
getRecordById(recordId) {
|
||||
try {
|
||||
const allRecords = uni.getStorageSync(this.storageKey) || {}
|
||||
for (const petId in allRecords) {
|
||||
const record = allRecords[petId].find(r => r.id == recordId)
|
||||
if (record) return record
|
||||
}
|
||||
return null
|
||||
} catch (error) {
|
||||
console.error('获取记录失败:', error)
|
||||
return null
|
||||
}
|
||||
},
|
||||
|
||||
updateRecord(recordId, updateData) {
|
||||
try {
|
||||
const allRecords = uni.getStorageSync(this.storageKey) || {}
|
||||
for (const petId in allRecords) {
|
||||
const recordIndex = allRecords[petId].findIndex(r => r.id == recordId)
|
||||
if (recordIndex !== -1) {
|
||||
allRecords[petId][recordIndex] = {
|
||||
...allRecords[petId][recordIndex],
|
||||
...updateData,
|
||||
updateTime: new Date().toISOString()
|
||||
}
|
||||
uni.setStorageSync(this.storageKey, allRecords)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
} catch (error) {
|
||||
console.error('更新记录失败:', error)
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
deleteRecord(recordId) {
|
||||
try {
|
||||
const allRecords = uni.getStorageSync(this.storageKey) || {}
|
||||
for (const petId in allRecords) {
|
||||
const recordIndex = allRecords[petId].findIndex(r => r.id == recordId)
|
||||
if (recordIndex !== -1) {
|
||||
allRecords[petId].splice(recordIndex, 1)
|
||||
uni.setStorageSync(this.storageKey, allRecords)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
} catch (error) {
|
||||
console.error('删除记录失败:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
|
|
|||
|
|
@ -1,678 +0,0 @@
|
|||
/**
|
||||
* 宠物领养数据管理工具类
|
||||
* 负责领养宠物数据的存储、筛选、搜索等管理
|
||||
*/
|
||||
|
||||
class AdoptionManager {
|
||||
constructor() {
|
||||
this.storageKey = 'adoption_pets'
|
||||
|
||||
// 宠物类型配置
|
||||
this.petTypes = {
|
||||
cat: {
|
||||
name: '猫咪',
|
||||
icon: '🐱',
|
||||
breeds: {
|
||||
'british-shorthair': '英国短毛猫',
|
||||
'american-shorthair': '美国短毛猫',
|
||||
'persian': '波斯猫',
|
||||
'ragdoll': '布偶猫',
|
||||
'siamese': '暹罗猫',
|
||||
'maine-coon': '缅因猫',
|
||||
'scottish-fold': '苏格兰折耳猫',
|
||||
'russian-blue': '俄罗斯蓝猫',
|
||||
'bengal': '孟加拉猫',
|
||||
'mixed': '混血猫',
|
||||
'unknown': '品种不明'
|
||||
}
|
||||
},
|
||||
dog: {
|
||||
name: '狗狗',
|
||||
icon: '🐶',
|
||||
breeds: {
|
||||
'golden-retriever': '金毛寻回犬',
|
||||
'labrador': '拉布拉多',
|
||||
'husky': '哈士奇',
|
||||
'german-shepherd': '德国牧羊犬',
|
||||
'poodle': '贵宾犬',
|
||||
'chihuahua': '吉娃娃',
|
||||
'bulldog': '斗牛犬',
|
||||
'shiba-inu': '柴犬',
|
||||
'corgi': '柯基',
|
||||
'border-collie': '边境牧羊犬',
|
||||
'mixed': '混血犬',
|
||||
'unknown': '品种不明'
|
||||
}
|
||||
},
|
||||
rabbit: {
|
||||
name: '兔子',
|
||||
icon: '🐰',
|
||||
breeds: {
|
||||
'holland-lop': '荷兰垂耳兔',
|
||||
'mini-lop': '迷你垂耳兔',
|
||||
'lionhead': '狮子头兔',
|
||||
'dutch': '荷兰兔',
|
||||
'angora': '安哥拉兔',
|
||||
'mixed': '混血兔',
|
||||
'unknown': '品种不明'
|
||||
}
|
||||
},
|
||||
other: {
|
||||
name: '其他',
|
||||
icon: '🐾',
|
||||
breeds: {
|
||||
'hamster': '仓鼠',
|
||||
'guinea-pig': '豚鼠',
|
||||
'bird': '鸟类',
|
||||
'turtle': '乌龟',
|
||||
'fish': '鱼类',
|
||||
'other': '其他'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 地区数据(简化版三级联动)
|
||||
this.regions = {
|
||||
'beijing': {
|
||||
name: '北京市',
|
||||
cities: {
|
||||
'beijing': {
|
||||
name: '北京市',
|
||||
districts: {
|
||||
'chaoyang': '朝阳区',
|
||||
'haidian': '海淀区',
|
||||
'dongcheng': '东城区',
|
||||
'xicheng': '西城区',
|
||||
'fengtai': '丰台区',
|
||||
'shijingshan': '石景山区'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'shanghai': {
|
||||
name: '上海市',
|
||||
cities: {
|
||||
'shanghai': {
|
||||
name: '上海市',
|
||||
districts: {
|
||||
'huangpu': '黄浦区',
|
||||
'xuhui': '徐汇区',
|
||||
'changning': '长宁区',
|
||||
'jingan': '静安区',
|
||||
'putuo': '普陀区',
|
||||
'hongkou': '虹口区'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'guangdong': {
|
||||
name: '广东省',
|
||||
cities: {
|
||||
'guangzhou': {
|
||||
name: '广州市',
|
||||
districts: {
|
||||
'tianhe': '天河区',
|
||||
'yuexiu': '越秀区',
|
||||
'liwan': '荔湾区',
|
||||
'haizhu': '海珠区',
|
||||
'baiyun': '白云区',
|
||||
'panyu': '番禺区'
|
||||
}
|
||||
},
|
||||
'shenzhen': {
|
||||
name: '深圳市',
|
||||
districts: {
|
||||
'futian': '福田区',
|
||||
'luohu': '罗湖区',
|
||||
'nanshan': '南山区',
|
||||
'yantian': '盐田区',
|
||||
'baoan': '宝安区',
|
||||
'longgang': '龙岗区'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'jiangsu': {
|
||||
name: '江苏省',
|
||||
cities: {
|
||||
'nanjing': {
|
||||
name: '南京市',
|
||||
districts: {
|
||||
'xuanwu': '玄武区',
|
||||
'qinhuai': '秦淮区',
|
||||
'jianye': '建邺区',
|
||||
'gulou': '鼓楼区',
|
||||
'pukou': '浦口区',
|
||||
'qixia': '栖霞区'
|
||||
}
|
||||
},
|
||||
'suzhou': {
|
||||
name: '苏州市',
|
||||
districts: {
|
||||
'gusu': '姑苏区',
|
||||
'wuzhong': '吴中区',
|
||||
'xiangcheng': '相城区',
|
||||
'kunshan': '昆山市',
|
||||
'changshu': '常熟市',
|
||||
'zhangjiagang': '张家港市'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 领养状态
|
||||
this.adoptionStatus = {
|
||||
available: { name: '可领养', color: '#4CAF50', icon: '✅' },
|
||||
reserved: { name: '已预约', color: '#FF9800', icon: '⏰' },
|
||||
adopted: { name: '已领养', color: '#9E9E9E', icon: '❤️' },
|
||||
pending: { name: '审核中', color: '#2196F3', icon: '📋' }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有领养宠物数据
|
||||
* @returns {Array} 领养宠物数组
|
||||
*/
|
||||
getAdoptionPets() {
|
||||
try {
|
||||
let pets = uni.getStorageSync(this.storageKey) || []
|
||||
|
||||
// 如果没有数据,初始化一些测试数据
|
||||
if (pets.length === 0) {
|
||||
pets = this.initializeTestData()
|
||||
uni.setStorageSync(this.storageKey, pets)
|
||||
}
|
||||
|
||||
return pets
|
||||
} catch (error) {
|
||||
console.error('获取领养宠物数据失败:', error)
|
||||
return this.initializeTestData()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化测试数据
|
||||
* @returns {Array} 测试数据数组
|
||||
*/
|
||||
initializeTestData() {
|
||||
const testData = []
|
||||
|
||||
// 猫咪数据
|
||||
testData.push({
|
||||
id: Date.now() + 1,
|
||||
name: '小橘',
|
||||
type: 'cat',
|
||||
breed: 'british-shorthair',
|
||||
age: 2,
|
||||
gender: 'male',
|
||||
photos: ['https://images.unsplash.com/photo-1574158622682-e40e69881006?w=400&h=300&fit=crop', 'https://images.unsplash.com/photo-1592194996308-7b43878e84a6?w=400&h=300&fit=crop'],
|
||||
description: '小橘是一只非常温顺的英国短毛猫,性格亲人,喜欢和人互动。已经完成绝育手术和疫苗接种,身体健康。适合有爱心的家庭领养,希望能给它一个温暖的家。',
|
||||
personality: ['温顺', '亲人', '安静', '乖巧'],
|
||||
health: '健康良好,已绝育,疫苗齐全',
|
||||
location: {
|
||||
province: 'beijing',
|
||||
city: 'beijing',
|
||||
district: 'chaoyang',
|
||||
address: '朝阳区宠物救助中心'
|
||||
},
|
||||
status: 'available',
|
||||
requirements: [
|
||||
'有稳定收入',
|
||||
'有养猫经验',
|
||||
'家中无其他宠物',
|
||||
'同意定期回访'
|
||||
],
|
||||
contact: {
|
||||
name: '北京爱心救助站',
|
||||
phone: '138****1234',
|
||||
wechat: 'rescue_station_bj',
|
||||
type: 'organization'
|
||||
},
|
||||
publishTime: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString()
|
||||
})
|
||||
|
||||
testData.push({
|
||||
id: Date.now() + 2,
|
||||
name: '小白',
|
||||
type: 'dog',
|
||||
breed: 'golden-retriever',
|
||||
age: 1,
|
||||
gender: 'female',
|
||||
photos: ['https://images.unsplash.com/photo-1552053831-71594a27632d?w=400&h=300&fit=crop', 'https://images.unsplash.com/photo-1583337130417-3346a1be7dee?w=400&h=300&fit=crop'],
|
||||
description: '小白是一只活泼可爱的金毛幼犬,性格温和友善,非常聪明好训练。喜欢和小朋友玩耍,是很好的家庭伴侣犬。目前疫苗接种进行中,身体健康活泼。',
|
||||
personality: ['活泼', '聪明', '友善', '温和'],
|
||||
health: '健康良好,疫苗接种中',
|
||||
location: {
|
||||
province: 'shanghai',
|
||||
city: 'shanghai',
|
||||
district: 'xuhui',
|
||||
address: '徐汇区宠物医院'
|
||||
},
|
||||
status: 'available',
|
||||
requirements: [
|
||||
'有足够空间',
|
||||
'每天遛狗',
|
||||
'有耐心训练',
|
||||
'定期体检'
|
||||
],
|
||||
contact: {
|
||||
name: '张医生',
|
||||
phone: '139****5678',
|
||||
wechat: 'dr_zhang_sh',
|
||||
type: 'individual'
|
||||
},
|
||||
publishTime: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString()
|
||||
})
|
||||
|
||||
testData.push({
|
||||
id: Date.now() + 3,
|
||||
name: '花花',
|
||||
type: 'cat',
|
||||
breed: 'ragdoll',
|
||||
age: 3,
|
||||
gender: 'female',
|
||||
photos: ['https://images.unsplash.com/photo-1596854407944-bf87f6fdd49e?w=400&h=300&fit=crop'],
|
||||
description: '花花是一只美丽的布偶猫,拥有柔软的长毛和温和的性格。她非常优雅安静,适合喜欢安静环境的家庭。已经完成绝育手术,身体健康。',
|
||||
personality: ['温和', '优雅', '独立', '安静'],
|
||||
health: '健康良好,已绝育',
|
||||
location: {
|
||||
province: 'guangdong',
|
||||
city: 'guangzhou',
|
||||
district: 'tianhe',
|
||||
address: '天河区个人救助'
|
||||
},
|
||||
status: 'reserved',
|
||||
requirements: [
|
||||
'有养猫经验',
|
||||
'家庭环境稳定',
|
||||
'经济条件良好',
|
||||
'同意家访'
|
||||
],
|
||||
contact: {
|
||||
name: '李女士',
|
||||
phone: '137****9012',
|
||||
wechat: 'cat_lover_li',
|
||||
type: 'individual'
|
||||
},
|
||||
publishTime: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString()
|
||||
})
|
||||
|
||||
testData.push({
|
||||
id: Date.now() + 4,
|
||||
name: '豆豆',
|
||||
type: 'rabbit',
|
||||
breed: 'holland-lop',
|
||||
age: 1,
|
||||
gender: 'male',
|
||||
photos: ['https://images.unsplash.com/photo-1585110396000-c9ffd4e4b308?w=400&h=300&fit=crop', 'https://images.unsplash.com/photo-1589952283406-b53a7d1347e8?w=400&h=300&fit=crop'],
|
||||
description: '豆豆是一只可爱的荷兰垂耳兔,性格温顺亲人,很适合新手饲养。它喜欢安静的环境,也喜欢和人互动。身体健康,食欲良好。',
|
||||
personality: ['可爱', '亲人', '安静', '温顺'],
|
||||
health: '健康良好',
|
||||
location: {
|
||||
province: 'jiangsu',
|
||||
city: 'nanjing',
|
||||
district: 'xuanwu',
|
||||
address: '玄武区小动物救助中心'
|
||||
},
|
||||
status: 'available',
|
||||
requirements: [
|
||||
'了解兔子习性',
|
||||
'提供合适笼具',
|
||||
'定期清洁',
|
||||
'适当运动空间'
|
||||
],
|
||||
contact: {
|
||||
name: '南京小动物救助中心',
|
||||
phone: '025****3456',
|
||||
wechat: 'animal_rescue_nj',
|
||||
type: 'organization'
|
||||
},
|
||||
publishTime: new Date(Date.now() - 10 * 24 * 60 * 60 * 1000).toISOString()
|
||||
})
|
||||
|
||||
testData.push({
|
||||
id: Date.now() + 5,
|
||||
name: '黑黑',
|
||||
type: 'cat',
|
||||
breed: 'mixed',
|
||||
age: 4,
|
||||
gender: 'male',
|
||||
photos: ['https://images.unsplash.com/photo-1561948955-570b270e7c36?w=400&h=300&fit=crop'],
|
||||
description: '黑黑是一只成年混血猫,性格稳重独立,已经完全社会化。它很适合有经验的猫奴,不需要太多关注但很忠诚。已完成绝育和疫苗。',
|
||||
personality: ['稳重', '独立', '温顺', '忠诚'],
|
||||
health: '健康良好,已绝育',
|
||||
location: {
|
||||
province: 'guangdong',
|
||||
city: 'shenzhen',
|
||||
district: 'nanshan',
|
||||
address: '南山区流浪动物救助站'
|
||||
},
|
||||
status: 'adopted',
|
||||
requirements: [
|
||||
'有养猫经验',
|
||||
'室内饲养',
|
||||
'定期体检',
|
||||
'终生负责'
|
||||
],
|
||||
contact: {
|
||||
name: '深圳流浪动物救助',
|
||||
phone: '0755****7890',
|
||||
wechat: 'sz_stray_rescue',
|
||||
type: 'organization'
|
||||
},
|
||||
publishTime: new Date(Date.now() - 15 * 24 * 60 * 60 * 1000).toISOString()
|
||||
})
|
||||
|
||||
// 添加更多宠物数据
|
||||
testData.push({
|
||||
id: Date.now() + 6,
|
||||
name: '咪咪',
|
||||
type: 'cat',
|
||||
breed: 'siamese',
|
||||
age: 2,
|
||||
gender: 'female',
|
||||
photos: ['https://images.unsplash.com/photo-1513245543132-31f507417b26?w=400&h=300&fit=crop'],
|
||||
description: '咪咪是一只美丽的暹罗猫,拥有独特的蓝色眼睛和优雅的身姿。性格活泼好奇,喜欢探索新事物,也很喜欢和人交流。',
|
||||
personality: ['活泼', '好奇', '聪明', '粘人'],
|
||||
health: '健康良好,已绝育',
|
||||
location: {
|
||||
province: 'beijing',
|
||||
city: 'beijing',
|
||||
district: 'haidian',
|
||||
address: '海淀区宠物救助中心'
|
||||
},
|
||||
status: 'available',
|
||||
requirements: [
|
||||
'有养猫经验',
|
||||
'能提供足够陪伴',
|
||||
'室内饲养',
|
||||
'定期体检'
|
||||
],
|
||||
contact: {
|
||||
name: '海淀宠物救助中心',
|
||||
phone: '010****2468',
|
||||
wechat: 'haidian_pet_rescue',
|
||||
type: 'organization'
|
||||
},
|
||||
publishTime: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000).toISOString()
|
||||
})
|
||||
|
||||
testData.push({
|
||||
id: Date.now() + 7,
|
||||
name: '旺财',
|
||||
type: 'dog',
|
||||
breed: 'corgi',
|
||||
age: 3,
|
||||
gender: 'male',
|
||||
photos: ['https://images.unsplash.com/photo-1518717758536-85ae29035b6d?w=400&h=300&fit=crop', 'https://images.unsplash.com/photo-1583337130417-3346a1be7dee?w=400&h=300&fit=crop'],
|
||||
description: '旺财是一只可爱的柯基犬,拥有短腿和大屁股的经典柯基特征。性格开朗活泼,很喜欢和人玩耍,是很好的家庭伴侣。',
|
||||
personality: ['开朗', '活泼', '友善', '忠诚'],
|
||||
health: '健康良好,已绝育',
|
||||
location: {
|
||||
province: 'jiangsu',
|
||||
city: 'suzhou',
|
||||
district: 'gusu',
|
||||
address: '姑苏区个人救助'
|
||||
},
|
||||
status: 'available',
|
||||
requirements: [
|
||||
'有养狗经验',
|
||||
'每天遛狗',
|
||||
'有足够空间',
|
||||
'耐心训练'
|
||||
],
|
||||
contact: {
|
||||
name: '王先生',
|
||||
phone: '151****7890',
|
||||
wechat: 'corgi_lover_wang',
|
||||
type: 'individual'
|
||||
},
|
||||
publishTime: new Date(Date.now() - 6 * 24 * 60 * 60 * 1000).toISOString()
|
||||
})
|
||||
|
||||
testData.push({
|
||||
id: Date.now() + 8,
|
||||
name: '雪球',
|
||||
type: 'cat',
|
||||
breed: 'persian',
|
||||
age: 1,
|
||||
gender: 'female',
|
||||
photos: ['https://images.unsplash.com/photo-1571566882372-1598d88abd90?w=400&h=300&fit=crop'],
|
||||
description: '雪球是一只纯白色的波斯猫幼猫,毛发柔软蓬松,眼睛是漂亮的蓝色。性格温和安静,喜欢被人抚摸,是很好的陪伴猫咪。',
|
||||
personality: ['温和', '安静', '亲人', '乖巧'],
|
||||
health: '健康良好,疫苗接种中',
|
||||
location: {
|
||||
province: 'shanghai',
|
||||
city: 'shanghai',
|
||||
district: 'jingan',
|
||||
address: '静安区宠物医院'
|
||||
},
|
||||
status: 'available',
|
||||
requirements: [
|
||||
'有养长毛猫经验',
|
||||
'定期梳毛',
|
||||
'室内饲养',
|
||||
'定期美容'
|
||||
],
|
||||
contact: {
|
||||
name: '静安宠物医院',
|
||||
phone: '021****5678',
|
||||
wechat: 'jingan_pet_hospital',
|
||||
type: 'organization'
|
||||
},
|
||||
publishTime: new Date(Date.now() - 4 * 24 * 60 * 60 * 1000).toISOString()
|
||||
})
|
||||
|
||||
testData.push({
|
||||
id: Date.now() + 9,
|
||||
name: '小柴',
|
||||
type: 'dog',
|
||||
breed: 'shiba-inu',
|
||||
age: 2,
|
||||
gender: 'male',
|
||||
photos: ['https://images.unsplash.com/photo-1605568427561-40dd23c2acea?w=400&h=300&fit=crop'],
|
||||
description: '小柴是一只帅气的柴犬,拥有经典的柴犬笑容和独立的性格。它很聪明但有时候有点固执,需要有经验的主人来训练。',
|
||||
personality: ['独立', '聪明', '固执', '忠诚'],
|
||||
health: '健康良好,已绝育',
|
||||
location: {
|
||||
province: 'guangdong',
|
||||
city: 'guangzhou',
|
||||
district: 'yuexiu',
|
||||
address: '越秀区个人救助'
|
||||
},
|
||||
status: 'reserved',
|
||||
requirements: [
|
||||
'有养狗经验',
|
||||
'了解柴犬特性',
|
||||
'有耐心训练',
|
||||
'定期运动'
|
||||
],
|
||||
contact: {
|
||||
name: '陈女士',
|
||||
phone: '138****9012',
|
||||
wechat: 'shiba_lover_chen',
|
||||
type: 'individual'
|
||||
},
|
||||
publishTime: new Date(Date.now() - 8 * 24 * 60 * 60 * 1000).toISOString()
|
||||
})
|
||||
|
||||
testData.push({
|
||||
id: Date.now() + 10,
|
||||
name: '毛毛',
|
||||
type: 'rabbit',
|
||||
breed: 'angora',
|
||||
age: 2,
|
||||
gender: 'female',
|
||||
photos: ['https://images.unsplash.com/photo-1585110396000-c9ffd4e4b308?w=400&h=300&fit=crop'],
|
||||
description: '毛毛是一只安哥拉兔,拥有非常柔软蓬松的长毛。性格温顺安静,喜欢被人轻柔地抚摸。需要定期梳毛和护理。',
|
||||
personality: ['温顺', '安静', '乖巧', '敏感'],
|
||||
health: '健康良好',
|
||||
location: {
|
||||
province: 'jiangsu',
|
||||
city: 'nanjing',
|
||||
district: 'qinhuai',
|
||||
address: '秦淮区小动物救助'
|
||||
},
|
||||
status: 'available',
|
||||
requirements: [
|
||||
'了解长毛兔护理',
|
||||
'定期梳毛',
|
||||
'安静环境',
|
||||
'耐心照料'
|
||||
],
|
||||
contact: {
|
||||
name: '秦淮动物救助',
|
||||
phone: '025****1357',
|
||||
wechat: 'qinhuai_animal_rescue',
|
||||
type: 'organization'
|
||||
},
|
||||
publishTime: new Date(Date.now() - 12 * 24 * 60 * 60 * 1000).toISOString()
|
||||
})
|
||||
|
||||
return testData
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索宠物
|
||||
* @param {string} keyword 关键词
|
||||
* @param {Array} pets 宠物数组
|
||||
* @returns {Array} 搜索结果
|
||||
*/
|
||||
searchPets(keyword, pets = null) {
|
||||
if (!pets) {
|
||||
pets = this.getAdoptionPets()
|
||||
}
|
||||
|
||||
if (!keyword) return pets
|
||||
|
||||
const lowerKeyword = keyword.toLowerCase()
|
||||
return pets.filter(pet => {
|
||||
return pet.name.toLowerCase().includes(lowerKeyword) ||
|
||||
pet.description.toLowerCase().includes(lowerKeyword) ||
|
||||
this.getPetTypeName(pet.type).includes(keyword) ||
|
||||
this.getPetBreedName(pet.type, pet.breed).includes(keyword) ||
|
||||
pet.personality.some(trait => trait.includes(keyword))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 筛选宠物
|
||||
* @param {Object} filters 筛选条件
|
||||
* @param {Array} pets 宠物数组
|
||||
* @returns {Array} 筛选结果
|
||||
*/
|
||||
filterPets(filters, pets = null) {
|
||||
if (!pets) {
|
||||
pets = this.getAdoptionPets()
|
||||
}
|
||||
|
||||
return pets.filter(pet => {
|
||||
// 宠物类型筛选
|
||||
if (filters.type && pet.type !== filters.type) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 品种筛选
|
||||
if (filters.breed && pet.breed !== filters.breed) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 地区筛选
|
||||
if (filters.province && pet.location.province !== filters.province) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (filters.city && pet.location.city !== filters.city) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (filters.district && pet.location.district !== filters.district) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
if (filters.status && pet.status !== filters.status) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 性别筛选
|
||||
if (filters.gender && pet.gender !== filters.gender) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 年龄筛选
|
||||
if (filters.ageRange) {
|
||||
const [minAge, maxAge] = filters.ageRange
|
||||
if (pet.age < minAge || pet.age > maxAge) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取宠物类型名称
|
||||
* @param {string} type 类型代码
|
||||
* @returns {string} 类型名称
|
||||
*/
|
||||
getPetTypeName(type) {
|
||||
return this.petTypes[type]?.name || '未知类型'
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取宠物品种名称
|
||||
* @param {string} type 类型代码
|
||||
* @param {string} breed 品种代码
|
||||
* @returns {string} 品种名称
|
||||
*/
|
||||
getPetBreedName(type, breed) {
|
||||
return this.petTypes[type]?.breeds[breed] || '未知品种'
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取地区名称
|
||||
* @param {string} province 省份代码
|
||||
* @param {string} city 城市代码
|
||||
* @param {string} district 区县代码
|
||||
* @returns {string} 地区名称
|
||||
*/
|
||||
getLocationName(province, city = null, district = null) {
|
||||
let locationName = this.regions[province]?.name || province
|
||||
|
||||
if (city) {
|
||||
const cityName = this.regions[province]?.cities[city]?.name
|
||||
if (cityName) {
|
||||
locationName += ' ' + cityName
|
||||
}
|
||||
}
|
||||
|
||||
if (district) {
|
||||
const districtName = this.regions[province]?.cities[city]?.districts[district]
|
||||
if (districtName) {
|
||||
locationName += ' ' + districtName
|
||||
}
|
||||
}
|
||||
|
||||
return locationName
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态信息
|
||||
* @param {string} status 状态代码
|
||||
* @returns {Object} 状态信息
|
||||
*/
|
||||
getStatusInfo(status) {
|
||||
return this.adoptionStatus[status] || {
|
||||
name: '未知状态',
|
||||
color: '#999999',
|
||||
icon: '❓'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export default new AdoptionManager()
|
||||
|
|
@ -1,501 +0,0 @@
|
|||
/**
|
||||
* 宠物健康数据管理工具类
|
||||
* 负责整合体重、疫苗、健康记录等数据,提供综合健康评估
|
||||
*/
|
||||
|
||||
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()
|
||||
|
|
@ -1,478 +0,0 @@
|
|||
/**
|
||||
* 宠物记录管理工具类
|
||||
* 负责记录数据的存储、分类、社交功能等管理
|
||||
*/
|
||||
|
||||
class RecordManager {
|
||||
constructor() {
|
||||
this.storageKey = 'pet_records'
|
||||
this.socialDataKey = 'pet_records_social'
|
||||
|
||||
// 记录分类配置
|
||||
this.categories = {
|
||||
// 一级分类
|
||||
health: {
|
||||
name: '健康',
|
||||
icon: '🏥',
|
||||
color: '#4CAF50',
|
||||
subCategories: {
|
||||
medical: { name: '就医记录', icon: '🏥' },
|
||||
checkup: { name: '体检记录', icon: '🔍' },
|
||||
vaccine: { name: '疫苗接种', icon: '💉' },
|
||||
weight: { name: '体重记录', icon: '⚖️' },
|
||||
symptom: { name: '异常症状', icon: '⚠️' },
|
||||
medication: { name: '用药记录', icon: '💊' }
|
||||
}
|
||||
},
|
||||
care: {
|
||||
name: '护理',
|
||||
icon: '🛁',
|
||||
color: '#2196F3',
|
||||
subCategories: {
|
||||
grooming: { name: '洗护美容', icon: '🛁' },
|
||||
cleaning: { name: '清洁护理', icon: '🧽' },
|
||||
nail: { name: '修剪指甲', icon: '✂️' },
|
||||
dental: { name: '口腔护理', icon: '🦷' },
|
||||
ear: { name: '耳部清洁', icon: '👂' },
|
||||
eye: { name: '眼部护理', icon: '👁️' }
|
||||
}
|
||||
},
|
||||
behavior: {
|
||||
name: '行为',
|
||||
icon: '🎾',
|
||||
color: '#FF9800',
|
||||
subCategories: {
|
||||
training: { name: '训练记录', icon: '🎯' },
|
||||
play: { name: '游戏互动', icon: '🎾' },
|
||||
social: { name: '社交活动', icon: '👥' },
|
||||
habit: { name: '习惯养成', icon: '📝' },
|
||||
milestone: { name: '成长里程碑', icon: '🏆' },
|
||||
mood: { name: '情绪状态', icon: '😊' }
|
||||
}
|
||||
},
|
||||
daily: {
|
||||
name: '日常',
|
||||
icon: '📝',
|
||||
color: '#9C27B0',
|
||||
subCategories: {
|
||||
feeding: { name: '喂食记录', icon: '🍽️' },
|
||||
sleep: { name: '睡眠记录', icon: '😴' },
|
||||
exercise: { name: '运动记录', icon: '🏃' },
|
||||
photo: { name: '拍照记录', icon: '📷' },
|
||||
note: { name: '随手记', icon: '📝' },
|
||||
weather: { name: '天气记录', icon: '🌤️' }
|
||||
}
|
||||
},
|
||||
expense: {
|
||||
name: '消费',
|
||||
icon: '💰',
|
||||
color: '#F44336',
|
||||
subCategories: {
|
||||
food: { name: '食物用品', icon: '🍽️' },
|
||||
toy: { name: '玩具用品', icon: '🧸' },
|
||||
medical: { name: '医疗费用', icon: '🏥' },
|
||||
grooming: { name: '美容费用', icon: '✂️' },
|
||||
insurance: { name: '保险费用', icon: '🛡️' },
|
||||
other: { name: '其他消费', icon: '💰' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取宠物的所有记录
|
||||
* @param {string} petId 宠物ID
|
||||
* @returns {Array} 记录数组
|
||||
*/
|
||||
getRecords(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,
|
||||
category: 'health',
|
||||
subCategory: 'checkup',
|
||||
title: '年度体检',
|
||||
content: '带小橘去宠物医院做年度体检,医生说各项指标都很正常,身体很健康!',
|
||||
recordTime: new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
photos: ['/static/checkup1.jpg', '/static/checkup2.jpg'],
|
||||
tags: ['体检', '健康', '正常'],
|
||||
location: '宠物医院',
|
||||
weather: '晴天',
|
||||
mood: 'happy',
|
||||
shareLevel: 'family',
|
||||
createTime: new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
updateTime: new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000).toISOString()
|
||||
})
|
||||
|
||||
// 护理记录
|
||||
testData.push({
|
||||
id: Date.now() + 2,
|
||||
petId: petId,
|
||||
category: 'care',
|
||||
subCategory: 'grooming',
|
||||
title: '洗澡美容',
|
||||
content: '给小橘洗澡和修剪毛发,全程很乖很配合,洗完后毛毛很蓬松很香!',
|
||||
recordTime: new Date(now.getTime() - 5 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
photos: ['/static/grooming1.jpg'],
|
||||
tags: ['洗澡', '美容', '乖巧'],
|
||||
location: '家里',
|
||||
weather: '阴天',
|
||||
mood: 'calm',
|
||||
shareLevel: 'family',
|
||||
createTime: new Date(now.getTime() - 5 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
updateTime: new Date(now.getTime() - 5 * 24 * 60 * 60 * 1000).toISOString()
|
||||
})
|
||||
|
||||
// 行为记录
|
||||
testData.push({
|
||||
id: Date.now() + 3,
|
||||
petId: petId,
|
||||
category: 'behavior',
|
||||
subCategory: 'milestone',
|
||||
title: '第一次用猫砂',
|
||||
content: '小橘第一次学会用猫砂盆,真是个聪明的小家伙!这是一个重要的成长里程碑。',
|
||||
recordTime: new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
photos: [],
|
||||
tags: ['第一次', '猫砂', '聪明'],
|
||||
location: '家里',
|
||||
weather: '晴天',
|
||||
mood: 'proud',
|
||||
shareLevel: 'public',
|
||||
createTime: new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
updateTime: new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000).toISOString()
|
||||
})
|
||||
|
||||
// 日常记录
|
||||
testData.push({
|
||||
id: Date.now() + 4,
|
||||
petId: petId,
|
||||
category: 'daily',
|
||||
subCategory: 'note',
|
||||
title: '今天很活泼',
|
||||
content: '小橘今天特别活泼,一直在客厅里跑来跑去,看起来心情很好!还主动来找我玩。',
|
||||
recordTime: new Date(now.getTime() - 1 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
photos: ['/static/active1.jpg', '/static/active2.jpg', '/static/active3.jpg'],
|
||||
tags: ['活泼', '开心', '互动'],
|
||||
location: '家里',
|
||||
weather: '晴天',
|
||||
mood: 'excited',
|
||||
shareLevel: 'family',
|
||||
createTime: new Date(now.getTime() - 1 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
updateTime: new Date(now.getTime() - 1 * 24 * 60 * 60 * 1000).toISOString()
|
||||
})
|
||||
|
||||
// 消费记录
|
||||
testData.push({
|
||||
id: Date.now() + 5,
|
||||
petId: petId,
|
||||
category: 'expense',
|
||||
subCategory: 'food',
|
||||
title: '购买猫粮和用品',
|
||||
content: '购买了新的猫粮、猫砂和一些玩具,希望小橘会喜欢。',
|
||||
recordTime: new Date(now.getTime() - 10 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
photos: ['/static/shopping1.jpg'],
|
||||
tags: ['猫粮', '猫砂', '玩具'],
|
||||
location: '宠物用品店',
|
||||
weather: '多云',
|
||||
mood: 'happy',
|
||||
shareLevel: 'private',
|
||||
amount: 268,
|
||||
store: '宠物用品店',
|
||||
createTime: new Date(now.getTime() - 10 * 24 * 60 * 60 * 1000).toISOString(),
|
||||
updateTime: new Date(now.getTime() - 10 * 24 * 60 * 60 * 1000).toISOString()
|
||||
})
|
||||
|
||||
return testData
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加记录
|
||||
* @param {string} petId 宠物ID
|
||||
* @param {Object} record 记录数据
|
||||
*/
|
||||
addRecord(petId, record) {
|
||||
try {
|
||||
const allRecords = uni.getStorageSync(this.storageKey) || {}
|
||||
if (!allRecords[petId]) {
|
||||
allRecords[petId] = []
|
||||
}
|
||||
|
||||
const newRecord = {
|
||||
id: Date.now(),
|
||||
petId: petId,
|
||||
category: record.category,
|
||||
subCategory: record.subCategory,
|
||||
title: record.title,
|
||||
content: record.content,
|
||||
recordTime: record.recordTime || new Date().toISOString(),
|
||||
photos: record.photos || [],
|
||||
tags: record.tags || [],
|
||||
location: record.location || '',
|
||||
weather: record.weather || '',
|
||||
mood: record.mood || '',
|
||||
shareLevel: record.shareLevel || 'family',
|
||||
amount: record.amount || 0,
|
||||
store: record.store || '',
|
||||
createTime: new Date().toISOString(),
|
||||
updateTime: new Date().toISOString()
|
||||
}
|
||||
|
||||
allRecords[petId].push(newRecord)
|
||||
allRecords[petId].sort((a, b) => new Date(b.recordTime) - new Date(a.recordTime))
|
||||
|
||||
uni.setStorageSync(this.storageKey, allRecords)
|
||||
|
||||
// 初始化社交数据
|
||||
this.initSocialData(newRecord.id)
|
||||
|
||||
return newRecord
|
||||
} catch (error) {
|
||||
console.error('添加记录失败:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取记录的社交数据
|
||||
* @param {string} recordId 记录ID
|
||||
* @returns {Object} 社交数据
|
||||
*/
|
||||
getSocialData(recordId) {
|
||||
try {
|
||||
const allSocialData = uni.getStorageSync(this.socialDataKey) || {}
|
||||
return allSocialData[recordId] || {
|
||||
likes: 0,
|
||||
views: 0,
|
||||
comments: [],
|
||||
likedBy: []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取社交数据失败:', error)
|
||||
return {
|
||||
likes: 0,
|
||||
views: 0,
|
||||
comments: [],
|
||||
likedBy: []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化社交数据
|
||||
* @param {string} recordId 记录ID
|
||||
*/
|
||||
initSocialData(recordId) {
|
||||
try {
|
||||
const allSocialData = uni.getStorageSync(this.socialDataKey) || {}
|
||||
if (!allSocialData[recordId]) {
|
||||
allSocialData[recordId] = {
|
||||
likes: Math.floor(Math.random() * 10), // 模拟点赞数
|
||||
views: Math.floor(Math.random() * 50) + 10, // 模拟浏览数
|
||||
comments: this.generateMockComments(), // 模拟评论
|
||||
likedBy: []
|
||||
}
|
||||
uni.setStorageSync(this.socialDataKey, allSocialData)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('初始化社交数据失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成模拟评论
|
||||
* @returns {Array} 评论数组
|
||||
*/
|
||||
generateMockComments() {
|
||||
const comments = [
|
||||
{ id: 1, user: '爱宠达人', content: '好可爱啊!', time: '2小时前' },
|
||||
{ id: 2, user: '猫咪专家', content: '看起来很健康呢', time: '1天前' },
|
||||
{ id: 3, user: '宠物医生', content: '定期体检很重要', time: '2天前' }
|
||||
]
|
||||
|
||||
// 随机返回0-3条评论
|
||||
const count = Math.floor(Math.random() * 4)
|
||||
return comments.slice(0, count)
|
||||
}
|
||||
|
||||
/**
|
||||
* 点赞记录
|
||||
* @param {string} recordId 记录ID
|
||||
* @param {string} userId 用户ID
|
||||
*/
|
||||
likeRecord(recordId, userId = 'current_user') {
|
||||
try {
|
||||
const allSocialData = uni.getStorageSync(this.socialDataKey) || {}
|
||||
if (!allSocialData[recordId]) {
|
||||
this.initSocialData(recordId)
|
||||
allSocialData[recordId] = this.getSocialData(recordId)
|
||||
}
|
||||
|
||||
const socialData = allSocialData[recordId]
|
||||
const likedIndex = socialData.likedBy.indexOf(userId)
|
||||
|
||||
if (likedIndex === -1) {
|
||||
// 点赞
|
||||
socialData.likes += 1
|
||||
socialData.likedBy.push(userId)
|
||||
} else {
|
||||
// 取消点赞
|
||||
socialData.likes -= 1
|
||||
socialData.likedBy.splice(likedIndex, 1)
|
||||
}
|
||||
|
||||
uni.setStorageSync(this.socialDataKey, allSocialData)
|
||||
return socialData
|
||||
} catch (error) {
|
||||
console.error('点赞失败:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加浏览量
|
||||
* @param {string} recordId 记录ID
|
||||
*/
|
||||
incrementViews(recordId) {
|
||||
try {
|
||||
const allSocialData = uni.getStorageSync(this.socialDataKey) || {}
|
||||
if (!allSocialData[recordId]) {
|
||||
this.initSocialData(recordId)
|
||||
allSocialData[recordId] = this.getSocialData(recordId)
|
||||
}
|
||||
|
||||
allSocialData[recordId].views += 1
|
||||
uni.setStorageSync(this.socialDataKey, allSocialData)
|
||||
} catch (error) {
|
||||
console.error('增加浏览量失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加评论
|
||||
* @param {string} recordId 记录ID
|
||||
* @param {string} content 评论内容
|
||||
* @param {string} user 用户名
|
||||
*/
|
||||
addComment(recordId, content, user = '我') {
|
||||
try {
|
||||
const allSocialData = uni.getStorageSync(this.socialDataKey) || {}
|
||||
if (!allSocialData[recordId]) {
|
||||
this.initSocialData(recordId)
|
||||
allSocialData[recordId] = this.getSocialData(recordId)
|
||||
}
|
||||
|
||||
const newComment = {
|
||||
id: Date.now(),
|
||||
user: user,
|
||||
content: content,
|
||||
time: '刚刚'
|
||||
}
|
||||
|
||||
allSocialData[recordId].comments.unshift(newComment)
|
||||
uni.setStorageSync(this.socialDataKey, allSocialData)
|
||||
|
||||
return newComment
|
||||
} catch (error) {
|
||||
console.error('添加评论失败:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取分类信息
|
||||
* @param {string} category 分类
|
||||
* @param {string} subCategory 子分类
|
||||
* @returns {Object} 分类信息
|
||||
*/
|
||||
getCategoryInfo(category, subCategory = null) {
|
||||
const categoryInfo = this.categories[category]
|
||||
if (!categoryInfo) {
|
||||
return { name: '未知分类', icon: '📝', color: '#999999' }
|
||||
}
|
||||
|
||||
if (subCategory && categoryInfo.subCategories) {
|
||||
const subCategoryInfo = categoryInfo.subCategories[subCategory]
|
||||
if (subCategoryInfo) {
|
||||
return {
|
||||
name: subCategoryInfo.name,
|
||||
icon: subCategoryInfo.icon,
|
||||
color: categoryInfo.color
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: categoryInfo.name,
|
||||
icon: categoryInfo.icon,
|
||||
color: categoryInfo.color
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索记录
|
||||
* @param {string} petId 宠物ID
|
||||
* @param {string} keyword 关键词
|
||||
* @returns {Array} 搜索结果
|
||||
*/
|
||||
searchRecords(petId, keyword) {
|
||||
const records = this.getRecords(petId)
|
||||
if (!keyword) return records
|
||||
|
||||
const lowerKeyword = keyword.toLowerCase()
|
||||
return records.filter(record => {
|
||||
return record.title.toLowerCase().includes(lowerKeyword) ||
|
||||
record.content.toLowerCase().includes(lowerKeyword) ||
|
||||
record.tags.some(tag => tag.toLowerCase().includes(lowerKeyword))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 按分类筛选记录
|
||||
* @param {string} petId 宠物ID
|
||||
* @param {string} category 一级分类
|
||||
* @param {string} subCategory 二级分类
|
||||
* @returns {Array} 筛选结果
|
||||
*/
|
||||
filterRecords(petId, category = null, subCategory = null) {
|
||||
const records = this.getRecords(petId)
|
||||
|
||||
if (!category) return records
|
||||
|
||||
return records.filter(record => {
|
||||
if (subCategory) {
|
||||
return record.category === category && record.subCategory === subCategory
|
||||
} else {
|
||||
return record.category === category
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default new RecordManager()
|
||||
|
|
@ -1,406 +0,0 @@
|
|||
/**
|
||||
* 宠物疫苗管理工具类
|
||||
* 负责疫苗数据的存储、分析和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()
|
||||
|
|
@ -1,397 +0,0 @@
|
|||
/**
|
||||
* 宠物体重管理工具类
|
||||
* 负责体重数据的存储、分析和AI建议生成
|
||||
*/
|
||||
|
||||
class WeightManager {
|
||||
constructor() {
|
||||
this.storageKey = 'pet_weight_records'
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取宠物的体重记录
|
||||
* @param {string} petId 宠物ID
|
||||
* @returns {Array} 体重记录数组
|
||||
*/
|
||||
getWeightRecords(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 = []
|
||||
|
||||
// 生成过去3个月的体重数据,模拟真实的体重变化
|
||||
const baseWeight = 3.8 // 基础体重
|
||||
const growthRate = 0.015 // 每周增长率
|
||||
|
||||
for (let i = 90; i >= 0; i -= 3) { // 每3天一个记录点
|
||||
const date = new Date(now.getTime() - i * 24 * 60 * 60 * 1000)
|
||||
const weeksPassed = (90 - i) / 7
|
||||
|
||||
// 模拟自然的体重增长,加入一些随机波动
|
||||
let weight = baseWeight + (weeksPassed * growthRate * baseWeight)
|
||||
weight += (Math.random() - 0.5) * 0.1 // 添加±0.05kg的随机波动
|
||||
weight = Math.round(weight * 10) / 10 // 保留一位小数
|
||||
|
||||
testData.push({
|
||||
id: Date.now() + i,
|
||||
weight: weight,
|
||||
date: date.toISOString(),
|
||||
note: i === 0 ? '最新记录' : '自动生成',
|
||||
timestamp: date.getTime()
|
||||
})
|
||||
}
|
||||
|
||||
return testData
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加体重记录
|
||||
* @param {string} petId 宠物ID
|
||||
* @param {Object} record 体重记录
|
||||
*/
|
||||
addWeightRecord(petId, record) {
|
||||
try {
|
||||
const allRecords = uni.getStorageSync(this.storageKey) || {}
|
||||
if (!allRecords[petId]) {
|
||||
allRecords[petId] = []
|
||||
}
|
||||
|
||||
const newRecord = {
|
||||
id: Date.now(),
|
||||
weight: record.weight,
|
||||
date: record.date || new Date().toISOString(),
|
||||
note: record.note || '',
|
||||
timestamp: Date.now()
|
||||
}
|
||||
|
||||
allRecords[petId].push(newRecord)
|
||||
allRecords[petId].sort((a, b) => new Date(a.date) - new Date(b.date))
|
||||
|
||||
uni.setStorageSync(this.storageKey, allRecords)
|
||||
return newRecord
|
||||
} catch (error) {
|
||||
console.error('添加体重记录失败:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前体重
|
||||
* @param {string} petId 宠物ID
|
||||
* @returns {number} 当前体重
|
||||
*/
|
||||
getCurrentWeight(petId) {
|
||||
const records = this.getWeightRecords(petId)
|
||||
if (records.length === 0) return 0
|
||||
return records[records.length - 1].weight
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算体重变化
|
||||
* @param {string} petId 宠物ID
|
||||
* @param {number} days 对比天数
|
||||
* @returns {Object} 变化数据
|
||||
*/
|
||||
calculateWeightChange(petId, days) {
|
||||
const records = this.getWeightRecords(petId)
|
||||
if (records.length < 2) {
|
||||
return {
|
||||
change: 0,
|
||||
percent: 0,
|
||||
trend: 'stable'
|
||||
}
|
||||
}
|
||||
|
||||
const now = new Date()
|
||||
const compareDate = new Date(now.getTime() - days * 24 * 60 * 60 * 1000)
|
||||
|
||||
const currentWeight = this.getCurrentWeight(petId)
|
||||
const compareRecord = records.find(record =>
|
||||
new Date(record.date) >= compareDate
|
||||
) || records[0]
|
||||
|
||||
const change = currentWeight - compareRecord.weight
|
||||
const percent = compareRecord.weight > 0 ? (change / compareRecord.weight) * 100 : 0
|
||||
|
||||
let trend = 'stable'
|
||||
if (Math.abs(percent) > 1) {
|
||||
trend = change > 0 ? 'increase' : 'decrease'
|
||||
}
|
||||
|
||||
return {
|
||||
change: Number(change.toFixed(1)),
|
||||
percent: Number(percent.toFixed(1)),
|
||||
trend: trend,
|
||||
previousWeight: compareRecord.weight
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成图表数据
|
||||
* @param {string} petId 宠物ID
|
||||
* @param {string} timeRange 时间范围
|
||||
* @returns {Object} 图表数据
|
||||
*/
|
||||
generateChartData(petId, timeRange) {
|
||||
const records = this.getWeightRecords(petId)
|
||||
if (records.length === 0) {
|
||||
return {
|
||||
categories: [],
|
||||
series: [{ name: "体重", data: [] }]
|
||||
}
|
||||
}
|
||||
|
||||
let filteredRecords = []
|
||||
const now = new Date()
|
||||
|
||||
switch (timeRange) {
|
||||
case 'week':
|
||||
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
|
||||
filteredRecords = records.filter(record => new Date(record.date) >= weekAgo)
|
||||
break
|
||||
case 'month':
|
||||
const monthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000)
|
||||
filteredRecords = records.filter(record => new Date(record.date) >= monthAgo)
|
||||
break
|
||||
case 'year':
|
||||
const yearAgo = new Date(now.getTime() - 365 * 24 * 60 * 60 * 1000)
|
||||
filteredRecords = records.filter(record => new Date(record.date) >= yearAgo)
|
||||
break
|
||||
default:
|
||||
filteredRecords = records
|
||||
}
|
||||
|
||||
// 如果数据点太少,补充一些模拟数据
|
||||
if (filteredRecords.length === 0) {
|
||||
filteredRecords = this.generateMockData(timeRange)
|
||||
}
|
||||
|
||||
const categories = filteredRecords.map(record => {
|
||||
const date = new Date(record.date)
|
||||
if (timeRange === 'year' || timeRange === 'all') {
|
||||
return `${date.getMonth() + 1}月`
|
||||
} else {
|
||||
return `${date.getMonth() + 1}/${date.getDate()}`
|
||||
}
|
||||
})
|
||||
|
||||
const data = filteredRecords.map(record => record.weight)
|
||||
|
||||
return {
|
||||
categories: categories,
|
||||
series: [{ name: "体重", data: data }]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成模拟数据(用于演示)
|
||||
* @param {string} timeRange 时间范围
|
||||
* @returns {Array} 模拟数据
|
||||
*/
|
||||
generateMockData(timeRange) {
|
||||
const now = new Date()
|
||||
const mockData = []
|
||||
const currentWeight = 4.2
|
||||
|
||||
switch (timeRange) {
|
||||
case 'week':
|
||||
// 近一周数据:每天一个点
|
||||
for (let i = 6; i >= 0; i--) {
|
||||
const date = new Date(now.getTime() - i * 24 * 60 * 60 * 1000)
|
||||
const weight = currentWeight - (i * 0.02) + (Math.random() - 0.5) * 0.05
|
||||
mockData.push({
|
||||
weight: Math.round(weight * 10) / 10,
|
||||
date: date.toISOString()
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'month':
|
||||
// 近一月数据:每周一个点
|
||||
for (let i = 4; i >= 0; i--) {
|
||||
const date = new Date(now.getTime() - i * 7 * 24 * 60 * 60 * 1000)
|
||||
const weight = currentWeight - (i * 0.08) + (Math.random() - 0.5) * 0.1
|
||||
mockData.push({
|
||||
weight: Math.round(weight * 10) / 10,
|
||||
date: date.toISOString()
|
||||
})
|
||||
}
|
||||
break
|
||||
case 'year':
|
||||
// 近一年数据:每两个月一个点
|
||||
for (let i = 5; i >= 0; i--) {
|
||||
const date = new Date(now.getTime() - i * 60 * 24 * 60 * 60 * 1000)
|
||||
const weight = currentWeight - (i * 0.2) + (Math.random() - 0.5) * 0.15
|
||||
mockData.push({
|
||||
weight: Math.round(weight * 10) / 10,
|
||||
date: date.toISOString()
|
||||
})
|
||||
}
|
||||
break
|
||||
default:
|
||||
// 全部历史:从小猫到现在
|
||||
const milestones = [
|
||||
{ months: 18, weight: 1.2 },
|
||||
{ months: 15, weight: 1.8 },
|
||||
{ months: 12, weight: 2.5 },
|
||||
{ months: 9, weight: 3.0 },
|
||||
{ months: 6, weight: 3.4 },
|
||||
{ months: 3, weight: 3.8 },
|
||||
{ months: 0, weight: 4.2 }
|
||||
]
|
||||
|
||||
milestones.forEach(milestone => {
|
||||
const date = new Date(now.getTime() - milestone.months * 30 * 24 * 60 * 60 * 1000)
|
||||
mockData.push({
|
||||
weight: milestone.weight,
|
||||
date: date.toISOString()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return mockData.sort((a, b) => new Date(a.date) - new Date(b.date))
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成AI健康分析
|
||||
* @param {string} petId 宠物ID
|
||||
* @param {Object} petInfo 宠物信息
|
||||
* @returns {Object} AI分析结果
|
||||
*/
|
||||
generateAIAnalysis(petId, petInfo) {
|
||||
const currentWeight = this.getCurrentWeight(petId)
|
||||
const weeklyChange = this.calculateWeightChange(petId, 7)
|
||||
const monthlyChange = this.calculateWeightChange(petId, 30)
|
||||
|
||||
// 根据品种、年龄、性别判断健康范围
|
||||
const healthRange = this.getHealthyWeightRange(petInfo)
|
||||
const isHealthy = currentWeight >= healthRange.min && currentWeight <= healthRange.max
|
||||
|
||||
// 健康评估
|
||||
let healthAssessment = ''
|
||||
if (isHealthy) {
|
||||
healthAssessment = `根据${petInfo.name}的品种(${petInfo.breed})、年龄(${petInfo.age}岁)和性别(${petInfo.gender}),当前体重${currentWeight}kg处于健康范围内(${healthRange.min}-${healthRange.max}kg)。`
|
||||
} else if (currentWeight < healthRange.min) {
|
||||
healthAssessment = `当前体重${currentWeight}kg低于健康范围(${healthRange.min}-${healthRange.max}kg),建议增加营养摄入。`
|
||||
} else {
|
||||
healthAssessment = `当前体重${currentWeight}kg超出健康范围(${healthRange.min}-${healthRange.max}kg),建议控制饮食并增加运动。`
|
||||
}
|
||||
|
||||
// 趋势分析
|
||||
let trendAnalysis = ''
|
||||
if (weeklyChange.trend === 'stable') {
|
||||
trendAnalysis = '近期体重保持稳定,这是一个良好的状态。'
|
||||
} else if (weeklyChange.trend === 'increase') {
|
||||
if (weeklyChange.percent > 5) {
|
||||
trendAnalysis = `近期体重上升较快(${weeklyChange.percent}%),需要关注饮食控制。`
|
||||
} else {
|
||||
trendAnalysis = `近期体重呈适度上升趋势(${weeklyChange.percent}%),增长速度适中。`
|
||||
}
|
||||
} else {
|
||||
if (weeklyChange.percent < -5) {
|
||||
trendAnalysis = `近期体重下降较快(${weeklyChange.percent}%),建议检查健康状况。`
|
||||
} else {
|
||||
trendAnalysis = `近期体重呈下降趋势(${weeklyChange.percent}%),请注意营养补充。`
|
||||
}
|
||||
}
|
||||
|
||||
// 建议
|
||||
let recommendations = this.generateRecommendations(currentWeight, healthRange, weeklyChange)
|
||||
|
||||
// 医疗建议
|
||||
let medicalAdvice = ''
|
||||
if (Math.abs(weeklyChange.percent) > 10 || !isHealthy) {
|
||||
medicalAdvice = '建议咨询兽医,进行专业的健康检查和营养指导。'
|
||||
}
|
||||
|
||||
return {
|
||||
healthAssessment,
|
||||
trendAnalysis,
|
||||
recommendations,
|
||||
medicalAdvice
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取健康体重范围
|
||||
* @param {Object} petInfo 宠物信息
|
||||
* @returns {Object} 健康体重范围
|
||||
*/
|
||||
getHealthyWeightRange(petInfo) {
|
||||
// 简化的品种体重范围映射
|
||||
const breedRanges = {
|
||||
'橘猫': { min: 3.5, max: 5.5 },
|
||||
'英短': { min: 3.0, max: 5.0 },
|
||||
'美短': { min: 3.5, max: 5.5 },
|
||||
'布偶': { min: 4.0, max: 7.0 },
|
||||
'波斯': { min: 3.0, max: 5.5 },
|
||||
'暹罗': { min: 2.5, max: 4.5 }
|
||||
}
|
||||
|
||||
const baseRange = breedRanges[petInfo.breed] || { min: 3.0, max: 6.0 }
|
||||
|
||||
// 根据性别调整(公猫通常比母猫重一些)
|
||||
if (petInfo.gender === '公') {
|
||||
baseRange.min += 0.5
|
||||
baseRange.max += 0.5
|
||||
}
|
||||
|
||||
return baseRange
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成个性化建议
|
||||
* @param {number} currentWeight 当前体重
|
||||
* @param {Object} healthRange 健康范围
|
||||
* @param {Object} weeklyChange 周变化
|
||||
* @returns {string} 建议内容
|
||||
*/
|
||||
generateRecommendations(currentWeight, healthRange, weeklyChange) {
|
||||
let recommendations = []
|
||||
|
||||
if (currentWeight < healthRange.min) {
|
||||
recommendations.push('增加高质量蛋白质摄入,如优质猫粮、煮熟的鸡胸肉')
|
||||
recommendations.push('少量多餐,每日3-4次定时喂食')
|
||||
recommendations.push('确保充足的饮水')
|
||||
} else if (currentWeight > healthRange.max) {
|
||||
recommendations.push('控制食物分量,减少高热量零食')
|
||||
recommendations.push('增加运动量,每日逗猫棒游戏20-30分钟')
|
||||
recommendations.push('选择低脂肪、高纤维的减肥猫粮')
|
||||
} else {
|
||||
recommendations.push('保持当前的饮食习惯,每日定时定量喂食')
|
||||
recommendations.push('适量运动,如逗猫棒游戏15-20分钟')
|
||||
recommendations.push('定期监测体重变化')
|
||||
}
|
||||
|
||||
if (weeklyChange.trend === 'increase' && weeklyChange.percent > 3) {
|
||||
recommendations.push('近期体重增长较快,建议减少零食摄入')
|
||||
} else if (weeklyChange.trend === 'decrease' && weeklyChange.percent < -3) {
|
||||
recommendations.push('近期体重下降,注意观察食欲和精神状态')
|
||||
}
|
||||
|
||||
return recommendations.join(';') + '。'
|
||||
}
|
||||
}
|
||||
|
||||
export default new WeightManager()
|
||||
Loading…
Reference in New Issue