fix(profile): align account dialogs and contracts

This commit is contained in:
2026-10-06 01:09:04 +08:00
parent f646fa8573
commit 248912f610
17 changed files with 1339 additions and 832 deletions
+1 -1
View File
@@ -167,7 +167,7 @@
var submitLabel = button ? button.innerHTML : '';
if (button) {
button.disabled = true;
button.innerHTML = '<i class="fa fa-spinner fa-spin mr6" aria-hidden="true"></i>正在提交…';
button.innerHTML = '<i class="fa fas fa-spinner fa-spin mr6" aria-hidden="true"></i>正在提交…';
}
toast('正在提交申请…', 'load', '', 'aksrt_identity_submit');
window.AksrtRequest.request(config.restBase, {
+323 -363
View File
@@ -349,7 +349,7 @@
}
/* ==================== 个人信息保存 ==================== */
function initProfileForm(root) {
root = root || document;
@@ -718,7 +718,6 @@
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',
@@ -727,252 +726,349 @@
onContent: function (_, session) {
var shell = session && session.shell;
if (!shell) return;
shell.setAttribute('aria-label', title);
var dialog = shell.querySelector('[data-account-dialog]');
var title = dialog && dialog.querySelector('[data-account-password-title], .modal-colorful-header .text-center > div:last-child');
if (title && title.textContent.trim()) shell.setAttribute('aria-label', title.textContent.trim());
var firstInput = shell.querySelector('input:not([type="hidden"]), textarea, select');
if (firstInput) firstInput.focus();
bindModalFields(shell);
initBindPhone(shell);
bindDialog(shell);
},
});
}
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');
}
function setStep(dialog, key) {
dialog.querySelectorAll('[data-account-step]').forEach(function (panel) {
panel.classList.toggle('is-active', panel.getAttribute('data-account-step') === key);
});
var panels = Array.prototype.slice.call(dialog.querySelectorAll('[data-account-step]'));
var index = Math.max(0, panels.findIndex(function (panel) { return panel.getAttribute('data-account-step') === key; }));
dialog.querySelectorAll('.account-step-list li').forEach(function (item, itemIndex) {
item.classList.toggle('active', itemIndex === index);
item.classList.toggle('done', itemIndex < index);
});
}
function resetCountdown(button) {
if (!button) return;
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') || '发送验证码';
}
function startCountdown(button, seconds) {
var idleText = button.textContent;
var left = Number(seconds || 60);
if (button.__aksrtCountdownTimer) window.clearInterval(button.__aksrtCountdownTimer);
button.disabled = true;
button.classList.add('is-counting');
button.setAttribute('data-idle-label', idleText);
button.textContent = left + 's';
button.__aksrtCountdownTimer = window.setInterval(function () {
left -= 1;
if (left <= 0) { resetCountdown(button); return; }
button.textContent = left + 's';
}, 1000);
}
function settingsRestRoot() {
return String((window.aksrtData && (window.aksrtData.restUrl || window.aksrtData.apiUrl)) || cfg.restUrl || '/wp-json/aksrt/v1').replace(/\/$/, '');
}
function settingsRestNonce() {
return (window.aksrtData && (window.aksrtData.restNonce || window.aksrtData.nonce)) || cfg.restNonce || '';
}
function formHasRequiredValues(form) {
var required = form.querySelectorAll('[required]');
for (var i = 0; i < required.length; i++) {
if (required[i].type === 'checkbox' && !required[i].checked) {
required[i].focus();
notyf(i18n.agreeTerms || '请先同意相关协议', 'warning');
return false;
}
if (required[i].type !== 'checkbox' && !String(required[i].value || '').trim()) {
required[i].focus();
notyf(i18n.fillRequired || '请填写必填项', 'warning');
return false;
}
}
return true;
}
function sendEmailCode(button, form, scene) {
if (!form || button.disabled) return;
if (scene === 'change_email' && !String((form.querySelector('[name="new_email"]') || {}).value || '').trim()) {
notyf('请输入邮箱', 'warning');
return;
}
runCaptcha('change_email', form).then(function (captchaPayload) {
if (captchaPayload === null) return null;
var data = new URLSearchParams();
data.set('action', 'aksrt_send_account_email_code');
data.set('_settings_nonce', settingsNonce);
data.set('scene', scene);
new FormData(form).forEach(function (value, key) { if (value !== '' && !data.has(key)) data.set(key, value); });
Object.keys(captchaPayload || {}).forEach(function (key) { data.set(key, captchaPayload[key]); });
button.disabled = true;
return fetch(ajaxUrl, { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: data.toString() });
}).then(function (response) { return response ? response.json() : null; }).then(function (payload) {
if (!payload) return;
if (!payload.success) throw new Error((payload.data && payload.data.message) || '发送失败');
notyf((payload.data && payload.data.message) || '验证码已发送', 'success');
startCountdown(button, payload.data && payload.data.countdown);
}).catch(function (error) {
resetCountdown(button);
notyf(error.message || '发送失败', 'danger');
}).finally(function () { resetCaptcha('change_email', form); });
}
function sendPhoneCode(button, form, stage) {
if (!form || button.disabled) return;
var phone = String((form.querySelector('[name="phone"]') || {}).value || '').trim();
if (!phone) { notyf('请输入手机号', 'warning'); return; }
runCaptcha('bind_phone', form).then(function (captchaPayload) {
if (captchaPayload === null) return null;
var restRoot = settingsRestRoot();
var nonce = settingsRestNonce();
var request = fetch(restRoot + '/user/phone-code', {
method: 'POST', credentials: 'same-origin',
headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': nonce },
body: JSON.stringify(Object.assign({ phone: phone, stage: stage }, captchaPayload || {}))
});
button.disabled = true;
return request;
}).then(function (response) { return response ? response.json().then(function (payload) { return { ok: response.ok, payload: payload }; }) : null; }).then(function (result) {
if (!result) return;
if (!result.ok || !result.payload.success) throw new Error(result.payload.message || '发送失败');
notyf(result.payload.message || '验证码已发送', 'success');
startCountdown(button, result.payload.data && result.payload.data.countdown);
}).catch(function (error) {
resetCountdown(button);
notyf(error.message || '发送失败', 'danger');
}).finally(function () { resetCaptcha('bind_phone', form); });
}
function postAjaxForm(form, action, scene, success) {
if (!formHasRequiredValues(form)) return;
var button = form.querySelector('[type="submit"]');
var label = button && button.querySelector('[data-aksrt-settings-submit-label]');
var original = label ? label.textContent : '';
if (button) { button.disabled = true; setSettingsSubmitLabel(button, i18n.submitting || '提交中...'); }
runCaptcha(scene, form).then(function (captchaPayload) {
if (captchaPayload === null) {
if (button) { button.disabled = false; setSettingsSubmitLabel(button, original); }
return null;
}
var data = new FormData(form);
data.set('action', action);
data.set('_settings_nonce', settingsNonce);
Object.keys(captchaPayload || {}).forEach(function (key) { data.set(key, captchaPayload[key]); });
return fetch(ajaxUrl, { method: 'POST', credentials: 'same-origin', body: data });
}).then(function (response) { return response ? response.json() : null; }).then(function (payload) {
if (!payload) return;
if (button) { button.disabled = false; setSettingsSubmitLabel(button, original); }
if (!payload.success) throw new Error((payload.data && payload.data.message) || '操作失败');
notyf((payload.data && payload.data.message) || '操作成功', 'success');
if (typeof success === 'function') success(payload);
}).catch(function (error) {
if (button) { button.disabled = false; setSettingsSubmitLabel(button, original); }
notyf(error.message || '操作失败', 'danger');
}).finally(function () { resetCaptcha(scene, form); });
}
function postPhoneForm(form, endpoint, success) {
if (!formHasRequiredValues(form)) return;
var button = form.querySelector('[type="submit"]');
var label = button && button.querySelector('[data-aksrt-settings-submit-label]');
var original = label ? label.textContent : '';
runCaptcha('bind_phone', form).then(function (captchaPayload) {
if (captchaPayload === null) return null;
var restRoot = settingsRestRoot();
var nonce = settingsRestNonce();
if (button) { button.disabled = true; setSettingsSubmitLabel(button, i18n.submitting || '提交中...'); }
var payload = {};
new FormData(form).forEach(function (value, key) { payload[key] = value; });
Object.keys(captchaPayload || {}).forEach(function (key) { payload[key] = captchaPayload[key]; });
return fetch(restRoot + endpoint, { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': nonce }, body: JSON.stringify(payload) });
}).then(function (response) { return response ? response.json().then(function (payload) { return { ok: response.ok, payload: payload }; }) : null; }).then(function (result) {
if (!result) return;
if (!result.ok || !result.payload.success) throw new Error(result.payload.message || '操作失败');
if (button) { button.disabled = false; setSettingsSubmitLabel(button, original); }
notyf(result.payload.message || '操作成功', 'success');
if (typeof success === 'function') success(result.payload);
}).catch(function (error) {
if (button) { button.disabled = false; setSettingsSubmitLabel(button, original); }
notyf(error.message || '操作失败', 'danger');
}).finally(function () { resetCaptcha('bind_phone', form); });
}
function sendRecoveryCode(button, form) {
if (!form || button.disabled) return;
var target = String((form.querySelector('[name="target"]') || {}).value || '').trim();
if (!target) { notyf('请输入邮箱或手机号', 'warning'); return; }
var dialog = form.closest('[data-account-dialog="password"]');
button.disabled = true;
runCaptcha('lostpassword', form).then(function (captchaPayload) {
if (captchaPayload === null) { notyf('请先完成人机验证', 'warning'); resetCountdown(button); return null; }
var payload = { scene: 'resetpassword', target: target, channel: dialog ? (dialog.getAttribute('data-recovery-channel') || '') : '' };
Object.keys(captchaPayload || {}).forEach(function (key) { payload[key] = captchaPayload[key]; });
return fetch(settingsRestRoot() + '/auth/code', {
method: 'POST', credentials: 'same-origin',
headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': settingsRestNonce() },
body: JSON.stringify(payload)
});
}).then(function (response) { return response ? response.json().then(function (payload) { return { ok: response.ok, payload: payload }; }) : null; }).then(function (result) {
if (!result) return;
if (!result.ok || !result.payload.success) throw new Error(result.payload.message || '发送失败');
notyf((result.payload.data && result.payload.data.message) || result.payload.message || '验证码已发送', 'success');
startCountdown(button, (result.payload.data && result.payload.data.countdown) || result.payload.countdown);
}).catch(function (error) {
resetCountdown(button);
notyf(error.message || '发送失败', 'danger');
}).finally(function () { resetCaptcha('lostpassword', form); });
}
function postRecoveryForm(form) {
if (!formHasRequiredValues(form)) return;
var password = form.querySelector('[name="password"]');
var confirm = form.querySelector('[name="password_confirm"]');
if (password && confirm && password.value !== confirm.value) { notyf('两次密码输入不一致', 'warning'); confirm.focus(); return; }
var button = form.querySelector('[type="submit"]');
var label = button && button.querySelector('[data-aksrt-settings-submit-label]');
var original = label ? label.textContent : '';
var dialog = form.closest('[data-account-dialog="password"]');
if (button) { button.disabled = true; setSettingsSubmitLabel(button, i18n.submitting || '提交中...'); }
runCaptcha('lostpassword', form).then(function (captchaPayload) {
if (captchaPayload === null) {
if (button) { button.disabled = false; setSettingsSubmitLabel(button, original); }
return null;
}
var payload = {};
new FormData(form).forEach(function (value, key) { payload[key] = value; });
payload.channel = dialog ? (dialog.getAttribute('data-recovery-channel') || '') : '';
Object.keys(captchaPayload || {}).forEach(function (key) { payload[key] = captchaPayload[key]; });
return fetch(settingsRestRoot() + '/auth/reset-password', {
method: 'POST', credentials: 'same-origin',
headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': settingsRestNonce() },
body: JSON.stringify(payload)
});
}).then(function (response) { return response ? response.json().then(function (payload) { return { ok: response.ok, payload: payload }; }) : null; }).then(function (result) {
if (!result) return;
if (!result.ok || !result.payload.success) throw new Error(result.payload.message || '重设密码失败');
if (button) { button.disabled = false; setSettingsSubmitLabel(button, original); }
notyf(result.payload.message || (result.payload.data && result.payload.data.message) || '密码已重置', 'success');
window.setTimeout(function () {
var redirect = result.payload.redirect || (result.payload.data && result.payload.data.redirect);
if (redirect) window.location.assign(redirect);
else window.location.reload();
}, 700);
}).catch(function (error) {
if (button) { button.disabled = false; setSettingsSubmitLabel(button, original); }
notyf(error.message || '重设密码失败', 'danger');
}).finally(function () { resetCaptcha('lostpassword', form); });
}
function bindDialog(shell) {
var dialog = shell.querySelector('[data-account-dialog]');
if (!dialog || dialog.dataset.aksrtBound === 'true') return;
dialog.dataset.aksrtBound = 'true';
dialog.querySelectorAll('.toggle-pwd').forEach(function (button) {
button.addEventListener('click', function () {
var input = dialog.querySelector('#' + button.getAttribute('data-target'));
if (!input) return;
input.type = input.type === 'password' ? 'text' : 'password';
var icon = button.querySelector('i');
if (icon) { icon.classList.toggle('fa-eye', input.type === 'password'); icon.classList.toggle('fa-eye-slash', input.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;
var type = dialog.getAttribute('data-account-dialog');
if (type === 'password') {
var recoveryTarget = dialog.querySelector('#pwd-recovery-target');
if (recoveryTarget && dialog.dataset.recoveryTarget) recoveryTarget.value = dialog.dataset.recoveryTarget;
dialog.querySelectorAll('[data-account-recovery-open],[data-account-recovery-back]').forEach(function (button) {
button.addEventListener('click', function () {
var showRecovery = button.hasAttribute('data-account-recovery-open');
dialog.querySelectorAll('[data-account-password-pane]').forEach(function (pane) {
pane.hidden = pane.getAttribute('data-account-password-pane') === 'recovery' ? !showRecovery : showRecovery;
});
var title = dialog.querySelector('[data-account-password-title]');
if (title) {
title.textContent = title.getAttribute(showRecovery ? 'data-recovery-label' : 'data-change-label') || title.textContent;
var shell = dialog.closest('.aksrt-user-settings-modal');
if (shell) shell.setAttribute('aria-label', title.textContent);
}
var input = dialog.querySelector(showRecovery ? '#pwd-recovery-target' : '#pwd-old');
if (input) input.focus();
});
});
var passwordForm = dialog.querySelector('#pwd-form');
if (passwordForm) passwordForm.addEventListener('submit', function (event) {
event.preventDefault();
var password = passwordForm.querySelector('[name="password"]');
var confirm = passwordForm.querySelector('[name="password2"]');
if (password && confirm && password.value !== confirm.value) { notyf('两次密码输入不一致', 'warning'); confirm.focus(); return; }
postAjaxForm(passwordForm, 'aksrt_change_password', 'change_password', function () { window.setTimeout(function () { window.location.reload(); }, 700); });
});
var recoveryForm = dialog.querySelector('#pwd-recovery-form');
var recoveryCodeButton = dialog.querySelector('[data-account-recovery-send]');
if (recoveryCodeButton && recoveryForm) recoveryCodeButton.addEventListener('click', function () { sendRecoveryCode(recoveryCodeButton, recoveryForm); });
if (recoveryForm) recoveryForm.addEventListener('submit', function (event) { event.preventDefault(); postRecoveryForm(recoveryForm); });
} else if (type === 'email') {
dialog.querySelectorAll('.account-code-send').forEach(function (button) {
button.addEventListener('click', function () { sendEmailCode(button, button.closest('form'), button.getAttribute('data-account-code-scene')); });
});
var oldForm = dialog.querySelector('#email-old-verify-form');
var newForm = dialog.querySelector('#email-new-form');
if (oldForm) oldForm.addEventListener('submit', function (event) {
event.preventDefault();
postAjaxForm(oldForm, 'aksrt_verify_account_email', 'change_email', function () { setStep(dialog, 'email-new'); });
});
if (newForm) newForm.addEventListener('submit', function (event) {
event.preventDefault();
postAjaxForm(newForm, 'aksrt_change_email', 'change_email', function () {
if (dialog.querySelector('[data-account-step="email-success"]')) setStep(dialog, 'email-success');
window.setTimeout(function () { window.location.reload(); }, 700);
});
});
} else if (type === 'phone') {
dialog.querySelectorAll('[data-aksrt-sms-trigger]').forEach(function (button) {
button.addEventListener('click', function () { sendPhoneCode(button, button.closest('form'), button.getAttribute('data-aksrt-sms-scene')); });
});
var phoneOldForm = dialog.querySelector('#phone-old-verify-form');
var phoneNewForm = dialog.querySelector('#aksrt-bind-phone-form');
if (phoneOldForm) phoneOldForm.addEventListener('submit', function (event) {
event.preventDefault();
postPhoneForm(phoneOldForm, '/user/phone-verify', function () { setStep(dialog, 'phone-new'); });
});
if (phoneNewForm) phoneNewForm.addEventListener('submit', function (event) {
event.preventDefault();
postPhoneForm(phoneNewForm, '/user/phone', function () {
if (dialog.querySelector('[data-account-step="phone-success"]')) setStep(dialog, 'phone-success');
window.setTimeout(function () { window.location.reload(); }, 700);
});
});
}
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'; }
shell.querySelectorAll('.account-code-send,[data-aksrt-sms-trigger],[data-account-recovery-send]').forEach(resetCountdown);
shell.querySelectorAll('form').forEach(function (form) { form.reset(); });
}
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);
});
document.addEventListener('aksrt:modal:close', function (event) { resetSettingsModal(event.detail && event.detail.shell); });
}
root.querySelectorAll('[data-aksrt-settings-modal]').forEach(function(btn) {
if (btn.dataset.aksrtSettingsInitialized === 'true') return;
btn.dataset.aksrtSettingsInitialized = 'true';
btn.addEventListener('click', function() {
openSettingsModal(this.getAttribute('data-aksrt-settings-modal'));
});
root.querySelectorAll('[data-aksrt-settings-modal]').forEach(function (button) {
if (button.dataset.aksrtSettingsInitialized === 'true') return;
button.dataset.aksrtSettingsInitialized = 'true';
button.addEventListener('click', function () { openSettingsModal(button.getAttribute('data-aksrt-settings-modal')); });
});
// 验证码发送
function getSendBtnIdleText(btnId) {
if (btnId === 'pwd-send-code') return i18n.sendToCurrentEmail || '发送到当前邮箱';
if (btnId === 'email-send-code') return i18n.sendToNewEmail || '发送到新邮箱';
return i18n.sendCode || '发送验证码';
}
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 = 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);
var data = new URLSearchParams();
data.set('action', 'aksrt_send_email_code');
data.set('_settings_nonce', settingsNonce);
data.set('scene', scene || '');
if (form) {
new FormData(form).forEach(function(value, key) {
if (data.has(key) || value === '' || value == null) return;
data.set(key, value);
});
}
Object.keys(captchaPayload || {}).forEach(function(key) { data.set(key, captchaPayload[key]); });
fetch(ajaxUrl, { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: data.toString() })
.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);
} else {
self.textContent = sec + 's 后重发';
}
}, 1000);
self.__aksrtCountdownTimer = t;
} else {
notyf((res.data && res.data.message) || i18n.sendFailed || '发送失败', 'danger', 5, toastId);
self.disabled = false;
self.textContent = getSendBtnIdleText(btnId);
}
})
.catch(function() {
notyf(i18n.networkError || '网络错误', 'danger', 5, toastId);
self.disabled = false;
self.textContent = getSendBtnIdleText(btnId);
})
.finally(function() {
resetCaptcha(scene, form);
});
}).catch(function() {
resetCaptcha(scene, form);
notyf('人机验证失败,请重试', 'danger', 5, toastId);
});
});
}
// 表单提交
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';
fm.addEventListener('submit', function(e) {
e.preventDefault();
var btn = fm.querySelector('[type=submit]');
var submitLabel = btn && btn.querySelector('[data-aksrt-settings-submit-label]');
var orig = submitLabel ? submitLabel.textContent : '';
var toastId = fid === 'pwd-form'
? 'aksrt-settings-password-toast'
: 'aksrt-settings-email-toast';
var reqs = fm.querySelectorAll('[required]');
for (var i = 0; i < reqs.length; i++) {
if (!reqs[i].value.trim()) { reqs[i].focus(); notyf(i18n.fillRequired || '请填写必填项', 'warning'); return; }
}
if (fid === 'pwd-form') {
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');
return;
}
}
if (btn) { btn.disabled = true; setSettingsSubmitLabel(btn, 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; setSettingsSubmitLabel(btn, 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]); });
return fetch(ajaxUrl, { method: 'POST', credentials: 'same-origin', body: d });
})
.then(function(r) { return r ? r.json() : null; })
.then(function(r) {
if (!r) return;
if (btn) { btn.disabled = false; setSettingsSubmitLabel(btn, orig); }
if (r.success) {
notyf((r.data && r.data.message) || '操作成功', 'success', 5, toastId);
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);
}
})
.catch(function() {
if (btn) { btn.disabled = false; setSettingsSubmitLabel(btn, orig); }
notyf(i18n.networkError || '网络错误', 'danger', 5, toastId);
})
.finally(function() {
resetCaptcha(scene, fm);
});
});
}
root.querySelectorAll('.binding-item--linkable').forEach(function(link) {
root.querySelectorAll('.binding-item--linkable').forEach(function(link) {
if (link.dataset.aksrtBindCaptchaInitialized === 'true') return;
link.dataset.aksrtBindCaptchaInitialized = 'true';
link.addEventListener('click', function(event) {
@@ -1134,145 +1230,9 @@
initNotifications();
initNotificationPreferences();
initSettings();
initBindPhone();
initSettingsCallbackToast();
});
// 绑定手机
function initBindPhone(root) {
root = root || document;
var form = root.id === 'aksrt-bind-phone-form' ? root : root.querySelector('#aksrt-bind-phone-form');
if (!form) return;
if (form.dataset.aksrtSettingsInitialized === 'true') return;
form.dataset.aksrtSettingsInitialized = 'true';
function isValidSmsPhone(phone) {
if (!window.aksrtData || aksrtData.smsNumberLimitCh !== false) {
return /^1[3-9]\d{9}$/.test(phone);
}
return /^(?:\d{5,}|\+\d{6,}|\+?\d{2,}[-\s]\d{2,}[0-9\-\s]{0,15})$/.test(phone);
}
var sendButton = form.querySelector('[data-aksrt-sms-trigger]');
if (sendButton) sendButton.addEventListener('click', function () {
var phone = String((form.querySelector('[name="phone"]') || {}).value || '').trim();
if (!isValidSmsPhone(phone)) { notyf('请输入正确的手机号', 'warning'); return; }
var toastId = 'aksrt-settings-phone-code-toast';
var restRoot = (window.aksrtData && aksrtData.apiUrl) || '/wp-json/aksrt/v1';
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;
}
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 || {}))
});
}).then(function (r) { return r ? r.json().then(function (j) { return { ok: r.ok, json: j }; }) : null; }).then(function (res) {
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);
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); });
});
form.addEventListener('submit', function (e) {
e.preventDefault();
var phoneEl = form.querySelector('[name="phone"]');
var codeEl = form.querySelector('[name="code"]');
var phone = phoneEl ? String(phoneEl.value || '').trim() : '';
var code = codeEl ? String(codeEl.value || '').trim() : '';
if (!isValidSmsPhone(phone)) {
notyf('请输入正确的手机号', 'warning');
return;
}
if (!/^\d{6}$/.test(code)) {
notyf('请输入短信验证码', 'warning');
return;
}
var submitBtn = form.querySelector('[type="submit"]');
var submitLabel = submitBtn && submitBtn.querySelector('[data-aksrt-settings-submit-label]');
var submitIdleText = submitLabel ? submitLabel.textContent : '';
if (submitBtn) {
submitBtn.disabled = true;
setSettingsSubmitLabel(submitBtn, '绑定中...');
}
function doBind(captcha) {
var toastId = 'aksrt-settings-phone-bind-toast';
notyf('正在绑定手机…', 'load', '', toastId);
var restRoot = (window.aksrtData && aksrtData.apiUrl) || '/wp-json/aksrt/v1';
var nonce = (window.aksrtData && aksrtData.restNonce) || '';
return fetch(String(restRoot).replace(/\/$/, '') + '/user/phone', {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': nonce
},
body: JSON.stringify(Object.assign({ phone: phone, code: code }, captcha))
})
.then(function (r) { return r.json().then(function (j) { return { ok: r.ok, json: j }; }); })
.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;
setSettingsSubmitLabel(submitBtn, submitIdleText);
}
})
.catch(function () {
notyf('网络错误', 'danger', 5, toastId);
if (submitBtn) {
submitBtn.disabled = false;
setSettingsSubmitLabel(submitBtn, submitIdleText);
}
});
}
runCaptcha('bind_phone', form).then(function(captchaPayload) {
if (captchaPayload === null) {
if (submitBtn) {
submitBtn.disabled = false;
setSettingsSubmitLabel(submitBtn, 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);
});
});
}
})();