baimacms/pages/category/category_detail.vue

608 lines
16 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="detail-page-container">
<!-- 沉浸式顶部标题栏 -->
<view class="nav-header">
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
<view class="header-content">
<view class="back-area" @click="goBack">
<text class="back-icon"></text>
</view>
<view class="title-area">
<text class="title-text">{{ cateName }}</text>
</view>
<view class="placeholder-area"></view>
</view>
</view>
<scroll-view scroll-y class="scroll-content" @scrolltolower="loadMore">
<!-- 占位适配固定头部 -->
<view class="header-placeholder" :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
<!-- 排序筛选栏 -->
<view class="filter-bar">
<view class="filter-item" :class="{ 'active': sortType === 1 }" @click="changeSort(1)">综合</view>
<view class="filter-item" :class="{ 'active': sortType === 2 }" @click="changeSort(2)">销量</view>
<view class="filter-item" :class="{ 'active': sortType === 3 }" @click="changeSort(3)">
券后价
<view class="price-arrows">
<text class="up" :class="{ 'hl': sortType === 3 && priceOrder === 'asc' }"></text>
<text class="down" :class="{ 'hl': sortType === 3 && priceOrder === 'desc' }"></text>
</view>
</view>
</view>
<!-- 高级筛选标签栏 -->
<view class="sub-filter-row">
<view class="sub-filter-item" :class="{ 'active': filterHasCoupon }" @click="toggleFilter('coupon')">有券</view>
<view class="sub-filter-item" :class="{ 'active': filterIsFlagship }" @click="toggleFilter('flagship')">旗舰店</view>
<view class="sub-filter-item" :class="{ 'active': filterIsTmall }" @click="toggleFilter('tmall')">天猫</view>
<view class="sub-filter-item" :class="{ 'active': filterIsBrand }" @click="toggleFilter('brand')">品牌</view>
</view>
<!-- 商品列表 (横向单列风格) -->
<view class="goods-list">
<view class="goods-item" v-for="(goods, idx) in goodsList" :key="idx" @click="goToDetail(goods.id)">
<view class="g-img-left">
<image class="goods-img" :src="goods.image" mode="aspectFill"></image>
</view>
<view class="goods-info">
<view class="goods-title-row">
<image src="https://img-haodanku-com.cdn.fudaiapp.com/FlyOSTvjC3LjrkUoJ0NPxx1qnGz4" class="platform-icon"></image>
<text class="title-text">{{ goods.title }}</text>
</view>
<!-- 标签组 -->
<view class="labels-row" v-if="goods.labels && goods.labels.length > 0">
<text class="label-tag" v-for="(label, lIdx) in goods.labels.slice(0, 2)" :key="lIdx">{{ label }}</text>
</view>
<view class="goods-price-section">
<view class="price-main">
<text class="price-tip">{{ goods.couponValue > 0 ? '券后' : '抢购价' }}</text>
<text class="price-symbol">¥</text>
<text class="price-integer">{{ goods.finalPrice }}</text>
<view class="coupon-box" v-if="goods.couponValue > 0">
<text class="coupon-icon">券</text>
<text class="coupon-txt">{{ goods.couponValue }}元</text>
</view>
</view>
</view>
<view class="goods-bottom-info">
<text class="sales">已售{{ goods.sales }}件</text>
<text class="split-line">|</text>
<text class="shop-name">店铺:{{ goods.shopName }}</text>
</view>
</view>
</view>
</view>
<!-- 更多推荐分割线 -->
<view class="recommend-divider" v-if="recommendList.length > 0">
<view class="line"></view>
<text class="dot">更多推荐</text>
<view class="line"></view>
</view>
<!-- 推荐商品列表 -->
<view class="goods-list recommend-list" v-if="recommendList.length > 0">
<view class="goods-item" v-for="(goods, idx) in recommendList" :key="idx" @click="goToDetail(goods.id)">
<view class="g-img-left">
<image class="goods-img" :src="goods.image" mode="aspectFill"></image>
</view>
<view class="goods-info">
<view class="goods-title-row">
<image src="https://img-haodanku-com.cdn.fudaiapp.com/FlyOSTvjC3LjrkUoJ0NPxx1qnGz4" class="platform-icon"></image>
<text class="title-text">{{ goods.title }}</text>
</view>
<view class="labels-row" v-if="goods.labels && goods.labels.length > 0">
<text class="label-tag" v-for="(label, lIdx) in goods.labels.slice(0, 2)" :key="lIdx">{{ label }}</text>
</view>
<view class="goods-price-section">
<view class="price-main">
<text class="price-tip">{{ goods.couponValue > 0 ? '券后' : '抢购价' }}</text>
<text class="price-symbol"></text>
<text class="price-integer">{{ goods.finalPrice }}</text>
<view class="coupon-box" v-if="goods.couponValue > 0">
<text class="coupon-icon">券</text>
<text class="coupon-txt">{{ goods.couponValue }}元</text>
</view>
</view>
</view>
<view class="goods-bottom-info">
<text class="sales">已售{{ goods.sales }}件</text>
<text class="split-line">|</text>
<text class="shop-name">店铺:{{ goods.shopName }}</text>
</view>
</view>
</view>
</view>
<view class="loading-status">
<view v-if="loading" class="loading-text">加载中...</view>
<view v-else-if="goodsList.length === 0" class="empty-text">暂无相关商品</view>
<view v-else-if="finished" class="finished-text">-- 已经到底啦 --</view>
</view>
<view class="footer-placeholder"></view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
statusBarHeight: 44,
cateName: '分类详情',
mainCatId: '',
secondCategory: '', // 子分类ID串
goodsList: [],
page: 1,
sortType: 1, // 1:综合, 2:销量, 3:价格
priceOrder: 'desc',
loading: false,
finished: false,
// 高级筛选状态
filterHasCoupon: false,
filterIsFlagship: false,
filterIsTmall: false,
filterIsBrand: false,
recommendList: [], // 更多推荐列表
loadingRecommend: false
}
},
onLoad(options) {
const sysInfo = uni.getSystemInfoSync();
this.statusBarHeight = sysInfo.statusBarHeight || 44;
if (options.cate_name) this.cateName = decodeURIComponent(options.cate_name);
if (options.id) this.mainCatId = options.id;
if (options.second_category) this.secondCategory = options.second_category;
this.getProducts(true);
},
methods: {
goBack() {
uni.navigateBack();
},
getProducts(refresh = false) {
if (refresh) {
this.page = 1;
this.finished = false;
this.goodsList = [];
}
if (this.loading || this.finished) return;
this.loading = true;
// 构建排序参数 (0:综合, 3:销量, 8:券后价低到高, 9:券后价高到低)
let sortParam = 0;
if (this.sortType === 2) {
sortParam = 3;
} else if (this.sortType === 3) {
sortParam = this.priceOrder === 'asc' ? 8 : 9;
}
// 构建高级筛选参数
let filterParams = '';
if (this.filterHasCoupon) filterParams += '&filtrate_type=16';
if (this.filterIsBrand) filterParams += '&is_brand=1';
// 动态构建 shoptype: 旗舰店为1天猫为2两者都选为1,2
let shopTypes = [];
if (this.filterIsFlagship) shopTypes.push(1);
if (this.filterIsTmall) shopTypes.push(2);
if (shopTypes.length > 0) {
filterParams += `&shoptype=${shopTypes.join(',')}`;
}
uni.request({
url: `https://api.cmspro.haodanku.com/find/allItemList?category_id=${this.mainCatId}&son_category=${encodeURIComponent(this.secondCategory)}&page=${this.page}&sort=${sortParam}&page_size=20&cid=YsWZ21tx${filterParams}`,
success: (res) => {
if (res.data && res.data.code === 200 && res.data.data && res.data.data.item_info) {
const list = res.data.data.item_info.map(item => ({
id: item.id,
image: item.itempic,
title: item.itemshorttitle && item.itemshorttitle.length > 18 ? item.itemshorttitle.substring(0, 18) + '...' : item.itemshorttitle,
finalPrice: item.itemendprice,
couponValue: item.couponmoney || 0,
sales: item.itemsale >= 10000 ? (item.itemsale / 10000).toFixed(1) + '万' : item.itemsale,
shopType: item.shoptype === 'B' ? '天猫' : '淘宝',
shopName: item.shopname,
labels: item.label || []
}));
if (list.length === 0) {
this.finished = true;
} else {
this.goodsList = [...this.goodsList, ...list];
this.page++;
if (list.length < 20) {
this.finished = true;
}
}
// 兜底逻辑:如果主列表数据少于 10 条且是首屏加载,则请求推荐列表
if (refresh && this.goodsList.length < 10) {
this.getRecommendList();
}
} else {
this.finished = true;
if (refresh) this.getRecommendList();
}
},
complete: () => {
this.loading = false;
}
});
},
getRecommendList() {
if (this.loadingRecommend) return;
this.loadingRecommend = true;
// 构建推荐接口的高级筛选参数
let recommendParams = '';
if (this.filterHasCoupon) recommendParams += '&is_coupon=1';
if (this.filterIsFlagship) recommendParams += '&is_tmall=1';
if (this.filterIsTmall) recommendParams += '&min_id=1';
uni.request({
url: `https://api.cmspro.haodanku.com/superSearch/getList?sort=0&page_size=20&category_id=${this.mainCatId}&son_category=${encodeURIComponent(this.secondCategory)}&cid=YsWZ21tx${recommendParams}`,
success: (res) => {
if (res.data && res.data.code === 200 && res.data.data) {
const list = res.data.data.map(item => ({
id: item.id || item.itemid,
image: item.itempic,
title: item.itemshorttitle && item.itemshorttitle.length > 18 ? item.itemshorttitle.substring(0, 18) + '...' : item.itemshorttitle,
finalPrice: item.itemendprice,
couponValue: item.couponmoney || 0,
sales: item.itemsale >= 10000 ? (item.itemsale / 10000).toFixed(1) + '万' : item.itemsale,
shopType: item.shoptype === 'B' ? '天猫' : '淘宝',
shopName: item.shopname,
labels: item.label || (item.couponmoney > 0 ? [`${item.couponmoney}元券`] : [])
}));
this.recommendList = list;
}
},
complete: () => {
this.loadingRecommend = false;
}
});
},
changeSort(type) {
if (type === 3 && this.sortType === 3) {
this.priceOrder = this.priceOrder === 'asc' ? 'desc' : 'asc';
} else {
this.sortType = type;
if (type === 3) this.priceOrder = 'desc';
}
this.getProducts(true);
},
toggleFilter(type) {
if (type === 'coupon') this.filterHasCoupon = !this.filterHasCoupon;
if (type === 'flagship') this.filterIsFlagship = !this.filterIsFlagship;
if (type === 'tmall') this.filterIsTmall = !this.filterIsTmall;
if (type === 'brand') this.filterIsBrand = !this.filterIsBrand;
this.getProducts(true);
},
loadMore() {
this.getProducts();
},
goToDetail(id) {
uni.navigateTo({
url: `/pages/detail/detail?id=${id}`
});
}
}
}
</script>
<style scoped>
.detail-page-container {
width: 100%;
height: 100vh;
background-color: #f5f6f8;
display: flex;
flex-direction: column;
}
/* 顶部导航 */
.nav-header {
position: fixed;
top: 0;
left: 0;
right: 0;
background-color: #ffffff;
z-index: 100;
box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.05);
}
.header-content {
height: 44px;
display: flex;
align-items: center;
padding: 0 30rpx;
}
.back-area {
width: 60rpx;
display: flex;
align-items: center;
}
.back-icon {
font-size: 36rpx;
color: #333;
font-weight: bold;
}
.title-area {
flex: 1;
text-align: center;
}
.title-text {
font-size: 32rpx;
color: #333;
font-weight: bold;
}
.placeholder-area {
width: 60rpx;
}
.scroll-content {
flex: 1;
height: 100%;
}
.header-placeholder {
width: 100%;
}
/* 筛选栏 */
.filter-bar {
display: flex;
background: #ffffff;
height: 88rpx;
align-items: center;
border-bottom: 1rpx solid #f1f1f1;
position: sticky;
top: 0;
z-index: 90;
}
.filter-item {
flex: 1;
text-align: center;
font-size: 28rpx;
color: #666;
display: flex;
align-items: center;
justify-content: center;
}
.filter-item.active {
color: #ff416c;
font-weight: bold;
}
/* 高级筛选标签栏 */
.sub-filter-row {
display: flex;
background: #ffffff;
padding: 10rpx 20rpx 20rpx;
align-items: center;
}
.sub-filter-item {
padding: 6rpx 20rpx;
background: #f5f6f8;
color: #333;
font-size: 24rpx;
border-radius: 8rpx;
margin-right: 16rpx;
transition: all 0.2s;
}
.sub-filter-item.active {
background: linear-gradient(to right, #ff758c, #ff416c);
color: #ffffff;
font-weight: 500;
}
.price-arrows {
display: flex;
flex-direction: column;
margin-left: 6rpx;
line-height: 1;
}
.price-arrows text {
font-size: 16rpx;
color: #ccc;
}
.price-arrows text.hl {
color: #ff416c;
}
/* 商品列表 */
.goods-list {
padding: 0;
background: #ffffff;
}
.goods-item {
display: flex;
padding: 24rpx;
border-bottom: 1rpx solid #f8f8f8;
background: #ffffff;
}
.g-img-left {
flex-shrink: 0;
width: 240rpx;
height: 240rpx;
border-radius: 12rpx;
overflow: hidden;
background: #f0f0f0;
}
.goods-img {
width: 100%;
height: 100%;
}
.goods-info {
flex: 1;
margin-left: 20rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 4rpx 0;
}
.goods-title-row {
display: flex;
align-items: center;
line-height: 1.4;
}
.platform-icon {
width: 26rpx;
height: 26rpx;
margin-right: 8rpx;
flex-shrink: 0;
}
.title-text {
font-size: 22rpx;
color: #333;
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
}
.labels-row {
display: flex;
flex-wrap: wrap;
margin-top: 12rpx;
}
.label-tag {
font-size: 20rpx;
color: #ff416c;
background: #fff5f7;
border: 1rpx solid #ffd6de;
padding: 2rpx 12rpx;
border-radius: 20rpx;
margin-right: 12rpx;
margin-bottom: 8rpx;
}
.goods-price-section {
margin-top: auto;
padding-bottom: 8rpx;
}
.price-main {
display: flex;
align-items: baseline;
}
.price-tip {
font-size: 24rpx;
color: #ff416c;
margin-right: 4rpx;
}
.price-symbol {
font-size: 24rpx;
color: #ff416c;
font-weight: bold;
}
.price-integer {
font-size: 40rpx;
color: #ff416c;
font-weight: bold;
margin-right: 12rpx;
}
.coupon-box {
display: flex;
align-items: center;
background: #ff416c;
border-radius: 4rpx;
overflow: hidden;
height: 32rpx;
}
.coupon-icon {
font-size: 20rpx;
color: #ffffff;
background: rgba(255,255,255,0.2);
padding: 0 6rpx;
height: 100%;
display: flex;
align-items: center;
}
.coupon-txt {
font-size: 20rpx;
color: #ffffff;
padding: 0 8rpx;
}
.goods-bottom-info {
display: flex;
align-items: center;
font-size: 22rpx;
color: #999;
margin-top: 8rpx;
}
.split-line {
margin: 0 10rpx;
color: #eee;
}
.loading-status {
text-align: center;
padding: 40rpx 0;
font-size: 24rpx;
color: #999;
}
/* 更多推荐分割线 */
.recommend-divider {
display: flex;
align-items: center;
justify-content: center;
padding: 30rpx 0;
background-color: #f8f8f8;
}
.recommend-divider .line {
width: 100rpx;
height: 1rpx;
background-color: #ddd;
}
.recommend-divider .dot {
margin: 0 30rpx;
font-size: 28rpx;
color: #999;
}
.recommend-list {
background: #ffffff;
}
.footer-placeholder {
height: 40rpx;
}
</style>