微信小程序自定义DataGrid

微信小程序自定义DataGrid数据网格组件


前言

提示:在微信小程序官方帮助文档并无表格控件相关内容, 需要自己进使用基本表单组件进行拼装:

这是写两种方式:一、采用纯CSS+View实现, 二、采用Component方式引用


一、实现方式(1)CSS+View?

实现效果:
在这里插入图片描述
实现步骤下以下

1、添加CSS

在根目录下的 app.wxss文件增加以下样式

/* 表格开始 */

.table {
  border: 0px solid darkgray;
  font-size: 12px;
  margin:10px 0;
 }
 .tr {
  display: flex;
  width: 100%;
  justify-content: center;
  height: 3rem;
  align-items: center;  
 }
 .tr:nth-child(2n+1){
  background: #E6F3F9;
 }
 .tr .sel{
  background: #29bbff;
 }
 .td,
 .th {
  width:60%;
  justify-content: center;
  text-align: center;
 }
 .th {
  font-weight: bold;
 }

 .th:first-child,.td:first-child{
  width: 15%;
 }
 .th:last-child,.td:last-child{
   width:20%;
 }
/* 表格结束 */

2、WXML文件中使用

在全局app.wxcc中已引入的样式, 则在Page页面可直接使用

  <view class="table">
          <view class="tr">
            <view class="th" style="width:10%"></view>
            <view class="th">分数</view>
            <view class="th" style="width:40%">排名</view>
            <view class="th" style="width:30%">同分人数</view>
          </view>
          <block wx:for="{{MyGrades.SameScoreData}}" wx:key="index">
            <view class="tr" data-item="{{item}}">
              <view class="td left" style="width:10%">{{item.GK_YEAR}}</view>
              <view class="td left">{{item.OMOP_MARK}}</view>
              <view class="td left" style="width:40%">{{item.OMOP_SORT}}</view>
              <view class="td left" style="width:30%">{{item.OMOP_NUMS}}</view>
            </view>
          </block>
        </view>

二、使用组件注册方式

实现效果如下截图
在这里插入图片描述
第二种方案优于第一种, 可以进行数据的筛选, 点击行可展示全部等

实现步骤如下

1、创建组件文件及文件夹

在component下创建文件夹DataGrid, 再创建Index Pgae页面 WXML,JS,和CSS文件如下:

a)wxml文件

<scroll-view bindscroll="rightScroll" scroll-x="false" style="width:100%;" class="table table-border">
    <!-- 表格头 start -->
    <view class="thead {{ border ? 'thead-border' : ''}} header-row-class-name" style="width:{{ scrolWidth }}rpx;">
        <view wx:for="{{ headers }}" wx:key="tthis" class="td" style="width:{{ item.width }}{{widthType?widthType:'rpx;'}}">
            <picker wx:if="{{item.showpicker}}" range="{{item.pickers}}" bindchange="bindPickerItemChange" data-item="{{item.pickers}}" data-key="{{item.prop}}" range-key="_picker">
                {{ item.label }}
                <image style="height: 12px;width:12px;" src="{{item.filterImg||'/image/iocn/filter.png'}}"></image>
            </picker>
            <label wx:else> {{ item.label }}</label>
        </view>
    </view>
    <!-- 表格头 end -->

    <!-- 表格体 start -->
    <scroll-view scroll-y="false" class="tbody" style="width:{{ scrolWidth }}rpx; height:{{ height ? height : 'auto' }};">
        <block wx:for-item="it" wx:for="{{ data }}" wx:key="thisp" wx:for-index="idx" wx:if="{{data.length > 0 &&it.showRow!=-1}}">
            <view wx:if="{{ShowRowIndex!=idx||!ShowRowDetail}}" class="tbody-tr {{ stripe ? 'tbody-tr-stripe' : '' }} {{ border ? 'tbody-tr-border' : ''}} row-class-name">
                <view wx:for-item="head" wx:for="{{ headers }}" wx:key="thischild" class="td cell-class-name" data-it="{{it}}" data-row="{{idx}}" data-column="{{index+1}}" style="width:{{ headers[index].width}}rpx;color:{{ headers[index].color }};display: flex;align-items: center;justify-content: center;{{headers[index].style }}" bindtap="onRowClick">
                    {{head["prop"]=="index"?idx+1:it[head["prop"]]}}
                </view>
            </view>
            <!-- <view>dasdf</view>  这里可扩展为同微信统计一样的功能  -->
            <view style="background-color: black;color:white;position: relative;width:100%;left: {{ShowRowLeft-150}}px;padding-left: 150px;" wx:if="{{ShowRowIndex==idx&&ShowRowDetail}}" data-it="{{it}}" bindtap="onCloseShowRow">
                <view style="margin:5px;" wx:for="{{ headers }}" wx:key="kkkk">
                    {{ item.label }} : {{item["prop"]=="index"?idx+1:it[item["prop_L"]]||it[item["prop"]]}}
                </view>
            </view>
        </block>
        <!-- 列表无数据处理 -->
        <block wx:if="{{ data.length === 0 }}">
            <view class="no-data">{{ msg }}</view>
        </block>
    </scroll-view>
    <!-- 表格体 end -->
</scroll-view>

b) js文件



Component({
    externalClasses: ['header-row-class-name', 'row-class-name', 'cell-class-name'],

    /**
     * 组件样式隔离
     */
    options: {
        styleIsolation: "isolated",
        multipleSlots: true // 支持多个slot
    },

    /**
     * 组件的属性列表
     */
    properties: {
        data: {
            type: Array,
            value: []
        },
        headers: {
            type: Array,
            value: []
        },
        // table的高度, 溢出可滚动
        height: {
            type: String,
            value: 'auto'
        },
        width: {
            type: Number || String,
            value: '100%'
        },
        // 单元格的宽度
        tdWidth: {
            type: Number,
            value: 35
        },
        outBorder: {
            type: Boolean,
            value: true
        }
        ,
        // 固定表头 thead达到Header的位置时就应该被fixed了
        offsetTop: {
            type: Number,
            value: 150
        },
        // 斑马条
        stripe: {
            type: Boolean,
            value: false
        },
        // 是否带有纵向边框
        border: {
            type: Boolean,
            value: false
        },
        msg: {
            type: String,
            value: '暂无数据~'
        },
        ShowRowDetail: {
            type: Boolean,
            value: true
        }
    },

    /**
     * 组件的初始数据
     */
    data: {
        scrolWidth: '100%',
        OpenShowRow: false,
        ShowRowLeft: 0
    },

    /**
     * 组件的监听属性
     */
    observers: {
        // 在 numberA 或者 numberB 被设置时,执行这个函数
        'headers': function tableHeader(_headers) {
            var reducer = function reducer(accumulator, currentValue) {
                return accumulator + Number(currentValue.width);
            };
            var scrolWidth = _headers.reduce(reducer, 0);

            this.setData({
                scrolWidth: scrolWidth
            });
        },
        'data': function row(_row) {
            this.data.headers.forEach(h => {
                let cn = []
                if (h.showpicker) {
                    let pic = { '_picker': '-ALL-' };
                    cn.push(pic);
                    this.data.data.forEach(r => {
                        if (h.showpicker) {
                            let exirow = false;
                            cn.forEach(c => {
                                if (c['_picker'] == r[h.prop]) {
                                    exirow = true;
                                }
                            })
                            if (!exirow) {
                                r._picker = r[h.prop];
                                cn.push({ '_picker': r[h.prop] });
                            }
                        }
                    })
                    h.pickers = cn;
                }
            })
            this.setData({
                ShowRowIndex: -1,
                headers: this.data.headers,
            })
        },
        'HideRowDetail': function setRowIndex(ri) {
            if (ri) {
                this.setData({
                    ShowRowIndex: -1
                })
            }
        }
    },

    /**
     * 组件的方法列表
     */
    methods: {
        onRowClick: function onRowClick(e) {
            let that = this;
            that.setData({
                ShowRowIndex: e.currentTarget.dataset.row
            })
            let aares = e.currentTarget.dataset;
            this.triggerEvent('rowClick', aares);
        },
        onCloseShowRow() {
            this.setData({
                ShowRowIndex: -1
            })
        },
        rightScroll(e) {
            this.setData({
                ShowRowLeft: e.detail.scrollLeft
            })
        },
        bindPickerItemChange(e) {
            let item = e.currentTarget.dataset.item;
            let key = e.currentTarget.dataset.key;
            let rows = [];
            let value = item[e.detail.value]['_picker'];
            rows = this.data.data;
            let isfilter = false;
            //点击第一个ALL显示所有
            if (e.detail.value == 0) {
                rows.forEach(r => {
                    r.showRow = 1
                })
            }
            else {
                isfilter = true;
                rows.forEach(r => {
                    if (r[key] == value) {
                        r.showRow = 1
                    }
                    else {
                        r.showRow = -1
                    }
                })
            }
            //更改过滤器图标为选中 橙色
            this.data.headers.forEach(h => {
                if (h.showpicker) {
                    h.filterImg = '/image/iocn/filter.png'
                }
                if (isfilter) {
                    if (h.prop == key) {
                        h.filterImg = '/image/iocn/filtered.png'
                    }
                }
            });
            this.setData(
                {
                    data: rows,
                    headers: this.data.headers
                }
            )

        },
        onHeaderOrder: function onHeaderOrder_(e) {
            let item = e.currentTarget.dataset.item;
            this.data.headers.forEach(h => {
                h.filterImg = null;
                if (h.Order && h.prop == item.prop) {
                    if (h.orderImg == '/image/iocn/order.png' || h.orderImg == '/image/iocn/order_A.png' || !h.orderImg) {
                        h.orderImg = '/image/iocn/order_D.png'
                        h.orderdirection = 'Desc'
                    }
                    else {
                        h.orderImg = '/image/iocn/order_A.png'
                        h.orderdirection = 'ASC'
                    }
                    item = h;
                }
                else {
                    h.orderImg = '/image/iocn/order.png'
                    h.orderdirection = 'ASC'
                }

            });
            this.setData({ headers: this.data.headers })
            this.triggerEvent('HeaderOrder', item);
        },
        onReload: function onReload_(e) {
            this.data.headers.forEach(h => {
                h.filterImg = null;
                h.orderImg = null;
                h.orderdirection = null;
            });
            this.setData({ headers: this.data.headers })
            let item = e.currentTarget.dataset.item;
            this.triggerEvent('Reload', item);
        }
    }
});

c) css文件

.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;
  color: #909399;
  border: 1px solid #ebeef5;
  box-sizing: border-box;
}
.thead .td {
  padding: 20rpx 10rpx;
  font-weight: bold;
  display: inline-block;   
  text-align: center;
  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;
}
/* .tr{
  display: flex;
  white-space:nowrap; 
} */
.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: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: #eefffb;
}
.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;
}

.triangle {
    width: 0;
    height: 0;
    border-left: 10px solid transparent;
    border-right: 10px solid transparent;
    border-top: 10px solid red;
   
  }

2、引用与使用

a) 在app.json中注册 刚创建的DtatGrid组件


"usingComponents": {
        "table-view": "/component/DataGrid",
      }

在页面中使用

  1. 使用页面 wxml
<table-view 
    headers="{{tableGrid.tableHeader}}" 
    data="{{ tableGrid.row }}" 
    stripe="{{ tableGrid.stripe }}"
    border="{{ tableGrid.border }}"
    msg="{{tableGrid.msg}}"
    height="400px"
    rowDetaile="false"
   bind:rowClick="thisonRowClick"
></table-view>
</view>
  1. 使用页面 JS
// pages/test/table.js
Page({

    /**
     * 页面的初始数据
     */
    data: {
        tableGrid: {
            tableHeader: [
                {
                    prop: 'datetime',
                    width: 150,
                    label: '日期',
                    color: '#55C355'
                },
                {
                    prop: 'sign_in_time',
                    width: 152,
                    label: '上班时间'
                },
                {
                    prop: 'sign_out_time',
                    width: 152,
                    label: '下班时间'
                },
                {
                    prop: 'work_hour',
                    width: 110,
                    label: '工时'
                },
                {
                    prop: 'status',
                    width: 210,
                    label: '状态'
                }
            ],
            stripe: true,
            border: false,
            outBorder: true,
            row: [
                {
                    "id": 1,
                    "status": '正常',
                    "datetime": "04-01",
                    "sign_in_time": '09:30:00',
                    "sign_out_time": '18:30:00',
                    "work_hour": 8,
                }, {
                    "id": 2,
                    "status": '迟到',
                    "datetime": "04-02",
                    "sign_in_time": '10:30:00',
                    "sign_out_time": '18:30:00',
                    "work_hour": 7,
                }, {
                    "id": 29,
                    "status": '正常',
                    "datetime": "04-03",
                    "sign_in_time": '09:30:00',
                    "sign_out_time": '18:30:00',
                    "work_hour": 8,
                }, {
                    "id": 318,
                    "status": '休息日',
                    "datetime": "04-04",
                    "sign_in_time": '',
                    "sign_out_time": '',
                    "work_hour": '',
                },
                {
                    "id": 1,
                    "status": '正常',
                    "datetime": "04-01",
                    "sign_in_time": '09:30:00',
                    "sign_out_time": '18:30:00',
                    "work_hour": 8,
                }, {
                    "id": 2,
                    "status": '迟到',
                    "datetime": "04-02",
                    "sign_in_time": '10:30:00',
                    "sign_out_time": '18:30:00',
                    "work_hour": 7,
                }, {
                    "id": 29,
                    "status": '正常',
                    "datetime": "04-03",
                    "sign_in_time": '09:30:00',
                    "sign_out_time": '18:30:00',
                    "work_hour": 8,
                }, {
                    "id": 318,
                    "status": '休息日',
                    "datetime": "04-04",
                    "sign_in_time": '',
                    "sign_out_time": '',
                    "work_hour": '',
                },
                {
                    "id": 1,
                    "status": '正常',
                    "datetime": "04-01",
                    "sign_in_time": '09:30:00',
                    "sign_out_time": '18:30:00',
                    "work_hour": 8,
                }, {
                    "id": 2,
                    "status": '迟到',
                    "datetime": "04-02",
                    "sign_in_time": '10:30:00',
                    "sign_out_time": '18:30:00',
                    "work_hour": 7,
                }, {
                    "id": 29,
                    "status": '正常',
                    "datetime": "04-03",
                    "sign_in_time": '09:30:00',
                    "sign_out_time": '18:30:00',
                    "work_hour": 8,
                }, {
                    "id": 318,
                    "status": '休息日',
                    "datetime": "04-04",
                    "sign_in_time": '',
                    "sign_out_time": '',
                    "work_hour": '',
                },
                {
                    "id": 1,
                    "status": '正常',
                    "datetime": "04-01",
                    "sign_in_time": '09:30:00',
                    "sign_out_time": '18:30:00',
                    "work_hour": 8,
                }, {
                    "id": 2,
                    "status": '迟到',
                    "datetime": "04-02",
                    "sign_in_time": '10:30:00',
                    "sign_out_time": '18:30:00',
                    "work_hour": 7,
                }, {
                    "id": 29,
                    "status": '正常',
                    "datetime": "04-03",
                    "sign_in_time": '09:30:00',
                    "sign_out_time": '18:30:00',
                    "work_hour": 8,
                }, {
                    "id": 318,
                    "status": '休息日',
                    "datetime": "04-04",
                    "sign_in_time": '',
                    "sign_out_time": '',
                    "work_hour": '',
                },
                {
                    "id": 1,
                    "status": '正常',
                    "datetime": "04-01",
                    "sign_in_time": '09:30:00',
                    "sign_out_time": '18:30:00',
                    "work_hour": 8,
                }, {
                    "id": 2,
                    "status": '迟到',
                    "datetime": "04-02",
                    "sign_in_time": '10:30:00',
                    "sign_out_time": '18:30:00',
                    "work_hour": 7,
                }, {
                    "id": 29,
                    "status": '正常',
                    "datetime": "04-03",
                    "sign_in_time": '09:30:00',
                    "sign_out_time": '18:30:00',
                    "work_hour": 8,
                }, {
                    "id": 318,
                    "status": '休息日',
                    "datetime": "04-04",
                    "sign_in_time": '',
                    "sign_out_time": '',
                    "work_hour": '',
                }, {
                    "id": 319,
                    "status": '正常',
                    "datetime": "04-05",
                    "sign_in_time": '09:30:00',
                    "sign_out_time": '18:30:00',
                    "work_hour": 8,
                }
            ],
            msg: '暂无数据'
        },
        ShowRowIndex:2
    },
    thisonRowClick: function (e,that) {
    },

    /**
     * 生命周期函数--监听页面加载
     */
    onLoad(options) {
    },

})

增加排序效果
在这里插入图片描述

以 上是个人开发中实现, 如各位码友有更好的实现方式请留言和拍砖。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值