zdtap-uniapp-main/pagesCoin/goodsDetail.vue

563 lines
12 KiB
Vue
Raw Permalink Normal View History

2025-03-29 16:01:43 +08:00
<template>
<view class="page">
<view class="page-content">
<!-- 自定义导航栏 -->
<view class="custom-nav" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="nav-content">
<view class="back-btn" @click="goBack">
<text class="cuIcon-back"></text>
2025-03-29 16:01:43 +08:00
</view>
</view>
</view>
<!-- 广告轮播区域 -->
<view class="ad-section">
<swiper class="ad-swiper" circular autoplay interval="3000" duration="500" @change="handleSwiperChange">
<swiper-item v-for="(item, index) in adList" :key="index">
<view class="ad-placeholder" v-if="!item.imageUrl">
<text class="placeholder-text">广告位</text>
</view>
<image v-else :src="item.imageUrl" mode="aspectFill" class="ad-image" @click="handleAdClick(item)"></image>
</swiper-item>
</swiper>
<view class="ad-dots" v-if="adList.length > 1">
<view class="dot" v-for="(item, index) in adList" :key="index" :class="{ active: currentAdIndex === index }"></view>
2025-03-29 16:01:43 +08:00
</view>
</view>
<!-- 商品信息区域 -->
<view class="section">
<view class="section-title">商品信息</view>
<view class="info-list">
<view class="info-item" @click="goBreweryDetail">
<text>服务商家</text>
<view class="right-content">
<image :src="breweryInfo.brandLogo" class="brand-logo"></image>
<text class="info-text">{{ breweryInfo.brandName}}</text>
<text class="cuIcon-right"></text>
</view>
</view>
<view class="info-item">
<text>商品名称</text>
<view class="right-content">
<text class="info-text">{{ detail.goodsName}}</text>
</view>
</view>
<view class="info-item">
<text>库存数量</text>
<view class="right-content">
<text class="info-text">限量<text class="highlight">{{ detail.stockNum}}</text>兑完即止</text>
</view>
</view>
<view class="info-item">
<text>已兑换</text>
<view class="right-content">
<text class="info-text highlight">{{ detail.salesCount }}</text>
</view>
</view>
2025-03-29 16:01:43 +08:00
</view>
</view>
<!-- 兑换须知区域 -->
<view class="section">
<view class="section-title">兑换须知</view>
<view class="notice-list">
<view class="notice-item">
<image src="@/static/info-circle.png" class="notice-icon"></image>
<text class="notice-text">限购说明此商品每人限兑1件</text>
</view>
</view>
</view>
<!-- 商品图片区域 -->
<view class="section">
<view class="section-title">商品展示</view>
<view class="goods-image-container">
<image class="goods-image" :src="detail.goodsCover" mode="aspectFit"></image>
</view>
</view>
</view>
<!-- 底部操作栏 -->
<view class="bottom-bar">
<view class="coin-info">
<view class="coin-amount">
<text class="amount">{{ detail.redeemedNum}}</text>
<image src="@/static/beerCoin.png" class="coin-icon"></image>
</view>
<view class="balance-info">
<text>当前可用品牌币{{ isLogin ? (coinBalance.balance || 0) : '登录后可见' }}</text>
<image src="@/static/beerCoin.png" class="coin-icon"></image>
2025-03-29 16:01:43 +08:00
</view>
</view>
<view class="action-button" @click="handleRedeem" :class="{'disabled': !canRedeem}">
<text>立即兑换</text>
</view>
2025-03-29 16:01:43 +08:00
</view>
<!-- 登录弹窗 -->
2025-03-29 16:01:43 +08:00
<loginPopup ref="loginRef"></loginPopup>
</view>
</template>
<script>
import loginPopup from '@/components/loginPopup.vue';
import { getGoodsInfo, getBreweryInfo, getBreweryCoinBalance } from '@/api/bar.js'
2025-03-29 16:01:43 +08:00
export default {
components: {
loginPopup
},
data() {
return {
id:'',
breweryId:'', // 品牌方id
detail: {},
breweryInfo:{},
coinBalance:{},
isLogin: false, // 登录状态
statusBarHeight: 0, // 状态栏高度
adList: [{}, {}, {}], // 广告列表测试阶段使用3个占位
currentAdIndex: 0, // 当前广告索引
2025-03-29 16:01:43 +08:00
};
},
onLoad({id, breweryId}) {
// 获取状态栏高度
this.statusBarHeight = uni.getSystemInfoSync().statusBarHeight
2025-03-29 16:01:43 +08:00
this.id = id
this.breweryId = breweryId
this.checkLoginStatus()
2025-03-29 16:01:43 +08:00
this.getBreweryInfoFun()
this.getDetailFun()
2025-03-29 16:01:43 +08:00
},
onShow() {
this.checkLoginStatus()
if(this.isLogin) {
this.getBreweryCoinBalanceFun()
}
2025-03-29 16:01:43 +08:00
},
methods: {
// 返回上一页
goBack() {
uni.navigateBack()
},
// 检查登录状态
checkLoginStatus() {
try {
const token = uni.getStorageSync('token')
const userInfo = uni.getStorageSync('userInfo')
this.isLogin = !!token && !!userInfo
// 如果登录了但没有用户信息,说明登录状态异常
if(token && !userInfo) {
uni.removeStorageSync('token')
this.isLogin = false
}
} catch(e) {
console.error('检查登录状态失败:', e)
this.isLogin = false
}
},
2025-03-29 16:01:43 +08:00
// 品牌方详情
getBreweryInfoFun() {
getBreweryInfo(this.breweryId).then(res => {
this.breweryInfo = res.data
}).catch(err => {
console.error('获取品牌信息失败:', err)
uni.showToast({
title: '获取品牌信息失败',
icon: 'none'
})
2025-03-29 16:01:43 +08:00
})
},
// 获取详情
getDetailFun() {
uni.showLoading({
title: '加载中',
mask: true
})
getGoodsInfo(this.id).then(res => {
uni.hideLoading()
if (res.code == 200) {
this.detail = res.data
}
}).catch(err => {
console.error('获取商品详情失败:', err)
2025-03-29 16:01:43 +08:00
uni.hideLoading()
uni.showToast({
title: '获取商品详情失败',
icon: 'none'
})
2025-03-29 16:01:43 +08:00
})
},
// 跳转品牌方详情
goBreweryDetail() {
uni.navigateTo({
url: '/pages/index/brandHome?breweryId=' + this.breweryId
})
},
// 特定品牌啤酒币余额
getBreweryCoinBalanceFun() {
getBreweryCoinBalance(this.breweryId).then(res => {
this.coinBalance = res.data
}).catch(err => {
console.error('获取币种余额失败:', err)
uni.showToast({
title: '获取币种余额失败',
icon: 'none'
})
2025-03-29 16:01:43 +08:00
})
},
// 兑换
async handleRedeem() {
// 重新检查登录状态
this.checkLoginStatus()
if(!this.isLogin) {
// 使用 nextTick 确保组件已加载
await this.$nextTick()
if(this.$refs.loginRef) {
this.$refs.loginRef.open()
} else {
uni.showToast({
title: '请稍后再试',
icon: 'none'
})
}
return
}
if(!this.coinBalance.balance || this.coinBalance.balance < this.detail.redeemedNum) {
uni.showToast({
title: '品牌币余额不足',
icon: 'none',
duration: 2000
})
return
}
2025-03-29 16:01:43 +08:00
uni.navigateTo({
url: '/pagesCoin/redeemOrder?goodsId=' + this.id
})
},
// 处理轮播图切换
handleSwiperChange(e) {
this.currentAdIndex = e.detail.current
},
// 处理广告点击
handleAdClick(item) {
if (item.linkUrl) {
uni.navigateTo({
url: item.linkUrl
})
}
},
},
computed: {
canRedeem() {
return this.isLogin && this.coinBalance.balance && this.coinBalance.balance >= this.detail.redeemedNum
}
2025-03-29 16:01:43 +08:00
}
}
</script>
<style lang="scss" scoped>
.page {
min-height: 100vh;
background: #F7F7F7;
.page-content {
padding: 0;
padding-bottom: calc(180rpx + env(safe-area-inset-bottom));
}
.ad-section {
margin: 0;
position: relative;
width: 100%;
overflow: hidden;
.ad-swiper {
width: 100%;
height: 500rpx;
.ad-image {
width: 100%;
height: 100%;
}
.ad-placeholder {
width: 100%;
height: 100%;
background: #F5F5F5;
display: flex;
align-items: center;
justify-content: center;
.placeholder-text {
font-size: 28rpx;
color: #999999;
2025-03-29 16:01:43 +08:00
}
}
}
.ad-dots {
position: absolute;
bottom: 20rpx;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 12rpx;
padding: 8rpx 16rpx;
background: rgba(0, 0, 0, 0.2);
border-radius: 20rpx;
.dot {
width: 12rpx;
height: 12rpx;
border-radius: 50%;
background: rgba(255, 255, 255, 0.6);
transition: all 0.3s ease;
&.active {
width: 24rpx;
border-radius: 6rpx;
background: #FFFFFF;
}
}
}
}
.custom-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 999;
background: transparent;
.nav-content {
height: 44px;
display: flex;
align-items: center;
padding: 0 32rpx;
.back-btn {
width: 64rpx;
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.8);
2025-03-29 16:01:43 +08:00
border-radius: 50%;
.cuIcon-back {
font-size: 36rpx;
color: #000;
}
2025-03-29 16:01:43 +08:00
}
}
}
.section {
background: #FFFFFF;
border-radius: 16rpx;
margin: 24rpx 32rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
overflow: hidden;
position: relative;
z-index: 10;
.section-title {
font-size: 32rpx;
font-weight: 600;
padding: 32rpx;
color: #333333;
position: relative;
&::after {
content: '';
position: absolute;
left: 32rpx;
bottom: 0;
width: 48rpx;
height: 4rpx;
background: #19367A;
border-radius: 2rpx;
margin-bottom: 12rpx;
}
}
}
2025-03-29 16:01:43 +08:00
.info-list {
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx;
border-bottom: 1rpx solid rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
&:last-child {
border-bottom: none;
}
text {
font-size: 28rpx;
color: #333333;
2025-03-29 16:01:43 +08:00
}
.right-content {
2025-03-29 16:01:43 +08:00
display: flex;
align-items: center;
gap: 16rpx;
2025-03-29 16:01:43 +08:00
.brand-logo {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
border: 1rpx solid #19367A;
2025-03-29 16:01:43 +08:00
}
.info-text {
font-size: 28rpx;
color: #666666;
text-align: right;
.highlight {
color: #19367A;
font-weight: 600;
}
2025-03-29 16:01:43 +08:00
}
.cuIcon-right {
color: #CCCCCC;
font-size: 24rpx;
margin-left: 8rpx;
}
2025-03-29 16:01:43 +08:00
}
}
}
.goods-image-container {
padding: 32rpx;
display: flex;
justify-content: center;
.goods-image {
width: 400rpx;
height: 400rpx;
border-radius: 12rpx;
background: #FFFFFF;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
}
2025-03-29 16:01:43 +08:00
}
.notice-list {
padding: 0 32rpx 32rpx;
.notice-item {
display: flex;
align-items: center;
gap: 16rpx;
.notice-icon {
width: 32rpx;
height: 32rpx;
}
.notice-text {
font-size: 28rpx;
color: #666666;
}
}
}
.bottom-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #FFFFFF;
padding: 32rpx;
padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 -4rpx 12rpx rgba(0, 0, 0, 0.05);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
z-index: 100;
.coin-info {
flex: 1;
.coin-amount {
display: flex;
align-items: center;
margin-bottom: 12rpx;
.amount {
font-size: 48rpx;
color: #333333;
font-weight: 700;
}
.coin-icon {
width: 40rpx;
height: 40rpx;
margin-left: 12rpx;
}
}
.balance-info {
display: flex;
align-items: center;
text {
font-size: 24rpx;
color: #666666;
}
.coin-icon {
width: 32rpx;
height: 32rpx;
margin-left: 8rpx;
}
}
}
.action-button {
width: 344rpx;
height: 88rpx;
background: linear-gradient(135deg, #19367A, #2C4C99);
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
color: #FFFFFF;
transition: all 0.3s ease;
box-shadow: 0 4rpx 16rpx rgba(25, 54, 122, 0.2);
&:active {
transform: scale(0.98);
box-shadow: 0 2rpx 8rpx rgba(25, 54, 122, 0.1);
}
&.disabled {
background: #CCCCCC;
box-shadow: none;
pointer-events: none;
opacity: 0.6;
}
}
}
}
// 确保登录弹窗在最顶层
:deep(.uni-popup) {
z-index: 9999;
}
2025-03-29 16:01:43 +08:00
</style>