尧图精选

开源表格引擎Univer:自定义可填区域与单元格锁定实践

🕒 发布时间:2026/10/2 9:54:33 📁 来源:尧图网络
作为一个长期做表格类产品和数据收集工具的人我最近被问爆了一个项目——Univer。不只是前端圈子在聊很多做低代码、协同办公、内部数据平台的团队也开始盯上它。热搜词条其实已经把这个项目的核心价值点出来了用户能自定义表格让用户去填一些单元格其他单元格用户无法修改在线使用。这三个词组合在一起指向一个非常真实的需求场景你给别人一张表但不希望他把不该动的地方弄乱。这种场景在财务收表、HR收集信息、供应链协同、调研问卷里太常见了。传统做法是自己写个表单或者用Excel发来发去改来改去要么灵活性差要么数据全乱。Univer正好是那种可以嵌进自己系统、让用户填指定单元格、其余区域锁死的开源在线表格引擎。这篇内容我结合自己实际接入Univer的完整过程来写把环境准备、区域保护配置、在线协作玩法以及那些文档里不会写但实操一定会碰到的坑全部过一遍。如果你也在评估用Univer做在线表格、做数据填报、做权限隔离的页面这篇文章应该能帮你省下不少弯路。1. Univer是什么不是又一个Excel网页版而是可嵌进业务的表格引擎先说结论再做拆解。Univer是一个基于TypeScript开发的开源表格/文档/幻灯片一体化协同办公方案核心亮点是Sheet表格部分可以像组件一样嵌入到你现有的前端项目里。它不像WPS在线表格、飞书表格那样是一个完整的独立产品而是一个你能二次开发、能按自己业务逻辑定制的底层引擎。1.1 和数据填报需求强相关的几个能力我研究Univer比较早在它早期还是LuckySheet的优化版本概念时就有接触后来看到它演进成这套插件化架构确实能感觉到它在下一盘大棋。贴合本文标题和热搜词Univer这几个能力是最值得关注的表格渲染与交互基于Canvas和DOM混合渲染操作手感非常接近原生Excel支持公式、合并单元格、批量样式、冻结行列、筛选排序这些基础高频操作。可嵌入性它是一个完整的npm包体系你可以用univerjs/presets一行初始化也可以只引入univerjs/sheets做纯数据展示再用univerjs/sheets-ui加界面交互。这种粒度让它的集成成本可控。单元格区域保护Protection这是和本标题最相关的能力。Univer原生支持工作表级别的保护也能配置允许编辑区域Allow Edit Ranges把这一片谁能改、那一格谁能填变成可配置数据而不是只能靠代码写死。协同编辑能力Univer的架构里预留了协同编辑协议和OTOperational Transformation变换支持配合官方协作化方案或自己写协同后端可以实现多人同时编辑一张表。1.2 为什么社区突然开始讨论Univer一个项目成为网络热词通常是因为它正好解决了一个大家都有但一直没被满足好的痛点。表格类产品的痛点是很明显的Excel能力强大但没法嵌进业务系统在线文档协作方便但自定义能力被平台锁死传统的input表单能收集数据但没法让用户看到整个表的样子也没法做表内勾稽关系校验。Univer出现的时间点很巧。低代码平台满天飞几乎每个企业内部都要做数据填报、台账管理、信息汇总大家发现这类页面本质上就是一张表一部分区域允许用户编辑与其自己从零写一个表格交互组件不如直接嵌入Univer然后用它的数据结构和权限API做业务控制。这正好解释了为什么univer支持用户定义表格然后让用户去填写一些单元格其他的单元格用户无法修改这个描述会变成核心热词——因为这就是大家最想要的东西。1.3 和竞品方案放在一张表里对比光说概念不够直观我直接把我评估过的几条路线放在一起看方案自定义表格能力单元格级锁定嵌入现有系统离线/在线协作自研Canvas表格完全自定义需从零实现完全可控均可难Luckysheet旧方案较强但维护停滞有限支持可嵌入在线为主弱HandsonTable较强支持单元格属性定制可嵌入均可部分x-spreadsheet基础够用有限可嵌入均可难Univer强插件化架构原生Protection API组件级嵌入React/Vue皆可均可架构预留可扩展我选Univer的核心原因就是它的变与不变拆得很清楚业务规则的绑定比如哪些单元格锁定、哪些可编辑通过数据和配置来完成界面交互由统一UI层处理自己的业务代码不需要去碰Canvas绘制细节。这意味着后续做权限控制、做填报逻辑、做数据校验都是在同一个数据模型里加约束不会牵一发动全身。2. 为什么用户自定义表格锁定其他单元格能做但一直不好做聊完Univer是什么回到标题里最核心的那个产品诉求。很多人第一反应是这有什么难的不就是设几个单元格可编辑其他disabled吗真正做过的都知道这里面的坑能绕死你三回。2.1 表单收集和表格填写的本质矛盾传统HTML表单收集数据页面结构是开发者定死的用户只能按顺序填。优点是数据格式可控、防错能力强缺点是用户看到的是一个表单而不是一张表上下文信息割裂。对于需要用户同时参照多列多行上下文来填写的数据表单体验是灾难。Excel式表格呢用户看到的就是完整的业务表结构和上下文填起来非常自然。但它把控制权全部交给了用户——他可以随意改公式、改表头、删行加列、输入非法数据。对于业务数据收集方来说这简直是一场噩梦我见过太多因为模板被改坏导致月底汇总数据全乱的案例。所以用户定义表格让用户去填特定单元格其他单元格锁死听起来简单实际上是两种产品思路的折中保留表格的完整上下文和自然交互同时用权限把填写和破坏隔离。这个折中需要表格引擎底层就支持区域粒度级别的数据保护不是简单给DOM元素加个readonly就能搞定的。2.2 单元格锁定的真实难度在哪里为什么很多自研表格项目最后都卡在这一步我拆开说锁定和选区的冲突用户全选CtrlA后直接打字如果引擎不处理选区内的保护单元格内容就会被覆盖。这要求引擎在键盘输入、粘贴、拖拽填充、自动填充等多个入口都强制校验区域权限。样式锁定和数据锁定的关系Excel里锁定单元格默认是格式和内容一起锁但有时候业务只希望用户不能改公式结果却允许调列宽行高。这需要引擎把保护做成颗粒状策略。合并单元格和行列维度点击一个可编辑的合并单元格时高亮选区可能覆盖到不可编辑的行列判断逻辑变复杂。复制粘贴绕过即使锁定单元格禁用了手工编辑如果用户复制一个可编辑区域再粘贴到锁定区域部分引擎并不会自动拦截。公式和引用联动如果锁定区域内有公式用户改了被引用单元格公式结果变化是否算被修改业务上经常需要保留这种联动又不能允许用户直接动公式。2.3 Univer为什么把区域保护做成了内置能力Univer的架构里Protection保护是作为数据模型层的能力存在的而不是UI层灰化的效果。这意味着保护判断发生在数据变更的事务级别不是事后校验编辑请求进来时Univer会先检查是否有权限写入目标单元格权限配置可以持久化存储比如存在JSON配置或后端数据库里花哨的锁定灰色效果只是UI反馈真正的强制逻辑在底层事务里已经拦住了。这就是我倾向于用Univer做这类产品的核心原因它在根子上就把权限数据和表格数据放在一起管比我自己写一堆拦截逻辑靠谱得多。在我的实测里Univer通过配置每个工作表的protect属性和allowRanges列表能轻松做到全表锁定仅开放指定区域可编辑这个能力我下一节详细展开。3. 把Univer接进工程依赖安装与页面初始化如果你已经决定试试这部分就看你怎么快速把Univer跑起来。我用的技术栈是React ViteUniver官方对React支持很好Vue3同样没问题逻辑基本一致。3.1 环境准备和依赖选择建一个标准前端项目后最关键是选对包。npm install univerjs/core univerjs/sheets univerjs/sheets-ui univerjs/sheets-formula univerjs/sheets-numfmt univerjs/ui这几个包各司其职univerjs/core数据模型和插件机制核心必装univerjs/sheets电子表格核心逻辑包括工作表、单元格、区域权限数据模型univerjs/sheets-ui表格界面和交互univerjs/sheets-formula公式支持如果有计算需求就装没有可以先省univerjs/sheets-numfmt数字格式univerjs/ui通用UI底层如果你用的是官方提供的一站式预设也可以用univerjs/presets里的封装一条初始化命令就能跑出完整默认界面。我建议第一次接触还是把核心包手动装一遍因为这样你对依赖关系才有概念后续排查问题不会抓瞎。3.2 初始化Univer实例初始化代码非常简单但也容易忽略一些细节。我直接贴一个能跑的最小示例import { Univer } from univerjs/core; import { defaultTheme } from univerjs/theme; import { UniverSheetsPlugin } from univerjs/sheets; import { UniverSheetsUIPlugin } from univerjs/sheets-ui; import { UniverFormulaEnginePlugin } from univerjs/engine-formula; import { UniverUIPlugin } from univerjs/ui; import { LocaleType } from univerjs/core; const univer new Univer({ theme: defaultTheme, locale: LocaleType.ZH_CN, }); univer.registerPlugin(UniverSheetsPlugin); univer.registerPlugin(UniverSheetsUIPlugin, { container: app, layout: { outerLeft: true, toolbar: true, }, }); univer.registerPlugin(UniverFormulaEnginePlugin); univer.registerPlugin(UniverUIPlugin);然后创建一张工作簿import { FWorksheet, FWorkbook } from univerjs/sheets/facade; const workbook univer.createUniverSheet({ id: mySheet, name: 业务填报模板, settings: {}, });这步之后页面里就会出现一个完整的在线表格。如果看到工具栏、列头和行号都正常渲染说明最基础的链路已经通了。接下来才进入核心怎么让特定单元格可填、其它单元格锁死。4. 让单元格只能填这一格区域保护配置实操这一节是全文重点也是标题里最核心的能力。Univer的区域保护配置我实际用下来基本等于Excel里的允许用户编辑区域的增强版本你可以设置一个白名单区域列表白名单之外全部不可编辑。4.1 用配置开启工作表保护Univer的Sheet数据模型里有protect字段置为开启后整张表默认进入锁定状态。然后再配置allowRanges把这些区域从锁定里放出来。初始配置写法如下workbook.getSheetByName(业务填报模板).setWorkbook({ protect: true, // 开启工作表保护 allowRanges: [ { name: B2:B10, range: { startRow: 1, startColumn: 1, endRow: 9, endColumn: 1, }, }, { name: D2:D10, range: { startRow: 1, startColumn: 3, endRow: 9, endColumn: 3, }, }, ], });startRow和startColumn都是从0开始计数的。也就是说B2对应的行索引是1列索引是1。我当时第一次配的时候习惯性从1开始写结果锁定了一片错位的区域还排查了半天这个坑你先记住。4.2 动态修改保护区域用户自定义表格的关键现实中模板往往是动态生成的列数、开放区域可能由后台返回。Univer支持运行时修改保护配置这就是它的灵活之处。假设用户先自己定义了一张表比如通过拖拽、插入行列设置了结构你再根据业务规则动态开放可填区域。可以通过Facade API来修改const workSheet workbook.getActiveSheet(); // 先解保护修改完再重新保护 workSheet.setWorkbook({ protect: false, allowRanges: [], }); // 业务动态计算开放区域 const openRanges [ { name: 当月数据填写区, range: { startRow: 1, startColumn: 1, endRow: 10, endColumn: 3, }, }, ]; workSheet.setWorkbook({ protect: true, allowRanges: openRanges, });这里有个实操经验修改前先解除保护一定不要直接在一个已经开启保护的状态上叠加新配置。Univer内部对配置的更新有一些边界判断先解再设是最稳妥的路径我因为这个踩过一次改完配置但保护失效的偶发问题。4.3 特定用户身份校验和动态权限判断如果只是全表锁定、白名单放行那适合做所有人填同一批格子的场景。但业务上通常需要做到这个客户只能填自己那一行别人填的格子它动不了。这就要引入用户身份和行级权限了。我的做法是初始化Univer时给数据模型注入当前用户的唯一标识然后在配置allowRanges之前先通过后端接口拉取该用户的权限范围再动态生成白名单。伪代码类似async function initSheetWithPermission(workbook, userId) { const editableRange await fetch(/api/getEditableRange?userId${userId}); workbook.getActiveSheet().setWorkbook({ protect: true, allowRanges: [ { name: 用户${userId}填写区, range: editableRange, }, ], }); }这样每个人进来看同一张模板但能修改的只有自己的区域。表格拓扑结构完全相同、统一管理区域权限因人而异。这一点如果是用传统表单来做就非常吃力而Univer做起来基本是顺水推舟。4.4 前端锁定的界线防君子不防小人这里必须明确一个安全边界Univer区域保护本质上是前端交互层的权限控制不是服务端安全边界。懂行的用户可以打开浏览器控制台、修改请求数据、直接调用底层API绕过保护。所以前端区域保护用来提升正常用户的填写体验、防止误操作、引导填写路径服务端必须在写入接口再次校验数据来源和字段合法性涉及敏感数据、强约束校验必须要后端兜底我见过一些团队把所有安全逻辑全押在前端锁定上后来被恶意脚本刷数据追悔莫及。这块一定要在评估阶段就设计好。Univer做了它该做的那一层剩下的服务端校验不要省。5. 多人填表场景的进阶玩法协作模式下的保护与冲突处理如果你只是做单用户填表提交上一节已经完全够用。但现在大多数业务系统都要求多人同时在线填写这就牵扯出协作一致性和锁定区域之间的微妙关系。5.1 Univer协作架构怎么理解Univer在架构设计上把数据变更抽象为一个个Operation操作这些Operation通过协同协议同步给所有在线客户端每个客户端再通过OT算法做状态融合。这套思路继承自Google Docs的协同设计。我用一个生活化类比解释协作模式下的每个用户不是直接改一份共享文件而是每个人操作本地副本再把我刚才改了哪个格、改成了什么这条消息广播出去大家收到后按算法合并。如果有人同时改了同一格就需要版本冲突处理策略。5.2 保护区域与协作的搭配实践在多人同时填写场景下我的建议有下列几点表格结构和模板布局由管理员维护开启保护后普通用户根本碰不到表头、公式、行列结构这本身就大幅降低了协作冲突概率。用户各写各的区域白名单范围做成行级隔离比如用户A只能碰第2列用户B只能碰第3列两人操作天然互斥减少了同时改同一单元格的概率。公式区完全锁定由系统统一计算。这样用户填完基础数据汇总、统计、占比等列自动刷新不需要任何人手动维护数据一致性直接提升一个数量级。5.3 协作场景下的三个配置文件如果你用Univer的协作方案可以理解为一个协同服务层需要在初始化时多接一个协同插件并配置传输通道。这里我不展开具体后端实现因为不同团队的消息总线策略差异很大但有几条通用建议后端消息路由要能识别操作的目标工作簿/工作表ID避免不同用户的表数据串线数据持久化时机建议做防抖改成“用户停顿几秒后自动保存”或者“手动保存按钮触发”不要每个单元格变更都直接写库不然高并发下数据库压力很大权限变更实时同步如果你要动态修改保护范围比如管理员临时多开放一块区域这个配置变更本身也需要走协作通道同步给所有在线客户端。5.4 我踩过的一个协作冲突坑有一次我在一个同时开着的两张表之间复制粘贴由于其中一张被设置了保护区域粘贴等待入目标表格时没有第一时间被拦截过了一会儿才在日志里看到一条权限拒绝的报错。排查之后发现Univer的默认保护校验主要在变更提交阶段管控而不是在粘贴初始动作时完全预判。解决办法是在拦截判断时同步监听onChange事件一旦发现目标区域有越权写入尝试立即回滚该操作或提示用户。这样比单纯依赖内置保护更稳也让你能记录到用户的越权尝试日志后续做行为分析非常有用。6. 从Demo到生产实测中踩过的坑和最后落地建议这一部分是我最想写的因为只有真正跌进坑里过才说得清。Univer项目整体质量不错但在细节上还是有几个地方需要你提前做好心理准备。6.1 性能大表格别过度保护我一开始给一个数百行、十几列的模板做保护把每个可编辑单元格都拆成独立的小区域加到allowRanges里。结果页面在低端机器上打开时有明显卡顿尤其是滚动的时候。原因很好理解保护配置越碎数据变更时的权限判断逻辑越复杂UI需要频繁计算。解决思路也很简单——能用行区间/列区间合并就不用单元格级能用连续矩形区域处理就不拆成多个碎片。后来我把可编辑列直接定义为整列区间性能问题基本消失。如果你的数据量大到单表超过几十万格建议提前做虚拟滚动和按需加载的规划不要把超大数据一次性全塞进Univer。它毕竟不是为无限数据量设计的。6.2 复制粘贴绕过保护的处理方案前面提到过粘贴是常见的绕过路径。我实测发现Univer对保护区域禁止粘贴有一定处理但并非在所有浏览器的剪贴板事件下都稳定。你需要在粘贴事件的回调里做二次校验// 监听变更事件做二次校验 univer.getActiveWorkbook().getActiveSheet().onChange((change) { const targetInfo change?.command?.params; if (!targetInfo) return; const isValid checkEditable(targetInfo; // 查询白名单 if (!isValid) { // 记录并回滚 univer.getActiveWorkbook().getActiveSheet().undo(); console.warn(检测到越权写入已回滚); } });这个方案的核心思路很简单不信任单一入口变更后再校验一次。虽然理论上改了再回滚的体验有轻微延迟但能保证数据绝对符合权限规则。对于收集类业务正确比实时更重要付出这点性能代价值得。6.3 数据格式校验和提交逻辑Univer帮你搞定了表格交互和权限隔离但它不会帮你做这一列必须填写手机号、“这一列必须在0到100之间”这类业务校验。我的最终落地做法是前端的实时校验用Univer的onChange监听单元格变更变完立即校验错误时在单元格旁边标记红色批注或者弹提示。提交前的整体校验用户点提交按钮后遍历所有开放区域的单元格做一遍完整校验有错就定位到具体单元格。服务端校验最终入库前再校验一遍防止绕过前端直接调接口。这三层校验层层递进真实项目中基本能满足数据质量要求。我强烈建议你不要因为Univer很强就省掉第二三层尤其是用户自定义表格这种灵活场景自由度高意味着异常数据也多没有完整校验链路容易失控。6.4 版本升级和插件化带来的维护成本Univer还在快速迭代期版本之间的API变动相当频繁。我接入的版本和几个月后的最新版之间有些插件注册方式已经发生了变化。如果你在一个长期维护的项目里用它建议锁定版本号不要随便升级升级前先看官方Changelog尤其是插件注册相关的破坏性变更把初始化Univer的逻辑封装到一个独立模块后续升级替换只改这个模块不要散落在业务代码里我在项目里就吃了这个亏升级的时候为了找一处API变更翻了一下午源码最后才意识到自己初始化代码写得太分散了。6.5 样式和体验上的几个设计建议可编辑区域视觉提示在开启保护的表格里建议把可编辑区域用浅色底色区分开锁定区域用浅灰底色。用户一打开就知道哪里能写哪里不能动体验会好很多。悬浮提示用户点击锁定单元格时最好能弹一个轻量提示比如该区域已被锁定如需修改请联系管理员不要只默默无反应。Univer本身不提供这个文案自己监听点击事件补上就好。工具栏精简对面向外部用户填写的场景建议把新增行列、删除行列、合并单元格这类结构操作从工具栏隐藏或禁用避免用户误触破坏表结构。Univer的UI插件支持自定义配置注意把可破坏结构的操作按钮关掉。6.6 最后说下我的整体评价Univer是我目前见过的、最适合做用户自定义表格指定单元格填写锁定其它单元格的开源方案。它的学习曲线不算陡核心API能快速上手但生产级应用还需要你补齐协作、校验、性能优化这几块拼图。如果你只是做一个简单的填表收集工具它可以开箱即用如果你要做协同级、跨部门、高并发的数据收集平台它也给你预留了足够的扩展空间。我在实际项目里的体会是表结构权限、填写引导、数据校验这三件事Univer替你解决了两件半剩下那半件和服务端校验永远要抓在自己手里。不要迷信任何一个开源组件能包办你的业务约束它保证了工作台顺手不代表业务规则已经自动成立。如果你正在评估或者已经开始试用Univer欢迎按这篇文章里的路径走一遍。尤其是第四章那段动态区域保护配置我建议你先拿一张带公式的小表试起来把保护-放行-变更-再保护的循环跑熟再一步步加协作和用户维度。基础路径通了后面怎么做都顺。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →