fix(user): align badge entry with medal settings

This commit is contained in:
2026-08-11 19:32:52 +08:00
parent e1dc9ab277
commit 51b52affae
7 changed files with 143 additions and 25 deletions
+57 -1
View File
@@ -7,7 +7,63 @@
* 加载条件与 user-badges.js 一致(badge surface 页面)。
* ============================================================ */
/* ---------- 分类分组(zibll .medal-cat-box / .medal-cat-name) ---------- */
/* ---------- 顶部资料卡入口(zibll user-medal-info parity) ---------- */
.aksrt-medal-entry {
display: inline-flex;
align-items: center;
gap: 4px;
min-width: 0;
margin: 2px 8px 2px 0;
padding: 2px 0;
border: 0;
background: transparent;
color: var(--muted-2-color, var(--color-ink-4));
font: inherit;
font-size: 12px;
line-height: 1.4;
cursor: pointer;
white-space: nowrap;
transition: color .15s ease, opacity .15s ease;
}
.aksrt-medal-entry:hover,
.aksrt-medal-entry:focus-visible {
color: var(--focus-color);
opacity: 1;
}
.aksrt-medal-entry:focus-visible {
outline: 2px solid var(--focus-color);
outline-offset: 2px;
border-radius: var(--mini-radius, 6px);
}
.aksrt-medal-entry__icons {
display: inline-flex;
align-items: center;
flex: none;
min-width: 0;
}
.aksrt-medal-entry__icon {
width: auto;
height: 1.45em;
max-width: 2.2em;
margin: 0 1px;
object-fit: contain;
vertical-align: middle;
}
.aksrt-medal-entry__icon + .aksrt-medal-entry__icon { margin-left: -1px; }
.aksrt-medal-entry__fallback {
display: inline-flex;
align-items: center;
justify-content: center;
width: 1.6em;
height: 1.6em;
color: var(--focus-color);
background: var(--focus-color-opacity1);
border-radius: 50%;
}
.aksrt-medal-entry__count { margin-left: 2px; }
.aksrt-medal-entry__arrow { margin-left: 1px; font-size: .9em; }
.medal-cat-box {
position: relative;
text-align: center;
+1 -2
View File
@@ -738,8 +738,7 @@
}
.aksrt-author-card__badge img { border-radius: 50%; }
/* 徽章远程弹窗样式已迁至 assets/css/frontend/medal.css(zibll medal 风格)。 */
.aksrt-author-hero__badges { margin-top: 8px; }
/* 徽章入口与弹窗样式统一由 assets/css/frontend/medal.css 提供。 */
/* Forum tab topics (batch6) */
.aksrt-ftab__nav {
+57
View File
@@ -234,6 +234,63 @@ if (!function_exists('aksrt_get_user_name')) {
}
}
if (!function_exists('aksrt_render_user_medal_entry')) {
/**
* Render the compact Zibll-style badge overview entry.
*
* The configured limit controls preview icons only; the count remains
* the complete owned badge count returned by the privacy-aware DTO.
*
* @param int $user_id
* @param string $context user-center|user-home
* @param bool $can_wear Whether the viewer may initially wear a badge.
*/
function aksrt_render_user_medal_entry(int $user_id, string $context = 'user-home', bool $can_wear = false): string {
if ($user_id <= 0 || !class_exists('AKSRT\\User\\Badges') || !class_exists('AKSRT\\Settings\\Settings')) {
return '';
}
$settings = \AKSRT\Settings\Settings::get_user_identity_settings();
$medal = is_array($settings['medal'] ?? null) ? $settings['medal'] : [];
if (empty($medal['enabled']) || ($context === 'user-home' && empty($medal['public_display']))) {
return '';
}
$data = \AKSRT\User\Badges::list_for_user($user_id);
if (empty($data['enabled']) || ($context === 'user-home' && empty($data['public_display']))) {
return '';
}
$limit = max(1, min(8, (int) ($medal['limit'] ?? 3)));
$icons = [];
foreach ((array) ($data['categories'] ?? []) as $category) {
foreach ((array) ($category['items'] ?? []) as $item) {
if (empty($item['owned'])) {
continue;
}
$icon_url = esc_url((string) ($item['icon_url'] ?? ''));
if ($icon_url === '') {
continue;
}
$label = sanitize_text_field((string) ($item['label'] ?? __('徽章', 'aksrt')));
$icons[] = '<img class="img-icon medal-icon aksrt-medal-entry__icon" src="' . $icon_url . '" alt="' . esc_attr($label) . '" title="' . esc_attr($label) . '" loading="lazy" decoding="async" />';
if (count($icons) >= $limit) {
break 2;
}
}
}
$count = max(0, (int) ($data['count'] ?? 0));
$label = $count > 0 ? sprintf(_n('%d枚徽章', '%d枚徽章', $count, 'aksrt'), $count) : __('获取徽章', 'aksrt');
$icons_html = implode('', $icons);
if ($icons_html === '') {
$icons_html = '<span class="aksrt-medal-entry__fallback" aria-hidden="true"><i class="fas fa-award"></i></span>';
}
$classes = 'aksrt-medal-entry aksrt-medal-entry--' . sanitize_html_class($context);
return '<button type="button" class="' . esc_attr($classes) . '" data-aksrt-badges-open data-aksrt-badges-user-id="' . (int) $user_id . '" data-aksrt-badges-can-wear="' . ($can_wear ? '1' : '0') . '" aria-label="' . esc_attr($label) . '"><span class="aksrt-medal-entry__icons">' . $icons_html . '</span><span class="aksrt-medal-entry__count">' . esc_html($label) . '</span><i class="fas fa-angle-right aksrt-medal-entry__arrow" aria-hidden="true"></i></button>';
}
}
if (!function_exists('aksrt_user_has_visible_badges')) {
/**
* Whether a user owns at least one badge that exists in the current catalog.
@@ -41,6 +41,9 @@ extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_
<?php endif; ?>
</h1>
<div class="uc-header__meta desc user-identity flex ac hh">
<?php if (function_exists('aksrt_render_user_medal_entry')) : ?>
<?php echo aksrt_render_user_medal_entry((int) $user_id, 'user-center', true); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif; ?>
<span class="uc-header__meta-item" title="<?php esc_attr_e('用户名', 'aksrt'); ?>">
<i class="fas fa-user" aria-hidden="true"></i> <?php echo esc_html($target_user->user_login); ?>
</span>
@@ -61,12 +64,6 @@ extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_
</div>
</div>
<div class="uc-header__actions">
<?php if (class_exists('\AKSRT\User\Badges') && \AKSRT\User\Badges::enabled()) : ?>
<button type="button" class="btn-ghost btn-sm" data-aksrt-badges-open data-aksrt-badges-user-id="<?php echo (int) $user_id; ?>" data-aksrt-badges-can-wear="1">
<i class="fas fa-award" aria-hidden="true"></i> <?php esc_html_e('我的徽章', 'aksrt'); ?>
</button>
<?php endif; ?>
<?php if (!empty($messages_url)) : ?>
<a href="<?php echo esc_url($messages_url); ?>" class="btn-ghost btn-sm">
<i class="fas fa-bell" aria-hidden="true"></i> <?php esc_html_e('消息通知', 'aksrt'); ?>
+16 -3
View File
@@ -168,7 +168,7 @@ class Badges {
return false;
}
$settings = \AKSRT\Settings\Settings::get_user_identity_settings();
return !empty($settings['badge']['enabled']);
return !empty($settings['medal']['enabled']);
}
public static function public_display(): bool {
@@ -176,7 +176,7 @@ class Badges {
return false;
}
$settings = \AKSRT\Settings\Settings::get_user_identity_settings();
return !empty($settings['badge']['enabled']) && !empty($settings['badge']['public_display']);
return !empty($settings['medal']['enabled']) && !empty($settings['medal']['public_display']);
}
/**
@@ -190,7 +190,20 @@ class Badges {
return self::$dictionary_cache = [];
}
$settings = \AKSRT\Settings\Settings::get_user_identity_settings();
$dict = is_array($settings['badge']['dictionary'] ?? null) ? $settings['badge']['dictionary'] : [];
$medal = is_array($settings['medal'] ?? null) ? $settings['medal'] : [];
$dict = [];
foreach ((array) ($medal['items_by_slug'] ?? []) as $item) {
if (!is_array($item)) {
continue;
}
$dict[] = [
'slug' => sanitize_key((string) ($item['slug'] ?? '')),
'label' => (string) ($item['name'] ?? $item['slug'] ?? ''),
'tone' => 'neutral',
'desc' => (string) ($item['desc'] ?? ''),
'icon_url' => (string) ($item['icon'] ?? ''),
];
}
$out = [];
foreach ($dict as $item) {
$slug = sanitize_key((string) ($item['slug'] ?? ''));
+2 -2
View File
@@ -161,8 +161,8 @@ class Home {
}
}
if (!empty($settings['badge']['enabled']) && !empty($settings['badge']['public_display'])) {
$limit = max(1, (int) ($settings['badge']['limit'] ?? 3));
if (!empty($settings['medal']['enabled']) && !empty($settings['medal']['public_display'])) {
$limit = max(1, (int) ($settings['medal']['limit'] ?? 3));
if (class_exists('AKSRT\\User\\Badges')) {
$list = Badges::list_for_user($user_id);
$badge_count = 0;
+6 -10
View File
@@ -100,22 +100,18 @@ $pm_url = ($pm_enabled && $messages_url !== '')
</h1>
<?php endif; ?>
<?php if (class_exists('\AKSRT\User\Badges') && \AKSRT\User\Badges::enabled() && \AKSRT\User\Badges::public_display()) : ?>
<div class="aksrt-author-hero__badges">
<button type="button" class="but but-sm jb-blue mt6" data-aksrt-badges-open data-aksrt-badges-user-id="<?php echo (int) $user_id; ?>" data-aksrt-badges-can-wear="<?php echo $is_own ? '1' : '0'; ?>">
<i class="fas fa-award" aria-hidden="true"></i> <?php esc_html_e('徽章明细', 'aksrt'); ?>
</button>
</div>
<?php endif; ?>
<?php
// IP 归属地 (zibll author_header_identity / user_city_s).
// Zibll parity: the badge overview leads the identity row.
$city_badge = function_exists('aksrt_get_user_city_badge_html')
? aksrt_get_user_city_badge_html($user_id, 'user_home', 'badg')
: '';
$medal_entry = function_exists('aksrt_render_user_medal_entry')
? aksrt_render_user_medal_entry($user_id, 'user-home', $is_own)
: '';
?>
<?php if ($city_badge !== '') : ?>
<?php if ($medal_entry !== '' || $city_badge !== '') : ?>
<div class="aksrt-author-hero__identity user-identity flex ac hh">
<?php echo $medal_entry; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php echo $city_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<?php endif; ?>