Files
msh-system/msh_single_uniapp/pages/tool/checkin-detail.vue

509 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
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="checkin-detail-page">
<!-- 内容区域 -->
<scroll-view class="content-scroll" scroll-y>
<!-- 图片轮播区域 -->
<view class="image-carousel">
<swiper
class="swiper"
:indicator-dots="true"
:autoplay="false"
:current="currentImageIndex"
@change="onImageChange"
indicator-color="rgba(255,255,255,0.5)"
indicator-active-color="#ffffff"
>
<swiper-item v-for="(image, index) in checkinData.images" :key="index">
<image class="carousel-image" :src="image" mode="aspectFill"></image>
</swiper-item>
</swiper>
<!-- 餐次标签 -->
<view class="meal-tag">
<text class="meal-icon">{{ getMealIcon(checkinData.mealType) }}</text>
<text class="meal-text">{{ getMealText(checkinData.mealType) }}</text>
</view>
<!-- 图片页码 -->
<view class="image-counter">
{{ currentImageIndex + 1 }} / {{ checkinData.images.length }}
</view>
</view>
<!-- 视频播放区域 -->
<view class="video-section" v-if="checkinData.videoUrl">
<view class="section-header">
<text class="header-icon">🎬</text>
<text class="header-title">打卡视频</text>
</view>
<video
class="checkin-video"
:src="checkinData.videoUrl"
controls
:show-center-play-btn="true"
:enable-progress-gesture="true"
object-fit="contain"
:poster="checkinData.images && checkinData.images.length > 0 ? checkinData.images[0] : ''"
></video>
</view>
<!-- 视频生成中状态 -->
<view class="video-generating" v-else-if="checkinData.enableAIVideo && checkinData.taskId">
<text class="generating-icon"></text>
<text class="generating-text">视频生成中请稍后刷新查看...</text>
</view>
<!-- 帖子内容区域 -->
<view class="post-content-section">
<!-- 作者信息 -->
<view class="author-section">
<view class="author-info">
<view class="author-avatar">
<image :src="userInfo.avatar" mode="aspectFill" class="avatar-img"></image>
</view>
<view class="author-details">
<view class="author-name">{{ userInfo.nickname }}</view>
<view class="post-time">{{ checkinData.createTime }}</view>
</view>
</view>
<view class="points-badge" v-if="checkinData.points > 0">
<text>+{{ checkinData.points }}积分</text>
</view>
</view>
<!-- 帖子描述 -->
<view class="post-description">
<text>{{ checkinData.notes || '暂无描述' }}</text>
</view>
</view>
<!-- 营养统计卡片 -->
<view class="nutrition-stats-card" v-if="checkinData.aiAnalysis">
<view class="stats-header">
<view class="stats-title">
<text class="title-icon">📊</text>
<text class="title-text">AI 营养分析</text>
</view>
</view>
<view class="ai-analysis-content">
<text>{{ checkinData.aiAnalysis }}</text>
</view>
</view>
<!-- 分享到社区按钮 -->
<view class="copy-checkin-btn" @click="handleCopyCheckin">
<text class="btn-icon">🎬</text>
<text class="btn-text">分享到社区</text>
</view>
<!-- 底部安全距离 -->
<view class="safe-bottom"></view>
</scroll-view>
</view>
</template>
<script>
import { getCheckinDetail } from '@/api/tool.js';
import { mapGetters } from 'vuex';
export default {
computed: {
...mapGetters(['userInfo'])
},
data() {
return {
currentImageIndex: 0,
checkinData: {
id: '',
images: [],
mealType: '',
createTime: '',
notes: '',
points: 0,
aiAnalysis: '',
videoUrl: '',
taskId: '',
enableAIVideo: false,
videoStatus: 0
}
}
},
onLoad(options) {
if (options.id) {
this.loadCheckinDetail(options.id);
} else if (options.item) {
try {
const item = JSON.parse(decodeURIComponent(options.item));
this.formatCheckinData(item);
} catch (e) {
console.error('解析打卡数据失败:', e);
uni.showToast({
title: '数据加载失败',
icon: 'none'
});
}
}
},
methods: {
async loadCheckinDetail(id) {
uni.showLoading({
title: '加载中...'
});
try {
const res = await getCheckinDetail(id);
if (res && res.code === 200) {
this.formatCheckinData(res.data);
} else {
throw new Error(res.message || '获取详情失败');
}
} catch (e) {
console.error('获取详情失败:', e);
uni.showToast({
title: '获取详情失败',
icon: 'none'
});
} finally {
uni.hideLoading();
}
},
formatCheckinData(item) {
let images = [];
if (item.photos) {
try {
if (typeof item.photos === 'string') {
if (item.photos.startsWith('[')) {
images = JSON.parse(item.photos);
} else {
images = [item.photos];
}
} else if (Array.isArray(item.photos)) {
images = item.photos;
}
} catch (e) {
console.error('解析图片失败:', e);
if (typeof item.photos === 'string') {
images = [item.photos];
}
}
}
this.checkinData = {
id: item.id,
images: images,
mealType: item.mealType,
createTime: item.date || item.createTime,
notes: item.notes,
points: item.points || 0,
aiAnalysis: item.aiAnalysis || item.nutritionScore,
videoUrl: item.videoUrl || '',
taskId: item.taskId || '',
enableAIVideo: item.enableAIVideo || false,
videoStatus: item.videoStatus || 0
};
},
onImageChange(e) {
this.currentImageIndex = e.detail.current
},
getMealText(type) {
const map = {
breakfast: '早餐',
lunch: '午餐',
dinner: '晚餐',
snack: '加餐'
};
return map[type] || type;
},
getMealIcon(type) {
const map = {
breakfast: '🌅',
lunch: '☀️',
dinner: '🌙',
snack: '🍪'
};
return map[type] || '🍽️';
},
handleCopyCheckin() {
// 将数据存储到本地缓存避免URL参数过长
uni.setStorageSync('checkin_copy_data', {
images: this.checkinData.images,
mealType: this.checkinData.mealType,
notes: this.checkinData.notes
});
// 跳转到发布页
uni.navigateTo({
url: '/pages/tool/checkin-publish?mode=copy'
});
}
}
}
</script>
<style lang="scss" scoped>
.checkin-detail-page {
min-height: 100vh;
background: linear-gradient(180deg, #fafafa 0%, #ffffff 100%);
display: flex;
flex-direction: column;
}
/* 内容滚动区域 */
.content-scroll {
flex: 1;
}
/* 图片轮播区域 */
.image-carousel {
width: 100%;
height: 750rpx;
position: relative;
background: #f4f5f7;
.swiper {
width: 100%;
height: 100%;
}
.carousel-image {
width: 100%;
height: 100%;
}
.meal-tag {
position: absolute;
left: 32rpx;
top: 32rpx;
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
border: 1rpx solid rgba(255, 255, 255, 0.8);
border-radius: 50rpx;
padding: 8rpx 16rpx;
display: flex;
align-items: center;
gap: 12rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
.meal-icon {
font-size: 32rpx;
}
.meal-text {
font-size: 28rpx;
color: #3e5481;
}
}
.image-counter {
position: absolute;
right: 32rpx;
bottom: 32rpx;
background: rgba(0, 0, 0, 0.7);
border-radius: 50rpx;
padding: 8rpx 16rpx;
font-size: 24rpx;
color: #ffffff;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
}
}
/* 帖子内容区域 */
.post-content-section {
padding: 32rpx;
background: #ffffff;
}
.author-section {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 24rpx;
}
.author-info {
display: flex;
align-items: center;
gap: 24rpx;
}
.author-avatar {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
overflow: hidden;
background: #f5f5f5;
.avatar-img {
width: 100%;
height: 100%;
}
}
.author-details {
display: flex;
flex-direction: column;
gap: 8rpx;
.author-name {
font-size: 28rpx;
color: #2e3e5c;
font-weight: 500;
}
.post-time {
font-size: 24rpx;
color: #9fa5c0;
}
}
.points-badge {
background: linear-gradient(135deg, #ff8c5a 0%, #ff6b35 100%);
border-radius: 50rpx;
padding: 8rpx 20rpx;
font-size: 24rpx;
color: #ffffff;
font-weight: 500;
}
.post-description {
font-size: 30rpx;
color: #3e5481;
line-height: 1.6;
white-space: pre-wrap;
}
/* 营养统计卡片 */
.nutrition-stats-card {
margin: 10rpx 32rpx;
padding: 32rpx;
background: linear-gradient(135deg, #fff5f0 0%, #ffe8dc 50%, #ffffff 100%);
border: 2rpx solid rgba(255, 136, 68, 0.3);
border-radius: 32rpx;
box-shadow: 0 16rpx 60rpx rgba(255, 136, 68, 0.15);
}
.stats-header {
display: flex;
align-items: center;
margin-bottom: 24rpx;
}
.stats-title {
display: flex;
align-items: center;
gap: 16rpx;
.title-icon {
width: 48rpx;
height: 48rpx;
background: linear-gradient(135deg, #ff9966 0%, #ff8844 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
box-shadow: 0 8rpx 12rpx rgba(0, 0, 0, 0.1);
}
.title-text {
font-size: 28rpx;
color: #2e3e5c;
font-weight: 500;
}
}
.ai-analysis-content {
font-size: 28rpx;
color: #3e5481;
line-height: 1.6;
}
// 视频播放区域
.video-section {
margin: 24rpx 32rpx;
background: #ffffff;
border-radius: 24rpx;
overflow: hidden;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.08);
.section-header {
padding: 24rpx 32rpx;
display: flex;
align-items: center;
gap: 12rpx;
border-bottom: 2rpx solid #f5f5f5;
.header-icon {
font-size: 32rpx;
}
.header-title {
font-size: 28rpx;
font-weight: 500;
color: #333333;
}
}
.checkin-video {
width: 100%;
height: 400rpx;
background: #000000;
}
}
// 视频生成中状态
.video-generating {
margin: 24rpx 32rpx;
padding: 40rpx;
background: #f9f9f9;
border-radius: 24rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 16rpx;
.generating-icon {
font-size: 48rpx;
animation: rotate 2s linear infinite;
}
.generating-text {
font-size: 24rpx;
color: #999999;
}
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* 一键借鉴打卡按钮 */
.copy-checkin-btn {
margin: 40rpx 32rpx;
height: 94rpx;
background: linear-gradient(135deg, #ff8844 0%, #ff6611 50%, #ff7722 100%);
border-radius: 24rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 16rpx;
box-shadow: 0 16rpx 60rpx rgba(255, 107, 53, 0.3);
.btn-icon {
font-size: 36rpx;
}
.btn-text {
font-size: 28rpx;
color: #ffffff;
font-weight: 500;
}
}
/* 底部安全距离 */
.safe-bottom {
height: 40rpx;
}
</style>