You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NextJs整合shadcn/ui data-table与sheet组件遇布局异常求助

问题解决思路:表格行点击弹出Sheet导致布局异常

问题根源

HTML表格对DOM结构有严格要求:<tbody>的直接子元素必须是<tr>。你当前把<Sheet>作为<TableBody>的子元素,相当于在表格行外层套了额外容器节点,浏览器无法正确解析表格结构,最终导致所有单元格挤入第一列的布局异常。

解决方案

不要用SheetTrigger直接包裹<TableRow>,而是通过状态控制Sheet的显隐,将Sheet组件移出表格结构之外,避免破坏表格的DOM层级。

修改后的代码示例

"use client";

import { useState } from "react";
import {
    ColumnDef,
    flexRender,
    getCoreRowModel,
    useReactTable,
} from "@tanstack/react-table";

import {
    Table,
    TableBody,
    TableCell,
    TableHead,
    TableHeader,
    TableRow,
} from "@/components/ui/table";

import {
    Sheet,
    SheetContent,
    SheetDescription,
    SheetHeader,
    SheetTitle,
    SheetClose,
} from "@/components/ui/sheet";

interface DataTableProps<TData, TValue> {
    columns: ColumnDef<TData, TValue>[];
    data: TData[];
}

export function DataTable<TData, TValue>({
    columns,
    data,
}: DataTableProps<TData, TValue>) {
    const table = useReactTable({
        data,
        columns,
        getCoreRowModel: getCoreRowModel(),
    });
    // 维护当前打开Sheet的行ID
    const [openSheetId, setOpenSheetId] = useState<string | null>(null);
    // 根据ID获取当前行的数据
    const currentRowData = data.find(row => row.id === openSheetId);

    return (
        <div className="rounded-md border">
            <Table>
                <TableHeader>
                    {table.getHeaderGroups().map((headerGroup) => (
                        <TableRow key={headerGroup.id}>
                            {headerGroup.headers.map((header) => {
                                return (
                                    <TableHead key={header.id}>
                                        {header.isPlaceholder
                                            ? null
                                            : flexRender(
                                                  header.column.columnDef.header,
                                                  header.getContext()
                                              )}
                                    </TableHead>
                                );
                            })}
                        </TableRow>
                    ))}
                </TableHeader>

                <TableBody>
                    {table.getRowModel().rows?.length ? (
                        table.getRowModel().rows.map((row) => (
                            <TableRow
                                key={row.id}
                                data-state={row.getIsSelected() && "selected"}
                                // 点击行时设置要打开的Sheet ID
                                onClick={() => setOpenSheetId(row.id)}
                                // 添加鼠标悬停效果,提示可点击
                                className="cursor-pointer hover:bg-muted/50"
                            >
                                {row.getVisibleCells().map((cell) => (
                                    <TableCell key={cell.id}>
                                        {flexRender(
                                            cell.column.columnDef.cell,
                                            cell.getContext()
                                        )}
                                    </TableCell>
                                ))}
                            </TableRow>
                        ))
                    ) : (
                        <TableRow>
                            <TableCell
                                colSpan={columns.length}
                                className="h-24 text-center"
                            >
                                No results.
                            </TableCell>
                        </TableRow>
                    )}
                </TableBody>
            </Table>

            {/* 将Sheet放在表格之外,避免破坏表格结构 */}
            {openSheetId && (
                <Sheet open={!!openSheetId} onOpenChange={(open) => !open && setOpenSheetId(null)}>
                    <SheetContent>
                        <SheetHeader>
                            <SheetTitle>行详情:{currentRowData?.name}</SheetTitle>
                            <SheetDescription>
                                这是ID为{openSheetId}的行数据详情,可根据需求自定义内容。
                            </SheetDescription>
                        </SheetHeader>
                        <div className="mt-4">
                            {/* 这里渲染当前行的具体数据 */}
                            {columns.map(col => (
                                <div key={col.id} className="mb-2">
                                    <span className="font-medium">{col.columnDef.header as string}:</span>
                                    <span className="ml-2">{currentRowData?.[col.id as keyof TData]}</span>
                                </div>
                            ))}
                        </div>
                        <SheetClose className="mt-6">关闭</SheetClose>
                    </SheetContent>
                </Sheet>
            )}
        </div>
    );
}

关键修改说明

  1. 状态管理:通过openSheetId记录当前要打开的行ID,控制Sheet的显示与隐藏。
  2. 结构调整:Sheet组件从<TableBody>中移出,放在表格外层,完全不影响表格的DOM结构。
  3. 点击事件:直接在<TableRow>上绑定onClick事件触发Sheet打开,替代SheetTrigger的包裹方式。
  4. 数据匹配:根据openSheetId从数据源中找到对应行的数据,渲染到Sheet中展示详情。

额外优化建议

  • 如果需要支持多行选中或批量操作,可调整状态为数组存储选中ID,但单Sheet场景下单个ID足够。
  • 为可点击的表格行添加cursor-pointer和悬停样式,提升用户体验。
  • 可以为Sheet添加关闭按钮或点击遮罩层关闭的逻辑,增强交互性。

内容的提问来源于stack exchange,提问作者Simo D.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 21:14:55