本模块基于 Univer 封装了一套「在线表格 + 模板管理」能力,包含基础示例、模板中心、模板设计、模板应用四个页面,以及一个可复用的组合式函数 useUniverSheet。模板数据持久化在浏览器 localStorage,无需后端即可完整跑通。
目录结构
univer/
├── basic.vue # 基础使用示例(填充数据/只读切换/新增表/导出快照)
├── scenes.vue # 模板中心(模板列表的增删改查入口)
├── designer.vue # 模板设计(两步式:基础信息 → 表格配置)
├── apply.vue # 模板应用(加载模板快照并可导出)
├── components/
│ ├── DesignerBasicStep.vue # 设计页第一步:模板基础信息表单
│ └── DesignerConfigStep.vue # 设计页第二步:表格设计容器
└── core/
├── useUniverSheet.js # Univer 实例的组合式封装(核心)
└── 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, // 表格容器引用,必须绑定到 DOM
workbookRef, // 当前工作簿(展示用)
activeSheetName, // 当前工作表名称
activeRangeText, // 当前选区 A1 表示
readyRef, // 是否已初始化完成
getWorkbook, // 获取当前工作簿实例
getActiveSheet, // 获取当前工作表实例
loadWorkbook, // 加载/重载工作簿快照
syncState, // 手动同步状态到响应式变量
ensureInitialized, // 确保实例已初始化(返回 Promise<boolean>)
} = 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); // true 为可编辑
新增工作表
const workbook = getWorkbook();
const sheet = workbook.create("工作表2", 80, 26);
导出快照
const workbook = getWorkbook();
const snapshot = workbook.save(); // 得到工作簿 JSON 快照
模板数据 API(core/templates.js)
import {
templateList, // 响应式模板列表
basicWorkbookData, // 基础示例工作簿数据
createEmptyTemplateWorkbook, // 生成空白模板工作簿
getTemplateByKey, // 按 key 查模板
getFallbackTemplateKey, // 取兜底模板 key
getTemplateStats, // 统计模板概况(表数/行/列)
createTemplate, // 新增模板
updateTemplate, // 更新模板
removeTemplate, // 删除模板
} from "./core/templates";
- 所有数据保存在
localStorage 的 univer-sheet-templates 键下。
createTemplate / updateTemplate 在 key 冲突时会抛出 模板标识已存在,updateTemplate / removeTemplate 在模板不存在时抛出 模板不存在,调用方需 try/catch 处理。
模板对象结构
{
key: "chemistry-assay", // 唯一标识(slug 化)
name: "化学化验审批模板",
category: "质量审批",
description: "......",
features: ["特性A", "特性B"], // 字符串数组
updatedAt: "2026-06-23T...", // ISO 时间
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 实例,页面卸载无需手动清理。