展品维保小程序前端代码接口
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

423 lines
10 KiB

<template>
<view class="shop-content">
<!-- 搜索框 -->
<view class="search-container">
<uv-search
v-model="title"
placeholder="搜索商品名"
:show-action="false"
bg-color="#f3f7f8"
inputAlign="left"
height="40"
margin="10rpx"
@search="onSearch"
@clickIcon="onSearch"
@clear="onSearch"
></uv-search>
</view>
<!-- <Search
v-model="title"
placeholder="🔍搜索商品名"
:show-cancel="false"
:show-icon="false"
text-align="center"
height="80rpx"
width="90%"
bg-color="#f3f7f8"
@search="onSearch"
@clickIcon="onSearch"
@clear="onSearch"
style="margin: 4rpx 0rpx;"
/> -->
<!-- Tab栏 -->
<view class="tab-container">
<scroll-view scroll-x="true" class="tab-scroll">
<view class="tab-list">
<!-- 固定的前三个Tab -->
<view
class="tab-item"
:class="{ active: currentTab === 0 }"
@click="onTabClick(0, '全部')"
>
<text class="tab-text">全部</text>
</view>
<view
class="tab-item sort-tab"
:class="{ active: currentTab === 1 }"
@click="onTabClick(1, '兑换积分')"
>
<text class="tab-text">兑换积分</text>
<view class="sort-arrows">
<view class="arrow up" :class="{ active: sortType === 'points_asc' }">▲</view>
<view class="arrow down" :class="{ active: sortType === 'points_desc' }">▼</view>
</view>
</view>
<view
class="tab-item sort-tab"
:class="{ active: currentTab === 2 }"
@click="onTabClick(2, '兑换量')"
>
<text class="tab-text">兑换量</text>
<view class="sort-arrows">
<view class="arrow up" :class="{ active: sortType === 'exchange_asc' }">▲</view>
<view class="arrow down" :class="{ active: sortType === 'exchange_desc' }">▼</view>
</view>
</view>
<!-- 从store获取的商品分类Tab -->
<view
v-for="(category, index) in categoryGoodsList"
:key="category.id"
class="tab-item"
:class="{ active: currentTab === index + 3 }"
@click="onTabClick(index + 3, category.title, category.id)"
>
<text class="tab-text">{{ category.title }}</text>
</view>
</view>
</scroll-view>
</view>
<!-- 商品列表 -->
<view class="goods-container">
<view class="goods-grid" v-if="goodsList.length > 0">
<view
class="goods-item click-animation"
v-for="(item, index) in goodsList"
:key="index"
@click="onGoodsClick(item)"
>
<view class="goods-image">
<image :src="item.image" mode="aspectFit" class="image"></image>
</view>
<view class="goods-info">
<text class="goods-name">{{ item.title }}</text>
<view class="goods-bottom">
<view class="points-info">
<image src="/static/积分图标.png" class="points-icon" mode="aspectFit"></image>
<text class="points-text">{{ item.price }}积分</text>
</view>
<uv-button
type="primary"
size="mini"
text="立即兑换"
:custom-style="buttonStyle"
></uv-button>
</view>
</view>
</view>
</view>
<uv-empty
v-else
icon="/static/暂无搜索结果.png"
text="暂无商品数据"
></uv-empty>
</view>
</view>
</template>
<script>
import Search from '@/pages/components/Search.vue'
export default {
name: 'ShopContent',
data() {
return {
// searchValue: '',
currentTab: 0,
pageNo: 1,
pageSize: 10,
title: '',
hasMore: true,
sortType: '', // 排序类型:points_asc, points_desc, exchange_asc, exchange_desc
goodsList: [],
buttonStyle: {
width: '128rpx',
height: '44rpx',
borderRadius: '28rpx',
fontSize: '22rpx'
},
// 额外的传参
extraParams : {}
}
},
components: {
Search
},
computed: {
// 从store获取商品分类列表
categoryGoodsList() {
return this.$store.state.categoryGoodsList || []
}
},
methods: {
async onSearch(value) {
if (value !== null || undefined) this.title = value
this.initData()
await this.getGoodsList({isRefresh : true})
},
async onTabClick(index, tabName, categoryId = null) {
this.currentTab = index
this.extraParams = {} // 不带任何额外参数
if (index === 0) {
// 全部Tab
console.log('点击了全部Tab')
} else if (index === 1) {
// 兑换积分Tab - 处理排序
if (this.sortType === 'points_asc') {
this.sortType = 'points_desc' // 积分降序
this.extraParams['price'] = 0
} else {
this.sortType = 'points_asc' // 积分升序
this.extraParams['price'] = 1
}
console.log('点击了兑换积分Tab,排序类型:', this.sortType)
} else if (index === 2) {
// 兑换量Tab - 处理排序
if (this.sortType === 'exchange_asc') {
this.sortType = 'exchange_desc' // 兑换量降序
this.extraParams['sales'] = 0
} else {
this.sortType = 'exchange_asc' // 兑换量升序
this.extraParams['sales'] = 1
}
console.log('点击了兑换量Tab,排序类型:', this.sortType)
} else {
// 商品分类Tab
console.log('点击了商品分类Tab:', tabName, '分类ID:', categoryId)
this.extraParams['categoryId'] = categoryId
}
this.initData()
await this.getGoodsList({isRefresh : true})
},
onGoodsClick(item) {
// 跳转到商品详情页
uni.navigateTo({
url: `/subPages/shop/goodsDetail?id=${item.id}`
})
},
// 如何默认isRefresh为false
async getGoodsList({isRefresh = false} = {}) {
if (!this.hasMore) return
if (this.title === undefined) this.title = ''
const res = await this.$api.shop.queryGoodsList({
pageNo: this.pageNo,
pageSize: this.pageSize,
title: this.title,
...this.extraParams
})
if (res.result.records.length) {
if (isRefresh) {
this.goodsList = res.result.records
} else {
this.goodsList.push(...res.result.records)
}
this.pageNo++
}else {
uni.showToast({
title: '暂无商品',
icon: 'none'
})
if (isRefresh) {
this.goodsList = []
}
this.hasMore = false
}
},
// 初始化请求参数
initData() {
this.pageNo = 1
// this.goodsList = []
this.hasMore = true
}
},
async mounted() {
// 确保store中的商品分类数据已加载
if (this.categoryGoodsList.length === 0) {
await this.$store.dispatch('getCategoryGoodsList')
}
// 初始化商品列表
// this.getGoodsList()
}
}
</script>
<style lang="scss" scoped>
.shop-content {
background: #f8f8f8;
min-height: calc(100vh - 400rpx);
}
.search-container {
position: sticky;
z-index: 999;
top: 10rpx;
padding: 15rpx 20rpx;
background: #ffffff;
}
.tab-container {
position: sticky;
z-index: 999;
top: 90rpx;
background: #ffffff;
border-bottom: 1rpx solid #f0f0f0;
padding-bottom: 20rpx;
.tab-scroll {
white-space: nowrap;
.tab-list {
display: flex;
padding: 0 30rpx;
.tab-item {
flex-shrink: 0;
display: flex;
align-items: center;
padding: 24rpx 32rpx;
margin-right: 16rpx;
border-radius: 32rpx;
background: #f8f9fa;
transition: all 0.3s ease;
.tab-text {
font-size: 28rpx;
color: #666666;
font-weight: 500;
}
&.active {
background: #218CDD;
.tab-text {
color: #ffffff;
}
.sort-arrows .arrow.active {
color: #ffffff;
}
}
&.sort-tab {
.sort-arrows {
margin-left: 8rpx;
display: flex;
flex-direction: column;
align-items: center;
.arrow {
font-size: 16rpx;
color: #cccccc;
line-height: 1;
transition: color 0.3s ease;
&.up {
margin-bottom: 2rpx;
}
&.active {
color: rgb(64, 64, 64);
}
}
}
}
}
}
}
}
.goods-container {
padding: 20rpx 30rpx;
background: #f8f8f8;
}
.goods-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20rpx;
}
.goods-item {
display: flex;
flex-direction: column;
background: #ffffff;
border-radius: 12rpx;
padding: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
border: 1rpx solid #f5f5f5;
.goods-image {
width: 100%;
height: 230rpx;
border-radius: 8rpx;
overflow: hidden;
margin-bottom: 16rpx;
border: 2rpx dashed #e0e0e0;
.image {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.goods-info {
flex: 1;
display: flex;
flex-direction: column;
.goods-name {
font-size: 28rpx;
color: #333333;
line-height: 1.4;
margin-bottom: 16rpx;
font-weight: 500;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
min-height: 72rpx;
}
.goods-bottom {
display: flex;
// flex-direction: column;
gap: 22rpx;
margin-top: auto;
.points-info {
display: flex;
align-items: center;
.points-icon {
width: 24rpx;
height: 24rpx;
margin-right: 6rpx;
}
.points-text {
font-size: 28rpx;
color: #218CDD;
font-weight: 700;
}
}
}
}
}
</style>