| | |
| | | <view class="coupon-value"><text>{{couponText}}</text><text class="chevron">›</text></view> |
| | | </view> |
| | | <view class="price-item"><text class="price-label">原价</text><text class="price-value">¥ {{originalAmountText}}</text></view> |
| | | <view class="price-item"><text class="price-label">优惠金额</text><text class="price-value">-¥ {{discountAmountText}}</text></view> |
| | | <view class="price-item"><text class="price-label">优惠金额</text><text class="price-value">-¥ {{couponAmountText}}</text></view> |
| | | <view class="price-item"><text class="price-label">用户实付</text><text class="price-value strong">¥ {{actualPayAmountText}}</text></view> |
| | | <view class="price-item"><text class="price-label">补贴金额</text><text class="price-value">¥ 0.00</text></view> |
| | | <view class="price-item"><text class="price-label">补贴金额</text><text class="price-value">¥ {{subsidyAmountText}}</text></view> |
| | | </view> |
| | | |
| | | <view class="actual-payment"> |
| | |
| | | <text class="actual-label">实际支付</text> |
| | | </view> |
| | | |
| | | <view class="confirm-button" hover-class="confirm-button-active" @tap="confirmPayment"> |
| | | <text>确认支付</text> |
| | | <view class="confirm-button" :class="isSubmitting ? 'confirm-button-disabled' : ''" hover-class="confirm-button-active" @tap="confirmPayment"> |
| | | <text>{{isSubmitting ? '支付中...' : '确认支付'}}</text> |
| | | </view> |
| | | <view class="safe-bottom"></view> |
| | | </view> |
| | |
| | | |
| | | <script setup lang="uts"> |
| | | import { safeBack } from '../../utils/navigation.uts' |
| | | import { createAppOrder, getOrderCouponQuote } from '../../services/portal.uts' |
| | | import { abandonPendingOrderPayment, createAppOrder, createPendingOrderPayment, getOrderCouponQuote } from '../../services/portal.uts' |
| | | import { resolveApiUrl } from '../../services/http.uts' |
| | | import { getSession } from '../../services/session.uts' |
| | | |
| | |
| | | const merchantId = ref('') |
| | | const productId = ref('') |
| | | const orderId = ref('') |
| | | const talentId = ref('') |
| | | const couponText = ref('无') |
| | | const couponAmount = ref(0.0) |
| | | const productName = ref('【川味盛宴】川菜12人餐') |
| | | const productImage = ref('/static/food-assets/food-1.jpg') |
| | | const productImage = ref('/static/common/image-placeholder.png') |
| | | const salePrice = ref(138.0) |
| | | const originalAmount = ref(266.0) |
| | | const productDiscount = ref('6折') |
| | | const salePriceText = computed(() => salePrice.value.toFixed(2)) |
| | | const originalAmountText = computed(() => originalAmount.value.toFixed(2)) |
| | | const discountAmountText = ref('0.00') |
| | | const couponAmountText = computed(() => couponAmount.value.toFixed(2)) |
| | | const subsidyAmountText = ref('0.00') |
| | | const actualPayAmountText = ref('0.00') |
| | | const createOrderIdempotencyKey = ref('') |
| | | let quoteSequence = 0 |
| | | |
| | | const applyProductById = (id: string) => { |
| | | if (id == 'food-set-002') { productName.value = '【川香四溢】川菜4人餐'; productImage.value = '/static/food-assets/food-2.jpg'; salePrice.value = 118.0; originalAmount.value = 198.0; productDiscount.value = '6折'; return } |
| | | if (id == 'food-set-002') { productName.value = '【川香四溢】川菜4人餐'; productImage.value = '/static/common/image-placeholder.png'; salePrice.value = 118.0; originalAmount.value = 198.0; productDiscount.value = '6折'; return } |
| | | if (id == 'food-set-003') { productName.value = '【欢聚时光】招牌菜6人餐'; salePrice.value = 168.0; originalAmount.value = 248.0; productDiscount.value = '6.8折'; return } |
| | | if (id == 'food-set-004') { productName.value = '【暖心小聚】特色菜双人餐'; productImage.value = '/static/food-assets/food-2.jpg'; salePrice.value = 88.0; originalAmount.value = 126.0; productDiscount.value = '7折'; return } |
| | | if (id == 'food-set-004') { productName.value = '【暖心小聚】特色菜双人餐'; productImage.value = '/static/common/image-placeholder.png'; salePrice.value = 88.0; originalAmount.value = 126.0; productDiscount.value = '7折'; return } |
| | | if (id == 'food-set-005') { productName.value = '【家庭聚餐】经典川菜8人餐'; salePrice.value = 238.0; originalAmount.value = 366.0; productDiscount.value = '6.5折'; return } |
| | | if (id == 'food-set-006') { productName.value = '【人气必点】招牌菜4人餐'; productImage.value = '/static/food-assets/food-2.jpg'; salePrice.value = 128.0; originalAmount.value = 188.0; productDiscount.value = '6.8折'; return } |
| | | if (id == 'food-set-006') { productName.value = '【人气必点】招牌菜4人餐'; productImage.value = '/static/common/image-placeholder.png'; salePrice.value = 128.0; originalAmount.value = 188.0; productDiscount.value = '6.8折'; return } |
| | | if (id == 'food-set-007') { productName.value = '【周末欢聚】特色菜10人餐'; salePrice.value = 298.0; originalAmount.value = 498.0; productDiscount.value = '6折'; return } |
| | | if (id == 'food-set-008') { productName.value = '【一人食】暖心工作餐'; productImage.value = '/static/food-assets/food-2.jpg'; salePrice.value = 39.0; originalAmount.value = 49.0; productDiscount.value = '8折'; return } |
| | | if (id == 'food-set-008') { productName.value = '【一人食】暖心工作餐'; productImage.value = '/static/common/image-placeholder.png'; salePrice.value = 39.0; originalAmount.value = 49.0; productDiscount.value = '8折'; return } |
| | | } |
| | | |
| | | onLoad((options: any) => { |
| | |
| | | if (options != null && options['merchantId'] != null) merchantId.value = options['merchantId'] as string |
| | | if (options != null && options['productId'] != null) productId.value = options['productId'] as string |
| | | if (options != null && options['orderId'] != null) orderId.value = options['orderId'] as string |
| | | const session = getSession() |
| | | if (session != null && session.talentId != null) talentId.value = '' + session.talentId |
| | | applyProductById(productId.value) |
| | | if (options != null && options['name'] != null) productName.value = decodeURIComponent(options['name'] as string) |
| | | if (options != null && options['image'] != null) productImage.value = decodeURIComponent(options['image'] as string) |
| | |
| | | if (!validOrderContext()) return |
| | | const sequence = quoteSequence + 1 |
| | | quoteSequence = sequence |
| | | getOrderCouponQuote(merchantId.value, productId.value, orderId.value, couponId, (data: any) => { |
| | | getOrderCouponQuote(merchantId.value, productId.value, orderId.value, couponId, talentId.value, (data: any) => { |
| | | if (sequence != quoteSequence || data == null) return |
| | | productName.value = data.productName != null ? data.productName as string : productName.value |
| | | if (data.coverUrl != null && data.coverUrl != '') productImage.value = resolveApiUrl(data.coverUrl as string) |
| | |
| | | couponAmount.value = toAmount(data.couponAmount) |
| | | couponText.value = data.couponLabel != null ? data.couponLabel as string : '无' |
| | | productDiscount.value = data.discountText != null ? data.discountText as string : productDiscount.value |
| | | discountAmountText.value = toAmount(data.discountAmount).toFixed(2) |
| | | subsidyAmountText.value = toAmount(data.subsidyAmount).toFixed(2) |
| | | actualPayAmountText.value = toAmount(data.payAmount).toFixed(2) |
| | | }, (error) => { |
| | | if (sequence != quoteSequence) return |
| | |
| | | const selectedCouponId: any = uni.getStorageSync(couponStorageKey()) |
| | | const couponId = selectedCouponId != null ? '' + selectedCouponId : '' |
| | | createAppOrder(merchantId.value, productId.value, couponId, createOrderIdempotencyKey.value, (data: any) => { |
| | | isSubmitting.value = false |
| | | uni.removeStorageSync(couponStorageKey()) |
| | | const realOrderNo = data != null && data.orderNo != null ? '' + data.orderNo : '' |
| | | if (realOrderNo == '') { |
| | | isSubmitting.value = false |
| | | uni.showToast({ title: '订单创建失败,请稍后重试', icon: 'none' }) |
| | | return |
| | | } |
| | | if (data != null && data.paid === true) { |
| | | isSubmitting.value = false |
| | | uni.redirectTo({ url: '/pages/payment-success/payment-success?orderId=' + encodeURIComponent(realOrderNo) }) |
| | | return |
| | | } |
| | | uni.redirectTo({ |
| | | url: '/pages/pending-payment/pending-payment?orderId=' + encodeURIComponent(realOrderNo) |
| | | + '&talentId=' + encodeURIComponent(talentId) |
| | | }) |
| | | // 一次点击即支付:用户既然点了「确认支付」,就不要再让他到第二页点同一个按钮—— |
| | | // 建单之后立刻发起预支付并拉起收银台,只有放弃或失败才落到待支付页。 |
| | | startPayment(realOrderNo, talentId, data != null && data.priceVersion != null ? Number(data.priceVersion) : 1) |
| | | }, (error) => { |
| | | isSubmitting.value = false |
| | | uni.showToast({ title: error.message, icon: 'none' }) |
| | | }) |
| | | } |
| | | const startPayment = (orderNo: string, talentId: string, priceVersion: number) => { |
| | | createPendingOrderPayment(orderNo, talentId, priceVersion, (data: any) => { |
| | | if (data != null && data.paymentStatus == 'PAID') { |
| | | openPaymentSuccess(orderNo) |
| | | return |
| | | } |
| | | if (data != null && data.paymentStatus == 'PAYMENT_PROCESSING') { |
| | | // 关单中或结果未定:落到待支付页由它等结论(那里会显示「支付处理中」并退避轮询)。 |
| | | openPendingPayment(orderNo, talentId, true) |
| | | return |
| | | } |
| | | const params: any = data != null ? data.paymentParams : null |
| | | if (params == null || params.timeStamp == null || params.nonceStr == null || params.package == null || params.paySign == null) { |
| | | uni.showToast({ title: '支付参数获取失败,请在待支付页重试', icon: 'none' }) |
| | | openPendingPayment(orderNo, talentId, false) |
| | | return |
| | | } |
| | | presentCashier(orderNo, talentId, params) |
| | | }, (error: any) => { |
| | | uni.showToast({ title: error.message, icon: 'none' }) |
| | | openPendingPayment(orderNo, talentId, false) |
| | | }) |
| | | } |
| | | const presentCashier = (orderNo: string, talentId: string, params: any) => { |
| | | // #ifdef MP-WEIXIN |
| | | uni.requestPayment({ |
| | | provider: 'wxpay', |
| | | timeStamp: '' + params.timeStamp, |
| | | nonceStr: '' + params.nonceStr, |
| | | package: '' + params.package, |
| | | signType: params.signType != null ? '' + params.signType : 'RSA', |
| | | paySign: '' + params.paySign, |
| | | success: () => openPendingPayment(orderNo, talentId, true), |
| | | fail: (error: any) => { |
| | | const message = error != null && error.errMsg != null ? '' + error.errMsg : '' |
| | | if (message.indexOf('cancel') >= 0) { |
| | | // 用户关掉了收银台:告诉服务端一声,让它立刻尝试关单(服务端不据此判定收款)。 |
| | | abandonPendingOrderPayment(orderNo, talentId, '', () => openPendingPayment(orderNo, talentId, false), () => openPendingPayment(orderNo, talentId, false)) |
| | | return |
| | | } |
| | | uni.showToast({ title: '支付未完成,请在待支付页继续', icon: 'none' }) |
| | | openPendingPayment(orderNo, talentId, false) |
| | | } |
| | | }) |
| | | // #endif |
| | | // #ifndef MP-WEIXIN |
| | | uni.showToast({ title: '请在微信小程序中完成支付', icon: 'none' }) |
| | | openPendingPayment(orderNo, talentId, false) |
| | | // #endif |
| | | } |
| | | const openPendingPayment = (orderNo: string, talentId: string, waitingPayment: boolean) => { |
| | | isSubmitting.value = false |
| | | uni.redirectTo({ |
| | | url: '/pages/pending-payment/pending-payment?orderId=' + encodeURIComponent(orderNo) |
| | | + '&talentId=' + encodeURIComponent(talentId) |
| | | + (waitingPayment ? '&waiting=1' : '') |
| | | }) |
| | | } |
| | | const openPaymentSuccess = (orderNo: string) => { |
| | | isSubmitting.value = false |
| | | uni.redirectTo({ url: '/pages/payment-success/payment-success?orderId=' + encodeURIComponent(orderNo) }) |
| | | } |
| | | </script> |
| | | |
| | |
| | | .confirm-button { display: flex; width: 514rpx; height: 86rpx; margin: 146rpx auto 0; border-radius: 45rpx; background-image: linear-gradient(90deg, #FF4C32 0%, #FF841C 100%); justify-content: center; align-items: center; } |
| | | .confirm-button text { color: #FFFFFF; font-size: 30rpx; font-weight: 600; } |
| | | .confirm-button-active { opacity: .82; transform: scale(.99); } |
| | | .confirm-button-disabled { opacity: .62; transform: none; } |
| | | .safe-bottom { height: 44rpx; height: calc(44rpx + env(safe-area-inset-bottom)); } |
| | | </style> |