bindbox-mini/components/PaymentPopup.vue
邹方成 6f7207da2d feat: 优化UI设计并重构样式系统
refactor(components): 重构ElCard、FlipGrid、YifanSelector和PaymentPopup组件样式
refactor(pages): 优化地址管理、商品详情、订单列表、积分记录和活动页面UI
style: 更新uni.scss全局样式变量和设计系统
docs: 添加说明文档记录UI优化进度
2025-12-17 14:32:55 +08:00

321 lines
7.7 KiB
Vue
Raw Permalink 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 lang="scss" 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: $bg-card;
border-radius: $radius-xl $radius-xl 0 0;
padding: $spacing-lg;
padding-bottom: calc($spacing-lg + constant(safe-area-inset-bottom));
padding-bottom: calc($spacing-lg + 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: $font-xs;
padding: $spacing-sm $spacing-md;
border-radius: $radius-md;
margin-bottom: $spacing-md;
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: $brand-primary;
font-weight: 500;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: $spacing-lg;
position: relative;
}
.popup-title {
font-size: $font-lg;
font-weight: 700;
color: $text-main;
}
.close-icon {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
font-size: 48rpx;
color: $text-placeholder;
line-height: 1;
padding: 10rpx;
transition: color 0.2s ease;
}
.close-icon:active {
color: $text-secondary;
}
.popup-body {
padding: $spacing-sm 0 $spacing-md;
}
.amount-section {
text-align: center;
margin-bottom: $spacing-xl;
padding: $spacing-md;
background: linear-gradient(145deg, #FFFFFF, #FFF8F3);
border-radius: $radius-lg;
border: 1rpx solid rgba(255, 159, 67, 0.1);
}
.amount-section .label {
font-size: $font-sm;
color: $text-secondary;
margin-right: $spacing-xs;
}
.amount-section .amount {
font-size: 56rpx;
font-weight: 800;
background: $gradient-brand;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.form-item {
margin-bottom: $spacing-md;
}
.form-item .label {
display: block;
font-size: $font-sm;
color: $text-main;
font-weight: 600;
margin-bottom: $spacing-xs;
}
.picker-display {
border: 2rpx solid $border-color-light;
border-radius: $radius-md;
padding: $spacing-md;
display: flex;
justify-content: space-between;
align-items: center;
font-size: $font-sm;
background: $bg-page;
transition: all 0.2s ease;
}
.picker-display:active {
border-color: $brand-primary;
background: #FFF8F3;
}
.selected-text {
color: $text-main;
font-weight: 500;
}
.placeholder {
color: $text-placeholder;
}
.arrow {
color: $text-placeholder;
width: 16rpx;
height: 16rpx;
border-right: 3rpx solid $text-placeholder;
border-bottom: 3rpx solid $text-placeholder;
transform: rotate(-45deg);
margin-right: $spacing-xs;
}
.popup-footer {
display: flex;
gap: $spacing-md;
margin-top: $spacing-sm;
}
.btn-cancel, .btn-confirm {
flex: 1;
border: none;
border-radius: $radius-lg;
font-size: $font-md;
padding: $spacing-md 0;
line-height: 1.5;
font-weight: 600;
transition: all 0.2s ease;
}
.btn-cancel::after, .btn-confirm::after {
border: none;
}
.btn-cancel {
color: $text-secondary;
background: #F3F4F6;
}
.btn-cancel:active {
background: $border-color-light;
}
.btn-confirm {
color: #FFFFFF;
background: $gradient-brand;
box-shadow: $shadow-lg;
}
.btn-confirm:active {
transform: scale(0.97);
box-shadow: $shadow-md;
}
</style>