本帖最后由 Matthew.Xue 于 2025-4-25 12:25 编辑
众所周知,SpreadJS支持多种单元格类型,比如Text、Button、CheckBoxList等等:
而每种单元格类型都会以不同的方式进行渲染,而渲染的函数是paint,以Text类型单元格为例:
可以看到,paint的参数包括单元格的值、位置、样式等信息,这样就可以确定单元格应该被如何绘制了。
在我们的业务中,经常会遇到要对单元格的表现形式做出修改的需求,比如要让空单元格显示为斜杠,当然我们可以通过单元格公式来做到这一点,但毕竟对所有单元格设置公式会很麻烦,所以直接修改Text的单元格类型就成为了一个很好的办法(例子A):
- let oldPaint = GC.Spread.Sheets.CellTypes.Text.prototype.paint
- GC.Spread.Sheets.CellTypes.Text.prototype.paint = function (ctx, value, x, y, w, h, style, options) {
- if(value === null || value === "") {
- oldPaint.apply(this, [ctx, "/", x, y, w, h, style, options])
- } else {
- oldPaint.apply(this, arguments)
- }
- }
复制代码 以上的内容都是老生常谈了,今天我们讨论另外一种更复杂的场景:
https://gcdn.grapecity.com.cn/forum.php?mod=viewthread&tid=235930
在上面这个帖子中,用户想要在Sheet未被保护的时候,正常显示CheckBoxList的样子,而在Sheet被保护后,则要直接显示文字(例子B),最终想要的效果如下:
聪明的你一定会想到,既然我们可以重写paint方法,那么我们在单元格paint内部判断一下,如果sheet被保护了,就使用Text类型单元格的paint方法渲染,否则就正常使用CheckBoxList的paint方法,这样不就解决问题了?
- let checkBoxListPaint = GC.Spread.Sheets.CellTypes.CheckBoxList.prototype.paint
- let textPaint = GC.Spread.Sheets.CellTypes.Text.prototype.paint
- GC.Spread.Sheets.CellTypes.CheckBoxList.prototype.paint = function (ctx, value, x, y, w, h, style, context) {
- let sheet = context.sheet
- // 如果sheet被保护,则使用Text的paint方法渲染
- if (sheet.options.isProtected) {
- textPaint.apply(this, arguments)
- } else {
- checkBoxListPaint.apply(this, arguments)
- }
- }
复制代码 恭喜你,已经很接近答案了!但是还差一点点,因为你会发现这样做了之后不会有任何效果:
这是为什么呢?明明我已经在Sheet被保护的时候使用了Text类型的单元格渲染,怎么会没有效果呢?
这其实和原型链有关,咱们细细展开。
在SpreadJS中,paint函数被执行后,其实还不是最终的结果,还需要执行paintContent和paintValue方法,大概的逻辑如下:
- class CheckBoxList{
- paint(ctx, value, x, y, w, h, style, context) {
- // do something
- this.paintContent(ctx, value, x, y, w, h, style, context)
- }
- paintContent(ctx, value, x, y, w, h, style, context) {
- // do something
- this.paintValue(ctx, value, x, y, w, h, style, context)
- }
- paintValue(ctx, value, x, y, w, h, style, context) {
- // do something
- }
- }
复制代码 在上面的例子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方法,就可以做到第一个动图中的效果了。
- let checkBoxListPaint = GC.Spread.Sheets.CellTypes.CheckBoxList.prototype.paintValue
- let textPaint = GC.Spread.Sheets.CellTypes.Text.prototype.paintValue
- GC.Spread.Sheets.CellTypes.CheckBoxList.prototype.paintValue = function (ctx, value, x, y, w, h, style, context) {
- let sheet = context.sheet
- // 如果sheet被保护,则使用Text的paint方法渲染
- if (sheet.options.isProtected) {
- textPaint.apply(this, arguments)
- } else {
- checkBoxListPaint.apply(this, arguments)
- }
- }
复制代码 当然,由于CheckBoxList的值是cn、en这些英文,所以只写上面的代码的话,在Sheet被保护时,显示的会是英文,所以你需要从style中拿到复选框对应的中文是什么,完整代码如下:
- let checkBoxListPaint = GC.Spread.Sheets.CellTypes.CheckBoxList.prototype.paintValue
- let textPaint = GC.Spread.Sheets.CellTypes.Text.prototype.paintValue
- GC.Spread.Sheets.CellTypes.CheckBoxList.prototype.paintValue = function (ctx, value, x, y, w, h, style, context) {
- let _sheet = context.sheet
- if (style.locked && _sheet.options.isProtected) {
- let items = style.cellType._items
- let _value = []
- if (value && Array.isArray(value)) {
- _value = value
- }
- let str = items.filter(v => {
- return _value.indexOf(v.value) > -1
- }).map(v => {
- return v.text
- }).join(",")
- textPaint.apply(this, [ctx, str, x, y, w, h, style, context]);
- }
- else {
- checkBoxListPaint.apply(this, arguments)
- }
- }
复制代码 了解了以上内容后,以后大家在修改单元格函数的过程中应该会更加得心应手~
|
|