1722 lines
52 KiB
Vue
1722 lines
52 KiB
Vue
<template>
|
||
<view class="scroll-container theme-light">
|
||
<view class="confirm-payment-page">
|
||
<view class="cp-body">
|
||
<!-- 订单信息 -->
|
||
<view class="cp-section">
|
||
<text class="cp-section__title">订单信息</text>
|
||
<view class="cp-order">
|
||
<view class="cp-order__item">
|
||
<text class="cp-order__label">订单编号</text>
|
||
<text class="cp-order__value">{{ orderNo }}</text>
|
||
</view>
|
||
<view class="cp-order__item">
|
||
<text class="cp-order__label">下单时间</text>
|
||
<text class="cp-order__value">{{ orderTime }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 商品信息 -->
|
||
<view class="cp-section" v-if="!isRecharge">
|
||
<text class="cp-section__title">购买商品</text>
|
||
<view class="cp-goods">
|
||
<view class="cp-goods__item" v-for="item in cart" :key="item.card.id">
|
||
<view class="cp-goods__icon">🏋️</view>
|
||
<view class="cp-goods__info">
|
||
<text class="cp-goods__name">{{ item.card.cardName }}</text>
|
||
<text class="cp-goods__desc">{{ getCardTypeName(item.card.cardType) }} · {{ getCardDesc(item.card) }}</text>
|
||
</view>
|
||
<view class="cp-goods__right">
|
||
<text class="cp-goods__count">×{{ item.count }}</text>
|
||
<text class="cp-goods__price">¥{{ (item.card.price * item.count).toFixed(2) }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 充值信息 -->
|
||
<view class="cp-section" v-else>
|
||
<text class="cp-section__title">充值信息</text>
|
||
<view class="cp-recharge">
|
||
<view class="cp-recharge__icon">💳</view>
|
||
<view class="cp-recharge__info">
|
||
<text class="cp-recharge__name">储值卡充值</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 支付方式 -->
|
||
<view class="cp-section">
|
||
<text class="cp-section__title">支付方式</text>
|
||
<view class="cp-payment">
|
||
<view
|
||
class="cp-payment__item"
|
||
:class="{ 'cp-payment__item--selected': selectedPayment === 'alipay' }"
|
||
@tap="selectPayment('alipay')"
|
||
>
|
||
<view class="cp-payment__icon cp-payment__icon--alipay">
|
||
<image src="https://gymfuture.oss-cn-chengdu.aliyuncs.com/static/images/alipay.png" mode="aspectFit" />
|
||
</view>
|
||
<view class="cp-payment__info">
|
||
<text class="cp-payment__name">支付宝支付</text>
|
||
<text class="cp-payment__desc">推荐使用,安全快捷</text>
|
||
</view>
|
||
<view class="cp-payment__check">
|
||
<view class="cp-payment__check-inner" :class="{ 'cp-payment__check-inner--checked': selectedPayment === 'alipay' }"></view>
|
||
</view>
|
||
</view>
|
||
|
||
<view
|
||
class="cp-payment__item"
|
||
:class="{
|
||
'cp-payment__item--selected': selectedPayment === 'stored',
|
||
'cp-payment__item--disabled': storedBalance < totalPrice
|
||
}"
|
||
v-if="!isRecharge"
|
||
@tap="storedBalance >= totalPrice && selectPayment('stored')"
|
||
>
|
||
<view class="cp-payment__icon cp-payment__icon--stored">💰</view>
|
||
<view class="cp-payment__info">
|
||
<text class="cp-payment__name">储值卡支付</text>
|
||
<text class="cp-payment__desc">余额: ¥{{ storedBalance.toFixed(2) }}</text>
|
||
</view>
|
||
<view class="cp-payment__check">
|
||
<view class="cp-payment__check-inner" :class="{ 'cp-payment__check-inner--checked': selectedPayment === 'stored' }"></view>
|
||
</view>
|
||
<view class="cp-payment__disabled" v-if="storedBalance < totalPrice">
|
||
<text>余额不足</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 金额明细 -->
|
||
<view class="cp-section">
|
||
<text class="cp-section__title">金额明细</text>
|
||
<view class="cp-amount">
|
||
<view class="cp-amount__item">
|
||
<text class="cp-amount__label">{{ isRecharge ? '充值金额' : '商品金额' }}</text>
|
||
<text class="cp-amount__value">¥{{ totalPrice.toFixed(2) }}</text>
|
||
</view>
|
||
<view class="cp-amount__item" v-if="!isRecharge && selectedPayment === 'stored' && storedBalance > 0">
|
||
<text class="cp-amount__label">储值抵扣</text>
|
||
<text class="cp-amount__value cp-amount__value--discount">-¥{{ Math.min(storedBalance, totalPrice).toFixed(2) }}</text>
|
||
</view>
|
||
<view class="cp-amount__item cp-amount__item--total">
|
||
<text class="cp-amount__label">实付金额</text>
|
||
<text class="cp-amount__value cp-amount__value--total">¥{{ actualPay.toFixed(2) }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部支付栏 -->
|
||
<view class="cp-bottom">
|
||
<view class="cp-bottom__left">
|
||
<text class="cp-bottom__label">实付金额</text>
|
||
<text class="cp-bottom__price">¥{{ actualPay.toFixed(2) }}</text>
|
||
</view>
|
||
<view
|
||
class="cp-bottom__btn"
|
||
:class="{ 'cp-bottom__btn--disabled': !canPay || loading }"
|
||
@tap="handlePay()"
|
||
>
|
||
<text class="cp-bottom__btn-text">{{ loading ? '处理中...' : '确认支付' }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 购买成功弹窗 -->
|
||
<view class="success-modal-overlay" v-if="showSuccessModalVisible" @tap="closeSuccessModal">
|
||
<view class="success-modal-dialog" @tap.stop>
|
||
<view class="success-modal__icon">🎉</view>
|
||
<text class="success-modal__title">{{ isRecharge ? '充值成功' : '购买成功' }}</text>
|
||
<text class="success-modal__desc">{{ isRecharge ? '恭喜您,储值卡充值成功!' : '恭喜您,会员卡已成功开通!' }}</text>
|
||
<view class="success-modal__buttons">
|
||
<view class="success-modal__btn success-modal__btn--secondary" @tap="handleBackFromConfirm">
|
||
<text>返回上一页</text>
|
||
</view>
|
||
<view class="success-modal__btn success-modal__btn--primary" @tap="handleGoToCardFromConfirm">
|
||
<text>{{ isRecharge ? '查看储值卡' : '去我的会员卡' }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 旧订单提示弹窗 -->
|
||
<view class="pending-order-modal-overlay" v-if="showPendingOrderModal" @tap.stop>
|
||
<view class="pending-order-modal-dialog">
|
||
<view class="pending-order-modal__header">
|
||
<text class="pending-order-modal__title">发现未完成订单</text>
|
||
</view>
|
||
<view class="pending-order-modal__body">
|
||
<text class="pending-order-modal__desc">您有一笔未支付的订单:</text>
|
||
<view class="pending-order-modal__info" v-if="pendingOrder">
|
||
<text class="pending-order-modal__order-no">订单号:{{ pendingOrder.orderId }}</text>
|
||
<text class="pending-order-modal__amount">金额:¥{{ (pendingOrder.transAmt / 100 || pendingOrder.transAmt).toFixed(2) }}</text>
|
||
<text class="pending-order-modal__time">创建时间:{{ pendingOrder.createdAt }}</text>
|
||
</view>
|
||
<text class="pending-order-modal__question">请选择您的操作:</text>
|
||
</view>
|
||
<view class="pending-order-modal__buttons">
|
||
<view class="pending-order-modal__btn pending-order-modal__btn--primary" @tap="handlePayPendingOrder">
|
||
<text>支付旧订单</text>
|
||
</view>
|
||
<view class="pending-order-modal__btn pending-order-modal__btn--secondary" @tap="handleCreateNewOrder">
|
||
<text>创建新订单</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 支付密码验证弹窗 -->
|
||
<view class="pay-password-modal-overlay" v-if="showPayPasswordModal" @tap="closePayPasswordModal">
|
||
<view class="pay-password-modal-dialog" @tap.stop>
|
||
<view class="pay-password-modal__header">
|
||
<text class="pay-password-modal__title">验证支付密码</text>
|
||
<view class="pay-password-modal__close" @tap="closePayPasswordModal">
|
||
<text>✕</text>
|
||
</view>
|
||
</view>
|
||
<view class="pay-password-modal__body">
|
||
<text class="pay-password-modal__amount">¥{{ totalPrice.toFixed(2) }}</text>
|
||
<VerifyCodeInput
|
||
ref="payPasswordInputRef"
|
||
v-model="payPassword"
|
||
:length="6"
|
||
:mask="true"
|
||
type="box"
|
||
:errorMsg="payPasswordError"
|
||
@complete="handlePayPasswordComplete"
|
||
/>
|
||
<view class="pay-password-modal__forgot" @tap="goForgotPassword">
|
||
<text>忘记密码?</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
|
||
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||
import { getMyMemberCards, purchaseMemberCard, createPayment, createQrCodePayment, checkPayPasswordSet, verifyPayPassword, payWithStoredCard as storedCardPay, getStoredCardInfo, getPendingOrder, closeOrder } from '@/api/main.js'
|
||
import { getMemberId } from '@/utils/request.js'
|
||
import { getLoginMemberInfo } from '@/common/memberInfo/store.js'
|
||
import VerifyCodeInput from '@/components/global/VerifyCodeInput.vue'
|
||
|
||
const cart = ref([])
|
||
const selectedPayment = ref('alipay')
|
||
const storedBalance = ref(0)
|
||
const loading = ref(false)
|
||
const orderNo = ref('')
|
||
const orderTime = ref('')
|
||
const isRecharge = ref(false)
|
||
const rechargeDesc = ref('')
|
||
const rechargeRemark = ref('')
|
||
const returnPage = ref('')
|
||
const cardType = ref('')
|
||
const rechargeAmount = ref(0) // 储值卡充值金额
|
||
|
||
// 旧订单相关状态
|
||
const pendingOrder = ref(null) // 未支付的旧订单
|
||
const showPendingOrderModal = ref(false) // 是否显示旧订单提示弹窗
|
||
|
||
// 支付相关状态
|
||
const paymentResolved = ref(false)
|
||
const currentOrderId = ref(null)
|
||
const currentMerchantOrderId = ref(null)
|
||
const currentHfSeqId = ref(null)
|
||
const currentPayUrl = ref('')
|
||
const showSuccessModalVisible = ref(false)
|
||
let pollTimer = null
|
||
let pollCount = 0
|
||
const MAX_POLL_COUNT = 10
|
||
|
||
// 支付密码弹窗
|
||
const showPayPasswordModal = ref(false)
|
||
const payPassword = ref('')
|
||
const payPasswordError = ref('')
|
||
const payPasswordInputRef = ref(null)
|
||
|
||
const totalPrice = computed(() => {
|
||
// 储值卡充值时使用 rechargeAmount
|
||
if (isRecharge.value) {
|
||
return rechargeAmount.value
|
||
}
|
||
return cart.value.reduce((sum, item) => sum + (item.card.price * item.count), 0)
|
||
})
|
||
|
||
const actualPay = computed(() => {
|
||
// 储值卡充值时,实际支付金额就是充值金额
|
||
if (isRecharge.value) {
|
||
return rechargeAmount.value
|
||
}
|
||
if (selectedPayment.value === 'stored') {
|
||
return Math.max(0, totalPrice.value - storedBalance.value)
|
||
}
|
||
return totalPrice.value
|
||
})
|
||
|
||
const canPay = computed(() => {
|
||
if (isRecharge.value) {
|
||
if (selectedPayment.value === 'alipay') {
|
||
return true
|
||
}
|
||
return storedBalance.value >= totalPrice.value
|
||
}
|
||
if (cart.value.length === 0) return false
|
||
if (selectedPayment.value === 'alipay') {
|
||
return true
|
||
}
|
||
return storedBalance.value >= totalPrice.value
|
||
})
|
||
|
||
function getCardTypeName(type) {
|
||
const map = {
|
||
DURATION: '时长卡',
|
||
TIME_CARD: '时长卡',
|
||
COUNT: '次卡',
|
||
COUNT_CARD: '次卡',
|
||
BALANCE: '储值卡',
|
||
STORED_VALUE_CARD: '储值卡'
|
||
}
|
||
return map[type] || type
|
||
}
|
||
|
||
function getCardDesc(card) {
|
||
if (!card) return ''
|
||
if (card.validityDays) return `${card.validityDays}天`
|
||
if (card.totalTimes) return `${card.totalTimes}次`
|
||
if (card.amount) return `${card.amount}元储值`
|
||
return ''
|
||
}
|
||
|
||
async function selectPayment(type) {
|
||
if (type === 'stored') {
|
||
// 检查用户是否登录
|
||
const memberId = getMemberId()
|
||
if (!memberId) {
|
||
uni.showToast({ title: '请先登录', icon: 'none' })
|
||
uni.navigateTo({ url: '/pages/memberInfo/login' })
|
||
return
|
||
}
|
||
|
||
if (storedBalance.value < totalPrice.value) {
|
||
uni.showToast({ title: '储值余额不足', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
uni.showLoading({ title: '检查支付密码...' })
|
||
try {
|
||
const res = await checkPayPasswordSet(memberId)
|
||
uni.hideLoading()
|
||
|
||
console.log('[Payment] 检查支付密码返回:', JSON.stringify(res))
|
||
|
||
// 兼容多种返回格式
|
||
let isSet = false
|
||
if (res?.data?.isSet !== undefined) {
|
||
isSet = res.data.isSet
|
||
} else if (res?.isSet !== undefined) {
|
||
isSet = res.isSet
|
||
}
|
||
|
||
console.log('[Payment] 支付密码是否已设置:', isSet)
|
||
|
||
if (!isSet) {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '您尚未设置支付密码,请先设置支付密码',
|
||
showCancel: false,
|
||
success: () => {
|
||
uni.navigateTo({ url: '/pages/memberInfo/setPayPassword' })
|
||
}
|
||
})
|
||
return
|
||
}
|
||
} catch (e) {
|
||
uni.hideLoading()
|
||
console.error('检查支付密码失败:', e)
|
||
uni.showToast({ title: '检查失败', icon: 'none' })
|
||
return
|
||
}
|
||
}
|
||
selectedPayment.value = type
|
||
}
|
||
|
||
async function loadStoredBalance() {
|
||
if (!getMemberId()) return
|
||
try {
|
||
const res = await getStoredCardInfo(getMemberId())
|
||
const balance = res.balance || 0
|
||
storedBalance.value = balance
|
||
console.log('[Payment] 从后端读取储值卡余额:', balance)
|
||
if (balance < totalPrice.value && selectedPayment.value === 'stored') {
|
||
selectedPayment.value = 'alipay'
|
||
}
|
||
} catch (e) {
|
||
console.error('加载储值余额失败:', e)
|
||
storedBalance.value = 0
|
||
}
|
||
}
|
||
|
||
async function handlePay() {
|
||
if (!canPay.value || loading.value) return
|
||
console.log('支付前会员ID:', getMemberId())
|
||
|
||
// 先检查是否有未支付的旧订单
|
||
const memberId = getMemberId()
|
||
if (!memberId) {
|
||
uni.showToast({ title: '请先登录', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
try {
|
||
uni.showLoading({ title: '检查订单...' })
|
||
const orderType = isRecharge.value ? 'STORED_CARD_RECHARGE' : 'MEMBER_CARD'
|
||
const pendingRes = await getPendingOrder(memberId, orderType)
|
||
uni.hideLoading()
|
||
|
||
// 如果有未支付的旧订单,提示用户
|
||
if (pendingRes && pendingRes.data && pendingRes.data.orderId) {
|
||
pendingOrder.value = pendingRes.data
|
||
showPendingOrderModal.value = true
|
||
return
|
||
}
|
||
} catch (e) {
|
||
uni.hideLoading()
|
||
console.log('检查旧订单失败,继续支付流程:', e)
|
||
}
|
||
|
||
// 没有旧订单,继续支付
|
||
loading.value = true
|
||
|
||
try {
|
||
if (isRecharge.value || selectedPayment.value === 'alipay') {
|
||
await payWithAlipay()
|
||
} else {
|
||
await payWithStoredCard()
|
||
}
|
||
} catch (e) {
|
||
console.error('支付失败:', e)
|
||
uni.showToast({ title: e.message || '支付失败', icon: 'none' })
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 处理旧订单:支付旧订单
|
||
async function handlePayPendingOrder() {
|
||
showPendingOrderModal.value = false
|
||
if (!pendingOrder.value || !pendingOrder.value.orderId) {
|
||
return
|
||
}
|
||
|
||
// 直接跳转到旧订单的支付流程
|
||
currentOrderId.value = pendingOrder.value.orderId
|
||
currentMerchantOrderId.value = pendingOrder.value.merchantOrderId
|
||
currentHfSeqId.value = pendingOrder.value.hfSeqId
|
||
|
||
// 根据支付方式处理
|
||
if (pendingOrder.value.tradeType === 'ALIPAY' || pendingOrder.value.payUrl) {
|
||
const payUrl = pendingOrder.value.payUrl
|
||
if (payUrl && typeof plus !== 'undefined') {
|
||
const encodedUrl = encodeURIComponent(payUrl)
|
||
const alipayScheme = `alipays://platformapi/startapp?appId=20000067&url=${encodedUrl}`
|
||
plus.runtime.openURL(alipayScheme, function(err) {
|
||
console.log('[Alipay] 唤起支付宝App失败,尝试H5支付:', err)
|
||
plus.runtime.openURL(payUrl, function(h5Err) {
|
||
console.error('[Alipay] H5支付也失败:', h5Err)
|
||
uni.showModal({ title: '提示', content: '无法唤起支付宝,请确认已安装', showCancel: false })
|
||
})
|
||
})
|
||
} else if (payUrl) {
|
||
window.location.href = payUrl
|
||
}
|
||
} else {
|
||
// 储值卡支付的旧订单,重新验证支付密码
|
||
loading.value = true
|
||
payPassword.value = ''
|
||
payPasswordError.value = ''
|
||
showPayPasswordModal.value = true
|
||
nextTick(() => {
|
||
if (payPasswordInputRef.value) {
|
||
payPasswordInputRef.value.focus()
|
||
}
|
||
})
|
||
}
|
||
|
||
pendingOrder.value = null
|
||
}
|
||
|
||
// 处理旧订单:关闭旧订单并创建新订单
|
||
async function handleCreateNewOrder() {
|
||
showPendingOrderModal.value = false
|
||
if (!pendingOrder.value || !pendingOrder.value.orderId) {
|
||
pendingOrder.value = null
|
||
loading.value = true
|
||
// 继续支付流程
|
||
try {
|
||
if (isRecharge.value || selectedPayment.value === 'alipay') {
|
||
await payWithAlipay()
|
||
} else {
|
||
await payWithStoredCard()
|
||
}
|
||
} catch (e) {
|
||
console.error('支付失败:', e)
|
||
uni.showToast({ title: e.message || '支付失败', icon: 'none' })
|
||
loading.value = false
|
||
}
|
||
return
|
||
}
|
||
|
||
try {
|
||
uni.showLoading({ title: '关闭旧订单...' })
|
||
await closeOrder(pendingOrder.value.orderId)
|
||
uni.hideLoading()
|
||
pendingOrder.value = null
|
||
|
||
// 继续支付流程
|
||
loading.value = true
|
||
try {
|
||
if (isRecharge.value || selectedPayment.value === 'alipay') {
|
||
await payWithAlipay()
|
||
} else {
|
||
await payWithStoredCard()
|
||
}
|
||
} catch (e) {
|
||
console.error('支付失败:', e)
|
||
uni.showToast({ title: e.message || '支付失败', icon: 'none' })
|
||
loading.value = false
|
||
}
|
||
} catch (e) {
|
||
uni.hideLoading()
|
||
uni.showToast({ title: '关闭旧订单失败,请稍后重试', icon: 'none' })
|
||
console.error('关闭旧订单失败:', e)
|
||
}
|
||
}
|
||
|
||
async function payWithStoredCard() {
|
||
if (storedBalance.value < totalPrice.value) {
|
||
uni.showToast({
|
||
title: `余额不足,当前余额:¥${storedBalance.value.toFixed(2)}`,
|
||
icon: 'none',
|
||
duration: 3000
|
||
})
|
||
loading.value = false
|
||
return
|
||
}
|
||
|
||
payPassword.value = ''
|
||
payPasswordError.value = ''
|
||
showPayPasswordModal.value = true
|
||
nextTick(() => {
|
||
payPasswordInputRef.value?.focusInput()
|
||
})
|
||
}
|
||
|
||
function closePayPasswordModal() {
|
||
showPayPasswordModal.value = false
|
||
payPassword.value = ''
|
||
payPasswordError.value = ''
|
||
if (loading.value) {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
function handlePayPasswordComplete(password) {
|
||
payPasswordError.value = ''
|
||
doPayWithStoredCard(password)
|
||
}
|
||
|
||
function goForgotPassword() {
|
||
uni.showToast({ title: '请联系客服重置密码', icon: 'none' })
|
||
}
|
||
|
||
async function doPayWithStoredCard(password) {
|
||
uni.showLoading({ title: '处理中...' })
|
||
try {
|
||
const payRes = await storedCardPay(getMemberId(), password, totalPrice.value)
|
||
|
||
if (payRes.code !== 200) {
|
||
uni.hideLoading()
|
||
const msg = payRes.message || '支付失败'
|
||
if (msg.includes('密码错误') || msg.includes('密码')) {
|
||
payPasswordError.value = msg
|
||
payPassword.value = ''
|
||
nextTick(() => {
|
||
payPasswordInputRef.value?.focusInput()
|
||
})
|
||
} else {
|
||
uni.showToast({ title: msg, icon: 'none' })
|
||
loading.value = false
|
||
closePayPasswordModal()
|
||
}
|
||
return
|
||
}
|
||
|
||
storedBalance.value = payRes.data?.balance || 0
|
||
|
||
closePayPasswordModal()
|
||
|
||
let successCount = 0
|
||
let failedCount = 0
|
||
let hasStoredCard = false
|
||
|
||
for (const item of cart.value) {
|
||
try {
|
||
// 使用主键id(而不是memberCardId业务字段)
|
||
const cardId = item.card.id
|
||
const res = await purchaseMemberCard({
|
||
memberCardId: cardId
|
||
})
|
||
|
||
if (res.code === 200 || res.id || res.memberCardRecordId) {
|
||
successCount += item.count
|
||
const cardType = item.card.cardType || item.card.memberCardType
|
||
if (cardType === 'STORED_VALUE_CARD' || cardType === 'BALANCE') {
|
||
hasStoredCard = true
|
||
}
|
||
} else {
|
||
failedCount += item.count
|
||
}
|
||
} catch (e) {
|
||
console.error('购买会员卡失败:', e)
|
||
failedCount += item.count
|
||
}
|
||
}
|
||
|
||
uni.hideLoading()
|
||
|
||
// 重新从后端查询余额(确保余额正确)
|
||
try {
|
||
const balanceRes = await getStoredCardInfo(getMemberId())
|
||
storedBalance.value = balanceRes.balance || 0
|
||
console.log('[Payment] 储值卡支付后更新余额:', storedBalance.value)
|
||
} catch (e) {
|
||
console.error('更新储值卡余额失败:', e)
|
||
}
|
||
|
||
if (successCount > 0) {
|
||
uni.showToast({ title: `购买成功,支付¥${totalPrice.value.toFixed(2)}`, icon: 'success' })
|
||
|
||
if (hasStoredCard) {
|
||
setTimeout(() => {
|
||
uni.showModal({
|
||
title: '设置支付密码',
|
||
content: '恭喜您购买储值卡成功!为了您的账户安全,请设置支付密码。设置后可使用储值卡余额支付。',
|
||
confirmText: '去设置',
|
||
cancelText: '稍后设置',
|
||
confirmColor: '#1677FF',
|
||
success: (modalRes) => {
|
||
if (modalRes.confirm) {
|
||
uni.navigateTo({ url: '/pages/memberInfo/setPayPassword' })
|
||
} else {
|
||
uni.redirectTo({ url: '/pages/memberInfo/memberCard' })
|
||
}
|
||
}
|
||
})
|
||
}, 1500)
|
||
} else {
|
||
setTimeout(() => {
|
||
uni.redirectTo({ url: '/pages/memberInfo/memberCard' })
|
||
}, 1500)
|
||
}
|
||
} else {
|
||
uni.showToast({ title: '会员卡购买失败,请联系客服', icon: 'none' })
|
||
loading.value = false
|
||
}
|
||
} catch (e) {
|
||
uni.hideLoading()
|
||
console.error('储值卡支付失败:', e)
|
||
uni.showToast({ title: '支付失败,请稍后重试', icon: 'none' })
|
||
loading.value = false
|
||
closePayPasswordModal()
|
||
}
|
||
}
|
||
|
||
async function payWithAlipay() {
|
||
let goodsDesc = ''
|
||
let remark = ''
|
||
let orderType = 'MEMBER_CARD'
|
||
|
||
if (isRecharge.value) {
|
||
goodsDesc = rechargeDesc.value || '储值卡充值'
|
||
remark = rechargeRemark.value || ''
|
||
orderType = 'STORED_CARD_RECHARGE'
|
||
} else {
|
||
goodsDesc = cart.value.map(item => `${item.card.cardName} × ${item.count}`).join('、')
|
||
remark = cart.value.map(item => `会员卡类型ID:${item.card.id},数量:${item.count}`).join(';')
|
||
}
|
||
|
||
const transAmt = String(Math.round(totalPrice.value * 100))
|
||
const confirmContent = isRecharge.value
|
||
? `确认支付 ¥${totalPrice.value.toFixed(2)} 进行${goodsDesc}?`
|
||
: `确认支付 ¥${totalPrice.value.toFixed(2)} 购买${goodsDesc}?`
|
||
|
||
uni.showModal({
|
||
title: '确认支付',
|
||
content: confirmContent,
|
||
confirmText: '确认支付',
|
||
confirmColor: '#1677FF',
|
||
success: async (modalRes) => {
|
||
if (!modalRes.confirm) {
|
||
loading.value = false
|
||
return
|
||
}
|
||
|
||
try {
|
||
const { getPendingOrder } = await import('@/api/main.js')
|
||
const pendingRes = await getPendingOrder(getMemberId(), orderType)
|
||
|
||
if (pendingRes.code === 200 && pendingRes.data) {
|
||
const pendingOrder = pendingRes.data
|
||
const pendingAmount = pendingOrder.transAmt || (pendingOrder.amount ? (pendingOrder.amount / 100).toFixed(2) : '0.00')
|
||
uni.showModal({
|
||
title: '存在未支付订单',
|
||
content: `您有一笔¥${pendingAmount}的${goodsDesc}订单尚未支付,是否继续支付该订单?\n\n点击"支付旧订单"继续支付\n点击"创建新订单"关闭旧订单并重新创建`,
|
||
confirmText: '支付旧订单',
|
||
cancelText: '创建新订单',
|
||
confirmColor: '#1677FF',
|
||
success: async (chooseRes) => {
|
||
if (chooseRes.confirm) {
|
||
useExistingOrder(pendingOrder)
|
||
} else {
|
||
try {
|
||
const { closeOrder } = await import('@/api/main.js')
|
||
const orderId = pendingOrder.orderId || pendingOrder.merchantOrderId || pendingOrder.orderNo
|
||
await closeOrder(orderId)
|
||
createNewPayment(orderType, goodsDesc, remark, transAmt)
|
||
} catch (e) {
|
||
console.error('[Payment] 关闭旧订单失败:', e)
|
||
uni.showToast({ title: '关闭旧订单失败,请重试', icon: 'none' })
|
||
loading.value = false
|
||
}
|
||
}
|
||
},
|
||
fail: () => {
|
||
loading.value = false
|
||
}
|
||
})
|
||
} else {
|
||
createNewPayment(orderType, goodsDesc, remark, transAmt)
|
||
}
|
||
} catch (e) {
|
||
console.error('[Payment] 查询未支付订单失败:', e)
|
||
createNewPayment(orderType, goodsDesc, remark, transAmt)
|
||
}
|
||
},
|
||
fail: () => {
|
||
loading.value = false
|
||
}
|
||
})
|
||
}
|
||
|
||
function useExistingOrder(order) {
|
||
currentOrderId.value = order.orderId || order.merchantOrderId || order.orderNo
|
||
currentMerchantOrderId.value = order.merchantOrderId || currentOrderId.value
|
||
currentHfSeqId.value = order.hfSeqId || order.hfOrderId
|
||
currentPayUrl.value = order.payUrl || order.payInfo || order.h5PayUrl
|
||
paymentResolved.value = false
|
||
|
||
const payUrl = currentPayUrl.value
|
||
const isWebEnv = typeof navigator !== 'undefined' && typeof plus === 'undefined'
|
||
|
||
if (isWebEnv && !payUrl) {
|
||
uni.showToast({ title: '请扫码支付', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
if (payUrl && typeof plus !== 'undefined') {
|
||
const encodedUrl = encodeURIComponent(payUrl)
|
||
const alipayScheme = `alipays://platformapi/startapp?appId=20000067&url=${encodedUrl}`
|
||
plus.runtime.openURL(alipayScheme, function(err) {
|
||
console.log('[Alipay] 唤起支付宝App失败,尝试H5支付:', err)
|
||
plus.runtime.openURL(payUrl, function(h5Err) {
|
||
console.error('[Alipay] H5支付也失败:', h5Err)
|
||
uni.showModal({ title: '提示', content: '无法唤起支付宝,请确认已安装', showCancel: false })
|
||
loading.value = false
|
||
})
|
||
})
|
||
} else if (payUrl) {
|
||
window.location.href = payUrl
|
||
}
|
||
}
|
||
|
||
async function createNewPayment(orderType, goodsDesc, remark, transAmt) {
|
||
const isWebEnv = typeof navigator !== 'undefined' && typeof plus === 'undefined'
|
||
|
||
if (isWebEnv) {
|
||
const { createQrCodePayment } = await import('@/api/main.js')
|
||
const payRes = await createQrCodePayment({
|
||
mid: getMemberId(),
|
||
orderType: orderType,
|
||
goodsDesc: goodsDesc,
|
||
transAmt: transAmt,
|
||
tradeType: 'QRCODE',
|
||
remark: remark
|
||
})
|
||
if (payRes.code !== 200) {
|
||
uni.showToast({ title: payRes.message || '创建订单失败', icon: 'none' })
|
||
loading.value = false
|
||
return
|
||
}
|
||
currentOrderId.value = payRes.data.orderId || payRes.data.merchantOrderId || payRes.data.tradeNo
|
||
currentMerchantOrderId.value = payRes.data.merchantOrderId || currentOrderId.value
|
||
currentHfSeqId.value = payRes.data.hfSeqId || payRes.data.hfOrderId
|
||
currentPayUrl.value = payRes.data.payUrl || payRes.data.payInfo || payRes.data.h5PayUrl
|
||
paymentResolved.value = false
|
||
uni.showToast({ title: '请扫码支付', icon: 'none' })
|
||
} else {
|
||
const { createPayment } = await import('@/api/main.js')
|
||
const payRes = await createPayment({
|
||
mid: getMemberId(),
|
||
orderType: orderType,
|
||
goodsDesc: goodsDesc,
|
||
transAmt: transAmt,
|
||
tradeType: 'ALIPAY',
|
||
remark: remark
|
||
})
|
||
if (payRes.code !== 200) {
|
||
uni.showToast({ title: payRes.message || '创建订单失败', icon: 'none' })
|
||
loading.value = false
|
||
return
|
||
}
|
||
|
||
currentOrderId.value = payRes.data.orderId || payRes.data.merchantOrderId || payRes.data.tradeNo
|
||
currentMerchantOrderId.value = payRes.data.merchantOrderId || currentOrderId.value
|
||
currentHfSeqId.value = payRes.data.hfSeqId || payRes.data.hfOrderId
|
||
currentPayUrl.value = payRes.data.payUrl || payRes.data.payInfo || payRes.data.h5PayUrl
|
||
paymentResolved.value = false
|
||
|
||
const payUrl = currentPayUrl.value
|
||
if (payUrl && typeof plus !== 'undefined') {
|
||
const encodedUrl = encodeURIComponent(payUrl)
|
||
const alipayScheme = `alipays://platformapi/startapp?appId=20000067&url=${encodedUrl}`
|
||
plus.runtime.openURL(alipayScheme, function(err) {
|
||
console.log('[Alipay] 唤起支付宝App失败,尝试H5支付:', err)
|
||
plus.runtime.openURL(payUrl, function(h5Err) {
|
||
console.error('[Alipay] H5支付也失败:', h5Err)
|
||
uni.showModal({ title: '提示', content: '无法唤起支付宝,请确认已安装', showCancel: false })
|
||
loading.value = false
|
||
})
|
||
})
|
||
} else if (payUrl) {
|
||
window.location.href = payUrl
|
||
}
|
||
}
|
||
}
|
||
|
||
onLoad((options) => {
|
||
// 打印当前登录用户ID
|
||
const loginInfo = getLoginMemberInfo()
|
||
const mid = getMemberId()
|
||
console.log('========== 确认支付页 - 当前登录用户 ==========')
|
||
console.log('会员ID (memberId):', mid)
|
||
console.log('登录信息 (loginInfo):', JSON.stringify(loginInfo, null, 2))
|
||
console.log('==============================================')
|
||
|
||
// 重置支付状态
|
||
paymentResolved.value = false
|
||
currentOrderId.value = null
|
||
currentMerchantOrderId.value = null
|
||
currentHfSeqId.value = null
|
||
currentPayUrl.value = ''
|
||
showSuccessModalVisible.value = false
|
||
loading.value = false
|
||
|
||
if (options && options.returnPage) {
|
||
returnPage.value = options.returnPage
|
||
}
|
||
if (options && options.cardType) {
|
||
cardType.value = options.cardType
|
||
}
|
||
if (options && options.orderType && options.orderType === 'STORED_CARD_RECHARGE') {
|
||
isRecharge.value = true
|
||
if (options.desc) {
|
||
rechargeDesc.value = decodeURIComponent(options.desc)
|
||
}
|
||
if (options.remark) {
|
||
rechargeRemark.value = decodeURIComponent(options.remark)
|
||
}
|
||
}
|
||
|
||
// 储值卡充值时设置金额
|
||
if (isRecharge.value && options && options.amount) {
|
||
const amt = parseFloat(options.amount)
|
||
if (!isNaN(amt)) {
|
||
rechargeAmount.value = amt
|
||
}
|
||
}
|
||
|
||
if (options && options.orderId) {
|
||
currentOrderId.value = options.orderId
|
||
// 已有订单,直接查询状态
|
||
setTimeout(() => {
|
||
checkPaymentStatus()
|
||
}, 500)
|
||
}
|
||
|
||
try {
|
||
if (options && options.cartData) {
|
||
cart.value = JSON.parse(decodeURIComponent(options.cartData))
|
||
} else if (options && options.cardInfo) {
|
||
const cardInfo = JSON.parse(decodeURIComponent(options.cardInfo))
|
||
const purchaseCount = parseInt(options.purchaseCount) || 1
|
||
cart.value = [{ card: cardInfo, count: purchaseCount }]
|
||
}
|
||
} catch (e) {
|
||
console.error('解析购物车数据失败:', e)
|
||
cart.value = []
|
||
}
|
||
|
||
const now = new Date()
|
||
orderNo.value = `PAY${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}${String(now.getHours()).padStart(2, '0')}${String(now.getMinutes()).padStart(2, '0')}${String(now.getSeconds()).padStart(2, '0')}`
|
||
orderTime.value = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`
|
||
|
||
loadStoredBalance()
|
||
})
|
||
|
||
// APP从后台返回时检查支付状态
|
||
function handleAppResume() {
|
||
if (paymentResolved.value || !currentOrderId.value) {
|
||
return
|
||
}
|
||
console.log('[Payment] APP从后台返回,查询支付结果')
|
||
startPolling()
|
||
}
|
||
|
||
function startPolling() {
|
||
if (paymentResolved.value || !currentOrderId.value) {
|
||
return
|
||
}
|
||
stopPolling()
|
||
pollCount = 0
|
||
console.log('[Payment] 开始轮询支付状态')
|
||
checkPaymentStatus()
|
||
pollTimer = setInterval(() => {
|
||
pollCount++
|
||
console.log(`[Payment] 轮询第 ${pollCount} 次`)
|
||
if (pollCount >= MAX_POLL_COUNT) {
|
||
console.log('[Payment] 轮询次数达到上限,停止轮询')
|
||
stopPolling()
|
||
return
|
||
}
|
||
checkPaymentStatus(true)
|
||
}, 2000)
|
||
}
|
||
|
||
function stopPolling() {
|
||
if (pollTimer) {
|
||
clearInterval(pollTimer)
|
||
pollTimer = null
|
||
}
|
||
}
|
||
|
||
function checkPaymentStatus(silent = false) {
|
||
if (paymentResolved.value || !currentOrderId.value) {
|
||
stopPolling()
|
||
return
|
||
}
|
||
if (!silent) {
|
||
uni.showLoading({ title: '查询支付结果...' })
|
||
}
|
||
|
||
const queryOrderId = currentOrderId.value
|
||
import('@/api/main.js').then(({ getPaymentStatus }) => {
|
||
getPaymentStatus(queryOrderId)
|
||
.then(res => {
|
||
if (!silent) {
|
||
uni.hideLoading()
|
||
}
|
||
if (res.code === 200 && res.data) {
|
||
const status = res.data.status || res.data.payStatus
|
||
|
||
console.log('[Payment] 支付状态:', status)
|
||
|
||
if (status === 'SUCCESS') {
|
||
paymentResolved.value = true
|
||
stopPolling()
|
||
uni.showToast({ title: '支付成功', icon: 'success', duration: 2000 })
|
||
handlePaymentSuccess()
|
||
} else if (status === 'FAIL') {
|
||
paymentResolved.value = true
|
||
stopPolling()
|
||
uni.showToast({ title: '支付失败', icon: 'error' })
|
||
loading.value = false
|
||
} else if (status === 'CLOSED') {
|
||
paymentResolved.value = true
|
||
stopPolling()
|
||
uni.showToast({ title: '订单已关闭', icon: 'none' })
|
||
loading.value = false
|
||
}
|
||
} else {
|
||
if (!silent) {
|
||
uni.hideLoading()
|
||
}
|
||
}
|
||
})
|
||
.catch(() => {
|
||
if (!silent) {
|
||
uni.hideLoading()
|
||
}
|
||
console.error('[Payment] 查询支付状态失败')
|
||
})
|
||
})
|
||
}
|
||
|
||
function mapTransStat(transStat) {
|
||
if (!transStat) return 'PENDING'
|
||
|
||
// 统一转为大写
|
||
const upper = String(transStat).toUpperCase()
|
||
const map = {
|
||
'S': 'SUCCESS',
|
||
'SUCCESS': 'SUCCESS',
|
||
'F': 'FAIL',
|
||
'FAIL': 'FAIL',
|
||
'P': 'PENDING',
|
||
'PENDING': 'PENDING',
|
||
'C': 'CLOSED',
|
||
'CLOSED': 'CLOSED',
|
||
'INIT': 'PENDING',
|
||
'WAITING': 'PENDING',
|
||
'PROCESSING': 'PENDING',
|
||
'PARTIAL_SUCCESS': 'SUCCESS',
|
||
'TRADE_SUCCESS': 'SUCCESS',
|
||
'TRADE_CLOSED': 'CLOSED',
|
||
'WAIT_BUYER_PAY': 'PENDING'
|
||
}
|
||
return map[upper] || 'PENDING'
|
||
}
|
||
|
||
async function handlePaymentSuccess() {
|
||
// 支付成功后,更新储值卡余额
|
||
try {
|
||
const res = await getStoredCardInfo(getMemberId())
|
||
storedBalance.value = res.balance || 0
|
||
console.log('[Payment] 支付成功后更新余额:', storedBalance.value)
|
||
} catch (e) {
|
||
console.error('支付成功后更新余额失败:', e)
|
||
}
|
||
|
||
if (isRecharge.value) {
|
||
// 储值卡充值成功,显示成功弹窗
|
||
uni.showToast({ title: '充值成功', icon: 'success' })
|
||
showSuccessModalVisible.value = true
|
||
return
|
||
}
|
||
|
||
if (cart.value.length === 0) return
|
||
|
||
uni.showLoading({ title: '开通会员卡...' })
|
||
let successCount = 0
|
||
let failedCount = 0
|
||
|
||
for (const item of cart.value) {
|
||
try {
|
||
const memberRes = await purchaseMemberCard({
|
||
memberCardId: item.card.id
|
||
})
|
||
if (memberRes.code === 200 || memberRes.id || memberRes.memberCardRecordId) {
|
||
successCount += item.count
|
||
} else {
|
||
failedCount += item.count
|
||
}
|
||
} catch (e) {
|
||
console.error('购买会员卡失败:', e)
|
||
failedCount += item.count
|
||
}
|
||
}
|
||
|
||
uni.hideLoading()
|
||
if (successCount > 0) {
|
||
uni.showToast({ title: `成功购买${successCount}张会员卡`, icon: 'success' })
|
||
// 显示成功弹窗,让用户选择
|
||
showSuccessModalVisible.value = true
|
||
} else {
|
||
uni.showToast({ title: '购买失败', icon: 'none' })
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
function closeSuccessModal() {
|
||
showSuccessModalVisible.value = false
|
||
}
|
||
|
||
function handleBackFromConfirm() {
|
||
closeSuccessModal()
|
||
uni.navigateBack()
|
||
}
|
||
|
||
function handleGoToCardFromConfirm() {
|
||
closeSuccessModal()
|
||
if (isRecharge.value) {
|
||
uni.redirectTo({ url: '/pages/memberInfo/memberCard?tab=stored' })
|
||
} else if (cardType.value === 'stored') {
|
||
uni.redirectTo({ url: '/pages/memberInfo/memberCard?tab=stored' })
|
||
} else {
|
||
uni.redirectTo({ url: '/pages/memberInfo/memberCard?tab=other' })
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
// 注册APP从后台返回事件
|
||
if (typeof plus !== 'undefined') {
|
||
plus.globalEvent.addEventListener('resume', handleAppResume)
|
||
}
|
||
})
|
||
|
||
onShow(() => {
|
||
// 页面显示时检查支付状态
|
||
if (currentOrderId.value && !paymentResolved.value) {
|
||
console.log('[Payment] 页面显示,检查支付状态')
|
||
startPolling()
|
||
}
|
||
})
|
||
|
||
onUnmounted(() => {
|
||
paymentResolved.value = true
|
||
stopPolling()
|
||
if (typeof plus !== 'undefined') {
|
||
plus.globalEvent.removeEventListener('resume', handleAppResume)
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
@import '@/common/style/base.css';
|
||
@import '@/common/style/memberInfo/pages/page-reset.css';
|
||
@import '@/common/style/memberInfo/pages/sub-page-base.css';
|
||
@import '@/common/style/memberInfo/member-info-component-reset.css';
|
||
@import '@/common/style/memberInfo/member-info-sub-nav.css';
|
||
|
||
.confirm-payment-page {
|
||
min-height: 100vh;
|
||
background: #F5F7FA;
|
||
padding-bottom: 120px;
|
||
}
|
||
|
||
.cp-body {
|
||
padding: 16px;
|
||
}
|
||
|
||
.cp-section {
|
||
background: #FFFFFF;
|
||
border-radius: 16px;
|
||
padding: 20px;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.cp-section__title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #1A202C;
|
||
margin-bottom: 16px;
|
||
display: block;
|
||
}
|
||
|
||
.cp-order {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.cp-order__item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.cp-order__label {
|
||
font-size: 14px;
|
||
color: #718096;
|
||
}
|
||
|
||
.cp-order__value {
|
||
font-size: 14px;
|
||
color: #2D3748;
|
||
font-family: 'Courier New', monospace;
|
||
}
|
||
|
||
.cp-goods {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.cp-goods__item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 12px;
|
||
background: #F7FAFC;
|
||
border-radius: 12px;
|
||
}
|
||
|
||
.cp-goods__icon {
|
||
font-size: 32px;
|
||
width: 48px;
|
||
height: 48px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: #FFFFFF;
|
||
border-radius: 12px;
|
||
}
|
||
|
||
.cp-goods__info {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
.cp-goods__name {
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
color: #2D3748;
|
||
}
|
||
|
||
.cp-goods__desc {
|
||
font-size: 12px;
|
||
color: #718096;
|
||
}
|
||
|
||
.cp-goods__right {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-end;
|
||
gap: 4px;
|
||
}
|
||
|
||
.cp-goods__count {
|
||
font-size: 12px;
|
||
color: #A0AEC0;
|
||
}
|
||
|
||
.cp-goods__price {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #E53E3E;
|
||
}
|
||
|
||
.cp-recharge {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 12px 0;
|
||
}
|
||
|
||
.cp-recharge__icon {
|
||
width: 48px;
|
||
height: 48px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
border-radius: 12px;
|
||
font-size: 24px;
|
||
margin-right: 12px;
|
||
}
|
||
|
||
.cp-recharge__info {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
.cp-recharge__name {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #2D3748;
|
||
}
|
||
|
||
.cp-recharge__desc {
|
||
font-size: 13px;
|
||
color: #A0AEC0;
|
||
}
|
||
|
||
.cp-payment {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.cp-payment__item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 16px;
|
||
border: 2px solid #E2E8F0;
|
||
border-radius: 12px;
|
||
transition: all 0.2s;
|
||
position: relative;
|
||
|
||
&--selected {
|
||
border-color: #1677FF;
|
||
background: #F0F7FF;
|
||
}
|
||
|
||
&--disabled {
|
||
opacity: 0.6;
|
||
cursor: not-allowed;
|
||
}
|
||
}
|
||
|
||
.cp-payment__icon {
|
||
width: 40px;
|
||
height: 40px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-right: 12px;
|
||
|
||
&--alipay {
|
||
background: #1677FF;
|
||
border-radius: 8px;
|
||
|
||
image {
|
||
width: 28px;
|
||
height: 28px;
|
||
}
|
||
}
|
||
|
||
&--stored {
|
||
font-size: 28px;
|
||
background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
|
||
border-radius: 50%;
|
||
}
|
||
}
|
||
|
||
.cp-payment__info {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
.cp-payment__name {
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
color: #2D3748;
|
||
}
|
||
|
||
.cp-payment__desc {
|
||
font-size: 12px;
|
||
color: #718096;
|
||
}
|
||
|
||
.cp-payment__check {
|
||
width: 24px;
|
||
height: 24px;
|
||
border: 2px solid #CBD5E0;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.cp-payment__check-inner {
|
||
width: 14px;
|
||
height: 14px;
|
||
border-radius: 50%;
|
||
background: transparent;
|
||
transition: all 0.2s;
|
||
|
||
&--checked {
|
||
background: #1677FF;
|
||
}
|
||
}
|
||
|
||
.cp-payment__disabled {
|
||
position: absolute;
|
||
right: 16px;
|
||
padding: 4px 12px;
|
||
background: #FED7D7;
|
||
border-radius: 12px;
|
||
font-size: 11px;
|
||
color: #E53E3E;
|
||
}
|
||
|
||
.cp-amount {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.cp-amount__item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
|
||
&--total {
|
||
padding-top: 12px;
|
||
border-top: 1px dashed #E2E8F0;
|
||
margin-top: 8px;
|
||
}
|
||
}
|
||
|
||
.cp-amount__label {
|
||
font-size: 14px;
|
||
color: #718096;
|
||
}
|
||
|
||
.cp-amount__value {
|
||
font-size: 14px;
|
||
color: #2D3748;
|
||
|
||
&--discount {
|
||
color: #38A169;
|
||
}
|
||
|
||
&--total {
|
||
font-size: 18px;
|
||
font-weight: 700;
|
||
color: #E53E3E;
|
||
}
|
||
}
|
||
|
||
.cp-bottom {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 16px 24px;
|
||
padding-bottom: calc(16px + env(safe-area-inset-bottom));
|
||
background: #FFFFFF;
|
||
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.05);
|
||
z-index: 100;
|
||
}
|
||
|
||
.cp-bottom__left {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 8px;
|
||
}
|
||
|
||
.cp-bottom__label {
|
||
font-size: 14px;
|
||
color: #718096;
|
||
}
|
||
|
||
.cp-bottom__price {
|
||
font-size: 28px;
|
||
font-weight: 700;
|
||
color: #E53E3E;
|
||
}
|
||
|
||
.cp-bottom__btn {
|
||
background: linear-gradient(135deg, #1677FF 0%, #0958D9 100%);
|
||
border-radius: 12px;
|
||
padding: 16px 48px;
|
||
transition: all 0.2s;
|
||
|
||
&--disabled {
|
||
background: #A0AEC0;
|
||
}
|
||
|
||
&:active:not(.cp-bottom__btn--disabled) {
|
||
transform: scale(0.98);
|
||
opacity: 0.9;
|
||
}
|
||
}
|
||
|
||
.cp-bottom__btn-text {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #FFFFFF;
|
||
}
|
||
|
||
// 购买成功弹窗样式
|
||
.success-modal-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 2000;
|
||
animation: fadeIn 0.3s ease;
|
||
}
|
||
|
||
.success-modal-dialog {
|
||
width: 85%;
|
||
max-width: 360px;
|
||
background: #FFFFFF;
|
||
border-radius: 20px;
|
||
padding: 40px 32px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
animation: modalPop 0.3s ease;
|
||
}
|
||
|
||
.success-modal__icon {
|
||
font-size: 64px;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.success-modal__title {
|
||
font-size: 22px;
|
||
font-weight: 700;
|
||
color: #1A202C;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.success-modal__desc {
|
||
font-size: 14px;
|
||
color: #718096;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.success-modal__buttons {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
width: 100%;
|
||
}
|
||
|
||
.success-modal__btn {
|
||
padding: 14px 24px;
|
||
border-radius: 12px;
|
||
text-align: center;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
|
||
&--primary {
|
||
background: linear-gradient(135deg, #1677FF 0%, #0958D9 100%);
|
||
color: #FFFFFF;
|
||
}
|
||
|
||
&--secondary {
|
||
background: #F5F7FA;
|
||
color: #4A5568;
|
||
}
|
||
|
||
&--outline {
|
||
background: transparent;
|
||
border: 1px solid #E2E8F0;
|
||
color: #4A5568;
|
||
}
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from {
|
||
opacity: 0;
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
@keyframes modalPop {
|
||
from {
|
||
opacity: 0;
|
||
transform: scale(0.9);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: scale(1);
|
||
}
|
||
}
|
||
|
||
.pay-password-modal-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 2000;
|
||
animation: fadeIn 0.3s ease;
|
||
}
|
||
|
||
.pay-password-modal-dialog {
|
||
width: 85%;
|
||
max-width: 360px;
|
||
background: #FFFFFF;
|
||
border-radius: 20px;
|
||
overflow: hidden;
|
||
animation: modalPop 0.3s ease;
|
||
}
|
||
|
||
.pay-password-modal__header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 20px 24px 12px;
|
||
|
||
&-title {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #1A202C;
|
||
}
|
||
|
||
&-close {
|
||
width: 24px;
|
||
height: 24px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #A0AEC0;
|
||
font-size: 16px;
|
||
}
|
||
}
|
||
|
||
.pay-password-modal__body {
|
||
padding: 0 24px 32px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.pay-password-modal__amount {
|
||
font-size: 32px;
|
||
font-weight: 700;
|
||
color: #E53E3E;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.pay-password-modal__forgot {
|
||
margin-top: 16px;
|
||
text-align: right;
|
||
width: 100%;
|
||
|
||
text {
|
||
font-size: 13px;
|
||
color: #1677FF;
|
||
}
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
|
||
// 旧订单提示弹窗样式
|
||
.pending-order-modal-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 1000;
|
||
animation: fadeIn 0.2s ease-out;
|
||
}
|
||
|
||
.pending-order-modal-dialog {
|
||
width: 85%;
|
||
max-width: 360px;
|
||
background: #FFFFFF;
|
||
border-radius: 20px;
|
||
padding: 24px;
|
||
animation: slideUp 0.3s ease-out;
|
||
}
|
||
|
||
.pending-order-modal__header {
|
||
text-align: center;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.pending-order-modal__title {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #1A202C;
|
||
}
|
||
|
||
.pending-order-modal__body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.pending-order-modal__desc {
|
||
font-size: 14px;
|
||
color: #718096;
|
||
}
|
||
|
||
.pending-order-modal__info {
|
||
background: #F7FAFC;
|
||
border-radius: 12px;
|
||
padding: 16px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.pending-order-modal__order-no {
|
||
font-size: 13px;
|
||
color: #2D3748;
|
||
font-family: 'Courier New', monospace;
|
||
}
|
||
|
||
.pending-order-modal__amount {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #E53E3E;
|
||
}
|
||
|
||
.pending-order-modal__time {
|
||
font-size: 12px;
|
||
color: #718096;
|
||
}
|
||
|
||
.pending-order-modal__question {
|
||
font-size: 14px;
|
||
color: #2D3748;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.pending-order-modal__buttons {
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
|
||
.pending-order-modal__btn {
|
||
flex: 1;
|
||
padding: 14px;
|
||
border-radius: 12px;
|
||
text-align: center;
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
|
||
&--primary {
|
||
background: linear-gradient(135deg, #1677FF 0%, #0958D9 100%);
|
||
color: #FFFFFF;
|
||
}
|
||
|
||
&--secondary {
|
||
background: #F7FAFC;
|
||
color: #2D3748;
|
||
border: 1px solid #E2E8F0;
|
||
}
|
||
}
|
||
|
||
@keyframes slideUp {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(20px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
</style>
|