美文网首页
选课功能

选课功能

作者: 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