const AuthPageUtil = { smsTimers: {}, slideUuid: '', slideX: 0, slideToken: '', slideCaptchaViewportCleanup: null, referralInviteSessionKey: 'pendingReferralInviteCode', getSessionStorage() { if (typeof sessionStorage !== 'undefined') return sessionStorage; if (typeof window !== 'undefined' && window.sessionStorage) return window.sessionStorage; return null; }, normalizeReferralInviteCode(inviteCode) { return String(inviteCode || '').trim(); }, getReferralInviteCodeFromUrl(search) { const query = search != null ? search : (typeof window !== 'undefined' && window.location && window.location.search) || ''; const Params = typeof URLSearchParams !== 'undefined' ? URLSearchParams : typeof window !== 'undefined' && window.URLSearchParams; if (!Params) return ''; try { const params = new Params(query || ''); return this.normalizeReferralInviteCode( params.get('inviteCode') || params.get('referralCode') || '', ); } catch (error) { return ''; } }, setPendingReferralInviteCode(inviteCode) { const code = this.normalizeReferralInviteCode(inviteCode); const storage = this.getSessionStorage(); if (code && storage) { storage.setItem(this.referralInviteSessionKey, code); } return code; }, getPendingReferralInviteCode() { const storage = this.getSessionStorage(); if (!storage) return ''; return this.normalizeReferralInviteCode(storage.getItem(this.referralInviteSessionKey)); }, clearPendingReferralInviteCode() { const storage = this.getSessionStorage(); if (storage) storage.removeItem(this.referralInviteSessionKey); }, captureReferralInviteFromUrl(search) { const code = this.getReferralInviteCodeFromUrl(search); if (code) { this.setPendingReferralInviteCode(code); } return code; }, bindPendingReferralInviteCode() { const inviteCode = this.getPendingReferralInviteCode(); if (!inviteCode) { return Promise.resolve({ skipped: true, reason: 'empty' }); } if ( typeof ApiClient === 'undefined' || !ApiClient.API || !ApiClient.API.referralBind || typeof ApiClient.post !== 'function' ) { return Promise.resolve({ skipped: true, reason: 'missing-api' }); } if (typeof ApiClient.token === 'function' && !ApiClient.token()) { return Promise.resolve({ skipped: true, reason: 'unauthenticated' }); } return ApiClient.post(ApiClient.API.referralBind, { inviteCode }) .then((res) => { const isSuccess = typeof ApiClient.isSuccess === 'function' ? ApiClient.isSuccess(res) : !!(res && (res.success === true || Number(res.code) === 0 || Number(res.code) === 200)); if (isSuccess) { this.clearPendingReferralInviteCode(); return { skipped: false, success: true, res }; } return { skipped: false, success: false, res }; }) .catch((error) => ({ skipped: false, success: false, error })); }, isPhone(phone) { return /^1[3-9]\d{9}$/.test(String(phone || '').trim()); }, isPassword(password) { const value = String(password || ''); return value.length >= 6 && value.length <= 20; }, loadVerifyType() { return ApiClient.get(ApiClient.API.authVerifyConfig, {}, { publicRequest: true }) .then((res) => { const data = (res && res.data) || res || {}; const yes = (value) => value === true || value === 1 || value === '1' || String(value).toLowerCase() === 'true'; if ( yes(data.lottery_slide_captcha) || yes(data.lotterySlideCaptcha) || yes(data.slideCaptchaEnabled) || yes(data.slideEnabled) || data.verifyType === 'slide' ) { return 'slide'; } return 'captcha'; }) .catch(() => 'captcha'); }, buildSmsParams(options = {}) { const params = { phoneNumber: options.phoneNumber, }; if (options.verifyType === 'slide') { params.verifyType = 'slide'; params.slideToken = options.slideToken || this.slideToken; params.slideUuid = options.slideUuid || this.slideUuid; params.slideX = options.slideX || this.slideX; } if (options.verifyType === 'captcha') { params.verifyType = 'captcha'; params.code = options.code || ''; params.uuid = options.uuid || ''; } return params; }, sendSmsCode(options) { return ApiClient.get(ApiClient.API.authSmsCode, this.buildSmsParams(options), { publicRequest: true, }); }, sendForgotPasswordSmsCode(options) { return ApiClient.get(ApiClient.API.authForgotPasswordSmsCode, this.buildSmsParams(options), { publicRequest: true, }); }, getImageCaptcha() { return ApiClient.get(ApiClient.API.authCode, {}, { publicRequest: true }); }, installSlideCaptchaTenantHeader() { if (typeof window === 'undefined' || window.__cxzSlideCaptchaTenantPatched) return; window.__cxzSlideCaptchaTenantPatched = true; const isSlideCaptchaUrl = (url) => String(url || '').indexOf('/api/web/auth/slide-captcha') >= 0; const tenantId = () => typeof ApiClient !== 'undefined' && ApiClient.tenantId ? ApiClient.tenantId : '936208'; if (typeof window.fetch === 'function') { const nativeFetch = window.fetch.bind(window); window.fetch = function (input, init = {}) { const requestUrl = typeof input === 'string' ? input : input && input.url; if (!isSlideCaptchaUrl(requestUrl)) return nativeFetch(input, init); const isRequestInput = typeof Request !== 'undefined' && input instanceof Request; const headers = new Headers(init.headers || (isRequestInput ? input.headers : undefined)); headers.set('tenantId', tenantId()); if (isRequestInput) { return nativeFetch(new Request(input, { ...init, headers })); } return nativeFetch(input, { ...init, headers }); }; } if (window.XMLHttpRequest && window.XMLHttpRequest.prototype) { const xhrProto = window.XMLHttpRequest.prototype; const nativeOpen = xhrProto.open; const nativeSetRequestHeader = xhrProto.setRequestHeader; const nativeSend = xhrProto.send; xhrProto.open = function (method, url, ...args) { this.__cxzSlideCaptchaRequest = isSlideCaptchaUrl(url); this.__cxzSlideTenantHeaderSet = false; return nativeOpen.call(this, method, url, ...args); }; xhrProto.setRequestHeader = function (name, value) { if (this.__cxzSlideCaptchaRequest && String(name || '').toLowerCase() === 'tenantid') { this.__cxzSlideTenantHeaderSet = true; } return nativeSetRequestHeader.call(this, name, value); }; xhrProto.send = function (...args) { if (this.__cxzSlideCaptchaRequest && !this.__cxzSlideTenantHeaderSet) { nativeSetRequestHeader.call(this, 'tenantId', tenantId()); this.__cxzSlideTenantHeaderSet = true; } return nativeSend.apply(this, args); }; } }, blurActiveElement() { if (typeof document === 'undefined') return; const activeElement = document.activeElement; if (activeElement && activeElement !== document.body && typeof activeElement.blur === 'function') { activeElement.blur(); } }, ensureSlideCaptchaMobileCenterStyle() { if (typeof document === 'undefined' || document.getElementById('auth-slide-captcha-mobile-center-style')) { return; } const style = document.createElement('style'); style.id = 'auth-slide-captcha-mobile-center-style'; style.textContent = ` @media (max-width: 640px) { #rvplus-slide-captcha-root.rvplus-slide-mask { box-sizing: border-box !important; align-items: center !important; justify-content: center !important; width: 100vw; max-width: 100vw; min-height: 100vh; min-height: 100dvh; padding: 12px !important; } #rvplus-slide-captcha-root .rvplus-slide-modal { box-sizing: border-box; max-width: calc(100vw - 24px); max-height: calc(100vh - 24px); max-height: calc(100dvh - 24px); overflow-y: auto; -webkit-overflow-scrolling: touch; } } `; document.head.appendChild(style); }, getSlideCaptchaViewport() { const fallbackWidth = (typeof window !== 'undefined' && window.innerWidth) || (typeof document !== 'undefined' && document.documentElement && document.documentElement.clientWidth) || 0; const fallbackHeight = (typeof window !== 'undefined' && window.innerHeight) || (typeof document !== 'undefined' && document.documentElement && document.documentElement.clientHeight) || 0; const viewport = typeof window !== 'undefined' ? window.visualViewport : null; const screenWidth = (typeof window !== 'undefined' && window.screen && window.screen.width) || 0; const screenHeight = (typeof window !== 'undefined' && window.screen && window.screen.height) || 0; const width = Math.round((viewport && viewport.width) || fallbackWidth); const height = Math.round((viewport && viewport.height) || fallbackHeight); let scale = Number(viewport && viewport.scale) || 1; if (screenWidth && screenWidth <= 640 && width > screenWidth && scale === 1) { scale = screenWidth / width; } return { width, height, offsetLeft: Math.round((viewport && viewport.offsetLeft) || 0), offsetTop: Math.round((viewport && viewport.offsetTop) || 0), scale, displayWidth: Math.round(width * scale), displayHeight: Math.round(height * scale), screenWidth, screenHeight, }; }, isCompactSlideCaptchaViewport(viewport) { return viewport.displayWidth <= 640 || (viewport.screenWidth > 0 && viewport.screenWidth <= 640); }, centerSlideCaptchaDialog() { if (typeof document === 'undefined') return false; const root = document.getElementById('rvplus-slide-captcha-root'); if (!root) return false; const viewport = this.getSlideCaptchaViewport(); if (!viewport.width || !viewport.height) return true; const compact = this.isCompactSlideCaptchaViewport(viewport); const visualPadding = compact ? 12 : 20; const padding = visualPadding / (viewport.scale || 1); const modalWidth = Math.max(0, Math.round(viewport.width - padding * 2)); const modalHeight = Math.max(0, Math.round(viewport.height - padding * 2)); root.style.position = 'fixed'; root.style.inset = 'auto'; root.style.left = viewport.offsetLeft + 'px'; root.style.top = viewport.offsetTop + 'px'; root.style.width = viewport.width + 'px'; root.style.height = viewport.height + 'px'; root.style.display = 'flex'; root.style.alignItems = 'center'; root.style.justifyContent = 'center'; root.style.boxSizing = 'border-box'; root.style.padding = padding + 'px'; const modal = root.querySelector && root.querySelector('.rvplus-slide-modal'); if (modal) { modal.style.boxSizing = 'border-box'; modal.style.maxWidth = modalWidth + 'px'; modal.style.maxHeight = modalHeight + 'px'; modal.style.overflowY = 'auto'; modal.style.webkitOverflowScrolling = 'touch'; modal.style.width = compact ? modalWidth + 'px' : ''; } return true; }, clearSlideCaptchaViewportCentering() { if (typeof this.slideCaptchaViewportCleanup !== 'function') return; const cleanup = this.slideCaptchaViewportCleanup; this.slideCaptchaViewportCleanup = null; cleanup(); }, scheduleSlideCaptchaCentering(callback) { if (typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function') { window.requestAnimationFrame(callback); return; } callback(); }, bindSlideCaptchaViewportCentering() { if (typeof window === 'undefined') return; this.clearSlideCaptchaViewportCentering(); const cleanups = []; const recenter = () => { if (!this.centerSlideCaptchaDialog()) { this.clearSlideCaptchaViewportCentering(); } }; const schedule = () => this.scheduleSlideCaptchaCentering(recenter); const addListener = (target, type) => { if (target && typeof target.addEventListener === 'function') { target.addEventListener(type, schedule); cleanups.push(() => target.removeEventListener(type, schedule)); } }; addListener(window.visualViewport, 'resize'); addListener(window.visualViewport, 'scroll'); addListener(window, 'resize'); addListener(window, 'orientationchange'); this.slideCaptchaViewportCleanup = () => { cleanups.forEach((cleanup) => cleanup()); }; schedule(); if (typeof window.setTimeout === 'function') { window.setTimeout(schedule, 80); window.setTimeout(schedule, 260); } }, deferClearSlideCaptchaViewportCentering() { const clear = () => this.clearSlideCaptchaViewportCentering(); if (typeof window !== 'undefined' && typeof window.setTimeout === 'function') { window.setTimeout(clear, 320); return; } clear(); }, wrapSlideCaptchaCallbacks(options = {}) { const wrapped = { ...options }; const wrap = (callback) => (...args) => { try { if (typeof callback === 'function') { return callback(...args); } } finally { this.deferClearSlideCaptchaViewportCentering(); } return undefined; }; wrapped.onSuccess = wrap(options.onSuccess); wrapped.onCancel = wrap(options.onCancel); return wrapped; }, patchSlideCaptchaMobileCentering(SlideCaptcha) { if (!SlideCaptcha || SlideCaptcha.__authPageMobileCentered || typeof SlideCaptcha.open !== 'function') { return SlideCaptcha; } const nativeOpen = SlideCaptcha.open.bind(SlideCaptcha); const nativeClose = typeof SlideCaptcha.close === 'function' ? SlideCaptcha.close.bind(SlideCaptcha) : null; SlideCaptcha.open = (options = {}) => { this.blurActiveElement(); this.ensureSlideCaptchaMobileCenterStyle(); const result = nativeOpen(this.wrapSlideCaptchaCallbacks(options)); this.bindSlideCaptchaViewportCentering(); return result; }; if (nativeClose) { SlideCaptcha.close = (...args) => { this.clearSlideCaptchaViewportCentering(); return nativeClose(...args); }; } SlideCaptcha.__authPageMobileCentered = true; return SlideCaptcha; }, startSmsCountdown(buttonSelector, seconds = 60) { const $button = layui.$(buttonSelector); let left = seconds; clearInterval(this.smsTimers[buttonSelector]); $button.prop('disabled', true).text(`${left}s后重发`); this.smsTimers[buttonSelector] = setInterval(() => { left -= 1; if (left <= 0) { clearInterval(this.smsTimers[buttonSelector]); $button.prop('disabled', false).text('获取验证码'); return; } $button.text(`${left}s后重发`); }, 1000); }, loadSlideCaptchaScript() { this.installSlideCaptchaTenantHeader(); if (window.SlideCaptcha) return Promise.resolve(this.patchSlideCaptchaMobileCentering(window.SlideCaptcha)); if (window.__slideCaptchaLoading) return window.__slideCaptchaLoading; window.__slideCaptchaLoading = new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = `${ApiClient.baseUrl}/static/slide-captcha/slide-captcha.js`; script.dataset.apiBase = ApiClient.baseUrl || ''; script.dataset.tenantId = ApiClient.tenantId || '936208'; script.onload = () => { if (window.SlideCaptcha) resolve(this.patchSlideCaptchaMobileCentering(window.SlideCaptcha)); else reject(new Error('滑动验证组件加载失败')); }; script.onerror = () => reject(new Error('滑动验证组件加载失败')); document.body.appendChild(script); }); return window.__slideCaptchaLoading; }, openSlideCaptcha(options = {}) { this.installSlideCaptchaTenantHeader(); this.loadSlideCaptchaScript() .then((SlideCaptcha) => { SlideCaptcha.open({ title: options.title || '安全验证', subtitle: options.subtitle || '拖动滑块完成验证', apiBase: ApiClient.baseUrl || '', tenantId: ApiClient.tenantId || '936208', headers: ApiClient.publicHeaders(), onSuccess: (result) => { const data = result || {}; const nested = data.data || {}; this.slideToken = data.slideToken || data.token || data.verifyToken || nested.slideToken || nested.token || nested.verifyToken || ''; this.slideUuid = data.slideUuid || data.uuid || nested.slideUuid || nested.uuid || ''; this.slideX = Number(data.slideX || data.x || nested.slideX || nested.x || 0) || 0; if (!this.slideToken && !this.slideUuid) { if (typeof options.onError === 'function') { options.onError(new Error('滑动验证未返回凭证,请重试')); } return; } if (typeof options.onSuccess === 'function') { options.onSuccess({ slideToken: this.slideToken, slideUuid: this.slideUuid, slideX: this.slideX, }); } }, onCancel: options.onCancel, onError: options.onError, }); }) .catch((error) => { if (typeof options.onError === 'function') options.onError(error); }); }, }; if (typeof window !== 'undefined') { window.AuthPageUtil = AuthPageUtil; }