> 文档中心 > 谷粒学院(十七)讲师列表页 | 讲师详情 | 课程列表页 | 课程详情 | 整合阿里云视频点播

谷粒学院(十七)讲师列表页 | 讲师详情 | 课程列表页 | 课程详情 | 整合阿里云视频点播

文章目录

    • 一、讲师列表页 - 前后端
      • 1、Controller类
      • 2、Service类
      • 3、使用Swagger测试
      • 4、创建 api
      • 5、讲师列表组件中调用api
      • 6、页面渲染
      • 7、页面效果展示
    • 二、讲师详情页 - 前后端
      • 1、Controller 类
      • 2、swagger测试
      • 3、teacher api
      • 4、讲师详情页中调用api
      • 5、页面渲染
      • 6、页面效果展示
    • 三、课程列表页 - 前后端
      • 1、Controller类
      • 2、Service类
      • 3、使用Swagger测试
      • 4、编写course api
      • 5、课程列表页面中调用api
      • 6、页面渲染
      • 7、页面效果展示
    • 四、课程详情页 - 前后端
      • 1、Controller类
      • 2、Service类
      • 3、Mapper类
      • 4、使用Swagger测试
      • 5、编写course api
      • 6、课程详情页面中调用API
      • 7、页面渲染
      • 8、页面效果展示
    • 五、视频点播后端获取播放凭证
      • 1、VideoController
      • 2、在service_edu中VideoVo类中添加属性
      • 3、Swagger测试
    • 六、前端播放器整合
      • 1、点击播放超链接
      • 2、layout
      • 3、api
      • 4、播放组件相关文档
      • 5、创建播放页面
      • 6、加入播放组件
      • 7、页面效果展示

一、讲师列表页 - 前后端

1、Controller类

@RestController@CrossOrigin@RequestMapping("/eduservice/teacherfront")public class TeacherFrontController {    @Autowired    private EduTeacherService teacherService;    @Autowired    private EduCourseService courseService;    //1.分页查询讲师的方法    @GetMapping("getTeacherFrontList/{page}/{limit}")    public R getTeacherFrontList(@PathVariable long page,@PathVariable long limit){ Page <EduTeacher> teacherPage = new Page <>(page,limit); Map<String,Object> map=  teacherService.getTeacherFrontList(teacherPage); return R.ok().data(map);    }}

2、Service类

//查询讲师列表 带分页@Overridepublic Map <String, Object> getTeacherFrontList(Page <EduTeacher> page) {    QueryWrapper <EduTeacher> wrapper = new QueryWrapper <>();    wrapper.orderByAsc("sort");    this.baseMapper.selectPage(page, wrapper);    List <EduTeacher> records = page.getRecords();    long current = page.getCurrent();    long pages = page.getPages();    long size = page.getSize();    long total = page.getTotal();    boolean hasNext = page.hasNext();    boolean hasPrevious = page.hasPrevious();    Map <String, Object> map = new HashMap <>();    map.put("items", records);    map.put("current", current);    map.put("pages", pages);    map.put("size", size);    map.put("total", total);    map.put("hasNext", hasNext);    map.put("hasPrevious", hasPrevious);    return map;}

3、使用Swagger测试

http://localhost:8001/swagger-ui.html

4、创建 api

创建文件夹api,api下创建teacher.js,用于封装讲师模块的请求

import request from '@/utils/request'export default {    //分页查询讲师的方法    getTeacheList(page,limit){ return request({     url:`/eduservice/teacherfront/getTeacherFrontList/${page}/${limit}`,     mehtod:'get' })    }}

5、讲师列表组件中调用api

pages/teacher/index.vue

<script>    import teacherApi from '@/api/teacher'export default { //  data() { //    return { //      data:[] //    } //  }, //异步调用(仅仅执行一次)  //params:相当于之前 this.$route.params =>> this.$route.params.id==params.id asyncData({ params, error}) {     return teacherApi.getTeacheList(1,8).then(response=>{  //this.data = response.data.data 和下面这行等价.  return {data:response.data.data}     }) }, methods: {     gotoPage(page){  teacherApi.getTeacheList(page,8).then(response=>{      this.data = response.data.data  })     } },};</script>

6、页面渲染

<template>  <div id="aCoursesList" class="bg-fa of">        <section class="container">      <header class="comm-title all-teacher-title"> <h2 class="fl tac">   <span class="c-333">全部讲师</span> </h2> <section class="c-tab-title">   <a id="subjectAll" title="全部" href="#">全部</a>   <!-- ${subject.subjectName }    --> </section>      </header>      <section class="c-sort-box unBr"> <div>      <section class="no-data-wrap" v-if="data.total==0">     <em class="icon30 no-data-ico"> </em>     <span class="c-666 fsize14 ml10 vam">没有相关数据,小编正在努力整理中...</span>   </section>      <article class="i-teacher-list" v-if="data.total > 0">     <ul class="of"><li v-for="item in data.items" :key="item.id">  <section class="i-teach-wrap">    <div class="i-teach-pic">      <a :href="'/teacher/'+item.id" :title="item.name" target="_blank"> <img :src="item.avatar" :alt="item.name">      </a>    </div>    <div class="mt10 hLh30 txtOf tac">      <a :href="'/teacher/'+item.id" :title="item.name" target="_blank" class="fsize18 c-666">{{item.name}}</a>    </div>    <div class="hLh30 txtOf tac">      <span class="fsize14 c-999">{{item.intro}}</span>    </div>    <div class="mt15 i-q-txt">      <p class="c-999 f-fA">{{item.career}}</p>    </div>  </section></li>     </ul>     <div class="clear"></div>   </article> </div>  <div>   <div class="paging">          <a:class="{undisable: !data.hasPrevious}"href="#"title="首页"@click.prevent="gotoPage(1)"disabled>首页</a>     <a:class="{undisable: !data.hasPrevious}"href="#"title="前一页"@click.prevent="gotoPage(data.current-1)"><span class="token entity" title="<</a>     <av-for="page in data.pages":key="page":class="{current: data.current == page, undisable: data.current == page}":title="''+page+''"href="#"@click.prevent="gotoPage(page)">{{ page }}</a>     <a:class="{undisable: !data.hasNext}"href="#"title="后一页"@click.prevent="gotoPage(data.current+1)">">></a>     <a:class="{undisable: !data.hasNext}"href="#"title="末页"@click.prevent="gotoPage(data.pages)">尾页</a>     <div class="clear"/>   </div> </div>       </section>    </section>      </div></template>

7、页面效果展示

谷粒学院(十七)讲师列表页 | 讲师详情 | 课程列表页 | 课程详情 | 整合阿里云视频点播

谷粒学院(十七)讲师列表页 | 讲师详情 | 课程列表页 | 课程详情 | 整合阿里云视频点播

二、讲师详情页 - 前后端

1、Controller 类

//2.讲师详情的功能@GetMapping("getTeacherFrontInfo/{teacherId}")public R getTeacherFrontInfo(@PathVariable String teacherId){    //1.根据讲师id查询讲师基本信息    EduTeacher eduTeacher = teacherService.getById(teacherId);    //2.根据讲师id查询讲师的所有课程    QueryWrapper <EduCourse> wrapper = new QueryWrapper <>();    wrapper.eq("teacher_id", teacherId);    List <EduCourse> courseList = courseService.list(wrapper);    return R.ok().data("teacher",eduTeacher).data("courseList",courseList);}

2、swagger测试

3、teacher api

api/teacher.js

//根据id获取讲师getTeacherInfo(teacherId){    return request({ url:`/eduservice/teacherfront/getTeacherFrontInfo/${teacherId}`, mehtod:'get'    })}

4、讲师详情页中调用api

pages/teacher/_id.vue

<script>import teacherApi from '@/api/teacher'export default {    asyncData({ params, error}) { //查询讲师详情信息  params.id==this.$route.params.id return teacherApi.getTeacherInfo(params.id).then(response=>{     return{  teacher:response.data.data.teacher,  courseList:response.data.data.courseList     } })    }};</script>

5、页面渲染

(1)讲师基本信息模板

 <section class="fl t-infor-box c-desc-content">   <div class="mt20 ml20">     <section class="t-infor-pic"><img :src="teacher.avatar">     </section>     <h3 class="hLh30"><span class="fsize24 c-333" v-if="teacher.level==1">{{teacher.name}} 高级讲师</span><span class="fsize24 c-333" v-if="teacher.level==2">{{teacher.name}} 特级讲师</span>     </h3>     <section class="mt10"><span class="t-tag-bg">{{teacher.career}}</span>     </section>     <section class="t-infor-txt"><p  class="mt20">{{teacher.intro}}</p>     </section>     <div class="clear"></div>   </div> </section>

(2)无数据提示

<section class="no-data-wrap" v-if="courseList.length==0">    <em class="icon30 no-data-ico"> </em>    <span class="c-666 fsize14 ml10 vam">没有相关数据,小编正在努力整理中...</span></section>

(3)当前讲师课程列表

<article class="comm-course-list" v-if="courseList.length>0">  <ul class="of">      <li v-for="course in courseList" :key="course.id">   <div class="cc-l-wrap"><section class="course-img">    <img :src="course.cover" class="img-responsive" >    <div class="cc-mask"> <a :href="'/course/'+course.id" title="开始学习" target="_blank" class="comm-btn c-btn-1">开始学习</a>    </div></section><h3 class="hLh30 txtOf mt10">    <a :href="'/course/'+course.id" :title="course.title" target="_blank" class="course-title fsize18 c-333">{{course.title}}</a></h3>   </div>      </li>     </ul>    <div class="clear"></div></article>

6、页面效果展示

点击讲师列表页面的任意一个讲师,可以进入讲师详情页面

谷粒学院(十七)讲师列表页 | 讲师详情 | 课程列表页 | 课程详情 | 整合阿里云视频点播

三、课程列表页 - 前后端

1、Controller类

@RestController@CrossOrigin@RequestMapping("/eduservice/coursefront")public class CourseFrontController {    @Autowired    private EduCourseService courseService;    @Autowired    private EduChapterService chapterService;    //1.条件查询带分页查询课程    @PostMapping("getFrontCourseList/{page}/{limit}")    public R getFrontCourseList(@PathVariable long page, @PathVariable long limit,    @RequestBody(required = false)CourseQueryVo courseQueryVo){ Page <EduCourse> coursePage = new Page <>(page, limit); Map <String,Object> map = courseService.getCourseFrontInfo(coursePage,courseQueryVo); //返回分页所有数据 return R.ok().data(map);    }}

2、Service类

//条件查询带分页@Overridepublic Map <String, Object> getCourseFrontInfo(Page <EduCourse> coursePage, CourseQueryVo courseQueryVo) {    QueryWrapper <EduCourse> wrapper = new QueryWrapper <>();    String buyCountSort = courseQueryVo.getBuyCountSort();    String gmtCreateSort = courseQueryVo.getGmtCreateSort();    String priceSort = courseQueryVo.getPriceSort();    String subjectParentId = courseQueryVo.getSubjectParentId();    String subjectId = courseQueryVo.getSubjectId();    if(!StringUtils.isEmpty(buyCountSort)){ if(buyCountSort.equals("1")){//1是降序     wrapper.orderByDesc("buy_count"); }else if(buyCountSort.equals("2")){//2是升序     wrapper.orderByAsc("buy_count"); }    }    if(!StringUtils.isEmpty(gmtCreateSort)){ if(gmtCreateSort.equals("1")){//1是降序     wrapper.orderByDesc("gmt_create"); }else if(gmtCreateSort.equals("2")){//2是升序     wrapper.orderByAsc("gmt_create"); }    }    if(!StringUtils.isEmpty(priceSort)){ if(priceSort.equals("1")){//1是降序     wrapper.orderByDesc("price"); }else if(priceSort.equals("2")){//2是升序     wrapper.orderByAsc("price"); }    }    if(!StringUtils.isEmpty(subjectParentId)){ wrapper.eq("subject_parent_id", subjectParentId);    }    if(!StringUtils.isEmpty(subjectId)){ wrapper.eq("subject_id", subjectId);    }    this.baseMapper.selectPage(coursePage, wrapper);    List <EduCourse> records = coursePage.getRecords();    long current = coursePage.getCurrent();    long pages = coursePage.getPages();    long size = coursePage.getSize();    long total = coursePage.getTotal();    boolean hasNext = coursePage.hasNext();    boolean hasPrevious = coursePage.hasPrevious();    Map <String, Object> map = new HashMap <>();    map.put("items", records);    map.put("current", current);    map.put("pages", pages);    map.put("size", size);    map.put("total", total);    map.put("hasNext", hasNext);    map.put("hasPrevious", hasPrevious);    return map;}

3、使用Swagger测试

4、编写course api

import request from '@/utils/request'export default {    //1.条件查询带分页查询课程    getCourseList(page,limit,searchObj){ return request({     url:`/eduservice/coursefront/getFrontCourseList/${page}/${limit}`,     method:'post',     data:searchObj })    },    //2.查询所有课程分类    getAllSubject(){ return request({     url:`/eduservice/subject/getAllSubject`,     method:'get' })    },}

5、课程列表页面中调用api

<script>import courseApi from '@/api/course'export default {  data() {    return {      page:1,      data:{},      subjectNestedList: [], // 一级分类列表      subSubjectList: [], // 二级分类列表      searchObj: {}, // 查询表单对象      oneIndex:-1,      twoIndex:-1,      buyCountSort:"",  //1是降序 2是升序  不选则不排序      gmtCreateSort:"",      priceSort:""    }  },  created() {    this.initCourseList()    this.initSubject()  },  methods: {    //1.查询第一页数据    initCourseList(){      courseApi.getCourseList(1,8,this.searchObj).then(response=>{ this.data = response.data.data      })    },    //2.查询所有一级分类 和所有二级分类    initSubject(){      courseApi.getAllSubject().then(response=>{ this.subjectNestedList = response.data.data.list this.subSubjectList = []//先清空所有的二级分类 // this.subSubjectList=this.subjectNestedList[0].children for(var i = 0;i < this.subjectNestedList.length; i++){   this.subSubjectList.push(...this.subjectNestedList[i].children) }      })    },    //3.分页跳转    gotoPage(page){      courseApi.getCourseList(page,8,this.searchObj).then(response=>{ this.data = response.data.data      })    },    //4.单击一级分类,显示二级分类    searchOne(subjectParentId,index){      this.oneIndex = index      //相关值的初始化      this.twoIndex = -1      this.searchObj.subjectId = ""      this.subSubjectList = []//清空二级分类      //把一级分类点击id,赋值给searchObj      this.searchObj.subjectParentId = subjectParentId      //点击单个一级分类进行条件查询      this.gotoPage(1)      for(var i = 0;i < this.subjectNestedList.length; i++){ var oneSubject = this.subjectNestedList[i] if(subjectParentId == oneSubject.id){   //从一级分类里面获取对应的二级分类   this.subSubjectList = oneSubject.children }      }    },    //5.单击二级分类,进行查询    searchTwo(subjectId,index){      this.twoIndex = index      // this.searchObj.subjectParentId = ""//一级分类搜索清空 这里不需要情况,因为      this.searchObj.subjectId = subjectId      this.gotoPage(1)    },    //6.点击全部分类,查询全部    searchAll(index){      this.oneIndex  = index      this.twoIndex = -1      //进行查询全部      this.searchObj = {}      this.gotoPage(1)      this.subSubjectList  = []//清空二级,填充所有二级分类      for(var i = 0;i < this.subjectNestedList.length; i++){   this.subSubjectList.push(...this.subjectNestedList[i].children) }    },    //7.根据销量排序    searchBuyCount(){      //设置对应变量值,为了样式生效      if(this.buyCountSort==""){ this.buyCountSort="1"      }else if(this.buyCountSort=="1"){ this.buyCountSort="2"      }else if(this.buyCountSort=="2"){ this.buyCountSort = "1"      }      console.log("this.buyCountSort:"+this.buyCountSort);      this.gmtCreateSort = ""      this.priceSort = ""      //把值赋值到searchObj      this.searchObj.buyCountSort = this.buyCountSort      this.searchObj.gmtCreateSort = this.gmtCreateSort      this.searchObj.priceSort = this.priceSort      //调用方法 进行搜索      this.gotoPage(1)    },    searchGmtCreate(){      //设置对应变量值,为了样式生效      if(this.gmtCreateSort==""){ this.gmtCreateSort="1"      }else if(this.gmtCreateSort=="1"){ this.gmtCreateSort="2"      }else if(this.gmtCreateSort=="2"){ this.gmtCreateSort = "1"      }      console.log("this.gmtCreateSort:"+this.gmtCreateSort);      this.buyCountSort = ""      this.priceSort = ""      //把值赋值到searchObj      this.searchObj.buyCountSort = this.buyCountSort      this.searchObj.gmtCreateSort = this.gmtCreateSort      this.searchObj.priceSort = this.priceSort      //调用方法 进行搜索      this.gotoPage(1)    },    searchPrice(){//设置对应变量值,为了样式生效if(this.priceSort==""){ this.priceSort="1"      }else if(this.priceSort=="1"){ this.priceSort="2"      }else if(this.priceSort=="2"){ this.priceSort = "1"      }      console.log("this.priceSort:"+this.priceSort);      this.buyCountSort = ""      this.gmtCreateSort = ""      //把值赋值到searchObj      this.searchObj.buyCountSort = this.buyCountSort      this.searchObj.gmtCreateSort = this.gmtCreateSort      this.searchObj.priceSort = this.priceSort      //调用方法 进行搜索      this.gotoPage(1)    }      },};</script></script><style scoped>  .active {    background: #68CB9B;  }  .active2 {    background: #00cc7e;  }  .hide {    display: none;  }  .show {    display: block;  }</style>

6、页面渲染

分类渲染+分类搜索功能+课程分页渲染

<template>  <div id="aCoursesList" class="bg-fa of">    <!-- /课程列表 开始 -->    <section class="container">      <header class="comm-title"> <h2 class="fl tac">   <span class="c-333">全部课程</span> </h2>      </header>      <section class="c-sort-box"> <section class="c-s-dl">   <dl>     <dt><span class="c-999 fsize14">课程类别</span>     </dt>     <dd class="c-s-dl-li"><ul class="clearfix">  <li>    <a title="全部" href="#" @click="searchAll(-1)" :class="{active:oneIndex==-1}">全部</a>  </li>  <li v-for="(item,index) in subjectNestedList" :key="item.id" @click="searchOne(item.id,index)" :class="{active:oneIndex==index}">    <a :title="item.title" href="#">{{item.title}}</a>  </li> </ul>     </dd>   </dl>   <dl>     <dt><span class="c-999 fsize14"></span>     </dt>     <dd class="c-s-dl-li"><ul class="clearfix">  <li v-for="(item,index) in subSubjectList" :key="index" @click="searchTwo(item.id,index)"  :class="{active2:twoIndex==index}">    <a :title="item.title" href="#" >{{item.title}}</a>  </li></ul>     </dd>   </dl>   <div class="clear"></div> </section> <div class="js-wrap">   <section class="fr">     <span class="c-ccc"><i class="c-master f-fM">1</i>/<i class="c-666 f-fM">1</i>     </span>   </section>   <section class="fl">     <ol class="js-tap clearfix"><li :class="{'current bg-orange':buyCountSort!=''}">  <a title="销量" href="javascript:void(0);" @click="searchBuyCount()">销量  <span :class="{hide:buyCountSort=='1'}" v-if="buyCountSort!=''"></span>  <span :class="{hide:buyCountSort=='2'}" v-if="buyCountSort!=''"></span>  </a></li><li :class="{'current bg-orange':gmtCreateSort!=''}">  <a title="最新" href="javascript:void(0);" @click="searchGmtCreate()">最新  <span :class="{hide:gmtCreateSort=='1'}" v-if="gmtCreateSort!=''"></span>  <span :class="{hide:gmtCreateSort=='2'}" v-if="gmtCreateSort!=''"></span>    </a></li><li :class="{'current bg-orange':priceSort!=''}">  <a title="价格" href="javascript:void(0);" @click="searchPrice()">价格&nbsp;    <span :class="{hide:priceSort=='1'}" v-if="priceSort!=''"></span>    <span :class="{hide:priceSort=='2'}" v-if="priceSort!=''"></span>  </a></li>     </ol>   </section> </div> <div class="mt40">   <!-- /无数据提示 开始-->   <section class="no-data-wrap" v-if="data.total == 0">     <em class="icon30 no-data-ico">&nbsp;</em>     <span class="c-666 fsize14 ml10 vam">没有相关数据,小编正在努力整理中...</span>   </section>   <!-- /无数据提示 结束-->   <article class="comm-course-list" v-if="data.total > 0">     <ul class="of" id="bna"><li v-for="item in data.items" :key="item.id">  <div class="cc-l-wrap">    <section class="course-img">      <img :src="item.cover" class="img-responsive" :alt="item.title">      <div class="cc-mask"> <a :href="'/course/'+item.id" title="开始学习" class="comm-btn c-btn-1">开始学习</a>      </div>    </section>    <h3 class="hLh30 txtOf mt10">      <a :href="'/course/'+item.id" :title="item.title" class="course-title fsize18 c-333">{{item.title}}</a>    </h3>    <section class="mt10 hLh20 of"><span class="fr jgTag bg-green" v-if="Number(item.price) === 0">     <i class="c-fff fsize12 f-fA">免费</i>   </span>   <span class="fr jgTag bg-green" v-else>      <i class="c-fff fsize12 f-fA">{{item.price}}</i>   </span>    <span class="fl jgAttr c-ccc f-fA"> <i class="c-999 f-fA">{{item.buyCount}}人学习</i> | <i class="c-999 f-fA">9634评论</i>      </span>    </section>  </div></li>     </ul>     <div class="clear"></div>   </article> </div> <!-- 公共分页 开始 --> <div>   <div class="paging">     <!-- undisable这个class是否存在,取决于数据属性hasPrevious -->     <a:class="{undisable: !data.hasPrevious}"href="#"title="首页"@click.prevent="gotoPage(1)"></a>     <a:class="{undisable: !data.hasPrevious}"href="#"title="前一页"@click.prevent="gotoPage(data.current-1)">&lt;</a>     <av-for="page in data.pages":key="page":class="{current: data.current == page, undisable: data.current == page}":title="'第'+page+'页'"href="#"@click.prevent="gotoPage(page)">{{ page }}</a>     <a:class="{undisable: !data.hasNext}"href="#"title="后一页"@click.prevent="gotoPage(data.current+1)">&gt;</a>     <a:class="{undisable: !data.hasNext}"href="#"title="末页"@click.prevent="gotoPage(data.pages)"></a>     <div class="clear"/>   </div> </div> <!-- 公共分页 结束 -->      </section>    </section>    <!-- /课程列表 结束 -->  </div></template>

7、页面效果展示

谷粒学院(十七)讲师列表页 | 讲师详情 | 课程列表页 | 课程详情 | 整合阿里云视频点播

四、课程详情页 - 前后端

1、Controller类

//2.课程详情的方法@GetMapping("getFrontCourseInfo/{courseId}")public R getFrontCourseInfo(@PathVariable String courseId){    //根据课程id,编写sql语句查询课程信息    CourseWebVo courseWebVo = courseService.getBaseCourseInfo(courseId);    //根据课程id,查询章节和小节    List <ChapterVo> chapterVideoList = chapterService.getChapterVideo(courseId);    return R.ok().data("course",courseWebVo).data("chapterVideoList",chapterVideoList);}

2、Service类

@Overridepublic CourseWebVo getBaseCourseInfo(String courseId) {    return this.baseMapper.getBaseCourseInfo(courseId);}

3、Mapper类

public CourseWebVo getBaseCourseInfo(String courseId);
<select id="getBaseCourseInfo" resultType="com.rg.eduservice.entity.frontvo.CourseWebVo" parameterType="string">    SELECT ec.id,ec.`title`,ec.`price`,ec.`lesson_num` AS lessonNum,ec.`cover`,ec.`buy_count` buyCount ,ec.`view_count` viewCount,    ecd.`description`,    et.`id` teacherId,    et.`name` teacherName,    et.`intro`,    et.`avatar`,    es1.`id` AS subjectLevelOneId,    es1.`title` AS subjectLevelOne,    es2.`id` AS subjectLevelTwoId,    es2.`title` AS subjectLevelTwo    FROM  edu_course ec 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`    LEFT OUTER JOIN edu_teacher et ON ec.`teacher_id` = et.`id`    LEFT OUTER JOIN edu_course_description ecd ON ec.`id` = ecd.`id`    WHERE ec.id=#{id}</select>

4、使用Swagger测试

5、编写course api

 //3.课程课程基本详情的方法getCourseInfo(courseId){    return request({ url:`/eduservice/coursefront/getFrontCourseInfo/${courseId}`, method:'get'    })}

6、课程详情页面中调用API

<script>import courseApi from '@/api/course'export default {  asyncData({ params, error}) {    //查询课程详情信息    return courseApi.getCourseInfo(params.id).then(response=>{      return{ course:response.data.data.course, chapterVideoList:response.data.data.chapterVideoList      }    })  }};</script>

7、页面渲染

<template>  <div id="aCoursesList" class="bg-fa of">        <section class="container">      <section class="path-wrap txtOf hLh30"> <a href="#" title class="c-999 fsize14">首页</a> \ <a href="#" title class="c-999 fsize14">{{course.subjectLevelOne}}</a> \ <span class="c-333 fsize14">{{course.subjectLevelTwo}}</span>      </section>      <div> <article class="c-v-pic-wrap" style="height: 357px;">   <section class="p-h-video-box" id="videoPlay">     <img :src="course.cover" :alt="course.title" class="dis c-v-pic">   </section> </article> <aside class="c-attr-wrap">   <section class="ml20 mr15">     <h2 class="hLh30 txtOf mt15"><span class="c-fff fsize24">{{course.title}}</span>     </h2>     <section class="c-attr-jg"><span class="c-fff">价格:</span><b class="c-yellow" style="font-size:24px;">¥{{course.price}}</b>     </section>     <section class="c-attr-mt c-attr-undis"><span class="c-fff fsize14">主讲: {{course.teacherName}}   </span>     </section>     <section class="c-attr-mt of"><span class="ml10 vam">  <em class="icon18 scIcon"></em>  <a class="c-fff vam" title="收藏" href="#" >收藏</a></span>     </section>     <section class="c-attr-mt"><a href="#" title="立即观看" class="comm-btn c-btn-3">立即观看</a>     </section>   </section> </aside> <aside class="thr-attr-box">   <ol class="thr-attr-ol clearfix">     <li><p> </p><aside>  <span class="c-fff f-fM">购买数</span>  <br>  <h6 class="c-fff f-fM mt10">{{course.buyCount}}</h6></aside>     </li>     <li><p> </p><aside>  <span class="c-fff f-fM">课时数</span>  <br>  <h6 class="c-fff f-fM mt10">{{course.lessonNum}}</h6></aside>     </li>     <li><p> </p><aside>  <span class="c-fff f-fM">浏览数</span>  <br>  <h6 class="c-fff f-fM mt10">{{course.viewCount}}</h6></aside>     </li>   </ol> </aside> <div class="clear"></div>      </div>            <div class="mt20 c-infor-box"> <article class="fl col-7">   <section class="mr30">     <div class="i-box"><div>  <section id="c-i-tabTitle" class="c-infor-tabTitle c-tab-title">    <a name="c-i" class="current" title="课程详情">课程详情</a>  </section></div><article class="ml10 mr10 pt20">  <div>    <h6 class="c-i-content c-infor-title">      <span>课程介绍</span>    </h6>    <div class="course-txt-body-wrap">      <section class="course-txt-body"> <p v-html="course.description">   {{course.description}} </p>      </section>    </div>  </div>    <div class="mt50">    <h6 class="c-g-content c-infor-title">      <span>课程大纲</span>    </h6>    <section class="mt20">      <div class="lh-menu-wrap"> <menu id="lh-menu" class="lh-menu mt10 mr10">   <ul>          <li class="lh-menu-stair" v-for="chapter in chapterVideoList" :key="chapter.id"><a href="javascript: void(0)" :title="chapter.title" class="current-1">  <em class="lh-menu-i-1 icon18 mr10"></em>{{chapter.title}}</a><ol class="lh-menu-ol" style="display: block;">  <li class="lh-menu-second ml30" v-for="video in chapter.children" :key="video.id">    <a :href="'/player/'+video.videoSourceId" title target="_blank">      <span class="fr"> <i class="free-icon vam mr10" v-if="video.isFree === true">免费试听</i>      </span>      <em class="lh-menu-i-2 icon16 mr5"> </em>{{video.title}}    </a>  </li>  </ol>     </li>   </ul> </menu>      </div>    </section>  </div>  </article>     </div>   </section> </article> <aside class="fl col-3">   <div class="i-box">     <div><section class="c-infor-tabTitle c-tab-title">  <a title href="javascript:void(0)">主讲讲师</a></section><section class="stud-act-list">  <ul style="height: auto;">    <li>      <div class="u-face">  <a :href="'/teacher/'+course.teacherId">   <img :src="course.avatar" width="50" height="50" :alt="course.teacherName"> </a>      </div>      <section class="hLh30 txtOf"> <a class="c-333 fsize16 fl" href="#">{{course.teacherName}}</a>      </section>      <section class="hLh20 txtOf"> <span class="c-999">{{course.intro}}</span>      </section>    </li>  </ul></section>     </div>   </div> </aside> <div class="clear"></div>      </div>    </section>      </div></template>

8、页面效果展示

谷粒学院(十七)讲师列表页 | 讲师详情 | 课程列表页 | 课程详情 | 整合阿里云视频点播

谷粒学院(十七)讲师列表页 | 讲师详情 | 课程列表页 | 课程详情 | 整合阿里云视频点播

五、视频点播后端获取播放凭证

1、VideoController

service_vod微服务中 VodController.java 中创建 getPlayAuth 接口方法

//根据视频id获取视频凭证@GetMapping("getPlayAuth/{id}")public R getPlayAuth(@PathVariable String id){    try { DefaultAcsClient client = InitVodClient.initVodClient(ConstantVodUtils.ACCESS_KEY_ID, ConstantVodUtils.ACCESS_KEY_SECRET); GetVideoPlayAuthRequest request = new GetVideoPlayAuthRequest(); request.setVideoId(id); GetVideoPlayAuthResponse response  = client.getAcsResponse(request); String playAuth = response.getPlayAuth(); return R.ok().data("playAuth", playAuth);    }catch (Exception e){ throw new GuLiException(20001, "获取凭证失败");    }}

2、在service_edu中VideoVo类中添加属性

private String videoSourceId;//视频id

3、Swagger测试

六、前端播放器整合

1、点击播放超链接

course/_id.vue 中修改课时目录超链接

谷粒学院(十七)讲师列表页 | 讲师详情 | 课程列表页 | 课程详情 | 整合阿里云视频点播

谷粒学院(十七)讲师列表页 | 讲师详情 | 课程列表页 | 课程详情 | 整合阿里云视频点播

2、layout

因为播放器的布局和其他页面的基本布局不一致,因此创建新的布局容器 layouts/video.vue

<template>  <div class="guli-player">    <div class="head">      <a href="#" title="谷粒学院"> <img class="logo" src="~/assets/img/logo.png" lt="谷粒学院">    </a></div>    <div class="body">      <div class="content"><nuxt/></div>    </div>  </div></template><script>export default {}</script><style>html,body{  height:100%;}</style><style scoped>.head {  height: 50px;  position: absolute;  top: 0;  left: 0;  width: 100%;}.head .logo{  height: 50px;  margin-left: 10px;}.body {  position: absolute;  top: 50px;  left: 0;  right: 0;  bottom: 0;  overflow: hidden;}</style>

3、api

创建api模块 api/vod.js,从后端获取播放凭证

import request from '@/utils/request'export default {    //根据视频id获取视频凭证    getPlayAuth(vid){ return request({     url:`/vodService/video/getPlayAuth/${vid}`,     mehtod:'get' })    },}

4、播放组件相关文档

集成文档:https://help.aliyun.com/document_detail/51991.html?spm=a2c4g.11186623.2.39.478e192b8VSdEn
在线配置:https://player.alicdn.com/aliplayer/setting/setting.html
功能展示:https://player.alicdn.com/aliplayer/presentation/index.html

5、创建播放页面

创建 pages/player/_vid.vue

(1)引入播放器js库和css样式

<template>  <div>        <link rel="stylesheet" href="https://g.alicdn.com/de/prismplayer/2.8.1/skins/default/aliplayer-min.css" >        <script charset="utf-8" type="text/javascript" src="https://g.alicdn.com/de/prismplayer/2.8.1/aliplayer-min.js" />        <div id="J_prismPlayer" class="prism-player" />  </div></template>

(2)获取播放凭证

<script>import vod from '@/api/vod'export default{    layout:'video',//应用video布局    asyncData({params, error}) { //注意 这里的params.vid 是写页面的名字,如果页面写的是_id,则要写params.id return vod.getPlayAuth(params.vid).then(response=>{     return {  vid:params.vid,  playAuth:response.data.data.playAuth     } })    },}</script>

(3)创建播放器

/**     * 页面渲染完成时:此时js脚本已加载,Aliplayer已定义,可以使用     * 如果在created生命周期函数中使用,Aliplayer is not defined错误     */    // 因为刚进入页面时,this.vid和this.playAuth都还没有,发送完异步请求后才有.所以视频播放的相关配置应该放在mounted,等页面加载完成后执行    mounted() { new Aliplayer({ id: 'J_prismPlayer', vid: this.vid, // 视频id playauth: this.playAuth, // 播放凭证 encryptType: '1', // 如果播放加密视频,则需设置encryptType=1,非加密视频无需设置此项 width: '100%', height: '500px', // 以下可选设置 cover: 'http://guli.shop/photo/banner/1525939573202.jpg', // 封面 qualitySort: 'asc', // 清晰度排序 mediaType: 'video', // 返回音频还是视频 autoplay: false, // 自动播放 isLive: false, // 直播 rePlay: false, // 循环播放 preload: true, controlBarVisibility: 'hover', // 控制条的显示方式:鼠标悬停 useH5Prism: true, // 播放器类型:html5 }, function(player) {     console.log('播放器创建成功') })    }

6、加入播放组件

功能展示:https://player.alicdn.com/aliplayer/presentation/index.html

7、页面效果展示

点击免费播放,进入视频播放页面

谷粒学院(十七)讲师列表页 | 讲师详情 | 课程列表页 | 课程详情 | 整合阿里云视频点播


如果有收获!!! 希望老铁们来个三连,点赞、收藏、转发。
创作不易,别忘点个赞,可以让更多的人看到这篇文章,顺便鼓励我写出更好的博客