684 lines
15 KiB
Vue
684 lines
15 KiB
Vue
<template>
|
|
<view class="record-detail-container page-container-with-bg">
|
|
<!-- 记录头部信息 -->
|
|
<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>
|
|
// 记录管理器 - 从 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() {
|
|
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>
|