element vue springboot
1. 添加一级章节按钮
1.1. 后端 - 章节表insertChapter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package com.online.edu.eduservice.controller;
import com.online.edu.common.R; import com.online.edu.eduservice.entity.EduChapter; import com.online.edu.eduservice.entity.query.QueryChapter; import com.online.edu.eduservice.service.EduChapterService; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController @RequestMapping("/eduservice/edu-chapter") @CrossOrigin public class EduChapterController {
@Autowired EduChapterService eduChapterService;
@ApiOperation("添加章节") @PostMapping("/insertChapter") public R insert(@ApiParam("章节") @RequestBody EduChapter eduChapter){ boolean flag = eduChapterService.save(eduChapter); if(flag){ return R.ok().message("章节添加成功").data("chapterId",eduChapter.getId()); }else{ return R.error().message("章节添加失败"); } } }
|
1.2. 前端 - chapter.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import request from '@/utils/request'
const base = '/eduservice/edu-chapter'
export default{
insertChapter(chapterObj){ return request({ url : `${base}/insertChapter`, method:'post', data:chapterObj }) } }
|
1.3. 前端 - el-dialog标签-实现增加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
| <el-button type="primary" round @click="insertChapter"> 添加一级分类 </el-button>
<!-- 章节表 --> <el-dialog title="添加章节" :visible.sync="chapterFormVisible" :show-close="false"> <el-form :model="chapterObj"> <el-form-item label="章节名称" :label-width="formLabelWidth"> <el-input v-model="chapterObj.title" autocomplete="off"></el-input> </el-form-item> <el-form-item label="章节排序" :label-width="formLabelWidth"> <el-tooltip content="数值越高排在越先" placement="right" effect="light"> <el-input-number :min="0" v-model="chapterObj.sort" /> </el-tooltip> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="cancel">取 消</el-button> <el-button type="primary" @click="saveOrUpdateChapter">确 定</el-button> </div> </el-dialog>
<script> import chapter from '@/api/edu/chapter' data(){ return{ chapterObj:{ id:'', courseId:'', title: '', sort: 0 }, formLabelWidth: '120px', chapterFormVisible:false, courseId: '', } }, created(){ //路径中的id值 this.courseId = this.$route.params.id this.chapterObj.courseId=this.courseId }, methods{ //添加一级章节 insertChapter(){ this.chapterFormVisible=true }, //章节取消按钮 cancel(){ this.chapterFormVisible = false this.chapterObj.title='' this.chapterObj.sort =0 }, //通过章节id判断进行的操作 saveOrUpdateChapter(){ if(this.chapterObj.id){
}else{ //插入 chapter.insertChapter(this.chapterObj).then(response=>{ this.dialogFormVisible=false this.chapterObj.id=response.data.chapterId this.$message({ type:'success', message:response.message }) }).catch(response=>{ this.$message({ type: 'error', message: response.message }) }) } } }
</script>
|
1.4. 最终实现

2. 显示章节
2.1. 后端 - QueryChapter类
得到所有的章节和所属章节下的课时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.online.edu.eduservice.entity.query;
import com.online.edu.eduservice.entity.EduVideo; import io.swagger.annotations.ApiParam; import lombok.Data;
import java.util.ArrayList; import java.util.List;
@Data public class QueryChapter {
@ApiParam("章节ID") private String id;
@ApiParam("章节名称") private String title;
@ApiParam("章节下视频") private List<EduVideo> children = new ArrayList<>(); }
|
2.2. 后端 - selectAllInfo(courseId)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| @Override public List<QueryChapter> selectAllInfo(String id){
List<QueryChapter> chapters = new ArrayList<>();
QueryWrapper<EduChapter> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("course_id",id); queryWrapper.orderByDesc("sort"); List<EduChapter> eduChapters = baseMapper.selectList(queryWrapper);
for(EduChapter chapter:eduChapters){ QueryChapter queryChapter = new QueryChapter(); queryChapter.setId(chapter.getId()); queryChapter.setTitle(chapter.getTitle());
QueryWrapper<EduVideo> videoWrapper = new QueryWrapper<>(); videoWrapper.eq("chapter_id",chapter.getId()); videoWrapper.orderByDesc("sort"); List<EduVideo> videos = eduVideoService.selectAll(videoWrapper);
queryChapter.setChildren(videos);
chapters.add(queryChapter); }
return chapters; }
|
2.3. 后端 - deleteWrapper - EduVideo
1 2 3 4 5
| @Override public List<EduVideo> selectAll(QueryWrapper<EduVideo> queryWrapper){
return baseMapper.selectList(queryWrapper); }
|
2.4. 后端 - 控制器方法-listChapter
1 2 3 4 5 6 7
| @ApiOperation("根据课程ID查询所有章节") @GetMapping("/listChapter/{id}") public R listChapter(@ApiParam("课程ID")@PathVariable String id){ List<QueryChapter> lists = eduChapterService.selectAllInfo(id);
return R.ok().data("chapterList",lists); }
|
2.5. 前端 - chapter.js
1 2 3 4 5 6
| getChapterList(id){ return request({ url: `${base}/listChapter/`+id, method:'get' }) }
|
2.6. 前端 - chapter.vue
v-for 显示所有章节及课时
样式来自 https://element.eleme.cn/#/zh-CN/component/button
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| <ul class="chapterList"> <li v-for="chapter in chapterList" :key="chapter.id"> <p> <strong>{{chapter.title}}</strong> <span class="acts"> <el-tooltip content="添加课时" placement="top" effect="light"> <el-button type="success" icon="el-icon-plus" circle v-on:click="insertVideo(chapter.id)"></el-button> </el-tooltip> <el-tooltip content="修改信息" placement="top" effect="light"> <el-button type="primary" icon="el-icon-edit" circle @click="updateChapter(chapter.id)"></el-button> </el-tooltip> <el-tooltip content="删除章节及课时" placement="top" effect="light"> <el-button type="danger" icon="el-icon-delete" circle @click="deleteChapter(chapter.id)"></el-button> </el-tooltip> </span> </p> <ul class="chapterList videoList"> <li v-for="video in chapter.children" :key="video.id"> <p>{{video.title}} <span class="acts"> <el-button type="text" @click="updateVideo(video.id)">编辑</el-button> <el-button type="text" @click="deleteVideo(video.id)">删除</el-button> </span> </p> </li> </ul> </li> </ul>
<script> import chapter from '@/api/edu/chapter' data(){ return{ coursId:'', chapterList:[] } } created(){ this.getChapterList(this.courseId) }, methods{ //所有章节及课时 getChapterList(id){ chapter.getChapterList(id).then(response=>{ this.chapterList=response.data.chapterList }).catch() } } </script>
<!-- 样式 --> <style scoped> .chapterList{ position: relative; list-style: none; padding-right: 0px; margin-right: 100px; } .chapterList li{ position: relative; }
.chanpterList p{ padding-right: 50px; } .chapterList strong{ font-size: 17px; } .chapterList .acts { float: right; font-size: 14px; justify-content: space-between; }
.videoList{ padding-left: 0px; margin-left:50px ; }
.videoList p{ padding-left: 10px; border-bottom: 2px dotted #DDD; } </style>
|
2.7. 最终样式

3. 更新章节
3.1. 后端-controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @ApiOperation("根据ID查询章节") @GetMapping("/selectChapterById/{id}") public R selectById(@ApiParam("章节ID") @PathVariable String id){ EduChapter chapter = eduChapterService.getById(id); if (chapter!=null){ return R.ok().data("items",chapter); }else{ return R.error().message("查询失败"); } }
@ApiOperation("根据ID更新章节") @PostMapping("/updateChapter") public R update(@ApiParam("章节信息") @RequestBody EduChapter eduChapter){ boolean flag = eduChapterService.updateById(eduChapter); if (flag){ return R.ok().message("更新成功"); }else{ return R.error().message("更新失败"); } }
|
3.2. 前端-chapter.js
1 2 3 4 5 6 7 8 9 10 11 12 13
| updateChapter(chapterObj){ return request({ url : `${base}/updateChapter`, method:'post', data:chapterObj }) }, getChapterById(id){ return request({ url: `${base}/selectChapterById/`+id, method:'get' }) }
|
3.3. 前端-chapter.vue
点击添加按钮显示输入框 , 按钮点击传递章节id v-on:click="updateChapter(chapter.id)
点击确认按钮提交数据 @click="saveOrUpdateChapter"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <script> saveOrUpdateChapter(){ //如果是插入,章节id是不存在的 if(this.chapterObj.id){ chapter.updateChapter(this.chapterObj).then(response=>{ this.$message({ type:'success', message: response.message }) //刷新页面 this.getChapterList(this.courseId) //关闭框 this.chapterFormVisible=false }).catch(response=>{ this.$message({ type:'error', message:response.message }) }) } }, //更新章节按钮 updateChapter(id){ this.chapterObj.id = id this.chapterFormVisible=true //根据id得到数据 this.getchapterById(id) }, //根据id查询章节 getchapterById(id){ chapter.getChapterById(id).then(response=>{ this.chapterObj=response.data.items }).catch(response=>{ this.$message({ type:'error', message:response.message }) })
}
</script>
|
4. 删除章节
4.1. 后端-删除章节及课时
1 2 3 4 5 6 7 8 9 10
| @Override public boolean deleteById(String id) { QueryWrapper<EduVideo> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("chapter_id",id); eduVideoService.deleteWrapper(queryWrapper);
int flag = baseMapper.deleteById(id); return flag>0; }
|
4.2. 后端-controller
1 2 3 4 5 6 7 8 9 10
| @ApiOperation("删除章节") @DeleteMapping("/deleteChapterById/{id}") public R deleteById(@ApiParam("章节ID")@PathVariable String id) { boolean flag = eduChapterService.deleteById(id); if (flag) { return R.ok().message("章节修改成功"); } else { return R.error().message("章节修改失败"); } }
|
4.3. 前端- chapter.js
1 2 3 4 5 6
| deleteChapterById(id){ return request({ url:`${base}/deleteChapterById/`+id, method:'delete' }) }
|
4.4. 前端 - chapter.vue
点击编辑按钮@click="updateChapter(chapter.id)"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <script> //删除章节按钮 deleteChapter(id){ this.$confirm('此操作将永久删除章节及课时信息, 是否继续?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }) .then(() => { //return 后then方法才会执行 return chapter.deleteChapterById(id) }) .then(response => { //重新所有分类 this.getChapterList(this.courseId) this.$message({ type: 'success', message: response.message })
}).catch(response => { //判断错误操作类别 //console.log(response) if(response === 'cancel'){ this.$message({ type: 'info', message: '已取消删除' }) } else{ this.$message({ type: 'error', message: response.message }) } }) } </script>
|
5. 添加课时
5.1. 后端-EduVideoServiceImpl
1 2 3 4 5
| @Override public boolean insert(EduVideo video) { int flag=baseMapper.insert(video); return flag>0; }
|
5.2. 后端-controller
1 2 3 4 5 6 7 8 9 10
| @ApiOperation("删除视频信息") @DeleteMapping("/deleteVideoById/{id}") public R deleteVideo(@ApiParam("视频ID")@PathVariable String id) { boolean flag = eduVideoService.deleteById(id); if (flag) { return R.ok().message("删除视频成功"); } else { return R.error().message("删除视频失败"); } }
|
5.3. 前端-video.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import request from '@/utils/request'
const base ='/eduservice/edu-video'
export default{ insertVideo(videoObj){ return request({ url:`${base}/insertVideo`, method:'post', data:videoObj }) } }
|
5.4. 前端-chapter.vue
videoFormVisible 控制表单的显示 , 当点击添加按钮,this.videoFormVisible=true 使表单显示
@click="saveOrUpdateVideo"
控制添加or更新课时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| <!-- 课时表 --> <el-dialog title="添加课时" :visible.sync="videoFormVisible" :show-close="false"> <el-form :model="videoObj"> <el-form-item label="视频名称" :label-width="formLabelWidth"> <el-input v-model="videoObj.title"/> </el-form-item> <el-form-item label="视频时长" :label-width="formLabelWidth"> <el-tooltip content="按0.02秒递增" placement="right" effect="light"> <el-input-number v-model="videoObj.duration" :min="0" :precision="2" controls-position="right" step="0.02" placeholder="00:00"/> 秒 </el-tooltip> </el-form-item> <el-form-item label="视频排名" :label-width="formLabelWidth"> <el-tooltip content="数值越高排在越先" placement="right" effect="light"> <el-input-number v-model="videoObj.sort" :min="0" /> </el-tooltip> </el-form-item> <el-form-item label="是否可以试听" :label-width="formLabelWidth"> <el-radio v-model="videoObj.isFree" :label="false"> 可以</el-radio> <el-radio v-model="videoObj.isFree" :label="true"> 不可以</el-radio> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="videoCancel">取 消</el-button> <el-button type="primary" @click="saveOrUpdateVideo">确 定</el-button> </div> </el-dialog>
<script> import video from '@/api/edu/video' data(){ return{ videoObj:{ id:'', chapterId: "", courseId: "", duration: '', isFree: false, size: 0, sort: 0, title: "" }, videoFormVisible:false, } } //根据章节ID添加课时按钮 insertVideo(id){ this.videoFormVisible=true this.videoObj.chapterId=id }, //视频确定按钮事件判断 saveOrUpdateVideo(){ if(this.videoObj.id){
}else{ console.log(this.videoObj) video.insertVideo(this.videoObj).then(response=>{ this.videoFormVisible=false this.videoObj.id=response.data.videoId this.$message(({ type:'success', message:response.message })) this.getChapterList(this.courseId) }).catch(response=>{ this.$message(({ type:'error', message:response.message }))
}) } //console.log(this.videoObj) } </script>
|
5.5. 最终显示

6. 更新课时
6.1. 后端-controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @ApiOperation("根据ID查询视频信息") @GetMapping("/selectVideoById/{id}") public R selectVideoById(@ApiParam("视频ID")@PathVariable String id) { EduVideo video = eduVideoService.getById(id); if (video != null) { return R.ok().data("items",video); } else { return R.error().message("查询视频失败"); } }
@ApiOperation("更新视频信息") @PostMapping("/updateVideo") public R updateVideo(@ApiParam("视频信息")@RequestBody EduVideo video) { boolean flag = eduVideoService.updateById(video); if (flag) { return R.ok().message("更新视频成功"); } else { return R.error().message("更新视频失败"); } }
|
6.2. 前端 - video.js
1 2 3 4 5 6 7 8 9 10 11 12 13
| selectVideoById(id){ return request({ url:`${base}/selectVideoById/`+id, method:'get' }) }, updateVideo(videoObj){ return request({ url:`${base}/updateVideo`, method:'post', data:videoObj }) }
|
6.3. 前端 -chapter.vue
点击编辑 @click="updateVideo(video.id)"
显示表单,查询id对应video表数据
@click="saveOrUpdateVideo"
确定按钮
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| <script> updateVideo(id){ this.videoFormVisible=true this.videoObj.id = id this.getVideoById(id) }, getVideoById(id){ video.selectVideoById(id).then(response=>{ this.videoObj=response.data.items }) } //视频确定按钮事件判断 saveOrUpdateVideo(){ if(this.videoObj.id){ video.updateVideo(this.videoObj).then(response=>{ this.$message({ type:'success', message: response.message }) this.getChapterList(this.courseId) this.videoFormVisible=false }).catch(response=>{ this.$message({ type:'error', message:response.message }) }) }else{ } }
</script>
|
7. 删除课时
7.1. 后端-controller
采用basemapper中的方法
1 2 3 4 5 6 7 8 9 10
| @ApiOperation("删除视频信息") @DeleteMapping("/deleteVideoById/{id}") public R deleteVideo(@ApiParam("视频ID")@PathVariable String id) { boolean flag = eduVideoService.deleteById(id); if (flag) { return R.ok().message("删除视频成功"); } else { return R.error().message("删除视频失败"); } }
|
7.2. 后端-EduVideoServiceImpl
1 2 3 4
| public boolean deleteById(String id){ int flag = baseMapper.deleteById(id); return flag>0; }
|
7.3. 前端 - video.js
1 2 3 4 5 6
| deleteVideoById(id){ return request({ url: `${base}/deleteVideoById/`+id, method:'delete' }) }
|
7.4. 前端 - chapter.vue
根据 @click="deleteVideo(video.id)" 删除
操作,传递的是课时的id,根据课时id删除课时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <script> deleteVideo(id){ this.$confirm('此操作将永久删除课时信息, 是否继续?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }) .then(() => { //return 后then方法才会执行 return video.deleteVideoById(id) }) .then(response => { //重新所有分类 this.getChapterList(this.courseId) this.$message({ type: 'success', message: response.message })
}).catch(response => { //判断错误操作类别 //console.log(response) if(response === 'cancel'){ this.$message({ type: 'info', message: '已取消删除' }) } else{ this.$message({ type: 'error', message: response.message }) } })
}
</script>
|
7.5. 最终显示

8. 传递课程id参数
@click="primary" 返回到上一步
@click="next" 保存并下一步
1 2 3 4 5 6 7 8 9 10 11 12 13
| <script> next(){ this.$router.push({ path:"/course/publish/"+this.courseId }) }, primary(){ this.$router.push({ path:'/course/info/'+this.courseId }) }
</script>
|