美文网首页
选课功能

选课功能

作者: amanohina | 来源:发表于2021-03-16 21:10 被阅读0次

    Course组件为选课页面,分为上中下三个部分,顶部为显示logo的导航区域,中间为课程选择区域(轮播+课程列表),底部使用公共组件LayoutFooter


    结构大概是长这个样子的

    course
    ├── components
    │ ├── CourseHeader.vue
    │ └── CourseContent.vue
    └── index.vue

    在course/index.vue中引入CourseHeader,CourseContent,LayoutFooter

    <template>
      <div class="course">
          <layout-footer></layout-footer>
          <course-header></course-header>
          <course-content></course-content>
      </div>
    </template>
    
    <script>
    import CourseHeader from './components/courseHeader'
    import CourseContent from './components/courseContent'
    import LayoutFooter from '@/components/LayoutFooter'
    export default {
      name: 'Course',
      components: {
        LayoutFooter,
        CourseHeader,
        CourseContent
      }
    }
    </script>
    
    <style lang="scss" scoped>
     
    </style>
    
    

    CourseHeader组件

    导航部分只有logo图显示,直接设置就行了
    logo图使用vant的Image组件,引入图片,调整样式就好

    <template>
      <div class="course-header">
        <van-image
        :src="require('@/assets/logo.png')"
        ></van-image>
      </div>
    </template>
    
    <script>
    export default {
      name: 'CourseHeader'
    }
    </script>
    
    <style lang="scss" scoped>
    .course-header {
      height: 50px;
    }
    .van-image {
      width: 180px;
      margin-left: -20px;
    }
    </style>
    
    

    CourseContent

    选课内容区域分为上下两部分,顶部为轮播图,底部为课程列表

    广告轮播图

    布局处理

    使用vant的Swipe轮播组件
    结构设置完毕之后,需要请求广告数据动态创建

    封装接口

    广告位需要用到两个接口,我们这边涉及到的项目与上一个后台管理其实是共通的功能,所以还是以前的地址

    • 获取所有的广告位:接口
    • 获取广告位及其对应某个约定id的广告:接口
      但是!这里获取所有广告位的接口无需再进行封装了,因为我们通过接口的响应数据(参考上一个项目)得到一个讯息那就是“首页顶部轮播图”的位置标记spaceKey为999,我们可以将这个数值固定使用,这个算是我们和后端的一种约定吧
      新建src/services/course.js 封装获取广告的接口功能就可以了
    import request from '@/utils/request'
    
    // 获取广告位及其对应的广告
    export const getAllAds = params => {
      return request({
        method: 'GET',
        url: '/front/ad/getAllAds',
        params
      })
    }
    
    

    1.引入,请求数据,存储,保存在data

    • 根据数据响应的格式,我们可以得知content[0].adDTOList就是当前广告位的广告列表
    • img:图片地址
    • id:课程id

    2.将轮播图项根据广告数据设置,进行一波样式处理
    3.状态筛选,由于广告返回数据中只有上架状态的数据才能展示,我们要进行一定程度的筛选,上架的status为1,下架为0,需要对结果进行遍历,我们推荐使用计算属性,这在一定程度上优化了性能

    综上所述:

    <template>
      <div class="course-content">
      <van-swipe class="my-swipe" :autoplay="3000" indicator-color="white">
        <van-swipe-item v-for="item in ativeAdList" :key="item.id">
          <img :src="item.img" alt="">
        </van-swipe-item>
      </van-swipe>
      </div>
    </template>
    
    <script>
    import { getAllAds } from '@/services/course'
    export default {
      name: 'CourseContent',
      data () {
        return {
        //   轮播图图片列表
          adList: []
        }
      },
      created () {
        // 请求轮播图图片信息
        this.loadAds()
      },
      methods: {
        async loadAds () {
          const { data } = await getAllAds({
            // 此处的999代表首页顶部轮播图的广告位
            spaceKeys: '999'
          })
          // 存储数据到adList
          this.adList = data.content[0].adDTOList
        }
      },
      computed: {
        ativeAdList () {
          return this.adList.filter(item => item.status === 1)
        }
      }
    }
    </script>
    
    <style lang="scss" scoped>
    .my-swipe {
      width: 100%;
    }
    .my-swipe img {
      height: 170px;
    }
    .my-swipe .van-swipe-item {
        display: flex;
        justify-content: center;
        overflow: hidden;
    }
    </style>
    
    

    轮播图完成

    课程列表

    基础布局

    创建course/components/CourseContentList.vue

    //CourseContentList.vue
    <template>
      <div class="course-content-list"></div>
    </template>
    
    <script>
    export default {
      name: 'CourseContentList'
    }
    </script>
    
    <style lang="scss" scoped></style>
    

    将该组件引入到CourseContent.vue中

    这个功能采用的是Vant的List列表组件
    设置到页面中

    • load事件:用于进行数据请求
      • list组件初始化之后会触发一次load事件,用于加载首屏的数据
      • 如果一次请求记载的数据较少,列表内容无法铺满屏幕,就会自动再次触发load,直到内容铺满屏幕或者加载全部的数据
      • 滑动列表触底时也会触发load
    • loading:控制(触底后)新数据是否加载
      • 未加载时loading为false,当load事件触发,loading自动变更为true,显示加载状态提示,请求过程中,无法再次触发load事件,请求完毕之后loading为false取消加载提示即可
    • finished:
      • 每次请求完毕之后,需要手动将loading设置为false,表示本次加载结束
      • 所有数据加载结束,finished变为true,这个时候就不会触发load事件了
    <template>
      <div class="course-content-list">
        <van-list
            v-model="loading"
            :finished="finished"
            finished-text="没有更多了"
            @load="onLoad"
            >
            <van-cell v-for="item in list" :key="item" :title="item" />
        </van-list>
      </div>
    </template>
    
    <script>
    export default {
      name: 'CourseContentList',
      data () {
        return {
          // 用于存储数据,这里我们模拟一下
          list: [1, 2, 3, 4, 5],
          // 是否处于加载中
          loading: false,
          // 是否加载完毕
          finished: false
        }
      },
      methods: {
        onLoad () {
          console.log('发送请求')
        }
      }
    }
    </script>
    <style lang="scss" scoped>
    </style>
    
    

    固定列表

    上述代码出现之后有两个问题

    • 列表滚动时实际上是整个course组件都在进行滚动,这个时候顶部的导航与轮播就会跟着一起滚动
    • 加载完毕之后,列表底部的提示内容被LayoutFooter的路由按钮功能遮挡住了
      解决方法:
    • 设置列表固定定位
      • CourseHeader 高度定为50px
      • 轮播高度定为170px
      • LayoutFooter高度定为50px
        故而我们进行样式设置:
    // CourseContentList.vue
    ...
    <style lang="scss" scoped>
    .course-content-list {
      position: fixed;
      left: 0;
      right: 0;
      top: 220px;
      bottom: 50px;
      overflow-y: auto;
    }
    </style>
    

    功能是没什么问题了,但是要注意有一个很明显的问题,课程列表组件是一个独立的组件功能,应该只对自身负责,当前操作中设置的top和bottom实际上是根据父组件的布局来设置的,那么要是父组件的布局变化,或者其他组件需要用到这个列表组件,都需要修改子组件的数据,这是非常不合理的
    说了这么多,总结一下也就是说:CourseContentList和CourseContent耦合了
    我们呢,应该将与父组件布局相关的top和left由父组件设置,进行解耦
    将CourseContentList的bottom和top修改为0

    // CourseContentList.vue
    ...
    <style lang="scss" scoped>
    .course-content-list {
      ...
      top: 0;
      bottom: 0;
    }
    </style>
    

    在父组件CourseContent中设置子组件容器的位置就好

    // CourseContent.vue
    ...
    // 底部课程列表的位置样式,不应该设置在组件内容
    .course-content-list {
      top: 220px;
      bottom: 50px;
    }
    

    封装接口

    需要使用以下接口:

    • 分页查询课程内容:接口
    ...
    // 分页查询课程信息
    export const getQueryCourses = data => {
      return request({
        method: 'POST',
        url: '/boss/course/getQueryCourses',
        data
      })
    }
    
    

    引入,并请求数据


    声明一个currentPage,来记录第几次触发下拉,触发一次请求一次数据,请求参数有三个,一个是当前请求次数,一个是一次请求多少数据,一个是上架课程状态码1,每次请求之后都要手动更新加载状态位false,请求结束后要finished结束

    布局与数据绑定

    根据数据进行布局设置,并且绑定数据
    在给list赋值之前要判断一下传来的数据是否有值,因为这个组件将来也是其他组件要使用的子组件

    // CourseContentList.vue
    ...
    <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" alt="">
        </div>
        <div class="course-info">
          <h3 v-text="item.courseName"></h3>
          <p class="course-preview" v-html="item.previewFirstField"></p>
          <p class="price-container">
            <span class="course-discounts">¥{{item.discounts}}</span>
            <s class="course-price">¥{{item.price}}</s>
          </p>
        </div>
      </van-cell>
    </van-list>
    ...
    <script>
    ...
      methods: {
        async onLoad () {
          const { data } = await getQueryCourses(...)
          // 检测,如果没有数据了,结束,如果有,保存
          if (data.data && data.data.records && data.data.records.length !== 0) {
            this.list.push(...data.data.records)
          }
                ...
        }
      }
    ...
    </script>
    ...
    <style lang="scss" scoped>
    .course-content-list {
      position: fixed;
      left: 0;
      right: 0;
      top: 220px;
      bottom: 50px;
      overflow-y: auto;
    }
    // 课程条目设置flex,内部元素左右显示
    .van-cell__value {
      height: 100px;
      padding: 10px 0;
      display: flex;
    }
    
    // 左侧图设置固定尺寸
    .van-cell__value img {
      width: 75px;
      height: 100%;
      border-radius: 5px;
    }
    
    // 右侧内容区域 flex: 1 撑满父元素
    .course-info {
      flex: 1;
      display: flex;
      flex-direction: column;
      padding: 0 10px;
    }
    
    .course-info .course-preview {
      flex-grow: 1;
    }
    
    .course-info .course-discounts {
      color: #ff7452;
      margin-right: 10px;
    }
    
    p, h3 {
      margin: 0;
    }
    </style>
    

    下拉刷新

    下拉的时候,CourseContentList需要刷新
    这里我们使用了Vant的PullRefresh下拉刷新组件

    • 这里示例中使用了Toast轻提示组件,在我们这种使用了全局导入vant的项目基础之下可以使用this.$toast()调用
    • 也可以使用下拉刷新组件的success-text与success-duration配合使用(自行探索)


      处理函数
      结构
      用于控制的数据

    相关文章

      网友评论

          本文标题:选课功能

          本文链接:https://www.haomeiwen.com/subject/gbhdcltx.html