App layout
parent
52a6968f4e
commit
50e3f4a8f0
File diff suppressed because it is too large
Load Diff
|
|
@ -9,7 +9,7 @@
|
||||||
"@types/node": "^16.18.88",
|
"@types/node": "^16.18.88",
|
||||||
"@types/react": "^18.2.65",
|
"@types/react": "^18.2.65",
|
||||||
"@types/react-dom": "^18.2.21",
|
"@types/react-dom": "^18.2.21",
|
||||||
"evergreen-ui": "^7.1.9",
|
"antd": "^5.15.2",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router-dom": "^6.22.3",
|
"react-router-dom": "^6.22.3",
|
||||||
|
|
|
||||||
|
|
@ -15,29 +15,10 @@
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
-->
|
-->
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
<!--
|
<title>EVA-Notify Admin Page</title>
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
|
||||||
It will be replaced with the URL of the `public` folder during the build.
|
|
||||||
Only files inside the `public` folder can be referenced from the HTML.
|
|
||||||
|
|
||||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
|
||||||
-->
|
|
||||||
<title>React App</title>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body style="margin: 0;">
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<!--
|
|
||||||
This HTML file is a template.
|
|
||||||
If you open it directly in the browser, you will see an empty page.
|
|
||||||
|
|
||||||
You can add webfonts, meta tags, or analytics to this file.
|
|
||||||
The build step will place the bundled scripts into the <body> tag.
|
|
||||||
|
|
||||||
To begin the development, run `npm start` or `yarn start`.
|
|
||||||
To create a production bundle, use `npm run build` or `yarn build`.
|
|
||||||
-->
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
.app {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
139
src/App.tsx
139
src/App.tsx
|
|
@ -1,13 +1,132 @@
|
||||||
import { Component, ReactNode } from 'react';
|
import { useState, FC } from 'react';
|
||||||
import { Route, Routes } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { TeamOutlined, UserOutlined, DesktopOutlined } from '@ant-design/icons';
|
||||||
|
import type { MenuProps } from 'antd';
|
||||||
|
import { Breadcrumb, Layout, Menu, theme } from 'antd';
|
||||||
|
import { AppRouter } from './router';
|
||||||
|
import './App.scss';
|
||||||
|
|
||||||
export default class App extends Component {
|
const { Content, Footer, Sider } = Layout;
|
||||||
render(): ReactNode {
|
|
||||||
|
type MenuItem = Required<MenuProps>['items'][number];
|
||||||
|
|
||||||
|
type KeyRouterMap = Map<React.Key, string>;
|
||||||
|
|
||||||
|
const keyRouterMap: KeyRouterMap = new Map();
|
||||||
|
|
||||||
|
function getItem(
|
||||||
|
label: React.ReactNode,
|
||||||
|
key: React.Key,
|
||||||
|
route?: { path: string; map: KeyRouterMap },
|
||||||
|
icon?: React.ReactNode,
|
||||||
|
children?: MenuItem[],
|
||||||
|
): MenuItem {
|
||||||
|
if (route !== undefined) {
|
||||||
|
route.map.set(key, route.path);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
key,
|
||||||
|
icon,
|
||||||
|
children,
|
||||||
|
label,
|
||||||
|
} as MenuItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
function defMenu(
|
||||||
|
label: React.ReactNode,
|
||||||
|
key: React.Key,
|
||||||
|
icon: React.ReactNode,
|
||||||
|
path: string,
|
||||||
|
map: KeyRouterMap,
|
||||||
|
): MenuItem {
|
||||||
|
return getItem(label, key, { path: path, map: map }, icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
function defRootMenu(
|
||||||
|
label: React.ReactNode,
|
||||||
|
key: React.Key,
|
||||||
|
icon: React.ReactNode,
|
||||||
|
children: MenuItem[],
|
||||||
|
): MenuItem {
|
||||||
|
return getItem(label, key, undefined, icon, children);
|
||||||
|
}
|
||||||
|
|
||||||
|
function defSubMenu(
|
||||||
|
label: React.ReactNode,
|
||||||
|
key: React.Key,
|
||||||
|
path: string,
|
||||||
|
map: KeyRouterMap,
|
||||||
|
): MenuItem {
|
||||||
|
return getItem(label, key, { path, map });
|
||||||
|
}
|
||||||
|
|
||||||
|
const items: MenuItem[] = [
|
||||||
|
defMenu('主页', '0', <DesktopOutlined />, '/', keyRouterMap),
|
||||||
|
defRootMenu('展示信息管理', 'sub1', <UserOutlined />, [
|
||||||
|
defSubMenu('选项1', '1', '/hello1', keyRouterMap),
|
||||||
|
defSubMenu('选项2', '2', '/hello2', keyRouterMap),
|
||||||
|
defSubMenu('选项3', '3', '/hello3', keyRouterMap),
|
||||||
|
]),
|
||||||
|
defRootMenu('值班组长', 'sub2', <TeamOutlined />, [
|
||||||
|
defSubMenu('选项1', '4', '/hi1', keyRouterMap),
|
||||||
|
defSubMenu('选项2', '5', '/hi2', keyRouterMap),
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
|
||||||
|
const AppLayout: FC<{ router: JSX.Element }> = (props) => {
|
||||||
|
const [collapsed, setCollapsed] = useState(false);
|
||||||
|
const { colorBgContainer, borderRadiusLG } = theme.getDesignToken();
|
||||||
|
const navigate = useNavigate();
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Layout style={{ minHeight: '100vh' }}>
|
||||||
<Route path='/' element={<p>Main</p>} />
|
<Sider
|
||||||
<Route path='/hello' element={<p>Hello</p>} />
|
collapsible
|
||||||
</Routes>
|
collapsed={collapsed}
|
||||||
|
onCollapse={(value) => setCollapsed(value)}
|
||||||
|
>
|
||||||
|
<div className='demo-logo-vertical' />
|
||||||
|
<Menu
|
||||||
|
theme='dark'
|
||||||
|
defaultSelectedKeys={['1']}
|
||||||
|
mode='inline'
|
||||||
|
items={items}
|
||||||
|
onClick={(info: { key: React.Key }) => {
|
||||||
|
console.log('path', keyRouterMap.get(info.key));
|
||||||
|
navigate(keyRouterMap.get(info.key) || '/');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Sider>
|
||||||
|
<Layout>
|
||||||
|
<Content style={{ margin: '0 16px' }}>
|
||||||
|
<Breadcrumb style={{ margin: '16px 0' }}>
|
||||||
|
<Breadcrumb.Item>User</Breadcrumb.Item>
|
||||||
|
<Breadcrumb.Item>Bill</Breadcrumb.Item>
|
||||||
|
</Breadcrumb>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: 24,
|
||||||
|
minHeight: 360,
|
||||||
|
background: colorBgContainer,
|
||||||
|
borderRadius: borderRadiusLG,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>{props.router}</div>
|
||||||
|
</div>
|
||||||
|
</Content>
|
||||||
|
<Footer style={{ textAlign: 'center' }}>
|
||||||
|
EVA-Notify Admin Page © {new Date().getFullYear()} EVA Tech
|
||||||
|
</Footer>
|
||||||
|
</Layout>
|
||||||
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
const App: FC = () => {
|
||||||
|
return (
|
||||||
|
<div className='app'>
|
||||||
|
<AppLayout router={<AppRouter />} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Routes, Route } from 'react-router-dom';
|
||||||
|
|
||||||
|
export const AppRouter: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<Routes>
|
||||||
|
<Route path='/' element={<p>Main</p>} />
|
||||||
|
<Route path='/hello1' element={<p>Hello1</p>} />
|
||||||
|
<Route path='/hello2' element={<p>Hello1</p>} />
|
||||||
|
<Route path='/hello3' element={<p>Hello1</p>} />
|
||||||
|
</Routes>
|
||||||
|
);
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue