8、课程分类多级联动
1、两个下拉列表实现
思路分析:使用多个下拉列表来达到多级联动效果,前面的被触发,后面的下拉列表被赋值填充.
(1)组件数据定义—定义data中
subjectOneList:[],//一级分类 subjectTwoList:[]//二级分类
2)组件模板
<el-form-item label="课程分类"> <el-select filterable 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 filterable 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>
(3)组件脚本
created() { //初始化一级分类 this.getOneSubject() }, method:{ //.... //查询所有的一级分类 getOneSubject() { subject.getSubjectList() .then(response => { this.subjectOneList = response.data.list }) }, 点击某个一级分类,触发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 = '' } }
(1)页面效果
2、使用级联组件
(1)组件模板
<el-form-item label="课程分类"> <el-cascader filterable v-model="subject" :options="subjectList" :props="subProps" @change="handleChange"> </el-cascader> </el-form-item>
2)模板数据
courseInfo:{ //... subjectId: '',//一级分类id subjectParentId:'',//二级分类id //... }, subjectList:[],//课程分类列表 subProps:{//模板数据 value:'id', label:'title', children:'children' }, subject:[],//存放级联列表的id数组
(3)组件js脚本
handleChange(){//参数为value数据,如果不需要可以不写. console.log(this.subject); this.courseInfo.subjectId = this.subject[1] //将对应的一二级分类id放入到courseInfo中 this.courseInfo.subjectParentId = this.subject[0] }
(4)页面效果
9、课程封面
参考 http://element-cn.eleme.io/#/zh-CN/component/upload 用户头像上传
1、定义data数据模板
courseInfo:{ //.... cover: 'https://guli-photos.oss-cn-hangzhou.aliyuncs.com/course.jpg',//默认头像 //.... }, BASE_API: process.env.BASE_API // 接口API地址
2、组件模板
<el-form-item label="课程封面"> <el-upload :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload" :action="BASE_API+'/ossService/file/upload'" class="avatar-uploader"> <img :src="courseInfo.cover"> </el-upload> </el-form-item>
3、结果回调
handleAvatarSuccess(res,file){//头像上传成功后 this.courseInfo.cover = res.data.url }, beforeAvatarUpload(file){//上传头像之前进行照片校验 const isPic = (file.type === 'image/jpeg' || file.type === 'image/jpg' || file.type === 'image/png') const isLt2M = file.size / 1024 / 1024 < 2 if (!isPic) { this.$message.error('上传头像图片只能是 JPG/JPEG/PNG 格式!') } if (!isLt2M) { this.$message.error('上传头像图片大小不能超过 2MB!') } return isPic && 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)引入组件
info.vue中引入 Tinymce
import Tinymce from '@/components/Tinymce' export default { components: { Tinymce }, ...... }
(2)组件模板
<!-- 课程简介--> <el-form-item label="课程简介"> <tinymce :height="300" v-model="courseInfo.description"/> </el-form-item>
(3)组件样式
<style scoped> .tinymce-container { line-height: 29px; } </style>
(4)组件效果图
11、课程基本信息页面效果展示
四、课程大纲列表 - 后端开发
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 { @Autowired private EduChapterService chapterService; //课程大纲列表,根据课程id进行查询 @ApiOperation("根据课程id查询章节和小节") @GetMapping("getChapterVideo/{courseId}") public R getChapterVideo(@PathVariable String courseId){ List<ChapterVo> chapterVoList = chapterService.getChapterVideo(courseId); return R.ok().data("list",chapterVoList); } }
3、编写Service
@Service public class EduChapterServiceImpl extends ServiceImpl<EduChapterMapper, EduChapter> implements EduChapterService { @Autowired private EduVideoService videoService; //根据课程id查询课程大纲 @Override public List <ChapterVo> getChapterVideo(String courseId) { //1.查询所有章节 QueryWrapper <EduChapter> chapterWrapper = new QueryWrapper <>(); chapterWrapper.eq("course_id",courseId); List <EduChapter> chapterList = this.baseMapper.selectList(chapterWrapper); //2.查询所有的小节 QueryWrapper <EduVideo> videoWrapper = new QueryWrapper <>(); videoWrapper.eq("course_id",courseId); List <EduVideo> videoList = videoService.list(videoWrapper); //定义最终的数据类型 List <ChapterVo> finalChapterList = new ArrayList <ChapterVo>(); //3.遍历章节集合,将其封装到List <ChapterVo> for (EduChapter chapter : chapterList) { ChapterVo chapterVo = new ChapterVo(); BeanUtils.copyProperties(chapter,chapterVo); finalChapterList.add(chapterVo);//将其放入结果集 List <VideoVo> children = new ArrayList <>(); //4.遍历小节集合,将其封装到对应的章节中 for (EduVideo video : videoList) { if(video.getChapterId().equals(chapter.getId())){//如果当前小节输入当前章节,则加入该章节 VideoVo videoVo = new VideoVo(); BeanUtils.copyProperties(video,videoVo); children.add(videoVo); } } chapterVo.setChildren(children);//将章节的对应小节设置进去 } return finalChapterList; } }
4、使用Swagger进行测试