创建一个react项目(redux,router,store,axios,antd)最后有项目地点 ...

打印 上一主题 下一主题

主题 529|帖子 529|积分 1587

第一步:利用cra脚手架 创建项目

文档地点:Create React App 中文文档
  1. npx create-react-app '你的项目名称'
复制代码
第二步:整理项目结构和删除多余代码

目标效果图:

在src目录下分别新建apis,assets,components,pages,router,store,utlis文件夹
原始项目目录结构只保留App.js,index.css,index.js这个三个文件
App.js:

index.css:

index.js:

拓展:安装scss

  1. npm install sass -D
复制代码
测试和利用:
App.js内容:

把index.css改为index.scss

index.js内容:

效果:

 第三步:安装ui组件库-Ant Design

官网链接:在 create-react-app 中利用 - Ant Design
  1. npm install antd --save
复制代码
测试利用:
src/App.js:
  1. import { Button } from 'antd';
  2. function App() {
  3.   return (
  4.     <div>
  5.       <Button type="primary">Button</Button>
  6.     </div>
  7.   );
  8. }
  9. export default App;
复制代码
效果图:

第四步:配置路由

中文文档:在开始之前 | React Router6 中文文档
第一步:安装
  1. npm install react-router-dom
复制代码
第二步:新建两个页面文件
在src/pages下新建两个文件Layout和Login

Layout/index.js:
  1. const Layout= ()=>{
  2.     return(
  3.         <div>
  4.             我是Layout
  5.         </div>
  6.     )
  7. }
  8. export default Layout
复制代码
Login/index.js:
  1. const Login= ()=>{
  2.     return(
  3.         <div>
  4.             我是Login
  5.         </div>
  6.     )
  7. }
  8. export default Login
复制代码
第三步:新建路由实例
 src/router下新建index.js
  1. import Layout from "../pages/Layout";
  2. import Login from "../pages/Login";
  3. import { createBrowserRouter } from "react-router-dom";
  4. // 配置路由
  5. const router = createBrowserRouter([
  6.     {
  7.         path: '/',
  8.         element: <Layout></Layout>
  9.     },
  10.     {
  11.         path: '/login',
  12.         element: <Login></Login>
  13.     }
  14. ])
  15. export default router
复制代码
第四步:利用挂载实例
在src/index.js种导入
  1. import React from 'react';
  2. import ReactDOM from 'react-dom/client';
  3. import './index.scss';
  4. import router from './router';
  5. import { RouterProvider } from 'react-router-dom';
  6. const root = ReactDOM.createRoot(document.getElementById('root'));
  7. root.render(
  8.   <React.StrictMode>
  9.     <RouterProvider router={router}></RouterProvider>
  10.   </React.StrictMode>
  11. );
复制代码
效果图:


第五步:封装request

第一步:修改src/pages/Login/index.js文件
  1. import { Card, Button, Form, Input, message } from 'antd';
  2. import './index.scss'
  3. import { useDispatch } from 'react-redux';
  4. import { fetchLogin } from '../../store/modules/user';
  5. import { useNavigate } from 'react-router-dom';
  6. const Login = () => {
  7.     const navigate= useNavigate()
  8.     // 实例的账号密码
  9.     // 13800000002
  10.     // 246810
  11.     const dispatch = useDispatch()
  12.     const onFinish =async (values) => {
  13.         await dispatch(fetchLogin(values))
  14.         // 跳转
  15.         navigate('/')
  16.         // 提示
  17.         message.success('登录成功')
  18.         console.log('Success:', values);
  19.       };
  20.       const onFinishFailed = (errorInfo) => {
  21.         console.log('Failed:', errorInfo);
  22.       };
  23.     return (
  24.         <div>
  25.             <Card className='card'>
  26.                 <Form
  27.                     name="basic"
  28.                     labelCol={{
  29.                         span: 8,
  30.                     }}
  31.                     wrapperCol={{
  32.                         span: 16,
  33.                     }}
  34.                     style={{
  35.                         maxWidth: 600,
  36.                     }}
  37.                     initialValues={{
  38.                         remember: true,
  39.                     }}
  40.                     onFinish={onFinish}
  41.                     onFinishFailed={onFinishFailed}
  42.                     autoComplete="off"
  43.                 >
  44.                     <Form.Item
  45.                         label="Username"
  46.                         name="mobile"
  47.                         rules={[
  48.                             {
  49.                                 required: true,
  50.                                 message: 'Please input your username!',
  51.                             },
  52.                         ]}
  53.                     >
  54.                         <Input />
  55.                     </Form.Item>
  56.                     <Form.Item
  57.                         label="Password"
  58.                         name="code"
  59.                         rules={[
  60.                             {
  61.                                 required: true,
  62.                                 message: 'Please input your password!',
  63.                             },
  64.                         ]}
  65.                     >
  66.                         <Input.Password />
  67.                     </Form.Item>
  68.                     <Form.Item
  69.                         wrapperCol={{
  70.                             offset: 8,
  71.                             span: 16,
  72.                         }}
  73.                     >
  74.                         <Button type="primary" htmlType="submit">
  75.                             Submit
  76.                         </Button>
  77.                     </Form.Item>
  78.                 </Form>
  79.             </Card>
  80.         </div>
  81.     )
  82. }
  83. export default Login
复制代码
在src/pages/Login下添加index.scss
  1. .card{
  2.     display: flex;
  3.     justify-content: center;
  4.     position: fixed;
  5.     top: 100px;
  6.     left:35%;
  7.     width: 500px;
  8. }
复制代码
第二步:封装request
中文文档起步 | Axios中文文档 | Axios中文网
安装axios
  1. npm install axios
复制代码
在src/utlis下新建request.js和index.js
request.js:
  1. // axios封装
  2. // 1 根域名配置
  3. // 2 超时时间
  4. // 3 请求拦截器
  5. // 4 响应拦截器
  6. import axios from "axios";
  7. import { getToken } from "./token";
  8. const request = axios.create({
  9.     baseURL:'http://geek.itheima.net/v1_0',
  10.     timeout:5000
  11. })
  12. // 添加请求拦截器
  13. request.interceptors.request.use(function (config) {
  14.     // 在发送请求之前做些什么
  15.     const token= getToken()
  16.     if(token){
  17.         config.headers.Authorization='Bearer '+token
  18.     }
  19.     return config;
  20.   }, function (error) {
  21.     // 对请求错误做些什么
  22.     return Promise.reject(error);
  23.   });
  24. // 添加响应拦截器
  25. request.interceptors.response.use(function (response) {
  26.     // 2xx 范围内的状态码都会触发该函数。
  27.     // 对响应数据做点什么
  28.     return response.data;
  29.   }, function (error) {
  30.     // 超出 2xx 范围的状态码都会触发该函数。
  31.     // 对响应错误做点什么
  32.     return Promise.reject(error);
  33.   });
  34. export {request}
复制代码
index.js:
  1. // 统一中转工具函数导出
  2. import { request } from "./request";
  3. import { setToken,getToken,removeToken } from "./token";
  4. export {
  5.     request,
  6.     setToken,
  7.     getToken,
  8.     removeToken
  9. }
复制代码
第六步:利用redux封装token

第一步安装react-redux/@reduxjs/toolkit
中文文档:入门 Redux | Redux 中文官网
  1. npm install @reduxjs/toolkit react-redux
复制代码
 在src/store新建modules/user.js和index.js
src/store/index.js:
  1. // 组合子模块
  2. import { configureStore } from "@reduxjs/toolkit";
  3. import userStore from "./modules/user";
  4. const store = configureStore({
  5.     reducer:{
  6.         userStore
  7.     }
  8. })
  9. export default store
复制代码
src/store/modules/user.js:
  1. // 放置和用户相关的
  2. import { createSlice } from "@reduxjs/toolkit";
  3. import { request,setToken as _setToken,getToken } from "../../utils";
  4. const userSlice= createSlice({
  5.     name:'user',
  6.     initialState:{
  7.         // token持久化
  8.         token:getToken()||'',
  9.     },
  10.     reducers:{
  11.         setToken:(state,action)=>{
  12.             state.token=action.payload
  13.             // token持久化
  14.             _setToken(action.payload)
  15.         }
  16.     }
  17. })
  18. // 解构
  19. const {setToken} = userSlice.actions
  20. // 获取reducer函数
  21. const userStore= userSlice.reducer
  22. // 封装一部方法 完成登录 获取token
  23. const fetchLogin=(loginForm)=>{
  24.     return async (dispatch)=>{
  25.         // 1 发送异步请求
  26.         const res = await request.post('/authorizations',loginForm)
  27.         // 2 提交同步修改方法
  28.         dispatch(setToken(res.data.token))
  29.     }
  30. }
  31. export {fetchLogin,setToken}
  32. export default userStore
复制代码
最后在src/index.js添加store
  1. import React from 'react';
  2. import ReactDOM from 'react-dom/client';
  3. import './index.scss';
  4. import router from './router';
  5. import { RouterProvider } from 'react-router-dom';
  6. import { Provider } from 'react-redux';
  7. import store from './store';
  8. const root = ReactDOM.createRoot(document.getElementById('root'));
  9. root.render(
  10.   <React.StrictMode>
  11.     <Provider store={store}>
  12.       <RouterProvider router={router}></RouterProvider>
  13.     </Provider>
  14.   </React.StrictMode>
  15. );
复制代码
 然后在login举行调用
封装存,取,删token的方法

在src/utlis新建token.js
  1. const TOKENKEY='token'
  2. const setToken =(token)=>{
  3.     localStorage.setItem(TOKENKEY,token)
  4. }
  5. const getToken=()=>{
  6.    return localStorage.getItem(TOKENKEY)
  7. }
  8. const removeToken=()=>{
  9.     localStorage.removeItem(TOKENKEY)
  10. }
  11. export {setToken,getToken,removeToken}
复制代码
在src/pages/Layout下举行测试

  1. // 测试toekn
  2. import { useEffect } from "react"
  3. import { request } from "../../utils"
  4. const Layout= ()=>{
  5.     useEffect(()=>{
  6.         request.get('/user/profile')
  7.     },[])
  8.     return(
  9.         <div>
  10.             我是Layout
  11.         </div>
  12.     )
  13. }
  14. export default Layout
复制代码
 第七步:根据token举行路由权限判断

在src/components下新建Auth.js
  1. // 封装高阶组件
  2. // 有token正常访问 无token跳转login
  3. import { getToken } from "../utils"
  4. import { Navigate } from "react-router-dom"
  5. const Auth = ({children}) => {
  6.     const token = getToken()
  7.     if(token){
  8.         return <>{children}</>
  9.     }else{
  10.         return <Navigate to={'/login'} replace></Navigate>
  11.     }
  12. }
  13. export default Auth
复制代码
 然后在router/index.js修改
  1. import Layout from "../pages/Layout";
  2. import Login from "../pages/Login";
  3. import { createBrowserRouter } from "react-router-dom";
  4. import Auth from "../components/Auth";
  5. import Home from "../pages/Home";
  6. import Article from "../pages/Article";
  7. import Publish from "../pages/Publish";
  8. // 配置路由
  9. const router = createBrowserRouter([
  10.     {
  11.         path: '/',
  12.         element: <Auth><Layout></Layout></Auth>,
  13.         children:[
  14.             {
  15.                 index:true,
  16.                 element:<Home></Home>
  17.             },
  18.             {
  19.                 path:'article',
  20.                 element:<Article></Article>
  21.             },            {
  22.                 path:'publish',
  23.                 element:<Publish></Publish>
  24.             }
  25.         ]
  26.     },
  27.     {
  28.         path: '/login',
  29.         element: <Login></Login>
  30.     }
  31. ])
  32. export default router
复制代码
新建src/pages下的Home,Article,Publish文件
 Home.js:
  1. const Home = ()=>{
  2.     return(
  3.         <div>我是Home</div>
  4.     )
  5. }
  6. export default Home
复制代码
下面两个文件类推!!!
 第八步:封装Laout样式组件

覆盖src/pages/Layout/index.js
  1. import React, { useEffect, useState } from 'react';
  2. import {
  3.     MenuFoldOutlined,
  4.     MenuUnfoldOutlined,
  5.     UploadOutlined,
  6.     UserOutlined,
  7.     VideoCameraOutlined,
  8. } from '@ant-design/icons';
  9. import { Button, Layout, Menu, theme, Popconfirm } from 'antd';
  10. import { Outlet, useLocation, useNavigate } from 'react-router-dom';
  11. import { useDispatch, useSelector } from 'react-redux';
  12. import { clearUserInfo, fetchUserInfo } from '../../store/modules/user';
  13. const { Header, Sider, Content } = Layout;
  14. const App = () => {
  15.     const navigste = useNavigate()
  16.     const dispatch = useDispatch()
  17.     const [collapsed, setCollapsed] = useState(false);
  18.     const {
  19.         token: { colorBgContainer, borderRadiusLG },
  20.     } = theme.useToken();
  21.     // 侧边跳转
  22.     const MenuClick = (key) => {
  23.         navigste(key)
  24.     }
  25.     useEffect(() => {
  26.         dispatch(fetchUserInfo())
  27.     }, [dispatch])
  28.     const userInfo = useSelector(state => state.userStore.userInfo)
  29.     // 退出按钮
  30.     const confirm = () => {
  31.         dispatch(clearUserInfo())
  32.         navigste('/login')
  33.     }
  34.     // 获取路由参数 初始化侧边高亮
  35.     const location = useLocation()
  36.     return (
  37.         <Layout style={{ height: '100vh' }}>
  38.             <Sider trigger={null} collapsible collapsed={collapsed}>
  39.                 <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '10px' }}>
  40.                     <img src='https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg' style={{ width: '50%', height: '50px' }} alt=''></img>
  41.                     {!collapsed &&
  42.                         <div style={{ color: '#fff', width: '50%' }}>哈哈哈</div>
  43.                     }
  44.                 </div>
  45.                 <Menu
  46.                     onClick={({ key }) => MenuClick(key)}
  47.                     theme="dark"
  48.                     mode="inline"
  49.                     defaultSelectedKeys={[location.pathname]}
  50.                     items={[
  51.                         {
  52.                             key: '/',
  53.                             icon: <UserOutlined />,
  54.                             label: '首页',
  55.                         },
  56.                         {
  57.                             key: '/article',
  58.                             icon: <VideoCameraOutlined />,
  59.                             label: '文章',
  60.                         },
  61.                         {
  62.                             key: '/publish',
  63.                             icon: <UploadOutlined />,
  64.                             label: '发布',
  65.                         },
  66.                     ]}
  67.                 />
  68.             </Sider>
  69.             <Layout>
  70.                 <Header
  71.                     style={{
  72.                         padding: '0 24px 0 0',
  73.                         background: colorBgContainer,
  74.                         display: 'flex',
  75.                         justifyContent: 'space-between'
  76.                     }}
  77.                 >
  78.                     <Button
  79.                         type="text"
  80.                         icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
  81.                         onClick={() => setCollapsed(!collapsed)}
  82.                         style={{
  83.                             fontSize: '16px',
  84.                             width: 64,
  85.                             height: 64,
  86.                         }}
  87.                     />
  88.                     <div style={{ display: 'flex', alignItems: 'center', fontSize: '16px' }}>
  89.                         <span>{userInfo.name}</span>
  90.                         <UserOutlined style={{ marginLeft: '10px' }} />
  91.                         <Popconfirm
  92.                             title="退出"
  93.                             description="确认退出吗?"
  94.                             onConfirm={confirm}
  95.                             okText="Yes"
  96.                             cancelText="No"
  97.                         >
  98.                             <span style={{ cursor: "pointer" }}>退出</span>
  99.                         </Popconfirm>
  100.                     </div>
  101.                 </Header>
  102.                 <Content
  103.                     style={{
  104.                         margin: '24px 16px',
  105.                         padding: 24,
  106.                         minHeight: 280,
  107.                         background: colorBgContainer,
  108.                         borderRadius: borderRadiusLG,
  109.                     }}
  110.                 >
  111.                     {/* 二级路由出口 */}
  112.                     <Outlet></Outlet>
  113.                 </Content>
  114.             </Layout>
  115.         </Layout>
  116.     );
  117. };
  118. export default App;
复制代码
封装退出方法
src/store/modules/user.js
  1. // 放置和用户相关的
  2. import { createSlice } from "@reduxjs/toolkit";
  3. import { request,setToken as _setToken,getToken, removeToken } from "../../utils";
  4. const userSlice= createSlice({
  5.     name:'user',
  6.     initialState:{
  7.         // token持久化
  8.         token:getToken()||'',
  9.         userInfo:{}
  10.     },
  11.     reducers:{
  12.         setToken:(state,action)=>{
  13.             state.token=action.payload
  14.             // token持久化
  15.             _setToken(action.payload)
  16.         },
  17.         setUserInfo:(state,action)=>{
  18.             state.userInfo=action.payload
  19.         },
  20.         clearUserInfo:(state)=>{
  21.             state.token=''
  22.             state.userInfo={}
  23.             removeToken()
  24.         }
  25.     }
  26. })
  27. // 解构
  28. const {setToken,setUserInfo,clearUserInfo} = userSlice.actions
  29. // 获取reducer函数
  30. const userStore= userSlice.reducer
  31. // 封装异步方法 完成登录 获取token
  32. const fetchLogin=(loginForm)=>{
  33.     return async (dispatch)=>{
  34.         // 1 发送异步请求
  35.         const res = await request.post('/authorizations',loginForm)
  36.         // 2 提交同步修改方法
  37.         dispatch(setToken(res.data.token))
  38.     }
  39. }
  40. // 获取个人信息
  41. const fetchUserInfo=()=>{
  42.     return async (dispatch)=>{
  43.         // 1 发送异步请求
  44.         const res = await request.get('/user/profile')
  45.         // 2 提交同步修改方法
  46.         dispatch(setUserInfo(res.data))
  47.     }
  48. }
  49. export {fetchLogin,setToken,fetchUserInfo,clearUserInfo}
  50. export default userStore
复制代码
 第九步:处置处罚token失效

src/utlis/request.js
  1. // axios封装
  2. // 1 根域名配置
  3. // 2 超时时间
  4. // 3 请求拦截器
  5. // 4 响应拦截器
  6. import axios from "axios";
  7. import { getToken, removeToken } from "./token";
  8. import router from "../router";
  9. const request = axios.create({
  10.     baseURL:'http://geek.itheima.net/v1_0',
  11.     timeout:5000
  12. })
  13. // 添加请求拦截器
  14. request.interceptors.request.use(function (config) {
  15.     // 在发送请求之前做些什么
  16.     const token= getToken()
  17.     if(token){
  18.         config.headers.Authorization='Bearer '+token
  19.     }
  20.     return config;
  21.   }, function (error) {
  22.     // 对请求错误做些什么
  23.     return Promise.reject(error);
  24.   });
  25. // 添加响应拦截器
  26. request.interceptors.response.use(function (response) {
  27.     // 2xx 范围内的状态码都会触发该函数。
  28.     // 对响应数据做点什么
  29.     return response.data;
  30.   }, function (error) {
  31.     // 超出 2xx 范围的状态码都会触发该函数。
  32.     // 对响应错误做点什么
  33.     // 处理token失效
  34.     if(error.response.status===401){
  35.         removeToken()
  36.         router.navigate('/login')
  37.         window.location.reload()
  38.     }
  39.     return Promise.reject(error);
  40.   });
  41. export {request}
复制代码
第十步:封装Api请求

src/apis/user.js
  1. import { request } from "../utils";
  2. const loginApi=(data)=>{
  3.     return request({
  4.         url:'/authorizations',
  5.         method:'post',
  6.         data
  7.     })
  8. }
  9. const userInfoApi=()=>{
  10.     return request({
  11.         url:'/user/profile',
  12.         method:'get',
  13.     })
  14. }
  15. export {loginApi,userInfoApi}
复制代码
修改src/store/modules/user.js
  1. // 放置和用户相关的
  2. import { createSlice } from "@reduxjs/toolkit";
  3. import {setToken as _setToken,getToken, removeToken } from "../../utils";
  4. import { loginApi, userInfoApi } from "../../apis/user";
  5. const userSlice= createSlice({
  6.     name:'user',
  7.     initialState:{
  8.         // token持久化
  9.         token:getToken()||'',
  10.         userInfo:{}
  11.     },
  12.     reducers:{
  13.         setToken:(state,action)=>{
  14.             state.token=action.payload
  15.             // token持久化
  16.             _setToken(action.payload)
  17.         },
  18.         setUserInfo:(state,action)=>{
  19.             state.userInfo=action.payload
  20.         },
  21.         clearUserInfo:(state)=>{
  22.             state.token=''
  23.             state.userInfo={}
  24.             removeToken()
  25.         }
  26.     }
  27. })
  28. // 解构
  29. const {setToken,setUserInfo,clearUserInfo} = userSlice.actions
  30. // 获取reducer函数
  31. const userStore= userSlice.reducer
  32. // 封装异步方法 完成登录 获取token
  33. const fetchLogin=(loginForm)=>{
  34.     return async (dispatch)=>{
  35.         // 1 发送异步请求
  36.         const res = await loginApi(loginForm)
  37.         // 2 提交同步修改方法
  38.         dispatch(setToken(res.data.token))
  39.     }
  40. }
  41. // 获取个人信息
  42. const fetchUserInfo=()=>{
  43.     return async (dispatch)=>{
  44.         // 1 发送异步请求
  45.         const res = await userInfoApi()
  46.         // 2 提交同步修改方法
  47.         dispatch(setUserInfo(res.data))
  48.     }
  49. }
  50. export {fetchLogin,setToken,fetchUserInfo,clearUserInfo}
  51. export default userStore
复制代码
拓展:利用图表

Echarts文档:Examples - Apache ECharts
在src/pages/新建一个Echarts文件来放置我们的echrats
src/pages/Echarts/index.js:
  1. import * as echarts from 'echarts';
  2. import { useEffect, useRef } from 'react';
  3. const Echarts = () => {
  4.     const chartRef= useRef(null)
  5.     useEffect(() => {
  6.         // 保证dom是可用的
  7.         // 1 获取要渲染的图标节点
  8.         const chartDom = chartRef.current
  9.         // 2 初始化生成echarts实例
  10.         const myChart = echarts.init(chartDom);
  11.         //  3 配置参数
  12.         const option = {
  13.             xAxis: {
  14.                 type: 'category',
  15.                 data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
  16.             },
  17.             yAxis: {
  18.                 type: 'value'
  19.             },
  20.             series: [
  21.                 {
  22.                     data: [120, 200, 150, 80, 70, 110, 130],
  23.                     type: 'bar'
  24.                 }
  25.             ]
  26.         };
  27.         // 4 使用参数完成渲染
  28.         option && myChart.setOption(option);
  29.     }, [])
  30.     return (
  31.         <div>
  32.             <div ref={chartRef} style={{width:'600px',height:'600px'}}></div>
  33.         </div>
  34.     )
  35. }
  36. export default Echarts
复制代码
在src/router/index.js添加路由
  1. import Layout from "../pages/Layout";
  2. import Login from "../pages/Login";
  3. import { createBrowserRouter } from "react-router-dom";
  4. import Auth from "../components/Auth";
  5. import Home from "../pages/Home";
  6. import Article from "../pages/Article";
  7. import Publish from "../pages/Publish";
  8. import Echarts from "../pages/Echarts";
  9. // 配置路由
  10. const router = createBrowserRouter([
  11.     {
  12.         path: '/',
  13.         element: <Auth><Layout></Layout></Auth>,
  14.         children: [
  15.             {
  16.                 index: true,
  17.                 element: <Home></Home>
  18.             },
  19.             {
  20.                 path: 'article',
  21.                 element: <Article></Article>
  22.             }, {
  23.                 path: 'publish',
  24.                 element: <Publish></Publish>
  25.             },
  26.             {
  27.                 path:'echarts',
  28.                 element:<Echarts></Echarts>
  29.             }
  30.         ]
  31.     },
  32.     {
  33.         path: '/login',
  34.         element: <Login></Login>
  35.     }
  36. ])
  37. export default router
复制代码
在src/pages/Layout/index.js追加路由
  1. import React, { useEffect, useState } from 'react';
  2. import {
  3.     MenuFoldOutlined,
  4.     MenuUnfoldOutlined,
  5.     UploadOutlined,
  6.     UserOutlined,
  7.     VideoCameraOutlined,
  8. } from '@ant-design/icons';
  9. import { Button, Layout, Menu, theme, Popconfirm } from 'antd';
  10. import { Outlet, useLocation, useNavigate } from 'react-router-dom';
  11. import { useDispatch, useSelector } from 'react-redux';
  12. import { clearUserInfo, fetchUserInfo } from '../../store/modules/user';
  13. const { Header, Sider, Content } = Layout;
  14. const App = () => {
  15.     const navigste = useNavigate()
  16.     const dispatch = useDispatch()
  17.     const [collapsed, setCollapsed] = useState(false);
  18.     const {
  19.         token: { colorBgContainer, borderRadiusLG },
  20.     } = theme.useToken();
  21.     // 侧边跳转
  22.     const MenuClick = (key) => {
  23.         navigste(key)
  24.     }
  25.     useEffect(() => {
  26.         dispatch(fetchUserInfo())
  27.     }, [dispatch])
  28.     const userInfo = useSelector(state => state.userStore.userInfo)
  29.     // 退出按钮
  30.     const confirm = () => {
  31.         dispatch(clearUserInfo())
  32.         navigste('/login')
  33.     }
  34.     // 获取路由参数 初始化侧边高亮
  35.     const location = useLocation()
  36.     return (
  37.         <Layout style={{ height: '100vh' }}>
  38.             <Sider trigger={null} collapsible collapsed={collapsed}>
  39.                 <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '10px' }}>
  40.                     <img src='https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg' style={{ width: '50%', height: '50px' }} alt=''></img>
  41.                     {!collapsed &&
  42.                         <div style={{ color: '#fff', width: '50%' }}>哈哈哈</div>
  43.                     }
  44.                 </div>
  45.                 <Menu
  46.                     onClick={({ key }) => MenuClick(key)}
  47.                     theme="dark"
  48.                     mode="inline"
  49.                     defaultSelectedKeys={[location.pathname]}
  50.                     items={[
  51.                         {
  52.                             key: '/',
  53.                             icon: <UserOutlined />,
  54.                             label: '首页',
  55.                         },
  56.                         {
  57.                             key: '/article',
  58.                             icon: <VideoCameraOutlined />,
  59.                             label: '文章',
  60.                         },
  61.                         {
  62.                             key: '/publish',
  63.                             icon: <UploadOutlined />,
  64.                             label: '发布',
  65.                         },
  66.                         {
  67.                             key: '/echarts',
  68.                             icon: <UploadOutlined />,
  69.                             label: '图标',
  70.                         },
  71.                     ]}
  72.                 />
  73.             </Sider>
  74.             <Layout>
  75.                 <Header
  76.                     style={{
  77.                         padding: '0 24px 0 0',
  78.                         background: colorBgContainer,
  79.                         display: 'flex',
  80.                         justifyContent: 'space-between'
  81.                     }}
  82.                 >
  83.                     <Button
  84.                         type="text"
  85.                         icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
  86.                         onClick={() => setCollapsed(!collapsed)}
  87.                         style={{
  88.                             fontSize: '16px',
  89.                             width: 64,
  90.                             height: 64,
  91.                         }}
  92.                     />
  93.                     <div style={{ display: 'flex', alignItems: 'center', fontSize: '16px' }}>
  94.                         <span>{userInfo.name}</span>
  95.                         <UserOutlined style={{ marginLeft: '10px' }} />
  96.                         <Popconfirm
  97.                             title="退出"
  98.                             description="确认退出吗?"
  99.                             onConfirm={confirm}
  100.                             okText="Yes"
  101.                             cancelText="No"
  102.                         >
  103.                             <span style={{ cursor: "pointer" }}>退出</span>
  104.                         </Popconfirm>
  105.                     </div>
  106.                 </Header>
  107.                 <Content
  108.                     style={{
  109.                         margin: '24px 16px',
  110.                         padding: 24,
  111.                         minHeight: 280,
  112.                         background: colorBgContainer,
  113.                         borderRadius: borderRadiusLG,
  114.                     }}
  115.                 >
  116.                     {/* 二级路由出口 */}
  117.                     <Outlet></Outlet>
  118.                 </Content>
  119.             </Layout>
  120.         </Layout>
  121.     );
  122. };
  123. export default App;
复制代码
效果图:

拓展:利用富文本编辑器

这里介绍的富文本叫ReactQuill,利用比较简单,用的也比较多,实用于大多数场景!
npm地点:react-quill - npm
  1. npm install react-quill --save
复制代码
在src/pages/Publish/index.js:
  1. import ReactQuill from 'react-quill';
  2. import 'react-quill/dist/quill.snow.css';
  3. // 以上为导入资源
  4. import { useState } from 'react';
  5. const Publish = () => {
  6.     // value 就是富文本的值
  7.     const [value, setValue] = useState('');
  8.     console.log(value);
  9.     return (
  10.         <div>
  11.             <ReactQuill style={{ width: '500px', height: '200px' }} theme="snow" value={value} onChange={setValue} >
  12.             </ReactQuill>
  13.         </div>
  14.     )
  15. }
  16. export default Publish
复制代码
效果图:

到这里恭喜你完成了一个简单版本的react后台模板
示例代码地点:react-demo-pc: 训练利用的一个react后台模板,主要熟悉接洽ui组件库,redux,router,axios

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

耶耶耶耶耶

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表