项目

85 阅读8分钟

react-admin

创建项目安装依赖

npx create-react-app react-admin

安装依赖:

cnpm i axios redux react-redux immutable redux-immutable redux-thunk node-sass react-router@5 react-router-dom@5 antd

将项目架构搭建起来

  1. 将所需要的 layout 组件拿过来

    ant.design/components/…

  2. 在 App.jsx 中复制刚选中的 layout 布局


// App.jsx
import {
  MenuFoldOutlined,
  MenuUnfoldOutlined,
  UploadOutlined,
  UserOutlined,
  VideoCameraOutlined,
} from '@ant-design/icons';
import { Layout, Menu, theme } from 'antd';
import React, { useState } from 'react';
import './App.scss'
const { Header, Sider, Content } = Layout;
const App = () => {
  const [collapsed, setCollapsed] = useState(false);
  const {
    token: { colorBgContainer },
  } = theme.useToken();
  return (
    <Layout id='components-layout'>
      <Sider trigger={null} collapsible collapsed={collapsed}>
        <div className="logo" />
        <Menu
          theme="dark"
          mode="inline"
          defaultSelectedKeys={['1']}
          items={[
            {
              key: '1',
              icon: <UserOutlined />,
              label: 'nav 1',
            },
            {
              key: '2',
              icon: <VideoCameraOutlined />,
              label: 'nav 2',
            },
            {
              key: '3',
              icon: <UploadOutlined />,
              label: 'nav 3',
            },
          ]}
        />
      </Sider>
      <Layout className="site-layout">
        <Header
          style={{
            padding: 0,
            background: colorBgContainer,
          }}
        >
          {React.createElement(collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, {
            className: 'trigger',
            onClick: () => setCollapsed(!collapsed),
          })}
        </Header>
        <Content
          style={{
            margin: '24px 16px',
            padding: 24,
            minHeight: 280,
            background: colorBgContainer,
          }}
        >
          Content
        </Content>
      </Layout>
    </Layout>
  );
};
export default App;
​
  1. 创建一个 index.scss 用来放全局样式
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  list-style: none;
  text-decoration: none;
  // 设置网页内容无法被选中
  user-select: none;
}
html, body, #root,#components-layout {
  height: 100%;
}
#components-layout .trigger {
  padding: 0 24px;
  font-size: 18px;
  line-height: 64px;
  cursor: pointer;
  transition: color 0.3s;
}
​
#components-layout .trigger:hover {
  color: #1890ff;
}
​
#components-layout .logo {
  height: 32px;
  margin: 16px;
  background: rgba(255, 255, 255, 0.3);
}
​
  1. 在入口的 Index.js 文件中导入 index.scss文件
  1. logo 处理
  1. 导入 logo , import logo from './logo.svg'

  2. 添加标题和 logo

    <div className="logo">
      <img style={{height:30}} src={logo} alt="" />
      {!collapsed && (<span style={{lineHeight: '32px', color: '#fff', fontSize: 18}}>嗨购管理系统</span>)}
    </div>
    

页面处理

app.jsx

// App.jsx
import {
  MenuFoldOutlined,
  MenuUnfoldOutlined,
  UploadOutlined,
  UserOutlined,
  VideoCameraOutlined,
} from '@ant-design/icons';
import { Layout, Menu, theme } from 'antd';
import React, { useState } from 'react';
import './App.scss'
// 导入小图标
import logo from './logo.svg'const { Header, Sider, Content } = Layout;
const App = () => {
  // 是否为打开
  const [collapsed, setCollapsed] = useState(false);
​
​
​
  return (
    <Layout id='components-layout'>
      <Sider trigger={null} collapsible collapsed={collapsed}>
        <div className="logo">
          <img style={{height:30}} src={logo} alt="" />
          {!collapsed && (<span style={{lineHeight: '32px', color: '#fff', fontSize: 18}}>嗨购管理系统</span>)}
        </div>
        <Menu
          theme="dark"
          mode="inline"
          defaultSelectedKeys={['1']}
          items={[
            {
              key: '1',
              icon: <UserOutlined />,
              label: '首页',
            },
            {
              key: '2',
              icon: <VideoCameraOutlined />,
              label: '账号管理',
            },
            {
              key: '3',
              icon: <UploadOutlined />,
              label: '轮播图管理',
            },
          ]}
        />
      </Sider>
      <Layout className="site-layout">
        <Header
          style={{
            padding: 0,
            background: '#fff',
          }}
        >
          {React.createElement(collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, {
            className: 'trigger',
            onClick: () => setCollapsed(!collapsed),
          })}
        </Header>
        <Content
          style={{
            margin: '24px 16px',
            padding: 24,
            minHeight: 280,
            background: '#fff',
          }}
        >
          Content
        </Content>
      </Layout>
    </Layout>
  );
};
export default App;
​

1. 拆分 layout

1. 头部

// AppHeaderimport {
  MenuFoldOutlined,
  MenuUnfoldOutlined,
} from '@ant-design/icons';
import { Layout } from 'antd';
import React, { useState } from 'react';
​
​
const { Header } = Layout;
​
const AppHeader = () => {
  // 是否为打开
  const [collapsed, setCollapsed] = useState(false);
​
  return (
    <Header
    style={{
      padding: 0,
      background: '#fff',
    }}
  >
    {React.createElement(collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, {
      className: 'trigger',
      onClick: () => setCollapsed(!collapsed),
    })}
  </Header>
  );
//
};
​
export default AppHeader;
​

2.侧边栏

// SiderBar
import {
  UploadOutlined,
  UserOutlined,
  VideoCameraOutlined,
} from '@ant-design/icons';
import { Layout, Menu } from 'antd';
import React, { useState } from 'react';
​
// 导入小图标
import logo from '../../logo.svg'const {  Sider } = Layout;
​
const SiderBar = () => {
  // 是否为打开
  const [collapsed] = useState(false);
​
  return (
    <Sider trigger={null} collapsible collapsed={collapsed}>
        <div className="logo">
          <img style={{height:30}} src={logo} alt="" />
          {!collapsed && (<span style={{lineHeight: '32px', color: '#fff', fontSize: 18}}>嗨购管理系统</span>)}
        </div>
        <Menu
          theme="dark"
          mode="inline"
          defaultSelectedKeys={['1']}
          items={[
            {
              key: '1',
              icon: <UserOutlined />,
              label: '首页',
            },
            {
              key: '2',
              icon: <VideoCameraOutlined />,
              label: '账号管理',
            },
            {
              key: '3',
              icon: <UploadOutlined />,
              label: '轮播图管理',
            },
          ]}
        />
      </Sider>
  );
};
​
export default SiderBar;
​

3. 内容

// AppMain
import { Layout } from 'antd';
import React from 'react';
​
​
const {  Content } = Layout;
​
const AppMain = () => {
  return (
    <Content
          style={{
            margin: '24px 16px',
            padding: 24,
            minHeight: 280,
            background: '#fff',
          }}
        >
          Content
        </Content>
  );
};
​
export default AppMain;
​

4. 收集导出组件

// 用来导入收集组件用的export { default as AppHeader } from './AppHeader'
export { default as AppMain } from './AppMain'
export { default as SiderBar } from './SiderBar'

5. Index.jsx 显示组件

import { Layout } from 'antd';
import React from 'react';
// 导入所有组件
import {AppHeader,AppMain,SiderBar} from './components/index'
const App = () => {
  return (
    <Layout id='components-layout'>
      <SiderBar />
      <Layout className="site-layout">
        <AppHeader />
        <AppMain />
      </Layout>
    </Layout>
  );
};
export default App;
​

2. 全局状态管理

  1. 先在 src/store/index.js 创建 store

    import {legacy_createStore as createStore,applyMiddleware} from 'redux'
    import thunk from 'redux-thunk'
    import {combineReducers} from 'redux-immutable'
    import app from './modules/app'
    const reducer = combineReducers({
        app
    })
    const store = createStore(reducer,applyMiddleware(thunk))
    export default store
    
  2. 在 src/store/modules/app.js 创建关于侧边栏是否展开状态的 reducer

    import { Map } from "immutable";
    const reducer = (state = Map({
        collapsed:localStorage.getItem('collapsed') === 'true'
    }),action) => {
        switch (action.type) {
            case 'change_collapsed':
                localStorage.setItem('collapsed',!state.get('collapsed'))
                return state.set('collapsed',!state.get('collapsed'))
        
            default:
                return state;
        }
    }
    export default reducer
    
  3. 入口 index.js 文件中配置 store

    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import './index.scss'
    import App from './App';
    import store from './store/index';*****
    import {Provider} from 'react-redux'****
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
       <Provider store={store}>
         <App />
       </Provider>
    );
    
  4. 在 AppHeader.jsx 中导入 connect 将全局状态的属性和方法获取到,然后使用

    import {
        MenuFoldOutlined,
        MenuUnfoldOutlined,
      } from '@ant-design/icons';
      import { Layout, } from 'antd';
      import React from 'react';
      import { connect } from 'react-redux';
      const { Header } = Layout;
    const Headers = ({changeCollapsed,collapsed}) => {
        return (
            <Header
              style={{
                padding: 0,
                background: '#fff',
              }}
            >
              {React.createElement(collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, {
                className: 'trigger',
                onClick: () => changeCollapsed(),
              })}
            </Header>
        );
    };
    ​
    export default connect(state => {
        return {
            collapsed:state.getIn(['app','collapsed'])
        }
    },dispatch => {
        return {
            changeCollapsed(){
                dispatch({type:'change_collapsed'})
            }
        }
    })(Headers);
    

    5.进入侧边栏组件通过 connect 拿到是否展开的属性,然后使用即可

    import {
        UploadOutlined,
        UserOutlined,
        VideoCameraOutlined,
      } from '@ant-design/icons';
      import { Layout, Menu } from 'antd';
      import React from 'react';
      import logo from '../../logo.svg'
      import {connect } from 'react-redux';
      const { Sider } = Layout;
    const Aside = ({collapsed}) => {
    ​
        return (
            <Sider trigger={null} collapsible collapsed={collapsed}>
            <div className="logo">
            <img style={{height:30}} src={logo} alt="" />
              {!collapsed && (<span style={{lineHeight: '32px', color: '#fff', fontSize: 14}}>OA后台管理系统</span>)}
            </div>
            <Menu
              theme="dark"
              mode="inline"
              defaultSelectedKeys={['1']}
              items={[
                {
                  key: '1',
                  icon: <UserOutlined />,
                  label: '首页',
                },
                {
                  key: '2',
                  icon: <VideoCameraOutlined />,
                  label: '组织管理',
                },
                {
                  key: '3',
                  icon: <UploadOutlined />,
                  label: '员工管理',
                },
              ]}
            />
          </Sider>
        );
    };
    ​
    export default connect(state =>{
        return {
            collapsed:state.getIn(['app','collapsed'])
        }
    })(Aside);
    

    路由模式

    //App.jsx
    import React from 'react';
    import Index from './layout/Index'
    import { HashRouter,Switch,Route } from 'react-router-dom';
    const App = () => {
      return (
        <HashRouter>
          <Switch>
            <Route path='/'component={Index}></Route>
          </Switch>
        </HashRouter>
      );
    };
    export default App;
    

登录模块

1. 创建一个 login 页面

进入UI组件库选择表单组件

将表单组件中的内容改成我们所需要的

在 App.jsx 中配置路由处理,加了登录的路由,且添加了一个精确匹配

//App.jsx
import React from 'react';
import Index from './layout/Index'
import Login from './views/login/Index'
import { HashRouter,Switch,Route } from 'react-router-dom';
const App = () => {
  return (
    <HashRouter>
      <Switch>
        <Route exact path='/login'component={Login}></Route>
        <Route path='/'component={Index}></Route>
      </Switch>
    </HashRouter>
  );
};
export default App;

测试登录的事件没问题

将 utils 的 request.js 中关于响应拦截器中的 token 失效的处理注释掉


import axios from 'axios'
const isDev = process.env.NODE_ENV === 'development'const request = axios.create({
  baseURL: isDev ? 'http://121.89.205.189:3000/admin' : 'http://121.89.205.189:3000/admin',
  timeout: 60000, // 默认值是 `0` (永不超时)
})
​
// 添加请求拦截器
request.interceptors.request.use(function (config) {
​
  const token = localStorage.getItem('token')
  config.headers.token = token
  return config;
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error);
});
​
// 添加响应拦截器
request.interceptors.response.use(function (response) {
  if (response.data.code === '10119') {
​
  }
  return response.data;
}, function (error) {
  // 对响应错误做点什么
  return Promise.reject(error);
});
​
export default function ajax (config) {
​
  const { url = '', method = 'GET', data = {}, headers = {} } = config
​
​
  switch (method.toUpperCase()) {
    case 'GET':
​
      return request.get(url, { params: data })
​
    case 'POST':
​
      if (headers['content-type'] === 'application/x-www-form-url-encoded') {
​
        const obj = new URLSearchParams()
        for (const key in data) {
          obj.append(key, data[key])
        }
        return request.post(url, obj, { headers })
      }
​
      if (headers['content-type'] === 'multipart/form-data') {
        const obj = new FormData()
        for (const key in data) {
          obj.append(key, data[key])
        }
        return request.post(url, obj, { headers })
      }
​
​
      return request.post(url, data)
​
    case 'PUT':
​
      return request.put(url, data)
    case 'DELETE':
​
      return request.delete(url, { data })
    case 'PATCH':
​
      return request.patch(url, data)
​
    default:
​
      return request.request(config)
  }
}
​
​

创建 api/user.js 文件封装一个登录的请求接口

import axios from '../utils/request'
export function loginFn(data){
    return axios({
        url:'/admin/login',
        method:'post',
        data
    }
    )
}

login/index.jsx 调登录接口

import React from 'react';
import './index.scss'
import { LockOutlined, UserOutlined } from '@ant-design/icons';
import { Button, Checkbox, Form, Input } from 'antd';
import { loginFn } from '../../api/user';
const Index = () => {
    const onFinish = (values) => {
        loginFn(values).then(res =>{
            console.log(res);
        })
      };
    return (
        <Form></Form>
    );
};
​
export default Index;

登录之后的错误处理

轻提示

import React from 'react';
import './index.scss'
import { LockOutlined, UserOutlined } from '@ant-design/icons';
import { Button, Checkbox, Form, Input,message, Space} from 'antd';
import { loginFn } from '../../api/user';
const Index = () => {
    const [messageApi, contextHolder] = message.useMessage();
    const onFinish = (values) => {
        loginFn(values).then(res =>{
            console.log(res);
            if(res.code ==='200'){
                messageApi.open({
                    type:'success',
                    content:res.message
                })
            }else{
                messageApi.open({
                    type:'error',
                    content:res.message
                })
            }
        })
      };
    return (
        <>
        {contextHolder}
        <Form
            name="normal_login"
            className="login-form"
            initialValues={{
                remember: true,
            }}
            onFinish={onFinish}
        >
            <Form.Item
                name="adminname"
                rules={[
                    {
                        required: true,
                        message: '用户名不能为空!',
                    },
                ]}
            >
                <Input prefix={<UserOutlined className="site-form-item-icon" />} placeholder="请输入用户名" />
            </Form.Item>
            <Form.Item
                name="password"
                rules={[
                    {
                        required: true,
                        message: '密码不能为空!',
                    },
                ]}
            >
                <Input
                    prefix={<LockOutlined className="site-form-item-icon" />}
                    type="password"
                    placeholder="请输入密码"
                />
            </Form.Item>
            <Form.Item>
                <Form.Item name="remember" valuePropName="checked" noStyle>
                    <Checkbox>记住密码</Checkbox>
                </Form.Item>
​
                <a className="login-form-forgot" href="">
                    忘记密码
                </a>
            </Form.Item>
​
            <Form.Item>
                <Button type="primary" htmlType="submit" className="login-form-button">
                    登录
                </Button>
                或者<a href="">去注册!</a>
            </Form.Item>
        </Form>
        </>
       
    );
};
​
export default Index;

2. 创建登录之后的用户状态管理

创建一个关于用户的 reducer,store/modules/user.js

import { Map } from "immutable";
const reducer = (state = Map({
    loginState: localStorage.getItem('loginState')=== 'true',
    token: localStorage.getItem('token')||'',
    adminname: localStorage.getItem('adminname')||'',
    role: localStorage.getItem('role') * 1||1,
    checkedkeys: JSON.parse(localStorage.getItem('checkedkeys'))||[],
}), { type, payload }) => {
    switch (type) {
        case 'change-loginState':
            return state.set('loginState', payload)
        case 'change-token':
            return state.set('token', payload)
        case 'change-adminname':
            return state.set('adminname', payload)
        case 'change-role':
            return state.set('role', payload)
        case 'change-checkedkeys':
            return state.set('checkedkeys', payload)
​
​
        default:
            return state;
    }
}
export default reducer

在 store/index.js 文件中进行配置该 reducer

import {legacy_createStore as createStore,applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import {combineReducers} from 'redux-immutable'
import app from './modules/app'
import user from './modules/user'
const reducer = combineReducers({
    app,
    user
})
const store = createStore(reducer,applyMiddleware(thunk))
export default store

在登录页面 通过 connect 将修改的方法映射到我们的 props 中

在提交表单的时候进行数据请求

3. 登录成功之后的处理

验证用户是否登录成功

失败给出对应的警告提示

成功我们会保存用户信息到本地和全局状态

跳转到首页面

import React from 'react';
import './index.scss'
import { LockOutlined, UserOutlined } from '@ant-design/icons';
import { Button, Checkbox, Form, Input,message} from 'antd';
import { useHistory } from 'react-router-dom';
import { connect } from 'react-redux';
import { loginFn } from '../../api/user';
const Index = ({loginDispatch}) => {
    const history = useHistory()
    const [messageApi, contextHolder] = message.useMessage();
    const onFinish = (values) => {
        loginDispatch(values,messageApi).then(()=> {
            //登录完成之后的跳转
            history.push('/')
        })
      };
    return (
        <>
        {contextHolder}
        <Form
            name="normal_login"
            className="login-form"
            initialValues={{
                remember: true,
            }}
            onFinish={onFinish}
        >
            <Form.Item
                name="adminname"
                rules={[
                    {
                        required: true,
                        message: '用户名不能为空!',
                    },
                ]}
            >
                <Input prefix={<UserOutlined className="site-form-item-icon" />} placeholder="请输入用户名" />
            </Form.Item>
            <Form.Item
                name="password"
                rules={[
                    {
                        required: true,
                        message: '密码不能为空!',
                    },
                ]}
            >
                <Input
                    prefix={<LockOutlined className="site-form-item-icon" />}
                    type="password"
                    placeholder="请输入密码"
                />
            </Form.Item>
            <Form.Item>
                <Form.Item name="remember" valuePropName="checked" noStyle>
                    <Checkbox>记住密码</Checkbox>
                </Form.Item>
​
                <a className="login-form-forgot" href="">
                    忘记密码
                </a>
            </Form.Item>
​
            <Form.Item>
                <Button type="primary" htmlType="submit" className="login-form-button">
                    登录
                </Button>
                或者<a href="">去注册!</a>
            </Form.Item>
        </Form>
        </>
       
    );
};
​
export default connect(null,dispatch => {
    return {
        loginDispatch(values,messageApi){
            return new Promise(resolve => {
                loginFn(values).then(res =>{
                    console.log(res);
                    if(res.code ==='200'){
                        messageApi.open({
                            type:'success',
                            content:res.message
                        })
                        //存储本地
                        localStorage.setItem('loginState',true)
                        localStorage.setItem('token',res.data.token)
                        localStorage.setItem('adminname',res.data.adminname)
                        localStorage.setItem('role',res.data.role)
                        localStorage.setItem('checkedkeys',JSON.stringify(res.data.checkedkeys))
                        
                        //存储全局
                        dispatch({type:'change-loginState',payload:true})
                        dispatch({type:'change-token',payload:res.data.token})
                        dispatch({type:'change-adminname',payload:res.data.adminname})
                        dispatch({type:'change-role',payload:res.data.role})
                        dispatch({type:'change-checkedkeys',payload:res.data.checkedkeys})
                       //告诉已经成功
                        resolve()
                    }else{
                        messageApi.open({
                            type:'error',
                            content:res.message
                        })
                    }
                })
            })
        }
    }
})(Index);

页面创建和路由处理

创建页面

banner

error

home

login

pro

user

1.创建 src/router/index.js 配置路由规则

import React from 'react'
import {
  UploadOutlined,
  UserOutlined,
  OrderedListOutlined
} from '@ant-design/icons';
​
const menus = [
  {
    key: '/',
    icon: <UserOutlined />,
    label: '系统首页',
    component: React.lazy(() => import('../vuews/home/Index'))
  },
  {
    key: '/user',
    icon: <UserOutlined />,
    label: '账号管理',
    children: [
      {
        key: '/user/list',
        icon: <UserOutlined />,
        label: '用户列表',
        component: React.lazy(() => import('../vuews/user/Index'))
      }, {
        key: '/user/admin',
        icon: <UserOutlined />,
        label: '管理员列表',
        component: React.lazy(() => import('../vuews/user/Admin'))
      }
    ]
  },
  {
    key: '/banner',
    icon: <UploadOutlined />,
    label: '轮播图管理',
    children: [
      {
        key: '/banner/list',
        icon: <UserOutlined />,
        label: '轮播图列表',
        component: React.lazy(() => import('../vuews/banner/Index'))
      }, {
        key: '/banner/add',
        icon: <UserOutlined />,
        label: '添加轮播图',
        component: React.lazy(() => import('../vuews/banner/Add'))
      }
    ]
  }, {
    key: '/pro',
    icon: <OrderedListOutlined />,
    label: '商品管理',
    children: [
      {
        key: '/pro/list',
        icon: <UserOutlined />,
        label: '商品列表',
        component: React.lazy(() => import('../vuews/pro/Index'))
      }, {
        key: '/pro/search',
        icon: <UserOutlined />,
        label: '筛选列表',
        component: React.lazy(() => import('../vuews/pro/Search'))
      }, {
        key: '/pro/recommend',
        icon: <UserOutlined />,
        label: '推荐列表',
        component: React.lazy(() => import('../vuews/pro/Recommend'))
      }, {
        key: '/pro/seckill',
        icon: <UserOutlined />,
        label: '秒杀列表',
        component: React.lazy(() => import('../vuews/pro/Seckill'))
      }
    ]
  },
]
​
​
export default menus
​

aside 中导入menus

import { Layout, Menu } from 'antd';
import menus from '../../router/index'
import React from 'react';
import logo from '../../logo.svg'
import {connect } from 'react-redux';
const { Sider } = Layout;
const Aside = ({collapsed}) => {
​
    return (
        <Sider trigger={null} collapsible collapsed={collapsed}>
        <div className="logo">
        <img style={{height:30}} src={logo} alt="" />
          {!collapsed && (<span style={{lineHeight: '32px', color: '#fff', fontSize: 14}}>OA后台管理系统</span>)}
        </div>
        <Menu
          theme="dark"
          mode="inline"
          defaultSelectedKeys={['1']}
          items={menus}
        />
      </Sider>
    );
};
​
export default connect(state =>{
    return {
        collapsed:state.getIn(['app','collapsed'])
    }
})(Aside);

2. 在 AppMain.jsx 中进行路由配置

// AppMain
import { Layout } from 'antd';
import React from 'react';
// 路由出口和路由配置
import { Switch, Route } from 'react-router-dom'// 路由的规则
import menus from '../../router';
​
const {  Content } = Layout;
​
const AppMain = () => {
​
  // 渲染路由的函数
  const renderRoute = (menus)=>{
    return menus.map(item => {
      if(item.children){
        // 有子路由
        return renderRoute(item.children)
      }else {
        // 无子路由
        return <Route exact key={item.key} path={item.key} component={item.component} />
      }
    })
  }
​
  return (
    <Content
          style={{
            margin: '24px 16px',
            padding: 24,
            minHeight: 280,
            background: '#fff',
          }}
        >
          {/* 配置路由 */}
          <React.Suspense fallback={<div>加载中...</div>}>
            <Switch>
              {renderRoute(menus)}
            </Switch>
          </React.Suspense>
​
        </Content>
  );
};
​
export default AppMain;
​

3 侧边栏默认选中项目

import { Layout, Menu } from 'antd';
import menus from '../../router/index'
import React, { useState } from 'react';
import logo from '../../logo.svg'
import { connect } from 'react-redux';
import { useHistory, useLocation } from 'react-router-dom';
const { Sider } = Layout;
const Aside = ({ collapsed }) => {
  const { pathname } = useLocation()
  const history = useHistory()
  const [openKeys] = useState(['/' + pathname.split('/')[1]])
  const [selectedKeys] = useState([pathname])
​
​
  return (
    <Sider trigger={null} collapsible collapsed={collapsed}>
      <div className="logo">
        <img style={{ height: 30 }} src={logo} alt="" />
        {!collapsed && (<span style={{ lineHeight: '32px', color: '#fff', fontSize: 14 }}>OA后台管理系统</span>)}
      </div>
      <Menu
        defaultOpenKeys={openKeys}
        theme="dark"
        mode="inline"
        defaultSelectedKeys={selectedKeys}
        items={menus}
        onClick={(item) => {
          if (item.key != pathname) {
            history.push(item.key)
          }
        }}
      />
    </Sider>
  );
};
​
export default connect(state => {
  return {
    collapsed: state.getIn(['app', 'collapsed'])
  }
})(Aside);

4.退出登录

header.jsx

import {
  MenuFoldOutlined,
  MenuUnfoldOutlined,
} from '@ant-design/icons';
import { Button, Layout, } from 'antd';
import React from 'react';
import { connect } from 'react-redux';
import { useHistory } from 'react-router-dom';
const { Header } = Layout;
​
const Headers = ({ changeCollapsed, collapsed,adminname,logout}) => {
  const history = useHistory()
  return (
    <Header
      style={{
        padding: 0,
        background: '#fff',
      }}
    >
      {React.createElement(collapsed ? MenuUnfoldOutlined : MenuFoldOutlined, {
        className: 'trigger',
        onClick: () => changeCollapsed(),
      })}
      <div style={{float:'right',marginRight:30}}>
      {adminname}
       {adminname? <Button onClick={()=>{
        logout()
        history.push('/login')
​
       }}>退出</Button> : <Button onClick={()=>{
        history.push('/login')
       }}>请登录</Button>}
      </div>
    </Header>
  );
};
​
export default connect(state => {
  return {
    collapsed: state.getIn(['app', 'collapsed']),
    adminname:state.getIn(['user', 'adminname'])
  }
}, dispatch => {
  return {
    changeCollapsed() {
      dispatch({ type: 'change_collapsed' })
    },
    logout(){
      localStorage.clear()
      dispatch({type:'change-loginState',payload:false})
      dispatch({type:'change-token',payload:''})
      dispatch({type:'change-adminname',payload:''})
      dispatch({type:'change-role',payload:1})
      dispatch({type:'change-checkedkeys',payload:[]})
    }
  }
})(Headers);

商品管理

1. 商品列表

创建 api/pro.js 编写请求接口

找到 Table 组件,将数据渲染到该组件中

该组件中 配置了数据源,每行显示规则,分页等配置

连调数据是否推荐

2.推荐页面

按照同 商品列表相同方式

先请求数据

然后渲染数据即可