Vue.js实现可排序的表格组件功能示例


Posted in Javascript onFebruary 19, 2019

本文实例讲述了Vue.js实现可排序的表格组件功能。分享给大家供大家参考,具体如下:

我们基于 Vue.js 实现一个可根据某列进行排序的表格组件。

一个表格包含表头和数据两部分内容。因此,我们定义两个数组,columns 表示表头信息,在 <thread> 中渲染,并可在此指定某一列是否需要排序;data 表示数据。

html:

<div id="app" v-cloak>
  <v-table :data="data" :columns="columns"></v-table>
  <button @click="add">新增</button>
</div>

把父组件中定义的 data 与 columns 传入 v-table 组件。

js:

Vue.component('vTable', {
  props: {
    //表头列名称
    columns: {
      type: Array,
      default: function () {
        return [];
      }
    },
    //数据
    data: {
      type: Array,
      default: function () {
        return [];
      }
    }
  },
  //为了不影响原始数据,这里定义了相应的需要操作的数据对象
  data: function () {
    return {
      currentColumns: [],
      currentData: []
    }
  },
  //render 实现方式
  render: function (createElement) {
    var that = this;
    /**
     * 创建列样式与表头
     */
    var ths = [];//<th> 标签数组
    var cols = [];//<cols> 标签数组
    this.currentColumns.forEach(function (col, index) {
      if (col.width) {//创建列样式
        cols.push(createElement('col', {
          style: {
            width: col.width
          }
        }))
      }
      if (col.sortable) {
        ths.push(createElement('th', [
          createElement('span', col.title),
          //升序
          createElement('a', {
            class: {
              on: col.sortType === 'asc'
            },
            on: {
              click: function () {
                that.sortByAsc(index)
              }
            }
          }, '↑'),
          //降序
          createElement('a', {
            class: {
              on: col.sortType === 'desc'
            },
            on: {
              click: function () {
                that.sortByDesc(index);
              }
            }
          }, '↓')
        ]));
      } else {
        ths.push(createElement('th', col.title));
      }
    });
    /**
     * 创建内容
     */
    var trs = [];//<tr> 标签数组
    this.currentData.forEach(function (row) {//遍历行
      var tds = [];//<td> 标签数组
      that.currentColumns.forEach(function (cell) {//遍历单元格
        tds.push(createElement('td', row[cell.key]));
      });
      trs.push(createElement('tr', tds));
    });
    return createElement('table', [
      createElement('colgroup', cols),
      createElement('thead', [
        createElement('tr', ths)
      ]),
      createElement('tbody', trs)
    ])
  },
  methods: {
    //初始化表头
    initColumns: function () {
      this.currentColumns = this.columns.map(function (col, index) {
        //新建字段,标识当前列排序类型;默认为“不排序”
        col.sortType = 'normal';
        //新建字段,标识当前列在数组中的索引
        col.index = index;
        return col;
      });
    },
    //初始化数据
    initData: function () {
      this.currentData = this.data.map(function (row, index) {
        //新建字段,标识当前行在数组中的索引
        row.index = index;
        return row;
      });
    },
    //排序
    order: function (index, type) {
      this.currentColumns.forEach(function (col) {
        col.sortType = 'normal';
      });
      //设置排序类型
      this.currentColumns[index].sortType = type;
      //设置排序函数
      var sortFunction;
      var key = this.currentColumns[index].key;
      switch (type) {
        default://默认为 asc 排序
        case 'asc':
          sortFunction = function (a, b) {
            return a[key] > b[key] ? 1 : -1;
          };
          break;
        case 'desc':
          sortFunction = function (a, b) {
            return a[key] < b[key] ? 1 : -1;
          };
          break;
      }
      this.currentData.sort(sortFunction);
    },
    //升序
    sortByAsc: function (index) {
      this.order(index, 'asc');
    },
    //降序
    sortByDesc: function (index) {
      this.order(index, 'desc');
    }
  },
  watch: {
    data: function () {
      this.initData();
      //找出排序字段
      var sortedColumn = this.currentColumns.filter(function (col) {
        return col.sortType !== 'normal';
      });
      if (sortedColumn.length > 0) {
        if (sortedColumn[0].sortType === 'asc') {
          this.sortByAsc(sortedColumn[0].index);
        } else {
          this.sortByDesc(sortedColumn[0].index);
        }
      }
    }
  },
  mounted() {
    this.initColumns();
    this.initData();
  }
});
var app = new Vue({
  el: '#app',
  data: {
    //title 、key 与 width 必填;sortable 选填
    columns: [
      {
        title: '名称',
        key: 'name',
        width:'60%'
      },
      {
        title: '数量',
        key: 'num',
        width:'20%',
        sortable: true
      },
      {
        title: '单价',
        key: 'unitPrice',
        width:'20%',
        sortable: true
      }
    ],
    data: [
      {
        name: '真果粒牛奶饮品',
        num: 2,
        unitPrice: 59.9
      },
      {
        name: '苏泊尔(SUPOR)电压力锅 ',
        num: 1,
        unitPrice: 378.0
      },
      {
        name: '乐事(Lay\'s)薯片',
        num: 3,
        unitPrice: 63.0
      }
    ]
  },
  methods:{
    add:function () {
      this.data.push( {
        name: '良品铺子 休闲零食大礼包',
        num: 5,
        unitPrice: 59.80
      });
    }
  }
});

为了让排序后的 columns 与 data 不影响原始数据,我们在组件的 data 中定义了相应的当前数据对象。因此在 method 中使用传入的值,初始化这些数据对象,最后在 mounted() 调用这些初始化方法。

columns 中的每一项都是包含 title(列名)、key(对应 data 中的字段名)、width(宽度) 以及 sortable(是否可排序) 的对象。其中,只有 sortable 为可选项,如果设定为 true,则表示该列可点击排序。

map() 会对数组的每一项运行给定函数,返回每次函数调用的结果组成的数组。

排序分为升序与降序,因为只能对某一列进行排序,所以是互斥操作。我们为每一列新增一个 sortType ,用于标识该列的排序类型,初始值为 normal,表示不排序。

因为排序字段可能是任意列,所以我们为每一列新增一个 index,用于标识当前列在数组中的索引。

在 Render 函数中,首先创建列样式与表头,接着创建内容。

Render 函数中的 createElement 可以简写为 h,这样代码会变得更简洁:

render: function (h) {
  var that = this;
  /**
   * 创建列样式与表头
   */
  var ths = [];//<th> 标签数组
  var cols = [];//<cols> 标签数组
  this.currentColumns.forEach(function (col, index) {
    if (col.width) {//创建列样式
      cols.push(h('col', {
        style: {
          width: col.width
        }
      }))
    }
    if (col.sortable) {
      ths.push(h('th', [
        h('span', col.title),
        //升序
        h('a', {
          class: {
            on: col.sortType === 'asc'
          },
          on: {
            click: function () {
              that.sortByAsc(index)
            }
          }
        }, '↑'),
        //降序
        h('a', {
          class: {
            on: col.sortType === 'desc'
          },
          on: {
            click: function () {
              that.sortByDesc(index);
            }
          }
        }, '↓')
      ]));
    } else {
      ths.push(h('th', col.title));
    }
  });
  /**
   * 创建内容
   */
  var trs = [];//<tr> 标签数组
  this.currentData.forEach(function (row) {//遍历行
    var tds = [];//<td> 标签数组
    that.currentColumns.forEach(function (cell) {//遍历单元格
      tds.push(h('td', row[cell.key]));
    });
    trs.push(h('tr', tds));
  });
  return h('table', [
    h('colgroup', cols),
    h('thead', [
      h('tr', ths)
    ]),
    h('tbody', trs)
  ])
}

创建内容时,我们首先遍历所有行,然后在循环内部遍历所有列,得出 <td> 与 <tr> 内容。

创建表头时,对是否排序做了相应的处理,并绑定了相应的点击事件。

点击事件定义在 methods 中,因为升序与降序逻辑大体相同,所以又封装了一层 order() 排序函数。

order() 排序函数内部使用了数组的 sort() 方法。sort() 方法会调用每个数组项的 toString() 方法,然后比较得到的字符串,即使数组中的每一项是数值,比较的也是字符串。这里传入了一个比较函数作为参数。为了兼容所有浏览器,在比较函数中,我们返回的是 1 或者 -1。

排序之前,先把所有列的排序类型都设置为不排序,然后再更新当前列的排序状态。这就会对应到 render 函数里绑定 <a> 标签的 class 中的 on 样式,即当前列排序状态会被高亮显示。

表格被初始化渲染之后,如果 data 发生变化,那么表格组件数据应该也要同步更新。因此,我们在 watch 中做了数据更新以及数据重排操作。

css:

[v-cloak] {
  display: none;
}
table {
  width: 100%;
  margin-bottom: 24px;
  /*合并边框模型*/
  border-collapse: collapse;
  border-spacing: 0;
  /*在空单元格周围绘制边框*/
  empty-cells: show;
  border: 1px solid #e9e9e9;
}
table th {
  font: bold 14px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif;
  background: #CAE8EA;
  color: #5c6b77;
  /*设置文本粗细*/
  font-weight: 600;
  /*段落中的文本不进行换行*/
  white-space: nowrap;
  border-top: 1px solid #C1DAD7;
}
table td, table th {
  padding: 8px 16px;
  text-align: left;
  border-right: 1px solid #C1DAD7;
  border-bottom: 1px solid #C1DAD7;
}
table th a {
  /*不独占一行的块级元素*/
  display: inline-block;
  margin: 0 4px;
  cursor: pointer;
}
table th a.on {
  color: #3399ff;
}
table th a:hover {
  color: #3399ff;
}

效果:

Vue.js实现可排序的表格组件功能示例

点击此处查看本文示例代码

PS:感兴趣的朋友还可以使用如下在线工具测试上述代码:

在线HTML/CSS/JavaScript前端代码调试运行工具:
http://tools.3water.com/code/WebCodeRun

在线HTML/CSS/JavaScript代码运行工具:
http://tools.3water.com/code/HtmlJsRun

希望本文所述对大家vue.js程序设计有所帮助。

Javascript 相关文章推荐
addRule在firefox下的兼容写法
Nov 30 Javascript
Jquery Ajax.ashx 高效分页实现代码
Oct 20 Javascript
Eclipse去除js(JavaScript)验证错误
Feb 11 Javascript
一个CSS+jQuery实现的放大缩小动画效果
Feb 19 Javascript
jQuery截取指定长度字符串的实现原理及代码
Jul 01 Javascript
Radio 单选JS动态添加的选项onchange事件无效的解决方法
Dec 12 Javascript
jQuery validate插件功能与用法详解
Dec 15 Javascript
javascript 中null和undefined区分和比较
Apr 19 Javascript
BootStrap Table前台和后台分页对JSON格式的要求
Jun 28 Javascript
jQuery判断网页是否已经滚动到浏览器底部的实现方法
Oct 27 jQuery
AngularJS实现controller控制器间共享数据的方法示例
Oct 30 Javascript
手把手教你 CKEDITOR 4 实现Dialog 内嵌 IFrame操作详解
Jun 18 Javascript
Angular7创建项目、组件、服务以及服务的使用
Feb 19 #Javascript
小程序转发探索示例
Feb 19 #Javascript
JS异步执行结果获取的3种解决方式
Feb 19 #Javascript
jQuery AJAX与jQuery事件的分析讲解
Feb 18 #jQuery
基于node.js实现爬虫的讲解
Feb 18 #Javascript
简单实现vue中的依赖收集与响应的方法
Feb 18 #Javascript
vue实现的网易云音乐在线播放和下载功能案例
Feb 18 #Javascript
You might like
探讨如何在PHP开启gzip页面压缩实例
2013/06/09 PHP
php简单实现快速排序的方法
2015/04/04 PHP
在WordPress的后台中添加顶级菜单和子菜单的函数详解
2016/01/11 PHP
php结合mysql与mysqli扩展处理事务的方法
2016/06/29 PHP
THINKPHP-Apache服务器中使用Alias虚拟目录URL重写 隐藏index.php
2021/03/09 PHP
javascript string字符串优化问题
2011/07/31 Javascript
jQuery内部原理和实现方式浅析
2015/02/03 Javascript
JS实现从网页顶部掉下弹出层效果的方法
2015/08/06 Javascript
jQuery+Ajax实现无刷新分页
2015/10/30 Javascript
JavaScript正则表达式替换字符串中图片地址(img src)的方法
2017/01/13 Javascript
canvas实现刮刮卡效果
2017/03/14 Javascript
js实现1,2,3,5数字按照概率生成
2017/09/12 Javascript
深入理解React高阶组件
2017/09/28 Javascript
浅谈Vuex的状态管理(全家桶)
2017/11/04 Javascript
vue todo-list组件发布到npm上的方法
2018/04/04 Javascript
解决JavaScript layui 下拉框不显示的问题
2018/08/14 Javascript
原生JS实现拖拽功能
2020/12/16 Javascript
[01:02:48]2018DOTA2亚洲邀请赛 4.1 小组赛 A组 LGD vs OG
2018/04/02 DOTA
linux系统使用python获取cpu信息脚本分享
2014/01/15 Python
Python实现类继承实例
2014/07/04 Python
简单了解Python下用于监视文件系统的pyinotify包
2015/11/13 Python
Python正则表达式如何进行字符串替换实例
2016/12/28 Python
Python多进程multiprocessing.Pool类详解
2018/04/27 Python
简单瞅瞅Python vars()内置函数的实现
2019/09/27 Python
python实现用户名密码校验
2020/03/18 Python
Python用5行代码实现批量抠图的示例代码
2020/04/14 Python
浅谈h5自定义audio(问题及解决)
2016/08/19 HTML / CSS
英国精品买手店:Browns Fashion
2016/09/29 全球购物
创意爱尔兰礼物:Creative Irish Gifts
2020/01/29 全球购物
应用英语专业自荐信
2014/01/26 职场文书
工业设计毕业生自荐信
2014/04/13 职场文书
运动会方阵口号
2014/06/07 职场文书
拔河比赛口号
2014/06/10 职场文书
教师学期个人总结
2015/02/11 职场文书
交通事故被告答辩状
2015/05/22 职场文书
深度学习小工程练习之垃圾分类详解
2021/04/14 Python