找回密码
 立即注册

QQ登录

只需一步,快速开始

KevinChen 讲师达人认证 悬赏达人认证 SpreadJS 开发认证
论坛元老   /  发表于:2020-9-30 11:47  /   查看:2633  /  回复:0
前言:
版主在日常沟通、技术社区等与使用SpreadJS的小伙伴们交流的过程中,遇到了很多前后端结合的问题。其实从定位来看,SpreadJS无论从技术架构,还是产品设计层面,都是纯前端的控件。但它毕竟最终还是要结合后端环境来开发部署和应用的,所以我有了写这篇文章的想法。
本文利用了一个比较贴近用户的前后端结合的示例工程,来给大家演示一下SpreadJS的一些使用场景和实现技巧,希望能够帮助大家打开思路,能够更好地解决业务问题,提升用户体验。
由于涉及的功能点较多,代码量也不小,这个主题我会分为三篇文章来发布,本篇文章是这个系列的第一篇。
需求背景描述:
SpreadJS是纯前端控件,受到JS前端语言特性的限制,无法原生实现跨Workbook的数据引用。但我们可以通过前后端结合的二次开发,结合业务场景来实现我们的需求。
需求/功能描述:
1、 能够远程加载服务器端的ssjson、Excel文档到前端页面;
2、 可以分析表单中跨Workbook的公式引用,手动建立引用目标文档。当多人同时打开相关联的几个文档时,能够远程关联公式的数据(协同的一种);
3、 能够建立单元格级别的数据关联,可以手动指定关联的目标文档的单元格;
4、 建立好关联单元格后,单元格内右侧出现 =、≠的符号,当关联的两个单元格的值相等时显示=,不相等时显示≠;
5、 同步数据,自动将引用数据回填到引用单元格中。
实现思路:
借助Ajax的帮助,上传、下载文件流已经可以完美解决远程文档加载和上传的问题,参考这篇帖子:https://gcdn.grapecity.com.cn/forum.php?mod=viewthread&tid=37389&extra=page%3D1
静态引用某个后端模板的值、或者是服务器端的数据,其实非常容易理解和实现。原理很简单,示例中采用了命名信息+自定义单元格的方式来实现,一方面实现了需求,另一方面也提供了良好的用户体验。
运行环境:
Java SpringBoot+ SpreadJS
运行方法:
解压后导入Eclipse(或其它IDE工具),Run Java Application ,访问localhost:8080即可。
关键代码讲解:
限于篇幅,本文只讲单元格与后台文档执行关联、删除、同步数据的实现。远程同步、跨Workbook公式的实现,请参考《SpreadJS结合Springboot实现跨文档数据公式关联 _ Part2》和《SpreadJS结合Springboot实现跨文档数据公式关联 _ Part3》。

  1. function CustomValidateCell(hostMargin) {
  2.         this.typeName = "CustomValidateCell";
  3.     this.hostMargin = hostMargin;
  4.         this.margin = 0;
  5.         this.color = 'red';
  6.         this.size = 20;
  7.         this.sideLength = 500;
  8.         this.eq = "lib/eq.png";
  9.         this.neq = "lib/neq.png";
  10.         this.unknown = "lib/unknown.png";
  11.         this.backgroundImage = this.unknown;
  12. }
  13. CustomValidateCell.prototype = new GC.Spread.Sheets.CellTypes.Text();
  14. // 渲染方法,用于实现=和≠的图标渲染(背景logo)
  15. CustomValidateCell.prototype.paint = function (ctx, value, x, y, w, h, style, options) {
  16.     style.hAlign = GC.Spread.Sheets.HorizontalAlign.left;
  17.     GC.Spread.Sheets.CellTypes.Text.prototype.paint.call(this, ctx, value, x, y, w, h, style, options);
  18.         
  19.     if (!ctx) {
  20.         return;
  21.     }
  22.     var tag = options.sheet.getTag(options.row, options.col, options.sheetArea);
  23.     var startX = x + w - this.size - this.margin;
  24.     var startY = y + (h - this.size) / 2 - this.margin;
  25.     style.backgroundImage = this.backgroundImage;
  26.     GC.Spread.Sheets.CellTypes.Text.prototype.paint.call(this, ctx, "", startX, startY, this.size, this.size, style, options);
  27. };
  28. // 监听鼠标动作和位置
  29. CustomValidateCell.prototype.getHitInfo = function (x, y, cellStyle, cellRect, context) {
  30.     var info = {
  31.         x: x,
  32.         y: y,
  33.         row: context.row,
  34.         col: context.col,
  35.         cellStyle: cellStyle,
  36.         cellRect: cellRect,
  37.         sheetArea: context.sheetArea
  38.     };
  39.     var hitX = x;
  40.     var hitY = y;
  41.     x = cellRect.x;
  42.     y = cellRect.y;
  43.     var w = cellRect.width;
  44.     var h = cellRect.height;

  45.     var startX = x + w - this.size + this.margin;
  46.     var startY = y + (h - this.size) / 2 + this.margin;
  47.     var endX = x + w - this.margin;
  48.     var endY = y + (h + this.size) / 2 - this.margin;
  49.     // 这里判断逻辑参考paint中绘制的逻辑
  50.     if (hitX > startX && hitX < endX && hitY > startY && hitY < endY) {
  51.         info.isReservedLocation = true;
  52.     }
  53.     return info;
  54. };
  55. // 添加鼠标移入事件,弹窗显示引用信息
  56. CustomValidateCell.prototype.processMouseEnter = function (hitInfo) {
  57.     var sheet = hitInfo.sheet;
  58.     if (sheet && hitInfo.isReservedLocation) {
  59.         if (!this._toolTipElement) {
  60.             var div = document.createElement("div");
  61.             $(div).css("position", "absolute")
  62.                 .css("border", "1px #C0C0C0 solid")
  63.                 .css("box-shadow", "1px 2px 5px rgba(0,0,0,0.4)")
  64.                 .css("font", "9pt Arial")
  65.                 .css("background", "white")
  66.                 .css("padding", 5)
  67.                                 .attr("class","toolTipElement");

  68.             this._toolTipElement = div;
  69.         }
  70.         validateCellForCustomerCell(hitInfo.row, hitInfo.col, this);
  71.         $(this._toolTipElement).html($('#validateCellInfo').html())
  72.             .css("top", hitInfo.y + this.hostMargin.y + 15)
  73.             .css("left", hitInfo.x + this.hostMargin.x + 15);
  74.         $(this._toolTipElement).hide();
  75.         document.body.insertBefore(this._toolTipElement, null);
  76.         $(this._toolTipElement).show("fast");
  77.         return true;
  78.     } else {
  79.         if (this._toolTipElement) {
  80.             $(".toolTipElement").remove();
  81.             this._toolTipElement = null;
  82.             return true;
  83.         }
  84.     }
  85.     return false;
  86. };
  87. // 销毁弹窗
  88. CustomValidateCell.prototype.processMouseLeave = function (hitInfo) {
  89.     var sheet = hitInfo.sheet;
  90.         $(".toolTipElement").remove();
  91.     this._toolTipElement = null;
  92.     return false;
  93. };
复制代码

以上代码,采用自定义单元格,实现了关联数据、显示=或≠图标,弹窗显示引用信息等功能。如图:
image.png946221923.png
具体实现细节,请结合注释食用。完整Demo参考附件。



SpreadJS_SpringBoot.zip

6.34 MB, 下载次数: 27

0 个回复

您需要登录后才可以回帖 登录 | 立即注册
返回顶部