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 = "课程讲师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;
}

3.编写controller和service
课程和简介要是一对一的关系
解决:设置简介id就是课程id,修改简介实体类id生成策略—修改EduCourseDescription的id的策略为input

@ApiModelProperty(value = "课程ID")
    @TableId(value = "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<EduCourseMapper, EduCourse> 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);
    }
}

前端实现

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

<template>
  <div class="app-container">
    <h2 style="text-align: center">发布新课程</h2>
    <el-steps :active="1" align-center style="margin: 20px 15px;" finish-status="success">
      <el-step title="填写课程基本信息" />
      <el-step title="创建课程大纲" />
      <el-step title="最终发布" />
    </el-steps>

    <el-form label-width="180px">
      <el-form-item>
        <el-button
          :disabled="saveBtnDisabled"
          type="primary"
          style="margin-top: 10px"
          @click="next"
        >保存并下一步</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>

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' })
    }
  }
}
</script>
<style scoped>
</style>

课程大纲页面 chapter.vue

<template>
  <div class="app-container">
    <h2 style="text-align: center">发布新课程</h2>
    <el-steps :active="2" align-center style="margin: 20px 15px;" finish-status="success">
      <el-step title="填写课程基本信息" />
      <el-step title="创建课程大纲" />
      <el-step title="最终发布" />
    </el-steps>

    <el-form label-width="180px">
      <el-form-item>
        <el-button
          :disabled="saveBtnDisabled"
          type="primary"
          style="margin-top: 10px;"
          @click="previous"
        >上一步</el-button>
        <el-button
          :disabled="saveBtnDisabled"
          type="primary"
          style="margin-top: 10px;"
          @click="next"
        >下一步</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>

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' })
    }
  }
}
</script>
<style scoped>
</style>

课程发布页面 publish.vue

<template>
  <div class="app-container">
    <h2 style="text-align: center">发布新课程</h2>
    <el-steps :active="3" align-center style="margin: 20px 15px;" finish-status="success">
      <el-step title="填写课程基本信息" />
      <el-step title="创建课程大纲" />
      <el-step title="最终发布" />
    </el-steps>

    <el-form label-width="180px">
      <el-form-item>
        <el-button
          :disabled="saveBtnDisabled"
          type="primary"
          style="margin-top: 10px;"
          @click="previous"
        >返回修改</el-button>
        <el-button
          :disabled="saveBtnDisabled"
          type="primary"
          style="margin-top: 10px;"
          @click="next"
        >发布课程</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>

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' })
    }
  }
}
</script>
<style scoped>
</style>

组件模板

    <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 -->
      <!-- 课程封面 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="next">保存并下一步</el-button>
      </el-form-item>
    </el-form>
<!-- 课程简介 TODO -->
      <el-form-item label="课程简介">
        <el-input v-model="courseInfo.description" placeholder=" "/>
      </el-form-item>

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<EduCourse> {

    //添加课程基本信息
    String saveCourseInfo(CourseInfoVo courseInfoVo);
}
@Service
public class EduCourseServiceImpl extends ServiceImpl<EduCourseMapper, EduCourse> 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是唯一标识

 <!-- 课程讲师 -->
      <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>

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.组件模板

 <!-- 课程分类 -->
      <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-form-item>

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.组件模板

       <el-select
          v-model="courseInfo.subjectId"
          placeholder="二级分类"
          style="margin-left: 20px;"
        >
          <el-option
            v-for="subject in subjectTwoList"
            :key="subject.id"
            :label="subject.title"
            :value="subject.id"
          />
        </el-select>

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 === oneSubject.id) {
          // 从一级分类获取里面所有的二级分类
          this.subjectTwoList = oneSubject.children
          // 把二级分类的id值清空
          this.courseInfo.subjectId = ''
        }
      }
    },

完整代码-info.vue

<template>
  <div class="app-container">
    <h2 style="text-align: center">发布新课程</h2>
    <el-steps :active="1" align-center style="margin: 20px 15px;" finish-status="success">
      <el-step title="填写课程基本信息" />
      <el-step title="创建课程大纲" />
      <el-step title="最终发布" />
    </el-steps>

    <el-form label-width="220px">
      <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="二级分类"
          style="margin-left: 20px;"
        >
          <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 v-model="courseInfo.lessonNum" :min="0" 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 v-model="courseInfo.price" :min="0" controls-position="right" placeholder="免费课程请设置为0元" /></el-form-item>
    </el-form>
    <el-form label-width="180px">
      <el-form-item>
        <el-button
          :disabled="saveBtnDisabled"
          type="primary"
          style="margin-top: 10px"
          @click="saveOrUpdate"
        >保存并下一步</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
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 === 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 })
        })
    }
  }
}
</script>
<style scoped>
</style>

课程封面上传

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.定义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 < 2

      if (!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中添加配置

在这里插入代码片

你可能感兴趣的:(项目,java,开发语言,vue.js,springboot,oss)