一、课程发布表单-步骤导航

1.1 需求分析

在线教育_Day07-项目课程发布-添加课程信息_servlet

在线教育_Day07-项目课程发布-添加课程信息_html_02

 

在线教育_Day07-项目课程发布-添加课程信息_html_03

 

 

1.2 课程相关表关系

在线教育_Day07-项目课程发布-添加课程信息_java_04

 

二、添加课程基本信息后台

2.1 生成课程相关代码

使用代码生成器生成课程相关的代码。

在线教育_Day07-项目课程发布-添加课程信息_servlet_05

 

2.2 定义form表单对象

我们添加基本课程信息时,需要涉及到两张表edu_course和edu_course_description,同时我们在添加课时还需要指定课程的老师和课程的分类。添加的内容比较多,所以我们将前台提交的数据,进行封装为一个CourseInfoFormVo对象。

在entity/vo下创建CourseInfoForm类


@ApiModel(value = "课程基本信息", description = "编辑课程基本信息的表单对象")@Data
public 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 = "课程标题")
    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.3 定义控制层EduCourseController

前面我们已经使用代码生成工具,将代码生成完毕,我们只需要在EduCourseController.java编写代码即可。


@Api(description="课程管理")@CrossOrigin //跨域
@RestController
@RequestMapping("/eduservice/course")
public class EduCourseController {

    @Autowired
    private CourseService courseService;

    @ApiOperation(value = "新增课程")
    @PostMapping("/addCourseInfo")
    public R addCourseInfo(@RequestBody CourseInfoFormVo courseInfoFormVo) {

        String courseId = eduCourseService.saveCourseInfo(courseInfoFormVo);

        return R.ok().data("courseId",courseId);
    }
}


2.4 定义业务层方法CourseService

接口:CourseService.java


/**     * 保存课程和课程详情信息
     * @param courseInfoForm
     * @return 新生成的课程id
     */
void saveCourseInfo(CourseInfoFormVo courseInfoFormVo);


实现:CourseServiceImpl.java


/**
 * <p>
 * 课程 服务实现类
 * </p>
 *
 * @author zjl
 */
@Service
public class EduCourseServiceImpl extends ServiceImpl<EduCourseMapper, EduCourse> implements EduCourseService {

    @Autowired
    EduCourseDescriptionService eduCourseDescriptionService;

    // 添加课程信息
    @Override
    public void saveCourseInfo(CourseInfoFormVo courseInfoFormVo) {

        // 向课程表添加课程基本信息
        EduCourse eduCourse = new EduCourse();
        BeanUtils.copyProperties(courseInfoFormVo, eduCourse);
        int insert = this.baseMapper.insert(eduCourse);

        // 判断添加课程基本信息是否成功
        if (insert==0) {
            throw new EduException(20001, "添加课程基本信息失败!");

        }

        // 向课程简介表添加课程简介
        EduCourseDescription eduCourseDescription = new EduCourseDescription();
        eduCourseDescription.setDescription(courseInfoFormVo.getDescription());
        eduCourseDescriptionService.save(eduCourseDescription);


    }
}

2.5 Swagger测试

在线教育_Day07-项目课程发布-添加课程信息_html_06

 

查看数据库信息

在线教育_Day07-项目课程发布-添加课程信息_代码生成_07

 

在数据库分析中,课程表和课程简介表是一对一的关系,但是添加数据时,却发现不是一对一的关系。

在edu_course表中的id是1548334513809661953, 在edu_course_description表中的id是1548334513809661954

通过两个id值发现它们两个表是没有关系的。想要一对一关系,在edu_course_description表的id值应该是edu_course表中的id值 。

2.6 修改CourseServiceImpl

在添加完课程信息之后,获取课程的Id,再将课程的id设置到课程描述信息中即可。

在线教育_Day07-项目课程发布-添加课程信息_代码生成_08

 

同时修改EduCourseDescription类中的id主键策略,这个表中的id不需要生动生成。需要我们手动插入。

在线教育_Day07-项目课程发布-添加课程信息_代码生成_09

 

三、添加基本课程信息前端

3.1 添加课程管理路由


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


3.2 添加vue组件

在线教育_Day07-项目课程发布-添加课程信息_代码生成_10

 

3.3 整合步骤条组件

参考 http://element-cn.eleme.io/#/zh-CN/component/steps

3.3.1 课程信息页面

在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 // 保存按钮是否禁用
    }
  },

  created() {
    console.log('info created')
  },

  methods: {

    next() {
      console.log('next')
      this.$router.push({ path: '/course/chapter/1' })
    }
  }
}
</script>


3.3.2 课程大纲页面

在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 // 保存按钮是否禁用
    }
  },

  created() {
    console.log('chapter created')
  },

  methods: {
    previous() {
      console.log('previous')
      this.$router.push({ path: '/course/info/1' })
    },

    next() {
      console.log('next')
      this.$router.push({ path: '/course/publish/1' })
    }
  }
}
</script>


3.3.3 课程发布页面

在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 // 保存按钮是否禁用
    }
  },

  created() {
    console.log('publish created')
  },

  methods: {
    previous() {
      console.log('previous')
      this.$router.push({ path: '/course/chapter/1' })
    },

    publish() {
      console.log('publish')
      this.$router.push({ path: '/course/list' })
    }
  }
}
</script>


3.4 添加课程页面实现

3.4.1 定义api

在src/api/edu下创建course.js文件,添加以下内容


import request from '@/utils/request'export default {
  addCourseInfo(courseInfo) {
    return request({
      url: `/eduservice/course/addCourseInfo`,
      method: 'post',
      data: courseInfo
    })
  }


3.4.2 组件模板

在edu/course/info.vue中添加以下页面信息:


<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="next">保存并下一步</el-button>
  </el-form-item>
</el-form>


3.4.3 添加js


<script>import course from '@/api/edu/course.js'
export default {
  data() {
    return {
      saveBtnDisabled: false ,// 保存按钮是否禁用
      courseInfo:{
            title: '',
            subjectId: '',
            teacherId: '',
            lessonNum: 0,
            description: '',
            cover: '',
            price: 0
      }
    }
  },

  created() {
    console.log('info created')
  },

  methods: {

    saveOrUpdate() {
      console.log('next')
      course.addCourseInfo(this.courseInfo).then(response =>{
         this.$message({
          type: 'success',
          message: '保存成功!'
        })
          this.$router.push({ path: '/course/chapter/1' })
      })
      
    }
  }
}
</script>


3.4.4 添加之后,返回课程id

添加课程信息之后,后面在添加小节时,还是需要课程的id,所以我们要如下改造:

修改EduCourseController

在EduCourseController中的addCourseInfo方法,添加返回值


@PostMapping("/addCourseInfo")public R addCourseInfo(@RequestBody CourseInfoFormVo courseInfoFormVo) {

    String courseId = eduCourseService.saveCourseInfo(courseInfoFormVo);

    return R.ok().data("courseId",courseId);
}


修改EduCourseService

添加返回值String

public interface EduCourseService extends IService<EduCourse> {    String saveCourseInfo(CourseInfoFormVo courseInfoFormVo);
}



// 添加课程信息@Override
public String saveCourseInfo(CourseInfoFormVo courseInfoFormVo) {

    // 向课程表添加课程基本信息
    EduCourse eduCourse = new EduCourse();
    BeanUtils.copyProperties(courseInfoFormVo, eduCourse);
    int insert = this.baseMapper.insert(eduCourse);

    // 判断添加课程基本信息是否成功
    if (insert==0) {
        throw new EduException(20001, "添加课程基本信息失败!");

    }

    // 获取添加课程之后的id
    String cid = eduCourse.getId();

    // 向课程简介表添加课程简介
    EduCourseDescription eduCourseDescription = new EduCourseDescription();
    eduCourseDescription.setId(cid);
    eduCourseDescription.setDescription(courseInfoFormVo.getDescription());
    eduCourseDescriptionService.save(eduCourseDescription);

    return cid;
}


修改info.vue

修改edu/course/info.vue中的saveOrUpdate方法


saveOrUpdate() {      console.log('next')
      course.addCourseInfo(this.courseInfo).then(response =>{
         this.$message({
          type: 'success',
          message: '保存成功!'
        })
          this.$router.push({ path: '/course/chapter/'+response.data.courseId })
      })
      
    }


在线教育_Day07-项目课程发布-添加课程信息_html_11

 

3.5 讲师下拉列表

3.5.1 组件模板

将以下代码添加到views/edu/course/info.vue中


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


3.5.2 定义获取讲师列表接口

直接调用EduTeacherController中的findAll接口即可。之前我们都已经写过。

在线教育_Day07-项目课程发布-添加课程信息_html_12

 

3.5.3 定义api

在api/edu/course.js中定义查询所有讲师


getListTeacher() {    return request({
        url: `/eduservice/edu-teacher/findAll`,
        method: 'get'
    })
},

3.5.4 组件脚本

定义data,不要定义在courseInfo中


teacherList: [] // 讲师列表


表单初始化时获取讲师列表


created() {    console.log('info created')
    // 初始化讲师列表
    this.getListTeacher()
},
    methods: {
        // 查询所有讲师
        getListTeacher(){
            course.getListTeacher().then(response =>{
                this.teacherList = response.data.items
            })
        },

    }


3.5.5 效果展示

在线教育_Day07-项目课程发布-添加课程信息_java_13

3.6 课程分类多级联动的实现

需求

在线教育_Day07-项目课程发布-添加课程信息_servlet_14

 

 

3.6.1 获取一级分类

组件数据定义

在views/edu/course/info.vue的data中定义一级分类和二级分类集合.最后在courseInfo中添加subjectParentId


subjectOneList: [],//一级分类列表subjectTwoList: []//二级分类列表


在线教育_Day07-项目课程发布-添加课程信息_一对一_15

 

组件模板

在course/info.vue中定义一级分类下拉框


<!-- 一级分类 --><el-form-item label="课程类别">
    <el-select
               v-model="courseInfo.subjectParentId"
               placeholder="请选择">
        <el-option
                   v-for="subject in subjectOneList"
                   :key="subject.id"
                   :label="subject.title"
                   :value="subject.id"/>
    </el-select>
</el-form-item>


组件脚本

在course/info.vue中引入subject.js


import subject from '@/api/edu/subject.js'


在线教育_Day07-项目课程发布-添加课程信息_代码生成_16

 

定义方法,查询所有的一级分类


created() {    console.log('info created')
    // 初始化讲师列表
    this.getListTeacher()
    // 初始化一级分类列表
    this.getOneSubjectList()
},

    methods: {
        // 查询所有的一级分类
        getOneSubjectList(){
            subject.getSubjectList().then(response =>{
                this.subjectOneList = response.data.list
            })
        },
    }


3.6.2 级联显示二级分类

组件模板


<!-- 二级分类 --><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>


注册change事件

在一级分类的<el-select>组件中注册change事件

<el-select @change="subjectLevelOneChanged" ......


定义change事件方法


subjectLevelOneChanged(value) {    console.log(value);
    // 遍历所有的分类,包含一级分类和二级分类
    for (let i = 0; i < this.subjectOneList.length; i++) {
        if (this.subjectOneList[i].id === value) {
            this.subjectTwoList = this.subjectOneList[i].children;
            // 清空二级分类
            this.courseInfo.subjectId = "";
        }
    }
},


3.7 课程封面

3.7.1 整合上传组件

参考 http://element-cn.eleme.io/#/zh-CN/component/upload 用户头像上传

3.7.2 上传默认封面

创建文件夹cover,上传默认的课程封面

在线教育_Day07-项目课程发布-添加课程信息_html_17

 

3.7.3 定义data数据


BASE_API: process.env.BASE_API // 接口API地址


3.7.4 组件模板

在info.vue中添加上传组件模板


<!-- 课程封面--><el-form-item label="课程封面">

  <el-upload
    :show-file-list="false"
    :on-success="handleAvatarSuccess"
    :before-upload="beforeAvatarUpload"
    :action="BASE_API+'/eduoss/file/upload'"
    class="avatar-uploader">
     <img :src="courseInfo.cover" width="250px" height="150px" />
  </el-upload>

</el-form-item>


3.7.5 定义默认封面


cover: "https://zjledu.oss-cn-beijing.aliyuncs.com/cover/%E9%BB%98%E8%AE%A4%E8%AF%BE%E7%A8%8B%E5%B0%81%E9%9D%A2.jpg",


3.7.6 结果回调


handleAvatarSuccess(res, file) {  console.log(res)// 上传响应
  console.log(URL.createObjectURL(file.raw))// base64编码
  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
}


3.7.8 综合测试

在线教育_Day07-项目课程发布-添加课程信息_java_18

 

四、富文本编辑器Tinymce

在课程基本信息添加页面中,集成富文本编辑器

4.1 组件初始化

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

4.1.1 复制脚本库

将资料中的components/Tinymce复制到前端工程中components目录下

在线教育_Day07-项目课程发布-添加课程信息_代码生成_19

 

将资料中的\static\tinymce4.7.5得到到static目录下

在线教育_Day07-项目课程发布-添加课程信息_html_20

 

4.1.2 配置html变量

在 /build/webpack.dev.conf.js 中添加配置 使在html页面中可是使用这里定义的BASE_URL变量

在线教育_Day07-项目课程发布-添加课程信息_一对一_21

 


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


注意:配置完成之后,最好重启一下前端服务器

4.1.3 引入js脚本

在/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>


在线教育_Day07-项目课程发布-添加课程信息_代码生成_22

 

4.2 使用文本编辑器

为了让Tinymce能用于Vue.js项目,vue-element-admin-master对Tinymce进行了封装,下面我们将它引入到我们的课程信息页面

4.2.1 引入组件

在课程信息info.vue中引入 Tinymce


import Tinymce from '@/components/Tinymce'export default {
  components: { Tinymce },
  ......
}


在线教育_Day07-项目课程发布-添加课程信息_代码生成_23

 

4.2.2 引用组件模板


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


4.2.3 组件样式

在info.vue文件的最后添加如下代码,调整上传图片按钮的高度


<style scoped>    .tinymce-container {
      line-height: 29px;
    }
</style>


4.2.5 图片的base64编码

Tinymce中的图片上传功能直接存储的是图片的base64编码,因此无需图片服务器

4.2.6 测试

测试会出现添加课程二级分类时,会没有数据。是因为,在CourseInfoFormVo中,没有subjectParentId这个字段。在CourseInfoFormVo类中添加该字段即可。

@ApiModelProperty(value = "一级分类ID")private String subjectParentId;