创建项目
安装工具
npm i create-react-app -g
创建项目
create-react-app test
vscode打开test项目 ,并启动项目
npm start
浏览器访问
基本目录设置
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>
)
}