Day213.讲师详细页、课程列表页面、课程详细页、阿里云视频播放测试、阿里云云视频播放器 -谷粒学院

谷粒学院

讲师详细页

一、后端部分

image-20210307112354681

1、TeacherFrontController

@RestController
@CrossOrigin
@RequestMapping("/eduservice/teacherFront")
public class TeacherFrontController {
    
    

    @Autowired
    private EduTeacherService eduTeacherService;

    @Autowired
    private EduCourseService eduCourseService;

    //根据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);
    }

}

这里不够严谨,其实想要再创service,把业务逻辑写在service中

2、swagger测试

image-20210307113801374


二、前端部分

1、teacher api

guli-front\api\teacher.js

import request from '@/utils/request'

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

2、讲师详情页中调用api

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

三、页面渲染

1、讲师基本信息模板

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

四、测试

image-20210307130740894

  • 有课程

image-20210307130751324

  • 如果没课程

image-20210307130801321


课程列表页面

一、后端接口

  • com.achang.eduservice.entity.frontVo.CourseFrontVo
@Data
public 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;

}
  • service

接口

public interface EduCourseService extends IService<EduCourse> {
    
    
	......
        
    //前台多条件分页查询
    Map<String, Object> getCourseFrontInfo(Page<EduCourse> pageCourse, CourseFrontVo courseFrontVo);
    
}

Impl

@Service
public class EduCourseServiceImpl extends ServiceImpl<EduCourseMapper, EduCourse> implements EduCourseService {
    
    

    //前台多条件分页查询
    @Override
    public Map<String, Object> getCourseFrontInfo(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;
    }

}


二、前端部分

1、定义api

api/course.js

import request from '@/utils/request'

export default{
    
    
    //前台多条件分页查询
    getConditionPage(page,limit,searchObj){
    
    
        return request({
    
    
            url: `/eduservice/courseFront/getConditionPage/${
      
      page}/${
      
      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: "",
        };
    },
    methods: {
    
    
        //根据价格进行排序
        searchPrice(){
    
    
            //设置对应变量值,为了样式生效
            this.buyCountSort=''
            this.gmtCreateSort=''
            this.priceSort='1'

            //把值赋值到searchObj中
            this.searchObj.buyCountSort = this.buyCountSort
            this.searchObj.priceSort = this.priceSort
            this.searchObj.gmtCreateSort = this.gmtCreateSort

            //调用方法查询
            this.gotoPage(1)
        },
        //根据最新进行排序
        searchGmtCreate(){
    
    
            //设置对应变量值,为了样式生效
            this.buyCountSort=''
            this.gmtCreateSort='1'
            this.priceSort=''

            //把值赋值到searchObj中
            this.searchObj.buyCountSort = this.buyCountSort
            this.searchObj.priceSort = this.priceSort
            this.searchObj.gmtCreateSort = this.gmtCreateSort

            //调用方法查询
            this.gotoPage(1)
        },
        //根据销量排序
        searchBuyCount(){
    
    
            //设置对应变量值,为了样式生效
            this.buyCountSort='1'
            this.gmtCreateSort=''
            this.priceSort=''

            //把值赋值到searchObj中
            this.searchObj.buyCountSort = this.buyCountSort
            this.searchObj.priceSort = this.priceSort
            this.searchObj.gmtCreateSort = this.gmtCreateSort

            //调用方法查询
            this.gotoPage(1)

        },
        //课程第一次查询
        initCourseFirst() {
    
    
            courseApi.getConditionPage(1, 8, this.searchObj).then((resp) => {
    
    
                this.data = resp.data.data;
            });
        },
        //查询所有一级分类
        initSubject() {
    
    
            courseApi.getAllSubject().then((resp) => {
    
    
                this.subjectNestedList = resp.data.data.list;
                // this.subSubjectList = resp.data.data.list.children
            });
        },
        //分页切换方法
        gotoPage(page) {
    
    
            courseApi.getConditionPage(page, 8, this.searchObj).then((resp) => {
    
    
                this.data = resp.data.data;
            });
        },
        //点击某个一级分类,查询对应的二级分类
        seacherOne(subjectParentId, index) {
    
    
            //把传递来的index赋值给ondex,为了active样式生效
            // this.oneIndex = index
            // this.twoIndex = -1
            // this.searchObj.subjectId=''
            // this.subSubjectList=[]

            //把一级分类点击的id值,赋值给searchObj
            this.searchObj.subjectParentId = subjectParentId;
            //点击某个一级分类进行条件查询
            this.gotoPage(1);

            //拿着点击的一级分类id 和 所有一级分类id进行比较
            //如果id相同,从那个一级分类中获取他的二级分类
            for (let i = 0; i < this.subjectNestedList.length; i++) {
    
    
                //获取每个一级分类
                var oneSubject = this.subjectNestedList[i];
                //比较id是否相同
                if (oneSubject.id == subjectParentId) {
    
    
                    this.subSubjectList = oneSubject.children;
                }
            }
        },
        //点击某个二级分类实现查询
        searchTwo(subjectId, index) {
    
    
            //把index赋值,为了样式生效
            this.twoIndex = index;
            //把二级分类点击id值,赋给searchObj
            this.searchObj.subjectId = subjectId;
            //点击某个二级分类进行调节查询
            this.gotoPage(1);
        },
    },
    created() {
    
    
        //课程第一次查询
        this.initCourseFirst();
        //一级分类显示
        this.initSubject();
    },
};
</script>
<style scoped>
    .active {
    
    
        background: #bdbdbd;
    }
.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="#">全部</a>
                </li>
                <li
                  v-for="subjectNested in subjectNestedList"
                  :key="subjectNested.id"
                >
                  <a
                    :title="subjectNested.title"
                    @click="seacherOne(subjectNested.id, index)"
                    href="#"
                    >{
   
   { subjectNested.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">
                  <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、测试

image-20210307160824389


课程详情页

一、后端部分

  • controller

com.achang.eduservice.controller.front.CourseFrontController

@RestController
@CrossOrigin
@RequestMapping("/eduservice/courseFront")
public class CourseFrontController {
    
    

    @Autowired
    private EduCourseService eduCourseService;
    
    @Autowired
    private EduChapterService eduChapterService;
    
    .......

    //课程详情的方法
    @GetMapping("/getFrontCourseInfo/{courseId}")
    public R getFrontCourseInfo(@PathVariable String courseId){
    
    
        //根据课程id,编写sql语句查询课程信息
        CourseWebVo courseWebVo = eduCourseService.getBaseCourseInfo(courseId);

        //根据课程id,查询章节和小节信息
        List<ChapterVo> chapterVideoList = eduChapterService.getChapterVideoByCourseId(courseId);

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

    }

}
  • service

接口

//前台根据课程id,查询课程基础信息
CourseWebVo getBaseCourseInfo(String courseId);

Impl

//前台根据课程id,查询课程基础信息
@Override
public CourseWebVo getBaseCourseInfo(String courseId) {
    
    
    return baseMapper.getBaseCourseInfo(courseId);
}
  • dao

EduCourseMapper接口

@Component
public interface EduCourseMapper extends BaseMapper<EduCourse> {
    
    
	......
    //前台根据课程id,查询课程基础信息
    CourseWebVo getBaseCourseInfo(String courseId);
}

Impl实现类xml文件

    <!--前台根据课程id,查询课程基础信息-->
    <select id="getBaseCourseInfo" resultType="com.achang.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.title AS subjectLevelOne,
            s1.id as subjectLevelOneId,
            s2.id as subjectLevelTwoId,
            s2.title AS subjectLevelTwo,
            t.name AS teacherName,
            t.id as teacherId,
            t.avatar,
            t.intro
        FROM
            edu_course ec
                LEFT JOIN edu_teacher t ON ec.teacher_id = t.id
                LEFT JOIN edu_subject s1 ON ec.subject_parent_id = s1.id
                LEFT JOIN edu_subject s2 ON ec.id = s2.id
                left join edu_course_description esd on ec.id = esd.id
        WHERE
            ec.id = #{id}
    </select>
  • 记得要指定xml配置文件配置,在properties配置文件中
#配置mapper xml文件的路径
mybatis-plus.mapper-locations=classpath:com/achang/eduservice/mapper/xml/*.xml
  • 测试

image-20210307175018878


二、前端部分

  • api

guli-front\api\course.js

import request from '@/utils/request'

export default{
    
    
	........
    //根据课程id,查询课程详细信息
    getFrontCourseInfo(courseId){
    
    
        return request({
    
    
            url: `/eduservice/courseFront/getFrontCourseInfo/${
      
      courseId}`,
            method: 'get'
        })
    }
}
  • 页面js脚本

pages/course/_id.vue

<script>
    import courseApi from "@/api/course";
export default {
    
    
    methods: {
    
    
        //获取课程详细信息
        getCourseInfo() {
    
    
            courseApi.getFrontCourseInfo(this.course.courseId).then((resp) => {
    
    
                this.chapterList = resp.data.data.chapterVideoList;
                this.course = resp.data.data.courseWebVo;
            });
        },
    },
    data() {
    
    
        return {
    
    
            chapterList: [],
            course: {
    
    
                courseId: "",
            },
        };
    },
    created() {
    
    
        this.course.courseId = this.$route.params.id;
        获取课程详细信息
        this.getCourseInfo();
    },
};
</script>
  • 页面html
<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>
  • 测试效果

image-20210307192514608


阿里云视频播放测试

一、获取播放地址播放

获取播放地址

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

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

image-20210307192626775

因此在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 : '你的视频播放地址',

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

image-20210307194213744

  • 效果

image-20210307194230586

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

阿里云播放器支持通过播放凭证自动换取播放地址进行播放,接入方式更为简单,且安全性更高。播放

凭证默认时效为100秒(最大为3000秒),只能用于获取指定视频的播放地址,不能混用或重复使用。

如果凭证过期则无法获取播放地址,需要重新获取凭证。

encryptType:'1',//如果播放加密视频,则需设置encryptType=1,非加密视频无需设置此项
vid : '视频id',
playauth : '视频授权码',

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

设置播放凭证的有效期

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

request.setAuthInfoTimeout(200L);

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

image-20210307201320308


整合阿里云视频播放器

一、后端接口

  • 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 AchangException(20001,"获取视频凭证失败");
    }

}
  • service

接口

//根据视频id获取视频凭证
String getPlayAuth(String id);

impl

    //根据视频id获取视频凭证
    @Override
    public String getPlayAuth(String id) {
    
    
        String accesskeyId = ConstantVodUtils.ACCESSKEY_ID;
        String accesskeySecret = ConstantVodUtils.ACCESSKEY_SECRET;

        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 AchangException(20001,"获取视频凭证失败");
        }
        
    }
  • ConstantVodUtils
@Component
public class ConstantVodUtils implements InitializingBean {
    
    

    @Value("${aliyun.vod.file.keyid}")
    private String accessKeyId;

    @Value("${aliyun.vod.file.keysecret}")
    private String accessKeySecret;

    public static String ACCESSKEY_ID;
    public static String ACCESSKEY_SECRET;


    @Override
    public void afterPropertiesSet() throws Exception {
    
    
        ACCESSKEY_ID = this.accessKeyId;
        ACCESSKEY_SECRET = this.accessKeySecret;
    }

}
  • InitObject
//初始化类
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;
    }
}
  • 测试

image-20210307203854056


二、前端部分

  • api

guli-front\api\vod.js

import request from '@/utils/request'

export default{
    
    
    //根据视频id,获取视频凭证
    getPlayAuthById(id){
    
    
        return request({
    
    
            url:`/eduvod/video/getPlayAuth/${
      
      id}`,
            method: 'get'
        })
    }
}
  • 页面js脚本

阿昌用之前的js写法不能用,在最后生命中mounted()中视频凭证的数据丢失了,不知道为什么

然后换成老师的这种异步调用一次的就成功了

<script>
import vodApi from "@/api/vod";
export default {
    
    
  layout: "video", //应用video布局
  //   created() {
    
    
  //     this.vid = this.$route.params.vid;
  //     this.getPlayAuth();
  //   },
  //   methods: {
    
    
  //     getPlayAuth() {
    
    
  //       vodApi.getPlayAuthById(this.videoSourceId).then((resp) => {
    
    
  //         this.playAuth = resp.data.data.playAuth;
  //       });
  //     },
  //   },
  //   data() {
    
    
  //     return {
    
    
  //       playAuth: "",
  //       vid: "",
  //     };
  //   },
  asyncData({
    
     params, error }) {
    
    
    return vodApi.getPlayAuthById(params.vid).then((response) => {
    
    
      return {
    
    
        playAuth: response.data.data.playAuth,
        vid: params.vid,
      };
    });
  },
  mounted() {
    
    
    //页面渲染之后  created
    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>
  • 页面标签
<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>
  • 测试

image-20210307221040811

  • 其他常见的可选配置
// 以下可选设置
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
  • 可选择的加入播放组件,如广告等。。。

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


猜你喜欢

转载自blog.csdn.net/qq_43284469/article/details/114501011