baimacms/pages/category/category_detail.vue

503 lines
12 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="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
}
},
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=qOstW90${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;
}
}
} else {
this.finished = true;
}
},
complete: () => {
this.loading = 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: flex-start;
line-height: 1.4;
}
.platform-icon {
width: 32rpx;
height: 32rpx;
margin-top: 6rpx;
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;
}
.footer-placeholder {
height: 40rpx;
}
</style>