feat(profile): rebuild account settings page
This commit is contained in:
1 parent
f00c3faeaa
commit
346146b4fa
14 files changed
+609
-750
No files matched your search
+1
-1
@@ -93,7 +93,7 @@
|
||||
var url = new URL(window.location.href);
|
||||
var action = url.searchParams.get('oauth_action');
|
||||
if (['login', 'bind', 'error'].indexOf(action) === -1) return;
|
||||
var userCenterOwnsBind = action === 'bind' && /\/user(?:\/settings)?\/?$/.test(url.pathname) && document.querySelector('[data-identity-root], .settings-section');
|
||||
var userCenterOwnsBind = action === 'bind' && /\/user(?:\/settings)?\/?$/.test(url.pathname) && document.querySelector('[data-identity-root], [data-aksrt-settings-root]');
|
||||
if (userCenterOwnsBind) return;
|
||||
document.documentElement.dataset.aksrtOauthResultHandled = 'true';
|
||||
|
||||
|
||||
+169
-116
@@ -709,93 +709,113 @@
|
||||
if (!ajaxUrl || !settingsNonce) return;
|
||||
root = root || document;
|
||||
|
||||
function openModal(id) {
|
||||
var m = document.getElementById(id);
|
||||
if (!m) return;
|
||||
m.hidden = false;
|
||||
m.classList.add('is-open');
|
||||
document.body.style.overflow = 'hidden';
|
||||
var firstInput = m.querySelector('input:not([type=hidden])');
|
||||
if (firstInput) setTimeout(function() { firstInput.focus(); }, 200);
|
||||
}
|
||||
|
||||
function closeModal(m) {
|
||||
if (typeof m === 'string') m = document.getElementById(m);
|
||||
if (!m) return;
|
||||
m.classList.remove('is-open');
|
||||
m.hidden = true;
|
||||
document.body.style.overflow = '';
|
||||
var f = m.querySelector('form');
|
||||
var sceneMap = { 'pwd-modal': 'change_password', 'email-modal': 'change_email', 'phone-modal': 'bind_phone' };
|
||||
var scene = m.id && sceneMap[m.id] ? sceneMap[m.id] : '';
|
||||
if (f) {
|
||||
f.reset();
|
||||
var bar = document.getElementById('pwd-strength-bar');
|
||||
var txt = document.getElementById('pwd-strength-text');
|
||||
if (bar && bar.firstChild) { bar.style.width = '0'; bar.firstChild.className = 'pwd-strength__fill'; }
|
||||
if (txt) { txt.textContent = ''; txt.className = 'pwd-strength__text'; }
|
||||
}
|
||||
if (scene) resetCaptcha(scene, f || m);
|
||||
}
|
||||
|
||||
root.querySelectorAll('.security-trigger').forEach(function(btn) {
|
||||
if (btn.dataset.aksrtSettingsInitialized === 'true') return;
|
||||
btn.dataset.aksrtSettingsInitialized = 'true';
|
||||
btn.addEventListener('click', function() { openModal(this.getAttribute('data-modal')); });
|
||||
});
|
||||
root.querySelectorAll('[data-close-modal], [data-modal-close]').forEach(function(el) {
|
||||
if (el.dataset.aksrtSettingsInitialized === 'true') return;
|
||||
el.dataset.aksrtSettingsInitialized = 'true';
|
||||
el.addEventListener('click', function() { closeModal(this.closest('.aksrt-modal')); });
|
||||
});
|
||||
if (document.documentElement.dataset.aksrtSettingsKeyboardInitialized !== 'true') {
|
||||
document.documentElement.dataset.aksrtSettingsKeyboardInitialized = 'true';
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape') {
|
||||
var openM = document.querySelector('.aksrt-modal.is-open');
|
||||
if (openM) closeModal(openM);
|
||||
}
|
||||
function openSettingsModal(kind) {
|
||||
var runtime = window.AksrtModalRuntime;
|
||||
var template = document.querySelector('template[data-aksrt-settings-modal-template="' + kind + '"]');
|
||||
if (!runtime || typeof runtime.open !== 'function' || !template) return;
|
||||
var title = kind === 'password' ? '修改密码' : (kind === 'email' ? '更改邮箱' : '绑定手机');
|
||||
runtime.open({
|
||||
html: template.innerHTML,
|
||||
kind: 'account-settings',
|
||||
className: 'aksrt-user-settings-modal modal-mini',
|
||||
mobileBottom: true,
|
||||
onContent: function (_, session) {
|
||||
var shell = session && session.shell;
|
||||
if (!shell) return;
|
||||
shell.setAttribute('aria-label', title);
|
||||
var firstInput = shell.querySelector('input:not([type="hidden"]), textarea, select');
|
||||
if (firstInput) firstInput.focus();
|
||||
bindModalFields(shell);
|
||||
initBindPhone(shell);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 密码显隐切换
|
||||
root.querySelectorAll('#pwd-modal .toggle-pwd, #email-modal .toggle-pwd').forEach(function(btn) {
|
||||
function bindModalFields(scope) {
|
||||
if (!scope) return;
|
||||
scope.querySelectorAll('.toggle-pwd').forEach(function(btn) {
|
||||
if (btn.dataset.aksrtSettingsInitialized === 'true') return;
|
||||
btn.dataset.aksrtSettingsInitialized = 'true';
|
||||
btn.addEventListener('click', function() {
|
||||
var inp = scope.querySelector('#' + this.getAttribute('data-target'));
|
||||
if (!inp) return;
|
||||
inp.type = inp.type === 'password' ? 'text' : 'password';
|
||||
this.classList.toggle('is-visible');
|
||||
var icon = this.querySelector('i');
|
||||
if (icon) {
|
||||
icon.classList.toggle('fa-eye', inp.type === 'password');
|
||||
icon.classList.toggle('fa-eye-slash', inp.type === 'text');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var pInput = scope.querySelector('#pwd-new');
|
||||
var sBar = scope.querySelector('#pwd-strength-bar');
|
||||
var sTxt = scope.querySelector('#pwd-strength-text');
|
||||
if (pInput && sBar && sTxt) {
|
||||
pInput.addEventListener('input', function() {
|
||||
var v = this.value;
|
||||
var fill = sBar.querySelector('.pwd-strength__fill');
|
||||
if (!v) { fill.className = 'pwd-strength__fill'; sTxt.className = 'pwd-strength__text'; sTxt.textContent = ''; return; }
|
||||
var score = 0;
|
||||
if (v.length >= 6) score++;
|
||||
if (v.length >= 10) score++;
|
||||
if (/[a-z]/.test(v) && /[A-Z]/.test(v)) score++;
|
||||
if (/\d/.test(v)) score++;
|
||||
if (/[^a-zA-Z0-9]/.test(v)) score++;
|
||||
var level = score <= 1 ? 'weak' : (score <= 3 ? 'medium' : 'strong');
|
||||
var label = score <= 1 ? '弱' : (score <= 3 ? '中' : '强');
|
||||
fill.className = 'pwd-strength__fill level-' + level;
|
||||
sTxt.className = 'pwd-strength__text level-' + level;
|
||||
sTxt.textContent = '密码强度: ' + label;
|
||||
});
|
||||
}
|
||||
bindSend('pwd-send-code', 'pwd-form', 'change_password', scope);
|
||||
bindSend('email-send-code', 'email-form', 'change_email', scope);
|
||||
submitForm('pwd-form', 'aksrt_change_password', scope);
|
||||
submitForm('email-form', 'aksrt_change_email', scope);
|
||||
}
|
||||
|
||||
function resetSettingsModal(shell) {
|
||||
if (!shell || !shell.classList.contains('aksrt-user-settings-modal')) return;
|
||||
shell.dataset.aksrtSettingsModalClosed = 'true';
|
||||
var forms = shell.querySelectorAll('form');
|
||||
var sceneMap = { 'pwd-form': 'change_password', 'email-form': 'change_email', 'aksrt-bind-phone-form': 'bind_phone' };
|
||||
forms.forEach(function(form) {
|
||||
form.reset();
|
||||
var scene = sceneMap[form.id];
|
||||
if (scene) resetCaptcha(scene, form);
|
||||
form.querySelectorAll('[data-aksrt-countdown]').forEach(function(button) {
|
||||
if (button.__aksrtCountdownTimer) window.clearInterval(button.__aksrtCountdownTimer);
|
||||
button.__aksrtCountdownTimer = null;
|
||||
button.disabled = false;
|
||||
button.classList.remove('is-counting');
|
||||
button.textContent = button.getAttribute('data-idle-label') || button.textContent;
|
||||
});
|
||||
});
|
||||
var bar = shell.querySelector('#pwd-strength-bar');
|
||||
var fill = bar && bar.querySelector('.pwd-strength__fill');
|
||||
var text = shell.querySelector('#pwd-strength-text');
|
||||
if (fill) fill.className = 'pwd-strength__fill';
|
||||
if (text) { text.textContent = ''; text.className = 'pwd-strength__text'; }
|
||||
}
|
||||
|
||||
if (document.documentElement.dataset.aksrtSettingsModalCleanupInitialized !== 'true') {
|
||||
document.documentElement.dataset.aksrtSettingsModalCleanupInitialized = 'true';
|
||||
document.addEventListener('aksrt:modal:close', function(event) {
|
||||
var shell = event.detail && event.detail.shell;
|
||||
resetSettingsModal(shell);
|
||||
});
|
||||
}
|
||||
|
||||
root.querySelectorAll('[data-aksrt-settings-modal]').forEach(function(btn) {
|
||||
if (btn.dataset.aksrtSettingsInitialized === 'true') return;
|
||||
btn.dataset.aksrtSettingsInitialized = 'true';
|
||||
btn.addEventListener('click', function() {
|
||||
var inp = document.getElementById(this.getAttribute('data-target'));
|
||||
if (!inp) return;
|
||||
inp.type = (inp.type === 'password') ? 'text' : 'password';
|
||||
this.classList.toggle('is-visible');
|
||||
openSettingsModal(this.getAttribute('data-aksrt-settings-modal'));
|
||||
});
|
||||
});
|
||||
|
||||
// 密码强度
|
||||
var pInput = document.getElementById('pwd-new');
|
||||
var sBar = document.getElementById('pwd-strength-bar');
|
||||
var sTxt = document.getElementById('pwd-strength-text');
|
||||
if (pInput && sBar && sTxt && pInput.dataset.aksrtSettingsInitialized !== 'true') {
|
||||
pInput.dataset.aksrtSettingsInitialized = 'true';
|
||||
pInput.addEventListener('input', function() {
|
||||
var v = this.value;
|
||||
var f = sBar.querySelector('.pwd-strength__fill');
|
||||
if (!v) { f.className = 'pwd-strength__fill'; sTxt.className = 'pwd-strength__text'; sTxt.textContent = ''; return; }
|
||||
var s = 0;
|
||||
if (v.length >= 6) s++;
|
||||
if (v.length >= 10) s++;
|
||||
if (/[a-z]/.test(v) && /[A-Z]/.test(v)) s++;
|
||||
if (/\d/.test(v)) s++;
|
||||
if (/[^a-zA-Z0-9]/.test(v)) s++;
|
||||
var lv = '', tx = '';
|
||||
if (s <= 1) { lv = 'weak'; tx = '弱'; }
|
||||
else if (s <= 3) { lv = 'medium'; tx = '中'; }
|
||||
else { lv = 'strong'; tx = '强'; }
|
||||
f.className = 'pwd-strength__fill level-' + lv;
|
||||
sTxt.className = 'pwd-strength__text level-' + lv;
|
||||
sTxt.textContent = '密码强度: ' + tx;
|
||||
});
|
||||
}
|
||||
|
||||
// 验证码发送
|
||||
function getSendBtnIdleText(btnId) {
|
||||
if (btnId === 'pwd-send-code') return i18n.sendToCurrentEmail || '发送到当前邮箱';
|
||||
@@ -803,20 +823,22 @@
|
||||
return i18n.sendCode || '发送验证码';
|
||||
}
|
||||
|
||||
function bindSend(btnId, formId, scene) {
|
||||
var b = document.getElementById(btnId);
|
||||
function bindSend(btnId, formId, scene, scope) {
|
||||
var container = scope || document;
|
||||
var b = container.querySelector ? container.querySelector('#' + btnId) : document.getElementById(btnId);
|
||||
if (!b) return;
|
||||
if (b.dataset.aksrtSettingsInitialized === 'true') return;
|
||||
b.dataset.aksrtSettingsInitialized = 'true';
|
||||
b.addEventListener('click', function() {
|
||||
if (this.classList.contains('is-counting')) return;
|
||||
var self = this;
|
||||
var form = document.getElementById(formId);
|
||||
var form = container.querySelector ? container.querySelector('#' + formId) : document.getElementById(formId);
|
||||
var toastId = scene === 'change_password'
|
||||
? 'aksrt-settings-password-code-toast'
|
||||
: 'aksrt-settings-email-code-toast';
|
||||
runCaptcha(scene, form).then(function(captchaPayload) {
|
||||
if (captchaPayload === null) return;
|
||||
if (container.dataset && container.dataset.aksrtSettingsModalClosed === 'true') return;
|
||||
self.disabled = true;
|
||||
self.textContent = i18n.sending || '发送中...';
|
||||
notyf(i18n.sending || '正在发送验证码…', 'load', '', toastId);
|
||||
@@ -835,13 +857,17 @@
|
||||
.then(function(r) { return r.json(); }).then(function(res) {
|
||||
if (res.success) {
|
||||
notyf((res.data && res.data.message) || i18n.codeSent || '验证码已发送', 'success', 5, toastId);
|
||||
if (container.dataset && container.dataset.aksrtSettingsModalClosed === 'true') return;
|
||||
var sec = 60;
|
||||
self.classList.add('is-counting');
|
||||
self.textContent = sec + 's 后重发';
|
||||
self.setAttribute('data-aksrt-countdown', '');
|
||||
self.setAttribute('data-idle-label', getSendBtnIdleText(btnId));
|
||||
var t = setInterval(function() {
|
||||
sec--;
|
||||
if (sec <= 0) {
|
||||
clearInterval(t);
|
||||
self.__aksrtCountdownTimer = null;
|
||||
self.disabled = false;
|
||||
self.classList.remove('is-counting');
|
||||
self.textContent = getSendBtnIdleText(btnId);
|
||||
@@ -849,6 +875,7 @@
|
||||
self.textContent = sec + 's 后重发';
|
||||
}
|
||||
}, 1000);
|
||||
self.__aksrtCountdownTimer = t;
|
||||
} else {
|
||||
notyf((res.data && res.data.message) || i18n.sendFailed || '发送失败', 'danger', 5, toastId);
|
||||
self.disabled = false;
|
||||
@@ -870,12 +897,10 @@
|
||||
});
|
||||
}
|
||||
|
||||
bindSend('pwd-send-code', 'pwd-form', 'change_password');
|
||||
bindSend('email-send-code', 'email-form', 'change_email');
|
||||
|
||||
// 表单提交
|
||||
function submitForm(fid, act) {
|
||||
var fm = document.getElementById(fid);
|
||||
function submitForm(fid, act, scope) {
|
||||
var container = scope || document;
|
||||
var fm = container.querySelector ? container.querySelector('#' + fid) : document.getElementById(fid);
|
||||
if (!fm) return;
|
||||
if (fm.dataset.aksrtSettingsInitialized === 'true') return;
|
||||
fm.dataset.aksrtSettingsInitialized = 'true';
|
||||
@@ -891,8 +916,8 @@
|
||||
if (!reqs[i].value.trim()) { reqs[i].focus(); notyf(i18n.fillRequired || '请填写必填项', 'warning'); return; }
|
||||
}
|
||||
if (fid === 'pwd-form') {
|
||||
var pwdNew = document.getElementById('pwd-new');
|
||||
var pwdConfirm = document.getElementById('pwd-new-confirm');
|
||||
var pwdNew = fm.querySelector('#pwd-new');
|
||||
var pwdConfirm = fm.querySelector('#pwd-new-confirm');
|
||||
if (pwdNew && pwdConfirm && pwdNew.value !== pwdConfirm.value) {
|
||||
pwdConfirm.focus();
|
||||
notyf(i18n.passwordMismatch || '两次输入的新密码不一致', 'warning');
|
||||
@@ -902,12 +927,15 @@
|
||||
if (btn) { btn.disabled = true; btn.textContent = i18n.submitting || '提交中...'; }
|
||||
notyf(i18n.submitting || '正在提交…', 'load', '', toastId);
|
||||
var scene = fid === 'pwd-form' ? 'change_password' : 'change_email';
|
||||
runCaptcha(scene, fm).then(function(captchaPayload) {
|
||||
if (captchaPayload === null) {
|
||||
if (btn) { btn.disabled = false; btn.textContent = orig; }
|
||||
return null;
|
||||
}
|
||||
var d = new FormData(fm);
|
||||
runCaptcha(scene, fm).then(function(captchaPayload) {
|
||||
if (captchaPayload === null) {
|
||||
if (btn) { btn.disabled = false; btn.textContent = orig; }
|
||||
return null;
|
||||
}
|
||||
if (container.dataset && container.dataset.aksrtSettingsModalClosed === 'true') return null;
|
||||
var modalShell = fm.closest('.aksrt-user-settings-modal');
|
||||
if (modalShell && modalShell.dataset.aksrtSettingsModalClosed === 'true') return null;
|
||||
var d = new FormData(fm);
|
||||
d.set('action', act);
|
||||
d.set('_settings_nonce', settingsNonce);
|
||||
Object.keys(captchaPayload || {}).forEach(function(key) { d.set(key, captchaPayload[key]); });
|
||||
@@ -919,7 +947,10 @@
|
||||
if (btn) { btn.disabled = false; btn.textContent = orig; }
|
||||
if (r.success) {
|
||||
notyf((r.data && r.data.message) || '操作成功', 'success', 5, toastId);
|
||||
closeModal(fm.closest('.aksrt-modal'));
|
||||
var activeModal = window.AksrtModalRuntime && window.AksrtModalRuntime.getActive();
|
||||
if (activeModal && activeModal.classList.contains('aksrt-user-settings-modal') && activeModal.contains(fm)) {
|
||||
window.AksrtModalRuntime.close('settings-submit');
|
||||
}
|
||||
if (r.data && r.data.redirect_to) setTimeout(function() { window.location.href = r.data.redirect_to; }, 2500);
|
||||
} else {
|
||||
notyf((r.data && r.data.message) || i18n.operationFailed || '操作失败', 'danger', 5, toastId);
|
||||
@@ -935,9 +966,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
submitForm('pwd-form', 'aksrt_change_password');
|
||||
submitForm('email-form', 'aksrt_change_email');
|
||||
|
||||
root.querySelectorAll('.binding-item--linkable').forEach(function(link) {
|
||||
if (link.dataset.aksrtBindCaptchaInitialized === 'true') return;
|
||||
link.dataset.aksrtBindCaptchaInitialized = 'true';
|
||||
@@ -1127,12 +1155,14 @@
|
||||
var nonce = (window.aksrtData && aksrtData.restNonce) || '';
|
||||
sendButton.disabled = true;
|
||||
notyf('正在发送验证码…', 'load', '', toastId);
|
||||
runCaptcha('bind_phone', form).then(function(captchaPayload) {
|
||||
if (captchaPayload === null) {
|
||||
sendButton.disabled = false;
|
||||
return null;
|
||||
}
|
||||
return fetch(String(restRoot).replace(/\/$/, '') + '/user/phone-code', {
|
||||
runCaptcha('bind_phone', form).then(function(captchaPayload) {
|
||||
if (captchaPayload === null) {
|
||||
sendButton.disabled = false;
|
||||
return null;
|
||||
}
|
||||
var modalShell = form.closest('.aksrt-user-settings-modal');
|
||||
if (modalShell && modalShell.dataset.aksrtSettingsModalClosed === 'true') return null;
|
||||
return fetch(String(restRoot).replace(/\/$/, '') + '/user/phone-code', {
|
||||
method: 'POST', credentials: 'same-origin',
|
||||
headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': nonce },
|
||||
body: JSON.stringify(Object.assign({ phone: phone }, captchaPayload || {}))
|
||||
@@ -1141,8 +1171,24 @@
|
||||
if (!res) return;
|
||||
if (!res.ok || !res.json.success) throw new Error(res.json.message || '发送失败');
|
||||
notyf(res.json.message || '验证码已发送', 'success', 5, toastId);
|
||||
var modalShell = form.closest('.aksrt-user-settings-modal');
|
||||
if (modalShell && modalShell.dataset.aksrtSettingsModalClosed === 'true') return;
|
||||
var left = Number((res.json.data && res.json.data.countdown) || 60);
|
||||
var timer = setInterval(function () { left -= 1; sendButton.textContent = left + 's'; if (left <= 0) { clearInterval(timer); sendButton.disabled = false; sendButton.textContent = '获取验证码'; } }, 1000);
|
||||
sendButton.setAttribute('data-aksrt-countdown', '');
|
||||
sendButton.setAttribute('data-idle-label', '获取验证码');
|
||||
sendButton.classList.add('is-counting');
|
||||
var timer = setInterval(function () {
|
||||
left -= 1;
|
||||
sendButton.textContent = left + 's';
|
||||
if (left <= 0) {
|
||||
clearInterval(timer);
|
||||
sendButton.__aksrtCountdownTimer = null;
|
||||
sendButton.disabled = false;
|
||||
sendButton.classList.remove('is-counting');
|
||||
sendButton.textContent = sendButton.getAttribute('data-idle-label');
|
||||
}
|
||||
}, 1000);
|
||||
sendButton.__aksrtCountdownTimer = timer;
|
||||
}).catch(function (error) { sendButton.disabled = false; notyf(error.message || '发送失败', 'danger', 5, toastId); })
|
||||
.finally(function () { resetCaptcha('bind_phone', form); });
|
||||
});
|
||||
@@ -1160,8 +1206,9 @@
|
||||
notyf('请输入短信验证码', 'warning');
|
||||
return;
|
||||
}
|
||||
var submitBtn = form.querySelector('[type="submit"]');
|
||||
if (submitBtn) {
|
||||
var submitBtn = form.querySelector('[type="submit"]');
|
||||
var submitIdleText = submitBtn ? submitBtn.textContent : '';
|
||||
if (submitBtn) {
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.textContent = '绑定中...';
|
||||
}
|
||||
@@ -1183,13 +1230,17 @@
|
||||
.then(function (res) {
|
||||
if (res.ok && res.json && res.json.success) {
|
||||
notyf(res.json.message || '绑定成功', 'success', 5, toastId);
|
||||
var activeModal = window.AksrtModalRuntime && window.AksrtModalRuntime.getActive();
|
||||
if (activeModal && activeModal.classList.contains('aksrt-user-settings-modal') && activeModal.contains(form)) {
|
||||
window.AksrtModalRuntime.close('settings-submit');
|
||||
}
|
||||
setTimeout(function () { window.location.reload(); }, 600);
|
||||
return;
|
||||
}
|
||||
notyf((res.json && (res.json.message || res.json.error)) || '绑定失败', 'danger', 5, toastId);
|
||||
if (submitBtn) {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = '确认绑定';
|
||||
submitBtn.textContent = submitIdleText;
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
@@ -1201,14 +1252,16 @@
|
||||
});
|
||||
}
|
||||
runCaptcha('bind_phone', form).then(function(captchaPayload) {
|
||||
if (captchaPayload === null) {
|
||||
if (submitBtn) {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = '确认绑定';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return doBind(captchaPayload);
|
||||
if (captchaPayload === null) {
|
||||
if (submitBtn) {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = submitIdleText;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var modalShell = form.closest('.aksrt-user-settings-modal');
|
||||
if (modalShell && modalShell.dataset.aksrtSettingsModalClosed === 'true') return null;
|
||||
return doBind(captchaPayload);
|
||||
}).finally(function() {
|
||||
resetCaptcha('bind_phone', form);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user