目录
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 [email protected] * @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 [email protected]
* @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 [email protected]
* @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 [email protected]
* @description
*/
@Mapper
public interface TbCourseMapper extends BaseMapper<TbCourse> {
/**
* 查询指定学生的选课
* @author 桐叔
* @email [email protected]
* @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 [email protected]
* @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 [email protected] * @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 [email protected] * @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 [email protected]
* @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 [email protected]
* @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 [email protected]
* @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 [email protected]
* @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 [email protected] * @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 [email protected]
* @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 [email protected]
* @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 [email protected]
* @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)修复前端
- 性别回显
- 添加级联显示