目录
- 需求:
- 功能点
- 效果图
- 实现思路
- 具体代码(react\taro3.0)
- 具体代码(小程序原生)
- 总结
需求:
微信小程序实现固定表头固定列表格组件(移动端做点小修改通用)
功能点
- 排序表格
- 表头可固定
- 首列固定(可以优化成可以配置指定列左侧右侧固定)
- 翻页(上拉加载)监听
效果图
实现思路
开始想用三个ScrollView去实现滚动联动,固定表头、列的话,表格内容滚动表头、列也应该对应滚动,写了demo后发现监听一个ScrollView的位置信息去设置另外两个ScrollView的位置真机会很卡,体验极差
使用position:sticky; 让表头相对表格顶部sticky,每行的第一个元素相对当前行左侧sticky。
遇到的问题:
- 表格左滑的时候,滑动一个屏幕后固定列跟着滑出屏幕了。解决方法:动态设置表格的宽度,原理:滑出去的原因是整行滑出屏幕了,而sticky是相对整行左侧定位的。
- 表格高度设置为100%后useReachBottom上拉监听失效 将表格高度设高的话固定表头就失效了。解决方法:表格用ScrollView套一层使用onScrollToLower监听加载
具体代码(react\taro3.0)
index.tsx
/** * 可滑动、固定表头、固定列表格组件 * @example <Table data={data} dataAttribute={dataAttribute} sortTypeChange={sortTypeChange} handleRow={toDetails}/> */ import React, { useState, useMemo, useEffect } from 'react' import classNames from 'classnames' // components import { View, Text, ScrollView } from '@tarojs/components' // utils import { noop } from '@/utils/util' // styles import styles from './index.module.less' interface DataAttributeItem { title: string key: string | number sortKey?: string | number } interface Props { data: Array<any> dataAttribute: Array<DataAttributeItem> sortTypeChange?: (sort_item_id: any, sort_desc: boolean) => void handleRow?: (data: any) => void handleScrollToLower?: (e: any) => void } export default function Table(props: Props) { const { data, dataAttribute, sortTypeChange = noop, handleRow = noop, handleScrollToLower = noop } = props const [isSortDesc, setIsSortDesc] = useState<boolean>(true) const [sortIndex, setSortIndex] = useState<number>(1) const tableWidth = useMemo(() => { return `${(dataAttribute.length * 148 + 48)}rpx` }, [dataAttribute]) const tableHeight = useMemo(() => { return `${((data.length + 1) * 96)}rpx` }, [data]) const handleSortItem = (attrItem, attrIndex) => { if (attrIndex === 0) { return } const beforeIndex = sortIndex const sortKey = attrItem.sortKey dataAttribute.map((item, index)=>{ if (item.sortKey === sortKey) { if (beforeIndex === index) { setIsSortDesc(!isSortDesc) } else { setSortIndex(index) setIsSortDesc(true) } } }) } useEffect(()=>{ const sort_desc = isSortDesc const sort_item_id = dataAttribute[sortIndex].sortKey sortTypeChange(sort_item_id,sort_desc) },[sortIndex, isSortDesc]) return ( <ScrollView className={styles['table']} scrollY scrollX onScrollToLower={handleScrollToLower}> <View className={styles['sticky-box']} style={{height: tableHeight}}> <View className={styles['grey-box']} style={{width: tableWidth, position: 'sticky'}}/> <View className={styles['table__head']} style={{width: tableWidth, position: 'sticky'}}> {dataAttribute.map((attrItem, attrIndex) => ( <View className={styles['table__head__td']} key={attrIndex} onClick={()=>handleSortItem(attrItem, attrIndex)}> <Text className={classNames({ [styles['table__head__td__text']]: true, [styles['table__head__td__text-active']]: sortIndex === attrIndex, })} key={attrIndex} >{attrItem.title}</Text> {attrIndex !== 0 && <View className={classNames({ [styles['table__head__td__sorter-indicate']]: true, [styles['table__head__td__sorter-indicate--asc-active']]: sortIndex === attrIndex && !isSortDesc, [styles['table__head__td__sorter-indicate--desc-active']]: sortIndex === attrIndex && isSortDesc })} />} </View> ))} </View> {data.map((dataItem, dataIndex) => ( <View className={styles['table__row']} key={dataIndex} style={{width: tableWidth}} onClick={() => handleRow(dataItem)}> {dataAttribute.map((attrItem, attrIndex) => { return ( <Text className={styles['table__row__td']} key={attrIndex}>{dataItem[attrItem.key] || '-'}</Text> ) })} </View> ))} </View> </ScrollView> ) }
index.module.less
@import '~@/assets/style/mixins/ellipsis.less'; page{ font-size: 26rpx; line-height: 60rpx; color: #222; height: 100%; width: 100%; } .grey-box{ height: 10rpx; top: 0; background: #f8f8f8; z-index: 100; } .table{ position: relative; overflow: scroll; width: 100%; height: 100%; overflow: scroll; &__head{ position: relative; height: 96rpx; white-space: nowrap; // position: sticky; top: 10rpx; z-index: 100; height: 88rpx; font-size: 24rpx; line-height: 88rpx; color: #aaabbd; background-color: #f8f8f8; border-bottom: 2rpx solid #ecf1f8; background-color: #fff; white-space: nowrap; display: flex; &__td{ .ellipsis(); width: 148rpx; // padding-right: 40rpx; display: flex; justify-content: flex-start; align-items: center; background-color: #fff; position: relative; box-sizing: border-box; &:nth-child(1) { padding-left: 24rpx; width: 154rpx; margin-right: 40rpx; position: sticky; z-index: 10; left: 0; } &__text{ display: inline; &-active{ color: #6d70ff; } } &__sorter-indicate{ width: 24rpx; height: 24rpx; display: inline-block; background-repeat: no-repeat; background-size: 100% 100%; background-image: url('https://icon1.png'); &--asc-active { background-image: url('https://icon2.png'); } &--desc-active { background-image: url('https://icon3.png'); } } } } &__row{ position: relative; height: 96rpx; white-space: nowrap; display: flex; justify-content: flex-start; align-items: center; border-bottom: 2rpx solid #ecf1f8; &__td{ // .ellipsis(); overflow: scroll; white-space: nowrap; width: 148rpx; // padding-right: 40rpx; display: inline-block; background-color: #fff; position: relative; box-sizing: border-box; font-size: 26rpx; line-height: 96rpx; &:nth-child(1) { margin-right: 40rpx; padding-left: 24rpx; width: 154rpx; position: sticky; z-index: 10; left: 0; } } } }
具体代码(小程序原生)
<ScrollView class="table" scroll-x scroll-y bindscrolltolower="handleScrollToLower"> <View class="sticky-box" style="height:{{tableHeight}}rpx;"> <View class="table__head" style="width:{{tableWidth}}rpx;"> <View class="table__head__td" wx:for="{{dataAttribute}}" wx:key="attrIndex" wx:for-index="attrIndex" wx:for-item="attrItem"> <Text class="table__head__td__text" >{{attrItem.title}}</Text> </View> </View> <View class="table__row" wx:for="{{data}}" wx:key="dataIndex" wx:for-index="dataIndex" wx:for-item="dataItem" style="width:{{tableWidth}}rpx;"> <Text class="table__row__td" wx:for="{{dataAttribute}}" wx:key="dataIndex" wx:for-index="attrIndex" wx:for-item="attrItem">{{dataItem[attrItem.key] || '-'}}</Text> </View> </View> </ScrollView>
const app = getApp() Page({ data: { data: [ { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, { a: 123, b: 456, c: 489, d: 789, e: 458, f: 789 }, ], dataAttribute: [ { title: '第一列', key: 'a' }, { title: '第2列', key: 'b' }, { title: '第3列', key: 'c' }, { title: '第4列', key: 'd' }, { title: '第5列', key: 'e' }, { title: '第6列', key: 'f' } ], tableHeight: (20 + 1) * 96, tableWidth: 200 * 6 + 60 } })
page{ font-size: 26rpx; line-height: 60rpx; color: #222; height: 100%; width: 100%; } .table{ display: block; position: relative; overflow: scroll; width: 100%; height: 100%; } .sticky-box{ } .table__head{ height: 96rpx; white-space: nowrap; position: sticky; top: 0rpx; z-index: 100; height: 88rpx; font-size: 24rpx; line-height: 88rpx; color: #aaabbd; background-color: #f8f8f8; border-bottom: 2rpx solid #ecf1f8; background-color: #fff; white-space: nowrap; display: flex; } .table__head__td{ width: 200rpx; display: flex; justify-content: flex-start; align-items: center; background-color: #fff; box-sizing: border-box; position: relative; overflow: hidden; white-space: nowrap; -o-text-overflow: ellipsis; text-overflow: ellipsis; } .table__head__td:nth-child(1) { padding-left: 24rpx; width: 260rpx; margin-right: 40rpx; position: sticky; z-index: 101; left: 0rpx; } .table__head__td__text{ display: inline; } .table__row{ position: relative; height: 96rpx; white-space: nowrap; display: flex; justify-content: flex-start; align-items: center; border-bottom: 2rpx solid #ecf1f8; } .table__row__td{ overflow: scroll; white-space: nowrap; width: 200rpx; display: inline-block; background-color: #fff; box-sizing: border-box; font-size: 26rpx; line-height: 96rpx; position: relative; overflow: hidden; white-space: nowrap; -o-text-overflow: ellipsis; text-overflow: ellipsis; } .table__row__td:nth-child(1) { margin-right: 40rpx; padding-left: 24rpx; width: 260rpx; position: sticky; z-index: 10; left: 0; }
总结
到此这篇关于微信小程序实现固定表头、列表格组件的文章就介绍到这了,更多相关微信小程序固定表头内容请搜索NICE源码以前的文章或继续浏览下面的相关文章希望大家以后多多支持NICE源码!