2025-10-14 17:46:23 +08:00

239 lines
7.1 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<uni-nav-bar
left-icon="left"
title="兑换福利卡"
@clickLeft="goBack"
fixed
color="#8B2316"
height="180rpx"
:border="false"
backgroundColor="#eeeeee"
></uni-nav-bar>
<view class="exchange-page">
<!-- 顶部红色横幅 -->
<view class="top-banner">
<view class="banner-text">
<view class="line1">已兑换{{ exchangedCount }}</view>
<view class="line2" @click="goMyWelfare">查看现有权益</view>
</view>
<view class="help-btn" @click="showHelp">帮助说明</view>
</view>
<!-- 使用统一的自定义居中模态框 -->
<view v-if="centerVisible" class="center-modal" @click.self="closeCenter">
<view class="center-modal-content">
<view class="center-title">{{ centerHelp ? '帮助说明' : '提示' }}</view>
<view v-if="centerHelp" class="help-content center-help">
<text>1点击兑换福利卡输入密码即可兑换相应权益</text>
<text>2每张福利卡仅限兑换一次兑换后权益可在我的福利-使用福利中查看</text>
<text>3福利卡长期有效福利卡不能退换或者折现</text>
<text>4查找文献权益不限文献类型如指南共识论文电子书课件或者视频</text>
</view>
<view v-else class="center-body">{{ centerText }}</view>
<view class="center-actions">
<button class="center-btn" @click="closeCenter">知道了</button>
</view>
</view>
</view>
<!-- 输入提示 -->
<view class="tips">请输入16位福利卡密码不区分大小写<text class="paste-action" @click="pasteFromClipboard">粘贴</text></view>
<!-- 四段输入框 -->
<view class="code-inputs">
<input class="code-box" type="text" v-model="code1" maxlength="4" placeholder="" :focus="f1" @input="handleInput(1, $event)" @paste="handlePaste"/>
<input class="code-box" type="text" v-model="code2" maxlength="4" placeholder="" :focus="f2" @input="handleInput(2, $event)"/>
<input class="code-box" type="text" v-model="code3" maxlength="4" placeholder="" :focus="f3" @input="handleInput(3, $event)"/>
<input class="code-box" type="text" v-model="code4" maxlength="4" placeholder="" :focus="f4" @input="handleInput(4, $event)"/>
</view>
<!-- 按钮 -->
<view class="btn-wrapper">
<button class="submit-btn" :disabled="!isFull" @click="submit">立即兑换</button>
</view>
</view>
</template>
<script setup>
import { ref, computed, nextTick } from 'vue'
import api from '@/api/api';
const exchangedCount = ref(5)
const code1 = ref('')
const code2 = ref('')
const code3 = ref('')
const code4 = ref('')
const f1 = ref(true)
const f2 = ref(false)
const f3 = ref(false)
const f4 = ref(false)
const helpVisible = ref(false)
const centerVisible = ref(false)
const centerText = ref('')
const centerHelp = ref(false)
const isFull = computed(() => (code1.value+code2.value+code3.value+code4.value).length === 16)
const goBack = () => {
uni.navigateBack({
fail() {
uni.redirectTo({ url: '/pages/index/index' })
}
})
}
const showHelp = () => {
centerHelp.value = true
centerVisible.value = true
}
// 通用模态框
const openCenter = (text) => {
centerText.value = text
centerHelp.value = false
centerVisible.value = true
}
const closeCenter = () => {
centerVisible.value = false
centerHelp.value = false
}
const setFocus = (idx) => {
f1.value = idx === 1
f2.value = idx === 2
f3.value = idx === 3
f4.value = idx === 4
}
const handleInput = (idx, e) => {
const raw = (e.detail && e.detail.value) || ''
const sanitized = raw.replace(/[^a-zA-Z0-9]/g, '')
if (idx === 1) code1.value = sanitized
if (idx === 2) code2.value = sanitized
if (idx === 3) code3.value = sanitized
if (idx === 4) code4.value = sanitized
if (sanitized.length === 4 && idx < 4) {
nextTick(() => setFocus(idx + 1))
}
}
const submit = () => {
if (!isFull.value) return
const code = (code1.value+code2.value+code3.value+code4.value).toUpperCase()
uni.showToast({ title: '兑换中: '+ code, icon: 'none' })
api.exchangeWelfareCard({password: code}).then(res => {
console.log(res)
if (res.code == 200) {
uni.showToast({ title: '兑换成功', icon: 'none' })
uni.navigateBack()
}
})
}
const goMyWelfare = () => {
uni.navigateTo({ url: '/pages_app/myWelfare/myWelfare' })
}
// 处理粘贴H5等支持paste事件的平台
const handlePaste = (e) => {
const text = (e.clipboardData && e.clipboardData.getData('text')) || ''
fillByText(text)
// 阻止默认粘贴到单个输入框
e && e.preventDefault && e.preventDefault()
}
// 从剪贴板读取App、小程序
const pasteFromClipboard = () => {
uni.getClipboardData({
success: (res) => {
fillByText(res.data || '')
}
})
}
const fillByText = (raw) => {
const v = String(raw || '').replace(/[^a-zA-Z0-9]/g, '').toUpperCase().slice(0, 16)
code1.value = v.slice(0, 4)
code2.value = v.slice(4, 8)
code3.value = v.slice(8, 12)
code4.value = v.slice(12, 16)
nextTick(() => setFocus(v.length >= 16 ? 4 : Math.floor((v.length)/4) + 1))
}
</script>
<style lang="scss" scoped>
$nav-height: 140rpx;
.exchange-page{
min-height: 100vh;
background: #f5f6f7;
}
.top-banner{
position: relative;
height: 280rpx;
background: linear-gradient(180deg,#ff6a4a 0%, #e93b2d 100%);
border-bottom-left-radius: 40rpx;
border-bottom-right-radius: 40rpx;
.banner-text{
position: absolute;
left: 48rpx;
top: 120rpx;
color: #fff;
.line1{font-size: 44rpx;font-weight: 600;}
.line2{font-size: 36rpx;margin-top: 12rpx;}
}
.help-btn{
position: absolute;
right: 40rpx;
top: 90rpx;
background: rgba(255,255,255,.95);
color: #e04835;
border-radius: 999rpx;
padding: 10rpx 24rpx;
font-size: 24rpx;
}
}
.tips{
margin: 48rpx;
color: #333;
font-size: 32rpx;
.paste-action{
color: #007aff;
text-decoration: underline;
}
}
.code-inputs{
display: flex;
gap: 30rpx;
padding: 0 48rpx;
.code-box{
flex: 1;
height: 96rpx;
background: #fff;
border-radius: 16rpx;
text-align: center;
font-size: 36rpx;
}
}
.btn-wrapper{
padding: 80rpx 48rpx 0;
.submit-btn{
width: 100%;
height: 100rpx;
background: linear-gradient(90deg,#ff4d2e,#e93b2d);
border-radius: 60rpx;
color: #fff;
font-size: 36rpx;
}
.submit-btn[disabled]{
opacity: .6;
}
}
/* 帮助弹层样式 */
.center-help{padding: 0 32rpx;display:flex;flex-direction:column;align-items:center;gap: 16rpx;color:#333;font-size:28rpx;line-height:1.6;}
/* 自定义通用模态框 */
.center-modal{position: fixed;left:0;right:0;top:0;bottom:0;background: rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;z-index: 9999;}
.center-modal-content{width: 640rpx;background:#fff;border-radius:24rpx;overflow:hidden;}
.center-title{font-size: 32rpx;color:#333;text-align:center;padding:28rpx 24rpx 12rpx;font-weight:600;}
.center-body{padding:0 32rpx 12rpx;color:#333;font-size:28rpx;line-height:1.7;text-align:center;}
.center-actions{padding: 24rpx 24rpx 28rpx;}
.center-btn{width:100%;height:88rpx;border-radius:999rpx;background:#e93b2d;color:#fff;font-size:30rpx;}
</style>