import { showToast, showLoadingToast, closeToast } from "vant"; import useApiFetch from "@/composables/useApiFetch"; // 阿里云验证码场景 ID const ALIYUN_CAPTCHA_SCENE_ID = "wynt39to"; // 是否启用加密模式(通过环境变量控制,非加密模式时前端不调用后端获取 EncryptedSceneId) const ENABLE_ENCRYPTED = import.meta.env.VITE_ALIYUN_CAPTCHA_ENCRYPTED === "false"; let captchaInitialised = false; /** 首次初始化后,SDK 会异步调用 getInstance,用此 Promise 在实例就绪后再 show */ let captchaReadyPromise = null; let captchaReadyResolve = null; async function ensureCaptchaInit() { if (captchaInitialised || typeof window === "undefined") return; if (typeof window.initAliyunCaptcha !== "function") return; captchaInitialised = true; window.captcha = null; window.__lastBizResponse = null; window.__onCaptchaBizSuccess = null; captchaReadyPromise = new Promise((resolve) => { captchaReadyResolve = resolve; }); // 非加密模式:仅传 SceneId,不调用后端接口 if (!ENABLE_ENCRYPTED) { window.initAliyunCaptcha({ SceneId: ALIYUN_CAPTCHA_SCENE_ID, mode: "popup", element: "#captcha-element", getInstance(instance) { window.captcha = instance; if (typeof captchaReadyResolve === "function") { captchaReadyResolve(); captchaReadyResolve = null; } }, captchaVerifyCallback(param) { return typeof window.__captchaVerifyCallback === "function" ? window.__captchaVerifyCallback(param) : Promise.resolve({ captchaResult: false, bizResult: false, }); }, onBizResultCallback(bizResult) { if (typeof window.__onBizResultCallback === "function") { window.__onBizResultCallback(bizResult); } window.__lastBizResponse = null; window.__onCaptchaBizSuccess = null; }, slideStyle: { width: 360, height: 40 }, language: "cn", }); return; } // 加密模式:先从后端获取 EncryptedSceneId,再初始化 const { data, error } = await useApiFetch("/captcha/encryptedSceneId") .post() .json(); const resp = data?.value; const encryptedSceneId = resp?.data?.encryptedSceneId; if (error?.value || !encryptedSceneId) { showToast({ message: "获取验证码参数失败,请稍后重试" }); captchaInitialised = false; captchaReadyPromise = null; captchaReadyResolve = null; return; } window.initAliyunCaptcha({ SceneId: ALIYUN_CAPTCHA_SCENE_ID, EncryptedSceneId: encryptedSceneId, mode: "popup", element: "#captcha-element", getInstance(instance) { window.captcha = instance; if (typeof captchaReadyResolve === "function") { captchaReadyResolve(); captchaReadyResolve = null; } }, captchaVerifyCallback(param) { return typeof window.__captchaVerifyCallback === "function" ? window.__captchaVerifyCallback(param) : Promise.resolve({ captchaResult: false, bizResult: false }); }, onBizResultCallback(bizResult) { if (typeof window.__onBizResultCallback === "function") { window.__onBizResultCallback(bizResult); } window.__lastBizResponse = null; window.__onCaptchaBizSuccess = null; }, slideStyle: { width: 360, height: 40 }, language: "cn", }); } /** * 阿里云滑块验证码通用封装。 * 依赖 index.html 中已加载的 AliyunCaptcha.js;初始化在首次调起时执行。 * * @param { (captchaVerifyParam: string) => Promise<{ data: Ref, error: Ref }> } bizVerify - 业务请求函数,接收滑块参数,返回 useApiFetch 的 { data, error } * @param { (res: any) => void } [onSuccess] - 业务成功回调(code===200 时调用,传入接口返回的 data.value) */ export function useAliyunCaptcha() { /** * 先弹出滑块,通过后执行 bizVerify(captchaVerifyParam),再根据结果调用 onSuccess。 */ async function runWithCaptcha(bizVerify, onSuccess) { if (typeof window === "undefined") { showToast({ message: "验证码仅支持浏览器环境" }); return; } const loading = showLoadingToast({ message: "安全验证加载中...", forbidClick: true, duration: 0, loadingType: "spinner", }); try { window.__captchaVerifyCallback = async (captchaVerifyParam) => { window.__lastBizResponse = null; const { data, error } = await bizVerify(captchaVerifyParam); const result = data?.value ?? data; if (error?.value || !result) { return { captchaResult: false, bizResult: false }; } window.__lastBizResponse = result; const captchaOk = result.captchaVerifyResult !== false; const bizOk = result.code === 200; return { captchaResult: captchaOk, bizResult: bizOk }; }; window.__onBizResultCallback = (bizResult) => { if ( bizResult === true && window.__lastBizResponse && typeof window.__onCaptchaBizSuccess === "function" ) { window.__onCaptchaBizSuccess(window.__lastBizResponse); } }; await ensureCaptchaInit(); // 首次初始化时 SDK 会异步调用 getInstance,需等待实例就绪后再 show if (captchaReadyPromise) { await captchaReadyPromise; captchaReadyPromise = null; } if (!window.captcha) { showToast({ message: "验证码未加载,请刷新页面重试" }); return; } window.__onCaptchaBizSuccess = onSuccess; window.captcha.show(); } finally { closeToast(); } } return { runWithCaptcha }; } export default useAliyunCaptcha;