课程分类 添加课程分类 现在的感觉就是,写一两集后端代码,剩下的两三天都是前端,真的好麻烦呀前端,可能就是没有系统学过Vue吧。 慢慢来吧~ 步骤和5-6天的差不多,基本上也是调接口和写vue代码再配合element组件就可以基本实现
添加课程接口调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import request from '@/utils/request' export default { getSubjectList ( ){ return request ({ url : `/eduservice/subject/getAllSubject` , method : 'get' , }) } }
以excel的形式直接传文件添加课程
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 <template > <div class ="app-container" > <el-form label-width ="120px" > <el-form-item label ="信息描述" > <el-tag type ="info" > excel模版说明</el-tag > <el-tag > <i class ="el-icon-download" /> <a :href ="'/static/01.xlsx'" > 点击下载模版</a > </el-tag > </el-form-item > <el-form-item label ="选择Excel" > <el-upload ref ="upload" :auto-upload ="false" :on-success ="fileUploadSuccess" :on-error ="fileUploadError" :disabled ="importBtnDisabled" :limit ="1" :action ="BASE_API+'/eduservice/subject/addSubject'" name ="file" accept =".xls,.xlsx" > <el-button slot ="trigger" size ="small" type ="primary" > 选取文件</el-button > <el-button :loading ="loading" style ="margin-left: 10px;" size ="small" type ="success" @click ="submitUpload" > {{ fileUploadBtnText }}</el-button > </el-upload > </el-form-item > </el-form > </div > </template > <script > export default { data ( ){ return { BASE_API : process.env .BASE_API , fileUploadBtnText : '上传到服务器' , importBtnDisabled : false , loading : false } }, created ( ){ }, methods : { submitUpload ( ){ this .importBtnDisabled = true this .loading = true this .$refs .upload .submit () }, fileUploadSuccess (response ){ this .loading = false this .$message({ type : 'success' , message : '添加课程分类成功' }) this .$touter .push ({path :'/subject/list' }) }, fileUploadError ( ){ this .loading = false this .$message({ type : 'error' , message : '添加课程分类失败' }) } } } </script >
课程分类显示(前后端) 前端直接贴上来了,树形结构的样式也是模板,重点在后端如何实现树形数据的传输
前端 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 <template > <div class ="app-container" > <el-input v-model ="filterText" placeholder ="Filter keyword" style ="margin-bottom:30px;" /> <el-tree ref ="tree2" :data ="data2" :props ="defaultProps" :filter-node-method ="filterNode" class ="filter-tree" default-expand-all /> </div > </template > <script > import subject from '@/api/edu/subject.js' export default { data ( ) { return { filterText : '' , data2 : [{}], defaultProps : { children : 'children' , label : 'title' } } }, created ( ) { this .getAllSubjectList () }, watch : { filterText (val ) { this .$refs .tree2 .filter (val) } }, methods : { getAllSubjectList ( ) { subject.getSubjectList () .then (response => { this .data2 = response.data .list }) }, filterNode (value, data ) { if (!value) return true return data.title .toLowerCase ().indexOf (value.toLowerCase ()) !== -1 } } } </script >
后端 后端要想实现这个树形结构的数据返回给前端使用,需要以下步骤 1、针对返回数据创建对应的实体类,称为Vo(前端和后端的过渡实体类,专门用来对接数据) 2、在两个实体类中表示关系,因为二级分类课程是包在一级分类里面的。
如下面代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.atguigu.eduservice.entity.subject;import lombok.Data;import java.util.ArrayList;import java.util.List;@Data public class OneSubject { private String id; private String title; private List<TwoSubject> children = new ArrayList <>(); }
1 2 3 4 5 6 7 8 9 10 11 package com.atguigu.eduservice.entity.subject;import lombok.Data;@Data public class TwoSubject { private String id; private String title; }
接着就可以在Service层里面写方法了,注意这里用到了四个实体类和两张表,容易弄混,注意区别!!!
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 @Override public List<OneSubject> getAllOneTwoSubject () { QueryWrapper<EduSubject> queryWrapper1 =new QueryWrapper <>(); queryWrapper1.eq("parent_id" ,"0" ); List<EduSubject> oneSubjectList = baseMapper.selectList(queryWrapper1); QueryWrapper<EduSubject> queryWrapper2 =new QueryWrapper <>(); queryWrapper1.ne("parent_id" ,"0" ); List<EduSubject> twoSubjectList = baseMapper.selectList(queryWrapper2); List<OneSubject> finalSubjectList = new ArrayList <>(); for (EduSubject eduSubject : oneSubjectList) { OneSubject oneSubject = new OneSubject (); BeanUtils.copyProperties(eduSubject,oneSubject); List<TwoSubject> twoFinalSubjectList = new ArrayList <>(); for (EduSubject twosSubject : twoSubjectList) { if (twosSubject.getParentId().equals(oneSubject.getId())){ TwoSubject subject = new TwoSubject (); BeanUtils.copyProperties(twosSubject,subject); twoFinalSubjectList.add(subject); } } oneSubject.setChildren(twoFinalSubjectList); finalSubjectList.add(oneSubject); } return finalSubjectList; } }
课程发布流程 整体的一个课程发布是使用了步骤条来实现一步步完成课程发布流程。内容比较多。
创建新表 下面具体讲一下这些表的作用,方便后面对于业务和代码的理解。 edu_course 课程表:存储课程基本信息
edu_course_description 课程简介表,用富文本框存储课程简介信息
edu_chapter 存储课程章节信息
edu_video 存储章节里面的小节信息
edu_teacher 存储讲师信息
edu_subject 存储课程分类信息
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 CREATE TABLE `edu_course` ( `id` char (19 ) NOT NULL COMMENT '课程ID' , `teacher_id` char (19 ) NOT NULL COMMENT '课程讲师ID' , `subject_id` char (19 ) NOT NULL COMMENT '课程专业ID' , `subject_parent_id` char (19 ) NOT NULL COMMENT '课程专业父级ID' , `title` varchar (50 ) NOT NULL COMMENT '课程标题' , `price` decimal (10 ,2 ) unsigned NOT NULL DEFAULT '0.00' COMMENT '课程销售价格,设置为0则可免费观看' , `lesson_num` int (10 ) unsigned NOT NULL DEFAULT '0' COMMENT '总课时' , `cover` varchar (255 ) CHARACTER SET utf8 NOT NULL COMMENT '课程封面图片路径' , `buy_count` bigint (10 ) unsigned NOT NULL DEFAULT '0' COMMENT '销售数量' , `view_count` bigint (10 ) unsigned NOT NULL DEFAULT '0' COMMENT '浏览数量' , `version` bigint (20 ) unsigned NOT NULL DEFAULT '1' COMMENT '乐观锁' , `status` varchar (10 ) NOT NULL DEFAULT 'Draft' COMMENT '课程状态 Draft未发布 Normal已发布' , `is_deleted` tinyint(3 ) DEFAULT NULL COMMENT '逻辑删除 1(true)已删除, 0(false)未删除' , `gmt_create` datetime NOT NULL COMMENT '创建时间' , `gmt_modified` datetime NOT NULL COMMENT '更新时间' , PRIMARY KEY (`id`), KEY `idx_title` (`title`), KEY `idx_subject_id` (`subject_id`), KEY `idx_teacher_id` (`teacher_id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8mb4 ROW_FORMAT= COMPACT COMMENT= '课程' ; CREATE TABLE `edu_course_description` ( `id` char (19 ) NOT NULL COMMENT '课程ID' , `description` text COMMENT '课程简介' , `gmt_create` datetime NOT NULL COMMENT '创建时间' , `gmt_modified` datetime NOT NULL COMMENT '更新时间' , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8mb4 COMMENT= '课程简介' ; CREATE TABLE `edu_chapter` ( `id` char (19 ) NOT NULL COMMENT '章节ID' , `course_id` char (19 ) NOT NULL COMMENT '课程ID' , `title` varchar (50 ) NOT NULL COMMENT '章节名称' , `sort` int (10 ) unsigned NOT NULL DEFAULT '0' COMMENT '显示排序' , `gmt_create` datetime NOT NULL COMMENT '创建时间' , `gmt_modified` datetime NOT NULL COMMENT '更新时间' , PRIMARY KEY (`id`), KEY `idx_course_id` (`course_id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8mb4 ROW_FORMAT= COMPACT COMMENT= '课程' ; CREATE TABLE `edu_video` ( `id` char (19 ) NOT NULL COMMENT '视频ID' , `course_id` char (19 ) NOT NULL COMMENT '课程ID' , `chapter_id` char (19 ) NOT NULL COMMENT '章节ID' , `title` varchar (50 ) NOT NULL COMMENT '节点名称' , `video_source_id` varchar (100 ) DEFAULT NULL COMMENT '云端视频资源' , `video_original_name` varchar (100 ) DEFAULT NULL COMMENT '原始文件名称' , `sort` int (10 ) unsigned NOT NULL DEFAULT '0' COMMENT '排序字段' , `play_count` bigint (20 ) unsigned NOT NULL DEFAULT '0' COMMENT '播放次数' , `is_free` tinyint(1 ) unsigned NOT NULL DEFAULT '0' COMMENT '是否可以试听:0收费 1免费' , `duration` float NOT NULL DEFAULT '0' COMMENT '视频时长(秒)' , `status` varchar (20 ) NOT NULL DEFAULT 'Empty' COMMENT 'Empty未上传 Transcoding转码中 Normal正常' , `size` bigint (20 ) unsigned NOT NULL DEFAULT '0' COMMENT '视频源文件大小(字节)' , `version` bigint (20 ) unsigned NOT NULL DEFAULT '1' COMMENT '乐观锁' , `gmt_create` datetime NOT NULL COMMENT '创建时间' , `gmt_modified` datetime NOT NULL COMMENT '更新时间' , PRIMARY KEY (`id`), KEY `idx_course_id` (`course_id`), KEY `idx_chapter_id` (`chapter_id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8mb4 ROW_FORMAT= COMPACT COMMENT= '课程视频' ;
添加课程信息(后端) 后端就还是熟悉的使用代码生成器生成对应的实体类以及三层架构
还是像上面一样,要创建一个vo实体类来接受前端的json数据
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 import java.math.BigDecimal;@Data public class CourseInfoVo { @ApiModelProperty(value = "课程ID") private String id; @ApiModelProperty(value = "课程讲师ID") private String teacherId; @ApiModelProperty(value = "一级分类ID") private String subjectParentId; @ApiModelProperty(value = "二级分类ID") private String subjectId; @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; }
之后就可以在EduCourseController里面写接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @RestController @RequestMapping("/eduservice/course") @CrossOrigin public class EduCourseController { @Autowired private EduCourseService courseService; @PostMapping("addCourseInfo") public R addCourseInfo (@RequestBody CourseInfoVo courseInfoVo) { String id = courseService.saveCourseInfo(courseInfoVo); return R.ok().data("courseId" ,id); } }
直接把核心Service贴上来了(操作了两张表,edu_course edu_course_description)
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 @Service public class EduCourseServiceImpl extends ServiceImpl <EduCourseMapper, EduCourse> implements EduCourseService { @Autowired private EduCourseDescriptionService courseDescriptionService; @Override public String saveCourseInfo (CourseInfoVo courseInfoVo) { EduCourse eduCourse = new EduCourse (); BeanUtils.copyProperties(courseInfoVo,eduCourse); int insert = baseMapper.insert(eduCourse); if (insert <= 0 ){ throw new GuliException (20001 ,"添加课程信息失败" ); } String id = eduCourse.getId(); EduCourseDescription courseDescription = new EduCourseDescription (); BeanUtils.copyProperties(courseInfoVo,courseDescription); courseDescription.setId(id); courseDescriptionService.save(courseDescription); return id; } }
添加课程信息(前端) 直接贴,不解释了哈哈哈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import request from '@/utils/request' export default { addCourseInfo (courseInfo ){ return request ({ url : `/eduservice/course/addCourseInfo` , method : 'post' , data :courseInfo }) }, getListTeacher ( ){ return request ({ url : `/eduservice/teacher/findAll` , method : 'get' }) } }
这个info.vue只是步骤条中的步骤一,后面还有两个后面在完善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 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 <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 label ="课程标题" > <el-input v-model ="courseInfo.title" placeholder =" 示例:机器学习项目课:从基础到搭建项目视频课程。专业名称注意大小写" /> </el-form-item > <el-form-item label ="课程分类" > <el-select v-model ="courseInfo.subjectParentId" placeholder ="一级分类" @change ="subjectLevelOneChanged" > <el-option v-for ="subject in subjectOneList" :key ="subject.id" :label ="subject.title" :value ="subject.id" /> </el-select > <el-select v-model ="courseInfo.subjectId" placeholder ="二级分类" > <el-option v-for ="subject in subjectTwoList" :key ="subject.id" :label ="subject.title" :value ="subject.id" /> </el-select > </el-form-item > <el-form-item label ="课程讲师" > <el-select v-model ="courseInfo.teacherId" placeholder ="请选择" > <el-option v-for ="teacher in teacherList" :key ="teacher.id" :label ="teacher.name" :value ="teacher.id" /> </el-select > </el-form-item > <el-form-item label ="总课时" > <el-input-number :min ="0" v-model ="courseInfo.lessonNum" controls-position ="right" placeholder ="请填写课程的总课时数" /> </el-form-item > <el-form-item label ="课程简介" > <tinymce :height ="300" v-model ="courseInfo.description" /> </el-form-item > <el-form-item label ="课程封面" > <el-upload :show-file-list ="false" :on-success ="handleAvatarSuccess" :before-upload ="beforeAvatarUpload" :action ="BASE_API+'/eduoss/fileoss/'" class ="avatar-uploader" > <img :src ="courseInfo.cover" width ="200px" height ="200px" > </el-upload > </el-form-item > <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 > </div > </template > <script > import course from '@/api/edu/course.js' import subject from '@/api/edu/subject.js' import Tinymce from '@/components/Tinymce' export default { components : { Tinymce }, data ( ) { return { saveBtnDisabled :false , courseInfo :{ title : '' , subjectId : '' , subjectParentId : '' , teacherId : '' , lessonNum : 0 , description : '' , cover : '/static/01.jpg' , price : 0 }, BASE_API : process.env .BASE_API , teacherList : [], subjectOneList : [], subjectTwoList : [] } }, created ( ) { this .getListTeacher () this .getOneSubject () }, methods : { handleAvatarSuccess (res,file ) { this .courseInfo .cover = res.data .url }, beforeAvatarUpload (file ) { const isJPG = file.type === 'image/jpeg' const isLt2M = file.size / 1024 / 1024 < 2 if (!isJPG) { this .$message .error ('上传头像图片只能是 JPG 格式!' ) } if (!isLt2M) { this .$message .error ('上传头像图片大小不能超过 2MB!' ) } return isJPG && isLt2M }, subjectLevelOneChanged (value ) { for (var i =0 ;i<this .subjectOneList .length ; i++) { var onesubject=this .subjectOneList [i] if (value === onesubject.id ) { this .subjectTwoList = onesubject.children this .courseInfo .subjectId = '' } } }, getOneSubject ( ) { subject.getSubjectList () .then (response => { this .subjectOneList = response.data .list }) }, getListTeacher ( ){ course.getListTeacher () .then (response => { this .teacherList =response.data .items }) }, saveOrUpdate ( ) { course.addCourseInfo (this .courseInfo ) .then (response => { this .$message({ type : 'success' , message : '添加课程信息成功!' }); this .$router .push ({path :"/course/chapter/" +response.data .courseId }) }) } }, } </script > <style scoped > .tinymce-container { line-height : 29px ; } </style >
章节后端(步骤条第二步) 这个后端代码基本一致,就是删除有点特色,只有章节下面没有小节的时候才可以删除章节(跟瑞吉里面的套餐下菜品在售不能删除套餐一样) 小节的后端和章节的一模一样就不贴了,就是删除那里可以直接用courseId在removeById方法直接删除,不需要判断。
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 @RestController @RequestMapping("/eduservice/chapter") @CrossOrigin public class EduChapterController { @Autowired private EduChapterService chapterService; @GetMapping("getChapterVideo/{courseId}") public R getChapterVideo (@PathVariable String courseId) { List<ChapterVo> list = chapterService.getChapterVideoByCourseId(courseId); return R.ok().data("allChapterVideo" ,list); } @PostMapping("addChapter") public R addChapter (@RequestBody EduChapter eduChapter) { chapterService.save(eduChapter); return R.ok(); } @GetMapping("getChapterInfoById/{chapterId}") public R getChapterInfo (@PathVariable String chapterId) { EduChapter eduChapter = chapterService.getById(chapterId); return R.ok().data("chapter" ,eduChapter); } @PostMapping("updateChapter") public R updateChapter (@RequestBody EduChapter eduChapter) { chapterService.updateById(eduChapter); return R.ok(); } @DeleteMapping("{chapterId}") public R deleteChapter (@PathVariable String chapterId) { boolean flag = chapterService.deleteChapter(chapterId); if (flag) { return R.ok(); } else { return R.error(); } } }
下面就只挂删除的核心代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Override public boolean deleteChapter (String chapterId) { 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); return result>0 ; } }
章节前端(步骤条第二步) 又到了可恶的前端(步骤条第二步的页面代码) 抽取出来的调用接口就懒得贴上来了,在页面上的代码都可以体现出来(懒~)
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 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 <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-button type ="text" @click ="openChapterDialog()" > 添加章节</el-button > <ul class ="chanpterList" > <li v-for ="chapter in chapterVideoList" :key ="chapter.id" > <p > {{ chapter.title }} <span class ="acts" > <el-button type ="text" @click ="openVideo(chapter.id)" > 添加小节</el-button > <el-button style ="" type ="text" @click ="openEditChapter(chapter.id)" > 编辑</el-button > <el-button type ="text" @click ="removeChapter(chapter.id)" > 删除</el-button > </span > </p > <ul class ="chanpterList videoList" > <li v-for ="video in chapter.children" :key ="video.id" > <p > {{ video.title }} <span class ="acts" > <el-button 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 > <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 ="上传视频" > </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 > <script > import chapter from '@/api/edu/chapter' import video from '@/api/edu/video' export default { data ( ) { return { saveBtnDisabled :false , courseId : '' , chapterVideoList :[], dialogChapterFormVisible : false , dialogVideoFormVisible : false , video : { title : '' , sort : 0 , free : 0 , videoSourseId : '' }, chapter : { title : '' , sort : 0 } } }, created ( ) { if (this .$route .params && this .$route .params .id ){ this .courseId = this .$route .params .id this .getChapterVideo () } }, methods : { updateVideo ( ){ video.updatedVideo (this .video ) .then (response => { this .dialogVideoFormVisible = false this .$message({ type : 'success' , message : '修改小节信息成功!' }); this .getChapterVideo () }) }, openEditVideo (videoId ){ this .dialogVideoFormVisible = true video.getVideo (videoId) .then (response => { this .video = response.data .video }) }, removeVideo (id ){ this .$confirm('此操作将删除小节, 是否继续?' , '提示' , { confirmButtonText : '确定' , cancelButtonText : '取消' , type : 'warning' }).then (() => { video.deleteVideoById (id) .then (response => { this .$message({ type : 'success' , message : '删除成功!' }); this .getChapterVideo () }) }) }, openVideo (chapterId ){ this .dialogVideoFormVisible = true this .video = {} this .video .chapterId = chapterId }, addVideo ( ) { this .video .courseId = this .courseId video.addVideo (this .video ) .then (response => { this .dialogVideoFormVisible = false this .$message({ type : 'success' , message : '添加小节信息成功!' }); this .getChapterVideo () }) }, saveOrUpdateVideo ( ){ if (!this .video .id ){ this .addVideo () } else { this .updateVideo () } }, removeChapter (chapterId ){ this .$confirm('此操作将删除章节, 是否继续?' , '提示' , { confirmButtonText : '确定' , cancelButtonText : '取消' , type : 'warning' }).then (() => { chapter.deleteChapterById (chapterId) .then (response => { this .$message({ type : 'success' , message : '删除成功!' }); this .getChapterVideo () }) }) }, openEditChapter (chapterId ){ this .dialogChapterFormVisible = true chapter.getChapter (chapterId) .then (response => { this .chapter = response.data .chapter }) }, openChapterDialog ( ){ this .dialogChapterFormVisible = true this .chapter = {} }, addChapter ( ){ this .chapter .courseId = this .courseId chapter.addChapter (this .chapter ) .then (response => { this .dialogChapterFormVisible = false this .$message({ type : 'success' , message : '添加章节信息成功!' }); this .getChapterVideo () }) }, updateChapter ( ){ chapter.updatedChapter (this .chapter ) .then (response => { this .dialogChapterFormVisible = false this .$message({ type : 'success' , message : '修改章节信息成功!' }); this .getChapterVideo () }) }, saveOrUpdate ( ) { if (!this .chapter .id ){ this .addChapter () } else { this .updateChapter () } }, getChapterVideo ( ){ chapter.addAllChapterVideo (this .courseId ) .then (response => { this .chapterVideoList =response.data .allChapterVideo }) }, previous ( ) { this .$router .push ({path :"/course/info/" + this .courseId }) }, next ( ) { this .$router .push ({path :"/course/publish/" +this .courseId }) } }, } </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 { font-size : 14px ; margin : 10px 0 ; padding : 10px ; height : 50px ; line-height : 30px ; width : 100% ; border : 1px dotted #DDD ; } </style >