当前位置:   article > 正文

vue2+vant2实现移动端下拉刷新、上拉加载及返回顶部功能_vue上拉刷新

vue上拉刷新

一、前提准备

1、引入vant2组件库
npm i vant@latest-v2 -S
  • 1
2、引入List 列表组件
import { List } from 'vant';
Vue.use(List);
  • 1
  • 2
3、引入PullRefresh 下拉刷新组件
import { PullRefresh } from 'vant';
Vue.use(PullRefresh);
  • 1
  • 2

二、代码详解

1、整体思路
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
  <van-list
    v-model="loading"
    :finished="finished"
    :offset="offset"
    finished-text="没有更多了"
    @load="onLoad"
  >
    列表信息
  </van-list>
</van-pull-refresh>

export default {
  data() {
    return {
      tableList : [], //列表数据,将接口获取的数据,存到list
      offset: "30", //滚动条与底部距离小于 offset 时触发load事件(重中之重)
      loading: false, //用于异步操作并更新数据,数据更新完毕后,将 loading 设置成 false,当loading 为 true,表示处于加载状态;
      finished: false, //若数据已全部加载完毕,则直接将 finished 设置成 true
      refreshing: false, //当refreshing为true时,表示正在下拉刷新状态,操作完成后将 refreshing 为 false,表示加载完成
      pageStart:1, //当前页
      limit: 10, //每页数量
    };
  },
  methods: {
    onLoad() {
      //后续详解
    },
    onRefresh() {
     //后续详解
    },
  },
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
2、获取列表数据
<template>
	<div>
		<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
  			<van-list
    		v-model="loading"
    		:finished="finished"
    		:offset="offset"
    		finished-text="没有更多了"
    		@load="onLoad">
    			列表信息
  			</van-list>
		</van-pull-refresh>
	</div>
</template>

export default {
  data() {
    return {
      tableList : [], 
      offset: "30", 
      loading: false, 
      finished: false, 
      refreshing: false, 
      pageStart:1, //当前页
      limit: 10, //每页数量
    };
  },
  methods: {
    getFaultIndicator(){
        let params = JSON.stringify({
          searchValue: this.input,
          pageStart: this.pageStart,
          limit: this.limit
        })
        api.post(
            params,
            responseData => {
              if (responseData.type === 'success') {
                //获取第一页的十条
                let data = responseData.data;
                //创建一个新数组,将获取到的数据和原来的数据保存其中
                this.tableList = this.tableList.concat(data)
                //加载完成
                this.loading = false
                //如果data长度为0,则加载完成(不是唯一方法,也可通过count总数判断)
                if(data.length === 0){
                  this.finished = true
                }else{
                  this.finished = false
                }
              } else {
                this.$f7.toast
                    .create({
                      text: responseData.message,
                      position: 'center',
                      closeTimeout: 1500
                    }).open();
              }
            },
            error => {
              this.$f7.toast
                  .create({
                    text: error.message,
                    position: 'center',
                    closeTimeout: 1500
                  }).open();
                 //当接口报错时,不处于一直加载状态
              	this.finished = true
            })
      },
  },
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
3、下拉刷新、下拉加载机制
<template>
	<div>
		<van-pull-refresh v-model="refreshing" class="fixedStyle" @refresh="onRefresh">
  			<van-list
    		v-model="loading"
    		:finished="finished"
    		:offset="offset"
    		finished-text="没有更多了"
    		@load="onLoad">
    			列表信息
  			</van-list>
		</van-pull-refresh>
	</div>
</template>

export default {
  data() {
    return {
      tableList : [], 
      offset: "30", 
      loading: false, 
      finished: false, 
      refreshing: false, 
      pageStart:1, //当前页
      limit: 10, //每页数量
    };
  },
  methods: {
    getFaultIndicator(){
        let params = JSON.stringify({
          searchValue: this.input,
          pageStart: this.pageStart,
          limit: this.limit
        })
        api.post(
            params,
            responseData => {
              if (responseData.type === 'success') {
                //获取第一页的十条
                let data = responseData.data;
                //创建一个新数组,将获取到的数据和原来的数据保存其中
                this.tableList = this.tableList.concat(data)
                //加载完成
                this.loading = false
                //如果data长度为0,则加载完成
                if(data.length === 0){
                  this.finished = true
                }else{
                  this.finished = false
                }
              } else {
                this.$f7.toast
                    .create({
                      text: responseData.message,
                      position: 'center',
                      closeTimeout: 1500
                    }).open();
              }
            },
            error => {
              this.$f7.toast
                  .create({
                    text: error.message,
                    position: 'center',
                    closeTimeout: 1500
                  }).open();
                 //当接口报错时,不处于一直加载状态
              	this.finished = true
            })
      },
    onLoad() {
      	if(this.isLoading){
      		//当下拉刷新时isLoading为true;将列表置空,重新从第一页查询,加载完成
          	this.tableList = []
          	this.pageStart = 1
          	this.isLoading = false
        }else{
        	//当触发上拉时isLoading为false,当前页加一
          	this.pageStart = this.pageStart + 1
       	 }
        //调用列表接口把下一页获取到的date通过concat更新到一个新数组中并赋值给tableList 
        this.getFaultIndicator()
      },
    },
    onRefresh() {
    	//数据开始加载
     	this.finished = false
     	//异步操作并更新数据
     	this.loading = true
     	//调用onLoad()方法
        this.onLoad()
    },
  },
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
4、滚动返回顶部功能
<template>
	//再合适位置放置
	<div class="disPlayButton" :style="{display: scrollTop1 < 400 ? 'none' : ''}">
    	<van-button round type="info" @click="disPlayBut()">返回顶部</van-button>
	</div>
</template>
export default {
    data () {
      return {
        scrollTop1: 0,初始滚动条为0
      }
    },
    mounted () {
        window.addEventListener("scroll", this.scrollTops, true);
    },
    //全局事件一定要解绑,不然会出现bug
    beforeDestroy() {
      window.removeEventListener("scroll", this.scrollTops);
    },
    methods: {
		scrollTops(){
        	this.scrollTop1 = document.querySelector('.fixedStyle').scrollTop
      	},
      	//点击返回顶部时,每隔20ms高度减五十,直到到达顶部
      	disPlayBut(){
        if(document.querySelector('.fixedStyle').scrollTop !== 0){
          if(document.querySelector('.fixedStyle').scrollTop < 50){
            document.querySelector('.fixedStyle').scrollTop = 0
          }else{
            document.querySelector('.fixedStyle').scrollTop -= 50
          }
          setTimeout(() =>{
            this.disPlayBut()
          },20)
        }
      },
	},
}

<style lang="less" scoped>
.disPlayButton{
   //对一些样式需要/deep/强制增加权重
  /deep/ .van-button {
    border-radius: 50%;
    background-color: #0b8b7d!important;
    height: 60px;
    border: 1px solid #0b8b7d!important;
    position: absolute;
    bottom: 20px;
    right: 20px;
    z-index: 100;
    width: 60px;
  }
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
4、长按直接返回顶部功能
<template>
	//在合适位置放置,当scrollTop1大于等于400时,出现按钮
	<div class="disPlayButton" :style="{display: scrollTop1 < 400 ? 'none' : ''}">
    	<van-button round type="info" v-on:touchstart="start">返回顶部</van-button>
	</div>
</template>
export default {
    data () {
      return {
      	timer: null,
        scrollTop1: 0,初始滚动条为0
      }
    },
    mounted () {
        window.addEventListener("scroll", this.scrollTops, true);
    },
    //全局事件一定要解绑,不然会出现bug
    beforeDestroy() {
      window.removeEventListener("scroll", this.scrollTops);
    },
    methods: {
		scrollTops(){
        	this.scrollTop1 = document.querySelector('.fixedStyle').scrollTop
      	},
      	start() {
        this.timer = setTimeout(() => {
          this.timer = null; // 清除计时器
          this.longPress(); // 触发长按事件
        }, 1000); // 设置长按时间为1秒
      },
      end() {
        if (this.timer) {
          clearTimeout(this.timer); // 清除计时器
        }
      },
      longPress() {
        //赋值为0,直接到顶部
        document.querySelector('.fixedStyle').scrollTop = 0
      },
      },
	},
}

<style lang="less" scoped>
.disPlayButton{
   //对一些样式需要/deep/强制增加权重
  /deep/ .van-button {
    border-radius: 50%;
    background-color: #0b8b7d!important;
    height: 60px;
    border: 1px solid #0b8b7d!important;
    position: absolute;
    bottom: 20px;
    right: 20px;
    z-index: 100;
    width: 60px;
  }
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
5、返回顶部功能合并
<template>
	//再合适位置放置
	<div class="disPlayButton" :style="{display: scrollTop1 < 400 ? 'none' : ''}">
    	<van-button round type="info" @click="disPlayBut()" v-on:touchstart="start">返回顶部</van-button>
	</div>
</template>
export default {
    data () {
      return {
      	timer: null,
        scrollTop1: 0,初始滚动条为0
      }
    },
    mounted () {
        window.addEventListener("scroll", this.scrollTops, true);
    },
    //全局事件一定要解绑,不然会出现bug
    beforeDestroy() {
      window.removeEventListener("scroll", this.scrollTops);
    },
    methods: {
		scrollTops(){
        	this.scrollTop1 = document.querySelector('.fixedStyle').scrollTop
      	},
      	start() {
        this.timer = setTimeout(() => {
          this.timer = null; // 清除计时器
          this.longPress(); // 触发长按事件
        	}, 1000); // 设置长按时间为1秒
      	},
      	end() {
        	if (this.timer) {
          		clearTimeout(this.timer); // 清除计时器
        	}
      	},
      	longPress() {
        	//赋值为0,直接到顶部
        	document.querySelector('.fixedStyle').scrollTop = 0
      	},
      	//点击返回顶部时,每隔20ms高度减五十,直到到达顶部
      	disPlayBut(){
        if(document.querySelector('.fixedStyle').scrollTop !== 0){
          if(document.querySelector('.fixedStyle').scrollTop < 50){
            document.querySelector('.fixedStyle').scrollTop = 0
          }else{
            document.querySelector('.fixedStyle').scrollTop -= 50
          }
          setTimeout(() =>{
            this.disPlayBut()
          },20)
        }
      },
	},
}

<style lang="less" scoped>
.disPlayButton{
   //对一些样式需要/deep/强制增加权重
  /deep/ .van-button {
    border-radius: 50%;
    background-color: #0b8b7d!important;
    height: 60px;
    border: 1px solid #0b8b7d!important;
    position: absolute;
    bottom: 20px;
    right: 20px;
    z-index: 100;
    width: 60px;
  }
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

三、完整代码

<template>
  <div class="gzjk">
    <van-search
        v-model="input"
        show-action
        label=""
        placeholder="请输入搜索信息"
        @search="getFaultIndicator">
      <template #action>
        <div @click="getFaultIndicator('search')">搜索</div>
      </template>
    </van-search>
    <van-pull-refresh v-model="isLoading" class="fixedStyle" @refresh="onRefresh">
      <van-list
          v-model="loading"
          :finished="finished"
          :offset="offset"
          @load="onLoad">
        <div v-for="(item,i) in tableList" :key="i">
          <div class="box_list" @click="quickList(item)">
            <div class="list_t">
              <div class="list_l">
                设备ID:<span style="font-size: 14px">{{item.equipmentId}}</span>
              </div>
              <div class="box_nav">所属单位:<span>{{item.cityName}}</span></div>
              <div class="box_nav">终端厂家:<span>{{item.factoryName}}</span></div>
              <div class="box_nav">SIM卡号:<span>{{item.sim}}</span></div>
            </div>
          </div>
        </div>
      </van-list>
      <van-empty description="暂无数据" v-if="tableList.length === 0"/>
    </van-pull-refresh>
  </div>
  <div class="disPlayButton" :style="{display: scrollTop1 < 400 ? 'none' : ''}">
    <van-button round type="info" @click="disPlayBut()" v-on:touchstart="start">返回顶部</van-button>
  </div>
</template>
<script>
import api from '@/utils/request-util'
export default {
  data () {
    return {
      timer: null,
      scrollTop1: 0,
      isLoading: false,
      offset: 50,
      pageStart:1,
      limit: 10,
      loading: false,
      finished: false,
      tableList:[],
      input: '',
    }
  },
  mounted () {
    this.getFaultIndicator()
    window.addEventListener("scroll", this.scrollTops, true);
  },
  beforeDestroy() {
    window.removeEventListener("scroll", this.scrollTops);
  },
  methods: {
    quickList(){
      console.log("跳转到详情界面")
    },
    start() {
      this.timer = setTimeout(() => {
        this.timer = null; // 清除计时器
        this.longPress(); // 触发长按事件
      }, 1000); // 设置长按时间为1秒
    },
    end() {
      if (this.timer) {
        clearTimeout(this.timer); // 清除计时器
      }
    },
    longPress() {
      document.querySelector('.fixedStyle').scrollTop = 0
    },
    scrollTops(){
      this.scrollTop1 = document.querySelector('.fixedStyle').scrollTop
    },
    disPlayBut(){
      if(document.querySelector('.fixedStyle').scrollTop !== 0){
        if(document.querySelector('.fixedStyle').scrollTop < 50){
          document.querySelector('.fixedStyle').scrollTop = 0
        }else{
          document.querySelector('.fixedStyle').scrollTop -= 50
        }
        setTimeout(() =>{
          this.disPlayBut()
        },20)
      }
    },
    getFaultIndicator(res){
      if(res == "search"){
        this.tableList = []
        this.pageStart = 1
        this.isLoading = false
      }
      let params = JSON.stringify(
          {
            searchValue: this.input,
            pageStart: this.pageStart.toString(),
            limit: this.limit.toString()
          })
      api.post(
          params,
          responseData => {
            if (responseData.type === 'success') {
              let data = responseData.data;
              this.tableList = this.tableList.concat(data)
              this.loading = false
              if(data.length === 0){
                this.finished = true
              }else{
                this.finished = false
              }
            } else {
              this.$f7.toast
                  .create({
                    text: responseData.message,
                    position: 'center',
                    closeTimeout: 1500
                  })
                  .open();
            }
          },
          error => {
            this.$f7.toast
                .create({
                  text: error.message,
                  position: 'center',
                  closeTimeout: 1500
                })
                .open();
            this.finished = true
          })
    },
    onRefresh(){
      this.finished = false
      this.loading = true
      this.onLoad()
    },
    onLoad() {
      if(this.isLoading){
        this.tableList = []
        this.pageStart = 1
        this.isLoading = false
      }else{
        this.pageStart = this.pageStart + 1
      }
      //初始化列表
      this.getFaultIndicator()
    },
  },
}
</script>
<style lang="less" scoped>
.gzjk {
  width: 100%;
  height: 100vh;
}
.search{
  width:100%;
  height:64px;
}
.van-search{
  background-color:#F8F8F8;
  .van-search__content{
    background-color:#ffffff;
  }
}
.box_list{
  width:95%;
  background:#ffffff;
  border-radius: 16px;
  margin: 8px 8px;
  box-shadow: 0 0 10px rgba(102,104,104,18%);
  .list_t{
    align-items: center;
    padding:7px 8px 7px 10px;
    border-bottom: 1px solid #E0E0E0;
    display:flex;
    justify-content: space-between;
    .list_l{
      color:#666666;
      font-size:14px;
    }
  }
  .box_nav{
    padding:0 8px 14px 12px;
    color:#666666;
    font-size:16px;
    >span:nth-child(1){
      color:#333333;
      display:inline-block;
      margin-left:10px;
    }
  }
}
/deep/ .van-pull-refresh__track{
  min-height: 100vh;
}
.fixedStyle {
  position: fixed;
  top: 100px;
  left: 0;
  right: 0;
  bottom: 0px;
  overflow: auto;
}
.disPlayButton{
  /deep/ .van-button {
    border-radius: 50%;
    background-color: #0b8b7d!important;
    height: 60px;
    border: 1px solid #0b8b7d!important;
    position: absolute;
    bottom: 20px;
    right: 20px;
    z-index: 100;
    width: 60px;
  }
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/730998
推荐阅读
相关标签
  

闽ICP备14008679号