import { clearAuthSession, getAuthContext, startAuthSync, switchAccount, } from '/static/portal/common/auth.js?v=20260913'; import { escapeHtml } from '/static/portal/common/formatters.js'; import { mountCustomerServiceWidget } from '/static/portal/common/customer-service-widget/widget.js'; const PUBLIC_LINKS = [ ['home', '首页', '/portal/guest/home/'], ['products', '基金产品', '/portal/guest/products/'], ['ranking', '基金排行', '/portal/guest/products/?view=ranking'], ]; const CUSTOMER_LINKS = [ ['dashboard', '资产总览', '/portal/customer/dashboard/'], ['holdings', '我的持仓', '/portal/customer/holdings/'], ['profit-loss', '收益明细', '/portal/customer/profit-loss/'], ['orders', '交易记录', '/portal/customer/orders/'], ['transactions', '成交明细', '/portal/customer/transactions/'], ['risk-questionnaire', '风险测评', '/portal/customer/risk-questionnaire/'], ]; const RISK_LINKS = [ ['risk-dashboard', '风险工作台', '/portal/employee-risk/dashboard/'], ]; const ADVISOR_LINKS = [ ['advisor-dashboard', '投顾工作台', '/portal/employee-advisor/dashboard/'], ['public-products', '公开产品', '/portal/guest/products/'], ]; const OPERATOR_LINKS = [ ['operator-dashboard', '运营工作台', '/portal/employee-operations/dashboard/'], ['public-products', '公开产品', '/portal/guest/products/'], ]; const ADMIN_LINKS = [ ['admin-workspace', '平台治理', '/portal/employee-console/workspace/'], ['risk-dashboard', '风控中心', '/portal/employee-risk/dashboard/'], ]; const ROLE_LABELS = Object.freeze({ customer: '客户账户', risk_operator: '风控人员', operator: '运营人员', advisor: '投顾人员', admin: '管理员', super_admin: '超级管理员', }); function accountActions(context) { const username = escapeHtml(context.username || '已登录用户'); const role = context.roles.map((item) => ROLE_LABELS[item]).find(Boolean) || '已认证账户'; return `
`; } function headerMarkup(links, active, context, mode) { const nav = links.map(([key, label, href]) => `${label}`).join(''); const actions = context ? accountActions(context) : '员工入口登录'; const menuButton = mode === 'customer' ? '' : ''; return ``; } function footerMarkup() { return '
'; } export function mountShell({ active, mode = 'public' }) { startAuthSync(); document.body.classList.add(`portal-${mode}`); const context = getAuthContext(); const links = mode === 'customer' ? CUSTOMER_LINKS : mode === 'risk' ? RISK_LINKS : mode === 'advisor' ? ADVISOR_LINKS : mode === 'operator' ? OPERATOR_LINKS : mode === 'admin' ? ADMIN_LINKS : PUBLIC_LINKS; document.body.insertAdjacentHTML('afterbegin', headerMarkup(links, active, context, mode)); document.body.insertAdjacentHTML('beforeend', footerMarkup()); const menu = document.querySelector('[data-menu]'); const nav = document.querySelector('[data-nav]'); menu?.addEventListener('click', () => { const open = nav.classList.toggle('site-header__nav--open'); menu.setAttribute('aria-expanded', String(open)); }); const accountMenu = document.querySelector('[data-account-menu]'); const accountToggle = document.querySelector('[data-account-toggle]'); const accountPopover = document.querySelector('[data-account-popover]'); const closeAccountMenu = () => { accountMenu?.classList.remove('account-menu--open'); accountToggle?.setAttribute('aria-expanded', 'false'); }; accountToggle?.addEventListener('click', () => { const open = accountMenu.classList.toggle('account-menu--open'); accountToggle.setAttribute('aria-expanded', String(open)); if (open) accountPopover?.querySelector('[role="menuitem"]')?.focus(); }); document.addEventListener('click', (event) => { if (accountMenu && !accountMenu.contains(event.target)) closeAccountMenu(); }); document.addEventListener('keydown', (event) => { if (event.key === 'Escape') { closeAccountMenu(); accountToggle?.focus(); } }); document.querySelector('[data-switch-account]')?.addEventListener('click', () => { switchAccount(mode === 'customer' ? 'customer' : 'staff'); }); document.querySelector('[data-logout]')?.addEventListener('click', () => { clearAuthSession(); window.location.assign('/portal/guest/home/?reason=signed-out'); }); const networkStatus = document.querySelector('[data-network-status]'); window.addEventListener('offline', () => { networkStatus.textContent = '网络已断开'; networkStatus.classList.add('network-status--visible'); }); window.addEventListener('online', () => { networkStatus.textContent = '网络已恢复,请点击刷新'; networkStatus.classList.add('network-status--visible'); window.setTimeout(() => networkStatus.classList.remove('network-status--visible'), 3000); }); window.addEventListener('portal:error', (event) => { const detail = event.detail || {}; networkStatus.textContent = detail.message || '请求未完成'; networkStatus.classList.add('network-status--visible'); }); window.addEventListener('portal:auth-expired', () => { networkStatus.textContent = '登录已过期,请重新登录'; networkStatus.classList.add('network-status--visible'); }); if (new URLSearchParams(window.location.search).get('reason') === 'signed-out') { networkStatus.textContent = '您已安全退出登录'; networkStatus.classList.add('network-status--visible'); window.setTimeout(() => networkStatus.classList.remove('network-status--visible'), 3200); window.history.replaceState({}, '', window.location.pathname); } const observer = new MutationObserver(() => { const traceId = document.documentElement.dataset.traceId || ''; const watermark = document.querySelector('[data-trace-watermark]'); if (watermark) watermark.textContent = traceId ? `追踪标识 ${traceId}` : ''; }); observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-trace-id'] }); const isCustomerContext = context?.roles?.includes('customer'); const canUseCustomerService = mode === 'customer' || mode === 'public' && (!context || isCustomerContext); if (canUseCustomerService) { const style = document.createElement('link'); style.rel = 'stylesheet'; style.href = '/static/portal/common/customer-service-widget/widget.css?v=20260913'; document.head.appendChild(style); mountCustomerServiceWidget(isCustomerContext ? 'customer' : mode); } }