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 相关文章推荐
JavaScrip单线程引擎工作原理分析
Sep 04 Javascript
基于jquery的DIV随滚动条滚动而滚动的代码
Jul 20 Javascript
实现点击列表弹出列表索引的两种方式
Mar 08 Javascript
JS两种类型的表单提交方法实例分析
Nov 28 Javascript
JS中用try catch对代码运行的性能影响分析
Dec 26 Javascript
jQuery插件FusionCharts绘制的2D条状图效果【附demo源码】
May 13 jQuery
Javascript实现找不同色块的游戏
Jul 17 Javascript
HTML5开发Kinect体感游戏的实例应用
Sep 18 Javascript
vue中手机号,邮箱正则验证以及60s发送验证码的实例
Mar 16 Javascript
vue中nextTick用法实例
Sep 11 Javascript
vue实现导航菜单和编辑文本的示例代码
Jul 04 Javascript
Express 配置HTML页面访问的实现
Nov 01 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编码规范之注释和文件结构说明
2010/07/09 PHP
写php分页时出现的Fatal error的解决方法
2011/04/18 PHP
Windows下Apache + PHP SESSION丢失的解决过程全纪录
2015/04/07 PHP
Yii操作数据库实现动态获取表名的方法
2016/03/29 PHP
php正则提取html图片(img)src地址与任意属性的方法
2017/02/08 PHP
对于Laravel 5.5核心架构的深入理解
2018/02/22 PHP
php5.6.x到php7.0.x特性小结
2019/08/17 PHP
禁止刷新,回退的JS
2006/11/25 Javascript
23个超流行的jQuery相册插件整理分享
2011/04/25 Javascript
jQuery EasyUI API 中文文档 DateTimeBox日期时间框
2011/10/16 Javascript
javascript删除option选项的多种方法总结
2013/11/22 Javascript
使用js画图之正弦曲线
2015/01/12 Javascript
jQuery实现流动虚线框的方法
2015/01/29 Javascript
JavaScript设置body高度为浏览器高度的方法
2015/02/09 Javascript
详解WordPress开发中get_current_screen()函数的使用
2016/01/11 Javascript
jQuery插件扩展测试实例
2016/06/21 Javascript
JSON 对象未定义错误的解决方法
2016/09/29 Javascript
Javascript 闭包详解及实例代码
2016/11/30 Javascript
javascript数据类型详解
2017/02/07 Javascript
微信小程序之数据双向绑定与数据操作
2017/05/12 Javascript
layui.js实现的表单验证功能示例
2017/11/15 Javascript
vue.js实现标签页切换效果
2018/06/07 Javascript
Vue插件打包与发布的方法示例
2018/08/20 Javascript
15个顶级开源JavaScript框架和库
2018/10/10 Javascript
Python基于select实现的socket服务器
2016/04/13 Python
python3 requests中使用ip代理池随机生成ip的实例
2018/05/07 Python
pandas DataFrame索引行列的实现
2019/06/04 Python
numpy数组广播的机制
2019/07/12 Python
python 爬取疫情数据的源码
2020/02/09 Python
详解python变量与数据类型
2020/08/25 Python
彪马美国官网:PUMA美国
2017/03/09 全球购物
关键字final的用法
2013/10/02 面试题
成品仓管员岗位职责
2013/12/11 职场文书
2014年客房部工作总结
2014/11/22 职场文书
2014年领导班子工作总结
2014/12/11 职场文书
2015年教师业务工作总结
2015/05/26 职场文书