表格封装 table
【wxml】
<view class="container">
<treeGrid
headers="{{tableHeader }}"
data="{{ row }}"
open="{{open}}"
border="{{ border }}"
maxHeight="{{maxHeight}}"
theadWid="{{theadWid}}"
defaultChangeColor="{{defaultChangeColor}}"
defaultChangeColor1="{{defaultChangeColor1}}"
bind:toTable2="getToTable2"
row_class_name="{{ row_class_name }}"
cell_class_name="{{ cell_class_name }}"
/>
</view>
【js】
//treegrid-treegrid.js
Component({
/**
* 组件的属性列表
*/
properties: {
targetId: {
type: String,
observer: function (data) {
this.init()
},
},
years: {
type: Number,
optionalTypes: [String],
value: new Date().getFullYear()
},
months: {
type: Number,
optionalTypes: [String],
value: new Date().getMonth()
},
tableHeader: {
type: Array,
value: []
},
row: {
type: Array,
optionalTypes: [Object],
value: null
},
open: {
type: Boolean,
value: true
},
maxHeight: {
type: String,
value: '90vh'
},
theadWid: {
type: String,
value: '900rpx'
},
defaultChangeColor: {
type: Boolean,
value: true
},
defaultChangeColor1: {
type: Boolean,
value: true
},
},
/**
* 组件的初始数据
*/
data: {
border: true,
outBorder: true,
row_class_name: "tr",
cell_class_name: "",
// row: {},
},
lifetimes: {
attached() {
// this.init()
}
},
onShow: function () {
onfire.un("onClickRow");
onfire.on("onClickRow", function (data) {
console.log('onfire => onClickRow ', data);
});
},
/**
* 组件的方法列表
*/
methods: {
init() {
// this.getData()
},
getToTable2(e) {
this.triggerEvent('getFinallyId', e.detail)
}
}
})
【wxss】
.table {
position: relative;
font-size: 28rpx;
background: #fff;
border-right: none;
border-radius: 8rpx;
overflow: hidden;
}
.thead {
border-bottom: none;
display: flex;
justify-content: flex-start;
border-top-right-radius: 8rpx;
border-top-left-radius: 8rpx;
overflow: visible;
background-color: #08A3EB;
color: #fff;
border: 2rpx solid #ebeef5;
box-sizing: border-box;
}
.thead .td {
padding: 20rpx 10rpx;
font-weight: bold;
display: inline-block;
text-align: left;
border-right: 1rpx solid #fff;
}
.thead .td:last-child {
border-right: none;
}
.thead-border .td {
border-right: 1rpx solid #ebeef5;
}
.thead-border .td:last-child {
border-right: none;
}
.tbody {
box-sizing: border-box;
font-size: 28rpx;
color: #666;
border: 2rpx solid #ebeef5;
border-top: none;
border-bottom-left-radius: 8rpx;
border-bottom-right-radius: 8rpx;
}
.tbody-tr {
display: flex;
border-bottom: 2rpx solid #ebeef5;
}
.tbody-tr:last-child {
border-bottom-left-radius: 8rpx;
border-bottom-right-radius: 8rpx;
}
.tbody-tr-stripe {
background: #fff;
border-bottom: none;
}
.tbody-tr-stripe:nth-child(2n) {
background: #F6F6F6;
}
.tbody-tr .td {
white-space: wrap;
padding: 20rpx 10rpx;
text-align: center;
}
.tbody-tr-border .td {
border-right: 1rpx solid #F6F6F6;
}
.tbody-tr-border .td:last-child {
border-right: none;
}
.no-data {
display: flex;
padding: 50rpx;
color: #666;
justify-content: center;
}
treeGrid
【wxml】
<!-- treegrid-treegrid.wxml -->
<view style="border: 1rpx solid #ecf1f8; border-bottom: 0">
<scroll-view scroll-x="true" scroll-left="{{scrollLeft}}">
<!-- 表格头 start -->
<view class="thead" style="width: {{theadWid}};" catchtouchmove="catchTouchMove">
<view
wx:for="{{ headers }}"
wx:key="index"
wx:for-index="attrIndex"
wx:for-item="attrItem"
class="td"
style="width:{{ headers[attrIndex].width }}; text-align:{{attrItem.align?attrItem.align:'center'}};"
>
<text class="table__head__td__text">{{attrItem.label}}</text>
</view>
</view>
<!-- 表格头 end -->
</scroll-view>
<!-- 表格体 start -->
<scroll-view
scroll-y="true"
class="tbody"
style="max-height:{{ maxHeight ? maxHeight : 'auto' }};"
bindscrolltolower="handleScrollToLower"
>
<scroll-view scroll-x="true" bindscroll="scrollX">
<block wx:if="{{ data != [] }}">
<block wx:for="{{data}}" wx:key="index">
<TreeGridLine
headers="{{ headers }}"
data="{{ item }}"
border="{{ border }}"
row_class_name="{{ row_class_name }}"
cell_class_name="{{ cell_class_name }}"
stripe="{{true}}"
open="{{open}}"
bind:getChangeId="ChangeId"
theadWid="{{theadWid}}"
defaultChangeColor="{{defaultChangeColor}}"
defaultChangeColor1="{{defaultChangeColor1}}"
showItem="{{showItem}}"
></TreeGridLine>
</block>
</block>
<!-- 列表无数据处理 -->
<block wx:else>
<view class="no-data">{{ msg }}</view>
</block>
</scroll-view>
</scroll-view>
<!-- 表格体 end -->
</view>
【js】
//treegrid-treegrid.js
Component({
/**
* 组件的属性列表
*/
properties: {
//树形表格的数据源
data: {
optionalTypes: [Array, null],
value: [],
},
//表头数据
headers: {
type: Array,
value: []
},
// 设置表格的最大显示高度, 溢出可滚动
maxHeight: {
type: String,
value: 'auto'
},
//设置表格的显示宽度
width: {
optionalTypes: [String, Number],
value: '100%'
},
// 单元格的宽度
tdWidth: {
type: Number,
value: 35
},
// 固定表头 thead达到Header的位置时就应该被fixed了
offsetTop: {
type: Number,
value: 150
},
// 是否带有纵向边框
border: {
type: Boolean,
value: false
},
msg: {
type: String,
value: '暂无数据~'
},
header_row_class_name: {
type: String,
value: "header-row-class-name"
},
row_class_name: {
type: String,
value: "row-class-name"
},
cell_class_name: {
type: String,
value: "cell-class-name"
},
open: {
type: Boolean,
value: true
},
theadWid: {
type: String,
value: '900rpx'
},
defaultChangeColor: {
type: Boolean,
value: true
},
defaultChangeColor1: {
type: Boolean,
value: true
},
},
/**
* 组件的初始数据
*/
data: {
scrolWidth: '100%',
scrollLeft: 0,
showItem: {
showCol1: '',
showId: '',
},
},
lifetimes: {
attached() {
// console.log(this.data.data)
}
},
//数据监听器
observers: {
'headers,cell_class_name': function (headers, cell_class_name) {
const reducer = (accumulator, currentValue) => {
return accumulator + Number(currentValue.width)
};
const scrolWidth = headers.reduce(reducer, 0)
this.setData({
scrolWidth: scrolWidth
})
}
},
/**
* 组件的方法列表
*/
methods: {
ChangeId(val) {
this.setData({
'showItem.showCol1': val.detail.col1,
'showItem.showId': val.detail.dataId
})
this.triggerEvent('toTable2', val.detail)
},
//禁止滚动
catchTouchMove(e) {
return false;
},
// 触底事件
handleScrollToLower(e) {
if (e.detail.direction == 'bottom') {
console.log('scroll-view触底事件在这里处理加载下一页数据')
}
},
//监听左右滚动
scrollX(e) {
this.setData({
scrollLeft: e.detail.scrollLeft,
});
},
}
})
【wxss】
.table {
position: relative;
font-size: 20rpx;
background: #fff;
border-right: none;
border-radius: 8rpx;
overflow: hidden;
}
.thead {
font-weight: 700;
position: sticky;
top: 0rpx;
z-index: 100;
/* width: 1500rpx; */
height: 60rpx;
line-height: 60rpx;
display: flex;
white-space: nowrap;
font-size: 20rpx;
color: #fff;
border-bottom: 2rpx solid #ecf1f8;
background-color: #08A3EB;
}
.thead .td {
/* padding: 20rpx 5rpx;
font-weight: bold;
display: inline-block;
text-align: left; */
position: relative;
display: flex;
justify-content: center;
align-items: center;
background-color: #08A3EB;
box-sizing: border-box;
overflow: hidden;
white-space: nowrap;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
/* border-right: 1rpx solid #fff; */
}
.thead .td:last-child {
border-right: none;
}
.thead .td:nth-child(1) {
position: sticky;
z-index: 101;
left: 0rpx;
/* justify-content: left; */
}
.table__head__td__text {
display: inline;
}
.tbody {
/* box-sizing: border-box;
font-size: 28rpx;
color: #666;
border: 2rpx solid #ebeef5;
border-top: none;
border-bottom-left-radius: 8rpx;
border-bottom-right-radius: 8rpx;
letter-spacing: -1rpx; */
display: block;
position: relative;
overflow: scroll;
/* width: 100%; */
height: 100vh;
}
.tbody-tr {
display: flex;
/* border-bottom: 1px solid #ebeef5; */
}
.tbody-tr:last-child {
border-bottom-left-radius: 8rpx;
border-bottom-right-radius: 8rpx;
}
.tbody-tr-stripe {
background: #fff;
/* border-bottom: none; */
}
.tbody-tr-stripe:nth-child(2n) {
background: #F6F6F6;
}
/* .tbody-tr .td {
white-space: wrap;
padding: 20rpx 10rpx;
text-align: center;
} */
.tbody-tr-border .td {
border-right: 1rpx solid #F6F6F6;
}
.tbody-tr-border .td:last-child {
border-right: none;
}
.no-data {
display: flex;
padding: 50rpx;
color: #666;
justify-content: center;
}
TreeGridLine
【wxml】
<view class="container" style="width: {{theadWid}};">
<view
class="tbody-tr {{ stripe ? 'tbody-tr-stripe' : '' }} {{ border ? 'tbody-tr-border' : ''}} {{data.col1==showItem.showCol1&&data.dataId==showItem.showId?'showActive':''}}"
data-it="{{data}}"
bindtap="onRowClick"
data-level="{{data.level}}"
>
<view
wx:for-item="head"
wx:key="index"
wx:for="{{ headers }}"
class="td"
style="width:{{ headers[index].width }};color:{{ headers[index].color }};font-size:13px;"
>
<block wx:if="{{data[head['prop']]==data.col1}}">
<image
wx:if="{{data.nodes.length > 0 && index == 0}}"
src="{{open ? 'https://cofco001-1308084433.cos.ap-beijing.myqcloud.com/image/miniprogram/Icon/show_more.png' : 'https://cofco001-1308084433.cos.ap-beijing.myqcloud.com/image/miniprogram/Icon/show_less.png'}}"
class="indent{{data.level}} tbody-tr_img"
catchtap="toggle"
></image>
<image
style="visibility: hidden"
wx:if="{{data.nodes.length == 0 && index == 0}}"
src="../../image/page_turning_right.png"
class="indent{{data.level}} tbody-tr_img"
bindtap="bubbling"
></image>
<!-- defaultChangeColor:首列没有点击不变色 -->
<view
wx:if="{{data.col4 && data.col5&&defaultChangeColor||data.col1&&data.col2 && data.col3 && defaultChangeColor1 }}"
style="color: #3583d8; display: inline-block; flex: 1"
>
{{data[head["prop"]]}}
</view>
<view wx:else style="display: inline-block; flex: 1">{{data[head["prop"]]}}</view>
</block>
<block wx:else>
<view style="text-align: center">{{data[head["prop"]]}}</view>
</block>
</view>
</view>
<view hidden="{{ !open }}">
<TreeGridLine
wx:for="{{ data.nodes }}"
wx:for-item="it"
wx:key="idx"
wx:for-index="idx"
headers="{{ headers }}"
data="{{ it }}"
stripe="{{ stripe }}"
border="{{ border }}"
row_class_name="{{ row_class_name }}"
cell_class_name="{{ cell_class_name }}"
open="{{open}}"
data-it="{{it}}"
data-it1="{{it}}"
data-level="{{data.level}}"
defaultChangeColor="{{defaultChangeColor}}"
defaultChangeColor1="{{defaultChangeColor1}}"
showItem="{{showItem}}"
bindtap="onRowClick"
></TreeGridLine>
</view>
</view>
【js】
Component({
/**
* 组件的属性列表
*/
properties: {
data: {
type: Object,
value: {},
},
headers: {
type: Array,
value: [],
},
// 是否带有纵向边框
border: {
type: Boolean,
value: false
},
row_class_name: {
type: String,
value: "row-class-name"
},
cell_class_name: {
type: String,
value: "cell-class-name"
},
// 默认树的状态(展开true 或 折叠false)
open: {
type: Boolean,
value: true
},
stripe: {
type: Boolean,
value: true,
},
theadWid: {
type: String,
value: '900rpx'
},
defaultChangeColor: {
type: Boolean,
value: true
},
defaultChangeColor1: {
type: Boolean,
value: true
},
showItem: {
type: Object,
value: {}
}
},
/**
* 组件的初始数据
*/
data: {
// open: true,
// url: ''
},
lifetimes: {
attached() {
// console.log('子 this.open = ', this.data)
}
},
/**
* 组件的方法列表
*/
methods: {
//catchtap 阻止冒泡事件 只展开或折叠 不触发行点击事件
toggle: function (e) {
// console.log('toggle e = ', e, this.data.open)
this.setData({
open: !this.data.open
})
console.log(88);
},
//catchtap 阻止冒泡事件 只展开或折叠 不触发行点击事件
bubbling: function () {
return;
},
onRowClick(e) {
const { it, level } = e.currentTarget.dataset
// console.log(it.level, level, 'it.level == level')
if (it.level == level) {
this.triggerEvent('getChangeId', it, {
bubbles: true,
composed: true,
})
// this.triggerEvent('getFinallyId', it)
}
// else {
// this.triggerEvent('getChangeId', it)
// }
// let dataId = e.currentTarget.dataset.it
// this.triggerEvent('getChangeId', dataId)
},
// ChangeId(val) {
// console.log(val, "bbb!!!!!!")
// this.triggerEvent('toTable2', val.detail)
// },
}
})
【wxss】
.container {
--indent-width: 14rpx;
/* width: 1500rpx; */
}
.indent2 {
margin-left: calc(var(--indent-width));
}
.indent3 {
margin-left: calc(var(--indent-width)*2);
}
.indent4 {
margin-left: calc(var(--indent-width)*3);
}
.indent5 {
margin-left: calc(var(--indent-width)*4);
}
.indent6 {
margin-left: calc(var(--indent-width)*5);
}
.indent7 {
margin-left: calc(var(--indent-width)*6);
}
.indent8 {
margin-left: calc(var(--indent-width)*7);
}
.indent9 {
margin-left: calc(var(--indent-width)*8);
}
.tbody {
box-sizing: border-box;
font-size: 28rpx;
color: #666;
border: 1px solid #ebeef5;
border-top: none;
border-bottom-left-radius: 8rpx;
border-bottom-right-radius: 8rpx;
}
.tbody-tr {
display: flex;
border-bottom: 1px solid #ebeef5;
}
.tbody-tr .tbody-tr_img {
width: 40rpx;
height: 40rpx;
vertical-align: middle;
}
.tbody-tr:last-child {
border-bottom-left-radius: 8rpx;
border-bottom-right-radius: 8rpx;
}
.tbody-tr-stripe {
background: #fff;
/* border-bottom: none; */
display: flex;
align-items: center;
/* justify-content: center; */
}
.tbody-tr-stripe:nth-child(2n) {
background: #F6F6F6;
}
.tbody-tr .td {
white-space: wrap;
/* padding: 20rpx 7rpx; */
}
.tbody-tr-border .td {
/* border: 0rpx solid #dbe2e9; */
/* height: 80rpx;
line-height: 80rpx; */
padding: 20rpx 0rpx;
}
.tbody-tr-border .td:last-child {
border-right: none;
}
.tbody-tr-border .td:nth-child(1) {
position: sticky;
z-index: 101;
left: 0;
background-color: #fff;
display: flex;
}
.showActive {
background-color: #F5F5F5;
}
.showActive .td:nth-child(1) {
background-color: #F5F5F5;
}
页面调用
<table
tableHeader="{{tableHeader}}"
row="{{row}}"
open="{{false}}"
bind:getFinallyId="finallyId"
maxHeight="{{'28vh'}}"
defaultChangeColor="{{true}}"
></table>
数据
tableHeader: [
{ label: "名称", prop: "col1", width: "28%", color: "#000", align: "left" },
{ label: "当前", prop: "col2", width: "17%", color: "#000" },
{ label: "同比", prop: "col3", width: "17%", color: "#000" },
{ label: "去年", prop: "col4", width: "17%", color: "#000" },
{ label: "全年", prop: "col5", width: "17%", color: "#000" },
{ label: "预算", prop: "col6", width: "17%", color: "#000" },
],
row: [
{
level: 1,
id: "83671",
dataId: "yyzsr",
years: "2025",
months: "10",
col1: "一、收入",
col2: "439",
col3: "27%",
col4: "4830",
col5: "5233",
col6: "-8%",
nodes: [
{
level: 2,
id: "83677",
delFlag: "0",
dataId: "zljt_yxfx_syqkb_yyzsr_qz_yysr",
years: "2025",
months: "10",
col1: " 其中:营业收入",
col2: "432",
col3: "25%",
col4: "4753",
col5: "5233",
col6: "-9%",
col7: "5798",
col8: "82%",
col9: "346.42",
},
],
},
{
level: 1,
id: "83621",
dataId: "yyzcb",
years: "2025",
months: "10",
col1: "二、总成本",
col2: "442",
col3: "35%",
col4: "4740",
col5: "5158",
col6: "-8%",
},
],