微信小程序?qū)崿F(xiàn)固定表頭、列表格組件
需求:
微信小程序?qū)崿F(xiàn)固定表頭固定列表格組件(移動(dòng)端做點(diǎn)小修改通用)
功能點(diǎn)
- 排序表格
- 表頭可固定
- 首列固定(可以優(yōu)化成可以配置指定列左側(cè)右側(cè)固定)
- 翻頁(yè)(上拉加載)監(jiān)聽
效果圖


實(shí)現(xiàn)思路
開始想用三個(gè)ScrollView去實(shí)現(xiàn)滾動(dòng)聯(lián)動(dòng),固定表頭、列的話,表格內(nèi)容滾動(dòng)表頭、列也應(yīng)該對(duì)應(yīng)滾動(dòng),寫了demo后發(fā)現(xiàn)監(jiān)聽一個(gè)ScrollView的位置信息去設(shè)置另外兩個(gè)ScrollView的位置真機(jī)會(huì)很卡,體驗(yàn)極差
使用position:sticky; 讓表頭相對(duì)表格頂部sticky,每行的第一個(gè)元素相對(duì)當(dāng)前行左側(cè)sticky。
遇到的問題:
- 表格左滑的時(shí)候,滑動(dòng)一個(gè)屏幕后固定列跟著滑出屏幕了。解決方法:動(dòng)態(tài)設(shè)置表格的寬度,原理:滑出去的原因是整行滑出屏幕了,而sticky是相對(duì)整行左側(cè)定位的。
- 表格高度設(shè)置為100%后useReachBottom上拉監(jiān)聽失效 將表格高度設(shè)高的話固定表頭就失效了。解決方法:表格用ScrollView套一層使用onScrollToLower監(jiān)聽加載
具體代碼(react\taro3.0)
index.tsx
/**
* 可滑動(dòng)、固定表頭、固定列表格組件
* @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;
}
總結(jié)
到此這篇關(guān)于微信小程序?qū)崿F(xiàn)固定表頭、列表格組件的文章就介紹到這了,更多相關(guān)微信小程序固定表頭內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
鼠標(biāo)懸浮在樹組件節(jié)點(diǎn)上展示當(dāng)前節(jié)點(diǎn)名稱的三種實(shí)現(xiàn)方式
這篇文章主要介紹了鼠標(biāo)懸浮在樹組件節(jié)點(diǎn)上展示當(dāng)前節(jié)點(diǎn)名稱的三種實(shí)現(xiàn)方式,第一種是使用css樣式設(shè)置,第二種在checkBox綁定,第三種使用tooltip,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2023-12-12
JS實(shí)現(xiàn)兼容各種瀏覽器的高級(jí)拖動(dòng)方法完整實(shí)例【測(cè)試可用】
這篇文章主要介紹了JS實(shí)現(xiàn)兼容各種瀏覽器的高級(jí)拖動(dòng)方法,以完整實(shí)例形式分析了JS實(shí)現(xiàn)響應(yīng)鼠標(biāo)事件動(dòng)態(tài)修改頁(yè)面元素的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06
javascript jquery對(duì)form元素的常見操作詳解
下面小編就為大家?guī)硪黄猨avascript jquery對(duì)form元素的常見操作詳解。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-06-06
拿捏javascript對(duì)象增刪改查應(yīng)用及示例
“撩過”c++的對(duì)象,“拿捏”了python的對(duì)象,那么今天我們看看javascript中的對(duì)象到底是什么,看能不能一次性拿下,不行的話就多來幾次,想做“海王”就多物色幾門語(yǔ)言的對(duì)象,多new幾個(gè),最終你會(huì)發(fā)現(xiàn)都差不多2022-03-03
XML文件轉(zhuǎn)化成NSData對(duì)象的方法
這篇文章主要介紹了XML文件轉(zhuǎn)化成NSData對(duì)象的方法,需要的朋友可以參考下2015-08-08
淺談bootstrap源碼分析之scrollspy(滾動(dòng)偵聽)
下面小編就為大家?guī)硪黄獪\談bootstrap源碼分析之scrollspy(滾動(dòng)偵聽)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-06-06
三種在ES6中將非數(shù)組轉(zhuǎn)換為數(shù)組的方法詳情
這篇文章主要介紹了三種在ES6中將非數(shù)組轉(zhuǎn)換為數(shù)組的方法詳情,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-08-08

