208 lines
6.0 KiB
TypeScript
208 lines
6.0 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { Outlet, useNavigate, useLocation } from 'react-router-dom';
|
|
import {
|
|
Layout, Menu, Button, Space, ConfigProvider, theme, Select, Avatar
|
|
} from 'antd';
|
|
import zhCN from 'antd/locale/zh_CN';
|
|
import {
|
|
DashboardOutlined, ExperimentOutlined, VideoCameraOutlined,
|
|
BellOutlined, UserOutlined, LogoutOutlined,
|
|
AppstoreOutlined, SettingOutlined, MenuUnfoldOutlined, MenuFoldOutlined
|
|
} from '@ant-design/icons';
|
|
import logo from '../assets/logo.png';
|
|
import { useSelector } from 'react-redux';
|
|
import { RootState, useAppDispatch } from '../store';
|
|
import { logout } from '../store/userSlice';
|
|
import { setCurrentStation, clearStation } from '../store/stationSlice';
|
|
import { getMenuList } from '../api/mune';
|
|
import { getStationList } from '../api/stationManage';
|
|
import envConfig from '../../env';
|
|
|
|
const { Header, Sider, Content } = Layout;
|
|
|
|
export default function AppLayout() {
|
|
const [collapsed, setCollapsed] = useState(false);
|
|
const [time, setTime] = useState(new Date().toLocaleString());
|
|
const [menuList, setMenuList] = useState([]);
|
|
const [stationList, setStationList] = useState([]);
|
|
|
|
const navigate = useNavigate();
|
|
const location = useLocation();
|
|
const dispatch = useAppDispatch();
|
|
|
|
const { userInfo } = useSelector((state: RootState) => state.user);
|
|
const { currentStation, stationId } = useSelector((state: RootState) => state.station);
|
|
|
|
const getStationData = () => {
|
|
const params = {
|
|
pageNum: 1,
|
|
pageSize: 9999,
|
|
orgId: userInfo?.orgId,
|
|
};
|
|
getStationList(params).then(res => {
|
|
if (res.code === 200) {
|
|
const list = res.rows || [];
|
|
setStationList(list);
|
|
if (!currentStation && list.length > 0) {
|
|
dispatch(setCurrentStation(list[0]));
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
const handleStationChange = (id: number) => {
|
|
const station = stationList.find(s => s.id === id);
|
|
if (station) {
|
|
dispatch(setCurrentStation(station));
|
|
}
|
|
};
|
|
|
|
const getMenu = () => {
|
|
const data = { pageNum: 1, pageSize: 10000 };
|
|
getMenuList(data).then(res => {
|
|
if (res.code == 200) {
|
|
const rawList = res.rows || [];
|
|
const topMenus = rawList.filter(item => item.parentId === 0);
|
|
setMenuList(topMenus);
|
|
}
|
|
});
|
|
};
|
|
|
|
const getIcon = (path) => {
|
|
if (path.includes('overview')) return <DashboardOutlined />;
|
|
if (path.includes('device')) return <ExperimentOutlined />;
|
|
if (path.includes('monitor')) return <VideoCameraOutlined />;
|
|
if (path.includes('alert')) return <BellOutlined />;
|
|
if (path.includes('user')) return <UserOutlined />;
|
|
if (path.includes('system')) return <SettingOutlined />;
|
|
if (path.includes('menu')) return <AppstoreOutlined />;
|
|
return <AppstoreOutlined />;
|
|
};
|
|
|
|
const menuItems = menuList.map(item => ({
|
|
key: item.path || `/menu/${item.menuId}`,
|
|
icon: getIcon(item.path),
|
|
label: item.menuName,
|
|
}));
|
|
|
|
const handleLogout = async () => {
|
|
await dispatch(logout());
|
|
dispatch(clearStation());
|
|
navigate('/login', { replace: true });
|
|
};
|
|
|
|
useEffect(() => {
|
|
const timer = setInterval(() => setTime(new Date().toLocaleString()), 1000);
|
|
getMenu();
|
|
getStationData();
|
|
return () => clearInterval(timer);
|
|
}, [userInfo]);
|
|
|
|
return (
|
|
<ConfigProvider
|
|
locale={zhCN}
|
|
theme={{
|
|
algorithm: theme.defaultAlgorithm,
|
|
token: { colorPrimary: '#1890ff', borderRadius: 2 },
|
|
components: {
|
|
Layout: { headerBg: '#fff', siderBg: '#fff' },
|
|
Menu: { itemBg: '#fff', itemSelectedBg: '#e6f4ff' }
|
|
}
|
|
}}
|
|
>
|
|
<Layout style={{ maxHeight: '100vh' }}>
|
|
<Header style={{
|
|
background: '#0837a8',
|
|
color: '#fff',
|
|
height: "50px",
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
padding: '0 16px'
|
|
}}>
|
|
<Space size="large" style={{ display: 'flex', alignItems: 'center' }}>
|
|
|
|
{/* ====================== 收起/展开按钮 ====================== */}
|
|
<Button
|
|
type="text"
|
|
icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
|
onClick={() => setCollapsed(!collapsed)}
|
|
style={{
|
|
fontSize: '16px',
|
|
color: '#fff',
|
|
width: 40,
|
|
height: 40,
|
|
}}
|
|
/>
|
|
|
|
<Space size="small">
|
|
<img src={logo} alt="Logo" style={{ width: 24, height: 24 }} />
|
|
{!collapsed && (
|
|
<span style={{ fontWeight: 'bold', fontSize: 16, color: '#fff' }}>
|
|
光伏电站智能监控与运维系统
|
|
</span>
|
|
)}
|
|
</Space>
|
|
|
|
<Select
|
|
value={stationId}
|
|
placeholder="请选择电站"
|
|
onChange={handleStationChange}
|
|
options={stationList.map(s => ({ label: s.siteName, value: s.id }))}
|
|
style={{
|
|
width: 180,
|
|
backgroundColor: '#0837a8',
|
|
color: '#fff',
|
|
border: 'none',
|
|
}}
|
|
/>
|
|
</Space>
|
|
|
|
<Space size="middle">
|
|
<span style={{ fontSize: 13 }}>{time}</span>
|
|
<Space
|
|
style={{ cursor: 'pointer', padding: '0 4px' }}
|
|
onClick={() => navigate('/profile')}
|
|
className="hover:opacity-80"
|
|
>
|
|
{userInfo?.avatar && (
|
|
<Avatar
|
|
size="small"
|
|
src={userInfo.avatar.startsWith('http') ? userInfo.avatar : `${envConfig.baseURL}${userInfo.avatar}`}
|
|
/>
|
|
)}
|
|
<span style={{ fontSize: 14 }}>{userInfo?.nickName || userInfo?.username}</span>
|
|
</Space>
|
|
<Button type="text" danger icon={<LogoutOutlined />} onClick={handleLogout}>
|
|
退出登录
|
|
</Button>
|
|
</Space>
|
|
</Header>
|
|
|
|
<Layout style={{ height: 'calc(100vh - 50px)', overflow: 'hidden', background: '#fff' }}>
|
|
<Sider
|
|
collapsible
|
|
collapsed={collapsed}
|
|
trigger={null}
|
|
width={150}
|
|
theme="light"
|
|
style={{ borderRight: '1px solid #e5e7eb' }}
|
|
>
|
|
<Menu
|
|
mode="inline"
|
|
theme="light"
|
|
selectedKeys={[location.pathname]}
|
|
items={menuItems}
|
|
onClick={({ key }) => navigate(key)}
|
|
/>
|
|
</Sider>
|
|
|
|
<Content style={{ background: '#f4f7f9', overflowY: 'auto', padding: '0px 16px' }}>
|
|
<Outlet />
|
|
</Content>
|
|
</Layout>
|
|
</Layout>
|
|
</ConfigProvider>
|
|
);
|
|
}
|