551 lines
12 KiB
Vue
551 lines
12 KiB
Vue
<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>
|
||
</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>
|
||
</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>
|
||
</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>
|
||
</view>
|
||
</view>
|
||
<view class="action-button" @click="handleRedeem">
|
||
<text>立即兑换</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 登录弹窗 -->
|
||
<loginPopup ref="loginRef"></loginPopup>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import loginPopup from '@/components/loginPopup.vue';
|
||
import { getGoodsInfo, getBreweryInfo, getBreweryCoinBalance } from '@/api/bar.js'
|
||
export default {
|
||
components: {
|
||
loginPopup
|
||
},
|
||
data() {
|
||
return {
|
||
id:'',
|
||
breweryId:'', // 品牌方id
|
||
detail: {},
|
||
breweryInfo:{},
|
||
coinBalance:{},
|
||
isLogin: false, // 登录状态
|
||
statusBarHeight: 0, // 状态栏高度
|
||
adList: [{}, {}, {}], // 广告列表,测试阶段使用3个占位
|
||
currentAdIndex: 0, // 当前广告索引
|
||
};
|
||
},
|
||
onLoad({id, breweryId}) {
|
||
// 获取状态栏高度
|
||
this.statusBarHeight = uni.getSystemInfoSync().statusBarHeight
|
||
|
||
this.id = id
|
||
this.breweryId = breweryId
|
||
this.checkLoginStatus()
|
||
this.getBreweryInfoFun()
|
||
this.getDetailFun()
|
||
},
|
||
onShow() {
|
||
this.checkLoginStatus()
|
||
if(this.isLogin) {
|
||
this.getBreweryCoinBalanceFun()
|
||
}
|
||
},
|
||
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
|
||
}
|
||
},
|
||
// 品牌方详情
|
||
getBreweryInfoFun() {
|
||
getBreweryInfo(this.breweryId).then(res => {
|
||
this.breweryInfo = res.data
|
||
}).catch(err => {
|
||
console.error('获取品牌信息失败:', err)
|
||
uni.showToast({
|
||
title: '获取品牌信息失败',
|
||
icon: 'none'
|
||
})
|
||
})
|
||
},
|
||
// 获取详情
|
||
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)
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: '获取商品详情失败',
|
||
icon: 'none'
|
||
})
|
||
})
|
||
},
|
||
// 跳转品牌方详情
|
||
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'
|
||
})
|
||
})
|
||
},
|
||
// 兑换
|
||
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
|
||
}
|
||
|
||
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
|
||
})
|
||
}
|
||
},
|
||
}
|
||
}
|
||
</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;
|
||
}
|
||
}
|
||
}
|
||
|
||
.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);
|
||
border-radius: 50%;
|
||
|
||
.cuIcon-back {
|
||
font-size: 36rpx;
|
||
color: #000;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.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;
|
||
}
|
||
}
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
.right-content {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16rpx;
|
||
|
||
.brand-logo {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
border-radius: 50%;
|
||
border: 1rpx solid #19367A;
|
||
}
|
||
|
||
.info-text {
|
||
font-size: 28rpx;
|
||
color: #666666;
|
||
text-align: right;
|
||
|
||
.highlight {
|
||
color: #19367A;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
|
||
.cuIcon-right {
|
||
color: #CCCCCC;
|
||
font-size: 24rpx;
|
||
margin-left: 8rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.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);
|
||
}
|
||
}
|
||
|
||
.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);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 确保登录弹窗在最顶层
|
||
:deep(.uni-popup) {
|
||
z-index: 9999;
|
||
}
|
||
</style> |