univer实现excel协同
快速入门
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="https://unpkg.com/@univerjs/umd/lib/univer.full.umd.js"></script><script src="https://unpkg.com/@univerjs/umd/lib/locale/zh-CN.js"></script><link rel="stylesheet" href="https://unpkg.com/@univerjs/umd/lib/univer.css"><style>#app{width: 100%;height: 100vh;}</style>
</head>
<body><div id="app"></div><script>var {UniverCore,UniverDesign,UniverEngineRender,UniverEngineFormula,UniverDocs,UniverDocsUi,UniverUi,UniverSheets,UniverSheetsUi,UniverSheetsNumfmt,UniverSheetsFormula,UniverFacade,} = windowvar univer = new UniverCore.Univer({theme: UniverDesign.defaultTheme,locale: UniverCore.LocaleType.ZH_CN,locales: {[UniverCore.LocaleType.ZH_CN]: UniverUMD['zh-CN'],},});univer.registerPlugin(UniverEngineRender.UniverRenderEnginePlugin);univer.registerPlugin(UniverEngineFormula.UniverFormulaEnginePlugin);univer.registerPlugin(UniverUi.UniverUIPlugin, {container: "app",});univer.registerPlugin(UniverDocs.UniverDocsPlugin);univer.registerPlugin(UniverDocsUi.UniverDocsUIPlugin);univer.registerPlugin(UniverSheets.UniverSheetsPlugin);univer.registerPlugin(UniverSheetsUi.UniverSheetsUIPlugin);univer.registerPlugin(UniverSheetsNumfmt.UniverSheetsNumfmtPlugin);univer.registerPlugin(UniverSheetsFormula.UniverSheetsFormulaPlugin);univer.createUnit(UniverCore.UniverInstanceType.UNIVER_SHEET, {})const univerAPI = UniverFacade.FUniver.newAPI(univer)</script>
</body>
</html>
使用univer后端服务协同
后端部署
bash -c "$(curl -fsSL https://get.univer.ai)"
打开链接获取token,后续的安装就开始了
访问8000
curl http://127.0.0.1:8000
如果机器停止了,重启之后可以运行
docker compose up
启动
页面部署
说明服务启动起来了,引入js库尝试,发现报错
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="https://unpkg.com/@univerjs/umd/lib/univer.full.umd.js"></script><script src="https://unpkg.com/@univerjs/umd/lib/locale/zh-CN.js"></script><script src="https://unpkg.com/@univerjs-pro/collaboration/lib/umd/index.js"></script><script src="https://unpkg.com/@univerjs-pro/collaboration-client/lib/umd/index.js"></script><link rel="stylesheet" href="https://unpkg.com/@univerjs/umd/lib/univer.css"><link rel="stylesheet" href="https://unpkg.com/@univerjs-pro/collaboration-client/lib/index.css"><style>#app {width: 100%;height: 100vh;}</style>
</head><body><div id="app"></div><script>var {UniverCore,UniverDesign,UniverEngineRender,UniverEngineFormula,UniverDocs,UniverDocsUi,UniverUi,UniverSheets,UniverSheetsUi,UniverSheetsNumfmt,UniverSheetsFormula,UniverFacade,UniverCollaborationPlugin,UniverCollaborationClientPlugin} = windowvar univer = new UniverCore.Univer({theme: UniverDesign.defaultTheme,locale: UniverCore.LocaleType.ZH_CN,locales: {[UniverCore.LocaleType.ZH_CN]: UniverUMD['zh-CN'],},});univer.registerPlugin(UniverEngineRender.UniverRenderEnginePlugin);univer.registerPlugin(UniverEngineFormula.UniverFormulaEnginePlugin);univer.registerPlugin(UniverUi.UniverUIPlugin, {container: "app",});univer.registerPlugin(UniverDocs.UniverDocsPlugin);univer.registerPlugin(UniverDocsUi.UniverDocsUIPlugin);univer.registerPlugin(UniverSheets.UniverSheetsPlugin);univer.registerPlugin(UniverSheetsUi.UniverSheetsUIPlugin);univer.registerPlugin(UniverSheetsNumfmt.UniverSheetsNumfmtPlugin);univer.registerPlugin(UniverSheetsFormula.UniverSheetsFormulaPlugin);univer.registerPlugin(UniverCollaborationPlugin);univer.registerPlugin(UniverCollaborationClientPlugin, {authzUrl: 'http://192.168.56.10:8000/universer-api/authz',snapshotServerUrl: 'http://192.168.56.10:8000/universer-api/snapshot',collabSubmitChangesetUrl: 'http://192.168.56.10:8000/universer-api/comb',collabWebSocketUrl: 'ws://192.168.56.10:8000/universer-api/comb/connect',});univer.createUnit(UniverCore.UniverInstanceType.UNIVER_SHEET, {})const univerAPI = UniverFacade.FUniver.newAPI(univer)</script>
</body></html>
vue3部署
采用vue3方式开发
pnpm initpnpm create vite --registry=http://registry.npm.taobao.org
pnpm install --registry=http://registry.npm.taobao.org
pnpm run dev
安装依赖包
pnpm add @univerjs/core @univerjs/design @univerjs/docs @univerjs/docs-ui @univerjs/engine-formula @univerjs/engine-render @univerjs/sheets @univerjs/sheets-formula @univerjs/sheets-ui @univerjs/ui --registry=http://registry.npm.taobao.orgpnpm add @univerjs/facade --registry=http://registry.npm.taobao.org
修改App.vue
<template><div ref="excelContent" style="width: 100%;height: 900px;"></div>
</template>
<script setup lang="ts">import "@univerjs/design/lib/index.css";
import "@univerjs/ui/lib/index.css";
import "@univerjs/docs-ui/lib/index.css";
import "@univerjs/sheets-ui/lib/index.css";
import "@univerjs/sheets-formula/lib/index.css";import { onMounted, ref } from 'vue'
import { LocaleType, Tools, Univer, UniverInstanceType } from "@univerjs/core";
import { defaultTheme } from "@univerjs/design";import { UniverFormulaEnginePlugin } from "@univerjs/engine-formula";
import { UniverRenderEnginePlugin } from "@univerjs/engine-render";import { UniverUIPlugin } from "@univerjs/ui";import { UniverDocsPlugin } from "@univerjs/docs";
import { UniverDocsUIPlugin } from "@univerjs/docs-ui";import { UniverSheetsPlugin } from "@univerjs/sheets";
import { UniverSheetsFormulaPlugin } from "@univerjs/sheets-formula";
import { UniverSheetsUIPlugin } from "@univerjs/sheets-ui";import DesignZhCN from '@univerjs/design/locale/zh-CN';
import UIZhCN from '@univerjs/ui/locale/zh-CN';
import DocsUIZhCN from '@univerjs/docs-ui/locale/zh-CN';
import SheetsZhCN from '@univerjs/sheets/locale/zh-CN';
import SheetsUIZhCN from '@univerjs/sheets-ui/locale/zh-CN';
import SheetsFormulaZhCN from '@univerjs/sheets-formula/locale/zh-CN';
import { FUniver } from '@univerjs/facade'const excelContent = ref<HTMLElement | null>(null)onMounted(() => {initSheet()
})const initSheet = () => {const univer = new Univer({theme: defaultTheme,locale: LocaleType.ZH_CN,locales: {[LocaleType.ZH_CN]: Tools.deepMerge(SheetsZhCN,DocsUIZhCN,SheetsUIZhCN,SheetsFormulaZhCN,UIZhCN,DesignZhCN,),},});univer.registerPlugin(UniverRenderEnginePlugin);univer.registerPlugin(UniverFormulaEnginePlugin);univer.registerPlugin(UniverUIPlugin, {container: excelContent.value!,});univer.registerPlugin(UniverDocsPlugin);univer.registerPlugin(UniverDocsUIPlugin);univer.registerPlugin(UniverSheetsPlugin);univer.registerPlugin(UniverSheetsUIPlugin);univer.registerPlugin(UniverSheetsFormulaPlugin);univer.createUnit(UniverInstanceType.UNIVER_SHEET, {});const univerAPI = FUniver.newAPI(univer)
}</script>
<style scoped>
.excel-container {width: 100%;height: 100%;min-height: 90vh;
}
</style>
启动项目查看效果
协同编辑
pnpm add @univerjs-pro/collaboration @univerjs-pro/collaboration-client --registry=http://registry.npm.taobao.org
修改App.vue
<template><div ref="excelContent" style="width: 100%;height: 900px;"></div>
</template>
<script setup lang="ts">import "@univerjs/design/lib/index.css";
import "@univerjs/ui/lib/index.css";
import "@univerjs/docs-ui/lib/index.css";
import "@univerjs/sheets-ui/lib/index.css";
import "@univerjs/sheets-formula/lib/index.css";
import '@univerjs-pro/collaboration-client/lib/index.css';import { onMounted, ref } from 'vue'
import { LocaleType, Tools, Univer, UniverInstanceType,IAuthzIoService,IUndoRedoService } from "@univerjs/core";
import { defaultTheme } from "@univerjs/design";import { UniverFormulaEnginePlugin } from "@univerjs/engine-formula";
import { UniverRenderEnginePlugin } from "@univerjs/engine-render";import { UniverUIPlugin } from "@univerjs/ui";import { UniverDocsPlugin } from "@univerjs/docs";
import { UniverDocsUIPlugin } from "@univerjs/docs-ui";import { UniverSheetsPlugin } from "@univerjs/sheets";
import { UniverSheetsFormulaPlugin } from "@univerjs/sheets-formula";
import { UniverSheetsUIPlugin } from "@univerjs/sheets-ui";import DesignZhCN from '@univerjs/design/locale/zh-CN';
import UIZhCN from '@univerjs/ui/locale/zh-CN';
import DocsUIZhCN from '@univerjs/docs-ui/locale/zh-CN';
import SheetsZhCN from '@univerjs/sheets/locale/zh-CN';
import SheetsUIZhCN from '@univerjs/sheets-ui/locale/zh-CN';
import SheetsFormulaZhCN from '@univerjs/sheets-formula/locale/zh-CN';
import { FUniver } from '@univerjs/facade'import { UniverCollaborationPlugin } from '@univerjs-pro/collaboration';
import { UniverCollaborationClientPlugin } from '@univerjs-pro/collaboration-client';
import CollaborationClientZhCN from '@univerjs-pro/collaboration-client/locale/zh-CN';const excelContent = ref<HTMLElement | null>(null)onMounted(() => {initSheet()
})const initSheet = () => {const univer = new Univer({// 通过将 override 选项设置为 [[IAuthzIoService, null]],可以告诉 Univer 不要注册内置的 IAuthzIoService。// 通过将 override 选项设置为 [[IUndoRedoService, null]],可以告诉 Univer 不要注册内置的 IUndoRedoService// 这样,Univer 将使用 UniverCollaborationPlugin 中提供的服务作为权限、重做恢复服务的实现。override: [[IAuthzIoService,null],[IUndoRedoService,null],],theme: defaultTheme,locale: LocaleType.ZH_CN,locales: {[LocaleType.ZH_CN]: Tools.deepMerge(SheetsZhCN,DocsUIZhCN,SheetsUIZhCN,SheetsFormulaZhCN,UIZhCN,DesignZhCN,CollaborationClientZhCN),},});univer.registerPlugin(UniverRenderEnginePlugin);univer.registerPlugin(UniverFormulaEnginePlugin);univer.registerPlugin(UniverUIPlugin, {container: excelContent.value!,});univer.registerPlugin(UniverDocsPlugin);univer.registerPlugin(UniverDocsUIPlugin);univer.registerPlugin(UniverSheetsPlugin);univer.registerPlugin(UniverSheetsUIPlugin);univer.registerPlugin(UniverSheetsFormulaPlugin);univer.registerPlugin(UniverCollaborationPlugin);univer.registerPlugin(UniverCollaborationClientPlugin, {authzUrl: 'http://192.168.56.10:8000/universer-api/authz',snapshotServerUrl: 'http://192.168.56.10:8000/universer-api/snapshot',collabSubmitChangesetUrl: 'http://192.168.56.10:8000/universer-api/comb',collabWebSocketUrl: 'ws://192.168.56.10:8000/universer-api/comb/connect',});univer.createUnit(UniverInstanceType.UNIVER_SHEET, {});const univerAPI = FUniver.newAPI(univer)if(univerAPI){console.log("univerAPI")}
}</script>
<style scoped>
.excel-container {width: 100%;height: 100%;min-height: 90vh;
}
</style>
一直离线状态,没搞清楚原因
把官方的例子跑起来了,说明后端是没有问题的
代码如下
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Univer UMD With Collaboration, Live Share, Print, Exchange</title><script src="https://unpkg.com/@univerjs/umd/lib/univer.full.umd.js"></script><script src="https://unpkg.com/@univerjs/umd/lib/locale/zh-CN.js"></script><link rel="stylesheet" href="https://unpkg.com/@univerjs/umd/lib/univer.css"><script src="https://unpkg.com/@univerjs-pro/license/lib/umd/index.js"></script><script src="https://unpkg.com/@univerjs-pro/collaboration/lib/umd/index.js"></script><script src="https://unpkg.com/@univerjs-pro/collaboration-client/lib/umd/index.js"></script><link rel="stylesheet" href="https://unpkg.com/@univerjs-pro/collaboration-client/lib/index.css"><script src="https://unpkg.com/@univerjs-pro/live-share/lib/umd/index.js"></script><link rel="stylesheet" href="https://unpkg.com/@univerjs-pro/live-share/lib/index.css"><script src="https://unpkg.com/@univerjs-pro/print/lib/umd/index.js"></script><script src="https://unpkg.com/@univerjs-pro/sheets-print/lib/umd/index.js"></script><link rel="stylesheet" href="https://unpkg.com/@univerjs-pro/sheets-print/lib/index.css"><script src="https://unpkg.com/@univerjs-pro/exchange-client/lib/umd/index.js"></script><link rel="stylesheet" href="https://unpkg.com/@univerjs-pro/exchange-client/lib/index.css"><script src="https://unpkg.com/@univerjs-pro/sheets-exchange-client/lib/umd/index.js"></script><script src="https://unpkg.com/@univerjs-pro/engine-pivot/lib/umd/index.js"></script><script src="https://unpkg.com/@univerjs-pro/sheets-pivot/lib/umd/index.js"></script><script src="https://unpkg.com/@univerjs-pro/sheets-pivot-ui/lib/umd/index.js"></script><link rel="stylesheet" href="https://unpkg.com/@univerjs-pro/sheets-pivot-ui/lib/index.css"><script src="https://unpkg.com/@univerjs-pro/edit-history-viewer/lib/umd/index.js"></script><script src="https://unpkg.com/@univerjs-pro/edit-history-loader/lib/umd/index.js"></script><link rel="stylesheet" href="https://unpkg.com/@univerjs-pro/edit-history-viewer/lib/index.css"><style>body {margin: 0;padding: 0;font-family: Arial, sans-serif;}#app {height: 100vh;width: 100vw;}</style></head><body><div id="app"></div><script>var {UniverCore,UniverDesign,UniverEngineRender,UniverEngineFormula,UniverDocs,UniverDocsUi,UniverUi,UniverSheets,UniverSheetsUi,UniverSheetsNumfmt,UniverCollaboration,UniverCollaborationClient,UniverSheetsThreadComment,UniverExchangeClient,UniverFacade} = window;var { UniverSheetsFormulaPlugin } = UniverSheetsFormula;var { UniverSheetsExchangeClientPlugin } = UniverSheetsExchangeClient;var { UniverSheetsNumfmtPlugin } = UniverSheetsNumfmt;var { UniverDocsPlugin } = UniverDocs;var { UniverRenderEnginePlugin } = UniverEngineRender;var { UniverFormulaEnginePlugin } = UniverEngineFormula;var { UniverUIPlugin } = UniverUi;var { UniverDocsUIPlugin } = UniverDocsUi;var { UniverSheetsPlugin } = UniverSheets;var { UniverSheetsUIPlugin } = UniverSheetsUi;var { UniverSheetsPivotTablePlugin } = UniverSheetsPivot;var { UniverSheetsPivotTableUIPlugin } = UniverSheetsPivotUi;var { UniverEditHistoryLoaderPlugin } = UniverEditHistoryLoader;var { UniverCollaborationPlugin } = UniverCollaboration;var { UniverCollaborationClientPlugin } = UniverCollaborationClient;var { UniverLiveSharePlugin } = UniverLiveShare;var { UniverSheetsPrintPlugin } = UniverSheetsPrint;var { UniverSheetsThreadCommentPlugin, IThreadCommentMentionDataService } = UniverSheetsThreadComment;var { defaultTheme, greenTheme } = UniverDesign;var { FUniver } = UniverFacade;var { UniverExchangeClientPlugin }= UniverExchangeClient;var {UniverLicensePlugin} = UniverLicense;var {UniverInstanceType,Tools,IUndoRedoService,IAuthzIoService,LocaleType,Univer,IConfigService} = window.UniverCore;</script><script>// 你好,开发者,这是一个简单的示例,展示如何使用 Univer UMD 实现协作、实时共享、打印和导入导出功能// 如果你想使用协作、实时共享、打印和交换功能,你需要配置 `universerEndpoint`// `universerEndpoint` 是提供协作、实时共享、打印和交换服务的 Univer 后端服务器地址// 你可以在自己的服务器上部署 Univer 服务器,详见部署指南:https://univer.ai/zh-CN/guides/sheet/server/dockerconst universerEndpoint = '192.168.56.10:8000';// 协同插件从 URL 中获取 unit 参数,如果没有 unit 参数,则创建一个新的 unitconst url = new URL(window.location.href)const unit = url.searchParams.get('unit')if (unit) {// 加载协作、打印、交换的国际化资源Promise.all([fetch('https://unpkg.com/@univerjs-pro/collaboration-client/lib/locale/zh-CN.json').then(res => res.json()),fetch('https://unpkg.com/@univerjs-pro/sheets-print/lib/locale/zh-CN.json').then(res => res.json()),fetch('https://unpkg.com/@univerjs-pro/exchange-client/lib/locale/zh-CN.json').then(res => res.json()),fetch('https://unpkg.com/@univerjs-pro/edit-history-viewer/lib/locale/zh-CN.json').then(res => res.json()),fetch('https://unpkg.com/@univerjs-pro/sheets-pivot/lib/locale/zh-CN.json').then(res => res.json()),fetch('https://unpkg.com/@univerjs-pro/sheets-pivot-ui/lib/locale/zh-CN.json').then(res => res.json()),]).then((langs) => {setup(Tools.deepMerge(UniverUMD['zh-CN'],...langs));})} else {const { UniverInstanceType } = window.UniverCore;fetch(`http://${universerEndpoint}/universer-api/snapshot/${UniverInstanceType.UNIVER_SHEET}/unit/-/create`, {method: 'POST',headers: {'Content-Type': 'application/json',},body: JSON.stringify({type: UniverInstanceType.UNIVER_SHEET,name: 'New Sheet By Univer',creator: 'user',}),}).then((response) => {if (!response.ok) {throw new Error('Failed to create new sheet')}return response.json()}).then((data) => {if (!data.unitID) {throw new Error('create unit failed')}url.searchParams.set('unit', data.unitID)url.searchParams.set('type', String(UniverInstanceType.UNIVER_SHEET))window.location.href = url.toString()}).catch((error) => {console.error(error)})}const setup = (locales) => {var univer = new Univer({theme: defaultTheme,locale: LocaleType.ZH_CN,locales:{[LocaleType.ZH_CN]: locales,},override: [[IAuthzIoService, null],[IUndoRedoService, null]]});univer.registerPlugin(UniverRenderEnginePlugin);univer.registerPlugin(UniverDocsPlugin);univer.registerPlugin(UniverFormulaEnginePlugin);univer.registerPlugin(UniverSheetsFormulaPlugin);univer.registerPlugin(UniverUIPlugin, {container: "app",});univer.registerPlugin(UniverDocsUIPlugin);univer.registerPlugin(UniverSheetsPlugin);univer.registerPlugin(UniverSheetsUIPlugin);const mockUser = {userID: 'mockId',name: 'MockUser',avatar: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAInSURBVHgBtZU9TxtBEIbfWRzFSIdkikhBSqRQkJqkCKTCFkqVInSUSaT0wC8w/gXxD4gU2nRJkXQWhAZowDUUWKIwEgWWbEEB3mVmx3dn4DA2nB/ppNuPeWd29mMIPXDr+RxwtgRHeW6+guNPRxogqnL7Dwz9psJ27S4NShaeZTH3kwXy6I81dlRKcmRui88swdq9AcSFL7Buz1Vmlns64MiLsCjzwnIYHLH57tbfFbs7KRaXyEU8FVZofqccOfA5l7Q8LPIkGrwnb2RPNEXWFVMUF3L+kDCk0btDDAMzOm5YfAHDwp4tG74wnzAsiOYMnJ3GoDybA7IT98/jm5+JNnfiIzAS6LlqHQBN/i6b2t/cV1Hh6BfwYlHnHP4AXi5q/8kmMMpOs8+BixZw/Fd6xUEHEbnkgclvQP2fGp7uShRKnQ3G32rkjV1th8JhIGG7tR/JyjGteSOZELwGMmNqIIigRCLRh2OZIE6BjItdd7pCW6Uhm1zzkUtungSxwEUzNpQ+GQumtH1ej1MqgmNT6vwmhCq5yuwq56EYTbgeQUz3yvrpV1b4ok3nYJ+eYhgYmjRUqErx2EDq0Fr8FhG++iqVGqxlUJI/70Ar0UgJaWHj6hYVHJrfKssAHot1JfqwE9WVWzXZVd5z2Ws/4PnmtEjkXeKJDvxUecLbWOXH/DP6QQ4J72NS0adedp1aseBfXP8odlZFfPvBF7SN/8hky1TYuPOAXAEipMx15u5ToAAAAABJRU5ErkJggg==',anonymous: false,canBindAnonymous: false,};class CustomMentionDataService {trigger = '@';async getMentions (search) {return [{id: mockUser.userID,label: mockUser.name,type: 'user',icon: mockUser.avatar,},{id: '2',label: 'User2',type: 'user',icon: mockUser.avatar,},];}}univer.registerPlugin(UniverSheetsThreadCommentPlugin, {overrides: [[IThreadCommentMentionDataService, { useClass: CustomMentionDataService }]],});// 注册协同插件univer.registerPlugin(UniverCollaborationPlugin);univer.registerPlugin(UniverCollaborationClientPlugin, {authzUrl: `http://${universerEndpoint}/universer-api/authz`,snapshotServerUrl: `http://${universerEndpoint}/universer-api/snapshot`,collabSubmitChangesetUrl: `http://${universerEndpoint}/universer-api/comb`,collabWebSocketUrl: `ws://${universerEndpoint}/universer-api/comb/connect`,// 如果你使用了导入导出插件,需要配置以下参数uploadFileServerUrl: `http://${universerEndpoint}/universer-api/stream/file/upload`,signUrlServerUrl: `http://${universerEndpoint}/universer-api/file/{fileID}/sign-url`,});// 注册实时共享插件univer.registerPlugin(UniverLiveSharePlugin)// 注册打印插件univer.registerPlugin(UniverSheetsPrintPlugin)// 注册导入导出插件univer.registerPlugin(UniverExchangeClientPlugin, {uploadFileServerUrl: `http://${universerEndpoint}/universer-api/stream/file/upload`,importServerUrl: `http://${universerEndpoint}/universer-api/exchange/{type}/import`,exportServerUrl: `http://${universerEndpoint}/universer-api/exchange/{type}/export`,getTaskServerUrl: `http://${universerEndpoint}/universer-api/exchange/task/{taskID}`,signUrlServerUrl: `http://${universerEndpoint}/universer-api/file/{fileID}/sign-url`,})univer.registerPlugin(UniverSheetsExchangeClientPlugin)}</script></body></html>
使用websocket协同
pnpm init
pnpm add ws --registry=http://registry.npm.taobao.org
修改package.json
{"name": "server","version": "1.0.0","description": "","main": "index.js","scripts": {"dev": "node index.js"},"keywords": [],"author": "","license": "ISC","dependencies": {"ws": "^8.18.0"}
}
新建index.js
//创建一个WebSocket服务器,在8080端口启动
const WebSocket = require('ws');
const server = new WebSocket.Server({ port: 8089 });
console.log("websocket服务器启动于:localhost:8089");// 监听前端连接websocket(ws.on的connection事件)
server.on('connection', function connection(ws, req) {const ip = req.socket.remoteAddress;const port = req.socket.remotePort;const clientName = ip + port;console.log('%s 连接了', clientName);ws.on('message', (msg = {}) => {console.log('客户端发送给服务器端', msg.toString('utf8'));// 由服务端往客户端发送数据server.clients.forEach((client) => {//console.log(client, 'client');client.send(msg);});});
});
//只要有WebSocket连接到该服务器,就会触发'connection'事件;req对象可以用来获取客户端的信息,如ip、端口号
//获取所有已连接的客户端信息,则可以使用server.clients数据集// 服务器接收数据(ws.on的message事件)
// module.exports.listener = () => {
// server.on('connection', (ws, req) => {
// console.log('有客户端连接成功了', ws, req);// // 对客户端的连接对象进行message事件的监听
// // 当客户端有消息发送给服务器时,服务器就能够触发该消息
// // msg:由客户端发给服务端的数据
// ws.on('message', (msg = {}) => {
// console.log('客户端发送给服务器端', msg);
// // 当接收到客户端传的参数之后服务器端可以执行某些操作(具体看需求)
// // 小编这里是做了一个数据返回给客户端
// // 是当客户端连接成功之后会发送一条信息告诉服务器,服务器监听到信息之后再返回数据给客户端// // 由服务端往客户端发送数据
// server.clients.forEach((client) => {
// console.log(client, 'client');
// // client.send(JSON.stringify(info));
// client.send(msg);
// });
// });
// });
// };
把websocket启动起来
pnpm run dev
前端参考vue3搭建起来,然后修改App.vue
<template><div id="excelContent" style="width: 100%;height: 900px;"></div>
</template><script setup lang="ts">
import "@univerjs/design/lib/index.css";
import "@univerjs/ui/lib/index.css";
import "@univerjs/docs-ui/lib/index.css";
import "@univerjs/sheets-ui/lib/index.css";
import "@univerjs/sheets-formula/lib/index.css";import { onMounted, ref } from 'vue'
import { LocaleType, Tools, Univer, UniverInstanceType } from "@univerjs/core";
import { defaultTheme } from "@univerjs/design";import { UniverFormulaEnginePlugin } from "@univerjs/engine-formula";
import { UniverRenderEnginePlugin } from "@univerjs/engine-render";import { UniverUIPlugin } from "@univerjs/ui";import { UniverDocsPlugin } from "@univerjs/docs";
import { UniverDocsUIPlugin } from "@univerjs/docs-ui";import { UniverSheetsPlugin } from "@univerjs/sheets";
import { UniverSheetsFormulaPlugin } from "@univerjs/sheets-formula";
import { UniverSheetsUIPlugin } from "@univerjs/sheets-ui";import DesignZhCN from '@univerjs/design/locale/zh-CN';
import UIZhCN from '@univerjs/ui/locale/zh-CN';
import DocsUIZhCN from '@univerjs/docs-ui/locale/zh-CN';
import SheetsZhCN from '@univerjs/sheets/locale/zh-CN';
import SheetsUIZhCN from '@univerjs/sheets-ui/locale/zh-CN';
import SheetsFormulaZhCN from '@univerjs/sheets-formula/locale/zh-CN';
import { FUniver } from '@univerjs/facade'onMounted(() => {initSheet()
})const initSheet = () => {const univer = new Univer({theme: defaultTheme,locale: LocaleType.ZH_CN,locales: {[LocaleType.ZH_CN]: Tools.deepMerge(SheetsZhCN,DocsUIZhCN,SheetsUIZhCN,SheetsFormulaZhCN,UIZhCN,DesignZhCN,),},});univer.registerPlugin(UniverRenderEnginePlugin);univer.registerPlugin(UniverFormulaEnginePlugin);univer.registerPlugin(UniverUIPlugin, {container: 'excelContent',});univer.registerPlugin(UniverDocsPlugin);univer.registerPlugin(UniverDocsUIPlugin);univer.registerPlugin(UniverSheetsPlugin);univer.registerPlugin(UniverSheetsUIPlugin);univer.registerPlugin(UniverSheetsFormulaPlugin);//必须指定iduniver.createUnit(UniverInstanceType.UNIVER_SHEET, {id: 'univer-1',sheets: {'sheet-01': {id: 'sheet-01',name: 'sheet1',cellData: {},},},});const univerAPI = FUniver.newAPI(univer)const ws = univerAPI.createSocket('ws://localhost:8089');ws.open$.subscribe(() => {console.log('websocket opened');// ws.send('hello')});ws.message$.subscribe((message) => {const data = message.data;let result = '';if (data instanceof Blob) {// Create a FileReader to read the Blob data const reader = new FileReader();// Define the onload event handler reader.onload = (e) => {// The result property contains the data as a typed array or string // Here we assume the data is a UTF-8 encoded string const resultData = e.target?.resultif (typeof resultData == 'string') {result = resultData;console.log(result);console.log('websocket message', result);const content = JSON.parse(result);//JSON.parse(message.data);console.log('content', content);const { command, options } = content;const { id, params } = command;console.log(params, 'params--------');// 接受到协同数据,本地落盘univerAPI.executeCommand(id, params, options);}};// Read the Blob data as a text string reader.readAsText(data, 'utf-8');} else if (typeof data === 'string') {result = data;//console.log('websocket message', JSON.parse(message.data));console.log('websocket message', result);const content = JSON.parse(result);//JSON.parse(message.data);console.log('content', content);const { command, options } = content;const { id, params } = command;console.log(params, 'params--------');// 接受到协同数据,本地落盘univerAPI.executeCommand(id, params, options);}});ws.close$.subscribe(() => {console.log('websocket closed');});ws.error$.subscribe((error) => {console.log('websocket error', error);});univerAPI.onCommandExecuted((command, options) => {// 仅同步本地 mutationif (command.type !== 2 ||options?.fromCollab ||options?.onlyLocal ||command.id === 'doc.mutation.rich-text-editing') {return;}const commandInfo = JSON.stringify({command,options: { fromCollab: true },});console.log(commandInfo, 'commandInfo');ws.send(commandInfo);});
}
</script><style scoped></style>
导入导出禁止编辑
IWorksheetData
IWorkBookData
ICellData
导入导出
导入导出
导入的例子
导入导出
创建vue+univer项目
pnpm create vite --registry=http://registry.npm.taobao.org
pnpm add @univerjs/core @univerjs/design @univerjs/docs @univerjs/docs-ui @univerjs/engine-formula @univerjs/engine-render @univerjs/sheets @univerjs/sheets-formula @univerjs/sheets-ui @univerjs/ui --registry=http://registry.npm.taobao.org
pnpm add @univerjs/facade --registry=http://registry.npm.taobao.org
pnpm add @univerjs-pro/facade --registry=http://registry.npm.taobao.org
App.vue
<template><div><button @click="importExcel">加载数据</button><button @click="updateData">更新数据</button><button @click="closeOrOpenEditor">禁止或者开启编辑</button><button @click="saveData">保存数据</button></div><div ref="excelContent" style="width: 100%;height: 900px;"></div>
</template><script setup lang="ts">import "@univerjs/design/lib/index.css";
import "@univerjs/ui/lib/index.css";
import "@univerjs/docs-ui/lib/index.css";
import "@univerjs/sheets-ui/lib/index.css";
import "@univerjs/sheets-formula/lib/index.css";import {onMounted, ref} from 'vue'
import {LocaleType, Tools, Univer, UniverInstanceType, IWorkbookData, BooleanNumber} from "@univerjs/core";
import {defaultTheme} from "@univerjs/design";import {UniverFormulaEnginePlugin} from "@univerjs/engine-formula";
import {UniverRenderEnginePlugin} from "@univerjs/engine-render";import {UniverUIPlugin} from "@univerjs/ui";import {UniverDocsPlugin} from "@univerjs/docs";
import {UniverDocsUIPlugin} from "@univerjs/docs-ui";import {UniverSheetsPlugin} from "@univerjs/sheets";
import {UniverSheetsFormulaPlugin} from "@univerjs/sheets-formula";
import {UniverSheetsUIPlugin} from "@univerjs/sheets-ui";import DesignZhCN from '@univerjs/design/locale/zh-CN';
import UIZhCN from '@univerjs/ui/locale/zh-CN';
import DocsUIZhCN from '@univerjs/docs-ui/locale/zh-CN';
import SheetsZhCN from '@univerjs/sheets/locale/zh-CN';
import SheetsUIZhCN from '@univerjs/sheets-ui/locale/zh-CN';
import SheetsFormulaZhCN from '@univerjs/sheets-formula/locale/zh-CN';
import {FUniver} from "@univerjs-pro/facade";const excelContent = ref<HTMLElement | null>(null)onMounted(() => {initSheet()
})let canEditor = true;
let univerAPI: FUniver | null = null;
let univer: Univer | null = null;
const initSheet = () => {univer = new Univer({theme: defaultTheme,locale: LocaleType.ZH_CN,locales: {[LocaleType.ZH_CN]: Tools.deepMerge(SheetsZhCN,DocsUIZhCN,SheetsUIZhCN,SheetsFormulaZhCN,UIZhCN,DesignZhCN,),},});univer.registerPlugin(UniverRenderEnginePlugin);univer.registerPlugin(UniverFormulaEnginePlugin);univer.registerPlugin(UniverUIPlugin, {container: excelContent.value!,});univer.registerPlugin(UniverDocsPlugin);univer.registerPlugin(UniverDocsUIPlugin);univer.registerPlugin(UniverSheetsPlugin);univer.registerPlugin(UniverSheetsUIPlugin);univer.registerPlugin(UniverSheetsFormulaPlugin);
}
const importExcel = () => {const workbook: IWorkbookData = {id: '007',//唯一idname: '部门人数统计',appVersion: '1.0.0',locale: LocaleType.ZH_CN,styles: {},sheetOrder: ['sheet1', 'sheet2'],sheets: {sheet1: {id: 'sheet1',name: '工作表 1',tabColor: '#FF0000',hidden: BooleanNumber.FALSE,freeze: {xSplit: 1, ySplit: 1, startRow: 1, startColumn: 1},rowCount: 1000,columnCount: 26,defaultColumnWidth: 100,defaultRowHeight: 25,mergeData: [],cellData: {// 第一行0: {//t表示类型 布尔、字符串和数字 https://univer.ai/typedoc/@univerjs/core/enumerations/CellValueType// 第一列0: {v: 'A1'},// 第二列1: {v: 'B1'},},// 第二行1: {// 第一列0: {v: 'A2'},// 第二列1: {v: 'B2'},},},rowData: [],columnData: [],rowHeader: {width: 40},columnHeader: {height: 20},showGridlines: BooleanNumber.TRUE,rightToLeft: BooleanNumber.FALSE},sheet2: {}}};if (univer != null) {console.log('univer!=null')if (univerAPI == null) {//模拟服务端获取数据univer.createUnit(UniverInstanceType.UNIVER_SHEET, workbook);univerAPI = FUniver.newAPI(univer);}} else {console.log('univer==null')}
}const updateData = () => {if (univerAPI != null) {const sheet = univerAPI.getActiveWorkbook()?.getActiveSheet();// A1 设置数字 100const range = sheet?.getRange(0, 0, 1, 1);range?.setValue(100);}
}const closeOrOpenEditor = () => {canEditor = !canEditor;if (univerAPI != null) {const workBook = univerAPI.getActiveWorkbook();workBook?.setEditable(canEditor);}
}const saveData = () => {if (univerAPI != null) {const savedExcel = univerAPI.getActiveWorkbook()?.save();if (savedExcel != null) {console.log(savedExcel)}}else{console.log('数据为空')}
}
</script><style scoped>
</style>
配置单元格边框颜色
cellData: {// 第一行0: {//t表示类型 布尔、字符串和数字 https://univer.ai/typedoc/@univerjs/core/enumerations/CellValueType// 第一列0: {v: 'A1',},// 第二列1: {v: 'B1'},},// 第二行1: {// 第一列0: {v: 'A2'},// 第二列1: {v: 'B2',//边框s: {bd: {// 上边框t: {s: 7, // 边框样式cl: { // 边框颜色rgb: '#ff0000'}},// 下边框b: {s: 7, // 边框样式cl: { // 边框颜色rgb: '#ff0000'}},// 左边框l: {s: 7, // 边框样式cl: { // 边框颜色rgb: '#ff0000'}},// 右边框r: {s: 7, // 边框样式cl: { // 边框颜色rgb: '#ff0000'}},}}},},
},
高亮显示
pnpm add @univerjs/sheets-crosshair-highlight --registry=http://registry.npm.taobao.org
参考
https://univer.ai/zh-CN/guides/sheet/introduction
https://blog.csdn.net/m0_73884922/article/details/139971295
https://github.com/dream-num/usip-example/tree/main
https://github.com/dream-num/univer-pro-sheet-start-kit
https://github.com/dream-num/univer-sheet-start-kit
相关文章:

univer实现excel协同
快速入门 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><script src&q…...

JavaScript进阶笔记--深入对象-内置构造函数及案例
深入对象 创建对象三种方式 利用对象字面量new Object({…})利用构造函数 // 1. 字面量创建对象const obj1 {name: pig,age: 18};console.log(obj1); // {name: "pig", age: 18}// 2. 构造函数创建对象function Pig(name, age) {this.name…...

网络爬虫自动化Selenium模拟用户操作
自动化测试和网络爬虫在现代软件开发中占据着重要的位置。它们通过自动化用户操作,减少了人工重复操作的时间成本。Selenium作为一个功能强大且应用广泛的自动化工具,不仅能在不同的浏览器中运行自动化测试,还能进行跨平台测试,并允许与多种编程语言集成。本教程将介绍如何…...

尚硅谷rabbitmq 2024 流式队列2024指定偏移量 第55节答疑
rabbitmq的stream: 4、对比 autoTrackingstrategy方式:始终监听Stream中的新消息(狗狗看家,忠于职守)指定偏移量方式:针对指定偏移量的消息消费之后就停止(狗狗叼飞盘,回来就完) 这两种分别怎么写?java 在 RabbitMQ 中,…...

NSSCTF-WEB-pklovecloud
目录 前言 正文 思路 尝试 结尾 前言 许久未见,甚是想念. 今天来解一道有意思的序列化题 正文 思路 <?php include flag.php; class pkshow {function echo_name(){return "Pk very safe^.^";} }class acp {protected $cinder;public $neutron;public $…...

深入Postman- 自动化篇
前言 在前两篇博文《Postman使用 - 基础篇》《玩转Postman:进阶篇》中,我们介绍了 Postman 作为一款专业接口测试工具在接口测试中的主要用法以及它强大的变量、脚本功能,给测试工作人员完成接口的手工测试带来了极大的便利。其实在自动化测试上,Postman 也能进行良好的支…...

react-JSX
JSX理念 jsx在编译的时候会被babel编译为react.createELement方法 在使用jsx的文件中,需要引入react。import React from "react" jsx会被编译为React.createElement,所有jsx的运行结果都是react element React Component 在react中,常使用…...

深度对比:IPguard与Ping32在企业网络管理中的应用
随着网络安全形势日益严峻,企业在选择网络管理工具时需慎之又慎。IPguard与Ping32是目前市场上两款颇具代表性的产品,它们在功能、性能以及应用场景上各有优势。本文将对这两款产品进行深度对比,以帮助企业找到最合适的解决方案。 IPguard以其…...

AI测试之 TestGPT
如今最火热的技术莫非OpenAI的ChatGPT莫属,AI技术也在很多方面得到广泛应用。今天我们要介绍的TestGPT就是一个软件测试领域中当红的应用。 TestGPT是什么? TestGPT是一家总部位于以色列特拉维夫的初创公司 CodiumAI Ltd.,发布的一款用于测…...

JavaEE-进程与线程
1.进程 1.1什么是进程 每个应⽤程序运⾏于现代操作系统之上时,操作系统会提供⼀种抽象,好像系统上只有这个程序在运 ⾏,所有的硬件资源都被这个程序在使⽤。这种假象是通过抽象了⼀个进程的概念来完成的,进程可 以说是计算机科学…...

JAVA软开-面试经典问题(6)-equals与hashcode方法
1.equals方法 1.Object类中的equals方法比较的是两个对象的地址(底层原理是 比较的,即比较的是对象的地址) return (this obj);2.基本数据类型的包装类和String类都重写了equals方法。 基本数据类型:比较的是数值的是否相等 …...

计算机网络(以Linux讲解)
计算机网络 网络协议初识协议分层OSI七层模型TCP/IP五层模型--初识 网络中的地址管理IP地址MAC地址 网络传输基本流程网络编程套接字预备知识网络字节序socket编程UDP socketTCP socket地址转换函数Jsoncpp 进程间关系与守护进程进程组会话控制终端作业控制守护进程 网络命令TC…...

计算机网络基本架构知识点
1. 网络体系结构模型: - OSI 七层模型: - 物理层:是网络通信的基础层,负责在物理介质上传输比特流。该层定义了物理连接的标准,如电缆的类型、接口的形状、插头的规格等,以及信号的传输方式,包括…...

GES DISC 的 ATMOS L2 潜在温度网格上的痕量气体,固定场格式 V3 (ATMOSL2TF)
ATMOS L2 Trace Gases on Potential Temperature Grid, Fixed Field Format V3 (ATMOSL2TF) at GES DISC 简介 GES DISC 的 ATMOS L2 潜在温度网格上的痕量气体,固定场格式 V3 (ATMOSL2TF) 这是版本3的气溶胶痕量分子光谱(ATMOS)第二级产品…...

MLCC贴片电容不同材质区别:【及电容工作原理】
贴片电容的材质常规有:NPO(COG),X7R,X5R,Y5V 等,主要区别是它们的填充介质不同。在相同的体积下由于填充介质不同所组成的电容器的容量就不同,随之带来的电容器的介质损耗、容量稳定…...

Word粘贴时出现“文件未找到:MathPage.WLL”的解决方案
解决方案 一、首先确定自己电脑的位数(这里默认大家的电脑都是64位)二、右击MathType桌面图标,点击“打开文件所在位置”,然后分别找到MathPage.WLL三、把这个文件复制到该目录下:C:\Program Files\Microsoft Office\r…...

前端开发笔记--html 黑马程序员1
文章目录 前端开发工具--VsCode前端开发基础语法VsCode优秀插件Chinese --中文插件Auto Rename Tag --自动重命名插件open in browserOpen in Default BrowserOpen in Other Browser Live Server -- 实时预览 前端开发工具–VsCode 轻量级与快速启动 快速加载:VSCo…...

ARM/Linux嵌入式面经(四四):华星光电
文章目录 1、自我介绍2、介绍一下你最得意的一个项目3、这个项目里面都用到了什么模块,什么型号,有什么作用4、移植操作系统的过程中,流程是什么,需要注意什么移植操作系统的流程需要注意的事项面试官可能的追问及回答5、你用的传感器挺多的,怎么保证传感器传输的稳定性,…...

帮助,有奖提问
<?php $u $_GET[“user”]; //变量获取 $v $_GET[“variable”]; //$v看flag,绕过正则 $flag‘flag{}; if(isset($u)&&(file_get_contents($u,‘r’)“im admin”)){//猜测data://协议 //检查u指向 echo “hello admin!<br>”; if(preg_…...

Java编辑工具IDEA
哪个编程工具让你的工作效率翻倍? 在日益繁忙的工作环境中,选择合适的编程工具已成为提升开发者工作效率的关键。不同的工具能够帮助我们简化代码编写、自动化任务、提升调试速度,甚至让团队协作更加顺畅。那么,哪款编程工具让你…...

闲谈Promise
预备知识 回调函数:当一个函数作为参数传入另一个函数中,并且它不会立刻执行,当满足一定条件之后,才会执行,这种函数称为回调函数。比如:定时器。异步任务:与之对应的概念是同步任务࿰…...

【C++堆(优先队列)】1882. 使用服务器处理任务|1979
本文涉及知识点 C堆(优先队列) LeetCode1882. 使用服务器处理任务 给你两个 下标从 0 开始 的整数数组 servers 和 tasks ,长度分别为 n 和 m 。servers[i] 是第 i 台服务器的 权重 ,而 tasks[j] 是处理…...

VBA高级应用30例应用3Excel中的ListObject对象:选择表的一部分
《VBA高级应用30例》(版权10178985),是我推出的第十套教程,教程是专门针对高级学员在学习VBA过程中提高路途上的案例展开,这套教程案例与理论结合,紧贴“实战”,并做“战术总结”,以…...

C语言-变量
全局变量可以定义在头文件中吗? 在C和C编程中,全局变量可以定义在头文件中,但通常不建议这样做,因为这可能导致多个源文件(.c 或 .cpp 文件)包含同一个头文件时,发生多重定义错误(m…...

linux下位机出现使用TCP socket为0的问题
问题现象:下位机做TCP服务器,上位机来连接下位机的TCP服务,中间会有主动断开(上位机主动关闭socket)和异常断开(网线断开)的情况,出现异常的时候,上位机连接下位机的TCP …...

论文笔记:Prototypical Verbalizer for Prompt-based Few-shot Tuning
论文来源:ACL 2022 论文地址:https://arxiv.org/pdf/2203.09770.pdfhttps://arxiv.org/pdf/2203.09770.pdf 论文代码:https://github.com/thunlp/OpenPrompthttps://github.com/thunlp/OpenPrompt Abstract 基于提示的预训练语言模型&#…...

nn.functional.softmax(X, dim=-1)
dim-1表示在最后一个维度(大概率是一行)应用Softmax函数,将值标准化为概率分布。 实例 假设我们有一个张量X,形状为(2,3),内容如下: import torch import torch.nn.…...

【动态规划】子数组系列(上)
1. 最大子数组和 53. 最大子数组和 状态表示:以 i 位置为结尾时的所有子数组中的最大和 状态转移方程: i 位置为结尾的子数组又可以分为长度为 1 的和大于 1 的,长度为 1 就是 nums[i] ,长度不为 1 就是 dp[i - 1] nums[i]&…...

字节青训营入门算法题:飞行棋分组
链接:飞行棋分组🔗🔗 题目 现在有一堆飞行棋棋子,每个棋子上标有数字序号。需要将这些棋子分成若干组,每组包含5个棋子,且组内所有棋子的数字序号必须相同。需要判断是否可以完成这样的分组。 解答 为了…...

# 执行 rpm -qa | grep qq 查询软件安装情况时报错 数据库损坏 db3 error(-30974)
执行 rpm -qa | grep qq 查询软件安装情况时报错 数据库损坏 db3 error(-30974) 一、问题描述: 在 linux 系统上,使用包管理工具 rpm 查询某一个软件安装情况,如:执行 rpm -qa | grep qq 时,报错 数据库损坏 db3 err…...