1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 最新《谷粒学院开发教程》:4 - 课程管理

最新《谷粒学院开发教程》:4 - 课程管理

时间:2020-12-10 04:36:09

相关推荐

最新《谷粒学院开发教程》:4 - 课程管理

目录

一、EasyExcel 基本使用二、课程分类添加2.1、后端接口2.2、前端展示 三、课程分类列表3.1、后端接口3.2、前端展示 四、添加课程信息4.1、数据库表4.2、后端接口4.3、前端展示 初始化步骤条4.4、填写课程基本信息4.5、讲师下拉列表显示4.6、课程分类多级联动4.7、课程封面4.8、富文本编辑器 Tinymce 五、课程大纲列表5.1、后端接口5.2、前端实现 六、修改课程信息6.1、回显课程信息6.2、修改课程信息6.3、前端实现6.4、优化回显6.5、优化表单6.6、修改课程信息

一、EasyExcel 基本使用

1.1、Excel导入导出的应用场景

数据导入:减轻录入工作量

数据导出:统计信息归档

数据传输:异构系统之间数据传输

1.2、EasyExcel简介

Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。

EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。

EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)

1.3、EasyExcel写

1、service_edu模块 引入依赖

<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.1.1</version></dependency>

2、创建实体类

//设置表头和添加的数据字段@Data@ToStringpublic class UserDemo {//设置excel表头名称@ExcelProperty("序号")private Integer no;//设置excel表头名称@ExcelProperty("姓名")private String name;}

3、实现写操作 (第一种)

创建方法循环设置要添加到Excel的数据(写法一)推荐

public class TestEasyExcel {public static void main(String[] args) {String filename = "C:\\Users\\lapto\\Desktop\\UserDemo.xlsx";EasyExcel.write(filename, UserDemo.class).sheet("用户列表").doWrite(getLists());}private static List<UserDemo> getLists() {ArrayList<UserDemo> list = new ArrayList<>();for (int i = 1; i < 4; i++) {UserDemo demoData = new UserDemo();demoData.setNo(i);demoData.setName("Laptoy:" + i);list.add(demoData);}return list;}}

4、实现写操作 (第二种)

public class TestEasyExcel {public static void main(String[] args) {String filename = "C:\\Users\\lapto\\Desktop\\UserDemo.xlsx";ExcelWriter excelWriter = EasyExcel.write(filename, UserDemo.class).build();WriteSheet build = EasyExcel.writerSheet("用户列表").build();// 这里 需要指定写用哪个class去写excelWriter.write(getLists(), build);// 千万别忘记finish 会帮忙关闭流excelWriter.finish();}private static List<UserDemo> getLists() {ArrayList<UserDemo> list = new ArrayList<>();for (int i = 1; i < 4; i++) {UserDemo demoData = new UserDemo();demoData.setNo(i);demoData.setName("Laptoy:" + i);list.add(demoData);}return list;}}

1.4、EasyExcel读

1、实体类

//设置表头和添加的数据字段@Data@ToStringpublic class UserDemo {//设置excel表头名称@ExcelProperty(value = "序号",index = 0)private Integer no;//设置excel表头名称@ExcelProperty(value = "姓名",index = 1)private String name;}

2、创建读取操作的监听器

public class ExcelListener extends AnalysisEventListener<UserDemo> {//创建list集合封装最终的数据List<UserDemo> list = new ArrayList<>();//一行一行去读取excel内容@Overridepublic void invoke(UserDemo demoData, AnalysisContext analysisContext) {System.out.println(demoData);list.add(demoData);}//读取excel表头信息@Overridepublic void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {System.out.println("表头信息" + headMap);}//读取完成后执行@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {}}

3、调用实现最终的读取

public static void main(String[] args) {String filename = "C:\\Users\\lapto\\Desktop\\UserDemo.xlsx";// 指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭EasyExcel.read(filename, UserDemo.class, new ExcelListener()).sheet().doRead();}

二、课程分类添加

2.1、后端接口

通过传入excel文件进行添加

添加时一级分类只需要添加一次,所以需要判空

1、使用代码生成器生成课程分类代码

strategy.setInclude("edu_subject");

2、控制层

@Api(tags = "课程分类管理")@CrossOrigin //解决跨域问题@RestController@RequestMapping("/eduservice/subject")public class EduSubjectController {@Autowiredprivate EduSubjectService eduSubjectService;//添加课程分类//获取上传过来的文件,把文件内容读取出来@PostMapping("/addSubject")public R addSubject(MultipartFile file) {//获取上传的excel文件 MultipartFileeduSubjectService.saveSubject(file, eduSubjectService);//判断返回集合是否为空return R.ok();}}

2、业务层

@Servicepublic class EduSubjectServiceImpl extends ServiceImpl<EduSubjectMapper, EduSubject> implements EduSubjectService {//添加课程分类@Overridepublic void saveSubject(MultipartFile file, EduSubjectService eduSubjectService) {InputStream inputStream = null;try {inputStream = file.getInputStream();//调用方法进行读取EasyExcel.read(inputStream, SubjectData.class, new SubjectExcelListener(eduSubjectService)).sheet().doRead();} catch (Exception e) {e.printStackTrace();throw new LaptoyException(20002, "请选择文件或文件格式不支持");}}}

3、Listener

public class SubjectExcelListener extends AnalysisEventListener<SubjectData> {//因为SubjectExcelListener態交给spring进行ioc管理,需要自己手动new,不能注入其他对象public EduSubjectService eduSubjectService;//有参,通过构造方法手动注入service进而操作数据库public SubjectExcelListener(EduSubjectService eduSubjectService) {this.eduSubjectService = eduSubjectService;}public SubjectExcelListener() {}//读取excel内容,一行一行读取@Overridepublic void invoke(SubjectData subjectData, AnalysisContext analysisContext) {//表示excel中没有数据,就不需要读取了if (subjectData == null) {throw new LaptoyException(20001, "添加失败");}//一行一行读取,每次读取有两个值,第一个值一级分类,第二个值二级分类//判断是否有一级分类是否重复EduSubject existOneSubject = this.existOneSubject(eduSubjectService, subjectData.getOneSubjectName());if (existOneSubject == null) {//没有相同的一级分类,进行添加existOneSubject = new EduSubject();existOneSubject.setParentId("0"); //设置一级分类id值,0代表为一级分类existOneSubject.setTitle(subjectData.getOneSubjectName());//设置一级分类名eduSubjectService.save(existOneSubject);//给数据库添加一级分类}//判断是否有二级分类是否重复EduSubject existTwoSubject = this.existTwoSubject(eduSubjectService, subjectData.getTwoSubjectName(), existOneSubject.getId());if (existTwoSubject == null) {existTwoSubject = new EduSubject();existTwoSubject.setParentId(existOneSubject.getId());existTwoSubject.setTitle(subjectData.getTwoSubjectName());eduSubjectService.save(existTwoSubject);}}// 判断一级分类不能重复添加private EduSubject existOneSubject(EduSubjectService eduSubjectService, String name) {QueryWrapper<EduSubject> wrapper = new QueryWrapper<>();wrapper.eq("title", name).eq("parent_id", "0");EduSubject oneSubject = eduSubjectService.getOne(wrapper);return oneSubject;}// 判断二级分类不能重复添加private EduSubject existTwoSubject(EduSubjectService eduSubjectService, String name, String parentId) {QueryWrapper<EduSubject> wrapper = new QueryWrapper<>();wrapper.eq("title", name).eq("parent_id", parentId);EduSubject twoSubject = eduSubjectService.getOne(wrapper);return twoSubject;}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {}}

4、测试之前给实体类 edu_subject 配置填充

@TableField(fill = FieldFill.INSERT)@ApiModelProperty(value = "创建时间")private Date gmtCreate;@TableField(fill = FieldFill.INSERT_UPDATE)@ApiModelProperty(value = "更新时间")private Date gmtModified;

5、测试

TestDemo.xlsx

数据库

2.2、前端展示

1、路由

// 课程分类模块路由{path: 'subject',component: Layout,redirect: '/edu/subject/list',name: '课程分类管理',meta: {title: '课程分类管理', icon: 'nested' },children: [{path: 'list',name: '课程分类列表',component: () => import('@/views/edu/subject/list.vue'),meta: {title: '课程分类列表', icon: 'table' }},{path: 'import',name: '课程分类导入',component: () => import('@/views/edu/subject/import.vue'),meta: {title: '导入课程分类', icon: 'nested' }}]},

2、组件 import.vue

<template><div class="app-container"><el-form label-width="120px"><el-form-item label="Excel模板"><el-tag><i class="el-icon-download" /><a :href="'/static/test.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="application/vnd.ms-excel"><el-button slot="trigger" size="small" type="primary">选取文件</el-button><el-button :loading="loading" style="margin-left: 10px" size="small" type="success" @click="submitUpload">上传到服务器</el-button></el-upload></el-form-item></el-form></div></template><script>export default {data() {return {BASE_API: process.env.BASE_API, // 接口API地址importBtnDisabled: false, // 按钮是否禁用,loading: false,};},methods: {//点击按钮上传文件到接口submitUpload() {this.importBtnDisabled = true;this.loading = true;this.$refs.upload.submit();},//上传成功后fileUploadSuccess(resp) {if (resp.success === true) {this.loading = false;this.$message({type: "success",message: resp.message,});}},//上传失败后fileUploadError(resp) {this.loading = false;this.$message({type: "error",message: "导入失败",});},},}</script>

三、课程分类列表

3.1、后端接口

1、创建VO用于封装树形结构数据

//一级分类@Datapublic class OneSubject {private String id;private String title;// 二级分类private List<TwoSubject> children = new ArrayList<>();}//二级分类@Datapublic class TwoSubject {private String id;private String title;}

2、控制层

// 课程分类列表(树形)@ApiOperation(value = "嵌套数据列表")@GetMapping("/getAllSubject")public R getAllSubject(){//ist集合泛型是一级分类,一级分类中本身含有二级分类List<OneSubject> list = eduSubjectService.getAllOneTwoSubject();return R.ok().data("data",list);}

3、业务层

// 树形显示课程分类@Overridepublic List<OneSubject> getAllOneTwoSubject() {// 查询所有一级分类 parent_id = 0QueryWrapper<EduSubject> wrapperOne = new QueryWrapper<>();wrapperOne.eq("parent_id", "0");List<EduSubject> oneSubjectsList = baseMapper.selectList(wrapperOne);// 查询所有二级分类 parent_id != 0QueryWrapper<EduSubject> wrapperTwo = new QueryWrapper<>();wrapperTwo.ne("parent_id", "0");List<EduSubject> twoSubjectsList = baseMapper.selectList(wrapperTwo);// 创建list集合,用于封装最终数据List<OneSubject> finalSubjectList = new ArrayList<>();//封装一级分类for (int i = 0; i < oneSubjectsList.size(); i++) {EduSubject oSubject = oneSubjectsList.get(i);OneSubject oneSubject = new OneSubject();BeanUtils.copyProperties(oSubject, oneSubject);// 添加到listfinalSubjectList.add(oneSubject);// 在一级分类循环遍历查询所有的二级分类// 创建list集合封装每个一级分类的二级分类ArrayList<TwoSubject> finalTwoSubjects = new ArrayList<>();// 遍历二级list集合for (int j = 0; j < twoSubjectsList.size(); j++) {EduSubject tSubject = twoSubjectsList.get(j);// 判断二级分类parentId和一级分类id是否一样if (tSubject.getParentId().equals(oSubject.getId())) {TwoSubject twoSubject = new TwoSubject();BeanUtils.copyProperties(tSubject, twoSubject);finalTwoSubjects.add(twoSubject);}}//把一级下面所有二级分类放到oneSubject里面oneSubject.setChildren(finalTwoSubjects);}return finalSubjectList;}

4、测试

3.2、前端展示

1、创建API -src/api/teacher/subject.js

import request from '@/utils/request' export default {//课程分类列表getSubjectList() {return request({url: "/eduservice/subject/getAllSubject",method: 'get'})}}

2、list.vue

<template><div class="app-container"><el-input v-model="filterText" placeholder="输入关键字进行查询" style="margin-bottom: 30px" /><el-tree ref="tree" :data="data" :props="defaultProps" :filter-node-method="filterNode" class="filter-tree" /></div></template><script>import subject from '@/api/teacher/subject.js'export default {data() {return {filterText: "",data: [], //返回所有分类的数据defaultProps: {children: "children",label: "title",},};},watch: {filterText(val) {this.$refs.tree.filter(val);},},methods: {getAllSubjectList() {subject.getSubjectList().then(resp => {this.data = resp.data.data})},// 不区分大小写检索filterNode(value, data) {if (!value) return true;return data.title.toLowerCase().indexOf(value.toLowerCase()) !== -1},},created() {this.getAllSubjectList()},};</script>

3、优化导入课程分类后路由到课程分类列表 import.vue

四、添加课程信息

4.1、数据库表

edu_course:课程表:存储课程基本信息edu_course_description:课程简介表:存储课程简介信息edu_chapter:课程章节表:存储课程章节信息edu_video:课程小节表:存储章节里面小节信息edu_teacher:讲师表edu_subject:分类表

edu_course 课程表

edu_course_description 课程简介表

edu_chapter 课程章节表

edu_video 课程小节表

4.2、后端接口

1、代码生成器生成代码

strategy.setInclude("edu_course", "edu_chapter", "edu_course_description", "edu_video");//根据数据库哪张表生成,有多张表就加逗号继续填写

2、创建vo用于封装表单提交数据

@ApiModel(value = "课程基本信息", description = "编辑课程基本信息的表单对象")@Datapublic class CourseInfoFormVo implements Serializable {private static final long serialVersionUID = 1L;@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;}

2、控制层

@Api(tags = "课程模块")@CrossOrigin //解决跨域问题@RestController@RequestMapping("/eduservice/course")public class EduCourseController {@Autowiredprivate EduCourseService eduCourseService;//添加课程基本信息方法@PostMapping("/addCourseInfo")public R addCourseInfo(@RequestBody CourseInfoFormVo vo){String id = eduCourseService.saveCourseInfo(vo);// 返回课程id给下一步操作return R.ok().data("courseId",id);}}

3、课程描述的id根据数据表分析,此id与课程id为同一个,所以我们修改实体类主键生成策略为手动输入

@TableId(value = "id", type = IdType.INPUT)private String id;

4、业务层

@Servicepublic class EduCourseServiceImpl extends ServiceImpl<EduCourseMapper, EduCourse> implements EduCourseService {@Autowiredprivate EduCourseDescriptionService descriptionService;@Overridepublic String saveCourseInfo(CourseInfoFormVo vo) {EduCourse course = new EduCourse();BeanUtils.copyProperties(vo, course);boolean flag = this.save(course);if (!flag) {// 保存失败throw new LaptoyException(20001, "添加课程失败");}// 保存成功// 保存课程描述信息,需要将保存成功的课程id传入课程描述表EduCourseDescription description = new EduCourseDescription();// 保存成功自动生成了课程id,将该id赋值给课程描述表description.setId(course.getId());description.setDescription(vo.getDescription());descriptionService.save(description);return course.getId();}}

5、测试

{"cover": "封面url","description": "描述信息","id": "","lessonNum": 0,"price": 0,"subjectId": "","subjectParentId": "","teacherId": "","title": "课程名称"}

4.3、前端展示 初始化步骤条

element-ui step步骤条

1、添加路由

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

初始化步骤组件

2、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="next">保存并下一步</el-button></el-form-item></el-form></div></template><script>export default {data() {return {saveBtnDisabled: false,};},methods: {next() {//跳转到第二步this.$router.push('/course/chapter/1')},},created() {}};</script>

3、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,};},methods: {previous() {//跳转到上一步this.$router.push("/course/info/1");},next() {//跳转到第三步this.$router.push("/course/publish/1");},},created() {},};</script>

4、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,};},methods: {// 跳转到上一步previous() {this.$router.push("/course/chapter/1");},publish() {this.$router.push("/course/list");}},};</script>

4.4、填写课程基本信息

1、定义API - src/api/course.js

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

2、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 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></div></template><script>import course from "@/api/teacher/course.js";export default {data() {return {saveBtnDisabled: false,courseInfo:{title: "",subjectId: "",teacherId: "",subjectParentId: "",lessonNum: 0,description: "",cover: "",price: 0,},};},methods: {saveOrUpdate() {course.addCourseInfo(this.courseInfo).then(resp => {this.$message({message: "添加课程信息成功",type: "success",})//跳转到第二步,并带着这个课程生成的idthis.$router.push({path: "/course/chapter/" + resp.data.courseId });});},},created() {},};</script>

4.5、讲师下拉列表显示

1、定义API - course.js

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

2、页面

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

3、逻辑

import course from "@/api/teacher/course.js";export default {data() {return {teacherLists: [], //封装所有讲师数据};},methods: {//查询所有讲师getListTeacher() {course.getAllTeacher().then((resp) => {this.teacherLists = resp.data.data;});},},created() {this.getListTeacher();},};

4.6、课程分类多级联动

ElmentUI - 选择器

1、API

// 查询课程分类getSubjectList() {return request({url: "/eduservice/subject/getAllSubject",method: 'get'})}

2、页面

<el-form-item label="所属分类"><el-select v-model="courseInfo.subjectParentId" placeholder="一级分类" @change="subjectLevelOneChanged"><el-option v-for="subject in subjectOneLists" :key="subject.id" :label="subject.title" :value="subject.id"></el-option></el-select><el-select v-model="courseInfo.subjectId" placeholder="二级分类"><el-option v-for="subject in subjectTwoLists" :key="subject.id" :label="subject.title" :value="subject.id"></el-option></el-select></el-form-item>

3、逻辑

export default {data() {return {subjectOneLists: [], //封装一级分类数据subjectTwoLists: [], //封装二级分类数据};},methods: {// 查出所有分类信息getSubjectList() {course.getSubjectList().then((resp) => {this.subjectOneLists = resp.data.data})},// 点击一级分类查出二级分类subjectLevelOneChanged(value) {// value就是一级分类的id值for (let i = 0; i < this.subjectOneLists.length; i++) {if (this.subjectOneLists[i].id === value) {this.subjectTwoLists = this.subjectOneLists[i].children;}}}},created() {this.getSubjectList();},};

4.7、课程封面

1、组件模板

<el-form-item label="课程封面"><el-upload :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload" :action="BASE_API + '/eduoss/fileoss/upload'" class="avatar-uploader"><img :src="courseInfo.cover" /></el-upload></el-form-item>

2、js

import course from "@/api/teacher/course.js";export default {data() {return {BASE_API: process.env.BASE_API, // 接口API地址courseInfo: {cover: "/img/flexible/logo/pc/result.png",},};},methods: {// 图片上传// 上传成功展示上传的图片到 <img />handleAvatarSuccess(resp, file) {this.courseInfo.cover = resp.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;}},};

4.8、富文本编辑器 Tinymce

简介通过富文本编辑器进行编辑

Tinymce是一个传统javascript插件,默认不能用于Vue.js因此需要做一些特殊的整合步骤

1、复制脚本库

将资源里的富文本编辑器脚本库componentsstatis复制到项目的对应目录下

2、配置html变量

在 /build/webpack.dev.conf.js 中添加配置

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

3、引入 js脚本 - index.html

<body><script src="<%=BASE_URL%>/tinymce4.7.5/tinymce.min.js"></script><script src="<%=BASE_URL%>/tinymce4.7.5/langs/zh_CN.js"></script><div id="app"></div><!-- built files will be auto injected --></body>

4、使用组件 - info.vue

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

<script>import Tinymce from '@/components/Tinymce';export default {components: {Tinymce },}</script><style scoped>.tinymce-container {line-height: 29px;}</style>

最终效果

测试完毕:

课程表与课程简介表、讲师表、课程分类表 关联并保存数据成功

五、课程大纲列表

5.1、后端接口

1、创建实体类 章节和小节

章节

@Datapublic class ChapterVo implements Serializable {private static final long serialVersionUID = 1L;private String id;private String title;//表示小节private List<VideoVo> children = new ArrayList<VideoVo>();}

小节 即每个视频

@Datapublic class VideoVo implements Serializable {private static final long serialVersionUID = 1L;private String id;private String title;private Boolean free;}

2、控制层

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

3、业务层

@Servicepublic class EduChapterServiceImpl extends ServiceImpl<EduChapterMapper, EduChapter> implements EduChapterService {@Autowiredprivate EduVideoService eduVideoService;@Overridepublic List<ChapterVo> getChapterVideoByCourseId(String courseId) {// 查询章节信息QueryWrapper<EduChapter> wrapper = new QueryWrapper<>();wrapper.eq("course_id",courseId);List<EduChapter> eduChapters = baseMapper.selectList(wrapper);// 查询小节信息QueryWrapper<EduVideo> wrapper1 = new QueryWrapper<>();wrapper1.eq("course_id",courseId);List<EduVideo> eduVideos = eduVideoService.list(wrapper1);// 创建章节vo列表ArrayList<ChapterVo> finalChapterVos = new ArrayList<>();// 填充章节vo数据for (int i = 0; i < eduChapters.size(); i++) {EduChapter chapter = eduChapters.get(i);// 创建章节vo对象ChapterVo chapterVo = new ChapterVo();BeanUtils.copyProperties(chapter,chapterVo);finalChapterVos.add(chapterVo);//填充课时vo对象ArrayList<VideoVo> finalVideoVos = new ArrayList<>();for (int j = 0; j < eduVideos.size(); j++) {EduVideo video = eduVideos.get(j);if (chapter.getId().equals(video.getChapterId())){VideoVo videoVo = new VideoVo();BeanUtils.copyProperties(video,videoVo);finalVideoVos.add(videoVo);}}chapterVo.setChildren(finalVideoVos);}return finalChapterVos;}}

5.2、前端实现

1、定义API - chapter.js

import request from '@/utils/request' //引入已经封装好的axios 和 拦截器export default {//根据课程id获取章节和小节数据列表getChapterVideoByCourseId(courseId) {return request({url: `/eduservice/chapter/getChapterVideo/${courseId}`,method: 'get',})},}

2、chapter.vue 页面

<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"><ul><li v-for="chapter in chapterVideoList" :key="chapter.id"><p>{{ chapter.title }}<span><el-button type="text">添加课时</el-button><el-button style="" type="text">编辑</el-button><el-button type="text">删除</el-button></span></p><ul><li v-for="video in chapter.children" :key="video.id"><p>{{ video.title }}<span class="acts"><el-button type="text">编辑</el-button><el-button type="text">删除</el-button></span></p></li></ul></li></ul><el-form-item><el-button @click="dialogChapterFormVisible=true">添加章节</el-button><el-button @click="previous">上一步</el-button><el-button :disabled="saveBtnDisabled" type="primary" @click="next">下 一步</el-button></el-form-item></el-form></div><script>import chapter from '@/api/teacher/chapter.js';export default {data() {return {saveBtnDisabled: false,courseId: '',chapterVideoList: [],};},methods: {//根据课程id查询对应的课程章节和小结getChapterVideoByCourseId() {chapter.getChapterVideoByCourseId(this.courseId).then(resp => {this.chapterVideoList = resp.data.dataconsole.log(resp.data.data)})},},created() {//获取路由里的id值if (this.$route.params && this.$route.params.id) {this.courseId = this.$route.params.id}//根据课程id查询对应的课程章节和小结this.getChapterVideoByCourseId();},};</script>

3、测试 -http://localhost:9528/#/course/chapter/18

六、修改课程信息

点击上一步按钮,回显当前课程id的信息,并通过 下一步按钮 进行更新课程信息

6.1、回显课程信息

1、控制层

// 根据课程id查询课程基本信息@GetMapping("/getCourseInfoById/{courseId}")public R getCourseInfoById(@PathVariable String courseId) {CourseInfoFormVo courseInfoForm = eduCourseService.getCourseInfo(courseId);return R.ok().data("data", courseInfoForm);}

2、业务层

@Autowiredprivate EduCourseDescriptionService descriptionService;@Overridepublic CourseInfoFormVo getCourseInfo(String courseId) {EduCourse eduCourse = this.getById(courseId);CourseInfoFormVo vo = new CourseInfoFormVo();BeanUtils.copyProperties(eduCourse, vo);EduCourseDescription description = descriptionService.getById(courseId);vo.setDescription(description.getDescription());return vo;}

6.2、修改课程信息

1、控制层

// 修改课程信息@PostMapping("/updateCourseInfo")public R updateCourseInfo(@RequestBody CourseInfoFormVo vo) {eduCourseService.updateCourseInfo(vo);return R.ok();}

2、业务层

@Overridepublic void updateCourseInfo(CourseInfoFormVo vo) {// 修改课程基本信息EduCourse eduCourse = new EduCourse();BeanUtils.copyProperties(vo, eduCourse);boolean flag = this.updateById(eduCourse);// 失败if(!flag){throw new LaptoyException(20001,"修改课程失败");}// 成功// 修改课程描述信息EduCourseDescription description = new EduCourseDescription();BeanUtils.copyProperties(vo,description);descriptionService.updateById(description);}

6.3、前端实现

1、API - course.js

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

2、chapter.vue

previous() {//跳转到上一步this.$router.push("/course/info/" + this.courseId);},next() {//跳转到第三步this.$router.push("/course/publish/" + this.courseId);},

3、info.vue

data() {return {courseId: ""}}methods: {// 获取课程信息getCourseInfo() {course.getCourseInfoById(this.courseId).then((resp) => {this.courseInfo = resp.data.data})},},created() {//判断路径中是否有课程idif (this.$route.params && this.$route.params.id) {this.courseId = this.$route.params.id;//根据课程id 查询课程基本信息this.getCourseInfo()};...},

4、测试

当返回上一步时二级分类回显为id

6.4、优化回显

默认二级分类的标题需要通过点击一级分类后的该方法进行展示

// 点击一级分类查出二级分类subjectLevelOneChanged(value) {// value就是一级分类的id值for (let i = 0; i < this.subjectOneLists.length; i++) {if (this.subjectOneLists[i].id === value) {this.subjectTwoLists = this.subjectOneLists[i].children;}}}

现在需要通过获取分类信息,并且将分类信息的每一个的id与当前课程id进行比较,如果相同就将二级分类进行回显标题

1、修改钩子函数

created() {//判断路径中是否有课程id,回显操作(修改)if (this.$route.params && this.$route.params.id) {this.courseId = this.$route.params.id;//根据课程id 查询课程基本信息this.getCourseInfo()} else {// 路径中无课程id,新增操作this.getSubjectList();}// 回显所有讲师this.getListTeacher();},

2、获取课程信息

// 获取课程信息getCourseInfo() {course.getCourseInfoById(this.courseId).then((resp) => {this.courseInfo = resp.data.data;//查询所有分类,包含一级和二级所有course.getSubjectList().then((resp) => {//获取所有一级分类this.subjectOneLists = resp.data.data;//把所有一级分类数组进行遍历for (var i = 0; i < this.subjectOneLists.length; i++) {//获取每个一级分类var oneSubject = this.subjectOneLists[i];//比较当前courseInfo里面的一级分类id和所有的一级分类id是否一样if (this.courseInfo.subjectParentId == oneSubject.id) {//获取一级分类中所有的二级分类this.subjectTwoLists = oneSubject.children;}}});});}

6.5、优化表单

回显数据后,再单击添加课程模块,数据还在,按理应该是清空数据

watch: {$route(to, from) {//路由变化方式,当路由发送变化,方法就执行this.courseInfo = {}},}

6.6、修改课程信息

saveOrUpdate() {if (this.courseInfo.id) {this.updateCourse();} else {this.saveCourse();}},saveCourse() {course.addCourseInfo(this.courseInfo).then(resp => {this.$message({message: "添加课程信息成功",type: "success",})//跳转到第二步,并带着这个课程生成的idthis.$router.push({path: "/course/chapter/" + resp.data.courseId });});},updateCourse() {course.updateCourseInfo(this.courseInfo).then(resp => {this.$message({message: "修改课程信息成功",type: "success",})//跳转到第二步,并带着这个课程生成的idthis.$router.push({path: "/course/chapter/" + this.courseId });});},

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。