feat(profile): rebuild account settings page

This commit is contained in:
kate522 committed 2026-10-05 18:58:49 +08:00
1 parent f00c3faeaa
commit 346146b4fa
14 files changed
+609 -750

No files matched your search

-1
View File
@@ -1,3 +1,2 @@
@import url("profile/layout.css");
@import url("profile/lists.css");
@import url("profile/modals.css");
+4 -127
View File
@@ -452,49 +452,15 @@
}
/* ==================== 账号设置区 ==================== */
.settings-section {
margin-bottom: 20px;
}
.settings-section__header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 14px;
}
.settings-icon {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 8px;
background: var(--color-primary);
color: var(--color-on-primary, #fff);
font-size: 16px;
flex-shrink: 0;
}
.settings-icon i,
.security-action-icon i,
.aksrt-modal__icon i,
.aksrt-modal__close i {
line-height: 1;
}
.settings-hint {
font-size: 13px;
color: var(--color-ink-4);
margin: 4px 0 12px;
}
.settings-hint--top {
margin-top: 0;
margin-bottom: 10px;
line-height: 1.6;
}
.settings-hint--bottom {
margin-top: 8px;
}
.settings-section__title {
margin: 0;
border: 0;
}
.settings-empty {
font-size: 13px;
color: var(--color-ink-4);
@@ -502,22 +468,7 @@
text-align: center;
}
/* ---- 第三方绑定列表 ---- */
.binding-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.binding-item {
display: flex;
align-items: center;
gap: 10px;
padding: 11px 14px;
background: var(--surface-1);
border: 1px solid var(--color-line);
border-radius: 8px;
transition: border-color 0.15s, background 0.15s;
}
/* ---- 第三方绑定列表公共行为 ---- */
.binding-item--linkable {
cursor: pointer;
text-decoration: none;
@@ -539,13 +490,6 @@
font-size: 22px;
line-height: 1;
}
.binding-item__meta {
display: flex;
flex: 1;
align-items: center;
gap: 8px;
min-width: 0;
}
.binding-status {
display: inline-flex;
align-items: center;
@@ -558,13 +502,9 @@
.binding-status--idle {
color: var(--color-ink-4);
}
.binding-item__name {
font-size: 14px;
font-weight: 600;
min-width: 0;
.badge__icon {
line-height: 1;
}
/* ---- 徽章 ---- */
.badge {
display: inline-flex;
align-items: center;
@@ -576,18 +516,6 @@
line-height: 1.5;
white-space: nowrap;
}
.badge__icon {
line-height: 1;
}
.badge--bound {
background: #ecfdf5;
color: #059669;
}
.badge--bind {
background: #eff6ff;
color: var(--color-primary);
border: 1px solid var(--color-primary-soft);
}
/* ---- 解绑按钮 ---- */
.binding-unbind-btn {
@@ -605,57 +533,6 @@
background: rgba(239, 68, 68, 0.08);
}
/* ---- 安全设置操作区 ---- */
.security-actions {
display: flex;
flex-direction: column;
gap: 12px;
}
.security-action-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 16px;
background: var(--surface-1);
border: 1px solid var(--color-line);
border-radius: 10px;
}
.security-action-info {
display: flex;
align-items: center;
gap: 12px;
flex: 1;
min-width: 0;
}
.security-action-icon {
display: flex;
align-items: center;
justify-content: center;
width: 38px;
height: 38px;
border-radius: 10px;
background: var(--color-primary-soft);
color: var(--color-primary);
flex-shrink: 0;
font-size: 18px;
}
.security-action-text {
display: flex;
flex-direction: column;
gap: 2px;
min-width: 0;
}
.security-action-title {
font-size: 14px;
font-weight: 600;
color: var(--color-ink);
}
.security-action-desc {
font-size: 12px;
color: var(--color-ink-4);
}
/* legacy-style user tabs: one panel outside, flat rows inside. */
.dashboard--public .profile-list,
.uc-content #panel-posts .profile-list,
-251
View File
@@ -1,251 +0,0 @@
/* ==================== Modal 弹窗 ==================== */
.aksrt-modal {
display: none;
position: fixed;
inset: 0;
z-index: 99998;
align-items: center;
justify-content: center;
}
.aksrt-modal[hidden] {
display: none !important;
}
.aksrt-modal.is-open {
display: flex;
}
.aksrt-modal__overlay {
position: absolute;
inset: 0;
background: rgba(18, 18, 18, 0.48);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
animation: modalFadeIn 0.2s ease forwards;
}
.aksrt-modal__box {
position: relative;
width: 92%;
max-width: 440px;
max-height: 90vh;
overflow-y: auto;
background: var(--surface-1);
border: none;
border-radius: var(--radius-2xl, 16px);
box-shadow: var(--shadow-drawer);
animation: modalSlideUp 0.28s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
@keyframes modalFadeIn { from { opacity: 0 } to { opacity: 1 } }
@keyframes modalSlideUp {
from { opacity: 0; transform: translateY(24px) scale(0.96); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
/* Modal 头部 */
.aksrt-modal__header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 18px 20px;
border-bottom: 1px solid var(--color-line);
}
.aksrt-modal__title-wrap {
display: flex;
align-items: center;
gap: 8px;
}
.aksrt-modal__icon {
display: flex;
align-items: center;
width: 30px;
height: 30px;
border-radius: 8px;
background: var(--color-primary-soft);
color: var(--color-primary);
font-size: 16px;
}
.aksrt-modal__title-wrap h4 {
margin: 0;
font-size: 16px;
font-weight: 700;
color: var(--color-ink);
}
.aksrt-modal__close {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border: none;
border-radius: 8px;
background: transparent;
color: var(--color-ink-4);
cursor: pointer;
font-size: 18px;
transition: background 0.15s, color 0.15s;
}
.aksrt-modal__close:hover {
background: var(--surface-2);
color: var(--color-ink);
}
/* Modal 表单字段 */
.aksrt-modal__box form {
padding: 20px;
}
.modal-field {
position: relative;
margin-bottom: 16px;
}
.modal-field label {
display: block;
margin-bottom: 6px;
font-size: 13px;
font-weight: 600;
color: var(--color-ink-2);
}
.modal-field input[type="password"],
.modal-field input[type="email"],
.modal-field input[type="tel"],
.modal-field input[type="text"] {
display: block;
width: 100%;
padding: 10px 40px 10px 14px;
font-size: 14px;
line-height: 1.5;
color: var(--color-ink);
background: var(--surface-4, var(--surface-2));
border: 1px solid var(--color-line);
border-radius: 8px;
outline: none;
transition: border-color 0.2s, box-shadow 0.2s;
box-sizing: border-box;
}
.modal-field input:focus {
border-color: var(--color-primary);
box-shadow: 0 0 0 3px var(--color-primary-soft);
background: var(--surface-1);
}
.toggle-pwd {
position: absolute;
right: 10px;
top: 31px;
width: 34px;
height: 34px;
border: none;
border-radius: 6px;
background: transparent;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--color-ink-4);
font-size: 16px;
}
.toggle-pwd:hover {
color: var(--color-ink);
}
/* 只读显示(当前邮箱) */
.readonly-display {
display: block;
padding: 10px 14px;
font-size: 14px;
color: var(--color-ink-4);
background: var(--surface-2);
border: 1.5px solid var(--color-line);
border-radius: 8px;
word-break: break-all;
}
/* 验证码输入组 */
.code-input-group {
display: flex;
gap: 8px;
align-items: stretch;
}
.code-input-group input {
flex: 1;
min-width: 0;
padding: 10px 14px !important;
font-family: inherit !important;
letter-spacing: 2px;
font-weight: 600;
}
.code-send-btn {
flex-shrink: 0;
white-space: nowrap;
min-width: 110px;
}
.code-send-btn.is-counting {
pointer-events: none;
opacity: 0.65;
}
/* 密码强度条 */
.pwd-strength {
display: flex;
gap: 4px;
margin-top: 7px;
height: 4px;
border-radius: 2px;
overflow: hidden;
background: var(--surface-3);
}
.pwd-strength__fill {
width: 0%;
height: 100%;
border-radius: 2px;
transition: width 0.3s ease, background-color 0.3s ease;
background-color: var(--color-error);
}
.pwd-strength__fill.level-weak { width: 33%; background-color: var(--color-error); }
.pwd-strength__fill.level-medium { width: 66%; background-color: var(--color-warning); }
.pwd-strength__fill.level-strong { width: 100%; background-color: var(--color-success); }
.pwd-strength__text {
font-size: 11px;
margin-top: 4px;
font-weight: 500;
transition: color 0.2s;
}
.pwd-strength__text.level-weak { color: var(--color-error); }
.pwd-strength__text.level-medium { color: var(--color-warning); }
.pwd-strength__text.level-strong { color: var(--color-success); }
/* 人机验证容器在弹窗中的适配 */
.aksrt-modal .aksrt-captcha-wrap {
margin: 14px 0 8px;
}
.aksrt-modal .aksrt-captcha-box {
display: block !important;
}
/* Modal 底部按钮栏 */
.aksrt-modal__footer {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 20px;
padding-top: 16px;
border-top: 1px solid var(--color-line);
}
/* Modal mobile */
@media (max-width: 640px) {
.aksrt-modal__box {
width: 96%;
max-height: 92vh;
border-radius: 14px;
}
.aksrt-modal__footer {
flex-direction: column-reverse;
}
.aksrt-modal__footer .btn,
.aksrt-modal__footer button {
width: 100%;
}
.code-input-group {
flex-direction: column;
}
.code-send-btn {
width: 100%;
}
}
+225 -31
View File
@@ -144,52 +144,155 @@
min-width: 112px;
}
/* 设置区增强 */
.uc-content .settings-section {
margin-bottom: 24px;
/* 账号设置:按参考主题 account-set / box-body / oauth-bind-box 分区与排布。 */
.uc-content #panel-settings .aksrt-account-settings {
margin: -24px;
overflow: hidden;
border-radius: inherit;
}
.uc-content .settings-section__header {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 16px;
.aksrt-account-settings__section {
padding: 15px;
border-bottom: 1px solid var(--color-line-soft, rgba(0, 0, 0, .06));
}
.uc-content .settings-icon {
width: 36px;
height: 36px;
border-radius: 10px;
.aksrt-account-settings__section:last-child { border-bottom: 0; }
.aksrt-account-settings__heading {
display: flex;
align-items: center;
justify-content: center;
background: var(--color-primary);
color: #fff;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
margin-bottom: 20px;
}
.aksrt-account-settings__heading .title-h-left {
margin: 0 0 8px;
font-size: 16px;
flex-shrink: 0;
}
.uc-content .settings-section__title {
.aksrt-account-settings__heading p {
margin: 0;
border: 0;
font-size: 16px;
color: var(--muted-2-color, var(--color-ink-4, #999));
font-size: 14px;
line-height: 1.6;
}
.uc-content .settings-section__title::before {
display: none;
.aksrt-account-list {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
}
/* 安全操作项增强 */
.uc-content .security-action-item {
border: 1px solid var(--color-line-soft, rgba(0,0,0,0.06));
border-radius: 10px;
padding: 16px;
transition: border-color 0.2s, box-shadow 0.2s;
.aksrt-account-row {
display: grid;
grid-template-columns: minmax(116px, .85fr) minmax(0, 1fr) auto;
align-items: center;
gap: 12px;
min-width: 0;
padding: 12px 14px;
border-radius: var(--main-radius, 8px);
background: var(--muted-border-color, var(--surface-2, #f5f6f7));
color: inherit;
}
.uc-content .security-action-item:hover {
border-color: var(--color-primary-soft);
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
.aksrt-account-row__identity {
display: flex;
align-items: center;
gap: 6px;
min-width: 0;
}
.aksrt-account-row__icon {
display: grid;
place-items: center;
width: 26px;
height: 26px;
flex: 0 0 26px;
border-radius: 50%;
background: var(--this-bg, rgba(41, 151, 247, .12));
color: var(--this-color, #2997f7);
}
.aksrt-account-row__icon--blue { --this-bg: rgba(41, 151, 247, .12); --this-color: #2997f7; }
.aksrt-account-row__icon--blue-2 { --this-bg: rgba(73, 174, 231, .12); --this-color: #49aee7; }
.aksrt-account-row__icon--purple { --this-bg: rgba(136, 84, 230, .12); --this-color: #8854e6; }
.aksrt-account-row__copy {
display: block;
min-width: 0;
overflow: hidden;
color: var(--color-ink, #333);
font-size: 14px;
font-weight: 400;
text-overflow: ellipsis;
white-space: nowrap;
}
.aksrt-account-row__social-name {
overflow: hidden;
color: var(--color-ink, #333);
font-size: 13px;
font-weight: 400;
text-overflow: ellipsis;
white-space: nowrap;
}
.aksrt-account-row__value {
overflow: hidden;
min-width: 0;
color: var(--muted-2-color, var(--color-ink-3, #777));
font-size: 12px;
text-align: right;
text-overflow: ellipsis;
white-space: nowrap;
}
.aksrt-account-row__action {
flex: 0 0 auto;
margin: 0;
padding: 4px 10px;
border-radius: 999px;
font-size: 12px;
line-height: 1.5;
white-space: nowrap;
}
.aksrt-account-row--simple { grid-template-columns: minmax(0, 1fr) auto; }
.aksrt-account-row__action--unavailable { color: var(--muted-2-color, #999); }
.aksrt-account-settings__section--social .binding-captcha { margin-bottom: 12px; }
.aksrt-account-list--social .binding-item { border: 0; }
.aksrt-account-list--social .aksrt-account-row { display: grid; grid-template-columns: 28px minmax(0, 1fr) auto auto; text-decoration: none; }
.aksrt-account-list--social .binding-item__icon { display: grid; place-items: center; width: 24px; height: 24px; color: var(--color-ink, #333); }
.aksrt-account-list--social .binding-item__icon i { font-size: 22px; line-height: 1; }
.aksrt-account-list--social .binding-status { display: inline-flex; align-items: center; gap: 4px; color: var(--color-success, #16a34a); font-size: 11px; white-space: nowrap; }
.aksrt-account-list--social .binding-status--idle { color: var(--muted-2-color, var(--color-ink-4, #999)); }
.aksrt-account-list--social .binding-unbind-btn { color: var(--this-color, #e4a11b); }
.aksrt-account-list--social .binding-item--linkable:hover { background: var(--color-primary-softer, var(--color-primary-soft, rgba(41, 151, 247, .08))); }
@media (max-width: 640px) {
.aksrt-account-list { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 767px) {
.uc-content #panel-settings .aksrt-account-settings { margin: -16px; }
}
@media (max-width: 520px) {
.uc-content #panel-settings .aksrt-account-settings { margin: -16px; }
.aksrt-account-settings__section { padding: 15px; }
.aksrt-account-row { grid-template-columns: minmax(0, 1fr) auto; gap: 8px 10px; padding: 12px; }
.aksrt-account-row__identity { grid-column: 1; }
.aksrt-account-row__value { grid-column: 1; grid-row: 2; padding-left: 38px; text-align: left; }
.aksrt-account-row__action { grid-column: 2; grid-row: 1 / span 2; }
.aksrt-account-list--social .aksrt-account-row { grid-template-columns: 24px minmax(0, 1fr) auto; gap: 6px 8px; }
.aksrt-account-list--social .binding-status { grid-column: 2; grid-row: 2; }
.aksrt-account-list--social .aksrt-account-row__action { grid-column: 3; grid-row: 1 / span 2; }
}
@media (max-width: 480px) {
.uc-content #panel-settings .aksrt-account-settings { margin: -14px; }
}
/* 邀请区增强 */
@@ -502,6 +605,97 @@
margin-top: 4px;
}
/* 账号设置弹窗:使用 AksrtModalRuntime,几何和标题区对齐参考 modal-mini。 */
.aksrt-modal-runtime.aksrt-user-settings-modal:not(.aksrt-modal-runtime--bottom) .aksrt-modal-runtime__dialog {
width: 400px;
max-width: calc(100vw - 20px);
max-height: calc(100vh - 20px);
max-height: calc(100dvh - 20px);
overflow: auto;
border-radius: var(--main-radius, 8px);
}
.aksrt-user-settings-modal .aksrt-modal-runtime__content { padding: 0; }
.aksrt-settings-modal {
position: relative;
overflow: hidden;
border-radius: var(--main-radius, 8px);
background: var(--main-bg-color, var(--surface-1, #fff));
}
.aksrt-settings-modal .modal-colorful-header {
position: absolute;
top: 0;
right: 0;
left: 0;
z-index: 1;
height: 120px;
border-radius: var(--main-radius, 8px) var(--main-radius, 8px) 0 0;
}
.aksrt-settings-modal .modal-colorful-header > .close {
width: 30px;
height: 30px;
padding: 0;
border: 0;
background: transparent;
cursor: pointer;
}
.aksrt-settings-modal form {
position: relative;
z-index: 0;
margin-top: 120px;
padding: 18px 15px 0;
}
.aksrt-settings-modal__fields { padding: 0 2px; }
.aksrt-settings-modal .modal-field { position: relative; margin-bottom: 14px; }
.aksrt-settings-modal .modal-field label { display: block; margin-bottom: 6px; color: var(--muted-2-color, #777); font-size: 12px; font-weight: 400; }
.aksrt-settings-modal .modal-field input,
.aksrt-settings-modal .readonly-display {
display: block;
width: 100%;
min-height: 38px;
box-sizing: border-box;
padding: 8px 38px 8px 11px;
border: 1px solid var(--main-border-color, var(--color-line, rgba(50, 50, 50, .12)));
border-radius: var(--mini-radius, 4px);
background: var(--main-bg-color, var(--surface-1, #fff));
color: var(--color-ink, #333);
font-size: 13px;
outline: 0;
}
.aksrt-settings-modal .readonly-display { padding-right: 11px; color: var(--muted-2-color, #777); word-break: break-all; }
.aksrt-settings-modal .modal-field input:focus { border-color: var(--focus-color, var(--color-primary, #2997f7)); box-shadow: 0 0 0 2px var(--focus-color-opacity1, rgba(41, 151, 247, .12)); }
.aksrt-settings-modal .toggle-pwd { position: absolute; right: 7px; bottom: 4px; width: 30px; height: 30px; padding: 0; border: 0; background: transparent; color: var(--muted-2-color, #999); cursor: pointer; }
.aksrt-settings-modal .toggle-pwd:hover { color: var(--color-ink, #333); }
.aksrt-settings-modal .code-input-group { display: flex; align-items: stretch; gap: 7px; }
.aksrt-settings-modal .code-input-group input { min-width: 0; padding-right: 11px; letter-spacing: 2px; }
.aksrt-settings-modal .code-send-btn { min-width: 112px; padding: 6px 10px; white-space: nowrap; }
.aksrt-settings-modal .code-send-btn.is-counting { pointer-events: none; opacity: .6; }
.aksrt-settings-modal .pwd-strength { height: 3px; margin-top: 6px; border-radius: 2px; background: var(--muted-border-color, #eee); overflow: hidden; }
.aksrt-settings-modal .pwd-strength__fill { width: 0; height: 100%; border-radius: inherit; transition: width .2s ease, background-color .2s ease; }
.aksrt-settings-modal .pwd-strength__fill.level-weak { width: 33%; background: var(--color-error, #ef4444); }
.aksrt-settings-modal .pwd-strength__fill.level-medium { width: 66%; background: var(--color-warning, #f59e0b); }
.aksrt-settings-modal .pwd-strength__fill.level-strong { width: 100%; background: var(--color-success, #10b981); }
.aksrt-settings-modal .pwd-strength__text { display: block; min-height: 16px; margin-top: 3px; color: var(--muted-2-color, #999); font-size: 11px; }
.aksrt-settings-modal .pwd-strength__text.level-weak { color: var(--color-error, #ef4444); }
.aksrt-settings-modal .pwd-strength__text.level-medium { color: var(--color-warning, #f59e0b); }
.aksrt-settings-modal .pwd-strength__text.level-strong { color: var(--color-success, #10b981); }
.aksrt-settings-modal .aksrt-captcha-wrap { margin: 12px 0 8px; }
.aksrt-settings-modal__footer { margin: 0 -15px; border-radius: 0 0 var(--main-radius, 8px) var(--main-radius, 8px); }
.aksrt-settings-modal__footer > .but { min-height: 46px; }
@media (max-width: 768px) {
.aksrt-modal-runtime.aksrt-user-settings-modal.aksrt-modal-runtime--bottom .aksrt-modal-runtime__dialog {
max-height: calc(100dvh - env(safe-area-inset-bottom));
}
.aksrt-settings-modal .code-input-group { gap: 6px; }
.aksrt-settings-modal .code-send-btn { min-width: 104px; }
}
/* ==================== 官方认证面板 ==================== */
/* 面板内容为 Zibll 同款 colorful-bg hero 卡片(参考主题 1:1),去掉外层白卡壳 */
+1 -1
View File
@@ -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
View File
@@ -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);
});