选课功能

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配合使用(自行探索)


    处理函数

    结构

    用于控制的数据
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容