556 lines
12 KiB
Vue
556 lines
12 KiB
Vue
<template>
|
||
<view class="point-mall-container">
|
||
<!-- 顶部导航栏 -->
|
||
<uni-nav-bar
|
||
left-icon="left"
|
||
title="积分商城"
|
||
@clickLeft="goBack"
|
||
fixed
|
||
color="#e74c3c"
|
||
height="140rpx"
|
||
:border="false"
|
||
backgroundColor="#ffffff"
|
||
>
|
||
<template v-slot:right>
|
||
<text class="search-btn" @click="goToSearch">搜索</text>
|
||
</template>
|
||
</uni-nav-bar>
|
||
|
||
<!-- 轮播图横幅 -->
|
||
<view class="banner-section">
|
||
<swiper class="banner-swiper" :indicator-dots="true" :autoplay="true" :interval="3000" :duration="500">
|
||
<swiper-item v-for="(banner, index) in bannerList" :key="banner.uuid">
|
||
<view class="banner-item" @click="goToBannerDetail(banner)">
|
||
<image class="banner-image" :src="banner.headImg" mode="aspectFill"></image>
|
||
<view class="banner-content">
|
||
<view class="banner-text green">{{ banner.title }}</view>
|
||
<view class="banner-text orange">{{ banner.create_date }}</view>
|
||
</view>
|
||
</view>
|
||
</swiper-item>
|
||
<!-- 如果没有数据,显示默认轮播图 -->
|
||
<swiper-item v-if="bannerList.length === 0">
|
||
<view class="banner-item">
|
||
<image class="banner-image" src="/static/banner-bg.jpg" mode="aspectFill"></image>
|
||
<view class="banner-content">
|
||
<view class="banner-text green">第二届京津冀感染肝病高峰论坛&</view>
|
||
<view class="banner-text green">第九届河北省感染科医师培训班</view>
|
||
<view class="banner-text orange">2016.10.14-2016.10.16</view>
|
||
<view class="banner-text orange">河北•石家庄</view>
|
||
</view>
|
||
</view>
|
||
</swiper-item>
|
||
</swiper>
|
||
</view>
|
||
|
||
<!-- 筛选排序栏 -->
|
||
<view class="filter-bar">
|
||
<view class="filter-item" @click="showFilter">
|
||
<text class="filter-text">{{ selectedTagName || '筛选' }}</text>
|
||
<text class="filter-icon">▼</text>
|
||
</view>
|
||
<view class="filter-divider"></view>
|
||
<view class="filter-item" @click="showSort">
|
||
<text class="filter-text">{{ selectedSortLabel || '排序' }}</text>
|
||
<text class="filter-icon">▼</text>
|
||
</view>
|
||
</view>
|
||
<!-- 筛选下拉 -->
|
||
<view v-if="showFilterDropdown" class="dropdown">
|
||
<view
|
||
class="dropdown-item"
|
||
:class="{ active: selectedTagId === null }"
|
||
@click="onTagSelect({ id: null, name: '全部' })">
|
||
全部
|
||
</view>
|
||
<view
|
||
v-for="tag in tagList"
|
||
:key="tag.id"
|
||
class="dropdown-item"
|
||
:class="{ active: selectedTagId === tag.id }"
|
||
@click="onTagSelect(tag)">
|
||
{{ tag.name }}
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 排序下拉 -->
|
||
<view v-if="showSortDropdown" class="dropdown">
|
||
<view
|
||
v-for="opt in sortOptions"
|
||
:key="opt.value"
|
||
class="dropdown-item"
|
||
:class="{ active: selectedSortValue === opt.value }"
|
||
@click="onSortSelect(opt)">
|
||
{{ opt.label }}
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 商品网格(上拉加载) -->
|
||
<scroll-view class="product-grid" scroll-y @scrolltolower="getGoodsList" :lower-threshold="100">
|
||
<view class="grid-wrap">
|
||
<view class="product-item" v-for="(product, index) in products" :key="product.uuid || index" @click="goToProductDetail(product)">
|
||
<view class="product-image-container">
|
||
<image class="product-image" :src="product.image" mode="aspectFill"></image>
|
||
</view>
|
||
<view class="product-info">
|
||
<text class="product-title">{{ product.title }}</text>
|
||
<text class="product-price">{{ product.price }}积分</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
<!-- 加载与无更多提示 -->
|
||
<view class="list-footer" v-if="isLoadingMore">
|
||
<text>加载中...</text>
|
||
</view>
|
||
<view class="list-footer" v-else-if="noMore && products.length > 0">
|
||
<text>没有更多了</text>
|
||
</view>
|
||
|
||
<!-- 底部导航栏 -->
|
||
<view class="bottom-nav">
|
||
<view class="nav-item active" @click="goToMyRedemption">
|
||
<view class="nav-icon">💰</view>
|
||
<text class="nav-text">我的兑换</text>
|
||
</view>
|
||
<view class="nav-divider"></view>
|
||
<view class="nav-item" @click="goToBuyPoints">
|
||
<view class="nav-icon">🛒</view>
|
||
<text class="nav-text">购买积分</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted } from 'vue'
|
||
import goods_api from '@/api/goods_api'
|
||
import docUrl from '@/utils/docUrl'
|
||
|
||
// 轮播图数据
|
||
const bannerList = ref([])
|
||
|
||
// 商品数据
|
||
const products = ref([])
|
||
const page = ref(1)
|
||
const pageSize = ref(10)
|
||
const isLoadingMore = ref(false)
|
||
const noMore = ref(false)
|
||
|
||
// 筛选相关
|
||
const tagList = ref([])
|
||
const selectedTagId = ref(null)
|
||
const selectedTagName = ref('')
|
||
const showFilterDropdown = ref(false)
|
||
|
||
// 排序相关
|
||
const showSortDropdown = ref(false)
|
||
const selectedSortValue = ref(null)
|
||
const selectedSortLabel = ref('')
|
||
const sortOptions = ref([
|
||
{ label: '积分从小到大', value: 4 },
|
||
{ label: '积分从大到小', value: 3 },
|
||
{ label: '兑换从多到少', value: 5 },
|
||
{ label: '兑换从少到多', value: 6 }
|
||
])
|
||
|
||
// 方法
|
||
const goBack = () => {
|
||
uni.navigateBack()
|
||
}
|
||
|
||
const goToSearch = () => {
|
||
uni.navigateTo({
|
||
url: '/pages_app/search/search'
|
||
})
|
||
}
|
||
|
||
const showFilter = () => {
|
||
showFilterDropdown.value = !showFilterDropdown.value
|
||
}
|
||
|
||
const showSort = () => {
|
||
showSortDropdown.value = !showSortDropdown.value
|
||
}
|
||
|
||
const onTagSelect = (tag) => {
|
||
selectedTagId.value = tag.id
|
||
selectedTagName.value = tag.name
|
||
showFilterDropdown.value = false
|
||
// 重置并拉取
|
||
products.value = []
|
||
page.value = 1
|
||
noMore.value = false
|
||
getGoodsList()
|
||
}
|
||
|
||
const onSortSelect = (opt) => {
|
||
selectedSortValue.value = opt.value
|
||
selectedSortLabel.value = opt.label
|
||
showSortDropdown.value = false
|
||
products.value = []
|
||
page.value = 1
|
||
noMore.value = false
|
||
getGoodsList()
|
||
}
|
||
|
||
const goToProductDetail = (product) => {
|
||
const id = product.uuid || product.id
|
||
uni.navigateTo({
|
||
url: `/pages_goods/productDetail/productDetail?id=${id}`
|
||
})
|
||
}
|
||
|
||
const goToMyRedemption = () => {
|
||
uni.navigateTo({
|
||
url: '/pages_goods/myRedemption/myRedemption'
|
||
})
|
||
}
|
||
|
||
const goToBuyPoints = () => {
|
||
uni.navigateTo({
|
||
url: '/pages_app/buyPoint/buyPoint'
|
||
})
|
||
}
|
||
|
||
const goToBannerDetail = (banner) => {
|
||
// 跳转到轮播图详情页面
|
||
uni.navigateTo({
|
||
url: `/pages_app/webview/webview?url=${encodeURIComponent(banner.path)}&title=${encodeURIComponent(banner.title)}`
|
||
})
|
||
}
|
||
|
||
const getGoodsNewsList = () => {
|
||
goods_api.goodsNewsList().then(res => {
|
||
console.log('轮播图数据:', res)
|
||
if (res.code === 200 && res.data && res.data.length > 0) {
|
||
// 处理轮播图数据
|
||
bannerList.value = res.data.map(item => ({
|
||
uuid: item.uuid,
|
||
title: item.title,
|
||
headImg: docUrl + item.headImg,
|
||
create_date: item.create_date,
|
||
path: item.path,
|
||
agreenum: item.agreenum,
|
||
readnum: item.readnum
|
||
}))
|
||
} else {
|
||
console.log('轮播图数据为空或请求失败')
|
||
}
|
||
}).catch(err => {
|
||
console.error('获取轮播图数据失败:', err)
|
||
uni.showToast({
|
||
title: '获取数据失败',
|
||
icon: 'none'
|
||
})
|
||
})
|
||
}
|
||
|
||
const getGoodsList = () => {
|
||
if (noMore.value || isLoadingMore.value) return
|
||
isLoadingMore.value = true
|
||
const tagParam = selectedTagId.value ? selectedTagId.value : ""
|
||
const sortParam = selectedSortValue.value ? selectedSortValue.value : 1
|
||
goods_api.goodsList({ name: '', page: page.value, sort: sortParam, tag_type: tagParam }).then(res => {
|
||
console.log('商品数据:', res)
|
||
if ((res.code === 200 || res.code === '200') && res.data && res.data.list) {
|
||
const list = res.data.list.map(item => ({
|
||
uuid: item.uuid,
|
||
title: item.name,
|
||
price: item.bonuspoints,
|
||
image: docUrl + item.img,
|
||
times: item.times,
|
||
type: item.type,
|
||
upan: item.upan
|
||
}))
|
||
products.value = products.value.concat(list)
|
||
const { isLastPage, pageNum } = res.data
|
||
page.value = pageNum + 1
|
||
noMore.value = !!isLastPage
|
||
} else {
|
||
console.log('商品列表为空或请求失败')
|
||
}
|
||
}).catch(err => {
|
||
console.error('获取商品列表失败:', err)
|
||
uni.showToast({ title: '获取商品失败', icon: 'none' })
|
||
}).finally(() => {
|
||
isLoadingMore.value = false
|
||
})
|
||
}
|
||
|
||
const getGoodsTagList = () => {
|
||
goods_api.goodsTagList({}).then(res => {
|
||
console.log('商品类型数据:', res)
|
||
if (res.code === 200 && res.data) {
|
||
tagList.value = res.data.map(i => ({ id: i.id, name: i.name }))
|
||
}
|
||
})
|
||
}
|
||
|
||
|
||
onMounted(() => {
|
||
getGoodsNewsList()
|
||
// 初始化分页并加载第一页
|
||
page.value = 1
|
||
noMore.value = false
|
||
products.value = []
|
||
getGoodsList()
|
||
getGoodsTagList()
|
||
})
|
||
|
||
</script>
|
||
|
||
<style scoped>
|
||
.point-mall-container {
|
||
width: 100%;
|
||
min-height: 100vh;
|
||
background-color: #f5f5f5;
|
||
position: relative;
|
||
padding-bottom: 80px;
|
||
}
|
||
|
||
/* 搜索按钮样式 */
|
||
.search-btn {
|
||
color: #e74c3c;
|
||
font-size: 28rpx;
|
||
padding: 10rpx;
|
||
}
|
||
|
||
/* 轮播图样式 */
|
||
.banner-section {
|
||
margin: 0;
|
||
position: relative;
|
||
}
|
||
|
||
.banner-swiper {
|
||
height: 200px;
|
||
}
|
||
|
||
.banner-item {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.banner-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(135deg, #87CEEB 0%, #98FB98 50%, #F0E68C 100%);
|
||
}
|
||
|
||
.banner-content {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 20px;
|
||
}
|
||
|
||
.banner-text {
|
||
background-color: rgba(255, 255, 255, 0.95);
|
||
padding: 6px 12px;
|
||
margin: 3px 0;
|
||
border-radius: 6px;
|
||
font-size: 13px;
|
||
font-weight: bold;
|
||
text-align: center;
|
||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.banner-text.green {
|
||
color: #27ae60;
|
||
}
|
||
|
||
.banner-text.orange {
|
||
color: #e67e22;
|
||
}
|
||
|
||
/* 轮播图指示器样式 */
|
||
.banner-swiper ::v-deep .uni-swiper-dots {
|
||
bottom: 10px;
|
||
}
|
||
|
||
.banner-swiper ::v-deep .uni-swiper-dot {
|
||
width: 8px;
|
||
height: 8px;
|
||
background-color: rgba(255, 255, 255, 0.5);
|
||
border-radius: 50%;
|
||
margin: 0 4px;
|
||
}
|
||
|
||
.banner-swiper ::v-deep .uni-swiper-dot-active {
|
||
background-color: #fff;
|
||
}
|
||
|
||
/* 筛选排序栏样式 */
|
||
.filter-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
background-color: #fff;
|
||
padding: 12px 16px;
|
||
border-bottom: 1px solid #eee;
|
||
}
|
||
|
||
.dropdown {
|
||
background: #fff;
|
||
border-top: 1px solid #eee;
|
||
border-bottom: 1px solid #eee;
|
||
padding: 8px 0;
|
||
}
|
||
|
||
.dropdown-item {
|
||
padding: 12px 16px;
|
||
font-size: 14px;
|
||
color: #333;
|
||
}
|
||
|
||
.dropdown-item.active {
|
||
color: #e74c3c;
|
||
font-weight: bold;
|
||
background: #fff7f7;
|
||
}
|
||
|
||
.filter-item {
|
||
display: flex;
|
||
align-items: center;
|
||
flex: 1;
|
||
justify-content: center;
|
||
padding: 8px;
|
||
}
|
||
|
||
.filter-text {
|
||
font-size: 14px;
|
||
color: #333;
|
||
margin-right: 4px;
|
||
}
|
||
|
||
.filter-icon {
|
||
font-size: 10px;
|
||
color: #666;
|
||
}
|
||
|
||
.filter-divider {
|
||
width: 1px;
|
||
height: 20px;
|
||
background-color: #ddd;
|
||
}
|
||
|
||
/* 商品网格样式 */
|
||
.product-grid {
|
||
height: calc(100vh - 320rpx); /* 预留顶部导航/筛选及底部栏 */
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
.grid-wrap {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
padding: 16px;
|
||
gap: 16px;
|
||
}
|
||
|
||
.list-footer {
|
||
text-align: center;
|
||
color: #999;
|
||
padding: 12px 0 80px; /* 避开底部吸底栏 */
|
||
}
|
||
|
||
.product-item {
|
||
width: calc(50% - 8px);
|
||
background-color: #fff;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
transition: transform 0.2s ease;
|
||
}
|
||
|
||
.product-item:active {
|
||
transform: scale(0.98);
|
||
}
|
||
|
||
.product-image-container {
|
||
width: 100%;
|
||
height: 120px;
|
||
overflow: hidden;
|
||
position: relative;
|
||
}
|
||
|
||
.product-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
background-color: #f0f0f0;
|
||
}
|
||
|
||
.product-info {
|
||
padding: 12px;
|
||
}
|
||
|
||
.product-title {
|
||
font-size: 12px;
|
||
color: #333;
|
||
line-height: 1.4;
|
||
margin-bottom: 8px;
|
||
display: block;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.product-price {
|
||
font-size: 14px;
|
||
color: #e74c3c;
|
||
font-weight: bold;
|
||
}
|
||
|
||
/* 底部导航栏样式 */
|
||
.bottom-nav {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: 60px;
|
||
background-color: #20b2aa;
|
||
display: flex;
|
||
align-items: center;
|
||
z-index: 1000;
|
||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.nav-item {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 8px;
|
||
transition: background-color 0.2s ease;
|
||
}
|
||
|
||
.nav-item.active {
|
||
background-color: rgba(255, 255, 255, 0.15);
|
||
border-radius: 8px;
|
||
margin: 4px;
|
||
}
|
||
|
||
.nav-icon {
|
||
font-size: 20px;
|
||
margin-bottom: 2px;
|
||
}
|
||
|
||
.nav-text {
|
||
font-size: 12px;
|
||
color: #fff;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.nav-divider {
|
||
width: 1px;
|
||
height: 40px;
|
||
background-color: rgba(255, 255, 255, 0.3);
|
||
}
|
||
</style>
|