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

 更新时间:2019年02月19日 09:51:39   作者:deniro_li  
这篇文章主要介绍了Vue.js实现可排序的表格组件功能,涉及vue.js事件响应、元素动态操作、排序、运算等相关操作技巧,需要的朋友可以参考下

本文实例讲述了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;
}

效果:

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

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

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

在线HTML/CSS/JavaScript代码运行工具:
http://tools.jb51.net/code/HtmlJsRun

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

相关文章

  • Vue中自定义全局组件的实现方法

    Vue中自定义全局组件的实现方法

    这两天学习了Vue.js 感觉组件这个地方知识点挺多的,而且很重要,所以这篇文章主要给大家介绍了关于Vue中自定义全局组件的实现方法,文中通过示例代码介绍的非常详细,对大家学习或者使用vue具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-12-12
  • vue3中 provide 和 inject 用法及原理

    vue3中 provide 和 inject 用法及原理

    这篇文章主要介绍vue3中 provide 和 inject 用法及原理,provide 和 inject可以 解决多次组件传递数据的问题,下面文章是具体的用法和实现原理,具有一定的参考价值,需要的朋友可以参考一下,希望对大家有所帮助
    2021-11-11
  • vue框架制作购物车小球动画效果实例代码

    vue框架制作购物车小球动画效果实例代码

    最近在学习前端制作了一个购物车小球的动画效果,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友参考下吧
    2019-09-09
  • Vue2.0 组件传值通讯的示例代码

    Vue2.0 组件传值通讯的示例代码

    本篇文章主要介绍了Vue2.0 组件传值通讯的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • 分享一个基于Ace的Markdown编辑器

    分享一个基于Ace的Markdown编辑器

    相信开发中或多或少都会有使用md的时候。那么一个简易的md编辑器显得尤为重要,如果想要在自己的项目中添加一个md编辑器,那么不妨来看看这篇文章
    2021-10-10
  • vue中如何防止用户频繁点击按钮详解

    vue中如何防止用户频繁点击按钮详解

    在后台使用过程中经常会因为按钮重复点击,而造成发送多次重复请求 以下方法可以避免这种情况,下面这篇文章主要给大家介绍了关于vue中如何防止用户频繁点击按钮的相关资料,需要的朋友可以参考下
    2022-09-09
  • 前端主流框架vue学习笔记第二篇

    前端主流框架vue学习笔记第二篇

    一步一步学Vue,这篇文章为大家分享了第二篇前端主流框架vue学习笔记,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • 详解Vue项目编译后部署在非网站根目录的解决方案

    详解Vue项目编译后部署在非网站根目录的解决方案

    这篇文章主要介绍了Vue项目编译后部署在非网站根目录的解决方案,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-04-04
  • Vue完整版和runtime版的区别详解

    Vue完整版和runtime版的区别详解

    这篇文章主要为大家介绍了Vue完整版和runtime版的区别详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • Vue.js递归组件实现组织架构树和选人功能

    Vue.js递归组件实现组织架构树和选人功能

    这篇文章主要介绍了Vue.js递归组件实现组织架构树和选人功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-07-07

最新评论