yurong/components/taoke/source/rank-source.vue

754 lines
18 KiB
Vue
Raw Permalink 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="rank-container">
<!-- 沉浸式渐变头部 -->
<view class="rank-header">
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
<!-- 榜单类型切换 -->
<view class="rank-main-tabs">
<view class="main-tab" :class="{ 'active': rankType === 1 }" @click="switchRankType(1)">
<view class="tab-content-wrap">
<image class="wheat-icon wheat-left" src="/static/wheat_icon.png" v-if="rankType === 1"></image>
<text class="tab-main-txt">实时热销榜</text>
<image class="wheat-icon wheat-right" src="/static/wheat_icon.png" v-if="rankType === 1"></image>
</view>
</view>
<view class="main-tab" :class="{ 'active': rankType === 2 }" @click="switchRankType(2)">
<view class="tab-content-wrap">
<text class="tab-main-txt">全天热销榜</text>
</view>
</view>
</view>
<!-- 分类滑动导航 -->
<scroll-view scroll-x class="cate-nav" :show-scrollbar="false">
<view class="cate-item" :class="{ 'active': currentCateId === item.id }"
v-for="(item, index) in categoryList" :key="index" @click="switchCategory(item.id)">
<text class="cate-txt">{{ item.name }}</text>
</view>
</scroll-view>
</view>
<scroll-view scroll-y class="rank-content" @scrolltolower="loadMore">
<!-- 顶部背景延伸区 (仅作为背景块,不包裹卡片) -->
<view class="rank-bg-header" :style="{ height: rankHeaderHeight + 'px' }"></view>
<!-- Top 1-3 领奖台布局 (参考 shop-sy2 样式) -->
<view class="top-three-wrap" v-if="goodsList.length >= 3">
<!-- Top 2 (左侧) -->
<view class="top-card top-card-side" @click="goToDetail(goodsList[1].id)">
<view class="top-badge-wrap badge-2">
<text class="top-badge-num">TOP 2</text>
</view>
<view class="top-img-box">
<image class="top-img" :src="goodsList[1].image" mode="aspectFill"></image>
<view class="top-sales-bar">已抢{{ goodsList[1].sales }}件</view>
</view>
<view class="top-info-box">
<text class="top-name">{{ goodsList[1].title }}</text>
<view class="top-price-box">
<text class="price-label">券后¥</text>
<text class="price-val">{{ goodsList[1].finalPrice }}</text>
</view>
<view class="coupon-wrap" v-if="goodsList[1].couponValue">
<text class="coupon-label">券</text>
<text class="coupon-value">{{ goodsList[1].couponValue }}元</text>
</view>
</view>
</view>
<!-- Top 1 (中间) -->
<view class="top-card top-card-center" @click="goToDetail(goodsList[0].id)">
<image class="crown-icon" src="https://img.bc.haodanku.com/cms_web/1698115530" mode="widthFix"></image>
<view class="top-badge-wrap badge-1">
<text class="top-badge-num">TOP 1</text>
</view>
<view class="top-img-box">
<image class="top-img" :src="goodsList[0].image" mode="aspectFill"></image>
<view class="top-sales-bar">已抢{{ goodsList[0].sales }}件</view>
</view>
<view class="top-info-box">
<text class="top-name">{{ goodsList[0].title }}</text>
<view class="top-price-box">
<text class="price-label">券后¥</text>
<text class="price-val">{{ goodsList[0].finalPrice }}</text>
</view>
<view class="coupon-wrap" v-if="goodsList[0].couponValue">
<text class="coupon-label">券</text>
<text class="coupon-value">{{ goodsList[0].couponValue }}元</text>
</view>
</view>
</view>
<!-- Top 3 (右侧) -->
<view class="top-card top-card-side" @click="goToDetail(goodsList[2].id)">
<view class="top-badge-wrap badge-3">
<text class="top-badge-num">TOP 3</text>
</view>
<view class="top-img-box">
<image class="top-img" :src="goodsList[2].image" mode="aspectFill"></image>
<view class="top-sales-bar">已抢{{ goodsList[2].sales }}件</view>
</view>
<view class="top-info-box">
<text class="top-name">{{ goodsList[2].title }}</text>
<view class="top-price-box">
<text class="price-label">券后¥</text>
<text class="price-val">{{ goodsList[2].finalPrice }}</text>
</view>
<view class="coupon-wrap" v-if="goodsList[2].couponValue">
<text class="coupon-label">券</text>
<text class="coupon-value">{{ goodsList[2].couponValue }}元</text>
</view>
</view>
</view>
</view>
<!-- Top 4+ 横向列表 -->
<view class="rest-list">
<view class="rest-item" v-for="(goods, idx) in goodsList.slice(3)" :key="idx" @click="goToDetail(goods.id)">
<view class="rest-left">
<view class="rest-badge-wrap">
<image class="rest-badge-img" src="/static/rank_badge.png" mode="widthFix"></image>
<text class="rest-badge-num">{{ idx + 4 }}</text>
</view>
<image class="rest-img" :src="goods.image" mode="aspectFill"></image>
</view>
<view class="rest-right">
<view class="rest-title-row">
<image src="https://img-haodanku-com.cdn.fudaiapp.com/FlyOSTvjC3LjrkUoJ0NPxx1qnGz4" class="p-icon"></image>
<text class="rest-name">{{ goods.title }}</text>
</view>
<view class="rest-price-row">
<text class="rest-price-tip">券后¥</text>
<text class="rest-price-val">{{ goods.finalPrice }}</text>
<text class="rest-price-old">¥{{ goods.originalPrice }}</text>
</view>
<view class="coupon-wrap" v-if="goods.couponValue">
<text class="coupon-label">券</text>
<text class="coupon-value">{{ goods.couponValue }}元</text>
</view>
<view class="rest-buy-bar">
<view class="btn-prev">
<text class="rest-buy-txt">近2小时已抢 <text class="rest-buy-num">{{ goods.itemsale2 }}</text> 件</text>
</view>
<view class="btn-next">马上抢</view>
</view>
</view>
</view>
</view>
<view class="loading-status" v-if="loading">火速加载中...</view>
<view class="footer-placeholder"></view>
</scroll-view>
<!-- 底部导航 -->
<bottom-nav :activeTab="1"></bottom-nav>
</view>
</template>
<script>
import BottomNav from '@/components/bottom-nav/bottom-nav.vue';
import http from '@/request/request.js';
export default {
components: {
BottomNav
},
data() {
return {
statusBarHeight: 44,
rankType: 1,
currentCateId: 0,
categoryList: [
{ id: 0, name: '全部' },
{ id: 10, name: '居家' },
{ id: 11, name: '美食' },
{ id: 8, name: '儿童' },
{ id: 3, name: '女装' },
{ id: 4, name: '美妆' }
],
goodsList: [],
loading: false,
page: 1
}
},
computed: {
rankHeaderHeight() {
return this.statusBarHeight + 90;
}
},
onLoad() {
this.initializePage();
},
mounted() {
if (this.__taokeInitialized) return;
this.__taokeInitialized = true;
this.initializePage();
},
methods: {
initializePage() {
if (this.__pageInitialized) return;
this.__pageInitialized = true;
const sysInfo = uni.getSystemInfoSync();
this.statusBarHeight = sysInfo.statusBarHeight || 44;
this.getRankList(true);
},
switchRankType(type) {
if (this.rankType === type) return;
this.rankType = type;
this.getRankList(true);
},
switchCategory(id) {
if (this.currentCateId === id) return;
this.currentCateId = id;
this.getRankList(true);
},
getRankList(refresh = false) {
if (refresh) {
this.page = 1;
this.goodsList = [];
}
if (this.loading) return;
this.loading = true;
http.get('https://api.cmspro.haodanku.com/ranking/lists', {
type: this.rankType,
category_id: this.currentCateId,
page: this.page,
page_size: 60,
cid: 'YsWZ21tx'
})
.then(res => {
if (res.data && res.data.list_item && res.data.list_item.list) {
const list = res.data.list_item.list.map(item => {
// 处理标题
let title = item.itemshorttitle || item.itemtitle;
if (title.length > 18) {
title = title.substring(0, 18) + '...';
}
// 处理销量显示 (总销量/今日销量)
let salesCount = this.rankType === 1 ? (item.todaysale || item.itemsale2 || 0) : item.itemsale;
let salesStr = salesCount >= 10000 ? (salesCount / 10000).toFixed(1) + '万' : salesCount;
// 近2小时销量 (itemsale2)
let itemSale2 = item.itemsale2 || 0;
let itemSale2Str = itemSale2 >= 10000 ? (itemSale2 / 10000).toFixed(1) + '万' : itemSale2;
return {
id: item.id,
image: item.itempic,
title: title,
finalPrice: item.itemendprice,
originalPrice: item.itemprice,
couponValue: item.couponmoney || 0,
sales: salesStr,
itemsale2: itemSale2Str,
shopName: item.shopname,
labels: item.label || []
};
});
this.goodsList = [...this.goodsList, ...list];
}
})
.finally(() => {
this.loading = false;
});
},
loadMore() {
// 榜单通常展示前60条
},
goToDetail(id) {
uni.navigateTo({
url: `/pages/detail/detail?id=${id}`
});
}
}
}
</script>
<style scoped>
.rank-container {
width: 100%;
height: 100vh;
background-color: #f5f6f8;
display: flex;
flex-direction: column;
}
/* ========== 头部区域 ========== */
.rank-header {
position: fixed;
top: 0;
left: 0;
right: 0;
background-image: linear-gradient(90deg, rgba(255, 154, 12, 0.9), rgba(253, 36, 39, 0.9)), url("https://img.alicdn.com/bao/uploaded/O1CN015WwOJy1qPYhqS1DwG_!!6000000005488-0-yinhe.jpg_310x310");
background-size: cover;
background-position: center;
z-index: 200; /* 提升层级至最高,确保滑动时文字不被卡片遮挡 */
padding-bottom: 0;
}
.status-bar {
width: 100%;
}
.rank-main-tabs {
display: flex;
align-items: center;
justify-content: center;
height: 88rpx;
gap: 60rpx;
}
.main-tab {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
.tab-content-wrap {
display: flex;
align-items: center;
gap: 8rpx;
}
.tab-main-txt {
font-size: 30rpx;
color: rgba(255, 255, 255, 0.7);
transition: all 0.3s;
padding-bottom: 8rpx;
}
.main-tab.active .tab-main-txt {
color: #ffffff;
font-size: 34rpx;
font-weight: bold;
}
.main-tab.active::after {
content: "";
width: 40rpx;
height: 4rpx;
background: #ffffff;
border-radius: 2rpx;
margin-top: 4rpx; /* 紧贴文字下方 */
}
.wheat-icon {
width: 32rpx;
height: 44rpx;
}
.wheat-right {
transform: scaleX(-1);
}
/* 分类导航 */
.cate-nav {
white-space: nowrap;
padding: 10rpx 0 20rpx;
background: transparent;
}
.cate-item {
display: inline-block;
padding: 8rpx 32rpx;
position: relative;
}
.cate-txt {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.8);
}
.cate-item.active .cate-txt {
color: #ffffff;
font-weight: bold;
font-size: 30rpx;
}
.cate-item.active::after {
content: "";
position: absolute;
bottom: 4rpx;
left: 50%;
transform: translateX(-50%);
width: 48rpx;
height: 6rpx;
background: #ffffff;
border-radius: 3rpx;
}
/* ========== 滚动内容 ========== */
.rank-content {
flex: 1;
height: 100%;
background-color: #f5f6f8;
overflow: visible;
}
.rank-bg-header {
width: 100%;
background-image: linear-gradient(90deg, rgba(255, 154, 12, 0.9), rgba(253, 36, 39, 0.9)), url("https://img.alicdn.com/bao/uploaded/O1CN015WwOJy1qPYhqS1DwG_!!6000000005488-0-yinhe.jpg_310x310");
background-size: cover;
background-position: bottom;
}
/* ========== Top 1-3 领奖台 ========== */
.top-three-wrap {
display: flex;
align-items: flex-end;
justify-content: space-between;
padding: 60rpx 20rpx 20rpx;
margin-top: -60rpx; /* 调小负边距,防止遮挡导航文字 */
position: relative;
z-index: 110;
box-sizing: border-box;
width: 100%;
}
.top-card {
display: flex;
flex-direction: column;
align-items: center;
background: #ffffff;
border-radius: 20rpx;
overflow: visible;
position: relative;
box-shadow: 0 16rpx 40rpx rgba(0,0,0,0.15);
}
.top-card-side {
width: 31%;
height: 410rpx; /* 侧边基准高度 */
z-index: 10;
}
.top-card-center {
width: 34%;
height: 470rpx; /* 中间拔高 */
z-index: 15;
border: 2rpx solid #ffd700;
}
.crown-icon {
position: absolute;
top: -50rpx;
left: 50%;
transform: translateX(-50%);
width: 76rpx;
z-index: 100;
}
/* 荣誉徽章体系 (参考 shop-sy2) */
.top-badge-wrap {
position: absolute;
top: 0;
left: 0;
padding: 4rpx 16rpx;
border-radius: 20rpx 0 20rpx 0;
z-index: 99;
display: flex;
align-items: center;
justify-content: center;
}
.badge-1 {
background: linear-gradient(135deg, #ff4b2b, #ff416c);
}
.badge-2 {
background: linear-gradient(135deg, #4facfe, #00f2fe);
}
.badge-3 {
background: linear-gradient(135deg, #f093fb, #f5576c);
}
.top-badge-num {
font-size: 20rpx;
font-weight: 900;
color: #ffffff;
font-style: italic;
}
.top-img-box {
width: 100%;
height: 210rpx;
overflow: visible;
position: relative;
background: #f5f5f5;
}
.top-card-center .top-img-box {
height: 270rpx;
}
.top-img {
width: 100%;
height: 100%;
border-radius: 16rpx 16rpx 0 0;
}
/* 胶囊式销量标签 */
.top-sales-bar {
position: absolute;
bottom: 0;
left: 50%;
transform: translate(-50%, 50%);
background: rgba(255, 75, 43, 0.9);
color: #ffffff;
font-size: 18rpx;
white-space: nowrap;
padding: 4rpx 16rpx;
border-radius: 40rpx;
font-weight: bold;
z-index: 100;
box-shadow: 0 4rpx 12rpx rgba(255, 75, 43, 0.3);
}
.top-info-box {
padding: 24rpx 16rpx 16rpx; /* 增加左右内边距 */
width: 100%;
display: flex;
flex-direction: column;
box-sizing: border-box;
background: #ffffff;
border-radius: 0 0 20rpx 20rpx;
text-align: left; /* 切换为左对齐 */
}
.top-name {
font-size: 24rpx;
color: #333;
font-weight: bold;
margin-bottom: 8rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.top-price-box {
display: flex;
align-items: baseline;
justify-content: flex-start; /* 切换为左对齐 */
color: #ff4b2b;
}
.price-label {
font-size: 18rpx;
font-weight: bold;
}
.price-val {
font-size: 34rpx;
font-weight: 900;
}
.coupon-wrap {
display: inline-flex;
align-items: center;
justify-content: center;
margin: 8rpx 0 0; /* 移除 auto改为左对齐 */
border: 1rpx solid #ff4b2b;
border-radius: 4rpx;
overflow: hidden;
background: #ffffff;
align-self: flex-start; /* 确保在 flex 容器中左对齐 */
}
.rest-right .coupon-wrap {
margin: 8rpx 0;
align-self: flex-start;
}
.coupon-label {
background: #ff4b2b;
color: #ffffff;
font-size: 18rpx;
padding: 0 8rpx;
height: 30rpx;
line-height: 30rpx;
font-weight: bold;
}
.coupon-value {
color: #ff4b2b;
font-size: 18rpx;
padding: 0 10rpx;
height: 30rpx;
line-height: 30rpx;
font-weight: bold;
}
/* ========== Top 4+ 列表 ========== */
.rest-list {
background: #ffffff;
margin: 0 16rpx;
border-radius: 16rpx;
overflow: hidden;
}
.rest-item {
display: flex;
padding: 24rpx;
border-bottom: 1rpx solid #f5f5f5;
}
.rest-left {
flex-shrink: 0;
width: 200rpx;
height: 200rpx;
border-radius: 12rpx;
overflow: visible;
position: relative;
background: #f5f5f5;
}
.rest-badge-wrap {
position: absolute;
top: -12rpx;
left: -4rpx;
width: 50rpx;
height: 50rpx;
z-index: 99;
display: flex;
align-items: center;
justify-content: center;
}
.rest-badge-img {
width: 50rpx;
position: absolute;
}
.rest-badge-num {
position: relative;
z-index: 11;
font-size: 22rpx;
font-weight: bold;
color: #ffffff;
margin-top: -4rpx;
}
.rest-img {
width: 100%;
height: 100%;
}
.rest-right {
flex: 1;
margin-left: 20rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
min-width: 0; /* 关键:防止 Flex 子项被内容撑开导致超伸 */
}
.rest-title-row {
display: flex;
align-items: center;
}
.p-icon {
width: 26rpx;
height: 26rpx;
margin-right: 8rpx;
flex-shrink: 0;
}
.rest-name {
font-size: 26rpx;
color: #333;
font-weight: bold;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
}
.rest-price-row {
display: flex;
align-items: baseline;
margin-top: 10rpx;
}
.rest-price-tip {
font-size: 22rpx;
color: #ff4b2b;
}
.rest-price-val {
font-size: 40rpx;
color: #ff4b2b;
font-weight: bold;
margin-right: 10rpx;
}
.rest-price-old {
font-size: 22rpx;
color: #bbb;
text-decoration: line-through;
}
.rest-buy-bar {
display: flex;
align-items: center;
background: url("https://web.cms.hykefu.cn/static/img/todayBg.png") no-repeat center;
background-size: 100% 100%;
border-radius: 40rpx;
margin-top: 10rpx;
overflow: hidden;
width: 100%;
height: 56rpx;
box-sizing: border-box; /* 确保 padding 不会增加总宽度 */
}
.btn-prev {
flex: 1;
display: flex;
align-items: center;
padding-left: 20rpx;
height: 100%;
}
.rest-buy-txt {
padding-left: 15rpx;
font-size: 20rpx;
color: #ff6534;
white-space: nowrap;
}
.rest-buy-num {
font-weight: bold;
color: #ff4b2b;
margin: 0 4rpx;
}
.btn-next {
width: 150rpx; /* 调整宽度以对齐图片中的按钮区域 */
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: transparent; /* 背景透明,直接使用底图效果 */
color: #ffffff !important;
font-size: 24rpx;
font-weight: bold;
flex-shrink: 0;
}
/* ========== 其他 ========== */
.loading-status {
text-align: center;
padding: 40rpx 0;
font-size: 24rpx;
color: #999;
}
.footer-placeholder {
height: 140rpx;
}
</style>