很多页面

This commit is contained in:
haomingming
2025-09-03 17:13:10 +08:00
parent 92bfc36d51
commit 84f59b20f6
26 changed files with 29536 additions and 616 deletions
+555
View File
@@ -0,0 +1,555 @@
<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>