@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
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';
|
||||
@@ -18,11 +18,14 @@ import {
|
||||
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<MenuProps['items']>[number];
|
||||
|
||||
const MENU_ITEMS: MenuProps['items'] = [
|
||||
{ key: '/', icon: <DashboardOutlined />, label: '概览' },
|
||||
{ key: '/users', icon: <UserOutlined />, label: '用户' },
|
||||
@@ -111,6 +114,75 @@ const MENU_ITEMS: MenuProps['items'] = [
|
||||
{ key: '/hq-accounts', icon: <SafetyOutlined />, label: 'HQ账户' },
|
||||
];
|
||||
|
||||
function menuAllowed(key: string, permissionKeys: string[]): boolean {
|
||||
const map: Record<string, string | 'system_settings_any'> = {
|
||||
'/': 'dashboard',
|
||||
'/users': 'users',
|
||||
'/wechat-bindings': 'wechat_bindings',
|
||||
'products-group': 'products',
|
||||
'/products': 'products',
|
||||
'/product-detail-templates': 'products',
|
||||
'/orders': 'orders',
|
||||
'/promo-codes': 'promo_codes',
|
||||
'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',
|
||||
'/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();
|
||||
@@ -138,6 +210,12 @@ export default function AdminLayout() {
|
||||
? '/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 (
|
||||
<Layout style={{ height: '100vh', overflow: 'hidden' }}>
|
||||
<Sider
|
||||
@@ -156,8 +234,8 @@ export default function AdminLayout() {
|
||||
theme="dark"
|
||||
mode="inline"
|
||||
selectedKeys={[selectedKey]}
|
||||
defaultOpenKeys={['products-group', 'stores-group', 'partners-group', 'finance-group', 'benefit-group', 'logs-group', 'deliveries-group']}
|
||||
items={MENU_ITEMS}
|
||||
defaultOpenKeys={[]}
|
||||
items={menuItems}
|
||||
onClick={({ key }) => {
|
||||
if (key.startsWith('/')) navigate(key);
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user