You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
351 lines
7.2 KiB
351 lines
7.2 KiB
|
4 hours ago
|
/**
|
||
|
|
* 阿里云验证码2.0工具模块
|
||
|
|
* 用于小程序发送验证码前的行为验证
|
||
|
|
*/
|
||
|
|
|
||
|
|
// 验证码插件实例
|
||
|
|
let AliyunCaptchaPluginInterface: any = null;
|
||
|
|
|
||
|
|
// 计时器
|
||
|
|
let timer: number | null = null;
|
||
|
|
|
||
|
|
// 页面实例引用
|
||
|
|
let pageInstance: any = null;
|
||
|
|
|
||
|
|
// 是否在发送中(防止重复请求)
|
||
|
|
let isSending: boolean = false;
|
||
|
|
|
||
|
|
// 倒计时剩余秒数
|
||
|
|
let remainingSeconds: number = 0;
|
||
|
|
|
||
|
|
// 发送验证码的接口配置
|
||
|
|
interface SendCodeConfig {
|
||
|
|
url: string;
|
||
|
|
mobileField?: string;
|
||
|
|
extraData?: Record<string, any>;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 验证码配置选项
|
||
|
|
interface CaptchaOptions {
|
||
|
|
sceneId: string;
|
||
|
|
sendCodeConfig: SendCodeConfig;
|
||
|
|
onSendSuccess?: () => void;
|
||
|
|
onSendFail?: (err: any) => void;
|
||
|
|
countdown?: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 当前配置
|
||
|
|
let currentOptions: CaptchaOptions | null = null;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 初始化验证码插件
|
||
|
|
*/
|
||
|
|
function initPlugin() {
|
||
|
|
if (!AliyunCaptchaPluginInterface) {
|
||
|
|
AliyunCaptchaPluginInterface = requirePlugin('AliyunCaptcha');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 验证通过回调函数
|
||
|
|
* @param captchaVerifyParam 验证码验证参数
|
||
|
|
*/
|
||
|
|
async function successCallback(captchaVerifyParam: string) {
|
||
|
|
if (!pageInstance || !currentOptions) return;
|
||
|
|
if (isSending) {
|
||
|
|
wx.showToast({
|
||
|
|
title: '发送中,请稍候',
|
||
|
|
icon: 'none',
|
||
|
|
});
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const { sendCodeConfig, onSendSuccess, onSendFail, countdown = 60 } = currentOptions;
|
||
|
|
const mobileField = sendCodeConfig.mobileField || 'mobile';
|
||
|
|
const mobile = pageInstance.data[mobileField];
|
||
|
|
|
||
|
|
if (!mobile) {
|
||
|
|
wx.showToast({
|
||
|
|
title: '请输入手机号',
|
||
|
|
icon: 'none',
|
||
|
|
});
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
isSending = true;
|
||
|
|
updateCountdownText('发送中...');
|
||
|
|
|
||
|
|
try {
|
||
|
|
const res = await wx.ajax({
|
||
|
|
method: 'POST',
|
||
|
|
url: sendCodeConfig.url,
|
||
|
|
data: {
|
||
|
|
[mobileField]: mobile,
|
||
|
|
captchaVerifyParam,
|
||
|
|
...sendCodeConfig.extraData,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
wx.showToast({
|
||
|
|
icon: 'none',
|
||
|
|
title: '验证码已发送~',
|
||
|
|
});
|
||
|
|
|
||
|
|
// 开始倒计时
|
||
|
|
startCountdown(countdown);
|
||
|
|
|
||
|
|
// 执行成功回调
|
||
|
|
onSendSuccess?.();
|
||
|
|
|
||
|
|
return res;
|
||
|
|
} catch (err: any) {
|
||
|
|
// 发送失败,重置状态(包括阿里云插件,以便下次重新验证)
|
||
|
|
resetCaptchaState(true);
|
||
|
|
|
||
|
|
const errorMsg = err.data?.msg || err.message || '发送失败,请重试';
|
||
|
|
wx.showToast({
|
||
|
|
title: errorMsg,
|
||
|
|
icon: 'none',
|
||
|
|
duration: 2000,
|
||
|
|
});
|
||
|
|
onSendFail?.(err);
|
||
|
|
throw err;
|
||
|
|
} finally {
|
||
|
|
isSending = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 验证失败回调函数
|
||
|
|
* @param error 错误信息
|
||
|
|
*/
|
||
|
|
function failCallback(error: any) {
|
||
|
|
console.error('阿里云验证码验证失败:', error);
|
||
|
|
wx.showToast({
|
||
|
|
title: '验证失败,请重试',
|
||
|
|
icon: 'none',
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 开始倒计时
|
||
|
|
* @param seconds 倒计时秒数
|
||
|
|
*/
|
||
|
|
function startCountdown(seconds: number) {
|
||
|
|
// 清除之前的计时器
|
||
|
|
if (timer) {
|
||
|
|
clearInterval(timer);
|
||
|
|
timer = null;
|
||
|
|
}
|
||
|
|
|
||
|
|
remainingSeconds = seconds;
|
||
|
|
updateCountdownText(`${remainingSeconds}s后重新发送`);
|
||
|
|
|
||
|
|
timer = setInterval(() => {
|
||
|
|
remainingSeconds--;
|
||
|
|
if (remainingSeconds <= 0) {
|
||
|
|
// 倒计时结束,重置状态(包括阿里云插件,以便下次重新验证)
|
||
|
|
resetCaptchaState(true);
|
||
|
|
} else {
|
||
|
|
updateCountdownText(`${remainingSeconds}s后重新发送`);
|
||
|
|
}
|
||
|
|
}, 1000) as unknown as number;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 重置验证码状态
|
||
|
|
* 在倒计时结束、发送失败或需要重置时使用
|
||
|
|
*/
|
||
|
|
function resetCaptchaState(resetPluginToo: boolean = false) {
|
||
|
|
if (timer) {
|
||
|
|
clearInterval(timer);
|
||
|
|
timer = null;
|
||
|
|
}
|
||
|
|
remainingSeconds = 0;
|
||
|
|
isSending = false;
|
||
|
|
updateCountdownText('发送验证码');
|
||
|
|
|
||
|
|
// 如果需要,同时重置阿里云插件状态(接口失败时使用)
|
||
|
|
if (resetPluginToo) {
|
||
|
|
refreshCaptchaComponent();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 重置阿里云验证码插件
|
||
|
|
* 用于验证失败后需要重新验证的情况
|
||
|
|
*/
|
||
|
|
function resetAliyunPlugin(): void {
|
||
|
|
// 销毁插件实例,下次会自动重新初始化
|
||
|
|
AliyunCaptchaPluginInterface = null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 强制刷新验证码组件
|
||
|
|
* 通过卸载并重新加载组件来重置阿里云插件状态
|
||
|
|
*/
|
||
|
|
export function refreshCaptchaComponent(callback?: () => void): void {
|
||
|
|
if (!pageInstance) return;
|
||
|
|
|
||
|
|
// 卸载组件
|
||
|
|
pageInstance.setData({
|
||
|
|
loadCaptcha: false,
|
||
|
|
});
|
||
|
|
|
||
|
|
// 重置插件实例
|
||
|
|
resetAliyunPlugin();
|
||
|
|
|
||
|
|
// 延迟重新加载组件,确保卸载完成
|
||
|
|
setTimeout(() => {
|
||
|
|
if (!pageInstance || !currentOptions) return;
|
||
|
|
|
||
|
|
// 重新初始化配置
|
||
|
|
const pluginProps = {
|
||
|
|
SceneId: currentOptions.sceneId,
|
||
|
|
mode: 'popup',
|
||
|
|
success: successCallback.bind(pageInstance),
|
||
|
|
fail: failCallback.bind(pageInstance),
|
||
|
|
slideStyle: {
|
||
|
|
width: 540,
|
||
|
|
height: 60,
|
||
|
|
},
|
||
|
|
language: 'cn',
|
||
|
|
region: 'cn',
|
||
|
|
};
|
||
|
|
|
||
|
|
// 重新加载组件
|
||
|
|
pageInstance.setData({
|
||
|
|
loadCaptcha: true,
|
||
|
|
pluginProps,
|
||
|
|
});
|
||
|
|
if (callback) callback();
|
||
|
|
}, 100);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 更新倒计时文本
|
||
|
|
* @param text 显示的文本
|
||
|
|
*/
|
||
|
|
function updateCountdownText(text: string) {
|
||
|
|
if (pageInstance) {
|
||
|
|
pageInstance.setData({ codeText: text });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 初始化验证码功能
|
||
|
|
* @param page 页面实例
|
||
|
|
* @param options 配置选项
|
||
|
|
* @returns 插件配置对象
|
||
|
|
*/
|
||
|
|
export function initCaptcha(page: any, options: CaptchaOptions) {
|
||
|
|
initPlugin();
|
||
|
|
pageInstance = page;
|
||
|
|
currentOptions = options;
|
||
|
|
|
||
|
|
const pluginProps = {
|
||
|
|
SceneId: options.sceneId,
|
||
|
|
mode: 'popup',
|
||
|
|
success: successCallback.bind(page),
|
||
|
|
fail: failCallback.bind(page),
|
||
|
|
slideStyle: {
|
||
|
|
width: 540,
|
||
|
|
height: 60,
|
||
|
|
},
|
||
|
|
language: 'cn',
|
||
|
|
region: 'cn',
|
||
|
|
};
|
||
|
|
|
||
|
|
return {
|
||
|
|
loadCaptcha: true,
|
||
|
|
pluginProps,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 显示验证码弹窗
|
||
|
|
* @returns 是否成功触发
|
||
|
|
*/
|
||
|
|
export function showCaptcha(): boolean {
|
||
|
|
if (!AliyunCaptchaPluginInterface) {
|
||
|
|
initPlugin();
|
||
|
|
}
|
||
|
|
|
||
|
|
if (isCountingDown()) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
wx.getNetworkType({
|
||
|
|
success: (res) => {
|
||
|
|
if (res.networkType === 'none') {
|
||
|
|
wx.showToast({
|
||
|
|
title: '网络连接不可用,请检查网络设置',
|
||
|
|
icon: 'none',
|
||
|
|
});
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
AliyunCaptchaPluginInterface.show();
|
||
|
|
},
|
||
|
|
fail: () => {
|
||
|
|
AliyunCaptchaPluginInterface.show();
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 显示验证码弹窗(带重置)
|
||
|
|
* 在接口报错后再次点击时使用,会重置插件状态
|
||
|
|
* @returns 是否成功触发
|
||
|
|
*/
|
||
|
|
export function showCaptchaWithReset(): boolean {
|
||
|
|
// 重置插件状态,清除已验证的状态
|
||
|
|
resetAliyunPlugin();
|
||
|
|
|
||
|
|
if (isCountingDown()) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 重新初始化后显示
|
||
|
|
initPlugin();
|
||
|
|
AliyunCaptchaPluginInterface.show();
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 检查是否正在倒计时
|
||
|
|
* @returns 是否正在倒计时
|
||
|
|
*/
|
||
|
|
export function isCountingDown(): boolean {
|
||
|
|
return timer !== null;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 清除倒计时
|
||
|
|
* 完全重置所有状态,可在页面卸载或需要完全重置时调用
|
||
|
|
*/
|
||
|
|
export function clearCountdown() {
|
||
|
|
resetCaptchaState();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 获取验证码插件接口
|
||
|
|
* @returns 插件接口
|
||
|
|
*/
|
||
|
|
export function getCaptchaPlugin() {
|
||
|
|
if (!AliyunCaptchaPluginInterface) {
|
||
|
|
initPlugin();
|
||
|
|
}
|
||
|
|
return AliyunCaptchaPluginInterface;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 页面卸载时清理资源
|
||
|
|
*/
|
||
|
|
export function destroyCaptcha() {
|
||
|
|
clearCountdown();
|
||
|
|
pageInstance = null;
|
||
|
|
currentOptions = null;
|
||
|
|
}
|