shinnosuke
4 天以前 38e39c50dcca12534c4efe2b49355a97c396b507
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
<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>