liding
7 小时以前 e3a05337b453b5f09e977f4196930e8c245fb139
src/workers/InspectionWorker.worker.js
@@ -10,7 +10,7 @@
let comparisonList = [];
// 当前样品信息
let currentSample = {};
// 当前项目:检测中心、装备电缆
// 当前项目:检测中心
let PROJECT = "";
// 套管
let bushing = "";
@@ -26,19 +26,63 @@
let param = null;
// 当前的模板
let currentTable = "";
// 当前模板的结构签名(行列坐标+占位符),用于判断模板内容是否被改过
let currentTemplateSig = "";
// 优化数采较多数据时-记录最后一个检验项的id
let getDataTypeId = null;
//当前检验项
let currentInsItem = null;
// 当前这次输入对应的原始检验项。计算过程中 pId 会切换到下游计算项,
// 保存时必须同时带回原始检验项,否则只会保存计算结果。
let sourceInsItemId = null;
// 当前消息版本。Worker 单线程串行处理消息,供递归公式计算完成后回传给主线程。
let currentRevision = null;
/**
 * 生成模板结构签名
 * 只包含行列坐标与占位符,不含值,所以用户填值不会触发签名变化
 *
 * @param list 主线程传过来的表格数据
 * @returns 结构签名字符串
 */
function buildTemplateSig(list) {
  if (!list || !list.length) {
    return "";
  }
  return list
    .map((table) => {
      const tpl = table && table.template;
      if (!tpl || !tpl.length) {
        return `${table && table.templateId}:0`;
      }
      const cells = tpl
        .map((cell) => {
          const ps = cell.v && cell.v.ps ? cell.v.ps.value : "";
          return `${cell.r}-${cell.c}-${ps}`;
        })
        .join("|");
      return `${table.templateId}:${tpl.length}:${cells}`;
    })
    .join(";");
}
// 接收到主线程处理逻辑的消息
self.onmessage = function (event) {
  // 保存主线程传过来的值
  const data = JSON.parse(event.data);
  if (currentTable != data.currentTable) {
    // 检验模板改变后需要刷新表格
  currentRevision = data.revision;
  // 模板内容变了(不只是模板id变了)也要刷新表格,否则改完模版后 Worker 仍用旧模板计算并把旧版式回写页面
  const nextTemplateSig = buildTemplateSig(data.tableList);
  if (
    currentTable != data.currentTable ||
    (nextTemplateSig && nextTemplateSig != currentTemplateSig)
  ) {
    tableList = data.tableList;
  }
  currentTable = data.currentTable;
  if (nextTemplateSig) {
    currentTemplateSig = nextTemplateSig;
  }
  if (data.type && data.type == "saveData") {
    // 更新表格数据和传递到后端的参数
    tableList = data.tableList;
@@ -54,6 +98,11 @@
  }
  modelType = data.modelType;
  code = data.code;
  const codeParts = typeof code === "string" ? code.split("-") : [];
  sourceInsItemId =
    data.currentInsItem && data.currentInsItem.i != null
      ? data.currentInsItem.i
      : codeParts[3];
  if (data.getDataTypeId) {
    // 记录 优化数采较多数据时-记录最后一个检验项的id
    getDataTypeId = data.getDataTypeId;
@@ -77,10 +126,9 @@
    tableList = data.tableList;
  }
  if (param) {
    // 如果传递到后端的参数存在,则更新当前修改检验项的传递到后端的参数
    // 参数按“模板id + 单元格坐标”存,模板id即 code 的第一段
    let str = code.split("-");
    let pId = str[3];
    param[pId] = data.param[pId];
    param[str[0]] = data.param[str[0]];
  } else {
    // 初始化传递到后端的参数
    param = data.param;
@@ -97,12 +145,64 @@
  changeInput("", code);
};
/**
 * 判断公式是否为数值公式(含算术运算或 Excel 函数)
 * 这类公式必须走数值计算分支,否则只会把算式当文本原样显示
 *
 * @param f 公式字符串
 * @returns 是否按数值计算
 */
function isNumericFormula(f) {
  if (typeof f !== "string") {
    return false;
  }
  // 去掉双引号内的文本,避免把 "A-B" 这类文本字面量误判为算术
  const probe = f.replace(/"[^"]*"/g, "");
  // 只认支持的算术运算符和 Excel 函数,括号单独出现不视为数值公式,
  // 以免 IF 等不支持的函数被误判
  if (
    /[+\-*/^%]/.test(probe) ||
    /SUM|MAX|MIN|AVERAGE|ABS|MEDIAN/.test(probe)
  ) {
    return true;
  }
  // 纯单元格引用也是数值公式。模版里"最终值"常写成 =F4 这种形式,
  // 不认的话会掉进文本分支,把最终值变成带空格的字符串
  return /^[A-Za-z]{1,3}\d{1,7}$/.test(f.replace(/[=\s]/g, ""));
}
/**
 * 按检验项id取当前样品下的检验项信息,取不到时返回空对象,避免公式计算因缺项中断
 *
 * @param id 检验项id
 * @returns 检验项信息
 */
function findInsItem(id) {
  if (!currentSample || !currentSample.insProduct) {
    return {};
  }
  return currentSample.insProduct.find((m) => m.id == id) || {};
}
/**
 * 判断计算结果是否可以写回单元格
 *
 * @param r 计算结果
 * @returns 结果有效返回 true
 */
function isValidComputedResult(r) {
  if (r === "" || r === null || r === undefined) {
    return false;
  }
  return !(typeof r === "number" && isNaN(r));
}
function changeInput(m, code) {
  let str = code.split("-");
  let r = str[1]; //当前行
  let c = str[2]; //当前列
  let id = str[0];
  let pId = str[3]; //当前检验项id,param[pId]为当前检验项的所有值,包含:过程值insValue、计算值comValue、设备编码equipValue、设备名称equipName、最终值resValue、结论insResult
  let pId = str[3]; //当前检验项id,用于计算完成后把结果写回该检验项所在行
  var list = []; //一个双层数组,里面保存有当前页面渲染的所有数据
  // 赋值当前模板的表格数据
  for (let a in tableList) {
@@ -149,7 +249,8 @@
                list2.push(c.v.v);
                // 组装参数的对象集合并赋值,列如{A3:12,B4:15}
                if (
                  getInspectionValueType(item.i) == 1 &&
                  (getInspectionValueType(item.i) == 1 ||
                    isNumericFormula(item.v.f)) &&
                  !isNaN(parseFloat(c.v.v))
                ) {
                  // 如果是数字输入框
@@ -169,361 +270,26 @@
          }
        });
      });
      // 如果此excel方法是结论判断方法,则执行此方法
      // console.log('item.v.ps.value---', item.v.ps.value)
      if (item.v.ps != undefined && item.v.ps.value == "结论") {
        try {
          if (currentSample.insProduct.find((m) => m.id == item.i)) {
            // 如果当前检验项是产品检验项,则执行此方法,找到此检验项的要求值
            let ask = currentSample.insProduct.find((m) => m.id == item.i).ask
              ? currentSample.insProduct
                  .find((m) => m.id == item.i)
                  .ask.split("&")
              : null;
            // 获取当前结论的参数,也就是当前检验项的最终值
            let res = Object.values(comValue)[0];
            if (typeof res == "string" && res.includes(">")) {
              res = res.replace(">", "");
            }
            if (typeof res == "string" && res.includes("≤")) {
              res = res.replace("≤", "");
            }
            if (typeof res == "string" && res.includes("≥")) {
              res = res.replace("≥", "");
            }
            if (typeof res == "string" && res.includes("<")) {
              res = res.replace("<", "");
            }
            if (typeof res == "string" && res.includes(">")) {
              res = res.replace(">", "");
            }
            if (typeof res == "string" && res.includes("<")) {
              res = res.replace("<", "");
            }
            if (
              typeof res == "string" &&
              (res === "断裂" || res === "脆化" || res === "断裂脆化")
            ) {
              item.v.v = 0;
              list.forEach((a) => {
                if (a[0].r == item.r) {
                  for (let b = 0; b < a.length; b++) {
                    if (a[b].c == item.c) {
                      a[b].v.v = 0;
                      break;
                    }
                  }
                }
              });
            }
            let comp = [];
            // 判断当前结论的参数(当前检验项的最终值)是否为空,如果为空,则直接赋值
            if (
              res === "" ||
              res === null ||
              res === undefined ||
              res === "Infinity"
            ) {
              item.v.v = null;
              list.forEach((a) => {
                if (a[0].r == item.r) {
                  for (let b = 0; b < a.length; b++) {
                    if (a[b].c == item.c) {
                      a[b].v.v = null;
                      break;
                    }
                  }
                }
              });
            } else {
              // 如果不为空,则判断当前结论就需要进行判断
              //要求值为-,/,—,则结论设置为不判定,结论赋值为3
              if (
                ask == null ||
                ask[0] == "-" ||
                ask[0] == "/" ||
                ask[0] == "—"
              ) {
                item.v.v = 3;
                list.forEach((a) => {
                  if (a[0].r == item.r) {
                    for (let b = 0; b < a.length; b++) {
                      if (a[b].c == item.c) {
                        a[b].v.v = 3;
                        break;
                      }
                    }
                  }
                });
              } else {
                // 要求值不为-,/,—,则进行判断
                if (ask) {
                  // 循环要求值列表,判断当前结论的参数是否符合要求
                  comp = ask.map((m, i) => {
                    // 如果要求值包含=,则判断当前结论的参数是否等于要求值,
                    // 以下判断基本一致,只是判断类型不一样,就不做注释了
                    if (m.includes("=")) {
                      // 处理要求值
                      let str = handleFraction(m.split("=")[1]);
                      if (typeof res == "string" && typeof str == "string") {
                        // 如果要求值和当前结论的参数都是字符串,则执行
                        if (res.includes("/")) {
                          // 如果结论的参数是分数,则判断
                          if (m.includes("/")) {
                            // 如果要求值是分数,则判断
                            return eval(res) == eval(str);
                          } else {
                            // 如果要求值不是分数,则判断
                            return handleMoreParam(res, m.split("=")[1], "=");
                          }
                        } else {
                          // 如果结论的参数不是分数,则判断
                          return (
                            res.trim().replace(/[.,。、;:'";??“,]/g, "") ==
                            str.trim().replace(/[.,。、;:'";??“,]/g, "")
                          );
                        }
                      } else {
                        // 如果要求值和当前结论的参数有一个是数字,则执行
                        return eval(res) == eval(str);
                      }
                    } else if (m.includes("≥")) {
                      if (typeof res == "string" && res.includes("/")) {
                        if (m.includes("/")) {
                          let str = handleFraction(m.split("≥")[1]);
                          return eval(res) >= eval(str);
                        } else {
                          return handleMoreParam(res, m.split("≥")[1], "≥");
                        }
                      } else {
                        let str = handleFraction(m.split("≥")[1]);
                        return eval(res) >= eval(str);
                      }
                    } else if (m.includes("≤")) {
                      if (typeof res == "string" && res.includes("/")) {
                        if (m.includes("/")) {
                          let str = handleFraction(m.split("≤")[1]);
                          return eval(res) <= eval(str);
                        } else {
                          return handleMoreParam(res, m.split("≤")[1], "≤");
                        }
                      } else {
                        let str = handleFraction(m.split("≤")[1]);
                        if (typeof str == "string" && str.includes("A")) {
                          str = str.replace("A", "");
                        }
                        if (typeof str == "string" && str.includes("D")) {
                          str = str.replace("D", "");
                        }
                        if (typeof res == "string" && res.includes("A")) {
                          res = res.replace("A", "");
                        }
                        if (typeof res == "string" && res.includes("D")) {
                          res = res.replace("D", "");
                        }
                        return eval(res) <= eval(str);
                      }
                    } else if (m.includes("<")) {
                      if (typeof res == "string" && res.includes("/")) {
                        if (m.includes("/")) {
                          let str = handleFraction(m.split("<")[1]);
                          return eval(res) < eval(str);
                        } else {
                          return handleMoreParam(res, m.split("<")[1], "<");
                        }
                      } else {
                        let str = handleFraction(m.split("<")[1]);
                        return eval(res) < eval(str);
                      }
                    } else if (m.includes(">")) {
                      if (typeof res == "string" && res.includes("/")) {
                        if (m.includes("/")) {
                          let str = handleFraction(m.split(">")[1]);
                          return eval(res) > eval(str);
                        } else {
                          return handleMoreParam(res, m.split(">")[1], ">");
                        }
                      } else {
                        let str = handleFraction(m.split(">")[1]);
                        return eval(res) > eval(str);
                      }
                    } else if (m.includes("~")) {
                      if (typeof res == "string" && res.includes("/")) {
                        if (m.includes("/")) {
                          let k = m.split("~");
                          return (
                            eval(res) >= eval(handleFraction(k[0])) &&
                            eval(res) <= eval(handleFraction(k[1]))
                          );
                        } else {
                          return handleMoreParam(res, m, "~");
                        }
                      } else {
                        let k = m.split("~");
                        return (
                          eval(res) >= eval(handleFraction(k[0])) &&
                          eval(res) <= eval(handleFraction(k[1]))
                        );
                      }
                    } else if (m.includes("-")) {
                      if (typeof res == "string" && res.includes("/")) {
                        if (m.includes("/")) {
                          let k = m.split("-");
                          return (
                            eval(res) >= eval(handleFraction(k[0])) &&
                            eval(res) <= eval(handleFraction(k[1]))
                          );
                        } else {
                          return handleMoreParam(res, m, "-");
                        }
                      } else {
                        let k = m.split("-");
                        return (
                          eval(res) >= eval(handleFraction(k[0])) &&
                          eval(res) <= eval(handleFraction(k[1]))
                        );
                      }
                    } else if (m.includes("±")) {
                      if (modelType.includes("φ-")) {
                        if (m.indexOf("±") == 0) {
                          m =
                            modelType.substring(modelType.indexOf("-") + 1) + m;
                        }
                      }
                      if (typeof res == "string" && res.includes("/")) {
                        if (m.includes("/")) {
                          let k = m.split("±");
                          return (
                            eval(res) >=
                              eval(
                                handleFraction(k[0]) - handleFraction(k[1])
                              ) &&
                            eval(res) <=
                              eval(
                                Number(handleFraction(k[0])) +
                                  Number(handleFraction(k[1]))
                              )
                          );
                        } else {
                          return handleMoreParam(res, m, "±");
                        }
                      } else {
                        let k = m.split("±");
                        return (
                          eval(res) >=
                            eval(handleFraction(k[0]) - handleFraction(k[1])) &&
                          eval(res) <=
                            eval(
                              Number(handleFraction(k[0])) +
                                Number(handleFraction(k[1]))
                            )
                        );
                      }
                    } else if (m.includes(">")) {
                      if (typeof res == "string" && res.includes("/")) {
                        if (m.includes("/")) {
                          let str = handleFraction(m.split(">")[1]);
                          return eval(res) > eval(str);
                        } else {
                          return handleMoreParam(res, m.split(">")[1], ">");
                        }
                      } else {
                        let str = handleFraction(m.split(">")[1]);
                        return eval(res) > eval(str);
                      }
                    } else if (m.includes("<")) {
                      if (typeof res == "string" && res.includes("/")) {
                        if (m.includes("/")) {
                          let str = handleFraction(m.split("<")[1]);
                          return eval(res) < eval(str);
                        } else {
                          return handleMoreParam(res, m.split("<")[1], "<");
                        }
                      } else {
                        let str = handleFraction(m.split("<")[1]);
                        return eval(res) < eval(str);
                      }
                    }
                  });
                }
                // 如果要求值的每个条件都符合,则给结论赋值为1,合格
                if (comp.every((m) => m)) {
                  item.v.v = 1;
                  list.forEach((a) => {
                    if (a[0].r == item.r) {
                      for (let b = 0; b < a.length; b++) {
                        if (a[b].c == item.c) {
                          a[b].v.v = 1;
                          break;
                        }
                      }
                    }
                  });
                } else {
                  // 否则给结论赋值为0,不合格
                  item.v.v = 0;
                  list.forEach((a) => {
                    if (a[0].r == item.r) {
                      for (let b = 0; b < a.length; b++) {
                        if (a[b].c == item.c) {
                          a[b].v.v = 0;
                          break;
                        }
                      }
                    }
                  });
                }
              }
            }
            let getDataType0 = false;
            // 优化数采较多数据时-记录最后一个检验项的id,如果当前检验项的id与记录的id相同,则多传一个参数到主线程,进行数据保存,否则数采就不进行保存
            if (item.i == getDataTypeId) {
              getDataType0 = true;
            }
            // 赋值传递到主线程的数据,method:saveInsContext表示此消息需要保存数据
            currentInsItemId = item.i;
            // 赋值传递到主线程的数据,method:saveInsContext表示此消息需要保存数据
            result = {
              method: "saveInsContext",
              value: {
                tableList, // 表格数据
                param: getParam(), //传给后端的参数
                getDataTypeId: getDataTypeId,
                currentInsItemId: item.i,
              },
            };
            // 发消息给主线程
            self.postMessage(JSON.stringify(result));
          }
        } catch (error) {
          console.log("error---", error);
        }
      } else {
        // 如果是函数方法,则执行此方法
        let comResult = ""; //初始化计算结果
        try {
          if (getInspectionValueType(item.i) == 1) {
            // 如果检验值类型是数字输入框
            let tell = currentSample.insProduct.find((m) => m.id == item.i).tell
              ? currentSample.insProduct
                  .find((m) => m.id == item.i)
                  .tell.split("&")
              : null;
          if (
            getInspectionValueType(item.i) == 1 ||
            isNumericFormula(item.v.f)
          ) {
            // 如果检验值类型是数字输入框,或公式本身含算术运算/Excel函数
            const itemInfo = findInsItem(item.i);
            let tell =
              typeof itemInfo.tell == "string" ? itemInfo.tell.split("&") : null;
            isPoint =
              tell &&
              tell.length > 0 &&
              typeof tell[0] == "string" &&
              tell[0].includes("/") &&
              tell[0] !== "/"; // 装备电缆--判断要求值是否为分数
            const inspectionItemClass = currentSample.insProduct.find(
              (m) => m.id == item.i
            ).inspectionItemClass; // 检验项分类
            const inspectionItem = currentSample.insProduct.find(
              (m) => m.id == item.i
            ).inspectionItem; // 检验项
            const inspectionItemSubclass = currentSample.insProduct.find(
              (m) => m.id == item.i
            ).inspectionItemSubclass; // 检验子项
              tell[0] !== "/"; // 判断要求值是否为分数
            const inspectionItemClass = itemInfo.inspectionItemClass; // 检验项分类
            const inspectionItem = itemInfo.inspectionItem; // 检验项
            const inspectionItemSubclass = itemInfo.inspectionItemSubclass; // 检验子项
            let isHaveSymbol = false;
            let symbol = "";
            for (var a in comValue) {
@@ -576,7 +342,12 @@
            ) {
              comResult = comResult + "";
            }
            if (inspectionItem === "邵氏硬度" && tell[0].includes("A")) {
            if (
              inspectionItem === "邵氏硬度" &&
              tell &&
              tell[0] &&
              tell[0].includes("A")
            ) {
              // 要求值如果有包含字母,最终结果要展示出字母
              comResult = comResult + "A";
            }
@@ -584,6 +355,8 @@
              inspectionItemClass === "绝缘机械物理性能" &&
              inspectionItem === "弯曲性能" &&
              inspectionItemSubclass === "最小弯曲半径" &&
              tell &&
              tell[0] &&
              tell[0].includes("D")
            ) {
              // 要求值如果有包含字母,最终结果要展示出字母
@@ -635,7 +408,7 @@
        try {
          // 循环表格数据,给表格数据进行赋值
          list.forEach((a) => {
            if (a[0].r == item.r && comResult !== "") {
            if (a[0].r == item.r && isValidComputedResult(comResult)) {
              // 判断当前行是否为当前检验项所在行,如果为当前行,则给表格数据赋值
              for (var b in a) {
                if (a[b].c == item.c) {
@@ -663,9 +436,7 @@
                      // 判断计算结果是否为科学计数法,如果为科学计数法,则给表格数据赋值为科学计数法
                      a[b].v.v = comResult;
                    } else {
                      const inspectionItem = currentSample.insProduct.find(
                        (m) => m.id == item.i
                      ).inspectionItem;
                      const inspectionItem = findInsItem(item.i).inspectionItem;
                      // 判断计算结果是否为数字,如果为数字,则给表格数据赋值为数字
                      if (inspectionItem == "铜线电阻率ρ20最大值") {
                        let val = parseFloat(Number(comResult).toFixed(5));
@@ -686,23 +457,27 @@
            }
          });
          // 如果此计算结果所属单元格,同时也是另一个excel函数的参数,那么就需要递归进行计算
          changeInput(comResult, `${id}-${item.r}-${item.c}-${pId}`); //改变最终值
          // 只有算出有效结果才继续向下游公式传递,避免把空值/NaN 传染给其它单元格
          if (isValidComputedResult(comResult)) {
            changeInput(comResult, `${id}-${item.r}-${item.c}-${pId}`); //改变最终值
          }
          currentInsItemId = item.i;
        } catch (error) {
          console.log("error---", error);
        }
      }
    }
  });
  if (index === -1 || (inputType === undefined && isSave === "true")) {
    // 赋值传递到主线程的数据,method:saveInsContext表示此消息需要保存数据
    result = {
      method: "saveInsContext",
      revision: currentRevision,
      value: {
        tableList, // 表格数据
        param: getParam(), //传给后端的参数
        getDataTypeId: getDataTypeId,
        currentInsItemId: currentInsItemId,
        sourceInsItemId: sourceInsItemId,
      },
    };
    // 发消息给主线程
@@ -712,21 +487,11 @@
  // 赋值多线程传输数据
  result = {
    method: "tableList",
    revision: currentRevision,
    value: tableList,
  };
  // 发送主线程数据
  self.postMessage(JSON.stringify(result));
  try {
    // 赋值多线程传输数据
    result = {
      method: "getCurrentInsProduct",
      value: pId,
    };
    // 发送主线程数据
    self.postMessage(JSON.stringify(result));
  } catch (error) {
    console.log("error---", error);
  }
}
/**
 * 获取检测值类型
@@ -825,64 +590,42 @@
 * @returns 返回处理后的参数对象
 */
function getParam() {
  tableList[0].arr.forEach((a) => {
    a.forEach((b) => {
      // 初始化传递到后端的参数
      if (param[b.i]) {
        param[b.i].insValue = [];
        param[b.i].comValue = [];
        param[b.i].equipValue = [];
        param[b.i].equipName = [];
        param[b.i].resValue = null;
        param[b.i].insResult = null;
      }
    });
  });
  tableList[0].arr.forEach((a) => {
    a.forEach((b) => {
      // 根据表格数据,赋值传递到后端的参数
      if (
        b.v.ps != undefined &&
        typeof b.v.ps.value == "string" &&
        b.v.ps.value.includes("检验值")
      ) {
        // 赋值检验值
        b.i && b.v.v && param[b.i].insValue.push(b);
      }
      if (b.v.ps != undefined && b.v.ps.value === "计算值") {
        // 赋值计算值
        b.i &&
          b.v.v &&
          b.valueList &&
          b.valueList.length > 0 &&
          param[b.i].comValue.push(b);
      }
      if (b.v.ps != undefined && b.v.ps.value === "设备编码") {
        // 赋值设备编码
        b.i && b.v && param[b.i].equipValue.push(b);
      }
      if (b.v.ps != undefined && b.v.ps.value === "设备名称") {
        // 赋值设备名称
        b.i && b.v && param[b.i].equipName.push(b);
      }
      if (b.v.ps != undefined && b.v.ps.value === "最终值") {
        // 赋值最终值
        b.i &&
          b.v &&
          b.valueList &&
          b.valueList.length > 0 &&
          (param[b.i].resValue = b);
      }
      if (b.v.ps != undefined && b.v.ps.value === "结论") {
        if (b.i && (b.v.v || b.v.v === 0 || b.v.v === "0")) {
          if (b.v.v != "合格" && b.v.v != "不合格") {
            // 赋值结论
            param[b.i].insResult = b;
          }
  // 按“模板id + 单元格坐标(r-c)”从当前模板快照出待保存参数
  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 =
          b.v.ps != undefined &&
          typeof b.v.ps.value == "string" &&
          (b.v.ps.value.includes("检验值") ||
            b.v.ps.value === "计算值" ||
            b.v.ps.value === "最终值" ||
            b.v.ps.value === "设备名称");
        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)
        ) {
          return;
        }
        resultParam[t.templateId][`${b.r}-${b.c}`] = { v: b.v.v, u: b.u };
      });
    });
  });
  param = resultParam;
  return param;
}
@@ -1177,6 +920,7 @@
 */
function getIdFromColumnName(id, arr) {
  try {
    id = String(id).replace(/\$/g, "");
    // Get the letters
    var t = /^[a-zA-Z]+/.exec(id);
    if (t) {
@@ -1257,6 +1001,9 @@
 */
function getABCList(f) {
  try {
    if (typeof f !== "string") {
      return [];
    }
    let regex = /[=\+\-\*\%\(\)\/\^\s]/g;
    // 上面计算函数新增后,这里也要同步增加
    let fouList = [
@@ -1270,8 +1017,12 @@
      "IF",
      "LOG",
    ];
    // 替换特殊字符
    // 替换特殊字符,去掉 Excel 绝对引用符号($D$14 与 D14 等价)
    f = f
      .replace(/\$/g, "")
      // 区间写法里的空格(AVERAGE( D14 : D15 ))会被下面的空白替换吃掉冒号,
      // 导致区间被拆成两个独立单元格,先把区间规整成 D14:D15
      .replace(/\s*:\s*/g, ":")
      .replace(regex, ",")
      .replace(new RegExp('"&', "g"), "")
      .replace(new RegExp('&"', "g"), "");
@@ -1308,6 +1059,22 @@
}
/**
 * 把单元格值转成可参与 eval 的字面量,空白单元格按 0 处理(与 Excel 一致)
 *
 * @param value 单元格的值
 * @returns 数值/原始值,空值返回 0
 */
function toNumberLiteral(value) {
  if (value === null || value === undefined) {
    return 0;
  }
  if (typeof value === "string" && value.trim() === "") {
    return 0;
  }
  return value;
}
/**
 * 计算函数
 *
 * @param f 字符串类型,表示待计算的公式
@@ -1316,7 +1083,9 @@
 */
function compute(f, comValue, isPoint) {
  try {
    let str = f;
    // 去掉 Excel 绝对引用符号,$D$14 与 D14 等价;
    // 区间里的空格也要一并去掉,否则下面把 ":" 换成 "-" 后 D14-D15 会因空格匹配不上而展开失败
    let str = f.replace(/\$/g, "").replace(/\s*:\s*/g, ":");
    // 获取单元格对应值
    let arr = getAllCell(f);
    for (var a in comValue) {
@@ -1383,9 +1152,18 @@
    for (var a in obj) {
      str = str.replace(new RegExp(a, "g"), obj[a]);
    }
    // 计算
    for (var a in arr) {
      str = str.replace(new RegExp(a, "g"), arr[a]);
    // 计算:一次性替换所有参数,避免 D1 与 D14 这类前缀互相污染;
    // 空白单元格按 0 处理,避免把 null/undefined 带进表达式导致整条公式算不出来
    const cellKeys = Object.keys(arr);
    if (cellKeys.length > 0) {
      const pattern = new RegExp(
        cellKeys
          .sort((x, y) => y.length - x.length)
          .map((k) => k.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
          .join("|"),
        "g"
      );
      str = str.replace(pattern, (m) => String(toNumberLiteral(arr[m])));
    }
    if (str.includes(",,")) {
      str = str.replace(new RegExp(",,", "g"), ",");
@@ -1407,8 +1185,9 @@
      return str
        .replace(new RegExp('&"/"&', "g"), "/")
        .replace(new RegExp("//", "g"), "");
    } else if (isPoint) {
      // 计算带小数点的
    } else if (isPoint && !/SUM|MAX|MIN|AVERAGE|ABS|MEDIAN/.test(str)) {
      // 分数形式的输入(如 3/4)按原文返回展示;
      // 但含 Excel 函数名的公式不能走这里,否则会把 "AVERAGE(...)" 这种算式文本当成结果写回单元格
      return str.replace("ABS", "").replace(/\(|\)/g, "");
    } else {
      if (str.includes("IF")) {
@@ -1435,7 +1214,12 @@
        }
      }
      // 计算常规的
      return eval(str);
      const value = eval(str);
      // 除零等非有限结果不写回单元格(与 Excel 的 #DIV/0! 一致:不出值)
      if (typeof value === "number" && !isFinite(value)) {
        return undefined;
      }
      return value;
    }
  } catch (error) {
    console.log("error", error);