背景
在工作中, 钉钉表格、飞书表格的在线协同编辑想必大家都用过, 那这种是怎么实现的? 小伙伴们一定有所好奇, 今天我实现一个最简单的表格协同编辑,带大家探探路。
在线体验
地址 dc-query-9ggxcidy3d7a2fa3-1257124629.tcloudbaseapp.com/index.html
技术选型
前端 vue3
服务端 midwayjs
(阿里开源的基于nodejs的开发框架)
实现分析
前端页面
听取了同事的意见,用原生的table里 tr td里加 input当表格的前端页面。
服务端呢
1、 搭建一个websocket服务
2、响应前端的ws连接, 推送最新表格数据
3、如果已经连了多个用户, 此时其中一人编辑了之后,给服务端推最新表格数据, 服务端保存最新数据后,广播给其他所有人
4、前端呢,得提前订阅广播的事件, 接收到服务端广播的最新数据后, 渲染页面
至此 最简流程完成。
逻辑图
代码实现
前端
html部分
<template>
<div>
<table cellspacing="0" width="100%" style="margin-bottom: 1em">
<tr v-for="(row, rowIndex) in list">
<td v-for="(item, index) in row"><input @blur="(val) => onBlur_value(val, rowIndex, index)" :value="item.value" type="text"></td>
</tr>
</table>
</div>
</template>
表格数据
格式 就是一个二维数据, 第一层代表tr, 第二层就是td了
const list = ref([
[
{
value: '1'
},
{
value: '1'
}
],
[
{
value: '1'
},
{
value: '1'
}
],
[
{
value: '1'
},
{
value: '1'
}
]
])
就是这样
连接websocket服务
这边前端后都用的socket.io
import {ref, onBeforeMount, onBeforeUnmount} from 'vue'
import io from "socket.io-client";
const list = ref([])
let socket = null;
onBeforeMount(() => {
socket = io('http://10.255.8.9:7001/table', {
// reconnection: false, //关闭自动重连
});
console.log(socket.connected); // socket是否与服务器连接
console.log(socket.disconnected); // socket是否与服务器断开连接
// 当服务器收到数据的时候时触发
socket.on("data", (data) => {
console.log(data)
if(data.cmd === 'tableData') {
list.value = data.data;
}
});
})
修改表格 推送数据
/**
* input失焦事件
* @param val
* @param rowIndex
* @param index
*/
const onBlur_value = (val, rowIndex, index) => {
console.log(val.target.value)
list.value[rowIndex][index].value = val.target.value
socket.emit("data", {
cmd: 'editTable', // cmd === 'editTable' 是服务端约定的修改表格数据的标识, 就跟后端接口路由一样
data: list.value // 最新全量数据
});
}
服务端
初始化一个midayjs应用
npm init midway
安装socket.io相关组件
npm i @midwayjs/socketio@3 --save
安装完以后照着上面链接里的说明配置好一切
创建socket专属目录
src创建专门放socket服务的目录
├── package.json
├── src
│ ├── configuration.ts ## 入口配置文件
│ ├── interface.ts
│ └── socket ## socket.io 服务的文件
创建table.ts
命名空间为 /table
import {
WSController,
OnWSConnection,
Inject,
OnWSMessage,
WSEmit,
Init,
App
} from '@midwayjs/decorator';
import {Context, Application} from '@midwayjs/socketio';
import {OnWSDisConnection} from "@midwayjs/core";
/**
* 客户端连接websocket
*/
@WSController('/table')
export class ClientController {
}
table数据
let tableData = [
[
{
value: '1'
},
{
value: '1'
}
],
[
{
value: '1'
},
{
value: '1'
}
],
[
{
value: '1'
},
{
value: '1'
}
]
]
响应连接
import {
WSController,
OnWSConnection,
Inject,
OnWSMessage,
WSEmit,
Init,
App
} from '@midwayjs/decorator';
import {Context, Application} from '@midwayjs/socketio';
import {OnWSDisConnection} from "@midwayjs/core";
/**
* 客户端连接websocket
*/
@WSController('/table')
export class ClientController {
@Inject()
ctx: Context;
@App('socketIO')
app: Application;
// 客户端连接
@OnWSConnection()
async onConnectionMethod() {
console.log('on client connect', this.ctx.id);
// 发送链接成功提示
this.ctx.emit('data', {
cmd: 'connected',
data: 'connected success'
});
// 推送最新table数据
this.ctx.emit("data", {
cmd: 'tableData',
data: tableData
});
}
}
接收表格修改的数据推送
/**
* 响应客户端发送的数据
* @param data
*/
@OnWSMessage('data')
@WSEmit('data')
async gotMessage(data = {cmd: '', data: {}}) {
switch (data.cmd) {
case "editTable":
this.editTable(data)
break;
default:
break;
}
}
保存最新数据,广播给其他人
/**
* 保存最新数据,广播给其他人
* @param data
*/
editTable(data) {
tableData = data.data
console.log(data.data)
this.ctx.broadcast.emit("data", {
cmd: 'tableData',
data: tableData
});
}
最后
至此, 一个最简单的表格协同编辑就完成了。
后面继续解决如下问题
- 显示在线编辑的用户
- 实时显示每个人聚焦的单元格
- 单元格争抢问题
- 修改记录 等等等等
正儿八经商用的表格协同编辑还要考虑和解决诸多问题, 我们下回接着完善, 欢迎持续关注。
散会。