背景
部门要进行在线数据填报:excel表格样式不限;
因为要支持直接从excel直接粘贴,经筛选选择:Univer Sheets
Univer Sheets 官网地址
https://docs.univer.ai/zh-CN/guides/sheets
注:内置的导入、导出、打印为高级功能,收费
1、安装和配置
# 安装核心包
pnpm add @univerjs/presets
正常到这就可以了,但是我这边前台报react版本错误,所以去源码包看了一下版本,单独指定react的版本(如下图)
# 指定依赖的 react
pnpm add react@18.2.0 react-dom@18.2.0 -D
# 最后的版本情况
{
"dependencies": {
"@univerjs/presets": "^0.8.2",
},
"devDependencies": {
"react": "18.2.0",
"react-dom": "18.2.0",
}
}
注意:若指定了react的版本前台还报错,则修改 vite.config.js,指定 react 的路径,如下在 resolve 新增 react react-dom 的路径
# vite.config.js
import {resolve} from 'path'
export default defineConfig(({command, mode}) => {
const envConfig = loadEnv(mode, './')
return {
server: {
.....
},
plugins: [
......
],
resolve: {
alias: {
'~': `${resolve(__dirname, './')}`,
'@': `${resolve(__dirname, 'src')}/`,
react: resolve('./node_modules/react'),
'react-dom': resolve('./node_modules/react-dom')
},
},
}
});
若不指定路径,前端会报警告:
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
2 编写基本样例功能
1)数据存储到浏览器缓存,再次打开加载缓存中的表格数据
2)启用、禁用表格编辑;
3)点击取消,销毁表格,并重新创建并加载
UI按钮模板是element-plus
<template>
<el-row style="margin-bottom: 10px;background: white;padding: 10px 20px" justify="end">
<el-col :span="18">
</el-col>
<el-col :span="6" class="d-flex">
<div style="margin-left: auto;">
<el-button v-if="!isEditable" type="primary" @click="setEditable(true)">编辑</el-button>
<el-button v-if="isEditable" type="primary" @click="submitForm">保存</el-button>
<el-button v-if="isEditable" @click="cancelEditable">取消</el-button>
</div>
</el-col>
</el-row>
<div ref="container" style="width: calc(100vw - 240px);height: 77vh;"></div>
</template>
<script setup>
import {onMounted, onBeforeUnmount, ref} from 'vue'
import {createUniver, defaultTheme, FUniver, LocaleType, merge, Univer} from '@univerjs/presets';
import {UniverSheetsCorePreset} from '@univerjs/presets/preset-sheets-core';
import UniverPresetSheetsCoreZhCN from '@univerjs/presets/preset-sheets-core/locales/zh-CN';
import '@univerjs/presets/lib/styles/preset-sheets-core.css';
const container = ref(null)
// univer 实例、操作api,因无需使用vue3特性,所以用的变量
let univerInstance = null
let univerAPIInstance = null
const STORAGE_KEY = 'univer-sheet-data'
const isEditable = ref(false) // 默认禁用编辑
// 设置编辑状态
const setEditable = (editable) => {
const workbook = univerAPIInstance?.getActiveWorkbook()
if (workbook) {
workbook.setEditable(editable)
isEditable.value = editable
univerAPIInstance?.setUIVisible(univerAPIInstance.Enum.BuiltInUIPart.TOOLBAR, editable);
univerAPIInstance?.setUIVisible(univerAPIInstance.Enum.BuiltInUIPart.FOOTER, editable);
}
}
// 取消编辑
const cancelEditable = () => {
loadData()
setEditable(false);
}
// 创建并加载表格数据
function loadData() {
// 1 加载表格前,先销毁已有的表格
const fWorkbook = univerAPIInstance?.getActiveWorkbook();
const unitId = fWorkbook?.getId();
if (unitId) {
univerAPIInstance.disposeUnit(unitId);
}
// 2 获取数据,并创建表格
const savedDataStr = localStorage.getItem(STORAGE_KEY)
const savedData = savedDataStr ? JSON.parse(savedDataStr) : null
if (savedData) {
univerAPIInstance?.createWorkbook(savedData)
console.log('已加载 localStorage 中的表格数据')
} else {
univerAPIInstance?.createWorkbook({name: 'Test Sheet'})
console.log('初始化新工作簿')
}
}
// 保存数据至存储
const submitForm = async () => {
const workbook = univerAPIInstance.getActiveWorkbook()
const snapshot = workbook.save()
localStorage.setItem(STORAGE_KEY, JSON.stringify(snapshot))
console.log('表格已保存到 localStorage')
setEditable(false)
}
onMounted(() => {
// 初始化基本信息:参考 https://docs.univer.ai/zh-CN/guides/sheets/integrations/vue
const {univer, univerAPI} = createUniver({
locale: LocaleType.ZH_CN,
locales: {
[LocaleType.ZH_CN]: merge(
{},
UniverPresetSheetsCoreZhCN
),
},
theme: defaultTheme,
presets: [
UniverSheetsCorePreset({
container: container.value,
toolbar: true, // 工具栏
footer: true // 底部
}),
],
})
univerInstance = univer
univerAPIInstance = univerAPI
// 加载数据
loadData()
// 默认禁用编辑
setEditable(false)
})
onBeforeUnmount(() => {
univerInstance?.dispose()
univerAPIInstance?.dispose()
univerInstance = null
univerAPIInstance = null
})
</script>
3、效果及评价
1)样式比较好,权限控制精细,未深入研究
2)测试的项目:打包从26M 变成36M,整个包新增相关较大
3)官方有按模块引用,这里没做精简引用,欢迎您测试,并留下评价
预览效果如下图