feat(admin): 整合财务模块导航为侧边栏子菜单与顶部联动Tab

- 侧边栏收敛:将散落在运营分组下的销售毛利、商品与成本、钱包管理、收支明细整合入「财务」二级子菜单,并支持智能展开与角色权限隔离
- 顶部导航联动:新增 FinanceSubNav 通用组件,在销售毛利、商品与成本中心、钱包管理、收支明细 4 个页面顶部统一提供一键切换 Tab
- 语义规范化:将原“财务”流水页面标题明确为“收支明细”,消除模块与页面的命名歧义
- 验证方式:npm run check 全量检查通过(Prettier、ESLint、tsc、单测 100% 通过),构建产物已同步至运行容器并验证路由与交互
This commit is contained in:
yml2213
2026-10-25 20:39:11 +08:00
parent eaffae4af0
commit dfc01db4ae
10 changed files with 203 additions and 75 deletions
+74 -43
View File
@@ -168,52 +168,81 @@ export default function AdminLayout() {
},
})
const selectedKey = resolveSelectedKey(location.pathname)
const isFinanceKey = [
'/admin/sales-profit',
'/admin/cost-center',
'/admin/fund',
'/admin/finance',
].includes(selectedKey)
const [openKeys, setOpenKeys] = useState<string[]>(['sub-finance'])
useEffect(() => {
if (isFinanceKey && !openKeys.includes('sub-finance')) {
setOpenKeys((prev) => [...prev, 'sub-finance'])
}
}, [isFinanceKey])
const menuItems = useMemo<MenuProps['items']>(() => {
const financeSubItems: MenuProps['items'] = [
{ key: '/admin/sales-profit', icon: <LineChartOutlined />, label: '销售毛利' },
{ key: '/admin/cost-center', icon: <ApartmentOutlined />, label: '商品与成本' },
{ key: '/admin/fund', icon: <WalletOutlined />, label: '钱包管理' },
{ key: '/admin/finance', icon: <AccountBookOutlined />, label: '收支明细' },
]
const financeSubMenu = {
key: 'sub-finance',
icon: <AccountBookOutlined />,
label: '财务',
children: financeSubItems,
}
const operationItems: MenuProps['items'] =
role === 'finance'
? [
{ key: '/admin/finance', icon: <AccountBookOutlined />, label: '财务' },
{ key: '/admin/sales-profit', icon: <LineChartOutlined />, label: '销售毛利' },
{ key: '/admin/cost-center', icon: <ApartmentOutlined />, label: '商品与成本' },
{ key: '/admin/fund', icon: <WalletOutlined />, label: '钱包管理' },
financeSubMenu,
{ key: '/admin/worker-platform', icon: <TrophyOutlined />, label: '资金审核' },
]
: [
{ key: '/admin/dashboard', icon: <DashboardOutlined />, label: '概览' },
{ key: '/admin/orders', icon: <OrderedListOutlined />, label: '订单' },
{
key: '/admin/fund',
icon: <WalletOutlined />,
label: role === 'support' ? '我的钱包' : '钱包管理',
},
...(role === 'support'
? []
: [
{ key: '/admin/finance', icon: <AccountBookOutlined />, label: '财务' },
{
key: '/admin/sales-profit',
icon: <LineChartOutlined />,
label: '销售毛利',
},
{
key: '/admin/cost-center',
icon: <ApartmentOutlined />,
label: '商品与成本',
},
]),
{ key: '/admin/tasks', icon: <UnorderedListOutlined />, label: '任务' },
{ key: '/admin/worker-platform', icon: <TrophyOutlined />, label: '接单平台' },
{
key: '/admin/kuaishou-industry',
icon: <SafetyCertificateOutlined />,
label: '电子凭证',
},
{
key: '/admin/douyin-mashida-certificates',
icon: <SafetyCertificateOutlined />,
label: '抖音券核销',
},
]
: role === 'support'
? [
{ key: '/admin/dashboard', icon: <DashboardOutlined />, label: '概览' },
{ key: '/admin/orders', icon: <OrderedListOutlined />, label: '订单' },
{
key: '/admin/fund',
icon: <WalletOutlined />,
label: '我的钱包',
},
{ key: '/admin/tasks', icon: <UnorderedListOutlined />, label: '任务' },
{ key: '/admin/worker-platform', icon: <TrophyOutlined />, label: '接单平台' },
{
key: '/admin/kuaishou-industry',
icon: <SafetyCertificateOutlined />,
label: '电子凭证',
},
{
key: '/admin/douyin-mashida-certificates',
icon: <SafetyCertificateOutlined />,
label: '抖音券核销',
},
]
: [
{ key: '/admin/dashboard', icon: <DashboardOutlined />, label: '概览' },
{ key: '/admin/orders', icon: <OrderedListOutlined />, label: '订单' },
financeSubMenu,
{ key: '/admin/tasks', icon: <UnorderedListOutlined />, label: '任务' },
{ key: '/admin/worker-platform', icon: <TrophyOutlined />, label: '接单平台' },
{
key: '/admin/kuaishou-industry',
icon: <SafetyCertificateOutlined />,
label: '电子凭证',
},
{
key: '/admin/douyin-mashida-certificates',
icon: <SafetyCertificateOutlined />,
label: '抖音券核销',
},
]
const items: MenuProps['items'] = [
{
@@ -333,8 +362,6 @@ export default function AdminLayout() {
</div>
)
const selectedKey = resolveSelectedKey(location.pathname)
return (
<Layout className="admin-shell">
<Layout.Sider
@@ -377,6 +404,8 @@ export default function AdminLayout() {
mode="inline"
inlineIndent={16}
selectedKeys={[selectedKey]}
openKeys={openKeys}
onOpenChange={(keys) => setOpenKeys(keys as string[])}
items={menuItems}
className="admin-menu"
onClick={({ key }) => navigate(String(key))}
@@ -802,8 +831,10 @@ function playNotificationSound(kind: NotificationAlertKind) {
function resolveSelectedKey(pathname: string) {
if (pathname.startsWith('/admin/orders')) return '/admin/orders'
if (pathname.startsWith('/admin/finance')) return '/admin/finance'
if (pathname.startsWith('/admin/sales-profit')) return '/admin/sales-profit'
if (pathname.startsWith('/admin/cost-center')) return '/admin/cost-center'
if (pathname.startsWith('/admin/fund')) return '/admin/fund'
if (pathname.startsWith('/admin/finance')) return '/admin/finance'
if (pathname.startsWith('/admin/tasks')) return '/admin/tasks'
if (pathname.startsWith('/admin/worker-platform')) return '/admin/worker-platform'
if (pathname.startsWith('/admin/kuaishou-industry')) return '/admin/kuaishou-industry'