文章目录
1. 名师列表功能
1.1分页查询名师接口
在 service_edu下创建TeacherFrontController接口
@RestController
@RequestMapping("/eduservice/teacherfront")
@CrossOrigin
public class TeacherFrontController {
@Autowired
private EduTeacherService eduTeacherService;
//分页查询讲师的方法
@PostMapping("/getTeacherFrontList/{page}/{limit}")
public R getTeacherFrontList(@PathVariable long page,@PathVariable long limit)
{
Page<EduTeacher> pageTeacher = new Page<EduTeacher>(page, limit);
Map<String,Object> map= eduTeacherService.getTeacherFrontList(pageTeacher);
return R.ok().data(map);
}
}
service
//分页查询讲师方法
@Override
public Map<String, Object> getTeacherFrontList(Page<EduTeacher> pageTeacher) {
QueryWrapper<EduTeacher> teacherQueryWrapper = new QueryWrapper<>();
teacherQueryWrapper.orderByDesc("id");
//把分页数据封装到pageTeacher里面
baseMapper.selectPage(pageTeacher,teacherQueryWrapper);
List<EduTeacher> records = pageTeacher.getRecords();
long total = pageTeacher.getTotal(); //总记录数
long size = pageTeacher.getSize(); //当前记录数
long pages = pageTeacher.getPages();
long current = pageTeacher.getCurrent();
boolean next = pageTeacher.hasNext();
boolean previous = pageTeacher.hasPrevious();
HashMap<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", next);
map.put("hasPrevious", previous);
return map;
}
2.2整合前端页面
创建一个teacher.js文件用于接口路径的调用
import request from '@/utils/request'
export default{
//分页讲师的查询
getTeacherList(page,limit){
return request({
url:`/eduservice/teacherfront/getTeacherFrontList/${page}/${limit}`,
method: 'post'
})
}
}
pages/teacher/index.vue
<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>
<!-- <c:forEach var="subject" items="${subjectList }">
<a id="${subject.subjectId}" title="${subject.subjectName }" href="javascript:void(0)" onclick="submitForm(${subject.subjectId})">${subject.subjectName }</a>
</c:forEach>-->
</section>
</header>
<section class="c-sort-box unBr">
<div>
<!-- /无数据提示 开始-->
<section class="no-data-wrap" v-if="data.tota">
<em class="icon30 no-data-ico"> </em>
<span class="c-666 fsize14 ml10 vam">没有相关数据,小编正在努力整理中...</span>
</section>
<!-- /无数据提示 结束-->
<article class="i-teacher-list">
<ul class="of">
<li v-for="teacher in data.items" :key="teacher.id">
<section class="i-teach-wrap">
<div class="i-teach-pic">
<a href="/teacher/1" :title="teacher.name" target="_blank">
<img :src="teacher.avatar" :alt="teacher.name">
</a>
</div>
<div class="mt10 hLh30 txtOf tac">
<a href="/teacher/1" :title="teacher.name" target="_blank" class="fsize18 c-666">{{teacher.name}}</a>
</div>
<div class="hLh30 txtOf tac">
<span class="fsize14 c-999">{{teacher.intro}}</span>
</div>
<div class="mt15 i-q-txt">
<p class="c-999 f-fA">{{teacher.career}}</p>
</div>
</section>
</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)"><</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>
<!-- 公共分页 结束 -->
<!-- 公共分页 结束 -->
</section>
</section>
<!-- /讲师列表 结束 -->
</div>
</template>
<script>
import teacher from "@/api/teacher"
export default {
asyncData({ params, error }) {
return teacher.getTeacherList(1, 4).then(response => {
// console.log(response.data.data);
return { data: response.data.data }
});
},
methods:{
//分页切换
gotoPage(page)
{
teacher.getTeacherList(page, 4)
.then(response=>{
this.data=response.data.data
})
}
}
};
</script>
2.讲师详情功能
2.1修改讲师页面的超链接,改成讲师id
pages/teacher/index.vue
2.2编写讲师详情接口
根据讲师id查询讲师基本信息,查询讲师所讲课程
在TeacherFrontContrroler里面
//2.讲师详情的查询
@GetMapping("/getTeacherFrontInfo/{teacherId}")
public R getTeacherFrontInfo(@PathVariable String teacherId)
{
//更加讲师id查询讲师基本信息
EduTeacher eduTeacher = eduTeacherService.getById(teacherId);
//查询该讲师的所有课程
QueryWrapper<EduCourse> wrapper = new QueryWrapper<>();
wrapper.eq("teacher_id",teacherId);
List<EduCourse> list = courseService.list(wrapper);
return R.ok().data("teacher",eduTeacher).data("courseList",list);
}
2.3讲师详情的前端
在pages/teacher/_id.vue中
<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>
<div class="t-infor-wrap">
<!-- 讲师基本信息 -->
<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">{{teacher.name}} {{teacher.level===1?'高级讲师':'首席讲师'}}</span>
</h3>
<section class="mt10">
<span class="t-tag-bg">{{teacher.intro}}</span>
</section>
<section class="t-infor-txt">
<p
class="mt20"
>{{teacher.career}}</p>
</section>
<div class="clear"></div>
</div>
</section>
<div class="clear"></div>
</div>
<section class="mt30">
<div>
<header class="comm-title all-teacher-title c-course-content">
<h2 class="fl tac">
<span class="c-333">主讲课程</span>
</h2>
<section class="c-tab-title">
<a href="javascript: void(0)"> </a>
</section>
</header>
<!-- /无数据提示 开始-->
<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>
<!-- /无数据提示 结束-->
<article class="comm-course-list">
<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="#" title="开始学习" target="_blank" class="comm-btn c-btn-1">开始学习</a>
</div>
</section>
<h3 class="hLh30 txtOf mt10">
<a href="#" :title="course.title" target="_blank" class="course-title fsize18 c-333">{{course.title}}</a>
</h3>
</div>
</li>
</ul>
<div class="clear"></div>
</article>
</div>
</section>
</section>
<!-- /讲师介绍 结束 -->
</div>
</template>
<script>
import teacherApi from '@/api/teacher'
export default {
//this.$route.params.id 等价于 params.id
//params.id获取路径中的id值
asyncData({params,error}){
return teacherApi.getTeacherInfo(params.id)
.then(response=>{
return {
teacher: response.data.data.teacher,
courseList: response.data.data.courseList
}
})
}
};
</script>
3.课程列表功能
3.1 后端部分
- 创建一个接收查询条件的实体类
@Data
public class CourseInfoVo {
@ApiModelProperty(value = "课程ID")
private String id;
@ApiModelProperty(value = "课程讲师ID")
private String teacherId;
@ApiModelProperty(value = "课程专业ID")
private String subjectId;
@ApiModelProperty(value = "一级分类级ID")
private String subjectParentId;
@ApiModelProperty(value = "课程标题")
private String title;
@ApiModelProperty(value = "课程销售价格,设置为0则可免费观看")
// 0.01
private BigDecimal price;
@ApiModelProperty(value = "总课时")
private Integer lessonNum;
@ApiModelProperty(value = "课程封面图片路径")
private String cover;
@ApiModelProperty(value = "课程简介")
private String description;
}
- 创建条件带分页查询的接口
@RestController
@RequestMapping("/eduservice/coursefront")
@CrossOrigin
public class CourseFromController {
@Autowired
private EduCourseService courseService;
//条件查询带分页查询课程
public R getFrontCourseList(@PathVariable long page, @PathVariable long limit , @RequestBody(required = false)CourseQueryVo courseQueryVo)
{
Page<EduCourse> coursePage = new Page<EduCourse>(page, limit);
HashMap<String,Object> map=courseService.getCourseFrontList(coursePage,courseQueryVo);
return R.ok().data(map);
}
}
- service层
//条件查询带分页
@Override
public HashMap<String, Object> getCourseFrontList(Page<EduCourse> coursePage, CourseQueryVo courseQuery) {
QueryWrapper<EduCourse> queryWrapper = new QueryWrapper<>();
if (!StringUtils.isEmpty(courseQuery.getSubjectParentId())) {
queryWrapper.eq("subject_parent_id", courseQuery.getSubjectParentId());
}
if (!StringUtils.isEmpty(courseQuery.getSubjectId())) {
queryWrapper.eq("subject_id", courseQuery.getSubjectId());
}
if (!StringUtils.isEmpty(courseQuery.getBuyCountSort())) {
queryWrapper.orderByDesc("buy_count");
}
if (!StringUtils.isEmpty(courseQuery.getGmtCreateSort())) {
queryWrapper.orderByDesc("gmt_create");
}
if (!StringUtils.isEmpty(courseQuery.getPriceSort())) {
queryWrapper.orderByDesc("price");
}
baseMapper.selectPage(coursePage,queryWrapper);
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();
HashMap<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.2课程列表前端
3.2.1. 在api中创建course.js文件,引入接口地址
import request from '@/utils/request'
export default {
getPageList(page, limit, searchObj) {
return request({
url: `/eduservice/coursefront/getFrontCourseList/${page}/${limit}`,
method: 'post',
data: searchObj
})
},
// 获取课程二级分类
getAllSubject() {
return request({
url: `/eduservice/subject/getAllSubject`,
method: 'get'
})
}
}
3.2.2 前端部分
pages/course/index.vue
<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="#">全部</a>
</li>
<li v-for="(it,index) in subjectNestedList" :key="index" :class="{active:oneIndex==index}">
<a :title="it.title" href="#" @click="searchOne(it.id,index)">{{it.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:oneIndex==index}">
<a :title="item.title" href="#" @click="searchTwo(item.id,index)">{{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==''}">↓</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>
</div>
<div class="mt40">
<!-- /无数据提示 开始-->
<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="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="fl jgAttr c-ccc f-fA">
<i class="c-999 f-fA">9634人学习</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)"><</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>
<!-- 公共分页 结束 -->
</section>
</section>
<!-- /课程列表 结束 -->
</div>
</template>
<script>
import course from '@/api/course'
export default {
data () {
return {
page:1,//当前页
data:{},//课程列表
subjectNestedList: [], // 一级分类列表
subSubjectList: [], // 二级分类列表
searchObj: {}, // 查询表单对象
oneIndex:-1,//一级分类选中效果
twoIndex:-1,//二级分类选中效果
buyCountSort:"",
gmtCreateSort:"",
priceSort:""
}
},
//加载完渲染时
created () {
//获取课程列表
this.initCourse()
//获取分类
this.initSubject()
},
methods: {
//查询课程列表 第一页数据
initCourse(){
course.getPageList(1, 4,this.searchObj).then(response => {
this.data = response.data.data
})
},
//查询所有一级分类
initSubject(){
//debugger
course.getAllSubject()
.then(response => {
this.subjectNestedList = response.data.data.list
})
},
//点击一级分类,显示对应的二级分类,查询数据
searchOne(subjectParentId, index) {
//debugger
//把传递过来的index给oneIndex,为active生成样式
this.oneIndex = index
this.twoIndex = -1
this.searchObj.subjectId = "";
this.subSubjectList = [];
this.searchObj.subjectParentId = subjectParentId;
// this.gotoPage(this.page)
this.gotoPage(1)
//拿着点击的一级分类和所有一级分类的id进行比较 如果id相同就从一级分类里面获取对应的二级分类
for (let i = 0; i < this.subjectNestedList.length; i++) {
if (this.subjectNestedList[i].id === subjectParentId) {
this.subSubjectList = this.subjectNestedList[i].children
}
}
},
//点击二级分类,查询数据
searchTwo(subjectId, index) {
//为index赋值 为了样式显示
this.twoIndex = index
this.searchObj.subjectId = subjectId;
// this.gotoPage(this.page)
this.gotoPage(1)
},
//购买量查询
searchBuyCount() {
this.buyCountSort = "1";
this.gmtCreateSort = "";
this.priceSort = "";
this.searchObj.buyCountSort = this.buyCountSort;
this.searchObj.gmtCreateSort = this.gmtCreateSort;
this.searchObj.priceSort = this.priceSort;
this.gotoPage(this.page)
},
//更新时间查询
searchGmtCreate() {
debugger
this.buyCountSort = "";
this.gmtCreateSort = "1";
this.priceSort = "";
this.searchObj.buyCountSort = this.buyCountSort;
this.searchObj.gmtCreateSort = this.gmtCreateSort;
this.searchObj.priceSort = this.priceSort;
this.gotoPage(this.page)
},
//价格查询
searchPrice() {
this.buyCountSort = "";
this.gmtCreateSort = "";
this.priceSort = "1";
this.searchObj.buyCountSort = this.buyCountSort;
this.searchObj.gmtCreateSort = this.gmtCreateSort;
this.searchObj.priceSort = this.priceSort;
this.gotoPage(this.page)
},
//分页查询
gotoPage(page) {
this.page = page
course.getPageList(page, 4, this.searchObj).then(response => {
this.data = response.data.data
})
}
}
}
</script>
<style scoped>
.active {
background: #bdbdbd;
}
.hide {
display: none;
}
.show {
display: block;
}
</style>
4.课程详情
4.1 后端部分
- 创建一个Vo用来封装返回的数据
@Data
public class CourseWebVo {
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;
}
- controller
//课程详情
@GetMapping("/getFrontCourseInfo/{courseId}")
public R getBaseCourseInfo(@PathVariable String courseId){
//根据课程id编写SQL语句,查询课程信息
CourseWebVo courseWebVo= courseService.getBaseCourseInfo(courseId);
//根据课程id查询章节和小节
List<ChapterVo> chapterVoList = chapterService.getChapterVideoByCourseId(courseId);
return R.ok().data("courseWebVo",courseWebVo).data("chapterVoList",chapterVoList);
}
- service
@Override
public CourseWebVo getBaseCourseInfo(String courseId) {
return baseMapper.getBaseCourseInfo(courseId);
}
- mapper实现
CourseWebVo getBaseCourseInfo(String courseId);
- 对应的xml
<select id="getBaseCourseInfo" resultType="com.atguigu.eduservice.entity.vo.CourseWebVo">
SELECT c.id,c.title,c.cover,CONVERT(c.price, DECIMAL(8,2)) AS price,c.lesson_num AS lessonNum,c.cover,c.buy_count AS buyCount,c.view_count AS viewCount,cd.description,t.id AS teacherId,
t.name AS teacherName,t.intro,t.avatar,s1.id AS subjectLevelOneId,s1.title AS subjectLevelOne,s2.id AS subjectLevelTwoId,s2.title AS subjectLevelTwo
FROM
edu_course c
LEFT JOIN edu_course_description cd ON c.id = cd.id
LEFT JOIN edu_teacher t ON c.teacher_id = t.id
LEFT JOIN edu_subject s1 ON c.subject_parent_id = s1.id
LEFT JOIN edu_subject s2 ON c.subject_id = s2.id
WHERE
c.id = #{id}
</select>
4.2 前端部分
- 在api/course.js中引入路由信息
//课程详情的方法
getCourseInfo(id) {
return request({
url:'/eduservice/coursefront/getFrontCourseInfo/'+id,
method: 'get'
})
}
- 在pages/course/_id.vue引入页面
<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">{{courseWebVo.subjectLevelOne}}</a>
\
<span class="c-333 fsize14">{{courseWebVo.subjectLevelTwo}}</span>
</section>
<div>
<article class="c-v-pic-wrap" style="height: 357px;">
<section class="p-h-video-box" id="videoPlay">
<img src="~/assets/photo/course/1442295581911.jpg" :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>
<!-- /课程封面介绍 -->
<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="courseWebVo.description">
{{courseWebVo.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 chapterVoList" :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">免费试听</i>
</span>
<em class="lh-menu-i-2 icon16 r5"> </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="#">
<img :src="courseWebVo.avatar" width="50" height="50" alt>
</a>
</div>
<section class="hLh30 txtOf">
<a class="c-333 fsize16 fl" href="#">{{courseWebVo.teacherName}}</a>
</section>
<section class="hLh20 txtOf">
<span class="c-999">{{courseWebVo.intro}}</span>
</section>
</li>
</ul>
</section>
</div>
</div>
</aside>
<div class="clear"></div>
</div>
</section>
<!-- /课程详情 结束 -->
</div>
</template>
<script>
import courseApi from '@/api/course'
export default {
asyncData({params,error}){
return courseApi.getCourseInfo(params.id)
.then(response =>{
return{
courseWebVo: response.data.data.courseWebVo,
chapterVoList: response.data.data.chapterVoList
}
})
}
};
</script>
5. 视频播放功能
5.1 后端
- 根据视频id获取视频播放凭证
//得到视频凭证
@GetMapping("/getPlayAuth/{id}")
public R getPlayAuth(@PathVariable String id)
{
try {
//创建初始化对象
DefaultAcsClient client = InitVodCilent.initVodClient(ConstantVodUtils.ACCESS_KEY_ID, ConstantVodUtils.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 (Exception e)
{
throw new GuliException(20001,"获取视频凭证失败");
}
}
- 在VideoVo添加源地址
5.2前端
- 在pages/couurse/_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>
- api
创建api模块 api/vod.js,从后端获取播放凭证
import request from '@/utils/request'
export default {
getPlayAuth(vid) {
return request({
url: `/eduvod/video/getPlayAuth/${vid}`,
method: 'get'
})
}
}
- 创建播放页面
创建 pages/player/_vid.vue
<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>
<script>
import vod from '@/api/vod'
export default {
layout:'video',//应用video布局
asyncData({params,error}){ //获取播放凭证
return vod.getPlayAuth(params.vid)
.then(response=>{
return{
playAuth: response.data.data.playAuth,
vid:params.vid
}
})
},
/**
* 页面渲染完成时:此时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',
// 以下可选设置
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('播放器创建成功')
})
}
}
</script>