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.
350 lines
7.2 KiB
350 lines
7.2 KiB
/** |
|
* 阿里云验证码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; |
|
}
|
|
|