小程序实现固定首行列表格
数据可视化, 数据报表一致是我比较头疼的问题, 这几年一直找不到一个合适的工具.
主要是编程这块并非是数据分析师的强项. 从最初用 Excel, 到用 Python, 到用各种商业BI工具, 就总是觉得体验不好, 虽然它的效率极高, 但移动端的支持不友好, 也很难进行个性化定制, 思来想去, 觉得还是要用标准的开发才是可视化的尽头. 然后就学了很多前端的技术, 后端的技术 ...
我自己既是设计者, 也是开发者, 也是用户. 从体验性, 便捷性上看, 觉得小程序是很适合做BI报表的. 非常轻量和便捷, 毕竟大家都有用微信, 在手机上也不占空间, 也跨平台, 也是移动化办公. 更重要的是它的开发难度不大, 能轻松上手, 技术变更不像 vue, react 这些那么卷, 我觉得很适合像我这样搞数据的同学用它来做一些数据报表, 数据可视化之类的.
图类的其实更简单, 基本都有一些开源的库, 想 echarts, ucharts ...这些, 基本都是基于 canvas 的. 要实在不能满足需求, 就自己用 canvas画吧. 比较复杂的其实是表格. 有些复杂的表格, 真的会把人搞崩溃. 用 table 来布局也是N多年前了, 估计也是基于此小程序官方就没有 table 这个组件吧.
但是在报表这块呢, table 又绝对绕不过去, 搜了很多网友的贡献, 就也是跟着来学一遍.
普通一屏表格
针对字段不多, 数据项也不多的总结性数, 手机上一屏就能显示全的, 也不用固定表头啥的操作, 就纯显示.
这种就直接用 flex 就行. 将表头和数据给分开写. 也能单独设置某列的宽度这样的.
js 数据部分:
Page({
data: {
headList: ['省份/直辖市', 'GDP', '增长率', '数值备注'],
trList: [
{ "province": "广东", "gdp": 72812456, "rate": "8%", " tip": 12345 },
{ "province": "河南", "gdp": 37010, "rate": "6.3%", "tip": 678910 },
{ "province": "江苏", "gdp": 70116, "rate": "5.5%", "tip": 11121314 },
{ "province": "广东", "gdp": 72812456, "rate": "8%", "tip": 12345 },
{ "province": "河南", "gdp": 37010, "rate": "6.3%", "tip": 678910 },
{ "province": "江苏", "gdp": 70116, "rate": "5.5%", "tip": 11121314 },
{ "province": "广东", "gdp": 72812456, "rate": "8%", "tip": 12345 },
{ "province": "河南", "gdp": 37010, "rate": "6.3%", "tip": 678910 }
]
}
})
wxml 结构部分:
.table{
font-size: 30rpx;
width: 100%;
border: 1px solid #666;
border-top: 0;
}
/*设置行宽填满, 高度凭感觉整一个吧 */
.tr {
width: 100%;
height: 50rpx;
display: flex;
}
.td, .th {
/* 这里就手动调下宽度呗, 手机也放不了几列 */
width: 25%;
box-sizing: border-box;
display: flex;
/* 单元格居中对齐 */
justify-content: center;
align-items: center;
border: 1px solid #666;
border-left: 0;
border-bottom: 0;
}
/* 设置表头, 背景色, 加粗 */
.th {
font-weight: 700;
background-color: greenyellow;
}
/* 用了 flex, 也可以再调整某列宽度, 比如首列 */
.th:nth-child(1) {
width: 40%;
}
.td:nth-child(1) {
width: 40%;
}
当然也可以用古老的 table 布局, 即 display: table 这里就不展示了, 俺觉得就 flex 已经很ok啦.
多字段表格
固定表头和表格首列, 右侧表格可以左右滚动.
这种就可以通过小程序内置的 scroll-view 滑动组件 结合 sticky 定位来实现. 表头和表数据分开来写, 通过设置相同的宽度即实现对齐. 表头可用 flex 布局, 表数据则用 grid 布局, 并将每行数据以列的方式排列, 即 grid-auto-flow: column 实现.
wxml 结构部分:
<view class="header">固定表头和表格首列 右侧表格可以左右滚动</view>
<view class="table">
<scroll-view scroll-y scroll-x style="height:{{totalHeight}}px;">
<view>
<view class="thead">
<view class="th" wx:for="{{headList}}" wx:key="index">{{item.title}}</view>
</view>
<view class="tbody">
<view class="tr" wx:for="{{trList}}" wx:key="index" wx:for-item="tr">
<view class="td">{{tr.date}}</view>
<view class="td">{{tr.name}}</view>
<view class="td">{{tr.jobNumber}}</view>
<view class="td">{{tr.week}}</view>
<view class="td">{{tr.upTime}}</view>
<view class="td">{{tr.belowTime}}</view>
<view class="td" style="color: red;">{{tr.lateTime}}</view>
<view class="td" style="color: red;">{{tr.earlyTime}}</view>
<view class="td">{{row.comment}}</view>
</view>
</view>
</view>
</scroll-view>
</view>
wxss 样式部分:
.header {
font-size: 30rpx;
text-align: center;
color: orangered;
}
.table {
margin-top: 30rpx;
/* 注意1: scroll-view 有默认 padding 很恶心 */
padding: 0;
/* 注意2: scroll-view 的外层盒子要有宽or高, 才能滑动 */
width: 100%;
height: 100%;
border: 1px solid #f4f6f4;
}
/* 表头部分 */
.thead {
/* 将第一个格设置 sticky, 往上滑则固定住 */
position: sticky;
top: 0;
display: flex;
font-size: 26rpx;
/* 同表数据共同设置一个宽, 让对齐 */
width: 1400rpx;
border: 1px solid #f4f6ff;
border-left: 0;
border-top: 0;
/* 提升表格的重叠权重, 显示出来, 同时将内容设置为透明, 就实现了固定表头的效果 */
z-index: 1;
background: #f4f6ff;
}
.th{
/* 设置背景色, 滑动的时候就不会重叠字样了. */
background-color: #f4f6ff;
width: 150rpx;
height: 60rpx;
border: 1px solid #e4e4e4;
border-top: 0;
border-left: 0;
font-weight: 700;
display: flex;
justify-content: center;
align-items: center;
}
/* 首行第一个单元格进行固定 */
.th:nth-child(1) {
position: sticky;
left: 0;
width: 180rpx;
}
/* 表格数据部分 */
/* 直接对数据这个标签进行 grid, 以 col 方式写入 */
.tr {
/* 保持和表格一样的宽度 */
width: 1380rpx;
display: grid;
grid-auto-flow: column;
font-size: 26rpx;
}
.td {
/* 对每个单元格设置宽高, 宽同表格报纸一致 */
width: 150rpx;
height: 60rpx;
border: 1px solid #e4e4e4;
border-left: 0;
border-top: 0;
/* 每个格背景设置透明, 滑动的时候就好隐藏 */
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
}
/* 每行第一个单元格进行固定, 宽度和表格一致对齐 */
.td:nth-child(1) {
position: sticky;
width: 180rpx;
left: 0;
}
/* 将滚动条设置隐藏 */
::-webkit-scrollbar {
width: 0;
height: 0;
}
js 数据部分:
Page({
data: {
totalHeight: 0,
headList: [
{ title: '时间日期' },
{ title: '姓名' },
{ title: '工号' },
{ title: '星期' },
{ title: '上班时间' },
{ title: '下班时间' },
{ title: '迟到' },
{ title: '早退' },
{ title: '备注' }
],
trList: [{
date: '2023-04-01',
name: '小陈总',
jobNumber: '4338',
week: '星期一',
upTime: '08:30:43',
belowTime: '18:01:56',
lateTime: '30:43',
earlyTime: '',
comment: '',
},
{
date: '2023-04-02',
name: '小陈总',
jobNumber: '4338',
week: '星期二',
upTime: '07:59:43',
belowTime: '17:00:00',
lateTime: '',
earlyTime: '60:00',
comment: '',
},
{
date: '2023-04-03',
name: '小陈总',
jobNumber: '4338',
week: '星期三',
upTime: '',
belowTime: '',
lateTime: '',
earlyTime: '',
comment: '调休',
},
{
date: '2023-04-04',
name: '小陈总',
jobNumber: '4338',
week: '星期四',
upTime: '08:30:43',
belowTime: '18:01:56',
lateTime: '30:43',
earlyTime: '',
comment: '',
},
{
date: '2023-04-05',
name: '小陈总',
jobNumber: '4338',
week: '星期五',
upTime: '07:59:43',
belowTime: '17:00:00',
lateTime: '',
earlyTime: '60:00',
comment: '',
},
{
date: '2023-04-06',
name: '小陈总',
jobNumber: '4338',
week: '星期六',
upTime: '',
belowTime: '',
lateTime: '',
earlyTime: '',
comment: '调休',
},
{
date: '2023-04-07',
name: '小陈总',
jobNumber: '4338',
week: '星期日',
upTime: '',
belowTime: '',
lateTime: '',
earlyTime: '',
comment: '休息',
},
{
date: '2023-04-01',
name: '小陈总',
jobNumber: '4338',
week: '星期一',
upTime: '08:30:43',
belowTime: '18:01:56',
lateTime: '30:43',
earlyTime: '',
comment: '',
},
{
date: '2023-04-02',
name: '小陈总',
jobNumber: '4338',
week: '星期二',
upTime: '07:59:43',
belowTime: '17:00:00',
lateTime: '',
earlyTime: '60:00',
comment: '',
},
{
date: '2023-04-03',
name: '小陈总',
jobNumber: '4338',
week: '星期三',
upTime: '',
belowTime: '',
lateTime: '',
earlyTime: '',
comment: '调休',
},
{
date: '2023-04-04',
name: '小陈总',
jobNumber: '4338',
week: '星期四',
upTime: '08:30:43',
belowTime: '18:01:56',
lateTime: '30:43',
earlyTime: '',
comment: '',
},
{
date: '2023-04-05',
name: '小陈总',
jobNumber: '4338',
week: '星期五',
upTime: '07:59:43',
belowTime: '17:00:00',
lateTime: '',
earlyTime: '60:00',
comment: '',
},
{
date: '2023-04-06',
name: '小陈总',
jobNumber: '4338',
week: '星期六',
upTime: '',
belowTime: '',
lateTime: '',
earlyTime: '',
comment: '调休',
},
{
date: '2023-04-07',
name: '小陈总',
jobNumber: '4338',
week: '星期日',
upTime: '',
belowTime: '',
lateTime: '',
earlyTime: '',
comment: '休息',
},
{
date: '2023-04-01',
name: '小陈总',
jobNumber: '4338',
week: '星期一',
upTime: '08:30:43',
belowTime: '18:01:56',
lateTime: '30:43',
earlyTime: '',
comment: '',
},
{
date: '2023-04-02',
name: '小陈总',
jobNumber: '4338',
week: '星期二',
upTime: '07:59:43',
belowTime: '17:00:00',
lateTime: '',
earlyTime: '60:00',
comment: '',
},
{
date: '2023-04-03',
name: '小陈总',
jobNumber: '4338',
week: '星期三',
upTime: '',
belowTime: '',
lateTime: '',
earlyTime: '',
comment: '调休',
},
{
date: '2023-04-04',
name: '小陈总',
jobNumber: '4338',
week: '星期四',
upTime: '08:30:43',
belowTime: '18:01:56',
lateTime: '30:43',
earlyTime: '',
comment: '',
},
{
date: '2023-04-05',
name: '小陈总',
jobNumber: '4338',
week: '星期五',
upTime: '07:59:43',
belowTime: '17:00:00',
lateTime: '',
earlyTime: '60:00',
comment: '',
},
{
date: '2023-04-06',
name: '小陈总',
jobNumber: '4338',
week: '星期六',
upTime: '',
belowTime: '',
lateTime: '',
earlyTime: '',
comment: '调休',
},
{
date: '2023-04-07',
name: '小陈总',
jobNumber: '4338',
week: '星期日',
upTime: '',
belowTime: '',
lateTime: '',
earlyTime: '',
comment: '休息',
},
{
date: '2023-04-01',
name: '小陈总',
jobNumber: '4338',
week: '星期一',
upTime: '08:30:43',
belowTime: '18:01:56',
lateTime: '30:43',
earlyTime: '',
comment: '',
},
{
date: '2023-04-02',
name: '小陈总',
jobNumber: '4338',
week: '星期二',
upTime: '07:59:43',
belowTime: '17:00:00',
lateTime: '',
earlyTime: '60:00',
comment: '',
},
{
date: '2023-04-03',
name: '小陈总',
jobNumber: '4338',
week: '星期三',
upTime: '',
belowTime: '',
lateTime: '',
earlyTime: '',
comment: '调休',
},
{
date: '2023-04-04',
name: '小陈总',
jobNumber: '4338',
week: '星期四',
upTime: '08:30:43',
belowTime: '18:01:56',
lateTime: '30:43',
earlyTime: '',
comment: '',
},
{
date: '2023-04-05',
name: '小陈总',
jobNumber: '4338',
week: '星期五',
upTime: '07:59:43',
belowTime: '17:00:00',
lateTime: '',
earlyTime: '60:00',
comment: '',
},
{
date: '2023-04-06',
name: '小陈总',
jobNumber: '4338',
week: '星期六',
upTime: '',
belowTime: '',
lateTime: '',
earlyTime: '',
comment: '调休',
},
{
date: '2023-04-07',
name: '小陈总',
jobNumber: '4338',
week: '星期日',
upTime: '',
belowTime: '',
lateTime: '',
earlyTime: '',
comment: '休息',
}
]
},
// 监听页面加载
onLoad() {
wx.getSystemInfo({
success: (res) => {
this.setData({
totalHeight: res.windowHeight - 140
})
}
})
},
})