【项目】小帽学堂(八)

小帽学堂

9. 添加课程信息完善(课程简介)

【项目】小帽学堂(八)_第1张图片

  • Tinymce可视化编辑器

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

    • 复制脚本库
      • 将脚本库复制到项目的static目录下(在vue-element-admin-master的static路径下)
    • 配置html变量
      • 在 vue-admin/build/webpack.dev.conf.js 中添加配置
      • 使在html页面中可是使用这里定义的BASE_URL变量
    new HtmlWebpackPlugin({
        ......,
        templateParameters: {
            BASE_URL: config.dev.assetsPublicPath + config.dev.assetsSubDirectory
        }
    })
    
    • 引入js脚本

      • 在vue-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>
      
  • 组件引入

    • 为了让Tinymce能用于Vue.js项目,vue-element-admin-master对Tinymce进行了封装
    • 复制组件 src/components/Tinymce
    • 引入组件


<el-form-item label="课程简介">
    <tinymce :height="300" 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>
  • 图片的base64编码
    • Tinymce中的图片上传功能直接存储的是图片的base64编码,因此无需图片服务器

10. 课程大纲列表(后端)

【项目】小帽学堂(八)_第2张图片

@Data
public class ChapterVo {
    private String id;
    private String title;

    // 表示小节
    private List<VideoVo> children = new ArrayList<>();
}
@Data
public class VideoVo {
    private String id;
    private String title;
}
@RestController
@RequestMapping("/eduservice/chapter")
@CrossOrigin
public class EduChapterController {
    @Autowired
    private EduChapterService chapterService;

    // 课程大纲列表,根据课程id进行查询
    @GetMapping("getChapterVideo/{courseId}")
    public R getChapterVideo(@PathVariable String courseId) {
        List<ChapterVo> list = chapterService.getChapterVideoByCourseId(courseId);
        return R.ok().data("allChapterVideo", list);
    }
}
public interface EduChapterService extends IService<EduChapter> {

    // 课程大纲列表,根据课程id进行查询
    List<ChapterVo> getChapterVideoByCourseId(String courseId);
}
@Service
public class EduChapterServiceImpl extends ServiceImpl<EduChapterMapper, EduChapter> implements EduChapterService {

    @Autowired
    private EduVideoService videoService;   // 注入小节service

    // 课程大纲列表,根据课程id进行查询
    @Override
    public List<ChapterVo> getChapterVideoByCourseId(String courseId) {
        // 1.根据课程id查询课程里面所有的章节
        QueryWrapper<EduChapter> wrapperChapter = new QueryWrapper<>();
        wrapperChapter.eq("course_id", courseId);
        List<EduChapter> eduChapterList = baseMapper.selectList(wrapperChapter);
        // 2.根据课程id查询课程里面所有的小节
        QueryWrapper<EduVideo> wrapperVideo = new QueryWrapper<>();
        wrapperVideo.eq("course_id", courseId);
        List<EduVideo> eduVideoList = videoService.list(wrapperVideo);

        // 创建list集合,用于最终封装数据
        List<ChapterVo> finalList = new ArrayList<>();
        // 3.遍历查询章节list集合进行封装
        // 遍历查询章节list集合
        for (int i = 0; i < eduChapterList.size(); i++) {
            // 每个章节
            EduChapter eduChapter = eduChapterList.get(i);
            // eduChapter 对象值复制到ChapterVo里面
            ChapterVo chapterVo = new ChapterVo();
            BeanUtils.copyProperties(eduChapter, chapterVo);
            // 把chapterVo放到最终list集合
            finalList.add(chapterVo);

            // 创建集合,用于封装章节的小节
            List<VideoVo> videoList = new ArrayList<>();
            // 4.遍历查询小节list集合进行封装
            for (int m = 0; m < eduVideoList.size(); m++) {
                // 得到每个小节
                EduVideo eduVideo = eduVideoList.get(m);
                // 判断:小节里面chapterId和章节里面的id是否一样
                if(eduVideo.getChapterId().equals(eduChapter.getId())) {
                    // 进行封装
                    VideoVo videoVo = new VideoVo();
                    BeanUtils.copyProperties(eduVideo,videoVo);
                    // 放到小节封装集合
                    videoList.add(videoVo);
                }
            }

            // 把封装之后小节list集合,放到章节对象里面
            chapterVo.setChildren(videoList);
        }
        return finalList;
    }
}

11. 课程大纲列表(前端)

  • 定义api
// src\api\edu\chapter.js
import request from '@/utils/request'

export default {
    // 1. 根据课程id获取章节和小节数据列表
    getAllChapterVideo(courseId) {
      return request({
        url: '/eduservice/chapter/getChapterVideo/' + courseId,
        method: 'get'
      })
    }
}
  • 定义组件
<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>


<ul class="chanpterList">
    <li
        v-for="chapter in chapterVideoList"
        :key="chapter.id">
        <p>
            {{ chapter.title }}
        p>
        
        <ul class="chanpterList videoList">
            <li
                v-for="video in chapter.children"
                :key="video.id">
                <p>{{ video.title }}
                p>
            li>
        ul>
    li>
ul>
<div>
    <el-button @click="previous">上一步el-button>
    <el-button :disabled="saveBtnDisabled" type="primary" @click="next">下一步el-button>
div>
  div>
template>
<script>
import chapter from '@/api/edu/chapter'
export default {
  data() {
    return {
      saveBtnDisabled: false, // 保存按钮是否禁用
      courseId: '', // 课程id
      chapterVideoList: []
    }
  },
  created() {
    // 获取路由的id值
    if(this.$route.params && this.$route.params.id) {
      this.courseId = this.$route.params.id
      // 根据课程id查询章节和小节
      this.getChapterVideo()
    }
  },
  methods: {
    // 根据课程id查询章节和小节
    getChapterVideo() {
      chapter.getAllChapterVideo(this.courseId)
        .then(response => {
          this.chapterVideoList = response.data.allChapterVideo
        })
    },
    previous(){
        this.$router.push({ path: '/course/info/1' })
    },
    next() {
        this.$router.push({ path: '/course/publish/1' })
    }
  }
}
script>

<style scoped>
.chanpterList{
    position: relative;
    list-style: none;
    margin: 0;
    padding: 0;
}
.chanpterList li{
  position: relative;
}
.chanpterList p{
  float: left;
  font-size: 20px;
  margin: 10px 0;
  padding: 10px;
  height: 70px;
  line-height: 50px;
  width: 100%;
  border: 1px solid #DDD;
}
.chanpterList .acts {
    float: right;
    font-size: 14px;
}
.videoList{
  padding-left: 50px;
}
.videoList p{
  float: left;
  font-size: 14px;
  margin: 10px 0;
  padding: 10px;
  height: 50px;
  line-height: 30px;
  width: 100%;
  border: 1px dotted #DDD;
}

style>

12. 修改课程信息(后端)

【项目】小帽学堂(八)_第3张图片

@RestController
@RequestMapping("/eduservice/course")
@CrossOrigin
public class EduCourseController {

    @Autowired
    private EduCourseService courseService;

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

    // 修改课程信息
    @PostMapping("updateCourseInfo")
    public R updateCourseInfo(@RequestBody CourseInfoVo courseInfoVo) {
        courseService.updateCourseInfo(courseInfoVo);
        return R.ok();
    }
}
public interface EduCourseService extends IService<EduCourse> {

    // 根据课程id查询课程基本信息
    CourseInfoVo getCourseInfo(String courseId);

    // 修改课程信息
    void updateCourseInfo(CourseInfoVo courseInfoVo);
}
@Service
public class EduCourseServiceImpl extends ServiceImpl<EduCourseMapper, EduCourse> implements EduCourseService {

    @Autowired
    private EduCourseDescriptionService courseDescriptionService;
    
    // 根据课程id查询课程基本信息
    @Override
    public CourseInfoVo getCourseInfo(String courseId) {

        // 1.查询课程表
        EduCourse eduCourse = baseMapper.selectById(courseId);
        CourseInfoVo courseInfoVo = new CourseInfoVo();
        BeanUtils.copyProperties(eduCourse, courseInfoVo);

        // 2.查询描述表
        EduCourseDescription courseDescription = courseDescriptionService.getById(courseId);
        courseInfoVo.setDescription(courseDescription.getDescription());

        return courseInfoVo;
    }

    // 修改课程信息
    @Override
    public void updateCourseInfo(CourseInfoVo courseInfoVo) {
        // 1.修改课程表
        EduCourse eduCourse = new EduCourse();
        BeanUtils.copyProperties(courseInfoVo, eduCourse);
        int update = baseMapper.updateById(eduCourse);
        if(update == 0) {
            throw new LemonException(20001, "修改课程信息失败");
        }

        // 2.修改描述表
        EduCourseDescription description = new EduCourseDescription();
        description.setId(courseInfoVo.getId());
        description.setDescription(courseInfoVo.getDescription());
        courseDescriptionService.updateById(description);
    }
}

13. 修改课程信息(前端)

// src\api\edu\course.js
import request from '@/utils/request'

export default {
    // 1. 添加课程信息
    addCourseInfo(courseInfo) {
      return request({
        url: '/eduservice/course/addCourseInfo',
        method: 'post',
        data: courseInfo
      })
    },
    // 2. 查询所有讲师
    getListTeacher() {
      return request({
        url: '/eduservice/teacher/findAll',
        method: 'get'
      })
    },
    // 3. 根据课程id查询课程基本信息
    getCourseInfoId(id) {
      return request({
        url: '/eduservice/course/getCourseInfo/' + id,
        method: 'get'
      })
    },
    // 4. 修改课程信息
    updateCourseInfo(courseInfo) {
      return request({
        url: '/eduservice/course/updateCourseInfo/',
        method: 'post',
        data: courseInfo
      })
    },
}
// src\views\edu\course\chapter.vue
methods: {
    previous(){
        this.$router.push({ path: '/course/info/' + this.courseId })
    },
    next() {
        this.$router.push({ path: '/course/publish/' + this.courseId })
    }
// src\views\edu\course\chapter.vue
created() {
    // 获取路由id值
    if(this.$route.params && this.$route.params.id) {
      this.courseId = this.$route.params.id
      // 调用根据id查询课程的方法
      this.getInfo()
    }
    // 初始化所有讲师
    this.getListTeacher()
    // 初始化一级分类
    this.getOneSubject()
  },
methods: {
    // 根据课程id查询
    getInfo() {
      course.getCourseInfoId(this.courseId)
        .then(response => {
          this.courseInfo = response.data.courseInfoVo
        })
    }
}
// src\views\edu\course\info.vue
<script>
import course from '@/api/edu/course'
import subject from '@/api/edu/subject'
import Tinymce from '@/components/Tinymce'  // 引入组件
export default {
  // 声明组件
  components: {Tinymce},
  data() {
    return {
      saveBtnDisabled: false, // 保存按钮是否禁用
      courseInfo: {
        title: '',
        subjectId: '',        // 二级分类ID
        subjectParentId: '',  // 一级分类ID
        teacherId: '',
        lessonNum: 0,
        description: '',
        cover: '/static/04.jpg',
        price: 0
      },
      courseId: '',
      BASE_API: process.env.BASE_API, // 接口API地址
      teacherList: [], // 封装所有的讲师
      subjectOneList: [], // 一级分类
      subjectTwoList: []  // 二级分类
    }
  },
  created() {
    // 获取路由id值
    if(this.$route.params && this.$route.params.id) {
      this.courseId = this.$route.params.id
      // 调用根据id查询课程的方法
      this.getInfo()
    } else {
        // 初始化所有讲师
        this.getListTeacher()
        // 初始化一级分类
        this.getOneSubject()
    }
  },
  watch: {
    $route(to,from) {
      this.courseInfo = {
        title: '',
        subjectId: '',        // 二级分类ID
        subjectParentId: '',  // 一级分类ID
        teacherId: '',
        lessonNum: 0,
        description: '',
        cover: '/static/04.jpg',
        price: 0
      },
      this.getListTeacher();
      this.getOneSubject()
    }
  },
  methods: {
    // 根据课程id查询
    getInfo() {
      course.getCourseInfoId(this.courseId)
        .then(response => {
          this.courseInfo = response.data.courseInfoVo
          // 1 查询所有的分类,包含一级和二级
          subject.getSubjectList()
            .then(response => {
                // 2 获取所有一级分类
                this.subjectOneList = response.data.list

                // 3 把所有的一级分类数组进行遍历
                for(var i = 0; i < this.subjectOneList.length; i++) {
                  // 获取一级分类
                  var oneSubject = this.subjectOneList[i]
                  // 比较当前 courseInfo 里面一级分类id和所有的一级分类id
                  if(this.courseInfo.subjectParentId == oneSubject.id) {
                    // 获取一级分类所有的二级分类
                    this.subjectTwoList = oneSubject.children
                  }
                }
            })
            // 初始化所有讲师
            this.getListTeacher()
        })
    },
    // 查询所有的一级分类
    getOneSubject() {
      subject.getSubjectList()
        .then(response => {
          this.subjectOneList = response.data.list
        })
    },
    // 添加课程
    addCourse() {
      course.addCourseInfo(this.courseInfo)
      .then(response => {
        // 提示
        this.$message({
          type: 'success',
          message: '添加课程信息成功!'
        });
        // 跳转到第二步
        this.$router.push({ path: '/course/chapter/' + response.data.courseId })
      })
    },
    // 修改课程
    updateCourse() {
      course.updateCourseInfo(this.courseInfo)
        .then(response => {
          // 提示
          this.$message({
            type: 'success',
            message: '修改课程信息成功!'
          });
          // 跳转到第二步
          this.$router.push({ path: '/course/chapter/' + this.courseId })
        })
    },
    saveOrUpdate() {
      // 判断添加还是修改
      if(!this.courseId) {
        // 添加
        this.addCourse()
      } else {
        this.updateCourse()
      }
    }
  }
}
script>

【项目】小帽学堂(八)_第4张图片

14. 章节后端接口开发

【项目】小帽学堂(八)_第5张图片

@RestController
@RequestMapping("/eduservice/chapter")
@CrossOrigin
public class EduChapterController {
    @Autowired
    private EduChapterService chapterService;

    // 添加章节
    @PostMapping("addChapter")
    public R addChapter(@RequestBody EduChapter eduChapter) {
        chapterService.save(eduChapter);
        return R.ok();
    }

    // 根据章节id查询
    @GetMapping("getChapterInfo/{chapterId}")
    public R getChapterInfo(@PathVariable String chapterId) {
        EduChapter eduChapter = chapterService.getById(chapterId);
        return R.ok().data("chapter", eduChapter);
    }

    // 修改章节
    @PostMapping("updateChapter")
    public R updateChapter(@RequestBody EduChapter eduChapter) {
        chapterService.updateById(eduChapter);
        return R.ok();
    }

    // 删除的方法
    @DeleteMapping("{chapterId}")
    public R deleteChapter(@PathVariable String chapterId) {
        boolean flag = chapterService.deleteChapter(chapterId);
        if(flag) {
            return R.ok();
        } else {
            return R.error();
        }
    }
}
public interface EduChapterService extends IService<EduChapter> {
    // 删除章节的方法
    boolean deleteChapter(String chapterId);
}
@Service
public class EduChapterServiceImpl extends ServiceImpl<EduChapterMapper, EduChapter> implements EduChapterService {

    @Autowired
    private EduVideoService videoService;   // 注入小节service

    // 删除章节的方法
    @Override
    public boolean deleteChapter(String chapterId) {
        // 根据chapterId 章节id查询小节表,如果查出数据,不进行删除
        QueryWrapper<EduVideo> wrapper = new QueryWrapper<>();
        wrapper.eq("chapter_id", chapterId);
        int count = videoService.count(wrapper);
        // 判断
        if(count > 0) { // 查询出小节,不进行删除
            throw new LemonException(20001, "不能删除");
        } else {    // 不能查询数据,进行删除
            // 删除章节
            int result = baseMapper.deleteById(chapterId);
            // 成功 1>0
            return result > 0;
        }
    }
}

15. 添加章节(前端)

// src\api\edu\chapter.js
import request from '@/utils/request'

export default {
    // 添加章节
    addChapter(chapter) {
      return request({
        url: '/eduservice/chapter/addChapter',
        method: 'post',
        data: chapter
      })
    },
    // 根据id查询章节
    getChapter(chapterId) {
      return request({
        url: '/eduservice/chapter/getChapterInfo/' + chapterId,
        method: 'get'
      })
    },
    // 修改章节
    updateChapter(chapter) {
      return request({
        url: '/eduservice/chapter/updateChapter/',
        method: 'post',
        data: chapter
      })
    },
    // 删除章节
    deleteChapter(chapterId) {
      return request({
        url: '/eduservice/chapter/' + chapterId,
        method: 'delete'
      })
    }
}
// src\views\edu\course\chapter.vue
<script>
import chapter from '@/api/edu/chapter'
export default {
  data() {
    return {
      saveBtnDisabled: false, // 保存按钮是否禁用
      courseId: '', // 课程id
      chapterVideoList: [],
      chapter: {  // 封装章节数据
        title: '',
        sort: 0
      },
      dialogChapterFormVisible: false // 章节弹框
    }
  },
  methods: {
    // 弹出添加章节页面
    openChapterDialog() {
      // 弹框
      this.dialogChapterFormVisible = true
      // 表单数据清空
      this.chapter.title = ''
      this.chapter.sort = 0
    },
    // 添加章节
    addChapter() {
      // 设置课程id到chapter对象里面
      this.chapter.courseId = this.courseId
      chapter.addChapter(this.chapter)
        .then(response => {
          // 关闭弹框
          this.dialogChapterFormVisible = false
          // 提示
          this.$message({
            type: 'success',
            message: '添加章节成功!'
          });
          // 刷新页面
          this.getChapterVideo()
        })
    },
    saveOrUpdate() {
      // 设置课程id到chapter对象里面
      this.addChapter()
    }
  }
}
script>

16. 修改/删除章节

// src\views\edu\course\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-button type="text" @click="openChapterDialog()">添加章节el-button>

<ul class="chanpterList">
    <li
        v-for="chapter in chapterVideoList"
        :key="chapter.id">
        <p>
            {{ chapter.title }}
            <span class="acts">
                <el-button style="" type="text" @click="openEditChapter(chapter.id)">编辑el-button>
                <el-button type="text" @click="removeChapter(chapter.id)">删除el-button>
            span>
        p>
        
        <ul class="chanpterList videoList">
            <li
                v-for="video in chapter.children"
                :key="video.id">
            <p>{{ video.title }}
            p>
            li>
        ul>
    li>
ul>
<div>
    <el-button @click="previous">上一步el-button>
    <el-button :disabled="saveBtnDisabled" type="primary" @click="next">下一步el-button>
div>



<el-dialog :visible.sync="dialogChapterFormVisible" title="添加章节">
    <el-form :model="chapter" label-width="120px">
        <el-form-item label="章节标题">
            <el-input v-model="chapter.title"/>
        el-form-item>
        <el-form-item label="章节排序">
            <el-input-number v-model="chapter.sort" :min="0" controls-position="right"/>
        el-form-item>
    el-form>
    <div slot="footer" class="dialog-footer">
        <el-button @click="dialogChapterFormVisible = false">取 消el-button>
        <el-button type="primary" @click="saveOrUpdate">确 定el-button>
    div>
el-dialog>
  div>
template>
<script>
import chapter from '@/api/edu/chapter'
export default {
  data() {
    return {
      saveBtnDisabled: false, // 保存按钮是否禁用
      courseId: '', // 课程id
      chapterVideoList: [],
      chapter: {  // 封装章节数据
        title: '',
        sort: 0
      },
      dialogChapterFormVisible: false // 章节弹框
    }
  },
  created() {
    // 获取路由的id值
    if(this.$route.params && this.$route.params.id) {
      this.courseId = this.$route.params.id
      // 根据课程id查询章节和小节
      this.getChapterVideo()
    }
  },
  methods: {
    // 删除章节
    removeChapter(chapterId) {
      this.$confirm('此操作将删除章节, 是否继续?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => { // 点击确定,执行then方法
          // 调用删除方法
          chapter.deleteChapter(chapterId)
          .then(response => { // 删除成功
            // 提示信息
            this.$message({
              type: 'success',
              message: '删除成功!'
            });
            // 回到列表页面
            this.getChapterVideo()
          })
        })
    },
    // 修改章节弹框数据回显
    openEditChapter(chapterId) {
      // 弹框
      this.dialogChapterFormVisible = true
      // 调用接口
      chapter.getChapter(chapterId)
        .then(response => {
          this.chapter = response.data.chapter
        })
    },
    // 弹出添加章节页面
    openChapterDialog() {
      // 弹框
      this.dialogChapterFormVisible = true
      // 表单数据清空
      this.chapter.title = ''
      this.chapter.sort = 0
    },
    // 添加章节
    addChapter() {
      // 设置课程id到chapter对象里面
      this.chapter.courseId = this.courseId
      chapter.addChapter(this.chapter)
        .then(response => {
          // 关闭弹框
          this.dialogChapterFormVisible = false
          // 提示
          this.$message({
            type: 'success',
            message: '添加章节成功!'
          });
          // 刷新页面
          this.getChapterVideo()
        })
    },
    // 修改章节的方法
    updateChapter() {
      chapter.updateChapter(this.chapter)
        .then(response => {
          // 关闭弹框
          this.dialogChapterFormVisible = false
          // 提示
          this.$message({
            type: 'success',
            message: '修改章节成功!'
          });
          // 刷新页面
          this.getChapterVideo()
        })
    },
    saveOrUpdate() {
      if(!this.chapter.id) {
        this.addChapter()
      } else {
        this.updateChapter()
      }
      // 设置课程id到chapter对象里面
      this.addChapter()
    },
    // 根据课程id查询章节和小节
    getChapterVideo() {
      chapter.getAllChapterVideo(this.courseId)
        .then(response => {
          this.chapterVideoList = response.data.allChapterVideo
        })
    },
    previous(){
        this.$router.push({ path: '/course/info/' + this.courseId })
    },
    next() {
        this.$router.push({ path: '/course/publish/' + this.courseId })
    }
  }
}
script>

17. 添加/删除/修改小节

@Data
public class VideoVo {
    @ApiModelProperty(value = "视频ID")
    private String id;
    @ApiModelProperty(value = "节点名称")
    private String title;
    @ApiModelProperty(value = "课程ID")
    private String courseId;
    @ApiModelProperty(value = "章节ID")
    private String chapterId;
    @ApiModelProperty(value = "视频资源")
    private String videoSourceId;
    @ApiModelProperty(value = "显示排序")
    private Integer sort;
    @ApiModelProperty(value = "是否可以试听:0默认 1免费")
    private Boolean free;
}
@RestController
@RequestMapping("/eduservice/video")
@CrossOrigin
public class EduVideoController {
    @Autowired
    private EduVideoService videoService;

    // 添加小节
    @PostMapping("addVideo")
    public R addVideo(@RequestBody VideoVo videoVo) {
        videoService.saveVideoInfo(videoVo);
        return R.ok();
    }

    // 删除小节
    // TODO 后面这个方法需要完善:删除小节时候,同时把里面视频删除
    @DeleteMapping("{id}")
    public R deleteVideo(@PathVariable String id) {
        videoService.removeById(id);
        return R.ok();
    }

    // 根据章节id查询
    @GetMapping("getVideoInfo/{videoId}")
    public R getVideoInfo(@PathVariable String videoId) {
        VideoVo videoVo = videoService.getVideoInfo(videoId);
        return R.ok().data("video", videoVo);
    }

    // 修改小节
    @PostMapping("updateVideo")
    public R updateVideoInfo(@RequestBody VideoVo videoVo) {
        videoService.updateVideoInfo(videoVo);
        return R.ok();
    }
}
public interface EduVideoService extends IService<EduVideo> {

    void saveVideoInfo(VideoVo videoVo);

    void updateVideoInfo(VideoVo videoVo);

    VideoVo getVideoInfo(String videoId);
}
@Service
public class EduVideoServiceImpl extends ServiceImpl<EduVideoMapper, EduVideo> implements EduVideoService {

    @Override
    public void saveVideoInfo(VideoVo videoVo) {
        EduVideo eduVideo = new EduVideo();
        BeanUtils.copyProperties(videoVo,eduVideo);
        if(videoVo.getFree()) {
            eduVideo.setIsFree(true);
        } else {
            eduVideo.setIsFree(false);
        }
        boolean result = this.save(eduVideo);

        if(!result) {
            throw new LemonException(20001, "保存失败");
        }
    }

    @Override
    public void updateVideoInfo(VideoVo videoVo) {
        EduVideo eduVideo = new EduVideo();
        BeanUtils.copyProperties(videoVo,eduVideo);
        if(videoVo.getFree()) {
            eduVideo.setIsFree(true);
        } else {
            eduVideo.setIsFree(false);
        }
        boolean result = this.updateById(eduVideo);

        if(!result) {
            throw new LemonException(20001, "保存失败");
        }
    }

    @Override
    public VideoVo getVideoInfo(String videoId) {
        EduVideo eduVideo = this.getById(videoId);
        if(eduVideo == null) {
            throw new LemonException(20001, "数据不存在");
        }
        VideoVo videoVo = new VideoVo();
        if(eduVideo.getIsFree()) {
            videoVo.setFree(true);
        } else {
            videoVo.setFree(false);
        }
        BeanUtils.copyProperties(eduVideo, videoVo);
        return videoVo;
    }
}
// src\views\edu\course\chapter.vue
<script>
import chapter from '@/api/edu/chapter'
import video from '@/api/edu/video'
export default {
  data() {
    return {
      saveBtnDisabled: false, // 保存按钮是否禁用
      saveVideoBtnDisabled: false, // 课时按钮是否禁用
      courseId: '', // 课程id
      chapterVideoList: [],
      chapter: {  // 封装章节数据
        title: '',
        sort: 0
      },
      video: {
        title: '',
        sort: 0,
        free: 0,
        videoSourceId: ''
      },
      dialogChapterFormVisible: false, // 章节弹框
      dialogVideoFormVisible: false // 小节弹框
    }
  },
  created() {
    // 获取路由的id值
    if(this.$route.params && this.$route.params.id) {
      this.courseId = this.$route.params.id
      // 根据课程id查询章节和小节
      this.getChapterVideo()
    }
  },
  methods: {
    openEditVideo(videoId) {
      // 弹框
      this.dialogVideoFormVisible = true
      // 调用接口
      video.getVideo(videoId)
        .then(response => {
          this.video = response.data.video
        })
    },
    // ==========================================小节操作==========================================
    // 删除小节
    removeVideo(videoId) {
      this.$confirm('此操作将删除小节, 是否继续?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => { // 点击确定,执行then方法
          // 调用删除方法
          video.deleteVideo(videoId)
          .then(response => { // 删除成功
            // 提示信息
            this.$message({
              type: 'success',
              message: '删除小节成功!'
            });
            // 回到列表页面
            this.getChapterVideo()
          })
        })
    },
    // 添加小节弹框的方法
    openVideo(chapterId){
      // 弹框
      this.dialogVideoFormVisible = true

      // 设置章节id
      this.video.chapterId = chapterId

      // 表单数据清空
      this.video.id = '',
      this.video.title =  '',
      this.video.sort =  0,
      this.video.free = 0,
      this.video.videoSourceId =  ''
    },

    // 添加小节
    addVideo() {
      // 设置课程id
      this.video.courseId = this.courseId
      video.addVideo(this.video)
        .then(response => {
          // 关闭弹框
          this.dialogVideoFormVisible = false
          // 提示
          this.$message({
            type: 'success',
            message: '添加小节成功!'
          });
          // 刷新页面
          this.getChapterVideo()
        })
    },
    updateVideo() {
      video.updateVideo(this.video)
        .then(response => {
          // 关闭弹框
          this.dialogVideoFormVisible = false
          // 提示
          this.$message({
            type: 'success',
            message: '修改小节成功!'
          });
          // 刷新页面
          this.getChapterVideo()
        })
    },
    saveOrUpdateVideo() {
      if(!this.video.id) {
        this.addVideo()
      } else {
        this.updateVideo()
      }
    }
}
script>

18. 课程信息确认(后端)

【项目】小帽学堂(八)_第6张图片

  • 根据id查询课程发布信息
    • 方式一:业务层组装多个表多次的查询结果
    • 方式二:数据访问层进行关联查询
    • 我们使用第二种方式实现
  • 定义vo
@Data
public class CoursePublishVo {
    private String id;
    private String title;
    private String cover;
    private Integer lessonNum;
    private String subjectLevelOne;
    private String subjectLevelTwo;
    private String teacherName;
    private String price;   //只用于显示
}
  • 数据访问层
public interface EduCourseMapper extends BaseMapper<EduCourse> {
    public CoursePublishVo getPublishCourseInfo(String courseId);
}

DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.alex.eduservice.mapper.EduCourseMapper">
    
    <select id="getPublishCourseInfo" resultType="com.alex.eduservice.entity.vo.CoursePublishVo">
        SELECT ec.id, ec.title, ec.price, ec.lesson_num AS lessonNum,ec.cover,et.name AS teacherName,es1.title AS subjectLevelOne, es2.title AS subjectLevelTwo
FROM edu_course ec LEFT OUTER JOIN edu_course_description ecd ON ec.id=ecd.id
			     LEFT OUTER JOIN edu_teacher et ON ec.teacher_id=et.id
			     LEFT OUTER JOIN edu_subject es1 ON ec.subject_parent_id=es1.id
			     LEFT OUTER JOIN edu_subject es2 ON ec.subject_id=es2.id
	WHERE ec.id=#{courseId}
    select>
mapper>
@RestController
@RequestMapping("/eduservice/course")
@CrossOrigin
public class EduCourseController {

    @Autowired
    private EduCourseService courseService;

    // 根据课程id查询课程确认信息
    @GetMapping("getPublishCourseInfo/{id}")
    public R getPublishCourseInfo(@PathVariable String id) {
        CoursePublishVo coursePublishVo = courseService.publishCourseInfo(id);
        return R.ok().data("publishCourse", coursePublishVo);
    }
}
public interface EduCourseService extends IService<EduCourse> {
    CoursePublishVo publishCourseInfo(String id);
}
@Service
public class EduCourseServiceImpl extends ServiceImpl<EduCourseMapper, EduCourse> implements EduCourseService {

    @Autowired
    private EduCourseDescriptionService courseDescriptionService;
    
    @Override
    public CoursePublishVo publishCourseInfo(String id) {
        // 调用mapper
        CoursePublishVo publishCourseInfo = baseMapper.getPublishCourseInfo(id);
        return publishCourseInfo;
    }
}

【项目】小帽学堂(八)_第7张图片

  • 报错解决
pom.xml(service)
<build>
        <resources>
            <resource>
                <directory>src/main/javadirectory>
                <includes>
                    <include>**/*.xmlinclude>
                includes>
                <filtering>falsefiltering>
            resource>
        resources>
    build>
src/main/resources/application.properties
#配置mapper xml文件的路径
mybatis-plus.mapper-locations=classpath:com/alex/eduservice/mapper/xml/*.xml

19. 课程信息确认(前端)

// src\api\edu\course.js
import request from '@/utils/request'

export default {
    // 课程确认信息显示
    getPublishCourseInfo(id) {
      return request({
        url: '/eduservice/course/getPublishCourseInfo/' + id,
        method: 'get'
      })
    }
}
// src\views\edu\course\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>
    <div class="ccInfo">
      <img :src="coursePublish.cover">
      <div class="main">
        <h2>{{ coursePublish.title }}h2>
        <p class="gray"><span>共{{ coursePublish.lessonNum }}课时span>p>
        <p><span>所属分类:{{ coursePublish.subjectLevelOne }} — {{ coursePublish.subjectLevelTwo }}span>p>
        <p>课程讲师:{{ coursePublish.teacherName }}p>
        <h3 class="red">¥{{ coursePublish.price }}h3>
      div>
    div>
    <div>
      <el-button @click="previous">返回修改el-button>
      <el-button :disabled="saveBtnDisabled" type="primary" @click="publish">发布课程el-button>
    div>
  div>
template>
<script>
import course from '@/api/edu/course'
export default {
  data() {
    return {
      saveBtnDisabled: false, // 保存按钮是否禁用
      courseId: '',
      coursePublish: {}
    }
  },
  created() {
    // 获取路由课程id值
    if(this.$route.params && this.$route.params.id) {
      this.courseId = this.$route.params.id
      // 调用接口方法根据课程id查询
      this.getCoursePublishId()
    }
  },
  methods: {
    // 根据课程id查询
    getCoursePublishId() {
      course.getPublishCourseInfo(this.courseId)
        .then(response => {
          this.coursePublish = response.data.publishCourse
        })
    },
    previous() {
      console.log('previous')
      this.$router.push({ path: '/course/chapter/1' })
    },

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

<style scoped>
.ccInfo {
    background: #f5f5f5;
    padding: 20px;
    overflow: hidden;
    border: 1px dashed #DDD;
    margin-bottom: 40px;
    position: relative;
}
.ccInfo img {
    background: #d6d6d6;
    width: 500px;
    height: 278px;
    display: block;
    float: left;
    border: none;
}
.ccInfo .main {
    margin-left: 520px;
}

.ccInfo .main h2 {
    font-size: 28px;
    margin-bottom: 30px;
    line-height: 1;
    font-weight: normal;
}
.ccInfo .main p {
    margin-bottom: 10px;
    word-wrap: break-word;
    line-height: 24px;
    max-height: 48px;
    overflow: hidden;
}
.ccInfo .main p {
    margin-bottom: 10px;
    word-wrap: break-word;
    line-height: 24px;
    max-height: 48px;
    overflow: hidden;
}
.ccInfo .main h3 {
    left: 540px;
    bottom: 20px;
    line-height: 1;
    font-size: 28px;
    color: #d32f24;
    font-weight: normal;
    position: absolute;
}
style>

你可能感兴趣的:(项目,java)