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()">价格
<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"> </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)"><</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)">></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}} </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">{{courseWebVo.buyCount}}</h6>
</aside>
</li>
<li>
<p> </p>
<aside>
<span class="c-fff f-fM">课时数</span>
<br>
<h6 class="c-fff f-fM mt10">{{courseWebVo.lessonNum}}</h6>
</aside>
</li>
<li>
<p> </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"> </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
网友评论