pet/pages/adoption/my-adoption.vue

903 lines
19 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="my-adoption-container">
<!-- 统计概览卡片 -->
<view class="stats-overview-card">
<view class="overview-header">
<view class="header-info">
<text class="header-title">我的领养</text>
<text class="header-desc">管理我的领养信息和申请记录</text>
</view>
<view class="header-icon">
<text class="icon-text">🏠</text>
</view>
</view>
<view class="stats-grid">
<view class="stat-item" @click="navigateTo('/pages/adoption/my-published')">
<view class="stat-number">{{ publishedCount }}</view>
<view class="stat-label">已发布</view>
<view class="stat-icon">📝</view>
</view>
<view class="stat-item" @click="navigateTo('/pages/adoption/my-applications')">
<view class="stat-number">{{ applicationCount }}</view>
<view class="stat-label">已申请</view>
<view class="stat-icon">📋</view>
</view>
<view class="stat-item" @click="showSuccessStories">
<view class="stat-number">{{ successCount }}</view>
<view class="stat-label">成功领养</view>
<view class="stat-icon"></view>
</view>
</view>
</view>
<!-- 快速操作卡片 -->
<view class="quick-actions-card">
<view class="card-header">
<text class="card-title">快速操作</text>
</view>
<view class="actions-grid">
<view class="action-item" @click="navigateTo('/pages/adoption/publish')">
<view class="action-icon"></view>
<text class="action-text">发布领养</text>
<text class="action-desc">发布新的领养信息</text>
</view>
<view class="action-item" @click="navigateTo('/pages/adoption/adoption')">
<view class="action-icon">🔍</view>
<text class="action-text">浏览领养</text>
<text class="action-desc">查看可领养宠物</text>
</view>
</view>
</view>
<!-- 最近发布卡片 -->
<view class="recent-published-card" v-if="recentPublished.length > 0">
<view class="card-header">
<text class="card-title">最近发布</text>
<view class="header-action" @click="navigateTo('/pages/adoption/my-published')">
<text class="action-text">查看全部</text>
<text class="action-arrow">→</text>
</view>
</view>
<view class="published-list">
<view
class="published-item"
v-for="item in recentPublished.slice(0, 3)"
:key="item.id"
@click="viewPublishedDetail(item)"
>
<view class="item-image">
<image class="pet-image" :src="item.photos[0]" mode="aspectFill" />
<view class="status-badge" :class="item.status">
<text class="status-text">{{ getStatusText(item.status) }}</text>
</view>
</view>
<view class="item-info">
<view class="pet-name">{{ item.name }}</view>
<view class="pet-meta">
<text class="meta-text">{{ item.breed }} · {{ item.gender }}</text>
</view>
<view class="publish-time">
<text class="time-text">{{ formatTime(item.publishTime) }}</text>
</view>
</view>
<view class="item-stats">
<view class="stat-row">
<text class="stat-label">申请</text>
<text class="stat-value">{{ item.applicationCount || 0 }}</text>
</view>
<view class="stat-row">
<text class="stat-label">浏览</text>
<text class="stat-value">{{ item.viewCount || 0 }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 最近申请卡片 -->
<view class="recent-applications-card" v-if="recentApplications.length > 0">
<view class="card-header">
<text class="card-title">最近申请</text>
<view class="header-action" @click="navigateTo('/pages/adoption/my-applications')">
<text class="action-text">查看全部</text>
<text class="action-arrow">→</text>
</view>
</view>
<view class="applications-list">
<view
class="application-item"
v-for="item in recentApplications.slice(0, 3)"
:key="item.id"
@click="viewApplicationDetail(item)"
>
<view class="item-image">
<image class="pet-image" :src="item.petPhoto" mode="aspectFill" />
</view>
<view class="item-info">
<view class="pet-name">{{ item.petName }}</view>
<view class="pet-meta">
<text class="meta-text">{{ item.petBreed }} · {{ item.petGender }}</text>
</view>
<view class="application-time">
<text class="time-text">{{ formatTime(item.applicationTime) }}</text>
</view>
</view>
<view class="item-status">
<view class="status-badge" :class="item.status">
<text class="status-text">{{ getApplicationStatusText(item.status) }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-if="publishedCount === 0 && applicationCount === 0">
<view class="empty-icon">🏠</view>
<view class="empty-text">还没有领养相关记录</view>
<view class="empty-desc">发布领养信息或申请领养可爱的宠物</view>
<view class="empty-actions">
<view class="empty-action primary" @click="navigateTo('/pages/adoption/publish')">
<text class="action-text">发布领养</text>
</view>
<view class="empty-action" @click="navigateTo('/pages/adoption/adoption')">
<text class="action-text">浏览领养</text>
</view>
</view>
</view>
<!-- 帮助提示卡片 -->
<view class="help-card">
<view class="help-header">
<text class="help-icon">💡</text>
<text class="help-title">领养小贴士</text>
</view>
<view class="help-content">
<view class="help-item">
<text class="help-text">• 发布领养信息时请提供详细的宠物信息和照片</text>
</view>
<view class="help-item">
<text class="help-text">• 申请领养时请如实填写个人信息和养宠经验</text>
</view>
<view class="help-item">
<text class="help-text"> 保持良好的沟通为宠物找到最合适的家庭</text>
</view>
</view>
</view>
</view>
</template>
<script>
import { reactive, ref, onMounted, computed } from 'vue'
export default {
name: 'MyAdoptionPage',
setup() {
// 响应式数据
const publishedCount = ref(0)
const applicationCount = ref(0)
const successCount = ref(0)
const recentPublished = ref([])
const recentApplications = ref([])
// 生命周期
onMounted(() => {
loadAdoptionData()
})
// 方法定义
const loadAdoptionData = () => {
try {
// 加载发布的领养信息
const publishedData = uni.getStorageSync('myPublishedAdoptions') || []
publishedCount.value = publishedData.length
recentPublished.value = publishedData.sort((a, b) =>
new Date(b.publishTime) - new Date(a.publishTime)
)
// 加载申请记录
const applicationData = uni.getStorageSync('myAdoptionApplications') || []
applicationCount.value = applicationData.length
recentApplications.value = applicationData.sort((a, b) =>
new Date(b.applicationTime) - new Date(a.applicationTime)
)
// 计算成功领养数量
const successfulAdoptions = publishedData.filter(item => item.status === 'completed').length +
applicationData.filter(item => item.status === 'approved').length
successCount.value = successfulAdoptions
// 如果没有数据,创建一些模拟数据用于展示
if (publishedData.length === 0 && applicationData.length === 0) {
createMockData()
}
} catch (error) {
console.error('加载领养数据失败:', error)
}
}
const createMockData = () => {
// 创建模拟发布数据
const mockPublished = [
{
id: 'pub_1',
name: '小橘',
breed: '橘猫',
gender: '公',
photos: ['/static/mock-cat1.jpg'],
status: 'active',
publishTime: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString(),
applicationCount: 3,
viewCount: 25
}
]
// 创建模拟申请数据
const mockApplications = [
{
id: 'app_1',
petName: '小白',
petBreed: '英短',
petGender: '母',
petPhoto: '/static/mock-cat2.jpg',
status: 'pending',
applicationTime: new Date(Date.now() - 1 * 24 * 60 * 60 * 1000).toISOString()
}
]
uni.setStorageSync('myPublishedAdoptions', mockPublished)
uni.setStorageSync('myAdoptionApplications', mockApplications)
publishedCount.value = mockPublished.length
applicationCount.value = mockApplications.length
recentPublished.value = mockPublished
recentApplications.value = mockApplications
}
const navigateTo = (url) => {
uni.navigateTo({
url,
fail: () => {
uni.showToast({
title: '页面开发中',
icon: 'none'
})
}
})
}
const showSuccessStories = () => {
if (successCount.value > 0) {
uni.showModal({
title: '成功领养',
content: `恭喜您!已成功完成 ${successCount.value} 次领养,为宠物们找到了温暖的家。`,
showCancel: false
})
} else {
uni.showToast({
title: '暂无成功记录',
icon: 'none'
})
}
}
const viewPublishedDetail = (item) => {
uni.navigateTo({
url: `/pages/adoption/adoption-detail?id=${item.id}&from=published`,
fail: () => {
uni.showToast({
title: '页面开发中',
icon: 'none'
})
}
})
}
const viewApplicationDetail = (item) => {
uni.showModal({
title: item.petName,
content: `申请状态:${getApplicationStatusText(item.status)}\n申请时间${formatTime(item.applicationTime)}`,
showCancel: false
})
}
const getStatusText = (status) => {
const statusMap = {
'active': '招募中',
'paused': '已暂停',
'completed': '已完成',
'cancelled': '已取消'
}
return statusMap[status] || '未知'
}
const getApplicationStatusText = (status) => {
const statusMap = {
'pending': '待审核',
'approved': '已通过',
'rejected': '已拒绝',
'cancelled': '已取消'
}
return statusMap[status] || '未知'
}
const formatTime = (timeString) => {
if (!timeString) return ''
const date = new Date(timeString)
const now = new Date()
const diffTime = now - date
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24))
if (diffDays === 0) {
return '今天'
} else if (diffDays === 1) {
return '昨天'
} else if (diffDays < 7) {
return `${diffDays}天前`
} else {
return date.toLocaleDateString('zh-CN')
}
}
return {
publishedCount,
applicationCount,
successCount,
recentPublished,
recentApplications,
navigateTo,
showSuccessStories,
viewPublishedDetail,
viewApplicationDetail,
getStatusText,
getApplicationStatusText,
formatTime
}
}
}
</script>
<style lang="scss" scoped>
.my-adoption-container {
background: linear-gradient(135deg, #FF8A80 0%, #FFB6C1 25%, #FECFEF 50%, #F8BBD9 100%);
min-height: 100vh;
padding-bottom: 40rpx;
}
/* 通用卡片样式 */
.stats-overview-card,
.quick-actions-card,
.recent-published-card,
.recent-applications-card,
.help-card {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20rpx);
margin: 0 30rpx 24rpx 30rpx;
border-radius: 24rpx;
padding: 32rpx;
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
border: 1rpx solid rgba(255, 255, 255, 0.3);
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
.card-title {
font-size: 32rpx;
font-weight: 600;
color: #333333;
}
.header-action {
display: flex;
align-items: center;
.action-text {
font-size: 24rpx;
color: #FF8A80;
margin-right: 8rpx;
}
.action-arrow {
font-size: 24rpx;
color: #FF8A80;
}
}
}
}
/* 统计概览卡片 */
.stats-overview-card {
.overview-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 32rpx;
.header-info {
flex: 1;
.header-title {
display: block;
font-size: 36rpx;
font-weight: 700;
color: #333333;
margin-bottom: 8rpx;
}
.header-desc {
font-size: 24rpx;
color: #666666;
}
}
.header-icon {
.icon-text {
font-size: 60rpx;
}
}
}
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 24rpx;
.stat-item {
text-align: center;
padding: 24rpx 16rpx;
border-radius: 16rpx;
background: rgba(255, 138, 128, 0.05);
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
background: rgba(255, 138, 128, 0.1);
}
.stat-number {
font-size: 40rpx;
font-weight: 700;
color: #FF8A80;
margin-bottom: 8rpx;
}
.stat-label {
font-size: 22rpx;
color: #666666;
margin-bottom: 8rpx;
}
.stat-icon {
font-size: 28rpx;
}
}
}
}
/* 快速操作卡片 */
.quick-actions-card {
.actions-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20rpx;
.action-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 32rpx 24rpx;
border-radius: 20rpx;
background: rgba(255, 138, 128, 0.05);
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
background: rgba(255, 138, 128, 0.1);
}
.action-icon {
font-size: 48rpx;
margin-bottom: 16rpx;
}
.action-text {
font-size: 28rpx;
font-weight: 600;
color: #333333;
margin-bottom: 8rpx;
}
.action-desc {
font-size: 22rpx;
color: #666666;
text-align: center;
line-height: 1.4;
}
}
}
}
/* 最近发布卡片 */
.recent-published-card {
.published-list {
.published-item {
display: flex;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid rgba(255, 255, 255, 0.3);
transition: all 0.3s ease;
&:last-child {
border-bottom: none;
}
&:active {
background: rgba(255, 138, 128, 0.05);
border-radius: 16rpx;
}
.item-image {
position: relative;
margin-right: 20rpx;
.pet-image {
width: 100rpx;
height: 100rpx;
border-radius: 16rpx;
}
.status-badge {
position: absolute;
top: -8rpx;
right: -8rpx;
padding: 4rpx 8rpx;
border-radius: 12rpx;
font-size: 18rpx;
&.active {
background: #4CAF50;
color: white;
}
&.paused {
background: #FF9800;
color: white;
}
&.completed {
background: #2196F3;
color: white;
}
&.cancelled {
background: #F44336;
color: white;
}
.status-text {
font-size: 18rpx;
}
}
}
.item-info {
flex: 1;
.pet-name {
font-size: 30rpx;
font-weight: 600;
color: #333333;
margin-bottom: 8rpx;
}
.pet-meta {
margin-bottom: 8rpx;
.meta-text {
font-size: 22rpx;
color: #666666;
}
}
.publish-time {
.time-text {
font-size: 20rpx;
color: #999999;
}
}
}
.item-stats {
text-align: right;
.stat-row {
display: flex;
align-items: center;
justify-content: flex-end;
margin-bottom: 8rpx;
&:last-child {
margin-bottom: 0;
}
.stat-label {
font-size: 20rpx;
color: #999999;
margin-right: 8rpx;
}
.stat-value {
font-size: 24rpx;
font-weight: 600;
color: #FF8A80;
}
}
}
}
}
}
/* 最近申请卡片 */
.recent-applications-card {
.applications-list {
.application-item {
display: flex;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid rgba(255, 255, 255, 0.3);
transition: all 0.3s ease;
&:last-child {
border-bottom: none;
}
&:active {
background: rgba(255, 138, 128, 0.05);
border-radius: 16rpx;
}
.item-image {
margin-right: 20rpx;
.pet-image {
width: 100rpx;
height: 100rpx;
border-radius: 16rpx;
}
}
.item-info {
flex: 1;
.pet-name {
font-size: 30rpx;
font-weight: 600;
color: #333333;
margin-bottom: 8rpx;
}
.pet-meta {
margin-bottom: 8rpx;
.meta-text {
font-size: 22rpx;
color: #666666;
}
}
.application-time {
.time-text {
font-size: 20rpx;
color: #999999;
}
}
}
.item-status {
.status-badge {
padding: 8rpx 16rpx;
border-radius: 16rpx;
font-size: 20rpx;
&.pending {
background: #FFF3E0;
color: #FF9800;
border: 1rpx solid #FFE0B2;
}
&.approved {
background: #E8F5E8;
color: #4CAF50;
border: 1rpx solid #C8E6C9;
}
&.rejected {
background: #FFEBEE;
color: #F44336;
border: 1rpx solid #FFCDD2;
}
&.cancelled {
background: #F5F5F5;
color: #757575;
border: 1rpx solid #E0E0E0;
}
.status-text {
font-size: 20rpx;
}
}
}
}
}
}
/* 空状态 */
.empty-state {
text-align: center;
padding: 80rpx 40rpx;
margin: 0 30rpx;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20rpx);
border-radius: 24rpx;
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
border: 1rpx solid rgba(255, 255, 255, 0.3);
.empty-icon {
font-size: 80rpx;
margin-bottom: 24rpx;
opacity: 0.5;
}
.empty-text {
font-size: 32rpx;
font-weight: 600;
color: #333333;
margin-bottom: 12rpx;
}
.empty-desc {
font-size: 24rpx;
color: #666666;
margin-bottom: 40rpx;
line-height: 1.5;
}
.empty-actions {
display: flex;
gap: 16rpx;
justify-content: center;
.empty-action {
padding: 16rpx 32rpx;
border-radius: 20rpx;
border: 2rpx solid #FF8A80;
transition: all 0.3s ease;
&.primary {
background: linear-gradient(135deg, #FF8A80, #FFB6C1);
.action-text {
color: white;
}
}
&:not(.primary) {
background: transparent;
.action-text {
color: #FF8A80;
}
}
&:active {
transform: scale(0.95);
}
.action-text {
font-size: 26rpx;
font-weight: 500;
}
}
}
}
/* 帮助提示卡片 */
.help-card {
.help-header {
display: flex;
align-items: center;
margin-bottom: 20rpx;
.help-icon {
font-size: 32rpx;
margin-right: 12rpx;
}
.help-title {
font-size: 28rpx;
font-weight: 600;
color: #333333;
}
}
.help-content {
.help-item {
margin-bottom: 12rpx;
&:last-child {
margin-bottom: 0;
}
.help-text {
font-size: 24rpx;
color: #666666;
line-height: 1.6;
}
}
}
}
/* 响应式设计 */
@media (max-width: 375px) {
.my-adoption-container {
.stats-overview-card,
.quick-actions-card,
.recent-published-card,
.recent-applications-card,
.help-card,
.empty-state {
margin: 0 20rpx 20rpx 20rpx;
padding: 24rpx;
}
.stats-grid {
gap: 16rpx;
.stat-item {
padding: 20rpx 12rpx;
.stat-number {
font-size: 36rpx;
}
}
}
.actions-grid {
gap: 16rpx;
.action-item {
padding: 24rpx 16rpx;
}
}
}
}
/* 动画效果 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.my-adoption-container > view {
animation: fadeIn 0.5s ease-out;
}
.my-adoption-container > view:nth-child(1) { animation-delay: 0.1s; }
.my-adoption-container > view:nth-child(2) { animation-delay: 0.2s; }
.my-adoption-container > view:nth-child(3) { animation-delay: 0.3s; }
.my-adoption-container > view:nth-child(4) { animation-delay: 0.4s; }
.my-adoption-container > view:nth-child(5) { animation-delay: 0.5s; }
.my-adoption-container > view:nth-child(6) { animation-delay: 0.6s; }
</style>