本模块基于 Univer 封装了一套「在线表格 + 模板管理」能力,包含基础示例、模板中心、模板设计、模板应用四个页面,以及一个可复用的组合式函数 useUniverSheet。模板数据持久化在浏览器 localStorage,无需后端即可完整跑通。
目录结构
univer/
├── basic.vue
├── scenes.vue
├── designer.vue
├── apply.vue
├── components/
│ ├── DesignerBasicStep.vue
│ └── DesignerConfigStep.vue
└── core/
├── useUniverSheet.js
└── templates.js
文件链接(Gitee 仓库)
核心概念
- 工作簿快照(workbookData):一份描述工作簿结构与内容的 JSON 对象,含
id、name、sheetOrder、sheets。模板保存的就是这份快照,应用页直接还原它。
- unit id:即工作簿快照里的
id,Univer 内部用它唯一标识一个工作簿单元。同一个 unit id 不能被重复创建,重载前必须先销毁。
- 懒加载初始化:容器被隐藏(
v-show/v-if)时宽高为 0,此时无法正确渲染表格,需等容器可见后再初始化。
useUniverSheet 用法
基本调用
import { useUniverSheet } from "./core/useUniverSheet";
const {
containerRef,
workbookRef,
activeSheetName,
activeRangeText,
readyRef,
getWorkbook,
getActiveSheet,
loadWorkbook,
syncState,
ensureInitialized,
} = useUniverSheet({
workbookData: someSnapshot,
onReady: () => {
},
});
模板必须把容器绑到 DOM
<template>
<section ref="containerRef" class="sheet-host" />
</template>
容器需要有明确的尺寸(如 min-height: 720px),否则初始化会被跳过。
参数
| 参数 | 类型 | 说明 |
|---|
workbookData | object | 初始工作簿快照,初始化成功后自动加载 |
onReady | Function | 实例就绪回调,入参为 { univer, univerAPI, workbook, getWorkbook, getActiveSheet, loadWorkbook, syncState, ensureInitialized } |
返回值
| 名称 | 类型 | 说明 |
|---|
containerRef | Ref | 表格容器引用,需绑定到 DOM 元素 |
workbookRef | ShallowRef | 当前工作簿引用(仅展示用) |
activeSheetName | Ref | 当前活动工作表名称 |
activeRangeText | Ref | 当前选区 A1 表示法 |
readyRef | Ref | 实例是否初始化完成 |
getWorkbook() | Function | 返回当前活动工作簿(FWorkbook) |
getActiveSheet() | Function | 返回当前活动工作表(FWorksheet) |
loadWorkbook(data) | Function | 加载/重载工作簿,返回 Promise |
syncState() | Function | 手动把工作簿状态同步到响应式变量 |
ensureInitialized() | Function | 确保实例已初始化,返回 Promise |
常见操作示例
写入数据与样式
const sheet = getActiveSheet();
sheet.getRange("A1:E1")
.setValues([["项目", "负责人", "状态", "进度", "预算"]])
.setBackground("#16324f")
.setFontColor("#ffffff")
.setFontWeight("bold");
切换只读
const workbook = getWorkbook();
workbook.setEditable(false);
新增工作表
const workbook = getWorkbook();
const sheet = workbook.create("工作表2", 80, 26);
导出快照
const workbook = getWorkbook();
const snapshot = workbook.save();
模板数据 API(core/templates.js)
import {
templateList,
basicWorkbookData,
createEmptyTemplateWorkbook,
getTemplateByKey,
getFallbackTemplateKey,
getTemplateStats,
createTemplate,
updateTemplate,
removeTemplate,
} from "./core/templates";
- 所有数据保存在
localStorage 的 univer-sheet-templates 键下。
createTemplate / updateTemplate 在 key 冲突时会抛出 模板标识已存在,updateTemplate / removeTemplate 在模板不存在时抛出 模板不存在,调用方需 try/catch 处理。
模板对象结构
{
key: "chemistry-assay",
name: "化学化验审批模板",
category: "质量审批",
description: "……",
features: ["特性A", "特性B"],
updatedAt: "2026-06-23T...",
workbookData: { },
}
页面工作流
模板中心(scenes) ──新增/编辑──▶ 模板设计(designer) ──保存──▶ 模板中心
│
└──应用模板──▶ 模板应用(apply) ──加载快照/导出──
- 模板中心:展示模板列表,提供新增、编辑、应用、删除入口。
- 模板设计:第一步填写基础信息(名称/标识/分类/说明/特性),第二步在表格中完成结构设计,保存时把工作簿快照写入模板。
- 模板应用:加载选中模板的工作簿快照,保持设计时的原始结构,可重新加载、跳转编辑或导出快照。
注意事项与坑点
- 隐藏容器无法初始化:表格容器被
v-show/v-if 隐藏时宽高为 0,ensureInitialized() 会直接返回 false。模块内已用 ResizeObserver 兜底——容器从隐藏变为可见时自动重试初始化。分步表单中切到表格步骤后,建议 await nextTick() 再加 requestAnimationFrame 等待布局完成,然后调用 ensureInitialized()。
- 重复 unit id 报错:同一个工作簿快照(相同
id)不能被 createWorkbook 两次,否则报 cannot create a unit with the same unit id。loadWorkbook 已在重建前自动 disposeUnit 销毁旧工作簿,正常使用不会触发。
- 销毁前先解绑事件:销毁工作簿会让其引用失效,若残留的选区/活动表事件订阅仍在,回调访问
getUnitId 会抛空指针。loadWorkbook 内已在 disposeUnit 前解绑监听,自定义扩展时也需遵循「先解绑、再销毁」的顺序。
- 跨组件传递容器引用用函数 ref:把
containerRef(ref 对象)作为 prop 传给子组件时,会被 Vue 自动解包成 .value(初始为 null),导致子组件 :ref 绑定失效。应改为传一个回调函数(如 setContainerRef(el)),在子组件用 :ref="setContainerRef" 绑定。
- 资源回收:
useUniverSheet 在 onBeforeUnmount 中已统一销毁事件监听、ResizeObserver 与 Univer 实例,页面卸载无需手动清理。