462 lines
15 KiB
JavaScript
462 lines
15 KiB
JavaScript
const AuthPageUtil = {
|
|
smsTimers: {},
|
|
slideUuid: '',
|
|
slideX: 0,
|
|
slideToken: '',
|
|
slideCaptchaViewportCleanup: 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') || '',
|
|
);
|
|
} catch (error) {
|
|
return '';
|
|
}
|
|
},
|
|
|
|
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(() => 'slide');
|
|
},
|
|
|
|
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;
|
|
}
|