【学生管理系统】学生管理(重点)
目录
4. 学生管理
4.1 需求
4.1.1 查询所有
4.1.2 添加:基本信息
4.1.3 添加:级联城市
4.1.4 添加:选课
4.2 环境搭建
4.2.1 前端环境
4.2.2 学生后端环境(9020)
4.2.3 课程后端环境(9030)
4.3 查询所有
4.3.1 基本功能:后端实现
4.3.2 基本功能:前端实现
4.3.3 班级:后端实现
4.3.4 班级:前端实现
4.3.5 选课详情:后端实现
4.3.6 选课详情:前端实现
4.5 添加
4.5.0 分析
4.5.1 基本操作
4.5.2 级联城市
4.5.2 选课
4. 6 修改
4.6.1 分析
4.5.2 查询详情
4.5.3 更新添加功能
4. 学生管理 4.1 需求 4.1.1 查询所有4.1.2 添加:基本信息
4.1.3 添加:级联城市
4.1.4 添加:选课
4.2 环境搭建 4.2.1 前端环境 4.2.2 学生后端环境(9020)
项目名:nacos-nuxt-student-service-student
pom文件
yml文件
启动类
拷贝配置类
基本结构
pom文件
<dependencies> <!--web起步依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- nacos 客户端 --> <dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> </dependency> <!-- nacos 服务发现 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--swagger2--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> </dependency> <!-- feign 远程调用 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--测试--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <!-- mybatis plus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> </dependency> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--自定义项目--> <dependency> <groupId>com.czxy</groupId> <artifactId>nacos-nuxt-student-domain</artifactId> </dependency> <!-- redis 启动器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- JavaMail 启动器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> <!-- MQ 启动器 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> <!-- fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> </dependency> <!--开发者工具--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> <!--jwt工具--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> </dependency> <!--joda 时间工具类 --> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> </dependency> <!--JavaBean工具类,用于JavaBean数据封装--> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> </dependency> </dependencies> yml文件
# 服务端口号 server: port: 9020 # 服务名 spring: application: name: student-service datasource: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/cloud_es_student?useUnicode=true&characterEncoding=utf8 username: root password: 1234 druid: #druid 连接池配置 initial-size: 1 #初始化连接池大小 min-idle: 1 #最小连接数 max-active: 20 #最大连接数 test-on-borrow: true #获取连接时候验证,会影响性能 cloud: nacos: discovery: server-addr: 127.0.0.1:8848 #nacos服务地址 #开启log4j打印SQL语句 logging: level: com: czxy: student: mapper: debug # mp日志打印 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-value: 1 logic-not-delete-value: 0 sc: jwt: secret: sc@Login(Auth}*^31)&czxy% # 登录校验的密钥 pubKeyPath: D:/rsa/rsa.pub # 公钥地址 priKeyPath: D:/rsa/rsa.pri # 私钥地址 expire: 360 # 过期时间,单位分钟启动类
package com.czxy; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; /** * @author 桐叔 * @email liangtong@itcast.cn * @description */ @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class StudentServiceApplication { public static void main(String[] args) { SpringApplication.run(StudentServiceApplication.class, args); } } 配置类
基本结构
4.2.3 课程后端环境(9030)
项目名:nacos-nuxt-student-service-course
4.3 查询所有 4.3.1 基本功能:后端实现
编写Vo
编写controller
编写service
编写Vo
package com.czxy.student.vo; import lombok.Data; /** * @author 桐叔 * @email liangtong@itcast.cn * @description */ @Data public class StudentVo { private String classesId; //班级 private String sname; //姓名 private String startAge; //开始年龄 private String endAge; //结束年龄 } 编写controller
@PostMapping("/condition/{size}/{current}") public BaseResult condition( @RequestBody StudentVo studentVo, @PathVariable("size") Integer size, @PathVariable("current") Integer current ) { // 查询 Page<TbStudent> page = tbStudentService.condition(studentVo, size, current); // 返回 return BaseResult.ok("查询成功", page); }
编写service
接口
Page<TbStudent> condition(StudentVo studentVo, Integer size, Integer current);实现类
@Override public Page<TbStudent> condition(StudentVo studentVo, Integer size, Integer current) { //1 条件 QueryWrapper<TbStudent> queryWrapper = new QueryWrapper<>(); if(StringUtils.isNotBlank(studentVo.getClassesId())) { queryWrapper.eq("c_id", studentVo.getClassesId()); } if(StringUtils.isNotBlank(studentVo.getSname())) { queryWrapper.like("sname", studentVo.getSname()); } if(StringUtils.isNotBlank(studentVo.getStartAge())) { queryWrapper.ge("age", studentVo.getStartAge()); } if(StringUtils.isNotBlank(studentVo.getEndAge())) { queryWrapper.le("age", studentVo.getEndAge()); } //2 分页 Page<TbStudent> page = new Page<>(current, size); //3 查询 baseMapper.selectPage(page, queryWrapper); //4 关联 //5 返回 return page; } 4.3.2 基本功能:前端实现列表
条件
分页
列表
<template> <div> <!-- 添加按钮 --> <el-button type="primary" @click="openStudentDialog">添加</el-button> <!-- 列表start --> <el-table :data="studentPage.records" stripe style="width: 100%"> <el-table-column type="selection" width="55"> </el-table-column> <el-table-column prop="sid" label="学生ID" fixed width="180"> </el-table-column> <el-table-column prop="cid" label="班级名称" fixed width="180"> </el-table-column> <el-table-column prop="sname" label="学生姓名" width="180"> </el-table-column> <el-table-column prop="age" label="年龄" width="180"> </el-table-column> <el-table-column prop="birthday" label="生日" width="180"> </el-table-column> <el-table-column prop="gender" label="性别" width="180"> <template slot-scope="scope"> {{scope.row.gender == 1 ? '男': '女'}} </template> </el-table-column> <el-table-column prop="courseCount" label="选课数" width="180"> </el-table-column> <el-table-column prop="courseList" label="选课详情" width="180"> </el-table-column> <el-table-column label="操作" fixed="right" width="180"> <template slot-scope="scope"> <el-button size="mini">编辑</el-button> <el-button size="mini" type="danger">删除</el-button> </template> </el-table-column> </el-table> <!-- 列表end --> <!-- 弹出框 start --> <el-dialog title="添加学生" :visible.sync="dialogStudentVisible"> <el-form :model="student" label-width="80px"> <el-form-item label="班级名称"> <el-select v-model="student.cid" placeholder="请选择班级"> <el-option v-for="(classes,index) in classesList" :key="index" :label="classes.cname" :value="classes.cid"></el-option> </el-select> </el-form-item> <el-form-item label="选择城市"> <el-cascader v-model="student.cids" :props="cityProps"></el-cascader> </el-form-item> <el-form-item label="姓名"> <el-input v-model="student.sname"></el-input> </el-form-item> <el-form-item label="年龄"> <el-input v-model="student.age"></el-input> </el-form-item> <el-form-item label="生日"> <el-date-picker v-model="student.birthday" value-format="yyyy-MM-dd" type="date" placeholder="请选择学生生日"> </el-date-picker> </el-form-item> <el-form-item label="性别"> <el-radio-group v-model="student.gender"> <el-radio :label="1">男</el-radio> <el-radio :label="0">女</el-radio> </el-radio-group> </el-form-item> <el-form-item label="选课"> <el-checkbox-group v-model="student.courseIds"> <el-checkbox v-for="(course,index) in courseList" :key="index" :label="course.cid">{{course.cname}}</el-checkbox> </el-checkbox-group> </el-form-item> </el-form> {{student}} <div slot="footer" class="dialog-footer"> <el-button @click="dialogStudentVisible = false">取 消</el-button> <el-button type="primary" @click="addStudent">确 定</el-button> </div> </el-dialog> <!-- 弹出框 end --> </div> </template> <script> let _vue = null export default { data() { _vue = this //缓存vue对象 return { dialogStudentVisible: false, //学生弹出框控制变量 student: { cids: [], courseIds: [] }, classesList: [], //班级列表 cityProps: { //城市级联菜单的属性设置 lazy: true, //开启 async lazyLoad (node, resolve) { //加载数据 // 1. 如果 node.root == true 表示第一次加载,也就是省 let parentId = null if(node.root == true) { parentId = "0" } else { // 其他 - node.value可以获得当前节点的id的值,例如:省的id parentId = node.value } console.info(node) // 2. ajax 查询所有的城市 let { data:baseResult } = await _vue.$axios.get(`/student-service/city/${parentId}`) // 处理查询结果,如果是县,表示是叶子 baseResult.data.forEach(city=>{ city.leaf = node.level >=2 }) // 通过调用resolve将子节点数据返回,通知组件数据加载完成 resolve(baseResult.data); }, label: 'cityName', value: 'cid' }, courseList: [], //课程列表 studentVo: { //条件 "classesId": "", "endAge": "", "sname": "", "startAge": "" }, studentPage: { //结果 + 分页 size: 3, current: 1, total: 0 } } }, methods: { openStudentDialog() { // 查询所有的班级 this.findAllClasses() // 查询所有的选课 this.findAllCourse() // 控制变量 this.dialogStudentVisible = true }, async findAllClasses() { // ajax let { data: baseResult } = await this.$axios.get('/classes-service/classes') // 处理 if(baseResult.code == 20000) { this.classesList = baseResult.data } else { this.$message.error(baseResult.message) } }, async addStudent() { // 处理所选城市数据 数组转字符串, [1,2,3] --> 1,2,3 this.student.cityIds = this.student.cids.join(',') // 发送ajax let {data:baseResult} = await this.$axios.post('/student-service/student', this.student) if(baseResult.code == 20000) { //成功 this.$message.success(baseResult.message) //刷新列表 this.conditionStudent() //关闭弹出框 this.dialogStudentVisible = false } else { // 失败 this.$message.error(baseResult.message) } }, async findAllCourse() { let {data:baseResult} = await this.$axios.get('/course-service/course') this.courseList = baseResult.data }, async conditionStudent() { // 查询 let url = `/student-service/student/condition/${this.studentPage.size}/${this.studentPage.current}` let { data: baseResult} = await this.$axios.post(url, this.studentVo) // 处理结果 this.studentPage = baseResult.data } }, mounted() { // 查询所有的学生 this.conditionStudent() }, } </script> <style> </style>条件
<el-row> <el-col :span="2"> <!-- 添加按钮 --> <el-button type="primary" size="mini" @click="openStudentDialog">添加</el-button> </el-col> <el-col :span="22"> <!-- 条件查询start --> <el-form :inline="true" :model="studentVo" size="mini" class="demo-form-inline"> <el-form-item label="班级"> <el-select v-model="studentVo.classesId" placeholder="请选择班级"> <el-option v-for="(classes,index) in classesList" :key="index" :label="classes.cname" :value="classes.cid"></el-option> </el-select> </el-form-item> <el-form-item label="姓名"> <el-input v-model="studentVo.sname" placeholder="请输入姓名"></el-input> </el-form-item> <el-form-item label="姓名"> <el-row> <el-col :span="11"> <el-input v-model="studentVo.startAge" placeholder="请输入开始年龄"></el-input> </el-col> <el-col :span="2" style="text-align: center;">-</el-col> <el-col :span="11"> <el-input v-model="studentVo.endAge" placeholder="请输入结束年龄"></el-input> </el-col> </el-row> </el-form-item> <el-form-item> <el-button type="primary" @click="conditionStudent(1)">查询</el-button> </el-form-item> </el-form> <!-- 条件查询end --> </el-col> </el-row>
分页
<!-- 分页 start --> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="studentPage.current" :page-sizes="[1, 2, 3, 5, 10]" :page-size="studentPage.size" layout="total, sizes, prev, pager, next, jumper" :total="studentPage.total"> </el-pagination> <!-- 分页 end --> <script> export default { methods: { handleSizeChange(val) { console.log(`每页 ${val} 条`); this.studentPage.size = val this.studentPage.current = 1 this.conditionStudent() }, handleCurrentChange(val) { console.log(`当前页: ${val}`); this.studentPage.current = val this.conditionStudent() } } } </script> 4.3.3 班级:后端实现
班级服务:查询详情
学生服务:
检查student对象,是否有班级对象
编写feign调用查询详情
修改service,给student填充班级信息
班级服务:查询详情
@GetMapping("/{cid}") public BaseResult<TbClass> findById(@PathVariable("cid") Integer cid) { // 查询详情 TbClass tbClass = tbClassesService.getById(cid); // 返回 return BaseResult.ok("查询成功", tbClass); }
学生服务:
检查student对象,是否有班级对象
编写feign调用查询详情
package com.czxy.student.feign; import com.czxy.domain.TbClass; import com.czxy.vo.BaseResult; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; /** * @author 桐叔 * @email liangtong@itcast.cn * @description */ @FeignClient(value = "classes-service", path = "/classes") public interface ClassesFeign { // 必须有泛型,否则获得的数据不是TbClass而是Map @GetMapping("/{cid}") public BaseResult<TbClass> findById(@PathVariable("cid") Integer cid) ; }
修改service,给student填充班级信息
@Override public Page<TbStudent> condition(StudentVo studentVo, Integer size, Integer current) { //1 条件 QueryWrapper<TbStudent> queryWrapper = new QueryWrapper<>(); if(StringUtils.isNotBlank(studentVo.getClassesId())) { queryWrapper.eq("c_id", studentVo.getClassesId()); } if(StringUtils.isNotBlank(studentVo.getSname())) { queryWrapper.like("sname", studentVo.getSname()); } if(StringUtils.isNotBlank(studentVo.getStartAge())) { queryWrapper.ge("age", studentVo.getStartAge()); } if(StringUtils.isNotBlank(studentVo.getEndAge())) { queryWrapper.le("age", studentVo.getEndAge()); } //2 分页 Page<TbStudent> page = new Page<>(current, size); //3 查询 baseMapper.selectPage(page, queryWrapper); //4 关联 page.getRecords().forEach(student -> { // 4.1 处理对应班级信息 BaseResult<TbClass> classesBaseResult = classesFeign.findById(student.getCid()); TbClass tbClass = classesBaseResult.getData(); student.setTbClass(tbClass); }); //5 返回 return page; } 4.3.4 班级:前端实现<el-table-column label="班级名称" fixed width="180"> <template slot-scope="scope"> {{scope.row.tbClass ? scope.row.tbClass.cname : ''}} </template> </el-table-column> 4.3.5 选课详情:后端实现
课程服务:查找指定学生的选课
mapper:sql语句多表查询
service
controller
学生服务
编写CourseFeign
修改学生service,完善级联操作
==异常==:先完成添加,再完成查询时,会存在一个服务,编写2个feign情况,启动时会有异常。
课程服务:查找指定学生的选课
mapper:sql语句多表查询
package com.czxy.course.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.czxy.domain.TbCourse; import com.czxy.domain.TbStudent; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import java.util.List; /** * @author 桐叔 * @email liangtong@itcast.cn * @description */ @Mapper public interface TbCourseMapper extends BaseMapper<TbCourse> { /** * 查询指定学生的选课 * @author 桐叔 * @email liangtong@itcast.cn * @return */ @Select("SELECT c.* FROM tb_course c, tb_student_course sc WHERE c.c_id = sc.c_id AND sc.s_id = #{sid}") public List<TbCourse> findAllByStudentId(@Param("sid") Integer sid); } service
接口
package com.czxy.course.service; import com.baomidou.mybatisplus.extension.service.IService; import com.czxy.domain.TbCourse; import org.apache.ibatis.annotations.Param; import java.util.List; /** * @author 桐叔 * @email liangtong@itcast.cn * @description */ public interface TbCourseService extends IService<TbCourse> { public List<TbCourse> findAllByStudentId(Integer sid); } 实现类
package com.czxy.course.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.czxy.course.mapper.TbCourseMapper; import com.czxy.course.service.TbCourseService; import com.czxy.domain.TbCourse; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; /** * @author 桐叔 * @email liangtong@itcast.cn * @description */ @Service @Transactional public class TbCourseServiceImpl extends ServiceImpl<TbCourseMapper, TbCourse> implements TbCourseService { @Override public List<TbCourse> findAllByStudentId(Integer sid) { return baseMapper.findAllByStudentId(sid); } } controller
package com.czxy.course.controller; import com.czxy.course.service.TbCourseService; import com.czxy.domain.TbCourse; import com.czxy.vo.BaseResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.List; /** * @author 桐叔 * @email liangtong@itcast.cn * @description */ @RestController @RequestMapping("/course") public class TbCourseController { @Resource private TbCourseService tbCourseService; @GetMapping("/student/{sid}") public BaseResult<List<TbCourse>> findAllByStudentId(@PathVariable("sid") Integer sid) { // 查询 List<TbCourse> courseList = tbCourseService.findAllByStudentId(sid); // 返回 return BaseResult.ok("查询成功", courseList); } } 学生服务
编写CourseFeign
package com.czxy.student.feign; import com.czxy.domain.TbStudentCourse; import com.czxy.vo.BaseResult; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; /** * @author 桐叔 * @email liangtong@itcast.cn * @description */ //@FeignClient(value = "服务名", path = "controller类路径") @FeignClient(value = "course-service", path = "/course") public interface CourseFeign { @GetMapping("/student/{sid}") public BaseResult<List<TbCourse>> findAllByStudentId(@PathVariable("sid") Integer sid); }
修改学生service,完善级联操作
@Override public Page<TbStudent> condition(StudentVo studentVo, Integer size, Integer current) { //1 条件 QueryWrapper<TbStudent> queryWrapper = new QueryWrapper<>(); if(StringUtils.isNotBlank(studentVo.getClassesId())) { queryWrapper.eq("c_id", studentVo.getClassesId()); } if(StringUtils.isNotBlank(studentVo.getSname())) { queryWrapper.like("sname", studentVo.getSname()); } if(StringUtils.isNotBlank(studentVo.getStartAge())) { queryWrapper.ge("age", studentVo.getStartAge()); } if(StringUtils.isNotBlank(studentVo.getEndAge())) { queryWrapper.le("age", studentVo.getEndAge()); } //2 分页 Page<TbStudent> page = new Page<>(current, size); //3 查询 baseMapper.selectPage(page, queryWrapper); //4 关联 page.getRecords().forEach(student -> { // 4.1 处理对应班级信息 BaseResult<TbClass> classesBaseResult = classesFeign.findById(student.getCid()); TbClass tbClass = classesBaseResult.getData(); student.setTbClass(tbClass); // 4.2 课程详情 BaseResult<List<TbCourse>> courseListBaseResult = courseFeign.findAllByStudentId(student.getSid()); List<TbCourse> courseList = courseListBaseResult.getData(); student.setCourseList(courseList); student.setCourseCount(courseList.size()); }); //5 返回 return page; }4.3.6 选课详情:前端实现 <template slot-scope="scope"> <el-tag v-for="(course,index) in scope.row.courseList" :key="index">{{course.cname}}</el-tag> </template> 4.5 添加 4.5.0 分析
4.5.1 基本操作
1)后端实现:查询所有的班级
/** * 查询所有 * @author 桐叔 * @email liangtong@itcast.cn * @return */ @GetMapping public BaseResult findAll() { // 查询所有 List<TbClass> list = tbClassesService.list(); // 返回 return BaseResult.ok("查询成功", list); }
2)前端实现:班级列表+表单
展示弹出框
填充表单
展示班级列表
展示弹出框
<template> <div> <!-- 添加按钮 --> <el-button type="primary" @click="dialogStudentVisible = true">添加</el-button> <!-- 弹出框 start --> <el-dialog title="添加学生" :visible.sync="dialogStudentVisible"> <el-form :model="student" label-width="80px"> <el-form-item label="活动名称"> <el-input v-model="student.name" autocomplete="off"></el-input> </el-form-item> <el-form-item label="活动区域"> <el-select v-model="student.region" placeholder="请选择活动区域"> <el-option label="区域一" value="shanghai"></el-option> <el-option label="区域二" value="beijing"></el-option> </el-select> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="dialogStudentVisible = false">取 消</el-button> <el-button type="primary" @click="dialogStudentVisible = false">确 定</el-button> </div> </el-dialog> <!-- 弹出框 end --> </div> </template> <script> export default { data() { return { dialogStudentVisible: true, //学生弹出框控制变量 student: { } } }, } </script> <style> </style>填充表单
<template> <div> <!-- 添加按钮 --> <el-button type="primary" @click="dialogStudentVisible = true">添加</el-button> <!-- 弹出框 start --> <el-dialog title="添加学生" :visible.sync="dialogStudentVisible"> <el-form :model="student" label-width="80px"> <el-form-item label="班级名称"> <el-select v-model="student.cid" placeholder="请选择班级"> <el-option label="区域一" value="shanghai"></el-option> </el-select> </el-form-item> <el-form-item label="姓名"> <el-input v-model="student.sname"></el-input> </el-form-item> <el-form-item label="年龄"> <el-input v-model="student.age"></el-input> </el-form-item> <el-form-item label="生日"> <el-date-picker v-model="student.birthday" value-format="yyyy-MM-dd" type="date" placeholder="请选择学生生日"> </el-date-picker> </el-form-item> <el-form-item label="性别"> <el-radio-group v-model="student.gender"> <el-radio :label="1">男</el-radio> <el-radio :label="0">女</el-radio> </el-radio-group> </el-form-item> </el-form> {{student}} <div slot="footer" class="dialog-footer"> <el-button @click="dialogStudentVisible = false">取 消</el-button> <el-button type="primary" @click="dialogStudentVisible = false">确 定</el-button> </div> </el-dialog> <!-- 弹出框 end --> </div> </template> <script> export default { data() { return { dialogStudentVisible: true, //学生弹出框控制变量 student: { } } }, } </script> <style> </style>展示班级列表
<template> <div> <!-- 添加按钮 --> <el-button type="primary" @click="openStudentDialog">添加</el-button> <!-- 弹出框 start --> <el-dialog title="添加学生" :visible.sync="dialogStudentVisible"> <el-form :model="student" label-width="80px"> <el-form-item label="班级名称"> <el-select v-model="student.cid" placeholder="请选择班级"> <el-option v-for="(classes,index) in classesList" :key="index" :label="classes.cname" :value="classes.cid"></el-option> </el-select> </el-form-item> <el-form-item label="姓名"> <el-input v-model="student.sname"></el-input> </el-form-item> <el-form-item label="年龄"> <el-input v-model="student.age"></el-input> </el-form-item> <el-form-item label="生日"> <el-date-picker v-model="student.birthday" value-format="yyyy-MM-dd" type="date" placeholder="请选择学生生日"> </el-date-picker> </el-form-item> <el-form-item label="性别"> <el-radio-group v-model="student.gender"> <el-radio :label="1">男</el-radio> <el-radio :label="0">女</el-radio> </el-radio-group> </el-form-item> </el-form> {{student}} <div slot="footer" class="dialog-footer"> <el-button @click="dialogStudentVisible = false">取 消</el-button> <el-button type="primary" @click="dialogStudentVisible = false">确 定</el-button> </div> </el-dialog> <!-- 弹出框 end --> </div> </template> <script> export default { data() { return { dialogStudentVisible: false, //学生弹出框控制变量 student: { }, classesList: [], //班级 } }, methods: { openStudentDialog() { // 查询所有的班级 this.findAllClasses() // 控制变量 this.dialogStudentVisible = true }, async findAllClasses() { // ajax let { data: baseResult } = await this.$axios.get('/classes-service/classes') // 处理 if(baseResult.code == 20000) { this.classesList = baseResult.data } else { this.$message.error(baseResult.message) } } }, } </script> <style> </style>3)后端实现:基本添加
编写service
编写controller
编写service
接口
实现类
@Service @Transactional public class TbStudentServiceImpl extends ServiceImpl<TbStudentMapper, TbStudent> implements TbStudentService { @Override public boolean addStudent(TbStudent tbStudent) { // 保存学生基本信息 int result = baseMapper.insert(tbStudent); return result == 1; } }编写controller
package com.czxy.student.controller; import com.czxy.domain.TbStudent; import com.czxy.student.service.TbStudentService; import com.czxy.vo.BaseResult; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.util.List; /** * @author 桐叔 * @email liangtong@itcast.cn * @description */ @RestController @RequestMapping("/student") public class TbStudentController { @Resource private TbStudentService tbStudentService; @PostMapping public BaseResult save(@RequestBody TbStudent tbStudent) { // 添加 boolean result = tbStudentService.addStudent(tbStudent); // 提示 if(result) { return BaseResult.ok("添加成功"); } return BaseResult.error("添加失败"); } } 4)前端实现:基本添加
<template> <div> <!-- 添加按钮 --> <el-button type="primary" @click="openStudentDialog">添加</el-button> <!-- 弹出框 start --> <el-dialog title="添加学生" :visible.sync="dialogStudentVisible"> <el-form :model="student" label-width="80px"> <el-form-item label="班级名称"> <el-select v-model="student.cid" placeholder="请选择班级"> <el-option v-for="(classes,index) in classesList" :key="index" :label="classes.cname" :value="classes.cid"></el-option> </el-select> </el-form-item> <el-form-item label="姓名"> <el-input v-model="student.sname"></el-input> </el-form-item> <el-form-item label="年龄"> <el-input v-model="student.age"></el-input> </el-form-item> <el-form-item label="生日"> <el-date-picker v-model="student.birthday" value-format="yyyy-MM-dd" type="date" placeholder="请选择学生生日"> </el-date-picker> </el-form-item> <el-form-item label="性别"> <el-radio-group v-model="student.gender"> <el-radio :label="1">男</el-radio> <el-radio :label="0">女</el-radio> </el-radio-group> </el-form-item> </el-form> {{student}} <div slot="footer" class="dialog-footer"> <el-button @click="dialogStudentVisible = false">取 消</el-button> <el-button type="primary" @click="addStudent">确 定</el-button> </div> </el-dialog> <!-- 弹出框 end --> </div> </template> <script> export default { data() { return { dialogStudentVisible: false, //学生弹出框控制变量 student: { }, classesList: [], //班级 } }, methods: { openStudentDialog() { // 查询所有的班级 this.findAllClasses() // 控制变量 this.dialogStudentVisible = true }, async findAllClasses() { // ajax let { data: baseResult } = await this.$axios.get('/classes-service/classes') // 处理 if(baseResult.code == 20000) { this.classesList = baseResult.data } else { this.$message.error(baseResult.message) } }, async addStudent() { let {data:baseResult} = await this.$axios.post('/student-service/student', this.student) if(baseResult.code == 20000) { //成功 this.$message.success(baseResult.message) //刷新列表 this.conditionStudent() //关闭弹出框 this.dialogStudentVisible = false } else { // 失败 this.$message.error(baseResult.message) } }, conditionStudent() { } }, } </script> <style> </style> 4.5.2 级联城市1)后端实现
@GetMapping("/{parentId}") public BaseResult findAllByParentId(@PathVariable("parentId") String parentId) { //1 根据父id查询所有城市 QueryWrapper<TbCity> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("parent_id", parentId); List<TbCity> list = tbCityService.list(queryWrapper); //2 返回结果 return BaseResult.ok("查询成功", list); }
2)前端实现
异步加载
完善添加
异步加载
<el-form-item label="选择城市"> <el-cascader v-model="student.cids" :props="cityProps"></el-cascader> </el-form-item> cityProps: { //城市级联菜单的属性设置 lazy: true, //开启 async lazyLoad (node, resolve) { //加载数据 // 1. 如果 node.root == true 表示第一次加载,也就是省 let parentId = null if(node.root == true) { parentId = "0" } else { // 其他 - node.value可以获得当前节点的id的值,例如:省的id parentId = node.value } console.info(node) // 2. ajax 查询所有的城市 let { data:baseResult } = await _vue.$axios.get(`/student-service/city/${parentId}`) // 处理查询结果,如果是县,表示是叶子 baseResult.data.forEach(city=>{ city.leaf = node.level >=2 }) // 通过调用resolve将子节点数据返回,通知组件数据加载完成 resolve(baseResult.data); }, label: 'cityName', value: 'cid' },
完善添加
<template> <div> <!-- 添加按钮 --> <el-button type="primary" @click="openStudentDialog">添加</el-button> <!-- 弹出框 start --> <el-dialog title="添加学生" :visible.sync="dialogStudentVisible"> <el-form :model="student" label-width="80px"> <el-form-item label="班级名称"> <el-select v-model="student.cid" placeholder="请选择班级"> <el-option v-for="(classes,index) in classesList" :key="index" :label="classes.cname" :value="classes.cid"></el-option> </el-select> </el-form-item> <el-form-item label="选择城市"> <el-cascader v-model="student.cids" :props="cityProps"></el-cascader> </el-form-item> <el-form-item label="姓名"> <el-input v-model="student.sname"></el-input> </el-form-item> <el-form-item label="年龄"> <el-input v-model="student.age"></el-input> </el-form-item> <el-form-item label="生日"> <el-date-picker v-model="student.birthday" value-format="yyyy-MM-dd" type="date" placeholder="请选择学生生日"> </el-date-picker> </el-form-item> <el-form-item label="性别"> <el-radio-group v-model="student.gender"> <el-radio :label="1">男</el-radio> <el-radio :label="0">女</el-radio> </el-radio-group> </el-form-item> </el-form> {{student}} <div slot="footer" class="dialog-footer"> <el-button @click="dialogStudentVisible = false">取 消</el-button> <el-button type="primary" @click="addStudent">确 定</el-button> </div> </el-dialog> <!-- 弹出框 end --> </div> </template> <script> let _vue = null export default { data() { _vue = this //缓存vue对象 return { dialogStudentVisible: false, //学生弹出框控制变量 student: { cids: [] }, classesList: [], //班级 cityProps: { //城市级联菜单的属性设置 lazy: true, //开启 async lazyLoad (node, resolve) { //加载数据 // 1. 如果 node.root == true 表示第一次加载,也就是省 let parentId = null if(node.root == true) { parentId = "0" } else { // 其他 - node.value可以获得当前节点的id的值,例如:省的id parentId = node.value } console.info(node) // 2. ajax 查询所有的城市 let { data:baseResult } = await _vue.$axios.get(`/student-service/city/${parentId}`) // 处理查询结果,如果是县,表示是叶子 baseResult.data.forEach(city=>{ city.leaf = node.level >=2 }) // 通过调用resolve将子节点数据返回,通知组件数据加载完成 resolve(baseResult.data); }, label: 'cityName', value: 'cid' }, } }, methods: { openStudentDialog() { // 查询所有的班级 this.findAllClasses() // 控制变量 this.dialogStudentVisible = true }, async findAllClasses() { // ajax let { data: baseResult } = await this.$axios.get('/classes-service/classes') // 处理 if(baseResult.code == 20000) { this.classesList = baseResult.data } else { this.$message.error(baseResult.message) } }, async addStudent() { // 处理所选城市数据 数组转字符串, [1,2,3] --> 1,2,3 this.student.cityIds = this.student.cids.join(',') // 发送ajax let {data:baseResult} = await this.$axios.post('/student-service/student', this.student) if(baseResult.code == 20000) { //成功 this.$message.success(baseResult.message) //刷新列表 this.conditionStudent() //关闭弹出框 this.dialogStudentVisible = false } else { // 失败 this.$message.error(baseResult.message) } }, conditionStudent() { } }, } </script> <style> </style> 4.5.2 选课
1)查询所有课程:后端实现
package com.czxy.course.controller; import com.czxy.course.service.TbCourseService; import com.czxy.domain.TbCourse; import com.czxy.vo.BaseResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.List; /** * @author 桐叔 * @email liangtong@itcast.cn * @description */ @RestController @RequestMapping("/course") public class TbCourseController { @Resource private TbCourseService tbCourseService; @GetMapping public BaseResult findAll() { List<TbCourse> list = tbCourseService.list(); return BaseResult.ok("查询成功",list); } }
2)显示所有课程:前端实现
<template> <div> <!-- 添加按钮 --> <el-button type="primary" @click="openStudentDialog">添加</el-button> <!-- 弹出框 start --> <el-dialog title="添加学生" :visible.sync="dialogStudentVisible"> <el-form :model="student" label-width="80px"> <el-form-item label="班级名称"> <el-select v-model="student.cid" placeholder="请选择班级"> <el-option v-for="(classes,index) in classesList" :key="index" :label="classes.cname" :value="classes.cid"></el-option> </el-select> </el-form-item> <el-form-item label="选择城市"> <el-cascader v-model="student.cids" :props="cityProps"></el-cascader> </el-form-item> <el-form-item label="姓名"> <el-input v-model="student.sname"></el-input> </el-form-item> <el-form-item label="年龄"> <el-input v-model="student.age"></el-input> </el-form-item> <el-form-item label="生日"> <el-date-picker v-model="student.birthday" value-format="yyyy-MM-dd" type="date" placeholder="请选择学生生日"> </el-date-picker> </el-form-item> <el-form-item label="性别"> <el-radio-group v-model="student.gender"> <el-radio :label="1">男</el-radio> <el-radio :label="0">女</el-radio> </el-radio-group> </el-form-item> <el-form-item label="选课"> <el-checkbox-group v-model="student.courseIds"> <el-checkbox v-for="(course,index) in courseList" :key="index" :label="course.cid">{{course.cname}}</el-checkbox> </el-checkbox-group> </el-form-item> </el-form> {{student}} <div slot="footer" class="dialog-footer"> <el-button @click="dialogStudentVisible = false">取 消</el-button> <el-button type="primary" @click="addStudent">确 定</el-button> </div> </el-dialog> <!-- 弹出框 end --> </div> </template> <script> let _vue = null export default { data() { _vue = this //缓存vue对象 return { dialogStudentVisible: false, //学生弹出框控制变量 student: { cids: [], courseIds: [] }, classesList: [], //班级列表 cityProps: { //城市级联菜单的属性设置 lazy: true, //开启 async lazyLoad (node, resolve) { //加载数据 // 1. 如果 node.root == true 表示第一次加载,也就是省 let parentId = null if(node.root == true) { parentId = "0" } else { // 其他 - node.value可以获得当前节点的id的值,例如:省的id parentId = node.value } console.info(node) // 2. ajax 查询所有的城市 let { data:baseResult } = await _vue.$axios.get(`/student-service/city/${parentId}`) // 处理查询结果,如果是县,表示是叶子 baseResult.data.forEach(city=>{ city.leaf = node.level >=2 }) // 通过调用resolve将子节点数据返回,通知组件数据加载完成 resolve(baseResult.data); }, label: 'cityName', value: 'cid' }, courseList: [], //课程列表 } }, methods: { openStudentDialog() { // 查询所有的班级 this.findAllClasses() // 查询所有的选课 this.findAllCourse() // 控制变量 this.dialogStudentVisible = true }, async findAllClasses() { // ajax let { data: baseResult } = await this.$axios.get('/classes-service/classes') // 处理 if(baseResult.code == 20000) { this.classesList = baseResult.data } else { this.$message.error(baseResult.message) } }, async addStudent() { // 处理所选城市数据 数组转字符串, [1,2,3] --> 1,2,3 this.student.cityIds = this.student.cids.join(',') // 发送ajax let {data:baseResult} = await this.$axios.post('/student-service/student', this.student) if(baseResult.code == 20000) { //成功 this.$message.success(baseResult.message) //刷新列表 this.conditionStudent() //关闭弹出框 this.dialogStudentVisible = false } else { // 失败 this.$message.error(baseResult.message) } }, async findAllCourse() { let {data:baseResult} = await this.$axios.get('/course-service/course') this.courseList = baseResult.data }, conditionStudent() { } }, } </script> <style> </style>
3)中间表:后端基本+添加
package com.czxy.course.controller; import com.czxy.course.service.TbStudentCourseService; import com.czxy.domain.TbStudentCourse; import com.czxy.vo.BaseResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * @author 桐叔 * @email liangtong@itcast.cn * @description */ @RestController @RequestMapping("/stduentCourse") public class TbStudentCourseController { @Resource private TbStudentCourseService tbStudentCourseService; @PostMapping public BaseResult save(@RequestBody TbStudentCourse tbStudentCourse) { // 保存 boolean result = tbStudentCourseService.save(tbStudentCourse); if(result) { return BaseResult.ok("添加成功"); } return BaseResult.error("添加失败"); } }
4)修改后端添加(feign远程调用)
在学生服务中
定义选课feign
修改学生添加,保存选课信息
定义选课feign
package com.czxy.student.feign; import com.czxy.domain.TbStudentCourse; import com.czxy.vo.BaseResult; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; /** * @author 桐叔 * @email liangtong@itcast.cn * @description */ //@FeignClient(value = "服务名", path = "controller类路径") @FeignClient(value = "course-service", path = "/stduentCourse") public interface StudentCourseFeign { @PostMapping public BaseResult save(@RequestBody TbStudentCourse tbStudentCourse); }
版本1:修改学生添加,保存选课信息
package com.czxy.student.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.czxy.domain.TbStudent; import com.czxy.domain.TbStudentCourse; import com.czxy.student.feign.StudentCourseFeign; import com.czxy.student.mapper.TbStudentMapper; import com.czxy.student.service.TbStudentService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; /** * @author 桐叔 * @email liangtong@itcast.cn * @description */ @Service @Transactional public class TbStudentServiceImpl extends ServiceImpl<TbStudentMapper, TbStudent> implements TbStudentService { @Resource private StudentCourseFeign studentCourseFeign; @Override public boolean addStudent(TbStudent tbStudent) { // 保存学生基本信息 int result = baseMapper.insert(tbStudent); // 保存选课信息 for (Integer courseId : tbStudent.getCourseIds()) { // 准备选课对象 TbStudentCourse tbStudentCourse = new TbStudentCourse(); tbStudentCourse.setCid(courseId); tbStudentCourse.setSid(tbStudent.getSid()); // TODO 保存 ,没有处理异常 studentCourseFeign.save(tbStudentCourse); } return result == 1; } }
版本2:修改学生添加,保存选课信息(课程有错,学生回滚)
package com.czxy.student.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.czxy.domain.TbStudent; import com.czxy.domain.TbStudentCourse; import com.czxy.student.feign.StudentCourseFeign; import com.czxy.student.mapper.TbStudentMapper; import com.czxy.student.service.TbStudentService; import com.czxy.vo.BaseResult; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; /** * @author 桐叔 * @email liangtong@itcast.cn * @description */ @Service @Transactional public class TbStudentServiceImpl extends ServiceImpl<TbStudentMapper, TbStudent> implements TbStudentService { @Resource private StudentCourseFeign studentCourseFeign; @Override public boolean addStudent(TbStudent tbStudent) { // 保存学生基本信息 int result = baseMapper.insert(tbStudent); // 记录操作结果 boolean flag = result == 1; // 保存选课信息 for (Integer courseId : tbStudent.getCourseIds()) { // 准备选课对象 TbStudentCourse tbStudentCourse = new TbStudentCourse(); tbStudentCourse.setCid(courseId); tbStudentCourse.setSid(tbStudent.getSid()); // 保存课程 正确 code == 20000 , 错误 code == 0 BaseResult baseResult = studentCourseFeign.save(tbStudentCourse); flag &= (baseResult.getCode() == 20000); } // 如果false抛异常 ,解决:课程有错,学生回滚 if(flag == false) { throw new RuntimeException("添加失败"); } //TODO 未解决问题:部分课程已经提交,需使用“分布式事务”解决 return flag; } } 4. 6 修改 4.6.1 分析4.5.2 查询详情
1) 分析
基本数据:只要查询就可以回显(姓名、性别等)
关联数据:
班级:有数据直接回显
城市:需要一个==数组==,存放一组城市数据,目前有的数据为字符串1,2,3。将在前端处理数据。
选课:也需要一个数组,存在一组课程id数据。将在后端处理数据。
2)后端
编写controller
编写service
编写controller
@GetMapping("/{studentId}") public BaseResult findById(@PathVariable("studentId") Integer studentId) { // 查询 TbStudent tbStudent = tbStudentService.findById(studentId); // 返回 return BaseResult.ok("查询详情", tbStudent); }
编写service
TbStudent findById(Integer studentId); @Override public TbStudent findById(Integer studentId) { //1 学生详情 TbStudent tbStudent = baseMapper.selectById(studentId); //2 选课信息 BaseResult<List<TbCourse>> tbCourseListBaseResult = courseFeign.findAllByStudentId(studentId); List<TbCourse> tbCourseList = tbCourseListBaseResult.getData(); tbStudent.setCourseList(tbCourseList); tbStudent.setCourseIds(tbCourseList.stream().map(course->course.getCid()).collect(Collectors.toList())); //3 返回 return tbStudent; }
3)前端
openStudentDialog(studentId) { // 查询所有的班级 this.findAllClasses() // 查询所有的选课 this.findAllCourse() // 修改前查询详情 if(studentId) { this.findStudentById(studentId) this.studentDialogTitle = '修改学生' } else { this.studentDialogTitle = '添加学生' } // 控制变量 this.dialogStudentVisible = true }, async findStudentById(sid) { let {data:baseResult} = await this.$axios.get(`/student-service/student/${sid}`) // 获得结果 this.student = baseResult.data // 处理数据:城市 "cityIds": "320000,321300,321322" --> cid : ["320000","321300","321322"] this.student.cids = this.student.cityIds.split(',') }
4)级联菜单回显
element ui的级联菜单先渲染组件时,如果有数据,将触发自动查询功能。
步骤:
定义变量,隐藏级联菜单
查询数据成功后,显示菜单
提交数据后,隐藏菜单
【待完善】,点击x后,没有处理
定义变量,隐藏级联菜单
查询数据成功后,显示菜单
提交数据后,隐藏菜单
【待完善】,点击x后,没有处理
4.5.3 更新添加功能1)学生服务
优化controller
修改service
编写feign
修改service
编写feign
2)课程服务
@DeleteMapping("/delete/student/{sid}") public BaseResult deleteAllBySid(@PathVariable("sid") Integer sid) { // 1 设置条件 QueryWrapper<TbStudentCourse> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("s_id", sid); // 2 删除 boolean remove = tbStudentCourseService.remove(queryWrapper); // 3 返回 if(remove) { return BaseResult.ok("删除成功"); } return BaseResult.error("删除失败"); }
3)修复前端
性别回显
添加级联显示