feat: AI Worker 平台 MVP v1.0.0 - 多租户/项目管理/AI Worker/任务编排/HITL审核/成本治理

This commit is contained in:
2026-08-12 13:31:14 +08:00
parent 5df2b9f4bd
commit b3c975f6c4
+194
View File
@@ -0,0 +1,194 @@
import { useState } from 'react';
import { Outlet, useNavigate, useLocation } from 'react-router-dom';
import {
Layout,
Menu,
Avatar,
Dropdown,
Space,
Typography,
Tag,
theme,
} from 'antd';
import {
DashboardOutlined,
ProjectOutlined,
CheckSquareOutlined,
RobotOutlined,
AuditOutlined,
LogoutOutlined,
MenuFoldOutlined,
MenuUnfoldOutlined,
UserOutlined,
} from '@ant-design/icons';
import type { MenuProps } from 'antd';
import { useAuthStore } from '@/stores/auth';
import type { UserRole } from '@/types';
const { Header, Sider, Content } = Layout;
const { Text } = Typography;
const roleLabels: Record<UserRole, string> = {
super_admin: '超级管理员',
tenant_admin: '租户管理员',
project_manager: '项目经理',
reviewer: '审核员',
worker: '执行者',
};
const roleColors: Record<UserRole, string> = {
super_admin: 'red',
tenant_admin: 'orange',
project_manager: 'blue',
reviewer: 'purple',
worker: 'green',
};
const menuItems: MenuProps['items'] = [
{
key: '/dashboard',
icon: <DashboardOutlined />,
label: '仪表盘',
},
{
key: '/projects',
icon: <ProjectOutlined />,
label: '项目管理',
},
{
key: '/tasks',
icon: <CheckSquareOutlined />,
label: '任务管理',
},
{
key: '/workers',
icon: <RobotOutlined />,
label: 'AI 工作者',
},
{
key: '/reviews',
icon: <AuditOutlined />,
label: '审核管理',
},
];
export default function AppLayout() {
const [collapsed, setCollapsed] = useState(false);
const navigate = useNavigate();
const location = useLocation();
const { user, logout } = useAuthStore();
const handleMenuClick: MenuProps['onClick'] = ({ key }) => {
navigate(key);
};
const handleLogout = () => {
logout();
navigate('/login');
};
const userMenuItems: MenuProps['items'] = [
{
key: 'user-info',
label: (
<Space direction="vertical" size={0}>
<Text strong>{user?.username}</Text>
<Text type="secondary">{user?.email}</Text>
</Space>
),
disabled: true,
},
{ type: 'divider' },
{
key: 'logout',
icon: <LogoutOutlined />,
label: '退出登录',
onClick: handleLogout,
},
];
const selectedKey = '/' + location.pathname.split('/')[1];
return (
<Layout style={{ minHeight: '100vh' }}>
<Sider
trigger={null}
collapsible
collapsed={collapsed}
theme="dark"
width={220}
style={{
overflow: 'auto',
height: '100vh',
position: 'sticky',
top: 0,
left: 0,
}}
>
<div
style={{
height: 64,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: '#fff',
fontSize: collapsed ? 14 : 18,
fontWeight: 700,
borderBottom: '1px solid rgba(255,255,255,0.1)',
}}
>
{collapsed ? 'AI' : 'AI Worker 平台'}
</div>
<Menu
theme="dark"
mode="inline"
selectedKeys={[selectedKey]}
items={menuItems}
onClick={handleMenuClick}
/>
</Sider>
<Layout>
<Header
style={{
padding: '0 24px',
background: '#fff',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
boxShadow: '0 1px 4px rgba(0,21,41,0.08)',
position: 'sticky',
top: 0,
zIndex: 9,
}}
>
<div
style={{ cursor: 'pointer', fontSize: 18 }}
onClick={() => setCollapsed(!collapsed)}
>
{collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
</div>
<Dropdown menu={{ items: userMenuItems }} placement="bottomRight">
<Space style={{ cursor: 'pointer' }}>
<Avatar icon={<UserOutlined />} style={{ backgroundColor: '#1677ff' }} />
<span>{user?.username || '用户'}</span>
{user?.role && (
<Tag color={roleColors[user.role]}>{roleLabels[user.role]}</Tag>
)}
</Space>
</Dropdown>
</Header>
<Content
style={{
margin: 24,
padding: 24,
background: '#fff',
borderRadius: 8,
minHeight: 280,
}}
>
<Outlet />
</Content>
</Layout>
</Layout>
);
}