Vue.js 结合 Bootstrap 实现前端分页与排序功能详解

作者:山岚时间:2026-10-21 23:24:30浏览:454

本文介绍如何利用 Vue.js 与 Bootstrap 在前端实现数据列表的分页显示与排序功能。通过 Vue 的数据绑定、事件处理、循环渲染及过滤器特性,结合 orderBy 过滤器实现排序,并手动计算分页逻辑,解决后台未返回分页数据时的前端处理问题。

项目背景与需求分析

在开始编写代码之前,先分享一些背景情况。原本专注于 .NET 后端开发,但随着项目需求的变化,逐渐转向了纯前端开发工作。近期的项目中,后台多由 Java 技术栈构建,导致 .NET 开发人员主要承担前端界面开发任务,无论是使用 WPF 还是 HTML 编写 Web 页面。

鉴于自身前端技术储备相对薄弱,虽然过去曾使用 ASP.NET 进行前后端全栈开发,但前端部分主要依赖现成的 JavaScript 库和页面模板,对 Vue.js 等现代前端框架缺乏实际接触经验。

由于不擅长编写 Java 后端代码,在最近负责的一个项目中,仅承担前端开发职责。然而,后台接口返回的数据并未进行分页处理,前端接收到全部数据后需要自行实现分页逻辑,这一情况令人感到无奈。

恰好近期正在学习 Vue.js,因此决定使用该框架来实现此页面功能,并在此过程中总结相关经验。

最终实现的界面效果如下图所示:

项目背景与需求分析

Vue.js 核心语法应用

实现过程中涉及以下核心语法:

数据绑定使用 {{...}} 语法或 v-model 指令。


<td >{{dataItem.id}}</td>
<input v-model="message">

事件绑定通过 v-on 指令实现。


<th v-on:click="sortBy('id')">ID</th>

循环渲染使用 v-for 指令。


<option v-for="item in arrPageSize" value="{{item}}">{{item}}</option>

条件判断使用 v-if 指令。


<span v-if="item==1" class="btn btn-default" v-on:click="showPage(1,$event)">首页</span>

自定义过滤器通过 Vue.filter 定义。


//定义
Vue.filter( 'name' , function(value) {
  return value * .5 ;
 });
//使用
<td>{{dataItem.age | name}}</td>
<input v-model="message | name">

排序功能利用 orderBy 过滤器实现。


<tr v-for="dataItem in arrayData | orderBy sortparam sorttype">
  <td >{{dataItem.id}}</td>
  <td >{{dataItem.name}}</td>
  <td>{{dataItem.age}}</td>
</tr>

HTML 结构代码如下:

前端分页与排序实现


<div id="test" class="form-group">
   <div class="form-group">
    <div class="page-header">
     数据
    </div>
    <table class="table table-bordered table-responsive table-striped">
     <tr>
      <th v-on:click="sortBy('id')">ID</th>
      <th>姓名</th>
      <th v-on:click="sortBy('age')">年龄</th>
     </tr>
     <tr v-for="dataItem in arrayData | orderBy sortparam sorttype">
      <td >{{dataItem.id}}</td>
      <td >{{dataItem.name}}</td>
      <td>{{dataItem.age}}</td>
     </tr>
    </table>
    <div class="page-header">分页</div>
    <div class="pager" id="pager">
     <span class="form-inline">
      <select class="form-control" v-model="pagesize" v-on:change="showPage(pageCurrent,$event,true)" number>
       <option v-for="item in arrPageSize" value="{{item}}">{{item}}</option>
      </select>
     </span>
     <template v-for="item in pageCount+1">
      <span v-if="item==1" class="btn btn-default" v-on:click="showPage(1,$event)">
       首页
      </span>
      <span v-if="item==1" class="btn btn-default" v-on:click="showPage(pageCurrent-1,$event)">
       上一页
      </span>
      <span v-if="item==1" class="btn btn-default" v-on:click="showPage(item,$event)">
       {{item}}
      </span>
      <span v-if="item==1&&item<showPagesStart-1" class="btn btn-default disabled">
       ...
      </span>
      <span v-if="item>1&&item<=pageCount-1&&item>=showPagesStart&&item<=showPageEnd&&item<=pageCount" class="btn btn-default" v-on:click="showPage(item,$event)">
       {{item}}
      </span>
      <span v-if="item==pageCount&&item>showPageEnd+1" class="btn btn-default disabled">
       ...
      </span>
      <span v-if="item==pageCount&&item!=1" class="btn btn-default" v-on:click="showPage(item,$event)">
       {{item}}
      </span>
      <span v-if="item==pageCount" class="btn btn-default" v-on:click="showPage(pageCurrent+1,$event)">
       下一页
      </span>
      <span v-if="item==pageCount" class="btn btn-default" v-on:click="showPage(pageCount,$event)">
       尾页
      </span>
     </template>
     <span class="form-inline">
      <input class="pageIndex form-control" style="width:60px;text-align:center" type="text" v-model="pageCurrent | onlyNumeric" v-on:keyup.enter="showPage(pageCurrent,$event,true)" />
     </span>
     <span>{{pageCurrent}}/{{pageCount}}</span>
    </div>
   </div>
  </div>

JavaScript 逻辑代码如下:


 //只能输入正整数过滤器
  Vue.filter('onlyNumeric', {
   // model -> view
   // 在更新 `<input>` 元素之前格式化值
   read: function (val) {
    return val;
   },
   // view -> model
   // 在写回数据之前格式化值
   write: function (val, oldVal) {
    var number = +val.replace(/[^\d]/g, '')
    return isNaN(number) ? 1 : parseFloat(number.toFixed(2))
   }
  })
  //模拟获取数据
  var getData=function(){
   var result = [];
   for (var i = 0; i < 500; i++) {
    result[i] ={name:'test'+i,id:i,age:(Math.random()*100).toFixed()};
    }
   return result;
  }

  var vue = new Vue({
   el: "#test",
   //加载完成后执行
   ready:function(){
    this.arrayDataAll = getData();
    this.totalCount = this.arrayDataAll.length;
    this.showPage(this.pageCurrent, null, true);
   },
   data: {
    //总项目数
    totalCount: 200,
    //分页数
    arrPageSize:[10,20,30,40],
    //当前分页数
    pageCount: 20,
    //当前页面
    pageCurrent: 1,
    //分页大小
    pagesize: 10,
    //显示分页按钮数
    showPages: 11,
    //开始显示的分页按钮
    showPagesStart: 1,
    //结束显示的分页按钮
    showPageEnd: 100,
    //所有数据
    arrayDataAll:[],
    //分页数据
    arrayData: [],
    //排序字段
    sortparam:"",
    //排序方式
    sorttype:1,


   },
   methods: {
    //分页方法
    showPage: function (pageIndex, $event, forceRefresh) {

     if (pageIndex > 0) {


      if (pageIndex > this.pageCount) {
       pageIndex = this.pageCount;
      }

      //判断数据是否需要更新
      var currentPageCount = Math.ceil(this.totalCount / this.pagesize);
      if (currentPageCount != this.pageCount) {
       pageIndex = 1;
       this.pageCount = currentPageCount;
      }
      else if (this.pageCurrent == pageIndex && currentPageCount == this.pageCount && typeof (forceRefresh) == "undefined") {
       console.log("not refresh");
       return;
      }

      //处理分页点中样式
      var buttons = $("#pager").find("span");
      for (var i = 0; i < buttons.length; i++) {
       if (buttons.eq(i).html() != pageIndex) {
        buttons.eq(i).removeClass("active");
       }
       else {
        buttons.eq(i).addClass("active");
       }
      }

      //从所有数据中取分页数据
      var newPageInfo = [];
      for (var i = 0; i < this.pagesize; i++) {
      var index =i+(pageIndex-1)*this.pagesize;
      if(index>this.totalCount-1)break;
       newPageInfo[newPageInfo.length] = this.arrayDataAll[index];
      }
      this.pageCurrent = pageIndex;
      this.arrayData = newPageInfo;

      //计算分页按钮数据
      if (this.pageCount > this.showPages) {
       if (pageIndex <= (this.showPages - 1) / 2) {
        this.showPagesStart = 1;
        this.showPageEnd = this.showPages - 1;
        console.log("showPage1")
       }
       else if (pageIndex >= this.pageCount - (this.showPages - 3) / 2) {
        this.showPagesStart = this.pageCount - this.showPages + 2;
        this.showPageEnd = this.pageCount;
        console.log("showPage2")
       }
       else {
        console.log("showPage3")
        this.showPagesStart = pageIndex - (this.showPages - 3) / 2;
        this.showPageEnd = pageIndex + (this.showPages - 3) / 2;
       }
      }
     }
     //排序
    },sortBy: function (sortparam) {
     this.sortparam = sortparam;
     this.sorttype = this.sorttype == -1 ? 1 : -1;
    }
   }
  });

参考资料与源码获取

参考资源:Vue.js 结合 Bootstrap 实现分页控件的相关文档。

完整源码可通过提供的链接下载:vue.js 分页示例。

以上即为本文的全部内容,希望能为读者的学习提供有益帮助,同时也欢迎大家继续支持本站。

作者最新文章