Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
237 lines
7.9 KiB
JavaScript
237 lines
7.9 KiB
JavaScript
(function () {
|
|
var mask = document.getElementById("loginDialog");
|
|
if (!mask) return;
|
|
|
|
var form = mask.querySelector("[data-login-dialog-form]");
|
|
var accountInput = mask.querySelector("[data-login-account]");
|
|
var passwordInput = mask.querySelector("[data-login-password]");
|
|
var submitBtn = mask.querySelector("[data-login-submit]");
|
|
var defaultSubmitText = (submitBtn && submitBtn.textContent) || "登录";
|
|
|
|
function setSubmitting(loading) {
|
|
if (!submitBtn) return;
|
|
submitBtn.disabled = !!loading;
|
|
submitBtn.classList.toggle("is-loading", !!loading);
|
|
submitBtn.setAttribute("aria-busy", loading ? "true" : "false");
|
|
submitBtn.textContent = loading ? "登录中..." : defaultSubmitText;
|
|
}
|
|
|
|
function getField(name) {
|
|
return mask.querySelector('[data-login-field="' + name + '"]');
|
|
}
|
|
|
|
function setFieldError(name, message) {
|
|
var field = getField(name);
|
|
if (!field) return;
|
|
var control = field.querySelector("[data-login-control]");
|
|
var error = field.querySelector("[data-login-error]");
|
|
if (control) control.classList.add("is-error");
|
|
if (error) {
|
|
error.textContent = message;
|
|
error.hidden = false;
|
|
}
|
|
}
|
|
|
|
function clearFieldError(name) {
|
|
var field = getField(name);
|
|
if (!field) return;
|
|
var control = field.querySelector("[data-login-control]");
|
|
var error = field.querySelector("[data-login-error]");
|
|
if (control) control.classList.remove("is-error");
|
|
if (error) error.hidden = true;
|
|
}
|
|
|
|
function clearAllErrors() {
|
|
clearFieldError("account");
|
|
clearFieldError("password");
|
|
}
|
|
|
|
function isValidLength(value) {
|
|
return value.length >= 4 && value.length <= 16;
|
|
}
|
|
|
|
function validateForm() {
|
|
var account = (accountInput && accountInput.value.trim()) || "";
|
|
var password = (passwordInput && passwordInput.value) || "";
|
|
var valid = true;
|
|
|
|
clearAllErrors();
|
|
|
|
if (!account) {
|
|
setFieldError("account", "账号格式不正确,请输入有效账号");
|
|
valid = false;
|
|
} else if (!window.AuthAPI || !AuthAPI.isValidLoginAccount(account)) {
|
|
setFieldError("account", "账号格式不正确,请输入有效账号");
|
|
valid = false;
|
|
}
|
|
|
|
if (!password) {
|
|
setFieldError("password", "密码错误,请检查后重新输入");
|
|
valid = false;
|
|
} else if (!isValidLength(password)) {
|
|
setFieldError("password", "密码错误,请检查后重新输入");
|
|
valid = false;
|
|
}
|
|
|
|
if (!valid) return null;
|
|
return { account: account, password: password };
|
|
}
|
|
|
|
function mapServerError(err) {
|
|
var tip = (err && err.message) || "登录失败,请稍后重试";
|
|
if (/密码|password/i.test(tip)) {
|
|
setFieldError("password", tip);
|
|
return;
|
|
}
|
|
if (/账号|账户|用户|邮箱|account|email|不存在|错误/i.test(tip)) {
|
|
setFieldError("account", tip);
|
|
return;
|
|
}
|
|
if (window.AuthAPI) AuthAPI.toast(tip, "error");
|
|
else if (typeof showToast === "function") showToast(tip, "error");
|
|
}
|
|
|
|
function openDialog(event) {
|
|
if (event) event.preventDefault();
|
|
if (typeof window.closeRegisterDialog === "function") {
|
|
window.closeRegisterDialog();
|
|
}
|
|
clearAllErrors();
|
|
mask.classList.add("is-open");
|
|
document.body.style.overflow = "hidden";
|
|
return true;
|
|
}
|
|
|
|
function closeDialog() {
|
|
mask.classList.remove("is-open");
|
|
clearAllErrors();
|
|
if (!document.getElementById("registerDialog") || !document.getElementById("registerDialog").classList.contains("is-open")) {
|
|
document.body.style.overflow = "";
|
|
}
|
|
}
|
|
|
|
function setLoginErrors(errors) {
|
|
clearAllErrors();
|
|
if (!errors) return;
|
|
if (errors.account) setFieldError("account", errors.account);
|
|
if (errors.password) setFieldError("password", errors.password);
|
|
}
|
|
|
|
if (form) {
|
|
form.addEventListener("submit", function (event) {
|
|
event.preventDefault();
|
|
if (submitBtn && submitBtn.classList.contains("is-loading")) return;
|
|
var payload = validateForm();
|
|
if (!payload) return;
|
|
if (!window.AuthAPI || !window.HttpRequest) {
|
|
return;
|
|
}
|
|
|
|
setSubmitting(true);
|
|
|
|
AuthAPI.login(payload)
|
|
.then(function () {
|
|
AuthAPI.toast("登录成功", "success");
|
|
closeDialog();
|
|
setTimeout(function () {
|
|
goAfterAuth();
|
|
}, 400);
|
|
})
|
|
.catch(function (err) {
|
|
setSubmitting(false);
|
|
mapServerError(err);
|
|
});
|
|
});
|
|
}
|
|
|
|
function isSafeRedirect(path) {
|
|
return typeof path === "string" && path.charAt(0) === "/" && path.charAt(1) !== "/";
|
|
}
|
|
|
|
function goAfterAuth() {
|
|
var bodyRedirect = document.body && document.body.getAttribute("data-auth-redirect");
|
|
if (isSafeRedirect(bodyRedirect)) {
|
|
window.location.href = bodyRedirect;
|
|
return;
|
|
}
|
|
var params = new URLSearchParams(window.location.search);
|
|
var redirect = params.get("redirect");
|
|
if (isSafeRedirect(redirect)) {
|
|
window.location.href = redirect;
|
|
return;
|
|
}
|
|
window.location.reload();
|
|
}
|
|
|
|
function consumeAuthQuery(kind) {
|
|
var params = new URLSearchParams(window.location.search);
|
|
if (params.get("auth") !== kind) return false;
|
|
params.delete("auth");
|
|
var qs = params.toString();
|
|
var next = window.location.pathname + (qs ? "?" + qs : "") + window.location.hash;
|
|
window.history.replaceState(null, "", next);
|
|
return true;
|
|
}
|
|
|
|
if (accountInput) {
|
|
accountInput.addEventListener("input", function () {
|
|
clearFieldError("account");
|
|
});
|
|
}
|
|
|
|
if (passwordInput) {
|
|
passwordInput.addEventListener("input", function () {
|
|
clearFieldError("password");
|
|
});
|
|
}
|
|
|
|
document.addEventListener("click", function (event) {
|
|
var openEl = event.target.closest("[data-login-dialog-open]");
|
|
if (openEl) {
|
|
openDialog(event);
|
|
return;
|
|
}
|
|
|
|
var loginLink = event.target.closest('a[href="/login/"], a[href="#login"]');
|
|
if (loginLink) {
|
|
openDialog(event);
|
|
return;
|
|
}
|
|
|
|
if (event.target.closest("[data-login-dialog-close]")) {
|
|
closeDialog();
|
|
return;
|
|
}
|
|
|
|
if (event.target.closest("[data-login-dialog-eye]")) {
|
|
var btn = event.target.closest("[data-login-dialog-eye]");
|
|
var input = document.getElementById("loginDialogPassword");
|
|
var img = btn.querySelector("img");
|
|
if (!input || !img) return;
|
|
var show = input.type === "password";
|
|
input.type = show ? "text" : "password";
|
|
img.src = show ? "/static/images/eye_open.svg" : "/static/images/eye_close.svg";
|
|
btn.setAttribute("aria-label", show ? "隐藏密码" : "显示密码");
|
|
return;
|
|
}
|
|
|
|
if (event.target === mask) {
|
|
closeDialog();
|
|
}
|
|
});
|
|
|
|
document.addEventListener("keydown", function (event) {
|
|
if (event.key === "Escape" && mask.classList.contains("is-open")) {
|
|
closeDialog();
|
|
}
|
|
});
|
|
|
|
window.openLoginDialog = openDialog;
|
|
window.closeLoginDialog = closeDialog;
|
|
window.setLoginDialogErrors = setLoginErrors;
|
|
|
|
if ((document.body && document.body.getAttribute("data-auth-mode") === "login") || consumeAuthQuery("login")) {
|
|
openDialog();
|
|
}
|
|
})();
|