news 2026/9/17 12:56:01

NocoBase SortHandle 组件详解:Table 拖拽排序手柄的用法与底层原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
NocoBase SortHandle 组件详解:Table 拖拽排序手柄的用法与底层原理

NocoBase SortHandle 组件详解:Table 拖拽排序手柄的用法与底层原理

【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase

SortHandle是 NocoBase@nocobase/client-v2包中Table组件拖拽排序功能的核心拖拽手柄(drag handle),它通过@dnd-kit/sortable提供的 activator 能力触发整行拖拽。本文基于 sort-handle.md 并结合 client-v2 源码,讲解 SortHandle 的用法、API、与Table/SortableRow的协作机制,以及自定制手柄列时的最佳实践。

SortHandle 是什么

SortHandleTable拖拽排序的拖拽手柄。在 NocoBase 的设置页表格场景中,拖拽排序是行级操作:用户按住手柄(默认是一行MenuOutlined图标),将行拖到目标位置后松手,onSortEnd(from, to)回调被触发,由调用方持久化排序结果。

从 源码实现 看,SortHandle本身并不包含任何拖拽逻辑:

export const SortHandle: React.FC<{ id?: string | number; style?: React.CSSProperties }> = (props) => { const { id: _id, ...otherProps } = props; const dragSortContext = React.useContext(DragSortRowContext); return ( <span ref={dragSortContext?.setActivatorNodeRef} {...dragSortContext?.attributes} {...dragSortContext?.listeners} {...otherProps} className={classNames(sortHandleClass)} > <MenuOutlined /> </span> ); };

关键点在于:它通过React.useContext(DragSortRowContext)读取父级行上下文,把setActivatorNodeRefattributeslisteners挂到自身<span>上,从而成为行的拖拽激活点(activator)。因此,SortHandle必须放在Table的可拖拽行上下文里;单独渲染它不会触发拖拽。

基本用法

通常来说,直接给 Table 传isDraggable就够了,组件会自动在行首渲染一个手柄列;只有当你要自己控制手柄所在列时,才需要直接使用SortHandle

import { SortHandle, Table } from '@nocobase/client-v2'; <Table rowKey="id" isDraggable showSortHandle={false} columns={[ { key: 'sort', width: 40, render: () => <SortHandle /> }, ...columns, ]} />;

这个示例的要点:

  • isDraggable开启拖拽排序能力,Table内部会自动把components.body.row替换为SortableRow,并用DndProvider+SortableContext包裹表格主体;
  • showSortHandle={false}关闭Table默认自动插入的拖拽手柄列(源码见 Table.tsx 中的showStandaloneHandleColumn判断);
  • 你自己定义一个宽度为40的列,在render中渲染<SortHandle />,手柄就出现在你指定的这一列里。

SortHandle需要放在Table的可拖拽行上下文里。单独渲染它不会触发拖拽。

工作原理:DragSortRowContext 上下文链

SortHandle之所以能"躺着"触发拖拽,是因为它消费了由SortableRow提供的 DragSortRowContext:

type DragSortRowContextValue = { attributes?: DraggableAttributes; listeners?: SyntheticListenerMap; setActivatorNodeRef?: (node: HTMLElement | null) => void; }; export const DragSortRowContext = React.createContext<DragSortRowContextValue | null>(null);

SortableRow是 antd Tablecomponents.body.row的拖拽实现(对应文档 sortable-row.md),它使用useSortable({ id })建立行级拖拽能力,并读取 antd 注入到<tr>上的data-row-key作为排序 id:

const id = props['data-row-key']?.toString(); const { setNodeRef, setActivatorNodeRef, attributes, listeners, active, over } = useSortable({ id });

随后它用DragSortRowContext.ProviderlistenersattributessetActivatorNodeRef提供给行内的所有后代组件:

<DragSortRowContext.Provider value={{ listeners, attributes, setActivatorNodeRef }}> <tr ref={(node) => { setNodeRef(node); }} {...others} className={...} /> </DragSortRowContext.Provider>

这样SortHandle就可以放在行的任意单元格中(通常是专门的第一列),而不必非得是整个行都可拖拽。整条链路是:

  1. Table开启isDraggable后,tableComponentscomponents.body.row替换为SortableRow(Table.tsx);
  2. TableDndProvider+SortableContext items={itemKeys}包裹表格,itemKeysrowKeydataSource逐行读取生成(Table.tsx);
  3. 行渲染时SortableRow通过useSortable注册自己,并通过 Context 把拖拽能力下发给SortHandle
  4. 用户按住SortHandle拖动,TablehandleDragEnd解析from/to记录并调用onSortEnd(from, to)(Table.tsx)。

组件还通过DndProvider(来自@nocobase/flow-engine)暴露的onDragStart在拖拽开始时用snapshotSourceRow快照源<tr>,配合DragOverlay渲染行拖拽预览(Table.tsx),拖拽过程中行高亮使用主题色token.colorPrimary生成上下边框指示落点(SortableRow.tsx)。

API

SortHandle的对外参数如下:

参数类型说明
idstring \| number保留参数,当前不会参与渲染
styleReact.CSSProperties自定义样式

其中id在源码中被显式解构为_id后丢弃(const { id: _id, ...otherProps } = props),即它是为未来扩展预留的参数,目前不影响任何行为。style等其余 props 会通过...otherProps透传到内部<span>上,可用于自定义手柄的大小、颜色、间距等。

与其他组件的协作

Table的协作

TableisDraggable开启时按以下规则决定手柄渲染位置(Table.tsx):

  • rowSelectionshowSortHandle为真:手柄渲染在选择列单元格内(showHandleInSelection);
  • rowSelectionshowSortHandle为真:自动在行首插入一个宽度为sortHandleColumnWidth(默认40)的独立手柄列(showStandaloneHandleColumn),列的render直接返回<SortHandle />(Table.tsx);
  • showSortHandle={false}:不渲染任何默认手柄,完全由你自定义。

也就是说,文档示例中showSortHandle={false}加自定义列的方式,本质上是把"手柄放哪一列"的控制权从Table手里拿回来。

SortableRow的协作

SortHandleSortableRow定义在同一个文件 SortableRow.tsx 中,并通过packages/core/client-v2/src/components/form/table/dnd/index.ts统一导出。在 flow 引擎的表格模型里,这两个组件也被复用:dragSortComponents.tsx直接 re-exportDragSortRowContextSortHandleSortableRow,而 dragSortHooks.tsx 中的useDragSortBodyWrapper会用DndProvider+SortableContext包裹<tbody>,并在拖拽结束时调用resource.runAction('move', { sourceId, targetId, sortField })完成服务端排序持久化,随后resource.refresh()刷新列表。

相关链接

  • Table — 设置页表格,isDraggable开启拖拽排序
  • SortableRow — antd Table body row 的拖拽实现,为SortHandle提供拖拽上下文

【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 12:53:53

杭电计组实验3:多功能ALU控制码、标志位与Logisim/Verilog实现

简介&#xff1a;杭州电子科技大学计算机组成原理与系统结构课程设计的实验三「多功能ALU设计」实验报告&#xff0c;面向计算机、电子信息类专业学生及Verilog HDL入门者&#xff0c;用于运算器建模与仿真验证。压缩包内1个doc文件&#xff0c;约89KB&#xff0c;为完整实验报…

作者头像 李华
网站建设 2026/9/17 12:52:40

OData核心实战:Metadata、$select与$filter构建高效查询接口

第一次看到 OData 的请求 URL 时&#xff0c;我是有点懵的——一条查询路径&#xff0c;后面跟着$filter、$select、$orderby&#xff0c;乍一看像把 SQL 直接塞进了网址里。这其实不是错觉。我记得最早给一个内部管理系统对接 SAP 数据&#xff0c;后端为了满足前端各种列表页…

作者头像 李华
网站建设 2026/9/17 12:51:28

MariaDB ZIP包安装:data目录下mysql文件夹无法自动创建的排查与解决

先问自己一句&#xff1a;你看到的 data 目录&#xff0c;是安装程序建的&#xff0c;还是你自己动手建的&#xff1f;用 ZIP 包在 Windows 上装 MariaDB 10&#xff0c;卡在 data 目录下的 mysql 文件夹无法自动创建&#xff0c;这是我见过的高频问题里最容易被误解的一个。很…

作者头像 李华
网站建设 2026/9/17 12:45:58

STM32C5串口调试实战:从引脚映射到printf重定向

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华