File size: 4,328 Bytes
c481f8a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
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>
  )
}