This commit is contained in:
yvan 2025-08-13 19:23:09 +08:00
parent 4e136fb467
commit c7b0b1e5b7
8 changed files with 4512 additions and 769 deletions

View File

@ -32,6 +32,14 @@
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/adoption/adoption-detail",
"style": {
"navigationBarTitleText": "领养详情",
"navigationBarBackgroundColor": "#FF8A80",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/profile/profile",
"style": {
@ -80,6 +88,14 @@
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/pets/record-detail",
"style": {
"navigationBarTitleText": "记录详情",
"navigationBarBackgroundColor": "#FF8A80",
"navigationBarTextStyle": "white"
}
},
{
"path": "pages/pets/pet-chat-simple",

View File

@ -0,0 +1,749 @@
<template>
<view class="adoption-detail-container">
<!-- 宠物图片轮播 -->
<view class="pet-gallery">
<swiper
class="gallery-swiper"
:indicator-dots="pet.photos && pet.photos.length > 1"
:autoplay="false"
:circular="true"
indicator-color="rgba(255,255,255,0.5)"
indicator-active-color="#FF8A80"
>
<swiper-item v-for="(photo, index) in pet.photos" :key="index">
<image
class="gallery-image"
:src="photo"
mode="aspectFill"
@click="previewImage(photo, index)"
/>
</swiper-item>
</swiper>
<!-- 状态标识 -->
<view class="pet-status-overlay" :class="pet.status">
<text class="status-icon">{{ getStatusIcon() }}</text>
<text class="status-text">{{ getStatusText() }}</text>
</view>
<!-- 收藏按钮 -->
<view class="favorite-btn" @click="toggleFavorite">
<text class="favorite-icon">{{ pet.isFavorite ? '❤️' : '🤍' }}</text>
</view>
</view>
<!-- 宠物基本信息 -->
<view class="pet-basic-info">
<view class="pet-header">
<text class="pet-name">{{ pet.name }}</text>
<view class="pet-stats">
<view class="stat-item">
<text class="stat-icon">👁</text>
<text class="stat-count">{{ pet.views }}</text>
</view>
<view class="stat-item">
<text class="stat-icon"></text>
<text class="stat-count">{{ pet.favorites }}</text>
</view>
</view>
</view>
<view class="pet-tags">
<view class="tag-item type-tag">
<text class="tag-text">{{ getPetTypeName() }}</text>
</view>
<view class="tag-item breed-tag">
<text class="tag-text">{{ getPetBreedName() }}</text>
</view>
</view>
<view class="pet-details-grid">
<view class="detail-item">
<text class="detail-label">年龄</text>
<text class="detail-value">{{ pet.age }}</text>
</view>
<view class="detail-item">
<text class="detail-label">性别</text>
<text class="detail-value">{{ pet.gender === 'male' ? '公' : '母' }}</text>
</view>
<view class="detail-item">
<text class="detail-label">地区</text>
<text class="detail-value">{{ getLocationText() }}</text>
</view>
<view class="detail-item">
<text class="detail-label">健康</text>
<text class="detail-value">{{ pet.health }}</text>
</view>
</view>
</view>
<!-- 宠物描述 -->
<view class="pet-description">
<view class="section-title">
<text class="title-text">🐾 关于{{ pet.name }}</text>
</view>
<text class="description-text">{{ pet.description }}</text>
</view>
<!-- 性格特点 -->
<view class="pet-personality" v-if="pet.personality && pet.personality.length > 0">
<view class="section-title">
<text class="title-text">😊 性格特点</text>
</view>
<view class="personality-tags">
<view
class="personality-tag"
v-for="trait in pet.personality"
:key="trait"
>
<text class="trait-text">{{ trait }}</text>
</view>
</view>
</view>
<!-- 领养条件 -->
<view class="adoption-requirements" v-if="pet.requirements && pet.requirements.length > 0">
<view class="section-title">
<text class="title-text">📋 领养条件</text>
</view>
<view class="requirements-list">
<view
class="requirement-item"
v-for="(requirement, index) in pet.requirements"
:key="index"
>
<text class="requirement-icon"></text>
<text class="requirement-text">{{ requirement }}</text>
</view>
</view>
</view>
<!-- 联系信息 */
<view class="contact-info">
<view class="section-title">
<text class="title-text">📞 联系方式</text>
</view>
<view class="contact-details">
<view class="contact-item">
<text class="contact-label">联系人</text>
<text class="contact-value">{{ pet.contact.name }}</text>
<text class="contact-type-badge">{{ pet.contact.type === 'organization' ? '机构' : '个人' }}</text>
</view>
<view class="contact-item">
<text class="contact-label">电话</text>
<text class="contact-value">{{ pet.contact.phone }}</text>
<view class="contact-action" @click="makeCall">
<text class="action-text">拨打</text>
</view>
</view>
<view class="contact-item" v-if="pet.contact.wechat">
<text class="contact-label">微信</text>
<text class="contact-value">{{ pet.contact.wechat }}</text>
<view class="contact-action" @click="copyWechat">
<text class="action-text">复制</text>
</view>
</view>
<view class="contact-item" v-if="pet.location.address">
<text class="contact-label">地址</text>
<text class="contact-value">{{ pet.location.address }}</text>
</view>
</view>
</view>
<!-- 发布信息 */
<view class="publish-info">
<view class="publish-item">
<text class="publish-label">发布时间</text>
<text class="publish-value">{{ formatTime(pet.publishTime) }}</text>
</view>
</view>
<!-- 底部操作按钮 -->
<view class="bottom-actions">
<view class="action-btn secondary" @click="shareAdoption">
<text class="btn-icon">📤</text>
<text class="btn-text">分享</text>
</view>
<view class="action-btn primary" @click="applyAdoption" v-if="pet.status === 'available'">
<text class="btn-text">申请领养</text>
</view>
<view class="action-btn disabled" v-else>
<text class="btn-text">{{ getStatusText() }}</text>
</view>
</view>
</view>
</template>
<script>
import adoptionManager from '@/utils/adoptionManager.js'
export default {
data() {
return {
petId: '',
pet: {
photos: [],
personality: [],
requirements: [],
contact: {},
location: {}
}
}
},
onLoad(options) {
this.petId = options.petId || ''
this.loadPetDetail()
},
methods: {
loadPetDetail() {
const pets = adoptionManager.getAdoptionPets()
this.pet = pets.find(p => p.id == this.petId) || {}
//
if (this.pet.name) {
uni.setNavigationBarTitle({
title: this.pet.name + '的领养信息'
})
}
},
getPetTypeName() {
return adoptionManager.getPetTypeName(this.pet.type)
},
getPetBreedName() {
return adoptionManager.getPetBreedName(this.pet.type, this.pet.breed)
},
getLocationText() {
return adoptionManager.getLocationName(
this.pet.location.province,
this.pet.location.city,
this.pet.location.district
)
},
getStatusIcon() {
return adoptionManager.getStatusInfo(this.pet.status).icon
},
getStatusText() {
return adoptionManager.getStatusInfo(this.pet.status).name
},
formatTime(timeStr) {
if (!timeStr) return ''
const time = new Date(timeStr)
const now = new Date()
const diff = now - time
const days = Math.floor(diff / (1000 * 60 * 60 * 24))
if (days === 0) {
return '今天发布'
} else if (days === 1) {
return '昨天发布'
} else if (days < 7) {
return `${days}天前发布`
} else {
return `${time.getFullYear()}-${(time.getMonth() + 1).toString().padStart(2, '0')}-${time.getDate().toString().padStart(2, '0')} 发布`
}
},
previewImage(photo, index) {
uni.previewImage({
urls: this.pet.photos,
current: index
})
},
toggleFavorite() {
this.pet.isFavorite = !this.pet.isFavorite
adoptionManager.toggleFavorite(this.petId, this.pet.isFavorite)
uni.showToast({
title: this.pet.isFavorite ? '已收藏' : '已取消收藏',
icon: 'success',
duration: 1000
})
},
makeCall() {
uni.makePhoneCall({
phoneNumber: this.pet.contact.phone.replace(/\*/g, ''),
fail: () => {
uni.showToast({
title: '拨打失败',
icon: 'none'
})
}
})
},
copyWechat() {
uni.setClipboardData({
data: this.pet.contact.wechat,
success: () => {
uni.showToast({
title: '微信号已复制',
icon: 'success'
})
}
})
},
shareAdoption() {
uni.share({
provider: 'weixin',
scene: 'WXSceneSession',
type: 0,
href: '',
title: `${this.pet.name}等待领养`,
summary: this.pet.description,
imageUrl: this.pet.photos[0],
success: () => {
uni.showToast({
title: '分享成功',
icon: 'success'
})
},
fail: () => {
uni.showToast({
title: '分享失败',
icon: 'none'
})
}
})
},
applyAdoption() {
uni.showModal({
title: '申请领养',
content: `确定要申请领养${this.pet.name}吗?我们会将您的联系方式转达给发布者。`,
confirmText: '确定申请',
cancelText: '再想想',
success: (res) => {
if (res.confirm) {
//
uni.showToast({
title: '申请已提交',
icon: 'success'
})
//
setTimeout(() => {
this.pet.status = 'pending'
uni.showToast({
title: '请耐心等待回复',
icon: 'none'
})
}, 2000)
}
}
})
}
}
}
</script>
<style lang="scss" scoped>
.adoption-detail-container {
background: linear-gradient(135deg, #FF8A80 0%, #FFB6C1 25%, #FECFEF 50%, #F8BBD9 100%);
min-height: 100vh;
padding-bottom: 120rpx;
}
/* 宠物图片轮播 */
.pet-gallery {
position: relative;
height: 600rpx;
.gallery-swiper {
width: 100%;
height: 100%;
.gallery-image {
width: 100%;
height: 100%;
}
}
.pet-status-overlay {
position: absolute;
top: 32rpx;
left: 32rpx;
display: flex;
align-items: center;
gap: 8rpx;
background: rgba(0, 0, 0, 0.7);
border-radius: 16rpx;
padding: 12rpx 20rpx;
&.available {
background: rgba(76, 175, 80, 0.9);
}
&.reserved {
background: rgba(255, 152, 0, 0.9);
}
&.adopted {
background: rgba(158, 158, 158, 0.9);
}
.status-icon {
font-size: 20rpx;
}
.status-text {
font-size: 22rpx;
color: white;
font-weight: 500;
}
}
.favorite-btn {
position: absolute;
top: 32rpx;
right: 32rpx;
width: 80rpx;
height: 80rpx;
background: rgba(255, 255, 255, 0.9);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
&:active {
transform: scale(0.9);
}
.favorite-icon {
font-size: 32rpx;
}
}
}
/* 宠物基本信息 */
.pet-basic-info {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20rpx);
margin: 20rpx;
border-radius: 20rpx;
padding: 24rpx;
box-shadow: 0 4rpx 16rpx rgba(255, 138, 128, 0.1);
border: 1rpx solid rgba(255, 255, 255, 0.3);
.pet-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
.pet-name {
font-size: 36rpx;
font-weight: bold;
color: #333333;
}
.pet-stats {
display: flex;
gap: 16rpx;
.stat-item {
display: flex;
align-items: center;
gap: 6rpx;
.stat-icon {
font-size: 18rpx;
}
.stat-count {
font-size: 20rpx;
color: #999999;
}
}
}
}
.pet-tags {
display: flex;
gap: 12rpx;
margin-bottom: 20rpx;
.tag-item {
border-radius: 16rpx;
padding: 8rpx 16rpx;
&.type-tag {
background: rgba(255, 138, 128, 0.1);
.tag-text {
color: #FF8A80;
}
}
&.breed-tag {
background: rgba(33, 150, 243, 0.1);
.tag-text {
color: #2196F3;
}
}
.tag-text {
font-size: 22rpx;
font-weight: 500;
}
}
}
.pet-details-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16rpx;
.detail-item {
display: flex;
flex-direction: column;
gap: 8rpx;
.detail-label {
font-size: 20rpx;
color: #999999;
}
.detail-value {
font-size: 24rpx;
color: #333333;
font-weight: 500;
}
}
}
}
/* 通用卡片样式 */
.pet-description,
.pet-personality,
.adoption-requirements,
.contact-info,
.publish-info {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20rpx);
margin: 0 20rpx 16rpx 20rpx;
border-radius: 20rpx;
padding: 24rpx;
box-shadow: 0 4rpx 16rpx rgba(255, 138, 128, 0.1);
border: 1rpx solid rgba(255, 255, 255, 0.3);
.section-title {
margin-bottom: 16rpx;
.title-text {
font-size: 28rpx;
font-weight: bold;
color: #333333;
}
}
}
/* 宠物描述 */
.pet-description {
.description-text {
font-size: 26rpx;
color: #666666;
line-height: 1.6;
}
}
/* 性格特点 */
.pet-personality {
.personality-tags {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
.personality-tag {
background: rgba(255, 138, 128, 0.05);
border-radius: 16rpx;
padding: 12rpx 20rpx;
.trait-text {
font-size: 22rpx;
color: #666666;
}
}
}
}
/* 领养条件 */
.adoption-requirements {
.requirements-list {
.requirement-item {
display: flex;
align-items: center;
gap: 12rpx;
margin-bottom: 12rpx;
&:last-child {
margin-bottom: 0;
}
.requirement-icon {
font-size: 20rpx;
color: #4CAF50;
font-weight: bold;
}
.requirement-text {
font-size: 24rpx;
color: #666666;
flex: 1;
}
}
}
}
/* 联系信息 */
.contact-info {
.contact-details {
.contact-item {
display: flex;
align-items: center;
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
.contact-label {
font-size: 22rpx;
color: #999999;
min-width: 100rpx;
}
.contact-value {
font-size: 24rpx;
color: #333333;
flex: 1;
}
.contact-type-badge {
background: rgba(255, 138, 128, 0.1);
border-radius: 12rpx;
padding: 4rpx 12rpx;
font-size: 18rpx;
color: #FF8A80;
margin-left: 12rpx;
}
.contact-action {
background: #FF8A80;
border-radius: 12rpx;
padding: 8rpx 16rpx;
margin-left: 12rpx;
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
}
.action-text {
font-size: 20rpx;
color: white;
font-weight: 500;
}
}
}
}
}
/* 发布信息 */
.publish-info {
.publish-item {
display: flex;
align-items: center;
.publish-label {
font-size: 22rpx;
color: #999999;
margin-right: 12rpx;
}
.publish-value {
font-size: 22rpx;
color: #666666;
}
}
}
/* 底部操作按钮 */
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20rpx);
padding: 20rpx 32rpx;
border-top: 1rpx solid rgba(255, 255, 255, 0.3);
display: flex;
gap: 16rpx;
.action-btn {
flex: 1;
text-align: center;
padding: 20rpx;
border-radius: 16rpx;
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
}
&.secondary {
background: rgba(255, 138, 128, 0.1);
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
flex: 0 0 120rpx;
.btn-icon {
font-size: 20rpx;
}
.btn-text {
font-size: 22rpx;
color: #FF8A80;
font-weight: 500;
}
}
&.primary {
background: linear-gradient(135deg, #FF8A80, #FFB6C1);
.btn-text {
font-size: 28rpx;
color: white;
font-weight: bold;
}
}
&.disabled {
background: #CCCCCC;
.btn-text {
font-size: 28rpx;
color: white;
font-weight: bold;
}
}
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -38,7 +38,7 @@
</u-card>
</u-form>
<view class="submit-section">
<view class="submit-section">
<u-button type="primary" text="保存记录" @click="submitForm" :loading="loading"></u-button>
</view>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,626 @@
<template>
<view class="record-detail-container">
<!-- 记录头部信息 -->
<view class="record-header">
<view class="header-info">
<view class="category-info">
<text class="category-icon">{{ getCategoryIcon() }}</text>
<text class="category-name">{{ getCategoryName() }}</text>
</view>
<text class="record-time">{{ formatTime(record.recordTime) }}</text>
</view>
<view class="share-info">
<text class="share-icon">{{ getShareIcon() }}</text>
</view>
</view>
<!-- 记录内容 -->
<view class="record-content">
<text class="record-title">{{ record.title }}</text>
<text class="record-text">{{ record.content }}</text>
</view>
<!-- 标签 -->
<view class="record-tags" v-if="record.tags && record.tags.length > 0">
<view class="tags-title">
<text class="title-text">🏷 标签</text>
</view>
<view class="tags-list">
<view
class="tag-item"
v-for="tag in record.tags"
:key="tag"
>
<text class="tag-text"># {{ tag }}</text>
</view>
</view>
</view>
<!-- 图片展示 -->
<view class="record-photos" v-if="record.photos && record.photos.length > 0">
<view class="photos-title">
<text class="title-text">📷 图片</text>
</view>
<view class="photos-grid">
<view
class="photo-item"
v-for="(photo, index) in record.photos"
:key="index"
@click="previewImage(photo, index)"
>
<image class="photo-image" :src="photo" mode="aspectFill" />
</view>
</view>
</view>
<!-- 详细信息 -->
<view class="record-details" v-if="hasDetails()">
<view class="details-title">
<text class="title-text">📋 详细信息</text>
</view>
<view class="details-list">
<view class="detail-item" v-if="record.location">
<text class="detail-label">📍 地点</text>
<text class="detail-value">{{ record.location }}</text>
</view>
<view class="detail-item" v-if="record.weather">
<text class="detail-label">🌤 天气</text>
<text class="detail-value">{{ record.weather }}</text>
</view>
<view class="detail-item" v-if="record.mood">
<text class="detail-label">😊 心情</text>
<text class="detail-value">{{ getMoodText() }}</text>
</view>
<view class="detail-item" v-if="record.amount">
<text class="detail-label">💰 金额</text>
<text class="detail-value">¥{{ record.amount }}</text>
</view>
<view class="detail-item" v-if="record.store">
<text class="detail-label">🏪 商店</text>
<text class="detail-value">{{ record.store }}</text>
</view>
</view>
</view>
<!-- 社交数据 -->
<view class="social-section">
<view class="social-stats">
<view class="stat-item" @click="toggleLike">
<text class="stat-icon" :class="{ liked: isLiked }">{{ isLiked ? '❤️' : '🤍' }}</text>
<text class="stat-count">{{ socialData.likes }}</text>
<text class="stat-label">喜欢</text>
</view>
<view class="stat-item">
<text class="stat-icon">👁</text>
<text class="stat-count">{{ socialData.views }}</text>
<text class="stat-label">浏览</text>
</view>
<view class="stat-item">
<text class="stat-icon">💬</text>
<text class="stat-count">{{ socialData.comments.length }}</text>
<text class="stat-label">评论</text>
</view>
</view>
</view>
<!-- 评论区域 -->
<view class="comments-section">
<view class="comments-header">
<text class="comments-title">💬 评论 ({{ socialData.comments.length }})</text>
</view>
<!-- 评论列表 -->
<view class="comments-list" v-if="socialData.comments.length > 0">
<view
class="comment-item"
v-for="comment in socialData.comments"
:key="comment.id"
>
<view class="comment-avatar">
<text class="avatar-text">{{ comment.user.charAt(0) }}</text>
</view>
<view class="comment-content">
<view class="comment-header">
<text class="comment-user">{{ comment.user }}</text>
<text class="comment-time">{{ comment.time }}</text>
</view>
<text class="comment-text">{{ comment.content }}</text>
</view>
</view>
</view>
<!-- 空评论状态 -->
<view class="empty-comments" v-else>
<text class="empty-text">还没有评论来说点什么吧~</text>
</view>
<!-- 添加评论 -->
<view class="add-comment">
<view class="comment-input-wrapper">
<input
class="comment-input"
v-model="newComment"
placeholder="写下你的评论..."
@confirm="addComment"
/>
<view class="comment-send" @click="addComment" v-if="newComment.trim()">
<text class="send-text">发送</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import recordManager from '@/utils/recordManager.js'
export default {
data() {
return {
recordId: '',
petId: '',
petName: '',
record: {},
socialData: {
likes: 0,
views: 0,
comments: [],
likedBy: []
},
isLiked: false,
newComment: ''
}
},
onLoad(options) {
this.recordId = options.recordId || ''
this.petId = options.petId || ''
this.petName = options.petName || '宠物'
this.loadRecordDetail()
},
methods: {
loadRecordDetail() {
//
const records = recordManager.getRecords(this.petId)
this.record = records.find(r => r.id == this.recordId) || {}
//
this.socialData = recordManager.getSocialData(this.recordId)
this.isLiked = this.socialData.likedBy.includes('current_user')
},
getCategoryIcon() {
return recordManager.getCategoryInfo(this.record.category, this.record.subCategory).icon
},
getCategoryName() {
return recordManager.getCategoryInfo(this.record.category, this.record.subCategory).name
},
getShareIcon() {
const icons = {
'public': '🌍',
'family': '👨‍👩‍👧‍👦',
'private': '🔒'
}
return icons[this.record.shareLevel] || '👨‍👩‍👧‍👦'
},
getMoodText() {
const moods = {
'happy': '开心',
'excited': '兴奋',
'calm': '平静',
'proud': '自豪',
'sad': '难过',
'worried': '担心'
}
return moods[this.record.mood] || this.record.mood
},
formatTime(timeStr) {
if (!timeStr) return ''
const time = new Date(timeStr)
const now = new Date()
const diff = now - time
const minutes = Math.floor(diff / (1000 * 60))
const hours = Math.floor(diff / (1000 * 60 * 60))
const days = Math.floor(diff / (1000 * 60 * 60 * 24))
if (minutes < 60) {
return minutes <= 0 ? '刚刚' : `${minutes}分钟前`
} else if (hours < 24) {
return `${hours}小时前`
} else if (days < 7) {
return `${days}天前`
} else {
return `${time.getFullYear()}-${(time.getMonth() + 1).toString().padStart(2, '0')}-${time.getDate().toString().padStart(2, '0')}`
}
},
hasDetails() {
return this.record.location || this.record.weather || this.record.mood || this.record.amount || this.record.store
},
previewImage(photo, index) {
uni.previewImage({
urls: this.record.photos,
current: index
})
},
toggleLike() {
const newSocialData = recordManager.likeRecord(this.recordId, 'current_user')
if (newSocialData) {
this.socialData = newSocialData
this.isLiked = this.socialData.likedBy.includes('current_user')
}
},
addComment() {
if (!this.newComment.trim()) return
const comment = recordManager.addComment(this.recordId, this.newComment.trim(), '我')
if (comment) {
this.socialData.comments.unshift(comment)
this.newComment = ''
uni.showToast({
title: '评论成功',
icon: 'success'
})
}
}
}
}
</script>
<style lang="scss" scoped>
.record-detail-container {
background: linear-gradient(135deg, #FF8A80 0%, #FFB6C1 25%, #FECFEF 50%, #F8BBD9 100%);
min-height: 100vh;
padding: 20rpx;
}
/* 记录头部 */
.record-header {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20rpx);
border-radius: 20rpx;
padding: 24rpx;
margin-bottom: 16rpx;
box-shadow: 0 4rpx 16rpx rgba(255, 138, 128, 0.1);
border: 1rpx solid rgba(255, 255, 255, 0.3);
display: flex;
justify-content: space-between;
align-items: center;
.header-info {
flex: 1;
.category-info {
display: flex;
align-items: center;
gap: 8rpx;
margin-bottom: 8rpx;
.category-icon {
font-size: 24rpx;
}
.category-name {
font-size: 24rpx;
font-weight: 500;
color: #666666;
}
}
.record-time {
font-size: 20rpx;
color: #999999;
}
}
.share-info {
.share-icon {
font-size: 20rpx;
}
}
}
/* 记录内容 */
.record-content {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20rpx);
border-radius: 20rpx;
padding: 24rpx;
margin-bottom: 16rpx;
box-shadow: 0 4rpx 16rpx rgba(255, 138, 128, 0.1);
border: 1rpx solid rgba(255, 255, 255, 0.3);
.record-title {
font-size: 32rpx;
font-weight: bold;
color: #333333;
margin-bottom: 16rpx;
display: block;
}
.record-text {
font-size: 26rpx;
color: #666666;
line-height: 1.6;
display: block;
}
}
/* 标签、图片、详细信息通用样式 */
.record-tags,
.record-photos,
.record-details {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20rpx);
border-radius: 20rpx;
padding: 24rpx;
margin-bottom: 16rpx;
box-shadow: 0 4rpx 16rpx rgba(255, 138, 128, 0.1);
border: 1rpx solid rgba(255, 255, 255, 0.3);
.tags-title,
.photos-title,
.details-title {
margin-bottom: 16rpx;
.title-text {
font-size: 26rpx;
font-weight: bold;
color: #333333;
}
}
}
/* 标签列表 */
.tags-list {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
.tag-item {
background: rgba(255, 138, 128, 0.1);
border-radius: 16rpx;
padding: 8rpx 16rpx;
.tag-text {
font-size: 22rpx;
color: #FF8A80;
}
}
}
/* 图片网格 */
.photos-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12rpx;
.photo-item {
aspect-ratio: 1;
border-radius: 12rpx;
overflow: hidden;
.photo-image {
width: 100%;
height: 100%;
}
}
}
/* 详细信息列表 */
.details-list {
.detail-item {
display: flex;
margin-bottom: 12rpx;
&:last-child {
margin-bottom: 0;
}
.detail-label {
font-size: 22rpx;
color: #999999;
min-width: 120rpx;
}
.detail-value {
font-size: 22rpx;
color: #666666;
flex: 1;
}
}
}
/* 社交数据 */
.social-section {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20rpx);
border-radius: 20rpx;
padding: 24rpx;
margin-bottom: 16rpx;
box-shadow: 0 4rpx 16rpx rgba(255, 138, 128, 0.1);
border: 1rpx solid rgba(255, 255, 255, 0.3);
.social-stats {
display: flex;
justify-content: space-around;
.stat-item {
text-align: center;
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
}
.stat-icon {
font-size: 32rpx;
margin-bottom: 8rpx;
display: block;
&.liked {
animation: heartbeat 0.6s ease-in-out;
}
}
.stat-count {
font-size: 24rpx;
font-weight: bold;
color: #333333;
margin-bottom: 4rpx;
display: block;
}
.stat-label {
font-size: 20rpx;
color: #999999;
display: block;
}
}
}
}
@keyframes heartbeat {
0% { transform: scale(1); }
50% { transform: scale(1.2); }
100% { transform: scale(1); }
}
/* 评论区域 */
.comments-section {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20rpx);
border-radius: 20rpx;
padding: 24rpx;
margin-bottom: 40rpx;
box-shadow: 0 4rpx 16rpx rgba(255, 138, 128, 0.1);
border: 1rpx solid rgba(255, 255, 255, 0.3);
.comments-header {
margin-bottom: 20rpx;
.comments-title {
font-size: 26rpx;
font-weight: bold;
color: #333333;
}
}
.comments-list {
margin-bottom: 20rpx;
.comment-item {
display: flex;
gap: 16rpx;
margin-bottom: 20rpx;
&:last-child {
margin-bottom: 0;
}
.comment-avatar {
width: 64rpx;
height: 64rpx;
background: linear-gradient(135deg, #FF8A80, #FFB6C1);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
.avatar-text {
font-size: 24rpx;
color: white;
font-weight: bold;
}
}
.comment-content {
flex: 1;
.comment-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8rpx;
.comment-user {
font-size: 22rpx;
font-weight: 500;
color: #333333;
}
.comment-time {
font-size: 18rpx;
color: #999999;
}
}
.comment-text {
font-size: 24rpx;
color: #666666;
line-height: 1.5;
}
}
}
}
.empty-comments {
text-align: center;
padding: 40rpx 0;
.empty-text {
font-size: 24rpx;
color: #CCCCCC;
}
}
.add-comment {
.comment-input-wrapper {
position: relative;
background: rgba(255, 138, 128, 0.05);
border-radius: 20rpx;
padding: 0 20rpx;
.comment-input {
width: 100%;
height: 72rpx;
font-size: 24rpx;
color: #333333;
background: transparent;
border: none;
outline: none;
padding-right: 80rpx;
}
.comment-send {
position: absolute;
right: 20rpx;
top: 50%;
transform: translateY(-50%);
background: #FF8A80;
border-radius: 16rpx;
padding: 8rpx 16rpx;
.send-text {
font-size: 20rpx;
color: white;
font-weight: 500;
}
}
}
}
}
</style>

559
utils/adoptionManager.js Normal file
View File

@ -0,0 +1,559 @@
/**
* 宠物领养数据管理工具类
* 负责领养宠物数据的存储筛选搜索等管理
*/
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: ['/static/dog1.jpg', '/static/dog2.jpg'],
description: '小白是一只活泼可爱的金毛幼犬,非常聪明,喜欢和小朋友玩耍。',
personality: ['活泼', '聪明', '友善'],
health: '健康良好,疫苗接种中',
location: {
province: 'shanghai',
city: 'shanghai',
district: 'xuhui',
address: '徐汇区某某宠物医院'
},
status: 'available',
requirements: [
'有足够空间',
'每天遛狗',
'有耐心训练',
'定期体检'
],
contact: {
name: '张医生',
phone: '139****5678',
wechat: 'dr_zhang',
type: 'individual'
},
publishTime: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000).toISOString(),
views: 89,
favorites: 12
})
testData.push({
id: Date.now() + 3,
name: '花花',
type: 'cat',
breed: 'ragdoll',
age: 3,
gender: 'female',
photos: ['/static/cat4.jpg'],
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(),
views: 234,
favorites: 45
})
testData.push({
id: Date.now() + 4,
name: '豆豆',
type: 'rabbit',
breed: 'holland-lop',
age: 1,
gender: 'male',
photos: ['/static/rabbit1.jpg', '/static/rabbit2.jpg'],
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(),
views: 67,
favorites: 8
})
testData.push({
id: Date.now() + 5,
name: '黑黑',
type: 'cat',
breed: 'mixed',
age: 4,
gender: 'male',
photos: ['/static/cat5.jpg'],
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(),
views: 178,
favorites: 34
})
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: '❓'
}
}
/**
* 增加浏览量
* @param {string} petId 宠物ID
*/
incrementViews(petId) {
try {
const pets = this.getAdoptionPets()
const petIndex = pets.findIndex(pet => pet.id == petId)
if (petIndex !== -1) {
pets[petIndex].views += 1
uni.setStorageSync(this.storageKey, pets)
}
} catch (error) {
console.error('增加浏览量失败:', error)
}
}
/**
* 切换收藏状态
* @param {string} petId 宠物ID
* @param {boolean} isFavorite 是否收藏
*/
toggleFavorite(petId, isFavorite) {
try {
const pets = this.getAdoptionPets()
const petIndex = pets.findIndex(pet => pet.id == petId)
if (petIndex !== -1) {
if (isFavorite) {
pets[petIndex].favorites += 1
} else {
pets[petIndex].favorites = Math.max(0, pets[petIndex].favorites - 1)
}
uni.setStorageSync(this.storageKey, pets)
}
} catch (error) {
console.error('切换收藏状态失败:', error)
}
}
}
export default new AdoptionManager()

478
utils/recordManager.js Normal file
View File

@ -0,0 +1,478 @@
/**
* 宠物记录管理工具类
* 负责记录数据的存储分类社交功能等管理
*/
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()