前言
我将以学生管理作为例子,使用react框架和antd样式来实现增删查改的功能
gitee项目地址: React 增删查改: 使用react完成一个简单的学生管理增删查改项目https://gitee.com/a-kodak-duck/react-crud.git
环境准备
关于后端
如果同学不会后端的语言,可以直接使用json-server来做
json-server 增删查改 快速上手
后续我将会补充springboot和flask的增删查改快速上手(也会把vue的增删查改写一遍)
关于数据库
json-server 的数据格式如下 gender=0为男,1为女
{
"student": [
{
"id": "1",
"name": "阿松大",
"age": 23,
"major": "软件",
"gender": 0
},
{
"id": "2",
"name": "撒旦",
"age": 23,
"major": "计算机",
"gender": 1
}
]
}
新建react项目
npx create-react-app 项目名称
npx create-react-app student-view
然后进入项目文件夹,启动项目(是的,我们不需要npm install)
PS C:\Users\Desktop> cd student-view
PS C:\Users\Desktop\student-view> npm start
出现这个页面,我们就成功了
然后我们,清理一些不相关的文件和代码,清理完后是下面这个样子
我们只需要
App.js(组件,我们直接在这个文件里写学生管理的页面,App只是个名字,你可以直接改为Student.js,改完后不要忘记也要修改在index.js对应的名字噢)
index.js(项目入口文件)
简化后,页面长这样
react是热部署的,您不需要重启应用和手动刷新
添加相关依赖
Antd 样式组件 Ant Design of React - Ant Design
为了让我们页面好看些,我们可以引入antd样式组件,antd相比于element而言,更支持react
npm i antd
Axios 异步请求工具 axios中文网|axios API 中文文档 | axios
axios是一个异步请求工具,比起原生的AJAX和Promise更方便,更简洁,我们将使用axios来向后端发起异步请求
npm i axios
前端样式搭建
为了解决大家时间,我直接把样式代码贴到这里
新建一个app.css文件
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.App {
padding: 5px;
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: space-between;
}
header {
width: 100%;
height: 8vh;
display: flex;
align-items: center;
}
main {
width: 100%;
height: 90vh;
}
/* .ant-table-body {
min-height: 72.5vh;
} */
App.js
import { Button, Input, Table, Tag } from 'antd';
import { useState } from 'react';
import './app.css';
function App() {
// 表格参数的控制
const columns = [
{
title: '学生姓名',
dataIndex: 'name',
key: 'name',
},
{
title: '年龄',
dataIndex: 'age',
key: 'age',
},
{
title: '专业',
dataIndex: 'major',
key: 'major',
},
{
title: '性别',
key: 'gender',
dataIndex: 'gender',
render: (_,{ gender }) => (
<span>{gender === 0 ? <Tag color='blue'>男</Tag>:<Tag color='orange'>女</Tag>}</span>
),
},
{
title: '操作',
key: 'action',
render: (record)=>(
<span>
<Button style={{'backgroundColor':'orange','color':'white', 'marginRight':'5px'}}>编辑</Button>
<Button type='primary' danger>删除</Button>
</span>
)
},
];
//假数据
const data = [
{
id: '1',
name: '张三',
age: 32,
major: '计算机',
gender:0
},
{
id: '2',
name: '李四',
age: 42,
major: '软件工程',
gender:0
},
{
id: '3',
name: '王五',
age: 32,
major: 'Sydney No. 1 Lake Park',
gender:1
},
];
//批量选中行的id
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
//批量选中事件
const rowSelection = {
onChange: (newSelectedRowKeys) => {
setSelectedRowKeys(newSelectedRowKeys);
}
};
return (
<div className="App">
{/* 头部:操作栏 */}
<header>
<Button type="primary" danger disabled={selectedRowKeys.length === 0}>批量删除</Button>
<Button type='primary' style={{'backgroundColor':'green','margin':'0 5px'}}>新增学生</Button>
<Input.Search style={{'width':'200px'}} allowClear placeholder="搜索学生姓名" enterButton />
</header>
{/* 主体:学生表格 */}
<main>
<Table
rowKey={(record)=>record.id}
pagination={{
current: 1,
pageSize: 10,
total: 100
}}
rowSelection={rowSelection}
columns={columns}
dataSource={data}
scroll={{y:1000}}/>
</main>
</div>
);
}
export default App;
最终长这个样子(样式大家凑合着看哈,按需改)
统一封装请求接口
我们可以先把所有的api接口封装到一个文件里面,再在页面中调用方法,这样能方便管理
新建一个api.js
import axios from "axios";
const Server_Url = 'http://localhost:8080/student'
/**
* 获取-学生列表信息
* @param {*} params 带条件的分页查询参数
* @returns 学生列表
*/
export const getStudentList = (params)=>axios.get(Server_Url,{
params
})
/**
* 获取-满足查询条件的所有学生列表信息(主要是为了分页查询的count数据)
* @returns 学生列表
*/
export const getTotalStudent = (name)=>axios.get(Server_Url,name)
/**
* 获取-学生个人信息
* @param {*} id 学生id
* @returns 学生个人信息
*/
export const getStudent = (id) => axios.get(Server_Url+'/'+id)
/**
* 删除-学生
* @param {*} id 学生id
* @returns
*/
export const deleteStudent = (id) => axios.delete(Server_Url+'/'+id)
/**
* 更新-学生信息
* @param {*} id 学生id
* @param {*} data 更新的学生信息
* @returns 更新后的学生信息
*/
export const updateStudent = (id,data) => axios.put(Server_Url+'/'+id,data)
/**
* 新建-学生信息
* @param {*} data 学生信息
* @returns
*/
export const createStudent = (data) => axios.post(Server_Url,data)
好了,启动后端接口,我们正式开始
增删查改
带条件分页查询
首先,我们先在页面上获取数据
//获取学生列表
useEffect(()=>{
async function handleGetStudentList(){
const res = await getStudentList()
console.log(res.data)
}
handleGetStudentList()
},[])
获取成功
我们再来完善这块的代码,将请求参数携带进去,并把相应的数据绑定到页面上,还有分页器的分页动作绑定
//带条件的分页搜索参数
const [params, setParams] = useState({
_page:1,//第一页
_per_page:2,//每页展示2页数据(json-server超过0.17就需要使用_per_page替换_limit)
name:''//学生姓名
})
//获取学生列表
const [studentList, setStudentList] = useState([])
const [total, setTotal] = useState(0)
useEffect(()=>{
async function handleGetStudentList(){
const res = await getStudentList(params)
setTotal(res.data.items)
setStudentList(res.data.data)
}
handleGetStudentList()
},[params])//监听搜索参数的变化,如果变化了,就重新获取数据
//分页器发生变化
function handlePaginationChange(newPageNum,newPageSize){
setParams({
...params,
_page:newPageNum,
_per_page:newPageSize
})
}
return (
<div className="App">
{/* 头部:操作栏 */}
<header>
<Button type="primary" danger disabled={selectedRowKeys.length === 0}>批量删除</Button>
<Button type='primary' style={{'backgroundColor':'green','margin':'0 5px'}}>新增学生</Button>
<Input.Search
value={params.name}
onChange={(e)=>setParams({...params, name:e.target.value})}
style={{'width':'200px'}}
allowClear
placeholder="搜索学生姓名"
enterButton />
</header>
{/* 主体:学生表格 */}
<main>
<Table
rowKey={(record)=>record.id}
pagination={{
current: params._page,
pageSize: params._per_page,
total:total,
onChange:handlePaginationChange
}}
rowSelection={rowSelection}
columns={columns}
dataSource={studentList}
scroll={{y:1000}}/>
</main>
</div>
);
注意:
name:'' //此处需要输入完整的学生姓名,很遗憾,即使我使用了name_like也无法实现模糊查询,因此只能特定查询完整的学生姓名了,如果有知道了,请告诉我一下,谢谢
添加学生
我们来新建一个弹窗,里面存一个学生数据表单
//控制窗口打开的函数
const [isModalOpen, setIsModalOpen] = useState(false)
const [form] = Form.useForm();
//打开新增窗口
const handleOpenAddModal = ()=>{
form.resetFields();//每次打开新增窗口时都要清空数据
setIsModalOpen(true)
}
// 关闭弹窗
const handleCancel = ()=>{
setIsModalOpen(false)
}
// 提交表单
const onFinish = async (val)=>{
await createStudent(val)
setParams({
...params
})//重新获取数据
message.success('新增学生成功')
setIsModalOpen(false)
}
{/* 新建/更新学生 */}
<Modal
title="新增学生"
open={isModalOpen}
onCancel={handleCancel}
footer={[]}// 设置footer为空,去掉 取消 确定默认按钮
>
<Form
form={form}//使用 Form 组件的 form 属性将 form 对象与 Form 组件进行关联
name="basic"
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
style={{ maxWidth: 400 }}
initialValues={{ remember: true }}
onFinish={onFinish}
autoComplete="off"
>
<Form.Item
label="学生姓名"
name="name"
rules={[{ required: true, message: '请输入学生姓名' }]}
>
<Input />
</Form.Item>
<Form.Item
label="年龄"
name="age"
rules={[{ required: true, message: '请输入年龄' }]}
>
<InputNumber />
</Form.Item>
<Form.Item
label="专业"
name="major"
rules={[{ required: true, message: '请输入专业' }]}
>
<Input />
</Form.Item>
<Form.Item label="性别" name='gender' rules={[{required:true,message:'请选择性别'}]}>
<Radio.Group>
<Radio value={0}> 男 </Radio>
<Radio value={1}> 女 </Radio>
</Radio.Group>
</Form.Item>
<Form.Item wrapperCol={{ offset: 8, span: 16 }}>
<Button type="primary" htmlType="submit">
确认提交
</Button>
</Form.Item>
</Form>
</Modal>
效果图
更新学生
我们与新增学生同一个表单
首先设置操作列 record 是当前行的数据对象
{
title: '操作',
key: 'action',
render: (record)=>(
<span>
<Button
style={{'backgroundColor':'orange','color':'white', 'marginRight':'5px'}}
onClick={()=>handleOpenUpdateModal(record)}>
编辑
</Button>
<Button type='primary' danger>删除</Button>
</span>
)
},
然后在打开弹窗后,编辑更新表单数据
//用一个变量来记录,当前是否是更新状态
const [isUpdate,setIsUpdate] = useState(false)
//打开编辑弹窗
const handleOpenUpdateModal = (value) => {
form.setFieldsValue(value)//将当前行的数据赋值给表单
setIsUpdate(true)
setIsModalOpen(true)
}
//编辑学生
const handleUpdateStudent = async (value) => {
await updateStudent(value.id,value)
message.success('编辑成功')
setIsModalOpen(false)
setParams({
...params
})
}
优化弹窗和表单,注意我添加了一个隐藏Form.Item来存储学生id
{/* 新建/更新学生 */}
<Modal
title={isUpdate?'编辑学生':'新增学生'}
open={isModalOpen}
onCancel={handleCancel}
footer={[]}// 设置footer为空,去掉 取消 确定默认按钮
>
<Form
form={form}//使用 Form 组件的 form 属性将 form 对象与 Form 组件进行关联
name="basic"
labelCol={{ span: 8 }}
wrapperCol={{ span: 16 }}
style={{ maxWidth: 400 }}
initialValues={{ remember: true }}
onFinish={isUpdate? handleUpdateStudent : handleCreateStudent}
autoComplete="off"
>
<Form.Item name='id' hidden>
<Input/>
</Form.Item>
<Form.Item
label="学生姓名"
name="name"
rules={[{ required: true, message: '请输入学生姓名' }]}
>
<Input />
</Form.Item>
<Form.Item
label="年龄"
name="age"
rules={[{ required: true, message: '请输入年龄' }]}
>
<InputNumber />
</Form.Item>
<Form.Item
label="专业"
name="major"
rules={[{ required: true, message: '请输入专业' }]}
>
<Input />
</Form.Item>
<Form.Item label="性别" name='gender' rules={[{required:true,message:'请选择性别'}]}>
<Radio.Group>
<Radio value={0}> 男 </Radio>
<Radio value={1}> 女 </Radio>
</Radio.Group>
</Form.Item>
<Form.Item wrapperCol={{ offset: 8, span: 16 }}>
<Button type="primary" htmlType="submit">
确认提交
</Button>
</Form.Item>
</Form>
</Modal>
效果图
删除学生
单个删除
首先,为了避免用户手滑误删,我们用个popover包裹一下删除按钮,进行二次确认
{
title: '操作',
key: 'action',
render: (record)=>(
<span>
<Button
style={{'backgroundColor':'orange','color':'white', 'marginRight':'5px'}}
onClick={()=>handleOpenUpdateModal(record)}>
编辑
</Button>
<Popover
title='警告!您确定要删除吗?'
content={
<Button
onClick={() => handleDeleteStudent(record.id)}
type='primary' danger
>
确定
</Button>
}>
<Button type='primary' danger>删除</Button>
</Popover>
</span>
)
},
然后就可以编写删除的逻辑了
//删除学生
const handleDeleteStudent = async (id) => {
await deleteStudent(id)
message.success('删除成功')
setParams({
...params
})
}
效果如图
批量删除
还记得最开始写的批量选中事件吗,我们可以直接这些得到的id数组使用fori遍历循环,每个都调用一次删除方法就可以了
首先我们还是需要对批量删除按钮进行二次确认警告的设置
<Popover
trigger="click" //注意要改为点击模式,默认为hover,但是如果是hover的话,批量按钮即使是disable也会弹出来确认框
title='警告!您确定要删除这些学生吗?'
content={
<Button
onClick={handleBatchDelete}
type='primary' danger
>
确定
</Button>
}>
<Button type="primary" danger disabled={selectedRowKeys.length === 0} >批量删除</Button>
</Popover>
然后就是逻辑处理
//批量删除
const handleBatchDelete = ()=>{
for(let i = 0; i < selectedRowKeys.length; i++){
deleteStudent(selectedRowKeys[i])
}
setParams({
...params
})
}
效果图
完结
以上所有的增删查改就做完了
gitee代码也做了同步更新
React 增删查改: 使用react完成一个简单的学生管理增删查改项目https://gitee.com/a-kodak-duck/react-crud.git
版权归原作者 前端开发洋洋 所有, 如有侵权,请联系我们删除。