day14【首页课程和名师功能】-02 首页课程功能

01-课程页面静态效果整合

一、列表页面

创建 pages/course/index.vue

二、详情页面

创建 pages/course/_id.vue



02-课程列表页面

一、课程后端接口

1、课程列表

(1)课程列表vo类(条件查询需要的对象)

@ApiModel(value = "课程查询对象", description = "课程查询对象封装")

@Data

public class CourseFrontVo {

    @ApiModelProperty(value = "课程名称")

    private String title;

    @ApiModelProperty(value = "讲师id")

    private String teacherId;

    @ApiModelProperty(value = "一级类别id")

    private String subjectParentId;

    @ApiModelProperty(value = "二级类别id")

    private String subjectId;

    @ApiModelProperty(value = "销量排序")

    private String buyCountSort;

    @ApiModelProperty(value = "最新时间排序")

    private String gmtCreateSort;

    @ApiModelProperty(value = "价格排序")

    private String priceSort;

}

(2)课程列表controller

@RestController

@CrossOrigin

@RequestMapping("/eduservice/coursefront")

public class CourseFrontController {

    @Autowired

    private EduCourseService courseService;

    //1 条件查询带分页查询课程

    @ApiOperation(value = "分页课程列表")

    @PostMapping("pageCourseCondition/{current}/{limit}")

    public R pageCourseCondition(@PathVariable long limit,

                                @PathVariable long current,

                                @RequestBody(required = false) CourseFrontVo courseFrontVo){

        //@RequestBody(required = false)使用RequestBody必须是post提交,不添加required = false必须要有值,添加后表示可以没有值

        Page<EduCourse> pageParam = new Page<EduCourse>(current,limit);

        Map<String, Object> map = courseService.pageListWeb(pageParam, courseFrontVo);

        return R.ok().data(map);

    }

}

//@RequestBody(required = false)使用RequestBody必须是post提交,不添加required = false必须要有值,添加后可以没有值

(3)课程列表service

//1 条件查询带分页查询课程

    @Override

    public Map<String, Object> pageListWeb(Page<EduCourse> pageParam, CourseFrontVo courseFrontVo) {

        QueryWrapper<EduCourse> queryWrapper = new QueryWrapper<>();

        //判断条件值是否为空,不为空拼接

        if (!StringUtils.isEmpty(courseFrontVo.getSubjectParentId())){//一级分类

            queryWrapper.eq("subject_parent_id",courseFrontVo.getSubjectParentId());

        }

        if (!StringUtils.isEmpty(courseFrontVo.getSubjectId())){//二级分类

            queryWrapper.eq("subject_id",courseFrontVo.getSubjectId());

        }

        if (!StringUtils.isEmpty(courseFrontVo.getBuyCountSort())){//销售数量

            queryWrapper.orderByDesc("buy_count",courseFrontVo.getBuyCountSort());

        }

        if (!StringUtils.isEmpty(courseFrontVo.getGmtCreateSort())){//创建时间

            queryWrapper.orderByDesc("gmt_create",courseFrontVo.getGmtCreateSort());

        }

        if (!StringUtils.isEmpty(courseFrontVo.getPriceSort())){//价格

            queryWrapper.orderByDesc("price",courseFrontVo.getPriceSort());

        }

        baseMapper.selectPage(pageParam, queryWrapper);


        List<EduCourse> records = pageParam.getRecords();

        long pages = pageParam.getPages();

        long current = pageParam.getCurrent();

        long total = pageParam.getTotal();

        long size = pageParam.getSize();

        boolean hasNext = pageParam.hasNext(); //下一页

        boolean hasPrevious = pageParam.hasPrevious(); //上一页


        Map<String,Object> map = new HashMap<String,Object>();

        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;

    }

二、课程列表前端

1、定义api

api/course.js

import request from '@/utils/request'

export default {

  //1 条件查询带分页查询课程

  getCourseList(current,limit,searchObj) {

    return request({

      url: `/eduservice/coursefront/pageCourseCondition/${current}/${limit}`,

      method: 'post',

      data:searchObj

    })

  },

  //查询所有分类的方法

  getAllSubject(){

    return request({

        url: `/eduservice/edu-subject/getAllSubject`,

        method: 'get'

      })

  }

}

2、页面调用接口

pages/course/index.vue

<script>

import courseApi from '@/api/course'

export default {

  data(){

    return{

      page:1,

      data:{},

      subjectNestedList: [], // 一级分类列表

      subSubjectList: [], // 二级分类列表

      searchObj: {}, // 查询表单对象

      oneIndex:-1,

      twoIndex:-1,

      buyCountSort:"",

      gmtCreateSort:"",

      priceSort:""

    }

  },

  created(){

    //获取课程列表

    this.initCourseFirst()

    //获取分类

    this.initSubject()

  },

  methods:{

    //1 查询第一页数据

    initCourseFirst(){

      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

      })

    },

    //分页切换方法

    gotoPage(page){

       courseApi.getCourseList(page,8,this.searchObj).then(response=>{

        this.data = response.data.data

      })

    },


    //4 点击某个一级分类,查询对应的二级分类

    searchOne(subjectParentId,index){

      //把传递index值赋值给oneIndex,为了active样式生效

      this.oneIndex = index


      //查询一级分类,二级分类不需要值

      this.twoIndex = -1

      this.searchObj.subjectId = "";

      this.subSubjectList = [];


      //把一级分类点击id值,赋值给searchObj

      this.searchObj.subjectParentId = subjectParentId

      //点击某个一级分类进行条件查询

      this.gotoPage(1) //或者gotoPage(this.page)  this.page的默认值为1


      //拿着点击一级分类id和所有一级分类id进行比较,

      //如果id相同,从一级分类里面获取对应的二级分类

      for(let i =0;i<this.subjectNestedList.length;i++){

          //获取每个一级分类

          var oneSubject = this.subjectNestedList[i]

          //如果id值相同

          if(subjectParentId == oneSubject.id){

              this.subSubjectList = oneSubject.children

          }

      }

    },

  }

};

</script>

完善当点击一级分类时,页面中显示的是该一级分类下的所有课程:

完善点击添一级分类显示样式:

<style scoped>

  .active {

    background: #bdbdbd;

  }

  .hide {

    display: none;

  }

  .show {

    display: block;

  }

</style>

如果oneIndex==index则添加样式:

结果:

完善点击某个二级分类实现查询

结果:

完善排序方式显示

<section class="fl">

            <ol class="js-tap clearfix">

              <li :class="{'current bg-orange':buyCountSort!=''}">

              <!-- :class="{'current bg-orange':buyCountSort!=''}"表示如果有值样式生效 -->

                <a title="销量" href="javascript:void(0);" @click="searchBuyCount()">销量

                  <span :class="{hide:buyCountSort==''}">↓</span>

                </a>

              </li>

              <li :class="{'current bg-orange':gmtCreateSort!=''}">

                <a title="最新" href="javascript:void(0);" @click="searchGmtCreate()">最新

                  <span :class="{hide:gmtCreateSort==''}">↓</span>

                </a>

              </li>

              <li :class="{'current bg-orange':priceSort!=''}">

                <a title="价格" href="javascript:void(0);" @click="searchPrice()">价格&nbsp;

                  <span :class="{hide:priceSort==''}">↓</span>

                </a>

              </li>

            </ol>

          </section>

    //6 根据销量进行排序

    searchBuyCount(){

      //设置对应变量值,为了样式生效

      this.buyCountSort = "1"

      this.gmtCreateSort = ""

      this.priceSort = ""

      //把值赋值到search0bj

      this.searchObj.buyCountSort = this.buyCountSort

      this.searchObj.gmtCreateSort = this.gmtCreateSort

      this.searchObj.priceSort = this.priceSort


      //调用方法查询

      this.gotoPage(1)

    },

    //7 最新排序

    searchGmtCreate(){

      //设置对应变量值,为了样式生效

      this.buyCountSort = ""

      this.gmtCreateSort = "1"

      this.priceSort = ""

      //把值赋值到search0bj

      this.searchObj.buyCountSort = this.buyCountSort

      this.searchObj.gmtCreateSort = this.gmtCreateSort

      this.searchObj.priceSort = this.priceSort

      //调用方法查询

      this.gotoPage(1)

    },

    //8 价格排序

    searchPrice(){

      //设置对应变量值,为了样式生效

      this.buyCountSort = ""

      this.gmtCreateSort = ""

      this.priceSort = "1"

      //把值赋值到search0bj

      this.searchObj.buyCountSort = this.buyCountSort

      this.searchObj.gmtCreateSort = this.gmtCreateSort

      this.searchObj.priceSort = this.priceSort

      //调用方法查询

      this.gotoPage(1)

    }

三、课程列表渲染

1、课程类别显示

    <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="#">全部</a>

                </li>

                <li v-for="(item,index) in subjectNestedList" :key="index" :class="{active:oneIndex==index}">

                  <a :title="item.title" href="#" @click="searchOne(item.id, index)">{{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"  :class="{active:twoIndex==index}">

                  <a :title="item.title" href="#" @click="searchTwo(item.id, index)">{{item.title}}</a>

                </li>

              </ul>

            </dd>

          </dl>

          <div class="clear"></div>

        </section>

2、无数据提示

添加:v-if="data.total==0"

         <!-- /无数据提示 开始-->

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

          <!-- /无数据提示 结束-->

3、列表

    <article v-if="data.total>0" class="comm-course-list">

            <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 v-if="Number(item.price) === 0"  class="fr jgTag bg-green">

                      <i class="c-fff fsize12 f-fA">免费</i>

                    </span>

                    <span class="fl jgAttr c-ccc f-fA">

                      <i class="c-999 f-fA">{{ item.viewCount }}人学习</i>

                      |

                      <i class="c-999 f-fA">9634评论</i>

                    </span>

                  </section>

                </div>

              </li>

            </ul>

            <div class="clear"></div>

          </article>

4、分页页面渲染

    <!-- 公共分页 开始 -->

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

              <a

                v-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>

        <!-- 公共分页 结束 -->



03-课程详情页

一、vo对象的定义

在项目中很多时候需要把model转换成dto用于网站信息的展示,按前端的需要传递对象的数据,保证model对外是隐私的,例如密码之类的属性能很好地避免暴露在外,同时也会减小数据传输的体积。

CourseWebInfoVo.java(用于封装课程的详情信息)

@ApiModel(value="课程信息", description="网站课程详情页需要的相关字段")

@Data

public class CourseWebInfoVo {

    private String id;

    @ApiModelProperty(value = "课程标题")

    private String title;

    @ApiModelProperty(value = "课程销售价格,设置为0则可免费观看")

    private BigDecimal price;

    @ApiModelProperty(value = "总课时")

    private Integer lessonNum;

    @ApiModelProperty(value = "课程封面图片路径")

    private String cover;

    @ApiModelProperty(value = "销售数量")

    private Long buyCount;

    @ApiModelProperty(value = "浏览数量")

    private Long viewCount;

    @ApiModelProperty(value = "课程简介")

    private String description;

    @ApiModelProperty(value = "讲师ID")

    private String teacherId;

    @ApiModelProperty(value = "讲师姓名")

    private String teacherName;

    @ApiModelProperty(value = "讲师资历,一句话说明讲师")

    private String intro;

    @ApiModelProperty(value = "讲师头像")

    private String avatar;

    @ApiModelProperty(value = "课程一级类别ID")

    private String subjectLevelOneId;

    @ApiModelProperty(value = "类别一级名称")

    private String subjectLevelOne;

    @ApiModelProperty(value = "课程二级类别ID")

    private String subjectLevelTwoId;

    @ApiModelProperty(value = "类别二级名称")

    private String subjectLevelTwo;

}

二、课程和讲师信息的获取

1、CourseFrontController

     //根据课程id,编写sq1语句查询课程信息

    @ApiOperation(value = "根据ID查询课程")

    @GetMapping("getFrontCourseInfo/{courseId}")

    public R getFrontCourseInfo(@PathVariable String courseId){

        //根据课程id,编写sql语句查询课程信息

        CourseWebInfoVo courseWebInfoVo = courseService.getBaseCourseInfo(courseId);

        //根据课程id查询章节和小节

        List<ChapterVo> chapterVideoByCourseId = chapterService.getChapterVideoByCourseId(courseId);

        return R.ok().data("courseWebVo",courseWebInfoVo).data("chapterVideoList",chapterVideoByCourseId);

    }

2、EduCourseService

    //根据课程id,编写sq1语句查询课程信息

    CourseWebInfoVo getBaseCourseInfo(String courseId);

3、EduCourseServiceImpl

    //根据课程id,编写sq1语句查询课程信息

    @Override

    public CourseWebInfoVo getBaseCourseInfo(String courseId) {

        return baseMapper.getBaseCourseInfo(courseId);

    }

4、Mapper中关联查询课程和讲师信息

EduCourseMapper.java

    //根据课程id,编写sq1语句查询课程信息

    CourseWebInfoVo getBaseCourseInfo(String courseId);

EduCourseMapper.xml

    <!-- sql语句:根据课程id,编写sq1语句查询课程信息 -->

    <select id="getBaseCourseInfo" resultType="com.atguigu.eduservice.entity.frontvo.CourseWebInfoVo">

        SELECT ec.id,ec.title,ec.price,ec.lesson_num AS lessonNum,ec.cover,

              ec.buy_count AS buyCount,ec.view_count AS viewCount,

              ecd.description,

              et.id AS teacherId,et.name AS 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_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>

因为现在方法中只有一个参数,当只有一个参数时,WHEREec.id=#{courseId}中courseId可以随便写。

默认xml文件不加载问题:


三、前端js

1、api/course.js

  // 课程详情的方法

  getCourseInfo(courseId){

    return request({

        url: `/eduservice/coursefront/getFrontCourseInfo/${courseId}`,

        method: 'get'

    })

  }

2、pages/course/_id.vue

<script>

import courseApi from '@/api/course'

export default {

  asyncData({ params, error }) {

    return courseApi.getCourseInfo(params.id).then(response=>{

      return {

        courseWebVo:response.data.data.courseWebVo,

        chapterVideoList:response.data.data.chapterVideoList

      }

    })

  }

};

</script>

四、页面模板

pages/course/_id.vue

1、课程所属分类

   <!-- 课程所属分类 开始 -->

    <section class="container">

      <section class="path-wrap txtOf hLh30">

        <a href="#" title class="c-999 fsize14">首页</a>

        \

        <a href="#" title class="c-999 fsize14">课程列表</a>

        \

        <span class="c-333 fsize14">{{courseWebVo.subjectLevelOne}}</span>

        \

        <span class="c-333 fsize14">{{courseWebVo.subjectLevelTwo}}</span>

      </section>

      <!-- /课程所属分类 结束 -->

2、课程基本信息

      <!-- 课程基本信息 开始 -->

      <div>

        <article class="c-v-pic-wrap" style="height: 357px;">

          <section class="p-h-video-box" id="videoPlay">

            <img :src="courseWebVo.cover" :alt="courseWebVo.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">{{courseWebVo.title}}</span>

            </h2>

            <section class="c-attr-jg">

              <span class="c-fff">价格:</span>

              <b class="c-yellow" style="font-size:24px;">¥{{courseWebVo.price}}</b>

            </section>

            <section class="c-attr-mt c-attr-undis">

              <span class="c-fff fsize14">主讲: {{courseWebVo.teacherName}}&nbsp;&nbsp;&nbsp;</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>&nbsp;</p>

              <aside>

                <span class="c-fff f-fM">购买数</span>

                <br>

                <h6 class="c-fff f-fM mt10">{{courseWebVo.buyCount}}</h6>

              </aside>

            </li>

            <li>

              <p>&nbsp;</p>

              <aside>

                <span class="c-fff f-fM">课时数</span>

                <br>

                <h6 class="c-fff f-fM mt10">{{courseWebVo.lessonNum}}</h6>

              </aside>

            </li>

            <li>

              <p>&nbsp;</p>

              <aside>

                <span class="c-fff f-fM">浏览数</span>

                <br>

                <h6 class="c-fff f-fM mt10">{{courseWebVo.viewCount}}</h6>

              </aside>

            </li>

          </ol>

        </aside>

        <div class="clear"></div>

      </div>

      <!-- /课程基本信息 结束 -->

3、课程详情介绍

在后端添加课程简介时可以添加样式,但是 {{courseWebVo.description}}只是进行了原样输出。

                    <!-- 将内容中的html翻译过来 -->

                      <p v-html="courseWebVo.description">

                        {{courseWebVo.description}}

                      </p>

4、课程大纲

            <!-- 课程大纲 开始-->

                <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="#" title>

                                  <span class="fr">

                                    <i class="free-icon vam mr10" v-if="video.free === true">免费试听</i>

                                  </span>

                                  <em class="lh-menu-i-2 icon16 mr5">&nbsp;</em>{{video.title}}

                                </a>

                              </li>

                            </ol>

                          </li>

                        </ul>

                      </menu>

                    </div>

                  </section>

                </div>

                <!-- /课程大纲 结束 -->

5、主讲讲师

   <!-- 主讲讲师 开始-->

            <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/'+courseWebVo.teacherId">

                        <img :src="courseWebVo.avatar" width="50" height="50" alt>

                      </a>

                    </div>

                    <section class="hLh30 txtOf">

                       <a class="c-333 fsize16 fl" :href="'/teacher/'+courseWebVo.teacherId"> {{courseWebVo.teacherName}}</a>

                    </section>

                    <section class="hLh20 txtOf">

                      <span class="c-999">{{courseWebVo.intro}}</span>

                    </section>

                  </li>

                </ul>

              </section>

            </div>

            <!-- /主讲讲师 结束 -->

最终显示结果:



04-视频播放测试

一、获取播放地址播放

获取播放地址

参考文档:https://help.aliyun.com/document_detail/61064.html

前面的 03-使用服务端SDK 介绍了如何获取非加密视频的播放地址。直接使用03节的例子获取加密视频播放地址会返回如下错误信息

Currently only the AliyunVoDEncryption stream exists, you must use the Aliyun player to play or set the value of ResultType to Multiple.

目前只有AliyunVoDEncryption流存在,您必须使用Aliyun player来播放或将ResultType的值设置为Multiple。

因此在testGetPlayInfo测试方法中添加 ResultType 参数,并设置为true

privateParams.put("ResultType","Multiple");

此种方式获取的视频文件不能直接播放,必须使用阿里云播放器播放

二、视频播放器

参考文档:https://help.aliyun.com/document_detail/61109.html

1、视频播放器介绍

阿里云播放器SDK(ApsaraVideo Player SDK)是阿里视频服务的重要一环,除了支持点播和直播的基础播放功能外,深度融合视频云业务,如支持视频的加密播放、安全下载、清晰度切换、直播答题等业务场景,为用户提供简单、快速、安全、稳定的视频播放服务。

2、集成视频播放器

参考文档:https://help.aliyun.com/document_detail/51991.html

参考 【播放器简单使用说明】一节

引入脚本文件和css文件

     <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"></script>

初始化视频播放器

<body>

    <div  class="prism-player" id="J_prismPlayer"></div>

        <script>

            var player = new Aliplayer({

                id: 'J_prismPlayer',

                width: '100%',

                autoplay: false,

                cover: 'http://liveroom-img.oss-cn-qingdao.aliyuncs.com/logo.png',

                //播放配置


            },function(player){

                console.log('播放器创建好了。')

            });

        </script>

</body>

3、播放地址播放

在Aliplayer的配置参数中添加如下属性

                //播放方式一:支持播放地址播放,此播放优先级最高,此种方式不能播放加密视频

                source : '你的视频播放地址',

测试:创建html文件(01播放地址播放.html)

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Document</title>


    <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"></script>

</head>

<body>

    <div  class="prism-player" id="J_prismPlayer"></div>

        <script>

            var player = new Aliplayer({

                id: 'J_prismPlayer',

                width: '100%',

                autoplay: false,

                cover: 'http://liveroom-img.oss-cn-qingdao.aliyuncs.com/logo.png',

                //播放配置

                //播放方式一:支持播放地址播放,此播放优先级最高,此种方式不能播放加密视频

                source : 'https://outin-b2b9512bc4dc11ea89cf00163e1c60dc.oss-cn-shanghai.aliyuncs.com/sv/25884263-1734c67e05f/25884263-1734c67e05f.mp4?Expires=1595476184&OSSAccessKeyId=LTAIxSaOfEzCnBOj&Signature=wj2Q5wH1m2snNTmPyDSo6cD4YTU%3D',

            },function(player){

                console.log('播放器创建好了。')

            });

        </script>

</body>

</html>

启动浏览器运行,测试视频的播放:

4、播放凭证播放(推荐)

阿里云播放器支持通过播放凭证自动换取播放地址进行播放,接入方式更为简单,且安全性更高。播放凭证默认时效为100秒(最大为3000秒),只能用于获取指定视频的播放地址,不能混用或重复使用。如果凭证过期则无法获取播放地址,需要重新获取凭证。

encryptType:'1',//如果播放加密视频,则需设置encryptType=1,非加密视频无需设置此项

vid:'视频id',

playauth:'视频授权码',

注意:播放凭证有过期时间,默认值:100秒 。取值范围:100~3000。

设置播放凭证的有效期

在获取播放凭证的测试用例中添加如下代码

request.setAuthInfoTimeout(200L);

在线配置参考:https://player.alicdn.com/aliplayer/setting/setting.html

测试:创建html文件(02播放凭证播放.html)

playauth是根据视频id获取。

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Document</title>


    <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"></script>

</head>

<body>

    <div  class="prism-player" id="J_prismPlayer"></div>

        <script>

            var player = new Aliplayer({

                id: 'J_prismPlayer',

                width: '100%',

                autoplay: false,

                cover: 'http://liveroom-img.oss-cn-qingdao.aliyuncs.com/logo.png',

                //播放配置

                //播放凭证播放

                encryptType:'1',//如果播放加密视频,则需设置encryptType=1,非加密视频无需设置此项

                vid : '07d8aac8a3b045c69cd91b84bdbedc19',

                playauth : 'eyJTZWN1cml0eVRva2VuIjoiQ0FJUzN3SjFxNkZ0NUIyeWZTaklyNWZrQ01uUnZMbEc0UFNETkdEZ3FITVVhc2RobjUvZ2pUejJJSGhKZVhOdkJPMGV0ZjQrbVdCWTdQY1lsck1xRXM0VUhST1lQSklwc01VSXJsUDRKcExGc3QySjZyOEpqc1V6aE5vMTFGaXBzdlhKYXNEVkVma3VFNVhFTWlJNS8wMGU2TC8rY2lyWVhEN0JHSmFWaUpsaFE4MEtWdzJqRjFSdkQ4dFhJUTBRazYxOUszemRaOW1nTGlidWkzdnhDa1J2MkhCaWptOHR4cW1qL015UTV4MzFpMXYweStCM3dZSHRPY3FjYThCOU1ZMVdUc3Uxdm9oemFyR1Q2Q3BaK2psTStxQVU2cWxZNG1YcnM5cUhFa0ZOd0JpWFNaMjJsT2RpTndoa2ZLTTNOcmRacGZ6bjc1MUN0L2ZVaXA3OHhtUW1YNGdYY1Z5R0dOLzZuNU9aUXJ6emI0WmhKZWVsQVJtWGpJRFRiS3VTbWhnL2ZIY1dPRGxOZjljY01YSnFBWFF1TUdxQ2QvTDlwdzJYT2x6NUd2WFZnUHRuaTRBSjVsSHA3TWVNR1YrRGVMeVF5aDBFSWFVN2EwNDQvNWVUWWFwazFNVWFnQUU1cXBMc2ZOQVB4S2lmQWRGbXZySjQyaWNaQVYyd3A4T05OODlVR1Q2NHEySkJnSE9NYlV3aHRYQUJvUmhBSXZLbVJtbVdIQnBwQ0hmRHB3cUxoNkRtUGw3bTQ0QUtrb0J0R0w0YmR2eUFMbGtBbWI0MWdWcXM2UERlVkUybTRHQ1BvRDNZNkFIUGJpYlpjWEJCQzNnaFpXOGhTa1dqMkJieFFKWHJEK09ZSFE9PSIsIkF1dGhJbmZvIjoie1wiQ0lcIjpcIkdCU2NldzJNQ0J4eHQ1d2MydXgyang0VTZzTFRESWtYMC9jMGpONnhiclFJUjR5MldxTHdLdzE4clJnRVZlTVJcXHJcXG5cIixcIkNhbGxlclwiOlwiYWovb2tGOC9CTFZOR0p2VEJlWHZWS3VYbE9oU1I3QldOVGRPRGVwVlB6az1cXHJcXG5cIixcIkV4cGlyZVRpbWVcIjpcIjIwMjAtMDctMjNUMDM6MTE6MThaXCIsXCJNZWRpYUlkXCI6XCIwN2Q4YWFjOGEzYjA0NWM2OWNkOTFiODRiZGJlZGMxOVwiLFwiU2lnbmF0dXJlXCI6XCJqcWRBRHp5OXliNkZJZmxLQlBDTHNyT3V4eVU9XCJ9IiwiVmlkZW9NZXRhIjp7IlN0YXR1cyI6Ik5vcm1hbCIsIlZpZGVvSWQiOiIwN2Q4YWFjOGEzYjA0NWM2OWNkOTFiODRiZGJlZGMxOSIsIlRpdGxlIjoiNiAtIFdoYXQgSWYgSSBXYW50IHRvIE1vdmUgRmFzdGVyIiwiQ292ZXJVUkwiOiJodHRwOi8vb3V0aW4tYjJiOTUxMmJjNGRjMTFlYTg5Y2YwMDE2M2UxYzYwZGMub3NzLWNuLXNoYW5naGFpLmFsaXl1bmNzLmNvbS8wN2Q4YWFjOGEzYjA0NWM2OWNkOTFiODRiZGJlZGMxOS9zbmFwc2hvdHMvNTkxZDRiYjM1MGQ1NDM4M2IzYWM4ZGVjYzI0ZGM0NGEtMDAwMDEuanBnP0V4cGlyZXM9MTU5NTQ3NzM3OCZPU1NBY2Nlc3NLZXlJZD1MVEFJeFNhT2ZFekNuQk9qJlNpZ25hdHVyZT13d0NieXh2R0gxeW1xM0g0d3RzMHlRTFMyelUlM0QiLCJEdXJhdGlvbiI6MTYuMjc2N30sIkFjY2Vzc0tleUlkIjoiU1RTLk5UUUNzZVFmZ1c2aDZGUUxzQWZITXBaS28iLCJBY2Nlc3NLZXlTZWNyZXQiOiJwNlNQdGZoOUhXS1FRMzZESERuOEg4QWlDTHRwM2tqODQ5V2hOeDRIUXZuIiwiUmVnaW9uIjoiY24tc2hhbmdoYWkiLCJDdXN0b21lcklkIjoxMTk5MDc0OTExNjk3NzgxfQ',                

            },function(player){

                console.log('播放器创建好了。')

            });

        </script>

</body>

</html>

启动浏览器运行,测试视频的播放:



05-整合阿里云视频播放器

一、后端获取播放凭证

1、VodController

service-vod微服务中创建 VodController.java

controller中创建 getPlayAuth接口方法

    //根据视频id获取视频凭证

    @GetMapping("getPlayAuth/{id}")

    public R getPlayAuth(@PathVariable String id){

        try {

            //创建初始化对象

            DefaultAcsClient client = InitObjectV.initVodClient(ConstantPropertiesUtil.ACCESS_KEY_ID,ConstantPropertiesUtil.ACCESS_KEY_SECRET);

            //创建获取凭证request和response对象

            GetVideoPlayAuthRequest request = new GetVideoPlayAuthRequest();

            //向request设置视频id

            request.setVideoId(id);

            //调用方法得到凭证

            GetVideoPlayAuthResponse response = client.getAcsResponse(request);

            String playAuth = response.getPlayAuth();

            return R.ok().data("playAuth",playAuth);

        } catch (ClientException e) {

            throw new GuliException(20001,"获取凭证失败");

        }

    }

2、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: `/eduvod/video/getPlayAuth/${vid}`,

            method: '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" />

    <!-- 定义播放器dom -->

    <div id="J_prismPlayer" class="prism-player" />

  </div>

</template>

(2)获取播放凭证

(3)创建播放器

     /**

     * 页面渲染完成时:此时js脚本已加载,Aliplayer已定义,可以使用

     * 如果在created生命周期函数中使用,Aliplayer is not defined错误

     */

    mounted() {

        new Aliplayer({

            id: 'J_prismPlayer',

            vid: this.vid, // 视频id

            playauth: this.playAuth, // 播放凭证

            encryptType: '1', // 如果播放加密视频,则需设置encryptType=1,非加密视频无需设置此项

            width: '100%',

            height: '500px'

        }, function(player) {

            console.log('播放器创建成功')

        })

    }

(4)其他常见的可选配置

        // 以下可选设置

            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

6、加入播放组件

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269