1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 最新《谷粒学院开发教程》:9 - 前台课程模块

最新《谷粒学院开发教程》:9 - 前台课程模块

时间:2020-03-24 06:19:34

相关推荐

最新《谷粒学院开发教程》:9 - 前台课程模块

文章目录

一、讲师列表页1.1、后端接口1.2、前端展示1.3、分页功能 二、讲师详情页2.1、后端接口2.2、前端展示 三、课程列表页3.1、后端接口3.2、前端展示 四、讲师详情页4.1、后端接口4.2、前端展示 五、整合阿里云视频播放器5.1、后端接口5.2、前端展示 六、课程评论功能6.1、数据库设计6.2、远程调用接口6.3、后端接口6.4、前端展示

一、讲师列表页

1.1、后端接口

1、控制层

@RestController@CrossOrigin@RequestMapping("eduservice/teacherFront")public class TeacherFrontController {@AutowiredEduTeacherService eduTeacherService;@RequestMapping("/getTeacherFrontPageList/{page}/{limit}")public R getTeacherFrontPageList(@PathVariable Long limit, @PathVariable Long page) {Page<EduTeacher> teacherPage = new Page<>(page, limit);Map<String, Object> map = eduTeacherService.getTeacherFrontPageList(teacherPage);return R.ok().data(map);}}

2、业务层

//前台系统分页查询讲师的方法@Overridepublic Map<String, Object> getTeacherFrontPageList(Page<EduTeacher> teacherPage) {QueryWrapper<EduTeacher> wrapper = new QueryWrapper<>();wrapper.orderByAsc("gmt_create");//把分页数据封装到pageTeacher对象中baseMapper.selectPage(teacherPage, wrapper);//把分页的数据获取出来返回一个map集合HashMap<String, Object> map = new HashMap<>();//总记录数long total = teacherPage.getTotal();//当前页long current = teacherPage.getCurrent();//每页记录数long size = teacherPage.getSize();//查询到的对象List<EduTeacher> teacherList = teacherPage.getRecords();//总页数long pages = teacherPage.getPages();//是否有上一页boolean hasPrevious = teacherPage.hasPrevious();//是否有下一页boolean hasNext = teacherPage.hasNext();//将数据封装到map中返回map.put("total", total);map.put("current", current);map.put("size", size);map.put("list", teacherList);map.put("hasPrevious", hasPrevious);map.put("hasNext", hasNext);map.put("pages", pages);return map;}

1.2、前端展示

1、api/teacher.js

import request from '@/utils/request'export default{getPageList(page, limit){return request({url:`/eduservice/teacherFront/getTeacherFrontPageList/${page}/${limit}`,method: 'get'})}}

2、teacher/index.vue

<script>import teacherAPI from "@/api/teacher";export default {//异步调用,进页面调用,且只会调一次//params:相当于之前的 this.$route.params.id 等价 params.id//error:错误信息asyncData({params, error }) {return teacherAPI.getPageList(1, 8).then((response) => {//this.data = response.data.datareturn {data: response.data.data };});},data() {return {};},};</script>

3、页面

<div><!-- /无数据提示 开始--><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="i-teacher-list" v-if="data.total > 0"><ul class="of"><li v-for="item in data.list" :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>

4、展示

1.3、分页功能

1、js

methods: {//分页切换方法//参数是页码数gotoPage(page) {teacherAPI.getPageList(page, 4).then((resp) => {this.data = resp.data.data;});},},

2、页面

<!-- 公共分页 开始 --><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><!-- 公共分页 结束 -->

二、讲师详情页

2.1、后端接口

编写接口:

根据讲师id查询讲师信息根据讲师id查询讲师所讲课程

/*** 根据id查询讲师信息*/@GetMapping("/getTeacherInfo/{id}")public R getTeacherInfo(@PathVariable String id) {EduTeacher teacher = eduTeacherService.getById(id);QueryWrapper<EduCourse> wrapper = new QueryWrapper<>();wrapper.eq("teacher_id", id);List<EduCourse> courseList = eduCourseService.list(wrapper);return R.ok().data("teacher", teacher).data("courseList", courseList);}

2.2、前端展示

1、api/teacher.js

import request from '@/utils/request'export default{// 根据ID查询讲师本身信息+课程信息getTeacherInfoByid(id){return request({url: `/eduservice/teacherFront/getTeacherInfo/${id}`,method: `get`})},}

2、js

import teacherApi from '@/api/teacher'export default {created() {this.teacherId = this.$route.params.idthis.getByid()},data() {return {teacher: {name: '',intro: '',career: '',level: '',},courseList: [],teacherId: '',}},methods: {getByid() {teacherApi.getTeacherInfoByid(this.teacherId).then(resp => {this.teacher = resp.data.data.teacherthis.courseList = resp.data.data.courseList})}},};

3、页面

<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}}&nbsp;{{teacher.level ===1?'高级讲师':'首席讲师'}}</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><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)">&nbsp;</a></section></header><!-- /无数据提示 开始--><section class="no-data-wrap" v-if="courseList.length==0"><em class="icon30 no-data-ico">&nbsp;</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="'/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></div></section></section><!-- /讲师介绍 结束 --></div></template>

4、展示

三、课程列表页

3.1、后端接口

1、创建vo进行模糊查询

package com.laptoy.eduservice.entity.frontVo;@Datapublic class CourseFrontVo implements Serializable {private static final long serialVersionUID = 1L;@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、控制层

@RestController@CrossOrigin@RequestMapping("eduservice/courseFront")public class CourseFrontController {@AutowiredEduCourseService courseService;@PostMapping("/getFrontCourseList/{page}/{limit}")public R getFrontCourseList(@PathVariable Long limit, @PathVariable Long page,@RequestBody CourseFrontVo courseFrontVo) {Page<EduCourse> coursePage = new Page<>(page, limit);Map<String, Object> map = courseService.getFrontCourseList(coursePage, courseFrontVo);return R.ok().data(map);}}

3、业务层

@Overridepublic Map<String, Object> getFrontCourseList(Page<EduCourse> pageCourse, CourseFrontVo courseFrontVo) {String title = null;String subjectId = null;String subjectParentId = null;String gmtCreateSort = null;String buyCountSort = null;String priceSort = null;String teacherId = null;if (!StringUtils.isEmpty(courseFrontVo)) {title = courseFrontVo.getTitle();subjectId = courseFrontVo.getSubjectId();subjectParentId = courseFrontVo.getSubjectParentId();gmtCreateSort = courseFrontVo.getGmtCreateSort();buyCountSort = courseFrontVo.getBuyCountSort();priceSort = courseFrontVo.getPriceSort();teacherId = courseFrontVo.getTeacherId();}QueryWrapper<EduCourse> wrapper = new QueryWrapper<>();//判断条件值是否为空,不为空拼接条件if (!StringUtils.isEmpty(subjectParentId)) {//一级分类wrapper.eq("subject_parent_id", subjectParentId);}if (!StringUtils.isEmpty(subjectId)) {//二级分类wrapper.eq("subject_id", subjectId);}if (!StringUtils.isEmpty(buyCountSort)) {//关注度wrapper.orderByDesc("buy_count");}if (!StringUtils.isEmpty(priceSort)) {//价格wrapper.orderByDesc("price");}if (!StringUtils.isEmpty(gmtCreateSort)) {//最新,创建时间wrapper.orderByDesc("gmt_create");}baseMapper.selectPage(pageCourse, wrapper);long total = pageCourse.getTotal();//总记录数List<EduCourse> courseList = pageCourse.getRecords();//数据集合long size = pageCourse.getSize();//每页记录数long current = pageCourse.getCurrent();//当前页long pages = pageCourse.getPages();//总页数boolean hasPrevious = pageCourse.hasPrevious();//是否有上一页boolean hasNext = pageCourse.hasNext();//是否有下一页HashMap<String, Object> map = new HashMap<>();map.put("total", total);map.put("list", courseList);map.put("size", size);map.put("current", current);map.put("pages", pages);map.put("hasPrevious", hasPrevious);map.put("hasNext", hasNext);return map;}

3.2、前端展示

1、api/course.js

import request from '@/utils/request'export default {//前台多条件分页查询getConditionPage(page, limit, searchObj) {return request({url: `/eduservice/courseFront/getFrontCourseList/${page}/${limit}`,method: 'post',data: searchObj})},//查询所有分类(一级分类、二级分类)的方法getAllSubject() {return request({url: `/eduservice/subject/getAllSubject`,method: 'get'})}}

2、js

<script>import courseApi from "@/api/course";export default {data() {return {page: 1, //当前页data: {}, //课程列表oneSubjects: [], // 一级分类列表subSubjectList: [], // 二级分类列表searchObj: {}, // 查询表单对象oneIndex: -1,twoIndex: -1,buyCountSort: "",gmtCreateSort: "",priceSort: "",index: 0};},methods: {// 1.1、初始化课程initCourseFirst() {courseApi.getConditionPage(1, 4, this.searchObj).then((resp) => {this.data = resp.data.data;});},// 1.2、初始化一级分类initSubject() {courseApi.getAllSubject().then((resp) => {this.oneSubjects = resp.data.data.data;});},// 2、分页切换方法gotoPage(page) {courseApi.getConditionPage(page, 4, this.searchObj).then((resp) => {this.data = resp.data.data;});},// 3.1、点击某个一级分类,查询对应的二级分类seacherOne(subjectParentId, index) {this.oneIndex = index;this.twoIndex = -1;// 1)把一级分类点击的id值,赋值给searchObj,以便于进行模糊查询this.searchObj.subjectParentId = subjectParentId;// 2)点击某个一级分类进行条件查询,此时给查询条件赋值了一级分类idthis.gotoPage(1);// 3)查出所有一级分类id并与传入的id作比较,如果相同则查出该id对应的所有二级分类for (let i = 0; i < this.oneSubjects.length; i++) {var oneSubject = this.oneSubjects[i];if (oneSubject.id == subjectParentId) {this.subSubjectList = oneSubject.children;}}// 4)查询完毕清空一级分类idthis.searchObj.subjectParentId = "";},// 3.2、点击某个二级分类实现查询searchTwo(subjectId, index) {// 把index赋值,为了样式生效this.twoIndex = index;// 1)把二级分类点击id值,赋给searchObjthis.searchObj.subjectId = subjectId;// 2)点击某个二级分类进行调节查询this.gotoPage(1);// 查询完毕清空二级分类idthis.searchObj.subjectId = "";},// 4.1、根据价格进行排序searchPrice() {//设置对应变量值,为了样式生效this.buyCountSort = ''this.gmtCreateSort = ''this.priceSort = '1'//把值赋值到searchObj中this.searchObj.buyCountSort = this.buyCountSortthis.searchObj.priceSort = this.priceSortthis.searchObj.gmtCreateSort = this.gmtCreateSort//调用方法查询this.gotoPage(1)},// 4.2、根据最新进行排序searchGmtCreate() {//设置对应变量值,为了样式生效this.buyCountSort = ''this.gmtCreateSort = '1'this.priceSort = ''//把值赋值到searchObj中this.searchObj.buyCountSort = this.buyCountSortthis.searchObj.priceSort = this.priceSortthis.searchObj.gmtCreateSort = this.gmtCreateSort//调用方法查询this.gotoPage(1)},// 4.3、根据销量排序searchBuyCount() {//设置对应变量值,为了样式生效this.buyCountSort = '1'this.gmtCreateSort = ''this.priceSort = ''//把值赋值到searchObj中this.searchObj.buyCountSort = this.buyCountSortthis.searchObj.priceSort = this.priceSortthis.searchObj.gmtCreateSort = this.gmtCreateSort//调用方法查询this.gotoPage(1)},// 5、显示全部showAll(){this.subSubjectList = [];this.gotoPage(1)}},created() {// 查询所有课程this.initCourseFirst();// 查询所有一级分类this.initSubject();},};</script><style scoped>.active {background: rgb(111, 199, 111);}.hide {display: none;}.show {display: block;}</style>

3、页面

<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="showAll">全部</a></li><li v-for="oneSubject in oneSubjects" :key="oneSubject.id" :class="{active:oneIndex==index}"><a :title="oneSubject.title" @click="seacherOne(oneSubject.id, index)" href="#">{{ oneSubject.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="subject in subSubjectList" :key="subject.id" :class="{active:twoIndex==index}"><a :title="subject.title" @click="searchTwo(subject.id, index)" href="#">{{ subject.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()">价格&nbsp;<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">&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.list" :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/1" title="开始学习" class="comm-btn c- btn-1">开始学习</a></div></section><h3 class="hLh30 txtOf mt10"><a href="/course/1" :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)">&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><!-- 公共分页 结束 --></section></section><!-- /课程列表 结束 --></div></template>

四、讲师详情页

4.1、后端接口

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

课程基本信息课程分类课程描述所属讲师

2、根据课程id查询章节和小节

1、courseWebVo

@Datapublic 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;}

2、控制层

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

3、业务层

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

4、dao

<!--前台根据课程id,查询课程基础信息--><select id="getBaseCourseInfo" resultType="com.laptoy.eduservice.entity.frontVo.CourseWebVo">SELECT ec.id,ec.title,ec.cover,ec.lesson_num as lessonNum,ec.price,ec.cover,ec.buy_count as buyCount,ec.view_count as viewCount,esd.description,s1.titleas subjectLevelOne,s2.titleAS subjectLevelTwo,s1.id as subjectLevelOneId,s2.id as subjectLevelTwoId,t.name AS teacherName,t.idas teacherId,t.avatar,t.introFROM edu_course ecLEFT JOIN edu_teacher t ON ec.teacher_id = t.idLEFT JOIN edu_course_description esd on ec.id = esd.idLEFT JOIN edu_subject s1 ON ec.subject_parent_id = s1.idLEFT JOIN edu_subject s2 ON ec.subject_id = s2.idWHERE ec.id = #{id}</select>

4.2、前端展示

按课程id进行跳转查询

1、api

//根据课程id查询getFrontCourseInfo(courseId){return request({url: `/eduservice/courseFront/getFrontCourseInfo/${courseId}`,method: 'get'})}

2、js - course/_id.vue

<script>import courseApi from "@/api/course";export default {data() {return {chapterList: [],course: {courseId: "",},};},created() {this.course.courseId = this.$route.params.id;this.getCourseInfo();},methods: {// 获取课程详细信息getCourseInfo() {courseApi.getFrontCourseInfo(this.course.courseId).then((resp) => {this.chapterList = resp.data.data.chapterVideoList;this.course = resp.data.data.courseWebVo;});},},};</script>

3、页面

<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="/course" 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" height="355px" width="630px" /></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 }}&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">{{ course.buyCount }}</h6></aside></li><li><p>&nbsp;</p><aside><span class="c-fff f-fM">课时数</span><br /><h6 class="c-fff f-fM mt10">{{ course.lessonNum }}</h6></aside></li><li><p>&nbsp;</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 chapterList" :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" v-if="video.free=== true"><i class="free-icon vam mr10">免费试听</i></span><em class="lh-menu-i-2 icon16 mr5">&nbsp;</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="course.avatar" width="50" height="50" alt /></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>

五、整合阿里云视频播放器

5.1、后端接口

1、VodController

//根据视频id获取视频凭证@GetMapping("/getPlayAuth/{id}")public R getPlayAuth(@PathVariable String id) {try {String playAuth = vodService.getPlayAuth(id);return R.ok().data("playAuth", playAuth);} catch (Exception e) {e.printStackTrace();throw new LaptoyException(20001, "获取视频凭证失败");}}

2、业务层

@Value("${aliyun.vod.file.keyid}")private String accessKeyId;@Value("${aliyun.vod.file.keysecret}")private String accessKeySecret;// 根据视频id获取视频凭证@Overridepublic String getPlayAuth(String id) {String accesskeyId = accessKeyId;String accesskeySecret = accessKeySecret;try {//创建初始化对象DefaultAcsClient cl = InitObject.initVodClient(accesskeyId, accesskeySecret);//创建获取视频地址request对象和response对象GetVideoPlayAuthRequest request = new GetVideoPlayAuthRequest();//向request对象设置视频id值request.setVideoId(id);GetVideoPlayAuthResponse response = cl.getAcsResponse(request);//获取视频播放凭证return response.getPlayAuth();} catch (ClientException e) {e.printStackTrace();throw new LaptoyException(20001, "获取视频凭证失败");}}

//初始化类public class InitObject {public static DefaultAcsClient initVodClient(String accessKeyId, String accessKeySecret) throws ClientException {String regionId = "cn-shanghai"; // 点播服务接入区域DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);DefaultAcsClient client = new DefaultAcsClient(profile);return client;}}

5.2、前端展示

1、修改超链接地址

2、在pages创建文件夹player,下级创建文件_vid.vue

3、videoVo添加

4、新建播放器布局 /layout/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>

5、vod.js

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

6、页面 _vid.vue

<template><div><!-- 阿里云视频播放器样式 --><link rel="stylesheet" href="/de/prismplayer/2.8.1/skins/default/aliplayer-min.css" /><!-- 阿里云视频播放器脚本 --><script charset="utf-8" type="text/javascript" src="/de/prismplayer/2.8.1/aliplayer-min.js" /><!-- 定义播放器dom --><div id="J_prismPlayer" class="prism-player" /></div></template>

7、js

<script>import vodApi from "@/api/vod";export default {layout: "video", //应用video布局// 异步数据请求asyncData({params }) {return vodApi.getPlayAuthById(params.vid).then((response) => {return {playAuth: response.data.data.playAuth,vid: params.vid,};});},mounted() {//页面渲染之后 creatednew Aliplayer({id: "J_prismPlayer",vid: this.vid, // 视频idplayauth: this.playAuth, // 播放凭证encryptType: "1", // 如果播放加密视频,则需设置encryptType=1,非加密视频无需设置此项width: "100%",height: "500px",// 以下可选设置cover: "https://img-/e3a9d8d7355f4e3db2407fbdd9fbe7df.png", // 封面qualitySort: "asc", // 清晰度排序mediaType: "video", // 返回音频还是视频autoplay: false, // 自动播放isLive: false, // 直播rePlay: false, // 循环播放preload: true,controlBarVisibility: "hover", // 控制条的显示方式:鼠标悬停useH5Prism: true, // 播放器类型:html5},function (player) {console.log("播放器创建成功");});},};</script>

六、课程评论功能

6.1、数据库设计

CREATE TABLE `edu_comment` (`id` char(19) NOT NULL COMMENT '讲师ID',`course_id` varchar(19) NOT NULL DEFAULT '' COMMENT '课程id',`teacher_id` char(19) NOT NULL DEFAULT '' COMMENT '讲师id',`member_id` varchar(19) NOT NULL DEFAULT '' COMMENT '会员id',`nickname` varchar(50) DEFAULT NULL COMMENT '会员昵称',`avatar` varchar(255) DEFAULT NULL COMMENT '会员头像',`content` varchar(500) DEFAULT NULL COMMENT '评论内容',`is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',`gmt_create` datetime NOT NULL COMMENT '创建时间',`gmt_modified` datetime NOT NULL COMMENT '更新时间',PRIMARY KEY (`id`),KEY `idx_course_id` (`course_id`),KEY `idx_teacher_id` (`teacher_id`),KEY `idx_member_id` (`member_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='评论';## Data for table "edu_comment"#INSERT INTO `edu_comment` VALUES ('1194499162790211585','1192252213659774977','1189389726308478977','1','小三123','/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','课程很好',0,'-11-13 14:16:08','-11-13 14:16:08'),('1194898406466420738','1192252213659774977','1189389726308478977','1','小三123','/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','11',0,'-11-14 16:42:35','-11-14 16:42:35'),('119489848438850','1192252213659774977','1189389726308478977','1','小三123','/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','111',0,'-11-14 16:42:53','-11-14 16:42:53'),('1195251020861317122','1192252213659774977','1189389726308478977','1',NULL,NULL,'2233',0,'-11-15 16:03:45','-11-15 16:03:45'),('1195251382720700418','1192252213659774977','1189389726308478977','1',NULL,NULL,'4455',0,'-11-15 16:05:11','-11-15 16:05:11'),('1195252819177570306','1192252213659774977','1189389726308478977','1','小三1231','/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','55',0,'-11-15 16:10:53','-11-15 16:10:53'),('1195252899448160258','1192252213659774977','1189389726308478977','1','小三1231','/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','55',0,'-11-15 16:11:13','-11-15 16:11:13'),('1195252920587452417','1192252213659774977','1189389726308478977','1','小三1231','/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','223344',0,'-11-15 16:11:18','-11-15 16:11:18'),('1195262128095559681','14','1189389726308478977','1','小三1231','/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','11',0,'-11-15 16:47:53','-11-15 16:47:53'),('1196264505170767874','1192252213659774977','1189389726308478977','1','小三1231','/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132','666666',0,'-11-18 11:10:58','-11-18 11:10:58');

6.2、远程调用接口

在service-ucenter模块,创建远程调用接口(识别用户信息),根据登录的用户进行发布评论并存储在edu模块的评论表中

1、创建vo存储信息 - monutils.vo.UcenterMemberVo

@Data@EqualsAndHashCode(callSuper = false)@Accessors(chain = true)@ApiModel(value = "UcenterMember对象", description = "会员表")public class UcenterMemberVo implements Serializable {private static final long serialVersionUID = 1L;@ApiModelProperty(value = "会员id")@TableId(value = "id", type = IdType.ID_WORKER_STR)private String id;@ApiModelProperty(value = "微信openid")private String openid;@ApiModelProperty(value = "手机号")private String mobile;@ApiModelProperty(value = "密码")private String password;@ApiModelProperty(value = "昵称")private String nickname;@ApiModelProperty(value = "性别 1 女,2 男")private Integer sex;@ApiModelProperty(value = "年龄")private Integer age;@ApiModelProperty(value = "用户头像")private String avatar;@ApiModelProperty(value = "用户签名")private String sign;@ApiModelProperty(value = "是否禁用 1(true)已禁用, 0(false)未禁用")private Boolean isDisabled;@ApiModelProperty(value = "逻辑删除 1(true)已删除, 0(false)未删除")private Boolean isDeleted;@ApiModelProperty(value = "创建时间")@TableField(fill = FieldFill.INSERT)private Date gmtCreate;@ApiModelProperty(value = "更新时间")@TableField(fill = FieldFill.INSERT_UPDATE)private Date gmtModified;}

2、UcenterMemberController

//根据用户id查询用户信息@PostMapping("/getMemberInfoById/{memberId}")public UcenterMemberVo getMemberInfoById(@PathVariable String memberId) {Member member = memberService.getById(memberId);UcenterMemberVo memberVo = new UcenterMemberVo();BeanUtils.copyProperties(member, memberVo);return memberVo;}

3、edu模块

远程调用接口(自行对应)

@Component@FeignClient(name = "service-ucenter", fallback = UcenterClientImpl.class)public interface UcenterClient {@PostMapping("/ucenter/member/getMemberInfoById/{memberId}")UcenterMemberVo getMemberInfoById(@PathVariable String memberId);}

熔断器

@Componentpublic class UcenterClientImpl implements UcenterClient {@Overridepublic UcenterMemberVo getMemberInfoById(String memberId) {return null;}}

6.3、后端接口

1、使用代码生成器生成

gc.setOutputDir("D:\\MyCode\\IdeaCode\\project\\gulicollege\\guli_parent\\service\\service_edu" + "/src/main/java"); //输出目录pc.setModuleName("eduservice"); //模块名strategy.setInclude("edu_comment");//根据数据库哪张表生成,有多张表就加逗号继续填写

EduComment填充@TableField注解

@TableField(fill = FieldFill.INSERT)@ApiModelProperty(value = "创建时间")private Date gmtCreate;@TableField(fill = FieldFill.INSERT_UPDATE)@ApiModelProperty(value = "更新时间")private Date gmtModified;

2、控制层

@CrossOrigin@RestController@RequestMapping("/eduservice/comment")public class EduCommentController {@Autowiredprivate EduCommentService commentService;//根据课程id_分页查询课程评论的方法@GetMapping("/getCommentPage/{page}/{limit}")public R getCommentPage(@PathVariable Long page, @PathVariable Long limit, String courseId) {Page<EduComment> commentPage = new Page<>(page, limit);Map<String,Object> map = commentService.getCommentPage(commentPage,limit,courseId);return R.ok().data(map);}//添加评论@PostMapping("/auth/addComment")public R addComment(HttpServletRequest request, @RequestBody EduComment eduComment) {commentService.addComment(request,eduComment);return R.ok();}}

3、业务层

@Servicepublic class EduCommentServiceImpl extends ServiceImpl<EduCommentMapper, EduComment> implements EduCommentService {@Autowiredprivate UcenterClient ucenterClient;@Overridepublic void addComment(HttpServletRequest request, EduComment eduComment) {String memberId = JwtUtils.getMemberIdByJwtToken(request);//判断用户是否登录if (StringUtils.isEmpty(memberId)) {throw new LaptoyException(20001, "请先登录");}eduComment.setMemberId(memberId);//远程调用ucenter根据用户id获取用户信息UcenterMemberVo memberVo = ucenterClient.getMemberInfoById(memberId);if (memberVo == null) {throw new LaptoyException(20001, "远程调用失败,请检测会员服务");}eduComment.setAvatar(memberVo.getAvatar());eduComment.setNickname(memberVo.getNickname());//保存评论this.save(eduComment);}@Overridepublic Map<String, Object> getCommentPage(Page<EduComment> commentPage, Long limit, String courseId) {QueryWrapper<EduComment> wrapper = new QueryWrapper<>();//判断课程id是否为空if (!StringUtils.isEmpty(courseId)) {wrapper.eq("course_id", courseId);}//按最新排序wrapper.orderByDesc("gmt_create");//数据会被封装到commentPage中this.page(commentPage, wrapper);List<EduComment> commentList = commentPage.getRecords();long current = commentPage.getCurrent();//当前页long size = commentPage.getSize();//一页记录数long total = commentPage.getTotal();//总记录数long pages = commentPage.getPages();//总页数boolean hasPrevious = commentPage.hasPrevious();//是否有上页boolean hasNext = commentPage.hasNext();//是否有下页HashMap<String, Object> map = new HashMap<>();map.put("current", current);map.put("size", size);map.put("total", total);map.put("pages", pages);map.put("hasPrevious", hasPrevious);map.put("hasNext", hasNext);map.put("list", commentList);return map;}}

6.4、前端展示

1、comment.js

import request from '@/utils/request'export default {getPageList(page, limit, courseId) {return request({url: `/eduservice/comment/getCommentPage/${page}/${limit}`,method: 'get',params: courseId})},addComment(comment) {return request({url: `/eduservice/comment/auth/addComment`,method: 'post',data: comment})}}

2、html - 放在course/_id.vue的下面

<!-- /课程评论 开始 --><div class="mt50 commentHtml"><div><h6 class="c-c-content c-infor-title" id="i-art-comment"><span class="commentTitle">课程评论</span></h6><section class="lh-bj-list pr mt20 replyhtml"><ul><li class="unBr"><aside class="noter-pic"><img width="50" height="50" class="picImg" src="~/assets/img/avatar-boy.gif" /></aside><div class="of"><section class="n-reply-wrap"><fieldset><textarea name="" v-model="comment.content" placeholder="输入您要评论的文字" id="commentContent"></textarea></fieldset><p class="of mt5 tar pl10 pr10"><span class="fl"><tt class="c-red commentContentmeg" style="display: none"></tt></span><input type="button" @click="addComment()" value="回复" class="lh-reply-btn" /></p></section></div></li></ul></section><section class=""><section class="question-list lh-bj-list pr"><ul class="pr10"><li v-for="comment in data.list" :key="comment.id"><aside class="noter-pic"><img width="50" height="50" class="picImg" :src="comment.avatar" /></aside><div class="of"><span class="fl"><font class="fsize12 c-blue">{{ comment.nickname }}</font><font class="fsize12 c-999 ml5">评论:</font></span></div><div class="noter-txt mt5"><p>{{ comment.content }}</p></div><div class="of mt5"><span class="fr"><font class="fsize12 c-999ml5">{{comment.gmtCreate}}</font></span></div></li></ul></section></section><!-- 公共分页 开始 --><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></div><!-- /课程评论 结束 -->

3、js

<script>import courseApi from "@/api/course";import comment from "@/api/comment";export default {methods: {// 获取课程详细信息getCourseInfo() {courseApi.getFrontCourseInfo(this.course.courseId).then((resp) => {this.chapterList = resp.data.data.chapterVideoList;this.course = resp.data.data.courseWebVo;this.course.courseId = resp.data.data.courseWebVo.id;});},// 初始化评论initComment() {comment.getPageList(this.page, this.limit, this.course.courseId).then((response) => {this.data = response.data.data;console.log(response.data.data);});},addComment() {ment.courseId = this.course.courseId;ment.teacherId = this.course.teacherId;comment.addComment(ment).then((response) => {if (response.data.success) {this.$message({message: "评论成功",type: "success",});ment.content = "";this.initComment();}});},gotoPage(page) {comment.getPageList(page, this.limit, this.courseId).then((response) => {this.data = response.data.data;});},},data() {return {chapterList: [],course: {courseId: "",},data: {},page: 1,limit: 4,total: 10,comment: {content: "",courseId: "",teacherId: "",},isbuyCourse: false,};},created() {this.course.courseId = this.$route.params.id;this.getCourseInfo();this.initComment();},};</script>

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。