10 changed files with 456 additions and 32 deletions
|
After Width: | Height: | Size: 4.7 KiB |
@ -0,0 +1,350 @@
@@ -0,0 +1,350 @@
|
||||
/** |
||||
* 阿里云验证码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; |
||||
} |
||||
@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
|
||||
/** |
||||
* 网络状态管理工具 |
||||
* 监听网络变化,断网时提示,恢复时引导刷新 |
||||
*/ |
||||
|
||||
interface NetworkStatus { |
||||
isConnected: boolean; |
||||
networkType: string; |
||||
} |
||||
|
||||
let networkListener: WechatMiniprogram.OnNetworkStatusChangeListener | null = null; |
||||
let isOfflineShown = false; |
||||
let isRecoveryShown = false; |
||||
|
||||
function showOfflineTip() { |
||||
if (isOfflineShown) return; |
||||
isOfflineShown = true; |
||||
wx.showModal({ |
||||
title: '网络连接失败', |
||||
content: '当前网络不可用,请检查网络设置', |
||||
showCancel: false, |
||||
confirmText: '知道了', |
||||
}); |
||||
} |
||||
|
||||
function showRecoveryTip() { |
||||
if (isRecoveryShown) return; |
||||
isRecoveryShown = true; |
||||
wx.showModal({ |
||||
title: '网络已恢复', |
||||
content: '网络连接已恢复,是否刷新小程序?', |
||||
confirmText: '立即刷新', |
||||
cancelText: '暂不刷新', |
||||
success: (res) => { |
||||
if (res.confirm) { |
||||
wx.reLaunch({ url: '/pages/home/index' }); |
||||
} |
||||
setTimeout(() => { |
||||
isRecoveryShown = false; |
||||
}, 3000); |
||||
}, |
||||
}); |
||||
} |
||||
|
||||
export function initNetworkMonitor(): void { |
||||
wx.getNetworkType({ |
||||
success: (res) => { |
||||
if (res.networkType === 'none') showOfflineTip(); |
||||
}, |
||||
}); |
||||
|
||||
networkListener = (res: NetworkStatus) => { |
||||
if (res.isConnected) { |
||||
isOfflineShown = false; |
||||
showRecoveryTip(); |
||||
} else { |
||||
isRecoveryShown = false; |
||||
showOfflineTip(); |
||||
} |
||||
}; |
||||
|
||||
wx.onNetworkStatusChange(networkListener); |
||||
} |
||||
Loading…
Reference in new issue