yeg
2026-09-28 85bfb9351803d97a33329d507f30a2f78bf1fc75
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<template>
    <view class="page">
        <view class="navigation-bar">
            <view class="back-button" @tap="goBack"><image class="back-icon" src="/static/icons/lucide/back-left.svg" mode="aspectFit"></image></view>
            <text class="navigation-title">核销输入</text>
        </view>
        <scroll-view class="content" scroll-y="true" :show-scrollbar="false">
            <view class="talent-card">
                <view class="card-decoration"></view>
                <view class="talent-head"><text class="talent-name">{{customerName}}</text><text class="talent-level">{{cardLevel}}</text></view>
                <text class="talent-mobile">{{displayMobile(customerMobile)}}</text>
                <view class="current-benefit"><text class="benefit-label">当前权益</text><text class="benefit-text">免费入住{{totalCount}}天</text><text v-if="discountText.length > 0" class="benefit-text benefit-extra">超过天数{{discountText}}</text></view>
            </view>
            <view class="method-card">
                <text class="section-label">优惠方式</text>
                <view class="tag-list"><text class="benefit-tag">免费入住{{totalCount}}天</text><text v-if="discountText.length > 0" class="benefit-tag">{{discountText}}</text></view>
            </view>
            <view class="stay-ticket">
                <view class="ticket-left"><text class="ticket-vertical">入<br/>住<br/>券</text></view>
                <view class="ticket-divider"></view>
                <view class="ticket-right"><text class="ticket-count">一天</text><text class="ticket-free">免费</text></view>
                <view class="ticket-notch ticket-notch-left"></view><view class="ticket-notch ticket-notch-right"></view>
            </view>
            <view class="content-bottom-space"></view>
        </scroll-view>
        <view class="bottom-bar"><view class="confirm-button" :class="submitting || loadError.length > 0 ? 'button-disabled' : ''" @tap="confirm"><text>{{submitting ? '核销中...' : '确认核销'}}</text></view></view>
    </view>
</template>
 
<script setup lang="uts">
    import { confirmVerificationCode } from '../../services/verification.uts'
 
    const verificationId = ref('')
    const customerName = ref('持卡人')
    const customerMobile = ref('')
    const cardLevel = ref('人才卡')
    const totalCount = ref(5)
    const discountText = ref('')
    const loadError = ref('')
    const submitting = ref(false)
 
    const textOption = (options: any, name: string, fallback: string): string => options != null && options[name] != null && ('' + options[name]).length > 0 ? decodeURIComponent('' + options[name]) : fallback
    const displayMobile = (mobile: string): string => mobile.length < 7 ? mobile : mobile.substring(0, 3) + '****' + mobile.substring(mobile.length - 4)
    const goBack = () => uni.navigateBack()
 
    const parseDiscount = (value: string): string => {
        if (value.length == 0 || value.indexOf('折') < 0) return ''
        const end = value.indexOf('折') + 1
        const parts = value.substring(0, end).split(';')
        return parts[parts.length - 1].split(',')[0].trim()
    }
 
    const confirm = () => {
        if (submitting.value || verificationId.value.length == 0 || loadError.value.length > 0) return
        uni.showModal({
            title: '确认核销',
            content: '确认核销本次免费入住券吗?',
            confirmText: '确认核销',
            confirmColor: '#FF6B2C',
            success: (modal: any) => {
                if (!modal.confirm) return
                submitting.value = true
                confirmVerificationCode(verificationId.value, 'merchant-free-stay-' + verificationId.value + '-' + Date.now(), (result: any, _traceId: string) => {
                    submitting.value = false
                    const verifyNo = result != null && result.verifyNo != null ? '' + result.verifyNo : ''
                    uni.redirectTo({ url: '/pages/verification-success/verification-success?orderNo=' + encodeURIComponent(verifyNo) + '&credentialType=FREE_STAY_CODE' })
                }, (error: any) => {
                    submitting.value = false
                    uni.showToast({ title: error != null && error.message != null ? '' + error.message : '核销失败,请重试', icon: 'none' })
                })
            }
        })
    }
 
    onLoad((options: any) => {
        verificationId.value = textOption(options, 'verificationId', '')
        customerName.value = textOption(options, 'customerName', '持卡人')
        customerMobile.value = textOption(options, 'customerMobile', '')
        cardLevel.value = textOption(options, 'cardLevel', '人才卡')
        totalCount.value = Math.max(Number(textOption(options, 'freeStayTotalCount', '5')), 1)
        // 优先用服务端按门店到店消费折扣算出来的文案;老版本没带这个参数时,仍从权益文案里兜底解析。
        discountText.value = textOption(options, 'discountText', '')
        if (discountText.value.length == 0) discountText.value = parseDiscount(textOption(options, 'benefitText', ''))
        if (verificationId.value.length == 0) loadError.value = '核销凭证无效,请返回重新扫码'
    })
</script>
 
<style>
    page { background-color: #F7F8FA; }
    .page { display: flex; width: 100%; height: 100vh; flex-direction: column; overflow: hidden; background-color: #F7F8FA; }
    .navigation-bar { display: flex; height: 174rpx; flex: none; justify-content: center; align-items: flex-end; padding-bottom: 25rpx; box-sizing: border-box; position: relative; }
    /* 标题绝对定位居中:bottom 取值让标题中线与返回按钮/胶囊中线对齐(与核销输入页保持一致)。 */
    .navigation-title { position: absolute; right: 0; bottom: 30rpx; left: 0; z-index: 1; color: #1F2937; font-size: 35rpx; line-height: 49rpx; font-weight: 700; text-align: center; }
    .back-button { display: flex; width: 72rpx; height: 72rpx; justify-content: center; align-items: center; position: absolute; top: 82rpx; left: 28rpx; }
    .back-icon { width: 40rpx; height: 40rpx; }
    .content { height: 0; flex: 1; padding: 20rpx 34rpx 0; box-sizing: border-box; }
    .talent-card { min-height: 238rpx; padding: 34rpx 34rpx 28rpx; border-radius: 38rpx; background-image: linear-gradient(135deg, #C99D32 0%, #F7E69D 55%, #E5BD4E 100%); box-shadow: 0 18rpx 38rpx rgba(188, 139, 37, 0.2); box-sizing: border-box; position: relative; overflow: hidden; }
    .card-decoration { width: 220rpx; height: 220rpx; border: 1rpx solid rgba(255, 255, 255, 0.35); border-radius: 110rpx; position: absolute; top: -72rpx; right: -12rpx; }
    .talent-head { display: flex; flex-direction: row; justify-content: space-between; align-items: center; position: relative; }
    .talent-name { color: #FFFFFF; font-size: 39rpx; line-height: 55rpx; font-weight: 700; }
    .talent-level { padding: 9rpx 20rpx; border-radius: 24rpx; color: #FFFFFF; font-size: 22rpx; line-height: 31rpx; font-weight: 600; background-color: rgba(149, 104, 22, 0.46); }
    .talent-mobile { display: block; margin-top: 15rpx; color: #FFFFFF; font-size: 29rpx; line-height: 41rpx; font-weight: 600; position: relative; }
    .current-benefit { display: flex; margin-top: 22rpx; flex-direction: row; align-items: center; position: relative; }
    .benefit-label { color: rgba(255, 255, 255, 0.78); font-size: 23rpx; line-height: 33rpx; }
    .benefit-text { margin-left: 18rpx; color: #FFFFFF; font-size: 23rpx; line-height: 33rpx; font-weight: 600; }
    /* 「超过天数门市价X折 / 立减X元」跟在免费额度后面,间隔比标签再大一点。 */
    .benefit-extra { margin-left: 14rpx; font-weight: 500; }
    .method-card { padding: 31rpx 34rpx; margin-top: 22rpx; border-radius: 30rpx; background-color: #FFFFFF; box-shadow: 0 14rpx 30rpx rgba(57, 67, 80, 0.06); }
    .section-label { display: block; color: #8D99AA; font-size: 27rpx; line-height: 38rpx; }
    .tag-list { display: flex; margin-top: 20rpx; flex-direction: row; flex-wrap: wrap; }
    .benefit-tag { display: flex; min-height: 48rpx; padding: 7rpx 18rpx; margin-right: 12rpx; margin-bottom: 4rpx; justify-content: center; align-items: center; border-radius: 24rpx; color: #FF772C; font-size: 23rpx; line-height: 33rpx; background-color: #FFF0E5; box-sizing: border-box; }
    .stay-ticket { display: flex; height: 210rpx; margin: 40rpx 42rpx 0; flex-direction: row; align-items: center; border-radius: 32rpx; background-image: linear-gradient(100deg, #FF5D25 0%, #FF8A27 100%); box-shadow: 0 18rpx 34rpx rgba(255, 98, 35, 0.23); position: relative; overflow: hidden; }
    .ticket-left { display: flex; width: 134rpx; justify-content: center; align-items: center; }
    .ticket-vertical { color: #FFFFFF; font-size: 28rpx; line-height: 37rpx; font-weight: 700; text-align: center; }
    .ticket-divider { width: 2rpx; height: 144rpx; background-color: rgba(255, 255, 255, 0.3); }
    .ticket-right { display: flex; flex: 1; flex-direction: column; justify-content: center; align-items: center; }
    .ticket-count { color: #FFFFFF; font-size: 43rpx; line-height: 60rpx; font-weight: 700; }
    .ticket-free { color: #FFFFFF; font-size: 43rpx; line-height: 60rpx; font-weight: 700; }
    .ticket-notch { width: 34rpx; height: 34rpx; border-radius: 20rpx; background-color: #F7F8FA; position: absolute; top: 88rpx; }
    .ticket-notch-left { left: -17rpx; }
    .ticket-notch-right { right: -17rpx; }
    .remaining-hint { margin-top: 20rpx; color: #98A2B1; font-size: 23rpx; line-height: 33rpx; text-align: center; }
    .content-bottom-space { height: 180rpx; }
    .bottom-bar { display: flex; height: 154rpx; padding: 20rpx 96rpx 34rpx; background-color: #FFFFFF; box-sizing: border-box; position: fixed; right: 0; bottom: 0; left: 0; z-index: 10; }
    .confirm-button { display: flex; height: 94rpx; flex: 1; justify-content: center; align-items: center; border-radius: 48rpx; color: #FFFFFF; font-size: 29rpx; line-height: 41rpx; font-weight: 700; background-image: linear-gradient(90deg, #FF7625 0%, #FF962F 100%); box-shadow: 0 12rpx 24rpx rgba(255, 107, 36, 0.22); }
    .button-disabled { opacity: 0.6; box-shadow: none; }
</style>