一、课程添加分析

发布流程图

数据表对应

课程相关表的关系

二、课程管理–添加课程后端

1、使用代码生成器生成相关课程相关的代码

CodeGenerator类中生成

2、细节问题

(1)创建vo实体类用户表单数据封装

(2)把表单提交过来的数据添加到数据库
向两张表添加数据:课程表 和 课程表述表

(3)把讲师和分类使用下拉列表显示
课程分类 做成二级联动

3、创建vo实体类

@ApiModel(value = "Course查询对象", description = "课程管理查询对象封装")
@Data
public class CourseInfoVo {@ApiModelProperty(value = "课程ID")private String id;@ApiModelProperty(value = "课程讲师ID")private String teacherId;@ApiModelProperty(value = "二级分类ID")private String subjectId;@ApiModelProperty(value = "一级分类ID")private String subjectParentId;@ApiModelProperty(value = "课程标题")private String title;@ApiModelProperty(value = "课程销售价格,设置为0则可免费观看")private BigDecimal price;@ApiModelProperty(value = "总课时")private Integer lessonNum;@ApiModelProperty(value = "课程封面图片路径")private String cover;@ApiModelProperty(value = "课程简介")private String description;
}

4、编写Controller类

@Api(description = "课程管理")
@RestController
@RequestMapping("/eduservice/course")
@CrossOrigin
public class EduCourseController {@Autowiredprivate EduCourseService courseService;@ApiOperation(value = "添加课程基本信息")@PostMapping("addCourseInfo")public R addCourseInfo(@RequestBody CourseInfoVo courseInfoVo){//返回添加之后课程id,为了后面添加大纲使用String id = courseService.saveCourseInfo(courseInfoVo);return R.ok().data("courseId",id);}
}

5、编写Service类

@Service
public class EduCourseServiceImpl extends ServiceImpl<EduCourseMapper, EduCourse> implements EduCourseService {@Autowiredprivate EduCourseDescriptionService courseDescriptionService;@Overridepublic String saveCourseInfo(CourseInfoVo courseInfoVo) {//1 向课程表添加课程基本信息EduCourse eduCourse =new EduCourse();//将courseInfoVo对象转化成eduCourseBeanUtils.copyProperties(courseInfoVo,eduCourse);int insert = baseMapper.insert(eduCourse);if (insert == 0) {throw new GuliException(20001,"添加课程信息失败");}//获取添加之后课程idString cid = eduCourse.getId();//2 向课程简介表添加课程简介EduCourseDescription courseDescription =new EduCourseDescription();courseDescription.setDescription(courseInfoVo.getDescription());//设置描述id 就是课程id,记得将实体类中的填充模式改为INPUTcourseDescription.setId(cid);courseDescriptionService.save(courseDescription);//这里存在一个问题就是:课程和描述是一对一关系,添加之后,id应该是相同的return cid;}
}

注意问题:

(1)课程和描述是一对一关系,添加之后,id应该是相同的。
EduCourseDescription类中id 属性@TableId(value = "id", type = IdType.INPUT)

(2)EduCourse类和EduCourseDescription类中的时间需要自动填充。@TableField(fill = FieldFill.INSERT)

6、启动Swagger测试

三、课程管理–添加课程前端–填写课程基本信息

效果图

1、添加课程管理路由

在api/router/index.js 中

{path: '/course',component: Layout,redirect: '/course/list',name: '课程管理',meta: { title: '课程管理', icon: 'example' },children: [{path: 'list',name: '课程列表',component: () => import('@/views/edu/course/list'),meta: { title: '课程列表', icon: 'table' }},{path: 'info',name: '添加课程',component: () => import('@/views/edu/course/info'),meta: { title: '添加课程', icon: 'tree' }},{path: 'info/:id',name: 'EduCourseInfoEdit',component: () => import('@/views/edu/course/info'),meta: { title: '编辑课程基本信息', noCache: true },hidden: true},{path: 'chapter/:id',name: 'EduCourseChapterEdit',component: () => import('@/views/edu/course/chapter'),meta: { title: '编辑课程大纲', noCache: true },hidden: true},{path: 'publish/:id',name: 'EduCoursePublishEdit',component: () => import('@/views/edu/course/publish'),meta: { title: '发布课程', noCache: true },hidden: true}]
},

注意:这里有三个模块添加隐藏域。

2、添加Vue组件

3、整合步骤条组件实现页面跳转

参考:http://element-cn.eleme.io/#/zh-CN/component/steps

(1)课程页面信息

info.vue

<template><div class="app-container"><h2 style="text-align: center;">发布新课程</h2><el-steps :active="1" process-status="wait" align-center style="margin-bottom: 40px;"><el-step title="填写课程基本信息"/><el-step title="创建课程大纲"/><el-step title="最终发布"/></el-steps><el-form label-width="120px"><el-form-item><el-button :disabled="saveBtnDisabled" type="primary" @click="saveOrUpdate">保存并下一步</el-button></el-form-item></el-form></div>
</template>
<script>
export default {data() {return {saveBtnDisabled:false   // 保存按钮是否禁用}   },created() {},methods:{saveOrUpdate() {//跳转到第二步this.$router.push({path:'/course/chapter/1'})}}
}
</script>

(2)课程大纲页面

chapter.vue

<template><div class="app-container"><h2 style="text-align: center;">发布新课程</h2><el-steps :active="2" process-status="wait" align-center style="margin-bottom: 40px;"><el-step title="填写课程基本信息"/><el-step title="创建课程大纲"/><el-step title="提交审核"/></el-steps><el-form label-width="120px"><el-form-item><el-button @click="previous">上一步</el-button><el-button :disabled="saveBtnDisabled" type="primary" @click="next">下一步</el-button></el-form-item></el-form></div>
</template>
<script>
export default {data() {return {saveBtnDisabled: false // 保存按钮是否禁用}},created() {},methods: {previous() {this.$router.push({ path: '/edu/course/info/1' })},next() {//跳转到第二步this.$router.push({ path: '/edu/course/publish/1' })}}
}
</script>

(3)课程发布页面

publish.vue

<template><div class="app-container"><h2 style="text-align: center;">发布新课程</h2><el-steps :active="3" process-status="wait" align-center style="margin-bottom: 40px;"><el-step title="填写课程基本信息"/><el-step title="创建课程大纲"/><el-step title="最终发布"/></el-steps><el-form label-width="120px"><el-form-item><el-button @click="previous">返回修改</el-button><el-button :disabled="saveBtnDisabled" type="primary" @click="publish">发布课程</el-button></el-form-item></el-form></div>
</template><script>export default {data() {return {saveBtnDisabled: false // 保存按钮是否禁用}},created() {console.log('publish created')},methods: {previous() {console.log('previous')this.$router.push({ path: '/course/chapter/1' })},publish() {console.log('publish')this.$router.push({ path: '/course/list' })}}
}
</script>

4、定义api

在api/edu/course.js 中

import request from '@/utils/request'export default{//1 添加课程信息addCourseInfo(courseInfo){return request({url: `/eduservice/course/addCourseInfo`,method: 'post',data:courseInfo})}
}

5、组件模板

在 src/views/edu/info.vue 中

<el-form label-width="120px"><el-form-item label="课程标题"><el-input v-model="courseInfo.title" placeholder=" 示例:机器学习项目课:从基础到搭建项目视频课程。专业名称注意大小写"/></el-form-item><!-- 所属分类 TODO --><!-- 课程讲师 TODO --><el-form-item label="总课时"><el-input-number :min="0" v-model="courseInfo.lessonNum" controls-position="right" placeholder="请填写课程的总课时数"/></el-form-item><!-- 课程简介 TODO --><el-form-item label="课程简介"><el-input v-model="courseInfo.description" placeholder=" "/></el-form-item><!-- 课程封面 TODO --><el-form-item label="课程价格"><el-input-number :min="0" v-model="courseInfo.price" controls-position="right" placeholder="免费课程请设置为0元"/> 元</el-form-item><el-form-item><el-button :disabled="saveBtnDisabled" type="primary" @click="saveOrUpdate">保存并下一步</el-button></el-form-item>
</el-form>

6、组件js

<script>
import course from '@/api/edu/course'
export default {data() {return {saveBtnDisabled:false,courseInfo:{title: '',subjectId: '',//二级分类idsubjectParentId:'',//一级分类idteacherId: '',lessonNum: 0,description: '',cover: '',price: 0},}   },created() {},methods:{saveOrUpdate() {course.addCourseInfo(this.courseInfo).then(response => {//提示this.$message({type: 'success',message: '添加课程信息成功!'});//跳转到第二步this.$router.push({path:'/course/chapter/'+response.data.courseId})})}}
}
</script>

7、讲师下拉列表

(1)组件模板

<!-- 课程讲师 -->
<el-form-item label="课程讲师"><el-selectv-model="courseInfo.teacherId"placeholder="请选择"><el-optionv-for="teacher in teacherList":key="teacher.id":label="teacher.name":value="teacher.id"/></el-select></el-form-item>

(2)定义api

api/edu/teacher.js

//2 查询所有讲师
getListTeacher() {return request({url: `/eduservice/teacher/findAll`,method: 'get'})
}

组件中引入teacher api

import teacher from '@/api/edu/teacher'

(3)组件脚本

定义data

teacherList: [] // 讲师列表

表单初始化时获取讲师列表

created() {//初始化所有讲师this.getListTeacher()
},
methods:{// .....//查询所有的讲师getListTeacher() {course.getListTeacher().then(response => {this.teacherList = response.data.items})},
}

8、课程分类多级联动

1、效果图

2、获取一级分类

(1)组件数据定义

定义data中

subjectOneList:[],//一级分类
subjectTwoList:[]//二级分类

(2)组件模板

<el-form-item label="课程分类"><el-selectv-model="courseInfo.subjectParentId"placeholder="一级分类"><el-optionv-for="subject in subjectOneList":key="subject.id":label="subject.title":value="subject.id"/></el-select></el-form-item>

(3)组件脚本

表单初始化时获取一级分类嵌套列表,引入subject api

import subject from '@/api/edu/subject'

定义方法

created() {//初始化一级分类this.getOneSubject()
},
method:{//....//查询所有的一级分类getOneSubject() {subject.getSubjectList().then(response => {this.subjectOneList = response.data.list})},
}

3、显示二级分类

1、组件模板

<!-- 二级分类 -->
<el-select v-model="courseInfo.subjectId" placeholder="二级分类"><el-optionv-for="subject in subjectTwoList":key="subject.id":label="subject.title":value="subject.id"/>
</el-select>

2、注册change事件

在一级分类的<el-select>组件中注册change事件

<el-select @change="subjectLevelOneChanged" ....>

3、定义change事件方法

//点击某个一级分类,触发change,显示对应二级分类
subjectLevelOneChanged(value) {//value就是一级分类id值//遍历所有的分类,包含一级和二级for(var i=0;i<this.subjectOneList.length;i++) {//每个一级分类var oneSubject = this.subjectOneList[i]//判断:所有一级分类id 和 点击一级分类id是否一样if(value === oneSubject.id) {//从一级分类获取里面所有的二级分类this.subjectTwoList = oneSubject.children//把二级分类id值清空this.courseInfo.subjectId = ''}}
},

9、课程封面

参考 http://element-cn.eleme.io/#/zh-CN/component/upload 用户头像上传

1、上传默认封面

创建文件夹cover,上传默认的课程封面

2、定义默认封面

courseInfo:{//....cover: '/static/01.jpg',//cover: process.env.OSS_PATH + '/cover/default.gif',//....
},

3、定义data数据

BASE_API: process.env.BASE_API // 接口API地址

4、组件模板

在info.vue中添加上传组件模板

<!-- 课程封面-->
<el-form-item label="课程封面"><!-- show-file-list:不显示文件上传信息on-success:上传成功before-upload:上传之前--><el-upload:show-file-list="false":on-success="handleAvatarSuccess":before-upload="beforeAvatarUpload":action="BASE_API+'/eduoss/fileoss'"class="avatar-uploader"><img :src="courseInfo.cover"></el-upload></el-form-item>

5、结果回调

//上传封面成功调用的方法
handleAvatarSuccess(res, file) {this.courseInfo.cover = res.data.url
},
//上传之前调用的方法
beforeAvatarUpload(file) {const isJPG = file.type === 'image/jpeg'const isLt2M = file.size / 1024 / 1024 < 2if (!isJPG) {this.$message.error('上传头像图片只能是 JPG 格式!')}if (!isLt2M) {this.$message.error('上传头像图片大小不能超过 2MB!')}return isJPG && isLt2M
},

10、富文本编辑器

1、Tinymce可视化编辑器

参考:
https://panjiachen.gitee.io/vue-element-admin/#/components/tinymce
https://panjiachen.gitee.io/vue-element-admin/#/example/create

2、组件初始化

(1)复制脚本库

将脚本库复制到项目的static目录下(在vue-element-admin-master的static路径下)

(2)配置html变量

在 guli-admin/build/webpack.dev.conf.js 中添加配置
使在html页面中可是使用这里定义的BASE_URL变量

new HtmlWebpackPlugin({//.....templateParameters: {BASE_URL: config.dev.assetsPublicPath + config.dev.assetsSubDirectory}
})

(3)引入js脚本

在guli-admin/index.html 中引入js脚本

<script src=<%= BASE_URL %>/tinymce4.7.5/tinymce.min.js></script>
<script src=<%= BASE_URL %>/tinymce4.7.5/langs/zh_CN.js></script>

3、组件引入

(1)引入组件

课程信息组件中引入 Tinymce

import Tinymce from '@/components/Tinymce'export default {components: { Tinymce },......
}

4、组件模板

<!-- 课程简介-->
<el-form-item label="课程简介"><tinymce :height="300" v-model="courseInfo.description"/>
</el-form-item>

5、组件样式

在info.vue文件的最后添加如下代码,调整上传图片按钮的高度

<style scoped>
.tinymce-container {line-height: 29px;
}
</style>

四、课程大纲列表 – 后端开发

效果图:

1、创建两个实体类,章节和小节

ChapterVo 类

@ApiModel(value = "章节封装类",description = "章节封装类")
@Data
public class ChapterVo {private String id;private String title;//表示小节private List<VideoVo> children = new ArrayList<>();
}

VideoVo 类

@ApiModel(value = "小节封装类",description = "小节封装类")
@Data
public class VideoVo {private String id;private String title;}

2、编写Controller

@Api(description = "章节")
@RestController
@RequestMapping("/eduservice/chapter")
@CrossOrigin
public class EduChapterController {@Autowiredprivate EduChapterService chapterService;//根据课程id进行查询@ApiOperation(value = "课程大纲列表")@GetMapping("/getChapterVideo/{courseId}")public R getChapterVideo(@PathVariable String courseId){List<ChapterVo> list = chapterService.getChapterVideoByCourseId(courseId);return R.ok().data("allChapterVideo",list);}}

3、编写Service

@Service
public class EduChapterServiceImpl extends ServiceImpl<EduChapterMapper, EduChapter> implements EduChapterService {@Autowiredprivate EduVideoService videoService;@Overridepublic List<ChapterVo> getChapterVideoByCourseId(String courseId) {//1 根据课程id查询课程里面的章节QueryWrapper<EduChapter> wrapperChapter = new QueryWrapper<>();wrapperChapter.eq("course_id",courseId);List<EduChapter> eduChapterList = baseMapper.selectList(wrapperChapter);//2 根据课程id查询课程里面的小节QueryWrapper<EduVideo> wrapperVideo = new QueryWrapper<>();wrapperVideo.eq("course_id",courseId);List<EduVideo> eduVideoList = videoService.list(wrapperVideo);//创建list集合,用于最终封装的集合List<ChapterVo> finallList = new ArrayList<>();//3 遍历查询章节list集合进行封装for (int i = 0; i < eduChapterList.size(); i++) {//得到每个章节EduChapter eduChapter = eduChapterList.get(i);//将edChapter对象复制到ChapterVo里面ChapterVo chapterVo = new ChapterVo();BeanUtils.copyProperties(eduChapter,chapterVo);//把chapterVo放到最终的list集合中finallList.add(chapterVo);//创建集合,用于封装章节中的小节List<VideoVo> videoList = new ArrayList<>();//4 遍历查询小节list集合进行封装for (int m = 0; m < eduVideoList.size(); m++) {//得到每个小节EduVideo eduVideo = eduVideoList.get(m);//判断:小节里面chapterid和章节里面的id是否一样if (eduVideo.getChapterId().equals(eduChapter.getId())) {//进行封装VideoVo videoVo = new VideoVo();BeanUtils.copyProperties(eduVideo,videoVo);//放在小节的集合中videoList.add(videoVo);}}//把封装之后的小节list集合,放到章节对象里面chapterVo.setChildren(videoList);}return finallList;}
}

4、使用Swagger进行测试

五、课程大纲列表 – 前端开发

1、定义api

import request from '@/utils/request'export default{//1 查询所有讲师getChapterVideo(courseId) {return request({url: `/eduservice/chapter/getChapterVideo/${courseId}`,method: 'get'})}
}

2、组件脚本

import chapter from '@/api/edu/chapter'

3、页面调用

在views/edu/course/chapter.vue 中

created() {//获取路由中的id值if(this.$route.params && this.$route.params.id) {this.courseId = this.$route.params.id//调用 根据课程id查询章节和小节this.getChapterVideo()}
},
methods:{//根据课程id查询章节和小节getChapterVideo() {chapter.getChapterVideo(this.courseId).then(response =>{this.chapterVideoList = response.data.allChapterVideo})},//......
}

4、添加页面

<!-- 章节 -->
<ul class="chanpterList"><liv-for="chapter in chapterVideoList":key="chapter.id"><p>{{ chapter.title }}<span class="acts"><el-button style="" type="text" @click="openVideo(chapter.id)">添加小节</el-button><el-button style="" type="text" @click="openEditChatper(chapter.id)">编辑</el-button><el-button type="text" @click="removeChapter(chapter.id)">删除</el-button></span></p><!-- 视频 --><ul class="chanpterList videoList"><liv-for="video in chapter.children":key="video.id"><p>{{ video.title }}<span class="acts"><el-button style="" type="text" @click="openEditVideo(video.id)">编辑</el-button><el-button type="text" @click="removeVideo(video.id)">删除</el-button></span></p></li></ul></li>
</ul>
<div><el-button @click="previous">上一步</el-button><el-button :disabled="saveBtnDisabled" type="primary" @click="next">下一步</el-button>
</div>

5、页面样式

<style scoped>
.chanpterList{position: relative;list-style: none;margin: 0;padding: 0;
}
.chanpterList li{position: relative;
}
.chanpterList p{float: left;font-size: 20px;margin: 10px 0;padding: 10px;height: 70px;line-height: 50px;width: 100%;border: 1px solid #DDD;
}
.chanpterList .acts {float: right;font-size: 14px;
}.videoList{padding-left: 50px;
}
.videoList p{float: left;font-size: 14px;margin: 10px 0;padding: 10px;height: 50px;line-height: 30px;width: 100%;border: 1px dotted #DDD;
}
</style>

六、修改课程基本信息

流程是:点击上一步的时候,回到第一步页面,把课程基本信息数据回显。修改数据点击保存实现更新数据效果。

1、后端Controller类

@ApiOperation(value = "根据课程查询课程基本信息")
@GetMapping("getCourseInfo/{courseId}")
public R getCourseInfo(@PathVariable String courseId) {CourseInfoVo courseInfoVo = courseService.getCourseInfo(courseId);return R.ok().data("courseInfoVo",courseInfoVo);
}@ApiOperation(value = "根据id修改课程信息")
@PostMapping("updateCourseInfo")
public R updateCourseInfo(@RequestBody CourseInfoVo courseInfoVo) {courseService.updateCourseInfo(courseInfoVo);return R.ok();
}

2、后端Service类

@Override
public CourseInfoVo getCourseInfo(String courseId) {//1 查询课程表EduCourse eduCourse = baseMapper.selectById(courseId);CourseInfoVo courseInfoVo =new CourseInfoVo();BeanUtils.copyProperties(eduCourse,courseInfoVo);//2 查询描述表EduCourseDescription courseDescription = courseDescriptionService.getById(courseId);courseInfoVo.setDescription(courseDescription.getDescription());return courseInfoVo;
}@Override
public void updateCourseInfo(CourseInfoVo courseInfoVo) {//1 修改课程表EduCourse eduCourse = new EduCourse();BeanUtils.copyProperties(courseInfoVo,eduCourse);int update = baseMapper.updateById(eduCourse);if(update ==0) {throw new GuliException(20001,"修改课程信息失败");}//2 修改描述表EduCourseDescription description = new EduCourseDescription();description.setId(courseInfoVo.getId());description.setDescription(courseInfoVo.getDescription());courseDescriptionService.updateById(description);
}

3、定义前端两个接口

在api/edu/course.js中

//根据课程id查询课程基本信息
getCourseInfoId(id) {return request({url: `/eduservice/course/getCourseInfo/${id}`,method: 'get'})
},
//修改课程信息
updateCourseInfo(courseInfo) {return request({url: `/eduservice/course/updateCourseInfo`,method: 'post',data: courseInfo})
}

4、修改/edu/course/chapter.vue中路径


5、在info.vue页面实现数据回显

获取路由中课程id,调用根据id查询的接口,数据显示。

created() {//获取路由id值,回显数据if(this.$route.params && this.$route.params.id) {this.courseId = this.$route.params.id//调用根据id查询课程的方法this.getInfo()}else{//初始化所有讲师this.getListTeacher()//初始化一级分类this.getOneSubject()}
},
methods:{//根据课程id查询,回显数据getInfo(){course.getCourseInfoId(this.courseId).then(response => {//在courseInfo课程基本信息,包含一级分类id和二级分类idthis.courseInfo = response.data.courseInfoVo//下面的代码加在这里。})}//....
}

注意:这里我们在运行的时候发现二级分类下拉框没有数据,因为subjectTwoList数组为空,所以只显示查询出来的id。

解决办法就是:因为在courseInfo课程基本信息,包含一级分类id和二级分类id,所以先查询所有一级分类,然后遍历所有一级分类,比较当前courseInfo里面一级分类id 和 所有的一级分类id,如果相同则遍历一级分类下面的二级分类。

//1 查询所有的分类,包括一级和二级
subject.getSubjectList().then(response => {//2 获取所有一级分类this.subjectOneList = response.data.list//3 把所有的一级分类数组进行遍历,比较当前courseInfo里面一级分类id和所有的一级分类idfor(var i=0;i<this.subjectOneList.length;i++){//获取每一个一级分类var oneSubject = this.subjectOneList[i]//比较当前courseInfo里面一级分类id和所有的一级分类idif(this.courseInfo.subjectParentId == oneSubject.id){//获取一级分类所有的二级分类this.subjectTwoList = oneSubject.children}}})//初始化所有讲师this.getListTeacher()

此时启动服务测试,页面数据即可回显。

6、更新数据 js

这里我们对之前写的加以修改,把添加和修改方法单独提出来写。

methods:{//.....//添加课程saveCourse() {course.addCourseInfo(this.courseInfo).then(response => {//提示this.$message({type: 'success',message: '添加课程信息成功!'});//跳转到第二步this.$router.push({path:'/course/chapter/'+response.data.courseId})})},//修改课程updateCourse() {course.updateCourseInfo(this.courseInfo).then(response => {//提示this.$message({type: 'success',message: '修改课程信息成功!'});//跳转到第二步this.$router.push({path:'/course/chapter/'+this.courseId})})},saveOrUpdate() {//判断添加还是修改if(!this.courseInfo.id) {//添加this.saveCourse()}else{//更新this.updateCourse()}}
}

7、启动服务测试即可

七、课程章节添加、修改、删除 --后端开发

1、编写Controller类

@ApiOperation(value = "添加章节")
@PostMapping("addChapter")
public R addChapter(@RequestBody EduChapter eduChapter){chapterService.save(eduChapter);return R.ok();
}@ApiOperation(value = "根据章节id查询")
@GetMapping("getChapterInfo/{chapterId}")
public R getChapterInfo(@PathVariable String chapterId) {EduChapter eduChapter = chapterService.getById(chapterId);return R.ok().data("chapter",eduChapter);
}@ApiOperation(value = "修改章节")
@PostMapping("updateChapter")
public R updateChapter(@RequestBody EduChapter eduChapter){chapterService.updateById(eduChapter);return R.ok();
}@ApiOperation(value = "删除章节")
@DeleteMapping("deleteChapter/{chapterId}")
public R deleteChapter(@PathVariable String chapterId){boolean flag = chapterService.deleteChapter(chapterId);if (flag){return R.ok();}else {return R.error();}
}

注意:在实体类中的时间加上@TableField(fill = FieldFill.INSERT)注解

2、编写Service类

@Override
public boolean deleteChapter(String chapterId) {//根据chapterid章节id查询小节表,如果查询到数据,不进行删除QueryWrapper<EduVideo> wrapper = new QueryWrapper<>();wrapper.eq("chapter_id",chapterId);int count = videoService.count(wrapper);if (count > 0) {//查询出小节,不进行删除throw new GuliException(20001,"不能删除");}else{//没有查询数据,进行删除//删除章节int result = baseMapper.deleteById(chapterId);//成功 1>0  0>0return result>0;}}

注意:这里我们要对删除进行判断

如果章节里面没有小节,直接删除。
如果章节里面有小节,如何删除?
第一种:删除章节的时候,把章节里面所有小节都删除。
第二种:如果删除章节下面有小节,不让进行删除。

八、课程章节添加、修改、删除 --前端开发

1、定义api接口

//2 添加章节
addChapter(chapter) {return request({url: `/eduservice/chapter/addChapter`,method: 'post',data:chapter})
},
//3 修改章节
updateChapter(chapter) {return request({url: `/eduservice/chapter/updateChapter`,method: 'post',data:chapter})
},
//4 删除章节
deleteChapter(chapterId) {return request({url: `/eduservice/chapter/deleteChapter/${chapterId}`,method: 'delete'})
},
//5 根据id进行查询
getChapterInfo(chapterId) {return request({url: `/eduservice/chapter/getChapterInfo/${chapterId}`,method: 'get'})
}

2、添加章节按钮

<el-button type="text" @click="openChapterDialog()">添加章节</el-button>

3、添加弹出框表单

<!-- 添加和修改章节表单 -->
<el-dialog :visible.sync="dialogChapterFormVisible" title="添加章节"><el-form :model="chapter" label-width="120px"><el-form-item label="章节标题"><el-input v-model="chapter.title"/></el-form-item><el-form-item label="章节排序"><el-input-number v-model="chapter.sort" :min="0" controls-position="right"/></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="dialogChapterFormVisible = false">取 消</el-button><el-button type="primary" @click="saveOrUpdate">确 定</el-button></div>
</el-dialog>

4、编写添加的js

data() {return {//....courseId:'',//课程idchapterVideoList:[],chapter:{//封装章节数据title: '',sort: 0},dialogChapterFormVisible:false//章节弹框}
},
methods:{//弹出添加章节页面openChapterDialog() {//弹框this.dialogChapterFormVisible = true//表单数据清空this.chapter.title = ''this.chapter.sort = ''},//添加章节addChapter() {//设置课程id到chapter对象里面this.chapter.courseId = this.courseIdchapter.addChapter(this.chapter).then(response => {//关闭弹框this.dialogChapterFormVisible = false//提示信息this.$message({type: 'success',message: '添加章节成功!'});//刷新页面this.getChapterVideo()})},saveOrUpdate() {this.addChapter()},//......
}

5、编写修改的js

//修改章节的方法
updateChapter() {chapter.updateChapter(this.chapter).then(response => {//关闭弹框this.dialogChapterFormVisible = false//提示信息this.$message({type: 'success',message: '修改章节成功!'});//刷新页面this.getChapterVideo()})
},
saveOrUpdate() {if(!this.chapter.id){this.addChapter()}else{this.updateChapter()}
},
//.....

6、编写删除的js

//删除章节
removeChapter(chapterId) {this.$confirm('此操作将永久删除章节, 是否继续?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {//点击确定,删除成功//调用删除方法chapter.deleteChapter(chapterId).then(response => {//删除成功//提示信息this.$message({type: 'success',message: '删除成功!'});//刷新页面this.getChapterVideo()}) })//点击取消,删除失败
},

注意:启动服务运行出现403错误。

出现问题原因:第一种是Controller类中跨域注解没有写@CrossOrigin。第二种是访问路径写的不对。

九、课程小节添加、修改、删除 --前端开发

1、编写Controller类

@Api(description = "小节")
@RestController
@RequestMapping("/eduservice/video")
@CrossOrigin
public class EduVideoController {@Autowiredprivate EduVideoService videoService;@ApiOperation(value = "添加小节")@PostMapping("addVideo")public R addVideo(@RequestBody EduVideo eduVideo){videoService.save(eduVideo);return R.ok();}@ApiOperation(value = "根据小节id查询")@GetMapping("getVideoInfo/{id}")public R getVideoInfo(@PathVariable String id){EduVideo eduVideo = videoService.getById(id);return R.ok().data("eduVideo",eduVideo);}@ApiOperation(value = "修改小节")@PostMapping("updateVideo")public R updateVideo(@RequestBody EduVideo eduVideo){videoService.updateById(eduVideo);return R.ok();}//TODO 后面这个方法需要完善:删除小节时候,同时要把里面的视频删除@ApiOperation(value = "删除小节")@DeleteMapping("deleteVideo/{id}")public R deleteVideo(@PathVariable String id){videoService.removeById(id);return R.ok();}
}

注意:在实体类中的时间加上@TableField(fill = FieldFill.INSERT)注解

十、课程小节添加、修改、删除 --前端开发

1、定义api接口

import request from '@/utils/request'export default{//添加小节addVideo(video) {return request({url: `/eduservice/video/addVideo`,method: 'post',data:video})},//根据id进行查询getVideoInfo(id) {return request({url: `/eduservice/video/getVideoInfo/${id}`,method: 'get'})},//修改小节updateVideo(video) {return request({url: `/eduservice/video/updateVideo`,method: 'post',data:video})},//删除小节deleteVideo(id) {return request({url: `/eduservice/video/deleteVideo/${id}`,method: 'delete'})}
}

2、添加修改页面

<!-- 添加和修改课时表单 -->
<el-dialog :visible.sync="dialogVideoFormVisible" title="添加课时">
<el-form :model="video" label-width="120px"><el-form-item label="课时标题"><el-input v-model="video.title"/></el-form-item><el-form-item label="课时排序"><el-input-number v-model="video.sort" :min="0" controls-position="right"/></el-form-item><el-form-item label="是否免费"><el-radio-group v-model="video.free"><el-radio :label="true">免费</el-radio><el-radio :label="false">默认</el-radio></el-radio-group></el-form-item><el-form-item label="上传视频"><!-- TODO --></el-form-item>
</el-form>
<div slot="footer" class="dialog-footer"><el-button @click="dialogVideoFormVisible = false">取 消</el-button><el-button :disabled="saveVideoBtnDisabled" type="primary" @click="saveOrUpdateVideo">确 定</el-button>
</div>
</el-dialog>

3、引入包

import video from '@/api/edu/video'

4、初始数据定义

data() {return {//....video: {//封装小节数据title: '',sort: 0,free: 0,videoSourceId: ''},dialogVideoFormVisible:false//小节弹框}
},

5、编写增删改查的js

//===========================小节操作=========================================
//删除小节
removeVideo(id) {this.$confirm('此操作将永久删除小节, 是否继续?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {//点击确定,删除成功//调用删除方法video.deleteVideo(id).then(response => {//删除成功//提示信息this.$message({type: 'success',message: '删除小节成功!'});//刷新页面this.getChapterVideo()}) })//点击取消,删除失败
},
//添加小节弹框
openVideo(chapterId) {//弹框this.dialogVideoFormVisible = true//设置章节idthis.video.chapterId = chapterId//表单数据清空this.video.title = ''this.video.sort = ''this.video.free = ''this.video.videoSourceId = ''
},
//添加小节
addVideo() {//设置课程idthis.video.courseId = this.courseIdvideo.addVideo(this.video).then(response => {//关闭弹框this.dialogVideoFormVisible = false//提示信息this.$message({type: 'success',message: '添加小节成功!'});//刷新页面this.getChapterVideo()})
},
//修改小节数据回显
openEditVideo(id) {//弹框this.dialogVideoFormVisible = true//回显数据video.getVideoInfo(id).then(response => {this.video = response.data.eduVideo})
},
//修改小节
updateVideo() {video.updateVideo(this.video).then(response => {//关闭弹框this.dialogVideoFormVisible = false//提示信息this.$message({type: 'success',message: '修改小节成功!'});//刷新页面this.getChapterVideo()})
},
saveOrUpdateVideo() {if(!this.video.id) {this.addVideo()}else{this.updateVideo() }
},

6、启动服务测试即可。


如果有收获!!! 希望老铁们来个三连,点赞、收藏、转发。
创作不易,别忘点个赞,可以让更多的人看到这篇文章,顺便鼓励我写出更好的博客

谷粒学院(十)课程管理模块 | 课程大纲列表 | 二级联动 | 富文本编辑器相关推荐

  1. 谷粒学院day5 讲师管理模块的前端实现

    day5 讲师管理模块的前端实现 目 录 day5 讲师管理模块的前端实现 1.登录功能改造 2.跨域问题 3.前端框架开发过程 4.讲师列表的前端实现 5.讲师删除功能的前端实现 6.讲师添加 7. ...

  2. 70-项目实战后续(课程管理模块)

    项目实战后续(课程管理模块) 项目介绍: 前面我们完成了初步的项目,接下来实现更高等级的作用(基于框架的项目编写) 页面原型展示: 技术选型: 前端技术选型: 后端技术选型: 项目开发环境 : 开发工 ...

  3. 拉勾教育后台管理系统(SSM)(课程管理模块开发)【学习笔记】

    文章目录 1.项目架构 1.1.项目介绍 1.2.技术选型 1.2.1.前端技术选型 1.2.2.后端技术选型 1.3.项目开发环境 2.Maven进阶使用(Maven聚合工程) 2.1.maven的 ...

  4. 06_04_任务一:拉勾教育后台管理系统[课程管理模块、图片上传、 BeanUtils封装实体类](SSM)

    拉勾教育后台管理系统(SSM) 1. 项目架构 1.1 项目介绍 ​ 拉勾教育后台管理系统,是提供给拉勾教育的相关业务人员使用的一个后台管理系统, 业务人员可以在 这个后台管理系统中,对课程信息.广告 ...

  5. 谷粒学院项目讲师管理(二)

    谷粒学院项目讲师管理 一.讲师列表 1.添加讲师列表路由 2.创建路由对应的vue页面 3.第三步在api文件夹创建teacher.js定义访问的接口地址 二.分页查询 三.条件查询 四.讲师添加 五 ...

  6. 【作业题】后台管理系统中的课程管理模块(后端篇)

    巩固web阶段知识,完成后台管理系统中的课程管理模块,该模块包含了添加课程.配置课程相关信息.管理课程章节等功能. 项目地址:课程管理模块 产品的原型图 课程管理 实现以下功能: 展示课程列表 根据课 ...

  7. 谷粒学苑 —— 6、课程管理:课程发布页面1 —— 添加课程信息

    目录 1.使用代码生成器生成代码 2.添加课程信息 2.1.后端代码 2.1.1.创建 VO 类用于封装表单信息 2.1.2.修改课程简介实体类的主键生成策略 2.1.3.Service 方法 2.1 ...

  8. luffcc项目-08-课程详情页、CKEditor富文本编辑器、课程详情页面、后台接口

    课程详情页 一.CKEditor富文本编辑器 富文本即具备丰富样式格式的文本.在运营后台,运营人员需要录入课程的相关描述,可以是包含了HTML语法格式的字符串.为了快速简单的让用户能够在页面中编辑带h ...

  9. vue管理后台项目中使用wangEditor富文本编辑器

    背景 公司需要做一个后台文章管理的模块,通过富文本编辑器编辑文章,在前端显示.调研了很久,决定使用wangEditor -- 轻量级 web 富文本编辑器,配置方便,使用简单.一些编辑器的说明. 开始 ...

最新文章

  1. 基于MEGA8的声音CLICK模块
  2. linux查看redis索引,linux的redis操作命令
  3. OC-@dynamic 关键字
  4. 是否可以限制蓝牙传输距离_技术文章—关于蓝牙传输范围的常见误解
  5. Notebook响应式扁平化后台UI框架模板
  6. SQL:查询重复出现记录
  7. Diccuz!NT的dll版本号控制技巧
  8. rdd 内生分组_04、常用RDD操作整理
  9. 用户工号转换成姓名加工号
  10. ORB-SLAM3从理论到代码实现(三):Optimizer全局优化
  11. Innosetup 多种安装 vc_redist 运行库方式
  12. 漫谈程序员系列:程序员到底是什么角色
  13. Switch函数的使用
  14. 剑指Offer 46.把数字翻译成字符串(Python)
  15. 西门子1200PLC模拟量测温案例1
  16. 《PyQT5软件开发 - 控件篇》第3章 单行文本框QLineEdit
  17. 单片机(STC系列8051内核单片机)
  18. 逆水寒 服务器维护要多久,逆水寒8月2日更新内容 逆水寒8月2日更新多久/维护时间几点好...
  19. python调用企业微信接口
  20. 高性能计算(HPC)

热门文章

  1. Java递归遍历JSON的N叉树数据结构
  2. android 编译PinyinIME输入法报错
  3. 物理实验——二次成像法测凸透镜焦距数据处理C++代码
  4. 【备战秋招系列-4】Java高频知识——并发、Spring、MySQL、redis
  5. 02java进阶03-异常、线程、同步、线程池、Lambda表达式、File类、递归
  6. 弹性系数系数在水文气象中的应用
  7. Unity密室逃脱-逃离房间游戏源码.,支持安卓+IOS双端 unity2021 C#语言开发
  8. 关于RTF提取图片和文字的方法。
  9. 微信关注 自动回复 关注消息 php
  10. 飞凌嵌入式全志A40i开发板试用体验之 称重系统-开发环境搭建(1)