Files
mnote/luckysheet-crdt-master/README-zh.md
T

20 KiB
Raw Permalink Blame History

Luckysheet CRDT Logo

Luckysheet CRDT

简体中文 | English

在线体验地址: https://luckysheet-crdt.netlify.app/

协同编辑演示


项目分支

  • master: 稳定版,提供可选数据库服务,功能完整实现
  • master-alpha: 开发版,提供可选数据库服务,功能完整实现
  • master-vue: 稳定版,提供用户系统、文件系统,功能完整实现,依赖数据库服务

仓库地址

⚠️ 温馨提示

最新的特性始终在Gitee上先更新,并且以 Gitee [master-alpha]Gitee [master]Github [master]的顺序更新。

项目说明

  1. 本项目基于 Luckysheet 源码修改,请遵循原作者开源协议,同时,请不要删除或修改源码头部版权声明
  2. 本项目以 Apache2.0 协议开源,请放心使用,同时,本项目也将回馈于 Luckysheet 社区,丰富社区生态,再次感谢 @Luckysheet 团队 ❤️
  3. 项目为 Luckysheet 协同增强版(全功能实现),意在提供协同实现思路、数据存储服务、协同演示
  4. 项目支持 可选数据库服务,没有数据库的用户数据无法持久化存储,协同功能并不受影响,⚠️ 仅对 mastermaster-alpha 分支有效,master-vue 依赖数据库功能实现用户系统
  5. 项目使用 Sequelize 作为 ORM 数据服务技术,支持 MySQL、SQLite、PostgreSQL、MSSQL 等数据库,方便用户快速迁移
  6. 项目使用 Typescript 作为主要开发语言,提供完整的类型提示,规范代码,提高开发效率。
  7. 个人精力有限,存在 BUG 及功能未完善之处,请提交 issue,我会及时处理
  8. 也欢迎大家 fork 项目,提交 PR,一起完善项目

收费声明

为了更好地驱动开源发展,从 2025-04-15e12d2f4850127f53292a5161445fc500593176b9 版本开始,项目不再提供 Luckysheet 源码修改部分。如您需要源码进行二次开发,请联系作者付费获取。

请注意,Luckysheet-CRDT 前后台功能完全开源,不依赖源码即可正常使用所有协同编辑功能,项目启动、运行、部署等环节均不需要源码。

源码主要用于二次开发场景下的功能拓展:

  • 如果您仅使用现有的协同编辑功能,无需获取源码
  • 如果您需要进行二次开发以满足特定需求,请联系作者付费获取源码

服务详情

  • 收费标准199 元
  • 服务内容:仅提供源码包(不包含持续的功能升级、BUG 修复,也不等同于产品购买)
  • 联系方式QQ 群: 522121825(推荐)| Email: <1982392655@qq.com>(推荐)

项目启动

  1. 克隆项目:
git clone https://gitee.com/wfeng0/luckysheet-crdt
  1. 下载依赖
# "dep-npm": "npm install --s && cd server && npm install --s",
# "dep-pnpm": "pnpm install --s && cd server && pnpm install --s"
npm run dep-npm | npm run dep-pnpm
# 推荐使用 pnpm 进行依赖的安装,避免出现版本冲突问题

温馨提示

1. 项目依赖分为前端依赖后端依赖独立的项目);
2. 推荐大家使用 `pnpm install` 安装依赖避免出现版本冲突问题
3. 如果依赖下载报错可以尝试删除 `package-lock.json` 文件重新执行依赖安装
4. 如果封装命令 `npm run dep-npm` 报错请尝试执行 `npm install --s` 命令进行前端依赖安装执行 `cd server && npm install --s` 命令进行后端依赖安装

---

**如果还报错请确认环境是否满足运行条件**
`node -v ==> v20.x.x` // 请确保 node 版本大于 18
`npm -v ==> 10.x.x` // 请确保 npm 版本大于 7.x.x
  1. 🚫如果无数据库服务,请跳过此步骤🚫 配置数据库参数:
// server/src/Config/index.ts
export const SQL_CONFIG = {
	port: 3306,
	host: "127.0.0.1", // localhost or 127.0.0.1
	database: "luckysheet_crdt",
	user: "root",
	password: "root",
};
  1. 🚫如果无数据库服务,请跳过此步骤🚫 同步数据库表:
npm run db

温馨提示

1. 请确保数据库配置正确可用
2. 请确保项目执行同步数据库命令 `npm run db`
3. 项目周期只需要执行一次,确保数据库内存在表结构即可。
  1. 启动服务:
    • 前端服务:npm run dev
    • 后端服务:npm run server
  2. 打开网址:http://localhost:5000 | http://localhost:9000 即可体验协同功能。

项目部署

方式一:Docker 部署(推荐)🐳

使用 Docker Compose 一键部署,无需手动配置环境和数据库!

1. 前提条件

  • 已安装 Docker 20.10+
  • 已安装 Docker Compose 2.0+

2. 快速开始

手动部署:

# 1. 复制环境变量配置文件
cp env.example .env

# 2. 根据需要修改 .env 文件中的配置(可选)
# vim .env

# 3. 启动所有服务
docker-compose up -d

# 4. 查看服务状态
docker-compose ps

# 5. 查看日志
docker-compose logs -f

3. 访问应用


方式二:传统部署

温馨提示:下列所有命令均在项目根目录下执行 /LUCKYSHEET-CRDT/

  1. 注意! 请修改部署的配置文件:src/config/index.ts
// 修改后台地址为 服务器IP地址
export const SERVER_URL = "http://localhost:9000";

// 修改协同地址为 服务器IP地址
export const WS_SERVER_URL = "ws://127.0.0.1:9000";
  1. 先打包前端项目:npm run build

    • 请注意,打包文件文件,默认输出到 server/public/dist
  2. 打包服务端代码:npm run build:server

    • 此时,整个项目的打包结果,会直接输出到 server/wwwroot 目录下,即可直接部署到服务器上。
  3. 请将 server/wwwroot 目录下的文件,上传到服务器上

构建输出目录

  1. 在服务器上安装 node 环境,相关教程可自行上网查询

  2. 同步数据库表:npm run db

    • 请确保数据库配置正确可用(如果无数据库服务,请跳过此步骤
  3. 启动服务:npm run start此命令仅打包后 wwwroot 文件夹下有效 - 等待依赖下载完成,启动服务npm run start,部署完成后访问 http://${ip}:9000 即可访问

服务端口说明

  1. 前端服务端口:5000
  2. 后端服务端口:9000
  3. 数据库服务端口:3306
// 1️⃣ 后端服务端口配置:server/src/Config/index.ts
export const SERVER_PORT = 9000;
// 2️⃣ 数据库服务端口配置:server/src/Config/index.ts
export const SQL_CONFIG = {
	port: 3306,
	// ... other config
};
// 3️⃣ 前端服务端口配置:src/config/index.ts
// 导出后端服务地址
export const SERVER_URL = "http://localhost:9000";

// 导出协同服务地址
export const WS_SERVER_URL = "ws://127.0.0.1:9000";

源项目优化

1️⃣ 页面 UI 重构

UI 重构

2️⃣ 图表协同

左侧为 vchart 渲染,右侧为 chartmix 渲染

VChart 与 ChartMix 对比

vchart 图表动画更加流畅,页面简洁美观

VChart 动画

vchart 图表设置

VChart 设置

chartmix 图表数据联动

ChartMix 数据联动

vchart 图表数据联动

VChart 数据联动

3️⃣ 新增左斜线|右斜线 边框类型

新增左斜线|右斜线 边框类型

4️⃣ 插件依赖优化

  1. 源码中的插件注册方案
plugins: [{ name: "chart" }, { name: "print" }];

原始插件注册方案

这会导致一个问题,在线链接在网络问题、内网限制等其他因素下,导致插件依赖无法正常下载。

  1. 优化方案
plugins: [
  {
    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"],
  },
],

优化后的插件注册方案

相关的插件依赖,相关的加载方案均封装好了,同时,还兼容在线方案

// 在线方案
plugins: [
  {
    name: "chart",
    dependScripts: [
      "https://unpkg.com/vue@2.6.11/dist/vue.min.js",
      // ...
    ],
  },
// 源码中的请求原理如下:
// 如果是 http 在线地址,直接请求
if (url.indexOf("http") == 0) {
	link.setAttribute("href", url);
} else link.setAttribute("href", window.location.origin + "/" + url);

// 如果是 http 在线地址 则直接请求
if (scripts[i].indexOf("http") === 0) {
	s[i].setAttribute("src", scripts[i]);
} else s[i].setAttribute("src", window.location.origin + "/" + scripts[i]);

5️⃣ 文件导入|文件导出

文件导入 支持协同~

文件导入

配置方法:
// 1. 配置导入插件
const options = {
	// ...other config
	plugins: [{ name: "fileImport" }],
};

luckysheet.create(options);

注意事项:

  1. 文件导入依赖于 luckyexcel 插件;
  2. 故而有些功能受限于插件,如需拓展,请自行实现;
  3. 请正确配置 plugins: [{ name: "fileImport" }] 后使用导入功能。

文件导出

文件导出

配置方法:
// 1. 配置导出插件
const options = {
	// ...other config
	plugins: [{ name: "fileExport" }],
};

luckysheet.create(options);

注意事项:

  1. 文件导入依赖于 exceljs | file-saver 插件;
  2. 故而有些功能受限于插件,如需拓展,请自行实现;
  3. 请正确配置 plugins: [{ name: "fileExport" }] 后使用导入功能。

6️⃣ 自定义菜单

配置方法:

自定义菜单

const options = {
   lang: "zh",
   title: "Luckysheet",
   // ...other config

   // 传入 menuHandler 配置项
   menuHandler:{
       hideDefaultMenu: string[], // 目前默认菜单为 导入导出 importFile | exportFile
       customs: MenuHandlerCustomsItem[]
   }
}

type MenuHandlerCustomsItem = {
  label: string
  value: string
  callback: () => void
  order?: string // 菜单排序,小的在上面,默认的菜单 order = 10 在默认菜单上面,需要比10小,不传默认放置在下面
  icon?: string
} |
// 分割线配置对象
{
  value: 'divider'
}

示例

menuHandler: {
	customs: [
		{
			label: "保存",
			value: "saveFile",
			order: 1,
		},
		{ value: "divider", order: 2 },
	];
}

自定义菜单的 icon 图标处理方法

  1. 下载 iconfont 资源,放置到 source/src/assets 目录下(下载到本地并解压)
  2. 在同级目录 iconfont/iconfont.css 引入当前下载的资源包
@import url("../font_3944349_xxxxx/iconfont.css");
  1. 打包输出即可正常使用 iconfont 图标

显示 Logo

显示 Logo

隐藏 Logo

隐藏 Logo

配置方法

const options = {
	showlogo: false,
	/// ...other config
};

7️⃣ 自定义请求头

很多人反映,应该在请求表格数据接口时,添加 cookies、token 等信息,以实现用户身份权限校验,目前已实现,具体配置如下:

const options = {
	// ... other config,
	// 添加请求头
	requestHeaders: {
		authorization: localForage.getItem("token"),
		"x-requested-with": "XMLHttpRequest",
		"custom-name": "custom-value",
		// ... other headers
	},
};

具体实现方案:源码/src/core.js

// 修改 $post 方法,改为 $ajax() 以实现添加请求头的功能
$.ajax({
	url: server.loadUrl,
	type: "POST",
	data: { gridKey: server.gridKey },
	beforeSend(xhr) {
		if (!extendsetting.requestHeaders) return;
		for (let key in extendsetting.requestHeaders) {
			xhr.setRequestHeader(key, extendsetting.requestHeaders[key]);
		}
	},
	timeout: 15000,
	success: function (d) {},
	error: function (error) {},
});

8️⃣ 打印相关

新增打印 API

// "sheet": 打印当前工作表  "areas": 打印执行选区  "pages": 打印指定页码
// type : "sheet" | "areas" | "pages"
luckysheet.print(type, neetToPreview); // 如果不需要预览,则第二个参数为false

打印模糊优化

const options = {
	/// ... other config,
	printDevicePixelRatio: 4, // 数值越大,打印越清晰,但绘制时间更长,请平衡性能
};

打印支持取消网格线

网格线

打印预览视图

打印预览

打印当前页

打印当前页

打印单元格

打印单元格

打印指定页码

打印指定页码

打印图片

打印图片

打印图表

打印图表

9️⃣ 单元格图片beta

新增单元格图片显示功能

  1. 支持插入浮动图片;
  2. 支持插入单元格图片;
  3. 支持浮动图片与单元格图片相互转换;
  4. 支持配置默认图片展示模式 config.imageMode = 'float' | 'cell'
  5. 支持单元格图片预览;
  6. 支持单元格图片合并自适应。

单元格图片

🔟 自定义快捷键

配置方法

const options = {
	/// ... other config,
	// 自定义快捷键
	customShortcutKeys: [
		{
			key: number,
			ctrl?: boolean,
			shift?: boolean,
			alt?: boolean,
			callback: (e) => {},
		},
	],
};

其他源码优化

  1. #Fix 修复多人协同提示框显示异常
  2. #Fix 修复同用户 ID 刷新后光标无法实现协同
  3. #Fix 修复公式链相关协同消息传递
  4. #Feat 批注导入实现、完善源码对批注的识别
  5. #Feat 优化 chartmix/vchart 数据联动及实现 server 数据更新存储记录
  6. #Fix 紧急修复初始化 sheet 时,返回数据 celldata f ='' 时, formula 判断异常,导致双击清空数据
  7. #Fix 修复删除列后撤销协同不更新 BUG
  8. #Fix 修复 inlineStr 富文本数据复制粘贴异常 BUG

Master-Vue 开箱即用版

本项目作为luckysheet-crdt的一个分支,附属于 luckysheet-crdt,仅作为 vue 版本的示例,提供完整的用户系统(登录、注册、修改信息)、文件系统(创建、修改、删除、协同、分享...),项目截图如下:

Master-Vue 登录 Master-Vue 主页 Master-Vue 邀请 Master-Vue 用户信息 Master-Vue 按钮

常见问题

  1. 导入文件时,提示 文件格式错误
目前仅支持 xlsx 格式,请检查文件格式是否正确。
  1. 页面显示协同服务不可用,当前为普通模式
try {
  const { data } = await fetch({
      url: "/api/getWorkerBook",
      method: "post",
      data: { gridKey },
   });
}
catch (error) {}

当且仅当! fetch 请求失败时,会进入 catch 块,
此时会提示 `协同服务不可用,当前为普通模式`
请检查服务是否正常,一般有下列可能:

1. 服务异常
2. 数据库异常
3. 数据库表结构异常
  1. 数据库数据混乱
造成该原因的唯一可能,就是应用没有相关的 delete 语句,
不是我不写哈,而是大家根据自己的实际业务,进行拓展。
下列步骤可恢复:
1. 删除 luckysheet_crdt 所有数据表;
2. 执行 npm run db 同步数据库表;
3. 执行 npm run server 启动服务;

上诉操作,会自己创建数据库表,同步最新的模型结构,
并且创建一个 gridkey-demo  workerbooksworkersheets 记录;
当且仅当,这两个表有记录的场景下,才能渲染 luckysheet

注意!如果两个表没有一条记录,也可能造成无法协同(问题2
注意!如果 workersheets 表有记录,但是 deleteFlag  true 的情况下,也会导致无法渲染 luckysheet
  1. 自定义创建图表类型 目前 vchart 创建图表是随机的饼图|折线图,如果想实现自定义的图表类型传递,需要修改 chartmix 相关源码,具体步骤可参考如下:

更改图表类型

1. 下载源码:https://gitee.com/mengshukeji/chartMix
2. 修改 src/utils/exportUtil.js createChart 方法,添加图表类型参数
3. 重新打包,将文件放置到项目中

开源贡献

  1. 提交 issue
  2. fork 本项目,提交 PR
  3. 加入交流群:

QQ 群