react+antd5.7

创建项目

安装工具

npm i create-react-app -g

创建项目 

create-react-app test

vscode打开test项目 ,并启动项目

npm start

浏览器访问

http://localhost:3000/

基本目录设置 

api         ajax相关
assets      公用资源
components  非路由组件
config      配置
pages       路由组件
utils       工具模块
App.js      应用根组件
index.js    入口js    

引入antd 

安装antd

npm install antd

安装路由

 npm i react-router-dom

配置BrowserRouter,必须要包裹着App

修改src\index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
//引入路由
import {BrowserRouter} from 'react-router-dom'

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

配置路由表

修改src\config\luYou.js

//引入路由
import {Navigate} from 'react-router-dom'
import Login from '../pages/login'
import Admin from '../pages/admin'

//暴露路由表
export default [
  {
      
      path:'/login',
      element:<Login/>
  },
  {  
    path:'/admin',
    element:<Admin/>
  },
  {
    //重定向到登陆组件
    path:'/',
    element:<Navigate to="/login"/>
  }
]

安装axios

 npm i axios 

 配置跨域代理

安装http-proxy-middleware

npm i http-proxy-middleware

创建src\setupProxy.js 

//setupProxy.js
const {createProxyMiddleware} = require('http-proxy-middleware')
 
module.exports = function (app) {
    app.use(
        createProxyMiddleware(
            //代理后端接口的前缀 不需要给后端传
            '/api',
            {
                //访问后台服务器
                target: 'http://localhost:8080/',
                //访问后端的域名+端口,true为localhost:8080,
                //false为localhost:3000
                changeOrigin: true,
                //访问后台接口去掉api前缀
                pathRewrite: {'^/api': ''}
            }
        )
    )
}

封装axios

创建src\api\ajax.js

//引入axios
import axios from "axios";
//引入antd的弹窗消息
import {message} from 'antd'

/**
 * 封装axios
 * @param {*} url 接口路径
 * @param {*} data 参数 默认为空对象
 * @param {*} type GET | POST
 */
export default function ajax(url,data={},type){
    //返回一个Promise对象
    return new Promise((resolve,reject)=>{
         let promise;
         if(type==='GET'){
            promise=axios.get(url,{
                params:data
            })     
         }else{
            promise=axios.post(url,data) 
         }   
         
        promise.then(response=>{
            //如果成功了 返回成功的对象 把数据放入
            resolve(response.data)
        }).catch(error=>{
            //弹出错误提示
            message.error(error.message)    
        })
         
    })
}

封装后端请求接口

创建src\api\index.js

//引入封装好的axios
import ajax from './ajax'

//暴露登陆接口 api不是后端的路径 是代理路径 
export const reqLogin=(data)=> ajax('/api/login',data,'POST')

后端springboot登陆接口 

package com.example.demo.controller;

import com.example.demo.entity.User;
import org.springframework.web.bind.annotation.*;

@RestController
public class LoginController {

    /**
     * 登陆接口
     * @param
     * @return
     * @throws Exception
     */
    @PostMapping("/login")
    @ResponseBody
    public String login(@RequestBody User user){
        System.out.println(user);
        //返回token
        return "1234567890abcdefghjkl";
    }

}
package com.example.demo.entity;

import lombok.Data;

@Data
public class User {
    //主键
    private Integer id;

    //账号
    private String username;

    //密码
    private String password;
}

登陆组件 

修改App.js

//引入路由
import {BrowserRouter,Route, Routes,Link,useRoutes,Outlet} from 'react-router-dom'
//引入路由表
import luYou  from './config/luYou';

function App() {
  //接收路由表
  const element=useRoutes(luYou)
  return (
    <div>
    
              {/*接收路由表的路径 跳转界面*/}
              {element}        
    </div>
  );
}

export default App;

创建src\pages\login\index.js

import React from 'react'
//引入antd的图标
import { LockOutlined, UserOutlined } from '@ant-design/icons';
//引入antd的组件
import { Button, Checkbox, Form, Input,message } from 'antd';
//引入登陆接口
import {reqLogin} from '../../api/index'
//引入路由
import {useNavigate} from 'react-router-dom'


export default function Login() {

    //使用编程式路由导航
    const navigate=useNavigate();

  //提交表单且数据验证成功后回调事件
  //values是一个对象 {username: '123', password: '55555'}
  const onFinish =async (values) => {
        //获取后端返回的结果
        let res=await reqLogin(values)
        if(res){
            //把token存储在本地
            localStorage.setItem('token',res)
            message.success('登陆成功')
            //跳转到管理界面
            navigate("/");
        }else{
            message.error('登陆失败')
        }

  };

  return (
    <div>

        <Form
            name="normal_login"
            className="login-form"
            onFinish={onFinish}
            >
            <Form.Item
                name="username"
                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>
                <Button type="primary" htmlType="submit" className="login-form-button">
                登陆
                </Button>
            </Form.Item>
            </Form>

    </div>
  )
}

创建src\pages\admin\index.js

import React from 'react'
//引入路由
import {useNavigate} from 'react-router-dom'

export default function Admin() {
  //使用路由导航
  const navigate=useNavigate();  
  //组件挂载时会调用一次
  React.useEffect(()=>{
    //从本地获取token
    const token=localStorage.getItem('token');
    if(!token){
        //如果token没有数据 跳转到登陆界面
        navigate("/login")
    }
  });


  return (
    <div>我是管理界面</div>
  )
}

默认访问登陆界面 

登陆成功后,会保存token,跳转到管理界面

如果管理界面,token为空,会跳回到登陆界面

菜单组件

修改src\config\luYou.js

//引入路由
import {Navigate} from 'react-router-dom'
//引入所有组件
import Login from '../pages/login'
import Admin from '../pages/admin'
import Home from '../pages/home'
import ZiChan from '../pages/ziChan'
import FenLei from '../pages/fenLei'
import ZiChanAdmin from '../pages/ziChanAdmin'
import User from '../pages/user'
import Role from '../pages/role'
import TuBiao from '../pages/tuBiao'
import ZhuZhuang from '../pages/zhuZhuang'
import ZheXian from '../pages/zheXian'
import Bing from '../pages/bing'

//暴露路由表
export default [
  {
      
      path:'/login',
      element:<Login />
  },
  {  
    path:'/',
    element:<Admin />,
    //二级路由必须放到/下面 否则不会在右侧显示
    children:[
      {  
        path:'home',
        element:<Home />
      },
      {  
        path:'ziChan',
        element: <ZiChan />,
      },
      {  
        path:'user',
        element: <User />
      },
      {  
        path:'role',
        element: <Role />
      },
      {  
        path:'fenLei',
        element: <FenLei />
      },
      {  
        path:'ziChanAdmin',
        element: <ZiChanAdmin />
      },
      {  
        path:'tuBiao',
        element: <TuBiao />,
      },
      {  
        path:'zhuZhuang',
        element: <ZhuZhuang />
      },
      {  
        path:'zheXian',
        element: <ZheXian />
      },
      {  
        path:'bing',
        element: <Bing />
      }
    ]
  }
]

修改src\config\menuConfig.js

//引入antd icon
import {
    UserOutlined,AppstoreAddOutlined,AppstoreOutlined,BorderlessTableOutlined,
    BranchesOutlined,ClusterOutlined,CompassOutlined,CompressOutlined,
    ContactsOutlined,DashboardOutlined
  } from '@ant-design/icons';
//暴露菜单 字段不能随便命名 否则报错
export default [
    {
        label:'首页',
        key:'/home',
        icon:<UserOutlined />
    },
    {
        label:'资产',
        key:'/ziChan',
        icon:<AppstoreAddOutlined />,
        children:[
            {
                label:'分类',
                key:'/fenLei',
                icon:<AppstoreOutlined />
            }, 
            {
                label:'资产管理',
                key:'/ziChanAdmin',
                icon:<BorderlessTableOutlined />
            }
        ]
    },
    {
        label:'用户管理',
        key:'/user',
        icon:<BranchesOutlined />
    },
    {
        label:'角色管理',
        key:'/role',
        icon:<ClusterOutlined />
    },
    {
        label:'图形图表',
        key:'/tuBiao',
        icon:<CompassOutlined />,
        children:[
            {
                label:'柱状图',
                key:'/zhuZhuang',
                icon:<CompressOutlined />
            }, 
            {
                label:'折线图',
                key:'/zheXian',
                icon:<ContactsOutlined />
            }, 
            {
                label:'饼图',
                key:'/bing',
                icon:<DashboardOutlined />
            }
        ]
    },
]

修改src\pages\admin\index.js,引入左侧菜单,设置整体布局

import React,{Suspense, useState} from 'react'
//引入路由
import {useNavigate,Outlet,Link, Routes, Route,useRoutes} from 'react-router-dom'

//引入antd
import { Breadcrumb, Layout, Menu, theme } from 'antd';
//引入菜单数据
import menuConfig from '../../config/menuConfig';
//引入路由表
import luYou  from '../../config/luYou';

import ZuoCe from '../zuoCe';

export default function Admin() {
  //使用路由导航
  const navigate=useNavigate();  
  //组件挂载时会调用一次
  React.useEffect(()=>{
    //从本地获取token
    const token=localStorage.getItem('token');
    if(!token){
        //如果token没有数据 跳转到登陆界面
        navigate("/login")
    }

    
  });
  //解构布局的字段
  const { Header, Content, Footer, Sider } = Layout;

  //使用状态 默认不收起 当触发了setCollapsed方法 会改变收起状态
  const [collapsed, setCollapsed] = useState(false);

  //组件的容器背景色,例如:默认按钮、输入框等。务必不要将其与 `colorBgElevated` 混淆
  const {token: { colorBgContainer },} = theme.useToken();

  
  return (
    <div>
      
        
    <Layout
      style={{
        minHeight: '100vh',
      }}
    >
      {/*
          collapsible 是否可收起 默认为false, true显示< 收起图标
          collapsed 当前收起状态
          onCollapse	展开-收起时的回调函数,有点击 trigger 以及响应式反馈两种方式可以触发
      */}
      <Sider collapsible collapsed={collapsed} onCollapse={(value) => {setCollapsed(value)}}>
        {/*引入左侧组件*/}
        <ZuoCe/>
      </Sider>
      <Layout>
        <Header
          style={{
            padding: 0,
            background: colorBgContainer,
          }}
        >头部内容
        
        </Header>
      <Content
          style={{
            margin: '0 16px',
          }}
        >
         
          <div
            style={{
              padding: 24,
              minHeight: 360,
              background: colorBgContainer,
            }}
          >
            {/*显示路由内容*/}
             <Outlet/>
           
          </div>
          </Content>
        <Footer
          style={{
            textAlign: 'center',
          }}
        >
          我是底部 ©2023 
        </Footer>
      </Layout>
    </Layout>

   
    </div>


  )
}

修改src\pages\bing\index.js

import React from 'react'

export default function Bing() {
  return (
    <div>bing</div>
  )
}

修改src\pages\fenLei\index.js

import React from 'react'

export default function FenLei() {
  return (
    <div>fenLei</div>
  )
}

修改src\pages\home\index.js

import React from 'react'
import {useLocation} from 'react-router-dom'
export default function Home() {
 
 
  return (
    
    <>Homeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
      aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
      aaaaaaaaaaaaaaaaaaaaa
      asdsadsadsad
    </>
  )
}

修改src\pages\role\index.js

import React from 'react'
//引入路由
import {useNavigate,Outlet,Link, Routes, Route} from 'react-router-dom'

export default function Role() {
  return (
    <div>
      jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj
    </div>
  )
}

修改src\pages\tuBiao\index.js

import React from 'react'

export default function TuBiao() {
  return (
    <div>tuBiasssssssssssssssssssssssssssssssssssssssssssssssssso</div>
  )
}

修改src\pages\user\index.js

import React from 'react'

export default function User() {
  return (
    <div>usssssssssssssssssssssssssssssssssssssssssser</div>
  )
}

修改src\pages\zheXian\index.js

import React from 'react'

export default function ZheXian() {
  return (
    <div>zheXsssssssssssssssssssssssssssssssssssssssssian</div>
  )
}

修改src\pages\zhuZhuang\index.js

import React from 'react'

export default function ZhuZhuang() {
  return (
    <div>zhuZhuang
      asdasdasssssssssssssssssssssssssssssssssssssdadadasdasd
    </div>
  )
}

修改src\pages\ziChan\index.js

import React from 'react'

export default function ZiChan() {
  return (
    <div>ziChan</div>
  )
}

修改src\pages\ziChanAdmin\index.js

import React from 'react'

export default function ZiChanAdmin() {
  return (
    <div>ziChanAsssssssssssssssssssssssssssssssssssssssssssdmin</div>
  )
}

修改src\pages\zuoCe\index.js,显示左侧菜单,点击菜单显示到右侧

import React from 'react'
//引入路由
import {useNavigate,useLocation} from 'react-router-dom'

//引入antd
import { Breadcrumb, Layout, Menu, theme } from 'antd';
//引入菜单数据
import menuConfig from '../../config/menuConfig';

export default function ZuoCe() {
    //使用路由导航
  const navigate=useNavigate();  
  const {pathname} =useLocation();
  
    //定义菜单内容
  const items = menuConfig
  //点击菜单触发的方法 item是antd自带的参数
  function dianJiCaiDan(item){
      //跳转到对应的路径
      navigate(item.key)
  }
  console.log('pathname',pathname)
  return (
    <div>
        {/*
           theme	主题颜色  light | dark
           
           items 显示菜单的数组
           selectedKeys 当前选中的菜单项 key 数组 界面刷新后高亮显示
           mode	菜单类型,现在支持垂直、水平、和内嵌模式三种	vertical | horizontal | inline
        */} 
        <Menu theme="dark"  mode="inline" items={items} 
            selectedKeys={[pathname]}
            onClick={dianJiCaiDan}
         />
    </div>
  )
}

头部组件

 修改src\pages\admin\index.js,引入头部组件 

//引入头部组件
import TouBu from '../touBu';

 {/*头部内容*/}
          <TouBu/>

 修改src\pages\touBu\index.js,点击左侧菜单,动态更新标题,增加退出登陆按钮

import React from 'react'
import { Breadcrumb,Button } from 'antd';
import {useNavigate,useLocation} from 'react-router-dom'
//引入菜单数据
import menuConfig from '../../config/menuConfig';
export default function TouBu() {

  //定义路由导航  
  const navigate=useNavigate();  
  //获取当前路径
  const {pathname} =useLocation()

  //退出登陆方法
  function tuiChu(){
    //清空本地存储
    localStorage.removeItem("token");
    //跳转到登陆界面
    navigate("/login")
  }  
  //获取菜单信息
  const menuList=menuConfig
  //定义标题
  let title;
  //遍历菜单
  menuList.forEach(x=>{
    //key
    if(x.key===pathname){
        //如果路径匹配上了,那么把标题拿到
        title=x.label
    }else if(x.children){
        //如果还有子节点
        x.children.find(y=>{
            //如果子节点也匹配上了路径 把标签拿到
            if(y.key===pathname){
                title=y.label
            }
        })
    }
  })

  return (
    <div>
        {/*动态更新标题*/}
        <Breadcrumb items={[{ title: title }]} />
        <Button type="primary" onClick={tuiChu}>退出</Button>
    </div>
  )
}

分页组件

后端springboot资产接口

package com.example.demo.entity;

import lombok.Data;

import java.util.List;

@Data
public class ZiChan {

    //主键
    private Integer id;

    //资产名称
    private String name;

    //分类
    private List<String> fenLei;

    //级别
    private String jiBie;
}
package com.example.demo.entity;

import lombok.Data;

import java.util.List;

@Data
public class ZiChanRes {

    //资产集合
    private List<ZiChan>list;


    //默认的当前页数
    private Integer defaultCurrent;

    //数据总数
    private Integer total;
}
package com.example.demo.entity;

import lombok.Data;

@Data
public class ZiChanVo {

    //当前第几页
    private Integer pageNumber;

    //每页条数
    private Integer pageSize;

}
package com.example.demo.controller;

import com.example.demo.entity.User;
import com.example.demo.entity.ZiChan;
import com.example.demo.entity.ZiChanRes;
import com.example.demo.entity.ZiChanVo;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@RestController
public class ZiChanController {

    /**
     * 资产列表
     * @param
     * @return
     * @throws Exception
     */
    @PostMapping("/ziChan/list")
    @ResponseBody
    public ZiChanRes login(@RequestBody ZiChanVo vo){
        System.out.println("进来了:"+vo);
        ZiChanRes res=new ZiChanRes();
        List<ZiChan>list=new ArrayList<>();
        for (int i = 1; i <=vo.getPageSize() ; i++) {
            ZiChan z=new ZiChan();
            z.setFenLei(Arrays.asList("1","2"));
            z.setId(i);
            z.setJiBie(i+"");
            z.setName("资产"+(i*vo.getPageNumber()));
            list.add(z);
        }

        res.setList(list);
        res.setTotal(list.size()*10);
        res.setDefaultCurrent(vo.getPageNumber());
        return res;
    }

}

资产管理模块使用分页

修改src\api\index.js,新增资产列表接口

//引入封装好的axios
import ajax from './ajax'

//暴露登陆接口 api不是后端的路径 是代理路径 
export const reqLogin=(data)=> ajax('/api/login',data,'POST')
//暴露资产列表接口
export const reqZiChanList=(data)=>ajax('/api/ziChan/list',data,'post')

修改src\pages\ziChanAdmin\index.js

import React from 'react'
//引入antd
import { Card,Button,Space, Table, Tag,Pagination  } from 'antd';
//引入后端接口
import {reqZiChanList} from '../../api/index'


export default function ZiChanAdmin() {
  //列的标题
  const columns = [
    {
      title: '资产名称',
      dataIndex: 'name',
      key: 'name',
    },
    {
      title: '分类',
      dataIndex: 'fenLei',
      key: 'fenLei',
      render: (_, { fenLei }) => (
        <>
          {fenLei.map((tag) => {
            return (
              <Tag color={'geekblue'} key={tag}>
                {tag}
              </Tag>
            );
          })}
        </>
      ),
    },
    {
      title: '级别',
      dataIndex: 'jiBie',
      key: 'jiBie',
    },
    {
      title: '操作',
      key: 'action',
      render: (_, record) => (
        <Space size="middle">
          <a>修改</a>
          <a>删除</a>
        </Space>
      ),
    },
  ];
  //定义空数组 使用React.useState
  const [data,setData]=React.useState([]);
  //定义默认页数
  const [defaultCurrent,setDefaultCurrent]=React.useState(1)
  //定义默认总条数
  const [total,setTotal]=React.useState(0)

  //初始化会调用一次,设置空数组 就是为了防止多次调用
  React.useEffect(()=>{
    //调用获取数据的方法 初始调用第一页,10条
    getList(1,10);
  },[])

  //获取数据调用后端接口
  async function getList(pageNumber,pageSize){
      let res=await reqZiChanList({pageNumber,pageSize});
      console.log('res',res)
      //更新数组的数据
      setData(res.list)
      //更新默认页数
      setDefaultCurrent(res.defaultCurrent)
      //更新总条数
      setTotal(res.total)
  }
  /**
   * 分页方法
   * @param {*} pageNumber 当前第几页
   * @param {*} pageSize 每页条数
   */
  const onChange = (pageNumber,pageSize) => {
    console.log('分页: ', pageNumber,pageSize);
    //刷新列表
    getList(pageNumber,pageSize);
  };

  return (
    <div>
           {/*Card 卡片*/}      
          <Card type="inner" title="资产管理" 
          extra={ 
                  <Button type="primary">新增</Button>
                }>
            {/*Table 表格  bordered	是否展示外边框和列边框
               columns	表格列的配置
               dataSource	数据数组
               rowKey 指定主键的名字
               pagination 是否显示小分页组件,这里设置不显示
            */}      
            <Table columns={columns} dataSource={data} bordered
            rowKey="id" pagination={false}
            />
            {/*分页 
              showQuickJumper	是否可以快速跳转至某页
              defaultCurrent	默认的当前页数
              total	数据总数
              onChange	页码或 pageSize 改变的回调,参数是改变后的页码及每页条数
            */}
            <Pagination showQuickJumper defaultCurrent={defaultCurrent} total={total} 
            onChange={onChange} />
   
          </Card>
         
     
    </div>
  )
}

修改src\index.js,使用ConfigProvider包裹着App 这样插件显示的内容就是中文了

  import React from 'react';
  import ReactDOM from 'react-dom/client';
  import App from './App';
  //引入路由
  import {BrowserRouter} from 'react-router-dom'
  //引入antd
  import zhCN from 'antd/locale/zh_CN';
  import { ConfigProvider } from 'antd';

  const root = ReactDOM.createRoot(document.getElementById('root'));
  root.render(
    <BrowserRouter>
    {/*包裹着App 这样插件显示的内容就是中文了*/}
    <ConfigProvider locale={zhCN}>
          <App />
      </ConfigProvider>
    </BrowserRouter>
  );

上传,级联,下拉组件 

后端springboot分类接口

package com.example.demo.controller;

import com.example.demo.entity.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@RestController
public class FenLeiController {

    /**
     * 上传文件
     * @param
     * @return
     * @throws Exception
     */
    @PostMapping("/upload")
    @ResponseBody
    public String upload(MultipartFile file){
        System.out.println(file.getName());
        System.out.println(file.getSize());
        System.out.println(file.getOriginalFilename());
        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println(inputStream);
        //返回文件地址
        return "http://www.baidu.com";
    }

    /**
     * 获取级别
     * @param
     * @return
     * @throws Exception
     */
    @PostMapping("/getJiBie")
    @ResponseBody
    public List<JiBie> getJiBie(){
        List<JiBie>res=new ArrayList<>();
        for (int i = 1; i <=3; i++) {
            JiBie j=new JiBie();
            j.setId(i);
            j.setName("级别"+i);
            res.add(j);
        }
        return res;
    }

    /**
     * 获取分类
     * @param
     * @return
     * @throws Exception
     */
    @PostMapping("/getFenLei")
    @ResponseBody
    public List<FenLei> getFenLei(){
        List<FenLei>res=new ArrayList<>();
        for (int i = 1; i <=3; i++) {
            FenLei f=new FenLei();
            f.setValue(""+i);
            f.setLabel("分类:"+i);
            List<FenLei>children=new ArrayList<>();
            for (int j = i*10; j <i*10+3 ; j++) {
                FenLei ff=new FenLei();
                ff.setLabel("分类"+j);
                ff.setValue(""+j);
                children.add(ff);
            }
            f.setChildren(children);
            res.add(f);
        }
        return res;
    }

    /**
     * 添加分类
     * @param
     * @return
     * @throws Exception
     */
    @PostMapping("/addFenLei")
    @ResponseBody
    public void addFenLei(@RequestBody ZiChan ziChan){
        System.out.println(ziChan);
    }
}
package com.example.demo.entity;

import lombok.Data;

import java.util.List;

@Data
public class FenLei {

    //值
    private String value;

    //名称
    private String label;

    //子节点
    private List<FenLei>children;
}
package com.example.demo.entity;

import lombok.Data;

@Data
public class JiBie {

    private Integer id;

    private String name;
}
package com.example.demo.entity;

import lombok.Data;

import java.util.List;

@Data
public class ZiChan {

    //主键
    private Integer id;

    //资产名称
    private String name;

    //分类
    private List<String> fenLei;

    //级别
    private String jiBie;

    //文件路径集合
    private List<String>wenJianList;
}

分类模块使用组件

修改src\api\index.js,引入分类的接口

//引入封装好的axios
import ajax from './ajax'

//暴露登陆接口 api不是后端的路径 是代理路径 
export const reqLogin=(data)=> ajax('/api/login',data,'POST')
//暴露资产列表接口
export const reqZiChanList=(data)=>ajax('/api/ziChan/list',data,'POST')
//暴露级别接口
export const reqJiBie=(data)=>ajax('/api/getJiBie',data,'POST')
//暴露获取分类接口
export const reqFenLei=(data)=>ajax('/api/getFenLei',data,'POST')
//暴露添加分类接口
export const reqAddFenLei=(data)=>ajax('/api/addFenLei',data,'POST')

修改src\pages\fenLei\index.js,引入上传,级联,下拉,卡片

import React from 'react'
import { UploadOutlined } from '@ant-design/icons';
//引入antd
import { Card,Form, Input,Button,Upload,Select,Cascader } from 'antd';
//引入后端接口
import {reqJiBie,reqFenLei,reqAddFenLei} from '../../api/index'

export default function FenLei() {
  //Option是Select组件里面的
  const { Option } = Select;
  //成功的回调
  const onFinish = (values) => {
    
    //定义文件集合
    let wenJianList=[];
    //解构对象
    const {wenJian}=values;

    wenJian.forEach(x => {
          //把每个文件的上传地址获取到
          wenJianList.push(x.response)
    });
    //添加分类
    reqAddFenLei({...values,wenJianList})
  };

  //上传文件方法
  const normFile = (e) => {
    //上传成功才打印
    if(e.file.status==="done"){
      //console.log('上传文件:', e);
    }
    if (Array.isArray(e)) {
      return e;
    }
    return e?.fileList;
  };
  //定义级别字段
  const [jiBieList,setJiBieList] =React.useState([]);
  //定义分类字段
  const [fenLeiList,setFenLeiList]=React.useState([])

  
  //初始化调用一次
  React.useEffect(()=>{
    //获取级别数据
    getJiBie();
    //获取分类的数据
    getFenLei();
  },[])

  //获取级别数据
  const getJiBie=async ()=>{
    let res=await reqJiBie();
    //更新级别的数据
    setJiBieList(res)
  }

  //获取分类数据
  const getFenLei=async ()=>{
    let res=await reqFenLei();
    //更新分类的数据
    setFenLeiList(res)
}
  
  const onChange = (value) => {
    console.log(value);
  };

  return (
    <div>
       {/*Card 卡片*/}      
      <Card type="inner" title="分类">
        <Form
          name="basic"
          
          onFinish={onFinish}
          autoComplete="off"
        >
          <Form.Item
            label="名称"
            name="name"
            rules={[
              {
                required: true,
                message: '请输入名称',
              },
            ]}
          >
            <Input />
          </Form.Item>

          <Form.Item
              name="wenJian"
              label="上传文件"
              valuePropName="fileList"
              getValueFromEvent={normFile}
            >
                  <Upload name="file" action="/api/upload" listType="picture">
                    <Button icon={<UploadOutlined />}>点击上传</Button>
                  </Upload>
          </Form.Item>

          <Form.Item
              name="jiBie"
              label="级别"
              hasFeedback
              rules={[
                {
                  required: true,
                  message: '请选择级别',
                },
              ]}
            >
              <Select>
                {
                  jiBieList.map((x)=>{
                    return <Option key={x.id} value={x.id}>{x.name}</Option>  
                  })
                }
              </Select>
          </Form.Item>

          <Form.Item  
              name="fenLei"
              label="分类">
              {/*
              defaultValue	默认的选中项
              options	可选项数据源
              onChange	选择完成后的回调
              */}  
              <Cascader 
              options={fenLeiList} onChange={onChange} />
          </Form.Item>

          <Form.Item
            wrapperCol={{
              offset: 8,
              span: 16,
            }}
          >
            <Button type="primary" htmlType="submit">
              提交
            </Button>
          </Form.Item>
        </Form>
       </Card>
    </div>
  )
}

角色组件 

后端springboot接口

package com.example.demo.controller;

import com.example.demo.entity.Role;
import com.example.demo.entity.ZiChan;
import com.example.demo.entity.ZiChanRes;
import com.example.demo.entity.ZiChanVo;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@RestController
public class RoleController {

    /**
     * 角色数据
     * @param
     * @return
     * @throws Exception
     */
    @PostMapping("/getRole")
    @ResponseBody
    public List<Role> getRole(){
        List<Role>list=new ArrayList<>();
        for (int i = 1; i <=3 ; i++) {
            Role r=new Role();
            r.setKey(i+"");
            r.setValue(i+"");
            r.setTitle("角色"+i);
            List<Role>res=new ArrayList<>();
            for (int j = i*10; j <i*10+3; j++) {
                Role rr=new Role();
                rr.setKey(j+"");
                rr.setValue(j+"");
                rr.setTitle("经理"+j);
                res.add(rr);
            }
            r.setChildren(res);
            list.add(r);
        }
        return list;
    }

}
package com.example.demo.entity;

import lombok.Data;

import java.util.List;

@Data
public class Role {

    //标题
    private String title;

    //值
    private String value;

    //唯一
    private String key;

    //子节点
    private List<Role> children;

}

TreeSelect组件

修改src\api\index.js,新增角色数据接口

//引入封装好的axios
import ajax from './ajax'

//暴露登陆接口 api不是后端的路径 是代理路径 
export const reqLogin=(data)=> ajax('/api/login',data,'POST')
//暴露资产列表接口
export const reqZiChanList=(data)=>ajax('/api/ziChan/list',data,'POST')
//暴露级别接口
export const reqJiBie=(data)=>ajax('/api/getJiBie',data,'POST')
//暴露获取分类接口
export const reqFenLei=(data)=>ajax('/api/getFenLei',data,'POST')
//暴露添加分类接口
export const reqAddFenLei=(data)=>ajax('/api/addFenLei',data,'POST')
//暴露角色数据接口
export const reqGetRole=(data)=>ajax('/api/getRole',data,'POST')

修改src\pages\role\index.js,引入TreeSelect组件

import React, { useState } from 'react';
import { TreeSelect,Button } from 'antd';
import {reqGetRole} from '../../api/index'
export default function Role() {
  const { SHOW_PARENT,SHOW_ALL} = TreeSelect;

    //定义value值
    const [value, setValue] = useState([]);
    //定义节点的数据
    const [treeData,setTreeData]=useState([])
    //选中的回调
    const onChange = (newValue) => {
      //console.log('新数据 ', newValue);
      //console.log('老数据 ', value);
      //更新节点数据
      setValue(newValue);
    };

    //初始化调用
    React.useEffect(()=>{
      //获取角色数据
      getRole();
    },[])

    //获取角色数据
    const getRole=async()=>{
      let res=await reqGetRole();
      //更新角色数据
      setTreeData(res);
    }

    //获取节点数据
    const huoQu=()=>{
      console.log('aaa',value)
    }

    const tProps = {
      //	treeNodes 数据,如果设置则不需要手动构造 TreeNode 节点(value 在整个树范围内唯一)
      treeData,
      //指定当前选中的条目
      value,
      //选中树节点时调用此函数
      onChange,
      //显示 Checkbox
      treeCheckable: true,
      //配置 treeCheckable 时,定义选中项回填的方式。
      //TreeSelect.SHOW_ALL: 显示所有选中节点(包括父节点)。
      //TreeSelect.SHOW_PARENT: 只显示父节点(当父节点下所有子节点都选中时)。 
      //默认只显示子节点
      showCheckedStrategy: SHOW_ALL,
      style: {
        width: '100%',
      },
      //默认展开所有节点
      treeDefaultExpandAll:true
    };
    return (
      <div>
        <TreeSelect {...tProps} />
        <Button type="primary" onClick={huoQu}>获取节点数据</Button>
      </div>
    
    )
}

二维码组件 

修改src\pages\user\index.js

import React from 'react'
import { QRCode } from 'antd';
export default function User() {
  return (
    <div>
      {/*errorLevel	二维码纠错等级	'L' | 'M' | 'Q' | 'H'
        value	扫描后的文本
      */}
       <QRCode
          errorLevel="H"
          value="http://www.baidu.com"
        />
    </div>
  )
}

图表

安装图表

npm install @ant-design/charts --save

柱状图

修改src\pages\zhuZhuang\index.js

import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import { Column } from '@ant-design/plots';

export default function ZhuZhuang() {
  const data = [
    {
      type: '家具家电',
      sales: 38,
    },
    {
      type: '粮油副食',
      sales: 52,
    },
    {
      type: '生鲜水果',
      sales: 61,
    },
    {
      type: '美容洗护',
      sales: 145,
    },
    {
      type: '母婴用品',
      sales: 48,
    },
    {
      type: '进口食品',
      sales: 38,
    },
    {
      type: '食品饮料',
      sales: 38,
    },
    {
      type: '家庭清洁',
      sales: 38,
    },
  ];
  const config = {
    data,
    xField: 'type',
    yField: 'sales',
    label: {
      // 可手动配置 label 数据标签位置
      position: 'middle',
      // 'top', 'bottom', 'middle',
      // 配置样式
      style: {
        fill: '#FFFFFF',
        opacity: 0.6,
      },
    },
    xAxis: {
      label: {
        autoHide: true,
        autoRotate: false,
      },
    },
    meta: {
      type: {
        alias: '类别',
      },
      sales: {
        alias: '销售额',
      },
    },
  };
  return (
    <div>
     <Column {...config} />
    </div>
  )
}

 

 

折线图

修改src\pages\zheXian\index.js

import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import { Line,Column } from '@ant-design/plots';

export default function ZheXian() {
    const data=[
      {
        "Date": "2010-01",
        "scales": 1998
      },
      {
        "Date": "2010-02",
        "scales": 1850
      },
      {
        "Date": "2010-03",
        "scales": 1720
      },
      {
        "Date": "2010-04",
        "scales": 1818
      },
      {
        "Date": "2010-05",
        "scales": 1920
      },
      {
        "Date": "2010-06",
        "scales": 1802
      },
      {
        "Date": "2010-07",
        "scales": 1945
      },
      {
        "Date": "2010-08",
        "scales": 1856
      },
      {
        "Date": "2010-09",
        "scales": 2107
      },
      {
        "Date": "2010-10",
        "scales": 2140
      },
      {
        "Date": "2010-11",
        "scales": 2311
      },
      {
        "Date": "2010-12",
        "scales": 1972
      },
      {
        "Date": "2011-01",
        "scales": 1760
      },
      {
        "Date": "2011-02",
        "scales": 1824
      },
      {
        "Date": "2011-03",
        "scales": 1801
      },
      {
        "Date": "2011-04",
        "scales": 2001
      },
      {
        "Date": "2011-05",
        "scales": 1640
      },
      {
        "Date": "2011-06",
        "scales": 1502
      },
      {
        "Date": "2011-07",
        "scales": 1621
      },
      {
        "Date": "2011-08",
        "scales": 1480
      },
      {
        "Date": "2011-09",
        "scales": 1549
      },
      {
        "Date": "2011-10",
        "scales": 1390
      }
    ]
    const config = {
      data,
      padding: 'auto',
      xField: 'Date',
      yField: 'scales',
      xAxis: {
        // type: 'timeCat',
        tickCount: 5,
      },
    };

  return (
    <div>
       <Line {...config} />
    </div>
  )
}

 

 

饼图

修改src\pages\bing\index.js

 

import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import { Pie } from '@ant-design/plots';


export default function Bing() {
  const data = [
    {
      type: '分类一',
      value: 27,
    },
    {
      type: '分类二',
      value: 25,
    },
    {
      type: '分类三',
      value: 18,
    },
    {
      type: '分类四',
      value: 15,
    },
    {
      type: '分类五',
      value: 10,
    },
    {
      type: '其他',
      value: 5,
    },
  ];
  const config = {
    appendPadding: 10,
    data,
    angleField: 'value',
    colorField: 'type',
    radius: 0.9,
    label: {
      type: 'inner',
      offset: '-30%',
      content: ({ percent }) => `${(percent * 100).toFixed(0)}%`,
      style: {
        fontSize: 14,
        textAlign: 'center',
      },
    },
    interactions: [
      {
        type: 'element-active',
      },
    ],
  };
  return (
    <div><Pie {...config} /></div>
  )
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值