Univer实战:构建可编辑区/只读区并存的在线填报表单
发布时间:2026/11/2 10:17:46 锦皓数字建站

1. 为什么我会盯上Univer数据收集场景下的表格困局做了几年前端有个需求反复出现让用户在一张表格里填写数据但表格里只有几个特定的单元格可以编辑其余区域必须锁死防止误改公式、误删表头、破坏模板结构。以前的做法无非两条路。一是接入表单工具把数据收集做成一个个输入框简单但表现力有限二是自己写一套在线表格从选区交互到单元格渲染全手撸工程量极大维护成本更高。直到我接触了Univer——一个开源、支持在线、能自定义表格交互的办公套件底层引擎才觉得这个困局终于有了解法。Univer 最吸引我的点恰恰对应了它的一个核心能力它支持用户定义表格然后让用户去填写一些单元格其他的单元格用户无法修改。这句话听起来平淡落到产品上就是“可编辑区/只读区并存的在线填报表单”能直接覆盖信息收集、数据登记、任务分配等大量真实场景。这篇文章不打算堆官方文档我从前后端工程视角聊聊 Univer 到底怎么用、权限锁定怎么配、会遇到哪些坑以及为什么我觉得它是目前同类方案里最值得投入的一个。适合正在做在线表格、低代码表单、数据填报类产品的开发者参考。2. Univer 是什么不止是又一个在线表格2.1 定位面向开发者的办公套件底层引擎Univer 是一套支持电子表格、文档、幻灯片的开源办公套件底层做了非常彻底的多端适配和插件化设计。对大多数开发者来说接触最多的场景是它的电子表格能力也就是 Spreadsheet 引擎。把它理解成一个“表格基础设施”可能更准确。Univer 提供了渲染层、交互层、公式引擎、数据模型、协作能力等一套体系开发者不需要纠结单元格怎么画、选区怎么处理、滚动怎么优化这些底层工作 Univer 已经完成你只需要关注业务层的数据和交互。在开源生态里常见的表格库大多只解决“展示”和“基础编辑”Univer 想做的是把 Excel 级别的复杂能力搬到 Web 上并且把这些能力以插件的形式解耦让你按需引入。这个定位决定了它特别适合做中后台系统、低代码平台、数据分析工具这类需要“嵌入一个完整表格”的项目。2.2 为什么选择 Univer 而不是从零造轮子我曾经有过一个表格需求只涉及几列数据、几十行内容但做下来发现从选区管理、键盘导航、单元格编辑到撤销重做、复制粘贴工程量远超预期。真正的复杂不在“画一个表格”而在“像一个表格软件一样工作”。Univer 的插件架构类似 VS Code核心包只负责骨架命令系统、公式、条件格式、协同编辑等都以插件形势注册进来。这种设计的价值在于你可以先用最小模型跑通一个表格再逐步把能力“插”进去。如果你只是要一个静态展示表格用普通渲染就够如果要做一套在线填报、数据联动、权限分区的系统Univer 能提供成熟的基础设施。选型的判断标准可以简单一点你对表格交互复杂度有要求吗有就值得上 Univer没有别为复杂度买单。2.3 Univer 在技术架构上做了什么从技术架构来看Univer 有几个关键特性值得聊。首先是多端渲染能力它通过同一套逻辑层适配 DOM 和 Canvas 渲染性能上有保障其次是插件化设计官方提供了丰富插件社区也能基于这套架构扩展再者是数据模型与视图分离的设计这让协同编辑和本地操作可以保持在同一个数据层上。我的感觉是Univer 正处于一个快速迭代期功能覆盖面越来越接近“网页版的 Excel 引擎”但在一些边界功能上仍在打磨。选它的项目最好接受“跟着版本走”的节奏而不是期待一个彻底稳定的老牌框架。这也是我在后面会重点强调版本策略的原因。3. 核心场景拆解数据收集中的“只读表格”需求3.1 一个真实的产品痛点填报表单还是在线表格产品经理常给我提两类需求。一类是“问卷式填表”用表单工具就能做另一类是“参照型填报”——用户在填写时能看到旁边的参考数据、计算逻辑、历史记录同时只能修改指定的输入区这就是表单工具的短板了。举个典型例子企业做年度预算填报各个部门在同一张预算表上填写本部门的预估费用。表格的左边是科目名称中间是去年的实际数右边是今年要填的预算数。实际数不能改科目结构不能被删部门只能填自己那块区域的预算单元格。用表单工具做会失去“一张表看全局”的感觉用 Excel 分发收集Excel 的“允许用户编辑区域”功能又很难 Web 化。这种场景本质上是“表格 权限控制”的组合。Univer 对表格的底层支持加上保护机制的配置能力让这个需求可以在一个统一框架里完成。3.2 “允许编辑区域”与“工作表锁定”的关系Excel 里有一个经典机制工作表保护 单元格锁定状态。默认情况下所有单元格都是“锁定”状态一旦开启工作表保护整个表都不能编辑你需要先在保护开启之前把某个区域设置为“不锁定”保护开启后这些区域依然能改。Univer 沿用了类似的思路。它提供了工作表保护配置可以在保护工作表的同时指定允许编辑的区域。当你拿到一个 Univer 实例后可以给某个 worksheet 设置保护再添加 selectionAllowList也就是允许选择的区域范围。落在产品上就是“锁定所有单元格但放开指定的几个区域”。这里有个很关键的细节保护工作表本身不等于配置了编辑区。如果开了保护但没有设置任何允许编辑的区域用户就真的一个格子都动不了反过来如果忘了开启保护所有区域都是可编辑的。这两个配置必须成对出现这也是我调试过程中遇到最典型的“看起来设置了权限但没生效”的原因。3.3 为什么这种模式比“禁用单元格”更可靠直接用样式把单元格置灰、或者监听编辑事件后拒绝输入是很多初级方案的做法。但这种方式有天然缺陷样式可以伪造事件拦截可能存在时序问题数据层本身没有被真正保护。Univer 的保护机制是落在数据模型层的。它不只是视觉上“看起来不能改”而是从命令系统层面拒绝对受保护区域的修改。也就是说即使用户通过控制台操作或者外部脚本尝试绕过交互层数据模型也不会接受这些变更。这本质上是一种“权限校验前置”的策略比事后检查要靠谱得多。在真实业务里这个区别很重要。你收集到的数据一旦要入库、入报表就不能只依赖前端交互层面的软限制而需要一个硬性的、从数据层就能保证的边界。Univer 的保护机制给了这个边界。4. 快速跑通在项目里集成 Univer 并渲染出一个可编辑表格4.1 环境准备与依赖安装在正式搭表格之前先确认基础环境。Univer 是 TypeScript 项目基于主流前端框架都能接入实际使用中其团队维护了一个比较完整的独立包不用额外引入太复杂的生态依赖。我以一个常见的 Vite Vue 项目为例先安装依赖。Univer 的近期版本将不同的能力拆到了多个包中至少需要关注以下几个模块npm install univerjs/core univerjs/engine-render univerjs/engine-formula univerjs/ui univerjs/sheets univerjs/sheets-ui安装过程通常不会遇到太大阻力但这里有一个实际问题Univer 的插件体系版本升级较快不同包之间的版本必须保持一致否则容易出现 API 找不到的情况。我的经验是直接让包管理器安装同一个大版本避免混搭新旧版本的包。这些包各有分工core 提供核心数据模型和命令能力engine-render 负责渲染层sheets 层则承担了电子表格领域的主要业务逻辑sheets-ui 则把编辑交互界面做出来了。4.2 从初始化到渲染出第一个表格依赖装完后核心代码并不复杂。Univer 采用工厂函数先创建一个实例再注册需要使用的插件最后定位到工作簿和工作表。代码如下import { Univer } from univerjs/core; import { defaultTheme } from univerjs/design; import { UniverFormulaEnginePlugin } from univerjs/engine-formula; import { UniverRenderEnginePlugin } from univerjs/engine-render; import { UniverUIPlugin } from univerjs/ui; import { UniverSheetsPlugin } from univerjs/sheets; import { UniverSheetsUIPlugin } from univerjs/sheets-ui; const univer new Univer({ theme: defaultTheme, }); univer.registerPlugin(UniverRenderEnginePlugin); univer.registerPlugin(UniverFormulaEnginePlugin); univer.registerPlugin(UniverUIPlugin, { container: app, }); univer.registerPlugin(UniverSheetsPlugin); univer.registerPlugin(UniverSheetsUIPlugin);这段代码渲染出的表格已经具备了基础编辑能力。你会发现一个能在线操作的表格界面就这样出来了不需要自己处理任何 Canvas 细节和交互事件。整个体验就像在一个 Web 容器里嵌入了 Excel 的核心交互。初次跑通之后别急着继续加功能先打开浏览器 DevTools 看看 console 有没有版本警告。Univer 在版本不匹配时通常会直接报错这是最高频的集成问题之一早发现早处理。4.3 创建数据源从空表到有内容的业务表实际业务里你不会让用户面对一张空白表格。Univer 支持多种方式创建工作簿数据源最简单的方式是在初始化时提供快照数据。下面的workbookData可以理解为一个完整的 Excel 文件在 JSON 层面的表达const workbookData { sheets: [ { id: sheet1, name: 预算填写表, cellData: { 0-0: { v: 科目名称 }, 0-1: { v: 去年实际数 }, 0-2: { v: 今年预算数 }, 1-0: { v: 市场费用 }, 2-0: { v: 人工成本 }, } } ] }; const univer new Univer({ theme: defaultTheme, locale: zhCN }); const sheetPlugin univer.registerPlugin(UniverSheetsPlugin); univer.registerPlugin(UniverSheetsUIPlugin, { container: app, workbookData });这里可以看到Univer 的数据结构是行列坐标 - 单元格对象的映射0-0表示第 0 行第 0 列。每一格可以包含字符串、数字、公式、样式等信息。这个数据结构本质上就是电子表格的最小模型二维网格加上每个格子的内容与状态。设计数据源时我建议你先梳理“哪些单元格是用户填写的哪些是系统展示的”这会直接影响后面权限区域的配置。数据源本身就是一种领域模型把业务字段位置规划好后续权限设置就清晰了。5. 核心环节实现“只能填这几个格子”的权限配置5.1 图层保护工作表级权限的基本配置权限配置听起来很玄但核心就是两件事。第一是给工作表开启保护第二是指定哪些区域在保护之下依然允许编辑。在 Univer 中工作表保护的概念与 Excel 的工作表保护逻辑基本一致。当你为工作表设置了保护后默认所有单元格都进入“不可编辑”状态然后你再显式地将某些区域加入“允许编辑列表”这些区域就是用户能够点击、选择和输入的格子。实现上最直接的方式是通过快照数据给 sheet 添加protect配置const workbookData { sheets: [ { id: sheet1, name: 预算填写表, cellData: { // ... }, protect: { locked: true, selectionAllowList: [ { row: [1, 10], col: [2, 2] }, { row: [12, 20], col: [2, 2] }, ] } } ] };上述配置的意思是整张表保护起来但是第 1 到 10 行的第 2 列、第 12 到 20 行的第 2 列保留了编辑权限。用户点这些格子时可以输入点其他格子时无法编辑。这里我想强调的是selectionAllowList的坐标是零索引而且前闭后闭记得先做好脑海里的坐标换算。我第一次配置的时候把第 1 行当成了 Excel 里的第 1 行结果发现实际锁定的是第 2 行。5.2 进阶控制用 API 动态切换可编辑区域静态配置能应付固定模板但很多业务需要动态切换。比如管理员先锁定全表某个用户申请后临时开放几个格子的权限再比如不同角色的用户看到不同的可编辑区域。Univer 提供了命令机制和能力入口你可以通过获取工作表实例的方式来操作保护配置也可以在运行时注册自定义命令来更新protect内容。一个简化的思路是修改工作表快照中的protect.selectionAllowList然后重新应用数据。const sheet univer.getCurrentWorkbook()?.getActiveSheet(); const manager sheet?.getCommandManager(); manager?.executeCommand({ type: UPDATE_WORKSHEET_PROTECT, payload: { sheetId: sheet1, protect: { locked: true, selectionAllowList: [ { row: [1, 5], col: [2, 2] }, ] } } });实际项目中我更推荐把“当前用户可编辑区域”设计成一个接口返回值前端根据角色计算动态权限。比如管理员可以从后台配置每个部门的预算填写格子范围前端通过接口拿回来后生成allowList。这种方式比把权限写死在代码里灵活得多。需要注意Univer 的底层命令系统设计得比较严整不同版本对保护更新的 API 名称可能有差异。升级版本时要留意 changelog否则容易出现命令注册上的问题。5.3 公式联动用户填数后自动计算填报表单还有个高频需求用户填了数字旁边自动算总和、比例。Univer 内置公式引擎可以直接在单元格上设置公式表达式。cellData: { 3-2: { v: 100 }, 4-2: { v: 200 }, 5-2: { f: C3C4, }, }这里f字段表示公式。Univer 会自行完成公式计算和缓存更新不需要你手动监听输入事件再重新计算。公式引擎是独立插件如果项目不用公式可以不装一旦要装建议认真读一遍它的依赖列表因为公式引擎相关的包往往还依赖其他底层模块。公式联动在填报场景里特别有用比如“只让用户填单价和数量金额列自动计算”这不需要后端参与计算前端表格引擎已经把结果算好了。数据提交时把公式结果一并读取出来即可。5.4 设置百科用条件格式提醒用户填错除了权限锁定Univer 也支持条件格式能力。对于填报类场景条件格式可以在用户填完内容后立即给出视觉反馈例如把超过预算限制的数字标红。条件格式的配置同样可以在快照里声明。其核心是反复判断的规则集合包括范围、条件和样式三个部分的组合。如果你在做一个预算填报系统可以对“今年预算数”这一列设置规则当数值大于某个阈值时单元格底色变成红色这样用户不填到最后也能直观看到异常项。条件格式虽然不参与数据校验但它在交互反馈层面的价值很大。尤其对非技术用户视觉提示比弹窗提示友好得多。6. 实操细节与避坑指南我在 Univer 上踩过的坑6.1 单元格锁定与复制粘贴的边界一个容易被忽视的问题是保护单元格不等于禁止复制粘贴到别处也未必能阻止用户把内容粘贴进锁定区。Univer 的保护机制主要拦截的是“编辑”操作但粘贴行为在某些版本里可能绕过校验导致锁定单元格的内容被外部数据覆盖。这个问题在不同版本上的表现不一样你需要根据自己使用的版本测试确认。我当时是通读文档关注了全局粘贴拦截逻辑再在项目里加入了一层业务校验对受保护区域强制拒绝 paste 命令。虽然命令行层面的拦截存在版本差异但整个拦截过程现在看还是有必要的。不要天真地认为保护配置等于一切安全边界。6.2 核心包版本不匹配的连锁反应Univer 的版本迭代封装节奏非常快。它不像很多老牌前端库那样一个包搞定所有事而是把渲染引擎、公式引擎、UI 层、Sheets 层全部拆开成独立 npm 包。这就带来一个问题各包必须以同一版本发布一旦混装小版本界面会出现各种离奇报错。有一次我把 sheets-ui 插件升级了一个小版本但 core 包没动结果点击单元格时直接抛异常页面白屏。排查了大半天最后才发现是版本不一致导致的事件类型对不上。解决办法就是统一 package.json 里的版本号尽量让所有 Univer 相关包复用同一个版本。建议在实际项目里发版前锁定所有 Univer 包的精确版本不要用^范围。因为 Univer 主版本内的 API 变动比很多老牌库频繁等你发现某个页面在交付前还能用、验收时却崩了多半就是自动升级了依赖。6.3 大数据量渲染与交互性能问题Univer 的渲染引擎在大量单元格的场景下表现已经不错但性能并不是无上限的。当工作表有数千行、且每格带复杂样式时首次渲染仍然会有明显卡顿。我的经验是两个方向一是尽量不加载多余的行列只在数据快照里声明实际用到的单元格区域Univer 不会强制渲染全表二是如果业务上有大量静态数据考虑用只读模式展示大数据表只有需要用户交互时才开启编辑和选区。还有一个容易被忽略的点工作表初始高度和宽度也会影响性能。如果表格里有大量“无内容但有样式”的单元格建议清理无用样式因为这些空样式也是渲染层要处理的负担。6.4 协同编辑与权限控制的组合策略如果你的场景需要多人同时在线编辑那问题会更复杂。协同编辑的核心是操作转换和数据冲突处理Univer 提供了一定的协作基础但要把“协同 权限”组合好业务层面还需要仔细设计。我目前的建议是先想清楚“权限对象”的粒度。Univer 的selectionAllowList是“区域级”的它不区分当前操作者是谁。如果两个用户同时编辑同一个允许区域必然产生冲突。对于需要区分角色的场景一个好的方案是做一层前端代理根据当前登录用户在切换工作表时动态注入不同的可编辑区域配置。当然这只解决了编辑入口的问题多人同时改同一个格子还需要一套 OT 或状态合并机制这就要看 Univer 后续协同插件的完善程度了。6.5 数据提交与服务端对接表格的数据最终要提交到服务端。直接把cellData整个快照丢给后端不是不行但会很浪费。真实业务往往只需要提交用户填写的那些单元格。我的做法是在用户提交时遍历允许编辑区域把每个格子的值收集成一个普通对象再附上 sheetId、行列坐标一起发送。如果某个格子的值是公式结果需要从单元格对象里提取计算后的显示值而不是取f原始公式。在一次实际项目中我经过了后端字段映射的重新定义让上传数据结构贴近业务字段而不是原始坐标。这看起来多了一步建模但长期维护效率和后续报表对接成本都大幅下降。7. 如何基于 Univer 构建一个完整的在线填报表单7.1 带模板的思路让用户“无脑填数”把上文的能力串起来你可以构建一个非常完整的在线填报模块。产品侧的结构大致是这样管理员创建一份“表格模板”在模板里指定哪些区域是表头、哪些区域是公式、哪些区域允许填写发布后用户打开这个模板只能填写被开放的区域。这种模式和传统的动态表单相比优势在于自由度。表单通常是你定义好一堆字段然后让用户填模板表格则允许你在一个完整的表格结构里指定填写区域表达力和复用性好得多。无脑填数能极大降低用户的学习成本。给非技术用户一批表格如果没有锁定保护很容易出现“删掉公式”“改错表头”“空行插入搞乱结构”等事故配置好保护和允许编辑区域后用户唯一能动的就是输入区操作出错率直线下降。7.2 权限配置与角色模型的映射真实项目里最关键的协同配置是角色与权限的映射。管理员能编辑表头和公式部门负责人能填写本部门的预算区间普通员工只能看只读报表。设计上可以把权限配置放在后端保存前端在加载表格前发起一次权限查询然后动态构造protect配置。这样角色分配、区域范围由后台统一控制前端只是把配置翻译成 Univer 的数据模型。前端翻译时注意几个细节。边界条件务必清晰比如“第 2 到 10 行”是否需要包含 10统一用闭区间还是开区间其次如果表格里新增了行保护范围是否需要自动扩展这两点在交付前要明确否则容易留下逻辑隐患。7.3 结果集导出与打印在线填报完成后用户通常需要导出一份填写好的结果文件。Univer 对 xlsx 格式的导入导出有一定支持但要做到完全无缝建议还是要测试边界场景。我经历过的印象较深的坑是有一次导出的文件里自定义样式丢失单元格背景色和边框全变成了默认格式。追查下来发现是导出插件版本里的序列化逻辑对部分样式字段支持不全。如果你对样式还原度要求很高一定要在项目初期就做一轮导出对比测试不要等上线了再发现。打印场景同样值得留意。Univer 的 UI 插件在打印方面提供了基本能力但要控制打印分页、页眉页脚等细节仍需要依赖额外的浏览器能力或格式化工具配合。8. 常见报错与排查方法速查实际集成过程中我把高频问题整理成了一个速查表希望能帮你节省排查时间现象可能原因解决办法表格完全不渲染插件注册不全或容器元素未就绪确认所有依赖插件已注册检查container元素已经挂载点击单元格无反应Univer 包内版本混装统一所有 univerjs 包的版本保护开启后所有格子都不能编辑selectionAllowList未配置检查配置中是否填入允许编辑的区域区分零索引和闭区间允许编辑区域输入后数据不更新保护配置覆盖了事件层重读当前版本的保护 API确认没有额外配置冲突公式算不出结果公式引擎插件未注册确认engine-formula相关插件已注册并检查公式写法导出的 xlsx 样式丢失导出插件版本或能力限制更新到最新版本对样式还原要求高的场景提前验证粘贴操作绕过了保护版本未覆盖粘贴校验需求在粘贴事件层加一层拦截作为兜底这里再补充一个排查原则Univer 的 issue 区域和国际社区代码更新都比较快遇到问题时先把你锁定的版本和复现步骤写清楚再到官方 issue 列表里搜索。很多坑并不冷门只是被藏在了版本相关的标题里。9. 这套能力还能怎么扩展不只是“填表”9.1 低代码平台的表格内核低代码平台里“表格组件”通常是最难做的一块。它要支持数据绑定、字段配置、行级权限、复杂计算和联动Univer 的底层能力恰好覆盖了这些点可以让低代码平台省去开发表格内核的巨大成本。平台内部可以基于 Univer 的插件机制扩展专属能力比如自定义单元格类型、特殊校验、业务专用工具栏。把 Univer 当内核比纯组件封装更灵活。9.2 数据分析与可视化面板的数据源Univer 强的地方在于它不只是一个编辑器还可以把编辑后的数据直接作为前端数据源。有人在表格里维护一组参数另一个页面读取这些参数动态生成图表这种“表格即配置”的思路在项目里很实用。配合 Univer 的公式引擎你的数据面板还能边改边算交互起来很顺手。尤其在做内部工具时这种组合能省掉大量写表单和状态同步的时间。9.3 从表格审核到数据入库的完整闭环当表格从“工具”变成“产品”你会遇到审核流、版本对比、审批记录等需求。Univer 的数据模型可以承载这些需要因为它保持了完整的快照结构和操作日志机制绕开它你会被迫再造一套数据层。我在集成过程中逐渐感受到构建此类应用时最重要的一步是在数据结构设计阶段就建立清晰的“单元格值与业务字段”映射。一开始就做映射后续审核、入库、修改记录都能轻松落实。10. 我对 Univer 的真实感受与选型建议如果在年初有人问我做在线表格场景选什么开源方案我可能会犹豫。但现在让我再选一次我会毫不犹豫地把 Univer 放进候选前三。它的优点很明显插件化架构、活跃的社区、完整的数据模型和公式体系让它在做“类 Excel 应用”时起步速度非常快。缺点同样存在版本变更频繁、部分能力仍在上限试错、文档覆盖不均衡、协同编辑需要业务方自己补一些逻辑。对这个项目比较务实的态度是工具属性已经足够好生态属性还需要你投入产出。从实施角度给三个建议。第一正式排期前锁定版本并且写一个最小可运行的 demo 走一遍核心流程第二权限场景务必做前后端双保险不要把表格完整性完全寄托在前端配置上第三尽早确定数据提交的字段模型表格坐标和后端字段的映射越早做后期返工越少。Univer 让我比较意外的是它不仅把公式、样式、保护这些 Excel 核心概念搬到了 Web 上还通过插件体系让你能组合出一套“恰好适合业务”的表格。对正在做数据收集、填报表单、低代码表格模块的团队来说这应该是目前最值得投入的方向之一。最后再分享一个我自己在项目里保持下来的习惯每次升级 Univer 版本后第一步不是跑业务用例而是打开官方示例工程对比行为差异。这个习惯帮我规避了至少两次肉眼可见的老版本交互回退问题。希望对你有用。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。