<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>
|
<view v-if="loading" class="page-state"><view class="loading-spinner"></view><text>正在加载观影券</text></view>
|
<view v-else-if="loadError" class="page-state error-state"><text>{{errorText}}</text><text class="retry-text" @tap="loadClaim">重新加载</text></view>
|
<view v-else class="content">
|
<image v-if="qrUrl.length > 0" class="voucher-qr" :src="qrUrl" mode="aspectFit"></image>
|
<text class="merchant-text">适用门店:{{merchantNames.length > 0 ? merchantNames : '以门店公示为准'}}</text>
|
<text class="time-text">领取时间:{{formatDateTime(createdAt)}}</text>
|
<text v-if="used" class="time-text">核销时间:{{formatDateTime(usedAt)}}</text>
|
<view v-if="canCopy" class="copy-button" hover-class="copy-button-active" @tap="copyExchangeCode"><text>复制兑换码</text></view>
|
<view v-else class="copy-button disabled-button"><text>{{buttonText}}</text></view>
|
</view>
|
</view>
|
</template>
|
|
<script setup lang="uts">
|
import { getTicketPackageClaim, getTicketPackageCodeImageUrl } from '../../services/portal.uts'
|
import { getSession } from '../../services/session.uts'
|
import { safeBack } from '../../utils/navigation.uts'
|
|
// Keep the first render below the status bar even before the MP capsule metrics arrive.
|
const navTop = ref(44); const navHeight = ref(32); const navWidth = ref(88); const navRight = ref(10)
|
const claimId = ref(''); const talentId = ref('')
|
const loading = ref(true); const loadError = ref(false); const errorText = ref('观影券详情加载失败')
|
const merchantNames = ref(''); const exchangeCode = ref(''); const claimStatus = ref('')
|
const createdAt = ref(''); const usedAt = ref('')
|
|
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['claimId'] != null) claimId.value = String(options['claimId'])
|
if (options != null && options['talentId'] != null) talentId.value = String(options['talentId'])
|
const session = getSession()
|
if (talentId.value == '' && session != null && session.talentId != null) talentId.value = String(session.talentId)
|
loadClaim()
|
})
|
|
const used = computed((): boolean => claimStatus.value == 'USED')
|
const canCopy = computed((): boolean => claimStatus.value == 'ISSUED' && exchangeCode.value != '')
|
const buttonText = computed((): string => used.value ? '已使用' : '已失效')
|
/** 二维码内容就是后台发的兑换码,店员扫码后走观影权益核销。 */
|
const qrUrl = computed((): string => claimId.value == '' ? '' : getTicketPackageCodeImageUrl(claimId.value))
|
const formatDateTime = (value: string): string => value.length > 0 ? value.replace('T', ' ') : '—'
|
const goBack = () => safeBack('/pages/order/order')
|
|
const loadClaim = () => {
|
if (claimId.value == '' || talentId.value == '') { loading.value = false; loadError.value = true; errorText.value = '观影券参数缺失'; return }
|
loading.value = true; loadError.value = false
|
getTicketPackageClaim(claimId.value, talentId.value, (data: any) => {
|
loading.value = false
|
merchantNames.value = data.merchantNames != null ? String(data.merchantNames) : (data.merchantName != null ? String(data.merchantName) : '')
|
exchangeCode.value = data.exchangeCode != null ? String(data.exchangeCode) : ''
|
claimStatus.value = data.claimStatus != null ? String(data.claimStatus) : (data.exchangeCodeStatus != null ? String(data.exchangeCodeStatus) : '')
|
createdAt.value = data.createdAt != null ? String(data.createdAt) : ''
|
usedAt.value = data.usedAt != null ? String(data.usedAt) : ''
|
}, (error: any) => { loading.value = false; loadError.value = true; errorText.value = error.message != null ? error.message : '观影券详情加载失败' })
|
}
|
|
const copyExchangeCode = () => {
|
if (!canCopy.value) return
|
uni.setClipboardData({ data: exchangeCode.value, success: () => uni.showToast({ title: '兑换码已复制', icon: 'success' }), fail: () => uni.showToast({ title: '复制失败,请重试', icon: 'none' }) })
|
}
|
</script>
|
|
<style>
|
.page { display: flex; width: 100%; height: 100vh; box-sizing: border-box; overflow: hidden; background-color: #FFFFFF; flex-direction: column; }
|
.page-header { position: relative; z-index: 2; flex-shrink: 0; }.safe-space { height: 28px; }
|
.nav-row { display: flex; box-sizing: border-box; padding-left: 20rpx; 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: #171B23; font-size: 36rpx; line-height: 48rpx; font-weight: 700; transform: translate(-50%, -50%); white-space: nowrap; }
|
.capsule-space { height: 100%; flex-shrink: 0; }
|
.content { display: flex; flex: 1; padding: 0 66rpx; box-sizing: border-box; flex-direction: column; align-items: center; }
|
.voucher-qr { width: 320rpx; height: 320rpx; margin-top: 60rpx; display: block; background-color: #FFFFFF; }
|
.merchant-text { margin-top: 56rpx; color: #3A4353; font-size: 27rpx; line-height: 38rpx; text-align: center; }
|
.time-text { margin-top: 30rpx; color: #3A4353; font-size: 27rpx; line-height: 38rpx; text-align: center; }
|
.copy-button { display: flex; width: 100%; height: 88rpx; margin-top: 96rpx; flex-shrink: 0; border-radius: 12rpx; color: #FFFFFF; background-color: #2F7BF6; flex-direction: row; justify-content: center; align-items: center; }
|
.copy-button-active { opacity: .78; }.copy-button text { font-size: 30rpx; font-weight: 600; }
|
.disabled-button { background-color: #C9CED8; }
|
.page-state { display: flex; min-height: 560rpx; color: #6F7885; font-size: 27rpx; flex-direction: column; align-items: center; justify-content: center; }
|
.loading-spinner { width: 42rpx; height: 42rpx; margin-bottom: 22rpx; box-sizing: border-box; border: 5rpx solid #DCE3EE; border-top-color: #2F7BF6; border-radius: 50%; animation: ticket-spin 750ms linear infinite; }
|
.error-state { color: #7D8490; }.retry-text { margin-top: 20rpx; color: #2F7BF6; }
|
@keyframes ticket-spin { to { transform: rotate(360deg); } }
|
</style>
|