jvs-excel-ui
v0.1.0
Published
JVS Excel UI - Vue 3 Component & Plugin Library
Readme
jvs-excel-ui
基于 Vue 3 + TypeScript + Vite + Element Plus 构建的在线电子表格组件库,底层表格引擎基于 UniverJS (@univerjs/* v1.4.0) 插件化模式封装,同时支持 独立 Web 站点部署 与 Vue 3 插件 NPM 发布。
1. 架构与特性
- 底层引擎:采用 UniverJS (
@univerjs/* 1.4.0) 插件模式构建,内置公式计算、数字格式化、筛选、排序、超链接、浮动图片/单元格图片、条件格式、数据验证、查找替换、十字高亮阅读模式等完整能力。 - 三段式模块化组件:
ExcelHeader(顶部自定义工具栏)UniverSheet(中间 UniverJS 核心表格画布)ExcelFooter(底部工作表管理、视图缩放、分页与高亮控制栏)
- 实例与 API 完全开放:支持通过
@ready事件或组件ref获取univerAPI(FUniverFacade 实例)与底层Univer实例,可直接调用 UniverJS 全部官方 API、命令与事件系统。
2. 开发与构建指令
# 安装依赖
pnpm install
# 启动本地开发调试
pnpm dev
# 类型检查
pnpm run type-check
# 构建可独立部署的 Web 应用(产物位于 dist/app)
pnpm build
# 构建可发布的 Vue 3 插件库(产物位于 dist/lib,包含 ES/UMD 与 .d.ts 类型声明)
pnpm build:lib3. 作为插件在外部项目中使用
3.1 全局注册
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import JvsExcelUi from 'jvs-excel-ui'
import 'jvs-excel-ui/style.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.use(JvsExcelUi)
app.mount('#app')3.2 按需引入与基础使用
<template>
<div style="width: 100%; height: 100vh;">
<JvsExcel
ref="excelRef"
:workbookData="workbookData"
:isEdit="isEdit"
:uploadImage="handleUploadImage"
@ready="handleReady"
@newChanges="handleNewChanges"
@rangeChange="handleRangeChange"
@univerCellChange="handleCellChange"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { JvsExcel, type JvsExcelExpose } from 'jvs-excel-ui'
import 'jvs-excel-ui/style.css'
const excelRef = ref<JvsExcelExpose | null>(null)
const isEdit = ref(true)
const workbookData = ref<Record<string, any> | undefined>(undefined)
// 表格初始化完成回调,直接抛出 univerAPI (FUniver) 与底层 univer 实例
function handleReady(univerAPI: any, univer: any) {
console.log('Univer Facade API 实例:', univerAPI)
console.log('Univer 核心实例:', univer)
}
// 增量变更回调(1s 防抖聚合,可用于自动保存或协同广播)
function handleNewChanges(commands: any[]) {
console.log('增量变更命令列表:', commands)
}
// 选区变化回调
function handleRangeChange(rangeInfo: any) {
console.log('当前选区信息:', rangeInfo)
}
// 单元格内容/状态变化回调
function handleCellChange(currentCell: any, oldVal: any, cellData: any) {
console.log('单元格变化:', currentCell, oldVal, cellData)
}
// 自定义图片上传(可选,不传则默认转 Base64 插入)
async function handleUploadImage(file: File): Promise<string> {
return URL.createObjectURL(file)
}
</script>4. 组件 Props、事件与暴露实例 API
4.1 Props 属性 (JvsExcelProps)
| 属性名 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| workbookData | Record<string, any> | undefined | 工作簿初始 JSON 数据(Univer IWorkbookData 快照结构),不传则自动创建默认空白工作簿 |
| fileContent | File \| Blob \| ArrayBuffer \| Uint8Array \| string | undefined | 直接传入 .xlsx / .xls / .csv 文件内容(支持 File、Blob、ArrayBuffer、Base64 字符串或 JSON 字符串)自动解析渲染 |
| fileUrl | string | undefined | 传入远程或本地 .xlsx / .xls / .csv 文件 URL 地址,组件自动拉取并解析渲染 |
| id | string | 'jvs-excel' | 实例唯一标识 |
| designInfo | Record<string, any> | { id: 'jvs-excel' } | 文档/报表扩展元信息 |
| isEdit | boolean | true | 是否处于编辑模式(false 时隐藏顶部工具栏并进入只读预览模式) |
| readonly | boolean | false | 是否只读(为 true 时强制只读) |
| locale | 'zh-CN' \| 'en-US' | 'zh-CN' | 界面语言 |
| memberList | any[] | [] | 协同编辑成员光标列表 |
| uploadImage | (file: File) => Promise<string> | undefined | 自定义图片上传钩子,返回图片 URL;不传则默认使用 Base64 插入 |
4.2 Events 事件
| 事件名 | 回调参数 | 说明 |
| --- | --- | --- |
| @ready | (univerAPI: FUniver, univer: Univer) | UniverJS 实例初始化完成时触发,返回 Facade API 实例与底层核心实例 |
| @newChanges | (commands: CommandListItem[]) | 工作簿产生数据变更时触发(1000ms 防抖聚合),返回命令数组 |
| @commandExecuted | (command: ICommandInfo) | 任意底层 Univer 命令执行后触发 |
| @rangeChange | (rangeInfo: Record<string, any>) | 选中单元格区域变化时触发 |
| @univerCellChange | (currentCell, oldCellValue, cellData) | 单元格值或样式变更时触发 |
| @changePage | (pageInfo: { key: string, value: any }) | 底部栏分页参数变更时触发 |
4.3 暴露的方法与属性 (defineExpose / JvsExcelExpose)
通过 ref(如 const excelRef = ref<JvsExcelExpose | null>(null))可调用以下方法与属性:
| 方法 / 属性名 | 类型 | 说明 |
| --- | --- | --- |
| getUniverAPI() | () => FUniver | 获取 UniverJS 官方 Facade API 实例(FUniver) |
| univerAPI | Ref<FUniver> | 响应式的 FUniver Facade API 引用 |
| getUniver() | () => Univer | 获取底层 Univer 核心实例(可通过 univer.__getInjector() 访问 DI 容器) |
| getActiveWorkbook() | () => FWorkbook | 快捷获取当前激活的工作簿 Facade 实例 |
| getActiveSheet() | () => FWorksheet | 快捷获取当前激活的工作表 Facade 实例 |
| getData(isEnd?) | (isEnd?: boolean) => Record<string, any> | 获取当前工作簿完整 JSON 快照数据(内部调用 activeWorkbook.save()) |
| setData(workbookData?) | (data?: Record<string, any>) => Promise<void> | 动态加载/重置工作簿 JSON 快照数据 |
| importExcelFile(source, fileName?) | (source: ExcelInputSource, fileName?: string) => Promise<Record<string, any>> | 导入并渲染 .xlsx / .xls / .csv 文件(支持 File、Blob、ArrayBuffer、Base64 或文件 URL 地址) |
| exportExcelFile(fileName?, download?) | (fileName?: string, download?: boolean) => Promise<Blob> | 将当前工作簿导出为 .xlsx 文件(返回 Blob,download 默认为 true 自动触发浏览器下载) |
| forceCommitEdit() | () => Promise<void> | 强制提交当前正在编辑中的单元格,并立即刷新待发送的 @newChanges 队列 |
| flushCommandList() | () => void | 立即触发尚未满 1s 防抖的增量变更队列(@newChanges) |
| resetCommandList() | () => void | 清空待发送的增量变更命令队列 |
| applyCollabCommands(cmds) | (commands: CommandListItem[]) => void | 将远端协同增量命令列表回放到当前工作簿(自动标记 fromCollab: true 避免循环触发) |
| doAction(item) | (item: any) => void | 触发顶部工具栏或底部栏内置动作 |
| createBaseReportData(docId?) | (docId?: string) => Record<string, any> | 生成一份标准的初始空白工作簿 JSON 数据结构 |
| univerSheetRef | Ref<any> | 中间 UniverSheet 子组件实例引用 |
5. 调用底层 UniverJS API 示例
当内置工具栏之外需要自定义业务交互时,可通过 @ready 或 excelRef.value?.getUniverAPI() 直接调用 UniverJS 官方 Facade API:
// 获取 Facade API 实例
const univerAPI = excelRef.value?.getUniverAPI()
const workbook = excelRef.value?.getActiveWorkbook()
const sheet = excelRef.value?.getActiveSheet()
// 1. 读写指定单元格或区域数据
const range = sheet.getRange(0, 0, 1, 1) // 第1行第1列 (A1)
range.setValue('自定义写入内容')
console.log('A1 当前值:', range.getValue(), '完整单元格数据:', range.getCellData())
// 2. 插入浮动图片
await sheet.insertImage('https://example.com/logo.png', 2, 2, 0, 0)
// 3. 执行任意 Univer 命令(如撤销、重做、加粗、冻结行列等)
univerAPI.executeCommand('univer.command.undo')
univerAPI.executeCommand('sheet.command.set-range-bold')
// 4. 监听 Univer 事件
const disposable = univerAPI.addEvent(univerAPI.Event.CellClicked, (params: any) => {
console.log('点击了单元格:', params.row, params.column)
})
// 取消监听:disposable.dispose()
// 5. 保存前强制结束单元格编辑并导出完整快照
await excelRef.value?.forceCommitEdit()
const snapshot = excelRef.value?.getData(true)
// 6. 导入本地 File / ArrayBuffer / 远程 URL Excel 文件 (.xlsx / .xls / .csv)
await excelRef.value?.importExcelFile(fileOrUrl, '报表名称')
// 7. 导出当前工作簿为 .xlsx 文件
const xlsxBlob = await excelRef.value?.exportExcelFile('导出报表', true)