src/workers/InspectionWorker.worker.js
@@ -35,6 +35,11 @@
// 当前这次输入对应的原始检验项。计算过程中 pId 会切换到下游计算项,
// 保存时必须同时带回原始检验项,否则只会保存计算结果。
let sourceInsItemId = null;
// 当前消息版本。Worker 单线程串行处理消息,供递归公式计算完成后回传给主线程。
let currentRevision = null;
// 本次输入是否往单元格写入过计算结果。链式公式只有最外层会回传保存,
// 用它在最外层判断计算的中间值是否也需要落库。
let hasComputedWrite = false;
/**
 * 生成模板结构签名
@@ -68,6 +73,7 @@
self.onmessage = function (event) {
  // 保存主线程传过来的值
  const data = JSON.parse(event.data);
  currentRevision = data.revision;
  // 模板内容变了(不只是模板id变了)也要刷新表格,否则改完模版后 Worker 仍用旧模板计算并把旧版式回写页面
  const nextTemplateSig = buildTemplateSig(data.tableList);
  if (
@@ -138,7 +144,8 @@
  comparisonList = JSON.parse(JSON.stringify(data.comparisonList));
  currentSample = JSON.parse(JSON.stringify(data.currentSample));
  PROJECT = data.PROJECT;
  // 执行计算方法
  // 执行计算方法。每次输入重置标记,由本次调用栈的最外层决定是否回传保存
  hasComputedWrite = false;
  changeInput("", code);
};
@@ -169,6 +176,18 @@
}
/**
 * 获取纯单元格引用公式的坐标名,例如 =F4、=$F$4。
 * 纯引用应直接复制原值,避免把“外观”等中文文本送入数值计算。
 */
function getDirectReferenceName(f) {
  if (typeof f !== "string") {
    return null;
  }
  const matched = f.trim().match(/^=\s*\$?([A-Za-z]{1,3})\$?(\d{1,7})\s*$/);
  return matched ? `${matched[1].toUpperCase()}${matched[2]}` : null;
}
/**
 * 按检验项id取当前样品下的检验项信息,取不到时返回空对象,避免公式计算因缺项中断
 *
 * @param id 检验项id
@@ -194,7 +213,7 @@
  return !(typeof r === "number" && isNaN(r));
}
function changeInput(m, code) {
function changeInput(m, code, isComputed) {
  let str = code.split("-");
  let r = str[1]; //当前行
  let c = str[2]; //当前列
@@ -208,8 +227,6 @@
      break;
    }
  }
  let isToExponential = ""; //是否为科学计数法
  let list2 = []; //一个数组,里面保存有当前检验项的所有参数值
  let isPoint = ""; //是否为小数点
  let inputType = null;
  let currentInsItemId = "";
@@ -243,7 +260,6 @@
                    break;
                  }
                }
                list2.push(c.v.v);
                // 组装参数的对象集合并赋值,列如{A3:12,B4:15}
                if (
                  (getInspectionValueType(item.i) == 1 ||
@@ -270,7 +286,11 @@
        // 如果是函数方法,则执行此方法
        let comResult = ""; //初始化计算结果
        try {
          if (
          const directReferenceName = getDirectReferenceName(item.v.f);
          if (directReferenceName && Object.prototype.hasOwnProperty.call(comValue, directReferenceName)) {
            // =F4 这类公式既可能引用数字,也可能引用中文文本,直接保留源值。
            comResult = comValue[directReferenceName];
          } else if (
            getInspectionValueType(item.i) == 1 ||
            isNumericFormula(item.v.f)
          ) {
@@ -318,22 +338,7 @@
            }
            // 根据输入的数值,进行计算
            comResult = compute(item.v.f.replace(/=/g, " "), comValue, isPoint);
            // 检验项为密度时,根据要求值的小数位改变最终值的小数位
            if (inspectionItem == "密度") {
              const str = "" + tell;
              const indexOfDecimal = str.indexOf(".");
              if (indexOfDecimal > -1) {
                const length = str.length - indexOfDecimal - 1;
                comResult = comResult.toFixed(length);
              }
            } else if (
              inspectionItemClass == "外护套机械物理性能" &&
              inspectionItem == "在IRM 902矿物油中浸泡后" &&
              (inspectionItemSubclass == "断裂伸长率变化率" ||
                inspectionItemSubclass == "抗张强度变化率")
            ) {
              comResult = Math.round(comResult).toFixed(0);
            } else if (
            if (
              typeof comResult == "string" &&
              (comResult.includes("断裂") || comResult.includes("脆化"))
            ) {
@@ -361,15 +366,6 @@
            }
            if (isHaveSymbol) {
              comResult = symbol + comResult;
            }
            let list3 = list2.map((item) => item + "");
            // 判断填的检验值是否为科学计数法,如果为科学计数法,则进行转化
            isToExponential = list3.some(
              (val) => val.includes("e+") || val.includes("e-")
            );
            if (isToExponential) {
              let num2 = new Big(comResult);
              comResult = num2.toExponential(1);
            }
          } else {
            // 如果检验值类型是文本输入框、下拉框
@@ -409,45 +405,8 @@
              // 判断当前行是否为当前检验项所在行,如果为当前行,则给表格数据赋值
              for (var b in a) {
                if (a[b].c == item.c) {
                  try {
                    if (comResult == 0) {
                      // 判断计算结果是否为0,如果为0,则给表格数据赋值为0
                      a[b].v.v = 0;
                    } else if (
                      a[b].v.ct &&
                      a[b].v.ct.fa &&
                      typeof a[b].v.ct.fa == "string" &&
                      a[b].v.ct.fa.includes(".")
                    ) {
                      // 判断当前单元格是否保留小数点,如果为保留小数点,则给表格数据赋值为保留小数点,这个是根据模板配置小数点来的
                      let num = 0;
                      let str = a[b].v.ct.fa.split(".")[1];
                      num = str.length;
                      a[b].v.v = comResult
                        ? Number(comResult).toFixed(num)
                        : comResult;
                    } else if (
                      typeof comResult == "string" &&
                      (comResult.includes("e+") || comResult.includes("e-"))
                    ) {
                      // 判断计算结果是否为科学计数法,如果为科学计数法,则给表格数据赋值为科学计数法
                      a[b].v.v = comResult;
                    } else {
                      const inspectionItem = findInsItem(item.i).inspectionItem;
                      // 判断计算结果是否为数字,如果为数字,则给表格数据赋值为数字
                      if (inspectionItem == "铜线电阻率ρ20最大值") {
                        let val = parseFloat(Number(comResult).toFixed(5));
                        a[b].v.v = isNaN(val) ? comResult : val;
                      } else {
                        let val = parseFloat(Number(comResult).toFixed(3));
                        a[b].v.v = isNaN(val) ? comResult : val;
                      }
                    }
                  } catch (error) {
                    // 如果以上判断都不支持,则直接赋值
                    a[b].v.v = comResult;
                    console.log("error---", error);
                  }
                  a[b].v.v = comResult;
                  hasComputedWrite = true;
                  break;
                }
              }
@@ -456,7 +415,9 @@
          // 如果此计算结果所属单元格,同时也是另一个excel函数的参数,那么就需要递归进行计算
          // 只有算出有效结果才继续向下游公式传递,避免把空值/NaN 传染给其它单元格
          if (isValidComputedResult(comResult)) {
            changeInput(comResult, `${id}-${item.r}-${item.c}-${pId}`); //改变最终值
            // 标记为计算层:它可能是下游公式的参数(isSave 会被置成 false),但自身也是结果,
            // 保存交给最外层统一回传,中间值不会被漏掉,也不会每一级都发一次保存请求
            changeInput(comResult, `${id}-${item.r}-${item.c}-${pId}`, true); //改变最终值
          }
          currentInsItemId = item.i;
        } catch (error) {
@@ -464,10 +425,14 @@
        }
    }
  });
  if (index === -1 || (inputType === undefined && isSave === "true")) {
  if (
    !isComputed &&
    (hasComputedWrite || index === -1 || (inputType === undefined && isSave === "true"))
  ) {
    // 赋值传递到主线程的数据,method:saveInsContext表示此消息需要保存数据
    result = {
      method: "saveInsContext",
      revision: currentRevision,
      value: {
        tableList, // 表格数据
        param: getParam(), //传给后端的参数
@@ -483,6 +448,7 @@
  // 赋值多线程传输数据
  result = {
    method: "tableList",
    revision: currentRevision,
    value: tableList,
  };
  // 发送主线程数据
@@ -589,6 +555,13 @@
  const resultParam = {};
  tableList.forEach((t) => {
    resultParam[t.templateId] = {};
    const cells = (t.arr || []).reduce((all, row) => all.concat(row), []);
    const resultHeader = cells.find((cell) =>
      cell.v && String(cell.v.v || "").trim() === "检验结果"
    );
    const exportRows = new Set(cells.filter((cell) =>
      cell.v && cell.v.ps && cell.v.ps.value === "导出值"
    ).map((cell) => cell.r));
    (t.arr || []).forEach((a) => {
      a.forEach((b) => {
        const isEditable =
@@ -598,10 +571,12 @@
            b.v.ps.value === "计算值" ||
            b.v.ps.value === "最终值" ||
            b.v.ps.value === "设备名称");
        if (!isEditable) {
        const isExportResult = resultHeader && exportRows.has(b.r) && b.c === resultHeader.c;
        if (!isEditable && !isExportResult) {
          return;
        }
        if (
          isEditable &&
          b.v.ps.value.includes("检验值") &&
          (b.v.v === "" || b.v.v === null || b.v.v === undefined)
        ) {
@@ -1199,6 +1174,12 @@
          return "断裂脆化";
        }
      }
      // 参数代入后含中文时(如检验值填了"均匀"),不能再当公式 eval ——
      // eval("均匀") 会抛 ReferenceError,被外层 catch 吞掉后返回 undefined,
      // 该单元格的结果既不写回也不向下游公式传递,整条计算链断在这里
      if (/[一-龥]/.test(str)) {
        return str.trim();
      }
      // 计算常规的
      const value = eval(str);
      // 除零等非有限结果不写回单元格(与 Excel 的 #DIV/0! 一致:不出值)