微信小程序表格组件(锁头、锁列、样式、字体颜色等)
因为微信不支持table组件,所以通过scroll-view组件来实现一个表格组件。
功能实现:
- 提供了三种表格样式
- 可以锁头
- 可以锁第一列
- 自定义表头宽度、高度、背景色、文字大小、字体颜色、是否加粗
- 自定义表格宽度、高度
- 表格内容的数字是否需要与0判断进行颜色区别(本人做金融,所以需要红绿色)
增加功能(2024年3月19日)
- 可以修改表格最大高度(从父组件中动态传值,防止表格高度过小下滑时表头无法固定)
- 表格数字可以转换单位
- 表格数字可以转成百分百形式
增加功能(2024年3月21日)
- 新增表格模式:可以横向或者纵向,用table_sl控制。具体前请见案例
- 新增表头点击事件(head_click)和表格行(row_click)点击事件
结果展示
注:该demo案例是table_sl为fir模式下
注:该demo案例是table_sl为sec模式下
组件代码:
wxml页面:
<!-- 小程序表格
参数说明:
totalHeight:表格最大高度(默认为250px)
table_sl:表格模式(提供两种:'fir'表示横向, 'sec'表示纵向)
line_sl:表格线条样式(提供三种:'all', 'fir', 'sec')
headList: 表头数据
table:表格数据
lock_header:是否锁表头(默认为true,锁头)
lock_first:是否锁第一列(默认为true,锁第一列)
table_width:表格宽度(默认为180rpx)
header_height:表头高度(默认为60rpx)
header_bg:表头背景色(默认为#fff,白色)
header_text_size:表头文字大小(默认为32rpx)
header_bold:表头文字是否加粗(默认为true,加粗)
header_text_color:表头文字颜色(默认为#111,黑色)
cell_height:表格内容高度(默认为100rpx)
is_unit:该列(table_sl='sec'时为行)表格内容(针对数字型|字符串型数字)是否需要转换成"亿,万"单位式
is_per:该列(table_sl='sec'时为行)表格内容(针对数字型|字符串型数字)是否需要转成百分比形式(默认保留两位小数)
注:如果表格内容中的数字不需要做不同标记,则使用字符串类型数字即可(颜色有:默认大于0为red,等于0为gray,小于0为green)
head_click:表头点击事件
row_click:表格行点击事件
-->
<view class="table_box" style="border-width: {{line_sl=='all'?'':(line_sl=='fir'?'0 0 1rpx 0':'0')}};">
<scroll-view class="table_scroll" scroll-y scroll-x="{{lock_first}}" style="max-height:{{totalHeight}}px;" bindscrolltolower="handleScrollToLower">
<view class="table_header" style="{{lock_header?'position: sticky': ''}};{{header_bold?'font-weight: bold': ''}}">
<view class="table_header_item" style="border-width: {{line_sl=='all'?'':'0'}};{{header_style}}" wx:for="{{headList}}" wx:key="index">
<text wx:if="{{table_sl=='fir'}}" style="width: {{table_width[index]?table_width[index]:'180'}}rpx" data-index="{{index}}" data-value="{{item}}" bind:tap="header_click">{{item}}</text>
<block wx:else>
<text wx:if="{{index==0}}" style="width: {{table_width[index]?table_width[index]:'180'}}rpx">{{item}}</text>
<view wx:else class="alignment" style="width: {{table_width[index]?table_width[index]:'180'}}rpx" data-index="{{index}}" data-value="{{item}}" bind:tap="header_click">
<view>
<view>{{item[1]}}</view>
<view style="font-size: 18rpx; color: gray; font-weight: normal;">{{item[0]}}</view>
</view>
<text class="arrow" style="margin-left: 10rpx;" />
</view>
</block>
</view>
</view>
<block wx:if="{{table_sl=='fir'}}">
<view class="table_content_line" wx:for="{{table}}" wx:key="rowIndex" wx:for-index="rowIndex" wx:for-item="rowItem">
<block wx:for="{{rowItem}}" wx:key="index">
<view wx:if="{{index==0}}" class="table_content_line_item" style="border-width: {{line_sl=='fir'?'0 0 1rpx 0':(line_sl=='sec'?'0':'')}};height: {{cell_height}}rpx">
<view style="width: {{table_width[index]?table_width[index]:'180'}}rpx;" data-index="{{rowIndex}}" data-value="{{rowItem}}" bind:tap="row_click">
<view>{{item[1]}}</view>
<view>{{item[0]}}</view>
</view>
</view>
<view wx:else class="table_content_line_item" style="border-width: {{line_sl=='fir'?'0 0 1rpx 0':(line_sl=='sec'?'0':'')}};height: {{cell_height}}rpx" bind:tap="row_click" data-index="{{rowIndex}}" data-value="{{rowItem}}">
<text style="width: {{table_width[index]?table_width[index]:'180'}}rpx; color: {{uts.judgeNumber(item)?(item>0?'red':(item==0?'gray':'green')):''}}">{{is_unit[index]?uts.change_unit(item):(is_per[index]?uts.change_per(item):item)}}</text>
</view>
</block>
</view>
</block>
<view wx:else>
<view class="table_content_line" wx:for="{{table}}" wx:key="rowIndex" wx:for-index="rowIndex" wx:for-item="rowItem">
<block wx:for="{{rowItem}}" wx:key="index">
<view class="table_content_line_item" style="border-width: {{line_sl=='fir'?'0 0 1rpx 0':(line_sl=='sec'?'0':'')}};height: {{cell_height}}rpx" data-index="{{rowIndex}}" data-value="{{rowItem}}" bind:tap="row_click">
<text style="width: {{table_width[rowIndex]?table_width[rowIndex]:'180'}}rpx; color: {{uts.judgeNumber(item)?(item>0?'red':(item==0?'gray':'green')):''}}">{{is_unit[rowIndex]?uts.change_unit(item):(is_per[rowIndex]?uts.change_per(item):item)}}</text>
</view>
</block>
</view>
</view>
</scroll-view>
</view>
<!-- wxs脚本 -->
<wxs module="uts">
/**
* 给数字增加单位
* @param {String|Number} item 需要转换的数字或者字符类型数字
*/
var change_unit = function (item) {
var num = parseFloat(item)
if (num) { // 先判断是否可以转化成数字
if (num >= 100000000 || -num >= 100000000) { // 因为在wxs脚本中不能使用abs函数,所以利用这个方法处理负数形式
return (num / 100000000).toFixed(2) + ' 亿';
} else if (num >= 10000 || -num >= 10000) {
return (num / 10000).toFixed(2) + ' 万';
}
return num
}
return item;
}
/**
* 给数字转成百分比形式
* @param {String|Number} item 需要转换的数字或者字符类型数字
*/
var change_per = function (item) {
var num = parseFloat(item)
if (num) {
return (num * 100).toFixed(2) + '%'
}
return item
}
// 判断所给元素是否为数字类型
var judgeNumber = function (item) {
if (typeof (item) == "number") {
return true
}
return false
}
module.exports = {
change_unit: change_unit,
change_per: change_per,
judgeNumber: judgeNumber
}
</wxs>
wxss页面:
.table_box {
width: 100%;
height: auto;
box-sizing: border-box;
position: relative;
top: 10rpx;
z-index: 999;
border: 1px solid #E4E4E4;
}
.table_scroll {
overflow: hidden;
background: #FFF;
}
.table_header {
top: 0;
z-index: 999;
display: grid;
/* display: grid; 网格布局 */
/* grid-auto-flow 属性控制自动放置的项目如何插入网格中 */
/* column 通过填充每一列来放置项目 */
grid-auto-flow: column;
font-size: 26rpx;
color: #333333;
background: #F4F6FF;
}
.table_header_item {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
box-sizing: border-box;
position: relative;
z-index: 888;
border: 1rpx solid #E4E4E4;
border-left: 0;
border-top: 0;
}
.table_header_item:nth-child(1) {
position: sticky;
left: 0;
z-index: 999;
}
.table_content {
background-color: #fff;
/* 这是兼容 iPhone x */
padding-bottom: 10rpx;
margin-bottom: constant(safe-area-inset-bottom);
margin-bottom: env(safe-area-inset-bottom);
}
.table_content_line {
display: grid;
grid-auto-flow: column;
position: relative;
}
.table_content_line_item {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
box-sizing: border-box;
background-color: #fff;
border: 1rpx solid #E4E4E4;
border-left: 0;
border-top: 0;
font-size: 26rpx;
}
.table_content_line_item:nth-child(1) {
position: sticky;
left: 0;
/* top: 0; */
}
.alignment {
display: flex;
align-items: center;
justify-content: center
}
/* 右箭头css样式 */
.arrow {
width: 0;
height: 0;
border-left: 10rpx solid #ccc;
/* 左侧透明 */
border-top: 10rpx solid transparent;
/* 顶部有颜色 */
border-bottom: 10rpx solid transparent;
}
js页面:
// components/table/table.js
Component({
/**
* 组件的属性列表
*/
properties: {
totalHeight: {
type: Number,
value: 250
},
headList: Array,
table: Array,
table_sl: {
type: String,
value: 'fir'
},
line_sl: {
type: String,
value: 'sec'
},
lock_header: {
type: Boolean,
value: true
},
lock_first: {
type: Boolean,
value: true
},
table_width: {
type: Array,
value: []
},
header_height: {
type: Number,
value: 60
},
header_bg: {
type: String,
value: '#fff'
},
header_text_size: {
type: Number,
value: 32
},
header_bold: {
type: Boolean,
value: true
},
header_text_color: {
type: String,
value: '#111'
},
cell_height: {
type: Number,
value: 100
},
is_unit: {
type: Array,
value: []
},
is_per: {
type: Array,
value: []
}
},
/**
* 组件的初始数据
*/
data: {
header_style: ""
},
/**
* 组件的方法列表
*/
methods: {
/**
* 表头元素点击事件
* @param {Obejct} e 所选表头数据
*/
header_click(e) {
// console.log(e)
let data = e.currentTarget.dataset
// 将所点击表头的索引和值传给父组件
this.triggerEvent('head_click', {
'index': data.index,
'value': data.value
})
},
/**
* 行点击事件
* @param {Object} e 每行数据
*/
row_click(e) {
// console.log(e)
let data = e.currentTarget.dataset
// 将所点击行的索引和值传给父组件
this.triggerEvent('row_click', {
'index': data.index,
'value': data.value
})
},
/**
* 下拉触底事件函数
* @param {Object} e scroll-view下拉触底变量
*/
handleScrollToLower(e) {
if (e.detail.direction == 'bottom') {
console.log('下拉到底加载事件')
// 将数据传给父组件
this.triggerEvent('tableToLower', {
'data': '111'
})
}
},
/**
* 设置表头样式
*/
process_header() {
let [header_height, header_bg, header_text_size, header_text_color] = [this.properties.header_height, this.properties.header_bg, this.properties.header_text_size, this.properties.header_text_color]
this.setData({
header_style: "background:" + header_bg + ";font-size:" + header_text_size + "rpx;color:" + header_text_color + ";height:" + header_height + "rpx"
})
},
},
lifetimes: {
ready() {
// // 获取屏幕高度用来分配表格的显示
// wx.getSystemInfo({
// success: (res) => {
// this.setData({
// totalHeight: res.windowHeight - 180
// })
// }
// })
this.process_header()
}
}
})
demo案例:
index.wxml中调用table组件
<view style="padding: 10rpx;">
<table2 headList="{{headList}}" table="{{list}}" table_sl="{{table_sl}}" totalHeight="280" header_text_size="20" is_unit="{{is_unit}}" bind:head_click="head_click" bind:row_click="row_click"/>
</view>
index.js
// pages/test3/index.js
Page({
/**
* 页面的初始数据
*/
data: {
totalHeight: 0,
/* 该headList、 list、is_unit、is_per是属于table_sl='fir'模式下的数据格式*/
// headList:['股票名称', '时间日期', '姓名', '百分比', '星期', '结果'],
// list: [
// [['123456', '测试数据'], '2022-04-01', '李四', '0.001', '星期二', -1000000000],
// [['123456', '测试数据'], '2022-04-01', '张三', '0.001', '星期二', 100000],
// [['123456', '测试数据'], '2022-04-01', '李四', '0.001', '星期二', 100],
// [['123456', '测试数据'], '2022-04-01', '张三', '0.001', '星期二', -100],
// [['123456', '测试数据'], '2022-04-01', '李四', '0.001', '星期二', 100],
// [['123456', '测试数据'], '2022-04-01', '张三', '0.001', '星期二', 100],
// [['123456', '测试数据'], '2022-04-01', '李四', '0.001', '星期二', 100],
// [['123456', '测试数据'], '2022-04-01', '张三', '0.001', '星期二', -100],
// [['123456', '测试数据'], '2022-04-01', '李四', '0.001', '星期二', 0],
// [['123456', '测试数据'], '2022-04-01', '张三', '0.001', '星期二', 100],
// [['123456', '测试数据'], '2022-04-01', '李四', '0.001', '星期二', 100],
// [['123456', '测试数据'], '2022-04-01', '张三', '0.001', '星期二', 100],
// [['123456', '测试数据'], '2022-04-01', '李四', '0.001', '星期二', -100],
// [['123456', '测试数据'], '2022-04-01', '张三', '0.001', '星期二', 100],
// [['123456', '测试数据'], '2022-04-01', '李四', '0.001', '星期二', 100],
// ],
// is_unit: ['', '', '', '', '', true], // 如果只需要该后面的,前面的需要空值占位
// is_per: ['','','',true], // 如果只需要该后面的,前面的需要空值占位
/* 该headList、 list、is_unit、is_per是属于table_sl='sec'模式下的数据格式*/
table_sl: 'sec',
headList: [
'指标名称',
['123456', '测试数据'],
['234567', '测试数据'],
['345678', '测试数据'],
['456789', '测试数据'],
['567890', '测试数据']
],
list: [
['时间日期', '2022-04-01', '2022-04-01', '2022-04-01', '2022-04-01', '2022-04-01'],
['姓名', '李四', '张三', '李四', '张三', '李四'],
['百分比', '0.001', '0.001', '0.001', '0.001', '0.001'],
['星期', '星期二', '星期二', '星期二', '星期二', '星期二'],
['结果', -1000000000, 100000, 100, -100, 100]
],
is_unit: ['', '', '', '', true],
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
// wx.getSystemInfo({
// success:(res)=> {
// this.setData({
// // totalHeight: (res.windowHeight * 2) - 240
// totalHeight: res.windowHeight - 120
// })
// }
// })
},
// 触底事件
handleScrollToLower(e) {
// if(e.detail.direction == 'bottom') {
// console.log('scroll-view触底事件在这里处理加载下一页数据')
// }
console.log(e)
},
/**
* 接收表格表头点击事件
* @param {Object} e
*/
head_click(e) {
console.log(e)
},
/**
* 表格的行点击事件
* @param {Object} e
*/
row_click(e) {
console.log(e)
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
index.json引入table组件
{
"usingComponents": {
"table": "../../components/table/table"
}
}