fix: 1.检验保存优化 2.设备选择 3.检验复核合格不合格选择
| | |
| | | return request({ |
| | | url: "/insOrderPlan/verifyPlan", |
| | | method: "post", |
| | | params: data, |
| | | data: data, |
| | | }); |
| | | } |
| | | |
| | |
| | | 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({ |
| | |
| | | <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> |
| | | </div> |
| | | <div class="center"> |
| | |
| | | checkSubmitPlan, |
| | | submitPlan, |
| | | saveInsContext, |
| | | saveTemplateContext, |
| | | getOrderDevices, |
| | | saveOrderDevices, |
| | | selectUserCondition, |
| | | downFile, |
| | | getFileList, |
| | |
| | | comparisonList: [], |
| | | excelMethodList: [], |
| | | equipOptions: [], |
| | | orderDeviceOptions: [], |
| | | selectedDeviceIds: [], |
| | | reviewLoading: false, |
| | | reviewDia: false, |
| | | noReason: "", |
| | |
| | | worker0: null, |
| | | // 检验值按触发顺序串行保存,避免同一检验项的旧请求覆盖新请求。 |
| | | saveQueue: null, |
| | | calculationRevision: 0, |
| | | completedCalculationRevision: 0, |
| | | wareLength: [], |
| | | dataAcquisitionInfo: {}, |
| | | dataAcquisitionInfoNew: {}, |
| | |
| | | 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); |
| | |
| | | } |
| | | } |
| | | try { |
| | | const revision = ++this.calculationRevision; |
| | | // 向 Worker 发送消息,开始处理逻辑 |
| | | this.worker.postMessage( |
| | | JSON.stringify({ |
| | |
| | | getDataTypeId: this.getDataTypeId, |
| | | modelType: this.sampleProduct[0].model, |
| | | currentInsItem: n, |
| | | revision: revision, |
| | | }) |
| | | ); |
| | | // 先保存主线程中已经输入的原始值。Worker 完成计算后会再次入队保存计算结果。 |
| | |
| | | // 监听 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": |
| | |
| | | ); |
| | | this.param = resultValue.param; |
| | | this.saveInsContext(); |
| | | this.completedCalculationRevision = Math.max( |
| | | this.completedCalculationRevision, |
| | | workerResult.revision || 0 |
| | | ); |
| | | }); |
| | | break; |
| | | case "tableList": |
| | |
| | | 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" && |
| | |
| | | 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) |
| | | ) { |
| | |
| | | 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("已保存"); |
| | | }) |
| | |
| | | } |
| | | }, |
| | | 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; |
| | | } |
| | |
| | | // 选择、移除、清空均由 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 = []; |
| | |
| | | @blur="subOtherForm(insOrder.remark, 'remark')"></el-input> |
| | | <!-- <el-tag v-if="currentKey">{{ insOrder.remark }}</el-tag> --> |
| | | </el-form-item> |
| | | <el-form-item v-if="isDirectTemplateOrder()" 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> |
| | | <div class="center"> |
| | |
| | | checkSubmitPlan, |
| | | submitPlan, |
| | | saveInsContext, |
| | | saveTemplateContext, |
| | | selectUserCondition, |
| | | downFile, |
| | | getFileList, |
| | |
| | | ps: {}, |
| | | param: {}, |
| | | saveQueue: null, |
| | | calculationRevision: 0, |
| | | completedCalculationRevision: 0, |
| | | currentKey: 1, |
| | | currentKey0: 1, |
| | | currentKey1: 1, |
| | |
| | | reviewLoading: false, |
| | | reviewDia: false, |
| | | noReason: "", |
| | | reviewInsResult: null, |
| | | tableWidth: 1000, |
| | | currentTable: null, |
| | | tableLists: [], |
| | |
| | | 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) => { |
| | |
| | | } |
| | | } |
| | | try { |
| | | const revision = ++this.calculationRevision; |
| | | // 向 Worker 发送消息,开始处理逻辑 |
| | | this.worker.postMessage( |
| | | JSON.stringify({ |
| | |
| | | getDataTypeId: this.getDataTypeId, |
| | | modelType: this.sampleProduct[0].model, |
| | | currentInsItem: n, |
| | | revision: revision, |
| | | }) |
| | | ); |
| | | // 先保存主线程中已经输入的原始值。Worker 完成计算后会再次入队保存计算结果。 |
| | |
| | | // 监听 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": |
| | |
| | | ); |
| | | this.param = resultValue.param; |
| | | this.saveInsContext(); |
| | | this.completedCalculationRevision = Math.max( |
| | | this.completedCalculationRevision, |
| | | workerResult.revision || 0 |
| | | ); |
| | | }); |
| | | break; |
| | | case "tableList": |
| | |
| | | upInsReview(e) { |
| | | if (e == 1) { |
| | | // 通过 |
| | | if (this.isDirectTemplateOrder() && this.reviewInsResult !== 0 && this.reviewInsResult !== 1) { |
| | | this.$message.error("请选择检验结论"); |
| | | return; |
| | | } |
| | | this.reviewLoading = true; |
| | | verifyPlan({ |
| | | orderId: this.orderId, |
| | |
| | | laboratory: this.sonLaboratory, |
| | | tell: null, |
| | | userId: this.checkUser, |
| | | insResult: this.isDirectTemplateOrder() ? this.reviewInsResult : null, |
| | | }).then((res) => { |
| | | if (res.code === 200) { |
| | | this.$message.success("操作成功"); |
| | |
| | | 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" && |
| | |
| | | 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) |
| | | ) { |
| | |
| | | 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("已保存"); |
| | | }) |
| | |
| | | // 选择、移除、清空均由 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 = []; |
| | |
| | | // 当前这次输入对应的原始检验项。计算过程中 pId 会切换到下游计算项, |
| | | // 保存时必须同时带回原始检验项,否则只会保存计算结果。 |
| | | let sourceInsItemId = null; |
| | | // 当前消息版本。Worker 单线程串行处理消息,供递归公式计算完成后回传给主线程。 |
| | | let currentRevision = null; |
| | | |
| | | /** |
| | | * 生成模板结构签名 |
| | |
| | | self.onmessage = function (event) { |
| | | // 保存主线程传过来的值 |
| | | const data = JSON.parse(event.data); |
| | | currentRevision = data.revision; |
| | | // 模板内容变了(不只是模板id变了)也要刷新表格,否则改完模版后 Worker 仍用旧模板计算并把旧版式回写页面 |
| | | const nextTemplateSig = buildTemplateSig(data.tableList); |
| | | if ( |
| | |
| | | // 赋值传递到主线程的数据,method:saveInsContext表示此消息需要保存数据 |
| | | result = { |
| | | method: "saveInsContext", |
| | | revision: currentRevision, |
| | | value: { |
| | | tableList, // 表格数据 |
| | | param: getParam(), //传给后端的参数 |
| | |
| | | // 赋值多线程传输数据 |
| | | result = { |
| | | method: "tableList", |
| | | revision: currentRevision, |
| | | value: tableList, |
| | | }; |
| | | // 发送主线程数据 |
| | |
| | | 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.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) |
| | | ) { |