后端程序员快速入门React+Antd进行开发
本文最后更新于2023.05.16-07:16
,某些文章具有时效性,若有错误或已失效,请在下方留言或联系涛哥。
一,通过create-react-app脚手架创建项目
安装脚手架
npm install -g create-react-app
安装yarn包管理器
npm install -g yarn
创建项目
npx create-react-app my-app --template typescript
如图我们就已经搭建好了一个react项目雏形
快速启动项目,运行
cd my-app
npm start
或
yarn start
二,引入Antd
yarn add antd
三,快速搭建后台模板
打开antd官网:
配置App.tsx
import './App.css';
import React, {useState} from 'react';
import {
DesktopOutlined,
FileOutlined,
MenuFoldOutlined,
MenuUnfoldOutlined,
PieChartOutlined,
TeamOutlined,
UserOutlined,
} from '@ant-design/icons';
import type {MenuProps} from 'antd';
import {Button, Breadcrumb, Layout, Menu, theme} from 'antd';
import {Link} from "react-router-dom";
import {renderRoutes} from "react-router-config";
import routes from "./router/index";
const {Header, Content, Footer, Sider} = Layout;
type MenuItem = Required<MenuProps>['items'][number];
function getItem(
label: React.ReactNode,
key: React.Key,
icon?: React.ReactNode,
children?: MenuItem[],
): MenuItem {
return {
key,
icon,
children,
label,
} as MenuItem;
}
const items: MenuItem[] = [
getItem(<Link to="/">主页</Link>, '1', <PieChartOutlined/>),
getItem(<Link to="/user">用户管理</Link>, '2', <DesktopOutlined/>),
getItem('文章管理', 'sub1', <UserOutlined/>, [
getItem(<Link to="/article">所有文章</Link>, '3'),
getItem('Bill', '4'),
getItem('Alex', '5'),
]),
getItem('Team', 'sub2', <TeamOutlined/>, [getItem('Team 1', '6'), getItem('Team 2', '8')]),
getItem('Files', '9', <FileOutlined/>),
];
const App: React.FC = () => {
const [collapsed, setCollapsed] = useState(false);
const {
token: {colorBgContainer},
} = theme.useToken();
return (
<Layout style={{minHeight: '100vh'}}>
<Sider collapsible collapsed={collapsed}
onCollapse={(value) => setCollapsed(value)}>
<div style={{height: 32, margin: 16, background: 'rgba(255, 255, 255, 0.2)'}}>
React-Antd-Admin
</div>
<Menu theme="dark" defaultSelectedKeys={['1']} mode="inline" items={items}/>
</Sider>
<Layout className="site-layout">
<Header style={{padding: 0, background: colorBgContainer}}>
<Button
type="text"
icon={collapsed ? <MenuUnfoldOutlined/> : <MenuFoldOutlined/>}
onClick={() => setCollapsed(!collapsed)}
style={{
fontSize: '16px',
width: 64,
height: 64,
}}
/>
</Header>
<Content style={{margin: '0 16px'}}>
<Breadcrumb items={[
{
title: <a href="">Home</a>,
},
{
title: 'User',
}
]} style={{margin: '16px 0'}}/>
<div style={{padding: 24, minHeight: 640, background: colorBgContainer}}>
{renderRoutes(routes)}
</div>
</Content>
<Footer style={{textAlign: 'center'}}>Ant Design ©2023 Created by Ant UED</Footer>
</Layout>
</Layout>
);
};
export default App;
四,配置路由
yarn add react-router-dom@5.2.0
yarn add react-router-config
yarn add @types/react-router-config
yarn add @types/react-router-dom
yarn add react-router@5.2.0 -S
router/index.ts
import { lazy } from 'react'
const routes = [
{
path:"/article",
component: lazy(()=>import('../views/Article')),
meta: {
title:"文章管理",
},
exact: true,
routes: []
},
{
path:"/user",
component: lazy(()=>import('../views/user/index')),
meta: {
title:"用户管理",
},
child: []
},
{
path:"/",
component: lazy(()=>import('../views/Home')),
meta: {
title:"App",
},
child: []
},
]
export default routes;
index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { HashRouter as Router } from "react-router-dom"
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<Router>
<App />
</Router>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
创建路由组件
views/user/index.tsx
import React, {useState} from 'react';
import {Button, Form, Input, Modal, Pagination, Space, Table, Tag} from 'antd';
import type {ColumnsType} from 'antd/es/table';
import {DeleteOutlined, EditOutlined,SearchOutlined} from '@ant-design/icons';
import UserForm from "./components/UserForm";
interface DataType {
key: string;
name: string;
age: number;
address: string;
tags: string[];
}
const rowSelection = {
onChange: (selectedRowKeys: React.Key[], selectedRows: DataType[]) => {
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
},
getCheckboxProps: (record: DataType) => ({
disabled: record.name === 'Disabled Index',
// Column configuration not to be checked
name: record.name,
}),
};
const data: DataType[] = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
tags: ['nice', 'developer'],
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
tags: ['loser'],
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sydney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
{
key: '4',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
tags: ['nice', 'developer'],
},
{
key: '5',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
tags: ['loser'],
},
{
key: '6',
name: 'Joe Black',
age: 32,
address: 'Sydney No. 1 Lake Park',
tags: ['cool', 'teacher'],
},
];
const Index: React.FC = () => {
const [open, setOpen] = useState(false);
const [title, setTile] = useState<String>();
const [rowData, setRow] = useState(null);
const [confirmLoading, setConfirmLoading] = useState(false);
const showModal = (title: String, record: any) => {
setRow(record);
setTile(title);
setOpen(true);
};
const handleOk = () => {
setConfirmLoading(true);
setTimeout(() => {
setOpen(false);
setConfirmLoading(false);
}, 2000);
};
const handleCancel = () => {
console.log('Clicked cancel button');
setOpen(false);
};
const onFinish = (values: any) => {
console.log('Success:', values);
};
const onFinishFailed = (errorInfo: any) => {
console.log('Failed:', errorInfo);
};
const columns: ColumnsType<DataType> = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
render: (text: String) => <a>{text}</a>,
},
{
title: 'Age',
dataIndex: 'age',
key: 'age',
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
},
{
title: 'Tags',
key: 'tags',
dataIndex: 'tags',
render: (tags: string[]) => (
<span>
{tags.map((tag) => {
let color = tag.length > 5 ? 'geekblue' : 'green';
if (tag === 'loser') {
color = 'volcano';
}
return (
<Tag color={color} key={tag}>
{tag.toUpperCase()}
</Tag>
);
})}
</span>
),
},
{
title: 'Action',
key: 'action',
render: (_, record) => (
<Space size="middle">
<Button type={"primary"} ghost icon={<EditOutlined/>}
onClick={() => showModal('修改', record)}>Edit</Button>
<Button type={"primary"} danger ghost icon={<DeleteOutlined/>}>Delete</Button>
</Space>
),
},
];
return (
<div>
<div style={{paddingBottom: '20px'}}>
<Form layout={"inline"}>
<Form.Item
label="姓名"
name="name"
>
<Input name={'name'} style={{width: 180}} placeholder={'请输入需要搜索是关键词'}/>
</Form.Item>
<Form.Item>
<Input name={'name'} style={{width: 180,marginLeft: 10}} placeholder={'请输入需要搜索是关键词'}/>
</Form.Item>
<Form.Item>
<Button name={'search'} icon={<SearchOutlined />} >搜索</Button>
<Button type={"primary"} danger name={'reset'} style={{marginLeft: 10}} icon={<SearchOutlined/>} >重置</Button>
</Form.Item>
</Form>
</div>
<div style={{paddingBottom: '20px'}}>
<Button type={"primary"} onClick={() => showModal('添加', null)}>add</Button>
<Button type={"primary"} style={{
marginLeft: '20px'
}}>delete</Button>
</div>
<Table columns={columns} rowSelection={{
type: 'checkbox',
...rowSelection,
}} dataSource={data} pagination={false}/>
<Pagination
total={85}
showSizeChanger
showQuickJumper
showTotal={(total) => `总共 ${total} 条`}
/>
<UserForm title={title}
open={open}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
handleCancel={handleCancel}
handleOk={handleOk}
confirmLoading={confirmLoading}
rowData={rowData}
/>
</div>
);
};
export default Index;
弹出窗组件
views/user/components/UserForm.tsx
import React, {useState} from 'react';
import {Button, Modal, Form, Input, InputNumber} from 'antd';
import {DeleteOutlined, EditOutlined} from '@ant-design/icons';
const UserForm: React.FC<any> = (props) => {
// console.log(props)
const {title,open,rowData,handleOk,onFinish,onFinishFailed,handleCancel,confirmLoading,}= props;
const [form] = Form.useForm();
form.resetFields();
if(title==='修改'){
form.setFieldsValue(rowData);
}
console.log(form)
return (
<div>
<Modal forceRender
title={title}
open={open}
onOk={handleOk}
confirmLoading={confirmLoading}
onCancel={handleCancel}
>
<Form
form={form}
name="basic"
labelCol={{ span: 6 }}
wrapperCol={{ span: 12 }}
style={{ maxWidth: 600 }}
initialValues={{ remember: true }}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off"
>
<Form.Item
label="姓名"
name="name"
rules={[{ required: true, message: 'Please input your username!' }]}
>
<Input allowClear />
</Form.Item>
<Form.Item
label="age"
name="age"
rules={[{ required: true, message: 'Please input your password!' }]}
>
<InputNumber />
</Form.Item>
<Form.Item
label="address"
name="address"
rules={[{ required: true, message: 'Please input your password!' }]}
>
<Input allowClear/>
</Form.Item>
</Form>
</Modal>
</div>
);
};
export default UserForm;
五,配置less
暴露配置项
yarn eject
安装less
和less-loader
yarn add less less-loader -S
配置webpack.config.js,在 //style files regexes 最下面配置less
// less
const lessRegex = /\.less$/;
const lessModuleRegex = /\.module\.less$/;
在大约530行左右配置
// less
{
test: lessRegex,
exclude: lessModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
},
'less-loader'
),
sideEffects: true,
},
// less
{
test: lessModuleRegex,
use: getStyleLoaders(
{
importLoaders: 2,
sourceMap: isEnvProduction && shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
'less-loader'
),
},
如何创建index.less 和index.scss并引用,重新启动项目
如果出现
如果 Failed to complie,就重新安装一下即可
yarn add sass -s
六,配置axios和反向代理
安装axios 和 http-proxy-middleware
yarn add axios http-proxy-middleware -s
在config下创建 setupProxy.js
const {createProxyMiddleware} = require('http-proxy-middleware');
module.exports = function(app) {
app.use('/api', createProxyMiddleware({
target: 'http://localhost:8080/',//后台服务器地址
changeOrigin: true,
pathRewrite: {
'^/api': '',
},}))
}
然后修改 config下 path.js的相关配置
配置 封装 /http/request.ts
import axios from "axios"
// 创建axios 赋值给常量service
const service = axios.create({
baseURL: '/api',
timeout: 5000,
headers: {//请求头
"Content-Type": "application/json;charset=UTF-8",
// "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
"token": null,
}
});
// 添加请求拦截器(Interceptors)
service.interceptors.request.use(function (config) {
// 发送请求之前做写什么
let token = localStorage.getItem("token");
// 如果有token
if(token){
// 将token放在请求头
config.headers.authorization = token;
}
return config;
}, function (error) {
// 请求错误的回调
return Promise.reject(error);
});
// 添加响应拦截器
service.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 响应错误的回调
return Promise.reject(error);
});
export default service
配置 /http/index.ts
import service from "./request";
// 这个模块封装各种请求方式(如:get,post等等)
export function post(url: any, params: {}) {
return new Promise(function (resovle, reject) {
service.post(url, params)
.then((res: any) => {
if (!res.data) {
resovle(res);
}
resovle(res.data);
}, (err: any) => {
reject(err);
})
.catch((err: any) => {
reject(err);
});
});
}
export function get(url: String, params: []) {
let querystr = "";
for (let key in params) {
if (params[key]) {
querystr += `${key}=${params[key]}&`
}
}
return new Promise(function (resovle, reject) {
service.get(url + "?" + querystr)
.then((res: any) => {
if (!res.data) {
resovle(res);
}
resovle(res.data);
}, (err: any) => {
reject(err);
})
.catch((err: any) => {
reject(err);
});
});
}
请求api 封装 /api/user.ts
//index.ts
import Service from "../http/request"
//获取所有用户
export function getUserList(config:any) {
const params = new URLSearchParams()
params.append("pageNum", config.pageNum)
params.append("pageSize", config.pageSize)
return Service({
url: "/getUsers",
data: params,
})
}
调用服务
getUserList(page).then((res: any) => {
setUsers(res);
setPage({pageNum: res.pageNum, pageSize: res.pageSize});
})
结尾
到这里我们的React+antd+axios+router的学习就就结束了,欢迎分享给有需要的小伙伴!!!
阅读剩余
版权声明:
作者:涛哥
链接:https://ltbk.net/front/react/article/1467.html
文章版权归作者所有,未经允许请勿转载。
作者:涛哥
链接:https://ltbk.net/front/react/article/1467.html
文章版权归作者所有,未经允许请勿转载。
THE END