import { useEffect, useMemo, useRef, useState } from 'react'; import { Outlet, useLocation, useNavigate } from 'react-router-dom'; import { Layout, Menu, Typography, Button, Space } from 'antd'; import type { MenuProps } from 'antd'; import { DashboardOutlined, UserOutlined, ShoppingOutlined, ShopOutlined, TeamOutlined, GiftOutlined, CarOutlined, SafetyOutlined, LogoutOutlined, CloudUploadOutlined, FileTextOutlined, LockOutlined, SettingOutlined, AccountBookOutlined, } from '@ant-design/icons'; import { hasAnySystemSettingsPermission } from '@dukang/shared-types'; import { clearAuth, request, type HqProfile } from '../lib/api'; import { bindAdminEllipsisTitle } from '../lib/ellipsis-title'; const { Header, Sider, Content } = Layout; type MenuItem = NonNullable[number]; const MENU_ITEMS: MenuProps['items'] = [ { key: '/', icon: , label: '概览' }, { key: '/users', icon: , label: '用户' }, { key: '/wechat-bindings', icon: , label: '微信绑定' }, { key: 'products-group', icon: , label: '商品', children: [ { key: '/products', label: '商品列表' }, { key: '/product-detail-templates', label: '详情模板' }, ], }, { key: '/orders', icon: , label: '订单' }, { key: '/promo-codes', icon: , label: '推广码' }, { key: '/wecom-bots', icon: , label: '企微机器人' }, { key: 'stores-group', icon: , label: '门店', children: [ { key: '/stores', label: '门店列表' }, { key: '/store-categories', label: '门店分类' }, { key: '/store-accounts', label: '门店账户' }, { key: '/store-media', label: '门店资源' }, ], }, { key: 'partners-group', icon: , label: '开城', children: [ { key: '/cities', label: '城市' }, { key: '/city-partners', label: '城市合伙人' }, { key: '/city-warehouses', label: '仓库' }, { key: '/fulfillment-providers', label: '仓配管理' }, ], }, { key: 'finance-group', icon: , label: '财务', children: [ { key: '/finance/store-bills', label: '门店账单' }, { key: '/finance/partner-bills', label: '合伙人账单' }, { key: '/finance/winery-bills', label: '酒厂账单' }, ], }, { key: 'benefit-group', icon: , label: '好客权益', children: [ { key: '/benefit/coupons', label: '权益券' }, { key: '/benefit/ledgers', label: '流水' }, { key: '/redeem-records', label: '核销记录' }, { key: '/redeem-pending', label: '待处理核销' }, { key: '/redeem/debug', label: '核销调试' }, ], }, { key: 'deliveries-group', icon: , label: '配送单', children: [ { key: '/deliveries', label: '配送单列表' }, { key: '/deliveries/xiaofeixia', label: '小飞侠联调' }, ], }, { key: 'tickets-group', icon: , label: '工单', children: [ { key: '/tickets', label: '工单中心' }, { key: '/tickets/support', label: '技术支持' }, ], }, { key: '/invoices', icon: , label: '发票管理' }, { key: '/resources', icon: , label: 'OSS 资源库' }, { key: 'logs-group', icon: , label: '日志', children: [ { key: '/logs/users', label: '用户日志' }, { key: '/logs/stores', label: '商户日志' }, { key: '/logs/partners', label: '合伙人日志' }, { key: '/logs/hq', label: 'HQ 操作日志' }, { key: '/logs/third-party', label: '第三方日志' }, ], }, { key: '/hq-permissions', icon: , label: '权限分配' }, { key: '/system-settings', icon: , label: '系统设置' }, { key: '/hq-accounts', icon: , label: 'HQ账户' }, ]; function menuAllowed(key: string, permissionKeys: string[]): boolean { if (key === 'tickets-group') { return permissionKeys.includes('tickets') || permissionKeys.includes('tech_support'); } const map: Record = { '/': 'dashboard', '/users': 'users', '/wechat-bindings': 'wechat_bindings', 'products-group': 'products', '/products': 'products', '/product-detail-templates': 'products', '/orders': 'orders', '/promo-codes': 'promo_codes', '/wecom-bots': 'wecom_bots', 'stores-group': 'stores', '/stores': 'stores', '/store-categories': 'stores', '/store-accounts': 'stores', '/store-media': 'stores', 'partners-group': 'partners', '/cities': 'partners', '/city-partners': 'partners', '/city-warehouses': 'partners', '/fulfillment-providers': 'partners', 'finance-group': 'finance', '/finance/store-bills': 'finance', '/finance/partner-bills': 'finance', '/finance/winery-bills': 'finance', 'benefit-group': 'benefit', '/benefit/coupons': 'benefit', '/benefit/ledgers': 'benefit', '/redeem-records': 'benefit', '/redeem-pending': 'benefit', '/redeem/debug': 'benefit', 'deliveries-group': 'deliveries', '/deliveries': 'deliveries', '/deliveries/xiaofeixia': 'deliveries', '/tickets': 'tickets', '/tickets/support': 'tech_support', '/invoices': 'invoices', '/resources': 'resources', 'logs-group': 'logs', '/logs/users': 'logs', '/logs/stores': 'logs', '/logs/partners': 'logs', '/logs/hq': 'logs', '/logs/third-party': 'logs', '/hq-permissions': 'hq_permissions', '/system-settings': 'system_settings_any', '/hq-accounts': 'hq_accounts', }; const need = map[key]; if (!need) return true; if (need === 'system_settings_any') return hasAnySystemSettingsPermission(permissionKeys); return permissionKeys.includes(need); } function filterMenuItems(items: MenuProps['items'], permissionKeys: string[]): MenuProps['items'] { if (!items) return items; return items .map((item) => { if (!item || typeof item !== 'object' || !('key' in item)) return item; const key = String(item.key); if ('children' in item && Array.isArray(item.children)) { if (!menuAllowed(key, permissionKeys)) return null; const children = filterMenuItems(item.children as MenuProps['items'], permissionKeys); if (!children?.length) return null; return { ...item, children } as MenuItem; } return menuAllowed(key, permissionKeys) ? item : null; }) .filter(Boolean) as MenuProps['items']; } export default function AdminLayout() { const navigate = useNavigate(); const location = useLocation(); const contentRef = useRef(null); const [profile, setProfile] = useState(null); useEffect(() => { request('/admin/auth/me').then(setProfile).catch(() => {}); }, []); useEffect(() => { contentRef.current?.scrollTo({ top: 0, left: 0 }); }, [location.pathname]); useEffect(() => { return bindAdminEllipsisTitle(); }, []); function logout() { clearAuth(); navigate('/login'); } const selectedKey = location.pathname.startsWith('/promo-codes') ? '/promo-codes' : location.pathname; const menuItems = useMemo(() => { if (!profile) return MENU_ITEMS; if (profile.adminRole === 'SUPER_ADMIN') return MENU_ITEMS; return filterMenuItems(MENU_ITEMS, profile.permissionKeys ?? []); }, [profile]); return (
杜康 HQ
{ if (key.startsWith('/')) navigate(key); }} />
管理后台 {profile?.name || '—'} {profile?.adminRole}
); }