629 lines
14 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="page-container">
<!-- 顶部币种展示 -->
<view class="coin-section">
<view class="coin-box left-box">
<view class="brand-header">
<image class="brand-logo" src="/static/logo.png" mode="aspectFill"></image>
<text class="title">通用啤酒币</text>
</view>
<view class="amount-row">
<text class="amount">{{commonCoin}}</text>
<text class="cuIcon-rechargefill coin-icon"></text>
</view>
<view class="desc">*用于抵扣任意品牌啤酒币</view>
</view>
<swiper class="coin-box right-box"
:current="currentBrandIndex"
@change="onSwiperChange"
:duration="100"
:autoplay="true"
:interval="2000"
circular
@click="toCoinCollect"
>
<swiper-item v-for="(item, index) in sortedBrandCoins" :key="index">
<view class="brand-content">
<view class="brand-header">
<image class="brand-logo" :src="item.brandLogo || '/static/logo.png'" mode="aspectFill"></image>
<text class="title">{{item.brandName}}</text>
</view>
<view class="amount-row">
<text class="amount">{{item.balance}}</text>
<text class="cuIcon-rechargefill coin-icon"></text>
</view>
<view class="desc">{{item.goodsNum}}款热门商品可兑换</view>
</view>
</swiper-item>
<!-- 添加空数据状态提示 -->
<swiper-item v-if="sortedBrandCoins.length === 0">
<view class="brand-content empty-content">
<view class="empty-icon">
<text class="cuIcon-info"></text>
</view>
<view class="empty-text">暂未获得任何品牌啤酒币</view>
<view class="empty-desc">扫描酒款即可获得品牌啤酒币</view>
</view>
</swiper-item>
</swiper>
</view>
<!-- 标签栏 -->
<view class="tab-section" :class="{'fixed': isTabFixed}">
<view
class="tab-item"
:class="{'active': curTag === index}"
v-for="(item, index) in ['全部', '关注厂牌', '人气排名', '最新发布']"
:key="index"
@click="changeTag(index)"
>{{item}}</view>
</view>
<!-- 商品列表 -->
<view class="goods-list" v-if="!initLoading">
<view
class="goods-item"
v-for="(item, index) in goodsList"
:key="index"
@click="toDetail(item)"
>
<image class="goods-image" :src="item.goodsCover" mode="aspectFill"></image>
<view class="goods-title text-cut">{{item.goodsName}}</view>
<view class="goods-price">
<view class="price-left">
<text class="price-num">{{item.redeemedNum}}</text>
<text class="cuIcon-rechargefill coin-icon"></text>
</view>
<text class="brand-name">{{item.brandName}}</text>
</view>
</view>
</view>
<!-- 加载更多 -->
<view class="load-more">
<template v-if="goodsList.length > 0">
<view v-if="loading" class="loading">
<text class="cuIcon-loading2 iconfont-spin"></text>
<text class="loading-text">加载中...</text>
</view>
<text v-else-if="isLoadAll" class="no-more">已经没有更多啦~</text>
<text v-else class="pull-tip">上拉加载更多</text>
</template>
<text v-else-if="!loading && !initLoading" class="empty">暂无数据</text>
</view>
<!-- 初始化加载中 -->
<view class="init-loading" v-if="initLoading">
<text class="cuIcon-loading2 iconfont-spin"></text>
<text class="loading-text">加载中...</text>
</view>
</view>
</template>
<style lang="scss" scoped>
/* 页面容器样式 */
.page-container {
min-height: 100vh;
background: #F6F7FB;
box-sizing: border-box;
padding: 24rpx;
}
/* 顶部币种展示区域 */
.coin-section {
display: flex;
justify-content: space-between;
margin-bottom: 24rpx;
/* 币种卡片通用样式 */
.coin-box {
background: #FFFFFF;
border-radius: 16rpx;
padding: 12rpx;
box-sizing: border-box;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04); // 简化阴影
border: 1rpx solid rgba(242, 242, 242, 0.8); // 简化边框
/* 品牌标题区域 */
.brand-header {
display: flex;
align-items: flex-start;
padding: 8rpx;
/* 品牌logo样式 */
.brand-logo {
width: 72rpx;
height: 72rpx;
margin-top: 12rpx;
border-radius: 12rpx;
margin-right: 12rpx;
}
/* 品牌名称样式 */
.title {
font-size: 28rpx;
color: #333333;
font-weight: 600;
text-align: right;
flex: 1;
padding-top: 8rpx;
}
}
/* 币种数量行样式 */
.amount-row {
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 12rpx;
margin-bottom: 8rpx;
margin-top: -24rpx;
/* 数量文字样式 */
.amount {
font-size: 48rpx;
color: #1A1A1A;
font-weight: 600;
line-height: 48rpx;
}
/* 币种图标样式 */
.coin-icon {
color: #FFD700;
font-size: 30rpx;
margin-left: 10rpx;
line-height: 48rpx;
}
}
/* 底部描述文字样式 */
.desc {
font-size: 20rpx;
color: #606060;
text-align: right;
line-height: 28rpx;
padding-right: 20rpx;
}
}
/* 左侧通用啤酒币卡片样式 */
.left-box {
width: 298rpx;
height: 198rpx;
}
/* 右侧品牌啤酒币轮播卡片样式 */
.right-box {
width: 382rpx;
height: 198rpx;
/* 品牌内容容器 */
.brand-content {
height: 100%;
padding-right: 24rpx;
box-sizing: border-box;
}
}
}
/* 标签栏样式 */
.tab-section {
display: flex;
border-radius: 12rpx;
margin-bottom: 28rpx;
padding: 24rpx;
background: #FFFFFF;
z-index: 99;
width: 100%;
box-sizing: border-box;
/* 固定定位状态样式 */
&.fixed {
position: fixed;
left: 0;
right: 0;
top: 0;
margin-bottom: 0;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
}
/* 标签项样式 */
.tab-item {
width: 144rpx;
height: 64rpx;
line-height: 64rpx;
font-size: 24rpx;
font-weight: 500;
text-align: center;
color: #666666;
background: #F9F9F9;
border-radius: 12rpx;
margin-right: 16rpx;
/* 激活状态样式 */
&.active {
color: #FFFFFF;
background: #D42E78;
}
&:last-child {
margin-right: 0;
}
}
}
/* 商品列表样式 */
.goods-list {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-top: v-bind('isTabFixed ? "112rpx" : "0"');
/* 商品项样式 */
.goods-item {
width: 336rpx;
background: #FFFFFF;
border-radius: 16rpx;
margin-bottom: 24rpx;
overflow: hidden;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04); // 简化阴影
/* 商品图片样式 */
.goods-image {
width: 100%;
height: 258rpx;
background: #F5F5F5;
}
/* 商品标题样式 */
.goods-title {
padding: 16rpx;
font-size: 24rpx;
color: #1A1A1A;
font-weight: 600;
}
/* 商品价格区域样式 */
.goods-price {
padding: 16rpx;
display: flex;
justify-content: space-between;
align-items: center;
/* 价格左侧区域 */
.price-left {
display: flex;
align-items: center;
/* 价格数字样式 */
.price-num {
font-size: 32rpx;
color: #1A1A1A;
font-weight: bold;
}
/* 价格图标样式 */
.coin-icon {
color: #FFD700;
font-size: 24rpx;
margin-left: 12rpx;
}
}
/* 品牌名称样式 */
.brand-name {
font-size: 20rpx;
color: #808080;
background: #F6F7FB;
padding: 4rpx 12rpx;
border-radius: 20rpx;
}
}
}
}
/* 加载更多提示样式 */
.load-more {
text-align: center;
padding: 24rpx;
color: #999999;
font-size: 24rpx;
.loading {
display: flex;
align-items: center;
justify-content: center;
.iconfont-spin {
animation: loading-rotate 1s linear infinite;
margin-right: 8rpx;
}
.loading-text {
color: #666666;
}
}
.no-more {
color: #999999;
}
.pull-tip {
color: #666666;
}
.empty {
color: #999999;
font-size: 28rpx;
}
}
@keyframes loading-rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* 文本截断样式 */
.text-cut {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
</style>
<script>
import { getGoodsList, getStoreCoinBalance } from '@/api/bar.js'
export default {
data() {
return {
curTag: 0,
loading: false,
initLoading: false, // 添加初始化loading状态
goodsList: [],
queryForm: {
pageNum: 1,
pageSize: 10,
sortType: 0, // 0: 全部, 1: 关注厂牌, 2: 人气排名, 3: 最新发布
orderBy: 'createTime', // 排序字段
orderType: 'desc' // 排序方式
},
total: 0,
commonCoin: 0,
currentBrandIndex: 0,
sortedBrandCoins: [],
isTabFixed: false,
statusBarHeight: 0,
tabOffsetTop: 160,
isLoadAll: false,
lastCoinBalanceTime: 0, // 添加最后获取啤酒币余额的时间戳
coinBalanceDebounceTime: 3000, // 防抖时间3秒内不重复请求
swiperHeight: '198rpx' // 添加轮播图高度控制
}
},
onLoad() {
// 获取状态栏高度
try {
const systemInfo = uni.getSystemInfoSync()
this.statusBarHeight = systemInfo.statusBarHeight || 0
} catch (e) {
console.error('获取系统信息失败', e)
}
// 初始化数据
this.initData()
},
onShow() {
// 检查是否需要刷新啤酒币余额
const now = Date.now()
if (now - this.lastCoinBalanceTime >= this.coinBalanceDebounceTime) {
this.getStoreCoinBalanceFun()
}
},
// 页面滚动监听
onPageScroll(e) {
if (e && typeof e.scrollTop === 'number') {
this.isTabFixed = e.scrollTop >= this.tabOffsetTop
}
},
// 添加上拉触底事件
onReachBottom() {
this.loadMore()
},
methods: {
// 初始化数据
async initData() {
if (this.initLoading) return
this.initLoading = true
try {
// 先获取啤酒币余额
await this.getStoreCoinBalanceFun()
// 然后获取商品列表
await this.getGoodsListFun()
} catch (error) {
console.error('初始化数据失败', error)
uni.showToast({
title: '加载失败',
icon: 'none'
})
} finally {
this.initLoading = false
}
},
// 切换标签
async changeTag(tag) {
if (this.curTag === tag) return
try {
// 设置排序参数
const sortConfig = {
0: { orderBy: 'createTime', orderType: 'desc' }, // 全部
1: { orderBy: 'isFollowed', orderType: 'desc' }, // 关注厂牌
2: { orderBy: 'popularity', orderType: 'desc' }, // 人气排名
3: { orderBy: 'createTime', orderType: 'desc' } // 最新发布
}
// 更新查询参数
this.queryForm = {
...this.queryForm,
pageNum: 1,
sortType: tag,
...sortConfig[tag]
}
// 请求数据
const res = await getGoodsList(this.queryForm)
// 更新UI
this.curTag = tag
this.total = res.total || 0
this.goodsList = res.rows || []
this.isLoadAll = this.goodsList.length >= this.total
} catch (error) {
console.error('切换标签失败', error)
uni.showToast({
title: '加载失败',
icon: 'none'
})
}
},
// 轮播切换事件
onSwiperChange(e) {
if (e && e.detail && typeof e.detail.current === 'number') {
this.currentBrandIndex = e.detail.current
}
},
// 获取商品列表
async getGoodsListFun() {
if (this.loading) return
this.loading = true
try {
const res = await getGoodsList(this.queryForm)
this.total = res.total || 0
// 如果是加载更多,则追加数据
if (this.queryForm.pageNum > 1) {
this.goodsList = [...this.goodsList, ...(res.rows || [])]
} else {
this.goodsList = res.rows || []
this.isLoadAll = false
}
// 判断是否已加载全部
this.isLoadAll = this.goodsList.length >= this.total
} catch (error) {
console.error('获取商品列表失败', error)
uni.showToast({
title: '加载失败',
icon: 'none'
})
} finally {
this.loading = false
}
},
// 获取啤酒币余额
async getStoreCoinBalanceFun() {
try {
const token = uni.getStorageSync('token');
const userInfo = uni.getStorageSync('userInfo');
if (!token || !userInfo) {
this.sortedBrandCoins = [];
this.commonCoin = 0;
return;
}
const res = await getStoreCoinBalance();
if (res.data?.length > 0) {
// 获取通用啤酒币
const commonCoin = res.data.find(it => it.breweryId == 0);
this.commonCoin = commonCoin?.balance || 0;
// 获取品牌啤酒币列表并排序
const brandCoins = res.data.filter(it => it.breweryId != 0);
if (brandCoins.length > 0) {
this.sortedBrandCoins = brandCoins.map(item => ({
id: item.id,
breweryId: item.breweryId,
brandName: item.brandName,
brandLogo: item.brandLogo,
balance: item.balance || 0,
goodsNum: item.goodsNum || 0,
barName: item.barName,
barCode: item.barCode,
barCity: item.barCity
})).sort((a, b) => b.balance - a.balance);
// 调整轮播图高度
this.swiperHeight = '198rpx'
} else {
this.sortedBrandCoins = [];
// 空数据时调整轮播图高度
this.swiperHeight = '120rpx'
}
} else {
this.sortedBrandCoins = [];
this.commonCoin = 0;
// 空数据时调整轮播图高度
this.swiperHeight = '120rpx'
}
// 更新最后获取时间
this.lastCoinBalanceTime = Date.now()
} catch (error) {
console.error('获取啤酒币余额失败', error);
this.sortedBrandCoins = [];
this.commonCoin = 0;
// 错误时调整轮播图高度
this.swiperHeight = '120rpx'
}
},
// 跳转商品详情
toDetail(item) {
if (!item || !item.id) return
uni.navigateTo({
url: `/pagesCoin/goodsDetail?id=${item.id}&breweryId=${item.breweryId || 0}`
})
},
// 加载更多
async loadMore() {
if (this.loading || this.isLoadAll || this.goodsList.length === 0) return
if (this.goodsList.length < this.total) {
this.queryForm.pageNum++
await this.getGoodsListFun()
} else {
this.isLoadAll = true
}
},
// 跳转到啤酒币收集页面
toCoinCollect() {
uni.navigateTo({
url: '/pagesCoin/coinCollect'
})
}
}
}
</script>