2026-07-09 13:25:54 +08:00

1886 lines
68 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(function () {
var ACTION_LABELS = {
'block-user': 'Block User',
'send-frame': 'Send Frame',
'send-car': 'Send Car',
'send-badge': 'Send Badge',
'send-vip': 'Send VIP',
'update-user-level': 'Update User Level',
'add-bd-leader': 'Add BD Leader',
'add-admin': 'Add Admin',
'add-superadmin': 'Add Superadmin',
'transfer-user-country': 'Transfer User Country',
};
var DAY_MS = 24 * 60 * 60 * 1000;
var PERMANENT_BLOCK_MS = 10 * 365 * DAY_MS;
var RESOURCE_TYPES = {
'send-frame': 'avatar_frame',
'send-car': 'vehicle',
'send-badge': 'badge',
};
var ACTION_PERMISSION_KEYS = {
'send-frame': 'canGrantAvatarFrame',
'send-car': 'canGrantVehicle',
'send-badge': 'canGrantBadge',
'send-vip': 'canGrantVip',
'update-user-level': 'canUpdateUserLevel',
'add-bd-leader': 'canAddBdLeader',
'add-admin': 'canAddAdmin',
'add-superadmin': 'canAddSuperadmin',
'block-user': 'canBlockUser',
'transfer-user-country': 'canTransferUserCountry',
};
var RESOURCE_GRANT_DURATIONS = [
{ label: '7 days', ms: 7 * DAY_MS },
{ label: '30 days', ms: 30 * DAY_MS },
{ label: '90 days', ms: 90 * DAY_MS },
];
var DEFAULT_RESOURCE_GRANT_DURATION_MS = RESOURCE_GRANT_DURATIONS[0].ms;
var VIP_GRANT_LEVELS = [1, 2, 3, 4, 5];
var TRACK_LABELS = {
wealth: 'Wealth',
charm: 'Charm',
game: 'Game',
};
var api = window.HyAppAPI && window.HyAppAPI.managerCenter;
var pageState = {
loading: true,
denied: false,
overview: null,
permissions: {},
resourcesByType: {},
countries: [],
};
var blockState = {
searchedUser: null,
selectedDurationMs: 0,
selectedDurationLabel: '',
blockedUsers: [],
submitting: false,
};
var levelState = {
searchedUser: null,
selectedTrack: 'wealth',
selectedLevel: 0,
submitting: false,
};
var grantState = {
action: '',
resourceType: '',
title: '',
searchedUser: null,
selectedResourceIds: [],
selectedResourceDurationMs: DEFAULT_RESOURCE_GRANT_DURATION_MS,
selectedVipLevel: 0,
submitting: false,
};
var bdLeaderState = {
action: '',
title: '',
searchedUser: null,
submitting: false,
};
var countryState = {
searchedUser: null,
selectedCountryCode: '',
submitting: false,
};
function $(id) {
return document.getElementById(id);
}
function root() {
return document.querySelector('.manager-center');
}
function t(key, fallback) {
return window.HyAppI18n && window.HyAppI18n.t
? window.HyAppI18n.t(key, fallback)
: fallback || key;
}
function toast(message) {
if (window.HyAppToast && window.HyAppToast.show) {
window.HyAppToast.show(message);
return;
}
window.alert(message);
}
function showError(error) {
var message =
error && error.status === 403
? 'Permission denied.'
: (error && error.message) || 'Request failed.';
toast(message);
}
function commandId(prefix) {
return [
prefix || 'manager',
Date.now(),
Math.random().toString(16).slice(2, 10),
].join('-');
}
function userName(user) {
return (
user.username ||
user.name ||
user.target_username ||
user.display_user_id ||
user.target_display_user_id ||
'User'
);
}
function userDisplayId(user) {
return user.display_user_id || user.target_display_user_id || '';
}
function userId(user) {
return user.user_id || user.target_user_id || '';
}
function avatarUrl(user) {
return user.avatar || user.target_avatar || '';
}
function resourceImageUrl(resource) {
return (
resource.preview_url ||
resource.previewUrl ||
resource.asset_url ||
resource.assetUrl ||
resource.icon_url ||
resource.iconUrl ||
''
);
}
function resourceKindLabel() {
if (grantState.action === 'send-frame') return 'Frame';
if (grantState.action === 'send-car') return 'Car';
if (grantState.action === 'send-badge') return 'Badge';
return 'Resource';
}
function currentVip(user) {
return user.current_vip || user.currentVip || user.vip || null;
}
function currentVipLevel(user) {
var vip = currentVip(user);
if (!vip || !vip.active) return 0;
return Number(vip.level) || 0;
}
function currentVipStatusText(user) {
var vip = currentVip(user);
if (!vip || !vip.active || !vip.level) return 'Current VIP: None';
var label = vip.name || 'VIP ' + vip.level;
var expiresAt = Number(vip.expires_at_ms || vip.expiresAtMs || 0);
if (expiresAt > 0) {
return label + ' active · Expires ' + formatDateTime(expiresAt);
}
return label + ' active';
}
function initial(text) {
return String(text || 'M')
.trim()
.charAt(0)
.toUpperCase();
}
function createNode(tag, className, text) {
var node = document.createElement(tag);
if (className) node.className = className;
if (text !== undefined) node.textContent = text;
return node;
}
function setAvatar(node, user) {
var url = avatarUrl(user);
node.innerHTML = '';
if (url) {
var img = document.createElement('img');
img.src = url;
img.alt = '';
node.appendChild(img);
return;
}
node.textContent = initial(userName(user) || userDisplayId(user));
}
function textValue(value, fallback) {
if (value === undefined || value === null || value === '') {
return fallback;
}
return String(value);
}
function isLocalPreviewError(error) {
var host = window.location && window.location.hostname;
var isLocalHost =
host === 'localhost' ||
host === '127.0.0.1' ||
host === '::1' ||
host === '';
var message = (error && error.message) || '';
return isLocalHost && /failed to fetch|network/i.test(message);
}
function firstValue(source, keys) {
source = source || {};
for (var i = 0; i < keys.length; i += 1) {
var key = keys[i];
if (
Object.prototype.hasOwnProperty.call(source, key) &&
source[key] !== undefined &&
source[key] !== null &&
source[key] !== ''
) {
return source[key];
}
}
return undefined;
}
function firstNestedValue(sources, keys) {
for (var i = 0; i < sources.length; i += 1) {
var value = firstValue(sources[i], keys);
if (value !== undefined) return value;
}
return undefined;
}
function formatMoney(value, fallback) {
if (value === undefined || value === null || value === '') {
return fallback;
}
if (typeof value === 'string' && value.indexOf('$') >= 0) {
return value;
}
var number = Number(String(value).replace(/,/g, ''));
if (!Number.isFinite(number)) return String(value);
return (
'$' +
number.toLocaleString('en-US', {
maximumFractionDigits: number % 1 === 0 ? 0 : 2,
minimumFractionDigits: number % 1 === 0 ? 0 : 2,
})
);
}
function formatPercent(value, fallback) {
if (value === undefined || value === null || value === '') {
return fallback;
}
if (typeof value === 'string' && value.indexOf('%') >= 0) {
// 服务端已带符号下发;只对正数补 +,零和负数原样透传,避免出现 "+0%"。
var parsed = parseFloat(value);
if (!Number.isFinite(parsed) || parsed <= 0) {
return value;
}
return '+' + value.replace(/^\+/, '');
}
var number = Number(value);
if (!Number.isFinite(number)) return String(value);
if (Math.abs(number) > 0 && Math.abs(number) <= 1) {
number *= 100;
}
return (number > 0 ? '+' : '') + Math.round(number) + '%';
}
function setText(id, value) {
var node = $(id);
if (node) node.textContent = value;
}
function updateManagerAvatar(profile) {
var image = $('managerAvatar');
var fallback = $('managerAvatarFallback');
if (!image || !fallback) return;
// 没有头像时展示首字母占位,不再使用演示头像假数据。
var url = avatarUrl(profile);
if (url) {
image.src = url;
image.hidden = false;
fallback.hidden = true;
return;
}
image.hidden = true;
fallback.hidden = false;
fallback.textContent = initial(
userName(profile) || userDisplayId(profile)
);
}
function dashboardSources() {
var overview = pageState.overview || {};
return [
overview.dashboard,
overview.summary,
overview.metrics,
overview.statistics,
overview.wallet,
overview,
];
}
function renderDashboard() {
var overview = pageState.overview || {};
var profile =
overview.user ||
overview.profile ||
overview.manager ||
overview.current_user ||
overview.currentUser ||
overview;
updateManagerAvatar(profile || {});
setText(
'managerName',
textValue(
firstValue(profile || {}, [
'username',
'name',
'nickname',
'target_username',
]),
'--'
)
);
setText(
'managerDisplayId',
textValue(
userDisplayId(profile || {}) || userId(profile || {}),
'--'
)
);
var sources = dashboardSources();
// 页面不再内置任何设计假数据:加载中由骨架屏占位,渲染时字段缺失(聚合降级/请求失败/无 API
// 一律显示中性占位符,避免把演示数值误当真实团队工资。
var fallback = {
thisMonth: '--',
lastMonth: '--',
hosts: '--',
growth: '--',
wallet: '--',
bankCards: '--',
};
setText(
'managerThisMonthSalary',
formatMoney(
firstNestedValue(sources, [
'this_month_salary',
'thisMonthSalary',
'current_month_salary',
'currentMonthSalary',
'team_salary_this_month',
'teamSalaryThisMonth',
]),
fallback.thisMonth
)
);
setText(
'managerLastMonthSalary',
formatMoney(
firstNestedValue(sources, [
'last_month_salary',
'lastMonthSalary',
'previous_month_salary',
'previousMonthSalary',
'team_salary_last_month',
'teamSalaryLastMonth',
]),
fallback.lastMonth
)
);
setText(
'managerActiveHosts',
textValue(
firstNestedValue(sources, [
'active_hosts',
'activeHosts',
'total_active_hosts',
'totalActiveHosts',
'host_count',
'hostCount',
]),
fallback.hosts
)
);
setText(
'managerGrowth',
formatPercent(
firstNestedValue(sources, [
'growth',
'growth_rate',
'growthRate',
'salary_growth',
'salaryGrowth',
'month_growth',
'monthGrowth',
]),
fallback.growth
)
);
setText(
'managerWallet',
formatMoney(
firstNestedValue(sources, [
'wallet',
'wallet_balance',
'walletBalance',
'balance',
]),
fallback.wallet
)
);
setText(
'managerBankCards',
textValue(
firstNestedValue(sources, [
'bank_cards',
'bankCards',
'bank_card_count',
'bankCardCount',
]),
fallback.bankCards
)
);
}
function formatDateTime(ms) {
if (!ms) return 'Never';
var date = new Date(Number(ms));
var year = date.getFullYear();
var month = String(date.getMonth() + 1).padStart(2, '0');
var day = String(date.getDate()).padStart(2, '0');
var hour = String(date.getHours()).padStart(2, '0');
var minute = String(date.getMinutes()).padStart(2, '0');
return year + '-' + month + '-' + day + ' ' + hour + ':' + minute;
}
function setPageLoading(loading) {
// 首屏加载态用页面级骨架屏整层覆盖common/skeleton.js数据就绪后整层淡出
// is-loading 状态类保留给页面内其他降级样式使用。
pageState.loading = loading;
var container = root();
if (container) container.classList.toggle('is-loading', loading);
if (!window.HyAppSkeleton) return;
if (loading) {
window.HyAppSkeleton.show();
} else {
window.HyAppSkeleton.hide();
}
}
function setDenied(denied) {
pageState.denied = denied;
var container = root();
if (!container) return;
container.classList.toggle('is-denied', denied);
}
function normalizeUser(item) {
var vip = item.current_vip || item.currentVip || item.vip || null;
return {
user_id: item.user_id || item.userId || item.target_user_id || '',
display_user_id:
item.display_user_id ||
item.displayUserId ||
item.target_display_user_id ||
'',
username: item.username || item.name || item.target_username || '',
avatar: item.avatar || item.target_avatar || '',
country: item.country || '',
levels: item.levels || {},
current_vip: vip
? {
level: Number(vip.level) || 0,
name: vip.name || '',
active: Boolean(vip.active),
started_at_ms: vip.started_at_ms || vip.startedAtMs || 0,
expires_at_ms: vip.expires_at_ms || vip.expiresAtMs || 0,
updated_at_ms: vip.updated_at_ms || vip.updatedAtMs || 0,
}
: null,
};
}
function normalizeBlock(item) {
return {
block_id: item.block_id || item.blockId || '',
target_user_id: item.target_user_id || item.targetUserId || '',
target_display_user_id:
item.target_display_user_id || item.targetDisplayUserId || '',
target_username: item.target_username || item.targetUsername || '',
target_avatar: item.target_avatar || item.targetAvatar || '',
country: item.country || '',
blocked_until_ms:
item.blocked_until_ms ||
item.blockedUntilMs ||
item.unblockAtMs ||
0,
status: item.status || 'active',
};
}
function normalizePermissions(raw) {
raw = raw || {};
return {
canGrantAvatarFrame:
raw.canGrantAvatarFrame !== undefined
? Boolean(raw.canGrantAvatarFrame)
: raw.can_grant_avatar_frame !== undefined
? Boolean(raw.can_grant_avatar_frame)
: true,
canGrantVehicle:
raw.canGrantVehicle !== undefined
? Boolean(raw.canGrantVehicle)
: raw.can_grant_vehicle !== undefined
? Boolean(raw.can_grant_vehicle)
: true,
canGrantBadge:
raw.canGrantBadge !== undefined
? Boolean(raw.canGrantBadge)
: raw.can_grant_badge !== undefined
? Boolean(raw.can_grant_badge)
: true,
canGrantVip:
raw.canGrantVip !== undefined
? Boolean(raw.canGrantVip)
: raw.can_grant_vip !== undefined
? Boolean(raw.can_grant_vip)
: true,
canUpdateUserLevel:
raw.canUpdateUserLevel !== undefined
? Boolean(raw.canUpdateUserLevel)
: raw.can_update_user_level !== undefined
? Boolean(raw.can_update_user_level)
: true,
canAddBdLeader:
raw.canAddBdLeader !== undefined
? Boolean(raw.canAddBdLeader)
: raw.can_add_bd_leader !== undefined
? Boolean(raw.can_add_bd_leader)
: true,
canAddAdmin:
raw.canAddAdmin !== undefined
? Boolean(raw.canAddAdmin)
: raw.can_add_admin !== undefined
? Boolean(raw.can_add_admin)
: true,
canAddSuperadmin:
raw.canAddSuperadmin !== undefined
? Boolean(raw.canAddSuperadmin)
: raw.can_add_superadmin !== undefined
? Boolean(raw.can_add_superadmin)
: true,
canBlockUser:
raw.canBlockUser !== undefined
? Boolean(raw.canBlockUser)
: raw.can_block_user !== undefined
? Boolean(raw.can_block_user)
: true,
canTransferUserCountry:
raw.canTransferUserCountry !== undefined
? Boolean(raw.canTransferUserCountry)
: raw.can_transfer_user_country !== undefined
? Boolean(raw.can_transfer_user_country)
: true,
};
}
function canUseAction(action) {
var key = ACTION_PERMISSION_KEYS[action];
if (!key) return true;
return pageState.permissions[key] !== false;
}
function renderActionPermissions() {
document.querySelectorAll('[data-action]').forEach(function (button) {
var action = button.getAttribute('data-action');
button.hidden = !canUseAction(action);
});
}
function ensureActionAllowed(action) {
if (canUseAction(action)) return true;
toast('Permission denied.');
return false;
}
function apiSearchUser(keyword, scope, action) {
if (!api || !api.searchUsers) {
return Promise.reject(new Error('API not available.'));
}
return api
.searchUsers(keyword, 20, scope, action)
.then(function (data) {
var items = (data && data.items) || [];
return items.map(normalizeUser);
});
}
function loadOverview() {
if (!api || !api.overview) {
// 无 API 注入(本地纯静态预览等)时立即渲染一次,把骨架屏收敛成中性占位符。
pageState.permissions = normalizePermissions({});
renderDashboard();
renderActionPermissions();
setDenied(false);
return Promise.resolve();
}
setPageLoading(true);
return api
.overview()
.then(function (data) {
pageState.overview = data || {};
pageState.permissions = normalizePermissions(
pageState.overview.permissions || pageState.overview
);
renderDashboard();
renderActionPermissions();
setDenied(false);
return canUseAction('block-user')
? refreshBlocks()
: Promise.resolve();
})
.catch(function (error) {
if (isLocalPreviewError(error)) {
pageState.permissions = normalizePermissions({});
renderDashboard();
renderActionPermissions();
setDenied(false);
return;
}
// 接口失败也要渲染一次:把骨架屏收敛成中性占位符,页面不残留加载态。
setDenied(true);
renderDashboard();
showError(error);
})
.finally(function () {
setPageLoading(false);
});
}
function refreshBlocks() {
if (!api || !api.listBlocks) return Promise.resolve();
return api
.listBlocks('active', 50)
.then(function (data) {
blockState.blockedUsers = ((data && data.items) || []).map(
normalizeBlock
);
renderBlockedList();
})
.catch(showError);
}
function goBack() {
if (window.HyAppBridge && window.HyAppBridge.back) {
window.HyAppBridge.back();
return;
}
if (window.history.length > 1) {
window.history.back();
}
}
function renderUserRow(user, actionText, onAction) {
var row = createNode('div', 'searched-user');
var avatar = createNode('div', 'searched-avatar');
setAvatar(avatar, user);
var copy = createNode('div', 'blocked-row-main');
copy.appendChild(createNode('div', 'searched-name', userName(user)));
copy.appendChild(
createNode('div', 'searched-meta', 'UID: ' + userDisplayId(user))
);
if (currentVip(user)) {
copy.appendChild(
createNode('div', 'searched-vip', currentVipStatusText(user))
);
}
var button = createNode('button', 'user-block-button', actionText);
button.type = 'button';
button.addEventListener('click', onAction);
row.appendChild(avatar);
row.appendChild(copy);
row.appendChild(button);
return row;
}
function renderSearchResult() {
var container = $('blockSearchResult');
if (!container) return;
container.innerHTML = '';
if (!blockState.searchedUser) return;
container.appendChild(
renderUserRow(blockState.searchedUser, 'Block', function () {
selectDuration('86400000', '1 day');
var panel = $('blockDurationPanel');
if (panel) panel.hidden = false;
})
);
}
function setupLevelGrid() {
var grid = $('levelGrid');
if (!grid || grid.children.length) return;
for (var level = 1; level <= 11; level += 1) {
var button = createNode('button', '', 'Lv. ' + level);
button.type = 'button';
button.setAttribute('data-level', String(level));
grid.appendChild(button);
}
}
function ensureTrackSelector() {
var panel = $('levelPanel');
if (!panel || $('levelTrackGrid')) return;
var title = createNode(
'div',
'block-section-title',
'Select level type'
);
var grid = createNode('div', 'track-grid');
grid.id = 'levelTrackGrid';
Object.keys(TRACK_LABELS).forEach(function (track) {
var button = createNode('button', '', TRACK_LABELS[track]);
button.type = 'button';
button.setAttribute('data-track', track);
button.addEventListener('click', function () {
levelState.selectedTrack = track;
renderLevelButtons();
renderLevelSearchResult();
});
grid.appendChild(button);
});
panel.insertBefore(grid, panel.firstChild);
panel.insertBefore(title, grid);
}
function currentTrackLevel(user) {
var track = levelState.selectedTrack;
var levels = (user && user.levels) || {};
return levels[track] && levels[track].level ? levels[track].level : 0;
}
function renderLevelSearchResult() {
var container = $('levelSearchResult');
if (!container) return;
container.innerHTML = '';
if (!levelState.searchedUser) return;
var user = levelState.searchedUser;
var row = createNode('div', 'searched-user');
var avatar = createNode('div', 'searched-avatar');
setAvatar(avatar, user);
var copy = createNode('div', 'blocked-row-main');
copy.appendChild(createNode('div', 'searched-name', userName(user)));
copy.appendChild(
createNode(
'div',
'searched-meta',
'UID: ' +
userDisplayId(user) +
' · ' +
TRACK_LABELS[levelState.selectedTrack] +
' Lv. ' +
currentTrackLevel(user)
)
);
var badge = createNode(
'div',
'block-list-count',
'Lv. ' + currentTrackLevel(user)
);
row.appendChild(avatar);
row.appendChild(copy);
row.appendChild(badge);
container.appendChild(row);
}
function renderLevelButtons() {
var trackButtons = document.querySelectorAll(
'#levelTrackGrid [data-track]'
);
trackButtons.forEach(function (button) {
button.classList.toggle(
'is-selected',
button.getAttribute('data-track') === levelState.selectedTrack
);
});
var buttons = document.querySelectorAll('#levelGrid [data-level]');
buttons.forEach(function (button) {
var isSelected =
Number(button.getAttribute('data-level')) ===
levelState.selectedLevel;
button.classList.toggle('is-selected', isSelected);
});
var confirmButton = $('confirmLevelButton');
if (confirmButton) {
confirmButton.disabled =
!levelState.searchedUser ||
!levelState.selectedLevel ||
levelState.submitting;
}
}
function selectLevel(level) {
levelState.selectedLevel = Number(level) || 0;
renderLevelButtons();
}
function renderDurationButtons() {
var buttons = document.querySelectorAll(
'#durationGrid [data-duration-ms]'
);
buttons.forEach(function (button) {
var isSelected =
Number(button.getAttribute('data-duration-ms')) ===
blockState.selectedDurationMs;
button.classList.toggle('is-selected', isSelected);
});
var confirmButton = $('confirmBlockButton');
if (confirmButton) {
confirmButton.disabled =
!blockState.searchedUser || blockState.submitting;
}
}
function selectDuration(durationMs, label) {
blockState.selectedDurationMs = Number(durationMs) || 0;
blockState.selectedDurationLabel = label || 'Permanent';
renderDurationButtons();
}
function renderBlockedList() {
var container = $('blockedList');
var count = $('blockListCount');
if (!container) return;
container.innerHTML = '';
if (count) count.textContent = String(blockState.blockedUsers.length);
if (!blockState.blockedUsers.length) {
container.appendChild(
createNode('div', 'blocked-empty', 'No blocked users.')
);
return;
}
blockState.blockedUsers.forEach(function (user) {
var row = createNode('div', 'blocked-row');
var avatar = createNode('div', 'blocked-avatar');
setAvatar(avatar, user);
var copy = createNode('div', 'blocked-row-main');
copy.appendChild(createNode('div', 'blocked-name', userName(user)));
copy.appendChild(
createNode('div', 'blocked-meta', 'UID: ' + userDisplayId(user))
);
copy.appendChild(
createNode(
'div',
'blocked-time',
'Unblock time: ' + formatDateTime(user.blocked_until_ms)
)
);
var unblockButton = createNode(
'button',
'user-unblock-button',
'Unblock'
);
unblockButton.type = 'button';
unblockButton.addEventListener('click', function () {
unblockUser(user.block_id);
});
row.appendChild(avatar);
row.appendChild(copy);
row.appendChild(unblockButton);
container.appendChild(row);
});
}
function unblockUser(blockId) {
if (!blockId || blockState.submitting) return;
blockState.submitting = true;
api.unblockUser(blockId, { reason: 'manager_center' })
.then(function () {
toast('User unblocked.');
return refreshBlocks();
})
.catch(showError)
.finally(function () {
blockState.submitting = false;
});
}
function resetBlockSearch() {
var input = $('blockSearchInput');
var panel = $('blockDurationPanel');
blockState.searchedUser = null;
blockState.selectedDurationMs = 0;
blockState.selectedDurationLabel = '';
if (input) input.value = '';
if (panel) panel.hidden = true;
renderSearchResult();
renderDurationButtons();
}
function openBlockModal() {
if (pageState.denied) {
toast('Permission denied.');
return;
}
if (!ensureActionAllowed('block-user')) return;
var modal = $('blockModal');
if (!modal) return;
resetBlockSearch();
refreshBlocks();
modal.hidden = false;
modal.setAttribute('aria-hidden', 'false');
setTimeout(function () {
var input = $('blockSearchInput');
if (input) input.focus();
}, 30);
}
function closeBlockModal() {
var modal = $('blockModal');
if (!modal) return;
modal.hidden = true;
modal.setAttribute('aria-hidden', 'true');
}
function resetLevelSearch() {
var input = $('levelSearchInput');
var panel = $('levelPanel');
levelState.searchedUser = null;
levelState.selectedTrack = 'wealth';
levelState.selectedLevel = 0;
if (input) input.value = '';
if (panel) panel.hidden = true;
renderLevelSearchResult();
renderLevelButtons();
}
function openLevelModal() {
if (pageState.denied) {
toast('Permission denied.');
return;
}
if (!ensureActionAllowed('update-user-level')) return;
var modal = $('levelModal');
if (!modal) return;
resetLevelSearch();
modal.hidden = false;
modal.setAttribute('aria-hidden', 'false');
setTimeout(function () {
var input = $('levelSearchInput');
if (input) input.focus();
}, 30);
}
function closeLevelModal() {
var modal = $('levelModal');
if (!modal) return;
modal.hidden = true;
modal.setAttribute('aria-hidden', 'true');
}
function searchBlockUser() {
var input = $('blockSearchInput');
var value = input ? input.value.trim() : '';
var panel = $('blockDurationPanel');
if (!value) {
toast('Enter user ID.');
return;
}
// 封禁搜索只校验 Block User 权限;不能借用赠送弹窗状态,否则会按资源/VIP 赠送权限误判。
apiSearchUser(value, '', 'block-user')
.then(function (items) {
if (!items.length) {
blockState.searchedUser = null;
toast('No user found.');
return;
}
blockState.searchedUser = items[0];
blockState.selectedDurationMs = 0;
blockState.selectedDurationLabel = '';
if (panel) panel.hidden = true;
renderSearchResult();
renderDurationButtons();
})
.catch(showError);
}
function confirmBlockUser() {
if (!blockState.searchedUser) {
toast('Search a user first.');
return;
}
if (blockState.submitting) return;
var durationMs = blockState.selectedDurationMs || PERMANENT_BLOCK_MS;
blockState.submitting = true;
renderDurationButtons();
api.blockUser({
command_id: commandId('manager-block'),
target_user_id: userId(blockState.searchedUser),
blocked_until_ms: Date.now() + durationMs,
reason: 'manager_center',
})
.then(function () {
toast('User blocked.');
resetBlockSearch();
return refreshBlocks();
})
.catch(showError)
.finally(function () {
blockState.submitting = false;
renderDurationButtons();
});
}
function searchLevelUser() {
var input = $('levelSearchInput');
var value = input ? input.value.trim() : '';
var panel = $('levelPanel');
if (!value) {
toast('Enter user ID.');
return;
}
// 等级调整搜索按自己的能力开关校验,避免资源赠送关闭时误挡等级操作。
apiSearchUser(value, '', 'update-user-level')
.then(function (items) {
if (!items.length) {
levelState.searchedUser = null;
toast('No user found.');
return;
}
levelState.searchedUser = items[0];
levelState.selectedLevel = 0;
if (panel) panel.hidden = false;
renderLevelSearchResult();
renderLevelButtons();
})
.catch(showError);
}
function confirmLevelUpdate() {
if (!levelState.searchedUser) {
toast('Search a user first.');
return;
}
if (!levelState.selectedLevel) {
toast('Select a user level.');
return;
}
if (levelState.submitting) return;
levelState.submitting = true;
renderLevelButtons();
api.updateLevel({
command_id: commandId('manager-level'),
target_user_id: userId(levelState.searchedUser),
track: levelState.selectedTrack,
level: levelState.selectedLevel,
reason: 'manager_center',
})
.then(function () {
toast('User level updated.');
closeLevelModal();
})
.catch(showError)
.finally(function () {
levelState.submitting = false;
renderLevelButtons();
});
}
function ensureGrantModal() {
if ($('grantModal')) return;
var modal = createNode('div', 'block-modal grant-modal');
modal.id = 'grantModal';
modal.hidden = true;
modal.setAttribute('aria-hidden', 'true');
modal.innerHTML =
'<button class="block-backdrop" id="grantBackdrop" type="button" aria-label="Close"></button>' +
'<section class="block-dialog" role="dialog" aria-modal="true" aria-labelledby="grantDialogTitle">' +
'<div class="block-dialog-header"><h2 id="grantDialogTitle">Send</h2>' +
'<button class="block-close" id="grantCloseButton" type="button" aria-label="Close"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6 6 18"/></svg></button></div>' +
'<div class="block-dialog-body"><div class="block-field"><label for="grantSearchInput">User ID</label>' +
'<div class="block-search-row"><input id="grantSearchInput" type="text" inputmode="numeric" autocomplete="off" placeholder="Enter user ID"/><button id="grantSearchButton" type="button">Search</button></div></div>' +
'<div class="block-search-result" id="grantSearchResult" aria-live="polite"></div>' +
'<div class="level-panel" id="grantPanel" hidden><div class="block-section-title" id="grantOptionTitle">Select item</div><div class="grant-option-grid" id="grantOptionGrid"></div><div class="block-section-title" id="grantDurationTitle" hidden>Select duration</div><div class="duration-grid grant-duration-grid" id="grantDurationGrid" hidden></div><button class="confirm-level-button" id="confirmGrantButton" type="button" disabled>Send now</button></div></div></section>';
document.querySelector('.manager-center').appendChild(modal);
$('grantBackdrop').addEventListener('click', closeGrantModal);
$('grantCloseButton').addEventListener('click', closeGrantModal);
$('grantSearchButton').addEventListener('click', searchGrantUser);
$('grantSearchInput').addEventListener('keydown', function (event) {
if (event.key === 'Enter') searchGrantUser();
});
$('confirmGrantButton').addEventListener('click', confirmGrant);
}
function openGrantModal(action) {
if (pageState.denied) {
toast('Permission denied.');
return;
}
if (!ensureActionAllowed(action)) return;
ensureGrantModal();
grantState.action = action;
grantState.resourceType = RESOURCE_TYPES[action] || '';
grantState.title = ACTION_LABELS[action] || 'Send';
grantState.searchedUser = null;
grantState.selectedResourceIds = [];
grantState.selectedResourceDurationMs =
DEFAULT_RESOURCE_GRANT_DURATION_MS;
grantState.selectedVipLevel = 0;
$('grantDialogTitle').textContent = grantState.title;
$('grantSearchInput').value = '';
$('grantSearchResult').innerHTML = '';
$('grantPanel').hidden = true;
$('grantModal').hidden = false;
$('grantModal').setAttribute('aria-hidden', 'false');
renderGrantOptions();
if (grantState.resourceType)
loadGrantResources(grantState.resourceType);
setTimeout(function () {
$('grantSearchInput').focus();
}, 30);
}
function closeGrantModal() {
var modal = $('grantModal');
if (!modal) return;
modal.hidden = true;
modal.setAttribute('aria-hidden', 'true');
}
function loadGrantResources(resourceType) {
if (pageState.resourcesByType[resourceType]) {
renderGrantOptions();
return;
}
api.listResources(resourceType, 20)
.then(function (data) {
pageState.resourcesByType[resourceType] =
(data && data.items) || [];
renderGrantOptions();
})
.catch(showError);
}
function searchGrantUser() {
var value = $('grantSearchInput').value.trim();
if (!value) {
toast('Enter user ID.');
return;
}
// 搜索入口也必须携带当前赠送动作Send VIP 只校验 VIP 赠送权限,不能落回资源赠送权限导致误拒。
apiSearchUser(value, '', grantState.action)
.then(function (items) {
if (!items.length) {
grantState.searchedUser = null;
toast('No user found.');
return;
}
grantState.searchedUser = items[0];
var container = $('grantSearchResult');
container.innerHTML = '';
container.appendChild(
renderUserRow(
grantState.searchedUser,
'Select',
function () {
$('grantPanel').hidden = false;
renderGrantOptions();
}
)
);
$('grantPanel').hidden = false;
renderGrantOptions();
})
.catch(showError);
}
function renderGrantOptions() {
var grid = $('grantOptionGrid');
if (!grid) return;
grid.innerHTML = '';
grid.classList.toggle(
'is-resource-grid',
grantState.action !== 'send-vip'
);
var durationTitle = $('grantDurationTitle');
var durationGrid = $('grantDurationGrid');
if (durationTitle) {
durationTitle.hidden = false;
durationTitle.textContent =
grantState.action === 'send-vip'
? 'Duration: 30 days'
: 'Select duration';
}
if (durationGrid) {
durationGrid.innerHTML = '';
durationGrid.hidden = grantState.action === 'send-vip';
}
if (grantState.action === 'send-vip') {
$('grantOptionTitle').textContent = 'Select VIP level';
var activeVipLevel = currentVipLevel(grantState.searchedUser || {});
if (
grantState.selectedVipLevel &&
activeVipLevel > grantState.selectedVipLevel
) {
grantState.selectedVipLevel = 0;
}
// VIP grants must stay on the explicit product allow-list so the UI never sends a level the backend has not opened.
VIP_GRANT_LEVELS.forEach(function (level) {
var button = createNode('button', '', 'VIP ' + level);
var blockedByCurrentVip = activeVipLevel > level;
button.type = 'button';
button.disabled = blockedByCurrentVip;
if (blockedByCurrentVip) {
button.title = 'Current VIP is higher';
}
button.classList.toggle(
'is-selected',
grantState.selectedVipLevel === level
);
button.addEventListener('click', function () {
if (blockedByCurrentVip) return;
grantState.selectedVipLevel = level;
renderGrantOptions();
});
grid.appendChild(button);
});
if (activeVipLevel > 1) {
grid.appendChild(
createNode(
'div',
'vip-grant-note',
activeVipLevel > Math.max.apply(null, VIP_GRANT_LEVELS)
? 'Current VIP is higher than manager-center gift levels.'
: 'Lower VIP levels are disabled because the user already has a higher VIP.'
)
);
}
} else {
$('grantOptionTitle').textContent = 'Select ' + resourceKindLabel();
var resources =
pageState.resourcesByType[grantState.resourceType] || [];
var selectedResourceSet = grantState.selectedResourceIds.reduce(
function (result, resourceId) {
result[String(resourceId)] = true;
return result;
},
{}
);
if (!resources.length) {
grid.appendChild(
createNode('div', 'blocked-empty', 'No resource available.')
);
}
resources.forEach(function (resource) {
var button = createNode('button', 'grant-resource-option');
var imageURL = resourceImageUrl(resource);
var resourceID = resource.resource_id;
var resourceKey = String(resourceID);
button.type = 'button';
button.setAttribute(
'aria-label',
'Select ' + resourceKindLabel()
);
button.classList.toggle(
'is-selected',
!!selectedResourceSet[resourceKey]
);
if (imageURL) {
var media = createNode('span', 'grant-resource-media');
var img = document.createElement('img');
img.src = imageURL;
img.alt = '';
media.appendChild(img);
button.appendChild(media);
} else {
button.appendChild(
createNode('span', 'grant-resource-fallback')
);
}
button.addEventListener('click', function () {
// 资源赠送是素材发放,不是互斥装备选择;同一弹窗允许一次选择多个素材,由后端拆成独立 grant 命令保证可重试。
if (selectedResourceSet[resourceKey]) {
grantState.selectedResourceIds =
grantState.selectedResourceIds.filter(
function (selectedID) {
return String(selectedID) !== resourceKey;
}
);
} else {
grantState.selectedResourceIds =
grantState.selectedResourceIds.concat(resourceID);
}
renderGrantOptions();
});
grid.appendChild(button);
});
if (grantState.selectedResourceIds.length) {
grid.appendChild(
createNode(
'div',
'resource-selected-note',
grantState.selectedResourceIds.length + ' selected'
)
);
}
if (durationGrid) {
RESOURCE_GRANT_DURATIONS.forEach(function (item) {
var durationButton = createNode('button', '', item.label);
durationButton.type = 'button';
durationButton.classList.toggle(
'is-selected',
grantState.selectedResourceDurationMs === item.ms
);
durationButton.addEventListener('click', function () {
grantState.selectedResourceDurationMs = item.ms;
renderGrantOptions();
});
durationGrid.appendChild(durationButton);
});
}
}
var confirm = $('confirmGrantButton');
if (confirm) {
confirm.disabled =
!grantState.searchedUser ||
grantState.submitting ||
(grantState.action === 'send-vip'
? !grantState.selectedVipLevel ||
currentVipLevel(grantState.searchedUser) >
grantState.selectedVipLevel
: !grantState.selectedResourceIds.length ||
!grantState.selectedResourceDurationMs);
}
}
function confirmGrant() {
if (!grantState.searchedUser || grantState.submitting) return;
grantState.submitting = true;
renderGrantOptions();
var payload = {
command_id: commandId('manager-grant'),
target_user_id: userId(grantState.searchedUser),
reason: 'manager_center',
};
var request;
if (grantState.action === 'send-vip') {
payload.level = grantState.selectedVipLevel;
request = api.grantVip(payload);
} else {
payload.resource_ids = grantState.selectedResourceIds.slice();
payload.duration_ms = grantState.selectedResourceDurationMs;
request = api.grantResource(payload);
}
request
.then(function () {
toast('Sent successfully.');
closeGrantModal();
})
.catch(showError)
.finally(function () {
grantState.submitting = false;
renderGrantOptions();
});
}
function ensureBDLeaderModal() {
if ($('bdLeaderModal')) return;
var modal = createNode('div', 'block-modal bd-leader-modal');
modal.id = 'bdLeaderModal';
modal.hidden = true;
modal.setAttribute('aria-hidden', 'true');
modal.innerHTML =
'<button class="block-backdrop" id="bdLeaderBackdrop" type="button" aria-label="Close"></button>' +
'<section class="block-dialog" role="dialog" aria-modal="true" aria-labelledby="bdLeaderDialogTitle">' +
'<div class="block-dialog-header"><h2 id="bdLeaderDialogTitle">Add BD Leader</h2>' +
'<button class="block-close" id="bdLeaderCloseButton" type="button" aria-label="Close"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6 6 18"/></svg></button></div>' +
'<div class="block-dialog-body"><div class="block-field"><label for="bdLeaderSearchInput">User ID</label>' +
'<div class="block-search-row"><input id="bdLeaderSearchInput" type="text" inputmode="numeric" autocomplete="off" placeholder="Enter user ID"/><button id="bdLeaderSearchButton" type="button">Search</button></div></div>' +
'<div class="block-search-result" id="bdLeaderSearchResult" aria-live="polite"></div>' +
'<div class="level-panel" id="bdLeaderPanel" hidden><div class="block-section-title">Add this user as BD Leader</div>' +
'<button class="confirm-level-button" id="confirmBDLeaderButton" type="button" disabled>Add BD Leader</button></div></div></section>';
document.querySelector('.manager-center').appendChild(modal);
$('bdLeaderBackdrop').addEventListener('click', closeBDLeaderModal);
$('bdLeaderCloseButton').addEventListener('click', closeBDLeaderModal);
$('bdLeaderSearchButton').addEventListener('click', searchBDLeaderUser);
$('bdLeaderSearchInput').addEventListener('keydown', function (event) {
if (event.key === 'Enter') searchBDLeaderUser();
});
$('confirmBDLeaderButton').addEventListener('click', confirmBDLeader);
}
function openBDLeaderModal(action) {
if (pageState.denied) {
toast('Permission denied.');
return;
}
if (!ensureActionAllowed(action)) return;
ensureBDLeaderModal();
bdLeaderState.action = action;
bdLeaderState.title = ACTION_LABELS[action] || 'Add BD Leader';
bdLeaderState.searchedUser = null;
bdLeaderState.submitting = false;
$('bdLeaderDialogTitle').textContent = bdLeaderState.title;
$('confirmBDLeaderButton').textContent = bdLeaderState.title;
$('bdLeaderSearchInput').value = '';
$('bdLeaderSearchResult').innerHTML = '';
$('bdLeaderPanel').hidden = true;
renderBDLeaderActions();
$('bdLeaderModal').hidden = false;
$('bdLeaderModal').setAttribute('aria-hidden', 'false');
setTimeout(function () {
$('bdLeaderSearchInput').focus();
}, 30);
}
function closeBDLeaderModal() {
var modal = $('bdLeaderModal');
if (!modal) return;
modal.hidden = true;
modal.setAttribute('aria-hidden', 'true');
}
function searchBDLeaderUser() {
var value = $('bdLeaderSearchInput').value.trim();
if (!value) {
toast('Enter user ID.');
return;
}
// BD/Admin/Superadmin 共用弹窗,搜索阶段先按当前按钮的能力开关过滤;写入口仍会二次校验真实落库能力。
apiSearchUser(value, '', bdLeaderState.action)
.then(function (items) {
if (!items.length) {
bdLeaderState.searchedUser = null;
$('bdLeaderSearchResult').innerHTML = '';
$('bdLeaderPanel').hidden = true;
renderBDLeaderActions();
toast('No user found.');
return;
}
bdLeaderState.searchedUser = items[0];
var container = $('bdLeaderSearchResult');
container.innerHTML = '';
container.appendChild(
renderUserRow(
bdLeaderState.searchedUser,
'Select',
function () {
$('bdLeaderPanel').hidden = false;
renderBDLeaderActions();
}
)
);
$('bdLeaderPanel').hidden = false;
renderBDLeaderActions();
})
.catch(showError);
}
function renderBDLeaderActions() {
var confirm = $('confirmBDLeaderButton');
if (!confirm) return;
confirm.disabled =
!bdLeaderState.searchedUser || bdLeaderState.submitting;
}
function confirmBDLeader() {
if (!bdLeaderState.searchedUser) {
toast('Search a user first.');
return;
}
if (!api || !api.createBDLeader) {
toast('API not available.');
return;
}
if (bdLeaderState.submitting) return;
bdLeaderState.submitting = true;
renderBDLeaderActions();
api.createBDLeader({
command_id: commandId('manager-bd-leader'),
target_user_id: userId(bdLeaderState.searchedUser),
reason: 'manager_center',
})
.then(function () {
toast('BD Leader added.');
closeBDLeaderModal();
})
.catch(showError)
.finally(function () {
bdLeaderState.submitting = false;
renderBDLeaderActions();
});
}
function normalizeCountry(item) {
item = item || {};
var code = String(
item.country_code ||
item.countryCode ||
item.code ||
item.alphaTwo ||
item.alpha_two ||
''
)
.trim()
.toUpperCase();
return {
code: code,
name:
item.country_display_name ||
item.countryDisplayName ||
item.country_name ||
item.countryName ||
item.name ||
code,
};
}
function countryLabel(country) {
if (!country) return '';
return country.name && country.name !== country.code
? country.name + ' · ' + country.code
: country.code;
}
function selectedCountry() {
for (var i = 0; i < pageState.countries.length; i += 1) {
if (
pageState.countries[i].code === countryState.selectedCountryCode
) {
return pageState.countries[i];
}
}
return null;
}
function loadCountries() {
if (pageState.countries.length) {
renderCountryOptions();
return Promise.resolve(pageState.countries);
}
if (!api || !api.listCountries) {
return Promise.reject(new Error('API not available.'));
}
return api
.listCountries()
.then(function (data) {
var list = (data && (data.countries || data.items)) || [];
pageState.countries = list
.map(normalizeCountry)
.filter(function (country) {
return !!country.code;
});
renderCountryOptions();
return pageState.countries;
})
.catch(showError);
}
function ensureCountryModal() {
if ($('countryModal')) return;
var modal = createNode('div', 'block-modal country-modal');
modal.id = 'countryModal';
modal.hidden = true;
modal.setAttribute('aria-hidden', 'true');
modal.innerHTML =
'<button class="block-backdrop" id="countryBackdrop" type="button" aria-label="Close"></button>' +
'<section class="block-dialog" role="dialog" aria-modal="true" aria-labelledby="countryDialogTitle">' +
'<div class="block-dialog-header"><h2 id="countryDialogTitle">Transfer User Country</h2>' +
'<button class="block-close" id="countryCloseButton" type="button" aria-label="Close"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6 6 18"/></svg></button></div>' +
'<div class="block-dialog-body"><div class="block-field"><label for="countrySearchInput">User ID</label>' +
'<div class="block-search-row"><input id="countrySearchInput" type="text" inputmode="numeric" autocomplete="off" placeholder="Enter user ID"/><button id="countrySearchButton" type="button">Search</button></div></div>' +
'<div class="block-search-result" id="countrySearchResult" aria-live="polite"></div>' +
'<div class="level-panel" id="countryPanel" hidden><div class="block-section-title">Select country</div><div class="grant-option-grid" id="countryOptionGrid"></div><button class="confirm-level-button" id="confirmCountryButton" type="button" disabled>Confirm Transfer</button></div></div></section>';
document.querySelector('.manager-center').appendChild(modal);
$('countryBackdrop').addEventListener('click', closeCountryModal);
$('countryCloseButton').addEventListener('click', closeCountryModal);
$('countrySearchButton').addEventListener('click', searchCountryUser);
$('countrySearchInput').addEventListener('keydown', function (event) {
if (event.key === 'Enter') searchCountryUser();
});
$('confirmCountryButton').addEventListener(
'click',
confirmCountryTransfer
);
}
function openCountryModal() {
if (pageState.denied) {
toast('Permission denied.');
return;
}
if (!ensureActionAllowed('transfer-user-country')) return;
ensureCountryModal();
countryState.searchedUser = null;
countryState.selectedCountryCode = '';
countryState.submitting = false;
$('countrySearchInput').value = '';
$('countrySearchResult').innerHTML = '';
$('countryPanel').hidden = true;
$('countryModal').hidden = false;
$('countryModal').setAttribute('aria-hidden', 'false');
loadCountries();
renderCountryActions();
setTimeout(function () {
$('countrySearchInput').focus();
}, 30);
}
function closeCountryModal() {
var modal = $('countryModal');
if (!modal) return;
modal.hidden = true;
modal.setAttribute('aria-hidden', 'true');
}
function searchCountryUser() {
var value = $('countrySearchInput').value.trim();
if (!value) {
toast('Enter user ID.');
return;
}
// 转国家是唯一允许跨国家搜索的入口,必须同时携带 scope=all 和动作,避免额外依赖资源赠送权限。
apiSearchUser(value, 'all', 'transfer-user-country')
.then(function (items) {
if (!items.length) {
countryState.searchedUser = null;
$('countrySearchResult').innerHTML = '';
$('countryPanel').hidden = true;
renderCountryActions();
toast('No user found.');
return;
}
countryState.searchedUser = items[0];
var container = $('countrySearchResult');
container.innerHTML = '';
container.appendChild(
renderUserRow(
countryState.searchedUser,
'Select',
function () {
$('countryPanel').hidden = false;
renderCountryActions();
}
)
);
$('countryPanel').hidden = false;
renderCountryActions();
})
.catch(showError);
}
function renderCountryOptions() {
var grid = $('countryOptionGrid');
if (!grid) return;
grid.innerHTML = '';
if (!pageState.countries.length) {
grid.appendChild(
createNode('div', 'blocked-empty', 'No country available.')
);
renderCountryActions();
return;
}
pageState.countries.forEach(function (country) {
var button = createNode('button', 'country-option');
button.type = 'button';
button.classList.toggle(
'is-selected',
countryState.selectedCountryCode === country.code
);
button.appendChild(createNode('span', '', countryLabel(country)));
button.addEventListener('click', function () {
countryState.selectedCountryCode = country.code;
renderCountryOptions();
renderCountryActions();
});
grid.appendChild(button);
});
renderCountryActions();
}
function renderCountryActions() {
var confirm = $('confirmCountryButton');
if (!confirm) return;
var currentCountry = String(
(countryState.searchedUser && countryState.searchedUser.country) ||
''
).toUpperCase();
confirm.disabled =
!countryState.searchedUser ||
!countryState.selectedCountryCode ||
countryState.selectedCountryCode === currentCountry ||
countryState.submitting;
var country = selectedCountry();
confirm.textContent = country
? 'Transfer to ' + countryLabel(country)
: 'Confirm Transfer';
}
function confirmCountryTransfer() {
if (!countryState.searchedUser) {
toast('Search a user first.');
return;
}
if (!countryState.selectedCountryCode) {
toast('Select a country.');
return;
}
if (countryState.submitting) return;
countryState.submitting = true;
renderCountryActions();
api.transferUserCountry({
command_id: commandId('manager-country'),
target_user_id: userId(countryState.searchedUser),
country: countryState.selectedCountryCode,
reason: 'manager_center_country_transfer',
})
.then(function () {
toast('User country transferred.');
closeCountryModal();
})
.catch(showError)
.finally(function () {
countryState.submitting = false;
renderCountryActions();
});
}
function copyManagerId() {
var value = $('managerDisplayId')
? $('managerDisplayId').textContent.trim()
: '';
if (!value) return;
if (window.navigator && window.navigator.clipboard) {
window.navigator.clipboard
.writeText(value)
.then(function () {
toast('Copied.');
})
.catch(function () {
toast(value);
});
return;
}
toast(value);
}
function bindEvents() {
var backButton = $('backButton');
if (backButton) backButton.addEventListener('click', goBack);
var copyButton = document.querySelector('.manager-copy-button');
if (copyButton) copyButton.addEventListener('click', copyManagerId);
document.querySelectorAll('[data-action]').forEach(function (button) {
button.addEventListener('click', function () {
var action = button.getAttribute('data-action');
if (!ensureActionAllowed(action)) {
return;
}
if (action === 'block-user') {
openBlockModal();
return;
}
if (action === 'update-user-level') {
openLevelModal();
return;
}
if (action === 'transfer-user-country') {
openCountryModal();
return;
}
if (
action === 'add-bd-leader' ||
action === 'add-admin' ||
action === 'add-superadmin'
) {
openBDLeaderModal(action);
return;
}
openGrantModal(action);
});
});
setupLevelGrid();
ensureTrackSelector();
var closeButton = $('blockCloseButton');
var backdrop = $('blockBackdrop');
var searchButton = $('blockSearchButton');
var searchInput = $('blockSearchInput');
var confirmButton = $('confirmBlockButton');
var levelCloseButton = $('levelCloseButton');
var levelBackdrop = $('levelBackdrop');
var levelSearchButton = $('levelSearchButton');
var levelSearchInput = $('levelSearchInput');
var confirmLevelButton = $('confirmLevelButton');
if (closeButton) closeButton.addEventListener('click', closeBlockModal);
if (backdrop) backdrop.addEventListener('click', closeBlockModal);
if (levelCloseButton)
levelCloseButton.addEventListener('click', closeLevelModal);
if (levelBackdrop)
levelBackdrop.addEventListener('click', closeLevelModal);
if (searchButton)
searchButton.addEventListener('click', searchBlockUser);
if (searchInput) {
searchInput.addEventListener('keydown', function (event) {
if (event.key === 'Enter') searchBlockUser();
});
}
if (levelSearchButton)
levelSearchButton.addEventListener('click', searchLevelUser);
if (levelSearchInput) {
levelSearchInput.addEventListener('keydown', function (event) {
if (event.key === 'Enter') searchLevelUser();
});
}
if (confirmButton)
confirmButton.addEventListener('click', confirmBlockUser);
if (confirmLevelButton) {
confirmLevelButton.addEventListener('click', confirmLevelUpdate);
}
document
.querySelectorAll('#durationGrid [data-duration-ms]')
.forEach(function (button) {
button.addEventListener('click', function () {
selectDuration(
button.getAttribute('data-duration-ms'),
button.getAttribute('data-duration-label')
);
});
});
document
.querySelectorAll('#levelGrid [data-level]')
.forEach(function (button) {
button.addEventListener('click', function () {
selectLevel(button.getAttribute('data-level'));
});
});
}
function init() {
bindEvents();
renderBlockedList();
// 首屏不预渲染仪表盘:骨架屏保持到 loadOverview 的任一分支调用 renderDashboard 收敛。
loadOverview();
if (window.HyAppBridge && window.HyAppBridge.ready) {
window.HyAppBridge.ready({ page: 'manager-center' });
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();