8.首页效果展示
8.1 上拉加载数据
实际上就是移动的端的分页的展示 Vant - Mobile UI Components built on Vue
List 组件通过
loading
和finished
两个变量控制加载状态,当组件滚动到底部时,会触发load
事件并将loading
设置成true
。此时可以发起异步操作并更新数据,数据更新完毕后,将loading
设置成false
即可。若数据已全部加载完毕,则直接将finished
设置成true
即可。隐藏条件,每加载一次,页码就要加1
:immediate-check="false" 属性可以避免刚开始校验列表的高度
<template> <div class="box"> <header class="header">home header</header> <div class="content"> <div class="myswiper"> <van-swipe class="my-swipe" :autoplay="3000" indicator-color="white"> <van-swipe-item v-for="item of bannerList" :key="item.bannerid"> <van-image :src="item.img" /> </van-swipe-item> </van-swipe> </div> <van-grid icon-size="40px" :column-num="5" :border="false"> <van-grid-item v-for="item of navList" :key="item.navid" :icon="item.imgurl" :text="item.title" /> </van-grid> <ul class="seckillTitle"> <li>嗨购秒杀</li> <li> <van-count-down :time="time"> <template #default="timeData"> <span class="block">{{ timeData.hours }}</span> <span class="colon">:</span> <span class="block">{{ timeData.minutes }}</span> <span class="colon">:</span> <span class="block">{{ timeData.seconds }}</span> </template> </van-count-down> </li> <li> 更多秒杀 <van-icon name="clock" color="#f66" size="16"/> </li> </ul> <ul class="seckillList"> <li v-for="item of seckillList" :key="item.proid"> <van-image width="55px" height="55px" :src="item.img1" /> <p> ¥{{ item.originprice }} </p> </li> </ul> <!-- 产品列表 --> <van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad" :immediate-check = "false" > <ProList :proList = "proList"/> </van-list> </div> </div> </template> <script> import Vue from 'vue' import { Swipe, SwipeItem, Grid, GridItem, Image as VanImage, CountDown, Icon, List } from 'vant' import { getBannerList, getSeckillList, getProList } from '@/api/home' // @ 代表的就是src的目录 import ProList from '@/components/ProList' Vue.use(Swipe) Vue.use(SwipeItem) Vue.use(Grid) Vue.use(GridItem) Vue.use(VanImage) Vue.use(CountDown) Vue.use(Icon) Vue.use(List) export default { components: { ProList }, data () { return { navList: [ { navid: 1, title: '嗨购超市', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/125678/35/5947/4868/5efbf28cEbf04a25a/e2bcc411170524f0.png' }, { navid: 2, title: '数码电器', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/178015/31/13828/6862/60ec0c04Ee2fd63ac/ccf74d805a059a44.png!q70.jpg' }, { navid: 3, title: '嗨购服饰', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/41867/2/15966/7116/60ec0e0dE9f50d596/758babcb4f911bf4.png!q70.jpg' }, { navid: 4, title: '嗨购生鲜', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/177902/16/13776/5658/60ec0e71E801087f2/a0d5a68bf1461e6d.png!q70.jpg.dpg' }, { navid: 5, title: '嗨购到家', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/196472/7/12807/7127/60ec0ea3Efe11835b/37c65625d94cae75.png!q70.jpg.dpg' }, { navid: 6, title: '充值缴费', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/185733/21/13527/6648/60ec0f31E0fea3e0a/d86d463521140bb6.png!q70.jpg.dpg' }, { navid: 7, title: '9.9元拼', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/36069/14/16068/6465/60ec0f67E155f9488/595ff3e606a53f02.png!q70.jpg.dpg' }, { navid: 8, title: '领券', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/186080/16/13681/8175/60ec0fcdE032af6cf/c5acd2f8454c40e1.png!q70.jpg.dpg' }, { navid: 9, title: '领金贴', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/196711/35/12751/6996/60ec1000E21b5bab4/38077313cb9eac4b.png!q70.jpg.dpg' }, { navid: 10, title: 'plus会员', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/37709/6/15279/6118/60ec1046E4b5592c6/a7d6b66354efb141.png!q70.jpg.dpg' } ], bannerList: [], seckillList: [], proList: [], time: 3 * 60 * 60 * 1000, loading: false, // 检测是不是在上拉加载状态 finished: false, // 表示还有没有数据,如果为真,表示所有的数据都已经加载完毕 count: 2 // mounted中默认加载了第一页的数据,所有从2开始 } }, methods: { onLoad () { // 页面上拉到底一定距离时触发 this.loading = true // 请求开始 getProList({ count: this.count }).then(res => { console.log('2', res.data) this.loading = false // 请求结束 // 判断数据有没有 if (res.data.data.length === 0) { // 没有数据 this.finished = true } else { // 累加数据 -- 合并数组 concat this.proList = [...this.proList, ...res.data.data] // 有数据要把页码加1 this.count++ } }) } }, mounted () { getBannerList().then(res => { // console.log(res.data) this.bannerList = res.data.data }) getSeckillList().then(res => { // console.log(res.data) this.seckillList = res.data.data }) getProList().then(res => { console.log('1', res.data) this.proList = res.data.data }) } } </script> <style lang='stylus' scoped> // scoped 代表该样式只在当前组件是有效的,不会影响其他组件的样式 .myswiper height 150px width 96% margin 10px 2% .my-swipe border-radius 10px height 100% .seckillList display flex li flex 1 height 0.7rem display flex flex-direction column justify-content center align-items center .van-image img width 0.55rem height 0.55rem p color #f66 .colon { display: inline-block; margin: 0 4px; color: #ee0a24; } .block { display: inline-block; width: 22px; color: #fff; font-size: 12px; text-align: center; background-color: #f66; } .seckillTitle display flex height 0.4rem li text-align left &:nth-child(1) width 60px margin-left 10px font-size 14px font-weight bold &:nth-child(3) width 120px text-align right font-size 14px margin-right 10px color #f66 .van-icon transform rotate(225deg) &:nth-child(2) flex: 1 </style>
实际上初始 的时候 自动执行了一个 onLoad 事件
参数 说明 类型 默认值 immediate-check 是否在初始化时立即执行滚动位置检查 boolean true
<van-list v-model="loading" :finished="finished" finished-text="没有更多了" :immediate-check="false" @load="onLoad" > <ProList :proList="proList"/> </van-list>
8.2 下拉刷新
相当于用户点击了一次浏览器的刷新按键(所有的状态都需要恢复到初始的值,数据重新获取)
Vant - Mobile UI Components built on Vue
下拉刷新时会触发
refresh
事件,在事件的回调函数中可以进行同步或异步操作,操作完成后将v-model
设置为false
,表示加载完成。
重置状态
实际项目中,下拉刷新请求的就是第一页的数据,但是接口返回的数据和上一次的下拉刷新的数据不一样
本项目中,是一样 的
下拉刷新以后需要重置 finished 和count 的值
<template> <div class="box"> <header class="header">home header</header> <div class="content" ref="content"> <van-pull-refresh v-model="isLoading" @refresh="onRefresh"> <div class="myswiper"> <van-swipe class="my-swipe" :autoplay="3000" indicator-color="white"> <van-swipe-item v-for="item of bannerList" :key="item.bannerid"> <van-image :src="item.img" /> </van-swipe-item> </van-swipe> </div> <van-grid icon-size="40px" :column-num="5" :border="false"> <van-grid-item v-for="item of navList" :key="item.navid" :icon="item.imgurl" :text="item.title" /> </van-grid> <ul class="seckillTitle"> <li>嗨购秒杀</li> <li> <van-count-down :time="time"> <template #default="timeData"> <span class="block">{{ timeData.hours }}</span> <span class="colon">:</span> <span class="block">{{ timeData.minutes }}</span> <span class="colon">:</span> <span class="block">{{ timeData.seconds }}</span> </template> </van-count-down> </li> <li> 更多秒杀 <van-icon name="clock" color="#f66" size="16"/> </li> </ul> <ul class="seckillList"> <li v-for="item of seckillList" :key="item.proid"> <van-image width="55px" height="55px" :src="item.img1" /> <p> ¥{{ item.originprice }} </p> </li> </ul> <!-- 产品列表 --> <van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad" :immediate-check = "false" > <ProList :proList = "proList"/> </van-list> </van-pull-refresh> </div> </div> </template> <script> import Vue from 'vue' import { Swipe, SwipeItem, Grid, GridItem, Image as VanImage, CountDown, Icon, List, PullRefresh } from 'vant' import { getBannerList, getSeckillList, getProList } from '@/api/home' // @ 代表的就是src的目录 import ProList from '@/components/ProList' Vue.use(Swipe) Vue.use(SwipeItem) Vue.use(Grid) Vue.use(GridItem) Vue.use(VanImage) Vue.use(CountDown) Vue.use(Icon) Vue.use(List) Vue.use(PullRefresh) export default { components: { ProList }, data () { return { navList: [ { navid: 1, title: '嗨购超市', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/125678/35/5947/4868/5efbf28cEbf04a25a/e2bcc411170524f0.png' }, { navid: 2, title: '数码电器', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/178015/31/13828/6862/60ec0c04Ee2fd63ac/ccf74d805a059a44.png!q70.jpg' }, { navid: 3, title: '嗨购服饰', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/41867/2/15966/7116/60ec0e0dE9f50d596/758babcb4f911bf4.png!q70.jpg' }, { navid: 4, title: '嗨购生鲜', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/177902/16/13776/5658/60ec0e71E801087f2/a0d5a68bf1461e6d.png!q70.jpg.dpg' }, { navid: 5, title: '嗨购到家', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/196472/7/12807/7127/60ec0ea3Efe11835b/37c65625d94cae75.png!q70.jpg.dpg' }, { navid: 6, title: '充值缴费', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/185733/21/13527/6648/60ec0f31E0fea3e0a/d86d463521140bb6.png!q70.jpg.dpg' }, { navid: 7, title: '9.9元拼', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/36069/14/16068/6465/60ec0f67E155f9488/595ff3e606a53f02.png!q70.jpg.dpg' }, { navid: 8, title: '领券', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/186080/16/13681/8175/60ec0fcdE032af6cf/c5acd2f8454c40e1.png!q70.jpg.dpg' }, { navid: 9, title: '领金贴', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/196711/35/12751/6996/60ec1000E21b5bab4/38077313cb9eac4b.png!q70.jpg.dpg' }, { navid: 10, title: 'plus会员', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/37709/6/15279/6118/60ec1046E4b5592c6/a7d6b66354efb141.png!q70.jpg.dpg' } ], bannerList: [], seckillList: [], proList: [], time: 3 * 60 * 60 * 1000, loading: false, // 检测是不是在上拉加载状态 finished: false, // 表示还有没有数据,如果为真,表示所有的数据都已经加载完毕 count: 2, // mounted中默认加载了第一页的数据,所有从2开始 isLoading: false } }, methods: { onRefresh () { this.isLoading = true getProList().then(res => { this.isLoading = false this.proList = res.data.data // 重置 页码以及 是否还有数据 --- 最容易忽略 this.count = 2 this.finished = false }) }, onLoad () { // 页面上拉到底一定距离时触发 this.loading = true // 请求开始 getProList({ count: this.count }).then(res => { console.log('2', res.data) this.loading = false // 请求结束 // 判断数据有没有 if (res.data.data.length === 0) { // 没有数据 this.finished = true } else { // 累加数据 -- 合并数组 concat this.proList = [...this.proList, ...res.data.data] // 有数据要把页码加1 this.count++ } }) } }, mounted () { getBannerList().then(res => { // console.log(res.data) this.bannerList = res.data.data }) getSeckillList().then(res => { // console.log(res.data) this.seckillList = res.data.data }) getProList().then(res => { console.log('1', res.data) this.proList = res.data.data }) } } </script> <style lang='stylus' scoped> // scoped 代表该样式只在当前组件是有效的,不会影响其他组件的样式 .myswiper height 150px width 96% margin 10px 2% .my-swipe border-radius 10px height 100% .seckillList display flex li flex 1 height 0.7rem display flex flex-direction column justify-content center align-items center .van-image img width 0.55rem height 0.55rem p color #f66 .colon { display: inline-block; margin: 0 4px; color: #ee0a24; } .block { display: inline-block; width: 22px; color: #fff; font-size: 12px; text-align: center; background-color: #f66; } .seckillTitle display flex height 0.4rem li text-align left &:nth-child(1) width 60px margin-left 10px font-size 14px font-weight bold &:nth-child(3) width 120px text-align right font-size 14px margin-right 10px color #f66 .van-icon transform rotate(225deg) &:nth-child(2) flex: 1
8.3 返回顶部
body.scrollTop = 0
是谁产生了滚动条
<template> <div class="box"> <header class="header">home header</header> <div class="content" ref="content"> <van-pull-refresh v-model="isLoading" @refresh="onRefresh"> <div class="myswiper"> <van-swipe class="my-swipe" :autoplay="3000" indicator-color="white"> <van-swipe-item v-for="item of bannerList" :key="item.bannerid"> <van-image :src="item.img" /> </van-swipe-item> </van-swipe> </div> <van-grid icon-size="40px" :column-num="5" :border="false"> <van-grid-item v-for="item of navList" :key="item.navid" :icon="item.imgurl" :text="item.title" /> </van-grid> <ul class="seckillTitle"> <li>嗨购秒杀</li> <li> <van-count-down :time="time"> <template #default="timeData"> <span class="block">{{ timeData.hours }}</span> <span class="colon">:</span> <span class="block">{{ timeData.minutes }}</span> <span class="colon">:</span> <span class="block">{{ timeData.seconds }}</span> </template> </van-count-down> </li> <li> 更多秒杀 <van-icon name="clock" color="#f66" size="16"/> </li> </ul> <ul class="seckillList"> <li v-for="item of seckillList" :key="item.proid"> <van-image width="55px" height="55px" :src="item.img1" /> <p> ¥{{ item.originprice }} </p> </li> </ul> <!-- 产品列表 --> <van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad" :immediate-check = "false" > <ProList :proList = "proList"/> </van-list> </van-pull-refresh> <div class="backTop" v-if="scrollTop > 300" @click="backTop"> <van-icon name="arrow-up" /> </div> </div> </div> </template> <script> import Vue from 'vue' import { Swipe, SwipeItem, Grid, GridItem, Image as VanImage, CountDown, Icon, List, PullRefresh } from 'vant' import { getBannerList, getSeckillList, getProList } from '@/api/home' // @ 代表的就是src的目录 import ProList from '@/components/ProList' Vue.use(Swipe) Vue.use(SwipeItem) Vue.use(Grid) Vue.use(GridItem) Vue.use(VanImage) Vue.use(CountDown) Vue.use(Icon) Vue.use(List) Vue.use(PullRefresh) export default { components: { ProList }, data () { return { navList: [ { navid: 1, title: '嗨购超市', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/125678/35/5947/4868/5efbf28cEbf04a25a/e2bcc411170524f0.png' }, { navid: 2, title: '数码电器', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/178015/31/13828/6862/60ec0c04Ee2fd63ac/ccf74d805a059a44.png!q70.jpg' }, { navid: 3, title: '嗨购服饰', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/41867/2/15966/7116/60ec0e0dE9f50d596/758babcb4f911bf4.png!q70.jpg' }, { navid: 4, title: '嗨购生鲜', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/177902/16/13776/5658/60ec0e71E801087f2/a0d5a68bf1461e6d.png!q70.jpg.dpg' }, { navid: 5, title: '嗨购到家', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/196472/7/12807/7127/60ec0ea3Efe11835b/37c65625d94cae75.png!q70.jpg.dpg' }, { navid: 6, title: '充值缴费', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/185733/21/13527/6648/60ec0f31E0fea3e0a/d86d463521140bb6.png!q70.jpg.dpg' }, { navid: 7, title: '9.9元拼', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/36069/14/16068/6465/60ec0f67E155f9488/595ff3e606a53f02.png!q70.jpg.dpg' }, { navid: 8, title: '领券', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/186080/16/13681/8175/60ec0fcdE032af6cf/c5acd2f8454c40e1.png!q70.jpg.dpg' }, { navid: 9, title: '领金贴', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/196711/35/12751/6996/60ec1000E21b5bab4/38077313cb9eac4b.png!q70.jpg.dpg' }, { navid: 10, title: 'plus会员', imgurl: 'https://m.360buyimg.com/mobilecms/s120x120_jfs/t1/37709/6/15279/6118/60ec1046E4b5592c6/a7d6b66354efb141.png!q70.jpg.dpg' } ], bannerList: [], seckillList: [], proList: [], time: 3 * 60 * 60 * 1000, loading: false, // 检测是不是在上拉加载状态 finished: false, // 表示还有没有数据,如果为真,表示所有的数据都已经加载完毕 count: 2, // mounted中默认加载了第一页的数据,所有从2开始 isLoading: false, scrollTop: 0 } }, methods: { backTop () { this.$refs.content.scrollTop = 0 }, onRefresh () { this.isLoading = true getProList().then(res => { this.isLoading = false this.proList = res.data.data // 重置 页码以及 是否还有数据 --- 最容易忽略 this.count = 2 this.finished = false }) }, onLoad () { // 页面上拉到底一定距离时触发 this.loading = true // 请求开始 getProList({ count: this.count }).then(res => { console.log('2', res.data) this.loading = false // 请求结束 // 判断数据有没有 if (res.data.data.length === 0) { // 没有数据 this.finished = true } else { // 累加数据 -- 合并数组 concat this.proList = [...this.proList, ...res.data.data] // 有数据要把页码加1 this.count++ } }) } }, mounted () { getBannerList().then(res => { // console.log(res.data) this.bannerList = res.data.data }) getSeckillList().then(res => { // console.log(res.data) this.seckillList = res.data.data }) getProList().then(res => { console.log('1', res.data) this.proList = res.data.data }) this.$refs.content.addEventListener('scroll', () => { this.scrollTop = this.$refs.content.scrollTop }, false) } } </script> <style lang='stylus' scoped> // scoped 代表该样式只在当前组件是有效的,不会影响其他组件的样式 .myswiper height 150px width 96% margin 10px 2% .my-swipe border-radius 10px height 100% .seckillList display flex li flex 1 height 0.7rem display flex flex-direction column justify-content center align-items center .van-image img width 0.55rem height 0.55rem p color #f66 .colon { display: inline-block; margin: 0 4px; color: #ee0a24; } .block { display: inline-block; width: 22px; color: #fff; font-size: 12px; text-align: center; background-color: #f66; } .seckillTitle display flex height 0.4rem li text-align left &:nth-child(1) width 60px margin-left 10px font-size 14px font-weight bold &:nth-child(3) width 120px text-align right font-size 14px margin-right 10px color #f66 .van-icon transform rotate(225deg) &:nth-child(2) flex: 1 .backTop position fixed width 32px height 32px border-radius 50% border 1px solid #cccccc display flex justify-content center align-items center bottom 100px right 10px background-color #fff </style>