
前端UI组件【免费下载链接】handsontableJavaScript Data Grid / Data Table with a Spreadsheet Look Feel. Works with React, Angular, and Vue. Supported by the Handsontable team ⚡项目地址https://gitcode.com/gh_mirrors/ha/handsontable点击查看免费下载本指南围绕 Handsontable 的SheetsBar插件展开讲解如何在网格下方或上方渲染一个类似电子表格的标签页栏让用户在多工作表工作簿中自由切换。你将掌握插件的启用与配置、全部配置项语义、程序化工作表管理 API、跨工作表公式引用以及切换时视图状态的捕获与恢复机制。OverviewSheetsBar 是什么SheetsBar插件在网格下方渲染一个标签页栏通过position选项也可以渲染在上方。每个标签页代表一个工作表sheet点击标签页即可将网格切换到该工作表的数据。每个工作表拥有自己独立的数据、独立的配置覆盖项以及独立的运行时视图状态如滚动位置、选区。该插件默认处于禁用状态需要通过配置项sheetsBar显式启用。从源码结构看插件被划分为几个职责清晰的模块见 handsontable/src/plugins/sheetsBarsheetModel.ts纯数据模型管理工作表集合、标签顺序与活跃工作表不依赖任何 Handsontable 上下文可独立单元测试viewState.ts负责捕获与恢复每个工作表的视图状态滚动、选区、排序、筛选、合并单元格等ui/目录bar.ts栏容器、tabStrip.ts标签条、menus.ts标签菜单与全部工作表菜单、overflow.ts分页箭头控制器、tabDrag.ts拖拽排序sheetsBar.ts插件主入口注册插件、绑定快捷键、协调各模块。插件在 sheetsBar.ts 中声明了PLUGIN_KEY sheetsBar与PLUGIN_PRIORITY 910并通过布局管理器以权重LAYOUT_WEIGHT 50注册到网格的顶部或底部布局槽位。启用 Sheets Bar将sheetsBar设置为true插件会把网格现有的数据包装成名为Sheet1的单一工作表也可以传入一个包含sheets数组的对象一次性定义整个工作簿。JavaScript 版示例完整代码见 javascript/example.jsimport Handsontable from handsontable/base; import { registerAllModules } from handsontable/registry; // Register all Handsontables modules. registerAllModules(); const container document.querySelector(#example); new Handsontable(container, { sheetsBar: { sheets: [ { name: Budget, data: [ [Marketing, 4200, 5100], [Engineering, 18700, 19200], [Operations, 3100, 2900], ], settings: { colHeaders: [Category • A, Q1 2026 • B, Q2 2026 • C], }, }, { name: Notes, data: [ [Reviewed by Ana García on 2026-03-14], [Pending sign-off from Finance], ], settings: { colHeaders: [Note • A], }, }, ], activeSheet: 0, }, rowHeaders: true, colHeaders: true, stretchH: all, height: 240, licenseKey: non-commercial-and-evaluation, });React 版示例完整代码见 react/example.jsximport { HotTable } from handsontable/react-wrapper; import { registerAllModules } from handsontable/registry; // register Handsontables modules registerAllModules(); const ExampleComponent () { return ( HotTable sheetsBar{{ sheets: [ { name: Budget, data: [ [Marketing, 4200, 5100], [Engineering, 18700, 19200], [Operations, 3100, 2900], ], settings: { colHeaders: [Category • A, Q1 2026 • B, Q2 2026 • C], }, }, { name: Notes, data: [[Reviewed by Ana García on 2026-03-14], [Pending sign-off from Finance]], settings: { colHeaders: [Note • A], }, }, ], activeSheet: 0, }} rowHeaders{true} colHeaders{true} stretchHall height{240} licenseKeynon-commercial-and-evaluation / ); }; export default ExampleComponent;运行后点击Budget和Notes标签页即可在两个工作表之间切换点击控件可以添加新工作表。配置选项详解插件定义在 sheetsBar.ts 中的默认设置如下选项默认值说明sheetsnull初始工作簿定义省略时网格自身数据被包装为Sheet1activeSheet0初始化时激活的工作表在sheets中的索引controlstrue显示/隐藏添加工作表按钮与全部工作表菜单按钮pagingtrue标签溢出时显示/隐藏滚动箭头positionbottom栏渲染在网格上方top还是下方bottomuiContainernull自定义 UI 注入容器省略时按position渲染在布局槽位中这些默认值同时对应插件中的运行时校验器SETTINGS_VALIDATORS见 sheetsBar.tsposition只接受top或bottomcontrols/paging必须是布尔值uiContainer必须是HTMLElement或null。sheets数组中的每一项描述一个工作表包含三个字段name标签页上显示的名称。省略时工作表自动获得下一个空闲的Sheet{n}名称data工作表的源数据二维数组settings该工作表处于激活状态时应用的网格配置例如columns、colWidths。工作表未声明的设置项继续使用网格级的值。完整的配置示例const configurationOptions { sheetsBar: { // Define the initial workbook. When omitted, the grids // own data becomes a single sheet named Sheet1. sheets: [ { // Set the sheets tab label. When omitted, the sheet gets // the next free Sheet{n} name. name: Budget, // Provide the sheets source data data: [[Item, Cost], [Rent, 1200]], // Apply grid settings while this sheet is active // (for example, columns or colWidths) settings: { colWidths: 120 }, }, { name: Notes, data: [[Draft]] }, ], // Set the index (within sheets) of the sheet to activate // on initialization. Passed later through updateSettings() // with a changed value, it switches the active sheet without // rebuilding the workbook activeSheet: 0, // Show or hide the add-sheet and all-sheets menu controls controls: true, // Show or hide the tab-scrolling arrows that appear when tabs // overflow the bars width paging: true, // Render the bar above or below the grid position: bottom, // Custom container where the sheets bar UI will be injected // (optional). When omitted, the bar renders in the layout slot // the position option names. uiContainer: null, } };关于栏渲染位置的更详细说明请参阅 layout slots 指南。值得一提的是自动命名与名称约束的实现细节SheetModel见 sheetModel.ts维护着按标签顺序排列的工作表 id 列表并通过单调递增的 id 计数器分配内部 id。工作表名称最长 50 个字符MAX_SHEET_NAME_LENGTHsheetModel.ts名称截断基于Intl.Segmenter按字素簇计数因此 emoji 或带组合标记的字母只算一个字符重名时自动追加(n)后缀默认名则按Sheet{n}递增。以编程方式管理工作表通过getPlugin()获取插件实例后即可调用其 API 方法const sheetsBar hot.getPlugin(sheetsBar); // list every sheet, in tab order sheetsBar.getSheets(); // [{ id: 1, name: Budget, isActive: true }, { id: 2, name: Notes, isActive: false }] // switch the active sheet, by id or by name sheetsBar.setActiveSheet(Notes); // append a new sheet sheetsBar.addSheet(Q3 Forecast, [[Category, Q3 2026]]); // rename, copy, or remove a sheet, by id sheetsBar.renameSheet(2, Comments); sheetsBar.duplicateSheet(1); sheetsBar.removeSheet(2);各 API 的返回值语义均可在 sheetsBar.ts 的对应方法实现中验证getSheets()返回每个工作表的SheetDescriptor包含id、name和isActivesetActiveSheet(idOrName)接受 id 或唯一名称成功返回true工作表不存在、已是活跃工作表或beforeSheetTabChange监听器取消了切换时返回falseaddSheet(name?, data?, settings?)追加新工作表并返回其描述符被beforeSheetTabAdd取消时返回null。省略data时新工作表以空数据开始按 sheetModel.ts 的createEmptySheetData默认 1000 行 × 26 列单元格为null而非空字符串以便校验器与导出插件正确识别为空单元格moveSheetToIndex(id, index)将工作表移动到新的标签顺序索引被拒绝时返回falserenameSheet(id, name)重命名被拒绝名称空白、未变化、与其他工作表重名、与公式引擎中的其他表冲突或beforeSheetTabRename取消时返回falseduplicateSheet(id)复制工作表副本紧跟原表之后返回新副本的描述符被拒绝时返回nullremoveSheet(id)移除工作表被拒绝时返回false。所有 API 操作都经过插件内部统一的提交管道#commitsheetsBar.ts先运行可取消的before钩子再执行变更最后运行after钩子——这也是文档中所有before钩子返回false即可取消操作这一行为在源码层的落点。标签页菜单与交互每个标签页都带有一个菜单包含Rename重命名、Duplicate复制、Delete删除、Move left左移和Move right右移操作。打开方式点击活跃标签页上的 chevron 箭头右键点击任意标签页在非活跃标签页上第一次点击 chevron 会先切换到该工作表再次点击才打开菜单双击标签页可直接内联重命名。全部工作表菜单通过添加按钮旁的≡控件打开列出所有工作表并支持直接跳转包括因溢出而滚动出视野的工作表。此外还可以拖拽标签页来重新排序工作表。删除活跃工作表时会激活其最近的剩余相邻工作表最后一个工作表无法被删除这一约束同时体现在SheetModel.removeSheet与菜单的canRemove判定中见 sheetModel.ts。溢出分页当标签页总宽度超过栏宽时标签条末端会出现分页箭头。点击箭头可按一页滚动标签条。设置paging: false可以隐藏箭头此时标签条直接溢出。OverflowControllerui/overflow.ts监听标签条并驱动分页箭头的显隐在 RTL 布局下箭头方向会随之交换。从右到左RTL布局在 right-to-left 布局 下标签条会镜像显示第一个工作表的标签渲染在最右侧分页箭头方向互换。标签菜单中的Move left和Move right始终指代视觉方向而非底层数组顺序。这一设计在源码中有清晰体现#moveSheet计算步进时会根据hot.isRtl()取反方向sheetsBar.ts菜单中canMoveLeft/canMoveRight的判定也会在 RTL 下交换sheetsBar.ts而moveSheetToIndex接收的是模型索引而非视觉索引。使用 Sheets Bar 钩子可以在不同的工作表操作前后运行自定义代码。所有before钩子都可以通过返回false取消对应操作。可用的钩子包括beforeSheetTabChange()afterSheetTabChange()beforeSheetTabAdd()afterSheetTabAdd()beforeSheetTabRemove()afterSheetTabRemove()beforeSheetTabRename()afterSheetTabRename()beforeSheetTabMove()afterSheetTabMove()afterSheetTabStateCapture()afterSheetTabStateRestore()关于钩子的通用机制可参阅 events-and-hooks 指南。JavaScript 写法const configurationOptions { beforeSheetTabChange(oldSheetId, newSheetId) { // add your code here return false; // to block the sheet switch }, afterSheetTabChange(oldSheetId, newSheetId) { // add your code here }, beforeSheetTabRemove(sheetId) { // add your code here return false; // to block the removal }, // ... };React 写法HotTable beforeSheetTabChange{(oldSheetId, newSheetId) { // add your code here return false; // to block the sheet switch }} afterSheetTabChange{(oldSheetId, newSheetId) { // add your code here }} beforeSheetTabRemove{(sheetId) { // add your code here return false; // to block the removal }} // ... /Vue 3 写法const hotSettings { beforeSheetTabChange(oldSheetId, newSheetId) { // add your code here return false; // to block the sheet switch }, afterSheetTabChange(oldSheetId, newSheetId) { // add your code here }, // ... };钩子参数中的source标记可以区分操作的发起方SOURCE_API SheetsBar.api表示通过公开 API 发起SOURCE_UI SheetsBar.ui表示通过栏 UI 发起见 sheetsBar.ts。例如通过 UI 添加工作表后会自动切换到新表而通过 API 添加则保持静默方便脚本批量构建工作簿而不来回切换视图。每工作表的视图状态切换工作表时插件会捕获离开的工作表的以下视图状态并在下次切回时恢复滚动位置与选区排序状态筛选条件隐藏与裁剪的行/列索引合并单元格自定义边框手动调整的列宽与行高单元格 meta 的变更。从未访问过的工作表以中性视图状态打开因此不会继承上一个工作表的筛选或尺寸设置。ViewState接口见 viewState.ts完整列出了被跟踪的状态字段行/列序列、隐藏行/列、裁剪行、稀疏的[物理索引, 尺寸]对形式的列宽行高、排序配置、筛选条件、合并单元格、fixedColumnsStart、自定义边框、跟踪的单元格 meta、选区与滚动位置。捕获与恢复还通过safeBatch挂起渲染与索引缓存重算避免监听器抛异常后网格永久停止渲染。两个钩子分别在这两个时机触发afterSheetTabStateCapture每次捕获视图状态后触发afterSheetTabStateRestore每次恢复视图状态后触发。跨工作表使用公式工作表可以通过自己的settings启用Formulas插件其公式在该工作表内重新计算。公式还可以引用工作簿中的其他工作表。方法是在所有工作表之间共享同一个 HyperFormula 实例并让每个工作表在该实例中拥有自己的sheetNameconst engine HyperFormula.buildEmpty({ licenseKey: internal-use-in-handsontable, }); const configurationOptions { sheetsBar: { sheets: [ { name: Rates, data: [[0.23]], settings: { formulas: { engine, sheetName: Rates } }, }, { name: Budget, data: [[100, A1*Rates!A1]], settings: { formulas: { engine, sheetName: Budget } }, }, ], }, };在一个工作表上编辑数值会触发其他工作表中引用它的公式重新计算。例如在Rates工作表上修改 VAT 税率再回到Budget工作表VAT 与总金额会同步重算。完整的交互示例见 javascript/example2.js及其 TypeScript、React、Angular 与 Vue 版本。需要注意必须传入构建好的引擎实例HyperFormula.buildEmpty()而不是HyperFormula类本身。传入类会让每个工作表各自构建一个独立引擎而独立引擎之间无法看到彼此的数据。插件在检测到类上带有sheetName时会在控制台输出警告见 sheetsBar.ts。运行时添加的工作表——无论是通过栏上的添加按钮还是通过不带settings参数的addSheet()——都会以自身名称加入工作簿的共享引擎因此公式立即可用跨表引用也能立即到达它。通过addSheet()传入settings参数则可以自行配置新工作表。源码层面的协同逻辑值得留意#registerFormulaSheet会在工作表从未被激活前就把该表注册进共享引擎避免跨表引用在首次访问前读到#REF!重命名工作表时如果引擎绑定名称与标签名一致会同步重命名引擎表并借助 HyperFormula 自动重写既有公式引用删除工作表时则从引擎中移除对应表避免剩余公式基于已无标签页的数据继续计算。已知限制同一时间只有一个工作表的数据与设置被加载进网格通过updateSettings()传入变化了的sheets值会重建整个工作簿并丢弃运行时通过addSheet()添加的工作表。判断是否变化的依据是每个工作表的settings结构相等、且每个工作表的data是同一个数组引用见isSameSheetsList/isSameSheetConfig的实现sheetsBar.ts。因此框架包装器在每次渲染时重新发射一份新的 settings 字面量并不会触发重建只要data数组引用稳定但如果内联数据字面量在每次渲染时被重新创建就会导致重置。重建时若新工作簿仍包含当前工作表名称且activeSheet选项未变化会按名称保留活跃工作表。仅修改栏的 UI 选项controls、paging、position永远不会触发重建切换工作表会在内部调用loadData()这会清空UndoRedo插件的撤销与重做栈用setCellMeta()设置的单元格 meta 在切换往返后仍然保留但valid标志除外——校验结果由下一次校验重新计算而不是被恢复源码中通过UNTRACKED_META_KEYS new Set([valid])排除见 sheetsBar.ts栏与网格共享容器。当窗口滚动网格时未设置height且没有可滚动的祖先元素栏会跟随最后一行因此在长工作表上需要滚过数据才能看到它。要让栏保持可见请为网格设置显式height或将其放入带固定高度与overflow: auto的容器中。Pagination栏行为相同。相关键盘快捷键每个标签页在 Tab 顺序中占一个焦点停靠点WindowsmacOS操作Enter/SpaceEnter/Space切换到聚焦标签页对应的工作表在活跃标签页上打开标签菜单方向键方向键沿标签条移动焦点Home/EndHome/End将焦点移动到第一个或最后一个标签页EscapeEscape关闭打开的菜单、取消内联重命名或取消正在进行的拖拽快捷键在插件自己的 shortcut context 中注册见#registerShortcutssheetsBar.ts仅在焦点位于标签条上时生效并通过焦点作用域#registerFocusScope与网格的 Tab 遍历集成。使用菜单中的Move left/Move right操作可以从键盘重新排序工作表使用Rename操作可以重命名工作表。相关 API 参考配置选项sheetsBar钩子afterSheetTabAddafterSheetTabChangeafterSheetTabMoveafterSheetTabRemoveafterSheetTabRenameafterSheetTabStateCaptureafterSheetTabStateRestorebeforeSheetTabAddbeforeSheetTabChangebeforeSheetTabMovebeforeSheetTabRemovebeforeSheetTabRename插件SheetsBar源码与测试插件主实现handsontable/src/plugins/sheetsBar/sheetsBar.ts工作表数据模型handsontable/src/plugins/sheetsBar/sheetModel.ts视图状态捕获/恢复handsontable/src/plugins/sheetsBar/viewState.tsUI 模块handsontable/src/plugins/sheetsBar/ui/单元测试handsontable/src/plugins/sheetsBar/tests/覆盖激活流程、drop 索引、SheetModel、视图状态、标签拖拽等赞分享前端UI组件【免费下载链接】handsontableJavaScript Data Grid / Data Table with a Spreadsheet Look Feel. Works with React, Angular, and Vue. Supported by the Handsontable team ⚡项目地址https://gitcode.com/gh_mirrors/ha/handsontable点击查看免费下载相关推荐Luckysheet多工作表管理实现Excel级工作簿功能Luckysheet多工作表管理实现Excel级工作簿功能 你是否还在为Web表格工具缺乏多工作表管理能力而烦恼是否需要在多个数据集间快速切换和关联分析本前端UI组件Rocket Redis快捷键与高效操作10个提升工作效率的实用技巧Rocket Redis快捷键与高效操作10个提升工作效率的实用技巧 Rocket Redis作为一款美观的Redis GUI工具不仅提供了直观的界面管理RNearPy社区贡献指南如何参与开源项目开发与维护NearPy社区贡献指南如何参与开源项目开发与维护 你是否对高维向量空间的快速最近邻搜索技术感兴趣想要为Python机器学习框架NearPy贡献自己的力量吗上一篇从零开始使用ChongqingAscend/deit-base-distilled-patch16-224环境配置与依赖安装指南 下一篇amis-ui 背景色工具类全解bg-* 系列 class 的取值、状态/响应式变体与源码实现创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考