[TOC]

学习功能

布局处理

  • src/views/learn/index.vue ```vue

    
    <a name="VLbUS"></a>
    ### 公共组件处理
    将 `src/views/course/components/CourseContentList.vue` 剪切到 `src/components/` 目录下作为公共组件
    
    - 修改 `src/views/course/components/courestContent.vue` 引入组件的路径
    ```vue
    <script>
      import CourseContentList from '@/components/CourseContentList'
    </script>
    

    接口抽离

    • src/components/CourseCountentList.vue ```vue
    
    - `src/views/course/components/courestContent.vue`
    ```vue
    <template>
      <div class="course-content">
        <!-- 顶部广告轮播图 -->
    
        <!-- 底部课程列表 -->
        <course-content-list :fetchData="fetchData"></course-content-list>
      </div>
    </template>
    <script>
    
    import { getAllAds, getQueryCourses } from '@/services/course'
    export default {
      name: 'CourseContent',
      components: {
        CourseContentList
      },
    
      methods: {
        // 传入请求
        fetchData (options) {
          return getQueryCourses(options)
        },
    
      },
    
    }
    </script>
    

    接口封装与数据绑定改进

    • src/services/course.js

      // 获取已购课程
      export const getPurchaseCourse = () => {
      return request({
        method: 'GET',
        url: '/front/course/getPurchaseCourse'
      })
      }
      
    • src/components/CourseContentList.vue

      <template>
      <div class="course-content-list">
        <!-- 下拉刷新 -->
        <van-pull-refresh v-model="isRefreshing" @refresh='onRefresh'>
        <!-- 课程列表 -->
        <van-list v-model="loading" :finished='finished' finished-text='没有更多了' @load='onLoad'>
          <van-cell v-for='item in list' :key='item.id'>
            <!-- 图片 -->
            <div>
              <!-- 所有商品与已购商品接口响应的图片地址属性名不同,需要检测 -->
              <img :src="item.courseImgUrl || item.image" alt="">
            </div>
            <div class="course-info">
              <h3 v-text="item.courseName || item.name"></h3>
              <p class="course-preview" v-html="item.previewFirstField"></p>
              <!-- 检测是否有价格,如果没有,说明为已购商品,不展示价格区域 -->
              <p v-if="item.price" class="price-container">
                <span class="course-discounts">¥{{item.discounts}}</span>
                <s class="course-price">¥{{item.price}}</s>
              </p>
            </div>
          </van-cell>
        </van-list>
        </van-pull-refresh>
      </div>
      </template>
      <script>
      // import { getQueryCourses } from '@/services/course'
      export default {
      name: 'CourseContentList',
      props: {
        // 用于请求数据的函数
        fetchData: {
          type: Function,
          required: true
        }
      },
      data () {
        return {
          // 数据列表
          list: [],
          // 是否要进行加载
          loading: false,
          // 是否加载完毕
          finished: false,
          // 数据页数
          currentPage: 1,
          // 下拉刷新状态
          isRefreshing: false
        }
      },
      methods: {
        async onRefresh () {
          // 还原数据页数为 1
          this.currentPage = 1
          // 重新请求数据
          const { data } = await this.fetchData({
            currentPage: this.currentPage,
            pageSize: 10
          })
          // 如果存在数据,清空并课程数据,否则结束
          if (data.data && data.data.records && data.data.records.length !== 0) {
            // 重新赋值课程数据
            this.list = data.data.records
          } else if (data.content && data.content.length !== 1) {
            this.list = data.content
          } else {
            this.finished = true
          }
          // 提示
          this.$toast('刷新成功')
      
          // 关闭下拉提示框
          this.isRefreshing = false
        },
        async onLoad () {
          // 请求数据
          const { data } = await this.fetchData({
            currentPage: this.currentPage,
            pageSize: 10,
            status: 1
          })
          // 检测,如果没有数据了,结束,如果有,保存
          if (data.data && data.data.records && data.data.records.length !== 0) {
            this.list.push(...data.data.records)
          } else if (data.content && data.content.length !== 1) {
            this.list = data.content
          } else {
            this.finished = true
          }
      
          // 下一次请求下一页
          this.currentPage++
          // 加载状态结束
          this.loading = false
          // 数据全部加载完成
          if (this.list.length < 10) {
            this.finished = true
          }
        }
      }
      }
      </script>
      

    课程详情

    组件准备

    • src/views/course-info/index.vue ```javascript

      
      - `src/router/index.js`
      ```javascript
      
      const routes = [
      
        // 课程详情页
        {
          path: '/course-info/:courseId/',
          name: 'course-info',
          component: () => import(/* webpackChunkName: 'course-info' */'@/views/course-info/index'),
          props: true
        },
        // 404
      
      ]
      
      • src/compontents/CourseContentList.vue 跳转课程详情 ```vue

      
      <a name="dOz6Y"></a>
      ### 接口封装
      
      - `scr/services/course.js`
      ```javascript
      // 获取课程详情
      export const getCourseById = params => {
        return request({
          method: 'GET',
          url: '/front/course/getCourseById',
          params
        })
      }
      

      主体内容区域

      • src/views/course-info/indec.vue ```vue

        
        <a name="rGOH9"></a>
        ### 选项卡处理
        
        - `src/views/course-info/index.vue`
        ```vue
        <!-- 课程详细内容 -->
              <van-cell class="course-detail">
                <!-- 选项卡 -->
                <van-tabs sticky scrollspy>
                  <van-tab title="详情">
                    <!-- 详情在后台是通过富文本编辑器设置的,内容为 HTML 文本 -->
                    <div v-html="course.courseDescription"></div>
                  </van-tab>
                  <van-tab title="内容">内容</van-tab>
                </van-tabs>
              </van-cell>
        

        章节组件封装

        • src/services/course.js

          // 获取课程章节
          export const getSectionAndLesson = params => {
          return request({
            method: 'GET',
            url: '/front/course/session/getSectionAndLesson',
            params
          })
          }
          
        • src/views/course-info/index.vue ```vue

        
        <a name="Hsewr"></a>
        #### 章节组件布局
        
        - `src/views/course-info/components/CourseSectionAndLesson.vue`
        ```vue
        <template>
          <div class="section-and-lesson">
            <!-- 章节标题 -->
            <h2 class="section" v-text="sectionData.sectionName"></h2>
            <!-- 课时标题 -->
            <p
            v-for="item in sectionData.courseLessons"
            :key="item.id"
            class="lesson"
            @click="handleClick(item)">
            <span v-text="item.theme"></span>
            <van-icon v-if="item.canPlay" name='play-circle' size='20'></van-icon>
            <van-icon v-else name='lock' size='20'></van-icon>
            </p>
          </div>
        </template>
        <script>
        export default {
          name: 'CourseSectionAndLesson',
          props: {
            // 章节信息
            sectionData: {
              type: Object,
              required: true
            }
          }
        }
        </script>
        <style lang="scss" scoped>
        .section-and-lesson {
          padding: 0 20px;
        }
        .lesson {
          display: flex;
          justify-content: space-between;
        }
        </style>
        

        底部支付处理

        • src/views/course-info/index.vue ```vue

          
          <a name="ptkJx"></a>
          ## 课时视频
          <a name="bwQq7"></a>
          ### 组件准备
          
          - src/router/index.js
          ```javascript
          const router = [
          
            // 视频页
            {
              path: '/lesson-video/:lessonId/',
              name: 'lesson-video',
              component: () => import(/* webpackChunkName: 'lesson-video' */'@/views/course-info/video'),
              props: true
            },
          
          ]
          
          • src/views/course-info/CourseSectionAndLesson.vue
            methods: {
              handleClick (lessonInfo) {
                if (lessonInfo.canPlay) {
                  this.$router.push({
                    name: 'lesson-video',
                    params: {
                      lessonId: lessonInfo.id
                    }
                  })
                }
              }
            }
            

          接口封装

          • src/services/course.js

            // 根据fileId获取阿里云对应的视频播放信息
            export const getVideoInfo = params => {
            return request({
              method: 'GET',
              url: '/front/course/media/videoPlayInfo',
              params
            })
            }
            
          • src/views/course-info/video.vue ```vue

            <a name="Hv8DG"></a>
            ### 阿里云视频播放
            
            - public/index.html
            ```html
            <!DOCTYPE html>
            <html lang="">
              <head>
                <meta charset="utf-8">
                <meta http-equiv="X-UA-Compatible" content="IE=edge">
                <meta name="viewport" content="width=device-width,initial-scale=1.0">
                <link rel="icon" href="<%= BASE_URL %>favicon.ico">
                <title><%= htmlWebpackPlugin.options.title %></title>
                <!-- 播放器样式 -->
                <link rel="stylesheet" href="https://g.alicdn.com/de/prismplayer/2.9.3/skins/default/aliplayer-min.css" />
              </head>
              <body>
                <noscript>
                  <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
                </noscript>
                <div id="app"></div>
                <!-- 播放器插件 -->
                <script src="https://g.alicdn.com/de/prismplayer/2.9.3/aliplayer-h5-min.js"></script>
                <!-- built files will be auto injected -->
              </body>
            </html>
            
            • src/views/course-info/video.vue ```vue

              ```

              手机测试视频播放

              • 确保手机与电脑在统一无限网络内
              • 打开终端输入命名 查找无限网的IP
                • win: ipconfig -all
                • mac: ifconfig
              • 手机输入IP,端口好与电脑测试的一致