mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-11-13 20:38:54 +08:00
fix(buyer): 优化秒杀页面布局与顶部搜索栏样式统一
- 增加秒杀顶部氛围横幅与数字倒计时模块 - 修复时间轴 Tab 场次少时异常拉伸撑满的问题,支持单场次居中对齐 - 重构秒杀商品网格与卡片排版,统一两行截断、进度条占位与按钮状态 - 顶部搜索栏统一引用 SearchHeader,规范整站搜索组件蓝色基调
This commit is contained in:
@@ -449,12 +449,12 @@ onUnmounted(() => {
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $theme_color;
|
color: #409EFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: $theme_color;
|
background: #409EFF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,70 +1,134 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="seckill-list">
|
<div class="seckill-list">
|
||||||
<BaseHeader></BaseHeader>
|
<BaseHeader></BaseHeader>
|
||||||
<Search />
|
<SearchHeader></SearchHeader>
|
||||||
<!-- 秒杀时间段 -->
|
|
||||||
<div class="promotion-decorate">限时秒杀</div>
|
<!-- 秒杀顶部氛围横幅 -->
|
||||||
<ul class="time-line">
|
<div class="seckill-hero">
|
||||||
<template v-for="(time, index) in list">
|
<div class="hero-inner">
|
||||||
<li v-if="index < 5" @click="currIndex = index" :key="index" :class="{ currTimeline: currIndex === index }">
|
<div class="hero-left">
|
||||||
<div>{{ time.timeLine + ":00" }}</div>
|
<div class="hero-tag">
|
||||||
<div v-if="currIndex === index">
|
<span class="lightning">⚡</span>
|
||||||
<p>{{ nowHour >= time.timeLine ? "秒杀中" : "即将开始" }}</p>
|
<span>限时特惠 · 爆款直降</span>
|
||||||
<p>
|
|
||||||
{{ nowHour >= time.timeLine ? "距结束" : "距开始" }} {{
|
|
||||||
currTime
|
|
||||||
}}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="not-curr">
|
<h1 class="hero-title">限时秒杀</h1>
|
||||||
{{ nowHour >= time.timeLine ? "秒杀中" : "即将开始" }}
|
<p class="hero-subtitle">正品严选 · 限量低价 · 抢完即止</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hero-countdown" v-if="list.length && list[currIndex]">
|
||||||
|
<div class="countdown-badge">
|
||||||
|
<span class="status-dot" :class="{ 'is-live': isCurrentSessionActive }"></span>
|
||||||
|
<span>{{ isCurrentSessionActive ? "正在抢购中" : "即将准点开抢" }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="countdown-label">
|
||||||
|
{{ isCurrentSessionActive ? "距本场结束仅剩" : "距本场开始仅剩" }}
|
||||||
|
</div>
|
||||||
|
<div class="countdown-boxes">
|
||||||
|
<span class="time-block">{{ countdownHours }}</span>
|
||||||
|
<span class="time-sep">:</span>
|
||||||
|
<span class="time-block">{{ countdownMinutes }}</span>
|
||||||
|
<span class="time-sep">:</span>
|
||||||
|
<span class="time-block">{{ countdownSeconds }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 秒杀时间段导航 -->
|
||||||
|
<div class="time-line-wrap" v-if="list.length">
|
||||||
|
<ul class="time-line" :class="{ 'single-session': list.length === 1 }">
|
||||||
|
<template v-for="(time, index) in list">
|
||||||
|
<li
|
||||||
|
v-if="index < 5"
|
||||||
|
:key="index"
|
||||||
|
@click="currIndex = index"
|
||||||
|
:class="{ currTimeline: currIndex === index }"
|
||||||
|
>
|
||||||
|
<div class="timeline-hour">{{ time.timeLine + ":00" }}</div>
|
||||||
|
<div class="timeline-status">
|
||||||
|
<span class="status-pill">{{ nowHour >= time.timeLine ? "秒杀中" : "即将开始" }}</span>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 秒杀商品列表 -->
|
<!-- 秒杀商品列表 -->
|
||||||
<div class="seckill-goods-wrap">
|
<div class="seckill-goods-wrap">
|
||||||
<div class="goods-list">
|
<div v-if="goodsList.length === 0" class="empty-wrap">
|
||||||
<empty v-if="goodsList.length === 0" />
|
<empty :_Title="list.length ? '当前场次暂无秒杀商品,去其他场次看看吧' : '今日暂无秒杀活动,去首页逛逛吧'" />
|
||||||
<div v-else class="goods-show-info1" v-for="(item, index) in goodsList" :key="index"
|
</div>
|
||||||
@click="goGoodsDetail(item.skuId, item.goodsId)">
|
|
||||||
<div class="goods-show-img">
|
<div v-else class="goods-grid">
|
||||||
|
<div
|
||||||
|
class="goods-card"
|
||||||
|
v-for="(item, index) in goodsList"
|
||||||
|
:key="index"
|
||||||
|
@click="goGoodsDetail(item.skuId, item.goodsId)"
|
||||||
|
>
|
||||||
|
<div class="goods-img-box">
|
||||||
<img :src="item.goodsImage" loading="lazy" decoding="async" :alt="item.goodsName" />
|
<img :src="item.goodsImage" loading="lazy" decoding="async" :alt="item.goodsName" />
|
||||||
|
<div v-if="isSoldOut(item)" class="sold-out-overlay">
|
||||||
|
<span>已抢光</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="goods-show-price">
|
|
||||||
<span class="seckill-price text-danger">{{ $filters.unitPrice(item.price, "¥") }}</span>
|
|
||||||
<span class="origin-price">{{ $filters.unitPrice(item.originalPrice, "¥") }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="goods-show-detail">
|
|
||||||
<span>{{ item.goodsName }}</span>
|
<div class="goods-price-row">
|
||||||
|
<div class="price-main">
|
||||||
|
<span class="price-symbol">¥</span>
|
||||||
|
<span class="price-val">{{ $filters.unitPrice(item.price, "") }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="nowHour >= list[currIndex].timeLine" class="goods-show-num1">
|
<div class="price-origin">
|
||||||
<span>已售{{ salePercent(item) }}%</span>
|
{{ $filters.unitPrice(item.originalPrice, "¥") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="goods-name-row" :title="item.goodsName">
|
||||||
|
{{ item.goodsName }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="goods-progress-row">
|
||||||
|
<template v-if="nowHour >= list[currIndex].timeLine">
|
||||||
|
<span class="progress-label">已抢 {{ salePercent(item) }}%</span>
|
||||||
<el-progress
|
<el-progress
|
||||||
:show-text="false"
|
:show-text="false"
|
||||||
|
:stroke-width="6"
|
||||||
:color="'var(--theme-color, #ff3c2a)'"
|
:color="'var(--theme-color, #ff3c2a)'"
|
||||||
:percentage="salePercent(item)"
|
:percentage="salePercent(item)"
|
||||||
/>
|
/>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<span class="upcoming-tip">限量抢购 · 准点开抢</span>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="goods-show-seller">
|
|
||||||
<span>{{ item.storeName }}</span>
|
<div class="goods-store-row" :title="item.storeName">
|
||||||
|
<span class="store-name">{{ item.storeName }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="goods-seckill-btn"
|
class="goods-action-btn"
|
||||||
:class="{ 'is-disabled': !canBuy(item) }"
|
:class="{
|
||||||
|
'btn-disabled': !canBuy(item),
|
||||||
|
'btn-upcoming': nowHour < list[currIndex].timeLine,
|
||||||
|
'btn-active': canBuy(item)
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
{{ buyText(item) }}
|
{{ buyText(item) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<BaseFooter />
|
<BaseFooter />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onBeforeUnmount, onMounted, ref, watch } from "vue";
|
import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import { seckillByDay } from "@/api/promotion";
|
import { seckillByDay } from "@/api/promotion";
|
||||||
|
import SearchHeader from "@/components/indexDecorate/components/SearchHeader.vue";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@@ -72,10 +136,19 @@ const list = ref([]); // 秒杀时段列表
|
|||||||
const goodsList = ref([]); // 商品列表
|
const goodsList = ref([]); // 商品列表
|
||||||
let interval = null; // 定时器
|
let interval = null; // 定时器
|
||||||
const currIndex = ref(0); // 当前时间段的下标
|
const currIndex = ref(0); // 当前时间段的下标
|
||||||
const currTime = ref(0); // 当前显示的倒计时
|
const currTime = ref("00:00:00"); // 当前显示的倒计时格式字符串
|
||||||
const diffSeconds = ref(0); // 倒计时时间戳
|
const countdownHours = ref("00");
|
||||||
|
const countdownMinutes = ref("00");
|
||||||
|
const countdownSeconds = ref("00");
|
||||||
|
const diffSeconds = ref(0); // 倒计时时间戳差值
|
||||||
const nowHour = ref(new Date().getHours()); // 当前小时数
|
const nowHour = ref(new Date().getHours()); // 当前小时数
|
||||||
|
|
||||||
|
// 当前选中场次是否处于开抢中
|
||||||
|
const isCurrentSessionActive = computed(() => {
|
||||||
|
if (!list.value.length || !list.value[currIndex.value]) return false;
|
||||||
|
return nowHour.value >= list.value[currIndex.value].timeLine;
|
||||||
|
});
|
||||||
|
|
||||||
watch(currIndex, (val) => {
|
watch(currIndex, (val) => {
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
interval = null;
|
interval = null;
|
||||||
@@ -85,26 +158,22 @@ watch(currIndex, (val) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
watch(diffSeconds, (val) => {
|
watch(diffSeconds, (val) => {
|
||||||
const hours = Math.floor(val / 3600);
|
|
||||||
// 当前秒数 / 60,向下取整
|
|
||||||
// 获取到所有分钟数 3600 / 60 = 60分钟
|
|
||||||
// 对60取模,超过小时数的分钟数
|
|
||||||
const minutes = Math.floor(val / 60) % 60;
|
|
||||||
// 当前的秒数 % 60,获取到 超过小时数、分钟数的秒数(秒数)
|
|
||||||
const seconds = val % 60;
|
|
||||||
currTime.value =
|
|
||||||
filteTime(hours) + ":" + filteTime(minutes) + ":" + filteTime(seconds);
|
|
||||||
if (val <= 0) {
|
if (val <= 0) {
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
interval = null;
|
interval = null;
|
||||||
|
countdownHours.value = "00";
|
||||||
|
countdownMinutes.value = "00";
|
||||||
|
countdownSeconds.value = "00";
|
||||||
|
currTime.value = "00:00:00";
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
function filteTime (time) {
|
const hours = Math.floor(val / 3600);
|
||||||
if (time < 10) {
|
const minutes = Math.floor(val / 60) % 60;
|
||||||
return "0" + time;
|
const seconds = val % 60;
|
||||||
} else {
|
countdownHours.value = hours < 10 ? "0" + hours : String(hours);
|
||||||
return time;
|
countdownMinutes.value = minutes < 10 ? "0" + minutes : String(minutes);
|
||||||
}
|
countdownSeconds.value = seconds < 10 ? "0" + seconds : String(seconds);
|
||||||
}
|
currTime.value = `${countdownHours.value}:${countdownMinutes.value}:${countdownSeconds.value}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
function getListByDay () {
|
function getListByDay () {
|
||||||
@@ -120,7 +189,7 @@ function getListByDay () {
|
|||||||
|
|
||||||
function goGoodsDetail (skuId, goodsId) {
|
function goGoodsDetail (skuId, goodsId) {
|
||||||
// 跳转商品详情
|
// 跳转商品详情
|
||||||
let routeUrl = router.resolve({
|
const routeUrl = router.resolve({
|
||||||
path: "/goodsDetail",
|
path: "/goodsDetail",
|
||||||
query: { skuId, goodsId },
|
query: { skuId, goodsId },
|
||||||
});
|
});
|
||||||
@@ -135,41 +204,46 @@ function salePercent (item) {
|
|||||||
|
|
||||||
// 场次已开始,且销量达到库存
|
// 场次已开始,且销量达到库存
|
||||||
function isSoldOut (item) {
|
function isSoldOut (item) {
|
||||||
|
if (!list.value[currIndex.value]) return false;
|
||||||
return nowHour.value >= list.value[currIndex.value].timeLine && item.quantity <= item.salesNum;
|
return nowHour.value >= list.value[currIndex.value].timeLine && item.quantity <= item.salesNum;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 场次已开始且未售罄才可抢
|
// 场次已开始且未售罄才可抢
|
||||||
function canBuy (item) {
|
function canBuy (item) {
|
||||||
|
if (!list.value[currIndex.value]) return false;
|
||||||
return nowHour.value >= list.value[currIndex.value].timeLine && !isSoldOut(item);
|
return nowHour.value >= list.value[currIndex.value].timeLine && !isSoldOut(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 按钮文案:未开始 / 已售罄 / 立即抢购
|
// 按钮文案:未开始 / 已售罄 / 立即抢购
|
||||||
function buyText (item) {
|
function buyText (item) {
|
||||||
|
if (!list.value[currIndex.value]) return "即将开始";
|
||||||
if (nowHour.value < list.value[currIndex.value].timeLine) return "即将开始";
|
if (nowHour.value < list.value[currIndex.value].timeLine) return "即将开始";
|
||||||
if (isSoldOut(item)) return "已售罄";
|
if (isSoldOut(item)) return "已售罄";
|
||||||
return "立即抢购";
|
return "立即抢购";
|
||||||
}
|
}
|
||||||
|
|
||||||
function countDown (currIndex) {
|
function countDown (currIndex) {
|
||||||
// 倒计时
|
if (!list.value[currIndex]) return;
|
||||||
// 0点时间戳
|
// 倒计时:0点时间戳
|
||||||
let zeroTime = new Date(new Date().toLocaleDateString()).getTime();
|
const zeroTime = new Date(new Date().toLocaleDateString()).getTime();
|
||||||
let currTime = new Date().getTime();
|
const currTimeMs = new Date().getTime();
|
||||||
let actTime = 0;
|
let actTime = 0;
|
||||||
let nowHour = new Date().getHours(); // 当前小时数
|
const currentHour = new Date().getHours();
|
||||||
if (list.value[currIndex].timeLine > nowHour) {
|
nowHour.value = currentHour;
|
||||||
// 活动未开始
|
|
||||||
|
if (list.value[currIndex].timeLine > currentHour) {
|
||||||
|
// 活动未开始:距开始时间
|
||||||
actTime = zeroTime + list.value[currIndex].timeLine * 3600 * 1000;
|
actTime = zeroTime + list.value[currIndex].timeLine * 3600 * 1000;
|
||||||
} else if (list.value[currIndex].timeLine <= nowHour) {
|
} else {
|
||||||
// 活动进行中
|
// 活动进行中:距结束时间
|
||||||
if (currIndex === list.value.length - 1) {
|
if (currIndex === list.value.length - 1) {
|
||||||
// 如果是最后一个活动,直到24点结束
|
|
||||||
actTime = zeroTime + 24 * 3600 * 1000;
|
actTime = zeroTime + 24 * 3600 * 1000;
|
||||||
} else {
|
} else {
|
||||||
actTime = zeroTime + list.value[currIndex + 1].timeLine * 3600 * 1000;
|
actTime = zeroTime + list.value[currIndex + 1].timeLine * 3600 * 1000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
diffSeconds.value = Math.floor((actTime - currTime) / 1000);
|
|
||||||
|
diffSeconds.value = Math.max(0, Math.floor((actTime - currTimeMs) / 1000));
|
||||||
interval = setInterval(() => {
|
interval = setInterval(() => {
|
||||||
diffSeconds.value--;
|
diffSeconds.value--;
|
||||||
}, 1000);
|
}, 1000);
|
||||||
@@ -183,216 +257,434 @@ onBeforeUnmount(() => {
|
|||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "../../assets/styles/goodsList.scss";
|
|
||||||
|
|
||||||
.seckill-list {
|
.seckill-list {
|
||||||
background: #f5f5f5;
|
background: #f7f8fa;
|
||||||
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.promotion-decorate {
|
/* 顶部氛围 Hero 横幅 */
|
||||||
width: auto;
|
.seckill-hero {
|
||||||
margin: 28px auto 16px;
|
background: linear-gradient(135deg, #e52d27 0%, #b31217 60%, #790c10 100%);
|
||||||
color: #171A26;
|
color: #fff;
|
||||||
font-size: 22px;
|
padding: 30px 0 38px;
|
||||||
font-weight: 600;
|
box-shadow: inset 0 -4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
|
||||||
&::before,
|
.hero-inner {
|
||||||
&::after {
|
width: 1200px;
|
||||||
display: none;
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-left {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.hero-tag {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 3px 10px;
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 13px;
|
||||||
|
width: fit-content;
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
|
||||||
|
.lightning {
|
||||||
|
color: #ffeb3b;
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hero-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 34px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-subtitle {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-countdown {
|
||||||
|
background: rgba(0, 0, 0, 0.25);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px 24px;
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.18);
|
||||||
|
|
||||||
|
.countdown-badge {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
.status-dot {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #ffc107;
|
||||||
|
|
||||||
|
&.is-live {
|
||||||
|
background: #4caf50;
|
||||||
|
box-shadow: 0 0 8px #4caf50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.countdown-label {
|
||||||
|
font-size: 13px;
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.countdown-boxes {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
|
||||||
|
.time-block {
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 38px;
|
||||||
|
height: 38px;
|
||||||
|
line-height: 38px;
|
||||||
|
text-align: center;
|
||||||
|
background: #1e1e1e;
|
||||||
|
color: #ffeb3b;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-family: Consolas, Monaco, monospace;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0 4px;
|
||||||
|
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-sep {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 时间轴导航 */
|
||||||
|
.time-line-wrap {
|
||||||
|
width: 1200px;
|
||||||
|
margin: -18px auto 0;
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
.time-line {
|
.time-line {
|
||||||
width: 1200px;
|
list-style: none;
|
||||||
max-width: 1200px;
|
padding: 0;
|
||||||
height: auto;
|
margin: 0;
|
||||||
margin: 0 auto;
|
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.06);
|
||||||
display: flex;
|
display: flex;
|
||||||
box-sizing: border-box;
|
|
||||||
border-radius: 8px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
&.single-session {
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
li {
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
width: auto;
|
min-width: 160px;
|
||||||
min-height: 78px;
|
max-width: 240px;
|
||||||
padding: 12px 20px;
|
height: 68px;
|
||||||
|
padding: 10px 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
text-align: center;
|
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
font-size: 18px;
|
cursor: pointer;
|
||||||
font-weight: 600;
|
background: #fff;
|
||||||
color: #171A26;
|
color: #333;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
position: relative;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
& + li {
|
& + li {
|
||||||
box-shadow: inset 1px 0 0 #f2f2f2;
|
box-shadow: inset 1px 0 0 #f0f0f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover:not(.currTimeline) {
|
||||||
cursor: pointer;
|
|
||||||
background: #fafafa;
|
background: #fafafa;
|
||||||
|
color: var(--theme-color, #e52d27);
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
.timeline-hour {
|
||||||
margin: 0;
|
font-size: 19px;
|
||||||
font-size: 13px;
|
font-weight: 700;
|
||||||
font-weight: 400;
|
line-height: 22px;
|
||||||
line-height: 18px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.not-curr {
|
.timeline-status {
|
||||||
border: none;
|
.status-pill {
|
||||||
border-radius: 0;
|
display: inline-block;
|
||||||
padding: 0;
|
font-size: 12px;
|
||||||
margin: 0;
|
padding: 1px 8px;
|
||||||
font-size: 13px;
|
border-radius: 10px;
|
||||||
font-weight: 400;
|
background: #f5f5f5;
|
||||||
color: #999;
|
color: #888;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.currTimeline {
|
.currTimeline {
|
||||||
background: $theme_color;
|
background: linear-gradient(135deg, var(--theme-color, #e52d27) 0%, #ff5252 100%);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
box-shadow: 0 4px 12px rgba(229, 45, 39, 0.3);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $theme_color;
|
background: linear-gradient(135deg, var(--theme-color, #e52d27) 0%, #ff5252 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
> div:nth-child(1) {
|
.timeline-hour {
|
||||||
font-size: 22px;
|
font-size: 21px;
|
||||||
line-height: 28px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
> div:nth-child(2) {
|
.timeline-status {
|
||||||
margin-left: 0;
|
.status-pill {
|
||||||
}
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
|
||||||
.not-curr {
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 秒杀商品容器 */
|
||||||
.seckill-goods-wrap {
|
.seckill-goods-wrap {
|
||||||
width: 1200px;
|
width: 1200px;
|
||||||
max-width: 1200px;
|
margin: 20px auto 50px;
|
||||||
margin: 16px auto 40px;
|
|
||||||
padding: 16px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 24px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-height: 380px;
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.04);
|
||||||
|
|
||||||
|
.empty-wrap {
|
||||||
|
padding: 60px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 商品卡片 */
|
||||||
|
.goods-card {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ebebeb;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
min-height: 320px;
|
|
||||||
|
|
||||||
.goods-list {
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
column-gap: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.empty) {
|
|
||||||
margin: 48px 0;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.goods-show-info1 {
|
|
||||||
width: calc(25% - 12px);
|
|
||||||
min-width: 0;
|
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
margin: 0 0 16px;
|
|
||||||
position: relative;
|
|
||||||
border: 1px solid #f0f0f0;
|
|
||||||
border-radius: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: #fff;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.25s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||||
|
position: relative;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: #eee;
|
border-color: #ffd0d0;
|
||||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.06);
|
transform: translateY(-4px);
|
||||||
}
|
box-shadow: 0 10px 24px rgba(229, 45, 39, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
.goods-show-img {
|
.goods-img-box {
|
||||||
display: flex;
|
position: relative;
|
||||||
justify-content: center;
|
width: 100%;
|
||||||
|
aspect-ratio: 1;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #fafafa;
|
background: #f9f9f9;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: 100%;
|
||||||
aspect-ratio: 1;
|
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover img {
|
||||||
|
transform: scale(1.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sold-out-overlay {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.55);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 6px 14px;
|
||||||
|
border-radius: 20px;
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
color: #333;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.goods-show-price {
|
.goods-price-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
margin-top: 10px;
|
margin-top: 12px;
|
||||||
|
|
||||||
|
.price-main {
|
||||||
|
color: var(--theme-color, #e52d27);
|
||||||
|
font-weight: 800;
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
|
||||||
|
.price-symbol {
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.origin-price {
|
.price-val {
|
||||||
color: #999;
|
font-size: 22px;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.price-origin {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
color: #aaa;
|
||||||
text-decoration: line-through;
|
text-decoration: line-through;
|
||||||
}
|
}
|
||||||
|
|
||||||
.goods-show-detail {
|
|
||||||
height: 40px;
|
|
||||||
margin: 8px 0;
|
|
||||||
color: #333;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.goods-show-num1 {
|
.goods-name-row {
|
||||||
|
margin-top: 8px;
|
||||||
|
height: 40px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
color: #333;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
word-break: break-all;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-progress-row {
|
||||||
|
margin-top: 10px;
|
||||||
|
height: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-bottom: 8px;
|
color: #777;
|
||||||
color: #666;
|
|
||||||
|
|
||||||
span {
|
.progress-label {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-progress) {
|
:deep(.el-progress) {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.goods-show-seller {
|
.upcoming-tip {
|
||||||
margin-bottom: 10px;
|
color: #e67e22;
|
||||||
color: #999;
|
font-size: 12px;
|
||||||
font-size: 13px;
|
background: #fff8f0;
|
||||||
}
|
padding: 2px 8px;
|
||||||
|
|
||||||
.goods-seckill-btn {
|
|
||||||
margin-top: auto;
|
|
||||||
height: 34px;
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
color: #fff;
|
}
|
||||||
text-align: center;
|
}
|
||||||
line-height: 34px;
|
|
||||||
font-size: 14px;
|
|
||||||
background-color: $theme_color;
|
|
||||||
|
|
||||||
&.is-disabled {
|
.goods-store-row {
|
||||||
background: #c8c8c8;
|
margin-top: 8px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
.store-name {
|
||||||
|
&:hover {
|
||||||
|
color: var(--theme-color, #e52d27);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.goods-action-btn {
|
||||||
|
margin-top: auto;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 6px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&.btn-active {
|
||||||
|
background: linear-gradient(135deg, var(--theme-color, #e52d27) 0%, #ff5252 100%);
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 4px 10px rgba(229, 45, 39, 0.25);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.92;
|
||||||
|
box-shadow: 0 6px 14px rgba(229, 45, 39, 0.35);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-upcoming {
|
||||||
|
background: #ff9800;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.92;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.btn-disabled {
|
||||||
|
background: #e0e0e0;
|
||||||
|
color: #888;
|
||||||
|
cursor: not-allowed;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user