Vue 固定头 固定列 点击表头可排序的表格组件

原理是将原table的指定行,指定列clone一份放在其上

实现代码如下:

<template>
 <div>
  <div id="divBox1" :style="{height:height}">
   <table id="tbTest1" cellpadding="0" cellspacing="0" style="text-align:center;background:rgba(244,249,255,0.4);">
    <tr>
     <th v-for="item in thead" @click="sortBy(item)">
      {{item}}<img style="width:0.16rem;height:0.20rem;margin-left:4px;" :src="filterUrl" alt="" v-if="$index!=0" data-img="{{filterUrl}}">
     </th>
    </tr>
    <tr v-for="row in tableRows | orderBy sortBykey sortOrders[sortKey]">
     <td style="overflow:hidden;white-space:nowrap;" v-for="item in gridColumns" v-html="row[item] | numberFilter" :id="$parent.$index">
     </td>
    </tr>
   </table>
  </div>
 </div>
</template>
<script>
 /*eslint-disable*/
 var ofixed_table_st = window.setTimeout;
 var hasLeft = '';
 var hasHead = '';
 window.setTimeout = function(fRef, mDelay) {
  if(typeof fRef == 'function') {
   var argu = Array.prototype.slice.call(arguments, 2);
   var f = (function() {
    fRef.apply(null, argu);
   });
   return ofixed_table_st(f, mDelay);
  }
  return ofixed_table_st(fRef, mDelay);
 };
 function oFixedTable(id, obj, _cfg) {
  this.id = id;
  this.obj = obj;
  this.box = this.obj.parentNode;
  this.config = {
   fixHead: _cfg.fixHead || true,
   rows: _cfg.rows || 1,
   cols: _cfg.cols || 0,
   background: _cfg.background || '#ffffff',
   zindex: _cfg.zindex || 10
  };
  window.setTimeout(this._fixTable, 100, this);
 }
 oFixedTable.prototype._fixTable = function(_) {
  if(_.obj.rows.length <= 0) {
   return false;
  }
  var hasLeft = _.buildLeft();
  var hasHead = _.buildHead();
  _.box.onscroll = function() {
   if(_.divHead != null) {
    _.divHead.scrollLeft = this.scrollLeft;
   }
   if(_.divLeft != null) {
    _.divLeft.scrollTop = this.scrollTop;
   }
  };
  if(hasHead && hasLeft) {
   _.buildTopLeft();
  }
 };
 oFixedTable.prototype.buildHead = function() {
  console.log(2222222222222222222)
  var _ = this;
  var strDivId = _.id + '_div_head';
  var strTbId = _.id + '_tb_header';
  var div = document.createElement('div');
  div.id = strDivId;
  div.style.cssText = 'position:absolute;overflow:hidden;z-index:' + (_.config.zindex + 1) + ';';
  div.innerHTML = '<table id="' + strTbId + '" cellpadding="0" cellspacing="0" style="background:' + _.config.background + ';"></table>';
  _.box.insertBefore(div, _.obj);
  _.divHead = div;
  _.tbHead = document.getElementById(strTbId);
  //判断是否出现纵向滚动条,若出现,高度减去滚动条宽度 16px
  var sw = _.obj.offsetHeight > _.box.offsetHeight ? 0 : 0;
  _.divHead.style.width = (_.box.offsetWidth - sw) + 'px';
  _.tbHead.style.textAlign = _.obj.style.textAlign;
  _.tbHead.style.width = _.obj.offsetWidth + 'px';
  var hasHead = false;
  if(_.config.fixHead && _.obj.tHead != null) {
   var tHead = _.obj.tHead;
   _.tbHead.appendChild(tHead.cloneNode(true));
   hasHead = true;
  } else {
   for(var i = 0; i < _.config.rows; i++) {
    var row = _.obj.rows[i];
    if(row != null) {
     _.tbHead.appendChild(row.cloneNode(true));
     hasHead = true;
    }
   }
  }
  return hasHead;
 };
 oFixedTable.prototype.buildLeft = function() {
  var _ = this;
  if(_.config.cols <= 0) {
   return false;
  }
  var strDivId = _.id + '_div_left';
  var strTbId = _.id + '_tb_left';
  var div = document.createElement('div');
  div.id = strDivId;
  div.style.cssText = 'position:absolute;overflow:hidden;z-index:' + _.config.zindex + ';box-shadow: #dddddd 2px 0px 2px;width: 2rem;';
  div.innerHTML = '<table id=' + strTbId + ' cellpadding="0" cellspacing="0" style="background:' + _.config.background + ';width: 2rem;"></table>';
  _.box.insertBefore(div, _.obj);
  _.divLeft = div;
  _.tbLeft = document.getElementById(strTbId);
  _.tbLeft.style.textAlign = _.obj.style.textAlign;
  //判断是否出现横向滚动条,若出现,高度减去滚动条高度 16px
  var sw = _.obj.offsetWidth > _.box.offsetWidth ? 0 : 0;
  _.divLeft.style.height = (_.box.offsetHeight - sw) + 'px';
  var hasLeft = false;
  for(var i = 0, rows = _.obj.rows.length; i < rows; i++) {
   var row = _.tbLeft.insertRow(_.tbLeft.rows.length);
   row.style.cssText = _.obj.rows[i].style.cssText;
   for(var j = 0; j < _.config.cols; j++) {
    var cell = _.obj.rows[i].cells[j];
    if(cell != null) {
     row.appendChild(cell.cloneNode(true));
     cell.style.cssText = _.obj.rows[i].cells[j].style.cssText;
     hasLeft = true;
    }
   }
  }
  return hasLeft;
 };
 oFixedTable.prototype.buildTopLeft = function() {
  var _ = this;
  var strDivId = _.id + '_div_top_left';
  var strTbId = _.id + '_tb_top_left';
  var div = document.createElement('div');
  div.id = strDivId;
  div.style.cssText = 'position:absolute;overflow:hidden;z-index:' + (_.config.zindex + 2) + ';box-shadow: #dddddd 2px 0px 2px;width: 2rem;';
  div.innerHTML = '<table id="' + strTbId + '" cellpadding="0" cellspacing="0" style="background:' + _.config.background + ';"></table>';
  _.box.insertBefore(div, _.obj);
  var tbTopLeft = document.getElementById(strTbId);
  tbTopLeft.style.textAlign = _.obj.style.textAlign;
  for(var i = 0; i < _.config.rows; i++) {
   var row = tbTopLeft.insertRow(tbTopLeft.rows.length);
   row.style.cssText = _.obj.rows[i].style.cssText;
   for(var j = 0; j < _.config.cols; j++) {
    var cell = _.obj.rows[i].cells[j];
    if(cell != null) {
     row.appendChild(cell.cloneNode(true));
     cell.style.cssText = _.obj.rows[i].cells[j].style.cssText;
     hasLeft = true;
    }
   }
  }
 };
 export default{
  // 接收父组件传过来的参数
  props: ['tableRows', 'gridColumns', 'thead', 'store', 'height', 'singleData'],
  // 监控
  watch: {
   'tableRows': function (val) {
    var self = this
    // 明星店铺页面时动态调整店铺名所在列的宽度s
    if (self.store) {
     document.querySelectorAll('table td:nth-child(3)')[0].style.width = 3 + 'rem'
     document.querySelectorAll('table th:nth-child(3)')[0].style.width = 3 + 'rem'
    }
    var length = self.gridColumns.length
    document.getElementById('tbTest1').style.width = 2 * length + 'rem'
    setTimeout(function () {
     if (self.singleData) {
      document.getElementById('ofix1_tb_left').classList.add('ofix1_tb_left')
     }
     document.querySelectorAll('#ofix1_tb_left td')[0].style.width = 2 + 'rem'
     var tbObj = document.getElementById('ofix1_tb_header')
     tbObj.addEventListener('click',function (event) {
      if(event.target.tagName === 'TH'){
       self.sortBy(event.target.innerText, event)
      }
     })
    }, 101)
   }
  },
  data: function() {
   var sortOrders = {}
   this.gridColumns.forEach(function (key) {
    sortOrders[key] = 1
   })
   return {
    sortKey: '',
    filterUrl: './static/img/indus/filter1.png',
    sortOrders: sortOrders
   }
  },
  methods: {
   sortBykey: function (a, b) {
    return parseFloat(a[this.sortKey]) - parseFloat(b[this.sortKey])
    console.log('11111111111')
   },
   sortBy: function (key, event) {
    // 每一次排序之前所有的图片重置
    var imgDom = document.querySelectorAll('#ofix1_tb_header th img')
    for (var x = 0; x < imgDom.length; x++) {
     imgDom[x].setAttribute('src', './static/img/indus/filter1.png')
    }
    // 排序
    var activeTheadIndex = 0
    for (var i = 0; i < this.thead.length; i++) {
     if (this.thead[i] === key) {
      activeTheadIndex = i
     }
    }
    this.sortKey = this.gridColumns[activeTheadIndex]
    this.sortOrders[this.gridColumns[activeTheadIndex]] = this.sortOrders[this.gridColumns[activeTheadIndex]] * -1
    // 排序时同步改变标识图片
    if (this.sortOrders[this.gridColumns[activeTheadIndex]] > 0) {
     event.target.getElementsByTagName('img')[0].setAttribute('src', './static/img/indus/filter2.png')
    } else {
     event.target.getElementsByTagName('img')[0].setAttribute('src', './static/img/indus/filter3.png')
    }
    // 排序时同步改变左边第一列的内容
    setTimeout(function(){
     var tdDom = document.querySelectorAll('#tbTest1 tr td:nth-child(1)')
     var tdDomLeft = document.querySelectorAll('#ofix1_tb_left td')
     for (var y = 0; y < tdDom.length; y++) {
      tdDomLeft[y].innerHTML = tdDom[y].innerHTML
     }
    },0)
   }
  },
  filters: {
   numberFilter: function (value) {
    if (value == 0) {
     return '0'
    } else if (!value) {
     return '/'
    } else {
     return value
    }
   }
  },
  components: {
  },
  ready: function() {
   var ofix1 = new oFixedTable('ofix1', document.getElementById('tbTest1'), {
    rows: 1,
    cols: 1
   })
  },
  created () {
  }
 }
</script>
<style lang="scss" scoped>
 #divBox1{
  overflow:auto;
  width:100%;
  font-size: 0.28rem;
 }
 #ofix1_div_left{
  box-shadow: #dddddd 2px 0px 2px;
  width: 2rem;
 }
 table {
  table-layout : fixed;
 }
 table td,
 table th {
  width: 2rem;
  line-height: 1rem;
  height: 1rem;
  padding: 0;
  color: #999999;
  overflow: hidden;
  white-space: nowrap;
  /*vertical-align: middle;*/
 }
 table th{
  background: rgba(188,219,255,0.4);
  color: #999;
  font-size: .28rem;
  font-weight: normal;
 }
 table th:nth-child(1){
  box-shadow: #dddddd 2px 0px 0px;
 }
 .ofix1_tb_left tr td:nth-child(1){
  /*display: inline-block;*/
  text-align: left;
 }
 #ofix1_div_top_left{
  box-shadow: #dddddd 2px 0px 2px;
 }
 #tbTest1 tr td:nth-child(1){
  box-shadow: #dddddd 2px 0px 0px;
 }
 #tbheader td {
  background: #fff;
 }
</style> 

父组件调用实例:

<template>
   <table-locked :table-rows="tableData" :grid-columns="gridColumns" :thead="thead" :height="height">
   </table-locked>
</template>
import TableLocked from '../../common/TableLocked.vue'
export default{
  components: {TableLocked},
  data () {
    data.gridColumns = ['brand', 'product_count', 'averagePrice', 'sales', 'huang_sale_per', 'sale_per', 'sales_amount', 'huang_sale_amount_per', 'sales_amount_per', 'score_num', 'scort_good_per']
   data.thead = ['品类', '产品种类', '均价', '销量', '销量环比', '销量占比', '销额(万元)', '销额环比', '销额占比', '评论总数', '好评率']
  }
}

以上所述是小编给大家介绍的Vue 固定头 固定列 点击表头可排序的表格组件,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Vue.js实现一个自定义分页组件vue-paginaiton

    vue实现一个分页组件vue-paginaiton vue使用了一段时间的感触就是,我再也不想直接操作DOM了.数据绑定式的编程体验真是好.实现的一个分页组件. 这里的css就不放出来了,可以看直接去github上下载:vue-pagination 先上一张实例图吧 模版 <div class="page-bar"> <ul> <li v-if="showFirstText"><a v-on:click="cur-

  • vuejs通过filterBy、orderBy实现搜索筛选、降序排序数据

    直接贴代码了: 先上输入前的样子: <style> #example{margin:100px auto;width:600px;} .show{margin:10px;} #searchText{display: block;margin:0 auto 10px;height:24px;line-height: 24px;width:200px;} .content ul li{text-align: center;} .content ul li span{display: inline-

  • Vue form 表单提交+ajax异步请求+分页效果

    废话不多说了,直接给大家贴代码了,具体代码如下所示: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <meta charset="UTF-

  • vue.js表格分页示例

    分页一般和表格一起用,分页链接作为表格的一部分,将分页链接封装成一个独立的组件,然后作为子组件嵌入到表格组件中,这样比较合理. 效果: 代码: 1.注册一个组件 js Vue.component('pagination',{ template:'#paginationTpl', replace:true, props:['cur','all','pageNum'], methods:{ //页码点击事件 btnClick: function(index){ if(index != this.cu

  • 基于Vue.js的表格分页组件

    一.Vue.js简介 1.Vue的主要特点: (1) 简洁 (2) 轻量 (3)快速 (4) 数据驱动 (5) 模块友好 (6) 组件化 (1) 简洁 下面看一段Angular的实现双向绑定的代码 // html <body ng-app="myApp"> <div ng-controller="myCtrl"> <p>{{ note }}</p> <input type="text" ng-

  • VUEJS实战之利用laypage插件实现分页(3)

    前言 在上两章的工作中,我们顺利的实现了首页的渲染,但是,只是渲染了一页数据而已.我们可能需要渲染更多的数据,这时候,我们就有必要考虑分页了. 分页有很多种方式,比如异步加载的分页方式.然而对于没有太多使用前端模版框架的朋友来说,一上来就使用这种方式,可能稍微有点难度.因此,我们这章的分页实现,是基于普通的链接分页的方式完成的. 在我们有了更多的前端框架的使用经验之后,我们可以使用更加丰富的分页方法. 事实上,我们自己构建一个分页组件也不是不可以,在移动端我都是自己的代码实现的.但是,我这里要推

  • 使用vue.js制作分页组件

    学习了vue.js一段时间,拿它来做2个小组件,练习一下. 我这边是用webpack进行打包,也算熟悉一下它的运用. 源码放在文末的 github 地址上. 首先是index.html <!DOCTYPE html> <html> <head> <title>Page</title> <style type="text/css"> * { margin: 0; padding: 0; font-family: 'O

  • Vue.js实现多条件筛选、搜索、排序及分页的表格功能

    与上篇实践教程一样,在这篇文章中,我将继续从一种常见的功能--表格入手,展示Vue.js中的一些优雅特性.同时也将对filter功能与computed属性进行对比,说明各自的适用场景,也为vue2.0版本中即将删除的部分filter功能做准备. 需求分析 还是先从需求入手,想想实现这样一个功能需要注意什么.大致流程如何.有哪些应用场景. 表格本身是一种非常常用的组件,用于展示一些复杂的数据时表现很好. 当数据比较多时,我们需要提供一些筛选条件,让用户更快列出他们关注的数据. 除了预设的一些筛选条

  • vue.js 表格分页ajax 异步加载数据

    Vue.js是一个轻巧.高性能.可组件化的MVVM库,同时拥有非常容易上手的API. 分页一般和表格一起用,分页链接作为表格的一部分,将分页链接封装成一个独立的组件,然后作为子组件嵌入到表格组件中,这样比较合理. 1.注册一个组件 js Vue.component('pagination',{ template:'#paginationTpl', replace:true, props:['cur','all','pageNum'], methods:{ //页码点击事件 btnClick: f

  • Vue.js bootstrap前端实现分页和排序

    写之前先抱怨几句.本来一心一意做.net开发的,渐渐地成了只做前端.最近项目基本都用java做后台,我们这些.net的就成了前端,不是用wpf做界面,就是用html写web页面. 深知自己前端技术不足,以前虽说用asp.net前后台都做,但是,对于前端都是用现成的js库和页面模板,对于vue.js等框架基本没有接触过. 只怪自己不会写java,最近做一个项目,负责前端,后台传来数据不分页,前端收到所有数据后自己分页.我尽是无语. 正好最近在看vue.js.这个页面就用它来实现吧.顺便总结下. 效

  • vue.js框架实现表单排序和分页效果

    本文实例为大家分享了vue.js实现表单排序和分页的具体代码,供大家参考,具体内容如下 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <script src="../lib/vue.min.js" type="text/javascript"></script> <title>表格组件</title

  • 基于Vue如何封装分页组件

    使用Vue做双向绑定的时候,可能经常会用到分页功能 接下来我们来封装一个分页组件 先定义样式文件 pagination.css ul, li { margin: 0px; padding: 0px; } .page-bar { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-se

  • vuejs2.0实现一个简单的分页示例

    最近几个项目用上vue了项目又刚好需要一个分页的功能.于是百度发现几篇文章介绍的实在方式有点复杂,没耐心看自己动手写了一个. 用js实现的分页结果如图所示: css .page-bar{ margin:40px; } ul,li{ margin: 0px; padding: 0px; } li{ list-style: none } .page-bar li:first-child>a { margin-left: 0px } .page-bar a{ border: 1px solid #dd

  • vue分页组件table-pagebar使用实例解析

    之前一直接触都是原始的前端模型,jquery+bootstrap,冗杂的dom操作,繁琐的更新绑定.接触vue后,对前端MVVM框架有了全新的认识.本文是基于webpack+vue构建,由于之前的工作主要是基于java的服务端开发工作,对前端框架和组件的理解,不够深入,借此来记录在前端框架使用和构建中的点点滴滴. 此分页组件参照于bootstrap-datatable底部分页开发完成,相关参数增加自定义功能. 最终使用展现效果图如下,数据来源于cnodejs[https://cnodejs.or

随机推荐