已修改9个文件
721 ■■■■■ 文件已修改
src/api/business/inspectionTask.js 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/inspectionReview/index.vue 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/inspectionTask/components/InspectionWord.vue 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/inspectionTask/index.vue 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/inspectionTask/inspection.vue 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/inspectionView/index.vue 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/productOrder/index.vue 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/reportPreparation/index.vue 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/workers/InspectionWorker.worker.js 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/business/inspectionTask.js
@@ -213,7 +213,7 @@
  return request({
    url: "/insOrderPlan/verifyPlan",
    method: "post",
    params: data,
    data: data,
  });
}
@@ -252,6 +252,32 @@
  return saveRequest;
}
export function saveTemplateContext(data) {
  return request({
    url: "/insOrderPlan/saveTemplateContext",
    method: "post",
    data: data,
    // 输入值和 Worker 公式结果会连续保存;由页面保存队列保证顺序,不走通用的 1 秒防重拦截。
    headers: { repeatSubmit: false },
  });
}
export function getOrderDevices(query) {
  return request({
    url: "/insOrderPlan/orderDevices",
    method: "get",
    params: query,
  });
}
export function saveOrderDevices(data) {
  return request({
    url: "/insOrderPlan/orderDevices",
    method: "put",
    data: data,
  });
}
// 附件下载
export function downFile(query) {
  return request({
src/views/business/inspectionReview/index.vue
@@ -6,18 +6,18 @@
          <el-input size="small" placeholder="请输入" clearable v-model="entity.entrustCode"
            @keyup.enter.native="refreshTable()"></el-input>
        </el-form-item>
        <el-form-item label="下单类别" prop="entrustCode">
          <el-select size="small" v-model="entity.typeSource" clearable style="width: 100%;" @change="refreshTable()">
            <el-option v-for="(a, i) in typeSourceList" :key="i" :label="a.label" :value="a.value"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="物料属性" prop="materialProp">
          <el-select clearable size="small" v-model="entity.materialProp" style="width: 100%">
            <el-option v-for="dict in dict.type.material_prop_type" :key="dict.value" :label="dict.label"
                       :value="dict.value">
            </el-option>
          </el-select>
        </el-form-item>
<!--        <el-form-item label="下单类别" prop="entrustCode">-->
<!--          <el-select size="small" v-model="entity.typeSource" clearable style="width: 100%;" @change="refreshTable()">-->
<!--            <el-option v-for="(a, i) in typeSourceList" :key="i" :label="a.label" :value="a.value"></el-option>-->
<!--          </el-select>-->
<!--        </el-form-item>-->
<!--        <el-form-item label="物料属性" prop="materialProp">-->
<!--          <el-select clearable size="small" v-model="entity.materialProp" style="width: 100%">-->
<!--            <el-option v-for="dict in dict.type.material_prop_type" :key="dict.value" :label="dict.label"-->
<!--                       :value="dict.value">-->
<!--            </el-option>-->
<!--          </el-select>-->
<!--        </el-form-item>-->
        <el-form-item>
          <el-button type="primary" size="mini" @click="refreshTable">查询</el-button>
          <el-button size="mini" @click="refresh">重置</el-button>
@@ -96,21 +96,21 @@
          linkMethod: "selectAllByOne",
        },
        { label: "样品名称", prop: "sample", width: "160px" },
        {
          label: '物料属性',
          prop: 'materialProp',
          formatData: (params) => {
            if (!params) return null
            for (let i = 0; i < this.dict.type.material_prop_type.length; i++) {
              const item = this.dict.type.material_prop_type[i]
              if (item.value == params) {
                return item.label
              }
            }
            return null
          }
        },
        // {
        //   label: '物料属性',
        //   prop: 'materialProp',
        //   formatData: (params) => {
        //     if (!params) return null
        //
        //     for (let i = 0; i < this.dict.type.material_prop_type.length; i++) {
        //       const item = this.dict.type.material_prop_type[i]
        //       if (item.value == params) {
        //         return item.label
        //       }
        //     }
        //     return null
        //   }
        // },
        {
          label: "下单类别",
          prop: "typeSource",
@@ -127,25 +127,25 @@
          },
        },
        { label: "样品型号", prop: "sampleModel", width: "120px" },
        {
          label: "紧急程度",
          prop: "type",
          dataType: "tag",
          formatData: (params) => {
            if (this.urgencyLevel.find((m) => m.value == params)) {
              return this.urgencyLevel.find((m) => m.value == params).label;
            } else {
              return null
            }
          },
          formatType: (params) => {
            if (this.urgencyLevel.find((m) => m.value == params)) {
              return this.urgencyLevel.find((m) => m.value == params).type;
            } else {
              return null
            }
          },
        },
        // {
        //   label: "紧急程度",
        //   prop: "type",
        //   dataType: "tag",
        //   formatData: (params) => {
        //     if (this.urgencyLevel.find((m) => m.value == params)) {
        //       return this.urgencyLevel.find((m) => m.value == params).label;
        //     } else {
        //       return null
        //     }
        //   },
        //   formatType: (params) => {
        //     if (this.urgencyLevel.find((m) => m.value == params)) {
        //       return this.urgencyLevel.find((m) => m.value == params).type;
        //     } else {
        //       return null
        //     }
        //   },
        // },
        {
          label: "检验类型",
          prop: "orderType",
src/views/business/inspectionTask/components/InspectionWord.vue
@@ -149,15 +149,6 @@
            </el-select>
          </div>
        </div>
        <div class="body" style="display: flex;padding: 10px;align-items: center;">
          <div class="search_label" style="width: 150px;"><span class="required-span">*</span>报告类型</div>
          <div class="search_input" style="width: 100%;">
            <el-select v-model="reportType" clearable placeholder="请选择" size="small" style="width: 100%;">
              <el-option label="检测报告" :value="0"></el-option>
              <el-option label="检验报告" :value="1"></el-option>
            </el-select>
          </div>
        </div>
        <span slot="footer" class="dialog-footer">
          <el-button @click="addVerifyDia = false">取 消</el-button>
          <el-button :loading="submitLoading" type="primary" @click="submit()">确 定</el-button>
@@ -335,7 +326,6 @@
      widthList: [],
      addVerifyDia: false,
      verifyUser: null,
      reportType: null,
      personList: [],
      enumList: [],
      tableLoading: false,
@@ -1347,10 +1337,6 @@
        this.$message.error("请指定复核人员")
        return
      }
      if (this.reportType === null || this.reportType === '') {
        this.$message.error("请选择报告类型")
        return
      }
      if (!this.otherForm.humidity) {
        this.$message.error("请输入湿度")
        return
@@ -1363,7 +1349,6 @@
      checkSubmitPlan({
        orderId: this.orderId,
        laboratory: this.sonLaboratory,
        reportType: this.reportType,
      }).then(res => {
        if (res.code === 200) {
          if (!res.data || res.data.errorMsg.length == 0) {
@@ -1373,8 +1358,7 @@
              laboratory: this.sonLaboratory,
              verifyUser: this.verifyUser,
              entrustCode: this.insOrder.entrustCode,
              registerInsResults: this.reportType === 1,
              reportType: this.reportType
              registerInsResults: false,
            }).then(res => {
              if (res.code === 200) {
                this.$message.success("操作成功")
@@ -1410,7 +1394,6 @@
                laboratory: this.sonLaboratory,
                verifyUser: this.verifyUser,
                registerInsResults: false,
                reportType: this.reportType
              }).then(res => {
                if (res.code === 200) {
                  this.$message.success("操作成功")
src/views/business/inspectionTask/index.vue
@@ -13,13 +13,13 @@
                         :value="a.value"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="物料属性" prop="materialProp">
            <el-select clearable size="small" v-model="queryParams.materialProp" style="width: 100%">
              <el-option v-for="dict in dict.type.material_prop_type" :key="dict.value" :label="dict.label"
                         :value="dict.value">
              </el-option>
            </el-select>
          </el-form-item>
<!--          <el-form-item label="物料属性" prop="materialProp">-->
<!--            <el-select clearable size="small" v-model="queryParams.materialProp" style="width: 100%">-->
<!--              <el-option v-for="dict in dict.type.material_prop_type" :key="dict.value" :label="dict.label"-->
<!--                         :value="dict.value">-->
<!--              </el-option>-->
<!--            </el-select>-->
<!--          </el-form-item>-->
          <el-form-item>
            <el-button size="mini" type="primary" @click="refreshTable()">查询</el-button>
            <el-button size="mini" @click="refresh()">重置</el-button>
@@ -29,7 +29,7 @@
      <div class="center" v-loading="tableLoading">
        <div class="center-options">
          <div style="display: flex; align-items: center">
            <span style="font-size: 14px">试验室种类:</span>
            <span style="font-size: 14px">试验室:</span>
            <ul class="tab">
              <li v-for="(m, i) in tabList" :key="i" :class="{ active: i == tabIndex }" @click="handleTab(m, i)">
                {{ m.label.replace("试验室", "") }}
@@ -286,8 +286,8 @@
      claimVisible: false,
      tabList: [
        { label: "委托", value: -1 },
        { label: "原材料", value: 1 },
        { label: "外购", value: 0 },
        // { label: "原材料", value: 1 },
        // { label: "外购", value: 0 },
      ],
      active: 1,
      tabIndex: 0,
@@ -360,21 +360,6 @@
        },
        { label: "样品名称", prop: "sample", width: "160px" },
        {
          label: '物料属性',
          prop: 'materialProp',
          formatData: (params) => {
            if (!params) return null
            for (let i = 0; i < this.dict.type.material_prop_type.length; i++) {
              const item = this.dict.type.material_prop_type[i]
              if (item.value == params) {
                return item.label
              }
            }
            return null
          }
        },
        {
          label: "下单类别",
          prop: "typeSource",
          width: "100px",
@@ -389,30 +374,29 @@
            }
          },
        },
        { label: "样品型号", prop: "sampleModel", width: "120px" },
        {
          label: "紧急程度",
          prop: "type",
          dataType: "tag",
          formatData: (params) => {
            if (params == 0) {
              return '普通'
            } else if (params == 1) {
              return '优先'
            } else {
              return '紧急'
            }
          },
          formatType: (params) => {
            if (params == 0) {
              return 'success'
            } else if (params == 1) {
              return 'warning'
            } else {
              return 'danger'
            }
          }
        },
        // {
        //   label: "紧急程度",
        //   prop: "type",
        //   dataType: "tag",
        //   formatData: (params) => {
        //     if (params == 0) {
        //       return '普通'
        //     } else if (params == 1) {
        //       return '优先'
        //     } else {
        //       return '紧急'
        //     }
        //   },
        //   formatType: (params) => {
        //     if (params == 0) {
        //       return 'success'
        //     } else if (params == 1) {
        //       return 'warning'
        //     } else {
        //       return 'danger'
        //     }
        //   }
        // },
        {
          label: "检验类型",
          prop: "orderType",
@@ -823,8 +807,7 @@
          sonLaboratory: this.sonLaboratory,
          state: this.state,
          typeSource: this.typeSource,
          orderId: this.orderId,
          isSplit: row.isSplitOrder
          orderId: this.orderId
        },
      });
    },
src/views/business/inspectionTask/inspection.vue
@@ -92,7 +92,6 @@
          <el-descriptions-item label="零件号">{{insOrder.partNo}}</el-descriptions-item>
          <el-descriptions-item label="零件名称">{{insOrder.partDetail}}</el-descriptions-item>
          <el-descriptions-item label="批次号">{{ insOrder.lotBatchNo }}</el-descriptions-item>
          <el-descriptions-item label="样品型号" >{{currentSample.model}}</el-descriptions-item>
          <el-descriptions-item label="抽检数量">{{insOrder.testQuantity}}</el-descriptions-item>
          <el-descriptions-item label="厂家密度">{{supplierDensity}}</el-descriptions-item>
          <template v-if="insOrder.ifsOrderType && insOrder.ifsOrderType==='02wg'">
@@ -109,8 +108,7 @@
          <el-descriptions-item label="委托编号">{{ insOrder.entrustCode }}</el-descriptions-item>
          <el-descriptions-item label="样品编号">{{ currentSample.sampleCode }}</el-descriptions-item>
          <el-descriptions-item label="样品名称">{{ currentSample.sample }}</el-descriptions-item>
          <el-descriptions-item label="样品数量">{{ sampleProduct.length }}</el-descriptions-item>
          <el-descriptions-item label="样品型号">{{ currentSample.model }}</el-descriptions-item>
          <el-descriptions-item label="样品数量">{{ insOrder.testQuantity }}</el-descriptions-item>
          <el-descriptions-item label="下发时间">{{ insOrder.sendTime }}</el-descriptions-item>
          <el-descriptions-item label="紧急程度">{{ insOrder.typeName }}</el-descriptions-item>
          <el-descriptions-item label="约定时间">{{ insOrder.appointed }}</el-descriptions-item>
@@ -119,6 +117,18 @@
        <el-descriptions-item label="备注">
          <el-input v-model="insOrder.remark" :disabled="state != 1" clearable placeholder="请输入" size="small"
                    @blur="subOtherForm(insOrder.remark, 'remark')"></el-input>
        </el-descriptions-item>
        <el-descriptions-item v-if="isDirectTemplateOrder()" label="设备" :span="2">
          <el-select v-model="selectedDeviceIds" multiple filterable clearable size="small" placeholder="请选择设备"
            :disabled="state != 1" @change="saveOverviewDevices">
            <el-option v-for="device in orderDeviceOptions" :key="device.id" :label="device.label" :value="device.id" />
          </el-select>
        </el-descriptions-item>
        <el-descriptions-item label="检验结论">
          <el-radio-group v-model="reviewInsResult" :disabled="state != 2">
            <el-radio :label="1">合格</el-radio>
            <el-radio :label="0">不合格</el-radio>
          </el-radio-group>
        </el-descriptions-item>
      </el-descriptions>
    </div>
@@ -385,17 +395,6 @@
          </el-select>
        </div>
      </div>
      <div class="body" style="display: flex; padding: 10px; align-items: center">
        <div class="search_label" style="width: 150px">
          <span class="required-span">*</span>报告类型
        </div>
        <div class="search_input" style="width: 100%">
          <el-select v-model="reportType" clearable placeholder="请选择" size="small" style="width: 100%">
            <el-option label="检测报告" :value="0"></el-option>
            <el-option label="检验报告" :value="1"></el-option>
          </el-select>
        </div>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="addVerifyDia = false">取 消</el-button>
        <el-button :loading="submitLoading" type="primary" @click="submit()">确 定</el-button>
@@ -579,6 +578,9 @@
  checkSubmitPlan,
  submitPlan,
  saveInsContext,
  saveTemplateContext,
  getOrderDevices,
  saveOrderDevices,
  selectUserCondition,
  downFile,
  getFileList,
@@ -661,16 +663,18 @@
      comparisonList: [],
      excelMethodList: [],
      equipOptions: [],
      orderDeviceOptions: [],
      selectedDeviceIds: [],
      reviewLoading: false,
      reviewDia: false,
      noReason: "",
      reviewInsResult: null,
      tableWidth: 1000,
      currentTable: null,
      tableLists: [],
      widthList: [],
      addVerifyDia: false,
      verifyUser: null,
      reportType: null,
      personList: [],
      enumList: [],
      tableLoading: false,
@@ -696,6 +700,8 @@
      worker0: null,
      // 检验值按触发顺序串行保存,避免同一检验项的旧请求覆盖新请求。
      saveQueue: null,
      calculationRevision: 0,
      completedCalculationRevision: 0,
      wareLength: [],
      dataAcquisitionInfo: {},
      dataAcquisitionInfoNew: {},
@@ -827,7 +833,6 @@
      },
      tableLoading0: false,
      // 任务切换列表--结束
      isSplit: 0, // 是否是拆单过来的
    };
  },
  // 用于上传文件的信息
@@ -839,7 +844,7 @@
  },
  created() {
    this.saveQueue = Promise.resolve();
    let { sonLaboratory, orderId, state, inspectorList, typeSource,isSplit } =
    let { sonLaboratory, orderId, state, inspectorList, typeSource } =
      this.$route.query;
    this.sonLaboratory = sonLaboratory;
    this.orderId = orderId;
@@ -847,7 +852,6 @@
    this.state = state;
    this.inspectorList = inspectorList;
    this.typeSource = typeSource;
    this.isSplit = isSplit;
  },
  mounted() {
    this.getTypeDicts(); // 获取紧急程度下拉框选项
@@ -1077,6 +1081,7 @@
        laboratory: this.sonLaboratory,
      }).then(async (res) => {
        this.insOrder = res.data.insOrder;
        this.reviewInsResult = this.insOrder.insResult;
        if(res.data.insOrder.ifsOrderType==='02wg'){
          //查询零件属性
          this.getPartProps(res.data.insOrder.ifsInventoryId)
@@ -1109,6 +1114,9 @@
          this.rawMaterialTag = "1";
        }
        this.getEquipOptions(1);
        if (this.sonLaboratory === "新聚") {
          this.loadOverviewDevices();
        }
        // 获取当前样品的检验项
        let list = await this.getCurrentProduct(this.currentSample.id, 0);
        this.currentSample.insProduct = this.HaveJson(list);
@@ -2003,6 +2011,7 @@
        }
      }
      try {
        const revision = ++this.calculationRevision;
        // 向 Worker 发送消息,开始处理逻辑
        this.worker.postMessage(
          JSON.stringify({
@@ -2017,6 +2026,7 @@
            getDataTypeId: this.getDataTypeId,
            modelType: this.sampleProduct[0].model,
            currentInsItem: n,
            revision: revision,
          })
        );
        // 先保存主线程中已经输入的原始值。Worker 完成计算后会再次入队保存计算结果。
@@ -2031,6 +2041,9 @@
      // 监听 Worker 返回的结果
      this.worker.onmessage = (event) => {
        const workerResult = JSON.parse(event.data);
        if (workerResult.revision != null && workerResult.revision < this.calculationRevision) {
          return;
        }
        this.result = workerResult;
        switch (workerResult.method) {
          case "saveInsContext":
@@ -2043,6 +2056,10 @@
              );
              this.param = resultValue.param;
              this.saveInsContext();
              this.completedCalculationRevision = Math.max(
                this.completedCalculationRevision,
                workerResult.revision || 0
              );
            });
            break;
          case "tableList":
@@ -2162,6 +2179,10 @@
    upInsReview(e) {
      if (e == 1) {
        // 通过
        if (this.reviewInsResult !== 0 && this.reviewInsResult !== 1) {
          this.$message.error("请选择检验结论");
          return;
        }
        this.reviewLoading = true;
        verifyPlan({
          orderId: this.orderId,
@@ -2169,6 +2190,7 @@
          laboratory: this.sonLaboratory,
          tell: null,
          userId: this.checkUser,
          insResult: this.reviewInsResult,
        }).then((res) => {
          if (res.code === 200) {
            this.$message.success("操作成功");
@@ -2214,17 +2236,15 @@
      this.$refs.viewManHourDia.showDialog(this.id, 99);
    },
    openAddVerifyDia() {
      this.reportType = null;
      this.addVerifyDia = true;
    },
    confirmSubmit(registerInsResults){
    confirmSubmit(){
      submitPlan({
        orderId: this.orderId,
        laboratory: this.sonLaboratory,
        verifyUser: this.verifyUser,
        entrustCode: this.insOrder.entrustCode,
        registerInsResults: this.reportType === 1 && registerInsResults,
        reportType: this.reportType
        registerInsResults: false,
      }).then((res) => {
        if (res.code === 200) {
          this.$message.success("操作成功");
@@ -2241,10 +2261,6 @@
    async submit() {
      if (this.verifyUser === null || this.verifyUser === "") {
        this.$message.error("请指定复核人员");
        return;
      }
      if (this.reportType === null || this.reportType === "") {
        this.$message.error("请选择报告类型");
        return;
      }
      if (!this.otherForm.humidity && this.insOrder.ifsOrderType!=='02wg') {
@@ -2264,27 +2280,11 @@
      checkSubmitPlan({
        orderId: this.orderId,
        laboratory: this.sonLaboratory,
        reportType: this.reportType,
      }).then((res) => {
        if (res.code === 200) {
          if (!res.data || res.data.errorMsg.length == 0) {
            this.submitLoading = true;
            //检验类型为原材料
            if(this.reportType === 1 && this.typeSource==1 && res.data.unInsOrderCount==0 && this.isSplit==1){
              const htmlStr = "是否登记IFS采购检验结果并移库?该操作会登记<span style='color:#ff4949'>同一零件同一订单的所有拆分批次</span>,请谨慎选择。"
              this.$confirm(htmlStr, '提示', {
                confirmButtonText: '是',
                cancelButtonText: '否',
                type: 'warning',
                dangerouslyUseHTMLString: true
              }).then(() => {
                this.confirmSubmit(true)
              }).catch(() => {
                this.confirmSubmit(false)
              });
            }else{
              this.confirmSubmit(true)
            }
            this.confirmSubmit();
          } else {
            let newData = [];
            const h = this.$createElement;
@@ -2320,7 +2320,7 @@
            })
              .then(() => {
                this.submitLoading = true;
                this.confirmSubmit(false)
                this.confirmSubmit()
                // submitPlan({
                //   orderId: this.orderId,
                //   laboratory: this.sonLaboratory,
@@ -2353,7 +2353,17 @@
      const param = {};
      this.tableList.forEach((a) => {
        param[a.templateId] = {};
        a.template.forEach((cell) => {
        // Worker 回传后 arr 是页面上的最新对象,template 仍可能保留计算前的旧值。
        const cells = a.arr && a.arr.length
          ? a.arr.reduce((all, row) => all.concat(row), [])
          : a.template;
        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));
        cells.forEach((cell) => {
          const isEditable =
            cell.v.ps != undefined &&
            typeof cell.v.ps.value === "string" &&
@@ -2361,10 +2371,12 @@
              cell.v.ps.value === "计算值" ||
              cell.v.ps.value === "最终值" ||
              cell.v.ps.value === "设备名称");
          if (!isEditable) {
          const isExportResult = resultHeader && exportRows.has(cell.r) && cell.c === resultHeader.c;
          if (!isEditable && !isExportResult) {
            return;
          }
          if (
            isEditable &&
            cell.v.ps.value.includes("检验值") &&
            (cell.v.v === "" || cell.v.v === null || cell.v.v === undefined)
          ) {
@@ -2410,7 +2422,14 @@
        const previousSave = this.saveQueue || Promise.resolve();
        this.saveQueue = previousSave
          .catch(() => undefined)
          .then(() => saveInsContext(payload))
          .then(() => this.isDirectTemplateOrder()
            ? saveTemplateContext({
                orderId: this.orderId,
                sampleId: this.currentSample.id,
                templateId: this.currentTable,
                values: param[this.currentTable] || {},
              })
            : saveInsContext(payload))
          .then(() => {
            this.$message.success("已保存");
          })
@@ -2433,6 +2452,20 @@
      }
    },
    async waitForPendingSaves() {
      const targetRevision = this.calculationRevision;
      if (this.completedCalculationRevision < targetRevision) {
        await new Promise((resolve) => {
          const startedAt = Date.now();
          const waitWorker = () => {
            if (this.completedCalculationRevision >= targetRevision || Date.now() - startedAt > 5000) {
              resolve();
              return;
            }
            setTimeout(waitWorker, 20);
          };
          waitWorker();
        });
      }
      if (this.saveQueue) {
        await this.saveQueue;
      }
@@ -2458,6 +2491,23 @@
      // 选择、移除、清空均由 change 事件触发,立即保存。
      this.saveInsContext();
    },
    isDirectTemplateOrder() {
      return this.sonLaboratory === "新聚" && this.currentSample &&
        (this.currentSample.insProduct || []).some((product) => product.templateRowIndex != null);
    },
    loadOverviewDevices() {
      Promise.all([search({ status: 0 }), getOrderDevices({ orderId: this.orderId })]).then(([devices, selected]) => {
        this.orderDeviceOptions = (devices.data || []).map((device) => ({
          id: device.id,
          label: `${device.deviceName}--${device.managementNumber}`,
        }));
        this.selectedDeviceIds = selected.data || [];
      });
    },
    saveOverviewDevices() {
      return saveOrderDevices({ orderId: this.orderId, deviceIds: this.selectedDeviceIds })
        .then(() => this.$message.success("设备已保存"));
    },
    getAuthorizedPerson() {
      selectUserCondition({ type: 1 }).then((res) => {
        let data = [];
src/views/business/inspectionView/index.vue
@@ -45,7 +45,7 @@
          <el-input v-model="currentSample.sample" clearable disabled placeholder="请输入" size="small"></el-input>
        </el-form-item>
        <el-form-item label="样品数量:">
          <el-input v-model="sampleProduct.length" clearable disabled placeholder="请输入" size="small"></el-input>
          <el-input v-model="insOrder.testQuantity" clearable disabled placeholder="请输入" size="small"></el-input>
        </el-form-item>
        <el-form-item label="样品型号:">
          <el-input v-model="currentSample.model" clearable disabled placeholder="请输入" size="small"></el-input>
@@ -73,6 +73,12 @@
          <el-input v-model="insOrder.remark" :disabled="state != 1" clearable placeholder="请输入" size="small"
                    @blur="subOtherForm(insOrder.remark, 'remark')"></el-input>
          <!-- <el-tag v-if="currentKey">{{ insOrder.remark }}</el-tag> -->
        </el-form-item>
        <el-form-item label="检验结论:">
          <el-radio-group v-model="reviewInsResult" :disabled="state != 2">
            <el-radio :label="1">合格</el-radio>
            <el-radio :label="0">不合格</el-radio>
          </el-radio-group>
        </el-form-item>
      </el-form>
    </div>
@@ -205,8 +211,8 @@
                          " />
                    <span v-else-if="getInspectionValueType(n.i) == 4"
                          :style="`font-family:${n.v.ff} !important;`">/</span>
                  </template>
                  <template v-else-if="
                    </template>
                    <template v-else-if="
                      n.v.ps != undefined && n.v.ps.value === '设备名称'
                    ">
                    <el-select v-model="n.v.v" :disabled="state > 1" class="table_input" filterable multiple
@@ -334,17 +340,6 @@
          <el-select v-model="verifyUser" clearable filterable placeholder="请选择" size="small" style="width: 100%">
            <el-option v-for="(item, i) in personList" :key="i" :label="item.label" :value="item.value">
            </el-option>
          </el-select>
        </div>
      </div>
      <div class="body" style="display: flex; padding: 10px; align-items: center">
        <div class="search_label" style="width: 150px">
          <span class="required-span">*</span>报告类型
        </div>
        <div class="search_input" style="width: 100%">
          <el-select v-model="reportType" clearable placeholder="请选择" size="small" style="width: 100%">
            <el-option label="检测报告" :value="0"></el-option>
            <el-option label="检验报告" :value="1"></el-option>
          </el-select>
        </div>
      </div>
@@ -486,6 +481,7 @@
  checkSubmitPlan,
  submitPlan,
  saveInsContext,
  saveTemplateContext,
  selectUserCondition,
  downFile,
  getFileList,
@@ -546,6 +542,8 @@
      ps: {},
      param: {},
      saveQueue: null,
      calculationRevision: 0,
      completedCalculationRevision: 0,
      currentKey: 1,
      currentKey0: 1,
      currentKey1: 1,
@@ -556,13 +554,13 @@
      reviewLoading: false,
      reviewDia: false,
      noReason: "",
      reviewInsResult: null,
      tableWidth: 1000,
      currentTable: null,
      tableLists: [],
      widthList: [],
      addVerifyDia: false,
      verifyUser: null,
      reportType: null,
      personList: [],
      enumList: [],
      tableLoading: false,
@@ -875,13 +873,15 @@
      this.checkUser = "";
    },
    // 刷新页面
    refreshView() {
    async refreshView() {
      await this.waitForPendingSaves();
      this.loading = true;
      doInsOrder({
        id: this.id,
        laboratory: this.sonLaboratory,
      }).then(async (res) => {
        this.insOrder = res.data.insOrder;
        this.reviewInsResult = this.insOrder.insResult;
        this.supplierDensity = res.data.supplierDensity;
        this.getList()
        this.urgentList.forEach((m) => {
@@ -1802,6 +1802,7 @@
        }
      }
      try {
        const revision = ++this.calculationRevision;
        // 向 Worker 发送消息,开始处理逻辑
        this.worker.postMessage(
          JSON.stringify({
@@ -1816,6 +1817,7 @@
            getDataTypeId: this.getDataTypeId,
            modelType: this.sampleProduct[0].model,
            currentInsItem: n,
            revision: revision,
          })
        );
        // 先保存主线程中已经输入的原始值。Worker 完成计算后会再次入队保存计算结果。
@@ -1830,6 +1832,9 @@
      // 监听 Worker 返回的结果
      this.worker.onmessage = (event) => {
        const workerResult = JSON.parse(event.data);
        if (workerResult.revision != null && workerResult.revision < this.calculationRevision) {
          return;
        }
        this.result = workerResult;
        switch (workerResult.method) {
          case "saveInsContext":
@@ -1842,6 +1847,10 @@
              );
              this.param = resultValue.param;
              this.saveInsContext();
              this.completedCalculationRevision = Math.max(
                this.completedCalculationRevision,
                workerResult.revision || 0
              );
            });
            break;
          case "tableList":
@@ -1961,6 +1970,10 @@
    upInsReview(e) {
      if (e == 1) {
        // 通过
        if (this.reviewInsResult !== 0 && this.reviewInsResult !== 1) {
          this.$message.error("请选择检验结论");
          return;
        }
        this.reviewLoading = true;
        verifyPlan({
          orderId: this.orderId,
@@ -1968,6 +1981,7 @@
          laboratory: this.sonLaboratory,
          tell: null,
          userId: this.checkUser,
          insResult: this.reviewInsResult,
        }).then((res) => {
          if (res.code === 200) {
            this.$message.success("操作成功");
@@ -2013,16 +2027,11 @@
      this.$refs.viewManHourDia.showDialog(this.id, 99);
    },
    openAddVerifyDia() {
      this.reportType = null;
      this.addVerifyDia = true;
    },
    submit() {
      if (this.verifyUser === null || this.verifyUser === "") {
        this.$message.error("请指定复核人员");
        return;
      }
      if (this.reportType === null || this.reportType === "") {
        this.$message.error("请选择报告类型");
        return;
      }
      if (!this.otherForm.humidity) {
@@ -2037,7 +2046,6 @@
      checkSubmitPlan({
        orderId: this.orderId,
        laboratory: this.sonLaboratory,
        reportType: this.reportType,
      }).then((res) => {
        if (res.code === 200) {
          if (!res.data || res.data.errorMsg.length == 0) {
@@ -2047,8 +2055,7 @@
              laboratory: this.sonLaboratory,
              verifyUser: this.verifyUser,
              entrustCode: this.insOrder.entrustCode,
              registerInsResults: this.reportType === 1,
              reportType: this.reportType,
              registerInsResults: false,
            }).then((res) => {
              if (res.code === 200) {
                this.$message.success("操作成功");
@@ -2101,7 +2108,6 @@
                  laboratory: this.sonLaboratory,
                  verifyUser: this.verifyUser,
                  registerInsResults: false,
                  reportType: this.reportType,
                }).then((res) => {
                  if (res.code === 200) {
                    this.$message.success("操作成功");
@@ -2130,7 +2136,17 @@
      const param = {};
      this.tableList.forEach((a) => {
        param[a.templateId] = {};
        a.template.forEach((cell) => {
        // Worker 回传后 arr 是页面上的最新对象,template 仍可能保留计算前的旧值。
        const cells = a.arr && a.arr.length
          ? a.arr.reduce((all, row) => all.concat(row), [])
          : a.template;
        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));
        cells.forEach((cell) => {
          const isEditable =
            cell.v.ps != undefined &&
            typeof cell.v.ps.value === "string" &&
@@ -2138,10 +2154,12 @@
              cell.v.ps.value === "计算值" ||
              cell.v.ps.value === "最终值" ||
              cell.v.ps.value === "设备名称");
          if (!isEditable) {
          const isExportResult = resultHeader && exportRows.has(cell.r) && cell.c === resultHeader.c;
          if (!isEditable && !isExportResult) {
            return;
          }
          if (
            isEditable &&
            cell.v.ps.value.includes("检验值") &&
            (cell.v.v === "" || cell.v.v === null || cell.v.v === undefined)
          ) {
@@ -2187,7 +2205,14 @@
        const previousSave = this.saveQueue || Promise.resolve();
        this.saveQueue = previousSave
          .catch(() => undefined)
          .then(() => saveInsContext(payload))
          .then(() => this.isDirectTemplateOrder()
            ? saveTemplateContext({
                orderId: this.orderId,
                sampleId: this.currentSample.id,
                templateId: this.currentTable,
                values: param[this.currentTable] || {},
              })
            : saveInsContext(payload))
          .then(() => {
            this.$message.success("已保存");
          })
@@ -2230,6 +2255,29 @@
      // 选择、移除、清空均由 change 事件触发,立即保存。
      this.saveInsContext();
    },
    isDirectTemplateOrder() {
      return this.sonLaboratory === "新聚" && this.currentSample &&
        (this.currentSample.insProduct || []).some((product) => product.templateRowIndex != null);
    },
    async waitForPendingSaves() {
      const targetRevision = this.calculationRevision;
      if (this.completedCalculationRevision < targetRevision) {
        await new Promise((resolve) => {
          const startedAt = Date.now();
          const waitWorker = () => {
            if (this.completedCalculationRevision >= targetRevision || Date.now() - startedAt > 5000) {
              resolve();
              return;
            }
            setTimeout(waitWorker, 20);
          };
          waitWorker();
        });
      }
      if (this.saveQueue) {
        await this.saveQueue;
      }
    },
    getAuthorizedPerson() {
      selectUserCondition({ type: 1 }).then((res) => {
        let data = [];
src/views/business/productOrder/index.vue
@@ -55,7 +55,6 @@
        <div>
          <el-button v-if="tabIndex === 2 || tabIndex === 3 || tabIndex === 6" size="small"
                     @click="downLoad">导出</el-button>
          <el-button v-if="tabIndex === 1" size="small" type="primary" @click="openAddIns">添加检验项</el-button>
          <el-button size="small" type="primary" @click="openPrint">标签打印</el-button>
          <el-button v-if="checkPermi(['add:insOrder'])" size="small" type="primary" @click="playOrder(1)">下单</el-button>
        </div>
@@ -294,8 +293,6 @@
    <!--标签打印弹框-->
    <print-dialog v-if="printDialog" ref="printDialog" :printDialog="printDialog"
      @closePrintDialog="closePrintDialog"></print-dialog>
    <!--添加遗漏检验项弹框-->
    <add-inspection-dia v-if="addInspectionDia" ref="addInspectionDia"></add-inspection-dia>
    <!--修改样品型号弹框-->
    <el-dialog :close-on-click-modal="false" :close-on-press-escape="false"
               :visible.sync="dialogVisible" title="修改样品型号" width="80%">
@@ -325,7 +322,6 @@
import vueQr from 'vue-qr'
import PrintJS from 'print-js'
import PrintDialog from "@/views/business/productOrder/components/printDialog.vue";
import AddInspectionDia from "@/views/business/productOrder/components/addInspectionDia.vue";
import limsTable from "@/components/Table/lims-table.vue";
import {
  checkUpdate, delInsOrder, getSampleByOrderId,
@@ -344,7 +340,6 @@
  name: 'ProductOrder',
  components: {
    limsTable,
    AddInspectionDia,
    PrintDialog,
    vueQr
  },
@@ -367,75 +362,25 @@
          linkMethod: "changeEntrustCode",
        },
        { label: '委托单位', prop: 'company' },
        {
          label: "样品名称",
          prop: "sampleName",
          width: "160px",
          dataType: "link",
          linkMethod: "selectAllByOne",
        },
        {
          label: "样品型号",
          prop: "sampleModel",
          width: "160px",
          dataType: "link",
          linkMethod: "editSampleModel",
        },
        { label: '样品数量', prop: 'sampleNum' },
        // {
        //   label: "样品名称",
        //   prop: "sampleName",
        //   width: "160px",
        //   dataType: "link",
        //   linkMethod: "selectAllByOne",
        // },
        // {
        //   label: "样品型号",
        //   prop: "sampleModel",
        //   width: "160px",
        //   dataType: "link",
        //   linkMethod: "editSampleModel",
        // },
        { label: '样品数量', prop: 'testQuantity' },
        { label: '检验人', prop: 'testingName' },
        {
          dataType: 'tag',
          label: '紧急程度',
          prop: 'type',
          formatData: (params) => {
            if (params == 0) {
              return '普通'
            } else if (params == 1) {
              return '优先'
            } else {
              return '紧急'
            }
          },
          formatType: (params) => {
            if (params == 0) {
              return 'success'
            } else if (params == 1) {
              return 'warning'
            } else {
              return 'danger'
            }
          }
        },
        { label: '备注', prop: 'remark' },
        { label: '检验进度%', prop: 'insProgress' },
        { label: '约定时间', prop: 'appointed' },
        {
          dataType: 'tag',
          label: '检验结果',
          prop: 'insResult',
          formatData: (params) => {
            if (params == 1) {
              return '合格'
            } else if (params == 0) {
              return '不合格'
            } else if (params == 3) {
              return '不判定'
            } else {
              return null
            }
          },
          formatType: (params) => {
            if (params == 1) {
              return 'success'
            } else if (params == 0) {
              return 'danger'
            } else if (params == 3) {
              return ''
            } else {
              return null
            }
          }
        },
        { label: '下单时间', prop: 'createTime' },
        { label: '下发时间', prop: 'sendTime' },
        { label: '退回理由', prop: 'tell' },
@@ -766,7 +711,6 @@
      qrData: [],
      multipleSelection: [],
      printDialog: false,
      addInspectionDia: false,
      dialogVisible: false,
      submitListLoad: false,
      sampleList: []
@@ -915,21 +859,6 @@
        targetStyles: ["*"], // 使用dom的所有样式,很重要
        font_size: '0.20cm',
      });
    },
    // 打开添加检验项弹框
    openAddIns() {
      if (this.multipleSelection.length === 0) {
        this.$message.warning('请选择需要添加检验项的数据')
        return
      }
      if (this.multipleSelection.length > 1) {
        this.$message.warning('只能选择一条数据')
        return
      }
      this.addInspectionDia = true
      this.$nextTick(() => {
        this.$refs.addInspectionDia.getDataList(this.multipleSelection[0])
      })
    },
    // 导出记录
    downLoad() {
src/views/business/reportPreparation/index.vue
@@ -11,23 +11,23 @@
            <el-option v-for="(a, i) in queryStatusList" :key="i" :label="a.label" :value="a.value"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="报告类型" prop="reportType">
          <el-select v-model="entity.reportType" clearable size="small" @change="refreshTable()">
            <el-option v-for="item in reportTypeList" :key="item.value" :label="item.label" :value="item.value"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="下单类别" prop="typeSource">
          <el-select v-model="entity.typeSource" clearable size="small" @change="refreshTable()">
            <el-option v-for="(a, i) in typeSourceList" :key="i" :label="a.label" :value="a.value"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="物料属性" prop="materialProp">
          <el-select clearable size="small" v-model="entity.materialProp" style="width: 100%">
            <el-option v-for="dict in dict.type.material_prop_type" :key="dict.value" :label="dict.label"
                       :value="dict.value">
            </el-option>
          </el-select>
        </el-form-item>
<!--        <el-form-item label="报告类型" prop="reportType">-->
<!--          <el-select v-model="entity.reportType" clearable size="small" @change="refreshTable()">-->
<!--            <el-option v-for="item in reportTypeList" :key="item.value" :label="item.label" :value="item.value"></el-option>-->
<!--          </el-select>-->
<!--        </el-form-item>-->
<!--        <el-form-item label="下单类别" prop="typeSource">-->
<!--          <el-select v-model="entity.typeSource" clearable size="small" @change="refreshTable()">-->
<!--            <el-option v-for="(a, i) in typeSourceList" :key="i" :label="a.label" :value="a.value"></el-option>-->
<!--          </el-select>-->
<!--        </el-form-item>-->
<!--        <el-form-item label="物料属性" prop="materialProp">-->
<!--          <el-select clearable size="small" v-model="entity.materialProp" style="width: 100%">-->
<!--            <el-option v-for="dict in dict.type.material_prop_type" :key="dict.value" :label="dict.label"-->
<!--                       :value="dict.value">-->
<!--            </el-option>-->
<!--          </el-select>-->
<!--        </el-form-item>-->
        <el-form-item label="检验类别" prop="orderType">
          <el-select v-model="entity.orderType" clearable size="small" @change="refreshTable()">
            <el-option v-for="(a, i) in orderTypeList" :key="i" :label="a.label" :value="a.value"></el-option>
@@ -276,8 +276,8 @@
      orderTypeList: [
        { label: '委托试验', value: 'Customer-ordered test' },
        { label: '抽检', value: '抽检' },
        { label: '进厂检验', value: '进厂检验' },
        { label: '季度检验', value: 'Quarterly inspection' },
        // { label: '进厂检验', value: '进厂检验' },
        // { label: '季度检验', value: 'Quarterly inspection' },
      ],
      reportTypeList: [
        { label: '检测报告', value: 0 },
@@ -366,44 +366,44 @@
          dataType: "link",
          linkMethod: "selectAllByOne",
        },
        {
          label: '报告类型',
          prop: 'reportType',
          width: '100px',
          dataType: 'tag',
          formatData: (params) => params === 0 ? '检测报告' : '检验报告',
          formatType: (params) => params === 0 ? 'warning' : 'success',
        },
        {
          label: "下单类别",
          prop: "typeSource",
          width: "100px",
          dataType: "tag",
          formatData: (params) => {
            if (params == 0) {
              return "外购下单";
            } else if(params==1) {
              return "原材料下单";
            }else{
              return "委托下单";
            }
          },
        },
        {
          label: '物料属性',
          prop: 'materialProp',
          formatData: (params) => {
            if (!params) return null
            for (let i = 0; i < this.dict.type.material_prop_type.length; i++) {
              const item = this.dict.type.material_prop_type[i]
              if (item.value == params) {
                return item.label
              }
            }
            return null
          }
        },
        // {
        //   label: '报告类型',
        //   prop: 'reportType',
        //   width: '100px',
        //   dataType: 'tag',
        //   formatData: (params) => params === 0 ? '检测报告' : '检验报告',
        //   formatType: (params) => params === 0 ? 'warning' : 'success',
        // },
        // {
        //   label: "下单类别",
        //   prop: "typeSource",
        //   width: "100px",
        //   dataType: "tag",
        //   formatData: (params) => {
        //     if (params == 0) {
        //       return "外购下单";
        //     } else if(params==1) {
        //       return "原材料下单";
        //     }else{
        //       return "委托下单";
        //     }
        //   },
        // },
        // {
        //   label: '物料属性',
        //   prop: 'materialProp',
        //   formatData: (params) => {
        //     if (!params) return null
        //
        //     for (let i = 0; i < this.dict.type.material_prop_type.length; i++) {
        //       const item = this.dict.type.material_prop_type[i]
        //       if (item.value == params) {
        //         return item.label
        //       }
        //     }
        //     return null
        //   }
        // },
        {
          label: "检验类型",
          prop: "orderType",
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);
};
@@ -194,7 +201,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]; //当前列
@@ -448,6 +455,7 @@
                    a[b].v.v = comResult;
                    console.log("error---", error);
                  }
                  hasComputedWrite = true;
                  break;
                }
              }
@@ -456,7 +464,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 +474,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 +497,7 @@
  // 赋值多线程传输数据
  result = {
    method: "tableList",
    revision: currentRevision,
    value: tableList,
  };
  // 发送主线程数据
@@ -589,6 +604,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 +620,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)
        ) {