新疆——新聚lims
1.检验任务的温湿度改为每个检验模版都填单独的温湿度
2.报告编制列表操作栏功能删除
3.检验单详情检验值输入框改为可以填汉字及数值
已修改9个文件
317 ■■■■■ 文件已修改
src/views/business/inspectionReview/index.vue 60 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/inspectionTask/components/InspectionWord.vue 109 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/inspectionTask/inspection.vue 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/materialOrder/customsInspectionOrder.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/materialOrder/customsInspectionView.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/productOrder/components/addOrder.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/productOrder/components/addView.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/reportPreparation/index.vue 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/workers/InspectionWorker.worker.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/inspectionReview/index.vue
@@ -198,36 +198,36 @@
                return row.checkName == null || (row.checkName && !row.checkName.includes(this.nickName))
              }
            },
            {
              name: "下载报告",
              type: "text",
              clickFun: (row) => {
                this.download(row);
              },
            },
            {
              name: "上传",
              type: "upload",
              accept: '.doc,.docx',
              url: '/insReport/inReport',
              uploadIdFun: (row) => {
                return row.insReportId
              }
            },
            {
              name: "还原",
              type: "text",
              clickFun: (row) => {
                this.handleRestore(row);
              },
            },
            {
              name: "查看报告",
              type: "text",
              clickFun: (row) => {
                this.handleIssued(row);
              }
            },
            // {
            //   name: "下载报告",
            //   type: "text",
            //   clickFun: (row) => {
            //     this.download(row);
            //   },
            // },
            // {
            //   name: "上传",
            //   type: "upload",
            //   accept: '.doc,.docx',
            //   url: '/insReport/inReport',
            //   uploadIdFun: (row) => {
            //     return row.insReportId
            //   }
            // },
            // {
            //   name: "还原",
            //   type: "text",
            //   clickFun: (row) => {
            //     this.handleRestore(row);
            //   },
            // },
            // {
            //   name: "查看报告",
            //   type: "text",
            //   clickFun: (row) => {
            //     this.handleIssued(row);
            //   }
            // },
            // {
            //   name: "产业链",
            //   type: "text",
src/views/business/inspectionTask/components/InspectionWord.vue
@@ -26,6 +26,21 @@
            </el-select>
          </div>
        </div>
        <div style="text-align: left; margin-top: 8px;">
          <el-form :inline="true" :model="currentOtherForm" class="form-inline" label-width="50px"
            style="text-align: left; display: inline">
            <el-form-item label="温度:" style="margin-bottom: 0">
              <el-input v-model="currentOtherForm.temperature" :disabled="state > 1" placeholder="" size="small"
                style="width: 90px" @change="m => subOtherForm(m, 'temperature')"></el-input>
              <span style="margin-left: 4px">℃</span>
            </el-form-item>
            <el-form-item label="湿度:" style="margin-bottom: 0">
              <el-input v-model="currentOtherForm.humidity" :disabled="state > 1" placeholder="" size="small"
                style="width: 90px" @change="m => subOtherForm(m, 'humidity')"></el-input>
              <span style="margin-left: 4px">%RH</span>
            </el-form-item>
          </el-form>
        </div>
        <!-- 常规检验原始记录 -->
        <div id="tableBox" v-loading="tableLoading" class="center-box">
          <table v-for="(item, index) in tableList" :key="index + currentTable + currentSample.id" border="1"
@@ -220,7 +235,8 @@
  getInsProductUnqualifiedRetest,
  search, selectUserCondition,
  submitPlan,
  verifyPlan
  verifyPlan,
  write
} from "@/api/business/inspectionTask";
import { getUserNow, saveUnqualifiedContext } from "@/api/business/rawMaterialOrder";
import InspectionWorker from '@/workers/InspectionWorker.worker';
@@ -340,10 +356,8 @@
      numOptions: [],
      temperatureOptions: [],
      wareTableData: [],
      otherForm: {
        humidity: null,
        temperature: null,
      },
      // 温度、湿度按模板 id 分组:{ [templateId]: { temperature, humidity } }
      otherForm: {},
      equipForm: {
        value0: null,
        code0: null,
@@ -418,6 +432,13 @@
    },
    action() {
      return this.javaApi + "/insOrderPlan/uploadFile";
    },
    // 当前模板对应的温度、湿度
    currentOtherForm() {
      return this.otherForm[this.currentTable] || {
        temperature: null,
        humidity: null
      }
    }
  },
  created() {
@@ -453,11 +474,15 @@
        this.sampleProduct = res.data.sampleProduct
        this.currentSample = this.HaveJson(this.sampleProduct[0])
        let insProduct = this.HaveJson(this.currentSample.insProduct)
        // 温度、湿度赋值
        this.otherForm = {
          temperature: this.insOrder.temperature ? this.insOrder.temperature : null,
          humidity: this.insOrder.humidity ? this.insOrder.humidity : null,
        }
        // 温度、湿度赋值:按模板 id 分组回显
        this.otherForm = {}
        const conditionList = this.insOrder.templateConditions || []
        conditionList.forEach(item => {
          this.$set(this.otherForm, item.templateId, {
            temperature: item.temperature != null ? item.temperature : null,
            humidity: item.humidity != null ? item.humidity : null
          })
        })
        if (this.typeSource == '1') {
          this.retestTag = '1'
        }
@@ -515,6 +540,8 @@
            if (this.tableLists && this.tableLists.length > 0) {
              this.tableList = null;
              this.tableList = this.tableLists.filter(m => m.templateId == val1)
              // 模板集合可能变化,补齐温度、湿度槽位
              this.tableLists.forEach(m => this.ensureOtherForm(m.templateId))
              // 对模板进行处理
              this.handleTableData()
            }
@@ -846,6 +873,8 @@
        this.tableList = null;
        this.tableList = [this.tableLists[0]]
        this.currentTable = this.tableLists[0].templateId;
        // 补齐每个模板的温度、湿度槽位
        this.tableLists.forEach(m => this.ensureOtherForm(m.templateId))
        // 处理页面列表数据
        this.handleTableData()
      }
@@ -875,6 +904,8 @@
        this.tableList = [this.tableLists[0]]
        this.currentTable = this.tableLists[0].templateId;
        this.currentSample.insProduct = this.HaveJson(list)
        // 补齐每个模板的温度、湿度槽位
        this.tableLists.forEach(m => this.ensureOtherForm(m.templateId))
        // 处理页面列表数据
        this.handleTableData()
      }
@@ -1210,15 +1241,24 @@
    // 对输入值进行格式校验
    handleInput(n) {
      try {
        n.v.v = n.v.v.replace(/[^\d.^e>\-/+]/g, '');
        n.v.v = n.v.v.replace(/\.{2,}/g, "."); //只保留第一个. 清除多余的
        n.v.v = n.v.v.replace(".", "$#$").replace(/\./g, "").replace("$#$", ".");
        n.v.v = n.v.v.replace(/\/{2,}/g, "/"); //只保留第一个/清除多余的
        n.v.v = n.v.v.replace("/", "$#$").replace(/\//g, "").replace("$#$", "/");
        const v = n.v.v;
        // 能力范围已废弃,检验值类型不再作为判断依据。整串只由算式字符组成时才规范化格式,
        // 出现中文等其它字符即按文本处理原样保留,否则文字会被逐字删掉
        if (typeof v !== "string" || /[^\d.^e>>≥≤<<断裂脆化\-/+]/.test(v)) {
          return;
        }
        n.v.v = v
          .replace(/\.{2,}/g, ".") //只保留第一个. 清除多余的
          .replace(".", "$#$")
          .replace(/\./g, "")
          .replace("$#$", ".")
          .replace(/\/{2,}/g, "/") //只保留第一个/清除多余的
          .replace("/", "$#$")
          .replace(/\//g, "")
          .replace("$#$", "/");
      } catch (error) {
        console.log(error);
      }
    },
    getInspectionItemType(id) {
      for (var a in this.currentSample.insProduct) {
@@ -1332,17 +1372,42 @@
        this.$message.error('未输入不通过原因')
      }
    },
    // 保证模板的温度、湿度槽位存在
    ensureOtherForm(templateId) {
      if (templateId == null) return
      if (!this.otherForm[templateId]) {
        this.$set(this.otherForm, templateId, {
          temperature: null,
          humidity: null
        })
      }
    },
    // 保存当前模板的温度、湿度
    subOtherForm(m, type) {
      write({
        id: this.insOrder.id,
        templateId: this.currentTable,
        [type]: Number(m)
      }).then(res => {
        this.$message.success("保存成功")
      })
    },
    submit() {
      if (this.verifyUser === null || this.verifyUser === '') {
        this.$message.error("请指定复核人员")
        return
      }
      if (!this.otherForm.humidity) {
        this.$message.error("请输入湿度")
        return
      }
      if (!this.otherForm.temperature) {
        this.$message.error("请输入温度")
      const labels = { temperature: "温度", humidity: "湿度" }
      const keys = ["temperature", "humidity"]
      const missing = []
      this.tableLists.forEach(m => {
        const form = this.otherForm[m.templateId] || {}
        keys.forEach(key => {
          if (!form[key]) missing.push(`「${m.templateName}」的${labels[key]}`)
        })
      })
      if (missing.length > 0) {
        this.$message.error(`请填写${missing[0]}`)
        return
      }
      this.submitLoading = true;
src/views/business/inspectionTask/inspection.vue
@@ -2,17 +2,17 @@
  <div v-loading="loading" class="inspection" style="background-color: rgb(245, 247, 251);">
    <el-row class="title">
      <el-col v-if="insOrder.ifsOrderType!=='02wg'" :span="8" style="text-align: left">
        <el-form :inline="true" :model="otherForm" class="form-inline" label-width="50px"
        <el-form :inline="true" :model="currentOtherForm" class="form-inline" label-width="50px"
          style="text-align: right; padding-top: 0; display: inline">
          <el-form-item label="温度:" style="margin-bottom: 0">
            <el-input v-model="otherForm.temperature" :disabled="state > 1" placeholder="" size="small"
            <el-input v-model="currentOtherForm.temperature" :disabled="state > 1" placeholder="" size="small"
              style="width: 90px; line-height: 60px" @change="(m) => subOtherForm(m, 'temperature')"></el-input>
            <span style="margin-left: 4px">℃</span>
          </el-form-item>
          <el-form-item label="湿度:" style="margin-bottom: 0">
            <el-input v-model="otherForm.humidity" :disabled="state > 1" placeholder="" size="small"
            <el-input v-model="currentOtherForm.humidity" :disabled="state > 1" placeholder="" size="small"
              style="width: 90px; line-height: 60px" @change="(m) => subOtherForm(m, 'humidity')"></el-input>
            <span style="margin-left: 4px">%</span>
            <span style="margin-left: 4px">%RH</span>
          </el-form-item>
        </el-form>
      </el-col>
@@ -22,7 +22,7 @@
        <el-button v-if="typeSource == 1" size="small" type="primary" @click="openPurchase">进货验证</el-button>
        <el-button v-if="state == 1 && [0,1].includes(Number(typeSource))" size="small" type="primary"
          @click="openUnPassDialog('add')">不合格处理</el-button>
        <el-button size="small" type="primary" @click="sampleVisible = true; uploadSample();">样品切换</el-button>
        <!-- <el-button size="small" type="primary" @click="sampleVisible = true; uploadSample();">样品切换</el-button> -->
        <!--        <el-button v-if="state == 1" size="small" type="primary" @click="taskVisible = true">任务切换</el-button>-->
        <el-button v-if="state == 1" size="small" type="primary" @click="addVerifyDia = true">提交</el-button>
        <!-- 复核 -->
@@ -301,12 +301,12 @@
                        align-items: center;
                        width: 100%;
                      ">
                        <i class="el-icon-caret-left table_caret" style="width: 16px" @click="caretSample(-1)"></i>
                        <!-- <i class="el-icon-caret-left table_caret" style="width: 16px" @click="caretSample(-1)"></i> -->
                        <div
                          :style="`font-family:${n.v.ff} !important;overflow: hidden;white-space: nowrap;width: calc(100% - 32px);`">
                          {{ currentSample.sampleCode }}
                        </div>
                        <i class="el-icon-caret-right table_caret" style="width: 16px" @click="caretSample(1)"></i>
                        <!-- <i class="el-icon-caret-right table_caret" style="width: 16px" @click="caretSample(1)"></i> -->
                      </div>
                    </template>
                    <template v-else-if="
@@ -336,6 +336,7 @@
      <lims-table :tableData="tableData" :column="column" :page="page" :tableLoading="tableLoading"
        :height="'calc(100vh - 290px)'" @pagination="pagination" style="height: 100%; margin-top: 16px"></lims-table>
    </div>
    <!-- 样品切换已停用
    <el-drawer :size="550" :visible.sync="sampleVisible" title="样品切换">
      <el-table v-if="sampleVisible" ref="productTable" :current-row-key="currentKey" :data="sampleProduct"
                :header-cell-style="{ background: '#f8f8f9', color: '#515a6e' }" border
@@ -356,7 +357,9 @@
            <span>{{ scope.row.isLeave == 0 ? "否" : "是" }}</span>
          </template>
        </el-table-column>
    -->
        <!-- 检验单会有一个是否有配套样品字段控制显示 -->
    <!--
        <el-table-column v-if="insOrder.mating > 0" label="配套样品型号" min-width="130px" prop="joinModel"
          show-overflow-tooltip></el-table-column>
        <el-table-column v-if="insOrder.mating > 0" label="配套样品名称" min-width="130px" prop="joinName"
@@ -365,6 +368,7 @@
          show-overflow-tooltip></el-table-column>
      </el-table>
    </el-drawer>
    -->
    <el-drawer :size="500" :visible.sync="taskVisible" title="任务切换">
      <lims-table :tableData="tableData0" :column="column0" :page="page0" :tableLoading="tableLoading0"
        :height="'calc(100vh - 90px)'" :currentChange="handleChangeTask"></lims-table>
@@ -685,10 +689,8 @@
        inspectionItem: 1,
        inspectionItemSubclass: "20(常温)",
      },
      otherForm: {
        humidity: null,
        temperature: null,
      },
      // 温度、湿度按模板 id 分组:{ [templateId]: { temperature, humidity } }
      otherForm: {},
      equipForm: {
        value0: null,
        code0: null,
@@ -841,6 +843,15 @@
    action() {
      return this.javaApi + "/insOrderPlan/uploadFile";
    },
    // 当前模板对应的温度、湿度
    currentOtherForm() {
      return (
        this.otherForm[this.currentTable] || {
          temperature: null,
          humidity: null,
        }
      );
    },
  },
  created() {
    this.saveQueue = Promise.resolve();
@@ -915,6 +926,8 @@
              this.tableList = this.tableLists.filter(
                (m) => m.templateId == val1
              );
              // 模板集合可能变化,补齐温度、湿度槽位
              this.tableLists.forEach((m) => this.ensureOtherForm(m.templateId));
              // 对模板进行处理
              this.handleTableData();
            }
@@ -1102,13 +1115,15 @@
        this.sampleProduct = res.data.sampleProduct;
        this.currentSample = this.HaveJson(this.sampleProduct[0]);
        let insProduct = this.HaveJson(this.currentSample.insProduct);
        // 温度、湿度赋值
        this.otherForm = {
          temperature: this.insOrder.temperature
            ? this.insOrder.temperature
            : null,
          humidity: this.insOrder.humidity ? this.insOrder.humidity : null,
        };
        // 温度、湿度赋值:按模板 id 分组回显
        this.otherForm = {};
        (this.insOrder.templateConditions || []).forEach((item) => {
          this.$set(this.otherForm, item.templateId, {
            temperature: item.temperature != null ? item.temperature : null,
            humidity: item.humidity != null ? item.humidity : null,
          });
        });
        this.tableLists.forEach((m) => this.ensureOtherForm(m.templateId));
        if (this.typeSource == "1") {
          this.getRawMaterialTag(this.currentSample.id); // 原材料的检验任务查询批数
          this.rawMaterialTag = "1";
@@ -1614,12 +1629,26 @@
        this.worker0 = null;
      }
    },
    // 保存湿度、温度数据
    // 保证模板的温度、湿度槽位存在
    ensureOtherForm(templateId) {
      if (templateId == null) return;
      if (!this.otherForm[templateId]) {
        this.$set(this.otherForm, templateId, {
          temperature: null,
          humidity: null,
        });
      }
    },
    // 保存当前模板的温度、湿度;备注是单据级,不带模板
    subOtherForm(m, type) {
      write({
        [type]: type === "remark" ? m : Number(m),
        id: this.insOrder.id,
      }).then((res) => {
      const payload = { id: this.insOrder.id };
      if (type === "remark") {
        payload.remark = m;
      } else {
        payload.templateId = this.currentTable;
        payload[type] = Number(m);
      }
      write(payload).then((res) => {
        this.$message.success("保存成功");
      });
    },
@@ -1786,6 +1815,8 @@
        this.tableList = null;
        this.tableList = [this.tableLists[0]];
        this.currentTable = this.tableLists[0].templateId;
        // 补齐每个模板的温度、湿度槽位
        this.tableLists.forEach((m) => this.ensureOtherForm(m.templateId));
        // 处理页面列表数据
        this.handleTableData();
      }
@@ -2132,14 +2163,18 @@
    // 对输入值进行格式校验
    handleInput(n) {
      try {
        n.v.v = n.v.v.replace(/[^\d.^e>>≥≤<<断裂脆化\-/+]/g, "");
        n.v.v = n.v.v.replace(/\.{2,}/g, "."); //只保留第一个. 清除多余的
        n.v.v = n.v.v
        const v = n.v.v;
        // 能力范围已废弃,检验值类型不再作为判断依据。整串只由算式字符组成时才规范化格式,
        // 出现中文等其它字符即按文本处理原样保留,否则文字会被逐字删掉
        if (typeof v !== "string" || /[^\d.^e>>≥≤<<断裂脆化\-/+]/.test(v)) {
          return;
        }
        n.v.v = v
          .replace(/\.{2,}/g, ".") //只保留第一个. 清除多余的
          .replace(".", "$#$")
          .replace(/\./g, "")
          .replace("$#$", ".");
        n.v.v = n.v.v.replace(/\/{2,}/g, "/"); //只保留第一个/清除多余的
        n.v.v = n.v.v
          .replace("$#$", ".")
          .replace(/\/{2,}/g, "/") //只保留第一个/清除多余的
          .replace("/", "$#$")
          .replace(/\//g, "")
          .replace("$#$", "/");
@@ -2267,13 +2302,19 @@
        this.$message.error("请选择设备");
        return;
      }
      if (!this.otherForm.humidity && this.insOrder.ifsOrderType!=='02wg') {
        this.$message.error("请输入湿度");
        return;
      }
      if (!this.otherForm.temperature && this.insOrder.ifsOrderType!=='02wg') {
        this.$message.error("请输入温度");
        return;
      if (this.insOrder.ifsOrderType !== "02wg") {
        const labels = { temperature: "温度", humidity: "湿度" };
        const missing = [];
        this.tableLists.forEach((m) => {
          const form = this.otherForm[m.templateId] || {};
          ["temperature", "humidity"].forEach((key) => {
            if (!form[key]) missing.push(`「${m.templateName}」的${labels[key]}`);
          });
        });
        if (missing.length > 0) {
          this.$message.error(`请填写${missing[0]}`);
          return;
        }
      }
      if (!this.insOrder.lotPartId && this.insOrder.ifsOrderType==='02wg') {
        this.$message.error("请填写IFS库存物料批次属性");
src/views/business/materialOrder/customsInspectionOrder.vue
@@ -520,7 +520,7 @@
        productionEn: null,
        typeSource: 1,
        ifsInventoryId: '',
        sampleStatus: 'In good condition',
        sampleStatus: '',
      },
      addObjRules: { // 表单校验规则
        testQuantity: [
src/views/business/materialOrder/customsInspectionView.vue
@@ -501,7 +501,7 @@
        productionEn: null,
        typeSource: 1,
        ifsInventoryId: '',
        sampleStatus: 'In good condition',
        sampleStatus: '',
      },
      addObjRules: { // 表单校验规则
        testQuantity: [
src/views/business/productOrder/components/addOrder.vue
@@ -705,7 +705,7 @@
        orderType: null,
        send: 1,
        formType: '送检', //  来样方式
        sampleStatus: 'In good condition', // 样品状态
        sampleStatus: '', // 样品状态
        testQuantity: '', // 样品状态
        production: null,
        productionEn: null,
src/views/business/productOrder/components/addView.vue
@@ -680,7 +680,7 @@
        orderType: null,
        send: 1,
        formType: '送检', //  来样方式
        sampleStatus: 'In good condition', // 样品状态
        sampleStatus: '', // 样品状态
        testQuantity: '', // 样品状态
        production: null,
        productionEn: null,
src/views/business/reportPreparation/index.vue
@@ -48,15 +48,15 @@
        :isSelection="true" :handleSelectionChange="handleChange" :height="'calc(100vh - 290px)'"
        @pagination="pagination" key="valueTableData">
        <div slot="action" slot-scope="scope">
          <el-button size="small" type="text" @click="viewIssued(scope.row)">查看报告</el-button>
<!--          <el-button size="small" type="text" @click="viewIssued(scope.row)">查看报告</el-button>-->
          <el-upload ref='upload1' style="display: inline;margin: 0 6px" :action="fileAction1 + '?id=' + scope.row.id"
            :auto-upload="true" :before-upload="fileBeforeUpload1" :headers="uploadHeader" :on-error="onError1"
            :on-success="handleSuccessUp1" :show-file-list="false" accept='.doc,.docx'>
            <el-button size="small" type="text"
              :disabled="scope.row.state != 0 || nickName !== scope.row.writeUserName">上传</el-button>
          </el-upload>
          <el-button type="text" size="small" :disabled="scope.row.state != 0 || nickName !== scope.row.writeUserName"
            @click="handleRestore(scope.row)">还原</el-button>
<!--          <el-button type="text" size="small" :disabled="scope.row.state != 0 || nickName !== scope.row.writeUserName"-->
<!--            @click="handleRestore(scope.row)">还原</el-button>-->
          <el-button type="text" size="small" :disabled="scope.row.state != 0 || nickName !== scope.row.writeUserName"
            @click="sendBackTask(scope.row)">退回任务</el-button>
          <el-button type="text" size="small" :disabled="scope.row.state != 0 || nickName !== scope.row.writeUserName"
@@ -67,17 +67,10 @@
          <el-button type="text" size="small"
            :disabled="scope.row.state == null || scope.row.state == 0 || scope.row.isExamine == 0 || scope.row.isExamine == null || scope.row.isRatify == 1 || nickName !== scope.row.ratifyUser"
            @click="handleApprove(scope.row)">批准</el-button>
          <el-popover placement="bottom" trigger="hover" style="margin-left: 6px">
            <template #reference>
              <el-button link type="text" size="small">更多</el-button>
            </template>
            <div>
              <el-button style="margin-left: 10px" type="text" size="small"
                :disabled="!scope.row.url && !scope.row.urlS" @click="download(scope.row)">下载</el-button>
              <el-button type="text" size="small" @click="viewInspectInfo(scope.row)">原始记录</el-button>
              <el-button type="text" size="small" @click="handleFileLook(scope.row)">附件上传</el-button>
            </div>
          </el-popover>
          <el-button style="margin-left: 10px" type="text" size="small"
                     :disabled="!scope.row.url && !scope.row.urlS" @click="download(scope.row)">下载</el-button>
          <el-button type="text" size="small" @click="viewInspectInfo(scope.row)">原始记录</el-button>
          <el-button type="text" size="small" @click="handleFileLook(scope.row)">附件上传</el-button>
        </div>
      </lims-table>
    </div>
src/workers/InspectionWorker.worker.js
@@ -1223,6 +1223,12 @@
          return "断裂脆化";
        }
      }
      // 参数代入后含中文时(如检验值填了"均匀"),不能再当公式 eval ——
      // eval("均匀") 会抛 ReferenceError,被外层 catch 吞掉后返回 undefined,
      // 该单元格的结果既不写回也不向下游公式传递,整条计算链断在这里
      if (/[一-龥]/.test(str)) {
        return str.trim();
      }
      // 计算常规的
      const value = eval(str);
      // 除零等非有限结果不写回单元格(与 Excel 的 #DIV/0! 一致:不出值)