项目|7-谷粒学苑

课程管理 需要用到的表格
edu_course 课程表:存储课程的基本信息
edu_course_description 课程简介表:存储课程的简介
edu_chapter 课程章节表:存储课程章节信息
edu_video 课程小节表:存储章节里的小节信息
edu_teacher 讲师表
edu_subject 分类表
添加课程基本信息
分析:
(1)创建vo实体类用于单表数据封装(已有的数据不全,要重新进行封装)
(2)把表单提交过来的数据添加到数据库,要向两张表添加数据,课程表和课程简介表 一对一关系
(3)把讲师和分类使用下拉列表显示
课程分类需要做成二级联动效果
实现:
1.使用代码生成器生成课程相关的代码

strategy.setInclude("edu_course","edu_course_description","edu_chapter","edu_video"); //数据库表名,多个的话,可以逗号隔开

2.创建vo类封装表单提交的数据
package com.atguigu.eduservice.entity.vo; import com.alibaba.excel.event.AbstractIgnoreExceptionReadListener; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.math.BigDecimal; /** * @author XueYanhong * @description * @date 2022/5/5 10:49 */ @Data public class CourseInfoVo {private String id; @ApiModelProperty(value = "https://www.it610.com/article/课程讲师ID") private String teacherId; @ApiModelProperty(value = "https://www.it610.com/article/课程专业ID") private String subjectId; @ApiModelProperty(value = "https://www.it610.com/article/课程专业父级ID") private String subjectParentId; @ApiModelProperty(value = "https://www.it610.com/article/课程标题") private String title; @ApiModelProperty(value = "https://www.it610.com/article/课程销售价格,设置为0则可免费观看") private BigDecimal price; @ApiModelProperty(value = "https://www.it610.com/article/总课时") private Integer lessonNum; @ApiModelProperty(value = "https://www.it610.com/article/课程封面图片路径") private String cover; @ApiModelProperty(value = "https://www.it610.com/article/课程简介") private String description; }

3.编写controller和service
课程和简介要是一对一的关系
解决:设置简介id就是课程id,修改简介实体类id生成策略—修改EduCourseDescription的id的策略为input
@ApiModelProperty(value = "https://www.it610.com/article/课程ID") @TableId(value = "https://www.it610.com/article/id", type = IdType.INPUT) private String id;

input表示手动设置/输入
controller
package com.atguigu.eduservice.controller; import com.atguigu.commonutils.R; import com.atguigu.eduservice.entity.EduCourse; import com.atguigu.eduservice.entity.vo.CourseInfoVo; import com.atguigu.eduservice.service.EduCourseService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/eduservice/course") @CrossOrigin public class EduCourseController {@Autowired private EduCourseService courseService; //添加课程基本信息 @PostMapping("addCourseInfo") public R addCourseInfo(@RequestBody CourseInfoVo courseInfoVo) { courseService.saveCourseInfo(courseInfoVo); return R.ok(); } }

serviceImpl
package com.atguigu.eduservice.service.impl; import com.atguigu.eduservice.entity.EduCourse; import com.atguigu.eduservice.entity.EduCourseDescription; import com.atguigu.eduservice.entity.vo.CourseInfoVo; import com.atguigu.eduservice.mapper.EduCourseMapper; import com.atguigu.eduservice.service.EduCourseDescriptionService; import com.atguigu.eduservice.service.EduCourseService; import com.atguigu.servicebase.exceptionhandler.GuliException; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class EduCourseServiceImpl extends ServiceImpl implements EduCourseService {@Autowired private EduCourseDescriptionService eduCourseDescriptionService; @Override public void saveCourseInfo(CourseInfoVo courseInfoVo) { //1.向课程表添加课程基本信息 //courseInfoVo对象转换成eduCourse对象 EduCourse eduCourse = new EduCourse(); BeanUtils.copyProperties(courseInfoVo,eduCourse); int insert = baseMapper.insert(eduCourse); if (insert == 0 ) { //添加失败 throw new GuliException(20001,"添加课程信息失败"); } //获取添加之后的课程id String cid = eduCourse.getId(); //2.向课程简介表添加 EduCourseDescription courseDescription = new EduCourseDescription(); courseDescription.setDescription(courseInfoVo.getDescription()); //手动设置简介id就是课程id courseDescription.setId(cid); eduCourseDescriptionService.save(courseDescription); } }

前端实现
【项目|7-谷粒学苑】1.添加课程管理路由
2.添加隐藏路由
hidden:true
{ path: '/course', component: Layout, redirect: '/course/list', name: '课程管理', meta: { title: '课程管理', icon: 'el-icon-s-help' }, children: [ { path: 'list', name: '课程列表', component: () => import('@/views/edu/course/list'), meta: { title: '课程列表', icon: 'table' } }, { path: 'info', name: '添加课程', component: () => import('@/views/edu/course/info'), meta: { title: '添加课程', icon: 'tree' } }, { path: 'chapter/:id', name: '编辑课程大纲', component: () => import('@/views/edu/course/chapter'), meta: { title: '编辑课程大纲', icon: 'tree' }, hidden: true }, { path: 'publish/:id', name: '发布课程', component: () => import('@/views/edu/course/publish'), meta: { title: '发布课程', icon: 'tree' }, hidden: true } ] },

3.Vue页面
课程基本信息页面 info.vue
>export default { name: 'Info', data() { return { active: 0, saveBtnDisabled: false } }, created() { console.log('info created') }, methods: {next() { console.log('next') // 跳转到第二步 this.$router.push({ path: '/course/chapter/1' }) } } } >

课程大纲页面 chapter.vue
>export default { name: 'Chapter', data() { return { saveBtnDisabled: false } }, created() { console.log('chapter created') }, methods: { previous() { console.log('previous') // 跳转到第一步 this.$router.push({ path: '/course/info' }) }, next() { console.log('next') // 跳转到第三步 this.$router.push({ path: '/course/publish/1' }) } } } >

课程发布页面 publish.vue
>export default { name: 'Publish', data() { return { saveBtnDisabled: false } }, created() { console.log('publish created') }, methods: { previous() { console.log('previous') // 跳转到第二步 this.$router.push({ path: '/course/chapter/1' }) }, next() { console.log('next') // 跳转到第三步 this.$router.push({ path: '/course/list' }) } } } >

组件模板
保存并下一步


3.定义api
import request from '@/utils/request'export default { // 添加课程列表 addCourseInfo(courseInfo) { return request({ url: `/eduservice/course/addCourseInfo`, method: 'post', data: courseInfo }) } }

4.编写表单页面,实现接口调用
5.添加之后,返回课程id
在controller中增加返回的内容
@RestController @RequestMapping("/eduservice/course") @CrossOrigin public class EduCourseController {@Autowired private EduCourseService courseService; //添加课程基本信息 @PostMapping("addCourseInfo") public R addCourseInfo(@RequestBody CourseInfoVo courseInfoVo) { //返回添加之后课程id,为了后面添加大纲使用 String id = courseService.saveCourseInfo(courseInfoVo); return R.ok().data("courseId",id); } }

public interface EduCourseService extends IService {//添加课程基本信息 String saveCourseInfo(CourseInfoVo courseInfoVo); }

@Service public class EduCourseServiceImpl extends ServiceImpl implements EduCourseService {@Autowired private EduCourseDescriptionService eduCourseDescriptionService; @Override public String saveCourseInfo(CourseInfoVo courseInfoVo) { //1.向课程表添加课程基本信息 //courseInfoVo对象转换成eduCourse对象 EduCourse eduCourse = new EduCourse(); BeanUtils.copyProperties(courseInfoVo,eduCourse); int insert = baseMapper.insert(eduCourse); if (insert == 0 ) { //添加失败 throw new GuliException(20001,"添加课程信息失败"); } //获取添加之后的课程id String cid = eduCourse.getId(); //2.向课程简介表添加 EduCourseDescription courseDescription = new EduCourseDescription(); courseDescription.setDescription(courseInfoVo.getDescription()); //手动设置简介id就是课程id courseDescription.setId(cid); eduCourseDescriptionService.save(courseDescription); return cid; } }

前端获取接口的id
this.$router.push({ path: '/course/chapter/' + response.data.courseId })

讲师下拉列表
1.Info.vue
v-for进行遍历,label是下拉列表显示的内容,key是唯一标识

2.定义data
teacherList: [] // 封装所有讲师

3.查询所有讲师并且进行初始化
created() { // 初始化所有讲师 this.getListTeacher() }, methods: { // 查询所有的讲师 getListTeacher() { course.getListTeacher() .then(response => { this.teacherList = response.data.items }) },

3.定义api
// 查询所有讲师 getListTeacher() { return request({ url: `/eduservice/teacher/findAll`, method: 'get' }) }

课程分类的二级联动
第一次进入页面,显示所有一级,二级为空
选择了某个一级分类,显示选择一级中对应的二级分类
一级分类 1.组件模板

2.引入subject api
import subject from '@/api/edu/subject'

3.定义方法
created() { // 初始化所有讲师 this.getListTeacher() // 初始化一级课程分类 this.getOneSubject() }, methods: { // 查询所有一级分类 getOneSubject() { subject.getSubjectList() .then(response => { this.subjectOneList = response.data.list }) },

4.定义data
subjectOneList: [], // 封装一级分类 subjectTwoList: [] // 封装二级分类

二级分类 1.组件模板

2.定义方法
在一级分类的组件中注册change事件
@change 当一级分类改变就取对应的二级分类的值
@change="subjectLevelOneChanged"

// 点击某个一级分类,触发change,显示对应二级分类 subjectLevelOneChanged(value) { // value 就是一级分类的id // 遍历所有分类,包含一级和二级 for (let i = 0; i < this.subjectOneList.length; i++) { // 每个一级分类 var oneSubject = this.subjectOneList[i] // 判断 所有一级分类id 和点击一级分类id是否一样 if (value =https://www.it610.com/article/== oneSubject.id) { // 从一级分类获取里面所有的二级分类 this.subjectTwoList = oneSubject.children // 把二级分类的id值清空 this.courseInfo.subjectId ='' } } },

完整代码-info.vue
> import course from '@/api/edu/course' import subject from '@/api/edu/subject' export default { name: 'Info', data() { return { active: 0, saveBtnDisabled: false, courseInfo: { title: '', subjectId: '', // 二级分类id teacherId: '', subjectParentId: '', // 一级分类id lessonNum: 0, description: '', cover: '', price: 0 }, teacherList: [], subjectOneList: [], // 封装一级分类 subjectTwoList: [] // 封装二级分类 } }, created() { // 初始化所有讲师 this.getListTeacher() // 初始化一级课程分类 this.getOneSubject() }, methods: { // 点击某个一级分类,触发change,显示对应二级分类 subjectLevelOneChanged(value) { // value 就是一级分类的id // 遍历所有分类,包含一级和二级 for (let i = 0; i < this.subjectOneList.length; i++) { // 每个一级分类 var oneSubject = this.subjectOneList[i] // 判断 所有一级分类id 和点击一级分类id是否一样 if (value =https://www.it610.com/article/== oneSubject.id) { // 从一级分类获取里面所有的二级分类 this.subjectTwoList = oneSubject.children // 把二级分类的id值清空 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 }) }) } } } >

课程封面上传
1.组件模板
项目|7-谷粒学苑
文章图片

2.定义data数据
BASE_API: process.env.VUE_APP_BASE_API, // 接口API地址

3.上传方法
beforeAvatarUpload()对文件的类型和大小进行限制
methods: { // 上传封面成功调用的方法 handleAvatarSuccess(res, file) { this.courseInfo.cover = res.data.url }, // 上传之前调用的方法,对文件的大小和类型进行限制 beforeAvatarUpload(file) { const isJPG = file.type === 'image/jpeg' const isLt2M = file.size / 1024 / 1024 < 2if (!isJPG) { this.$message.error('上传头像图片只能是 JPG 格式!') } if (!isLt2M) { this.$message.error('上传头像图片大小不能超过 2MB!') } return isJPG && isLt2M },

Tinymce可视化编辑器
Tinymce是一个传统javascript插件,默认不能用于Vue.js,因此需要做一些特殊的整合步骤
1.复制文本编辑器组件
tinymce复制到components里面
tinymce4.7.5复制到static里面
2.配置html变量
找到build/webpack.dev.conf.js中添加配置
在这里插入代码片

    推荐阅读