feat: integrate luckysheet inline and fullscreen

This commit is contained in:
liaibo
2025-11-23 17:22:35 +08:00
parent c8e479aeab
commit 994dd4e67c
529 changed files with 403413 additions and 201 deletions
+35
View File
@@ -0,0 +1,35 @@
// 封装用户请求axios
import axios, { AxiosRequestConfig } from "axios";
import { isDev } from "../utils";
export const fetch = (options: AxiosRequestConfig) => {
return axios({
...options,
});
};
axios.defaults.baseURL = isDev() ? "/api" : "";
// 添加请求拦截器
axios.interceptors.request.use(
(config) => {
// 在发送请求之前进行操作
return config;
},
(error) => {
// 对请求错误进行操作
return Promise.reject(error);
}
);
// 添加响应拦截器
axios.interceptors.response.use(
function (res) {
// 解构返回
return res;
},
function (error) {
// 对响应错误进行操作
return Promise.reject(error);
}
);
+19
View File
@@ -0,0 +1,19 @@
import { fetch } from "./core";
// 导出初始化工作簿 API
export const API_getWorkerBook = (gridKey: string) => {
return fetch({
url: "/getWorkerBook",
method: "post",
data: { gridKey },
});
};
// 导出图片上传API
export const API_uploadImage = (data: FormData) => {
return fetch({
url: "/uploadImage",
method: "POST",
data,
});
};
@@ -0,0 +1,23 @@
// 导出后台服务地址
export const SERVER_URL = "http://localhost:9000";
// 导出协同服务地址
export const WS_SERVER_URL = "ws://127.0.0.1:9000";
// 协同服务初始化失败时,提供的默认初始化数据
export const defaultSheetData = [
{
name: "Sheet1",
celldata: [
{
r: 0,
c: 0,
v: {
v: "协同服务不可用,当前为普通模式",
bg: "#ff0000",
fc: "#ffffff",
},
},
],
},
];
+86
View File
@@ -0,0 +1,86 @@
/**
* @author https://gitee.com/wfeng0/luckysheet-crdt
* @description Luckysheet CRDT 协同全功能实现 - Web App
* @license Apache 2.0
* @copyright Copyright (c) 2025 https://gitee.com/wfeng0/luckysheet-crdt
* @time 2024年12月05日
*/
import "./style/index.css";
import { API_getWorkerBook } from "./axios";
import { defaultSheetData, WS_SERVER_URL } from "./config";
import { uploadImage, imageUrlHandle, getRandom, getLoadUrl, registerPlugins } from "./utils";
window.onload = initLuckysheet;
const luckysheet = Reflect.get(window, "luckysheet");
/**
* 需要监听刷新、退出浏览器事件,关闭socket 连接,避免协同异常
*/
window.onbeforeunload = () => luckysheet && luckysheet.closeWebSocket();
/**
* 初始化前台 Luckysheet
* 请注意,目前前台仅为展示,并无其他能力,因此加载的是默认协同演示 worker books 数据,gridkey = gridkey_demo
* 常理来说,当前工作簿的数据,都应该通过 fileid (gridkey 请求得来
*/
async function initLuckysheet() {
const id = getRandom();
const username = encodeURIComponent(`用户_${id}`);
const gridKey = "gridkey_demo"; // 请注意大小写哈~
const options = {
lang: "zh",
title: "Luckysheet",
container: "luckysheetContainer",
// showinfobar: false, // 隐藏顶部的信息栏
// showlogo: false, // 隐藏顶部的 logo
allowUpdate: false, // 配置协同功能
loadUrl: "",
updateUrl: "", // 协同服务转发服务
plugins: registerPlugins(),
menuHandler: {
customs: [
{
label: "测试",
value: "demo",
},
],
},
};
// 是否开启协同模式
const isCollaboration = true;
if (!isCollaboration) return luckysheet.create(options);
try {
// 根据 gridkey 请求当前 workerbook 数据
const { data } = await API_getWorkerBook(gridKey);
// 定义请求是否成功
const isSuccess = data.code === 200;
/**
* 兼容无数据库服务场景
* 1. 如果请求成功,则使用数据库配置的 workerbook 数据
* 2. 如果请求失败,则使用默认配置的 workerbook 数据
*/
options.lang = isSuccess ? data.data.lang : "zh";
options.title = isSuccess ? data.data.title : "未命名工作簿";
// 协同场景下,才进行图片优化
Reflect.set(options, "uploadImage", uploadImage);
Reflect.set(options, "imageUrlHandle", imageUrlHandle);
options.allowUpdate = true;
options.loadUrl = getLoadUrl(gridKey);
options.updateUrl = `${WS_SERVER_URL}?type=luckysheet&userid=${id}&username=${username}&gridkey=${gridKey}`;
luckysheet.create(options);
} catch (error) {
console.error("==> 协同服务异常", error);
// 不然初始化普通模式,避免页面空白
Reflect.set(options, "data", defaultSheetData);
luckysheet.create(options);
}
}
@@ -0,0 +1,33 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body,
#app,
.luckysheetContainer {
width: 100vw;
height: 100vh;
overflow: hidden;
}
#app {
display: flex;
}
iframe {
flex: 1;
}
.left {
position: relative;
height: 100%;
width: 100%;
}
.right {
position: relative;
flex: 1;
}
+70
View File
@@ -0,0 +1,70 @@
import { SERVER_URL } from "../config";
import { API_uploadImage } from "../axios";
// 处理协同图片上传
export const uploadImage = async (file: File) => {
// 此处拿到的是上传的 file 对象,进行文件上传 ,配合 node 接口实现
const formData = new FormData();
formData.append("image", file);
const { data } = await API_uploadImage(formData);
// *** 关键步骤:需要返回一个地址给 luckysheet ,用于显示图片
if (data.code === 200) return Promise.resolve(data.url);
else return Promise.resolve("image upload error");
};
// 处理上传图片的地址
export const imageUrlHandle = (url: string) => {
// 已经是 // http data 开头则不处理
if (/^(?:\/\/|(?:http|https|data):)/i.test(url)) return url;
// 不然拼接服务器路径
return SERVER_URL + url;
};
// 获取随机值
export const getRandom = () => {
return Math.random().toString(16).slice(2, 8);
};
// 获取当前环境是否为 开发环境
export const isDev = () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return import.meta.env.MODE === "development";
};
// 获取 loadUrl 地址
export const getLoadUrl = (gridKey: string) => {
return `${isDev() ? "/api/" : "/"}loadSheetData?gridkey=${gridKey}`;
};
// 注册插件
export const registerPlugins = () => {
return [
{
name: "chart",
dependScripts: [
"/lib/expendPlugins/libs/vue@2.6.11.min.js",
"/lib/expendPlugins/libs/vuex.min.js",
"/lib/expendPlugins/libs/elementui.min.js",
"/lib/expendPlugins/libs/echarts.min.js",
"/lib/expendPlugins/libs/chartmix.umd.min.js",
],
dependLinks: ["/lib/expendPlugins/libs/element-ui.css", "/lib/expendPlugins/libs/chartmix.css"],
},
{
name: "vchart",
dependScripts: ["/lib/expendPlugins/libs/vchart.min.js"],
dependLinks: ["/lib/expendPlugins/libs/vchart.css"],
},
{
name: "fileImport",
dependScripts: ["/lib/expendPlugins/libs/luckyexcel.umd.js"],
},
{
name: "fileExport",
dependScripts: ["/lib/expendPlugins/libs/exceljs.min.js", "/lib/expendPlugins/libs/fileSaver.min.js"],
},
];
};