bindbox-mini/components/PaymentPopup.vue
邹方成 d930756130 feat(订单): 实现订单详情页功能
添加订单详情页路由配置
开发订单详情页UI及交互逻辑
对接订单详情和取消订单API
更新文档记录开发进度
优化订单状态显示逻辑
2025-12-18 15:06:32 +08:00

337 lines
8.1 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
class="picker-full"
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
class="picker-full"
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, () => (Array.isArray(props.coupons) ? props.coupons.length : 0)],
([vis, len]) => {
if (!vis) return
cardIndex.value = -1
if (len <= 0) {
couponIndex.value = -1
return
}
if (couponIndex.value < 0) {
couponIndex.value = 0
}
},
{ immediate: true }
)
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-full {
width: 100%;
display: block;
}
.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>