<template>
|
<view v-if="visible" class="stock-toast" @tap="_handleGoList">
|
<!-- 左侧:图标 + 文案(单笔缺货额外展示支付倒计时) -->
|
<view class="stock-toast-left">
|
<view class="stock-toast-icon"></view>
|
<text class="stock-toast-title">{{ titleText }}</text>
|
<!-- 单笔缺货:展示支付倒计时;多笔缺货:仅展示具体数量,不展示倒计时 -->
|
<view v-if="count === 1" class="stock-toast-countdown">
|
<view class="countdown-block">{{ minuteText }}</view>
|
<text class="countdown-colon">:</text>
|
<view class="countdown-block">{{ secondText }}</view>
|
</view>
|
</view>
|
<!-- 右侧:前往支付按钮 -->
|
<view class="stock-toast-btn">
|
<text class="stock-toast-btn-text">前往支付</text>
|
<view class="stock-toast-btn-arrow"></view>
|
</view>
|
</view>
|
</template>
|
|
<script setup>
|
/**
|
* 全局缺货订单提示(Toast)
|
* 规则:存在缺货订单的所有页面均弹出;点击跳转缺货订单列表页;
|
* 单笔展示支付倒计时,多笔仅展示具体数量、不展示倒计时。
|
* 用法:在各业务页面模板中直接引入 <stock-order-toast /> 即可。
|
*/
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
import { getStockOrderCount, STOCK_ORDER_LIST_PAGE } from '@/utils/stock-order.js'
|
|
/** 缺货订单数量(Mock 数据源,接口就绪后由数据层替换) */
|
const count = ref(getStockOrderCount())
|
|
/** 是否存在缺货订单 */
|
const visible = computed(() => count.value > 0)
|
|
/** 提示文案:单笔用「一个」,多笔展示具体数量 */
|
const titleText = computed(() =>
|
count.value > 1 ? `您有${count.value}个缺货订单待支付` : '您有一个缺货订单待支付'
|
)
|
|
// ======================== 支付倒计时(仅单笔缺货时展示) ========================
|
|
/** 倒计时初始剩余秒数(Mock 30 分钟) */
|
const COUNTDOWN_SECONDS = 30 * 60
|
|
const remainSeconds = ref(COUNTDOWN_SECONDS)
|
let countdownTimer = null
|
|
/** 剩余分钟(补零两位) */
|
const minuteText = computed(() => String(Math.floor(remainSeconds.value / 60)).padStart(2, '0'))
|
/** 剩余秒数(补零两位) */
|
const secondText = computed(() => String(remainSeconds.value % 60).padStart(2, '0'))
|
|
/** 启动倒计时(多笔缺货时按规则不展示倒计时,无需启动) */
|
const _startCountdown = () => {
|
if (count.value !== 1) return
|
countdownTimer = setInterval(() => {
|
if (remainSeconds.value <= 0) {
|
clearInterval(countdownTimer)
|
countdownTimer = null
|
return
|
}
|
remainSeconds.value -= 1
|
}, 1000)
|
}
|
|
onMounted(_startCountdown)
|
onUnmounted(() => {
|
if (countdownTimer) clearInterval(countdownTimer)
|
})
|
|
/** 跳转缺货订单列表页 */
|
const _handleGoList = () => {
|
uni.navigateTo({ url: STOCK_ORDER_LIST_PAGE })
|
}
|
</script>
|
|
<style scoped lang="scss">
|
/* 通栏胶囊:距原生 tabBar 24rpx,左右各 24rpx,毛玻璃白底 + 柔和投影 */
|
.stock-toast {
|
position: fixed;
|
left: 24rpx;
|
right: 24rpx;
|
/* 原生 tabBar 由系统渲染在 webview 之外,内容区 bottom 即 tabBar 上沿 */
|
bottom: calc(24rpx + constant(safe-area-inset-bottom));
|
bottom: calc(24rpx + env(safe-area-inset-bottom));
|
z-index: 85;
|
height: 80rpx;
|
box-sizing: border-box;
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
padding: 0 16rpx 0 24rpx;
|
border-radius: 40rpx;
|
background: linear-gradient(135deg, rgba(255, 255, 255, 0.96) 0%, rgba(255, 255, 255, 0.88) 100%);
|
border: 2rpx solid #ffffff;
|
box-shadow: 0 8rpx 28rpx rgba(15, 41, 66, 0.13);
|
}
|
|
.stock-toast-left {
|
flex: 1;
|
min-width: 0;
|
display: flex;
|
align-items: center;
|
}
|
|
/* 前置图标:lucide clipboard-list 15×15pt */
|
.stock-toast-icon {
|
width: 30rpx;
|
height: 30rpx;
|
margin-right: 13rpx;
|
flex-shrink: 0;
|
background-repeat: no-repeat;
|
background-position: center center;
|
background-size: 100% 100%;
|
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMEYxNzJBIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHJlY3Qgd2lkdGg9IjgiIGhlaWdodD0iNCIgeD0iOCIgeT0iMiIgcng9IjEiIHJ5PSIxIi8+PHBhdGggZD0iTTE2IDRoMmEyIDIgMCAwIDEgMiAydjE0YTIgMiAwIDAgMS0yIDJINmEyIDIgMCAwIDEtMi0yVjZhMiAyIDAgMCAxIDItMmgyIi8+PHBhdGggZD0iTTEyIDExaDQiLz48cGF0aCBkPSJNMTIgMTZoNCIvPjxwYXRoIGQ9Ik04IDExaC4wMSIvPjxwYXRoIGQ9Ik04IDE2aC4wMSIvPjwvc3ZnPg==');
|
}
|
|
.stock-toast-title {
|
font-size: 22rpx;
|
font-weight: 700;
|
line-height: 1;
|
color: #0f172a;
|
white-space: nowrap;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
}
|
|
/* 倒计时组合:红底数字块 + 冒号 */
|
.stock-toast-countdown {
|
display: flex;
|
align-items: center;
|
margin-left: 13rpx;
|
flex-shrink: 0;
|
}
|
|
.countdown-block {
|
width: 32rpx;
|
height: 28rpx;
|
border-radius: 6rpx;
|
background: #e53935;
|
color: #ffffff;
|
font-size: 19rpx;
|
font-weight: 700;
|
line-height: 1;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
|
.countdown-colon {
|
margin: 0 4rpx;
|
font-size: 20rpx;
|
font-weight: 700;
|
line-height: 1;
|
color: #e53935;
|
}
|
|
/* 前往支付按钮:红色渐变胶囊 */
|
.stock-toast-btn {
|
flex-shrink: 0;
|
height: 50rpx;
|
padding: 0 18rpx;
|
border-radius: 25rpx;
|
display: flex;
|
align-items: center;
|
background: linear-gradient(90deg, #ef4444 0%, #dc2626 100%);
|
box-shadow: 0 3rpx 10rpx rgba(220, 38, 38, 0.21);
|
}
|
|
.stock-toast-btn-text {
|
font-size: 21rpx;
|
font-weight: 700;
|
line-height: 1;
|
color: #ffffff;
|
}
|
|
/* 按钮内箭头:lucide chevron-right 9×9pt */
|
.stock-toast-btn-arrow {
|
width: 18rpx;
|
height: 18rpx;
|
margin-left: 4rpx;
|
background-repeat: no-repeat;
|
background-position: center center;
|
background-size: 100% 100%;
|
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjRkZGRkZGIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggZD0ibTkgMTggNi02LTYtNiIvPjwvc3ZnPg==');
|
}
|
</style>
|