Views: 0
逻辑
逻辑和教师信息一样,所以可以直接复制教师信息
在数据库加上学生库,建立学生实体类就好了,前端更改一些配置,一些信息就好了,基本上都不需要改,后端内容只要把教师信息改为学生信息
要学会看报错信息
核心数据结构:
每个学员包含基本信息:ID、姓名、学号、所属学院、专业、年级等 可能还包含联系方式、学分信息等 主要功能:
增:添加新学员信息 删:删除学员信息 改:修改学员信息 查: 查看所有学员列表 按条件搜索 分页查看学员信息
前后端交互:
前端通过类似/Student/
的接口与后端交互 管理页面可以查看学员列表并进行增删改查操作
学生信息数据库
CREATE TABLE `student` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`username` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '账号',
`sex` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '性别',
`avatar` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '角色',
`password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '密码',
`name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '姓名',
`role` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '角色',
`code` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '学号',
`college_id` int DEFAULT NULL COMMENT '学院ID',
`score` int DEFAULT NULL COMMENT '学分',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='学生信息';
entitv
添加学生类
public class Student {
private Integer id;
private String username;
private String password;
private String name;
private String sex;
private String role;
private String avatar;
private String code;
private Integer collegeId;
private String collegeName;
private Integer score;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public Integer getScore() {
return score;
}
public void setScore(Integer score) {
this.score = score;
}
public Integer getCollegeId() {
return collegeId;
}
public void setCollegeId(Integer collegeId) {
this.collegeId = collegeId;
}
public String getCollegeName() {
return collegeName;
}
public void setCollegeName(String collegeName) {
this.collegeName = collegeName;
}
}
学生信息
<el-menu-item index="/student">
<el-icon><Avatar /></el-icon>
<span>学生信息</span>
</el-menu-item>
配置路由,加一个student信息
{ path: 'student', component: () => import('@/views/manager/Student.vue')},
前端
<template>
<div>
<!--查询重置按钮 -->
<div class="card" style="margin-bottom: 5px;">
<el-input v-model="data.name" style="width: 300px; margin-right: 10px" placeholder="请输入学生姓名"></el-input>
<el-button type="primary" @click="load">查询</el-button>
<el-button type="info" style="margin: 0 10px" @click="reset">重置</el-button>
</div>
<!-- 新增按钮 可以参考Student.vue里面的内容 -->
<div class="card" style="margin-bottom: 5px">
<div style="margin-bottom: 10px">
<!-- 新增学生按钮,点击触发handleAdd方法 -->
<el-button type="primary" @click="handleAdd">新增</el-button>
<!--tableData 所有表格数据在里面,-->
<el-table :data="data.tableData" stripe>
<!--表格列,label是列的标题,prop是列的属性,prop要与后端保持一致,不然接收不到-->
<el-table-column label="用户名" prop="username"></el-table-column>
<el-table-column label="头像" prop="avatar">
<!--头像列,scope是当前行的数据,scope.row.avatar是当前行的头像路径-->
<template v-slot="scope">
<!--scope图片组件,src是图片的路径,style是图片的样式,width是图片的宽度,height是图片的高度,border-radius是图片的圆角-->
<el-image :src="scope.row.avatar" style="width: 40px; height: 40px; border-radius: 50%"></el-image>
</template>
</el-table-column>
<el-table-column label="姓名" prop="name"></el-table-column>
<el-table-column label="性别" prop="sex"></el-table-column>
<el-table-column label="学号" prop="code"></el-table-column>
<el-table-column label="所属学院" prop="college_id"></el-table-column>
<el-table-column label="学分" prop="score"></el-table-column>
<el-table-column label="角色" prop="role"></el-table-column>
<el-table-column label="操作" align="center" width="160">
<template #default="scope">
<!--scope里面有row,row就是当前行的数据,scope.row.id就是当前行的id-->
<el-button type="primary" @click="handleEdit(scope.row)">编辑</el-button>
<el-button type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
<!--分页-->
<div class="card">
<!-- prev上一页, pager当前页, next下一页,分页查询的页码, 每页显示的记录数, 总记录数, 分页查询的总记录数-->
<el-pagination background layout="prev, pager, next" v-model:page-size="data.pageSize" v-model:current-page="data.pageNum" :total="data.total" @current-change = "changePage"/>
</div>
<!-- 学生信息弹窗对话框 -->
<el-dialog title="学生信息" width="40%" v-model="data.formVisible" :close-on-click-modal="false" destroy-on-close>
<!--prop v-model 里面要与后端保持一致不然接收不到 -->
<el-form :model="data.form" label-width="100px" style="padding-right: 50px">
<!--图片上传,上传成功后会调用handleImgSuccess方法,上传成功后会返回一个url,我们把这个url赋值给data.form.avatar-->
<el-form-item label="头像" prop="avatar">
<!--el-upload 上传组件,action是上传的地址,list-type是上传的类型,on-success是上传成功后的回调方法-->
<el-upload :action="uploadUrl" list-type="picture" :on-success="handleImgSuccess">
<!--el-button 按钮组件,type是按钮的类型,primary是主按钮-->
<el-button type="primary">上传图片</el-button>
</el-upload>
</el-form-item>
<el-form-item label="账号" prop="username">
<el-input v-model="data.form.username" autocomplete="off" placeholder="请输入账号"/>
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="data.form.name" autocomplete="off" placeholder="请输入姓名"/>
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-select v-model="data.form.sex" placeholder="请选择性别" style="width: 100%">
<el-option label="男" value="男"></el-option>
<el-option label="女" value="女"></el-option>
</el-select>
</el-form-item>
<el-form-item label="学号" prop="code">
<el-input v-model="data.form.code" autocomplete="off" placeholder="请输入学号"/>
</el-form-item>
</el-form>
<!-- 弹窗取消,保存 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="data.formVisible = false">取 消</el-button>
<el-button type="primary" @click="save">保 存</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
//导包
import {reactive} from "vue";
import request from "@/utils/request";
import {ElMessage, ElMessageBox} from "element-plus";
//定义上传接口地址,这里是后端提供的接口地址
//VITE_BASE_URL是在.env.development文件中定义的,这里是开发环境的地址
// /files/upload是后端提供的接口地址
const uploadUrl = import.meta.env.VITE_BASE_URL + '/files/upload';
const data = reactive({
// 学生信息弹窗对话框
formVisible: false,
// 学生信息表单数据
form: {
},
// 学生信息表格数据的数组,初始为空数组
tableData: [],
// 分页查询的页码
pageNum: 1,
// 分页查询的每页显示的记录数
pageSize: 5,
// 分页查询的总记录数,初始为0
total: 0,
// 学生姓名
name: null,
});
const load = () => {
// 调用后端接口查询学生信息
// 这里可以使用axios或者其他请求库发送请求
// 例如:axios.get('/api/student/selectPage', { params: { pageNum: data.pageNum, pageSize: data.pageSize } })
request.get('/student/selectPage',{//请求路径
params:{//查询条件
pageNum: data.pageNum,//页码
pageSize: data.pageSize, //每页显示的记录数
name: data.name,//学生姓名
}
}
).then(res => {
if (res.code === '200') {
// 将查询到的学生信息数据赋值给tableData
// res.data?.list 是一个数组,包含了查询到的学生信息数据
// ?是一个可选链操作符,用于安全地访问对象的属性,如果对象为null或undefined,则不会抛出错误,而是返回undefined
data.tableData = res.data?.list
// 将查询到的总记录数赋值给total
data.total = res.data?.total
}else {
// 查询失败,提示用户
ElMessage.error(res.msg)
}
})
}
// 分页查询,点击页码触发,改变页码,触发load()
const changePage = (pageNum) => {
data.pageNum = pageNum //页码
load()
}
// 新增学生按钮点击事件
const handleAdd = () => {
// 清空学生信息表单数据
data.form = {
}
// 打开学生信息弹窗对话框
data.formVisible = true
}
// 编辑学生按钮点击事件
const handleEdit = (row) => {
// 将选中的学生信息数据赋值给学生信息表单数据
// 这里可以使用JSON.parse(JSON.stringify(row))来深拷贝row对象,避免修改row对象时影响到tableData
// 因为row是一个对象,而对象是引用类型,直接赋值给data.form会导致修改row时也会修改data.form,所以需要深拷贝
// 将row对象转换为字符串,再转换为对象,这样就会创建一个新的对象,不会影响到原来的row对象
data.form = JSON.parse(JSON.stringify(row))
// 打开学生信息弹窗对话框
data.formVisible = true
}
// 删除学生按钮点击事件
const handleDelete = (id) => {
// 弹出确认框,提示用户是否确定删除
// 如果用户点击确定,就调用后端接口删除学生信息
// 如果用户点击取消,就不做任何操作
ElMessageBox.confirm('删除后数据无法恢复,您确定删除吗?', '删除确认', { type: 'warning' }).then(res => {
// 调用后端接口删除学生信息
// 这里可以使用axios或者其他请求库发送请求
// 例如:axios.delete('/api/student/deleteById/' + id)
request.delete('/student/deleteById/' + id).then(res => {
// 提示用户删除成功或者失败
if (res.code === '200') {
ElMessage.success('操作成功')
load()
} else {
ElMessage.error(res.msg)
}
})
}).catch(err => {})
}
// 新增保存按钮点击事件
const add = () => {
// 调用后端接口保存学生信息
// 这里可以使用axios或者其他请求库发送请求
// 例如:axios.post('/api/student/add', data.form)
request.post('/student/add', data.form).then(res => {
if (res.code === '200') {
// 保存成功,提示用户
ElMessage.success('操作成功')
// 重新加载学生信息
load()
// 关闭学生信息弹窗对话框
data.formVisible = false
}else {
// 保存失败,提示用户
ElMessage.error(res.msg)
}
})
}
// 更新保存学生信息按钮点击事件
const update = () => {
// 调用后端接口更新学生信息
// 这里可以使用axios或者其他请求库发送请求
// 例如:axios.put('/api/student/update', data.form)
request.put('/student/update', data.form).then(res => {
if (res.code === '200') {
// 更新成功,提示用户
ElMessage.success('操作成功') //提示用户
// 重新加载学生信息
load()
}else {
// 更新失败,提示用户
ElMessage.error(res.msg)
}
})
}
// 保存学生信息按钮点击事件
const save = () => {
// 判断学生信息表单数据是否有id属性
// 如果有id属性,说明是编辑学生信息,调用更新学生信息接口
// 如果没有id属性,说明是新增学生信息,调用新增学生信息接口
// 三元运算符,如果data.form.id有值,就调用update(),否则调用add()
data.form.id ? update() : add()
}
const reset = () => {
// 重置查询条件
data.name = null
// 重新加载学生信息
load()
}
//上传成功后调用的方法,把上传成功后的图片地址赋值给data.form.avatar
//res.data.url 是上传成功后的图片地址
const handleImgSuccess = (res) => {
// res.data 是上传成功后的图片
// 我们把这个地址赋值给data.form.avatar
data.form.avatar = res.data
//相当于和avatar绑定了
}
load()
</script>
后端
Controller—StudentController
/**
* 学生模块前端操作接口入口
* 拿到数据后,调用service层的方法,返回结果
* 前端调用接口时,需要传入参数,调用service层的方法,返回结果
**/
@RestController
@RequestMapping("/student")
public class StudentController {
@Resource
private StudentService studentService;
/**
* 新增学生
* */
@PostMapping("/add")
public Result add(@RequestBody Student student){
studentService.add(student);
return Result.success();
}
/**
* 更新学生
*/
@PutMapping("/update")
public Result update(@RequestBody Student student){
studentService.updateById(student);
return Result.success();
}
/**
* 删除学生
*/
@DeleteMapping("/deleteById/{id}")
public Result deleteById(@PathVariable Integer id) {
studentService.deleteById(id);
return Result.success();
}
/**
* 分页查询
* */
@GetMapping("selectPage")
public Result selectPage(Student student,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "5") Integer pageSize){
PageInfo<Student> pageInfo = studentService.selectPage(student,pageNum,pageSize);
return Result.success(pageInfo);
}
}
Service—StudentService 核心逻辑
/**
* 学生模块业务逻辑接口
* 要把数据往数据库里存,调用mapper层的方法,返回结果
* 业务逻辑接口调用mapper层的方法,返回结果
*/
@Service
public class StudentService {
// 注入StudentMapper
@Resource
private StudentMapper studentMapper;
/**
* 新增学生
*/
public void add(Student student) {
// 确保这里没有设置或使用student.setTitle()方法
Student dbStudent = studentMapper.selectByUsername(student.getUsername());
if (ObjectUtil.isNotEmpty(dbStudent)) {
throw new CustomException("用户名已存在");
}
if(ObjectUtil.isEmpty(student.getPassword())){
student.setPassword("123456");
}
student.setRole("STUDENT");
student.setScore(0);
studentMapper.insert(student);
}
/**
* 分页查询
*/
public PageInfo<Student> selectPage(Student student,Integer pageNum, Integer pageSize) {
// ToDo 分页查询逻辑处理
// 1. 分页查询学生信息
// 2. 返回学生信息
// 3. 如果没有学生信息,返回空列表
// 4. 如果有学生信息,返回学生信息列表
List<Student> list;//定义一个list集合
PageHelper.startPage(pageNum,pageSize);//分页查询
if(ObjectUtil.isNotEmpty(student.getName())){//如果name不为空,就按照name查询
list = studentMapper.selectByName(student.getName());
}else{//如果name为空,就查询所有学生信息
list = studentMapper.selectAll();
}
return PageInfo.of(list);//返回学生信息列表
}
//根据id查询学生信息,返回一个student对象
public void updateById(Student student) {//传入一个student对象
studentMapper.updateById(student);//调用mapper层的方法,返回结果
}
//根据id删除学生信息
public void deleteById(Integer id) {
studentMapper.deleteById(id);
}
}
Mapper—StudentMapper
@Mapper
public interface StudentMapper {
// 新增学生
void insert(Student student);
@Select("select * from student where username = #{username}")
Student selectByUsername(String username);
// 查询所有学生信息
@Select("select * from student")
List<Student> selectAll();
// 根据学生姓名查询学生信息,模糊查询
@Select("select * from student where name like concat('%',#{name},'%')")
List<Student> selectByName(String name);
void updateById(Student student);//更新学生信息
// 根据id删除学生信息
@Delete("delete from student where id = #{id}")
void deleteById(Integer id);
}
StudentMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.StudentMapper">
<insert id="insert" parameterType="com.example.entity.Student" useGeneratedKeys="true">
insert into student (username, password, name, sex, role, avatar, college_id, code, score)
values (#{username}, #{password}, #{name}, #{sex}, #{role}, #{avatar}, #{collegeId}, #{code}, #{score})
</insert>
<update id="updateById" parameterType="com.example.entity.Student">
update student
set username = #{username}, password = #{password}, name = #{name},
sex = #{sex}, college_id = #{collegeId}, role = #{role}, avatar = #{avatar},
score = #{score}, code = #{code}
where id = #{id}
</update>
</mapper>