diff --git a/assets/css/routes/sign.css b/assets/css/routes/sign.css
index d350e486..71eb0f8e 100644
--- a/assets/css/routes/sign.css
+++ b/assets/css/routes/sign.css
@@ -51,9 +51,7 @@
.sign-submit.jb-blue { background: linear-gradient(135deg, #59c3ff, #2b70ff); }
.sign-submit.jb-green { background: linear-gradient(135deg, #50e3a4, #16a085); }
.sign-mode-switch { justify-self: center; font-size: 12px; }
-.sign-message,
.sign-notice { margin-bottom: 16px; padding: 10px 12px; border-radius: 5px; background: rgba(255, 86, 86, .08); color: #e55353; font-size: 12px; }
-.sign-message.is-success { background: rgba(45, 194, 117, .1); color: #1e9d5d; }
.sign-hint { margin-top: -10px; color: var(--muted-2-color, #999); font-size: 11px; }
.sign-agreement { display: flex; align-items: flex-start; gap: 6px; margin: 0; color: var(--muted-2-color, #999); font-size: 11px; font-weight: 400; }
.sign-agreement a { color: var(--focus-color, #f04494); }
diff --git a/assets/js/auth.js b/assets/js/auth.js
index cf28877a..b366ec51 100644
--- a/assets/js/auth.js
+++ b/assets/js/auth.js
@@ -30,13 +30,15 @@
return data;
}
- function showMessage(root, text, success) {
- var active = root.querySelector('[data-auth-panel]:not([hidden])');
- var box = active && active.querySelector('[data-auth-message]');
- if (!box) return;
- box.textContent = text || '';
- box.hidden = !text;
- box.classList.toggle('is-success', !!success);
+ function escapeHtml(value) {
+ return String(value || '').replace(/[&<>"']/g, function (char) {
+ return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[char];
+ });
+ }
+
+ function showToast(text, type) {
+ if (!text) return;
+ window.notyf(escapeHtml(text), type || 'info');
}
function activate(root, tab, updateUrl) {
@@ -44,7 +46,6 @@
if (!panel) return;
root.querySelectorAll('[data-auth-panel]').forEach(function (item) { item.hidden = item !== panel; });
root.dataset.activeTab = tab;
- showMessage(root, '', false);
if (updateUrl && root.dataset.surface === 'page' && window.history && window.URL) {
var url = new URL(window.location.href);
url.searchParams.set('tab', tab);
@@ -63,42 +64,45 @@
box.dataset.loading = '0';
box.dataset.loaded = '1';
box.innerHTML = result.qr_html || '';
- if (result.mode === 'scan' && result.token) pollQr(root, box, result.token, 0);
+ if (result.mode === 'scan' && result.token) pollQr(box, result.token, 0);
}).catch(function (error) {
box.dataset.loading = '0';
- box.innerHTML = '' + String(error.message).replace(/[&<>"']/g, '') + '';
+ box.dataset.loaded = '0';
+ box.innerHTML = '';
+ showToast(error.message, 'danger');
});
}
- function pollQr(root, box, token, count) {
+ function pollQr(box, token, count) {
if (!document.documentElement.contains(box) || count >= 100) return;
window.setTimeout(function () {
request('check-qrcode-login', { token: token }).then(function (result) {
if (result.logged_in) {
- showMessage(root, '登录成功', true);
+ showToast('登录成功', 'success');
window.location.assign(result.redirect || window.location.href);
return;
}
if (result.status === 'expired') {
box.dataset.loaded = '0';
box.innerHTML = '';
+ showToast('二维码已过期,请刷新后重试', 'warning');
return;
}
- pollQr(root, box, token, count + 1);
- }).catch(function () { pollQr(root, box, token, count + 1); });
+ pollQr(box, token, count + 1);
+ }).catch(function () { pollQr(box, token, count + 1); });
}, 3000);
}
- function submitQrCode(root, form) {
+ function submitQrCode(form) {
var data = formData(form);
var button = form.querySelector('button[type="submit"]');
if (button) button.disabled = true;
request('weixingzh-code', data).then(function (result) {
- showMessage(root, '登录成功', true);
+ showToast('登录成功', 'success');
window.location.assign(result.redirect || window.location.href);
}).catch(function (error) {
if (button) button.disabled = false;
- showMessage(root, error.message, false);
+ showToast(error.message, 'danger');
});
}
@@ -148,11 +152,11 @@
if (captcha && typeof captcha.reset === 'function') captcha.reset();
}
- function sendCode(root, button) {
+ function sendCode(button) {
var form = button.closest('form');
var target = form && form.querySelector('[name="target"]');
if (!target || !target.value.trim()) {
- showMessage(root, '请先填写邮箱或手机号', false);
+ showToast('请先填写邮箱或手机号', 'warning');
return;
}
button.disabled = true;
@@ -163,6 +167,7 @@
verifyCaptcha(payload.scene, function (verified) {
if (!verified) {
button.disabled = false;
+ showToast('请先完成人机验证', 'warning');
return;
}
payload = formData(form);
@@ -170,30 +175,29 @@
payload.target = target.value.trim();
payload.channel = (form.querySelector('[name="channel"]') || {}).value || '';
request('code', payload).then(function (result) {
- showMessage(root, result.message || '验证码已发送', true);
+ showToast(result.message || '验证码已发送', 'success');
startCountdown(button, result.countdown || 60);
}).catch(function (error) {
button.disabled = false;
- showMessage(root, error.message, false);
+ showToast(error.message, 'danger');
}).finally(function () {
resetCaptcha(payload.scene);
});
});
}
- function submit(root, form) {
+ function submit(form) {
var action = form.dataset.authForm;
var payload = formData(form);
if (action === 'register' && payload.channel !== 'phone') {
payload.user_email = payload.target;
}
setBusy(form, true);
- showMessage(root, '', false);
request(action, payload).then(function (result) {
- showMessage(root, result.message || '操作成功', true);
+ showToast(result.message || '操作成功', 'success');
if (result.redirect) window.setTimeout(function () { window.location.assign(result.redirect); }, 350);
}).catch(function (error) {
- showMessage(root, error.message, false);
+ showToast(error.message, 'danger');
setBusy(form, false);
});
}
@@ -212,6 +216,7 @@
root.querySelectorAll('input').forEach(function (input) {
if (!input.hasAttribute('placeholder')) input.setAttribute('placeholder', ' ');
});
+ root.querySelectorAll('form').forEach(function (form) { form.noValidate = true; });
var initial = root.dataset.initialTab || 'signin';
if (initial === 'oauth' && window.matchMedia && window.matchMedia('(max-width: 767px)').matches) initial = 'signin';
activate(root, initial, false);
@@ -226,7 +231,7 @@
return;
}
var send = event.target.closest('[data-send-code]');
- if (send) { event.preventDefault(); sendCode(root, send); return; }
+ if (send) { event.preventDefault(); sendCode(send); return; }
var mode = event.target.closest('[data-login-mode]');
if (mode) { event.preventDefault(); toggleLoginMode(root, mode); }
var refreshQr = event.target.closest('[data-auth-refresh-qr]');
@@ -236,17 +241,25 @@
var qrForm = event.target.closest('[data-aksrt-weixingzh-code]');
if (qrForm) {
event.preventDefault();
- if (!qrForm.reportValidity || qrForm.reportValidity()) submitQrCode(root, qrForm);
+ if (validateForm(qrForm)) submitQrCode(qrForm);
return;
}
var form = event.target.closest('[data-auth-form]');
if (!form) return;
event.preventDefault();
- if (form.reportValidity && !form.reportValidity()) return;
- submit(root, form);
+ if (!validateForm(form)) return;
+ submit(form);
});
}
+ function validateForm(form) {
+ if (!form.checkValidity || form.checkValidity()) return true;
+ var field = form.querySelector(':invalid');
+ showToast((field && field.validationMessage) || '请完整填写表单', 'warning');
+ if (field && typeof field.focus === 'function') field.focus();
+ return false;
+ }
+
var modal;
function authPageUrl(tab, redirect) {
var routes = config.routes || {};
diff --git a/src/Core/ScriptsPart1c.php b/src/Core/ScriptsPart1c.php
index 83013e76..2517688b 100644
--- a/src/Core/ScriptsPart1c.php
+++ b/src/Core/ScriptsPart1c.php
@@ -404,7 +404,7 @@ trait ScriptsPart1c {
wp_enqueue_script(
'aksrt-auth',
$base_uri . '/assets/js/auth.js',
- ['aksrt-runtime'],
+ ['aksrt-notyf', 'aksrt-runtime'],
(string) filemtime($auth_js),
true
);
diff --git a/templates/parts/auth-content.php b/templates/parts/auth-content.php
index c9ee41d1..1a5d17d3 100644
--- a/templates/parts/auth-content.php
+++ b/templates/parts/auth-content.php
@@ -56,7 +56,6 @@ $oauth_provider_name = $is_oauth_bind ? \AKSRT\Auth\Service::get_oauth_provider_