React笔记(七)Antd

一、登录功能

  • 首先要使用antd,要先下载
javascript 复制代码
yarn add antd
  • 登录页面关键代码
javascript 复制代码
import React from 'react'
/*
  1、如果要在react中完成样式隔离,需要如下操作
  1)命名一个xx.module.scss    webpack要求
  2) 在需要的组件中通过ES6方式进行导入,导入的格式如下
  import 模块名 from 'xx.module.scss的路径'
  3) 在页面的标签中引用,引用的格式<标签名 className={模块名.类样式名称}/>或者className={模块名['类样式名称']}/>
*/
/*
  2.antd中Form组件的使用
  2.1 触发Form中的onFinish事件的回调函数它的的条件
  1)必须让<Button>的类型是submit,具体的做法是<Button htmlType="submit">
  2) 如何获取表单中文本框或者密码中的数据,需要在<Form.Item>设置name属性
*/
import loginStyle from '../assets/css/login.module.scss'
import {Form,Input,Button,message} from 'antd'
import {useNavigate} from 'react-router-dom'
import api from '../api'
export default function Login() {
  const [messageApi,contextHolder]=message.useMessage()
  const nav=useNavigate()
  const login=async values=>{
      const {code,message,token}=await api.users.login(values)
      if(code){
        messageApi.open({
          type:'success',
          content:message
        })
        //保存token到localStorage中
        localStorage.setItem('token',token)
        //跳转到后台首页
        nav('/home')
      }else{
        messageApi.open({
          type: 'error',
          content: '用户名或者密码有误',
        });
      }
  }
  return (
​
    <div>
        {contextHolder}
        <div className={loginStyle.container}>
           <div className={loginStyle['login-box']}>
              <Form onFinish={login}>
                  <Form.Item 
                      name="username"
                      rules={[
                        {
                          required:true,
                          message:'账号不能为空'
                        }
                      ]}
                      className={loginStyle.item}>
                      <Input className={loginStyle.txt}></Input>
                  </Form.Item>
                  <Form.Item 
                     name="password"
                     rules={[
                      {
                        required:true,
                        message:'密码不能为空'
                      }
                      ]}>
                      <Input.Password></Input.Password>
                  </Form.Item>
                  <Form.Item className={loginStyle.item}>
                      <Button type='primary' htmlType='submit' className={loginStyle.loginBtn}>登录</Button>
                  </Form.Item>
              </Form>
           </div>
        </div>
    </div>
  )
}

二、后台页面设计

javascript 复制代码
import React,{useState,useEffect} from 'react'
import homeStyle from '../assets/css/home.module.scss'
import {Layout,Menu} from 'antd'
import {WindowsOutlined,TrademarkCircleOutlined,UserOutlined} from '@ant-design/icons'
import {useNavigate,Outlet} from 'react-router-dom'
​
const {Header,Sider,Content,Footer}=Layout
​
​
export default function Home() {
  const [menuList,setMenuList]=useState([])
  const nav=useNavigate()
  useEffect(()=>{
    const list=[
      {
        key:'sub1',
        label:'日常业务',
        icon:<WindowsOutlined />,
        children:[
          {
            label:'学员管理',
            key:'/home/students'
          },
          {
            label:'班级管理',
            key:'/home/classes'
          }
        ]
      },
      {
        key:'sub2',
        label:'校区管理',
        icon:<TrademarkCircleOutlined />,
        children:[
          {
            label:'班主任管理',
            key:'/home/directors'
          },
          {
            label:'专业管理',
            key:'/home/subjects'
          }
        ]
      },
      {
        key:'sub3',
        label:'系统管理',
        icon:<UserOutlined />,
        children:[
          {
            key:'/home/users',
            label:'用户管理'
          }
        ]
      }
    ]
    setMenuList(list)
  },[])
  const go=(item)=>{
    nav(item.key)
  }
  return (
    <>
      <Layout>
          <Header>
            <div className={homeStyle.logo}>蜗牛BOSS管理系统</div>
          </Header>  
          <Layout style={{height:'750px'}}>
              <Sider>
                  <Menu 
                    items={menuList} 
                    onClick={go}
                    mode="inline"
                    theme="dark"
                    defaultOpenKeys={['sub1','sub2']}
                    defaultSelectedKeys={['/home/students']}></Menu>
              </Sider>
              <Content>
                  {/* 设置子路由出口 */}
                  <Outlet></Outlet>
              </Content>
          </Layout>
          <Footer style={{ textAlign: 'center'}}>Ant Design ©2023 Created by Ant UED</Footer>
      </Layout> 
    </>
  )
}

三、用户列表

javascript 复制代码
import React, { useState, useEffect } from 'react'
import { Card, Table, Avatar, Button, Space, Popconfirm } from 'antd';
import api from '../api'
export default function Users() {
    const [list, setList] = useState([])
    const columns = [
        {
            title: '用户名',
            dataIndex: 'username'
        },
        {
            title: '邮箱',
            dataIndex: 'email'
        },
        {
            title: '手机',
            dataIndex: 'phone'
        },
        {
            title: '角色',
            dataIndex: 'auth',
            render: (item) => {
                return item == 1 ? '超级管理员' : item == 2 ? '普通管理员' : '暂无'
            }
        },
        {
            title: '头像',
            dataIndex: 'image',
            render: (item) => {
                return <Avatar shape="square" size={64} src={item} />
            }
        },
        {
            title: '操作',
            key: 'action',
            render: (arg1) => {
                return (
                    <Space>
                        <Button type='primary'>查看</Button>
                        <Popconfirm
                            placement="top"
                            title="提示"
                            description="您确定要删除吗?"
                            onConfirm={()=>{deleteUser(arg1._id)}}
                            okText="确认"
                            cancelText="取消">
                            <Button type='primary' danger>删除</Button>
                        </Popconfirm>
                    </Space>
                )
            }
        }
​
    ]
    useEffect(() => {
        getUsers()
    }, [])
    const getUsers = async () => {
        const result = await api.users.getUsers()
        setList(result.data.result)
    }
​
    const deleteUser = (_id) => {
        console.log('_id', _id);
    }
    return (
        <div style={{ display: 'flex', justifyContent: 'center' }}>
            <Card bordered={true} style={{ width: '98%', marginTop: '10px' }}>
                <Table dataSource={list} columns={columns} rowKey="_id"></Table>
            </Card>
        </div>
    )
}

四、动态菜单

  • 在api/modules/users下编写获取权限菜单的接口
javascript 复制代码
import request from '../../utils/request'
export default{
    getAuthMenus:()=>request.get('/menus/getAuthMenus')
}
  • 在SysMenus.jsx中调用getAuthMenus接口来完成权限菜单数据的获取
javascript 复制代码
 useEffect(()=>{
    getAuthMenus()
  },[])
  const getAuthMenus=async()=>{
    const result=await api.users.getAuthMenus()
    console.log(result.data);
    const rlist=transformDataToMenus(result.data)
    console.log('转换后的结果',rlist);
    setMenuList(rlist)
  }
  • 将后台的权限菜单数据转成antd格式的菜单数据
javascript 复制代码
/**
   * 将后台的权限菜单数据转成antd格式的菜单数据
   */
  const transformDataToMenus=(list)=>{
    return list.map(item=>{
        let menuItem={label:item.title,key:item.path,icon:React.createElement(icons[item.icon])}
        if(item.children){
            menuItem.children=transformDataToMenus(item.children)
        }
        return menuItem
    })
  }
  • 渲染导航列表
javascript 复制代码
<Menu
     items={menuList}
     mode="inline"
     theme="dark"
     onClick={goNav}>
 </Menu>

五、路由鉴权

  • 在components文件夹下创建函数Auth组件

  • 在router/index.js的路由配置中使用<Auth><Home>包裹起来

  • 关键代码如下

javascript 复制代码
import React,{useEffect} from 'react'
import {Navigate,useNavigate} from 'react-router-dom'
import api from '../api'
import {message} from 'antd'
export default function Auth({children}) {
  //从localStorage获取token
  //如何将结果转成boolean类型
  const nav=useNavigate()
  const isAuth=!!localStorage.getItem('token')
  useEffect(()=>{
    getUserInfo()
  },[])
  const getUserInfo=async()=>{
     try {
        await api.users.getUserInfo()
     } catch (error) {
        message.warning('您的token已失效,请重新登录')
        nav('/login')
     }
  }
  if(isAuth){
    return (
        <>
        {children}
        </>
      )
  }else{
    message.warning('您还没有登录请登录')
    return (<>
        <Navigate to={"/login"}></Navigate>
    </>)
  }
}

注意:需要在request.js的响应拦截器中完成Promise.reject()

javascript 复制代码
axios.interceptors.response.use(response=>{
    return response.data
},error=>{
    return Promise.reject(error)
})

六、分页操作

javascript 复制代码
import React, { useState, useEffect } from 'react'
import { Card, Table, Avatar, Button, Space, Popconfirm,Pagination } from 'antd';
import api from '../../api'
export default function Users() {
    const [list, setList] = useState([])
    const [total,setTotal]=useState(0)
    const [pageSize,setPageSize]=useState(10)
    const [current,setCurrent]=useState(1)
    const columns = [
        {
            title: '用户名',
            dataIndex: 'username'
        },
        {
            title: '邮箱',
            dataIndex: 'email'
        },
        {
            title: '手机',
            dataIndex: 'phone'
        },
        {
            title: '角色',
            dataIndex: 'auth',
            render: (item) => {
                return item == 1 ? '超级管理员' : item == 2 ? '普通管理员' : '暂无'
            }
        },
        {
            title: '头像',
            dataIndex: 'image',
            render: (item) => {
                return <Avatar shape="square" size={64} src={item} />
            }
        },
        {
            title: '操作',
            key: 'action',
            render: (arg1) => {
                return (
                    <Space>
                        <Button type='primary'>查看</Button>
                        <Popconfirm
                            placement="top"
                            title="提示"
                            description="您确定要删除吗?"
                            onConfirm={()=>{deleteUser(arg1._id)}}
                            okText="确认"
                            cancelText="取消">
                            <Button type='primary' danger>删除</Button>
                        </Popconfirm>
                    </Space>
                )
            }
        }
    ]
    useEffect(() => {
        getUsers()
    }, [])
    const getUsers = async (params={pageSize:10,currentPage:1}) => {
        const result = await api.users.getUsers(params)
        setList(result.data.result)
        setTotal(result.data.total)
    }

    const deleteUser = (_id) => {
        console.log('_id', _id);
    }

    const onChange=(page,pageSize)=>{
        let params={pageSize,currentPage:page}
        getUsers(params)
        setCurrent(page)
        setPageSize(pageSize)
    }

    const onSizeChange=(current,pageSize)=>{
        console.log('pageSize',pageSize);
        console.log('current',current);
        let params={pageSize,currentPage:current}
        getUsers(params)
    }
    return (
        <div style={{ display: 'flex', justifyContent: 'center' }}>
            <Card bordered={true} style={{ width: '98%', marginTop: '10px' }}>
                <Table 
                    dataSource={list} 
                    columns={columns} 
                    rowKey="_id"
                    pagination={false}></Table>
                    <Pagination 
                        style={{marginTop:'20px'}}
                        total={total}
                        pageSize={pageSize}
                        current={current}
                        showSizeChanger={true}
                        pageSizeOptions={[3,5,10,15,20]}
                        onChange={onChange}
                        onShowSizeChange={onSizeChange}>
                    </Pagination>
            </Card>
        </div>
    )
}

七、面包屑

javascript 复制代码
import React,{useMemo,useEffect,useState} from 'react'
import { Breadcrumb } from 'antd';
import {useLocation} from 'react-router-dom'
import api from '../api'
export default function MyBreadcrumb() {
 const location=useLocation()
 let pathname=location.pathname
 const [breadcrumbAry,setBreadcrumbAry]=useState([])
 useEffect(()=>{
   getAuthMenu()
 },[])
 const getAuthMenu=async()=>{
   const result=await api.users.getAuthMenus()
   transformAry(result.data)
 }
 const transformAry=(list)=>{
  let breadcrumbData={}
  list.forEach(item=>{
      if(item.children){
         item.children.forEach(subItem=>{
          breadcrumbData[subItem.path]=[item,subItem]
         })
      }
   })
  setBreadcrumbAry(breadcrumbData[pathname])
 }
  return (
    <>
        <Breadcrumb>
            {
                !breadcrumbAry?[].map((item,index)=><Breadcrumb.Item key={index}>{item.title}</Breadcrumb.Item>):breadcrumbAry.map((item,index)=><Breadcrumb.Item key={index}>{item.title}</Breadcrumb.Item>)
            }
        </Breadcrumb>
    </>
  )
}

八、增加操作

javascript 复制代码
import React,{useEffect,useState} from 'react'
import MyBreadcrumb from '../../components/MyBreadcrumb';
import {Button,Card,Modal,Form, Input, Select,Upload,Radio} from 'antd'
import { PlusOutlined,LoadingOutlined  } from '@ant-design/icons';
import api from '../../api'


export default function StudentList() {
  const [addForm]=Form.useForm()

  const [classesOptions,setClassesOptions]=useState([])
  const [subjectsOptions,setSubjectsOptions]=useState([])
  const [loading, setLoading] = useState(false);
  const [imageUrl, setImageUrl] = useState();
  useEffect(()=>{
      getAllSubjects()
  },[])
  
  const getAllSubjects=async()=>{
      const result=await api.subjects.getSubjects()
      setSubjectsOptions(result.data.result)
  }
  const changeSubject=async(arg)=>{
      const result=await api.classes.getClassesBySubjectsId(arg)
      setClassesOptions(result.data.result)
  }
  const handleChange = (info) => {
      if (info.file.status === 'uploading') {
          setLoading(true);
          return;
      }
      if (info.file.status === 'done') {
          setLoading(false);
          setImageUrl(`http://www.zhaijizhe.cn:3005/${info.file.response.data[0]}`)
      }
  }
  const uploadButton = (
      <div>
        {loading ? <LoadingOutlined /> : <PlusOutlined />}
        <div
          style={{
            marginTop: 8,
          }}
        >
          上传头像
        </div>
      </div>
    );

  const [isModalOpen, setIsModalOpen] = useState(false);
  const showModal = () => {
    setIsModalOpen(true);
  };

  const handleOk = () => {
     setIsModalOpen(false);
     const params={
      name:addForm.getFieldValue(['name']),
      age:addForm.getFieldValue(['age']),
      gender:addForm.getFieldValue(['gender'])?addForm.getFieldValue(['gender']):"男",
      subjectsId:addForm.getFieldValue(['subjectsId']),
      classesId:addForm.getFieldValue(['classesId']),
    }
    if(addForm.getFieldValue(['imagePath']).file.response){
        params.imageUrl=`http://www.zhaijizhe.cn:3005${addForm.getFieldValue(['imagePath']).file.response.data[0]}`
    }
    console.log('params',params);
  };

  const handleCancel = () => {
    setIsModalOpen(false);
  };
  return (
    <div> 
        <MyBreadcrumb></MyBreadcrumb>
        <Card>
        <Button type="primary" onClick={showModal}>添加学生</Button>
        <Modal title="添加学生" open={isModalOpen} onOk={handleOk} onCancel={handleCancel} cancelText="取消" okText="确定">
          <Form form={addForm}>
                <Form.Item label="姓名" name="name">
                    <Input></Input>
                </Form.Item>
                <Form.Item label="年龄" name="age">
                    <Input></Input>
                </Form.Item>
                <Form.Item label="性别" name="gender">
                <Radio.Group name="radiogroup" defaultValue={"男"}>
                    <Radio value={"男"}>男</Radio>
                    <Radio value={"女"}>女</Radio>
                </Radio.Group>
                </Form.Item>
                <Form.Item label="专业" name="subjectsId">
                    <Select 
                        options={subjectsOptions} 
                        fieldNames={{label:'name',value:'_id'}}
                        onChange={changeSubject}></Select>
                </Form.Item>
                <Form.Item label="专业" name="classesId">
                    <Select 
                        options={classesOptions} 
                        fieldNames={{label:'name',value:'_id'}}></Select>
                </Form.Item>
                <Form.Item label="头像" name="imagePath">
                        <Upload
                            name="file"
                            listType="picture-card"
                            showUploadList={false}
                            onChange={handleChange}
                            action="http://www.zhaijizhe.cn:3005/images/uploadImages">
                                {imageUrl ? (
                                    <img
                                    src={imageUrl}
                                    alt="avatar"
                                    style={{
                                        width: '100%',
                                    }}
                                    />
                                ) : (
                                    uploadButton
                                )}
                        </Upload>
                </Form.Item>
            </Form>
        </Modal>
        </Card>
    </div>
  )
}
相关推荐
阿伟来咯~22 分钟前
记录学习react的一些内容
javascript·学习·react.js
吕彬-前端27 分钟前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱30 分钟前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai39 分钟前
uniapp
前端·javascript·vue.js·uni-app
密码小丑1 小时前
11月4日(内网横向移动(一))
笔记
bysking2 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
鸭鸭梨吖2 小时前
产品经理笔记
笔记·产品经理
王哲晓2 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4112 小时前
无网络安装ionic和运行
前端·npm
理想不理想v2 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试