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 相关文章推荐
使用JavaScript switch case 另类写法
Mar 14 Javascript
TreeView 用法(有代码)(asp.net)
Jul 15 Javascript
jQuery 1.9.1源码分析系列(十五)之动画处理
Dec 03 Javascript
JavaScript通过代码调用Flash显示的方法
Feb 02 Javascript
jQuery继承extend用法详解
Oct 10 Javascript
jQuery.Validate表单验证插件的使用示例详解
Jan 04 Javascript
随机生成10个不重复的0-100的数字(实例讲解)
Aug 16 Javascript
微信小程序实现之手势锁功能实例代码
Jul 19 Javascript
angularJs中orderBy筛选以及filter过滤数据的方法
Sep 30 Javascript
layui 上传插件 带预览 非自动上传功能的实例(非常实用)
Sep 23 Javascript
js实现简单掷骰子效果
Oct 24 Javascript
Vue实现开关按钮拖拽效果
Sep 22 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 阴历-农历-转换类代码
2012/01/16 PHP
php基础教程 php内置函数实例教程
2012/08/21 PHP
php+mysql查询优化简单实例
2015/01/13 PHP
php阳历转农历优化版
2016/08/08 PHP
自制PHP框架之路由与控制器
2017/05/07 PHP
PHP实现基于PDO扩展连接PostgreSQL对象关系数据库示例
2018/03/31 PHP
浅谈PHPANALYSIS提取关键字
2019/03/08 PHP
JS 事件绑定函数代码
2010/04/28 Javascript
AJAX 网页保留浏览器前进后退等功能
2011/02/12 Javascript
FireBug 调试JS入门教程 如何调试JS
2013/12/23 Javascript
网页右下角弹出窗体实现代码
2014/06/05 Javascript
使用GruntJS构建Web程序之Tasks(任务)篇
2014/06/06 Javascript
JS实现自动切换文字的导航效果代码
2015/08/27 Javascript
jQuery实现仿QQ头像闪烁效果的文字闪动提示代码
2015/11/03 Javascript
原生JavaScript编写canvas版的连连看游戏
2016/05/29 Javascript
vue2.0构建单页应用最佳实战
2017/04/01 Javascript
JavaScript原生数组Array常用方法
2017/04/06 Javascript
微信小程序登录态控制深入分析
2017/04/12 Javascript
JS+canvas绘制的动态机械表动画效果
2017/09/12 Javascript
JavaScript基于面向对象实现的猜拳游戏
2018/01/03 Javascript
vue.js提交按钮时进行简单的if判断表达式详解
2018/08/08 Javascript
vue 登录滑动验证实现代码
2018/08/24 Javascript
如何使用CSS3和JQuery easing 插件制作绚丽菜单
2019/06/18 jQuery
[02:20]DOTA2中文配音宣传片
2013/05/22 DOTA
[03:04]2018年国际邀请赛典藏宝瓶&莱恩声望物品展示 片尾有彩蛋
2018/06/04 DOTA
部署Python的框架下的web app的详细教程
2015/04/30 Python
Python中的hypot()方法使用简介
2015/05/18 Python
详解Python中的from..import绝对导入语句
2016/06/21 Python
使用pandas对矢量化数据进行替换处理的方法
2018/04/11 Python
对Python的zip函数妙用,旋转矩阵详解
2018/12/13 Python
Django中文件上传和文件访问微项目的方法
2020/04/27 Python
埃弗顿足球俱乐部官方网上商店:Everton Direct
2018/01/13 全球购物
实践单位评语
2014/04/26 职场文书
民族团结先进集体事迹材料
2014/05/22 职场文书
Golang标准库syscall详解(什么是系统调用)
2021/05/25 Golang
MySQL中VARCHAR与CHAR格式数据的区别
2021/05/26 MySQL