请选择 进入手机版 | 继续访问电脑版
 找回密码
 立即注册

QQ登录

只需一步,快速开始

Matthew.Xue

超级版主

10

主题

947

帖子

1555

积分

超级版主

Rank: 8Rank: 8

积分
1555
Matthew.Xue
超级版主   /  发表于:2025-4-25 12:21  /   查看:92  /  回复:0
本帖最后由 Matthew.Xue 于 2025-4-25 12:25 编辑

众所周知,SpreadJS支持多种单元格类型,比如Text、Button、CheckBoxList等等:
image.png459167785.png
而每种单元格类型都会以不同的方式进行渲染,而渲染的函数是paint,以Text类型单元格为例:
image.png922837524.png
可以看到,paint的参数包括单元格的值、位置、样式等信息,这样就可以确定单元格应该被如何绘制了。
在我们的业务中,经常会遇到要对单元格的表现形式做出修改的需求,比如要让空单元格显示为斜杠,当然我们可以通过单元格公式来做到这一点,但毕竟对所有单元格设置公式会很麻烦,所以直接修改Text的单元格类型就成为了一个很好的办法(例子A):
  1. let oldPaint = GC.Spread.Sheets.CellTypes.Text.prototype.paint

  2. GC.Spread.Sheets.CellTypes.Text.prototype.paint = function (ctx, value, x, y, w, h, style, options) {
  3.     if(value === null || value === "") {
  4.         oldPaint.apply(this, [ctx, "/", x, y, w, h, style, options])
  5.     } else {
  6.         oldPaint.apply(this, arguments)
  7.     }
  8. }
复制代码
以上的内容都是老生常谈了,今天我们讨论另外一种更复杂的场景:
https://gcdn.grapecity.com.cn/forum.php?mod=viewthread&tid=235930
在上面这个帖子中,用户想要在Sheet未被保护的时候,正常显示CheckBoxList的样子,而在Sheet被保护后,则要直接显示文字(例子B),最终想要的效果如下:
有效果.gif
聪明的你一定会想到,既然我们可以重写paint方法,那么我们在单元格paint内部判断一下,如果sheet被保护了,就使用Text类型单元格的paint方法渲染,否则就正常使用CheckBoxList的paint方法,这样不就解决问题了?
  1. let checkBoxListPaint = GC.Spread.Sheets.CellTypes.CheckBoxList.prototype.paint
  2. let textPaint = GC.Spread.Sheets.CellTypes.Text.prototype.paint

  3. GC.Spread.Sheets.CellTypes.CheckBoxList.prototype.paint = function (ctx, value, x, y, w, h, style, context) {
  4.     let sheet = context.sheet
  5.     // 如果sheet被保护,则使用Text的paint方法渲染
  6.     if (sheet.options.isProtected) {
  7.         textPaint.apply(this, arguments)
  8.     } else {
  9.         checkBoxListPaint.apply(this, arguments)
  10.     }
  11. }
复制代码
恭喜你,已经很接近答案了!但是还差一点点,因为你会发现这样做了之后不会有任何效果:
无效果.gif
这是为什么呢?明明我已经在Sheet被保护的时候使用了Text类型的单元格渲染,怎么会没有效果呢?
这其实和原型链有关,咱们细细展开。
在SpreadJS中,paint函数被执行后,其实还不是最终的结果,还需要执行paintContent和paintValue方法,大概的逻辑如下:
  1. class CheckBoxList{
  2.     paint(ctx, value, x, y, w, h, style, context) {
  3.         // do something
  4.         this.paintContent(ctx, value, x, y, w, h, style, context)
  5.     }

  6.     paintContent(ctx, value, x, y, w, h, style, context) {
  7.         // do something
  8.         this.paintValue(ctx, value, x, y, w, h, style, context)
  9.     }

  10.     paintValue(ctx, value, x, y, w, h, style, context) {
  11.         // do something        
  12.     }
  13. }
复制代码
在上面的例子A中,我们只是修改了Text的paint函数中的一个参数value而已,后续调用paintContent时,由于我们使用了oldPaint.apply(this, [ctx, "/", x, y, w, h, style, options])方法传入了this(即Text单元格实例),所以执行this.paintContent()时,this指的是Text原型的paintContent方法。

而在例子B中,也是一样,虽然我们在CheckBoxList的paint中,调用了textPaint.apply(this, arguments),但是这里的this仍然指向CheckBoxList得实例,后面执行this.paintContent()时,执行的仍然是CheckBoxList的paintContent方法,而不是预期中Text的paintContent方法,paintValue也是一样的道理。
也就是说,我们其实只在CheckBoxList中执行了Text的paint方法,而后续的paintContent和paintValue使用的仍然是CheckBoxList单元格的方法,所以它没有效果。

那么根据单元格的值绘制单元格时,最终起作用的到底是哪个方法呢?其实是paintValue方法。那么结论就很明显了,我们只需要用同样的方式修改paintValue方法,就可以做到第一个动图中的效果了。
  1. let checkBoxListPaint = GC.Spread.Sheets.CellTypes.CheckBoxList.prototype.paintValue
  2. let textPaint = GC.Spread.Sheets.CellTypes.Text.prototype.paintValue

  3. GC.Spread.Sheets.CellTypes.CheckBoxList.prototype.paintValue = function (ctx, value, x, y, w, h, style, context) {
  4.     let sheet = context.sheet
  5.     // 如果sheet被保护,则使用Text的paint方法渲染
  6.     if (sheet.options.isProtected) {
  7.         textPaint.apply(this, arguments)
  8.     } else {
  9.         checkBoxListPaint.apply(this, arguments)
  10.     }
  11. }
复制代码
当然,由于CheckBoxList的值是cn、en这些英文,所以只写上面的代码的话,在Sheet被保护时,显示的会是英文,所以你需要从style中拿到复选框对应的中文是什么,完整代码如下:
  1. let checkBoxListPaint = GC.Spread.Sheets.CellTypes.CheckBoxList.prototype.paintValue
  2. let textPaint = GC.Spread.Sheets.CellTypes.Text.prototype.paintValue
  3. GC.Spread.Sheets.CellTypes.CheckBoxList.prototype.paintValue = function (ctx, value, x, y, w, h, style, context) {
  4.     let _sheet = context.sheet
  5.     if (style.locked && _sheet.options.isProtected) {
  6.         let items = style.cellType._items
  7.         let _value = []
  8.         if (value && Array.isArray(value)) {
  9.             _value = value
  10.         }
  11.         let str = items.filter(v => {
  12.             return _value.indexOf(v.value) > -1
  13.         }).map(v => {
  14.             return v.text
  15.         }).join(",")
  16.         textPaint.apply(this, [ctx, str, x, y, w, h, style, context]);
  17.     }
  18.     else {
  19.         checkBoxListPaint.apply(this, arguments)
  20.     }
  21. }
复制代码
了解了以上内容后,以后大家在修改单元格函数的过程中应该会更加得心应手~

0 个回复

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