728 lines
16 KiB
Vue
728 lines
16 KiB
Vue
<template>
|
||
<view class="point-mall-container">
|
||
<!-- 顶部导航栏 -->
|
||
<view class="navbox">
|
||
<view class="status_bar"></view>
|
||
<uni-nav-bar
|
||
left-icon="left"
|
||
title="积分商城"
|
||
@clickLeft="goBack"
|
||
color="#8B2316"
|
||
:border="false"
|
||
backgroundColor="#eeeeee"
|
||
>
|
||
<template v-slot:right>
|
||
<view class="nav-actions">
|
||
<view class="btn" @click="goToSearch">搜索</view>
|
||
</view>
|
||
</template>
|
||
</uni-nav-bar>
|
||
</view>
|
||
<!-- <uni-nav-bar
|
||
left-icon="left"
|
||
title="积分商城"
|
||
@clickLeft="goBack"
|
||
fixed
|
||
color="#e74c3c"
|
||
height="180rpx"
|
||
:border="false"
|
||
backgroundColor="#ffffff"
|
||
>
|
||
<template v-slot:right>
|
||
<text class="search-btn" @click="goToSearch">搜索</text>
|
||
</template>
|
||
</uni-nav-bar> -->
|
||
|
||
<!-- 轮播图横幅 -->
|
||
<view class="banner-section">
|
||
<view class="swipemask">
|
||
<view class="banner-subtitle" v-if="bannerList.length > 0">{{
|
||
bannerList[currentBannerIndex].title
|
||
}}</view>
|
||
<view class="dotbox">
|
||
<view
|
||
class="circle"
|
||
:class="{ active: currentBannerIndex == index }"
|
||
v-for="(banner, index) in bannerList"
|
||
:key="banner.id"
|
||
></view>
|
||
</view>
|
||
</view>
|
||
<swiper
|
||
class="banner-swiper"
|
||
:indicator-dots="true"
|
||
:autoplay="true"
|
||
:interval="3000"
|
||
:duration="500"
|
||
indicator-active-color="#8B2316"
|
||
@change="onSwiperChange"
|
||
>
|
||
<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 tips">
|
||
<view class="banner-text green">{{ banner.title }}</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" :class="{ active: selectedTagName }">{{
|
||
selectedTagName || "筛选"
|
||
}}</text>
|
||
<up-image
|
||
:src="selectedTagName ? filterOn : filter"
|
||
width="26rpx"
|
||
height="26rpx"
|
||
></up-image>
|
||
</view>
|
||
<view class="filter-divider"></view>
|
||
<view class="filter-item" @click="showSort">
|
||
<text class="filter-text" :class="{ active: selectedSortValue }">{{
|
||
selectedSortLabel || "排序"
|
||
}}</text>
|
||
<up-image
|
||
:src="selectedSortValue ? filterOn : filter"
|
||
width="26rpx"
|
||
height="26rpx"
|
||
></up-image>
|
||
</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> -->
|
||
<up-image :src="pointImg" width="44rpx" height="44rpx"></up-image>
|
||
<text class="nav-text">我的兑换</text>
|
||
</view>
|
||
<view class="nav-divider"></view>
|
||
<view class="nav-item" @click="goToBuyPoints">
|
||
<!-- <view class="nav-icon">🛒</view> -->
|
||
<up-image :src="buyPointImg" width="44rpx" height="44rpx"></up-image>
|
||
<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";
|
||
import pointImg from "@/static/integralticket.png";
|
||
import buyPointImg from "@/static/buy_points.png";
|
||
import filter from "@/static/triangle_normal.png";
|
||
import filterOn from "@/static/triangle_green_theme.png";
|
||
// 轮播图数据
|
||
const bannerList = ref([]);
|
||
const currentBannerIndex = ref(0);
|
||
|
||
// 商品数据
|
||
const products = ref([]);
|
||
const page = ref(1);
|
||
const pageSize = ref(10);
|
||
const isLoadingMore = ref(false);
|
||
const noMore = ref(false);
|
||
const onSwiperChange = (e) => {
|
||
currentBannerIndex.value = e.detail.current;
|
||
};
|
||
|
||
// 筛选相关
|
||
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;
|
||
showSortDropdown.value = false;
|
||
};
|
||
|
||
const showSort = () => {
|
||
showSortDropdown.value = !showSortDropdown.value;
|
||
showFilterDropdown.value = false;
|
||
};
|
||
|
||
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=${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 lang="scss">
|
||
.point-mall-container {
|
||
width: 100%;
|
||
min-height: 100vh;
|
||
background-color: #f5f5f5;
|
||
position: relative;
|
||
padding-bottom: 80px;
|
||
}
|
||
.nav-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 28rpx;
|
||
color: #8b2316;
|
||
padding: 10rpx;
|
||
}
|
||
/* 搜索按钮样式 */
|
||
.search-btn {
|
||
color: #e74c3c;
|
||
font-size: 28rpx;
|
||
padding: 10rpx;
|
||
}
|
||
|
||
/* 轮播图样式 */
|
||
.banner-section {
|
||
top: calc(var(--status-bar-height) + 44px);
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 100;
|
||
.swipemask {
|
||
width: 100%;
|
||
z-index: 3;
|
||
box-sizing: border-box;
|
||
position: absolute;
|
||
bottom: 0;
|
||
padding: 0 20rpx;
|
||
height: 80rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
background-color: rgba(0, 0, 0, 0.7);
|
||
.banner-subtitle{
|
||
color:#fff;
|
||
font-size: 28rpx;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.dotbox {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.circle {
|
||
margin: 0 4rpx;
|
||
width: 16rpx;
|
||
height: 16rpx;
|
||
background-color: #eee;
|
||
border-radius: 50%;
|
||
}
|
||
.circle.active {
|
||
background-color: #8b2316;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.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;
|
||
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
padding: 0 5px;
|
||
}
|
||
.tips {
|
||
bottom: 0;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
|
||
color: #fff;
|
||
}
|
||
|
||
.banner-text {
|
||
padding: 6px 12px;
|
||
margin: 3px 0;
|
||
border-radius: 6px;
|
||
font-size: 13px;
|
||
text-align: center;
|
||
color: #fff;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
/* .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 {
|
||
position: fixed;
|
||
top: calc(var(--status-bar-height) + 44px + 200px);
|
||
left: 0;
|
||
right: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
background-color: #fff;
|
||
padding: 0px 16px;
|
||
height: 88rpx;
|
||
border-bottom: 1px solid #eee;
|
||
}
|
||
|
||
.dropdown {
|
||
position: fixed;
|
||
top: calc(var(--status-bar-height) + 44px + 200px + 88rpx);
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 100;
|
||
background: #fff;
|
||
border-top: 1px solid #eee;
|
||
/* border-bottom: 1px solid #eee; */
|
||
}
|
||
|
||
.dropdown-item {
|
||
padding: 12px 16px;
|
||
font-size: 14px;
|
||
color: #333;
|
||
text-align: center;
|
||
border-bottom: 1px solid #eee;
|
||
}
|
||
|
||
.dropdown-item.active {
|
||
color: #8b2316;
|
||
}
|
||
|
||
.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-text.active {
|
||
color: #8b2316;
|
||
}
|
||
|
||
.filter-icon {
|
||
font-size: 10px;
|
||
color: #666;
|
||
}
|
||
|
||
.filter-divider {
|
||
width: 1px;
|
||
height: 20px;
|
||
background-color: #ddd;
|
||
}
|
||
|
||
/* 商品网格样式 */
|
||
.product-grid {
|
||
position: fixed;
|
||
top: calc(var(--status-bar-height) + 44px + 200px + 88rpx);
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 1;
|
||
height: calc(
|
||
100vh - var(--status-bar-height) - 44px - 88rpx
|
||
); /* 预留顶部导航/筛选及底部栏 */
|
||
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;
|
||
}
|
||
|
||
/* 底部导航栏样式 */
|
||
.bottom-nav {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: 50px;
|
||
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;
|
||
|
||
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: 13px;
|
||
color: #fff;
|
||
margin-left: 5rpx;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.nav-divider {
|
||
width: 1px;
|
||
height: 40px;
|
||
background-color: #fff;
|
||
}
|
||
</style>
|