一、先书写查询接口
1.创建两个实体类
@Data public class ChapterVO { private String id; private String title; private List<VideoVO> children=new ArrayList<VideoVO>(); }
@Data public class VideoVO { private String id; private String title; }
2.给实体类
EduChapter
加注解
3.书写controller方法
@RestController @CrossOrigin @RequestMapping("/eduservice/edu-chapter") public class EduChapterController { @Autowired EduChapterService chapterService; //根据课程id进行查询 @GetMapping("getChapterVideo/{courseId}") public R getChapterVideo(@PathVariable String courseId){ List<ChapterVO> list= chapterService.getChapterVideoBYcourseId(courseId); return R.OK().data("AllChapterVideo",list); } }
4.service
public interface EduChapterService extends IService<EduChapter> { List<ChapterVO> getChapterVideoBYcourseId(String courseId); }
5.serviceImpl
@Service public class EduChapterServiceImpl extends ServiceImpl<EduChapterMapper, EduChapter> implements EduChapterService { @Autowired EduVideoService eduVideoService; @Override public List<ChapterVO> getChapterVideoBYcourseId(String courseId) { //1根据id查询出课程里面所有的章节 QueryWrapper<EduChapter> wrapper=new QueryWrapper<>(); wrapper.eq("course_id",courseId); List<EduChapter> Chapterlist=baseMapper.selectList(wrapper); //2根据id查询出课程里面所有的小节 QueryWrapper<EduVideo> Videowrapper=new QueryWrapper<>(); Videowrapper.eq("course_id",courseId); List<EduVideo> eduVideolist=eduVideoService.list(Videowrapper); List<ChapterVO> finalList=new ArrayList<>(); //3便利查询章节list集合进行封装 for (int i = 0; i < Chapterlist.size() ; i++) { EduChapter eduChapter=Chapterlist.get(i); //eduChapter对象里的值复制到ChapterVO里 ChapterVO chapterVO=new ChapterVO(); BeanUtils.copyProperties(eduChapter,chapterVO); finalList.add(chapterVO); List<VideoVO> videoList = new ArrayList<>(); //4便利查询小节list集合进行封装 for (int m = 0; m < eduVideolist.size() ; m++) { EduVideo eduVideo=eduVideolist.get(m); //eduChapter对象里的值复制到ChapterVO里 //判断小节的chapter_id和章节的id是否一样 if(eduVideo.getChapterId().equals(eduChapter.getId())){ VideoVO videoVO=new VideoVO(); BeanUtils.copyProperties(eduVideo,videoVO); videoList.add(videoVO); } } chapterVO.setChildren(videoList); } return finalList; } }
测试:
二、前端
当前页面
<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:'/course/info/1'}) }, next() { //跳转到第二步 this.$router.push({path:'/course/publish/1'}) } } } </script>
添加js
页面
<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> <ul> <li v-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> <li v-for="video in chapter.children" :key="video.id"> <p> {{ video.title }} <span class="acts"> <el-button style type="text">编辑</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> <!-- 添加和修改章节表单 --> <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> <!-- 添加和修改课时表单 --> <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> </div> </template>
最终页面
js代码
data() { return { saveBtnDisabled: false, chapterVideoList: [], courseId: "", chapter:{ //封装章节数据 title: '', sort: 0 }, video: { title: '', sort: 0, free: 0, videoSourceId: '' }, dialogChapterFormVisible:false,//章节弹框 dialogVideoFormVisible:false //小节弹框 }; }, created() { //获取路由里的id值 if (this.$route.params && this.$route.params.id) { this.courseId = this.$route.params.id; } //根据课程id查询章节和小节 this.getChapterVideo(); }, methods: { //==============================小节操作==================================== //删除小节 removeVideo(id) { this.$confirm('此操作将删除小节, 是否继续?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { //点击确定,执行then方法 //调用删除的方法 video.deleteVideo(id) .then(response =>{//删除成功 //提示信息 this.$message({ type: 'success', message: '删除小节成功!' }); //刷新页面 this.getChapterVideo() }) }) //点击取消,执行catch方法 }, //添加小节弹框的方法 openVideo(chapterId) { //弹框 this.dialogVideoFormVisible = true //设置章节id this.video.chapterId = chapterId }, //添加小节 addVideo() { //设置课程id this.video.courseId = this.courseId video.addVideo(this.video) .then(response => { //关闭弹框 this.dialogVideoFormVisible = false //提示 this.$message({ type: 'success', message: '添加小节成功!' }); //刷新页面 this.getChapterVideo() }) }, saveOrUpdateVideo() { this.addVideo() }, //==============================章节操作==================================== //删除章节 removeChapter(chapterId) { this.$confirm('此操作将删除章节, 是否继续?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { //点击确定,执行then方法 //调用删除的方法 chapter.deleteChapter(chapterId) .then(response =>{//删除成功 //提示信息 this.$message({ type: 'success', message: '删除成功!' }); //刷新页面 this.getChapterVideo() }) }) //点击取消,执行catch方法 }, //修改章节弹框数据回显 openEditChatper(chapterId) { //弹框 this.dialogChapterFormVisible = true //调用接口 chapter.getChapter(chapterId) .then(response => { this.chapter = response.data.chapter }) }, //弹出添加章节页面 openChapterDialog() { //弹框 this.dialogChapterFormVisible = true //表单数据清空 this.chapter.title = '' this.chapter.sort = 0 }, //添加章节 addChapter() { //设置课程id到chapter对象里面 this.chapter.courseId = this.courseId chapter.addChapter(this.chapter) .then(response => { //关闭弹框 this.dialogChapterFormVisible = false //提示 this.$message({ type: 'success', message: '添加章节成功!' }); //刷新页面 this.getChapterVideo() }) }, //修改章节的方法 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() } }, //根据课程id查询章节和小节 getChapterVideo() { chapter.getAllChapterVideo(this.courseId) .then(response => { this.chapterVideoList = response.data.allChapterVideo }) }, getChapterVideo() { chapter.getChapterVideo(this.courseId).then(response => { this.chapterVideoList = response.data.AllChapterVideo; }); }, previous() { this.$router.push({ path: "/course/info/1" }); }, next() { //跳转到第二步 this.$router.push({ path: "/course/publish/1" }); } } }; </script> <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>