
Refine v5 Mantine 高级表格实战基于 refinedev/react-table 实现筛选、排序、行内编辑与批量删除【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine本指南基于 Refine 官方示例 table-mantine-advanced以refinedev/react-table适配器为技术底座完整讲解如何在 Mantine 项目中搭建一张生产级高级数据表格列筛选Column Filtering、列排序Column Sorting、行展开预览、行内编辑Inline Edit以及批量选择删除Bulk Delete。读完本文你将掌握refinedev/react-table与 TanStack TableReact Table的组合模式并能在自己的 Refine v5 项目中直接复用这些能力。背景从基础表格到高级表格Refine 官方文档将 Mantine 表格示例分为两个层级基础示例演示如何用refinedev/react-table适配器 MantineTable组件渲染一个带服务端分页的基础表格高级示例在基础示例之上把删除、编辑和筛选三种能力同时放进同一张表格中。本篇文章聚焦的正是高级示例。它说明了一个核心观点Refine 允许你借助refinedev/react-table适配器直接使用 TanStack Table原 React Table的全部特性同时管理服务端数据操作分页、排序、筛选都走 Refine data provider 打到后端 API形成前端表格交互 服务端数据流的双向同步。适配器的职责划分从源码 packages/react-table/src/useTable/index.ts 可以看到useTable返回的对象被明确拆成两部分export type UseTableReturnTypeTData extends BaseRecord BaseRecord, ... { reactTable: TableTData; // TanStack Table 实例 refineCore: useTableReturnTypeCoreTData, TError; // Refine 核心数据流 };也就是说refinedev/react-table的useTable内部同时调用了 Refine Core 的useTableCore负责数据请求、分页、筛选、排序的服务端状态和 TanStack 的useReactTable负责表格 UI 的客户端状态并用useEffect把两边状态双向同步表格的pagination变化 → 调用setCurrentPage更新 Refine 当前页见 index.ts表格的sorting变化 → 转换为CrudSorting并调用setSorters见 index.ts表格的columnFilters变化 → 通过columnFiltersToCrudFilters转换为CrudFilter[]并调用setFilters见 index.ts。关键点在于manualSorting与manualFiltering的取值当refineCoreProps.filters?.mode/sorters?.mode为server默认值时适配器会关闭 TanStack 的客户端排序/筛选行模型把状态交给 Refine 走服务端请求反之若设为client则启用getSortedRowModel/getFilteredRowModel做纯前端处理。这正是高级表格里筛选、排序能打到 API 的底层原理。示例工程速览示例代码位于 examples/table-mantine-advanced关键文件结构如下examples/table-mantine-advanced/ ├── src/ │ ├── App.tsx # Refine 根组件、路由与主题 │ ├── main.tsx │ ├── components/ │ │ ├── index.ts │ │ └── table/ │ │ ├── columnFilter.tsx # 列筛选弹层组件 │ │ ├── columnSorter.tsx # 列排序图标组件 │ │ └── index.ts │ ├── interfaces/ │ │ └── index.d.ts # IPost / ICategory 类型 │ └── pages/ │ ├── index.ts │ └── posts/ │ ├── index.ts │ └── list.tsx # 高级表格主实现 ├── package.json └── vite.config.ts示例使用refinedev/simple-rest作为 data provider数据源为https://api.fake-rest.refine.dev见 App.tsx。路由上只注册了一个posts资源list: /posts并开启了syncWithLocation与warnWhenUnsavedChanges两个全局选项见 App.tsxRefine routerProvider{routerProvider} dataProvider{dataProvider(API_URL)} notificationProvider{useNotificationProvider} resources{[{ name: posts, list: /posts }]} options{{ syncWithLocation: true, warnWhenUnsavedChanges: true, }} 核心依赖见 package.jsonrefinedev/core^5.0.12Refine 核心refinedev/react-table^6.0.1TanStack Table 适配器refinedev/mantine^3.0.2Mantine UI 集成含List、useForm、SaveButton等tanstack/react-table^8.2.6表格引擎uiw/react-md-editor用于 Markdown 内容的编辑与预览。数据模型与列定义示例数据类型定义在 interfaces/index.d.tsexport interface ICategory { id: number; title: string; } export interface IPost { id: number; title: string; content: string; status: published | draft | rejected; category: { id: number }; }在 list.tsx 中列定义通过React.useMemo生成使用 TanStack Table v8 的ColumnDefIPost[]类型。各列要点列 id说明关键配置selection复选框 展开按钮 批量删除按钮enableSorting: false、enableColumnFilter: falseid主键列默认排序/筛选title标题列参与筛选meta: { filterOperator: contains }category.id分类外键列展示时映射为分类标题enableColumnFilter: falsecell中通过table.options.meta读取分类数据createdAt创建时间列cell用DateField渲染formatLLLactions行内编辑/删除按钮enableColumnFilter: false、enableSorting: falsemeta.filterOperator是连接 TanStack 列筛选与 RefineCrudFilter的关键columnFiltersToCrudFilters会读取列的meta.filterOperator把标题包含某关键词这类语义转换为{field: title, operator: contains, value: ...}的服务端筛选条件。不配置时默认使用eq。列排序与列筛选组件高级表格的表头把排序、筛选做成独立的可复用组件放在 components/table 下。ColumnSorter一键排序columnSorter.tsx 只做一件事根据column.getCanSort()判断该列是否可排序然后渲染一个ActionIcon点击触发column.getToggleSortingHandler()。图标根据column.getIsSorted()状态切换未排序 →IconSelector中性灰升序asc→IconChevronDown降序desc→IconChevronUp。由于适配器默认manualSorting: true服务端模式点击排序图标后useTable的同步useEffect会把排序状态转换为CrudSorting并写入 Refine 核心最终由 data provider 生成?sorttitleorderasc之类的查询参数请求后端同时若非首页则会自动setCurrentPage(1)回到第一页见 index.ts。ColumnFilter弹层筛选columnFilter.tsx 是一个 MantineMenu弹层若column.getCanFilter()为 false直接返回null打开时把column.getFilterValue()存入本地state在弹层内通过TextInput修改点击对勾保存时调用column.setFilterValue(state.value)点击叉号清空时调用column.setFilterValue(undefined)若列定义了自定义的meta.filterElement组件则优先渲染该组件renderFilterElement分支否则回退到TextInput。保存筛选后适配器通过columnFiltersToCrudFilters把列筛选转成CrudFilter[]并同步给 Refine 核心从而触发携带筛选参数的服务端查询。这解释了示例中title列配置filterOperator: contains后输入关键词即能实现模糊查询的原因。主列表页组装高级表格主实现集中在 list.tsx。下面按功能拆解。useTable 的接入const { reactTable: { setOptions, getAllColumns, getHeaderGroups, getRowModel, resetRowSelection, }, refineCore: { tableQuery: { data: tableData }, setCurrentPage: setCurrent, pageCount, currentPage: current, }, } useTableIPost({ columns, getRowId: (originalRow) originalRow.id.toString(), });这里getRowId用id的字符串形式作为行唯一标识是 TanStack 行选择row selection能正确工作的前提。表格渲染时表头遍历getHeaderGroups()对每个 header 渲染flexRender(header.column.columnDef.header, header.getContext())并附加ColumnSorter与ColumnFilter表体遍历getRowModel().rows每个单元格通过flexRender(cell.column.columnDef.cell, cell.getContext())渲染底部用 MantinePagination组件对接 Refine 的分页状态total{pageCount}、page{current}、onChange{setCurrent}。关联数据分类标题的两种获取方式由于IPost.category只存了id表格展示时需要把分类 id 映射为分类标题。示例展示了两种数据获取方式行渲染用useMany从当前页tableData.data收集category.id集合调用useMany批量拉取见 list.tsx再通过setOptions把结果塞进table.options.meta供category.id列的cell读取const categoryIds tableData?.data?.map((item) item.category.id) ?? []; const { result: categoriesData } useManyICategory({ resource: categories, ids: categoryIds, queryOptions: { enabled: categoryIds.length 0 }, }); setOptions((prev) ({ ...prev, meta: { ...prev.meta, categoriesData }, }));行内编辑下拉用useSelect行内编辑的分类下拉框需要完整分类列表因此用useSelectresource: categories、pagination: { mode: server }拉取并把selectProps展开到 MantineSelect上。行展开Markdown 内容预览selection列的第二格是一个展开按钮点击调用row.toggleExpanded()当row.getIsExpanded()为 true 时在数据行下方插入一个colSpan等于可见列数的tr用uiw/react-md-editor以previewpreview模式渲染row.original.content见 list.tsx。这样不必进入详情页即可预览长文本内容。行内编辑useForm 驱动行内编辑通过 Refine Mantine 的useForm实现const { refineCore: { id, setId }, getInputProps, saveButtonProps, } useFormIPost({ refineCoreProps: { redirect: false, // 保存后不跳转 action: edit, // 复用编辑动作 }, initialValues: { title: , category: { id: }, content: }, });在表体渲染时若当前行的id等于useForm的id则渲染renderEditRow(row)——一个包含TextInput标题、Select分类、SaveButton与Cancel按钮的编辑行Cancel按钮通过setId(undefined)退出编辑态见 list.tsx。触发编辑入口是actions列里的EditButtonEditButton hideText onClick{() { setId(getValue() as number); }} /这里EditButton不再走路由跳转而是把行 id 写入useForm的id实现就地编辑。配合App.tsx里的warnWhenUnsavedChanges: true未保存时切换行会得到未保存变更提醒。批量删除useDeleteMany表头复选框与行复选框通过 TanStack 的行选择 API 联动Checkbox checked{table.getIsAllRowsSelected()} indeterminate{table.getIsSomeRowsSelected()} onChange{table.getToggleAllRowsSelectedHandler()} /当有部分行被选中时表头出现红色Delete按钮点击后取出table.getSelectedRowModel().flatRows的 id 列表交给useDeleteManyconst { mutate } useDeleteManyIPost(); const deleteSelectedItems (ids: number[]) { mutate( { resource: posts, ids }, { onSuccess: () { resetRowSelection(); } }, ); };删除成功后调用resetRowSelection()清空选择状态见 list.tsx。useDeleteMany会按 data provider 的deleteMany实现发出批量删除请求并自动刷新列表数据。运行示例在仓库根目录使用 pnpm 安装依赖并启动开发服务器pnpm install pnpm --filter table-mantine-advanced dev示例本身也在package.json中提供了dev/build/start脚本refine dev/refine build/refine start。Node 版本要求20。数据来自公开的 fake REST APIhttps://api.fake-rest.refine.dev无需本地后端即可体验完整的服务端分页、排序与筛选。小结通过refinedev/react-table适配器Refine 把 TanStack Table 的客户端交互能力与 Refine 的服务端数据流无缝衔接列排序TanStack 排序状态 →CrudSorting→ data provider 查询参数列筛选列meta.filterOperator 筛选值 →CrudFilter[]→ 服务端过滤行内编辑useForm(action: edit)setId就地切换编辑行无需路由跳转批量删除TanStack 行选择 useDeleteManyresetRowSelection形成完整闭环。高级示例的全部实现细节都可在 examples/table-mantine-advanced 中查看适配器的状态同步逻辑则见 packages/react-table/src/useTable/index.ts。以此为模板你可以快速为自己的管理后台构建带筛选、排序、行内编辑和批量操作的 Mantine 数据表格。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考