From 315aee1b120a1fb308e4f9bbe1b69dc1d329164e Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 21 Aug 2026 12:49:19 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BA=86=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E4=B8=80=E4=BA=9B=E5=B7=B2=E7=9F=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/css/frontend/header-userbox.css | 40 +++++++++---------- assets/widgets/frontend.css | 4 +- src/WidgetSystem/Renderers/UserRenderer.php | 7 ++-- .../page-display-contract.test.cjs | 14 +++++-- 4 files changed, 37 insertions(+), 28 deletions(-) diff --git a/assets/css/frontend/header-userbox.css b/assets/css/frontend/header-userbox.css index b3a686e4..df0ebccf 100644 --- a/assets/css/frontend/header-userbox.css +++ b/assets/css/frontend/header-userbox.css @@ -24,6 +24,20 @@ flex: 0 0 auto; } +.navbar-top .aksrt-navbar-account > li { + position: relative; + padding: 0; + overflow: visible; +} + +.navbar-top .aksrt-navbar-account > li > .sub-menu { + top: calc(100% + 5px); + right: 0; + left: auto; + z-index: 1002; + margin: 0; +} + .navbar-top .aksrt-navbar-account { gap: 4px; min-height: 50px; @@ -93,42 +107,28 @@ margin: 0; } -/* The full search surface must sit above a fixed header (z-index: 999). */ +/* Keep the drop-down search panel above a fixed header (z-index: 999). */ .navbar-search { z-index: 1001; - height: 100%; - overflow: hidden; } .navbar-search > .container { position: relative; - height: 100%; - max-height: 100%; - padding-bottom: 76px; + max-height: calc(100vh - 40px); overflow-y: auto; } .navbar-search .search-close-box { - position: fixed; - right: max(30px, calc((100vw - var(--container, 1200px)) / 2 + 30px)); - bottom: 24px; - z-index: 2; - width: auto; - max-width: none; - margin: 0; + display: flex; + justify-content: flex-end; + max-width: 660px; + margin: 16px auto 0; } .navbar-search .search-close-box > .but { float: none; } -@media (max-width: 767.98px) { - .navbar-search .search-close-box { - right: 15px; - bottom: calc(15px + env(safe-area-inset-bottom, 0px)); - } -} - .aksrt-header-userbox { width: 260px; color: var(--color-ink-2); diff --git a/assets/widgets/frontend.css b/assets/widgets/frontend.css index 2388b851..db32141f 100644 --- a/assets/widgets/frontend.css +++ b/assets/widgets/frontend.css @@ -104,7 +104,7 @@ .aksrt-widget-text-title h2 { margin: 0; font-size: clamp(26px,4vw,44px); line-height: 1.25; }.aksrt-widget-text-title p { margin: 12px 0 0; color: var(--muted-color,#8a8c98); line-height: 1.8; } .aksrt-widget-buttons { display: flex; flex-wrap: wrap; gap: var(--aksrt-button-gap,10px); }.aksrt-widget-buttons.is-center { justify-content: center; }.aksrt-widget-buttons.is-right { justify-content: flex-end; } .aksrt-widget-button { display: inline-flex; min-height: 40px; padding: 9px 18px; align-items: center; justify-content: center; gap: 7px; border: 0; border-radius: 9px; background: var(--focus-color,#ff5473); color: #fff !important; font-weight: 600; cursor: pointer; transition: filter .2s, transform .2s; } -.aksrt-widget-button:hover { filter: brightness(1.07); transform: translateY(-1px); }.aksrt-widget-button.is-ghost { background: rgba(127,127,127,.12); color: inherit !important; } +.aksrt-widget-button:hover { filter: brightness(1.07); transform: translateY(-1px); }.aksrt-widget-button.is-secondary { background: rgba(127,127,127,.12); color: inherit !important; } .aksrt-widget-gap { height: var(--aksrt-gap); } .aksrt-widget-tag-cloud { display: flex; flex-wrap: wrap; gap: 8px; }.aksrt-widget-tag-cloud a { padding: 6px 11px; border-radius: 6px; background: rgba(127,127,127,.1); font-size: 13px; transition: background .2s,color .2s; }.aksrt-widget-tag-cloud a:hover { background: var(--focus-color,#ff5473); color: #fff; }.aksrt-widget-tag-cloud small { margin-left: 5px; opacity: .65; } .aksrt-widget-comments { display: grid; gap: 15px; margin: 0; padding: 0; list-style: none; }.aksrt-widget-comments li { display: flex; gap: 11px; }.aksrt-widget-comments__avatar img { width: 42px; height: 42px; border-radius: 50%; }.aksrt-widget-comments p { margin: 3px 0; color: var(--muted-color,#8a8c98); font-size: 13px; line-height: 1.55; }.aksrt-widget-comments__source { font-size: 12px; opacity: .65; } @@ -126,7 +126,7 @@ .aksrt-slider { height: var(--aksrt-slider-height); overflow: hidden; border-radius: var(--aksrt-widget-radius); background: #111; }.aksrt-slider .swiper-slide, .aksrt-slider .swiper-slide > a { position: relative; display: block; width: 100%; height: 100%; overflow: hidden; }.aksrt-slider .swiper-slide > img, .aksrt-slider .swiper-slide > video, .aksrt-slider .swiper-slide > a > img, .aksrt-slider .swiper-slide > a > video { width: 100%; height: 100%; object-fit: cover; }.aksrt-slide-copy { position: absolute; z-index: 2; right: 8%; bottom: 12%; left: 8%; color: #fff; text-shadow: 0 2px 12px rgba(0,0,0,.4); }.aksrt-slide-copy.is-center { top: 50%; bottom: auto; text-align: center; transform: translateY(-50%); }.aksrt-slide-copy.is-right-bottom { text-align: right; }.aksrt-slide-copy h3 { margin: 0; font-size: clamp(25px,4vw,44px); }.aksrt-slide-copy p { margin: 8px 0 0; }.aksrt-slide-layer { position: absolute; z-index: 1; inset: 0; width: 100%; height: 100%; object-fit: contain; pointer-events: none; }.aksrt-slider-combo { display: grid; grid-template-columns: minmax(0,2fr) minmax(240px,1fr); gap: 15px; }.aksrt-slider-combo__side { display: grid; gap: 15px; }.aksrt-slider-combo__side .aksrt-media-card { min-height: 0; }.aksrt-slider-combo__side .aksrt-media-card__media { aspect-ratio: 2/1; } -.aksrt-user-card { position: relative; overflow: hidden; border-radius: var(--main-radius,8px); background: var(--main-bg-color,#fff); box-shadow: 0 0 10px var(--main-shadow,rgba(116,116,116,.08)); text-align: center; }.aksrt-user-card__cover { height: 105px; background: var(--muted-border-color,#eee); background-position: center; background-size: cover; }.aksrt-user-card__body { position: relative; padding: 15px; }.aksrt-user-card__avatar { display: inline-block; margin-top: -62px; padding: 4px; border-radius: 50%; background: var(--main-bg-color,#fff); }.aksrt-user-card__avatar img { display: block; width: 84px; height: 84px; border-radius: 50%; object-fit: cover; }.aksrt-user-card.is-guest .aksrt-user-card__avatar { margin-top: -58px; }.aksrt-user-card h3 { margin: 7px 0; }.aksrt-user-card__identity { margin: 6px 0; }.aksrt-user-card__body > p { color: var(--muted-color,#8a8c98); }.aksrt-user-card__stats { display: flex; justify-content: center; gap: 24px; margin: 16px 0; }.aksrt-user-card__stats span { display: flex; flex-direction: column; font-size: 12px; color: var(--muted-color,#8a8c98); }.aksrt-user-card__stats b { color: inherit; font-size: 17px; }.aksrt-user-card__actions { display: flex; justify-content: center; gap: 9px; }.aksrt-user-card__follow { position: absolute; top: 14px; right: 14px; padding: 5px 12px; border: 0; border-radius: 999px; background: var(--focus-color,#ff5473); color: #fff; cursor: pointer; }.aksrt-user-card__follow.is-following { background: rgba(0,0,0,.5); }.aksrt-user-card__posts { padding: 0 16px 16px; text-align: left; } +.aksrt-user-card { position: relative; overflow: hidden; border-radius: var(--main-radius,8px); background: var(--main-bg-color,#fff); box-shadow: 0 0 10px var(--main-shadow,rgba(116,116,116,.08)); text-align: center; }.aksrt-user-card__cover { height: 105px; background: var(--muted-border-color,#eee); background-position: center; background-size: cover; }.aksrt-user-card__body { position: relative; padding: 15px; }.aksrt-user-card__avatar { display: inline-block; margin-top: -62px; padding: 4px; border-radius: 50%; background: var(--main-bg-color,#fff); }.aksrt-user-card__avatar img { display: block; width: 84px; height: 84px; border-radius: 50%; object-fit: cover; }.aksrt-user-card.is-guest .aksrt-user-card__avatar { margin-top: -58px; }.aksrt-user-card h3 { margin: 7px 0; }.aksrt-user-card__identity { margin: 6px 0; }.aksrt-user-card__body > p { color: var(--muted-color,#8a8c98); }.aksrt-user-card__stats { display: flex; justify-content: center; gap: 24px; margin: 16px 0; }.aksrt-user-card__stats span { display: flex; flex-direction: column; font-size: 12px; color: var(--muted-color,#8a8c98); }.aksrt-user-card__stats b { color: inherit; font-size: 17px; }.aksrt-user-card__actions { display: flex; justify-content: center; gap: 9px; }.aksrt-user-card__actions > .aksrt-widget-button, .aksrt-user-card__actions > .aksrt-user-card__checkin { flex: 1 1 0; min-width: 0; }.aksrt-user-card__checkin > .aksrt-header-checkin { height: 100%; min-height: 40px; }.aksrt-user-card__checkin.is-primary > .aksrt-header-checkin { background: var(--focus-color,#ff5473) !important; color: #fff !important; }.aksrt-user-card__checkin.is-primary > .aksrt-header-checkin > i { color: inherit !important; }.aksrt-user-card__checkin.is-secondary > .aksrt-header-checkin { background: rgba(127,127,127,.12) !important; color: inherit !important; }.aksrt-user-card__checkin.is-secondary > .aksrt-header-checkin > i { color: inherit !important; }.aksrt-user-card__follow { position: absolute; top: 14px; right: 14px; padding: 5px 12px; border: 0; border-radius: 999px; background: var(--focus-color,#ff5473); color: #fff; cursor: pointer; }.aksrt-user-card__follow.is-following { background: rgba(0,0,0,.5); }.aksrt-user-card__posts { padding: 0 16px 16px; text-align: left; } .aksrt-user-ranking { display: grid; gap: 11px; margin: 0; padding: 0; list-style: none; }.aksrt-user-ranking li { display: flex; align-items: center; gap: 9px; }.aksrt-user-ranking__index { width: 24px; text-align: center; color: var(--muted-color,#8a8c98); font-weight: 700; }.aksrt-user-ranking__index.is-top { color: #f59e0b; }.aksrt-user-tile { display: flex; min-width: 0; flex: 1; align-items: center; gap: 10px; }.aksrt-user-tile img { width: 44px; height: 44px; border-radius: 50%; object-fit: cover; }.aksrt-user-tile span { display: flex; min-width: 0; flex-direction: column; }.aksrt-user-tile strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }.aksrt-user-tile small { color: var(--muted-color,#8a8c98); }.aksrt-user-list { --aksrt-cols: 4; }.aksrt-user-list article { padding: 14px; border-radius: 10px; background: var(--main-bg,#fff); box-shadow: 0 6px 20px rgba(31,35,48,.06); } .aksrt-user-card.no-cover .aksrt-user-card__avatar { margin-top: 0; }.aksrt-user-card__oauth { margin-top: 14px; } diff --git a/src/WidgetSystem/Renderers/UserRenderer.php b/src/WidgetSystem/Renderers/UserRenderer.php index 81a30617..8b6ed1cc 100644 --- a/src/WidgetSystem/Renderers/UserRenderer.php +++ b/src/WidgetSystem/Renderers/UserRenderer.php @@ -33,7 +33,7 @@ final class UserRenderer { $oauth_html = (string) $oauth->get_login_buttons(); } } - return '
' . (function_exists('aksrt_get_avatar_html') ? aksrt_get_avatar_html(0, 88) : get_avatar(0, 88)) . '

' . esc_html__('Hi,请登录', 'aksrt') . '

' . esc_html__('登录后管理课程、会员、订单与创作内容。', 'aksrt') . '

' . ($oauth_html !== '' ? '
' . $oauth_html . '
' : '') . '
'; + return '
' . (function_exists('aksrt_get_avatar_html') ? aksrt_get_avatar_html(0, 88) : get_avatar(0, 88)) . '

' . esc_html__('Hi,请登录', 'aksrt') . '

' . esc_html__('登录后管理课程、会员、订单与创作内容。', 'aksrt') . '

' . ($oauth_html !== '' ? '
' . $oauth_html . '
' : '') . '
'; } $user = get_userdata($user_id); return $user instanceof \WP_User ? self::profile_card($user, $i, true) : ''; @@ -79,8 +79,9 @@ final class UserRenderer { private static function action(string $action, \WP_User $user, bool $primary): string { $labels = ['home' => __('个人主页', 'aksrt'), 'submit' => __('发布文章', 'aksrt'), 'vip' => __('会员中心', 'aksrt'), 'orders' => __('订单中心', 'aksrt'), 'recharge' => __('余额充值', 'aksrt'), 'signin' => __('签到', 'aksrt')]; + $variant = $primary ? ' is-primary' : ' is-secondary'; if ($action === 'signin' && function_exists('aksrt_header_user_checkin_btn') && get_current_user_id() === (int) $user->ID) { - return '' . aksrt_header_user_checkin_btn((int) $user->ID, true) . ''; + return '' . aksrt_header_user_checkin_btn((int) $user->ID, true) . ''; } if ($action === 'home') { $url = function_exists('aksrt_get_user_home_url') ? aksrt_get_user_home_url($user) : get_author_posts_url((int) $user->ID); @@ -93,7 +94,7 @@ final class UserRenderer { if ($url === '') { return ''; } - return '' . esc_html($labels[$action] ?? $labels['home']) . ''; + return '' . esc_html($labels[$action] ?? $labels['home']) . ''; } private static function ranking(array $i): string { diff --git a/tests/widget-system/page-display-contract.test.cjs b/tests/widget-system/page-display-contract.test.cjs index 2521c15c..25cb555c 100644 --- a/tests/widget-system/page-display-contract.test.cjs +++ b/tests/widget-system/page-display-contract.test.cjs @@ -210,6 +210,10 @@ test('user profile widget owns its only card surface and keeps membership below assert.match(userRenderer, /display_name\) \. '<\/a><\/h3>'/) assert.match(userRenderer, /aksrt-user-card__identity.*aksrt_render_user_identity_chips\(\$user_id, 'widget'\)/s) assert.doesNotMatch(userRenderer, /display_name\) \. '<\/a>' \. \(function_exists\('aksrt_render_vip_badge'/) + assert.match(userRenderer, /\$variant = \$primary \? ' is-primary' : ' is-secondary'/) + assert.match(userRenderer, /aksrt-widget-button is-primary signin-loader/) + assert.match(userRenderer, /aksrt-widget-button is-secondary signup-loader/) + assert.doesNotMatch(userRenderer, /aksrt-widget-button is-ghost/) }) test('desktop header actions share one row without generic button masks', () => { @@ -219,11 +223,15 @@ test('desktop header actions share one row without generic button masks', () => assert.match(headerCss, /\.navbar-top \.aksrt-navbar-actions\s*\{[^}]*display:\s*flex;[^}]*align-items:\s*center;/s) assert.match(headerCss, /\.navbar-top \.aksrt-nav-icon,[\s\S]*?background:\s*transparent !important;/) assert.match(headerCss, /\.navbar-top \.aksrt-navbar-actions \.navbar-cta\.radius\s*\{[^}]*border-radius:\s*50px;/s) + assert.match(headerCss, /\.navbar-top \.aksrt-navbar-account > li\s*\{[^}]*position:\s*relative;/s) + assert.match(headerCss, /\.navbar-top \.aksrt-navbar-account > li > \.sub-menu\s*\{[^}]*top:\s*calc\(100% \+ 5px\);[^}]*right:\s*0;/s) }) -test('search surface clears the fixed header and closes from the lower-right corner', () => { - assert.match(headerCss, /\.navbar-search\s*\{[^}]*z-index:\s*1001;[^}]*height:\s*100%;[^}]*overflow:\s*hidden;/s) - assert.match(headerCss, /\.navbar-search \.search-close-box\s*\{[^}]*position:\s*fixed;[^}]*right:[^;]+;[^}]*bottom:/s) +test('search dropdown clears the fixed header and closes from the panel lower-right', () => { + assert.match(headerCss, /\.navbar-search\s*\{[^}]*z-index:\s*1001;/s) + assert.doesNotMatch(headerCss, /\.navbar-search\s*\{[^}]*height:\s*100%/s) + assert.match(headerCss, /\.navbar-search \.search-close-box\s*\{[^}]*display:\s*flex;[^}]*justify-content:\s*flex-end;[^}]*margin:\s*16px auto 0;/s) + assert.doesNotMatch(headerCss, /\.navbar-search \.search-close-box\s*\{[^}]*position:\s*fixed/s) assert.ok(headerSearch.indexOf('echo $remote') < headerSearch.indexOf('search-close-box')) })