vue实现使用给ElementUI的下拉选择器添加分页组件

项目需要select显示的选项太多了,添加分页方便查找。新建一个组件dropdownPagination组件,可以实现远程搜索、分页查询的功能。项目使用,记录一下,方便后续引用。

子组件代码

创建组件页面src\components\Select\index.vue

<!-- 下拉框组件 -->
<template>
  <el-select
    v-model="localSelectValue"
    :filterable="filterable"
    :multiple="multiple"
    :size="selectSize"
    :clearable="selectClearable"
    :collapse-tags="selectCollapseTags"
    :placeholder="selectPlaceholder"
    :no-match-text="selectNoMatchText"
    @search="handleSearch"
    @clear="handleClear"
    remote
    :remote-method="handleSearch"
  >
    <el-option
      v-for="item in selectOptions"
      :key="item.value"
      :label="item.label"
      :value="item.value"
    >
    </el-option>
    <div style="margin: 15px 10px">
      <el-pagination
        :background="background"
        :current-page.sync="currentPage"
        :page-size.sync="pageSize"
        :layout="layout"
        :page-sizes="pageSizes"
        :pager-count="pagerCount"
        :total="total"
        v-bind="$attrs"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      />
    </div>
  </el-select>
</template>

<script>
/**
 * 定义组件的props,包括类型、默认值等属性。
 *
 * @param {Boolean} selectCollapseTags - 是否选择折叠标签,默认值为true
 * @param {Boolean}  selectClearable - 是否可清除选择,默认值为true
 * @param {String}  selectSize - 选择框的大小,默认值为"small"
 * @param {Boolean}  filterable - 是否可过滤,默认值为true
 * @param {Boolean}  multiple - 是否允许多选,默认值为false
 * @param {String}  selectPlaceholder - 选择框的占位符文本,默认值为"请选择"
 * @param {Number}  total - 必需,数据总数
 * @param {Number}  page - 当前页码,默认值为1
 * @param {Number}  limit - 每页显示条数,默认值为20
 * @param {Array}  pageSizes - 可选的每页显示条数数组,默认为[10, 20, 30, 50, 80, 100]
 * @param {String}  layout - 分页组件的布局,默认值为"total, sizes, prev, pager, next, jumper"
 * @param {Boolean}  background - 是否显示背景,默认值为true
 * @param {Boolean}  hidden - 是否隐藏组件,默认值为false
 * @param {Array}  selectOptions - 可选的下拉选项数组,默认为空数组[]
 * @param {(String|Number)}  selectValue - 当前选中的值,可以是字符串或数字,默认值为""
 * @param {Number}  pagerCount - 分页器显示的按钮数量,根据屏幕宽度动态调整,默认为document.body.clientWidth < 992 ? 5 : 7
 */
export default {
  name: "dropdownPagination",
  props: {
    selectCollapseTags: {
      type: Boolean,
      default: true,
    },
    selectNoMatchText: {
      type: String,
      default: "该页无匹配数据",
    },
    selectClearable: {
      type: Boolean,
      default: true,
    },
    selectSize: {
      type: String,
      default: "small",
    },
    filterable: {
      type: Boolean,
      default: true,
    },
    multiple: {
      type: Boolean,
      default: false,
    },
    selectPlaceholder: {
      type: String,
      default: "请选择",
    },
    total: {
      required: true,
      type: Number,
    },
    page: {
      type: Number,
      default: 1,
    },
    limit: {
      type: Number,
      default: 20,
    },
    pageSizes: {
      type: Array,
      default() {
        return [10, 20, 30, 50, 80, 100];
      },
    },
    layout: {
      type: String,
      default: "total, sizes, prev, pager, next, jumper",
    },
    background: {
      type: Boolean,
      default: true,
    },
    hidden: {
      type: Boolean,
      default: false,
    },
    selectOptions: {
      type: Array,
      default() {
        return [];
      },
    },
    selectValue: {
      type: [String, Number],
      default: "",
    },
    pagerCount: {
      type: Number,
      default: () => (document.body.clientWidth < 992 ? 5 : 7),
    },
  },
  data() {
    return {
      // 搜索查询
      searchQuery: "",
    };
  },
  computed: {
    /**
     * 获取本地选择值,并触发更新事件
     * @returns {string|number} 本地选择值
     * @description 本地选择值是通过计算属性 localSelectValue 获取的,它是一个 getter/setter。
     * 当获取本地选择值时,它会返回 selectValue 属性。当设置本地选择值时,它通过触发 update:selectValue 事件来更新外部的 selectValue 值。
     * 这样,当本地选择值发生变化时,外部的 selectValue 也会相应更新。这通常用于父子组件之间的数据同步或双向绑定场景。
     * 
     */
    localSelectValue: {
      // 获取本地选择值,并触发更新事件
      get() {
        // 返回 selectValue 的值
        return this.selectValue;
      },
      // 设置本地选择值,并触发更新事件
      set(val) {
        // 触发 update:selectValue 事件,并传递 val 作为参数
        this.$emit("update:selectValue", val);
      },
    },
    /**
     * 获取当前页码
     *
     * @returns {number} 当前页码
     */
    currentPage: {
      get() {
        // 返回当前页面
        return this.page;
      },
      set(val) {
        // 触发 update:page 事件,并传递 val 作为参数
        this.$emit("update:page", val);
      },
    },
    /**
     * 获取每页显示的条目数
     *
     * @returns {number} 每页显示的条目数
     */
    pageSize: {
      get() {
        // 返回限制值
        return this.limit;
      },
      set(val) {
        // 触发 update:limit 事件,并传递 val 作为参数
        this.$emit("update:limit", val);
      },
    },
  },
  methods: {
    /**
     * 处理清空事件
     */
    handleClear() {
      this.searchQuery = "";
      this.currentPage = 1;
      this.$emit("pagination", { page: 1, limit: this.pageSize, query: "" });
    },
    /**
     * 处理分页大小改变事件
     *
     * @param val 每页显示的条目数
     */
    handleSizeChange(val) {
      // 如果当前页与每页显示条数的乘积大于总条数
      if (this.currentPage * val > this.total) {
        // 将当前页重置为第一页
        this.currentPage = 1;
      }
      this.$emit("pagination", {
        page: this.currentPage,
        limit: val,
        query: this.searchQuery,
      });
    },
    /**
     * 处理当前页码变化的方法
     *
     * @param {number} val - 当前页码
     */
    handleCurrentChange(val) {
      this.$emit("pagination", {
        page: val,
        limit: this.pageSize,
        query: this.searchQuery,
      });
    },
    /**
     * 处理搜索事件的方法
     *
     * @param query 查询字符串
     */
    handleSearch(query) {
      // 输出查询关键字
      console.log(query);
      // 将查询关键字赋值给searchQuery属性
      this.searchQuery = query;
      // 重置当前页码为1
      this.currentPage = 1;
      // 触发pagination事件,传递页码、每页显示条数和查询关键字
      // 触发父组件的pagination事件,传递当前页码、每页显示条数和查询关键字
      this.$emit("pagination", { page: 1, limit: this.pageSize, query });
    },
  },
};
</script>

全局引用:
在src\main.js文件中引用

//下拉选择器+分页组件
import dropdownPagination from "@/components/Select";

// 全局组件挂载
Vue.component('dropdownPagination', dropdownPagination)

父组件使用

 在需要使用的父组件页面调用子组件
 

 <template>
      <div class="container">
        <el-button type="primary"
             @click="handleShare">打开弹窗</el-button>
        <el-dialog
          title="当前设备分配用户"
          :visible.sync="centerDialogVisible"
          width="30%"
          center>
              <dropdown-pagination
                v-show="userTotal > 0"
                :total="userTotal"
                :page.sync="userQueryParams.pageNum"
                :limit.sync="userQueryParams.pageSize"
                :selectValue.sync="selectValue"
                :selectOptions="selectOptionsUser"
                :selectPlaceholder="'请选择用户'"
                :pageSizes="[5, 10, 20, 30, 50]"
                @pagination="fetchUserList"
                  />

              <span slot="footer" class="dialog-footer">
                    <el-button @click="centerDialogVisible = false">取 消</el-button>
                    <el-button type="primary" @click="handleShareSub">确 定</el-button>
              </span>
        </el-dialog>
      </div>
</template>

<script>
import { listUser } from "@/api/system/user";
export default {
    data() {
        return {
          centerDialogVisible: false, //居中弹框
          selectValue: "", //选择的用户名
          userTotal: 0, //用户总数
          userQueryParams: {
          pageNum: 1,
          pageSize: 10,
         },
         selectOptionsUser: [], //用户列表
    },

    methods: {
        handleShare(row) {
          this.centerDialogVisible = true; //居中弹框打开
          this.fetchUserList(); //获取用户列表数据
        },

        /**
         * 获取用户列表
         *
         * 从后端获取用户列表数据,并更新组件的状态
         */
        fetchUserList({ page, limit, query } = {}) {
          console.log(page, limit, query);//输出子组件传回的参数信息
          const params = {
            pageNum: page || this.userQueryParams.pageNum || 1,// 分页数,默认为1
            pageSize: limit || this.userQueryParams.pageSize || 10,//每页数量,默认为10
            userName: query || undefined,// 用户名查询条件,默认为undefined
          };

          //调用获取用户列表的API
          listUser(params).then((res) => {
            // 设置用户总数
            this.userTotal = res.total;

            // 设置用户选择框的选项
            this.selectOptionsUser = res.rows.map((item) => ({
            label: item.nickName,// 显示名称
            value: item.nickName,// 值
            }));
          });
        },
};
</script>

效果图:


 

<think>好的,用户想了解如何在移动应用或网页中实现下拉分页功能。首先,我需要回忆一下相关的知识,并结合提供的引用内容来组织回答。 根据引用[1],滚动分页加载(无限滚动)是在用户滚动到底部时自动加载下一页,提升用户体验。而引用[2]提到了Uni-App中使用onReachBottom和scrollView组件来实现上拉加载和下拉刷新。用户的问题主要集中在下拉分页,可能是指下拉刷新后加载新内容,或者是滚动到底部加载更多。需要先明确下拉分页的具体需求,但根据常见情况,通常下拉刷新是重新加载最新数据,而上拉加载更多是分页加载。不过用户提到“下拉分页”,可能是指滚动到底部的分页加载,所以可能需要先澄清,但根据引用内容,可能用户指的是无限滚动分页。 接下来,我需要分步骤解释实现方法。首先,前端部分,包括监听滚动事件、计算滚动位置、触发加载和更新数据。然后,后端需要支持分页参数,比如页码和每页数量,并返回对应数据。最后,性能优化方面,比如防抖、加载状态提示和数据清理。 在引用[1]中提到了前端实现Vue),可以举例使用Vue的@scroll事件或Intersection Observer API。引用[2]中提到了Uni-App的onReachBottom方法,所以可能需要提到不同框架的具体实现方式,比如在Uni-App中使用内置方法,而在网页中使用JavaScript监听滚动。 需要确保回答结构清晰,分步骤说明,并给出代码示例,比如VueJavaScript的例子,以及后端的Spring Boot示例。同时,注意提到防抖和加载提示等优化点,确保用户体验流畅。 最后,生成相关问题,帮助用户进一步理解。比如性能优化、不同框架的实现差异以及后端分页设计等。</think>### 如何在移动应用或网页中实现下拉分页功能? #### 一、实现原理 下拉分页功能(即滚动分页加载)的核心是**监听滚动位置**,当用户滚动到页面底部时触发加载下一页数据的请求。关键技术点包括: 1. **滚动位置计算**:通过容器滚动高度、可视区域高度和滚动条位置判断是否触底[^1]; 2. **异步数据加载**:通过AJAX或Fetch API请求后端分页接口; 3. **数据合并与渲染**:将新数据追加到现有列表并更新视图。 --- #### 二、前端实现步骤(以Web为例) ##### 1. 监听滚动事件 ```javascript // 监听容器的滚动事件 const container = document.getElementById('list-container'); container.addEventListener('scroll', function() { const { scrollTop, scrollHeight, clientHeight } = container; if (scrollHeight - scrollTop <= clientHeight + 100) { // 距离底部100px时触发 loadNextPage(); } }); ``` ##### 2. 分页数据请求 ```javascript let currentPage = 1; async function loadNextPage() { if (isLoading) return; // 防止重复加载 isLoading = true; const response = await fetch(`/api/data?page=${currentPage}&size=10`); const newData = await response.json(); renderData(newData); // 渲染新数据 currentPage++; isLoading = false; } ``` ##### 3. 使用Intersection Observer API(更高效) ```javascript const sentinel = document.getElementById('load-more-trigger'); const observer = new IntersectionObserver((entries) => { if (entries[0].isIntersecting) { loadNextPage(); } }); observer.observe(sentinel); ``` --- #### 三、移动端框架实现(Uni-App为例) ##### 1. 使用`onReachBottom`生命周期 ```javascript // 页面配置中启用 { "onReachBottomDistance": 50 // 距离底部50px触发 } // 页面逻辑 export default { onReachBottom() { this.loadNextPage(); } } ``` ##### 2. 使用`scroll-view`组件 ```html <scroll-view scroll-y @scrolltolower="loadNextPage" :lower-threshold="100" > <!-- 列表内容 --> </scroll-view> ``` --- #### 四、后端接口设计 需提供支持分页参数的API,例如: ```java // Spring Boot示例 @GetMapping("/api/data") public Page<Data> getData( @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size ) { return repository.findAll(PageRequest.of(page-1, size)); } ``` --- #### 五、性能优化要点 1. **防抖处理**:避免频繁触发滚动事件(例如500ms内仅触发一次); 2. **加载状态提示**:显示“加载中”提示或骨架屏; 3. **数据清理**:保留最近N页数据,避免内存溢出; 4. **错误重试**:加载失败时提供重试按钮[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值