选课功能

2021-03-16  本文已影响0人  amanohina

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轮播组件
结构设置完毕之后,需要请求广告数据动态创建

封装接口

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

import request from '@/utils/request'

// 获取广告位及其对应的广告
export const getAllAds = params => {
  return request({
    method: 'GET',
    url: '/front/ad/getAllAds',
    params
  })
}

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

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列表组件
设置到页面中

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

固定列表

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

// 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下拉刷新组件

上一篇 下一篇

猜你喜欢

热点阅读