<template>
|
<view class="page">
|
<view class="page-header">
|
<view class="safe-space" :style="'height:' + navTop + 'px'"></view>
|
<view class="nav-row" :style="'height:' + navHeight + 'px;padding-right:' + navRight + 'px'">
|
<view class="back-button" hover-class="back-button-active" @tap="goBack"><image src="/static/news-assets/arrow-left.svg" mode="aspectFit"></image></view>
|
<text class="nav-title">待使用</text>
|
<view class="capsule-space" :style="'width:' + navWidth + 'px'"></view>
|
</view>
|
</view>
|
|
<scroll-view class="content" scroll-y="true" show-scrollbar="false">
|
<view class="content-inner">
|
<view class="qr-card">
|
<image v-if="codeImage != ''" class="code-image" :src="codeImage" mode="aspectFit" @error="handleCodeImageError"></image><text v-else class="dynamic-code-placeholder" @tap="refreshCode">{{codeError ? '核销码加载失败,点击重试' : '正在生成核销码'}}</text>
|
<text class="qr-tip" @tap="refreshCode">{{codeText}}</text>
|
</view>
|
|
<view class="product-card">
|
<image class="product-image" :src="productCoverUrl" mode="aspectFill" @error="handleCoverImageError"></image>
|
<view class="product-info"><text class="product-name">{{productName}}</text><text class="product-original">¥ {{originalAmount}}</text></view>
|
<view class="product-payment"><text class="payment-label">实付</text><text class="payment-price">¥ {{payAmount}}</text></view>
|
</view>
|
|
<view class="merchant-card">
|
<view class="merchant-info"><text class="merchant-name">{{merchantName}}</text><text class="merchant-address">{{merchantAddress}}</text></view>
|
<view class="merchant-actions">
|
<view class="merchant-action" hover-class="action-active" @tap="openNavigation"><image src="/static/merchant-assets/navigation.svg" mode="aspectFit"></image><text>导航</text></view>
|
<view class="merchant-action" hover-class="action-active" @tap="callMerchant"><image src="/static/merchant-assets/phone.svg" mode="aspectFit"></image><text>电话</text></view>
|
</view>
|
</view>
|
|
<view class="order-card">
|
<text class="order-title">订单信息</text>
|
<view class="order-row"><text>订单号:</text><text>{{orderId}}</text></view><view class="order-row"><text>商品金额:</text><text>{{originalAmount}}元</text></view><view class="order-row"><text>优惠金额:</text><text>-{{discountAmount}}元</text></view><view class="order-row"><text>实际支付:</text><text>{{payAmount}}元</text></view><view class="order-row"><text>下单时间:</text><text>{{createdAt}}</text></view><view class="order-row"><text>核销时间:</text><text>{{verifyTime}}</text></view>
|
</view>
|
<view class="content-bottom"></view>
|
</view>
|
</scroll-view>
|
|
<view v-if="canRefund" class="refund-footer">
|
<view class="refund-button" hover-class="refund-button-active" @tap="requestRefund"><text>申请退款</text></view>
|
</view>
|
</view>
|
</template>
|
|
<script setup lang="uts">
|
import { apiEnvironment } from '../../config/api.uts'
|
import { resolveApiUrl } from '../../services/http.uts'
|
import { getPendingUseOrder, issueOrderVerificationCode, refundPendingUseOrder } from '../../services/portal.uts'
|
import { safeBack } from '../../utils/navigation.uts'
|
|
const navTop = ref(28)
|
const navHeight = ref(32)
|
const navWidth = ref(88)
|
const navRight = ref(10)
|
const isSubmittingRefund = ref(false)
|
const productCoverUrl = ref('/static/common/image-placeholder.png')
|
const recordId = ref('')
|
const orderId = ref(''); const talentId = ref(''); const codeImage = ref(''); const codeText = ref('正在生成动态核销码'); const codeError = ref(false); const productName = ref('人才权益服务'); const originalAmount = ref('0.00'); const payAmount = ref('0.00'); const discountAmount = ref('0.00'); const merchantName = ref(''); const merchantAddress = ref(''); const merchantMobile = ref(''); const merchantLatitude = ref(0.0); const merchantLongitude = ref(0.0); const createdAt = ref(''); const verifyTime = ref('-'); const canRefund = ref(false); let refreshTimer = 0
|
|
onLoad((options: any) => {
|
// #ifdef MP-WEIXIN
|
const capsule = uni.getMenuButtonBoundingClientRect()
|
const systemInfo = uni.getSystemInfoSync()
|
navTop.value = capsule.top
|
navHeight.value = capsule.height
|
navWidth.value = capsule.width
|
navRight.value = systemInfo.windowWidth - capsule.right
|
// #endif
|
if (options != null && options['recordId'] != null) recordId.value = decodeURIComponent(options['recordId'] as string)
|
if (options != null && options['orderId'] != null) orderId.value = options['orderId'] as string
|
talentId.value = options != null && options['talentId'] != null ? options['talentId'] as string : (apiEnvironment == 'development' ? '2' : '')
|
loadOrder()
|
})
|
onShow(() => { if (orderId.value != '') { loadOrder(); refreshCode() } })
|
onHide(() => stopRefresh())
|
onUnload(() => stopRefresh())
|
|
const goBack = () => safeBack('/pages/order/order')
|
const loadOrder = () => getPendingUseOrder(orderId.value, talentId.value, (data:any) => {
|
// 已退款(含退款中)或已取消的订单不再是「待使用」:这一页不发核销码,直接进订单状态页看退款结果。
|
if (Number(data.orderStatus) == 2 || data.refundStatus != null) {
|
uni.redirectTo({ url: '/pages/order-cancelled/order-cancelled?recordId=' + encodeURIComponent(recordId.value) + '&orderId=' + encodeURIComponent(orderId.value) + '&talentId=' + encodeURIComponent(talentId.value) })
|
return
|
}
|
productName.value=data.productNameSnapshot||productName.value; productCoverUrl.value=resolveApiUrl(data.productCoverUrlSnapshot||'/static/common/image-placeholder.png'); originalAmount.value=Number(data.originalAmount).toFixed(2); payAmount.value=Number(data.payAmount).toFixed(2); discountAmount.value=Number(data.discountAmount).toFixed(2); merchantName.value=data.merchantNameSnapshot||''; merchantAddress.value=data.merchantAddressSnapshot||''; merchantMobile.value=data.merchantMobileSnapshot||''; merchantLatitude.value=Number(data.merchantLatitudeSnapshot); merchantLongitude.value=Number(data.merchantLongitudeSnapshot); createdAt.value=(''+data.createdAt).replace('T',' ').substring(0,19); verifyTime.value=data.verifyTime?(''+data.verifyTime).replace('T',' ').substring(0,19):'-'; canRefund.value=data.canRefund===true }, () => {})
|
const stopRefresh=()=>{if(refreshTimer!=0){clearTimeout(refreshTimer);refreshTimer=0}}
|
const refreshCode=()=>{stopRefresh(); if(orderId.value=='')return; codeError.value=false; issueOrderVerificationCode(orderId.value,talentId.value,(data:any)=>{codeImage.value=resolveApiUrl(data.imagePath as string);codeText.value='核销码 '+(data.displayCode as string)+',请向商家出示';refreshTimer=setTimeout(()=>refreshCode(),Math.max(1,Number(data.refreshAfterSeconds)-3)*1000)},()=>{codeImage.value='';codeError.value=true;codeText.value='点击重新生成核销码'})}
|
// @error 必须收回图片地址:二维码取不回来时页面只会剩一片空白,用户分不清是没生成、没网还是码失效。
|
// 这是 7.22 遗留问题 P1-006(远程图片失败会留白)的同一类缺陷,人才码和免费券码页都已按此处理。
|
const handleCodeImageError=()=>{codeImage.value='';codeError.value=true;codeText.value='核销码加载失败,点击重新生成'}
|
const handleCoverImageError=()=>{productCoverUrl.value='/static/common/image-placeholder.png'}
|
const openNavigation = () => { if(merchantLatitude.value==0||merchantLongitude.value==0){uni.showToast({title:'商户暂未配置导航地址',icon:'none'});return};uni.openLocation({latitude:merchantLatitude.value,longitude:merchantLongitude.value,name:merchantName.value,address:merchantAddress.value,scale:16}) }
|
const callMerchant = () => { if(merchantMobile.value==''){uni.showToast({title:'商户暂未配置联系电话',icon:'none'});return};uni.makePhoneCall({phoneNumber:merchantMobile.value}) }
|
const requestRefund = () => {
|
if (isSubmittingRefund.value) return
|
uni.showModal({
|
title: '申请退款',
|
content: '确认申请退款吗?提交后将由后台审核处理。',
|
confirmText: '确认申请',
|
confirmColor: '#FF6330',
|
success: (result) => {
|
if (!result.confirm) return
|
if (isSubmittingRefund.value) return
|
isSubmittingRefund.value = true
|
refundPendingUseOrder(orderId.value,talentId.value,'用户申请退款',()=>{isSubmittingRefund.value=false;canRefund.value=false;uni.showToast({title:'退款申请已提交',icon:'success'});loadOrder()},(error)=>{isSubmittingRefund.value=false;uni.showToast({title:error.message,icon:'none'})})
|
}
|
})
|
}
|
</script>
|
|
<style>
|
.page { display: flex; width: 100%; height: 100vh; background-color: #F5F7FA; background-image: linear-gradient(180deg, #FFF0D7 0%, #FFF2DE 250rpx, #F5F7FA 520rpx); flex-direction: column; }
|
.page-header { position: relative; z-index: 1; flex-shrink: 0; padding-bottom: 55rpx; }
|
.safe-space { height: 28px; }
|
.nav-row { display: flex; height: 32px; box-sizing: border-box; padding-left: 20rpx; padding-right: 10px; position: relative; flex-direction: row; align-items: center; justify-content: space-between; }
|
.back-button { display: flex; width: 70rpx; height: 64rpx; margin-left: -4rpx; justify-content: center; align-items: center; }
|
.back-button image { width: 36rpx; height: 36rpx; }
|
.back-button-active { opacity: .55; }
|
.nav-title { position: absolute; left: 50%; top: 50%; color: #171B22; font-size: 37rpx; line-height: 48rpx; font-weight: 700; transform: translate(-50%, -50%); white-space: nowrap; }
|
.capsule-space { width: 88px; height: 100%; flex-shrink: 0; }
|
.content { min-height: 0; z-index: 2; flex: 1; background-color: transparent; }
|
.content-inner { position: relative; z-index: 2; padding: 0 28rpx; }
|
.qr-card { display: flex; height: 348rpx; margin-top: -3rpx; border-radius: 26rpx; background-color: #FFFFFF; flex-direction: column; justify-content: center; align-items: center; box-shadow: 0 8rpx 24rpx rgba(69,79,92,.025); }
|
.code-image { width: 232rpx; height: 232rpx; }
|
.dynamic-code-placeholder { max-width: 360rpx; color: #606B78; font-size: 28rpx; line-height: 40rpx; font-weight: 600; text-align: center; }
|
.qr-tip { margin-top: 5rpx; color: #B0B6BF; font-size: 20rpx; line-height: 28rpx; }
|
.product-card { display: flex; height: 158rpx; margin-top: 27rpx; box-sizing: border-box; padding: 27rpx 28rpx 27rpx 17rpx; border-radius: 25rpx; background-color: #FFFFFF; flex-direction: row; align-items: center; }
|
.product-image { width: 146rpx; height: 106rpx; flex-shrink: 0; border-radius: 13rpx; background-color: #E7EBEF; }
|
.product-info { display: flex; min-width: 0; flex: 1; height: 103rpx; margin-left: 23rpx; flex-direction: column; justify-content: space-between; }
|
.product-name { color: #29303A; font-size: 28rpx; line-height: 39rpx; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
.product-original { color: #A7AEB8; font-size: 22rpx; line-height: 31rpx; }
|
.product-payment { display: flex; height: 103rpx; margin-left: 14rpx; flex-shrink: 0; flex-direction: column; align-items: flex-end; justify-content: space-between; }
|
.payment-label { color: #79818D; font-size: 21rpx; line-height: 30rpx; }
|
.payment-price { color: #FF542F; font-size: 31rpx; line-height: 39rpx; font-weight: 700; }
|
.merchant-card { display: flex; min-height: 174rpx; margin-top: 27rpx; box-sizing: border-box; padding: 31rpx 27rpx; border-radius: 25rpx; background-color: #FFFFFF; flex-direction: row; align-items: center; }
|
.merchant-info { min-width: 0; flex: 1; }
|
.merchant-name { display: block; color: #29303A; font-size: 29rpx; line-height: 41rpx; font-weight: 700; }
|
.merchant-address { display: block; margin-top: 15rpx; color: #A0A8B3; font-size: 21rpx; line-height: 30rpx; }
|
.merchant-actions { display: flex; width: 137rpx; margin-left: 20rpx; flex-shrink: 0; flex-direction: row; justify-content: space-between; align-items: center; }
|
.merchant-action { display: flex; width: 60rpx; flex-direction: column; align-items: center; }
|
.merchant-action image { width: 39rpx; height: 39rpx; }
|
.merchant-action text { margin-top: 10rpx; color: #303640; font-size: 20rpx; line-height: 27rpx; font-weight: 600; }
|
.action-active { opacity: .55; }
|
.order-card { margin-top: 27rpx; box-sizing: border-box; padding: 31rpx 28rpx 30rpx; border-radius: 25rpx; background-color: #FFFFFF; }
|
.order-title { display: block; margin-bottom: 18rpx; color: #29303A; font-size: 28rpx; line-height: 39rpx; font-weight: 700; }
|
.order-row { display: flex; min-height: 49rpx; flex-direction: row; align-items: center; justify-content: space-between; color: #7E8794; font-size: 23rpx; line-height: 34rpx; }
|
.order-row text:last-child { margin-left: 24rpx; color: #3F4855; text-align: right; }
|
.content-bottom { height: 34rpx; }
|
.refund-footer { flex-shrink: 0; padding: 20rpx 64rpx; padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); background-color: transparent; }
|
.refund-button { display: flex; height: 84rpx; border-radius: 44rpx; background-image: linear-gradient(90deg, #FF4E35 0%, #FF8318 100%); justify-content: center; align-items: center; }
|
.refund-button text { color: #FFFFFF; font-size: 28rpx; line-height: 40rpx; font-weight: 700; }
|
.refund-button-active { opacity: .78; transform: scale(.99); }
|
</style>
|