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