<template>
|
<view class="groupbuy-product-detail" :data-theme='theme()'>
|
<view class="swiper-container">
|
<swiper class="swiper" :indicator-dots="true" :autoplay="true" :interval="3000" :duration="500">
|
<swiper-item v-for="(image, index) in detail.product.image" :key="index">
|
<image :src="image.file_path" mode="widthFix" class="slide-image"></image>
|
</swiper-item>
|
</swiper>
|
</view>
|
|
<view class="product-info">
|
<view class="product-title">{{ detail.product.product_name }}</view>
|
<view class="product-price">
|
<text class="groupbuy-price">¥{{ detail.groupbuySku[0]?.groupbuy_price || detail.product.product_price }}</text>
|
<text class="original-price">¥{{ detail.product.product_price }}</text>
|
</view>
|
<view class="product-meta">
|
<text class="sales">已售{{ detail.product.product_sales }}件</text>
|
<text class="group-num">还需{{ detail.groupbuy_num - detail.actual_people }}人成团</text>
|
</view>
|
</view>
|
|
<view class="group-progress">
|
<view class="progress-header">
|
<view class="progress-left">
|
<text class="progress-title">团购进度</text>
|
<text class="progress-tips">邀请好友加速成团</text>
|
</view>
|
<view class="progress-right">
|
<text class="progress-percent">{{ Math.round((detail.actual_people / detail.groupbuy_num) * 100) }}%</text>
|
</view>
|
</view>
|
<view class="progress-bar">
|
<view class="progress-fill" :style="{ width: (detail.actual_people / detail.groupbuy_num) * 100 + '%' }"></view>
|
<view class="progress-shine"></view>
|
</view>
|
<view class="progress-desc">
|
<view class="people-count">
|
<text class="current">{{ detail.actual_people }}</text>
|
<text class="separator">/</text>
|
<text class="target">{{ detail.groupbuy_num }}人</text>
|
</view>
|
<view class="countdown">
|
<text class="countdown-label">还剩</text>
|
<text class="countdown-time">{{ countdownText }}</text>
|
</view>
|
</view>
|
</view>
|
|
<view class="participants">
|
<view class="section-header">
|
<text class="section-title">正在拼团</text>
|
<text class="section-subtitle">已有{{ detail.actual_people }}人参团</text>
|
</view>
|
<view class="participant-list">
|
<view class="participant-item" v-for="(user, index) in participants" :key="index">
|
<image :src="user.avatar" class="avatar" mode="aspectFill"></image>
|
<view class="user-info">
|
<text class="nickname">{{ user.nickname }}</text>
|
<text class="time">{{ user.join_time }}</text>
|
</view>
|
<view class="join-status">已参团</view>
|
</view>
|
</view>
|
</view>
|
|
<view class="specs" v-if="detail.groupbuySku.length > 1">
|
<view class="section-header">
|
<text class="section-title">选择规格</text>
|
</view>
|
<view class="specs-list">
|
<view class="spec-item"
|
v-for="(sku, index) in detail.groupbuySku"
|
:key="index"
|
:class="{ active: selectedSkuIndex === index }"
|
@click="selectSku(index)">
|
<text class="spec-name">{{ sku.sku_name || '默认规格' }}</text>
|
<text class="spec-price">¥{{ sku.groupbuy_price }}</text>
|
</view>
|
</view>
|
</view>
|
|
<view class="footer-actions">
|
<view class="left-actions">
|
<view class="action-item" @click="goToHome">
|
<text class="iconfont icon-shouye"></text>
|
<text class="action-text">首页</text>
|
</view>
|
<view class="action-item" @click="goToCart">
|
<text class="iconfont icon-gouwuche"></text>
|
<text class="action-text">购物车</text>
|
</view>
|
</view>
|
<view class="right-actions">
|
<button class="btn-join" @click="joinGroupbuy">立即参团</button>
|
<button class="btn-invite" @click="showInviteModal">邀请好友</button>
|
</view>
|
</view>
|
|
<!-- 邀请弹窗 -->
|
<view class="modal" v-if="showInvite">
|
<view class="modal-content">
|
<view class="modal-header">
|
<text class="modal-title">邀请好友参团</text>
|
<text class="close" @click="closeInviteModal">×</text>
|
</view>
|
<view class="invite-options">
|
<view class="invite-option" @click="shareToFriends">
|
<text class="iconfont icon-weixin"></text>
|
<text class="option-text">分享给朋友</text>
|
</view>
|
<view class="invite-option" @click="copyLink">
|
<text class="iconfont icon-fuzhi"></text>
|
<text class="option-text">复制链接</text>
|
</view>
|
</view>
|
</view>
|
</view>
|
</view>
|
</template>
|
|
<script>
|
export default {
|
data() {
|
return {
|
productId: null,
|
detail: {},
|
participants: [],
|
selectedSkuIndex: 0,
|
showInvite: false,
|
countdownTimer: null,
|
countdownTime: 0
|
};
|
},
|
computed: {
|
countdownText() {
|
if (this.countdownTime <= 0) return '00:00:00';
|
const hours = Math.floor(this.countdownTime / 3600);
|
const minutes = Math.floor((this.countdownTime % 3600) / 60);
|
const seconds = this.countdownTime % 60;
|
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
|
}
|
},
|
onLoad(options) {
|
this.productId = options.id;
|
this.loadData();
|
},
|
onUnload() {
|
if (this.countdownTimer) {
|
clearInterval(this.countdownTimer);
|
}
|
},
|
methods: {
|
loadData() {
|
// 加载团购商品详情
|
this._get('plus.groupbuy.product/detail', {
|
groupbuy_product_id: this.productId
|
}, (res) => {
|
this.detail = res.data.detail;
|
// 计算团购倒计时
|
this.calculateCountdown();
|
});
|
|
// 加载参团用户
|
this.loadParticipants();
|
},
|
calculateCountdown() {
|
if (this.detail.active && this.detail.active.end_time) {
|
const endTime = new Date(this.detail.active.end_time).getTime() / 1000;
|
const now = Math.floor(Date.now() / 1000);
|
const diff = endTime - now;
|
this.countdownTime = diff > 0 ? diff : 0;
|
this.startCountdown();
|
}
|
},
|
startCountdown() {
|
if (this.countdownTimer) {
|
clearInterval(this.countdownTimer);
|
}
|
this.countdownTimer = setInterval(() => {
|
if (this.countdownTime > 0) {
|
this.countdownTime--;
|
} else {
|
clearInterval(this.countdownTimer);
|
}
|
}, 1000);
|
},
|
loadParticipants() {
|
// 模拟参团用户数据
|
this.participants = [
|
{
|
avatar: '/static/images/default-avatar.png',
|
nickname: '张三',
|
join_time: '刚刚'
|
},
|
{
|
avatar: '/static/images/default-avatar.png',
|
nickname: '李四',
|
join_time: '2分钟前'
|
},
|
{
|
avatar: '/static/images/default-avatar.png',
|
nickname: '王五',
|
join_time: '5分钟前'
|
}
|
];
|
},
|
selectSku(index) {
|
this.selectedSkuIndex = index;
|
},
|
joinGroupbuy() {
|
// 获取当前选中的SKU
|
const selectedSku = this.detail.groupbuySku[this.selectedSkuIndex];
|
|
// 跳转到下单页面
|
this.gotoPage(`/pages/order/confirm-order?product_id=${this.detail.product.product_id}&sku_id=${selectedSku.product_sku_id}&groupbuy=true`);
|
},
|
showInviteModal() {
|
this.showInvite = true;
|
},
|
closeInviteModal() {
|
this.showInvite = false;
|
},
|
shareToFriends() {
|
// 分享到微信好友
|
uni.share({
|
provider: 'weixin',
|
scene: 'WXSceneSession',
|
type: 0,
|
href: `${this.websiteUrl}/pages/plus/groupbuy/product-detail?id=${this.productId}`,
|
title: `我发现一个好东西,一起来参团吧!`,
|
summary: this.detail.product.product_name,
|
imageUrl: this.detail.product.image[0]?.file_path
|
});
|
},
|
copyLink() {
|
uni.setClipboardData({
|
data: `${this.websiteUrl}/pages/plus/groupbuy/product-detail?id=${this.productId}`,
|
success: () => {
|
uni.showToast({
|
title: '链接已复制',
|
icon: 'success'
|
});
|
}
|
});
|
},
|
goToHome() {
|
this.gotoPage('/pages/index/index');
|
},
|
goToCart() {
|
this.gotoPage('/pages/cart/cart');
|
}
|
}
|
};
|
</script>
|
|
<style lang="scss">
|
.groupbuy-product-detail {
|
background: #f5f5f5;
|
min-height: 100vh;
|
|
.swiper-container {
|
height: 750rpx;
|
|
.swiper {
|
height: 100%;
|
|
.slide-image {
|
width: 100%;
|
height: 100%;
|
}
|
}
|
}
|
|
.product-info {
|
background: #fff;
|
padding: 30rpx;
|
margin-bottom: 20rpx;
|
|
.product-title {
|
font-size: 32rpx;
|
color: #333;
|
margin-bottom: 20rpx;
|
line-height: 1.4;
|
}
|
|
.product-price {
|
display: flex;
|
align-items: flex-end;
|
margin-bottom: 20rpx;
|
|
.groupbuy-price {
|
font-size: 40rpx;
|
color: #e74748;
|
font-weight: bold;
|
margin-right: 20rpx;
|
}
|
|
.original-price {
|
font-size: 24rpx;
|
color: #999;
|
text-decoration: line-through;
|
}
|
}
|
|
.product-meta {
|
display: flex;
|
justify-content: space-between;
|
font-size: 24rpx;
|
color: #999;
|
}
|
}
|
|
.group-progress {
|
background: #fff;
|
padding: 30rpx;
|
margin-bottom: 20rpx;
|
border-radius: 16rpx;
|
|
.progress-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 20rpx;
|
|
.progress-left {
|
.progress-title {
|
font-size: 32rpx;
|
color: #333;
|
font-weight: bold;
|
display: block;
|
margin-bottom: 8rpx;
|
}
|
|
.progress-tips {
|
font-size: 24rpx;
|
color: #999;
|
}
|
}
|
|
.progress-right {
|
.progress-percent {
|
font-size: 48rpx;
|
color: #e74748;
|
font-weight: bold;
|
}
|
}
|
}
|
|
.progress-bar {
|
height: 24rpx;
|
background: #f0f0f0;
|
border-radius: 12rpx;
|
overflow: hidden;
|
margin-bottom: 20rpx;
|
position: relative;
|
|
.progress-fill {
|
height: 100%;
|
background: linear-gradient(to right, #e74748, #f17a5f);
|
border-radius: 12rpx;
|
transition: width 0.5s ease;
|
position: relative;
|
z-index: 1;
|
}
|
|
.progress-shine {
|
position: absolute;
|
top: 0;
|
left: 0;
|
width: 100%;
|
height: 100%;
|
background: linear-gradient(90deg,
|
transparent 0%,
|
rgba(255, 255, 255, 0.3) 50%,
|
transparent 100%);
|
animation: shine 2s infinite;
|
z-index: 2;
|
}
|
}
|
|
.progress-desc {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
font-size: 24rpx;
|
|
.people-count {
|
color: #333;
|
|
.current {
|
font-size: 36rpx;
|
color: #e74748;
|
font-weight: bold;
|
}
|
|
.separator {
|
margin: 0 5rpx;
|
color: #999;
|
}
|
|
.target {
|
color: #999;
|
}
|
}
|
|
.countdown {
|
color: #666;
|
background: #fef5f5;
|
padding: 8rpx 16rpx;
|
border-radius: 20rpx;
|
|
.countdown-label {
|
font-size: 24rpx;
|
}
|
|
.countdown-time {
|
font-size: 28rpx;
|
color: #e74748;
|
font-weight: bold;
|
margin-left: 5rpx;
|
}
|
}
|
}
|
}
|
|
@keyframes shine {
|
0% {
|
transform: translateX(-100%);
|
}
|
100% {
|
transform: translateX(100%);
|
}
|
}
|
|
.participants {
|
background: #fff;
|
padding: 30rpx;
|
margin-bottom: 20rpx;
|
|
.section-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 30rpx;
|
|
.section-title {
|
font-size: 28rpx;
|
color: #333;
|
font-weight: bold;
|
}
|
|
.section-subtitle {
|
font-size: 24rpx;
|
color: #999;
|
}
|
}
|
|
.participant-list {
|
.participant-item {
|
display: flex;
|
align-items: center;
|
padding: 20rpx 0;
|
border-bottom: 1rpx solid #f0f0f0;
|
|
&:last-child {
|
border-bottom: none;
|
}
|
|
.avatar {
|
width: 60rpx;
|
height: 60rpx;
|
border-radius: 50%;
|
margin-right: 20rpx;
|
}
|
|
.user-info {
|
flex: 1;
|
display: flex;
|
flex-direction: column;
|
|
.nickname {
|
font-size: 26rpx;
|
color: #333;
|
margin-bottom: 5rpx;
|
}
|
|
.time {
|
font-size: 22rpx;
|
color: #999;
|
}
|
}
|
|
.join-status {
|
font-size: 24rpx;
|
color: #e74748;
|
padding: 5rpx 15rpx;
|
border: 1rpx solid #e74748;
|
border-radius: 20rpx;
|
}
|
}
|
}
|
}
|
|
.specs {
|
background: #fff;
|
padding: 30rpx;
|
margin-bottom: 20rpx;
|
|
.section-header {
|
margin-bottom: 30rpx;
|
|
.section-title {
|
font-size: 28rpx;
|
color: #333;
|
font-weight: bold;
|
}
|
}
|
|
.specs-list {
|
display: flex;
|
flex-wrap: wrap;
|
|
.spec-item {
|
flex: 0 0 calc(50% - 20rpx);
|
margin-right: 20rpx;
|
margin-bottom: 20rpx;
|
padding: 20rpx;
|
border: 2rpx solid #f0f0f0;
|
border-radius: 10rpx;
|
text-align: center;
|
|
&:nth-child(2n) {
|
margin-right: 0;
|
}
|
|
&.active {
|
border-color: #e74748;
|
background: #fef8f8;
|
}
|
|
.spec-name {
|
display: block;
|
font-size: 26rpx;
|
color: #333;
|
margin-bottom: 10rpx;
|
}
|
|
.spec-price {
|
font-size: 24rpx;
|
color: #e74748;
|
}
|
}
|
}
|
}
|
|
.footer-actions {
|
position: fixed;
|
bottom: 0;
|
left: 0;
|
right: 0;
|
display: flex;
|
height: 120rpx;
|
background: #fff;
|
border-top: 1rpx solid #eee;
|
|
.left-actions {
|
flex: 1;
|
display: flex;
|
|
.action-item {
|
flex: 1;
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
justify-content: center;
|
|
.iconfont {
|
font-size: 36rpx;
|
color: #666;
|
margin-bottom: 5rpx;
|
}
|
|
.action-text {
|
font-size: 20rpx;
|
color: #666;
|
}
|
}
|
}
|
|
.right-actions {
|
display: flex;
|
|
.btn-join {
|
width: 200rpx;
|
height: 120rpx;
|
background: linear-gradient(to right, #e74748, #f17a5f);
|
color: #fff;
|
border: none;
|
border-radius: 0;
|
font-size: 32rpx;
|
font-weight: bold;
|
}
|
|
.btn-invite {
|
width: 200rpx;
|
height: 120rpx;
|
background: linear-gradient(to right, #f17a5f, #f5a623);
|
color: #fff;
|
border: none;
|
border-radius: 0;
|
font-size: 32rpx;
|
font-weight: bold;
|
}
|
}
|
}
|
|
.modal {
|
position: fixed;
|
top: 0;
|
left: 0;
|
right: 0;
|
bottom: 0;
|
background: rgba(0, 0, 0, 0.5);
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
z-index: 9999;
|
|
.modal-content {
|
width: 80%;
|
background: #fff;
|
border-radius: 20rpx;
|
overflow: hidden;
|
|
.modal-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding: 30rpx;
|
border-bottom: 1rpx solid #f0f0f0;
|
|
.modal-title {
|
font-size: 32rpx;
|
color: #333;
|
font-weight: bold;
|
}
|
|
.close {
|
font-size: 40rpx;
|
color: #999;
|
line-height: 1;
|
}
|
}
|
|
.invite-options {
|
padding: 30rpx;
|
|
.invite-option {
|
display: flex;
|
align-items: center;
|
padding: 20rpx;
|
border: 1rpx solid #f0f0f0;
|
border-radius: 10rpx;
|
margin-bottom: 20rpx;
|
|
&:last-child {
|
margin-bottom: 0;
|
}
|
|
.iconfont {
|
font-size: 40rpx;
|
margin-right: 20rpx;
|
color: #07c160;
|
}
|
|
.option-text {
|
font-size: 28rpx;
|
color: #333;
|
}
|
}
|
}
|
}
|
}
|
}
|
</style>
|