vant van-pull-refresh + van-list实现list列表支持搜索和下拉刷新

1 介绍

在使用 van-pull-refresh + van-list实现list列表下拉刷新时遇到几个问题在这里进行一个总结。

2 出现的问题

问题一:当van-pull-refresh + van-list组合使用时,下拉刷新会调用两个加载图标。

解答:去除van-pull-refresh加载图标,防止下拉刷新时出现两个加载图标

javascript 复制代码
      <!--  去除加载图标,防止下拉刷新时出现两个加载图标 -->
            <template #loading>
              <div></div>
            </template>

问题二: 通过筛选条件查询数据时,van-list会请求多次接口

解答:因为当this.finished = false时, 会触发 onLoad 事件,onLoad方法会调用一次fetchData方法,onRefresh方法也会调用fetchData方法这就使得你会调用两次接口。所以我们只好把查询单独写一个方法onSearch,然后在onLoad中判断是不是刷新操作,如果说是刷新就设置为false,只调用onload方法。

javascript 复制代码
    onSearch() {
      this.medicalList = []
      this.pageIndex = 1
      this.finished = false
      this.loading = true
      this.refreshing = false
      this.fetchData()
    },
    onLoad() {
      if (this.refreshing) {
        this.medicalList = []
        this.refreshing = false
      }
      // 触底加载下一页数据
      // 网页刚打开也会触发一次
      this.fetchData()
    },
        // 下拉刷新
    onRefresh() {
      this.medicalList = []
      this.pageIndex = 1
      // finished 设置为 false 则会触发 onLoad 事件
      this.finished = false
      this.loading = true
      this.fetchData()
    },

3 结果展示

这个页面叫做TreatmentStation分为两部分:顶部的搜索和中间的内容。中间的内容是一个单独的组件(MedicalList),是TreatmentStation的子组件,父组件通过传值的方式将查询条件传给子组件,子组件调用接口获取数据,渲染至页面。

4 代码实现

TreatmentStation 组件

javascript 复制代码
<template>
  <div id="treatment">
    <!-- 筛选条件 -->
    <div class="filter-criteria">
      <van-search
          v-model="model.keyword"
          placeholder="请输入患者姓名/就诊号"
          show-action
          @search="onSearch"
      >
        <template #action>
          <div style="display: flex; align-items: center;justify-content: center">
            <van-button size="small" type="info" @click="onSearch">搜索</van-button>
          </div>
        </template>
      </van-search>
      <van-dropdown-menu active-color="#1989fa">
        <van-dropdown-item ref="dateItem" v-model="model.appointTime" :title="model.appointTime.toString()">
          <van-datetime-picker
              v-model="currentDate"
              title="选择日期"
              type="date"
              @cancel="onCancelDate"
              @confirm="onConfirmDate"
          />
        </van-dropdown-item>
        <van-dropdown-item v-model="model.departmentName" :options="model.deptOption" @change="changeDropdown"/>
        <van-dropdown-item v-model="model.my" :options="model.myOption" @change="changeDropdown"/>
      </van-dropdown-menu>
    </div>
    <back-top/>
    <!-- list-->
    <medical-list ref="medicalList" :model="model"/>
  </div>
</template>

<script>
import moment from 'moment'
import MedicalList from './components/medical-list.vue'
import api from '../../api'
import BackTop from '../../components/back-top.vue'

export default {
  name: 'TreatmentStation',
  components: {BackTop, MedicalList},
  data() {
    return {
      model: {
        keyword: '',
        my: true,
        myOption: [
          {text: '我的', value: true},
          {text: '全部医师', value: false}
        ],
        departmentName: '',
        deptOption: [{text: '全部科室', value: ''}],
        appointTime: ''
      },
      medicalList: [],
      pageIndex: 1,
      currentDate: new Date()
    }
  },
  mounted() {
    //
    this.model.appointTime = moment().format('YYYY-MM-DD')
    const sessionModel = JSON.parse(sessionStorage.getItem('sessionModel'))
    if (sessionModel !== null) {
      this.model = sessionModel
    }
    this.getDepartList()
  },
  methods: {
    // 搜索确定
    onSearch(val) {
      this.$refs.medicalList.onSearch()
    },
    // 搜索框取消按钮
    onCancel() {
      // Toast('取消')
      this.$refs.medicalList.onSearch()
    },
    onConfirmDate(value) {
      this.model.appointTime = moment(value).format('YYYY-MM-DD')
      this.$refs.dateItem.toggle()
      this.$refs.medicalList.onSearch()
    },
    onCancelDate() {
      // 收起
      this.$refs.dateItem.toggle()
    },
    changeDropdown(value) {
      this.$refs.medicalList.onSearch()
    },
    // 获取所有科室
    getDepartList() {
      api.getDepartment().then(res => {
        this.model.deptOption = res.data.map(item => ({
          text: item.name,
          value: item.name
        }))
        this.model.deptOption.unshift({text: '全部科室', value: ''})
      }).catch(error => {
        console.log(error)
      })
    }
  },
  beforeRouteLeave(to, from, next) {
    // 当路由跳转到治疗页面时存储sessionModel
    if (to.name === 'treatmentContent') {
      sessionStorage.setItem('sessionModel', JSON.stringify(this.model))
    } else {
      sessionStorage.removeItem('sessionModel')
    }
    next()
  }
}
</script>

<style scoped>
#treatment {
  height: 100%;
}
</style>

MedicalList组件

javascript 复制代码
<template>
  <div id="medical-list">
    <van-pull-refresh
        v-model="refreshing"
        :class="{'content': activeClass}"
        :offset="100"
        success-text="刷新成功"
        @refresh="onRefresh"
    >
      <!--  去除加载图标,防止下拉刷新时出现两个加载图标 -->
            <template #loading>
              <div></div>
            </template>
      <van-list
          v-model="loading"
          :finished="finished"
          finished-text="没有更多了"
          @load="onLoad"
      >
        <van-cell v-for="item in medicalList" :key="item.id"
                  :to="{
                    name:'treatmentContent',
                    params:{
                      medicalInfo_Id: item.id,
                      appointTime: model.appointTime,
                      patientName: item.patientName,
                      patient_Id: item.patient_Id
                    }
                  }"
                  is-link
                  style="text-align: left; align-items: center"
        >
          <template #title>
            <span style="margin-left: 10px">{{ item.patientName }}</span>
          </template>
          <template #label>
            <span style="margin-left: 10px">{{ '就诊号: ' + item.medicalNo }}</span>
          </template>
          <template #default>
            <span>{{ item.bedNumber ? '床号: ' + item.bedNumber : '' }}</span>
          </template>
          <template #icon>
            <van-icon name="contact-o" size="25px"/>
          </template>
        </van-cell>
      </van-list>
    </van-pull-refresh>
  </div>
</template>
<script>
import api from '../../../api'

export default {
  name: 'MedicalList',
  props: ['model'],
  data() {
    return {
      medicalList: [],
      loading: false,
      finished: false,
      refreshing: false,
      pageIndex: 1,
      pageSize: 50,
      pullRefreshDisabled: false
    }
  },
  methods: {
    // list列表方法
    onLoad() {
      if (this.refreshing) {
        this.medicalList = []
        this.refreshing = false
      }
      // 触底加载下一页数据
      // 网页刚打开也会触发一次
      this.fetchData()
    },
    // 下拉刷新
    onRefresh() {
      this.medicalList = []
      this.pageIndex = 1
      // finished 设置为 false 则会触发 onLoad 事件
      this.finished = false
      this.loading = true
      this.fetchData()
    },
    onSearch() {
      this.medicalList = []
      this.pageIndex = 1
      this.finished = false
      this.loading = true
      this.refreshing = false
      this.fetchData()
    },
    // 获取就诊信息数据
    fetchData() {
      api.query({
        'page.index': this.pageIndex,
        'page.size': this.pageSize,
        My: this.model.my,
        keyword: this.model.keyword,
        appointTime: this.model.appointTime,
        departmentName: this.model.departmentName
      }).then(res => {
        if (!res.data) return
        this.medicalList = this.medicalList.concat(res.data.rows)
        // 所有数据加载完成
        if (this.medicalList.length >= res.data.total) {
          this.finished = true
        } else {
          this.pageIndex++
        }
      }).catch(error => {
        console.log(error)
        this.finished = true
      }).finally(() => {
        this.refreshing = false
        this.loading = false
      })
    },
    bedNumber(value) {
      return value ? '床号:' + value : ''
    }
  },
  computed: {
    activeClass() {
      return this.medicalList.length <= 5
    }
  }
}
</script>
<style scoped>
#medical-list {
}

#medical-list .content {
  height: 80vh;
}
</style>

back-top组件

悬浮回到顶部按钮

javascript 复制代码
<template>
  <!--  回到顶部悬浮按钮-->
  <div id="back-top">
    <van-sticky>
      <van-icon v-if="showBackTop" class="back-to-top" name="back-top" size="20px" @click="scrollToTop"/>
    </van-sticky>
  </div>
</template>
<script>
export default {
  name: 'BackTop',
  data() {
    return {
      showBackTop: false

    }
  },
  beforeDestroy() {
    window.removeEventListener('scroll', this.handleScroll)
  },
  mounted() {
    window.addEventListener('scroll', this.handleScroll)
  },
  methods: {
    handleScroll() {
      this.showBackTop = window.pageYOffset >= 300
    },
    scrollToTop() {
      window.scrollTo({top: 0, behavior: 'smooth'})
    }
  }
}
</script>

<style scoped>
#back-top .back-to-top {
  position: fixed;
  bottom: 50px;
  right: 30px;
  padding: 15px 15px;
  background-color: whitesmoke;
  color: black;
  border-radius: 25px;
  cursor: pointer;
  z-index: 1000;
}

</style>
相关推荐
迷雾漫步者19 分钟前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-1 小时前
验证码机制
前端·后端
燃先生._.2 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖3 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235243 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240254 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar4 小时前
纯前端实现更新检测
开发语言·前端·javascript
寻找沙漠的人4 小时前
前端知识补充—CSS
前端·css
GISer_Jing5 小时前
2025前端面试热门题目——计算机网络篇
前端·计算机网络·面试