V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
GrapeCityChina
V2EX  ›  推广

Svelte 框架实现表格协同文档

  •  
  •   GrapeCityChina · 2023-01-17 15:09:29 +08:00 · 862 次点击
    这是一个创建于 694 天前的主题,其中的信息可能已经有所发展或是发生改变。

    首先,从框架搭建上,本篇示例采用当下流行的前后端分离的开发方式,前端使用 npm 作为脚手架搭建 Svelte 框架。 后端使用 Java 的 SpringBoot 作为后端框架。 首先,介绍下在前端 Svelte 框架下搭建在线表格编辑器。 1 、在 pageage.json 文件中引入相关资源

       "@grapecity/spread-excelio": "15.2.5",
        "@grapecity/spread-sheets": "15.2.5",
        "@grapecity/spread-sheets-barcode": "15.2.5",
        "@grapecity/spread-sheets-charts": "15.2.5",
        "@grapecity/spread-sheets-designer": "15.2.5",
        "@grapecity/spread-sheets-designer-resources-cn": "15.2.5",
        "@grapecity/spread-sheets-languagepackages": "15.2.5",
        "@grapecity/spread-sheets-pdf": "15.2.5",
        "@grapecity/spread-sheets-pivot-addon": "15.2.5",
        "@grapecity/spread-sheets-pivots": "^14.0.0",
        "@grapecity/spread-sheets-print": "15.2.5",
        "@grapecity/spread-sheets-resources-zh": "15.2.5",
        "@grapecity/spread-sheets-shapes": "15.2.5",
        "@grapecity/spread-sheets-tablesheet": "15.2.5",
    

    2 、然后,集成在线表格编辑器 Svelte 组件版。在上一篇文章中,我们介绍了如何在 Svelte 框架中实现在线表格编辑器。 我们按照此思路新建一个 SpreadSheet.svelte 文件,写入基础在线表格编辑器。

    <script>
    import {onMount} from 'svelte';
    import '@grapecity/spread-sheets-print';
    import "@grapecity/spread-sheets-charts";
    import '@grapecity/spread-sheets-shapes';
    import '@grapecity/spread-sheets-pivot-addon';
    import '@grapecity/spread-sheets-tablesheet';
    import '@grapecity/spread-sheets-designer-resources-cn';
    import '@grapecity/spread-sheets-designer';
    import * as GC from '@grapecity/spread-sheets';
    import * as GCDesigner from '@grapecity/spread-sheets-designer';
    
    let designer = null;
    onMount(async () => {
    designer = new GCDesigner.Spread.Sheets.Designer.Designer(document.getElementById("designerHost"));
    let spread = designer.getWorkbook();
    });
    
    </script>
    <div id="designerHost" class="designer-host"></div>
    
    <style scoped>
    @import "@grapecity/spread-sheets-designer/styles/gc.spread.sheets.designer.min.css";
    @import '@grapecity/spread-sheets/styles/gc.spread.sheets.excel2013white.css';
    
    .designer-host {
    width: 100%;
    height: 100vh;
    }
    
    </style>
    

    3 、协同文档可能不止一个,我们需要在页面上创建一个文档列表,来允许用户选择编辑哪个文档,所以我们需要创建一个文档列表页面 OnlineSheets.svelte 。在此页面中,我们要实现路由跳转,和加载文档数据。 这里我们用了 svelte-spa-router 进行路由跳转 与 isomorphic-fetch 进行前后端数据传输。

    <script>
        import {onMount} from 'svelte';
        import { link } from "svelte-spa-router";
        import {Utility} from "../utility.js";
    
        let docList = [];
        onMount(async () => {
            Utility.getDocList().then(result => {
                docList  = result.map((item,index)=>{
                    return {
                        path:'/Spreadsheet/' + item.substring(0, item.lastIndexOf('.')),
                        index,
                        fileName:item
                    }
                })
            });
        });
    </script>
    <main class="main">
        <table className='table' aria-labelledby="tabelLabel">
            <thead>
            <tr>
                <th>Document</th>
                <th></th>
            </tr>
            </thead>
            <tbody>
            {#each docList as docItem}
                <tr>
                    <td>{docItem.index}</td>
                    <td>{docItem.fileName}</td>
                    <td className='row'>
                        <a use:link={docItem.path}> Open</a>
                    </td>
                </tr>
            {/each}
            </tbody>
        </table>
    </main>
    

    以上代码实现了文档列表查看与文档跳转,使用 Open将跳转至前面设计好的在线表格编辑器中。 至此,前端的相关内容就准备好了,接下来搭建下后端工作。 后端的准备工作,首先安装 gradle 作为包管理器。当然,这里也可以用其他工具来代替,例如 maven ,或者源生引入 jar 包的方式将需要用到的 jar 包引入进来。之后创建 springboot 工程配合搭建 gradle 引用 GCExcel 以及后面协同需要用到的 websocket 。

    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
    
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <version>2.4.3</version>
    </dependency>
    
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
    <version>2.4.3</version>
    </dependency>
    
    <dependency>
    <groupId>com.grapecity.documents</groupId>
    <artifactId>gcexcel</artifactId>
    <version>4.0.3</version>
    </dependency>
    
    <dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.6</version>
    </dependency>
    
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
    </dependency>
    
    <dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
    <version>10.0.2</version>
    </dependency>
    
    <dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>2.5.0</version>
    </dependency>
    </dependencies>
    

    这样子,我们做了框架的基本环境搭建,接下来我们介绍下如何搭建 webSocket 。 在 SpreadSheet.svelte 文件中写入如下代码建立 webSocket 链接:

        function connectDocument(docName) {
            if (webSocket != null) {
                return;
            }
            var ws = new WebSocket(Utility.webSocketUrl);  //'ws://localhost:8090/spreadjs'
            ws.onopen = function () {
                var data = {
                    cmd: "connect",
                    docID: docName
                }
                ws.send(JSON.stringify(data));
            }
            ws.onmessage = onmessage;
            webSocket = ws;
        }
    

    接下来我们访问下文档列表页,从文档列表页跳转进入文档,进行编辑。

    接下来我们需要监听前端发出的操作。这里因为在线表格编辑器本身将所有用户可能做的操作全部做了封装,所以省下了很多的功夫。

       onMount(async () => {
            //初始化 Designer
            designer = new GCDesigner.Spread.Sheets.Designer.Designer(document.getElementById("designerHost"));
            let spread = designer.getWorkbook();
            //fromJSON
            openDocument(docName);
            //建立 webSocket
            connectDocument(docName);
            var cm = spread.commandManager();
            cm.addListener('myListener', onCommandExecute)
        });
    

    根据 cmd 去判断并且对命令再做一些简单封装,之后将封装过的命令发到服务端,之后通过 websocket 发同步指令:

      function onCommandExecute(args) {
            console.log(args.command);
            var command = args.command;
            var ServerCommand = null;
    
            switch (command.cmd) {
                case Utility.ServerCommands.EditCell:
                    ServerCommand = {
                        sheetName: command.sheetName,
                        row: command.row,
                        column: command.col,
                        newValue: command.newValue
                    }
                    break;
                case Utility.ServerCommands.ResizeRow:
                    ServerCommand = {
                        sheetName: command.sheetName,
                        rows: command.rows,
                        size: command.size
                    };
                    break;
                case Utility.ServerCommands.ResizeColumn:
                    ServerCommand = {
                        sheetName: command.sheetName,
                        columns: command.columns,
                        size: command.size
                    };
                    break;
                case 'Designer.' + Utility.ServerCommands.SetFontFamily:
                case 'Designer.' + Utility.ServerCommands.SetFontSize:
                case 'Designer.' + Utility.ServerCommands.SetBackColor:
                case 'Designer.' + Utility.ServerCommands.SetForeColor:
                case 'Designer.' + Utility.ServerCommands.SetFontWeight:
                case 'Designer.' + Utility.ServerCommands.SetFontStyle:
                case 'Designer.' + Utility.ServerCommands.SetUnderline:
                case 'Designer.' + Utility.ServerCommands.SetDoubleUnderline:
                    if (command.value && command.value.indexOf('undefined') === -1) {
                        ServerCommand = {
                            sheetName: command.sheetName,
                            selections: command.selections,
                            value: command.value
                        }
                    }
                    break;
                case Utility.ServerCommands.MoveFloatingObjects:
                    ServerCommand = {
                        sheetName: command.sheetName,
                        floatingObjects: command.floatingObjects,
                        offsetX: command.offsetX,
                        offsetY: command.offsetY
                    };
                    break;
                case Utility.ServerCommands.ResizeFloatingObjects:
                    ServerCommand = {
                        sheetName: command.sheetName,
                        floatingObjects: command.floatingObjects,
                        offsetX: command.offsetX,
                        offsetY: command.offsetY,
                        offsetWidth: command.offsetWidth,
                        offsetHeight: command.offsetHeight
                    };
                    break;
                case Utility.ServerCommands.InsertColumns:
                case Utility.ServerCommands.InsertRows:
                    ServerCommand = {
                        sheetName: command.sheetName,
                        selections: command.selections
                    };
                    break;
                default:
            }
    
            if (ServerCommand != null) {
    
                var cmd = command.cmd;
                var dotIndex = cmd.lastIndexOf('.');
                if (dotIndex !== -1) {
                    cmd = cmd.substring(dotIndex + 1);
                }
                ServerCommand.cmd = cmd;
                ServerCommand.docID = params.fileName;
    
                Utility.ExecuteCommandAtServer(ServerCommand);
    
                command.docID = ServerCommand.docID;
                webSocket.send(JSON.stringify(command))
            }
        }
    

    当协同端通过 websocket 接收到请求的时候,使用 onmessage 方法做同步命令。这里在协同端执行 command 之前需要先撤销之前的监听,避免再发送 websocket 导致死循环。在执行之后,再次添加监听。

      function onmessage(message) {
            var command = JSON.parse(message.data);
            command._styles = null;
            let spread = designer.getWorkbook()
            var cm = spread.commandManager();
            cm.removeListener('myListener');
    
            spread.commandManager().execute(command);
    
            cm.addListener('myListener', onCommandExecute);
        }
    

    至此,协同基础内容搭建结束,我们来看看编辑单元格内容后,发生了什么吧。 如下图所示,修改 E4 单元格内容,同时打开控制台网络 tab 。 将 E4 单元格数值 2500 改为 2000 ,此时触发了 EditCell 事件,同时发出了交互指令:

    此时新建一个窗口,复制链接,查看文档内容已经变为了 2000 。 如下动图所示:

    拓展阅读

    React + Springboot + Quartz ,从 0 实现 Excel 报表自动化

    电子表格也能做购物车?简单三步就能实现

    使用纯前端类 Excel 表格控件 SpreadJS 构建企业现金流量表

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   871 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 20:57 · PVG 04:57 · LAX 12:57 · JFK 15:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.