bindbox-mini/components/PaymentPopup.vue

321 lines
7.5 KiB
Vue
Raw 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 v-if="visible" class="payment-popup-mask" @tap="handleMaskClick">
<view class="payment-popup-content" @tap.stop>
<!-- 顶部提示 -->
<view class="risk-warning">
<text>盲盒具有随机性请理性消费购买即表示同意</text>
<text class="agreement-link" @tap="openAgreement">购买协议</text>
</view>
<view class="popup-header">
<text class="popup-title">确认支付</text>
<view class="close-icon" @tap="handleClose">×</view>
</view>
<view class="popup-body">
<view class="amount-section" v-if="amount !== undefined && amount !== null">
<text class="label">支付金额</text>
<text class="amount">¥{{ amount }}</text>
</view>
<view class="form-item">
<text class="label">优惠券</text>
<picker
mode="selector"
:range="coupons"
range-key="name"
@change="onCouponChange"
:value="couponIndex"
:disabled="!coupons || coupons.length === 0"
>
<view class="picker-display">
<text v-if="selectedCoupon" class="selected-text">{{ selectedCoupon.name }} (-¥{{ selectedCoupon.amount }})</text>
<text v-else-if="!coupons || coupons.length === 0" class="placeholder">暂无优惠券可用</text>
<text v-else class="placeholder">请选择优惠券</text>
<text class="arrow"></text>
</view>
</picker>
</view>
<view class="form-item" v-if="showCards">
<text class="label">道具卡</text>
<picker
mode="selector"
:range="propCards"
range-key="name"
@change="onCardChange"
:value="cardIndex"
:disabled="!propCards || propCards.length === 0"
>
<view class="picker-display">
<text v-if="selectedCard" class="selected-text">{{ selectedCard.name }}</text>
<text v-else-if="!propCards || propCards.length === 0" class="placeholder">暂无道具卡可用</text>
<text v-else class="placeholder">请选择道具卡</text>
<text class="arrow"></text>
</view>
</picker>
</view>
</view>
<view class="popup-footer">
<button class="btn-cancel" @tap="handleClose">取消</button>
<button class="btn-confirm" @tap="handleConfirm">确认支付</button>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed, watch } from 'vue'
const props = defineProps({
visible: { type: Boolean, default: false },
amount: { type: [Number, String], default: 0 },
coupons: { type: Array, default: () => [] },
propCards: { type: Array, default: () => [] },
showCards: { type: Boolean, default: true }
})
const emit = defineEmits(['update:visible', 'confirm', 'cancel'])
const couponIndex = ref(-1)
const cardIndex = ref(-1)
const selectedCoupon = computed(() => {
if (couponIndex.value >= 0 && props.coupons[couponIndex.value]) {
return props.coupons[couponIndex.value]
}
return null
})
const selectedCard = computed(() => {
if (cardIndex.value >= 0 && props.propCards[cardIndex.value]) {
return props.propCards[cardIndex.value]
}
return null
})
watch(() => props.visible, (val) => {
if (val) {
couponIndex.value = -1
cardIndex.value = -1
}
})
function onCouponChange(e) {
couponIndex.value = e.detail.value
}
function onCardChange(e) {
cardIndex.value = e.detail.value
}
function openAgreement() {
uni.navigateTo({
url: '/pages/agreement/purchase' // 假设协议页面路径,如果没有请替换为实际路径
})
}
function handleMaskClick() {
handleClose()
}
function handleClose() {
emit('update:visible', false)
emit('cancel')
}
function handleConfirm() {
emit('confirm', {
coupon: selectedCoupon.value,
card: props.showCards ? selectedCard.value : null
})
}
</script>
<style scoped>
/* ============================================
奇盒潮玩 - 支付弹窗组件
采用暖橙色调的底部弹窗设计
============================================ */
.payment-popup-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.55);
z-index: 999;
display: flex;
align-items: flex-end;
}
.payment-popup-content {
width: 100%;
background: #FFFFFF;
border-radius: 32rpx 32rpx 0 0;
padding: 32rpx;
padding-bottom: calc(32rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
animation: slideUp 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes slideUp {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
.risk-warning {
background: linear-gradient(135deg, #FFF8F3, #FFF4E6);
color: #B45309;
font-size: 24rpx;
padding: 20rpx 24rpx;
border-radius: 16rpx;
margin-bottom: 24rpx;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
line-height: 1.5;
text-align: center;
border: 1rpx solid rgba(255, 159, 67, 0.2);
}
.agreement-link {
color: #FF9F43;
font-weight: 500;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 32rpx;
position: relative;
}
.popup-title {
font-size: 36rpx;
font-weight: 700;
color: #1F2937;
}
.close-icon {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
font-size: 48rpx;
color: #9CA3AF;
line-height: 1;
padding: 10rpx;
transition: color 0.2s ease;
}
.close-icon:active {
color: #6B7280;
}
.popup-body {
padding: 16rpx 0 24rpx;
}
.amount-section {
text-align: center;
margin-bottom: 40rpx;
padding: 24rpx;
background: linear-gradient(145deg, #FFFFFF, #FFF8F3);
border-radius: 20rpx;
border: 1rpx solid rgba(255, 159, 67, 0.1);
}
.amount-section .label {
font-size: 28rpx;
color: #6B7280;
margin-right: 10rpx;
}
.amount-section .amount {
font-size: 56rpx;
font-weight: 800;
background: linear-gradient(135deg, #FF6B35, #FF9F43);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.form-item {
margin-bottom: 24rpx;
}
.form-item .label {
display: block;
font-size: 28rpx;
color: #1F2937;
font-weight: 600;
margin-bottom: 16rpx;
}
.picker-display {
border: 2rpx solid #E5E7EB;
border-radius: 16rpx;
padding: 24rpx;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 28rpx;
background: #F9FAFB;
transition: all 0.2s ease;
}
.picker-display:active {
border-color: #FF9F43;
background: #FFF8F3;
}
.selected-text {
color: #1F2937;
font-weight: 500;
}
.placeholder {
color: #9CA3AF;
}
.arrow {
color: #9CA3AF;
width: 16rpx;
height: 16rpx;
border-right: 3rpx solid #9CA3AF;
border-bottom: 3rpx solid #9CA3AF;
transform: rotate(-45deg);
margin-right: 8rpx;
}
.popup-footer {
display: flex;
gap: 20rpx;
margin-top: 16rpx;
}
.btn-cancel, .btn-confirm {
flex: 1;
border: none;
border-radius: 24rpx;
font-size: 32rpx;
padding: 24rpx 0;
line-height: 1.5;
font-weight: 600;
transition: all 0.2s ease;
}
.btn-cancel::after, .btn-confirm::after {
border: none;
}
.btn-cancel {
color: #6B7280;
background: #F3F4F6;
}
.btn-cancel:active {
background: #E5E7EB;
}
.btn-confirm {
color: #FFFFFF;
background: linear-gradient(135deg, #FF9F43, #FF6B35);
box-shadow: 0 8rpx 24rpx rgba(255, 107, 53, 0.35);
}
.btn-confirm:active {
transform: scale(0.97);
box-shadow: 0 4rpx 12rpx rgba(255, 107, 53, 0.25);
}
</style>