yeg
2026-09-17 dd22571b369970b35ca6c48f32010e2876b43d25
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
<template>
    <view class="page">
        <view class="navigation-bar">
            <view class="back-button" @tap.stop="goBack"><image class="back-icon" src="/static/icons/lucide/back-left.svg" mode="aspectFit"></image></view>
            <text class="navigation-title">手输核销码</text>
        </view>
        <view class="content">
            <text class="hint">请输入顾客屏幕上的 6 位数字核销码</text>
            <view class="input-shell" :class="errorText.length > 0 ? 'field-invalid' : ''">
                <input class="code-input" type="number" maxlength="6" :value="code" placeholder="6 位数字"
                    placeholder-class="code-placeholder" @input="onCodeInput" @confirm="submit" />
            </view>
            <text v-if="errorText.length > 0" class="field-error">{{errorText}}</text>
            <text class="tip">连续输错会短暂锁定,锁定期间请改用扫码核销。</text>
        </view>
        <view class="bottom-bar">
            <view class="submit-button" :class="submitting || !codeReady() ? 'submit-disabled' : ''" hover-class="submit-active" @tap="submit">
                <text>{{submitting ? '核验中...' : '核验并核销'}}</text>
            </view>
        </view>
    </view>
</template>
 
<script setup lang="uts">
    import { scanVerificationCode, VerificationScanResult } from '../../services/verification.uts'
 
    const code = ref('')
    const errorText = ref('')
    const submitting = ref(false)
 
    const goBack = () => uni.navigateBack()
    const codeReady = () : boolean => code.value.length == 6
 
    // 只留数字:数字键盘上仍然可能粘进空格或其它字符。
    const onCodeInput = (event: any) : void => {
        const raw = event != null && event.detail != null && event.detail.value != null ? '' + event.detail.value : ''
        code.value = raw.replace(/[^0-9]/g, '').substring(0, 6)
        if (errorText.value.length > 0) errorText.value = ''
    }
 
    const submit = () : void => {
        if (submitting.value || !codeReady()) return
        submitting.value = true
        errorText.value = ''
        scanVerificationCode(code.value, (result: VerificationScanResult, _traceId: string) => {
            submitting.value = false
            if (!result.canVerify) {
                errorText.value = result.message.length > 0 ? result.message : '核销码无效,请核对后重新输入'
                return
            }
            // 确认核销放在承接页上(与扫码同一条动线):那里会再把金额、权益与顾客信息摆一次让商户确认。
            // 数字码这条路服务端不返回订单号,所以带 numeric=1 让确认页走按扫码尝试确认的接口。
            uni.navigateTo({
                url: '/pages/verification-confirm/verification-confirm?numeric=1'
                    + '&verificationId=' + encodeURIComponent(result.verificationId)
                    + '&credentialType=' + encodeURIComponent(result.credentialType)
                    + '&customerName=' + encodeURIComponent(result.customerName)
                    + '&customerMobile=' + encodeURIComponent(result.customerMobile)
                    + '&cardLevel=' + encodeURIComponent(result.cardLevel)
            })
        }, (error: any) => {
            submitting.value = false
            errorText.value = error != null && error.message != null ? '' + error.message : '核验失败,请重试'
        })
    }
 
    onLoad(() => {})
</script>
 
<style>
    page { background-color: #F7F7F7; }
    .page { display: flex; width: 100%; height: 100vh; flex-direction: column; background-color: #F7F7F7; }
    .navigation-bar { display: flex; height: 174rpx; flex: none; position: relative; align-items: center; justify-content: center; }
    .back-button { display: flex; width: 72rpx; height: 72rpx; justify-content: center; align-items: center; position: absolute; left: 28rpx; bottom: 22rpx; }
    .back-icon { width: 40rpx; height: 40rpx; }
    .navigation-title { margin-top: 60rpx; color: #181A1E; font-size: 36rpx; font-weight: 700; }
    .content { display: flex; padding: 24rpx 44rpx 0; flex-direction: column; }
    .hint { color: #4A5261; font-size: 28rpx; line-height: 42rpx; }
    .input-shell { display: flex; height: 104rpx; margin-top: 26rpx; padding: 0 30rpx; border-width: 1rpx; border-style: solid; border-color: #E0E5EC; border-radius: 30rpx; background-color: #FFFEFD; box-sizing: border-box; align-items: center; }
    .field-invalid { border-color: #FF633A; }
    .code-input { width: 100%; height: 100%; color: #1F2530; font-size: 40rpx; font-weight: 700; letter-spacing: 12rpx; }
    .code-placeholder { color: #B4BAC4; font-size: 30rpx; font-weight: 400; letter-spacing: 0; }
    .field-error { margin-top: 16rpx; color: #FF633A; font-size: 25rpx; line-height: 36rpx; }
    .tip { margin-top: 18rpx; color: #97A0AE; font-size: 23rpx; line-height: 34rpx; }
    .bottom-bar { display: flex; padding: 24rpx 44rpx; padding-bottom: calc(24rpx + env(safe-area-inset-bottom)); margin-top: auto; }
    .submit-button { display: flex; flex: 1; height: 92rpx; justify-content: center; align-items: center; border-radius: 46rpx; background-image: linear-gradient(90deg, #FF7625 0%, #FF962F 100%); }
    .submit-button text { color: #FFFFFF; font-size: 29rpx; font-weight: 700; }
    .submit-disabled { opacity: .55; }
    .submit-active { opacity: .82; }
</style>