Files
wordpress-theme-aksrt/inc/helpers/header/aksrt-header-navbar.php
T
Claude 4325ce0c1c fix(ui): render user level icon inline beside the name
The level (等级) badge was wrapping to its own line below the username and
rendering at the intrinsic PNG size in most surfaces. Centralize the badge
sizing after zibll's `.img-icon` (height: 1.1em, inline-block) in base.css so
every route gets the same compact inline badge, and keep the name row from
flex-wrapping.

- Add shared `.aksrt-user-level-icon / .aksrt-identity-medal` rule + nowrap name
  rows in base.css (loaded on all routes).
- Header dropdown: append the level icon next to the display name (was a
  separate badges row); constrain the name to ellipsis so the badge never wraps.
- User center profile header & author hero: render the level icon inside the
  <h1> name row; drop the duplicate level chip from the signals row.
- Single article author bar / after-box author card: nowrap the name row and
  size the level icon to 1.2em (was unstyled → intrinsic PNG size).
- Comments: inner <name> nowrap with a name clamp; medal sized to 1em.
- Consolidate the per-route `.aksrt-user-level-icon` duplicates in
  user-center/level.css and profile/layout.css onto the shared rule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 18:13:41 +08:00

381 lines
17 KiB
PHP

<?php
/**
* Desktop navbar pieces (zibll navbar header / menu / buttons).
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
if (!function_exists('aksrt_menu_items')) :
/**
* PC top menu (zibll zib_menu_items).
*
* @param string $location Menu location.
* @param bool $echo Echo or return.
* @return string|void
*/
function aksrt_menu_items(string $location = 'primary', bool $echo = true) {
$walker = class_exists('\AKSRT\Frontend\NavWalker')
? new \AKSRT\Frontend\NavWalker(
function_exists('aksrt_get_menu_decoration_settings')
? aksrt_get_menu_decoration_settings()
: []
)
: null;
$args = [
'container' => false,
'echo' => false,
'fallback_cb' => false,
'items_wrap' => '<ul class="nav navbar-nav">%3$s</ul>',
'theme_location' => $location,
];
if ($walker) {
$args['walker'] = $walker;
}
if (!wp_is_mobile()) {
$args['depth'] = 0;
} else {
$args['depth'] = 3;
}
$menu = '';
if (has_nav_menu($location)) {
$menu = (string) wp_nav_menu($args);
}
if ($menu === '' && is_super_admin()) {
$menu = '<ul class="nav navbar-nav"><li><a href="' . esc_url(admin_url('nav-menus.php')) . '">'
. esc_html__('添加导航菜单', 'aksrt') . '</a></li></ul>';
}
if ($echo) {
echo $menu; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return;
}
return $menu;
}
endif;
if (!function_exists('aksrt_navbar_header')) :
/**
* Logo + mobile toggles (zibll zib_navbar_header).
*
* @param array<string,mixed> $ctx
*/
function aksrt_navbar_header(array $ctx): void {
$brand_name = (string) ($ctx['brand_name'] ?? get_bloginfo('name'));
$logo_inner = !empty($ctx['logo_html'])
? $ctx['logo_html']
: '<span class="navbar-logo-text">' . esc_html($brand_name) . '</span>';
$logo = '<a class="navbar-logo" href="' . esc_url(home_url('/')) . '">' . $logo_inner . '</a>';
$button = '<button type="button" data-toggle-class="mobile-navbar-show" data-target="body" class="navbar-toggle" aria-label="'
. esc_attr__('打开菜单', 'aksrt') . '">';
$button .= '<i class="em12 css-icon i-menu"><i></i></i></button>';
if (($ctx['mobile_layout'] ?? 'center') === 'center' && !empty($ctx['search_enabled'])) {
$button .= '<button type="button" class="navbar-toggle main-search-btn nav-search-btn" data-toggle-class="show" data-target=".navbar-search" aria-label="'
. esc_attr__('打开搜索', 'aksrt') . '">';
$button .= '<i class="fas fa-search" aria-hidden="true"></i></button>';
}
echo '<div class="navbar-header">';
echo '<div class="navbar-brand">' . $logo . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $button; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '</div>';
}
endif;
if (!function_exists('aksrt_get_menu_search')) :
/**
* Layout-2 inline search form (zibll zib_get_menu_search simplified).
*
* @param array<string,mixed> $ctx
*/
function aksrt_get_menu_search(array $ctx): void {
if (is_search()) {
return;
}
$placeholder = (string) ($ctx['search_placeholder'] ?? __('搜索内容', 'aksrt'));
// zibll: form + input + remote-box (action=menu_search).
echo '<form method="get" class="navbar-form navbar-left hover-show" action="' . esc_url(home_url('/')) . '" role="search" data-aksrt-search-form="inline">';
echo '<div class="form-group relative dropdown">';
echo '<input type="text" class="form-control search-input focus-show" name="s" value="'
. esc_attr(get_search_query()) . '" placeholder="' . esc_attr($placeholder) . '" autocomplete="off">';
echo '<div class="abs-right muted-3-color"><button type="submit" class="null" aria-label="'
. esc_attr__('搜索', 'aksrt') . '"><i class="fas fa-search" aria-hidden="true"></i></button></div>';
if (function_exists('aksrt_get_remote_box')) {
echo aksrt_get_remote_box([ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'class' => '',
'loader' => ' ',
'query' => [
'action' => 'menu_search',
],
]);
} else {
$remote_url = add_query_arg(['action' => 'menu_search'], admin_url('admin-ajax.php'));
echo '<div class="lazyload" remote-box="' . esc_url($remote_url) . '" lazyload-action="ias">';
echo '<i class="loading-spot"><i></i></i></div>';
}
echo '</div></form>';
}
endif;
if (!function_exists('aksrt_header_user_box_html')) :
/**
* User dropdown inner content for nav sub-menu (logged-in / guest).
*
* @param array<string,mixed> $ctx
*/
function aksrt_header_user_box_html(array $ctx): string {
static $html = null;
if ($html !== null) {
return $html;
}
if (empty($ctx['user_menu'])) {
$html = '';
return $html;
}
$user_id = (int) ($ctx['user_id'] ?? 0);
$con = '';
if ($user_id > 0) {
$user = $ctx['current_user'] instanceof WP_User ? $ctx['current_user'] : wp_get_current_user();
$display_name = $user->display_name ?: $user->user_login;
$avatar = get_avatar($user_id, 44, '', esc_attr($display_name), ['loading' => 'lazy']);
$desc = (string) ($ctx['user_desc'] ?? '');
$vip_badge = function_exists('aksrt_render_vip_badge') ? aksrt_render_vip_badge($user_id, 'sm') : '';
// Level icon rides inline to the right of the display name (zibll parity).
$level_badge = (class_exists('\AKSRT\User\Level') && method_exists('\AKSRT\User\Level', 'get_badge_html'))
? \AKSRT\User\Level::get_badge_html($user_id, 'aksrt-user-level-icon--header')
: '';
$payvip = function_exists('aksrt_header_payvip_bar') ? aksrt_header_payvip_bar($user_id) : '';
// nav_user_pay_vip gate: empty payvip when switch off for non-VIP CTA only — helper already handles VIP status.
if (empty($ctx['nav_user_pay_vip']) && $payvip !== '' && strpos($payvip, 'is-vip') === false) {
$payvip = '';
}
$checkin = function_exists('aksrt_header_user_checkin_btn') ? aksrt_header_user_checkin_btn($user_id) : '';
$wallet = function_exists('aksrt_header_user_wallet_mini') ? aksrt_header_user_wallet_mini($user_id) : '';
$badges = function_exists('aksrt_header_user_badges_row') ? aksrt_header_user_badges_row($user_id) : '';
$unread = (int) ($ctx['unread_count'] ?? 0);
$msg_url = (string) ($ctx['messages_url'] ?? '');
$msg_icon = '';
if ($msg_url !== '') {
$msg_icon = '<a class="abs-right" href="' . esc_url($msg_url) . '" title="' . esc_attr__('消息中心', 'aksrt') . '">';
$msg_icon .= '<i class="fas fa-bell" aria-hidden="true"></i>';
if ($unread > 0) {
$msg_icon .= '<span class="aksrt-notification-badge">' . esc_html((string) min(99, $unread)) . '</span>';
}
$msg_icon .= '</a>';
}
$href = '<div class="flex jsa header-user-href">';
$href .= '<a rel="nofollow" href="' . esc_url(function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url() : home_url('/')) . '">';
$href .= '<div class="badg mb6 toggle-radius c-blue"><i class="fas fa-user" aria-hidden="true"></i></div>';
$href .= '<div class="c-blue">' . esc_html__('用户中心', 'aksrt') . '</div></a>';
$orders = (string) ($ctx['orders_url'] ?? '');
if ($orders !== '') {
$href .= '<a rel="nofollow" href="' . esc_url($orders) . '">';
$href .= '<div class="badg mb6 toggle-radius c-purple"><i class="fas fa-shopping-bag" aria-hidden="true"></i></div>';
$href .= '<div class="c-purple">' . esc_html__('我的订单', 'aksrt') . '</div></a>';
}
if (!empty($ctx['can_publish'])) {
$all = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : [];
$pub_text = (string) ($all['header_user_newposts_text'] ?? '');
if ($pub_text === '') {
$pub_text = __('发布内容', 'aksrt');
}
$pub_type = sanitize_key((string) ($all['header_user_newposts_btn'] ?? 'post'));
$pub_url = (string) $ctx['publish_url'];
if ($pub_type === 'topic' && function_exists('aksrt_get_forum_compose_url')) {
$u = aksrt_get_forum_compose_url();
if ($u) { $pub_url = $u; }
} elseif ($pub_type === 'product') {
if (function_exists('aksrt_get_product_edit_url')) {
$u = (string) aksrt_get_product_edit_url();
if ($u !== '') { $pub_url = $u; }
} else {
$pub_url = admin_url('post-new.php?post_type=aksrt_product');
}
}
$href .= '<a rel="nofollow" href="' . esc_url($pub_url) . '">';
$href .= '<div class="badg mb6 toggle-radius c-green"><i class="fas fa-pen" aria-hidden="true"></i></div>';
$href .= '<div class="c-green">' . esc_html($pub_text) . '</div></a>';
}
$href .= '<a href="javascript:;" data-toggle="modal" data-target="#modal_signout">';
$href .= '<div class="badg mb6 toggle-radius c-red"><i class="fas fa-sign-out-alt" aria-hidden="true"></i></div>';
$href .= '<div class="c-red">' . esc_html__('退出登录', 'aksrt') . '</div></a>';
$href .= '</div>';
if (is_super_admin()) {
$href .= '<div class="flex jsa header-user-href">';
$href .= '<a rel="nofollow" target="_blank" href="' . esc_url((string) $ctx['theme_settings_url']) . '">';
$href .= '<div class="badg mb6 toggle-radius c-yellow"><i class="fas fa-sliders-h" aria-hidden="true"></i></div>';
$href .= '<div class="c-yellow">' . esc_html__('主题设置', 'aksrt') . '</div></a>';
// zibll: 模块布局 → widgets.
$href .= '<a rel="nofollow" target="_blank" href="' . esc_url(admin_url('widgets.php')) . '">';
$href .= '<div class="badg mb6 toggle-radius c-yellow"><i class="fas fa-chart-pie" aria-hidden="true"></i></div>';
$href .= '<div class="c-yellow">' . esc_html__('模块布局', 'aksrt') . '</div></a>';
// zibll: 商城中心.
$shop_url = post_type_exists('aksrt_product')
? admin_url('edit.php?post_type=aksrt_product')
: admin_url('admin.php?page=aksrt_pay');
$href .= '<a rel="nofollow" target="_blank" href="' . esc_url($shop_url) . '">';
$href .= '<div class="badg mb6 toggle-radius c-yellow"><i class="fas fa-shopping-bag" aria-hidden="true"></i></div>';
$href .= '<div class="c-yellow">' . esc_html__('商城中心', 'aksrt') . '</div></a>';
$href .= '<a rel="nofollow" target="_blank" href="' . esc_url(admin_url()) . '">';
$href .= '<div class="badg mb6 toggle-radius c-yellow"><i class="fas fa-cog" aria-hidden="true"></i></div>';
$href .= '<div class="c-yellow">' . esc_html__('后台管理', 'aksrt') . '</div></a>';
$href .= '</div>';
}
$con = '<div class="user-info flex ac relative">';
$con .= $avatar;
$con .= '<div class="user-right flex flex1 ac jsb ml10">';
$con .= '<div class="flex1" style="max-width: calc(100% - 40px);"><b>'
. '<span class="display-name text-ellipsis">' . esc_html($display_name) . '</span>' . $vip_badge . $level_badge
. '</b><div class="px12 muted-2-color text-ellipsis">' . esc_html($desc) . '</div></div>';
$con .= '</div>';
$con .= $msg_icon;
$con .= '</div>';
$con .= $payvip ? $payvip : '';
$con .= $checkin ? '<div class="mt6 em09" style="padding:2px;">' . $checkin . '</div>' : '';
$con .= $wallet ? '<div class="mt6" style="padding:2px;">' . $wallet . '</div>' : '';
$con .= $badges;
$con .= '<div class="relative opacity5"><i class="line-form-line"></i></div>';
$con .= '<div class="mt10 text-center">' . $href . '</div>';
} else {
$vip_guest = '';
if (!empty($ctx['nav_user_pay_vip']) && class_exists('\AKSRT\Settings\Settings')) {
$vs = \AKSRT\Settings\Settings::get_payment_vip_settings();
if (!empty($vs['enabled'])) {
$vip_guest = '<div><a class="em09 signin-loader but jb-red radius4 payvip-icon btn-block mt10" href="javascript:;">'
. '<i class="fas fa-gem" aria-hidden="true"></i> '
. esc_html__('开通会员 尊享会员权益', 'aksrt') . '</a></div>';
}
}
$href = $vip_guest;
$href .= '<div class="flex jsa header-user-href">';
$href .= '<a href="javascript:;" class="signin-loader"><div class="badg mb6 toggle-radius c-blue"><i class="fas fa-sign-in-alt" aria-hidden="true"></i></div><div class="c-blue">'
. esc_html__('登录', 'aksrt') . '</div></a>';
if (get_option('users_can_register')) {
$href .= '<a href="javascript:;" class="signup-loader"><div class="badg mb6 toggle-radius c-green"><i class="fas fa-user-plus" aria-hidden="true"></i></div><div class="c-green">'
. esc_html__('注册', 'aksrt') . '</div></a>';
}
$href .= '<a rel="nofollow" href="' . esc_url(wp_lostpassword_url(home_url('/'))) . '"><div class="badg mb6 toggle-radius c-purple"><i class="fas fa-key" aria-hidden="true"></i></div><div class="c-purple">'
. esc_html__('找回密码', 'aksrt') . '</div></a>';
$href .= '</div>';
$con = '<div class="text-center">' . $href . '</div>';
if (class_exists('\AKSRT\Auth\OAuth') && method_exists(\AKSRT\Auth\OAuth::get_instance(), 'render_social_buttons')) {
$oauth_html = (string) \AKSRT\Auth\OAuth::get_instance()->render_social_buttons(__('快速登录', 'aksrt'));
if ($oauth_html !== '') {
$con .= '<p class="social-separator separator muted-3-color em09 mt10">' . esc_html__('快速登录', 'aksrt') . '</p>';
$con .= '<div class="social_loginbar">' . $oauth_html . '</div>';
}
}
}
$html = '<div class="sub-user-box">' . $con . '</div>';
return $html;
}
endif;
if (!function_exists('aksrt_menu_button')) :
/**
* Right cluster: fold, publish/VIP, theme, user, search (zibll zib_menu_button).
*
* @param array<string,mixed> $ctx
*/
function aksrt_menu_button(array $ctx): void {
$layout = (string) ($ctx['layout'] ?? '1');
$user_id = (int) ($ctx['user_id'] ?? 0);
$button = '';
// Publish + VIP (respect nav_pay_vip).
if (function_exists('aksrt_render_header_menu_buttons')) {
$button .= aksrt_render_header_menu_buttons();
}
// Theme toggle.
$radius_but = '';
if (in_array('pc_nav', (array) ($ctx['theme_positions'] ?? []), true)) {
$radius_but .= '<a href="javascript:;" class="toggle-theme toggle-radius" id="theme-toggle-desktop" title="'
. esc_attr__('切换亮暗色', 'aksrt') . '">';
$radius_but .= '<i class="fas fa-moon theme-icon-moon" aria-hidden="true"></i>';
$radius_but .= '<i class="fas fa-sun theme-icon-sun" aria-hidden="true"></i>';
$radius_but .= '</a>';
}
if (function_exists('aksrt_get_translate_language_btn')) {
$radius_but .= aksrt_get_translate_language_btn('ml10 pull-right');
}
if ($radius_but !== '') {
$button .= '<div class="navbar-form navbar-right flex ac">' . $radius_but . '</div>';
}
// User + search.
$sign_but = '';
$user_sub = '';
if (!empty($ctx['user_menu'])) {
$user_sub = '<div class="padding-10">' . aksrt_header_user_box_html($ctx) . '</div>';
if ('2' === $layout) {
if ($user_id > 0) {
$avatar = get_avatar($user_id, 36, '', '', ['loading' => 'lazy', 'class' => 'avatar']);
$sign_but = '<li><a href="javascript:;" class="navbar-avatar">' . $avatar . '</a><ul class="sub-menu">'
. $user_sub . '</ul></li>';
} else {
$sign_but = '<li><a href="javascript:;" class="signin-loader">' . esc_html__('登录', 'aksrt') . '</a></li>';
if (get_option('users_can_register')) {
$sign_but .= '<li><a href="javascript:;" class="signup-loader">' . esc_html__('注册', 'aksrt') . '</a></li>';
}
}
} else {
$trigger = $user_id > 0
? get_avatar($user_id, 36, '', '', ['loading' => 'lazy'])
: '<i class="fas fa-user" aria-hidden="true"></i>';
$cls = $user_id > 0 ? 'navbar-avatar' : 'btn signin-loader';
$dot = ($user_id > 0 && (int) ($ctx['unread_count'] ?? 0) > 0)
? '<span class="nav-user__dot" aria-hidden="true"></span>'
: '';
$sign_but = '<li class="relative"><a href="javascript:;" class="' . esc_attr($cls) . '">'
. $trigger . $dot . '</a><ul class="sub-menu">' . $user_sub . '</ul></li>';
}
}
$search_but = '';
if ('2' !== $layout && !empty($ctx['search_enabled'])) {
$search_but = '<li class="relative"><a href="javascript:;" class="btn nav-search-btn main-search-btn" data-toggle-class="show" data-target=".navbar-search" aria-label="'
. esc_attr__('打开搜索', 'aksrt') . '"><i class="fas fa-search" aria-hidden="true"></i></a></li>';
}
$right_but = '<div class="navbar-form navbar-right'
. ($user_id <= 0 && '2' === $layout ? ' navbar-text' : '') . '">';
$right_but .= '<ul class="list-inline splitters relative">'
. $sign_but . $search_but . '</ul></div>';
$fold = '<div class="navbar-form navbar-right hide show-nav-but" style="margin-right:-10px;">'
. '<a data-toggle-class data-target=".nav.navbar-nav" href="javascript:;" class="but" aria-label="'
. esc_attr__('展开菜单', 'aksrt') . '"><i class="fas fa-bars" aria-hidden="true"></i></a></div>';
$html = $fold;
if ('3' === $layout) {
$html .= $right_but . $button;
} else {
$html .= $button . $right_but;
}
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
endif;