ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Univer在线表格:限定填写区域与单元格保护实战指南

Univer在线表格:限定填写区域与单元格保护实战指南 如果你做过后台管理系统大概都遇到过这样的需求给用户一张预设好结构的表格让他只填写其中一部分单元格其余区域完全不能动。以前我处理这种需求要么用动态生成的 HTML 表格加一堆 input要么在前端费劲地控制选中状态数据一多、交互一复杂维护成本直接起飞。后来调研在线表格方案时反复有人提到 Univer一个开源的纯 TypeScript 在线表格/文档方案核心能力几乎是为这类场景准备的可以自己定义表格结构可以指定某些单元格允许用户填写剩下的单元格直接锁定。这篇文章就围绕“Univer 在线表格实现限定填写区域”这个场景展开从环境搭建、权限配置到协作模式下的坑一次说清楚。1. 需求拆解与方案选型为什么最终选择了 Univer先说需求本身。这类“模板填写”场景在企业内部特别常见典型的表现是管理员或者运营人员先画好一张表单比如进销存台账、项目信息登记表、月度数据填报表头和公式是固定的用户只需要在指定的空白格里填数字或者选选项。如果用传统 Web 开发思路做大致有几种做法但每种都有明显短板。第一种是纯 HTML 表格配合 input 标签前端 JS 统一收集数据。优点是实现直观缺点是一旦表格列数多、样式复杂或者需要支持复制粘贴、公式联动、数据校验开发和联调成本会成倍增加。第二种是用现成的 JavaScript 表格库比如核心表格组件、类 Excel 库这类方案确实能快速渲染大表格但要做到“部分单元格可编辑、部分锁定”往往需要深度定制事件和配置很多库并没有直接的单元格保护 API。第三种是直接把 Excel 模板上传到后端让用户下载填写后再回传这种方式在协同和实时性上几乎为零而且数据抽取也是噩梦。Univer 的定位刚好补上了这些短板。它是开源项目UniverSheet 的在线表格能力已经相当完整而且架构是插件式的支持自定义业务扩展。更重要的是Univer 原生就有工作簿保护、工作表保护的概念可以细粒度地控制哪些单元格可编辑、哪些单元格锁定。这个“定义表格 指定填写区 其余区域只读”的组合对于它来说不是旁门左道而是正规能力。从这个角度说选型本质上不是“哪个表格库更好”而是“哪个方案能同时满足在线展示、精细权限、数据收集、导出导出这四条线”。我在本地把 Univer 和另外几个常见库做了对比简单列了个对照表方案部分单元格锁定类 Excel 交互实时协作扩展成本社区活跃度原生 HTML 表格需要自行实现弱无极高-核心表格需要深度定制中无高可类 Excel 编辑器 A部分支持强有限中一般Univer原生支持强支持中低热实测下来Univer 的初始化配置和权限控制的 API 风格很统一做模板定义场景很顺手这也是我最终确定拿它做底层的核心理由。2. Univer 环境搭建与工作簿初始化2.1 快速搭建一个可运行的 Univer 实例Univer 的选择很多官方提供 CDN、npm 包和预设包presets三种方式。在正式项目里我建议直接用 npm 把核心模块安装进来模块之间的关系清晰后续升级也容易。先用一个最简单的命令引入基础依赖。npm install univerjs/core univerjs/sheets univerjs/sheets-ui univerjs/ui这是最精简的一组包。其中univerjs/core负责数据处理和命令系统univerjs/sheets是表格领域逻辑univerjs/sheets-ui提供表格的界面交互univerjs/ui负责整体 UI 外壳。有人可能问为什么拆这么细这是 Univer 的设计核心之一数据逻辑和 UI 解耦这样你在服务端跑计算、在客户端渲染才能复用同一套数据模型。初始化代码也非常直接。先创建 Univer 实例然后注册 Sheet 插件和 UI 插件最后挂载到指定的 DOM 节点上。import { Univer } from univerjs/core; import { UniverSheetsPlugin } from univerjs/sheets; import { UniverSheetsUIPlugin } from univerjs/sheets-ui; import { UniverUIPlugin } from univerjs/ui; const univer new Univer({ locale: zhCN, }); univer.registerPlugin(UniverSheetsPlugin); univer.registerPlugin(UniverUIPlugin, { container: app, }); univer.registerPlugin(UniverSheetsUIPlugin);这里有一个非常实用的细节locale参数支持中文不用额外做语言包处理。我见过很多项目在表格库上还要单独引入中文语言包Univer 直接在初始化参数里解决省事不少。如果你用的是 vite 或者 webpack这种模块化引入方式也完全兼容热更新开发体验很顺。2.2 通过数据快照定义表格结构创建完实例下一步就是往工作簿里塞数据。Univer 的初始化数据是一份 JSON 快照这个快照把工作簿结构描述得非常具体工作簿里有什么工作表每个工作表有多少行多少列每个单元格的内容、样式、合并信息全在里面。你可以把这份 JSON 想象成 Excel 文件的解压版结构完全透明。我第一次上手时习惯先把一个静态的快照文件导入在界面上调整样式然后通过浏览器里的控制台把当前数据结构导出来做成默认模板。这样定义表格结构效率极高。核心结构大致是这样的。const workbookData { id: workbook-001, name: , sheets: [ { id: sheet-001, name: 月度填写表, rowCount: 20, columnCount: 8, cellData: { 0:0: { v: 项目名称, s: headerStyle }, 0:1: { v: 报名人数, s: headerStyle }, 0:2: { v: 实际到场, s: headerStyle }, 1:0: { v: 示例项目, s: editableCellStyle }, }, styles: { headerStyle: { : { bl: 1, bg: rgb(245,245,245), ht: 2, vt: 2 }, }, }, }, ], };在这个 JSON 里cellData的 key 是行:列value 里的v是单元格内容s是引用样式 ID。样式对象里bl是加粗、bg是背景色、ht是水平对齐、vt是垂直对齐这套约定非常接近 Excel 的内部表示熟悉 Office 的开发者很容易迁移。实际操作中我不建议手写这份 JSON太容易出错。更好的做法是先初始化一个空白工作簿用 UI 把表头、列宽、数据校验全部配好然后调getSnapshot()之类的接口把当前结构取出来存成模板数据。之后再初始化时直接把这个快照塞进data参数即可。3. 核心功能实现只让用户填写指定的单元格3.1 理解 Univer 的保护模型这个场景的关键不是“表格长得像 Excel”而是“哪些单元格可以被编辑”。Univer 的保护模型继承自表格软件常见的两层概念工作簿保护和工作表保护。工作簿保护决定谁能改工作簿结构比如新增工作表、删除工作表、调整工作表顺序工作表保护决定工作表内单元格的编辑权限、行列操作权限、对象编辑权限。放到“限定填写区域”这个需求里我们真正关心的是工作表保护。锁定的思路和 Excel 相反默认情况下在工作表保护开启后所有单元格都是只读的你要“解锁”的部分反而需要单独设置属性。这个模型初看有点绕但想通后就觉得很合理因为大多数模板都是固定结构只有一小块区域是给用户填写的把大部分区域默认锁定只解封几个填写区反而比逐个锁定只读区省事得多。3.2 用命令构造保护配置Univer 的业务操作都是通过命令command系统实现的。保护工作表也是一条命令你需要把工作表的保护参数传进去。大致逻辑如下。import { SetWorksheetProtectCommand } from univerjs/sheets; univer.getCommandService().executeCommand({ id: SetWorksheetProtectCommand.id, params: { unitId: workbook-001, worksheetId: sheet-001, protect: { lock: true, password: , permission: { editCell: true, insertRow: false, deleteRow: false, insertColumn: false, deleteColumn: false, }, }, }, });这段代码的核心在permission对象上。editCell控制单元格编辑insertRow控制插入行deleteRow控制删除行以此类推。我在做模板时通常只会保留editCell: true和一些基础视图操作其他行和列的操作全部关掉这样用户再怎么误操作也破坏不了表格整体结构。但这里有一个新手最容易踩的坑开启了工作表保护后你只是把“修改”这个大动作统一关掉了但并不意味着某个格子可以编辑。真正决定单元格能否被编辑的还有单元格自身的锁定属性。只有同时满足两个条件工作表保护开启且单元格未被锁定单元格才能进入编辑状态。3.3 设置单元格锁定与解锁那怎么给单元格做解锁呢如果是初始化模板最直接的方式就是在数据快照里给对应单元格设置样式属性。简单做法是在styles里准备两类样式一类是普通样式一类是允许编辑的样式在允许编辑的样式中设置lock: false。const styles { lockedCell: { : { lock: true, bl: 0, bg: rgb(230,240,255), }, }, editableCell: { : { lock: false, bg: rgb(255,255,255), }, }, };lock这个样式属性就是单元格锁定开关。只有lock: false的单元格在工作表保护开启后才可编辑。如果lock为true就算你把editCell: true打开这个格子也还是只读的。把这两者配合好才能得到一张“只有蓝色区域可填写”的模板表。如果在运行过程中需要动态解锁某个区域也可以通过命令批量修改样式。实际操作时我封装了一个工具方法传入工作表 ID 和一个范围比如A2:D10然后把这个范围内所有单元格的样式都改成lock: false。这种方式非常适合“用户先上传 Excel 模板系统解析后自动识别填写区”的场景。3.4 隐藏公式与避免误删模板表格里经常有自动计算的单元格比如合计、汇总、平均值。这些单元格的内容是通过公式生成的如果用户能随便点进去修改数据就乱了。在 Univer 里公式单元格的保护有两个层次一是单元格锁定二是隐藏公式。锁定能让用户改不了隐藏公式能让用户即使点到这个单元格也看不到输入框里的原始公式只看到计算结果。这在填报场景下尤其重要不然你辛辛苦苦做的毛利率计算逻辑用户一个偶然的点击就全看光了。隐藏公式会在样式层面增加一个属性大致是hideFormula: true。配合单元格锁定使用效果立竿见影。我建议把所有带公式的单元格统一整理到一个样式里同时设置lock: true和hideFormula: true从源头上避免公式被改、被看。4. 在线填写、数据收集与协作边界4.1 用户填写后如何拿到数据限定填写区域的目的是为了高效收集数据。所以当用户填写完系统必须能把这批数据取出来要么落到后端数据库要么转存成结构化 JSON要么导出 Excel。Univer 在这一点上处理得比较优雅因为它的整个工作簿数据就是一份 JSON 快照你可以随时拿到最新状态。const snapshot univer.getCurrentWorkbook()?.getSnapshot();得到快照后我会在业务层写一个遍历逻辑只遍历允许编辑的那些单元格把行号、列号、值组合成一个对象数组再 POST 到后端。为什么只遍历可编辑单元格因为模板里的表头、说明文字、公式结果都是固定结构保存下来没有意义还容易造成冗余。只取填写区相当于前端先做了一次数据清洗。4.2 善用数据校验约束填写内容单元格锁定只是第一层控制数据校验才是第二层保障。Univer 支持给单元格加下拉列表、数字范围、必填校验等规则。我在做“月度填报”模板时会给“项目类型”一列设置下拉校验给“参与人数”一列设置正整数校验给“备注”一列设置长度限制。这些校验配合锁定区域能大幅减少脏数据入库的概率。数据校验在快照结构里有独立的配置区每个校验规则需要指定范围、类型和参数。比如下拉列表的校验规则大致长这样。const validation { ref: C2:C100, type: list, formula1: 线上活动,线下活动,混合活动, showDropDown: true, };下拉校验的showDropDown属性一定要打开不然用户只能手输根本不知道有哪些选项。设置完这条规则后用户点击 C 列单元格时会自动出现下拉箭头填表体验和 Excel 几乎没有差别。4.3 多人同时填写的协作问题Univer 支持实时协作架构上通过操作转换把不同终端的编辑同步到同一份文档上。但这带来一个新问题协作模式下保护规则往往只存在于前端如果两个用户同时操作同一个区域或者某个用户通过特殊方式绕过前端接口数据就可能被污染。我个人的经验是单元格保护这类规则一定要当成“交互层体验”而不是“安全防线”。真正要保证数据安全后端还需要做两条兜底。第一前端提交数据时后端必须校验提交的单元格是否在允许列表范围内超范围的数据直接拒绝。第二有必要时把工作簿快照同步到后端做一次服务端权限计算。也就是说前端负责提供顺滑的填写体验后端负责最终裁决双保险。4.4 与后端接口的定时保存策略在线填表最怕用户填写到一半浏览器崩溃或者误刷新全部白填。Univer 本身不负责持久化我通常会在业务层做一个自动保存机制用户在可编辑区域内输入一次内容前端就触发一次防抖5 到 10 秒后把整个快照 POST 到后端保存。这样做有一个额外好处后端保存的是完整快照下次用户打开页面时可以直接恢复上次的填写状态甚至能保留他的填写历史。如果控制粒度再细一点你还可以把每次变更的操作指令记录下来形成类似操作日志的数据方便后续审计。5. 常见问题与实操避坑清单5.1 单元格锁定后无法复制粘贴这是很多人在第一次配置保护时遇到的问题。工作表保护开启后默认情况下用户连复制粘贴都做不了。如果业务上允许用户在某些填写区域内复制粘贴或者需要从外部 Excel 粘贴数据进来需要单独放行剪贴板权限。Univer 中可以在保护配置里显式打开剪切板相关操作或者在交互层面注册自定义粘贴行为。实测下来建议至少放行“复制”和“粘贴”两个操作否则用户在填报大量数据时会非常痛苦。5.2 数据校验配置后不生效有个比较容易忽略的点数据校验的优先级和单元格锁定是独立的。就算单元格是锁定的校验规则依然会渲染出来只是用户无法通过 UI 修改内容。如果你希望某些只读单元格也显示下拉箭头那直接在只读单元格上配置校验规则即可。反过来如果你希望某个填写区域有校验但是校验不生效大概率是你把校验范围写错了比如写的是A2:A100但实际可编辑区域是B2:B100。这个问题排查时先看保护配置再看校验范围引用绝大多数都是范围不匹配。5.3 导入 Excel 后保护规则丢失Univer 支持导入 Excel 文件但导入对保护规则的支持并不是 100% 完整。如果你依赖用户上传的 Excel 模板来决定哪些单元格可编辑导入之后一定要再遍历一遍每个工作表检查protect配置和单元格的lock样式。我现在的做法是模板文件由后台统一管理用户上传后由预处理服务解析解析出的填充区域列表单独存一份不依赖 Excel 文件本身的保护标记这样即使导入解析有偏差业务数据也不会出问题。5.4 大数据量下的性能优化如果工作簿有几千行、几十列数据同时又要开启保护、数据校验、协作同步性能压力确实会有。Univer 底层的渲染做了虚拟化只渲染可视区这一点不担心。真正容易出现性能问题的是频繁改动样式或校验配置。我的优化经验是初始化模板时一次性把锁区和校验全部配置好不要在运行时频繁调用命令逐个修改单元格如果一定需要动态修改尽量以范围为粒度一次性批量应用而不是一格格循环操作。5.5 保护与快捷键的冲突工作表保护开启后部分快捷键会被禁用比如删除行、插入行、删除单元格内容等。这是保护功能的正常表现但也会带来一些交互困扰。比如用户习惯性按 CtrlC、CtrlV如果权限没有放行页面会毫无反应用户会以为是系统卡了。解决方法是在 UI 层主动给可编辑区域加一个高亮提示比如蓝色边框或者浅色底纹让用户明确知道哪些区域可以操作同时把锁定的单元格背景颜色统一设为灰色形成视觉上的“禁区”。6. 经验总结Univer 在这个场景里的真正价值把整个流程跑通后我对 Univer 的感受可以用一句话概括它最大的价值不是帮你在网页里画了一个 Excel而是把“表格结构、权限、交互、协作”这几件事统一到了一个可编程的数据模型里。以前我做一个填表页面要分别维护 HTML 结构、CSS 样式、前端校验逻辑、后端字段白名单四套东西经常对不齐。用 Univer 以后模板结构本身包含了可编辑范围、样式、校验、公式数据控管的逻辑高度内聚。代码量没有变少但心智负担明显变小了。如果你现在正在做类似的填表功能我建议你在动手写代码之前先花一天时间把 Univer 的交互玩熟重点理解两个东西一个是工作表保护和工作簿保护的区别另一个是单元格lock样式属性与保护配置的配合关系。把这两个核心点想透后续所有扩展比如动态解锁区域、多工作表模板、协作权限分级都会变得特别顺。最后分享一个我自己的小习惯每次上线这类模板之前我会把最终的快照数据导出一份用 Excel 打开检查所有锁定区域和数据校验是否符合预期。因为快照数据和 Excel 源文件之间的转换存在细节差异肉眼检查一遍最稳妥。这个习惯帮我挡掉过好几次“线上表头被误改成可编辑”的风险。你可以把这个步骤写进发布检查清单里关键时刻真能救命。
返回列表