XHS / frontend /src /layouts /AppLayout.tsx
Trae Bot
Upload Spider_XHS project
c481f8a
import {
BugOutlined,
DatabaseOutlined,
DashboardOutlined,
FileTextOutlined,
LineChartOutlined,
RobotOutlined,
SnippetsOutlined,
UnorderedListOutlined,
MessageOutlined,
SafetyCertificateOutlined,
PhoneOutlined,
} from '@ant-design/icons'
import { Layout, Menu, Typography, theme } from 'antd'
import type { MenuProps } from 'antd'
import { useMemo, useState } from 'react'
import { Link, Outlet, useLocation } from 'react-router-dom'
const { Header, Content, Sider } = Layout
const menuItems: MenuProps['items'] = [
{
key: '/dashboard',
icon: <DashboardOutlined />,
label: <Link to="/dashboard">健康概览</Link>,
},
{
key: '/tasks',
icon: <UnorderedListOutlined />,
label: <Link to="/tasks">任务中心</Link>,
},
{
key: '/resources',
icon: <DatabaseOutlined />,
label: '资源池',
children: [
{
key: '/resources/accounts',
label: <Link to="/resources/accounts">账号池</Link>,
},
{
key: '/resources/sessions',
label: <Link to="/resources/sessions">会话池</Link>,
},
{
key: '/resources/proxies',
label: <Link to="/resources/proxies">代理池</Link>,
},
],
},
{
key: '/errors',
icon: <BugOutlined />,
label: <Link to="/errors">错误中心</Link>,
},
{
key: '/content/raw-notes',
icon: <FileTextOutlined />,
label: <Link to="/content/raw-notes">原始快照</Link>,
},
{
key: '/content/cleaned-notes',
icon: <SnippetsOutlined />,
label: <Link to="/content/cleaned-notes">清洗笔记库</Link>,
},
{
key: '/ai/generation',
icon: <MessageOutlined />,
label: <Link to="/ai/generation">AI 生产内容</Link>,
},
{
key: '/compliance/review',
icon: <SafetyCertificateOutlined />,
label: <Link to="/compliance/review">合规检测</Link>,
},
{
key: '/leads',
icon: <PhoneOutlined />,
label: <Link to="/leads">线索转化池</Link>,
},
{
key: '/rpa',
icon: <RobotOutlined />,
label: <Link to="/rpa">RPA 兜底</Link>,
},
{
key: '/metrics',
icon: <LineChartOutlined />,
label: <Link to="/metrics">监控指标</Link>,
},
]
export default function AppLayout() {
const location = useLocation()
const {
token: { colorBgContainer, borderRadiusLG, colorBorderSecondary },
} = theme.useToken()
const selectedKeys = useMemo(() => {
if (location.pathname.startsWith('/tasks')) return ['/tasks']
if (location.pathname.startsWith('/errors')) return ['/errors']
return [location.pathname]
}, [location.pathname])
const defaultOpenKeys = useMemo(() => {
if (location.pathname.startsWith('/resources')) return ['/resources']
return []
}, [location.pathname])
const [openKeys, setOpenKeys] = useState(defaultOpenKeys)
const onOpenChange = (keys: string[]) => {
setOpenKeys(keys)
}
return (
<Layout style={{ minHeight: '100vh' }}>
<Sider
breakpoint="lg"
collapsedWidth={56}
style={{ borderRight: `1px solid ${colorBorderSecondary}` }}
>
<div
style={{
height: 56,
display: 'flex',
alignItems: 'center',
paddingInline: 16,
}}
>
<Typography.Text strong style={{ color: '#fff' }}>
Spider XHS
</Typography.Text>
</div>
<Menu
theme="dark"
mode="inline"
selectedKeys={selectedKeys}
openKeys={openKeys}
onOpenChange={onOpenChange}
items={menuItems}
/>
</Sider>
<Layout>
<Header
style={{
paddingInline: 16,
display: 'flex',
alignItems: 'center',
background: colorBgContainer,
borderBottom: `1px solid ${colorBorderSecondary}`,
}}
>
<Typography.Text>Ops Console</Typography.Text>
</Header>
<Content style={{ padding: 16 }}>
<div
style={{
background: colorBgContainer,
padding: 16,
minHeight: 280,
borderRadius: borderRadiusLG,
}}
>
<Outlet />
</div>
</Content>
</Layout>
</Layout>
)
}