Richard.Ma 发表于 2024-7-5 14:07:05

自定义单元格实现单元格显示修约值【四舍六入五成双(银行家算法)】

本帖最后由 Richard.Ma 于 2024-7-5 16:43 编辑

此前,我们写过一个教程“利用自定义公式实现“四舍六入五成双”
https://gcdn.grapecity.com.cn/showtopic-141668-1-5.html
通过上述方式,可以设置公式来得到一个符合“银行家算法”的公式结果作为单元格值。

但是在一些场景中,“四舍六入五成双(银行家算法)”的修约结果仅需要作为最终的显示内容。用户仍需要单元格的值保持修约前的原始值来参与后续的计算

基于上述场景,就有了这篇教程,会介绍通过自定义单元格,在单元格的paint方法中,
根据单元格的实际值,和要保留的小数位数(来自单元格格式字符串)
进行修约后显示,其中的“四舍六入五成双”处理逻辑和上篇教程相同


演示效果如下
左右两列的单元格值相同,左侧为默认单元格类型。右侧设置了BankersRoundCellType来实现以修约结果显示
可以看到编辑值未受到影响





关键代码

定义自定义单元格类型,继承Text单元格类型,仅重写paint方法,不影响编辑
function BankersRoundCellType() {
}
BankersRoundCellType.prototype = new spreadNS.CellTypes.Text();
BankersRoundCellType.prototype.paint = function (ctx, value, x, y, w, h, style, options) {
    if (value) {
      //在这里对单元格值进行处理,使用value和formatter中获取的小数位数,对数字继续修约,其他类型保持原值
      let places = this.getDecimalPlaces(style.formatter);//小数点位数
      let resultvalue = this.bankersRound(value,places);
      

      spreadNS.CellTypes.Text.prototype.paint.apply(this, );
    }
};

上述代码中用到的和银行家算法相关的其他方法也添加到自定义单元格原型中

BankersRoundCellType.prototype.bankersRound = function (value, places) {
    if (!isNaN(parseInt(value)) && !isNaN(parseInt(places))) {
      value = this.numGeneral(value);
      if (!this.isNumber(value)) {
            return value;
      }
      var d = places || 0;
      var m = Math.pow(10, d);
      var n = +(d ? value * m : value).toFixed(8); // Avoid rounding errors
      var i = Math.floor(n),
            f = n - i;
      var e = 1e-8; // Allow for rounding errors in f
      var r = f > 0.5 - e && f < 0.5 + e ? (i % 2 == 0 ? i : i + 1) : Math.round(n);
      var result = d ? r / m : r;

      if (places > 0) {
            var s_x = result.toString();
            var pos_decimal = s_x.indexOf(".");
            if (pos_decimal < 0) {
                pos_decimal = s_x.length;
                s_x += ".";
            }
            while (s_x.length <= pos_decimal + places) {
                s_x += "0";
            }
            return Number(s_x);
      } else {
            return Number(result);
      }
    } else {
      return value;
    }
}

BankersRoundCellType.prototype.numGeneral = function (val, mand, type) {

    var matCheck = function (v) {
      return String(v).match(
            /^([\D\-(?!\d)\.(?!\d)]*?)((\-?\d*\.?\d+)((e|E)(\+|\-)?(\d+))?)(.*)$/
      );
    };
    var reVal = matCheck(val);
    if (reVal) {
      if (typeof mand == "string" && !type) type = mand;
      var reMatch = function (matchs) {
            var num = matchs,
                res = "",
                last = matchs || "",
                isF = Number.isFinite || isFinite;
            if (type === "number") {
                return ;
            } else if (matchs) {
                var p1 = "",
                  p2 = "",
                  pm = 1;
                if (num.indexOf("-") === 0) (pm = 0), (num = num.substr(1));
                num = num.replace(/^0+/, "").replace(/^\./, "0.");
                var dot = num.indexOf("."),
                  power = Number(matchs) || 0,
                  ONnum = num
                        .replace(/(e|E).+$/, "")
                        .replace(/(\.\d+?)0+$/, "$1")
                        .replace(/\.0+$|\./g, "");
                if (dot != -1) {
                  (p1 = ONnum.substring(0, dot)),
                        (p2 = ONnum.substring(dot, ONnum.length));
                }
                if (matchs == "-") {
                  if (dot == -1) {
                        ONnum = ONnum.replace(/^0+/, "");
                        if (power < ONnum.length) {
                            res =
                              ONnum.substr(0, ONnum.length - power) +
                              "." +
                              ONnum.substr(-power);
                        } else {
                            for (var i = 0, le = power - ONnum.length; i < le; i++)
                              ONnum = "0" + ONnum;
                            res = "0." + ONnum;
                        }
                  } else {
                        if (!/[^0]/.test(p1)) {
                            p1 = "0.";
                            for (var i = 0; i < power; i++) p1 += "0";
                        } else if (power < p1.length) {
                            p1 = p1.substr(0, p1.length - power) + "." + p1.substr(-power);
                        } else {
                            for (var i = 0, le = power - p1.length; i < le; i++)
                              p1 = "0" + p1;
                            p1 = "0." + p1;
                        }
                        res = p1 + p2;
                  }
                } else {
                  if (dot == -1) {
                        res = ONnum.replace(/^0+/, "");
                        for (var i = 0; i < power; i++) res += "0";
                  } else {
                        if (power < p2.length) {
                            p2 = p2.substr(0, power) + "." + p2.substr(power);
                        } else {
                            for (var i = 0, le = power - p2.length; i < le; i++) p2 += "0";
                        }
                        res = p1 + p2;
                  }
                }
                res = res
                  .replace(/^0+(?!\.)/, "")
                  .replace(/(\.\d+?)0+$/, "$1")
                  .replace(/\.0+$|\.$/, "");
                if (isF(Number(res)) && !String(Number(res)).match(/e|E/)) {
                  res = pm < 1 ? -Number(res) : Number(res);
                } else {
                  res = pm < 1 ? "-" + res : res;
                }
            } else {
                res =
                  matchs && isF(Number(matchs)) && !matCheck(Number(matchs))
                        ? Number(matchs)
                        : num;
            }
            return ;
      };
      if (reVal && mand == true) {
            var has = true,
                matARR = ;
            var numARR = ];
            while (has == true) {
                var lastMat = matCheck(matARR);
                if (lastMat) {
                  var last = reMatch(lastMat);
                  matARR.push(last), numARR.push(last);
                } else has = false;
            }
            return numARR.length > 1 ? numARR : matARR;
      } else {
            return reMatch(reVal);
      }
    } else {
      return val;
    }
}

BankersRoundCellType.prototype.isNumber = function (val) {
   
    var regPos = /^\d+(\.\d+)?$/; //非负浮点数
    var regNeg =
      /^(-((+\.**)|(**\.+)|(**)))$/; //负浮点数
    if (regPos.test(val) || regNeg.test(val)) {
      return true;
    } else {
      return false;
    }
}

BankersRoundCellType.prototype.getDecimalPlaces = function (format) {
    if (format === undefined) {
      return 0;
    }
    // 找到小数点的位置
    const decimalPointIndex = format.indexOf('.');

    // 如果没有小数点,返回0
    if (decimalPointIndex === -1) {
      return 0;
    }

    // 小数点后的字符数即为保留的小数位数
    return format.length - decimalPointIndex - 1;
}


具体的demo,请打开下面的在线示例
https://jscodemine.grapecity.com/sample/CAm5KT0HUUqBojok77DSZA/







或者下载在本地运行

页: [1]
查看完整版本: 自定义单元格实现单元格显示修约值【四舍六入五成双(银行家算法)】