feat(sale): 添加秒杀专区,剩余库存展示

This commit is contained in:
woody 2025-07-31 17:59:22 +08:00
parent 3cf560dd7d
commit 09f6157991
4 changed files with 591 additions and 10 deletions

View File

@ -19,7 +19,7 @@ module.exports = vm => {
//#ifdef DEV_SERVER
console.log('DEV_SERVER')
config.baseURL = 'http://localhost:8080'
config.baseURL = 'http://192.168.0.86:8080'
//#endif
//#ifdef QA_SERVER

View File

@ -39,6 +39,7 @@
:list="item.waresList"
:title="item.specialAreaName"
size="small"
:showSales="item.specialArea"
></area-product-list>
<!-- <view class="goods-top">
<view class="title">{{ item.specialAreaName }}</view>
@ -251,7 +252,13 @@ export default {
},
],
},
{
label: '秒杀专区',
value: 14,
name: 'live',
isShow: true,
children: [],
},
{
label: '复购专区',
value: 3,
@ -282,12 +289,6 @@ export default {
name: 'cooperation',
isShow: true,
},
{
label: '直播专区',
value: 14,
name: 'live',
isShow: true,
},
{
label: '工具流',
value: 12,
@ -585,11 +586,13 @@ export default {
},
navTap(item) {
let tapx = -1
this.zoneList.forEach((items, index) => {
if (items.value == item.specialArea) {
tapx = index
}
})
if (tapx == -1) {
this.zoneList.forEach((items, index) => {
items.children.forEach(ctem => {

View File

@ -45,6 +45,39 @@
</view>
<view class="t_hei">{{ goodDetail.waresName }}</view>
</view>
<!-- 库存进度条 - 预售状态 -->
<view
class="inventory-progress-container"
v-if="goodDetail.useRatio && goodDetail.inventory !== undefined"
>
<view class="inventory-header">
<view class="inventory-label">
<text class="inventory-status">库存剩余</text>
</view>
<view
class="inventory-percentage"
:class="getPercentageClass(goodDetail.inventory)"
>
{{ getInventoryPercentage(goodDetail.inventory) }}%
</view>
</view>
<view class="inventory-progress-bar">
<view
class="inventory-progress-fill"
:style="{
width: getInventoryPercentage(goodDetail.inventory) + '%',
}"
:class="getProgressClass(goodDetail.inventory)"
>
<!-- 添加流动光效 -->
<view
class="progress-shine"
v-if="getInventoryPercentage(goodDetail.inventory) <= 30"
></view>
</view>
</view>
</view>
</view>
</view>
@ -75,7 +108,9 @@
>BV:{{ goodDetail.goodsAssAchive | numberToCurrency }}</view
>
</view>
<view class="t_hui1">销量:{{ goodDetail.sales | seles }}</view>
<view v-if="!goodDetail.useRatio" class="t_hui1"
>销量:{{ goodDetail.sales | seles }}</view
>
<!-- <view class="disFlex" v-show="specialArea == 31">
<view class="t_hui1">统一零售价:{{ goodDetail.retailPrice | numberToCurrency | isLocal }}</view>
</view> -->
@ -85,6 +120,39 @@
</view>
<view class="t_hei">{{ goodDetail.waresName }}</view>
</view>
<!-- 库存进度条 -->
<view
class="inventory-progress-container"
v-if="goodDetail.useRatio && goodDetail.inventory !== undefined"
>
<view class="inventory-header">
<view class="inventory-label">
<text class="inventory-status">库存剩余</text>
</view>
<view
class="inventory-percentage"
:class="getPercentageClass(goodDetail.inventory)"
>
{{ getInventoryPercentage(goodDetail.inventory) }}%
</view>
</view>
<view class="inventory-progress-bar">
<view
class="inventory-progress-fill"
:style="{
width: getInventoryPercentage(goodDetail.inventory) + '%',
}"
:class="getProgressClass(goodDetail.inventory)"
>
<!-- 添加流动光效 -->
<view
class="progress-shine"
v-if="getInventoryPercentage(goodDetail.inventory) <= 30"
></view>
</view>
</view>
</view>
</view>
<view class="kuang t_hei">
{{ '急速下单即享优先发货' }}
@ -573,6 +641,45 @@ export default {
//
return d + '天' + h + ':' + m + ':' + s
},
//
getInventoryPercentage(inventory) {
if (typeof inventory === 'string') {
//
const numStr = inventory.replace('%', '').trim()
const num = parseFloat(numStr)
return isNaN(num) ? 0 : Math.min(100, Math.max(0, num))
} else if (typeof inventory === 'number') {
// 0-100
return Math.min(100, Math.max(0, inventory))
}
return 0
},
//
getProgressClass(inventory) {
const percentage = this.getInventoryPercentage(inventory)
if (percentage >= 80) {
return 'progress-high'
} else if (percentage >= 60) {
return 'progress-medium-high'
} else if (percentage >= 40) {
return 'progress-medium'
} else if (percentage >= 20) {
return 'progress-low'
} else {
return 'progress-critical'
}
},
//
getPercentageClass(inventory) {
const percentage = this.getInventoryPercentage(inventory)
if (percentage <= 20) {
return 'percentage-critical'
} else if (percentage <= 30) {
return 'percentage-urgent'
} else {
return 'percentage-normal'
}
},
},
}
</script>
@ -970,4 +1077,194 @@ export default {
justify-content: flex-end;
margin-bottom: 10rpx;
}
/* 库存进度条样式 */
.inventory-progress-container {
margin-top: 20rpx;
.inventory-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12rpx;
.inventory-label {
display: flex;
align-items: center;
font-size: 24rpx;
.inventory-status {
color: #333;
}
}
.inventory-percentage {
font-size: 24rpx;
font-weight: 700;
font-family:
DIN Alternate,
Arial,
sans-serif;
&.percentage-critical {
color: #ff3742;
animation: percentageBlink 1s ease-in-out infinite;
text-shadow: 0 0 10rpx rgba(255, 55, 66, 0.3);
}
&.percentage-urgent {
color: #ff5252;
animation: percentageGlow 2s ease-in-out infinite;
}
&.percentage-normal {
color: #005bac;
}
}
}
.inventory-progress-bar {
width: 100%;
height: 16rpx;
background: rgba(0, 0, 0, 0.08);
border-radius: 8rpx;
overflow: hidden;
position: relative;
box-shadow: inset 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
.inventory-progress-fill {
height: 100%;
border-radius: 8rpx;
transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
/* 进度条渐变色彩 */
&.progress-critical {
background: linear-gradient(
90deg,
#ff1744 0%,
#d50000 50%,
#ff1744 100%
);
box-shadow: 0 0 20rpx rgba(255, 23, 68, 0.4);
animation: criticalPulse 1.5s ease-in-out infinite;
}
&.progress-low {
background: linear-gradient(
90deg,
#ff5722 0%,
#e64a19 50%,
#ff5722 100%
);
box-shadow: 0 0 15rpx rgba(255, 87, 34, 0.3);
}
&.progress-medium {
background: linear-gradient(
90deg,
#ff9800 0%,
#f57c00 50%,
#ff9800 100%
);
box-shadow: 0 0 10rpx rgba(255, 152, 0, 0.2);
}
&.progress-medium-high {
background: linear-gradient(
90deg,
#2196f3 0%,
#1976d2 50%,
#2196f3 100%
);
box-shadow: 0 0 8rpx rgba(33, 150, 243, 0.2);
}
&.progress-high {
background: linear-gradient(
90deg,
#4caf50 0%,
#388e3c 25%,
#005bac 75%,
#003d82 100%
);
box-shadow: 0 0 8rpx rgba(0, 91, 172, 0.2);
}
/* 流动光效 */
.progress-shine {
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
transparent 0%,
rgba(255, 255, 255, 0.8) 50%,
transparent 100%
);
animation: shineFlow 2s ease-in-out infinite;
}
/* 高光效果 */
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 60%;
background: linear-gradient(
to bottom,
rgba(255, 255, 255, 0.4) 0%,
rgba(255, 255, 255, 0.1) 50%,
transparent 100%
);
border-radius: 8rpx 8rpx 0 0;
}
}
}
}
/* 动画定义 */
@keyframes percentageBlink {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.7;
}
}
@keyframes percentageGlow {
0%,
100% {
text-shadow: 0 0 5rpx rgba(255, 82, 82, 0.3);
}
50% {
text-shadow: 0 0 15rpx rgba(255, 82, 82, 0.6);
}
}
@keyframes criticalPulse {
0%,
100% {
box-shadow: 0 0 20rpx rgba(255, 23, 68, 0.4);
}
50% {
box-shadow: 0 0 30rpx rgba(255, 23, 68, 0.7);
}
}
@keyframes shineFlow {
0% {
left: -100%;
}
100% {
left: 100%;
}
}
</style>

View File

@ -103,8 +103,42 @@
<span class="qzbq" v-if="item.prefixLabelTarget != undefined">{{
item.prefixLabelTarget.label
}}</span>
{{ item.waresName }}
<span style="font-weight: 500">{{ item.waresName }}</span>
</view>
<!-- 库存进度条 -->
<view
class="inventory-progress-container"
v-if="item.useRatio && item.inventory !== undefined"
>
<view class="inventory-header">
<view class="inventory-label">
<text class="inventory-status">库存剩余</text>
</view>
<view
class="inventory-percentage"
:class="getPercentageClass(item.inventory)"
>
{{ getInventoryPercentage(item.inventory) }}%
</view>
</view>
<view class="inventory-progress-bar">
<view
class="inventory-progress-fill"
:style="{
width: getInventoryPercentage(item.inventory) + '%',
}"
:class="getProgressClass(item.inventory)"
>
<!-- 添加流动光效 -->
<view
class="progress-shine"
v-if="getInventoryPercentage(item.inventory) <= 30"
></view>
</view>
</view>
</view>
<view
class="pv"
v-if="
@ -490,6 +524,45 @@ export default {
})
}
},
//
getInventoryPercentage(inventory) {
if (typeof inventory === 'string') {
//
const numStr = inventory.replace('%', '').trim()
const num = parseFloat(numStr)
return isNaN(num) ? 0 : Math.min(100, Math.max(0, num))
} else if (typeof inventory === 'number') {
// 0-100
return Math.min(100, Math.max(0, inventory))
}
return 0
},
//
getProgressClass(inventory) {
const percentage = this.getInventoryPercentage(inventory)
if (percentage >= 80) {
return 'progress-high'
} else if (percentage >= 60) {
return 'progress-medium-high'
} else if (percentage >= 40) {
return 'progress-medium'
} else if (percentage >= 20) {
return 'progress-low'
} else {
return 'progress-critical'
}
},
//
getPercentageClass(inventory) {
const percentage = this.getInventoryPercentage(inventory)
if (percentage <= 20) {
return 'percentage-critical'
} else if (percentage <= 30) {
return 'percentage-urgent'
} else {
return 'percentage-normal'
}
},
},
}
</script>
@ -516,6 +589,8 @@ export default {
}
.tabList_a {
/* 二级分类标签容器样式 */
margin-bottom: 10rpx;
}
.index_btm {
@ -802,4 +877,210 @@ export default {
left: -4rpx;
font-size: 20rpx;
}
/* 库存进度条样式 - 升级版 */
.inventory-progress-container {
margin-top: 10rpx;
.inventory-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12rpx;
.inventory-label {
display: flex;
align-items: center;
font-size: 24rpx;
.fire-icon,
.warning-icon {
font-size: 28rpx;
margin-right: 8rpx;
animation: iconBounce 1.5s ease-in-out infinite;
}
.inventory-status {
color: #333;
}
}
.inventory-percentage {
font-size: 24rpx;
font-weight: 700;
font-family:
DIN Alternate,
Arial,
sans-serif;
&.percentage-critical {
color: #ff3742;
animation: percentageBlink 1s ease-in-out infinite;
text-shadow: 0 0 10rpx rgba(255, 55, 66, 0.3);
}
&.percentage-urgent {
color: #ff5252;
animation: percentageGlow 2s ease-in-out infinite;
}
&.percentage-normal {
color: #005bac;
}
}
}
.inventory-progress-bar {
width: 100%;
height: 16rpx;
background: rgba(0, 0, 0, 0.08);
border-radius: 8rpx;
overflow: hidden;
position: relative;
box-shadow: inset 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
.inventory-progress-fill {
height: 100%;
border-radius: 8rpx;
transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
/* 进度条渐变色彩 */
&.progress-critical {
background: linear-gradient(
90deg,
#ff1744 0%,
#d50000 50%,
#ff1744 100%
);
box-shadow: 0 0 20rpx rgba(255, 23, 68, 0.4);
animation: criticalPulse 1.5s ease-in-out infinite;
}
&.progress-low {
background: linear-gradient(
90deg,
#ff5722 0%,
#e64a19 50%,
#ff5722 100%
);
box-shadow: 0 0 15rpx rgba(255, 87, 34, 0.3);
}
&.progress-medium {
background: linear-gradient(
90deg,
#ff9800 0%,
#f57c00 50%,
#ff9800 100%
);
box-shadow: 0 0 10rpx rgba(255, 152, 0, 0.2);
}
&.progress-medium-high {
background: linear-gradient(
90deg,
#2196f3 0%,
#1976d2 50%,
#2196f3 100%
);
box-shadow: 0 0 8rpx rgba(33, 150, 243, 0.2);
}
&.progress-high {
background: linear-gradient(
90deg,
#4caf50 0%,
#388e3c 25%,
#005bac 75%,
#003d82 100%
);
box-shadow: 0 0 8rpx rgba(0, 91, 172, 0.2);
}
/* 流动光效 */
.progress-shine {
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
transparent 0%,
rgba(255, 255, 255, 0.8) 50%,
transparent 100%
);
animation: shineFlow 2s ease-in-out infinite;
}
/* 高光效果 */
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 60%;
background: linear-gradient(
to bottom,
rgba(255, 255, 255, 0.4) 0%,
rgba(255, 255, 255, 0.1) 50%,
transparent 100%
);
border-radius: 8rpx 8rpx 0 0;
}
}
}
}
/* 动画定义 */
@keyframes iconBounce {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.2);
}
}
@keyframes percentageBlink {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.7;
}
}
@keyframes percentageGlow {
0%,
100% {
text-shadow: 0 0 5rpx rgba(255, 82, 82, 0.3);
}
50% {
text-shadow: 0 0 15rpx rgba(255, 82, 82, 0.6);
}
}
@keyframes criticalPulse {
0%,
100% {
box-shadow: 0 0 20rpx rgba(255, 23, 68, 0.4);
}
50% {
box-shadow: 0 0 30rpx rgba(255, 23, 68, 0.7);
}
}
@keyframes shineFlow {
0% {
left: -100%;
}
100% {
left: 100%;
}
}
</style>