Mui DataGrid详情列:实现超8行显示展开按钮及行数判断
MUI DataGrid详情列折叠/展开实现(仅内容超8行时显示按钮)
需求
- 详情列默认最多显示8行内容
- 内容超出8行时,显示「显示更多」按钮,点击后展开全部内容;展开后可切换回收起状态
当前问题
已通过WebkitLineClamp: 8实现默认8行限制,也想到用状态记录展开行ID来切换显示行数,但无法判断内容是否超过8行,导致按钮始终显示,不符合需求。
解决方案
判断内容是否超行的核心逻辑
通过对比元素的实际内容高度和限制8行后的高度:
- 渲染一个与显示元素样式完全一致但不限制行数的隐藏元素,获取其完整高度
- 获取限制8行的显示元素的高度
- 若隐藏元素高度 > 显示元素高度,说明内容超出8行,显示按钮
完整代码实现
import { useState, useRef, useEffect } from 'react'; import { DataGrid, GridColDef } from '@mui/x-data-grid'; import { Box, Typography, Button } from '@mui/material'; // 假设t是你的国际化函数 import { t } from './your-i18n-module'; const YourComponent = () => { // 记录展开的行ID集合 const [expandedRowIds, setExpandedRowIds] = useState<Set<number>>(new Set()); // 保留原有分页状态 const [currentPage, setCurrentPage] = useState(0); const [pageSize, setPageSize] = useState(25); const mockData = [/* 你的业务数据 */]; const columns: GridColDef[] = [ { field: 'title', headerName: t`Title`, flex: 1, }, { field: 'details', headerName: t`Details`, flex: 2, renderCell: ({ row }) => { const isExpanded = expandedRowIds.has(row.id); const visibleTextRef = useRef<HTMLDivElement>(null); const hiddenTextRef = useRef<HTMLDivElement>(null); const [showMore, setShowMore] = useState(false); useEffect(() => { if (visibleTextRef.current && hiddenTextRef.current) { // 对比高度判断内容是否超出行数 setShowMore(hiddenTextRef.current.scrollHeight > visibleTextRef.current.clientHeight); } }, [row.details]); return ( <Box sx={{ padding: 1, position: 'relative' }}> {/* 显示的文本,根据展开状态控制行数 */} <Typography ref={visibleTextRef} variant="body2" sx={{ display: '-webkit-box', overflow: 'hidden', WebkitBoxOrient: 'vertical', WebkitLineClamp: isExpanded ? 'unset' : 8, lineHeight: 1.5, // 固定行高保证高度计算准确 }} > {row.details} </Typography> {/* 隐藏的文本,用于获取完整内容高度 */} <Typography ref={hiddenTextRef} variant="body2" sx={{ position: 'absolute', visibility: 'hidden', top: 0, left: 0, right: 0, lineHeight: 1.5, // 和显示文本保持一致样式 whiteSpace: 'pre-wrap', // 确保换行逻辑一致 }} > {row.details} </Typography> {/* 仅当内容超行时显示按钮 */} {showMore && ( <Button sx={{ color: 'primary', padding: 0, margin: '8px 0 0' }} onClick={() => { setExpandedRowIds(prev => { const newSet = new Set(prev); isExpanded ? newSet.delete(row.id) : newSet.add(row.id); return newSet; }); }} > {isExpanded ? '收起' : '显示更多'} </Button> )} </Box> ); }, }, ]; return ( <DataGrid sx={{ border: 0, '& .MuiDataGrid-columnHeaders': { backgroundColor: '#F5F6F8' }, }} isRowSelectable={() => false} autoHeight getRowHeight={() => 'auto'} loading={false} columns={columns} rows={mockData || []} rowCount={5} page={currentPage} onPageChange={(newPage) => setCurrentPage(newPage)} onPageSizeChange={setPageSize} pageSize={pageSize} rowsPerPageOptions={[25, 50, 100]} /> ); }; export default YourComponent;
关键说明
- 两个Typography必须保持完全一致的样式(行高、字体、宽度等),否则高度对比会出错
- 使用
useEffect监听row.details变化,确保内容更新时重新判断是否超行 - 用
Set存储展开行ID,避免重复状态,提升性能 - 展开后将
WebkitLineClamp设为unset,让内容完全显示
内容的提问来源于stack exchange,提问作者Jacki
相关产品推荐
相关产品推荐

