移动端复杂表格表头
- 2019 年 11 月 13 日
- 笔记
前言
最近做移动端的h5项目,要做一个可配置表头的复杂表格,网上找了很久也没什么好方法,结合网上的一些例子,在此做一了一个完整的vue版的例子。
效果
无图无真相,先上最终效果图再说 。

table.gif
方法一:iscroll 插件版
第一步:npm install
引入 iscroll
npm i iscroll --save
第二步:封装
对插件再做一层封装,封装成 iscrollTable.js 方便调用,代码如下:
// 统一使用 const iScollProbe = require('iscroll/build/iscroll-probe'); let scroller = null; let Selector = ""; export function createIScroller(selector) { Selector = selector; scroller = new iScollProbe(Selector, { preventDefault: false, // 阻止浏览器滑动默认行为 probeType: 3, //需要使用 iscroll-probe.js 才能生效 probeType : 1 滚动不繁忙的时候触发 probeType : 2 滚动时每隔一定时间触发 probeType : 3 每滚动一像素触发一次 mouseWheel: true, //是否监听鼠标滚轮事件。 scrollX: true, // 启动x轴滑动 scrollY: true, // 启动y轴滑动 // momentum: false, lockDirection: false, snap: false, //自动分割容器,用于制作走马灯效果等。Options.snap:true// 根据容器尺寸自动分割 //snapSpeed: 400, scrollbars: false, //是否显示默认滚动条 freeScroll: true, //主要在上下左右滚动都生效时使用,可以向任意方向滚动。 deceleration: 0.0001, //滚动动量减速越大越快,建议不大于 0.01,默认:0.0006 disableMouse: true, //是否关闭鼠标事件探测。如知道运行在哪个平台,可以开启它来加速。 disablePointer: true, //是否关闭指针事件探测。如知道运行在哪个平台,可以开启它来加速。 disableTouch: false, //是否关闭触摸事件探测。如知道运行在哪个平台,可以开启它来加速。 eventPassthrough: false, //使用 IScroll 的横轴滚动时,如想使用系统立轴滚动并在横轴上生效,请开启。 bounce: false //是否启用弹力动画效果,关掉可以加速 }); scroller.on('scroll', updatePosition); scroller.on('scrollEnd', updatePosition); scroller.on('beforeScrollStart', function () { scroller.refresh(); }); function updatePosition() { let frozenCols = document.querySelectorAll(selector + ' table tr td.cols'); let frozenRows = document.querySelectorAll(selector + ' table tr th.rows'); let frozenCrosses = document.querySelectorAll(selector + ' table tr th.cross'); for (let i = 0; i < frozenCols.length; i++) { frozenCols[i].style.transform = 'translate(' + -1 * this.x + 'px, 0px) translateZ(0px)'; } for (let i = 0; i < frozenRows.length; i++) { frozenRows[i].style.transform = 'translate(0px, ' + -1 * this.y + 'px) translateZ(0px)'; } for (let i = 0; i < frozenCrosses.length; i++) { frozenCrosses[i].style.transform = 'translate(' + -1 * this.x + 'px,' + -1 * this.y + 'px) translateZ(0px)'; } } return scroller; } export function refreshScroller() { if (scroller === null) { console.error("先初始化scroller"); return; } setTimeout(() => { scroller.refresh(); scroller.scrollTo(0, 0); let frozenCols = document.querySelectorAll(Selector + ' table tr td.cols'); let frozenRows = document.querySelectorAll(Selector + ' table tr th.rows'); let frozenCrosses = document.querySelectorAll(Selector + ' table tr th.cross'); for (let i = 0; i < frozenCols.length; i++) { frozenCols[i].style.transform = 'translate(0px, 0px) translateZ(0px)'; } for (let i = 0; i < frozenRows.length; i++) { frozenRows[i].style.transform = 'translate(0px, 0px) translateZ(0px)'; } for (let i = 0; i < frozenCrosses.length; i++) { frozenCrosses[i].style.transform = 'translate(0px, 0px) translateZ(0px)'; } }, 0); }
第三步:使用
引用前面的自己封装的iscrollTable.js,用到的table.vue的具体代码如下:
<template> <div class="pages-tables " id="pages-tables"> <div class="waterMask" id="watermark"></div> <div class="rolling-table meal-table" ref="tableBox" :style="{height: maxHeight + 'px'}"> <table class="table" id="table" cellpadding="0" cellspacing="0" ref="rollingTable"> <tr v-for="(x,i) in xList" :key="i"> <th class="rows " :class="{'cross': index == 0 && i == 0}" v-for="(l,index) in x" :key="index" :colspan="l.colspan" :rowspan="l.rowspan">{{l.name}}</th> </tr> <tr v-for="(l,i) in yList" :key="i + 'a'"> <template v-for="(x, xKey) in xField"> <td v-for="(ll,yKey) in l" :key="yKey" v-if="x === yKey" :class="{'cols': yKey == xField[0]}"> {{ yList[i][yKey]}} </td> </template> </tr> <tr></tr> </table> </div> </div> </template> <script> import { createIScroller, refreshScroller } from "libs/iscrollTable"; import { addWaterMarker } from "libs/common/common"; export default { data() { return { maxHeight:'100%', scroll: { scroller: null }, xList: [ [ { field_name: "statis_date", name: "第一行合并3行1列", colspan: 1, //指定单元格 横向 跨越的 列数 rowspan: 3, //指定单元格 纵向 跨越的 行数 }, { field_name: "custom_field", name: "第一行合并2列", colspan: 2, rowspan: 1, }, { field_name: "custom_field", name: "第一行合并2列", colspan: 2, rowspan: 1, }, { field_name: "custom_field", name: "第一行合并3列", colspan: 3, rowspan: 1, }, ], [ { field_name: "custom_field", name: "第二行日期", colspan: 1, //指定单元格 横向 跨越的 列数 rowspan: 1, //指定单元格 纵向 跨越的 行数 }, { field_name: "custom_field", name: "第二行日期合并2列", colspan: 2, rowspan: 1, }, { field_name: "custom_field", name: "第二行日期合并2列", colspan: 2, rowspan: 1, }, { field_name: "custom_field", name: "第二行日期合并3列", colspan: 3, rowspan: 1, }, ], [ { field_name: "area_name", name: "第三行当月新增", colspan: 1, //指定单元格 横向 跨越的 列数 rowspan: 1, //指定单元格 纵向 跨越的 行数 }, { field_name: "area_name1", name: "第三行当月新增1", colspan: 1, rowspan: 1, }, { field_name: "area_name2", name: "第三行当月新增2", colspan: 1, rowspan: 1, }, { field_name: "area_name3", name: "第三行当月新增3", colspan: 1, rowspan: 1, }, { field_name: "area_name4", name: "第三行当月新增4", colspan: 1, rowspan: 1, }, { field_name: "area_name5", name: "第三行当月新增5", colspan: 1, rowspan: 1, }, { field_name: "area_name6", name: "第三行当月新增6", colspan: 1, rowspan: 1, }, ], ], xField: ['statis_date', 'area_name', "area_name1", "area_name2", "area_name3", "area_name4", "area_name5", "area_name6",], yList: [ { area_name: "新增数据开始", area_name1: "新增数据开始1", area_name2: "新增数据开始2", area_name3: "新增数据开始3", area_name4: "新增数据开始4", area_name5: "新增数据开始5", area_name6: "新增数据开始6", statis_date: 100007, }, { area_name: "新增数据", area_name1: "新增数据1", area_name2: "新增数据2", area_name3: "新增数据3", area_name4: "新增数据4", area_name5: "新增数据5", area_name6: "新增数据6", statis_date: 201807, }, { area_name: "新增数据", area_name1: "新增数据1", area_name2: "新增数据2", area_name3: "新增数据3", area_name4: "新增数据4", area_name5: "新增数据5", area_name6: "新增数据6", statis_date: 201807, }, { area_name: "新增数据", area_name1: "新增数据1", area_name2: "新增数据2", area_name3: "新增数据3", area_name4: "新增数据4", area_name5: "新增数据5", area_name6: "新增数据6", statis_date: 201807, }, { area_name: "新增数据", area_name1: "新增数据1", area_name2: "新增数据2", area_name3: "新增数据3", area_name4: "新增数据4", area_name5: "新增数据5", area_name6: "新增数据6", statis_date: 201807, }, { area_name: "新增数据", area_name1: "新增数据1", area_name2: "新增数据2", area_name3: "新增数据3", area_name4: "新增数据4", area_name5: "新增数据5", area_name6: "新增数据6", statis_date: 201807, }, { area_name: "新增数据", area_name1: "新增数据1", area_name2: "新增数据2", area_name3: "新增数据3", area_name4: "新增数据4", area_name5: "新增数据5", area_name6: "新增数据6", statis_date: 201807, }, { area_name: "新增数据", area_name1: "新增数据1", area_name2: "新增数据2", area_name3: "新增数据3", area_name4: "新增数据4", area_name5: "新增数据5", area_name6: "新增数据6", statis_date: 201807, }, { area_name: "新增数据", area_name1: "新增数据1", area_name2: "新增数据2", area_name3: "新增数据3", area_name4: "新增数据4", area_name5: "新增数据5", area_name6: "新增数据6", statis_date: 201807, }, { area_name: "新增数据", area_name1: "新增数据1", area_name2: "新增数据2", area_name3: "新增数据3", area_name4: "新增数据4", area_name5: "新增数据5", area_name6: "新增数据6", statis_date: 201807, }, { area_name: "新增数据", area_name1: "新增数据1", area_name2: "新增数据2", area_name3: "新增数据3", area_name4: "新增数据4", area_name5: "新增数据5", area_name6: "新增数据6", statis_date: 201807, }, { area_name: "新增数据", area_name1: "新增数据1", area_name2: "新增数据2", area_name3: "新增数据3", area_name4: "新增数据4", area_name5: "新增数据5", area_name6: "新增数据6", statis_date: 201807, }, { area_name: "新增数据", area_name1: "新增数据1", area_name2: "新增数据2", area_name3: "新增数据3", area_name4: "新增数据4", area_name5: "新增数据5", area_name6: "新增数据6", statis_date: 201807, }, ] } }, mounted() { this.maxHeight = window.screen.height this.scroll.scroller = createIScroller(".meal-table"); // addWaterMarker(document.getElementById('watermark')) } } </script> <style lang="less" scoped> .pages-tables { -webkit-overflow-scrolling: touch; // ios滑动顺畅 position: relative; } .rolling-table { height: 100%; font-size: 0.28rem; color: #86939a; background-color: #fff; width: 100%; -webkit-overflow-scrolling: touch; position: relative; top: 0; overflow: hidden; } .rows { position: relative; z-index: 3; } .cross { position: relative; z-index: 5; } table td { border: 0px solid #000; font-size: 0.32rem; background: #fff; } ::-webkit-scrollbar { display: none; } .table { // border-collapse: collapse; //去掉重复的border color: #86939e; font-size: 0.32rem; border: 0px solid #000; min-height: 100%; text-align: center; td { border-bottom: 0.02rem solid #eee; white-space: nowrap; height: 0.86rem; line-height: 0.86rem; padding: 0 0.2rem; } th { color: #43484d; white-space: nowrap; height: 0.74rem; line-height: 0.74rem; padding: 0rem 0.3rem; background-color: #f3f4f6; font-weight: normal; padding-bottom: 0; padding-top: 0; border: 0.02rem solid red; } } tr{ position: relative; background-color: #fff; &:nth-of-type(odd){ td{ // background-color: pink; } } } </style>
注意点:
- table 外的盒子 .rolling-table 要设置高度,不然向上滚动失效 2.固定和行与列,即:rows、cross 的position要设为relative
最终效果就如上图。