diff --git a/assets/css/routes/profile.css b/assets/css/routes/profile.css index 806c2ae3..dcff7b35 100644 --- a/assets/css/routes/profile.css +++ b/assets/css/routes/profile.css @@ -1,3 +1,2 @@ @import url("profile/layout.css"); @import url("profile/lists.css"); -@import url("profile/modals.css"); diff --git a/assets/css/routes/profile/lists.css b/assets/css/routes/profile/lists.css index 0e3ac5d9..6924c5db 100644 --- a/assets/css/routes/profile/lists.css +++ b/assets/css/routes/profile/lists.css @@ -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, diff --git a/assets/css/routes/profile/modals.css b/assets/css/routes/profile/modals.css deleted file mode 100644 index 466981a1..00000000 --- a/assets/css/routes/profile/modals.css +++ /dev/null @@ -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%; - } -} diff --git a/assets/css/routes/user-center/settings.css b/assets/css/routes/user-center/settings.css index 0c986e46..8959fc61 100644 --- a/assets/css/routes/user-center/settings.css +++ b/assets/css/routes/user-center/settings.css @@ -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),去掉外层白卡壳 */ diff --git a/assets/js/auth.js b/assets/js/auth.js index d7c5fe07..80861b7b 100644 --- a/assets/js/auth.js +++ b/assets/js/auth.js @@ -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'; diff --git a/assets/js/routes/user-center.js b/assets/js/routes/user-center.js index ec3254e1..1bcb6d17 100644 --- a/assets/js/routes/user-center.js +++ b/assets/js/routes/user-center.js @@ -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); }); diff --git a/inc/helpers/user-center-renderers/profile-account-modals.php b/inc/helpers/user-center-renderers/profile-account-modals.php deleted file mode 100644 index be470e39..00000000 --- a/inc/helpers/user-center-renderers/profile-account-modals.php +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - - - diff --git a/inc/helpers/user-center-renderers/profile-settings.php b/inc/helpers/user-center-renderers/profile-settings.php index 209fca0e..059013f5 100644 --- a/inc/helpers/user-center-renderers/profile-settings.php +++ b/inc/helpers/user-center-renderers/profile-settings.php @@ -11,7 +11,5 @@ $context = is_array($args['context'] ?? null) ? $args['context'] : []; extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract $dir = __DIR__; -require $dir . '/settings-bindings.php'; -require $dir . '/settings-security.php'; +require $dir . '/settings-account.php'; require $dir . '/settings-runtime-fields.php'; -require $dir . '/profile-account-modals.php'; diff --git a/inc/helpers/user-center-renderers/settings-account.php b/inc/helpers/user-center-renderers/settings-account.php new file mode 100644 index 00000000..8270a316 --- /dev/null +++ b/inc/helpers/user-center-renderers/settings-account.php @@ -0,0 +1,144 @@ +ID : (int) ($user_id ?? 0); +$account_email = $account_user instanceof WP_User ? (string) $account_user->user_email : ''; +$account_phone = $account_user_id > 0 ? (string) get_user_meta($account_user_id, 'aksrt_phone', true) : ''; +$user_settings = class_exists('\\AKSRT\\Settings\\Settings') ? \AKSRT\Settings\Settings::get_all() : []; +$sms_settings = class_exists('\\AKSRT\\Sms\\SMS') ? \AKSRT\Sms\SMS::get_instance()->get_provider_config() : []; +$phone_available = !empty($user_settings['bind_phone']) && (($sms_settings['sms_sdk'] ?? 'null') !== 'null'); +$phone_visible = $account_phone !== '' || $phone_available; +$phone_label = $account_phone !== '' ? $account_phone : __('暂未绑定', 'aksrt'); +$email_action = $account_email !== '' ? __('修改', 'aksrt') : __('绑定', 'aksrt'); +$phone_action = $account_phone !== '' ? __('修改', 'aksrt') : __('绑定', 'aksrt'); + +$render_row = static function (string $icon, string $title, string $value, string $action, string $modal, string $icon_tone, string $action_tone, bool $interactive = true): void { + ?> +
+
+ + +
+ +
+ + + + + + +
+ +
+
+ + +
+ + +
+ + +
+ + + +
+ + +
+ + + + + + + + +
diff --git a/inc/helpers/user-center-renderers/settings-bindings.php b/inc/helpers/user-center-renderers/settings-bindings.php index 258838ba..1d3a7b80 100644 --- a/inc/helpers/user-center-renderers/settings-bindings.php +++ b/inc/helpers/user-center-renderers/settings-bindings.php @@ -48,41 +48,38 @@ foreach ((array) ($oauth_data['available'] ?? []) as $binding) { ]; } ?> -
-
- -

+