一、使用createBrowserRouter()创建路由:
- 路由懒加载
- 路由鉴权
- 路由不存在则返回错误页面
1、src/router/index.tsx:
import React, { lazy } from "react";
import { createBrowserRouter } from "react-router-dom";
const ErrorPage = lazy(() => import("@/pages/404/"));
const Auth = lazy(() => import("@/components/auth"));
const router = createBrowserRouter([
{
path: "/",
errorElement: <ErrorPage />,
async lazy() {
const { default: App } = await import("@/App");
return { Component: App };
},
children: [
{
errorElement: <ErrorPage />,
children: [
{
index: true,
async lazy() {
const { default: Home } = await import("@/pages/home/");
return {
element: (
<Auth>
<Home />
</Auth>
)
};
}
},
{
path: "collapse",
async lazy() {
const { default: Collapse } = await import("@/pages/collapse/");
return {
element: (
<Auth>
<Collapse />
</Auth>
)
};
}
},
{
path: "timeline",
async lazy() {
const { default: Timeline } = await import("@/pages/timeline/");
return {
element: (
<Auth>
<Timeline />
</Auth>
)
};
}
},
{
path: "app",
async lazy() {
const { default: Application } = await import(
"@/pages/application/"
);
return {
element: (
<Auth>
<Application />
</Auth>
)
};
}
}
]
}
]
},
{
path: "/login",
async lazy() {
const { default: Admin } = await import("@/pages/login/");
return {
element: (
<Auth>
<Admin />
</Auth>
)
};
}
}
]);
export default router;
注意其中的index: true
{
index: true,
async lazy() {
const { default: Home } = await import("@/pages/home/");
return {
element: (
<Auth>
<Home />
</Auth>
)
};
}
}
2、src/index.tsx:
import React from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider } from "react-router-dom";
import router from "@/router";
import reportWebVitals from "@/reportWebVitals";
import "@/index.css";
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
root.render(<RouterProvider router={router} />);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
注意其中的:
<RouterProvider router={router} />
3、src/App.tsx:添加占位符<Outlet/>
import React from "react";
import { Layout } from "antd";
import Dropdown from "@/components/header/Dropdown";
import Search from "@/components/header/Search";
import SiderMenu from "@/components/sider-menu/";
import { Outlet } from "react-router-dom";
import { observer } from "mobx-react";
import mobxData from "@/store/mobx-data";
import "@/App.css";
const { Header, Content, Sider } = Layout;
const App: React.FC = () => {
return (
<Layout className="app-layout">
<Header className="app-header">
<div className="app-logo">
<img height="36" src={require("@/logo.png")} />
</div>
<div className="search">
<Search />
</div>
<Dropdown />
</Header>
<Layout className="app-wrapper">
<Sider width={mobxData.width} className="app-sider">
<SiderMenu />
</Sider>
<Layout className="app-wrapper-in">
<Content className="app-content">
<Outlet />
</Content>
</Layout>
</Layout>
</Layout>
);
};
export default observer(App);
注意其中的:
<Outlet />
4、src/sider-menu/index.tsx:添加导航NavLink
import React, { useState } from "react";
import { Button, Tooltip } from "antd";
import {
HomeOutlined,
FolderOutlined,
AppstoreOutlined,
ReadOutlined,
TableOutlined,
ProductOutlined,
MenuFoldOutlined,
MenuUnfoldOutlined
} from "@ant-design/icons";
import { NavLink } from "react-router-dom";
import mobxData from "@/store/mobx-data";
import "./index.css";
const App: React.FC = () => {
const [status, setStatus] = useState(false);
const onChangeWidth = () => {
const elements = document.querySelectorAll(".menu-item dd") as NodeListOf<
HTMLElement
>;
if (status) {
mobxData.setWidth(90);
for (let i = 0; i < elements.length; i++) {
(elements[i].style as any).display = "block";
}
} else {
mobxData.setWidth(60);
for (let i = 0; i < elements.length; i++) {
(elements[i].style as any).display = "none";
}
}
setStatus(!status);
};
return (
<div className="menu">
<div className="menu-item-all">
<NavLink to="/">
<dl className="menu-item">
<dt>
{status ? (
<Tooltip
placement="right"
title="我的门户"
className="menu-tooltip"
>
<Button type="text" icon={<HomeOutlined />} />
</Tooltip>
) : (
<HomeOutlined />
)}
</dt>
<dd>我的门户</dd>
</dl>
</NavLink>
<NavLink to="/timeline">
<dl className="menu-item">
<dt>
{status ? (
<Tooltip
placement="right"
title="文档中心"
className="menu-tooltip"
>
<Button type="text" icon={<FolderOutlined />} />
</Tooltip>
) : (
<FolderOutlined />
)}
</dt>
<dd>文档中心</dd>
</dl>
</NavLink>
<NavLink to="/collapse">
<dl className="menu-item">
<dt>
{status ? (
<Tooltip
placement="right"
title="工作中心"
className="menu-tooltip"
>
<Button type="text" icon={<AppstoreOutlined />} />
</Tooltip>
) : (
<AppstoreOutlined />
)}
</dt>
<dd>工作中心</dd>
</dl>
</NavLink>
<dl className="menu-item">
<dt>
{status ? (
<Tooltip
placement="right"
title="知识中心"
className="menu-tooltip"
>
<Button type="text" icon={<ReadOutlined />} />
</Tooltip>
) : (
<ReadOutlined />
)}
</dt>
<dd>知识中心</dd>
</dl>
<dl className="menu-item">
<dt>
{status ? (
<Tooltip
placement="right"
title="表格中心"
className="menu-tooltip"
>
<Button type="text" icon={<TableOutlined />} />
</Tooltip>
) : (
<TableOutlined />
)}
</dt>
<dd>表格中心</dd>
</dl>
<NavLink to="/app">
<dl className="menu-item">
<dt>
{status ? (
<Tooltip
placement="right"
title="应用"
className="menu-tooltip"
>
<Button type="text" icon={<ProductOutlined />} />
</Tooltip>
) : (
<ProductOutlined />
)}
</dt>
<dd>应用</dd>
</dl>
</NavLink>
</div>
<div className="collapse">
<Tooltip placement="right" title={status ? "展开" : "收起"}>
<Button
type="text"
icon={
status ? (
<MenuUnfoldOutlined onClick={onChangeWidth} />
) : (
<MenuFoldOutlined onClick={onChangeWidth} />
)
}
onClick={onChangeWidth}
/>
</Tooltip>
</div>
</div>
);
};
export default App;
注意其中的NavLink及css中添加的active属性
.menu a.active {
color: #126ee3;
border-right: 3px solid #126ee3;
background: #dddee2;
}
5、路由鉴权:src/components/auth/index.tsx
- 直接输入登录页,如果没有登录则进入登录页;如果已经登录,则直接进入首页;
- 输入子页,如果没有登录,则跳转至登录页;如果已经登录,则直接进入该页;
import React, { useEffect } from "react";
// import { useNavigate } from "react-router-dom";
import { useLocation, Navigate } from "react-router-dom";
interface Props {
children: React.ReactElement;
}
export default function Auth({ children }: Props) {
const { pathname } = useLocation();
const isAuth = localStorage.getItem("token");
if (pathname === "/login") {
if (isAuth && Number(isAuth) !== 1) {
return <Navigate to="/" />;
} else {
return <>{children}</>; // 写成:return <Navigate to="/login" />会造成无限循环
}
}
if (isAuth && Number(isAuth) !== 1) {
return <>{children}</>;
} else {
return <Navigate to="/login" />;
}
}
6、src/pages/404/index.tsx:错误页面
import React from "react"
import {Button} from "antd"
const App: React.FC = () => {
return <div>
<p>404,当前页面不存在!</p>
<div><Button type="primary" onClick={()=>window.history.back()}>返回</Button></div>
</div>
}
export default App;
二、使用标签创建:
- 路由懒加载
- 路由鉴权
- 路由不存在则返回错误页面
1、src/index.tsx:
import React from "react";
import ReactDOM from "react-dom/client";
import reportWebVitals from "@/reportWebVitals";
import "@/index.css";
import BaseRouter from "@/routes"
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<BaseRouter/>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
2、 src/routes/index.tsx:路由配置文件
import React, { lazy, Suspense } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Auth from "@/components/auth/";
const App = lazy(() => import("@/App"));
const Home = lazy(() => import("@/pages/home/"));
const Collapse = lazy(() => import("@/pages/collapse/"));
const Timeline = lazy(() => import("@/pages/timeline/"));
const Application = lazy(() => import("@/pages/application/"));
const Login = lazy(() => import("@/pages/login/"));
const NotFound = lazy(() => import("@/pages/404/"));
const suspense = (Comp: JSX.Element) => (
<Suspense fallback={<div>loading...</div>}>{Comp}</Suspense>
);
const BaseRouter = () => (
<BrowserRouter>
<Suspense fallback={<div>loading...</div>}>
<Routes>
<Route
path="/"
element={suspense(
<Auth>
<App />
</Auth>
)}
>
<Route index element={suspense(<Home />)}></Route>
<Route path="collapse" element={suspense(<Collapse />)}></Route>
<Route path="timeline" element={suspense(<Timeline />)}></Route>
<Route path="app" element={suspense(<Application />)}></Route>
</Route>
<Route
path="login"
element={suspense(
<Auth>
<Login />
</Auth>
)}
></Route>
<Route path="*" element={suspense(<NotFound />)}></Route>
</Routes>
</Suspense>
</BrowserRouter>
);
export default BaseRouter;
3、src/App.tsx:首页,<Outlet/>为占位符
import React from "react";
import { Outlet } from "react-router-dom"
import { Layout } from "antd";
import Dropdown from "@/components/header/Dropdown";
import Search from "@/components/header/Search";
import SiderMenu from "@/components/sider-menu/";
import { observer } from "mobx-react";
import mobxData from "@/store/mobx-data";
import "@/App.css";
const { Header, Content, Sider } = Layout;
const App: React.FC = () => {
return (
<Layout className="app-layout">
<Header className="app-header">
<div className="app-logo">
<img height="36" src={require("@/logo.png")} />
</div>
<div className="search">
<Search />
</div>
<Dropdown />
</Header>
<Layout className="app-wrapper">
<Sider width={mobxData.width} className="app-sider">
<SiderMenu />
</Sider>
<Layout className="app-wrapper-in">
<Content className="app-content">
{/* {element} */}
<Outlet/>
</Content>
</Layout>
</Layout>
</Layout>
);
};
export default observer(App);
4、src/login/index.tsx:登录页
import React from "react";
import { useNavigate } from "react-router-dom";
import { Dropdown, Space, Button, Checkbox, Form, Input } from "antd";
import {
DownloadOutlined,
GlobalOutlined,
DownOutlined,
LockOutlined,
UserOutlined
} from "@ant-design/icons";
import "./index.css";
const items1 = [
{
key: "1",
label: "下载客户端"
},
{
key: "2",
label: "下载证书",
children: [
{
key: "2-1",
label: "下载应用证书"
},
{
key: "2-2",
label: "证书安装文档"
}
]
},
{
key: "3",
label: "RESTFUL API"
},
{
key: "4",
label: "Web组件"
}
];
const items2 = [
{
key: "5",
label: "简体中文"
},
{
key: "6",
label: "繁体中文"
},
{
key: "7",
label: "ENGLISH"
}
];
const App: React.FC = () => {
let navigate = useNavigate();
const onFinish = (values: string) => {
console.log("Received values of form: ", values);
localStorage.setItem("token", "2");
navigate("/");
};
return (
<div className="container">
<div className="background-container"></div>
<div className="wrapper">
<div className="oem">
<div className="oem-img"></div>
</div>
<div className="index">
<div className="wrap-header-bar">
<div className="app-index-bar" style={{ width: "504px" }}>
<div
className="app-index-bar-item"
style={{ paddingRight: "16px" }}
>
<Dropdown
menu={{
items: items1
}}
trigger={["click"]}
>
<a onClick={e => e.preventDefault()}>
<DownloadOutlined
style={{
fontSize: "14px",
cursor: "pointer",
marginRight: "5px"
}}
/>
<Space>
下载
<DownOutlined />
</Space>
</a>
</Dropdown>
</div>
<div className="app-index-bar-item">
<Dropdown
menu={{
items: items2
}}
trigger={["click"]}
>
<a onClick={e => e.preventDefault()}>
<GlobalOutlined
style={{
fontSize: "14px",
cursor: "pointer",
marginRight: "5px"
}}
/>
<Space>
简体中文
<DownOutlined />
</Space>
</a>
</Dropdown>
</div>
</div>
</div>
<div className="wrap-login">
<div className="index-panel">
<div className="logo-wrapper">
<img
className="logo-image"
src={require("./images/logo.png")}
/>
</div>
<div className="title-wrapper">企业内容门户</div>
<div className="signin-wrapper">
<Form
name="normal_login"
className="login-form"
initialValues={{
remember: true
}}
onFinish={onFinish}
>
<Form.Item
name="username"
rules={[
{
required: true,
message: "请输入账号!"
}
]}
>
<Input
prefix={<UserOutlined className="site-form-item-icon" />}
placeholder="请输入账号"
className="login-form-input"
/>
</Form.Item>
<Form.Item
name="password"
className="login-form-item"
rules={[
{
required: true,
message: "请输入密码!"
}
]}
>
<Input
prefix={<LockOutlined className="site-form-item-icon" />}
type="password"
placeholder="请输入密码"
className="login-form-input"
/>
</Form.Item>
<Form.Item>
<Form.Item name="remember" valuePropName="checked" noStyle>
<Checkbox>记住登录状态</Checkbox>
</Form.Item>
</Form.Item>
<Form.Item>
<Button
type="primary"
htmlType="submit"
className="login-form-button"
>
登 录
</Button>
</Form.Item>
</Form>
</div>
</div>
</div>
<div className="wrap-footer">
<div>
<div className="login-view-all">
登录即表示同意<a>用户协议</a>
<span>、 </span>
<span>
<a>隐私政策</a>
</span>
</div>
</div>
<div className="split"></div>
<div className="app-index-about" style={{ marginTop: "0px" }}>
<span className="version-information">版本信息</span>
<div className="app-index-about-row">
<span className="app-index-separator"> I </span>
<a
className="app-index-record"
href="http://beian.miit.gov.cn"
target="_blank"
>
沪ICP备09089247号-9
</a>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default App;
5、src/pages/404/index.tsx:错误页
import React from "react"
import {Button} from "antd"
const App: React.FC = () => {
return <div>
<p>404,当前页面不存在!</p>
<div><Button type="primary" onClick={()=>window.history.back()}>返回</Button></div>
</div>
}
export default App;
6、src/sider-menu/index.tsx:添加导航<NavLink></NavLink>
import React, { useState } from "react";
import { Button, Tooltip } from "antd";
import {
HomeOutlined,
FolderOutlined,
AppstoreOutlined,
ReadOutlined,
TableOutlined,
ProductOutlined,
MenuFoldOutlined,
MenuUnfoldOutlined
} from "@ant-design/icons";
import { NavLink } from "react-router-dom";
import mobxData from "@/store/mobx-data";
import "./index.css";
const App: React.FC = () => {
const [status, setStatus] = useState(false);
const onChangeWidth = () => {
const elements = document.querySelectorAll(".menu-item dd") as NodeListOf<
HTMLElement
>;
if (status) {
mobxData.setWidth(90);
for (let i = 0; i < elements.length; i++) {
(elements[i].style as any).display = "block";
}
} else {
mobxData.setWidth(60);
for (let i = 0; i < elements.length; i++) {
(elements[i].style as any).display = "none";
}
}
setStatus(!status);
};
return (
<div className="menu">
<div className="menu-item-all">
<NavLink to="/">
<dl className="menu-item">
<dt>
{status ? (
<Tooltip
placement="right"
title="我的门户"
className="menu-tooltip"
>
<Button type="text" icon={<HomeOutlined />} />
</Tooltip>
) : (
<HomeOutlined />
)}
</dt>
<dd>我的门户</dd>
</dl>
</NavLink>
<NavLink to="/timeline">
<dl className="menu-item">
<dt>
{status ? (
<Tooltip
placement="right"
title="文档中心"
className="menu-tooltip"
>
<Button type="text" icon={<FolderOutlined />} />
</Tooltip>
) : (
<FolderOutlined />
)}
</dt>
<dd>文档中心</dd>
</dl>
</NavLink>
<NavLink to="/collapse">
<dl className="menu-item">
<dt>
{status ? (
<Tooltip
placement="right"
title="工作中心"
className="menu-tooltip"
>
<Button type="text" icon={<AppstoreOutlined />} />
</Tooltip>
) : (
<AppstoreOutlined />
)}
</dt>
<dd>工作中心</dd>
</dl>
</NavLink>
<dl className="menu-item">
<dt>
{status ? (
<Tooltip
placement="right"
title="知识中心"
className="menu-tooltip"
>
<Button type="text" icon={<ReadOutlined />} />
</Tooltip>
) : (
<ReadOutlined />
)}
</dt>
<dd>知识中心</dd>
</dl>
<dl className="menu-item">
<dt>
{status ? (
<Tooltip
placement="right"
title="表格中心"
className="menu-tooltip"
>
<Button type="text" icon={<TableOutlined />} />
</Tooltip>
) : (
<TableOutlined />
)}
</dt>
<dd>表格中心</dd>
</dl>
<NavLink to="/app">
<dl className="menu-item">
<dt>
{status ? (
<Tooltip
placement="right"
title="应用"
className="menu-tooltip"
>
<Button type="text" icon={<ProductOutlined />} />
</Tooltip>
) : (
<ProductOutlined />
)}
</dt>
<dd>应用</dd>
</dl>
</NavLink>
</div>
<div className="collapse">
<Tooltip placement="right" title={status ? "展开" : "收起"}>
<Button
type="text"
icon={
status ? (
<MenuUnfoldOutlined onClick={onChangeWidth} />
) : (
<MenuFoldOutlined onClick={onChangeWidth} />
)
}
onClick={onChangeWidth}
/>
</Tooltip>
</div>
</div>
);
};
export default App;
7、路由鉴权:src/components/auth/index.tsx
import React from "react";
// import { useNavigate } from "react-router-dom";
import { useLocation, Navigate } from "react-router-dom";
interface Props {
children: React.ReactElement;
}
export default function Auth({ children }: Props) {
const {pathname} = useLocation();
const isAuth = localStorage.getItem("token");
if (pathname === "/login") {
if (isAuth && Number(isAuth) !== 1) {
return <Navigate to="/" />;
} else {
return <>{children}</>; // 写成:return <Navigate to="/login" />会造成无限循环
}
}
if (isAuth && Number(isAuth) !== 1) {
return <>{children}</>;
} else {
return <Navigate to="/login" />;
}
}
三、使用useRoutes()创建:
- 路由懒加载
- 路由鉴权
- 路由不存在则返回错误页面
1、src/index.tsx:使用BrowserRouter
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter} from "react-router-dom";
import reportWebVitals from "@/reportWebVitals";
import App from "./App";
import "@/index.css";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
reportWebVitals();
2、src/App.tsx:使用useRoutes()
使用<Suspense></Suspense>是为了防止在使用路由懒加载时报错。
import React, { Suspense } from "react";
import { useRoutes } from "react-router-dom"
import routes from "@/routes/"
import "@/App.css";
const App: React.FC = () => {
const myRoutes = useRoutes(routes);
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
{myRoutes}
</Suspense>
</div>
);
};
export default App;
3、src/pages/Layout/index.tsx:首页,使用<Outlet />在其特定位置插入子页面
import React from "react";
import { Outlet } from "react-router-dom"
import { Layout } from "antd";
import Dropdown from "@/components/header/Dropdown";
import Search from "@/components/header/Search";
import SiderMenu from "@/components/sider-menu/";
import { observer } from "mobx-react";
import mobxData from "@/store/mobx-data";
import "@/App.css";
const { Header, Content, Sider } = Layout;
const App: React.FC = () => {
return (
<Layout className="app-layout">
<Header className="app-header">
<div className="app-logo">
<img height="36" src={require("@/logo.png")} />
</div>
<div className="search">
<Search />
</div>
<Dropdown />
</Header>
<Layout className="app-wrapper">
<Sider width={mobxData.width} className="app-sider">
<SiderMenu />
</Sider>
<Layout className="app-wrapper-in">
<Content className="app-content">
<Outlet />
</Content>
</Layout>
</Layout>
</Layout>
);
};
export default observer(App);
4、src/pages/login/index.tsx:登录页
import React from "react";
import { useNavigate } from "react-router-dom";
import { Dropdown, Space, Button, Checkbox, Form, Input } from "antd";
import {
DownloadOutlined,
GlobalOutlined,
DownOutlined,
LockOutlined,
UserOutlined
} from "@ant-design/icons";
import "./index.css";
const items1 = [
{
key: "1",
label: "下载客户端"
},
{
key: "2",
label: "下载证书",
children: [
{
key: "2-1",
label: "下载应用证书"
},
{
key: "2-2",
label: "证书安装文档"
}
]
},
{
key: "3",
label: "RESTFUL API"
},
{
key: "4",
label: "Web组件"
}
];
const items2 = [
{
key: "5",
label: "简体中文"
},
{
key: "6",
label: "繁体中文"
},
{
key: "7",
label: "ENGLISH"
}
];
const App: React.FC = () => {
let navigate = useNavigate();
const onFinish = (values: string) => {
console.log("Received values of form: ", values);
localStorage.setItem("token", "2");
navigate("/");
};
return (
<div className="container">
<div className="background-container"></div>
<div className="wrapper">
<div className="oem">
<div className="oem-img"></div>
</div>
<div className="index">
<div className="wrap-header-bar">
<div className="app-index-bar" style={{ width: "504px" }}>
<div
className="app-index-bar-item"
style={{ paddingRight: "16px" }}
>
<Dropdown
menu={{
items: items1
}}
trigger={["click"]}
>
<a onClick={e => e.preventDefault()}>
<DownloadOutlined
style={{
fontSize: "14px",
cursor: "pointer",
marginRight: "5px"
}}
/>
<Space>
下载
<DownOutlined />
</Space>
</a>
</Dropdown>
</div>
<div className="app-index-bar-item">
<Dropdown
menu={{
items: items2
}}
trigger={["click"]}
>
<a onClick={e => e.preventDefault()}>
<GlobalOutlined
style={{
fontSize: "14px",
cursor: "pointer",
marginRight: "5px"
}}
/>
<Space>
简体中文
<DownOutlined />
</Space>
</a>
</Dropdown>
</div>
</div>
</div>
<div className="wrap-login">
<div className="index-panel">
<div className="logo-wrapper">
<img
className="logo-image"
src={require("./images/logo.png")}
/>
</div>
<div className="title-wrapper">企业内容门户</div>
<div className="signin-wrapper">
<Form
name="normal_login"
className="login-form"
initialValues={{
remember: true
}}
onFinish={onFinish}
>
<Form.Item
name="username"
rules={[
{
required: true,
message: "请输入账号!"
}
]}
>
<Input
prefix={<UserOutlined className="site-form-item-icon" />}
placeholder="请输入账号"
className="login-form-input"
/>
</Form.Item>
<Form.Item
name="password"
className="login-form-item"
rules={[
{
required: true,
message: "请输入密码!"
}
]}
>
<Input
prefix={<LockOutlined className="site-form-item-icon" />}
type="password"
placeholder="请输入密码"
className="login-form-input"
/>
</Form.Item>
<Form.Item>
<Form.Item name="remember" valuePropName="checked" noStyle>
<Checkbox>记住登录状态</Checkbox>
</Form.Item>
</Form.Item>
<Form.Item>
<Button
type="primary"
htmlType="submit"
className="login-form-button"
>
登 录
</Button>
</Form.Item>
</Form>
</div>
</div>
</div>
<div className="wrap-footer">
<div>
<div className="login-view-all">
登录即表示同意<a>用户协议</a>
<span>、 </span>
<span>
<a>隐私政策</a>
</span>
</div>
</div>
<div className="split"></div>
<div className="app-index-about" style={{ marginTop: "0px" }}>
<span className="version-information">版本信息</span>
<div className="app-index-about-row">
<span className="app-index-separator"> I </span>
<a
className="app-index-record"
href="http://beian.miit.gov.cn"
target="_blank"
>
沪ICP备09089247号-9
</a>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default App;
5、src/pages/404/index.tsx:错误页
import React from "react"
import {Button} from "antd"
const App: React.FC = () => {
return <div>
<p>404,当前页面不存在!</p>
<div><Button type="primary" onClick={()=>window.history.back()}>返回</Button></div>
</div>
}
export default App;
6、components/sider-menu/index.tsx:导航组件,使用<NavLink></NavLink>
import React, { useState } from "react";
import { Button, Tooltip } from "antd";
import {
HomeOutlined,
FolderOutlined,
AppstoreOutlined,
ReadOutlined,
TableOutlined,
ProductOutlined,
MenuFoldOutlined,
MenuUnfoldOutlined
} from "@ant-design/icons";
import { NavLink } from "react-router-dom";
import mobxData from "@/store/mobx-data";
import "./index.css";
const App: React.FC = () => {
const [status, setStatus] = useState(false);
const onChangeWidth = () => {
const elements = document.querySelectorAll(".menu-item dd") as NodeListOf<
HTMLElement
>;
if (status) {
mobxData.setWidth(90);
for (let i = 0; i < elements.length; i++) {
(elements[i].style as any).display = "block";
}
} else {
mobxData.setWidth(60);
for (let i = 0; i < elements.length; i++) {
(elements[i].style as any).display = "none";
}
}
setStatus(!status);
};
return (
<div className="menu">
<div className="menu-item-all">
<NavLink to="/">
<dl className="menu-item">
<dt>
{status ? (
<Tooltip
placement="right"
title="我的门户"
className="menu-tooltip"
>
<Button type="text" icon={<HomeOutlined />} />
</Tooltip>
) : (
<HomeOutlined />
)}
</dt>
<dd>我的门户</dd>
</dl>
</NavLink>
<NavLink to="/timeline">
<dl className="menu-item">
<dt>
{status ? (
<Tooltip
placement="right"
title="文档中心"
className="menu-tooltip"
>
<Button type="text" icon={<FolderOutlined />} />
</Tooltip>
) : (
<FolderOutlined />
)}
</dt>
<dd>文档中心</dd>
</dl>
</NavLink>
<NavLink to="/collapse">
<dl className="menu-item">
<dt>
{status ? (
<Tooltip
placement="right"
title="工作中心"
className="menu-tooltip"
>
<Button type="text" icon={<AppstoreOutlined />} />
</Tooltip>
) : (
<AppstoreOutlined />
)}
</dt>
<dd>工作中心</dd>
</dl>
</NavLink>
<dl className="menu-item">
<dt>
{status ? (
<Tooltip
placement="right"
title="知识中心"
className="menu-tooltip"
>
<Button type="text" icon={<ReadOutlined />} />
</Tooltip>
) : (
<ReadOutlined />
)}
</dt>
<dd>知识中心</dd>
</dl>
<dl className="menu-item">
<dt>
{status ? (
<Tooltip
placement="right"
title="表格中心"
className="menu-tooltip"
>
<Button type="text" icon={<TableOutlined />} />
</Tooltip>
) : (
<TableOutlined />
)}
</dt>
<dd>表格中心</dd>
</dl>
<NavLink to="/app">
<dl className="menu-item">
<dt>
{status ? (
<Tooltip
placement="right"
title="应用"
className="menu-tooltip"
>
<Button type="text" icon={<ProductOutlined />} />
</Tooltip>
) : (
<ProductOutlined />
)}
</dt>
<dd>应用</dd>
</dl>
</NavLink>
</div>
<div className="collapse">
<Tooltip placement="right" title={status ? "展开" : "收起"}>
<Button
type="text"
icon={
status ? (
<MenuUnfoldOutlined onClick={onChangeWidth} />
) : (
<MenuFoldOutlined onClick={onChangeWidth} />
)
}
onClick={onChangeWidth}
/>
</Tooltip>
</div>
</div>
);
};
export default App;
7、/src/routes/index.tsx:路由文件,主要是三个页面:首页(及子页面)、登录页、错误页
使用<Suspense></Suspense>是为了防止在使用路由懒加载时报错及防止页面切换时页面抖动。
import React, { Suspense, lazy } from "react";
import Auth from "@/components/auth";
import Layout from "@/Layout"
import Home from "@/pages/home/";
const Collapse = lazy(() => import("@/pages/collapse/"));
const Timeline = lazy(() => import("@/pages/timeline/"));
const Application = lazy(() => import("@/pages/application/"));
const Login = lazy(() => import("@/pages/login/"));
const NotFound = lazy(()=>import("@/pages/404/"))
const withLoadingComponent = (comp: JSX.Element) => (
<Suspense fallback={<div>Loading...</div>}>{comp}</Suspense>
)
const routes = [
{
path: "/",
element: <Layout />,
children: [
{
index: true,
element: withLoadingComponent(<Home />),
},
{
path: "collapse",
element: withLoadingComponent(<Auth><Collapse /></Auth>),
},
{
path: "timeline",
element: withLoadingComponent(<Auth><Timeline /></Auth>),
},
{
path: "app",
element: withLoadingComponent(<Auth><Application /></Auth>),
}
]
},
{
path: "/login",
element: withLoadingComponent(<Auth><Login /></Auth>),
},
{ path: '*', element: <NotFound /> },
];
export default routes;
8、路由鉴权:src/components/auth/index.tsx
- 直接输入登录页,如果没有登录则进入登录页;如果已经登录,则跳转至首页;
- 输入子页,如果没有登录,则跳转至登录页;如果已经登录,则直接进入该页;
import React from "react";
// import { useNavigate } from "react-router-dom";
import { useLocation, Navigate } from "react-router-dom";
interface Props {
children: React.ReactElement;
}
export default function Auth({ children }: Props) {
const {pathname} = useLocation();
const isAuth = localStorage.getItem("token");
if (pathname === "/login") {
if (isAuth && Number(isAuth) !== 1) {
return <Navigate to="/" />;
} else {
return <>{children}</>; // 写成:return <Navigate to="/login" />会造成无限循环
}
}
if (isAuth && Number(isAuth) !== 1) {
return <>{children}</>;
} else {
return <Navigate to="/login" />;
}
}
四、三种方法比较:
使用useRoutes()创建的方式比前两者多了一个页面:
import React, { Suspense } from "react";
import { useRoutes } from "react-router-dom"
import routes from "@/routes/"
import "@/App.css";
const App: React.FC = () => {
const myRoutes = useRoutes(routes);
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
{myRoutes}
</Suspense>
</div>
);
};
export default App;