Files
ks-app-employment-service/pages/login/sms-verify.vue
2026-04-10 01:01:03 +08:00

593 lines
18 KiB
Vue
Raw 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>
<view class="sms-verify-page">
<!-- 顶部导航栏 -->
<view class="nav-bar">
<view class="nav-back" @click="goBack">
<uni-icons type="arrowleft" size="24" color="#333"></uni-icons>
</view>
<view class="nav-title">短信验证</view>
<view class="nav-placeholder"></view>
</view>
<!-- 页面内容 -->
<view class="page-content">
<!-- 安全提示 -->
<view class="security-tip">
<view class="tip-icon">
<uni-icons type="auth-filled" size="32" color="#256BFA"></uni-icons>
</view>
<view class="tip-text">为了您的账户安全需要您进行验证</view>
</view>
<!-- 手机号显示 -->
<view class="phone-display">
<view class="phone-label">验证码将发送至</view>
<view class="phone-number">{{ formattedPhone }}</view>
</view>
<!-- 验证码输入区域 -->
<view class="sms-input-area">
<view class="input-label">请输入6位数字验证码</view>
<view class="single-input-container">
<input
ref="smsInput"
class="single-input"
type="tel"
inputmode="numeric"
pattern="[0-9]*"
autocomplete="one-time-code"
maxlength="6"
:value="smsCode"
@input="onCodeInput"
@paste="onPaste"
placeholder="请输入验证码"
:focus="autoFocus"
/>
</view>
</view>
<!-- 倒计时和重发 -->
<view class="countdown-section">
<view v-if="countdown > 0" class="countdown-text">
{{ countdown }}秒后重新获取
</view>
<view v-else class="resend-text" @click="resendSms">
重新获取验证码
</view>
<view class="paste-section">
<view class="paste-text" @click="pasteFromClipboard">
<uni-icons type="clipboard" size="16" color="#256BFA"></uni-icons>
<text>粘贴验证码</text>
</view>
</view>
</view>
<!-- 提交按钮 -->
<button class="submit-btn" :disabled="!canSubmit" @click="submitVerification">
<text v-if="!loading">确认</text>
<view v-else class="loading-spinner"></view>
</button>
</view>
</view>
</template>
<script setup>
import { ref, inject, computed, onMounted, onUnmounted } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
const { $api } = inject('globalFunction');
// 从页面参数获取数据
const phone = ref('');
const openid = ref('');
const unionid = ref('');
const userType = ref('');
const orgType = ref('');
// 验证码相关
const smsCode = ref(''); // 单个字符串验证码
const smsInput = ref(null); // 输入框引用
const countdown = ref(60); // 倒计时
const timer = ref(null);
const loading = ref(false);
const autoFocus = ref(true); // 自动聚焦
// 格式化手机号:只显示前三位和后四位,中间用星号代替
const formattedPhone = computed(() => {
console.log('formattedPhone computed called, phone.value:', phone.value);
if (!phone.value || phone.value.trim() === '') {
console.log('手机号为空,显示未知号码');
return '未知号码';
}
// 清理手机号,移除空格和特殊字符
const cleanPhone = phone.value.replace(/\D/g, '');
if (cleanPhone.length < 11) {
console.log('手机号长度不足11位显示原始值', phone.value);
return phone.value;
}
const prefix = cleanPhone.substring(0, 3);
const suffix = cleanPhone.substring(cleanPhone.length - 4);
const formatted = `${prefix}****${suffix}`;
console.log('格式化后的手机号:', formatted);
return formatted;
});
// 是否可以提交
const canSubmit = computed(() => {
const can = smsCode.value.length === 6 && !loading.value;
console.log('canSubmit计算属性调用', {
smsCodeLength: smsCode.value.length,
loading: loading.value,
canSubmit: can
});
return can;
});
// 页面加载时获取参数
onLoad(async (options) => {
console.log('短信验证页面参数:', options);
console.log('所有参数键值对:');
Object.keys(options).forEach(key => {
console.log(` ${key}: ${options[key]}`);
});
// 尝试多种可能的手机号字段名(按优先级)
const possiblePhoneFields = [
'phone', 'mobile', 'phoneNumber', 'tel',
'phoneNum', 'userPhone', 'telephone', 'mobilePhone', 'cellphone',
'userTel', 'userMobile', 'contactPhone'
];
let foundPhone = '';
for (const field of possiblePhoneFields) {
if (options[field]) {
foundPhone = options[field];
console.log(`找到手机号字段 "${field}": ${foundPhone}`);
break;
}
}
// 如果没找到尝试从URL参数中解析
if (!foundPhone) {
console.log('未在options中找到手机号字段尝试从当前页面URL解析');
const currentPages = getCurrentPages();
if (currentPages.length > 0) {
const currentPage = currentPages[currentPages.length - 1];
console.log('当前页面对象:', currentPage);
// 尝试从页面路由参数中获取
if (currentPage.$page && currentPage.$page.fullPath) {
const urlParams = new URLSearchParams(currentPage.$page.fullPath.split('?')[1] || '');
for (const field of possiblePhoneFields) {
const value = urlParams.get(field);
if (value) {
foundPhone = value;
console.log(`从URL参数中找到手机号字段 "${field}": ${foundPhone}`);
break;
}
}
}
}
}
phone.value = foundPhone || '';
openid.value = options.openid || '';
unionid.value = options.unionid || '';
userType.value = options.userType || '';
orgType.value = options.orgType || '';
console.log('最终获取到的手机号:', phone.value);
console.log('手机号长度:', phone.value.length);
console.log('formattedPhone值', formattedPhone.value);
// 检查所有可能包含手机号的字段
console.log('所有可能包含手机号的字段值:');
possiblePhoneFields.forEach(field => {
if (options[field]) {
console.log(` ${field}: ${options[field]}`);
}
});
// 如果手机号仍然为空,显示错误信息
if (!phone.value) {
console.error('无法获取手机号,请检查参数传递');
uni.showToast({ title: '无法获取手机号,请返回重试', icon: 'none' });
}
// 开始倒计时
startCountdown();
// 自动聚焦到输入框
setTimeout(() => {
if (smsInput.value && typeof smsInput.value.focus === 'function') {
smsInput.value.focus();
}
}, 300);
});
// 开始倒计时
const startCountdown = () => {
clearInterval(timer.value);
countdown.value = 60;
timer.value = setInterval(() => {
if (countdown.value > 0) {
countdown.value--;
} else {
clearInterval(timer.value);
}
}, 1000);
};
// 重发验证码
const resendSms = async () => {
if (countdown.value > 0) return;
// 调用重新发送验证码接口(如果需要)
uni.showLoading({ title: '发送中...' });
try {
// 假设有一个重新发送验证码的接口
// await $api.createRequest('/app/resendSms', { phone: phone.value }, 'post');
uni.hideLoading();
uni.showToast({ title: '验证码已发送', icon: 'success' });
startCountdown();
} catch (error) {
uni.hideLoading();
uni.showToast({ title: error.msg || '发送失败', icon: 'none' });
}
};
// 验证码输入处理
const onCodeInput = (e) => {
let value = e.detail.value.replace(/\D/g, ''); // 只保留数字
// 限制为6位数字
if (value.length > 6) {
value = value.substring(0, 6);
}
smsCode.value = value;
// 如果输入了6位数字可以自动提交可选
if (value.length === 6) {
// 这里可以添加自动提交逻辑,或者让用户手动点击按钮
console.log('6位验证码已输入完整');
}
};
// 粘贴事件处理
const onPaste = (e) => {
console.log('onPaste event triggered', e);
// #ifdef MP-WEIXIN
// 微信小程序中,使用异步方式获取剪贴板
console.log('MP-WEIXIN: onPaste called, using async clipboard API');
setTimeout(() => {
pasteFromClipboard();
}, 50);
// 尝试阻止默认行为
if (e && e.preventDefault) {
e.preventDefault();
}
if (e && e.stopPropagation) {
e.stopPropagation();
}
// #endif
// #ifndef MP-WEIXIN
// 其他平台H5/App使用标准粘贴处理
console.log('Non-MP-WEIXIN: Standard paste handling');
e.preventDefault();
e.stopPropagation();
let pasteText = '';
if (e.clipboardData && e.clipboardData.getData) {
pasteText = e.clipboardData.getData('text');
console.log('Got pasteText from clipboardData:', pasteText);
}
if (pasteText) {
// 处理粘贴的文本
handlePaste(pasteText);
} else {
// 使用异步方式获取剪贴板
setTimeout(() => {
pasteFromClipboard();
}, 100);
}
return false;
// #endif
};
// 处理粘贴的文本
const handlePaste = (text) => {
// 提取数字
const digits = text.replace(/\D/g, '');
console.log('Extracted digits from paste:', digits);
if (digits.length === 0) {
uni.showToast({ title: '剪贴板中没有找到验证码', icon: 'none' });
return;
}
// 限制为6位数字
const code = digits.substring(0, 6);
smsCode.value = code;
uni.showToast({ title: '已粘贴验证码', icon: 'success' });
// 如果粘贴了6位数字可以自动提交可选
if (code.length === 6) {
console.log('6位验证码已通过粘贴输入完整');
}
};
// 从剪贴板粘贴验证码
const pasteFromClipboard = () => {
console.log('pasteFromClipboard called');
uni.getClipboardData({
success: (res) => {
console.log('getClipboardData success, data:', res.data);
const text = res.data || '';
handlePaste(text);
},
fail: (err) => {
console.error('读取剪贴板失败:', err);
uni.showToast({ title: '读取剪贴板失败', icon: 'none' });
}
});
};
// 提交验证
const submitVerification = async () => {
console.log('submitVerification called, canSubmit:', canSubmit.value, 'loading:', loading.value);
if (!canSubmit.value) {
console.log('不能提交,直接返回');
return;
}
console.log('开始提交验证设置loading为true');
loading.value = true;
const code = smsCode.value;
console.log('提交的验证码:', code, '手机号:', phone.value);
// 检查手机号是否为空
if (!phone.value) {
console.error('手机号为空,无法提交验证');
uni.showToast({ title: '手机号获取失败,请返回重试', icon: 'none' });
loading.value = false;
return;
}
try {
// 调用接口 /app/appLoginPhone
const res = await $api.createRequest('/app/appLoginPhone', {
smsCode: code,
phone: phone.value,
openid: openid.value,
unionid: unionid.value,
userType: userType.value,
orgType: orgType.value
}, 'post');
console.log('短信验证接口返回:', res);
if (res.token) {
// 登录成功存储token
const userStore = useUserStore();
await userStore.loginSetToken(res.token);
// 更新用户类型到缓存
if (res.isCompanyUser !== undefined) {
const userInfo = uni.getStorageSync('userInfo') || {};
userInfo.isCompanyUser = res.isCompanyUser ? 0 : 1;
uni.setStorageSync('userInfo', userInfo);
}
uni.showToast({ title: '登录成功', icon: 'success' });
// 返回上一页或跳转到首页
setTimeout(() => {
uni.navigateBack({ delta: 2 }); // 返回两页(跳过登录页面)
}, 1500);
} else {
uni.showToast({ title: res.msg || '验证失败', icon: 'none' });
}
} catch (error) {
console.error('短信验证失败:', error);
uni.showToast({ title: error.msg || '验证失败,请重试', icon: 'none' });
} finally {
console.log('submitVerification finally块执行设置loading为false');
loading.value = false;
console.log('loading设置完成当前loading值', loading.value);
}
};
// 返回上一页
const goBack = () => {
uni.navigateBack();
};
// 组件销毁时清除定时器
onUnmounted(() => {
clearInterval(timer.value);
});
// 需要导入useUserStore
import useUserStore from '@/stores/useUserStore';
</script>
<style lang="stylus" scoped>
.sms-verify-page
min-height: 100vh
background: #FFFFFF
.nav-bar
display: flex
align-items: center
justify-content: space-between
height: 88rpx
padding: 0 32rpx
border-bottom: 1rpx solid #f5f5f5
.nav-back
width: 60rpx
height: 60rpx
display: flex
align-items: center
justify-content: center
.nav-title
font-size: 32rpx
font-weight: 600
color: #333333
.nav-placeholder
width: 60rpx
.page-content
padding: 60rpx 40rpx 40rpx
.security-tip
display: flex
align-items: center
justify-content: center
margin-bottom: 40rpx
.tip-icon
margin-right: 16rpx
.tip-text
font-size: 28rpx
font-weight: 500
color: #333333
.phone-display
text-align: center
margin-bottom: 60rpx
.phone-label
font-size: 26rpx
color: #666666
margin-bottom: 12rpx
.phone-number
font-size: 36rpx
font-weight: 600
color: #333333
.sms-input-area
margin-bottom: 40rpx
.input-label
font-size: 28rpx
color: #666666
text-align: center
margin-bottom: 20rpx
.single-input-container
margin-bottom: 16rpx
.single-input
width: 100%
height: 100rpx
border: 2rpx solid #E5E5E5
border-radius: 16rpx
font-size: 40rpx
font-weight: 600
color: #333333
background: #F7F8FA
text-align: center
padding: 0 32rpx
box-sizing: border-box
outline: none
caret-color: #256BFA
transition: all 0.3s ease
&:focus
border-color: #256BFA
background: #FFFFFF
box-shadow: 0 4rpx 12rpx rgba(37, 107, 250, 0.15)
&::placeholder
color: #999999
font-size: 32rpx
font-weight: normal
.input-hint
font-size: 24rpx
color: #999999
text-align: center
.countdown-section
text-align: center
margin-bottom: 60rpx
.countdown-text
font-size: 26rpx
color: #999999
.resend-text
font-size: 26rpx
color: #256BFA
text-decoration: underline
cursor: pointer
.paste-section
margin-top: 20rpx
.paste-text
display: inline-flex
align-items: center
justify-content: center
font-size: 26rpx
color: #256BFA
cursor: pointer
text
margin-left: 8rpx
.submit-btn
width: 100%
height: 88rpx
background: linear-gradient(135deg, #256BFA 0%, #1E5BFF 100%)
border-radius: 44rpx
color: #FFFFFF
font-size: 32rpx
font-weight: 500
border: none
display: flex
align-items: center
justify-content: center
&:disabled
opacity: 0.5
cursor: not-allowed
.loading-spinner
width: 40rpx
height: 40rpx
border: 4rpx solid rgba(255, 255, 255, 0.3)
border-radius: 50%
border-top: 4rpx solid #FFFFFF
animation: spin 1s linear infinite
// 按钮重置样式
button::after
border: none
// 动画定义
@keyframes spin
0%
transform: rotate(0deg)
100%
transform: rotate(360deg)
</style>