已修改22个文件
1524 ■■■■■ 文件已修改
.env.development 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.production 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.staging 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/index.html 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/cnas/process/method/standardMethod.js 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Onlyoffice/onlyoffice.vue 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Preview/filePreview.vue 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/standardTree.js 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/CNAS/process/method/standardMethod/index.vue 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/inspectionTask/components/InspectionWord.vue 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/inspectionTask/inspection.vue 280 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/inspectionView/index.vue 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/productOrder/components/addOrder.vue 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/productOrder/components/addView.vue 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/business/productOrder/index.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/standard/standardLibrary/index.vue 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/structural/premises/index.vue 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/workers/DataWorker.worker.js 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/workers/InspectionWorker.worker.js 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vue.config.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.development
@@ -10,5 +10,8 @@
# 本地环境ip+端口
VUE_APP_BASE_URL = 'http://127.0.0.1:8001/lims'
# OnlyOffice 文档服务地址(预览 pdf 用,没有 http:// 前缀会当成相对路径)
VUE_APP_ONLYOFFICE_API = 'http://192.168.21.53:9001'
# 路由懒加载
VUE_CLI_BABEL_TRANSPILE_MODULES = true
.env.production
@@ -9,3 +9,6 @@
# 生产环境ip+端口
VUE_APP_BASE_URL = 'http://192.168.21.53:8001/lims'
# OnlyOffice 文档服务地址(预览 pdf 用,没有 http:// 前缀会当成相对路径)
VUE_APP_ONLYOFFICE_API = 'http://192.168.21.53:9001'
.env.staging
@@ -12,7 +12,11 @@
VUE_APP_BASE_API = '/stage-api'
# 测试环境ip+端口
VUE_APP_BASE_URL = http://127.0.0.1:8001/lims
# VUE_APP_BASE_URL = http://127.0.0.1:8001/lims
VUE_APP_BASE_URL = '/stage-api'
# OnlyOffice 文档服务地址(预览 pdf 用,没有 http:// 前缀会当成相对路径)
VUE_APP_ONLYOFFICE_API = 'http://192.168.21.53:9001'
# 运行端口
PORT= '80'
package.json
@@ -48,14 +48,16 @@
    "echarts": "5.4.0",
    "element-resize-detector": "^1.2.4",
    "element-ui": "2.15.14",
    "fast-png": "^5.0.0",
    "file-saver": "2.0.5",
    "fuse.js": "6.4.3",
    "highlight.js": "9.18.5",
    "html2canvas": "^1.4.1",
    "iobuffer": "^5.0.0",
    "js-beautify": "1.13.0",
    "js-cookie": "3.0.1",
    "jsencrypt": "3.0.0-rc.1",
    "jspdf": "^3.0.0",
    "jspdf": "^2.5.0",
    "mammoth": "^1.9.0",
    "moment": "^2.30.1",
    "nprogress": "0.2.0",
public/index.html
@@ -234,9 +234,5 @@
    <script src="<%= BASE_URL %>luckysheet/plugins/js/plugin.js"></script>
    <script src="<%= BASE_URL %>luckysheet/luckysheet.umd.js"></script>
  </body>
  <script
    type="text/javascript"
    src="http://192.168.21.53:9001/web-apps/apps/api/documents/api.js"
  ></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.min.js"></script>
</html>
src/api/cnas/process/method/standardMethod.js
@@ -46,3 +46,25 @@
  });
}
export function selectIndustryOptions() {
  return request({ url: '/standardMethod/selectIndustryOptions', method: 'get' })
}
export function selectAttachments(query) {
  return request({ url: '/standardMethod/selectAttachments', method: 'get', params: query })
}
export function uploadAttachment(data, standardMethodId) {
  return request({
    url: '/standardMethod/uploadAttachment',
    method: 'post',
    params: { standardMethodId },
    data,
    headers: { 'Content-Type': 'multipart/form-data' }
  })
}
export function deleteAttachment(query) {
  return request({ url: '/standardMethod/deleteAttachment', method: 'delete', params: query })
}
src/components/Onlyoffice/onlyoffice.vue
@@ -4,6 +4,37 @@
</template>
<script>
// OnlyOffice 文档服务地址,按环境配置;脚本在真正需要预览时才加载
const OFFICE_API_BASE = process.env.VUE_APP_ONLYOFFICE_API || "http://192.168.21.53:9001";
// 多个页面共用编辑器,api.js 只加载一次
let docsApiPromise = null;
function loadDocsApi() {
  if (window.DocsAPI) {
    return Promise.resolve(window.DocsAPI);
  }
  if (!docsApiPromise) {
    docsApiPromise = new Promise((resolve, reject) => {
      const script = document.createElement("script");
      script.src = OFFICE_API_BASE.replace(/\/$/, "") + "/web-apps/apps/api/documents/api.js";
      script.onload = () => {
        if (window.DocsAPI) {
          resolve(window.DocsAPI);
        } else {
          reject(new Error("api.js 加载完成但未注册 DocsAPI"));
        }
      };
      script.onerror = () => reject(new Error("OnlyOffice 文档服务脚本加载失败:" + script.src));
      document.head.appendChild(script);
    });
    // 加载失败后允许下次重试
    docsApiPromise.catch(() => {
      docsApiPromise = null;
    });
  }
  return docsApiPromise;
}
export default {
  name: "VabOnlyOffice",
  props: ['options'],
@@ -50,10 +81,7 @@
    }
  },
  beforeDestroy() {
    if (this.docEditor !== null) {
      this.docEditor.destroyEditor();
      this.docEditor = null;
    }
    this.destroyEditor();
  },
  watch: {
    option: {
@@ -70,10 +98,17 @@
    }
  },
  methods: {
    async setEditor(option) {
    destroyEditor() {
      if (this.docEditor !== null) {
        this.docEditor.destroyEditor();
        this.docEditor = null;
      }
    },
    async setEditor(option) {
      this.destroyEditor();
      // 没有文档地址就不初始化编辑器,避免访问尚未加载的 DocsAPI
      if (!option || !option.url) {
        return;
      }
      this.doctype = this.getFileType(option.fileType);
      let config = {
@@ -116,8 +151,17 @@
        token: option.token || ""
      };
      // eslint-disable-next-line no-undef,no-unused-vars
      this.docEditor = new DocsAPI.DocEditor("vabOnlyOffice", config);
      try {
        await loadDocsApi();
      } catch (error) {
        console.error(error);
        this.$message.error("文档服务暂时不可用,无法预览");
        return;
      }
      if (this._isDestroyed) {
        return;
      }
      this.docEditor = new window.DocsAPI.DocEditor("vabOnlyOffice", config);
    },
    getFileType(fileType) {
      let docType = "";
src/components/Preview/filePreview.vue
@@ -4,10 +4,10 @@
      <img :src="imgUrl" alt="Image Preview" />
    </div>
    <div v-if="isPdf" style="height: 80vh;">
<!--      <object :data="fileUrl" type="application/pdf" width="100%" height="750px">-->
<!--        <p>您的浏览器不支持 PDF 预览。<a :href="fileUrl" style="color: #3a7bfa;" target="_blank">下载 PDF 文件</a></p>-->
<!--      </object>-->
      <onlyoffice ref="onlyoffice" :options="option" style="width: 100%;height: 100%;" />
      <onlyoffice v-if="useOnlyOffice" ref="onlyoffice" :options="option" style="width: 100%;height: 100%;" />
      <iframe v-else-if="pdfUrl" :src="pdfUrl" title="PDF 预览" style="width: 100%;height: 100%;border: none;"></iframe>
      <p v-else-if="pdfError">PDF 预览失败,<a :href="fileUrl" style="color: #3a7bfa;" target="_blank">点此下载查看</a></p>
      <p v-else>正在加载 PDF…</p>
    </div>
    <div v-if="isDoc">
      <p v-if="!isDocShow">文档无法直接预览,请下载查看。</p>
@@ -53,6 +53,7 @@
</template>
<script>
import axios from 'axios'
import VueOfficeDocx from '@vue-office/docx'
//引入相关样式
import '@vue-office/docx/lib/index.css'
@@ -92,7 +93,9 @@
        transformData: (workbookData) => { return workbookData }, //将获取到的excel数据进行处理之后且渲染到页面之前,可通过transformData对即将渲染的数据及样式进行修改,此时每个单元格的text值就是即将渲染到页面上的内容
      },
      csvList: [],//csv文件数据
      imgUrl: ''
      imgUrl: '',
      pdfUrl: '',       //pdf本地预览地址
      pdfError: false,
    }
  },
  computed: {
@@ -134,9 +137,46 @@
    },
    isSupported() {
      return this.isImage || this.isPdf || this.isDoc || this.isZipOrRar || this.isCsv || this.isXls;
    }
    },
    // 调用方传了 OnlyOffice 的配置才走文档服务,否则用浏览器内置阅读器
    useOnlyOffice() {
      return !!(this.option && this.option.url);
    },
  },
  watch: {
    fileUrl: {
      handler() {
        this.loadPdf();
      },
      immediate: true,
    },
  },
  beforeDestroy() {
    this.releasePdfUrl();
  },
  methods: {
    releasePdfUrl() {
      if (this.pdfUrl) {
        URL.revokeObjectURL(this.pdfUrl);
        this.pdfUrl = "";
      }
    },
    async loadPdf() {
      this.releasePdfUrl();
      this.pdfError = false;
      if (!this.isPdf || this.useOnlyOffice) {
        return;
      }
      try {
        const { data } = await axios.get(this.fileUrl, { responseType: "blob" });
        // 后端返回的 Content-Type 不一定带 application/pdf,这里强制指定,
        // 浏览器才会用内置阅读器打开而不是直接下载
        this.pdfUrl = URL.createObjectURL(new Blob([data], { type: "application/pdf" }));
      } catch (error) {
        console.error(error);
        this.pdfError = true;
      }
    },
    renderedHandler() {
      console.log("渲染完成")
      this.isDocShow = true
src/main.js
@@ -66,7 +66,9 @@
  return JSON.parse(JSON.stringify(val));
};
// Vue.prototype.javaApi = process.env.NODE_ENV === "production"?"http://192.168.21.53:8001/lims":"http://127.0.0.1:8001/lims";
Vue.prototype.javaApi = process.env.VUE_APP_BASE_URL && process.env.NODE_ENV==='production'?process.env.VUE_APP_BASE_URL : "http://127.0.0.1:8001/lims";
// Vue.prototype.javaApi = process.env.VUE_APP_BASE_URL && process.env.NODE_ENV==='production'?process.env.VUE_APP_BASE_URL : "http://127.0.0.1:8001/lims";
Vue.prototype.javaApi =
  process.env.VUE_APP_BASE_URL || process.env.VUE_APP_BASE_API;
Vue.prototype.checkPermi = checkPermi;
Vue.prototype.uploadHeader = {
  Authorization: "Bearer " + getToken(),
src/utils/standardTree.js
@@ -36,7 +36,10 @@
  let cur = node;
  while (cur && cur.parent) {
    if (isLeaf(cur.data)) {
      tokens.push(cur.label, leafMarker);
      const leafValue = cur.data && cur.data.standardMethodId
        ? (cur.data.value || cur.label)
        : cur.label;
      tokens.push(leafValue, leafMarker);
    } else {
      tokens.push(cur.label);
    }
@@ -75,6 +78,14 @@
// 去掉路径末段,用于「当前是叶子但查不到标准」时回退到父级查询
export function dropLastLabel(path) {
  if (!path) return '';
  const idx = path.lastIndexOf(' - ');
  return idx === -1 ? '' : path.slice(0, idx);
  const tokens = path.split(' - ');
  if (tokens.length <= 1) return '';
  // 叶子路径格式为“行业 - null - 标准”或“行业 -  - 标准”。
  // 去掉标准名后还要去掉仅用于标识叶子的占位段,父路径才是真实的行业路径。
  tokens.pop();
  const marker = tokens[tokens.length - 1];
  if (marker === '' || marker === 'null') {
    tokens.pop();
  }
  return tokens.join(' - ');
}
src/views/CNAS/process/method/standardMethod/index.vue
@@ -34,23 +34,17 @@
    <!-- 新增/编辑 -->
    <el-dialog :title="title" :visible.sync="addDlog" width="500px">
      <el-form :model="addForm" ref="addForm" :rules="addRules" label-position="right" label-width="120px">
        <el-form-item label="领域" prop="field">
          <el-input size="small" placeholder="请输入" clearable v-model="addForm.field"></el-input>
        <el-form-item label="行业" prop="industryIds">
          <el-select v-model="addForm.industryIds" multiple filterable clearable collapse-tags placeholder="请选择行业" style="width: 100%">
            <el-option v-for="item in industryOptions" :key="item.id" :label="item.laboratoryName" :value="item.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="标准编号" prop="code">
          <el-input size="small" placeholder="请输入" clearable v-model="addForm.code">
          </el-input>
        </el-form-item>
        <el-form-item label="检验对象" prop="structureTestObjectId">
          <el-cascader size="small" :options="tandardTree" v-model="addForm.structureTestObjectId" collapse-tags
            :props="{ multiple: true, checkStrictly: true }" clearable style="width: 100%"></el-cascader>
        </el-form-item>
        <el-form-item label="标准描述" prop="name">
          <el-input size="small" placeholder="请输入" clearable v-model="addForm.name">
          </el-input>
        </el-form-item>
        <el-form-item label="标准描述EN" prop="nameEn">
          <el-input size="small" placeholder="请输入" clearable v-model="addForm.nameEn">
          </el-input>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
@@ -75,28 +69,48 @@
            <el-option label="是" :value="1"> </el-option>
          </el-select>
        </el-form-item>
        <el-form-item v-if="title === '新增'" label="附件">
          <el-upload ref="addUpload" action="#" :auto-upload="false" multiple :file-list="pendingFiles"
            :on-change="pendingFileChange" :on-remove="pendingFileRemove" :before-upload="beforeFileUpload"
            accept=".jpg,.jpeg,.png,.gif,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip,.rar">
            <el-button size="small" type="primary">选择附件</el-button><div slot="tip" class="el-upload__tip">支持多个附件,单个不超过10MB</div>
          </el-upload>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="addDlog = false">取 消</el-button>
        <el-button :loading="addLoading" type="primary" @click="submitProduct('addForm')">确 认</el-button>
      </span>
    </el-dialog>
    <el-dialog title="标准附件" :visible.sync="attachmentDialog" width="760px">
      <el-upload v-if="currentMethod.id" :action="attachmentAction" :headers="uploadHeader" multiple :show-file-list="false"
        :before-upload="beforeFileUpload" :on-success="attachmentUploadSuccess"
        accept=".jpg,.jpeg,.png,.gif,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.pdf,.zip,.rar">
        <el-button size="small" type="primary">上传附件</el-button>
      </el-upload>
      <el-table :data="attachmentList" border size="small" style="margin-top:12px">
        <el-table-column label="文件名称" prop="fileName" min-width="280" />
        <el-table-column label="上传时间" prop="createTime" width="170" />
        <el-table-column label="操作" width="180"><template slot-scope="scope"><el-button type="text" @click="previewAttachment(scope.row)">预览</el-button><el-button type="text" @click="downloadAttachment(scope.row)">下载</el-button><el-button type="text" style="color:#f56c6c" @click="removeAttachment(scope.row)">删除</el-button></template></el-table-column>
      </el-table>
    </el-dialog>
    <el-dialog title="预览附件" :visible.sync="previewDialog" fullscreen><file-preview v-if="previewDialog" :current-file="currentAttachment" :file-url="previewUrl" /></el-dialog>
  </div>
</template>
<script>
import limsTable from "@/components/Table/lims-table.vue";
import filePreview from "@/components/Preview/filePreview.vue";
import {
  selectStandardMethodList,
  getStandardTree2,
  delStandardMethod,
  addStandardMethod,
  upStandardMethod,
  upStandardMethod, selectIndustryOptions, selectAttachments, uploadAttachment, deleteAttachment,
} from "@/api/cnas/process/method/standardMethod";
export default {
  name: 'StandardMethod',
  components: {
    limsTable,
    limsTable, filePreview,
  },
  data() {
    return {
@@ -106,11 +120,9 @@
      queryParams: {},
      tableData: [],
      column: [
        { label: "领域", prop: "field", width: "100px" },
        { label: "行业", prop: "industryNames", width: "140px" },
        { label: "标准编号", prop: "code", width: "100px" },
        { label: "检验对象", prop: "structureTestObjectId", width: "140px" },
        { label: "标准描述", prop: "name", width: "100px" },
        { label: "标准描述EN", prop: "nameEn", width: "100px" },
        { label: "备注", prop: "remark" },
        { label: "资质", prop: "qualificationId" },
        {
@@ -168,6 +180,15 @@
              },
            },
            {
              name: "上传附件", type: "text", clickFun: (row) => this.openAttachments(row),
            },
            {
              name: "下载附件", type: "text", clickFun: (row) => this.downloadAttachments(row),
            },
            {
              name: "预览附件", type: "text", clickFun: (row) => this.previewAttachments(row),
            },
            {
              name: "删除",
              type: "text",
              clickFun: (row) => {
@@ -187,7 +208,9 @@
      addDlog: false,
      addLoading: false,
      qualificationList: [],
      tandardTree: [],
      industryOptions: [],
      pendingFiles: [], attachmentDialog: false, attachmentList: [], currentMethod: {},
      previewDialog: false, currentAttachment: {}, previewUrl: '',
      addRules: {
        code: [{ required: true, message: "请输入标准编号", trigger: "blur" }],
        name: [{ required: true, message: "请输入标准描述", trigger: "blur" }],
@@ -202,7 +225,7 @@
  },
  mounted() {
    this.getQualificationList();
    this.selectTestObjectByName();
    this.loadIndustryOptions();
    this.getList();
  },
  methods: {
@@ -230,26 +253,25 @@
    refreshTable() {
      this.page.current = 1;
      this.getList();
      this.selectTestObjectByName();
      this.loadIndustryOptions();
    },
    refresh() {
      this.selectTestObjectByName();
      this.loadIndustryOptions();
      this.page.current = 1;
      this.queryParams = {};
      this.getList();
    },
    openAdd() {
      this.title = "新增";
      this.addForm = {};
      this.addForm = { industryIds: [], isProduct: 1, isUse: 1 };
      this.pendingFiles = [];
      this.addDlog = true;
    },
    openEdit(row) {
      this.title = "编辑";
      this.addDlog = true;
      this.addForm = this.HaveJson(row)
      this.addForm.structureTestObjectId = JSON.parse(
        this.addForm.structureTestObjectId
      );
      this.addForm.industryIds = this.addForm.industryIds || [];
    },
    getQualificationList() {
      this.getDicts("cnas_method_qualification").then((response) => {
@@ -262,40 +284,24 @@
        this.qualificationList = arr;
      });
    },
    selectTestObjectByName() {
      getStandardTree2().then((res) => {
        res.data.forEach((a) => {
          this.cascaderFieldData(a);
        });
        this.tandardTree = res.data;
      });
    },
    cascaderFieldData(val) {
      if (val.children === undefined) {
        return;
      } else if (val.children.length == 0) {
        delete val.children;
      } else {
        val.children.forEach((a) => {
          this.cascaderFieldData(a);
        });
      }
    },
    loadIndustryOptions() { selectIndustryOptions().then(res => { this.industryOptions = res.data || []; }); },
    submitProduct(formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          this.addLoading = true;
          let addForm = JSON.parse(JSON.stringify(this.addForm));
          addForm.structureTestObjectId = JSON.stringify(
            addForm.structureTestObjectId
          );
          if (this.title == "新增") {
            addStandardMethod(addForm)
              .then((res) => {
                const id = res.data;
                if (!id) throw new Error('新增标准未返回主键ID');
                return Promise.all(this.pendingFiles.map(item => {
                  const formData = new FormData();
                  formData.append('file', item.raw);
                  return uploadAttachment(formData, id);
                }));
              }).then(() => {
                this.addLoading = false;
                if (res.code != 200) {
                  return;
                }
                this.$message.success("提交成功");
                this.getList();
                this.addDlog = false;
@@ -323,6 +329,31 @@
        }
      });
    },
    beforeFileUpload(file) { if (file.size > 10 * 1024 * 1024) { this.$message.error('上传文件不超过10MB'); return false; } return true; },
    pendingFileChange(file, files) { this.pendingFiles = files; },
    pendingFileRemove(file, files) { this.pendingFiles = files; },
    openAttachments(row) { this.currentMethod = row; this.attachmentDialog = true; this.loadAttachments(); },
    downloadAttachments(row) {
      selectAttachments({ standardMethodId: row.id }).then(res => {
        const attachments = res.data || [];
        if (!attachments.length) this.$message.warning('暂无附件');
        else if (attachments.length === 1) this.downloadAttachment(attachments[0]);
        else this.openAttachments(row);
      });
    },
    previewAttachments(row) {
      selectAttachments({ standardMethodId: row.id }).then(res => {
        const attachments = res.data || [];
        if (!attachments.length) this.$message.warning('暂无附件');
        else if (attachments.length === 1) this.previewAttachment(attachments[0]);
        else this.openAttachments(row);
      });
    },
    loadAttachments() { selectAttachments({ standardMethodId: this.currentMethod.id }).then(res => { this.attachmentList = res.data || []; }); },
    attachmentUploadSuccess(res) { if (res.code === 200) { this.$message.success('上传成功'); this.loadAttachments(); } },
    downloadAttachment(row) { this.$download.saveAs(row.fileUrl, row.fileName); },
    previewAttachment(row) { this.currentAttachment = row; this.previewUrl = /\.(jpg|jpeg|png|gif)$/i.test(row.fileUrl) ? this.javaApi + '/img/' + row.fileUrl : this.javaApi + '/word/' + row.fileUrl; this.previewDialog = true; },
    removeAttachment(row) { this.$confirm('是否删除该附件?', '提示', { type: 'warning' }).then(() => deleteAttachment({ id: row.id })).then(() => this.loadAttachments()); },
    // 删除
    handleDelete(row) {
      this.$confirm("是否删除该条数据?", "提示", {
@@ -339,5 +370,6 @@
        .catch(() => { });
    },
  },
  computed: { attachmentAction() { return this.javaApi + '/standardMethod/uploadAttachment?standardMethodId=' + this.currentMethod.id; } },
};
</script>
src/views/business/inspectionTask/components/InspectionWord.vue
@@ -26,18 +26,45 @@
            </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">
        <div class="template-panel">
          <el-form :inline="true" :model="currentOtherForm" class="form-inline" size="small" label-width="84px">
            <el-form-item label="温度:">
              <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>
              <span class="template-panel__unit">℃</span>
            </el-form-item>
            <el-form-item label="湿度:" style="margin-bottom: 0">
            <el-form-item label="湿度:">
              <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>
              <span class="template-panel__unit">%RH</span>
            </el-form-item>
            <el-form-item label="检测时间:">
              <el-date-picker v-model="currentOtherForm.detectionTime" :disabled="state > 1" type="date"
                value-format="yyyy-MM-dd" placeholder="请选择日期" size="small" style="width: 160px"
                @change="m => subOtherForm(m, 'detectionTime')"></el-date-picker>
            </el-form-item>
            <el-form-item label="检测地点:">
              <el-input v-model="currentOtherForm.detectionPlace" :disabled="state > 1" clearable
                placeholder="请输入检测地点" size="small" style="width: 200px"
                @change="m => subOtherForm(m, 'detectionPlace')"></el-input>
            </el-form-item>
            <el-form-item label="设备:">
              <el-select v-model="currentOtherForm.deviceIds" :disabled="state != 1" multiple filterable clearable
                size="small" placeholder="请选择设备" style="width: 360px"
                @change="m => subOtherForm(m, 'deviceIds')">
                <el-option v-for="device in deviceOptions" :key="device.id" :label="device.label"
                  :value="device.id" />
              </el-select>
            </el-form-item>
          </el-form>
          <el-form :inline="true" :model="currentOtherForm" class="form-inline template-panel__conclusion--divided"
            size="small" label-width="84px">
            <el-form-item label="检验结论:">
              <el-radio-group v-model="currentOtherForm.insResult" :disabled="state != 2"
                @change="m => subOtherForm(m, 'insResult')">
                <el-radio :label="1">合格</el-radio>
                <el-radio :label="0">不合格</el-radio>
              </el-radio-group>
            </el-form-item>
          </el-form>
        </div>
@@ -88,9 +115,9 @@
                      </el-select>
                    </template>
                    <template v-else-if="n.v.ps != undefined && n.v.ps.value === '计算值' && state == 1"><span
                        :style="`font-family:${n.v.ff} !important;`">{{ toFixed(n.v.v, n.v.ct) }}</span></template>
                        :style="`font-family:${n.v.ff} !important;`">{{ n.v.v }}</span></template>
                    <template v-else-if="n.v.ps != undefined && n.v.ps.value === '最终值' && state == 1">
                      <span :style="`font-family:${n.v.ff} !important;`">{{ toFixed(n.v.v, n.v.ct) }}</span>
                      <span :style="`font-family:${n.v.ff} !important;`">{{ n.v.v }}</span>
                    </template>
                    <template v-else-if="n.v.ps != undefined && n.v.ps.value === '样品编号'">
                      <div :title="currentSample.sampleCode"
@@ -356,8 +383,9 @@
      numOptions: [],
      temperatureOptions: [],
      wareTableData: [],
      // 温度、湿度按模板 id 分组:{ [templateId]: { temperature, humidity } }
      // 检测条件按模板 id 分组:{ [templateId]: { temperature, humidity, detectionTime, detectionPlace, deviceIds } }
      otherForm: {},
      deviceOptions: [],
      equipForm: {
        value0: null,
        code0: null,
@@ -433,11 +461,15 @@
    action() {
      return this.javaApi + "/insOrderPlan/uploadFile";
    },
    // 当前模板对应的温度、湿度
    // 当前模板对应的检测条件
    currentOtherForm() {
      return this.otherForm[this.currentTable] || {
        temperature: null,
        humidity: null
        humidity: null,
        detectionTime: null,
        detectionPlace: null,
        deviceIds: [],
        insResult: null
      }
    }
  },
@@ -474,19 +506,24 @@
        this.sampleProduct = res.data.sampleProduct
        this.currentSample = this.HaveJson(this.sampleProduct[0])
        let insProduct = this.HaveJson(this.currentSample.insProduct)
        // 温度、湿度赋值:按模板 id 分组回显
        // 检测条件赋值:按模板 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
            humidity: item.humidity != null ? item.humidity : null,
            detectionTime: item.detectionTime != null ? item.detectionTime : null,
            detectionPlace: item.detectionPlace != null ? item.detectionPlace : null,
            deviceIds: Array.isArray(item.deviceIds) ? [...item.deviceIds] : [],
            insResult: item.insResult != null ? item.insResult : null
          })
        })
        if (this.typeSource == '1') {
          this.retestTag = '1'
        }
        this.getEquipOptions(1)
        this.loadDeviceOptions()
        // 获取当前样品的检验项
        let list = await this.getCurrentProduct(this.currentSample.id, 0)
        if (list === undefined) {
@@ -1205,13 +1242,6 @@
    getValue(v) {
      // 对页面展示数据进行处理,@,代表换行
      let str = v.v ? v.v : (v.v === 0 ? v.v : (v.ct && v.ct.s ? v.ct.s.length > 0 && v.ct.s[0].v.replace(new RegExp('\n', 'g'), '<br/>').replace(new RegExp('@', 'g'), '<br/>') : ''))
      // 对数据保留小数点进行处理
      if (v.ct && v.ct.fa && v.ct.fa.includes('.') && str) {
        let num = 0
        let str0 = v.ct.fa.split('.')[1]
        num = str0.length
        str = Number(str).toFixed(num)
      }
      if (v.v && typeof v.v == 'string' && v.v.includes('@')) {
        str = v.v.replace(new RegExp('@', 'g'), '<br/>')
      }
@@ -1318,6 +1348,16 @@
        })
      }
    },
    loadDeviceOptions() {
      search({ status: 0 }).then(res => {
        this.deviceOptions = (res.data || []).map(device => ({
          id: device.id,
          label: `${device.deviceName}--${device.managementNumber}`
        }))
      }).catch(error => {
        console.error(error)
      })
    },
    getUserInfo() {
      getUserNow().then(res => {
        this.userId = res.data.id
@@ -1327,13 +1367,23 @@
    upInsReview(e) {
      if (e == 1) {
        // 通过
        // 每个模板都要有结论;全部合格才算整单合格
        const insResults = this.tableLists.map(
          item => (this.otherForm[item.templateId] || {}).insResult
        )
        const missingIndex = insResults.findIndex(result => result !== 0 && result !== 1)
        if (missingIndex > -1) {
          this.$message.error(`请选择「${this.tableLists[missingIndex].templateName}」的检验结论`)
          return
        }
        this.reviewLoading = true;
        verifyPlan({
          orderId: this.orderId,
          type: 1,
          laboratory: this.sonLaboratory,
          tell: null,
          userId: this.checkUser
          userId: this.checkUser,
          insResult: insResults.every(result => result === 1) ? 1 : 0
        }).then(res => {
          if (res.code === 200) {
            this.$message.success("操作成功")
@@ -1372,23 +1422,34 @@
        this.$message.error('未输入不通过原因')
      }
    },
    // 保证模板的温度、湿度槽位存在
    // 保证模板的检测条件槽位存在
    ensureOtherForm(templateId) {
      if (templateId == null) return
      if (!this.otherForm[templateId]) {
        this.$set(this.otherForm, templateId, {
          temperature: null,
          humidity: null
          humidity: null,
          detectionTime: null,
          detectionPlace: null,
          deviceIds: [],
          insResult: null
        })
      }
    },
    // 保存当前模板的温度、湿度
    // 保存当前模板的检测条件
    subOtherForm(m, type) {
      write({
      const payload = {
        id: this.insOrder.id,
        templateId: this.currentTable,
        [type]: Number(m)
      }).then(res => {
        templateId: this.currentTable
      }
      if (type === 'deviceIds') {
        payload.deviceIds = Array.isArray(m) ? [...m] : []
      } else if (type === 'detectionTime' || type === 'detectionPlace') {
        payload[type] = m === '' || m == null ? null : m
      } else {
        payload[type] = Number(m)
      }
      write(payload).then(res => {
        this.$message.success("保存成功")
      })
    },
@@ -1397,17 +1458,19 @@
        this.$message.error("请指定复核人员")
        return
      }
      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 (!form.temperature) missing.push(`请填写「${m.templateName}」的温度`)
        if (!form.humidity) missing.push(`请填写「${m.templateName}」的湿度`)
        if (!form.detectionTime) missing.push(`请选择「${m.templateName}」的检测时间`)
        if (!form.detectionPlace) missing.push(`请填写「${m.templateName}」的检测地点`)
        if (!form.deviceIds || form.deviceIds.length === 0) {
          missing.push(`请选择「${m.templateName}」的设备`)
        }
      })
      if (missing.length > 0) {
        this.$message.error(`请填写${missing[0]}`)
        this.$message.error(missing[0])
        return
      }
      this.submitLoading = true;
@@ -1669,26 +1732,6 @@
      }).catch(error => {
      })
    },
    /**
     * 将数值v保留ct.fa中'##'后的指定小数位数,并返回格式化后的字符串。
     *
     * @param v 要格式化的数值
     * @param ct 包含格式化配置的对象
     * @param ct.fa 格式化配置字符串,若包含'##'则按照其后的内容确定小数位数
     * @returns 格式化后的字符串或原始数值(若配置不符合要求)
     */
    toFixed(v, ct) {
      if (v && ct && ct.fa) {
        if (ct.fa.includes('.')) {
          let num = ct.fa.slice(4).length
          return Number(v).toFixed(num)
        } else {
          return v
        }
      } else {
        return v
      }
    }
  }
}
@@ -1737,6 +1780,37 @@
  text-align: left;
}
/* 当前检验模板的检测条件 */
.template-panel {
  background-color: #f8f9fb;
  border: 1px solid #ebeef5;
  border-radius: 3px;
  padding: 16px 16px 4px;
  margin-top: 12px;
  text-align: left;
}
/* form-item 用 inline-flex 保证 label 与控件同行,不依赖行盒宽度推算 */
.template-panel>>>.el-form-item {
  display: inline-flex;
  align-items: center;
  margin-bottom: 12px;
}
.template-panel>>>.el-form-item__label {
  flex: none;
}
.template-panel__unit {
  margin-left: 4px;
  color: #606266;
}
.template-panel__conclusion--divided {
  border-top: 1px dashed #e4e7ed;
  padding-top: 12px;
}
.center {
  width: calc(100% - 40px);
  /* max-height: 580px; */
src/views/business/inspectionTask/inspection.vue
@@ -1,22 +1,7 @@
<template>
  <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="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="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="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">%RH</span>
          </el-form-item>
        </el-form>
      </el-col>
      <el-col :span="insOrder.ifsOrderType!=='02wg'?16:24" style="text-align: right">
      <el-col :span="24" style="text-align: right">
        <el-button v-if="insOrder.ifsOrderType && insOrder.ifsOrderType==='02wg'" size="small" type="primary" @click="showMaterialPropsDialog">IFS物料属性更新</el-button>
        <el-button size="small" type="primary" @click="refreshView">刷新</el-button>
        <el-button v-if="typeSource == 1" size="small" type="primary" @click="openPurchase">进货验证</el-button>
@@ -106,8 +91,9 @@
        </template>
        <template v-else>
          <el-descriptions-item label="委托编号">{{ insOrder.entrustCode }}</el-descriptions-item>
          <el-descriptions-item label="检测依据">{{ standardDisplayName(insOrder.sample) }}</el-descriptions-item>
          <el-descriptions-item label="样品名称">{{ insOrder.sampleView || '-' }}</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="样品数量">{{ insOrder.testQuantity }}</el-descriptions-item>
          <el-descriptions-item label="下发时间">{{ insOrder.sendTime }}</el-descriptions-item>
          <el-descriptions-item label="紧急程度">{{ insOrder.typeName }}</el-descriptions-item>
@@ -117,18 +103,6 @@
        <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>
@@ -200,6 +174,49 @@
            @click="dataAcquisitionEidtAble = !dataAcquisitionEidtAble">{{ dataAcquisitionEidtAble ? "关闭编辑" : "编辑数采"
            }}</el-button>
        </div>
      </div>
      <!-- 当前检验模板的检测条件与检验结论 -->
      <div class="template-panel">
        <el-form :inline="true" :model="currentOtherForm" class="form-inline"
          size="small" label-width="84px">
          <el-form-item v-if="insOrder.ifsOrderType!=='02wg'" label="温度:">
            <el-input v-model="currentOtherForm.temperature" :disabled="state > 1" placeholder="" size="small"
              style="width: 90px" @change="(m) => subOtherForm(m, 'temperature')"></el-input>
            <span class="template-panel__unit">℃</span>
          </el-form-item>
          <el-form-item v-if="insOrder.ifsOrderType!=='02wg'" label="湿度:">
            <el-input v-model="currentOtherForm.humidity" :disabled="state > 1" placeholder="" size="small"
              style="width: 90px" @change="(m) => subOtherForm(m, 'humidity')"></el-input>
            <span class="template-panel__unit">%RH</span>
          </el-form-item>
          <el-form-item label="检测时间:">
            <el-date-picker v-model="currentOtherForm.detectionTime" :disabled="state > 1" type="date"
              value-format="yyyy-MM-dd" placeholder="请选择日期" size="small" style="width: 160px"
              @change="(m) => subOtherForm(m, 'detectionTime')"></el-date-picker>
          </el-form-item>
          <el-form-item label="检测地点:">
            <el-input v-model="currentOtherForm.detectionPlace" :disabled="state > 1" clearable placeholder="请输入检测地点"
              size="small" style="width: 200px" @change="(m) => subOtherForm(m, 'detectionPlace')"></el-input>
          </el-form-item>
          <el-form-item label="设备:">
            <el-select v-model="currentOtherForm.deviceIds" :disabled="state != 1" multiple filterable clearable
              size="small" placeholder="请选择设备" style="width: 360px"
              @change="(m) => subOtherForm(m, 'deviceIds')">
              <el-option v-for="device in orderDeviceOptions" :key="device.id" :label="device.label" :value="device.id" />
            </el-select>
          </el-form-item>
        </el-form>
        <!-- 复核时才能选 -->
        <el-form :inline="true" :model="currentOtherForm" class="form-inline template-panel__conclusion--divided"
          size="small" label-width="84px">
          <el-form-item label="检验结论:">
            <el-radio-group v-model="currentOtherForm.insResult" :disabled="state != 2"
              @change="(m) => subOtherForm(m, 'insResult')">
              <el-radio :label="1">合格</el-radio>
              <el-radio :label="0">不合格</el-radio>
            </el-radio-group>
          </el-form-item>
        </el-form>
      </div>
      <!-- 常规检验原始记录 -->
      <div id="nav" v-loading="tableLoading" class="center-box">
@@ -281,7 +298,7 @@
                      n.v.ps.value === '计算值' &&
                      state == 1
                    "><span :style="`font-family:${n.v.ff} !important;`">{{
                      toFixed(n.v.v, n.v.ct)
                      n.v.v
                    }}</span></template>
                    <template v-else-if="
                      n.v.ps != undefined &&
@@ -289,7 +306,7 @@
                      state == 1
                    ">
                      <span :style="`font-family:${n.v.ff} !important;`">{{
                        toFixed(n.v.v, n.v.ct)
                        n.v.v
                      }}</span>
                    </template>
                    <template v-else-if="
@@ -583,8 +600,6 @@
  submitPlan,
  saveInsContext,
  saveTemplateContext,
  getOrderDevices,
  saveOrderDevices,
  selectUserCondition,
  downFile,
  getFileList,
@@ -668,11 +683,9 @@
      excelMethodList: [],
      equipOptions: [],
      orderDeviceOptions: [],
      selectedDeviceIds: [],
      reviewLoading: false,
      reviewDia: false,
      noReason: "",
      reviewInsResult: null,
      tableWidth: 1000,
      currentTable: null,
      tableLists: [],
@@ -689,7 +702,7 @@
        inspectionItem: 1,
        inspectionItemSubclass: "20(常温)",
      },
      // 温度、湿度按模板 id 分组:{ [templateId]: { temperature, humidity } }
      // 检测条件与检验结论按模板 id 分组:{ [templateId]: { temperature, humidity, detectionTime, detectionPlace, deviceIds, insResult } }
      otherForm: {},
      equipForm: {
        value0: null,
@@ -843,12 +856,16 @@
    action() {
      return this.javaApi + "/insOrderPlan/uploadFile";
    },
    // 当前模板对应的温度、湿度
    // 当前模板对应的检测条件与检验结论
    currentOtherForm() {
      return (
        this.otherForm[this.currentTable] || {
          temperature: null,
          humidity: null,
          detectionTime: null,
          detectionPlace: null,
          deviceIds: [],
          insResult: null,
        }
      );
    },
@@ -926,7 +943,7 @@
              this.tableList = this.tableLists.filter(
                (m) => m.templateId == val1
              );
              // 模板集合可能变化,补齐温度、湿度槽位
              // 模板集合可能变化,补齐检测条件槽位
              this.tableLists.forEach((m) => this.ensureOtherForm(m.templateId));
              // 对模板进行处理
              this.handleTableData();
@@ -946,6 +963,11 @@
    this.stopWorker();
  },
  methods: {
    standardDisplayName(value) {
      if (!value) return '-';
      const separatorIndex = value.indexOf(' - ');
      return separatorIndex > -1 ? value.slice(0, separatorIndex) : value;
    },
    validateQuality(){
      let inboundLength = Number(this.calcInbondLength(this.ifsMaterialPropsForm.startMeterMark,this.ifsMaterialPropsForm.endMeterMark));
      let testQuantity = Number(this.insOrder.testQuantity);
@@ -1094,7 +1116,6 @@
        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)
@@ -1115,12 +1136,16 @@
        this.sampleProduct = res.data.sampleProduct;
        this.currentSample = this.HaveJson(this.sampleProduct[0]);
        let insProduct = this.HaveJson(this.currentSample.insProduct);
        // 温度、湿度赋值:按模板 id 分组回显
        // 检测条件与检验结论赋值:按模板 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,
            detectionTime: item.detectionTime != null ? item.detectionTime : null,
            detectionPlace: item.detectionPlace != null ? item.detectionPlace : null,
            deviceIds: Array.isArray(item.deviceIds) ? [...item.deviceIds] : [],
            insResult: item.insResult != null ? item.insResult : null,
          });
        });
        this.tableLists.forEach((m) => this.ensureOtherForm(m.templateId));
@@ -1129,9 +1154,7 @@
          this.rawMaterialTag = "1";
        }
        this.getEquipOptions(1);
        if (this.sonLaboratory === "新聚") {
          this.loadOverviewDevices();
        }
        this.loadDeviceOptions();
        // 获取当前样品的检验项
        let list = await this.getCurrentProduct(this.currentSample.id, 0);
        this.currentSample.insProduct = this.HaveJson(list);
@@ -1629,28 +1652,46 @@
        this.worker0 = null;
      }
    },
    // 保证模板的温度、湿度槽位存在
    // 保证模板的检测条件与检验结论槽位存在
    ensureOtherForm(templateId) {
      if (templateId == null) return;
      if (!this.otherForm[templateId]) {
        this.$set(this.otherForm, templateId, {
          temperature: null,
          humidity: null,
          detectionTime: null,
          detectionPlace: null,
          deviceIds: [],
          insResult: null,
        });
      }
    },
    // 保存当前模板的温度、湿度;备注是单据级,不带模板
    // 保存当前模板的检测条件与检验结论;备注是单据级,不带模板
    subOtherForm(m, type) {
      const payload = { id: this.insOrder.id };
      if (type === "remark") {
        payload.remark = m;
      } else {
        payload.templateId = this.currentTable;
        payload[type] = Number(m);
        if (type === "deviceIds") {
          payload.deviceIds = Array.isArray(m) ? [...m] : [];
        } else if (type === "detectionTime" || type === "detectionPlace") {
          payload[type] = m === "" || m == null ? null : m;
        } else {
          payload[type] = Number(m);
        }
      }
      write(payload).then((res) => {
        this.$message.success("保存成功");
      });
      const previousSave = this.saveQueue || Promise.resolve();
      this.saveQueue = previousSave
        .catch(() => undefined)
        .then(() => write(payload))
        .then(() => {
          this.$message.success("保存成功");
        })
        .catch((error) => {
          console.error("检验条件保存失败", error);
          this.$message.error("检验条件保存失败,请重试");
        });
    },
    exportTable(tableId) {
      // 获取table元素
@@ -1815,7 +1856,7 @@
        this.tableList = null;
        this.tableList = [this.tableLists[0]];
        this.currentTable = this.tableLists[0].templateId;
        // 补齐每个模板的温度、湿度槽位
        // 补齐每个模板的检测条件槽位
        this.tableLists.forEach((m) => this.ensureOtherForm(m.templateId));
        // 处理页面列表数据
        this.handleTableData();
@@ -2125,13 +2166,6 @@
              .replace(new RegExp("\n", "g"), "<br/>")
              .replace(new RegExp("@", "g"), "<br/>")
            : "";
      // 对数据保留小数点进行处理
      if (v.ct && v.ct.fa && v.ct.fa.includes(".") && str) {
        let num = 0;
        let str0 = v.ct.fa.split(".")[1];
        num = str0.length;
        str = Number(str).toFixed(num);
      }
      if (v.v && typeof v.v == "string" && v.v.includes("@")) {
        str = v.v.replace(new RegExp("@", "g"), "<br/>");
      }
@@ -2162,6 +2196,11 @@
    },
    // 对输入值进行格式校验
    handleInput(n) {
      // 直绑模板未配置检验值类型时按自由文本处理,允许录入外观等中文结果。
      const inspectionValueType = this.getInspectionValueType(n.i);
      if (inspectionValueType === undefined || inspectionValueType === null) {
        return;
      }
      try {
        const v = n.v.v;
        // 能力范围已废弃,检验值类型不再作为判断依据。整串只由算式字符组成时才规范化格式,
@@ -2214,8 +2253,15 @@
    upInsReview(e) {
      if (e == 1) {
        // 通过
        if (this.reviewInsResult !== 0 && this.reviewInsResult !== 1) {
          this.$message.error("请选择检验结论");
        // 每个模板都要有结论;全部合格才算整单合格
        const insResults = this.tableLists.map(
          (m) => (this.otherForm[m.templateId] || {}).insResult
        );
        const missingIndex = insResults.findIndex((r) => r !== 0 && r !== 1);
        if (missingIndex > -1) {
          this.$message.error(
            `请选择「${this.tableLists[missingIndex].templateName}」的检验结论`
          );
          return;
        }
        this.reviewLoading = true;
@@ -2225,7 +2271,7 @@
          laboratory: this.sonLaboratory,
          tell: null,
          userId: this.checkUser,
          insResult: this.reviewInsResult,
          insResult: insResults.every((r) => r === 1) ? 1 : 0,
        }).then((res) => {
          if (res.code === 200) {
            this.$message.success("操作成功");
@@ -2298,29 +2344,46 @@
        this.$message.error("请指定复核人员");
        return;
      }
      if (this.isDirectTemplateOrder() && (!this.selectedDeviceIds || this.selectedDeviceIds.length === 0)) {
        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;
      const missing = [];
      this.tableLists.forEach((m) => {
        const form = this.otherForm[m.templateId] || {};
        if (this.insOrder.ifsOrderType !== "02wg") {
          if (!form.temperature) missing.push(`请填写「${m.templateName}」的温度`);
          if (!form.humidity) missing.push(`请填写「${m.templateName}」的湿度`);
        }
        if (!form.detectionTime) missing.push(`请选择「${m.templateName}」的检测时间`);
        if (!form.detectionPlace) missing.push(`请填写「${m.templateName}」的检测地点`);
        if (!form.deviceIds || form.deviceIds.length === 0) {
          missing.push(`请选择「${m.templateName}」的设备`);
        }
      });
      if (missing.length > 0) {
        this.$message.error(missing[0]);
        return;
      }
      if (!this.insOrder.lotPartId && this.insOrder.ifsOrderType==='02wg') {
        this.$message.error("请填写IFS库存物料批次属性");
        return;
      }
      await this.waitForPendingSaves();
      try {
        await Promise.all(this.tableLists.map((template) => {
          const form = this.otherForm[template.templateId] || {};
          return write({
            id: this.insOrder.id,
            templateId: template.templateId,
            temperature: form.temperature == null ? null : form.temperature,
            humidity: form.humidity == null ? null : form.humidity,
            detectionTime: form.detectionTime || null,
            detectionPlace: form.detectionPlace || null,
            deviceIds: Array.isArray(form.deviceIds) ? [...form.deviceIds] : [],
          });
        }));
      } catch (error) {
        console.error("检验条件保存失败", error);
        this.$message.error("检验条件保存失败,请重试");
        return;
      }
      this.submitLoading = true;
      checkSubmitPlan({
        orderId: this.orderId,
@@ -2537,21 +2600,17 @@
      this.saveInsContext();
    },
    isDirectTemplateOrder() {
      return this.sonLaboratory === "新聚" && this.currentSample &&
        (this.currentSample.insProduct || []).some((product) => product.templateRowIndex != null);
      return this.sonLaboratory === "新聚" &&
        Array.isArray(this.insOrder.templateConditions) &&
        this.insOrder.templateConditions.length > 0;
    },
    loadOverviewDevices() {
      Promise.all([search({ status: 0 }), getOrderDevices({ orderId: this.orderId })]).then(([devices, selected]) => {
    loadDeviceOptions() {
      search({ status: 0 }).then((devices) => {
        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) => {
@@ -2651,26 +2710,6 @@
      })
        .catch((error) => { });
    },
    /**
     * 将数值v保留ct.fa中'##'后的指定小数位数,并返回格式化后的字符串。
     *
     * @param v 要格式化的数值
     * @param ct 包含格式化配置的对象
     * @param ct.fa 格式化配置字符串,若包含'##'则按照其后的内容确定小数位数
     * @returns 格式化后的字符串或原始数值(若配置不符合要求)
     */
    toFixed(v, ct) {
      if (v && ct && ct.fa) {
        if (ct.fa.includes(".")) {
          let num = ct.fa.slice(4).length;
          return Number(v).toFixed(num);
        } else {
          return v;
        }
      } else {
        return v;
      }
    },
    async goback() {
      await this.waitForPendingSaves();
      this.$router.go(-1)
@@ -2724,6 +2763,37 @@
  text-align: left;
}
/* 当前检验模板的检测条件与检验结论 */
.template-panel {
  background-color: #f8f9fb;
  border: 1px solid #ebeef5;
  border-radius: 3px;
  padding: 16px 16px 4px;
  margin-bottom: 12px;
  text-align: left;
}
/* form-item 用 inline-flex 保证 label 与控件同行,不依赖行盒宽度推算 */
.template-panel>>>.el-form-item {
  display: inline-flex;
  align-items: center;
  margin-bottom: 12px;
}
.template-panel>>>.el-form-item__label {
  flex: none;
}
.template-panel__unit {
  margin-left: 4px;
  color: #606266;
}
.template-panel__conclusion--divided {
  border-top: 1px dashed #e4e7ed;
  padding-top: 12px;
}
.center {
  width: 100%;
  /* max-height: 580px; */
src/views/business/inspectionView/index.vue
@@ -1,22 +1,7 @@
<template>
  <div v-loading="loading" class="inspection" style="background-color: rgb(245, 247, 251);">
    <el-row class="title">
      <el-col :span="8" style="text-align: left">
        <el-form :inline="true" :model="otherForm" 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"
                      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"
                      style="width: 90px; line-height: 60px" @change="(m) => subOtherForm(m, 'humidity')"></el-input>
            <span style="margin-left: 4px">%</span>
          </el-form-item>
        </el-form>
      </el-col>
      <el-col :span="16" style="text-align: right">
      <el-col :span="24" style="text-align: right">
        <el-button size="small" type="primary" @click="refreshView">刷新</el-button>
        <el-button v-if="typeSource == 1" size="small" type="primary" @click="openPurchase">进货验证</el-button>
        <el-button v-if="state == 1 && typeSource == 1" size="small" type="primary"
@@ -36,13 +21,18 @@
        <el-form-item label="委托编号:">
          <el-input v-model="insOrder.entrustCode" clearable disabled placeholder="请输入" size="small"></el-input>
        </el-form-item>
        <el-form-item label="检测依据:">
          <el-tooltip :content="standardDisplayName(insOrder.sample)" :disabled="!insOrder.sample">
            <el-input :value="standardDisplayName(insOrder.sample)" clearable disabled size="small"></el-input>
          </el-tooltip>
        </el-form-item>
        <el-form-item label="样品名称:">
          <el-input :value="insOrder.sampleView || '-'" clearable disabled size="small"></el-input>
        </el-form-item>
        <el-form-item label="样品编号:">
          <el-tooltip :content="currentSample.sampleCode" :disabled="!currentSample.sampleCode">
            <el-input v-model="currentSample.sampleCode" clearable disabled placeholder="请输入" size="small"></el-input>
          </el-tooltip>
        </el-form-item>
        <el-form-item label="样品名称:">
          <el-input v-model="currentSample.sample" clearable disabled placeholder="请输入" size="small"></el-input>
        </el-form-item>
        <el-form-item label="样品数量:">
          <el-input v-model="insOrder.testQuantity" clearable disabled placeholder="请输入" size="small"></el-input>
@@ -73,12 +63,6 @@
          <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>
@@ -150,6 +134,50 @@
                     @click="dataAcquisitionEidtAble = !dataAcquisitionEidtAble">{{ dataAcquisitionEidtAble ? "关闭编辑" : "编辑数采"
            }}</el-button>
        </div>
      </div>
      <!-- 当前检验模板的检测条件与检验结论 -->
      <div class="template-panel">
        <el-form :inline="true" :model="currentOtherForm" class="form-inline"
                 size="small" label-width="84px">
          <el-form-item v-if="insOrder.ifsOrderType !== '02wg'" label="温度:">
            <el-input v-model="currentOtherForm.temperature" :disabled="state > 1" size="small"
                      style="width: 90px" @change="m => subOtherForm(m, 'temperature')" />
            <span class="template-panel__unit">℃</span>
          </el-form-item>
          <el-form-item v-if="insOrder.ifsOrderType !== '02wg'" label="湿度:">
            <el-input v-model="currentOtherForm.humidity" :disabled="state > 1" size="small"
                      style="width: 90px" @change="m => subOtherForm(m, 'humidity')" />
            <span class="template-panel__unit">%RH</span>
          </el-form-item>
          <el-form-item label="检测时间:">
            <el-date-picker v-model="currentOtherForm.detectionTime" :disabled="state > 1" type="date"
                            value-format="yyyy-MM-dd" placeholder="请选择日期" size="small" style="width: 160px"
                            @change="m => subOtherForm(m, 'detectionTime')" />
          </el-form-item>
          <el-form-item label="检测地点:">
            <el-input v-model="currentOtherForm.detectionPlace" :disabled="state > 1" clearable
                      placeholder="请输入检测地点" size="small" style="width: 200px"
                      @change="m => subOtherForm(m, 'detectionPlace')" />
          </el-form-item>
          <el-form-item label="设备:">
            <el-select v-model="currentOtherForm.deviceIds" :disabled="state != 1" multiple filterable clearable
                       placeholder="请选择设备" size="small" style="width: 360px"
                       @change="m => subOtherForm(m, 'deviceIds')">
              <el-option v-for="device in orderDeviceOptions" :key="device.id" :label="device.label"
                         :value="device.id" />
            </el-select>
          </el-form-item>
        </el-form>
        <el-form :inline="true" :model="currentOtherForm" class="form-inline template-panel__conclusion--divided"
                 size="small" label-width="84px">
          <el-form-item label="检验结论:">
            <el-radio-group v-model="currentOtherForm.insResult" :disabled="state != 2"
                            @change="m => subOtherForm(m, 'insResult')">
              <el-radio :label="1">合格</el-radio>
              <el-radio :label="0">不合格</el-radio>
            </el-radio-group>
          </el-form-item>
        </el-form>
      </div>
      <!-- 常规检验原始记录 -->
      <div id="nav" v-loading="tableLoading" class="center-box">
@@ -229,7 +257,7 @@
                      n.v.ps.value === '计算值' &&
                      state == 1
                    "><span :style="`font-family:${n.v.ff} !important;`">{{
                      toFixed(n.v.v, n.v.ct)
                      n.v.v
                    }}</span></template>
                  <template v-else-if="
                      n.v.ps != undefined &&
@@ -237,7 +265,7 @@
                      state == 1
                    ">
                      <span :style="`font-family:${n.v.ff} !important;`">{{
                          toFixed(n.v.v, n.v.ct)
                          n.v.v
                        }}</span>
                  </template>
                  <template v-else-if="
@@ -551,10 +579,10 @@
      comparisonList: [],
      excelMethodList: [],
      equipOptions: [],
      orderDeviceOptions: [],
      reviewLoading: false,
      reviewDia: false,
      noReason: "",
      reviewInsResult: null,
      tableWidth: 1000,
      currentTable: null,
      tableLists: [],
@@ -571,10 +599,8 @@
        inspectionItem: 1,
        inspectionItemSubclass: "20(常温)",
      },
      otherForm: {
        humidity: null,
        temperature: null,
      },
      // 检测条件与检验结论按模板 ID 分组
      otherForm: {},
      equipForm: {
        value0: null,
        code0: null,
@@ -724,8 +750,19 @@
    action() {
      return this.javaApi + "/insOrderPlan/uploadFile";
    },
    currentOtherForm() {
      return this.otherForm[this.currentTable] || {
        temperature: null,
        humidity: null,
        detectionTime: null,
        detectionPlace: null,
        deviceIds: [],
        insResult: null,
      };
    },
  },
  created() {
    this.saveQueue = Promise.resolve();
    let { sonLaboratory, orderId, state, inspectorList, typeSource } =
      this.$route.query;
    this.sonLaboratory = sonLaboratory;
@@ -881,7 +918,6 @@
        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) => {
@@ -898,18 +934,24 @@
        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,
        };
        // 按模板回显检测条件与检验结论
        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,
            detectionTime: item.detectionTime != null ? item.detectionTime : null,
            detectionPlace: item.detectionPlace != null ? item.detectionPlace : null,
            deviceIds: Array.isArray(item.deviceIds) ? [...item.deviceIds] : [],
            insResult: item.insResult != null ? item.insResult : null,
          });
        });
        if (this.typeSource == "1") {
          this.getRawMaterialTag(this.currentSample.id); // 原材料的检验任务查询批数
          this.rawMaterialTag = "1";
        }
        this.getEquipOptions(1);
        this.loadDeviceOptions();
        // 获取当前样品的检验项
        let list = await this.getCurrentProduct(this.currentSample.id, 0);
        this.currentSample.insProduct = this.HaveJson(list);
@@ -920,6 +962,7 @@
          this.currentSample["index"] = 1;
        let bushing = this.currentSample.bushing;
        this.getTableLists(); //处理模板列表信息
        this.tableLists.forEach((item) => this.ensureOtherForm(item.templateId));
        this.currentKey = 1;
        this.getCableTag(this.currentSample.id); // 查询是否有要多次检验的电缆配置
@@ -1406,12 +1449,33 @@
        this.worker0 = null;
      }
    },
    // 保存湿度、温度数据
    ensureOtherForm(templateId) {
      if (templateId == null || this.otherForm[templateId]) return;
      this.$set(this.otherForm, templateId, {
        temperature: null,
        humidity: null,
        detectionTime: null,
        detectionPlace: null,
        deviceIds: [],
        insResult: 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;
        if (type === "deviceIds") {
          payload.deviceIds = Array.isArray(m) ? [...m] : [];
        } else if (type === "detectionTime" || type === "detectionPlace") {
          payload[type] = m === "" || m == null ? null : m;
        } else {
          payload[type] = Number(m);
        }
      }
      write(payload).then((res) => {
        this.$message.success("保存成功");
      });
    },
@@ -1885,13 +1949,6 @@
              .replace(new RegExp("\n", "g"), "<br/>")
              .replace(new RegExp("@", "g"), "<br/>")
            : "";
      // 对数据保留小数点进行处理
      if (v.ct && v.ct.fa && v.ct.fa.includes(".") && str) {
        let num = 0;
        let str0 = v.ct.fa.split(".")[1];
        num = str0.length;
        str = Number(str).toFixed(num);
      }
      if (v.v && typeof v.v == "string" && v.v.includes("@")) {
        str = v.v.replace(new RegExp("@", "g"), "<br/>");
      }
@@ -1922,6 +1979,11 @@
    },
    // 对输入值进行格式校验
    handleInput(n) {
      // 直绑模板未配置检验值类型时按自由文本处理,允许录入外观等中文结果。
      const inspectionValueType = this.getInspectionValueType(n.i);
      if (inspectionValueType === undefined || inspectionValueType === null) {
        return;
      }
      try {
        n.v.v = n.v.v.replace(/[^\d.^e>>≥≤<<断裂脆化\-/+]/g, "");
        n.v.v = n.v.v.replace(/\.{2,}/g, "."); //只保留第一个. 清除多余的
@@ -1970,10 +2032,16 @@
    upInsReview(e) {
      if (e == 1) {
        // 通过
        if (this.reviewInsResult !== 0 && this.reviewInsResult !== 1) {
          this.$message.error("请选择检验结论");
        // 每个模板都要有结论;全部合格才算整单合格
        const insResults = this.tableLists.map(
          item => (this.otherForm[item.templateId] || {}).insResult
        );
        const missingIndex = insResults.findIndex(result => result !== 0 && result !== 1);
        if (missingIndex > -1) {
          this.$message.error(`请选择「${this.tableLists[missingIndex].templateName}」的检验结论`);
          return;
        }
        const finalInsResult = insResults.every(result => result === 1) ? 1 : 0;
        this.reviewLoading = true;
        verifyPlan({
          orderId: this.orderId,
@@ -1981,7 +2049,7 @@
          laboratory: this.sonLaboratory,
          tell: null,
          userId: this.checkUser,
          insResult: this.reviewInsResult,
          insResult: finalInsResult,
        }).then((res) => {
          if (res.code === 200) {
            this.$message.success("操作成功");
@@ -2034,12 +2102,21 @@
        this.$message.error("请指定复核人员");
        return;
      }
      if (!this.otherForm.humidity) {
        this.$message.error("请输入湿度");
        return;
      }
      if (!this.otherForm.temperature) {
        this.$message.error("请输入温度");
      const missing = [];
      this.tableLists.forEach((item) => {
        const form = this.otherForm[item.templateId] || {};
        if (this.insOrder.ifsOrderType !== "02wg") {
          if (!form.temperature) missing.push(`请填写「${item.templateName}」的温度`);
          if (!form.humidity) missing.push(`请填写「${item.templateName}」的湿度`);
        }
        if (!form.detectionTime) missing.push(`请选择「${item.templateName}」的检测时间`);
        if (!form.detectionPlace) missing.push(`请填写「${item.templateName}」的检测地点`);
        if (!form.deviceIds || form.deviceIds.length === 0) {
          missing.push(`请选择「${item.templateName}」的设备`);
        }
      });
      if (missing.length > 0) {
        this.$message.error(missing[0]);
        return;
      }
      await this.waitForPendingSaves();
@@ -2256,9 +2333,25 @@
      // 选择、移除、清空均由 change 事件触发,立即保存。
      this.saveInsContext();
    },
    standardDisplayName(value) {
      if (!value) return "-";
      const separatorIndex = value.indexOf(" - ");
      return separatorIndex > -1 ? value.slice(0, separatorIndex) : value;
    },
    isDirectTemplateOrder() {
      return this.sonLaboratory === "新聚" && this.currentSample &&
        (this.currentSample.insProduct || []).some((product) => product.templateRowIndex != null);
      return this.sonLaboratory === "新聚" &&
        Array.isArray(this.insOrder.templateConditions) &&
        this.insOrder.templateConditions.length > 0;
    },
    loadDeviceOptions() {
      search({ status: 0 }).then((res) => {
        this.orderDeviceOptions = (res.data || []).map((device) => ({
          id: device.id,
          label: `${device.deviceName}--${device.managementNumber}`,
        }));
      }).catch((error) => {
        console.error(error);
      });
    },
    async waitForPendingSaves() {
      const targetRevision = this.calculationRevision;
@@ -2377,26 +2470,6 @@
      })
        .catch((error) => { });
    },
    /**
     * 将数值v保留ct.fa中'##'后的指定小数位数,并返回格式化后的字符串。
     *
     * @param v 要格式化的数值
     * @param ct 包含格式化配置的对象
     * @param ct.fa 格式化配置字符串,若包含'##'则按照其后的内容确定小数位数
     * @returns 格式化后的字符串或原始数值(若配置不符合要求)
     */
    toFixed(v, ct) {
      if (v && ct && ct.fa) {
        if (ct.fa.includes(".")) {
          let num = ct.fa.slice(4).length;
          return Number(v).toFixed(num);
        } else {
          return v;
        }
      } else {
        return v;
      }
    },
    goback() {
      this.$router.go(-1)
    }
@@ -2448,6 +2521,35 @@
  text-align: left;
}
.template-panel {
  background-color: #f8f9fb;
  border: 1px solid #ebeef5;
  border-radius: 3px;
  padding: 16px 16px 4px;
  margin-bottom: 12px;
  text-align: left;
}
.template-panel>>>.el-form-item {
  display: inline-flex;
  align-items: center;
  margin-bottom: 12px;
}
.template-panel>>>.el-form-item__label {
  flex: none;
}
.template-panel__unit {
  margin-left: 4px;
  color: #606266;
}
.template-panel__conclusion--divided {
  border-top: 1px dashed #e4e7ed;
  padding-top: 12px;
}
.center {
  width: 100%;
  /* max-height: 580px; */
src/views/business/productOrder/components/addOrder.vue
@@ -1,77 +1,74 @@
<template>
  <div class="app-container">
    <div v-show="!cableConfigShow&&!auxiliaryShow">
      <div style="display: flex;justify-content: space-between;align-items:center;">
        <div>委托单信息</div>
        <div>
          <el-cascader
            v-model="addObj.quarterItemId"
            :disabled="active>1"
            :options="quarterItemOptions"
            size="small"
            style="width: 380px"
            @focus="getQuarterOnOrderList"></el-cascader>
          <el-select v-show="active==1" v-model="template" placeholder="下单模板" size="small"
                     @change="selectInsOrderTemplateById">
            <el-option v-for="(a, ai) in templates" :key="ai" :label="a.name" :value="a.id">
              <div style="display: flex; align-items: center; justify-content: space-between;">
                <span>{{ a.name }}</span>
                <i class="el-icon-delete"
                   style="color: #66b1ff; font-size: 16px; cursor: pointer;"
                   @click.stop="handleDelete(a)">
                </i>
              </div>
            </el-option>
          </el-select>
          <el-button v-show="active==1" size="small" @click="templateDia=true">
            <span style="color: #3A7BFA;">保存模板</span>
          </el-button>
          <el-button v-if="active==1&&addObj.sampleType!=undefined&&(addObj.sampleType.indexOf('电缆')>-1 ||addObj.sampleType.indexOf('综合')>-1)" size="small" type="primary" @click="openCableConfig">电缆配置</el-button>
          <el-button v-if="active==1&&addObj.sampleType!=undefined&&addObj.sampleType.indexOf('电缆')>-1" size="small" type="primary" @click="openAuxiliaryCore">辅助线芯配置</el-button>
          <el-button v-show="active==1||(tabIndex==4&&active==2)" :loading="saveLoad" size="small" type="primary" @click="save">提交</el-button>
          <!-- 审核 -->
          <el-button v-show="active==3" :disabled="saveLoad" :loading="saveLoad" size="small"
                     @click="upInsOrderOfState(2)">不通过</el-button>
          <el-button v-show="active==3" :loading="saveLoad" size="small" type="primary"
                     @click="upInsOrderOfState(1)">通过</el-button>
          <el-button size="small" @click="goBack">
            <span style="color: #3A7BFA;">返回</span>
          </el-button>
        </div>
    <div v-show="!cableConfigShow&&!auxiliaryShow" class="order-header">
      <span class="order-header__title">委托单信息</span>
      <div class="order-header__actions">
        <el-cascader
          v-model="addObj.quarterItemId"
          :disabled="active>1"
          :options="quarterItemOptions"
          placeholder="请选择季度"
          size="small"
          class="order-header__quarter"
          @focus="getQuarterOnOrderList"></el-cascader>
        <el-select v-show="active==1" v-model="template" placeholder="下单模板" size="small"
                   class="order-header__template"
                   @change="selectInsOrderTemplateById">
          <el-option v-for="(a, ai) in templates" :key="ai" :label="a.name" :value="a.id">
            <div style="display: flex; align-items: center; justify-content: space-between;">
              <span>{{ a.name }}</span>
              <i class="el-icon-delete"
                 style="color: #66b1ff; font-size: 16px; cursor: pointer;"
                 @click.stop="handleDelete(a)">
              </i>
            </div>
          </el-option>
        </el-select>
        <el-button v-show="active==1" size="small" @click="templateDia=true">保存模板</el-button>
        <el-button v-if="active==1&&addObj.sampleType!=undefined&&(addObj.sampleType.indexOf('电缆')>-1 ||addObj.sampleType.indexOf('综合')>-1)" size="small" type="primary" plain @click="openCableConfig">电缆配置</el-button>
        <el-button v-if="active==1&&addObj.sampleType!=undefined&&addObj.sampleType.indexOf('电缆')>-1" size="small" type="primary" plain @click="openAuxiliaryCore">辅助线芯配置</el-button>
        <el-button v-show="active==1||(tabIndex==4&&active==2)" :loading="saveLoad" size="small" type="primary" @click="save">提交</el-button>
        <!-- 审核 -->
        <el-button v-show="active==3" :disabled="saveLoad" :loading="saveLoad" size="small"
                   @click="upInsOrderOfState(2)">不通过</el-button>
        <el-button v-show="active==3" :loading="saveLoad" size="small" type="primary"
                   @click="upInsOrderOfState(1)">通过</el-button>
        <el-button size="small" @click="goBack">返回</el-button>
      </div>
    </div>
    <div v-show="!cableConfigShow&&!auxiliaryShow" style="margin-top: 10px">
      <div class="search">
        <el-form ref="newJuOrder" :inline="true" :model="addObj" label-width="108px" label-position="right">
          <el-row>
            <el-col :span="6"><el-form-item label="产品名称"><el-input v-model="addObj.sample" disabled size="small"><template slot="append"><el-button :disabled="active>1" icon="el-icon-search" @click="selectStandardTree = true" /></template></el-input></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="生产单位"><el-input v-model="addObj.production" :disabled="active>1" size="small" /></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="委托单位"><el-input v-model="addObj.company" disabled size="small"><template slot="append"><el-button :disabled="active>1" icon="el-icon-search" @click="openCompanyList" /></template></el-input></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="检验类别"><el-select v-model="addObj.orderType" :disabled="active>1" size="small"><el-option v-for="item in dict.type.check_type1" :key="item.value" :label="item.label" :value="item.value" /></el-select></el-form-item></el-col>
          </el-row>
          <el-row v-if="addObj.standardTreeId || boundTemplateList.length">
            <el-col :span="24">
    <div v-show="!cableConfigShow&&!auxiliaryShow" class="order-body">
      <el-card shadow="never" class="order-card">
        <el-form ref="newJuOrder" class="newju-order-form" :model="addObj" size="small" label-width="108px" label-position="right">
          <el-row :gutter="16">
            <el-col :span="6"><el-form-item label="检测依据"><el-input v-model="addObj.sample" disabled size="small"><template slot="append"><el-button :disabled="active>1" icon="el-icon-search" size="small" @click="selectStandardTree = true" /></template></el-input></el-form-item></el-col>
            <el-col :span="18">
              <el-form-item label="原始记录模板">
                <div v-loading="templateBindingLoading" class="template-tag-list">
                  <el-tag v-for="item in boundTemplateList" :key="item.templateId" size="small" effect="plain">{{ item.name }}</el-tag>
                  <el-tag v-for="(item, index) in boundTemplateList" :key="item.templateId" :closable="active==1" size="small" effect="plain" @close="removeBoundTemplate(index)">{{ item.name }}</el-tag>
                  <span v-if="!templateBindingLoading && boundTemplateList.length === 0" class="empty-template-text">暂未绑定原始记录模板</span>
                </div>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
          <el-row :gutter="16">
            <el-col :span="6"><el-form-item label="样品名称"><el-input v-model="addObj.sampleView" :disabled="active>1" size="small" /></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="生产单位"><el-input v-model="addObj.production" :disabled="active>1" size="small" /></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="委托单位"><el-input v-model="addObj.company" disabled size="small"><template slot="append"><el-button :disabled="active>1" icon="el-icon-search" size="small" @click="openCompanyList" /></template></el-input></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="检验类别"><el-select v-model="addObj.orderType" :disabled="active>1" size="small"><el-option v-for="item in dict.type.check_type1" :key="item.value" :label="item.label" :value="item.value" /></el-select></el-form-item></el-col>
          </el-row>
          <el-row :gutter="16">
            <el-col :span="6"><el-form-item label="样品数量"><el-input v-model="addObj.testQuantity" :disabled="active>1" placeholder="如:2桶、500mL" size="small" /></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="样品编号"><el-input v-model="sampleCode" :disabled="active>1" size="small" /></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="样品外观"><el-input v-model="addObj.sampleStatus" :disabled="active>1" size="small" /></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="送样人"><el-input v-model="addObj.prepareUser" :disabled="active>1" size="small" /></el-form-item></el-col>
          </el-row>
          <el-row>
          <el-row :gutter="16">
            <el-col :span="6"><el-form-item label="生产日期"><el-date-picker v-model="addObj.productionDate" type="date" value-format="yyyy-MM-dd" :disabled="active>1" size="small" /></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="生产批号"><el-input v-model="addObj.productionBatch" :disabled="active>1" size="small" /></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="送样日期"><el-date-picker v-model="addObj.sampleDate" type="date" value-format="yyyy-MM-dd" :disabled="active>1" size="small" /></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="联系方式"><el-input v-model="addObj.phone" :disabled="active>1" size="small" /></el-form-item></el-col>
          </el-row>
          <el-row><el-col :span="12"><el-form-item label="备注"><el-input v-model="addObj.remark" :disabled="active>1" size="small" /></el-form-item></el-col></el-row>
          <el-row :gutter="16"><el-col :span="12"><el-form-item label="备注"><el-input v-model="addObj.remark" :disabled="active>1" size="small" /></el-form-item></el-col></el-row>
        </el-form>
        <el-form v-if="false" ref="addObj" :inline="true" :model="addObj" :rules="addObjRules" label-width="108px" label-position="right">
          <el-row>
@@ -235,7 +232,7 @@
            </el-col>
          </el-row>
        </el-form>
      </div>
      </el-card>
      <div v-if="false">
        <div style="display: flex;justify-content: space-between;background-color: #F5F7FB;margin-bottom: 6px">
          <div v-if="active==1">
@@ -731,7 +728,7 @@
          { required: true, message: '请选择紧急程度', trigger: 'change' }
        ],
        sample: [
          { required: true, message: '请选择样品名称', trigger: 'change' }
          { required: true, message: '请选择检测依据', trigger: 'change' }
        ],
        sampleNum: [
          { required: true, message: '请填写样品数量', trigger: 'blur' }
@@ -960,7 +957,7 @@
          };
          this.addObj.type = String(this.addObj.type)
          const samples = this.HaveJson(res.data.sampleProduct)
          this.sampleCode = samples.length ? samples[0].sampleCode || '' : ''
          this.sampleCode = res.data.sampleCode || (samples.length ? samples[0].sampleCode || '' : '')
          this.boundTemplateList = this.sortTemplateList(res.data.templateList)
          const loadSamples = this.canSelectProduct
            ? Promise.all(samples.map(sample => this.loadSelectableProducts(sample)))
@@ -1194,7 +1191,7 @@
      save() {
        if (this.addObj.standardTreeId) {
          if (!this.addObj.sample || !this.addObj.company || !this.addObj.orderType || !this.addObj.testQuantity) {
            this.$message.error('请填写产品名称、委托单位、检验类别和样品数量');
            this.$message.error('请填写检测依据、委托单位、检验类别和样品数量');
            return;
          }
          this.saveData([]);
@@ -1434,7 +1431,12 @@
        delete newJuOrder.temperature
        delete newJuOrder.humidity
        const payload = this.addObj.standardTreeId
          ? { insOrder: newJuOrder, standardTreeId: this.addObj.standardTreeId, sampleCode: this.sampleCode }
          ? {
              insOrder: newJuOrder,
              standardTreeId: this.addObj.standardTreeId,
              sampleCode: this.sampleCode,
              templateList: this.boundTemplateList.map((item, index) => ({ templateId: item.templateId, sort: index }))
            }
          : { insOrder: {...this.addObj,typeSource:-1}, sampleList: sampleList }
        addInsOrder(payload).then(res => {
          this.saveLoad = false
@@ -1501,7 +1503,6 @@
        })
      },
      getUserNowData() {
      this.addObj.prepareUser = this.nickName
      this.addObj.prepareUserEn = this.nameEn
      this.addObj.prepareCode = this.userName
        getUserNow().then(res => {
@@ -1511,7 +1512,6 @@
          this.addObj.company = selects.company
          this.addObj.custom = selects.name
          this.addObj.code = selects.code
          this.addObj.phone = selects.phone
          this.addObj.companyId = selects.departId
          this.addObj.production = ''
          this.addObj.productionEn = ''
@@ -1578,6 +1578,10 @@
      },
      sortTemplateList(list) {
        return (list || []).slice().sort((a, b) => (a.sort || 0) - (b.sort || 0))
      },
      // 下单时从标准库绑定的模板里删减,提交时随 templateList 一起下发
      removeBoundTemplate(index) {
        this.boundTemplateList.splice(index, 1)
      },
      async loadTemplateBindings(standardTreeId) {
        this.boundTemplateList = []
@@ -2386,7 +2390,81 @@
  flex-wrap: wrap;
  gap: 8px;
}
.newju-order-form >>> .el-form-item {
  display: flex;
  margin-right: 0;
}
.newju-order-form >>> .el-form-item__label {
  flex: 0 0 108px;
}
.newju-order-form >>> .el-form-item__content {
  flex: 1;
  min-width: 0;
  margin-left: 0 !important;
}
.newju-order-form >>> .el-input,
.newju-order-form >>> .el-select,
.newju-order-form >>> .el-date-editor {
  width: 100%;
}
.empty-template-text {
  color: #909399;
}
/* 委托单信息:顶部操作栏 */
.order-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px;
  padding-bottom: 12px;
  border-bottom: 1px solid #ebeef5;
}
.order-header__title {
  font-size: 16px;
  font-weight: bold;
  color: #303133;
}
.order-header__actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}
/* 间距统一交给 gap,去掉相邻按钮自带的 margin-left */
.order-header__actions>>>.el-button+.el-button {
  margin-left: 0;
}
.order-header__quarter {
  width: 220px;
}
.order-header__template {
  width: 160px;
}
/* 委托单信息:表单卡片 */
.order-body {
  margin-top: 12px;
}
.order-card>>>.el-card__body {
  padding: 20px 20px 6px;
}
.order-card>>>.el-form-item {
  margin-bottom: 16px;
}
/* 控件统一占满标签之外的整列宽度:el-date-editor 默认写死 220px、
   el-select 宽度收缩成固有宽度,不指定就会和其他输入框左右边缘对不齐 */
.order-card>>>.el-input,
.order-card>>>.el-select {
  width: 100%;
}
/* append 单元格的 20px 内边距 + 按钮 -10px 负边距是按默认 40px 按钮标定的,
   small 输入下按钮会比输入框高、上下溢出;改成按钮直接撑满单元格 */
.order-card>>>.el-input-group__append {
  padding: 0;
}
.order-card>>>.el-input-group__append .el-button {
  margin: 0;
  padding: 9px 15px;
  font-size: 12px;
}
</style>
src/views/business/productOrder/components/addView.vue
@@ -2,7 +2,7 @@
  <div class="app-container">
    <div v-show="!cableConfigShow&&!auxiliaryShow">
      <div style="display: flex;justify-content: space-between;align-items:center;">
        <div>委托单信息</div>
        <div class="order-detail__title">委托单信息</div>
        <div>
          <el-cascader v-if="false"
            v-model="addObj.quarterItemId"
@@ -38,38 +38,28 @@
    </div>
    <div v-show="!cableConfigShow&&!auxiliaryShow" style="margin-top: 10px">
      <div class="search">
        <el-form :inline="true" :model="addObj" label-width="108px" label-position="right">
          <el-row>
            <el-col :span="6"><el-form-item label="产品名称"><el-input v-model="addObj.sample" disabled size="small" /></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="生产单位"><el-input v-model="addObj.production" disabled size="small" /></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="委托单位"><el-input v-model="addObj.company" disabled size="small" /></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="检验类别"><el-select v-model="addObj.orderType" disabled size="small"><el-option v-for="item in dict.type.check_type1" :key="item.value" :label="item.label" :value="item.value" /></el-select></el-form-item></el-col>
          </el-row>
          <el-row v-if="boundTemplateList.length">
            <el-col :span="24">
              <el-form-item label="原始记录模板">
                <div class="template-tag-list">
                  <el-tag v-for="item in boundTemplateList" :key="item.templateId" size="small" effect="plain">{{ item.name }}</el-tag>
                </div>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-col :span="6"><el-form-item label="样品数量"><el-input v-model="addObj.testQuantity" disabled size="small" /></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="样品编号"><el-input v-model="sampleCode" disabled size="small" /></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="样品外观"><el-input v-model="addObj.sampleStatus" disabled size="small" /></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="送样人"><el-input v-model="addObj.prepareUser" disabled size="small" /></el-form-item></el-col>
          </el-row>
          <el-row>
            <el-col :span="6"><el-form-item label="生产日期"><el-date-picker v-model="addObj.productionDate" type="date" value-format="yyyy-MM-dd" disabled size="small" /></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="生产批号"><el-input v-model="addObj.productionBatch" disabled size="small" /></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="送样日期"><el-date-picker v-model="addObj.sampleDate" type="date" value-format="yyyy-MM-dd" disabled size="small" /></el-form-item></el-col>
            <el-col :span="6"><el-form-item label="联系方式"><el-input v-model="addObj.phone" disabled size="small" /></el-form-item></el-col>
          </el-row>
          <el-row>
            <el-col :span="12"><el-form-item label="备注"><el-input v-model="addObj.remark" disabled size="small" style="width: 520px" /></el-form-item></el-col>
          </el-row>
        </el-form>
        <el-descriptions class="order-detail" :column="4" border>
          <el-descriptions-item label="检测依据">{{ standardDisplayName(addObj.sample) }}</el-descriptions-item>
          <el-descriptions-item label="样品名称">{{ addObj.sampleView || '-' }}</el-descriptions-item>
          <el-descriptions-item label="生产单位">{{ addObj.production || '-' }}</el-descriptions-item>
          <el-descriptions-item label="委托单位">{{ addObj.company || '-' }}</el-descriptions-item>
          <el-descriptions-item label="检验类别">{{ orderTypeLabel }}</el-descriptions-item>
          <el-descriptions-item label="样品数量">{{ addObj.testQuantity || '-' }}</el-descriptions-item>
          <el-descriptions-item label="样品编号">{{ sampleCode || '-' }}</el-descriptions-item>
          <el-descriptions-item label="样品外观">{{ addObj.sampleStatus || '-' }}</el-descriptions-item>
          <el-descriptions-item label="送样人">{{ addObj.prepareUser || '-' }}</el-descriptions-item>
          <el-descriptions-item label="生产日期">{{ addObj.productionDate || '-' }}</el-descriptions-item>
          <el-descriptions-item label="生产批号">{{ addObj.productionBatch || '-' }}</el-descriptions-item>
          <el-descriptions-item label="送样日期">{{ addObj.sampleDate || '-' }}</el-descriptions-item>
          <el-descriptions-item label="联系方式">{{ addObj.phone || '-' }}</el-descriptions-item>
          <el-descriptions-item label="原始记录模板" :span="4">
            <div v-if="boundTemplateList.length" class="template-tag-list">
              <el-tag v-for="item in boundTemplateList" :key="item.templateId" size="small" effect="plain">{{ item.name }}</el-tag>
            </div>
            <span v-else class="order-detail__empty">暂未绑定原始记录模板</span>
          </el-descriptions-item>
          <el-descriptions-item label="备注" :span="4">{{ addObj.remark || '-' }}</el-descriptions-item>
        </el-descriptions>
        <el-form v-show="false" ref="addObj" :inline="true" :model="addObj" :rules="addObjRules" label-width="108px" label-position="right">
          <el-row>
            <el-col :span="6">
@@ -643,6 +633,13 @@
    newJuLaboratoryList() {
      return (this.dict.type.sys_sub_lab || []).filter(item => item.label === '新聚')
    },
    // 检验类别按字典名称展示
    orderTypeLabel() {
      const hit = (this.dict.type.check_type1 || []).find(
        item => String(item.value) === String(this.addObj.orderType)
      )
      return hit ? hit.label : (this.addObj.orderType || '-')
    },
  },
  dicts: ['check_type1', 'urgency_level', 'form_type', 'sample_status_list', 'sys_sub_lab'],
  data() {
@@ -706,7 +703,7 @@
          { required: true, message: '请选择紧急程度', trigger: 'change' }
        ],
        sample: [
          { required: true, message: '请选择样品名称', trigger: 'change' }
          { required: true, message: '请选择检测依据', trigger: 'change' }
        ],
        sampleNum: [
          { required: true, message: '请填写样品数量', trigger: 'blur' }
@@ -880,6 +877,11 @@
    this.getInfo();
  },
  methods: {
    standardDisplayName(value) {
      if (!value) return '-';
      const separatorIndex = value.indexOf(' - ');
      return separatorIndex > -1 ? value.slice(0, separatorIndex) : value;
    },
    getInfo() {
      this.selectStandardTreeList()
      this.getAuthorizedPerson();
@@ -896,7 +898,7 @@
          };
          this.addObj.type = String(this.addObj.type)
          this.sampleList = this.HaveJson(res.data.sampleProduct);
          this.sampleCode = this.sampleList.length ? this.sampleList[0].sampleCode : ''
          this.sampleCode = res.data.sampleCode || (this.sampleList.length ? this.sampleList[0].sampleCode : '')
          this.boundTemplateList = (res.data.templateList || []).slice().sort((a, b) => (a.sort || 0) - (b.sort || 0))
          this.specialStandardMethod = this.sampleList[0].specialStandardMethod
          this.getProNum()
@@ -1485,7 +1487,6 @@
        } else {
          this.addObj.sample = trees[3]
        }
        this.addObj.sampleView = this.addObj.sample
        this.addObj.sampleViewEn = this.sampleViewEn
        this.addObj.model = (trees[4] == undefined ? null : trees[4])
        this.selectStandardTree = false
@@ -2254,4 +2255,24 @@
  flex-wrap: wrap;
  gap: 8px;
}
/* 委托单信息:只读详情 */
.order-detail__title {
  font-size: 16px;
  font-weight: bold;
  color: #303133;
}
.order-detail>>>.el-descriptions-item__label.is-bordered-label {
  width: 104px;
  white-space: nowrap;
}
.order-detail>>>.el-descriptions-item__content {
  color: #303133;
}
/* 描述列表内不需要额外的行高占位,标签行与其它行等高 */
.order-detail .template-tag-list {
  min-height: 0;
}
.order-detail__empty {
  color: #c0c4cc;
}
</style>
src/views/business/productOrder/index.vue
@@ -362,6 +362,8 @@
          linkMethod: "changeEntrustCode",
        },
        { label: '委托单位', prop: 'company' },
        { label: '检测依据', prop: 'sample' },
        { label: '样品名称', prop: 'sampleView' },
        // {
        //   label: "样品名称",
        //   prop: "sampleName",
@@ -391,6 +393,13 @@
          label: '操作',
          operation: [
            {
              name: '查看详情',
              type: 'text',
              clickFun: (row) => {
                this.selectAllByOne(row);
              }
            },
            {
              name: '数据查看',
              type: 'text',
              clickFun: (row) => {
src/views/standard/standardLibrary/index.vue
@@ -6,14 +6,17 @@
        <pane size="12">
          <el-col>
            <div class="head-container addButton">
              <el-input v-model="search" clearable placeholder="输入关键字进行搜索" size="small" style="margin-bottom: 5px"
              <el-input v-model="search" clearable placeholder="输入关键字进行搜索" size="small"
                        style="margin-bottom: 5px; flex: 1; min-width: 0"
                        suffix-icon="el-icon-search" @keydown.enter.native="searchFilter" @blur="searchFilter" @clear="searchFilter"></el-input>
              <el-button circle icon="el-icon-plus" size="mini" type="primary" @click="openAddDia"></el-button>
              <el-button icon="el-icon-refresh" size="small" title="刷新"
                         style="margin-bottom: 5px; margin-left: 8px"
                         @click="selectStandardTreeList"></el-button>
            </div>
            <div class="head-container">
              <el-tree ref="tree" v-loading="treeLoad" :data="list"
                       :default-expanded-keys="expandedKeys" :filter-node-method="filterNode"
                       :props="{ children: 'children', label: 'label' }" highlight-current node-key="label"
                       :props="{ children: 'children', label: 'label' }" highlight-current node-key="id"
                       style="
                        height: calc(100vh - 173px);
                        overflow-y: scroll;
@@ -32,22 +35,6 @@
                {{ data.label }}
              </span>
                    </el-col>
                    <el-col v-if="
              checkPermi(['standard:standardLibrary:delStandardTree']) &&
              isLeaf(node.data)
            " :span="2" style="text-align: right">
                      <el-button size="mini" type="text" @click.stop="editTreeName(node)">
                        <i class="el-icon-edit"></i>
                      </el-button>
                    </el-col>
                    <el-col v-if="
              checkPermi(['standard:standardLibrary:delStandardTree']) &&
              isLeaf(node.data)
            " :span="2" style="text-align: right">
                      <el-button size="mini" type="text" @click.stop="remove(node, data)">
                        <i class="el-icon-delete"></i>
                      </el-button>
                    </el-col>
                  </el-row>
                </div>
              </el-tree>
@@ -58,7 +45,7 @@
        <pane size="88">
          <el-card v-if="selected.isLeaf" shadow="never" class="template-binding-card">
            <div slot="header"><span>原始记录模板</span><el-button v-if="checkPermi(['standard:standardLibrary:upStandardProduct'])" type="primary" size="mini" style="float:right" @click="saveBindings">保存绑定</el-button></div>
            <p v-if="!templateBindings.length" class="binding-tip">请为当前标准绑定原始记录模板;模板中须使用“检验项”标记。</p>
            <p v-if="!templateBindings.length" class="binding-tip">请为当前标准绑定原始记录模板。</p>
            <el-table :data="templateBindings" border size="small">
              <el-table-column type="index" label="顺序" width="70" /><el-table-column label="模板编号" prop="number" width="180" /><el-table-column label="模板名称" prop="name" /><el-table-column label="检测依据(模板备注)" prop="remark" />
              <el-table-column v-if="checkPermi(['standard:standardLibrary:upStandardProduct'])" label="操作" width="150"><template slot-scope="scope"><el-button type="text" :disabled="scope.$index === 0" @click="moveBinding(scope.$index, -1)">上移</el-button><el-button type="text" :disabled="scope.$index === templateBindings.length - 1" @click="moveBinding(scope.$index, 1)">下移</el-button><el-button type="text" @click="removeBinding(scope.$index)">移除</el-button></template></el-table-column>
@@ -585,7 +572,14 @@
    selectStandardTreeList() {
      this.treeLoad = true;
      selectStandardTreeList().then((res) => {
        this.list = res.data;
        this.list = (res.data || []).map(industry => ({
          ...industry,
          children: (industry.children || []).map(standard => ({
            ...standard,
            // 二级节点只展示标准编号;兼容尚未重启的旧后端返回格式。
            label: standard.standardCode || String(standard.label || '').split(' - ')[0],
          })),
        }));
        this.expandedKeys = [];
        this.list.forEach((a) => {
          this.expandedKeys.push(a.label);
src/views/structural/premises/index.vue
@@ -3,11 +3,11 @@
    <div class="search">
      <div>
        <el-form :model="queryParams" ref="queryForm" size="small" :inline="true">
          <el-form-item label="实验室名称" prop="laboratoryName">
          <el-form-item label="行业名称" prop="laboratoryName">
            <el-input size="small" placeholder="请输入" clearable v-model="queryParams.laboratoryName"
              @keyup.enter.native="refreshTable"></el-input>
          </el-form-item>
          <el-form-item label="实验室编码" prop="laboratoryNumber">
          <el-form-item label="行业编码" prop="laboratoryNumber">
            <el-input size="small" placeholder="请输入" clearable v-model="queryParams.laboratoryNumber"
              @keyup.enter.native="refreshTable"></el-input>
          </el-form-item>
@@ -25,17 +25,17 @@
      <lims-table :tableData="tableData" :column="column" :height="'calc(100vh - 250px)'" @pagination="pagination"
        :page="page" :tableLoading="tableLoading"></lims-table>
    </div>
    <!--    新增实验室-->
    <!--    新增行业-->
    <el-dialog :title="formTitle" :visible.sync="addDia" width="450px">
      <el-form ref="laboratoryForm" :model="laboratoryForm" :rules="userRules" label-position="right"
        label-width="100px">
        <el-form-item label="实验室名称" prop="laboratoryName">
        <el-form-item label="行业名称" prop="laboratoryName">
          <el-input v-model="laboratoryForm.laboratoryName" size="small" clearable></el-input>
        </el-form-item>
        <el-form-item label="场所编码" prop="laboratoryNumber">
          <el-input v-model="laboratoryForm.laboratoryNumber" size="small" clearable></el-input>
        </el-form-item>
        <el-form-item label="实验室代号" prop="laboratoryCode">
        <el-form-item label="行业代号" prop="laboratoryCode">
          <el-input v-model="laboratoryForm.laboratoryCode" size="small" clearable></el-input>
        </el-form-item>
        <el-form-item label="负责人" prop="head">
@@ -113,9 +113,9 @@
      tableData: [],
      tableLoading: false,
      column: [
        { label: '实验室名称', prop: 'laboratoryName' },
        { label: '行业名称', prop: 'laboratoryName' },
        { label: '场所编码', prop: 'laboratoryNumber' },
        { label: '实验室代号', prop: 'laboratoryCode' },
        { label: '行业代号', prop: 'laboratoryCode' },
        { label: '负责人', prop: 'head' },
        { label: '负责人电话', prop: 'phoneNumber' },
        { label: '地址', prop: 'address' },
@@ -161,7 +161,7 @@
      },
      operationType: '',
      userRules: {
        laboratoryName: [{ required: true, message: '请输入实验室名称', trigger: 'blur' }],
        laboratoryName: [{ required: true, message: '请输入行业名称', trigger: 'blur' }],
        laboratoryNumber: [{ required: true, message: '请输入场所编码', trigger: 'blur' }],
        head: [{ required: true, message: '请输入负责人', trigger: 'blur' }],
        phoneNumber: [{ required: true, message: '请输入负责人电话', trigger: 'blur' }],
@@ -170,7 +170,7 @@
      fileComponentTableLoading: false,
      fileComponentData: [],
      fileComponentDataColumn: [
        { label: '实验室名称', prop: 'laboratoryName' },
        { label: '行业名称', prop: 'laboratoryName' },
        { label: '印章图片', prop: 'address', dataType: 'image' },
        { label: '印章类型', prop: 'type' },
      ],
@@ -194,8 +194,8 @@
      props: { multiple: false, emitPath: false, },
      options: [
        {
          value: '实验室资质',
          label: '实验室资质',
          value: '行业资质',
          label: '行业资质',
          children: []
        },
        {
@@ -238,14 +238,14 @@
      this.refreshTable()
    },
    openAdd(type, row) {
      this.formTitle = type === 'add' ? '新增实验室' : '编辑实验室'
      this.formTitle = type === 'add' ? '新增行业' : '编辑行业'
      this.operationType = type
      if (type === 'edit') {
        this.laboratoryForm = this.HaveJson(row)
      }
      this.addDia = true
    },
    // 提交新增、编辑实验室表单
    // 提交新增、编辑行业表单
    customAdd() {
      this.$refs['laboratoryForm'].validate((valid) => {
        if (valid) {
@@ -278,7 +278,7 @@
      this.resetForm('laboratoryForm')
      this.addDia = false
    },
    // 删除实验室
    // 删除行业
    delete(row) {
      this.$confirm('是否删除当前数据?', "警告", {
        confirmButtonText: "确定",
src/workers/DataWorker.worker.js
@@ -65,16 +65,8 @@
                  if(i+1==arr[1]){
                    // 赋值数采优化检验项列表
                    arrSpecial.push(n.i)
                    let num0 = 0
                    if(n.v.ct&&n.v.ct.fa&&typeof n.v.ct.fa == 'string'&&n.v.ct.fa.includes('.')){
                      // 保留模板配置的小数点位数
                      let str0 = n.v.ct.fa.split('.')[1]
                      num0 = str0.length
                      n.v.v = dataAcquisitionInfo[str].value[i]?Number(dataAcquisitionInfo[str].value[i]).toFixed(num0):dataAcquisitionInfo[str].value[i]
                    }else{
                      // 直接赋值
                      n.v.v = dataAcquisitionInfo[str].value[i]
                    }
                    // 直接赋值
                    n.v.v = dataAcquisitionInfo[str].value[i]
                    // 传递给主线程
                    result = {
                      method:'changeInput',
@@ -92,16 +84,8 @@
                  // 如果数采次数等于检验值序号,则赋值给当前检验值
                  arrSpecial.push(n.i)
                  setTimeout(()=>{
                    let num0 = 0
                    if(n.v.ct&&n.v.ct.fa&&typeof n.v.ct.fa == 'string'&&n.v.ct.fa.includes('.')){
                      // 保留模板配置的小数点位数
                      let str0 = n.v.ct.fa.split('.')[1]
                      num0 = str0.length
                      n.v.v = dataAcquisitionInfo[str].value?Number(dataAcquisitionInfo[str].value).toFixed(num0):dataAcquisitionInfo[str].value
                    }else{
                      // 直接赋值
                      n.v.v = dataAcquisitionInfo[str].value
                    }
                    // 直接赋值
                    n.v.v = dataAcquisitionInfo[str].value
                    // 传递给主线程
                    result = {
                      method:'changeInput',
@@ -117,16 +101,8 @@
                  if(n.v.ps.value.includes(num)){
                    arrSpecial.push(n.i)
                    setTimeout(()=>{
                      let num0 = 0
                      if(n.v.ct&&n.v.ct.fa&&typeof n.v.ct.fa == 'string'&&n.v.ct.fa.includes('.')){
                        // 保留模板配置的小数点位数
                        let str0 = n.v.ct.fa.split('.')[1]
                        num0 = str0.length
                        n.v.v = dataAcquisitionInfo[str].value?Number(dataAcquisitionInfo[str].value).toFixed(num0):dataAcquisitionInfo[str].value
                      }else{
                        // 直接赋值
                        n.v.v = dataAcquisitionInfo[str].value
                      }
                      // 直接赋值
                      n.v.v = dataAcquisitionInfo[str].value
                      // 传递给主线程
                      result = {
                        method:'changeInput',
src/workers/InspectionWorker.worker.js
@@ -176,6 +176,18 @@
}
/**
 * 获取纯单元格引用公式的坐标名,例如 =F4、=$F$4。
 * 纯引用应直接复制原值,避免把“外观”等中文文本送入数值计算。
 */
function getDirectReferenceName(f) {
  if (typeof f !== "string") {
    return null;
  }
  const matched = f.trim().match(/^=\s*\$?([A-Za-z]{1,3})\$?(\d{1,7})\s*$/);
  return matched ? `${matched[1].toUpperCase()}${matched[2]}` : null;
}
/**
 * 按检验项id取当前样品下的检验项信息,取不到时返回空对象,避免公式计算因缺项中断
 *
 * @param id 检验项id
@@ -215,8 +227,6 @@
      break;
    }
  }
  let isToExponential = ""; //是否为科学计数法
  let list2 = []; //一个数组,里面保存有当前检验项的所有参数值
  let isPoint = ""; //是否为小数点
  let inputType = null;
  let currentInsItemId = "";
@@ -250,7 +260,6 @@
                    break;
                  }
                }
                list2.push(c.v.v);
                // 组装参数的对象集合并赋值,列如{A3:12,B4:15}
                if (
                  (getInspectionValueType(item.i) == 1 ||
@@ -277,7 +286,11 @@
        // 如果是函数方法,则执行此方法
        let comResult = ""; //初始化计算结果
        try {
          if (
          const directReferenceName = getDirectReferenceName(item.v.f);
          if (directReferenceName && Object.prototype.hasOwnProperty.call(comValue, directReferenceName)) {
            // =F4 这类公式既可能引用数字,也可能引用中文文本,直接保留源值。
            comResult = comValue[directReferenceName];
          } else if (
            getInspectionValueType(item.i) == 1 ||
            isNumericFormula(item.v.f)
          ) {
@@ -325,22 +338,7 @@
            }
            // 根据输入的数值,进行计算
            comResult = compute(item.v.f.replace(/=/g, " "), comValue, isPoint);
            // 检验项为密度时,根据要求值的小数位改变最终值的小数位
            if (inspectionItem == "密度") {
              const str = "" + tell;
              const indexOfDecimal = str.indexOf(".");
              if (indexOfDecimal > -1) {
                const length = str.length - indexOfDecimal - 1;
                comResult = comResult.toFixed(length);
              }
            } else if (
              inspectionItemClass == "外护套机械物理性能" &&
              inspectionItem == "在IRM 902矿物油中浸泡后" &&
              (inspectionItemSubclass == "断裂伸长率变化率" ||
                inspectionItemSubclass == "抗张强度变化率")
            ) {
              comResult = Math.round(comResult).toFixed(0);
            } else if (
            if (
              typeof comResult == "string" &&
              (comResult.includes("断裂") || comResult.includes("脆化"))
            ) {
@@ -368,15 +366,6 @@
            }
            if (isHaveSymbol) {
              comResult = symbol + comResult;
            }
            let list3 = list2.map((item) => item + "");
            // 判断填的检验值是否为科学计数法,如果为科学计数法,则进行转化
            isToExponential = list3.some(
              (val) => val.includes("e+") || val.includes("e-")
            );
            if (isToExponential) {
              let num2 = new Big(comResult);
              comResult = num2.toExponential(1);
            }
          } else {
            // 如果检验值类型是文本输入框、下拉框
@@ -416,45 +405,7 @@
              // 判断当前行是否为当前检验项所在行,如果为当前行,则给表格数据赋值
              for (var b in a) {
                if (a[b].c == item.c) {
                  try {
                    if (comResult == 0) {
                      // 判断计算结果是否为0,如果为0,则给表格数据赋值为0
                      a[b].v.v = 0;
                    } else if (
                      a[b].v.ct &&
                      a[b].v.ct.fa &&
                      typeof a[b].v.ct.fa == "string" &&
                      a[b].v.ct.fa.includes(".")
                    ) {
                      // 判断当前单元格是否保留小数点,如果为保留小数点,则给表格数据赋值为保留小数点,这个是根据模板配置小数点来的
                      let num = 0;
                      let str = a[b].v.ct.fa.split(".")[1];
                      num = str.length;
                      a[b].v.v = comResult
                        ? Number(comResult).toFixed(num)
                        : comResult;
                    } else if (
                      typeof comResult == "string" &&
                      (comResult.includes("e+") || comResult.includes("e-"))
                    ) {
                      // 判断计算结果是否为科学计数法,如果为科学计数法,则给表格数据赋值为科学计数法
                      a[b].v.v = comResult;
                    } else {
                      const inspectionItem = findInsItem(item.i).inspectionItem;
                      // 判断计算结果是否为数字,如果为数字,则给表格数据赋值为数字
                      if (inspectionItem == "铜线电阻率ρ20最大值") {
                        let val = parseFloat(Number(comResult).toFixed(5));
                        a[b].v.v = isNaN(val) ? comResult : val;
                      } else {
                        let val = parseFloat(Number(comResult).toFixed(3));
                        a[b].v.v = isNaN(val) ? comResult : val;
                      }
                    }
                  } catch (error) {
                    // 如果以上判断都不支持,则直接赋值
                    a[b].v.v = comResult;
                    console.log("error---", error);
                  }
                  a[b].v.v = comResult;
                  hasComputedWrite = true;
                  break;
                }
vue.config.js
@@ -37,7 +37,7 @@
      // detail: https://cli.vuejs.org/config/#devserver-proxy
      [process.env.VUE_APP_BASE_API]: {
        // target: `http://36.213.90.123:9015/lims`,
        target: `http://192.168.0.24:8001/lims`,
        target: `http://192.168.21.53:9001/lims`,
        changeOrigin: true,
        pathRewrite: {
          ["^" + process.env.VUE_APP_BASE_API]: "",