動端React表格組件:支持合并

前言

在移動端開發中,表格組件是一個常見但復雜的需求。相比PC端,移動端表格面臨著屏幕空間有限、交互方式不同、性能要求更高等挑戰。本文將詳細介紹如何從零開始構建一個功能完整的移動端React表格組件,包含固定列、智能單元格合并、排序等高級功能。

項目背景

在實際項目中,我們經常遇到以下痛點:

  • 現有表格組件在移動端體驗不佳
  • 復雜的單元格合并需求難以實現
  • 固定列在不同屏幕尺寸下對齊問題
  • 大數據量下的性能優化

基于這些需求,我們開發了 @wtechtec/mobile-table 組件庫。

技術棧選擇

  • React 18 - 主框架
  • TypeScript - 類型安全
  • NutUI React - 基礎UI組件庫
  • Rollup - 構建工具
  • PostCSS - 樣式處理

核心功能設計

1. 基礎表格結構

首先定義表格的基礎類型:

export interface BasicTableProps extends BasicComponent {columns: Array<TableColumnProps>data: Array<any>bordered: booleansummary?: React.ReactNodestriped?: booleannoData?: React.ReactNodesorterIcon?: React.ReactNodeonSort?: (column: TableColumnProps, sortedData: Array<any>) => voidshowHeader?: boolean
}export interface TableColumnProps {key: stringtitle?: stringalign?: stringsorter?: ((a: any, b: any) => number) | boolean | stringrender?: (rowData: any, rowIndex: number) => string | React.ReactNodefixed?: 'left' | 'right'width?: numberonCell?: (rowData: any, rowIndex: number) => CellConfig
}

2. 固定列實現原理

固定列是移動端表格的核心功能,實現思路如下:

// 計算固定列寬度
const useTableSticky = (columns: TableColumnProps[], rtl: boolean) => {const [stickyLeftWidth, setStickyLeftWidth] = useState(0)const [stickyRightWidth, setStickyRightWidth] = useState(0)useEffect(() => {// 計算左固定列總寬度let leftWidth = 0let rightWidth = 0columns.forEach(col => {if (col.fixed === 'left' && col.width) {leftWidth += col.width}if (col.fixed === 'right' && col.width) {rightWidth += col.width}})setStickyLeftWidth(leftWidth)setStickyRightWidth(rightWidth)}, [columns])return { stickyLeftWidth, stickyRightWidth }
}

關鍵點:以實際渲染寬度為準

在實際開發中,我們發現設置的 width 和渲染出來的寬度可能不一致,因此采用動態獲取DOM寬度的方案:

useEffect(() => {// 獲取所有 fixed: 'left' 列的實際寬度let width = 0columns.forEach(col => {if (col.fixed === 'left' && thRefs.current[col.key]) {width += thRefs.current[col.key]!.offsetWidth}})setStickyLeftWidth(width)
}, [columns, data])

3. 智能單元格合并算法

這是本組件的亮點功能,能夠自動識別相同值并進行最優的矩形區域合并:

// 創建多行多列合并配置
export const createMultiRowColumnMergeCellConfig = (data: any[], columns: string[]) => {const mergeCellMap = new Map<string, { rowSpan: number; colSpan: number; isMainCell: boolean;value: any;mergeType: 'row' | 'column' | 'block';}>()// 創建值到位置的映射const valueToPositions = new Map<any, Array<{row: number, col: number, colKey: string}>>()// 收集所有相同值的位置data.forEach((item, rowIndex) => {columns.forEach((colKey, colIndex) => {const value = item[colKey]if (value !== null && value !== undefined && value !== '') {if (!valueToPositions.has(value)) {valueToPositions.set(value, [])}valueToPositions.get(value)!.push({row: rowIndex,col: colIndex,colKey})}})})// 處理每個相同值的合并valueToPositions.forEach((positions, value) => {if (positions.length <= 1) returnconst mergeAreas = findMaxRectangleAreas(positions)mergeAreas.forEach(area => {if (area.positions.length > 1) {createMergeArea(area, value, mergeCellMap)}})})return mergeCellMap
}

矩形區域識別算法

// 查找最大矩形合并區域
const findMaxRectangleAreas = (positions: Array<{row: number, col: number, colKey: string}>) => {const areas = []const usedPositions = new Set<string>()const sortedPositions = [...positions].sort((a, b) => {if (a.row !== b.row) return a.row - b.rowreturn a.col - b.col})for (const startPos of sortedPositions) {const startKey = `${startPos.row}-${startPos.col}`if (usedPositions.has(startKey)) continue// 嘗試找到以當前位置為起點的最大矩形const maxRect = findLargestRectangleFromPosition(startPos, positions, usedPositions)if (maxRect.positions.length > 1) {areas.push(maxRect)maxRect.positions.forEach(pos => {usedPositions.add(`${pos.row}-${pos.col}`)})}}return areas
}

4. 排序功能實現

const handleSorterClick = (item: TableColumnProps) => {if (item.sorter && !sortedMapping.current[item.key]) {const copied = [...innerValue]if (typeof item.sorter === 'function') {copied.sort(item.sorter as (a: any, b: any) => number)} else if (item.sorter === 'default') {copied.sort()}sortedMapping.current[item.key] = truesetValue(copied, true)onSort && onSort(item, copied)} else {sortedMapping.current[item.key] = falsesetValue(data)}
}

樣式設計與優化

1. 移動端適配

.nut-table {overflow: hidden;position: relative;word-wrap: break-word;word-break: break-all;
}.nut-table-wrapper {display: flex;width: 100%;flex-direction: column;font-size: 14px;color: #1a1a1a;overflow-y: auto;overflow-x: hidden;position: relative;border: 1px solid #f0f0f0;
}.nut-table-wrapper-sticky {overflow-x: auto;
}

2. 固定列樣式

.nut-table-fixed-left,
.nut-table-fixed-right {position: sticky;z-index: 2;
}.nut-table-sticky-left {left: 1px;box-shadow: 6px 0 6px -4px rgba(0, 0, 0, 0.15);
}.nut-table-sticky-right {right: 1px;box-shadow: -6px 0 6px -4px rgba(0, 0, 0, 0.15);
}

構建配置優化

Rollup 配置

export default {input: 'src/index.ts',output: [{file: pkg.main,format: 'cjs',sourcemap: true,exports: 'named'},{file: pkg.module,format: 'esm',sourcemap: true,exports: 'named'},{file: pkg.unpkg,format: 'umd',name: 'MobileTable'}],external: ['react', 'react-dom', '@nutui/nutui-react'],plugins: [resolve({extensions: ['.ts', '.tsx', '.js', '.jsx'],preferBuiltins: false,dedupe: ['react', 'react-dom']}),postcss({inject: true,extract: false,modules: false  // 關鍵:禁用CSS模塊化}),typescript({tsconfig: './tsconfig.json',declaration: true,declarationDir: 'dist',rootDir: 'src'})]
}

使用示例

基礎用法

import { Table } from '@wtechtec/mobile-table'const columns = [{ key: 'name', title: '姓名', width: 100, fixed: 'left' },{ key: 'age', title: '年齡', width: 80 },{ key: 'address', title: '地址', width: 200 }
]const data = [{ name: '張三', age: 25, address: '北京市朝陽區' },{ name: '李四', age: 30, address: '上海市浦東新區' }
]<Table columns={columns} data={data} />

智能合并用法

import { Table, createMultiMergeOnCellFunction, createMultiRowColumnMergeCellConfig 
} from '@wtechtec/mobile-table'const mergeColumns = ['gender', 'age', 'class']
const multiMergeCellMap = createMultiRowColumnMergeCellConfig(data, mergeColumns)const columns = [{key: 'gender',title: '性別',onCell: createMultiMergeOnCellFunction(multiMergeCellMap, 'gender')}// ...
]

性能優化策略

1. 虛擬滾動(大數據量)

const VirtualTable = ({ data, height = 400 }) => {const [startIndex, setStartIndex] = useState(0)const [endIndex, setEndIndex] = useState(20)const visibleData = useMemo(() => {return data.slice(startIndex, endIndex)}, [data, startIndex, endIndex])return <Table data={visibleData} />
}

2. 合并計算緩存

const useMergeCellMap = (data: any[], columns: string[]) => {return useMemo(() => {return createMultiRowColumnMergeCellConfig(data, columns)}, [data, columns])
}

遇到的技術難點與解決方案

1. CSS樣式無效問題

問題:npm包引用后樣式無效

原因:Rollup配置中開啟了CSS模塊化,導致類名被哈希化

解決方案

postcss({inject: true,extract: false,modules: false  // 禁用CSS模塊化
})

2. 固定列對齊問題

問題:設置的width與實際渲染寬度不一致

解決方案:以實際DOM寬度為準,動態計算sticky區域寬度

3. 單元格合并復雜度

問題:如何實現智能的多行多列合并

解決方案:設計矩形區域識別算法,自動找到最優合并方案

測試與發布

單元測試

describe('Table Component', () => {test('renders basic table', () => {render(<Table columns={columns} data={data} />)expect(screen.getByText('姓名')).toBeInTheDocument()})test('merge cells correctly', () => {const mergeCellMap = createMultiRowColumnMergeCellConfig(data, ['gender'])expect(mergeCellMap.size).toBeGreaterThan(0)})
})

發布流程

# 構建
pnpm run build# 發布到npm
pnpm publish --access public

總結與展望

通過本次開發,我們成功構建了一個功能完整的移動端表格組件,主要收獲:

  1. 架構設計:合理的類型定義和組件拆分
  2. 算法優化:智能合并算法的設計與實現
  3. 性能優化:虛擬滾動、計算緩存等策略
  4. 工程化:完整的構建、測試、發布流程

未來規劃

  • 支持表格編輯功能
  • 增加更多主題樣式
  • 優化大數據量性能
  • 支持表格導出功能

參考資料

  • React官方文檔
  • NutUI React
  • Rollup官方文檔

項目地址:GitHub - @wtechtec/mobile-table

NPM包:@wtechtec/mobile-table

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/908975.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/908975.shtml
英文地址,請注明出處:http://en.pswp.cn/news/908975.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

廣告系統中后鏈路數據為什么要使用流批一體技術?流批一體技術是什么?

在大規模廣告系統的后鏈路(離線和實時特征計算、模型訓練與上線、效果監控等)中,往往既有對海量歷史數據的批量計算需求(離線特征、離線模型訓練、報表匯總),又有對在線請求的低延遲實時計算需求(實時特征、在線打分、實時監控/告警)。傳統將二者割裂、用 Lambda 架構…

6.10 - 常用 SQL 語句以及知識點

MySQL 技術 SQL 是結構化查詢語言&#xff0c;他是關系型數據庫的通用語言 SQL 可以分為分為以下三個類別 DDL (data definition languages) 語句 數據定義語言&#xff0c;定義了 不同的數據庫、表、索引等數據庫對象的定義。常用的的語句關鍵字包括 **create、drop、alter …

OpenCV CUDA 模塊光流計算------稀疏光流算法類SparsePyrLKOpticalFlow

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 OpenCV CUDA 模塊中實現的稀疏光流算法類&#xff0c;基于 Lucas-Kanade 方法&#xff0c;并支持圖像金字塔結構。適用于特征點跟蹤任務&#xf…

免費工具-微軟Bing Video Creator

目錄 引言 一、揭秘Bing Video Creator 二、輕松上手&#xff1a;三步玩轉Bing Video Creator 2.1 獲取與訪問&#xff1a; 2.2 創作流程&#xff1a; 2.3 提示詞撰寫技巧——釋放AI的想象力&#xff1a; 三、核心特性詳解&#xff1a;靈活滿足多樣化需求 3.1 雙重使用模…

MySQL技術內幕1:內容介紹+MySQL編譯使用介紹

文章目錄 1.整體內容介紹2.下載編譯流程2.1 安裝編譯工具和依賴庫2.2 下載編譯 3.配置MySQL3.1 數據庫初始化3.2 編輯配置文件3.3 啟動停止MySQL3.4 登錄并修改密碼 1.整體內容介紹 MySQL技術系列文章將從MySQL下載編譯&#xff0c;使用到MySQL各組件使用原理源碼分析&#xf…

MySQL 事務詳解

MySQL 事務詳解 一、事務是什么&#xff1f;為什么需要事務&#xff1f; 二、事務的四大特性&#xff08;ACID&#xff09;舉例說明&#xff1a;轉賬操作 三、MySQL 中事務的支持四、事務分類&#xff1a;隱式 vs 顯式1. 隱式事務&#xff08;自動提交&#xff09;2. 顯式事務&…

深入淺出Asp.Net Core MVC應用開發系列-AspNetCore中的日志記錄

ASP.NET Core 是一個跨平臺的開源框架&#xff0c;用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 應用。 ASP.NET Core 中的日志記錄 .NET 通過 ILogger API 支持高性能結構化日志記錄&#xff0c;以幫助監視應用程序行為和診斷問題。 可以通過配置不同的記錄提供程…

利用coze工作流制作一個自動生成PPT的智能體

在Coze平臺中&#xff0c;通過工作流實現PPT自動化生成是一個高效且靈活的解決方案&#xff0c;尤其適合需要快速產出標準化演示文稿的場景。以下是基于Coze工作流制作PPT的核心邏輯與操作建議&#xff1a; 理論流程 一、核心流程設計 需求輸入與解析 用戶輸入&#xff1a;主…

vue3 按鈕級別權限控制

在Vue 3中實現按鈕級別的權限控制&#xff0c;可以通過多種方式實現。這里我將介紹幾種常見的方法&#xff1a; 方法1&#xff1a;使用Vue 3的Composition API 在Vue 3中&#xff0c;你可以使用Composition API來創建一個可復用的邏輯來處理權限控制。 創建權限控制邏輯 首…

spa首屏加載慢怎樣解決

SPA&#xff08;Single Page Application&#xff0c;單頁應用&#xff09;首屏加載慢是一個常見問題&#xff0c;主要原因通常是首次加載需要拉取體積較大的 JavaScript 文件、樣式表、初始化數據等。以下是一些常見的 優化策略&#xff0c;可以幫助你 提升首屏加載速度&#…

UE5 音效系統

一.音效管理 音樂一般都是WAV,創建一個背景音樂類SoudClass,一個音效類SoundClass。所有的音樂都分為這兩個類。再創建一個總音樂類&#xff0c;將上述兩個作為它的子類。 接著我們創建一個音樂混合類SoundMix&#xff0c;將上述三個類翻入其中&#xff0c;通過它管理每個音樂…

2.Vue編寫一個app

1.src中重要的組成 1.1main.ts // 引入createApp用于創建應用 import { createApp } from "vue"; // 引用App根組件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要寫三種標簽 <template> <!--html--> </template>…

NTT印地賽車:數字孿生技術重構賽事體驗范式,驅動觀眾參與度革命

引言&#xff1a;數字孿生技術賦能體育賽事&#xff0c;開啟沉浸式觀賽新紀元 在傳統體育賽事觀賽模式遭遇體驗天花板之際&#xff0c;NTT與印地賽車系列賽&#xff08;NTT INDYCAR SERIES&#xff09;的深度合作&#xff0c;通過數字孿生&#xff08;Digital Twin&#xff09…

解構與重構:PLM 系統如何從管理工具進化為創新操作系統?

在智能汽車、工業物聯網等新興領域的沖擊下&#xff0c;傳統產品生命周期管理&#xff08;PLM&#xff09;系統正在經歷前所未有的范式轉換。當某頭部車企因 ECU 軟件與硬件模具版本失配導致 10 萬輛智能電車召回&#xff0c;損失高達 6 億美元時&#xff0c;這場危機不僅暴露了…

【Ubuntu 16.04 (Xenial)??】安裝docker及容器詳細教程

Ubuntu 16.04 安裝docker詳細教程 一、docker安裝1.1 前期準備1.2 使用 Docker 官方安裝腳本安裝&#xff08;推薦&#xff09; 查看ubuntu版本&#xff1a;lsb_release -a 這里我的系統是 ??Ubuntu 16.04 (Xenial)??&#xff0c;在 ??Ubuntu 16.04 (Xenial)?? 上安裝…

.Net框架,除了EF還有很多很多......

文章目錄 1. 引言2. Dapper2.1 概述與設計原理2.2 核心功能與代碼示例基本查詢多映射查詢存儲過程調用 2.3 性能優化原理2.4 適用場景 3. NHibernate3.1 概述與架構設計3.2 映射配置示例Fluent映射XML映射 3.3 查詢示例HQL查詢Criteria APILINQ提供程序 3.4 高級特性3.5 適用場…

MySQL:InnoDB架構(內存架構篇)

目錄 0.前置知識 0.1二級索引的概念 二級索引查詢原理 1.整體架構 1.1為什么innoDB的架構會分為兩個部分? 2.內存架構 2.1BufferPool 2.2ChangeBuffer 唯一性檢查不是實時性會出現的問題? ChangeBuffer的優勢 2.3Adaptive Hash Index 2.4LogBuffer 0.前置知識 0.…

鷹盾加密器“一機一碼”技術全維度剖析:從底層實現到生態防護體系

“一機一碼”加密技術的深度解析與實現路徑 引言 在數字內容版權保護和軟件授權管理領域&#xff0c;“一機一碼”技術作為一種重要的安全防護手段&#xff0c;能夠有效防止授權碼濫用和非法傳播。它通過建立設備與授權碼的唯一對應關系&#xff0c;確保每份授權僅在特定設備…

Android 中使用 OkHttp 創建多個 Client

在 Android 開發中&#xff0c;有時我們需要創建多個 OkHttpClient 實例來滿足不同的網絡請求需求。以下是創建和管理多個 OkHttpClient 的方法&#xff1a; 基本創建方式 // 創建默認的 OkHttpClient val defaultClient OkHttpClient()// 創建帶有自定義配置的 Client val …

C++中的跳轉語句

C中的跳轉語句包括break、continue和goto&#xff0c;它們用于改變程序的正常執行流程。下面分別介紹它們的作用、使用場景和注意事項&#xff1a; 1. break 作用&#xff1a; ? 立即終止當前所在的循環&#xff08;for、while、do while&#xff09;或switch語句&#xff…