前言:
由于最近需要做移动端的项目
有个pc端的后台系统里面需要移一部分页面过来
而里面就有很多的表格,我就开始惯例网上先找前人栽的树,我好乘凉
然后找了一圈发现,不管是主流的移动端ui库或者网上自己写的帖子,或者uniapp的插件网站
都没有看到符合我要求的表格,然后如果要改别人的源码那我看代码都要看很久,好切有些还奇奇怪怪的bug不兼容
可能是别人使用了某些组件和插件之类的。导致我很多设置不生效。没错,我也改过别人的源码了,后来放弃了。
所以我就直接手写了一个简单的表格展示组件,配上一些我需要的功能。可以先用着了。
重点是我是原生的标签写的,不是引入一大堆的插件之类的,uniapp可以直接用。
想修改源码也简单。我都注释好了
效果图
小程序页面兼容,可以看到点击按钮之后会拿到对应行的数据
H5页面的,也是一样兼容的可能,也能拿到数据
移动端,手机预览效果,展示了数据和点击生效,滚动到底部可以触发方法,可以在这里写加载第二页表格的方法
功能:
1,数据展示:只需要往组件内传入表头和列表数据就能展示,列表数据和elementul的表格一样。表头要自己配置
2,固定表头:上滚动的时候表头会定位在上面不动
3,固定列:表头内配置属性,可以让这一列固定左边不动。目前只能固定一列
4,行底纹:就是给一行的单元格加上背景色,需要在tabledata列表中添加一个字段bgcolor就可以了
5,列底纹:同上,区别是在表头内添加一个字段bgcolor
6,单元格文字颜色改变:这是我们项目的要求,需要拿到每个单元格的数据和指标对比大小来标红。这里在父组件就可以配置
7,操作列:表头添加操作列,key给edit就会出现一个编辑和删除的列。不写就没有这一列。点击按钮会触发父组件的方法
8,滚动到底部:滚动到底部会触发父组件方法。可以做业务操作,比如拿第二页的表格
9,自动列宽:根据表格内单元格内容的宽度来动态赋值几个宽度,宽度是提前定义好的。由于表头和表体需要宽度一致,所以提前设置几个档次的固定宽度。具体使用是自动根据表体或者表头哪一个长,用哪一个的宽。保证数据的展示完全。当数据过长的时候会自动隐藏并省略号显示。这里我就没有加其他的功能了,后期可以自行更改,把这个文字加一个组件包裹,就是点击可以显示全部文字的弹框那个。
引入组件
uniapp可以直接使用,整体就引入了一个组件,uniapp带有的scroll-view组件。需要去uniapp文档内引入一下,直接插件市场下载一下就好了
代码:
组件部分:
写一个tableDiv的vue文件,当然名字随便你换
然后把代码复制进去。
<template>
<view class="wrap">
<!-- @scrolltolower:滚动到底部触发 lower-threshold:距离底部多少距离触发@scrolltolower -->
<scroll-view class="scroll-view_H" scroll-x="true" scroll-y="true" @scrolltolower='scrollBottom'
:lower-threshold='2'>
<view class="top" id="top" :style="{width:tableWidths+'px'}">
<view v-for="(h,n) in header" :key='n' :class="{'header_dyg':true,'flexs':h.flxe}"
:style="{width:h.hWidth+'px'}">
{{h.name}}
</view>
</view>
<view class="bottom" :style="{maxHeight: tableHeight+'px',width:tableWidths+'px'}">
<view class="tablebox" v-for="(t,s) in tableData" :key='s' @click="jumpDetailed(t)">
<view v-for="(h,n) in header" :key="n"
:class="{'table_dyg':true,'tdColClass':h.bgcolor,'tdRowClass':t.bgcolor,'flexs':h.flxe}"
:style="{width:h.hWidth+'px'}">
<!-- 不等于操作列就显示文字 -->
<text v-if="h.key!=='edit'" :style="{color:getColor(t,h)}">{{t[h.key]}}</text>
<!-- 操作列显示按钮,后期用插槽 -->
<view class="uni-group" v-else style="background-color: #fff;">
<button class="uni-button" size="mini" type="primary" style="margin-right: 5px;"
@click="editTable(t)">编辑</button>
<button class="uni-button" size="mini" type="warn" @click="deleteTable(t)">删除</button>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
// 表头配置:flxe(固定列),bgcolor(列底纹)
// 表体配置:bgcolor(行底纹)
export default {
props: {
data2: {
type: Array,
required: true,
default: function() {
return [];
}
},
head: {
type: Array,
required: true,
default: function() {
return [];
}
},
tableHeight: {
required: true,
type: [Number, String],
default: function() {
return 0;
}
}
},
data() {
return {
//表体
tableData: [],
//表头
header: [],
// 表格总长度
tableWidths:0
}
},
watch:{
// 监听列表
'data2'(a,b){
this.tableData = a
},
// 监听表头
'head'(a,b){
this.header = a
//计算列宽
this.tableWidth()
},
},
created() {
//#ifdef H5
this.tableData = this.data2 //列表
this.header = this.head //表头
this.tableWidth()
// #endif
},
methods: {
// 返回行数据
jumpDetailed(row){
this.$emit('rowData',row)
},
// 滚动到底部,调用父组件方法
scrollBottom(e) {
// 滚动到底部才触发,滚动到右边不触发
if (e.detail.direction == "bottom") {
this.$emit('scrollBottom')
}
},
// 颜色对比
getColor(row, col) {
let color = 'black'
// 传值给父组件,通过父组件的方法内计算判断当前单元格数据是否需要标红,然后通过回调函数,返回一个color值来渲染
this.$emit('getTextColor', row, col, val => {
color = val
})
return color
},
// 修改按钮
editTable(val) {
this.$emit('getEdit', val)
},
// 删除按钮
deleteTable(val) {
this.$emit('getDelete', val)
},
// 计算单元格宽度
tableWidth() {
let w=0 //计算表格长度
this.header.forEach((head, index) => {
let hw = head.name.length //表头单元格宽度
let dw = 0 //列表单元格宽度
this.tableData.forEach(data => {
// 如果是操作列,就直接给十个字符长度,也就是列宽自动150,不是操作列的统一看字符串长度决定宽度
let a = (head.key == 'edit' ? '1234567891' : (data[head.key]?data[head.key].toString():'1'))
let tw = (head.key == 'edit' ? 10 : a.length)
// 这里每次循环找出更大的数赋值,确保dw中是表体单元格这一列中最大宽度,根据最大宽度来判断单元格显示
if (dw < tw) {
dw = tw
}
})
// 表体单元格内容宽度小于表头内容时,以表头的宽度为主。根据表头的字符长度来区分宽度
if (dw <= hw) {
if (hw <= 3) {
head['hWidth'] = 50
} else if (hw <= 5) {
head['hWidth'] = 80
} else {
head['hWidth'] = 130
}
} else {
// 表体内容宽度大于表头内容宽度时,以表体宽度为主。根据表头的字符长度来区分宽度
if (dw <= 3) {
head['hWidth'] = 50
} else if (dw <= 5) {
head['hWidth'] = 80
} else {
head['hWidth'] = 130
}
}
w+=head['hWidth'] //叠加表格总长度
})
this.tableWidths=w //给表格赋值总长度
}
}
}
</script>
<style lang="scss">
.wrap {
width: 100%;
}
// 表头
.top {
display: flex;
position: sticky; //表头向上滚动时固定住
top: 0;
//width: 750px; //左右滚动时不会把固定的表头滚动走
z-index: 100; //滑动时表头不被覆盖
.header_dyg {
height: 40px;
text-align: center;
line-height: 40px;
border-top: 1px solid #ccc;
border-right: 1px solid #ccc;
border-bottom: 1px solid #ccc;
padding: 0 5px;
background-color: #f5f5f6;
font-size: 14px;
font-weight: bold;
color: #2b2b2b;
flex-shrink: 0;
}
// 列定位固定单元格
.flexs {
position: sticky;
left: 0;
background-color: #f5f5f6;
z-index: 10;
}
}
// 表格列表
.bottom {
//width: 750px;
.tablebox {
display: flex;
font-size: 14px;
.table_dyg {
height: 30px;
text-align: center;
line-height: 30px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
border-right: 1px solid #ccc;
border-bottom: 1px solid #ccc;
padding: 0 5px;
flex-shrink: 0;
}
// 列定位固定单元格
.flexs {
position: sticky;
left: 0;
background-color: #fff;
z-index: 10;
}
}
}
// 列的颜色
.tdColClass {
background-color: #d9edf7;
}
// 行的颜色
.tdRowClass {
background-color: #afdfe4;
}
</style>
然后父组件引入子组件使用
<template>
<view class="box">
<!-- 表格组件:参数解释:@getTextColor:调用方法判断数据后返回对比颜色,可以改变单元格文字的颜色 -->
<!-- @getEdit:点击表格中编辑按钮会触发的方法 @getDelete:点击表格中删除按钮会触发的方法 @scrollBottom:滚动到底部时触发 @rowData:获取点击行的数据-->
<!-- data:列表数据,格式和elementul表格一样,head:表头数据,格式[{name:'列名',key:'对应列表的key',bgcolor:1代表这一列添加背景色,flxe:1代表这一列固定}],tableHeight:表格表体高度 -->
<mytable @scrollBottom='scrollBottom' @getTextColor='getRedText' @getEdit='editTable'
@getDelete='deleteTable' @rowData='getRow' :data2='tableData' :head='header' :tableHeight='310'>
</mytable>
</view>
</template>
<script>
import tableDiv from './tableDiv.vue'
export default {
components: {
tableDiv
},
data() {
return {
//表体
tableData: [{
"date": "2020-09-01",
"name": "11",
"address": "上海市普陀区金沙江路 1518 弄",
"age": "18",
bgcolor: 1
}, {
"date": "2020-09-02",
"name": "22",
"address": "上海市普陀区金沙江路 1517 弄",
"age": "18"
}, {
"date": "2020-09-03",
"name": "33",
"address": "上海市普陀区金沙江路 1519 弄",
"age": "18"
}, {
"date": "2020-09-04",
"name": "44",
"address": "上海市普陀区金沙江路 1516 弄",
"age": "18"
}, {
"date": "2020-09-05",
"name": "55",
"address": "上海市普陀区金沙江路 1518 弄",
"age": "18"
}, {
"date": "2020-09-06",
"name": "66",
"address": "上海市普陀区金沙江路 1517 弄",
"age": "18"
}, {
"date": "2020-09-06",
"name": "66",
"address": "上海市普陀区金沙江路 1517 弄",
"age": "18"
}, {
"date": "2020-09-06",
"name": "66",
"address": "上海市普陀区金沙江路 1517 弄",
"age": "18"
}, {
"date": "2020-09-06",
"name": "66",
"address": "上海市普陀区金沙江路 1517 弄",
"age": "18"
}, {
"date": "2020-09-06",
"name": "66",
"address": "上海市普陀区金沙江路 1517 弄",
"age": "18"
}, {
"date": "2020-09-06",
"name": "66",
"address": "上海市普陀区金沙江路 1517 弄",
"age": "18"
}, {
"date": "2020-09-06",
"name": "66",
"address": "上海市普陀区金沙江路 1517 弄",
"age": "18"
}, {
"date": "2020-09-06",
"name": "66",
"address": "上海市普陀区金沙江路 1517 弄",
"age": "18"
}],
//表头
header: [{
name: '日期',
key: 'date',
flxe: 1 //固定的列,只能有一个列
}, {
name: '姓名',
key: 'name',
bgcolor: 1 //列底纹
}, {
name: '地址',
key: 'address'
}, {
name: '年龄',
key: 'age'
}, {
name: '年龄',
key: 'age'
}, {
name: '操作',
key: 'edit'
}]
}
},
methods: {
// 自定义事件方法,业务逻辑判断是否需要标红,然后回调给子组件颜色
getRedText(row, col, callback) {
let color = 'black'
// 判断值是否需要标红
if (row[col.key] == '22') {
color = 'red'
} else {
color = 'black'
}
// 通过回调函数返回值
callback(color);
},
// 编辑按钮
editTable(val){
console.log(val,'编辑');
uni.showToast({
title: val.date+'编辑'
})
},
// 删除按钮
deleteTable(val){
console.log(val,'删除');
uni.showToast({
title: val.date+'删除'
})
},
// 滚动到底部
scrollBottom(){
uni.showToast({
title: '滚动到底部了'
})
console.log('滚动到底部了');
}
}
}
</script>
<style lang="scss">
</style>
注意点:
由于小程序不支持直接vue那种父子组件传参形式。拿不到数据,所以这里需要用watch监听一下props变化及时更新子组件。
H5页面是可以支持vue的原本写法的。直接进来拿props就可以使用了。所以这里直接created内赋值一下就可以了