(function () {
'use strict';
if (window.__adminInteractionLayerInstalled) return;
window.__adminInteractionLayerInstalled = true;
var toastRegion = document.getElementById('adminToastRegion');
var confirmMask = document.getElementById('adminConfirmModal');
var confirmTitle = document.getElementById('adminConfirmTitle');
var confirmMessage = document.getElementById('adminConfirmMessage');
var confirmAccept = document.getElementById('adminConfirmAccept');
var confirmCancel = document.getElementById('adminConfirmCancel');
var guide = document.getElementById('adminOperationGuide');
var guideText = document.getElementById('adminOperationGuideText');
var guideSteps = document.getElementById('adminOperationGuideSteps');
var guideToggle = document.getElementById('adminOperationGuideToggle');
var pendingConfirmButton = null;
var previousFocus = null;
var busyButton = null;
var busyWasDisabled = false;
var activeFetches = 0;
var activeXhrs = 0;
var mainContent = document.getElementById('adminContent');
var guides = {
users: { text: '先用筛选定位账号,再编辑角色和菜单权限;删除账号会要求二次确认。', steps: ['筛选账号', '编辑权限', '确认保存'] },
columns: { text: '菜单会影响后台和软件端的可见范围。建议先填写名称、标识和页面路径,再设置上级菜单。', steps: ['新增或调整菜单', '设置层级', '检查权限'] },
'dedupe-total-data': { text: '支持按关键词、用户、分组和国家筛选;导入前先确认所选分组,导出会按日期范围生成文件。', steps: ['选择分组', '筛选或导入', '核对并导出'] },
'invalid-asin-data': { text: '维护不符合规则的 ASIN 或品牌。添加后可使用上方筛选快速回查。', steps: ['填写 ASIN/品牌', '选择分组', '保存并回查'] },
'shop-keys': { text: '紫鸟令牌属于敏感配置。白名单状态可悬停查看检测详情,编辑前请先核对账号名称。', steps: ['新增或筛选密钥', '查看白名单状态', '编辑或删除'] },
'shop-manage': { text: '店铺信息按分组管理。长商城名会自动缩略,悬停即可查看完整内容。', steps: ['选择分组', '维护店铺信息', '筛选核对结果'] },
'skip-price-asin': { text: '最低价 ASIN 以店铺和国家为单位维护。每个国家可单独编辑 ASIN 与最低价。', steps: ['选择店铺和国家', '填写 ASIN/最低价', '保存或批量导入'] },
'query-asin': { text: '查询 ASIN 按店铺和国家独立维护。空国家列可直接编辑,已有记录可修改或删除。', steps: ['选择店铺和国家', '维护各国 ASIN', '筛选或导出'] },
'product-categories': { text: '类目树支持展开查看层级。搜索、编辑和删除都在同一列表中完成。', steps: ['搜索类目', '展开层级', '新增或编辑'] },
'image-video-tasks': { text: '可先使用筛选缩小任务范围,再查看任务状态、结果和权限范围。', steps: ['设置筛选', '查看任务结果', '按需处理任务'] },
'shop-data-crawl-tasks': { text: '店铺数据任务按状态和时间筛选。批量操作前请核对已选任务。', steps: ['筛选任务', '检查状态', '执行批量操作'] },
history: { text: '生成记录可按用户和时间范围回溯,用于核对结果文件和执行时间。', steps: ['设置时间范围', '筛选记录', '查看结果预览'] },
version: { text: '上传版本后请核对版本号和下载链接,再通知用户更新。', steps: ['上传压缩包', '检查版本记录', '维护历史版本'] },
'digital-human-version': { text: '数字人版本需先上传草稿,再发布并标记最新版本。', steps: ['上传草稿', '确认更新日志', '发布或设为最新'] }
};
function cleanText(value) {
return String(value == null ? '' : value).replace(/\s+/g, ' ').trim();
}
function showToast(message, type) {
var value = cleanText(message);
if (!value || !toastRegion) return;
var item = document.createElement('div');
item.className = 'admin-toast' + (type === 'error' ? ' is-error' : type === 'info' ? ' is-info' : '');
var content = document.createElement('span');
content.className = 'admin-toast__text';
content.textContent = value;
item.appendChild(content);
toastRegion.appendChild(item);
window.setTimeout(function () {
item.style.opacity = '0';
item.style.transform = 'translateY(-6px)';
item.style.transition = 'opacity 160ms ease, transform 160ms ease';
window.setTimeout(function () { item.remove(); }, 180);
}, type === 'error' ? 5200 : 3200);
}
window.__adminToast = showToast;
function activeTabName() {
var tab = document.querySelector('#adminMenu .tab.active');
return tab ? (tab.dataset.tab || '') : '';
}
function updateGuide(tabName) {
if (!guide || !guideText || !guideSteps) return;
var config = guides[tabName] || { text: '先使用筛选定位记录,再进行新增、编辑、导出等操作。涉及删除的数据会要求二次确认。', steps: ['选择筛选条件', '处理记录', '核对反馈'] };
guideText.textContent = config.text;
guideSteps.innerHTML = (config.steps || []).map(function (step) { return '
' + step + ''; }).join('');
guide.dataset.tab = tabName || '';
}
window.__adminUpdateOperationGuide = updateGuide;
function setGuideCollapsed(collapsed) {
if (!guide || !guideToggle) return;
guide.classList.toggle('is-collapsed', collapsed);
guideToggle.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
guideToggle.textContent = collapsed ? '展开提示' : '收起提示';
try { localStorage.setItem('shufuAdminGuideCollapsed', collapsed ? '1' : '0'); } catch (error) {}
}
if (guideToggle) {
var collapsed = false;
try { collapsed = localStorage.getItem('shufuAdminGuideCollapsed') === '1'; } catch (error) {}
setGuideCollapsed(collapsed);
guideToggle.addEventListener('click', function () {
setGuideCollapsed(!guide.classList.contains('is-collapsed'));
});
}
function hashTabName() {
var raw = (window.location.hash || '').replace(/^#/, '');
var match = raw.match(/(?:^|&)tab=([^&]+)/);
return match ? decodeURIComponent(match[1]) : '';
}
function syncTabHash(tabName) {
if (!tabName || !window.history || !window.history.replaceState) return;
var next = '#tab=' + encodeURIComponent(tabName);
if (window.location.hash !== next) window.history.replaceState(null, '', next);
}
function navigateToHash(attemptsLeft) {
var tabName = hashTabName();
if (!tabName) {
updateGuide(activeTabName());
return;
}
var tab = document.querySelector('#adminMenu .tab[data-tab="' + tabName + '"]');
if (tab && typeof tab.onclick === 'function') {
if (!tab.classList.contains('active')) tab.click();
else updateGuide(tabName);
return;
}
if (attemptsLeft > 0) {
window.setTimeout(function () { navigateToHash(attemptsLeft - 1); }, 80);
} else {
updateGuide(activeTabName());
}
}
function closeConfirm() {
if (!confirmMask) return;
confirmMask.classList.remove('show');
confirmMask.setAttribute('aria-hidden', 'true');
document.body.classList.remove('admin-confirm-open');
var focus = previousFocus;
pendingConfirmButton = null;
previousFocus = null;
if (focus && focus.isConnected) focus.focus();
}
function openConfirm(button) {
if (!confirmMask || !confirmMessage || !confirmAccept) return;
pendingConfirmButton = button;
previousFocus = document.activeElement;
var customMessage = cleanText(button.dataset.confirmMessage);
var label = cleanText(button.getAttribute('aria-label') || button.textContent || '删除');
var subject = cleanText(button.dataset.name || button.dataset.value || button.dataset.shopManageName || button.dataset.shopName || button.dataset.ziniaoAccountName || '');
var country = cleanText(button.dataset.country || '');
if (!customMessage && country && subject) subject += '(' + country + ')';
if (!customMessage && subject) customMessage = (button.classList.contains('btn-danger') ? '确认删除“' : '确认执行“') + subject + '”吗?此操作可能影响已有数据。';
if (confirmTitle) confirmTitle.textContent = button.classList.contains('btn-danger') ? '删除前确认' : '请确认操作';
confirmMessage.textContent = customMessage || ('确认执行“' + label + '”吗?此操作可能影响已有数据。');
confirmAccept.textContent = button.dataset.confirmActionLabel || (button.classList.contains('btn-danger') ? '确认删除' : '确认操作');
confirmMask.classList.add('show');
confirmMask.setAttribute('aria-hidden', 'false');
document.body.classList.add('admin-confirm-open');
window.setTimeout(function () { confirmAccept.focus(); }, 0);
}
function keepConfirmFocus(event) {
if (!confirmMask || !confirmMask.classList.contains('show') || event.key !== 'Tab') return;
var focusable = Array.prototype.filter.call(confirmMask.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'), function (el) {
return !el.disabled && el.offsetParent !== null;
});
if (!focusable.length) return;
var first = focusable[0];
var last = focusable[focusable.length - 1];
if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus(); }
else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus(); }
}
if (confirmCancel) confirmCancel.addEventListener('click', closeConfirm);
if (confirmMask) confirmMask.addEventListener('click', function (event) {
if (event.target === confirmMask) closeConfirm();
});
if (confirmAccept) confirmAccept.addEventListener('click', function () {
var target = pendingConfirmButton;
closeConfirm();
if (!target) return;
window.__adminConfirmBypass = true;
try { target.click(); }
finally { window.setTimeout(function () { window.__adminConfirmBypass = false; }, 0); }
});
document.addEventListener('keydown', function (event) {
if (event.key === 'Enter' && event.target && event.target.matches && event.target.matches('input:not([type="file"]), select') && !event.target.closest('textarea')) {
var searchScope = event.target.closest('.form-row, .form-box');
var searchButton = searchScope && searchScope.querySelector('button[id^="btnSearch"], button[id*="Search"]');
if (searchButton && !searchButton.disabled) {
event.preventDefault();
searchButton.click();
return;
}
}
if (event.key === 'Escape' && confirmMask && confirmMask.classList.contains('show')) {
event.preventDefault();
closeConfirm();
return;
}
keepConfirmFocus(event);
});
var nativeAlert = window.alert ? window.alert.bind(window) : null;
window.alert = function (message) {
showToast(message, /失败|错误|无权|不能为空|不正确|异常/.test(String(message || '')) ? 'error' : 'info');
};
var nativeConfirm = window.confirm ? window.confirm.bind(window) : null;
window.confirm = function (message) {
if (window.__adminConfirmBypass) return true;
return nativeConfirm ? nativeConfirm(message) : false;
};
function updatePageBusy() {
if (!mainContent) return;
mainContent.setAttribute('aria-busy', activeFetches || activeXhrs ? 'true' : 'false');
}
function startBusy() {
var button = window.__adminLastActionButton;
if (!button || !button.isConnected || button.disabled || button.classList.contains('tab') || button.classList.contains('menu-group-title') || button === confirmAccept) return;
busyButton = button;
busyWasDisabled = button.disabled;
button.classList.add('is-busy');
button.setAttribute('aria-busy', 'true');
button.disabled = true;
}
function finishBusy() {
var finishedButton = busyButton;
if (finishedButton && finishedButton.isConnected) {
finishedButton.classList.remove('is-busy');
finishedButton.removeAttribute('aria-busy');
if (!busyWasDisabled) finishedButton.disabled = false;
}
if (window.__adminLastActionButton === finishedButton) window.__adminLastActionButton = null;
busyButton = null;
busyWasDisabled = false;
}
if (window.fetch) {
var nativeFetch = window.fetch.bind(window);
window.fetch = function () {
activeFetches += 1;
updatePageBusy();
if (activeFetches === 1) startBusy();
var request;
try { request = nativeFetch.apply(window, arguments); }
catch (error) { activeFetches = Math.max(0, activeFetches - 1); if (!activeFetches && !activeXhrs) finishBusy(); throw error; }
return Promise.resolve(request).finally(function () {
activeFetches = Math.max(0, activeFetches - 1);
updatePageBusy();
if (!activeFetches && !activeXhrs) finishBusy();
});
};
}
if (window.XMLHttpRequest) {
var nativeSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function () {
activeXhrs += 1;
updatePageBusy();
if (activeXhrs === 1) startBusy();
this.addEventListener('loadend', function () {
activeXhrs = Math.max(0, activeXhrs - 1);
updatePageBusy();
if (!activeFetches && !activeXhrs) finishBusy();
}, { once: true });
try { return nativeSend.apply(this, arguments); }
catch (error) { activeXhrs = Math.max(0, activeXhrs - 1); if (!activeFetches && !activeXhrs) finishBusy(); throw error; }
};
}
function addButtonHint(button) {
if (!button || button.title) return;
var label = cleanText(button.textContent);
if (label === '编辑') button.title = '编辑当前记录';
else if (label === '删除') button.title = '删除当前记录,需二次确认';
else if (label === '查询') button.title = '按当前筛选条件查询';
else if (/^导出/.test(label)) button.title = '导出当前筛选结果';
else if (/^上传并/.test(label)) button.title = '上传文件并执行相应操作';
else if (label === '管理分组') button.title = '新增、编辑或删除分组';
else if (label === '选择店铺') button.title = '从店铺列表选择并回填';
}
function enhance(root) {
var scope = root && root.querySelectorAll ? root : document;
scope.querySelectorAll('button').forEach(addButtonHint);
scope.querySelectorAll('.table-ellipsis').forEach(function (element) {
if (!element.title) element.title = cleanText(element.textContent);
});
scope.querySelectorAll('.empty-tip').forEach(function (element) { element.setAttribute('role', 'status'); });
scope.querySelectorAll('.msg').forEach(function (element) {
var value = cleanText(element.textContent);
if (!value || (!element.classList.contains('ok') && !element.classList.contains('err'))) return;
var key = value + '|' + element.className;
if (element.dataset.adminToastKey === key) return;
element.dataset.adminToastKey = key;
showToast(value, element.classList.contains('err') ? 'error' : 'success');
});
}
document.addEventListener('click', function (event) {
var button = event.target && event.target.closest ? event.target.closest('button') : null;
if (!button || button.disabled) return;
if (button.classList.contains('tab') || button.classList.contains('menu-group-title')) {
window.__adminLastActionButton = null;
} else if (button !== confirmAccept) {
window.__adminLastActionButton = button;
}
if (button.classList.contains('tab')) {
window.setTimeout(function () {
var tabName = activeTabName();
updateGuide(tabName);
syncTabHash(tabName);
}, 0);
}
if ((!button.matches('.btn-danger') && !button.hasAttribute('data-admin-confirm')) || button === confirmAccept || window.__adminConfirmBypass) return;
event.preventDefault();
event.stopImmediatePropagation();
openConfirm(button);
}, true);
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
enhance(mutation.target && mutation.target.nodeType === 1 ? mutation.target : document);
if (mutation.type === 'attributes' && mutation.target.matches && mutation.target.matches('#adminMenu .tab')) {
window.setTimeout(function () { updateGuide(activeTabName()); }, 0);
}
});
});
enhance(document);
observer.observe(document.body, { childList: true, subtree: true, characterData: true, attributes: true, attributeFilter: ['class'] });
window.addEventListener('hashchange', function () { navigateToHash(0); });
navigateToHash(25);
})();