feat(badges): zibll-style badge modal with category grid, detail view, and wear UX

This commit is contained in:
kate522 committed 2026-08-04 12:46:44 +08:00
1 parent 4849869289
commit ed19937e99
9 files changed
+904 -246

No files matched your search

+352
View File
@@ -0,0 +1,352 @@
/* ============================================================
* 徽章弹窗(zibll medal 1:1 风格)
* ------------------------------------------------------------
* 共享于两个入口:
* 1. 用户中心「我的徽章」弹窗(.uc-badges-modal,JS 渲染)
* 2. 远程弹窗徽章明细/单卡(.medal-modal,PHP 渲染)
* 加载条件与 user-badges.js 一致(badge surface 页面)。
* ============================================================ */
/* ---------- 分类分组(zibll .medal-cat-box / .medal-cat-name) ---------- */
.medal-cat-box {
position: relative;
text-align: center;
padding: 8px 8px 4px;
margin-bottom: 16px;
background: var(--muted-border-color, rgba(136, 136, 136, 0.06));
border-radius: var(--main-radius, 8px);
}
.medal-cat-box:last-child { margin-bottom: 0; }
.medal-cat-head { margin-bottom: 6px; }
.medal-cat-name {
display: inline-block;
margin-top: -8px;
padding: 6px 22px;
font-size: 13px;
font-weight: 600;
color: var(--focus-color);
background: var(--focus-color-opacity1);
border-radius: 0 0 1000px 1000px;
}
/* ---------- 卡片网格(zibll .medal-list-box / .medal-list) ---------- */
.medal-list-box {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 8px 4px;
}
.medal-list {
display: block;
min-width: 0;
padding: 0;
border: 0;
background: none;
font: inherit;
color: inherit;
cursor: pointer;
-webkit-appearance: none;
appearance: none;
}
.medal-card {
position: relative;
text-align: center;
padding: 8px 4px 6px;
border-radius: var(--mini-radius, 6px);
transition: transform 0.15s ease, background-color 0.15s ease;
}
.medal-list:hover .medal-card,
.medal-list:focus-visible .medal-card {
transform: translateY(-2px);
background: var(--focus-color-opacity1);
}
.medal-list:focus-visible { outline: 2px solid var(--focus-color); outline-offset: 2px; border-radius: var(--mini-radius, 6px); }
.medal-card .medal-icon {
width: auto;
height: 70px;
max-width: 100%;
object-fit: contain;
}
.medal-card .medal-icon--fa {
display: inline-flex;
align-items: center;
justify-content: center;
width: 56px;
height: 70px;
font-size: 34px;
color: var(--focus-color);
}
/* 未获得:灰度 + 透明(zibll .no-has) */
.medal-card.no-has {
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
opacity: 0.6;
}
.medal-card__name {
margin-top: 6px;
font-size: 12px;
line-height: 1.4;
color: var(--muted-color, var(--color-ink-3));
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.medal-card.is-has .medal-card__name { color: var(--key-color, var(--color-ink)); }
.medal-card__wear {
margin-top: 2px;
font-size: 11px;
line-height: 1.3;
color: var(--focus-color);
}
/* ---------- 单徽章详情卡(zibll .medal-single-card) ---------- */
.medal-single-card {
position: relative;
overflow: hidden;
text-align: center;
padding: 22px 14px 18px;
margin-bottom: 14px;
background: var(--muted-border-color, rgba(136, 136, 136, 0.06));
border-radius: var(--main-radius, 8px);
}
.medal-single-bg {
position: absolute;
top: 43px;
left: 0;
right: 0;
bottom: 0;
opacity: 0.5;
pointer-events: none;
}
.medal-single-bg img {
width: 100%;
height: 100%;
object-fit: cover;
}
.medal-single-main { position: relative; }
.medal-single-card .medal-icon {
width: auto;
height: 120px;
max-width: 200px;
object-fit: contain;
}
.medal-single-card .medal-icon--fa {
display: inline-flex;
align-items: center;
justify-content: center;
height: 120px;
font-size: 72px;
color: var(--focus-color);
}
.medal-single-name {
margin-top: 12px;
font-size: 16px;
font-weight: 700;
}
.medal-single-desc {
margin-top: 8px;
font-size: 12px;
line-height: 1.6;
}
.medal-single-state {
margin-top: 10px;
font-size: 12px;
}
/* ---------- 弹窗骨架(远程弹窗 .medal-modal) ---------- */
.medal-modal { position: relative; padding: 6px 4px 12px; }
.medal-modal__empty {
padding: 24px 0;
text-align: center;
color: var(--muted-2-color, var(--color-ink-4));
}
.medal-modal__head {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 12px;
padding-right: 28px;
}
.medal-modal__avatar {
width: 42px;
height: 42px;
border-radius: 50%;
object-fit: cover;
box-shadow: 0 0 0 2px var(--focus-color-opacity1);
}
.medal-modal__title {
display: flex;
align-items: center;
flex-wrap: wrap;
min-width: 0;
font-size: 15px;
font-weight: 700;
}
.medal-modal__title .medal-icon {
width: auto;
height: 1.6em;
margin-left: 4px;
object-fit: contain;
}
.medal-modal__scroll {
max-height: 50vh;
max-height: 50dvh;
overflow-y: auto;
padding: 2px;
}
/* 底部操作区(zibll .modal-buts.but-average 语义) */
.medal-modal__buts {
display: flex;
margin: 12px -4px -12px;
border-top: 1px solid var(--main-border-color, var(--color-line));
}
.medal-modal__buts > .but {
flex: 1;
padding: 0.7em;
background: transparent;
border-radius: 0;
}
.medal-modal__buts > .but + .but { border-left: 1px solid var(--main-border-color, var(--color-line)); }
.medal-modal__buts > .but:hover { background: var(--focus-color-opacity05); opacity: 1; }
/* 单卡弹窗 */
.medal-modal--single { padding: 10px 8px 14px; text-align: center; }
.medal-modal__actions { margin-top: 4px; }
.medal-modal__actions .but.btn-block,
.medal-modal__actions .badg.btn-block { display: block; width: 100%; }
.medal-modal__back {
display: inline-block;
margin-top: 12px;
text-decoration: none;
}
.medal-modal__back:hover { color: var(--focus-color); }
/* ---------- 用户中心「我的徽章」弹窗 ---------- */
.uc-badges-dialog {
max-width: 560px;
padding: 0;
overflow: hidden;
}
.uc-badges-head {
display: flex;
align-items: center;
gap: 10px;
padding: 16px 46px 12px 18px;
border-bottom: 1px solid var(--main-border-color, var(--color-line));
}
.uc-badges-head__icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 34px;
height: 34px;
flex: none;
font-size: 16px;
color: var(--focus-color);
background: var(--focus-color-opacity1);
border-radius: 10px;
}
.uc-badges-head__title {
flex: 1;
min-width: 0;
font-size: 16px;
font-weight: 700;
}
.uc-badges-head__count {
margin-left: 6px;
font-size: 12px;
font-weight: 400;
color: var(--muted-2-color, var(--color-ink-4));
}
.uc-badges-head__wear { flex: none; }
.uc-badges-head__wear-tag {
display: inline-flex;
align-items: center;
gap: 5px;
max-width: 140px;
padding: 3px 10px;
font-size: 12px;
color: var(--focus-color);
background: var(--focus-color-opacity1);
border-radius: 100px;
}
.uc-badges-head__wear-tag .medal-icon {
width: auto;
height: 18px;
object-fit: contain;
}
.uc-badges-head__wear-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.uc-badges-body {
max-height: 56vh;
max-height: 56dvh;
overflow-y: auto;
padding: 16px 14px 18px;
}
.uc-badges-loading,
.uc-badges-error {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
padding: 32px 0;
}
/* 详情视图 */
.uc-badges-view--detail { padding: 8px 16px 18px; }
.uc-badges-detail__head { margin-bottom: 6px; }
.uc-badges-back {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 10px 4px 6px;
border: 0;
background: none;
font: inherit;
font-size: 13px;
color: var(--muted-2-color, var(--color-ink-4));
cursor: pointer;
border-radius: var(--mini-radius, 6px);
transition: color 0.15s ease, background-color 0.15s ease;
}
.uc-badges-back:hover {
color: var(--focus-color);
background: var(--focus-color-opacity1);
}
/* ---------- 细滚动条(zibll .mini-scrollbar) ---------- */
.mini-scrollbar { scrollbar-width: thin; scrollbar-color: var(--muted-border-color, rgba(136, 136, 136, 0.3)) transparent; }
.mini-scrollbar::-webkit-scrollbar { width: 5px; height: 5px; }
.mini-scrollbar::-webkit-scrollbar-thumb { background: var(--muted-border-color, rgba(136, 136, 136, 0.3)); border-radius: 10px; }
.mini-scrollbar::-webkit-scrollbar-track { background: transparent; }
/* ---------- 响应式(zibll: 桌面 5 列 / 移动 4 列) ---------- */
@media (max-width: 768px) {
.medal-list-box { grid-template-columns: repeat(4, 1fr); }
.medal-card .medal-icon { height: 55px; }
.medal-card .medal-icon--fa { width: 44px; height: 55px; font-size: 26px; }
.medal-single-card .medal-icon { height: 96px; }
.uc-badges-dialog { max-width: 100%; }
.uc-badges-body { max-height: 62vh; max-height: 62dvh; }
}
@media (max-width: 400px) {
.medal-list-box { grid-template-columns: repeat(3, 1fr); }
}
+1 -43
View File
@@ -738,49 +738,7 @@
}
.aksrt-author-card__badge img { border-radius: 50%; }
/* Badge remote modal (batch6) */
.aksrt-badge-modal { padding: 8px 4px 12px; }
.aksrt-badge-modal__head {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 12px;
position: relative;
padding-right: 28px;
}
.aksrt-badge-modal__avatar {
width: 40px; height: 40px; border-radius: 50%; object-fit: cover;
}
.aksrt-badge-modal__title { font-weight: 700; font-size: 15px; flex: 1; min-width: 0; }
.aksrt-badge-modal__wear-tag {
display: inline-block;
margin-left: 6px;
font-size: 12px;
font-weight: 500;
color: var(--color-primary, #3b82f6);
}
.aksrt-badge-modal__list { max-height: 50vh; overflow: auto; }
.aksrt-badge-modal__item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 4px;
border-bottom: 1px solid var(--color-line, #eee);
}
.aksrt-badge-modal__item:last-child { border-bottom: 0; }
.aksrt-badge-modal__item .medal-icon { width: 40px; height: 40px; object-fit: contain; }
.aksrt-badge-modal__icon-fa {
width: 40px; height: 40px; border-radius: 50%;
display: inline-flex; align-items: center; justify-content: center;
background: var(--surface-2, #f3f4f6); color: #f59e0b;
}
.aksrt-badge-modal__icon-fa.lg { width: 72px; height: 72px; font-size: 28px; }
.aksrt-badge-modal__meta { flex: 1; min-width: 0; }
.aksrt-badge-modal__name { font-weight: 600; font-size: 14px; }
.aksrt-badge-modal__empty { text-align: center; color: var(--muted-2-color, #999); padding: 24px 0; }
.aksrt-badge-modal--single { text-align: center; padding: 16px 8px 20px; }
.aksrt-badge-modal__card { padding: 12px 8px 8px; }
.aksrt-badge-modal__actions { margin-top: 12px; }
/* 徽章远程弹窗样式已迁至 assets/css/frontend/medal.css(zibll medal 风格)。 */
.aksrt-author-hero__badges { margin-top: 8px; }
/* Forum tab topics (batch6) */
+1 -7
View File
@@ -1023,13 +1023,7 @@ body.uc-modal-open {
}
.focus-color { color: var(--focus-color, #f59e0b); }
/* User badges wear modal */
.uc-badges-list { max-height: 60vh; overflow: auto; }
.uc-badges-item { gap: 12px; }
.uc-badges-item .medal-icon { width: 28px; height: 28px; object-fit: contain; }
.uc-badges-wear-tag { margin-left: 6px; }
.uc-badges-empty { padding: 16px 0; text-align: center; }
/* 用户中心「我的徽章」弹窗样式已迁至 assets/css/frontend/medal.css(zibll medal 风格)。 */
.uc-points-tasks__row.is-done { opacity: .72; }
.uc-points-tasks__progress-bar {
+271 -72
View File
@@ -1,5 +1,9 @@
/**
* AKSRT user badges — wear modal.
* AKSRT user badges — zibll medal UX parity.
*
* User-center modal: category-grouped medal card grid + in-modal detail
* view (medal-background card, obtain time, wear action). Also provides the
* global wear handler used inside remote (AJAX HTML) badge modals.
*/
(function () {
'use strict';
@@ -45,40 +49,36 @@
.replace(/"/g, '"');
}
function renderList(root, data, canWear) {
var body = root.querySelector('[data-aksrt-badges-body]');
if (!body) return;
var items = (data && data.items) || [];
if (!items.length) {
body.innerHTML = '<div class="uc-badges-empty muted-2-color em09">' + escapeHtml('暂无徽章') + '</div>';
return;
/* ---------- shared partials ---------- */
function iconHtml(item, cls) {
var icon = item && item.icon_url ? String(item.icon_url) : '';
var label = item && item.label ? String(item.label) : '';
if (icon) {
return '<img class="medal-icon img-icon' + (cls ? ' ' + cls : '') + '" src="' + escapeHtml(icon) + '" alt="' + escapeHtml(label) + '" title="' + escapeHtml(label) + '" loading="lazy" decoding="async">';
}
body.innerHTML = items.map(function (item) {
var icon = item.icon_url
? '<img class="medal-icon img-icon" src="' + escapeHtml(item.icon_url) + '" alt="">'
: '<span class="badg badg-sm aksrt-identity-chip aksrt-identity-chip--' + escapeHtml(item.tone || 'neutral') + '"><i class="fa fa-award"></i> ' + escapeHtml(item.label) + '</span>';
var wearBadge = item.wear ? '<span class="uc-badges-wear-tag c-yellow em09">佩戴中</span>' : '';
var action = '';
if (canWear && !item.wear) {
action = '<button type="button" class="but jb-blue but-sm" data-aksrt-badge-wear="' + escapeHtml(item.slug) + '">佩戴此徽章</button>';
} else if (item.wear) {
action = '<span class="muted-2-color em09">当前佩戴</span>';
}
return '<div class="uc-badges-item flex jsb ac border-bottom padding-h10" data-slug="' + escapeHtml(item.slug) + '">' +
'<div class="flex ac gap8">' + icon + '<div><div class="font-bold">' + escapeHtml(item.label) + '</div>' + wearBadge + '</div></div>' +
'<div class="shrink0">' + action + '</div></div>';
}).join('');
return '<span class="medal-icon medal-icon--fa' + (cls ? ' ' + cls : '') + '"><i class="fas fa-award" aria-hidden="true"></i></span>';
}
function openModal(root) {
root.hidden = false;
document.body.classList.add('uc-modal-open');
function emptyHtml(text, desc) {
return '<div class="empty-state empty-state--sm">' +
'<div class="empty-state__icon"><i class="fas fa-award" aria-hidden="true"></i></div>' +
'<div class="empty-state__title">' + escapeHtml(text) + '</div>' +
(desc ? '<div class="empty-state__desc">' + escapeHtml(desc) + '</div>' : '') +
'</div>';
}
function closeModal(root) {
root.hidden = true;
document.body.classList.remove('uc-modal-open');
function cardHtml(item) {
var cls = 'medal-card' + (item.owned ? ' is-has' : ' no-has') + (item.wear ? ' is-wear' : '');
return '<div class="' + cls + '">' +
iconHtml(item) +
'<div class="medal-card__name">' + escapeHtml(item.label) + '</div>' +
(item.wear ? '<div class="medal-card__wear">' + escapeHtml('佩戴中') + '</div>' : '') +
'</div>';
}
/* ---------- user-center modal ---------- */
function init() {
var openers = document.querySelectorAll('[data-aksrt-badges-open]');
var modal = document.querySelector('[data-aksrt-badges-modal]');
@@ -86,24 +86,193 @@
var userId = parseInt(modal.getAttribute('data-user-id') || '0', 10);
var canWear = modal.getAttribute('data-can-wear') === '1';
var medalBg = modal.getAttribute('data-medal-bg') || '';
var body = modal.querySelector('[data-aksrt-badges-body]');
var detailBox = modal.querySelector('[data-aksrt-badges-detail]');
var countSlot = modal.querySelector('[data-aksrt-badges-count]');
var wearSlot = modal.querySelector('[data-aksrt-badges-wear-slot]');
function load() {
if (!userId) return Promise.resolve();
var body = modal.querySelector('[data-aksrt-badges-body]');
if (body) body.innerHTML = '<div class="muted-2-color em09">加载中…</div>';
return api('/users/' + userId + '/badges').then(function (r) {
if (!r.ok || !r.json || !r.json.data) {
if (body) body.innerHTML = '<div class="c-red em09">加载失败</div>';
return;
var state = { data: null, detailSlug: '' };
function findItem(slug) {
var cats = (state.data && state.data.categories) || [];
for (var i = 0; i < cats.length; i++) {
var items = cats[i].items || [];
for (var j = 0; j < items.length; j++) {
if (items[j].slug === slug) return items[j];
}
}
return null;
}
function wornItem() {
var wear = state.data && state.data.wear;
return wear ? findItem(wear) : null;
}
/* ----- view switching ----- */
function showView(name) {
modal.querySelectorAll('[data-aksrt-badges-view]').forEach(function (view) {
view.hidden = view.getAttribute('data-aksrt-badges-view') !== name;
});
if (name === 'grid') state.detailSlug = '';
}
function openModal() {
modal.hidden = false;
document.body.classList.add('uc-modal-open');
}
function closeModal() {
modal.hidden = true;
document.body.classList.remove('uc-modal-open');
showView('grid');
}
/* ----- render: head ----- */
function renderHead() {
var count = state.data && typeof state.data.count === 'number' ? state.data.count : 0;
if (countSlot) {
countSlot.textContent = count > 0 ? '共 ' + count + ' 枚' : '';
}
if (!wearSlot) return;
var worn = wornItem();
wearSlot.innerHTML = worn
? '<span class="uc-badges-head__wear-tag" title="' + escapeHtml('当前佩戴:' + worn.label) + '">' + iconHtml(worn) + '<span class="uc-badges-head__wear-name">' + escapeHtml(worn.label) + '</span></span>'
: '';
}
/* ----- render: grid ----- */
function renderGrid() {
if (!body) return;
var cats = (state.data && state.data.categories) || [];
if (!cats.length) {
body.innerHTML = emptyHtml('暂无徽章', '积极参与社区互动即可获得徽章');
return;
}
body.innerHTML = cats.map(function (cat) {
var cards = (cat.items || []).map(function (item) {
return '<button type="button" class="medal-list" data-aksrt-badge-detail="' + escapeHtml(item.slug) + '" aria-label="' + escapeHtml(item.label) + '">' +
cardHtml(item) +
'</button>';
}).join('');
return '<div class="medal-cat-box">' +
'<div class="medal-cat-head"><b class="medal-cat-name">' + escapeHtml(cat.cat_name || '') + '</b></div>' +
'<div class="medal-list-box">' + cards + '</div>' +
'</div>';
}).join('');
}
/* ----- render: detail ----- */
function renderDetail(slug) {
var item = findItem(slug);
if (!item || !detailBox) return;
state.detailSlug = slug;
var stateLine = '';
if (item.owned && item.time) {
stateLine = '<div class="medal-single-state c-blue">' + escapeHtml(item.time + ' 获得') + '</div>';
} else if (!item.owned) {
stateLine = '<div class="medal-single-state c-yellow">' + escapeHtml('暂未获得') + '</div>';
}
var action = '';
if (canWear && item.owned && !item.wear) {
action = '<button type="button" class="but jb-blue btn-block radius" data-aksrt-badge-wear="' + escapeHtml(item.slug) + '">' + escapeHtml('佩戴此徽章') + '</button>';
} else if (item.wear) {
action = '<span class="badg btn-block c-blue">' + escapeHtml('当前佩戴此徽章') + '</span>';
}
detailBox.innerHTML =
'<div class="uc-badges-detail__head">' +
'<button type="button" class="uc-badges-back" data-aksrt-badges-back><i class="fas fa-angle-left" aria-hidden="true"></i> ' + escapeHtml('返回列表') + '</button>' +
'</div>' +
'<div class="medal-single-card">' +
(medalBg ? '<div class="medal-single-bg"><img class="fit-cover" src="' + escapeHtml(medalBg) + '" alt=""></div>' : '') +
'<div class="medal-single-main">' +
iconHtml(item) +
'<div class="medal-single-name">' + escapeHtml(item.label) + '</div>' +
(item.desc ? '<div class="medal-single-desc muted-2-color">' + escapeHtml(item.desc) + '</div>' : '') +
stateLine +
'</div>' +
'</div>' +
(action ? '<div class="medal-modal__actions">' + action + '</div>' : '');
showView('detail');
}
/* ----- header worn icon sync (profile header, next to the name) ----- */
function syncHeaderIcon() {
var worn = wornItem();
if (!worn || !worn.icon_url) return;
document.querySelectorAll('.aksrt-identity-medal.is-wear').forEach(function (node) {
if (node.tagName === 'IMG') {
node.src = worn.icon_url;
node.alt = worn.label;
node.title = worn.label;
} else {
var img = document.createElement('img');
img.className = 'medal-icon img-icon ml3 aksrt-identity-medal is-wear';
img.src = worn.icon_url;
img.alt = worn.label;
img.title = worn.label;
img.setAttribute('loading', 'lazy');
img.setAttribute('decoding', 'async');
node.parentNode.replaceChild(img, node);
}
renderList(modal, r.json.data, canWear);
});
}
/* ----- data ----- */
function load() {
if (!userId) return Promise.resolve();
if (body) {
body.innerHTML = '<div class="uc-badges-loading muted-2-color em09"><i class="fas fa-spinner fa-spin" aria-hidden="true"></i> ' + escapeHtml('加载中…') + '</div>';
}
return api('/users/' + userId + '/badges').then(function (r) {
if (!r.ok || !r.json || !r.json.data) {
if (body) {
body.innerHTML = '<div class="uc-badges-error">' +
'<span class="c-red em09">' + escapeHtml('加载失败') + '</span>' +
'<button type="button" class="but but-sm" data-aksrt-badges-retry>' + escapeHtml('重试') + '</button>' +
'</div>';
}
return;
}
state.data = r.json.data;
renderHead();
renderGrid();
});
}
function wear(slug, btn) {
btn.disabled = true;
api('/users/me/badges/wear', {
method: 'POST',
body: JSON.stringify({ slug: slug }),
}).then(function (r) {
if (!r.ok) {
toast((r.json && (r.json.message || (r.json.data && r.json.data.message))) || '佩戴失败', 'error');
btn.disabled = false;
return;
}
toast((r.json && r.json.message) || '已佩戴徽章', 'success');
state.data = (r.json && r.json.data) || state.data;
renderHead();
renderGrid();
renderDetail(slug);
syncHeaderIcon();
}).catch(function () {
toast('网络错误', 'error');
btn.disabled = false;
});
}
/* ----- events ----- */
openers.forEach(function (btn) {
btn.addEventListener('click', function (e) {
e.preventDefault();
openModal(modal);
openModal();
showView('grid');
load();
});
});
@@ -111,62 +280,92 @@
modal.querySelectorAll('[data-aksrt-badges-close]').forEach(function (btn) {
btn.addEventListener('click', function (e) {
e.preventDefault();
closeModal(modal);
closeModal();
});
});
document.addEventListener('keydown', function (e) {
if (e.key !== 'Escape' || modal.hidden) return;
var detailView = modal.querySelector('[data-aksrt-badges-view="detail"]');
// Detail view open: ESC steps back to the grid first.
if (detailView && !detailView.hidden) {
showView('grid');
return;
}
closeModal();
});
modal.addEventListener('click', function (e) {
var back = e.target.closest('[data-aksrt-badges-back]');
if (back) {
e.preventDefault();
showView('grid');
return;
}
var retry = e.target.closest('[data-aksrt-badges-retry]');
if (retry) {
e.preventDefault();
load();
return;
}
var wearBtn = e.target.closest('[data-aksrt-badge-wear]');
if (!wearBtn || !canWear) return;
e.preventDefault();
var slug = wearBtn.getAttribute('data-aksrt-badge-wear') || '';
wearBtn.disabled = true;
api('/users/me/badges/wear', {
method: 'POST',
body: JSON.stringify({ slug: slug }),
}).then(function (r) {
if (!r.ok) {
toast((r.json && (r.json.message || (r.json.data && r.json.data.message))) || '佩戴失败', 'error');
wearBtn.disabled = false;
return;
}
toast((r.json && r.json.message) || '已佩戴徽章', 'success');
renderList(modal, (r.json && r.json.data) || {}, canWear);
}).catch(function () {
toast('网络错误', 'error');
wearBtn.disabled = false;
});
if (wearBtn && canWear) {
e.preventDefault();
wear(wearBtn.getAttribute('data-aksrt-badge-wear') || '', wearBtn);
return;
}
var card = e.target.closest('[data-aksrt-badge-detail]');
if (card) {
e.preventDefault();
renderDetail(card.getAttribute('data-aksrt-badge-detail') || '');
}
});
}
// Global wear handler (remote modal + UC modal).
document.addEventListener("click", function (e) {
var wearBtn = e.target.closest("[data-aksrt-badge-wear]");
/* ---------- remote modal: global wear handler ---------- */
document.addEventListener('click', function (e) {
var wearBtn = e.target.closest('[data-aksrt-badge-wear]');
if (!wearBtn) return;
// UC modal has its own handler when canWear on modal root.
var modalRoot = wearBtn.closest("[data-aksrt-badges-modal]");
var modalRoot = wearBtn.closest('[data-aksrt-badges-modal]');
if (modalRoot) return;
e.preventDefault();
var slug = wearBtn.getAttribute("data-aksrt-badge-wear") || "";
var slug = wearBtn.getAttribute('data-aksrt-badge-wear') || '';
if (!slug) return;
wearBtn.disabled = true;
api("/users/me/badges/wear", {
method: "POST",
api('/users/me/badges/wear', {
method: 'POST',
body: JSON.stringify({ slug: slug }),
}).then(function (r) {
if (!r.ok) {
toast((r.json && (r.json.message || (r.json.data && r.json.data.message))) || "佩戴失败", "error");
toast((r.json && (r.json.message || (r.json.data && r.json.data.message))) || '佩戴失败', 'error');
wearBtn.disabled = false;
return;
}
toast((r.json && r.json.message) || "已佩戴徽章", "success");
// Soft refresh modal content if remote host present.
toast((r.json && r.json.message) || '已佩戴徽章', 'success');
// Re-open the refreshed single-badge modal so state stays in sync.
var refreshed = null;
try {
document.dispatchEvent(new CustomEvent("aksrt:remote-modal:close"));
var uid = parseInt((r.json && r.json.data && r.json.data.user_id) || '0', 10);
var data = r.json && r.json.data;
var isSelf = data && data.is_self;
if (isSelf && data.wear) {
refreshed = (window.aksrtData && aksrtData.ajaxUrl)
? aksrtData.ajaxUrl + '?action=aksrt_single_badge_info_modal&name=' + encodeURIComponent(data.wear) + '&user_id=' + (uid || 0)
: null;
}
} catch (_) {}
try {
if (refreshed && window.AksrtRemoteModal && typeof window.AksrtRemoteModal.openRemote === 'function') {
window.AksrtRemoteModal.openRemote({ url: refreshed, className: 'modal-mini', mobileBottom: true });
} else {
document.dispatchEvent(new CustomEvent('aksrt:remote-modal:close'));
}
} catch (_) {
try { document.dispatchEvent(new CustomEvent('aksrt:remote-modal:close')); } catch (__) {}
}
}).catch(function () {
toast("网络错误", "error");
toast('网络错误', 'error');
wearBtn.disabled = false;
});
});