695593266@qq.com 1 месяц назад
Родитель
Сommit
712d2a148b

+ 74 - 0
src/api/inspectionReport/index.js

@@ -0,0 +1,74 @@
+import request from '@/utils/request';
+
+// 列表
+export async function getList(params) {
+  const res = await request.get('/qms/quality_work_order/pageByReport', {
+    params
+  });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// 检测报告基本信息
+export async function queryInspectionReportData({ id, type }) {
+  const res = await request.get(
+    `/qms/quality_work_order/queryInspectionReportData/${id}`,
+    {
+      params: { type }
+    }
+  );
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// 检测报告检测项列表信息
+export async function queryInspectionReportList({ id, type }) {
+  const res = await request.get(
+    `/qms/quality_work_order/queryInspectionReportList/${id}`,
+    {
+      params: { type }
+    }
+  );
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// 生成/修改检测报告
+export async function generateReport(data) {
+  const res = await request.post(
+    `/qms/quality_work_order/generateReport`,
+    data
+  );
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// 删除检测报告
+export async function deleteReport(id) {
+  const res = await request.delete(
+    `/qms/quality_work_order/deleteReport/${id}`
+  );
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+/**
+ * 企业信息
+ */
+export async function enterprisePage(params) {
+  const res = await request.get(`/main/enterprise/page`, { params });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 8 - 0
src/api/inspectionWork/index.js

@@ -38,6 +38,14 @@ export async function getById(id) {
   return Promise.reject(new Error(res.data.message));
 }
 
+export async function getDetailById(id) {
+  const res = await request.get(`/qms/quality_work_order/getDetailById/${id}`);
+  if (res.data.code == 0) {
+    return res.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
 // 新增合格证
 export async function qualificationSave(data) {
   const res = await request.post(`/qms/qualityWorkOrderCertificate/save`, data);

+ 50 - 0
src/api/unacceptedProduct/reasonType.js

@@ -0,0 +1,50 @@
+import request from '@/utils/request';
+
+/**
+ * 分页
+ */
+export async function getList(data) {
+  let par = new URLSearchParams(data);
+  const res = await request.get(`/qms/reasontype/page?` + par, {});
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+/**
+ * 详情
+ */
+export async function getById(id) {
+  const res = await request.get(`/qms/reasontype/getById/` + id, {});
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//新建
+export async function save(data) {
+  const res = await request.post(`/qms/reasontype/save`, data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//修改
+export async function updateData(data) {
+  const res = await request.put('/qms/reasontype/update', data);
+  if (res.data.code == 0) {
+    return res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// 删除
+export async function deleteList(data) {
+  const res = await request.delete('/qms/reasontype/delete', { data });
+  if (res.data.code == 0) {
+    return res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 50 - 0
src/api/unacceptedProduct/unqualifiedName.js

@@ -0,0 +1,50 @@
+import request from '@/utils/request';
+
+/**
+ * 分页
+ */
+export async function getList(data) {
+  let par = new URLSearchParams(data);
+  const res = await request.get(`/qms/badname/page?` + par, {});
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+/**
+ * 详情
+ */
+export async function getById(id) {
+  const res = await request.get(`/qms/badname/getById/` + id, {});
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//新建
+export async function save(data) {
+  const res = await request.post(`/qms/badname/save`, data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//修改
+export async function updateData(data) {
+  const res = await request.put('/qms/badname/update', data);
+  if (res.data.code == 0) {
+    return res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// 删除
+export async function deleteList(data) {
+  const res = await request.delete('/qms/badname/delete', { data });
+  if (res.data.code == 0) {
+    return res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 50 - 0
src/api/unacceptedProduct/unqualifiedType.js

@@ -0,0 +1,50 @@
+import request from '@/utils/request';
+
+/**
+ * 分页
+ */
+export async function getList(data) {
+  let par = new URLSearchParams(data);
+  const res = await request.get(`/qms/badtype/page?` + par, {});
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+/**
+ * 详情
+ */
+export async function getById(id) {
+  const res = await request.get(`/qms/badtype/getById/` + id, {});
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//新建
+export async function save(data) {
+  const res = await request.post(`/qms/badtype/save`, data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//修改
+export async function updateData(data) {
+  const res = await request.put('/qms/badtype/update', data);
+  if (res.data.code == 0) {
+    return res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// 删除
+export async function deleteList(data) {
+  const res = await request.delete('/qms/badtype/delete', { data });
+  if (res.data.code == 0) {
+    return res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 49 - 8
src/views/batchRecord/components/tables/pendingBatchRecordTable.vue

@@ -21,14 +21,14 @@
     </ele-pro-table>
 
     <unqualified-product-review ref="unqualifiedProductReviewRef" />
-    <release-order-print ref="releaseOrderPrintRef" />
+    <product-release-approval ref="productReleaseApproval" />
   </div>
 </template>
 
 <script>
   import printSelector from '../printSelector.vue';
   import unqualifiedProductReview from '../../print/electrodeBatchRecord/unqualifiedProductReview.vue';
-  import releaseOrderPrint from '../../print/electrodeBatchRecord/releaseOrderPrint.vue';
+  import productReleaseApproval from '../../print/electrodeBatchRecord/productReleaseApproval.vue';
   import { checklistrecordPage } from '@/api/checklistrecord/index';
   import { samplingRecordsPage } from '@/api/samplingRecords';
   import { getList as unacceptedProductPage } from '@/api/unacceptedProduct/index';
@@ -43,7 +43,7 @@
     components: {
       printSelector,
       unqualifiedProductReview,
-      releaseOrderPrint
+      productReleaseApproval
     },
     props: {
       tableQuery: {
@@ -69,9 +69,9 @@
         ],
         releaseOrderPrintOptions: [
           {
-            key: 'releaseOrderPrint',
-            label: '放行单',
-            code: '放行单详情'
+            key: 'productReleaseApproval',
+            label: '成品放行审批单',
+            code: 'ZL-065'
           }
         ],
         disposeList: []
@@ -660,10 +660,51 @@
           this.$refs.unqualifiedProductReviewRef.open(row);
           return;
         }
-        if (key === 'releaseOrderPrint') {
-          this.$refs.releaseOrderPrintRef.open(row);
+        if (key === 'productReleaseApproval') {
+          this.$refs.productReleaseApproval.open(
+            this.buildReleaseOrderPrintData(row)
+          );
         }
       },
+      buildReleaseOrderPrintData(row) {
+        const firstOrder = this.getFirstOrder(row);
+        return {
+          ...row,
+          productCode:
+            row.productCode || this.getOrderField(row, 'productCode'),
+          productName:
+            row.productName || this.getOrderField(row, 'productName'),
+          specification:
+            row.specification || this.getOrderField(row, 'specification'),
+          productModel:
+            row.productModel || this.getOrderField(row, 'productModel'),
+          batchNo: row.batchNo || this.getOrderField(row, 'batchNo'),
+          quantity:
+            row.quantity ||
+            row.formedNum ||
+            firstOrder.formedNum ||
+            firstOrder.formingNum ||
+            this.getOrderField(row, 'formedNum') ||
+            this.getOrderField(row, 'formingNum'),
+          sterilizationBatchNo:
+            row.sterilizationBatchNo ||
+            row.sterileBatchNo ||
+            firstOrder.sterilizationBatchNo ||
+            firstOrder.sterileBatchNo ||
+            '',
+          inspectionCode:
+            row.inspectionCode ||
+            row.inspectionNo ||
+            firstOrder.inspectionCode ||
+            firstOrder.inspectionNo ||
+            ''
+        };
+      },
+      getFirstOrder(row) {
+        return Array.isArray(row.orders) && row.orders.length
+          ? row.orders[0]
+          : {};
+      },
       getOrderField(row, key) {
         if (row[key] !== undefined && row[key] !== null && row[key] !== '') {
           return row[key];

+ 0 - 330
src/views/batchRecord/print/electrodeBatchRecord/releaseOrderPrint.vue

@@ -1,330 +0,0 @@
-<template>
-  <ele-modal
-    title="放行单打印"
-    :visible.sync="visible"
-    v-if="visible"
-    width="90%"
-    :maxable="true"
-    append-to-body
-  >
-    <div id="printSection_releaseOrderPrint">
-      <div class="release-print-page">
-        <div class="release-header">
-          <div class="company">湖南特瑞精密医疗器械有限公司</div>
-          <div class="title">放行单详情</div>
-        </div>
-
-        <table class="info-table">
-          <tr>
-            <td class="label">放行单编码</td>
-            <td>{{ field('code') }}</td>
-            <td class="label">放行单名称</td>
-            <td>{{ field('name') }}</td>
-          </tr>
-          <tr>
-            <td class="label">放行类型</td>
-            <td>{{ checklistTypeLabel }}</td>
-            <td class="label">审核状态</td>
-            <td>{{ approvalStatusLabel }}</td>
-          </tr>
-          <tr>
-            <td class="label">创建人</td>
-            <td>{{ field('createUserName') }}</td>
-            <td class="label">模板名称</td>
-            <td>{{ field('templateName') }}</td>
-          </tr>
-        </table>
-
-        <div class="section-title">物品清单</div>
-        <table class="data-table">
-          <thead>
-            <tr>
-              <th style="width: 45px">序号</th>
-              <th>批次号</th>
-              <th>产品编码</th>
-              <th>产品名称</th>
-              <th>生产工单号</th>
-              <th>要求生产数量</th>
-              <th>实际生产数量</th>
-              <th>规格</th>
-              <th>型号</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr v-for="(item, index) in orders" :key="'order_' + index">
-              <td>{{ index + 1 }}</td>
-              <td>{{ item.batchNo }}</td>
-              <td>{{ item.productCode }}</td>
-              <td>{{ item.productName }}</td>
-              <td>{{ item.workOrderCode }}</td>
-              <td>{{ item.formingNum }}</td>
-              <td>{{ item.formedNum }}</td>
-              <td>{{ item.specification }}</td>
-              <td>{{ item.productModel }}</td>
-            </tr>
-            <tr v-if="!orders.length">
-              <td colspan="9" class="empty-cell">暂无数据</td>
-            </tr>
-          </tbody>
-        </table>
-
-        <div class="section-title">生产放行规则</div>
-        <table class="data-table rule-table">
-          <thead>
-            <tr>
-              <th style="width: 45px">序号</th>
-              <th>指标名称</th>
-              <th style="width: 90px">审核结果</th>
-              <th>备注</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr v-for="(item, index) in workDetails" :key="'work_' + index">
-              <td>{{ index + 1 }}</td>
-              <td class="left">{{ item.mainIndicatorName }}</td>
-              <td>{{ passLabel(item.isPass) }}</td>
-              <td class="left">{{ item.remark }}</td>
-            </tr>
-            <tr v-if="!workDetails.length">
-              <td colspan="4" class="empty-cell">暂无数据</td>
-            </tr>
-          </tbody>
-        </table>
-
-        <table class="conclusion-table">
-          <tr>
-            <td class="label">结论</td>
-            <td>{{ conclusionLabel(form.workConclution) }}</td>
-            <td class="label">验收人</td>
-            <td>{{ field('workCheckUserName') }}</td>
-            <td class="label">验收时间</td>
-            <td>{{ field('workCheckTime') }}</td>
-          </tr>
-        </table>
-
-        <div class="section-title">质检放行规则</div>
-        <table class="data-table rule-table">
-          <thead>
-            <tr>
-              <th style="width: 45px">序号</th>
-              <th>指标名称</th>
-              <th style="width: 90px">审核结果</th>
-              <th>备注</th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr
-              v-for="(item, index) in qualityDetails"
-              :key="'quality_' + index"
-            >
-              <td>{{ index + 1 }}</td>
-              <td class="left">{{ item.mainIndicatorName }}</td>
-              <td>{{ passLabel(item.isPass) }}</td>
-              <td class="left">{{ item.remark }}</td>
-            </tr>
-            <tr v-if="!qualityDetails.length">
-              <td colspan="4" class="empty-cell">暂无数据</td>
-            </tr>
-          </tbody>
-        </table>
-
-        <table class="conclusion-table">
-          <tr>
-            <td class="label">结论</td>
-            <td>{{ conclusionLabel(form.qualityConclution) }}</td>
-            <td class="label">验收人</td>
-            <td>{{ field('qualityCheckUserName') }}</td>
-            <td class="label">验收时间</td>
-            <td>{{ field('qualityCheckTime') }}</td>
-          </tr>
-        </table>
-      </div>
-    </div>
-    <div slot="footer">
-      <el-button @click="print">打印预览</el-button>
-      <el-button @click="visible = false">关闭</el-button>
-    </div>
-  </ele-modal>
-</template>
-
-<script>
-  import dictMixins from '@/mixins/dictMixins';
-  import { checklistrecordGetById } from '@/api/checklistrecord/index';
-
-  export default {
-    name: 'ReleaseOrderPrint',
-    mixins: [dictMixins],
-    data() {
-      return {
-        visible: false,
-        form: {}
-      };
-    },
-    computed: {
-      orders() {
-        return Array.isArray(this.form.orders) ? this.form.orders : [];
-      },
-      workDetails() {
-        return this.details.filter((item) => item.checkType == 1);
-      },
-      qualityDetails() {
-        return this.details.filter((item) => item.checkType == 2);
-      },
-      details() {
-        return Array.isArray(this.form.details) ? this.form.details : [];
-      },
-      checklistTypeLabel() {
-        return this.getDictValue('放行类型', this.form.checklistType + '');
-      },
-      approvalStatusLabel() {
-        switch (this.form.approvalStatus) {
-          case 0:
-            return '未提交';
-          case 1:
-            return '审核中';
-          case 2:
-            return '审核通过';
-          case 3:
-            return '审核不通过';
-          default:
-            return '';
-        }
-      }
-    },
-    created() {
-      this.requestDict('放行类型');
-    },
-    methods: {
-      async open(row) {
-        this.form = row || {};
-        this.visible = true;
-        if (row?.id) {
-          try {
-            this.form = await checklistrecordGetById(row.id);
-          } catch (error) {
-            this.$message.error(error.message || '查询放行单详情失败');
-          }
-        }
-      },
-      field(key) {
-        const value = this.form[key];
-        return value === 0 || value ? value : '';
-      },
-      passLabel(value) {
-        if (value === 1) {
-          return '是';
-        }
-        if (value === 0) {
-          return '否';
-        }
-        return '';
-      },
-      conclusionLabel(value) {
-        if (value === 1) {
-          return '符合规定';
-        }
-        if (value === 0) {
-          return '不符合规定';
-        }
-        return '';
-      },
-      print() {
-        const el = document.getElementById('printSection_releaseOrderPrint');
-        const win = window.open('', '_blank');
-        win.document.open();
-        win.document.write('<html><head><title>放行单打印</title>');
-        win.document.write(`<style>${this.getPrintStyle()}</style>`);
-        win.document.write('</head><body>');
-        win.document.write(el.innerHTML);
-        win.document.write('</body></html>');
-        win.document.close();
-        win.onload = function () {
-          win.print();
-        };
-      },
-      getPrintStyle() {
-        return `
-          @page{size:A4 landscape;margin:10mm;}
-          body{margin:0;background:#fff;font-family:SimSun,serif;color:#000;font-size:12px;}
-          .release-print-page{box-sizing:border-box;width:100%;padding:8mm;background:#fff;}
-          .release-header{text-align:center;margin-bottom:8px;}
-          .company{font-size:18px;letter-spacing:4px;margin-bottom:6px;}
-          .title{font-size:20px;font-weight:bold;}
-          .section-title{font-size:15px;font-weight:bold;margin:12px 0 6px;border-left:4px solid #000;padding-left:8px;}
-          table{width:100%;border-collapse:collapse;table-layout:fixed;}
-          td,th{border:1px solid #000;padding:6px 8px;line-height:1.45;text-align:center;vertical-align:middle;word-break:break-all;}
-          th{font-weight:bold;background:#f5f5f5;}
-          .info-table .label,.conclusion-table .label{width:90px;font-weight:bold;background:#f5f5f5;}
-          .data-table .left{text-align:left;}
-          .rule-table td{min-height:34px;}
-          .conclusion-table{margin-top:-1px;}
-          .empty-cell{height:34px;color:#666;}
-        `;
-      }
-    }
-  };
-</script>
-
-<style scoped>
-  .release-print-page {
-    font-family: SimSun, serif;
-    background: #fff;
-    color: #000;
-    padding: 20px;
-    font-size: 12px;
-  }
-  .release-header {
-    text-align: center;
-    margin-bottom: 8px;
-  }
-  .company {
-    font-size: 18px;
-    letter-spacing: 4px;
-    margin-bottom: 6px;
-  }
-  .title {
-    font-size: 20px;
-    font-weight: bold;
-  }
-  .section-title {
-    font-size: 15px;
-    font-weight: bold;
-    margin: 12px 0 6px;
-    border-left: 4px solid #000;
-    padding-left: 8px;
-  }
-  table {
-    width: 100%;
-    border-collapse: collapse;
-    table-layout: fixed;
-  }
-  td,
-  th {
-    border: 1px solid #000;
-    padding: 6px 8px;
-    line-height: 1.45;
-    text-align: center;
-    vertical-align: middle;
-    word-break: break-all;
-  }
-  th {
-    font-weight: bold;
-    background: #f5f5f5;
-  }
-  .info-table .label,
-  .conclusion-table .label {
-    width: 90px;
-    font-weight: bold;
-    background: #f5f5f5;
-  }
-  .data-table .left {
-    text-align: left;
-  }
-  .conclusion-table {
-    margin-top: -1px;
-  }
-  .empty-cell {
-    height: 34px;
-    color: #666;
-  }
-</style>

+ 99 - 59
src/views/batchRecord/print/electrodeBatchRecord/unqualifiedProductReview.vue

@@ -34,7 +34,15 @@
           >表单编号:TR.D-060/ZL&nbsp;&nbsp;&nbsp;版本:B/3</div
         >
 
-        <table class="uqr-table">
+        <table class="uqr-table uqr-info-table">
+          <colgroup>
+            <col style="width: 21%" />
+            <col style="width: 18%" />
+            <col style="width: 16%" />
+            <col style="width: 10%" />
+            <col style="width: 17.5%" />
+            <col style="width: 17.5%" />
+          </colgroup>
           <tr>
             <td class="nowrap" colspan="4">
               不合格品类别
@@ -118,18 +126,27 @@
               <div class="confirm-sign">确认人/日期:</div>
             </td>
           </tr>
+        </table>
+
+        <table class="uqr-table review-table">
+          <colgroup>
+            <col style="width: 9%" />
+            <col style="width: 13%" />
+            <col style="width: 57%" />
+            <col style="width: 21%" />
+          </colgroup>
           <tr>
-            <td class="section-title" colspan="6">不合格评审</td>
+            <td class="section-title" colspan="4">不合格评审</td>
           </tr>
           <tr class="review-head">
             <td rowspan="6" class="review-label">评审<br />意见</td>
             <td>部门</td>
-            <td colspan="3">意见</td>
+            <td>意见</td>
             <td>评审人签字/日期</td>
           </tr>
           <tr v-for="n in 5" :key="'review_' + n" class="review-row">
             <td></td>
-            <td colspan="3"></td>
+            <td></td>
             <td></td>
           </tr>
         </table>
@@ -163,9 +180,13 @@
           >表单编号:TR.D-060/ZL&nbsp;&nbsp;&nbsp;版本:B/3</div
         >
 
-        <table class="uqr-table">
+        <table class="uqr-table approval-table">
+          <colgroup>
+            <col style="width: 63%" />
+            <col style="width: 37%" />
+          </colgroup>
           <tr>
-            <td class="approval-cell" colspan="4">
+            <td class="approval-cell">
               <div class="bold">管代审批:</div>
               <div class="approval-options">
                 <span class="check">□</span>退货
@@ -190,7 +211,7 @@
               ></div>
               <div class="approval-sign">签名/日期:</div>
             </td>
-            <td class="approval-cell" colspan="2">
+            <td class="approval-cell">
               <div class="bold">总经理/副总经理审批:</div>
               <div>(不适用于受托不合格品处置)</div>
               <div class="gm-options">
@@ -200,11 +221,19 @@
               <div class="approval-sign">签名/日期:</div>
             </td>
           </tr>
+        </table>
+
+        <table class="uqr-table record-table">
+          <colgroup>
+            <col style="width: 33.33%" />
+            <col style="width: 33.33%" />
+            <col style="width: 33.34%" />
+          </colgroup>
           <tr>
-            <td class="section-title" colspan="6">不合格品的处置记录</td>
+            <td class="section-title" colspan="3">不合格品的处置记录</td>
           </tr>
           <tr>
-            <td colspan="6" class="record-cell">
+            <td colspan="3" class="record-cell">
               <div>处理方法:</div>
               <div
                 ><span class="check">□</span>退货,退货日期:<span
@@ -256,9 +285,9 @@
             </td>
           </tr>
           <tr class="bottom-sign-row">
-            <td colspan="2">执行人/日期:</td>
-            <td colspan="2">执行部门负责人/日期:</td>
-            <td colspan="2">监督人(质量部)/日期:</td>
+            <td>执行人/日期:</td>
+            <td>执行部门负责人/日期:</td>
+            <td>监督人(质量部)/日期:</td>
           </tr>
         </table>
 
@@ -324,10 +353,10 @@
         return `
           @page{size:A4;margin:0;}
           body{margin:0;background:#fff;font-family:SimSun,serif;color:#000;}
-          .uqr-page{width:210mm;min-height:297mm;margin:0 auto;padding:12mm 18mm 14mm;box-sizing:border-box;position:relative;page-break-after:always;background:#fff;font-size:14px;}
+          .uqr-page{width:210mm;height:297mm;margin:0 auto;padding:12mm 18mm 10mm;box-sizing:border-box;position:relative;page-break-after:always;background:#fff;font-size:14px;}
           .uqr-page:last-child{page-break-after:auto;}
-          .uqr-header{height:28mm;display:flex;align-items:flex-start;justify-content:center;position:relative;padding-top:4mm;}
-          .uqr-logo{position:absolute;left:7mm;top:4mm;display:flex;align-items:center;color:#999;font-family:Arial,'Microsoft YaHei',sans-serif;}
+          .uqr-header{height:27mm;display:flex;align-items:flex-start;justify-content:center;position:relative;padding-top:3.5mm;}
+          .uqr-logo{position:absolute;left:6mm;top:3mm;display:flex;align-items:center;color:#9a9a9a;font-family:Arial,'Microsoft YaHei',sans-serif;opacity:.7;transform:scale(.88);transform-origin:left top;}
           .logo-mark{width:22px;height:24px;margin-right:6px;position:relative;}
           .logo-mark span{position:absolute;display:block;border-radius:2px;background:#86b958;}
           .logo-mark span:nth-child(1){width:6px;height:18px;left:6px;top:0;transform:rotate(20deg);}
@@ -335,12 +364,14 @@
           .logo-mark span:nth-child(3){width:6px;height:16px;left:1px;top:10px;transform:rotate(-35deg);background:#d6c95b;}
           .logo-cn{font-size:10px;font-weight:bold;line-height:1.2;}
           .logo-en{font-size:5px;line-height:1.2;}
-          .uqr-company{font-size:15px;letter-spacing:8px;margin-top:4mm;}
-          .uqr-title{text-align:center;font-size:20px;font-weight:bold;margin-top:-12mm;margin-bottom:5mm;color:#666;}
-          .uqr-meta{text-align:right;font-size:12px;margin-top:-5mm;margin-bottom:1mm;}
+          .uqr-company{font-size:15px;letter-spacing:8px;margin-top:3.5mm;}
+          .uqr-title{text-align:center;font-size:20px;font-weight:bold;margin-top:-12.5mm;margin-bottom:5.5mm;color:#666;}
+          .uqr-meta{text-align:right;font-size:12px;margin-top:-5.5mm;margin-bottom:1mm;}
           .uqr-table{width:100%;border-collapse:collapse;table-layout:fixed;}
+          .uqr-table+.uqr-table{margin-top:-1px;}
           .uqr-table td{border:1px solid #000;padding:4px 6px;vertical-align:top;line-height:1.55;word-break:break-all;}
           .section-title{text-align:center;font-weight:bold;font-size:16px;padding:2px 6px!important;line-height:1.2!important;}
+          .uqr-info-table tr:first-child td{height:26px;vertical-align:middle;}
           .nowrap{white-space:nowrap;}
           .check{font-family:SimSun,serif;margin:0 2px 0 7px;}
           .fill-line,.short-line,.long-line,.approval-line,.form-line{display:inline-block;border-bottom:1px solid #000;height:16px;vertical-align:baseline;}
@@ -348,40 +379,40 @@
           .short-line{width:90px;}
           .long-line{width:130px;}
           .center-row td{text-align:center;vertical-align:middle;}
-          .info-row td{height:42px;text-align:center;vertical-align:middle;}
+          .info-row td{height:54px;text-align:center;vertical-align:middle;}
           .source-cell{text-align:left!important;line-height:1.5!important;}
           .source-cell span{display:inline-block;}
-          .large-cell{height:278px;}
-          .write-area{min-height:108px;white-space:pre-wrap;}
-          .write-area.small{min-height:44px;}
+          .large-cell{height:342px;}
+          .write-area{min-height:146px;white-space:pre-wrap;}
+          .write-area.small{min-height:64px;}
           .suggestion{margin-top:2px;}
           .signature-row{display:flex;justify-content:space-between;margin-top:14px;}
-          .confirm-cell{height:84px;}
-          .confirm-sign{text-align:center;margin-top:42px;}
+          .confirm-cell{height:104px;}
+          .confirm-sign{text-align:center;margin-top:50px;}
           .review-head td{text-align:center;vertical-align:middle;}
-          .review-label{text-align:center!important;vertical-align:middle!important;width:64px;font-size:15px;}
-          .review-row td{height:32px;}
+          .review-label{text-align:center!important;vertical-align:middle!important;font-size:15px;}
+          .review-row td{height:40px;}
           .page-number{position:absolute;bottom:10mm;left:0;right:0;text-align:center;color:#666;font-size:12px;}
           .crop{position:absolute;width:8mm;height:8mm;}
           .crop-tl{left:13mm;top:13mm;border-left:1px solid #999;border-bottom:1px solid #999;}
           .crop-tr{right:13mm;top:13mm;border-right:1px solid #999;border-bottom:1px solid #999;}
           .crop-bl{left:13mm;bottom:13mm;border-left:1px solid #999;border-top:1px solid #999;}
           .crop-br{right:13mm;bottom:13mm;border-right:1px solid #999;border-top:1px solid #999;}
-          .approval-cell{height:145px;}
+          .approval-cell{height:204px;}
           .bold{font-weight:bold;}
           .approval-options{margin:10px 0;}
           .approval-line{width:130px;margin-left:5px;}
           .approval-line.mid{width:165px;}
           .indent-line{padding-left:14px;}
-          .approval-sign{margin-top:28px;text-align:left;padding-left:20px;}
-          .gm-options{text-align:center;margin:16px 0 46px;}
-          .record-cell{height:510px;font-size:14px;}
-          .record-cell div{margin-bottom:13px;}
+          .approval-sign{margin-top:34px;text-align:left;padding-left:20px;}
+          .gm-options{text-align:center;margin:20px 0 54px;}
+          .record-cell{height:516px;font-size:14px;}
+          .record-cell div{margin-bottom:14px;}
           .form-line{width:160px;margin:0 5px;}
           .form-line.wide{width:220px;}
-          .form-line.full{width:520px;}
+          .form-line.full{width:560px;}
           .form-line.short{width:160px;}
-          .bottom-sign-row td{height:56px;vertical-align:top;}
+          .bottom-sign-row td{height:72px;vertical-align:top;}
           .uqr-note{font-size:12px;margin-top:4px;}
         `;
       }
@@ -392,9 +423,9 @@
 <style scoped>
   .uqr-page {
     width: 210mm;
-    min-height: 297mm;
+    height: 297mm;
     margin: 0 auto 18px;
-    padding: 12mm 18mm 14mm;
+    padding: 12mm 18mm 10mm;
     box-sizing: border-box;
     position: relative;
     page-break-after: always;
@@ -408,21 +439,24 @@
     page-break-after: auto;
   }
   .uqr-header {
-    height: 28mm;
+    height: 27mm;
     display: flex;
     align-items: flex-start;
     justify-content: center;
     position: relative;
-    padding-top: 4mm;
+    padding-top: 3.5mm;
   }
   .uqr-logo {
     position: absolute;
-    left: 7mm;
-    top: 4mm;
+    left: 6mm;
+    top: 3mm;
     display: flex;
     align-items: center;
-    color: #999;
+    color: #9a9a9a;
     font-family: Arial, 'Microsoft YaHei', sans-serif;
+    opacity: 0.7;
+    transform: scale(0.88);
+    transform-origin: left top;
   }
   .logo-mark {
     width: 22px;
@@ -471,20 +505,20 @@
   .uqr-company {
     font-size: 15px;
     letter-spacing: 8px;
-    margin-top: 4mm;
+    margin-top: 3.5mm;
   }
   .uqr-title {
     text-align: center;
     font-size: 20px;
     font-weight: bold;
-    margin-top: -12mm;
-    margin-bottom: 5mm;
+    margin-top: -12.5mm;
+    margin-bottom: 5.5mm;
     color: #666;
   }
   .uqr-meta {
     text-align: right;
     font-size: 12px;
-    margin-top: -5mm;
+    margin-top: -5.5mm;
     margin-bottom: 1mm;
   }
   .uqr-table {
@@ -492,6 +526,9 @@
     border-collapse: collapse;
     table-layout: fixed;
   }
+  .uqr-table + .uqr-table {
+    margin-top: -1px;
+  }
   .uqr-table td {
     border: 1px solid #000;
     padding: 4px 6px;
@@ -506,6 +543,10 @@
     padding: 2px 6px !important;
     line-height: 1.2 !important;
   }
+  .uqr-info-table tr:first-child td {
+    height: 26px;
+    vertical-align: middle;
+  }
   .nowrap {
     white-space: nowrap;
   }
@@ -538,7 +579,7 @@
     vertical-align: middle;
   }
   .info-row td {
-    height: 42px;
+    height: 54px;
     text-align: center;
     vertical-align: middle;
   }
@@ -550,14 +591,14 @@
     display: inline-block;
   }
   .large-cell {
-    height: 278px;
+    height: 342px;
   }
   .write-area {
-    min-height: 108px;
+    min-height: 146px;
     white-space: pre-wrap;
   }
   .write-area.small {
-    min-height: 44px;
+    min-height: 64px;
   }
   .suggestion {
     margin-top: 2px;
@@ -568,11 +609,11 @@
     margin-top: 14px;
   }
   .confirm-cell {
-    height: 84px;
+    height: 104px;
   }
   .confirm-sign {
     text-align: center;
-    margin-top: 42px;
+    margin-top: 50px;
   }
   .review-head td {
     text-align: center;
@@ -581,11 +622,10 @@
   .review-label {
     text-align: center !important;
     vertical-align: middle !important;
-    width: 64px;
     font-size: 15px;
   }
   .review-row td {
-    height: 32px;
+    height: 40px;
   }
   .page-number {
     position: absolute;
@@ -626,7 +666,7 @@
     border-top: 1px solid #999;
   }
   .approval-cell {
-    height: 145px;
+    height: 204px;
   }
   .bold {
     font-weight: bold;
@@ -645,20 +685,20 @@
     padding-left: 14px;
   }
   .approval-sign {
-    margin-top: 28px;
+    margin-top: 34px;
     text-align: left;
     padding-left: 20px;
   }
   .gm-options {
     text-align: center;
-    margin: 16px 0 46px;
+    margin: 20px 0 54px;
   }
   .record-cell {
-    height: 510px;
+    height: 516px;
     font-size: 14px;
   }
   .record-cell div {
-    margin-bottom: 13px;
+    margin-bottom: 14px;
   }
   .form-line {
     width: 160px;
@@ -668,13 +708,13 @@
     width: 220px;
   }
   .form-line.full {
-    width: 520px;
+    width: 560px;
   }
   .form-line.short {
     width: 160px;
   }
   .bottom-sign-row td {
-    height: 56px;
+    height: 72px;
     vertical-align: top;
   }
   .uqr-note {

+ 620 - 0
src/views/inspectionReport/components/reportTemplateWrapper.vue

@@ -0,0 +1,620 @@
+<template>
+  <ele-modal
+    :visible="visible"
+    width="70%"
+    append-to-body
+    title="检测报告"
+    @close="cancel"
+    :maxable="true"
+  >
+    <div class="switch">
+      <div class="switch_left">
+        <ul>
+          <li
+            v-for="item in tabOptions"
+            :key="item.key"
+            :class="{ active: activeComp == item.key }"
+            @click="activeComp = item.key"
+          >
+            {{ item.name }}
+          </li>
+        </ul>
+      </div>
+    </div>
+    <component
+      :is="targetComponent"
+      ref="templateRef"
+      v-if="visible"
+      v-show="activeComp == 'main'"
+      :key="componentName"
+      :isView="isView"
+      :type="type"
+    ></component>
+
+    <template v-slot:footer>
+      <el-button v-if="isView" type="primary" @click="print">打印</el-button>
+      <el-button v-if="!isView" type="primary" @click="save">确认</el-button>
+      <el-button
+        v-if="!isView && isReportApproval == 1"
+        type="primary"
+        @click="save('submit')"
+        >提交</el-button
+      >
+      <el-button @click="cancel">关闭</el-button>
+    </template>
+
+    <unqualifiedProduct
+      ref="unqualifiedProductRef"
+      v-show="activeComp == 'unqualifiedList'"
+      @setQualifiedNumber="setQualifiedNumber"
+      :total="basicInfoData.total"
+      :isView="isView"
+      @setLossNumber="setLossNumber"
+    >
+    </unqualifiedProduct>
+    <div style="margin-top: 10px" v-if="activeComp != 'bpm'">
+      结论:
+      <el-select
+        v-model="basicInfoData.qualityResults"
+        placeholder="请选择"
+        style="width: 120px"
+        @change="selectResult"
+        :disabled="isView"
+      >
+        <el-option label="整单合格" :value="1" />
+        <el-option label="整单不合格" :value="2" />
+        <el-option label="部分合格" :value="3" />
+        <el-option label="整单让步接收" :value="4" />
+      </el-select>
+
+      合格数:
+      <el-input
+        v-model="basicInfoData.qualifiedNumber"
+        placeholder="合格数"
+        style="width: 110px"
+        disabled
+      ></el-input>
+
+      不合格数:
+      <el-input
+        v-model="basicInfoData.noQualifiedNumber"
+        placeholder="不合格数"
+        disabled
+        style="width: 110px"
+      ></el-input>
+      留样数(合格):
+      <el-input
+        v-model="basicInfoData.retainedSampleQuantity"
+        placeholder=" "
+        disabled
+        style="width: 80px"
+      ></el-input>
+      留样数(不合格):
+      <el-input
+        v-model="basicInfoData.retainedSampleUnqualified"
+        placeholder=" "
+        disabled
+        style="width: 80px"
+      ></el-input>
+      消耗数(合格):
+      <el-input
+        v-model="basicInfoData.lossNumber"
+        placeholder=" "
+        disabled
+        style="width: 80px"
+      ></el-input>
+      消耗数(不合格):
+      <el-input
+        v-model="basicInfoData.lossNumberUnqualified"
+        placeholder=" "
+        disabled
+        style="width: 80px"
+      ></el-input>
+    </div>
+    <bpmDetail
+      v-if="activeComp === 'bpm' && processInstanceId"
+      :id="processInstanceId"
+    ></bpmDetail>
+
+    <processSubmitDialog
+      :processSubmitDialogFlag.sync="processSubmitDialogFlag"
+      v-if="processSubmitDialogFlag"
+      ref="processSubmitDialogRef"
+      @reload="search"
+    ></processSubmitDialog>
+  </ele-modal>
+</template>
+
+<script>
+  import inspection_report1 from '../template/inspection_report1.vue';
+  import inspection_report2 from '../template/inspection_report2.vue';
+  import inspection_report3 from '../template/inspection_report3.vue';
+  import processSubmitDialog from '@/components/processSubmitDialog/processSubmitDialog.vue';
+  import { getCategoryByCode } from '@/api/main/index';
+  import { parameterGetByCode } from '@/api/system/dictionary-data';
+
+  import {
+    queryInspectionReportData,
+    queryInspectionReportList,
+    generateReport,enterprisePage
+  } from '@/api/inspectionReport';
+  import bpmDetail from '@/views/bpm/processInstance/detail.vue';
+  import unqualifiedProduct from '@/views/inspectionReport/components/unqualifiedProduct.vue';
+  import { getList } from '@/api/unacceptedProduct/index';
+  import { getCode } from '@/api/login';
+  const defForm = {
+    poList: [],
+    unqualifiedProductsCode: '',
+    sourceCode: '',
+    batchNo: '',
+    brandNum: '',
+    categoryCode: '',
+    categoryId: '',
+    categoryName: '',
+    specification: '',
+    modelType: '',
+    produceRoutingId: '',
+    produceRoutingName: '',
+    taskId: '',
+    taskName: '',
+    quantity: '',
+    measureQuantity: '',
+    measureUnit: '',
+    weight: '',
+    weightUnit: '',
+    qualityType: ''
+  };
+  export default {
+    name: 'ReportTemplateWrapper',
+    components: {
+      inspection_report1,
+      inspection_report2,
+      inspection_report3,
+      unqualifiedProduct,
+      bpmDetail,
+      processSubmitDialog
+    },
+
+    data() {
+      return {
+        unqualifiedProducts: {
+          ...defForm
+        },
+        isReportApproval: 0,
+        isView: false,
+        visible: false,
+        loading: false,
+        processSubmitDialogFlag: false,
+        currentRow: {}, // 存储当前行数据
+        templateItem: {}, // 存储当前模板项数据
+        // 基本信息数据
+        basicInfoData: {},
+        componentName: '',
+        // 检验项目
+        inspectionItems: [],
+        tabOptions: [],
+        activeComp: 'main',
+        processInstanceId: '',
+        reportApprovalTaskVos: [],
+        groupName: ''
+      };
+    },
+    props: {
+      type: {
+        type: String,
+        default: '0'
+      }
+    },
+    methods: {
+      /* 打开质检报告 */
+      async open({ row, item }, isView) {
+        parameterGetByCode({
+          code: 'qms_report_approval'
+        }).then((res) => {
+          this.isReportApproval = res.value;
+        });
+        this.activeComp = 'main';
+        this.isView = isView;
+        this.componentName = row.reportTemplateCode;
+        this.currentRow = row;
+        this.templateItem = item?.id ? item : row;
+
+        this.processInstanceId = row.reportProcessInstanceId;
+        this.reportApprovalTaskVos = row.reportApprovalTaskVos;
+        this.tabOptions = [{ key: 'main', name: '业务详情' }];
+        if (isView) {
+          this.tabOptions.push({
+            key: 'unqualifiedList',
+            name: '不合格品台账'
+          });
+        }
+
+        if (this.processInstanceId) {
+          this.tabOptions.push({ key: 'bpm', name: '流程详情' });
+        }
+
+        this.visible = true;
+        if (this.currentRow.reportTemplateJson?.basicInfoData) {
+          this.basicInfoData =
+            this.currentRow.reportTemplateJson.basicInfoData || {};
+          this.inspectionItems =
+            this.currentRow.reportTemplateJson.inspectionItems || [];
+
+          const reviewTime =
+            this.reportApprovalTaskVos?.[
+              this.reportApprovalTaskVos.length - 2
+            ]?.endTime?.split(' ')[0] || '';
+          const approvedDate =
+            this.reportApprovalTaskVos?.[
+              this.reportApprovalTaskVos.length - 3
+            ]?.endTime?.split(' ')[0] || '';
+          const reviewer =
+            this.reportApprovalTaskVos?.[this.reportApprovalTaskVos.length - 2]
+              ?.approvalUserName || '';
+          const checker =
+            this.reportApprovalTaskVos?.[this.reportApprovalTaskVos.length - 3]
+              ?.approvalUserName || '';
+
+          this.$set(
+            this.basicInfoData,
+            'inspectionTime',
+            this.basicInfoData.inspectionTime?.split(' ')[0] || ''
+          );
+          this.$set(
+            this.basicInfoData,
+            'reviewTime',
+            this.basicInfoData.reviewTime?.split(' ')[0] || reviewTime || ''
+          );
+          this.$set(
+            this.basicInfoData,
+            'approvedDate',
+            this.basicInfoData.approvedDate?.split(' ')[0] || approvedDate || ''
+          );
+          this.$set(
+            this.basicInfoData,
+            'reviewer',
+            this.basicInfoData.reviewer || reviewer || ''
+          );
+          this.$set(
+            this.basicInfoData,
+            'checker',
+            this.basicInfoData.checker || checker || ''
+          );
+          this.$set(
+            this.basicInfoData,
+            'qualityResults',
+            this.basicInfoData.qualityResults
+          );
+          this.$set(
+            this.basicInfoData,
+            'qualifiedNumber',
+            this.basicInfoData.qualifiedNumber
+          );
+          this.$set(
+            this.basicInfoData,
+            'noQualifiedNumber',
+            this.basicInfoData.noQualifiedNumber
+          );
+          this.$nextTick(() => {
+            this.$refs.templateRef.init(
+              this.basicInfoData,
+              this.inspectionItems
+            );
+          });
+          this.initUnqualifiedProduct();
+        } else {
+          await this.getBasicInfo();
+          await this.getInspectionItems();
+          await this.enterprisePage();
+
+          this.$nextTick(() => {
+            this.$refs.templateRef.init(
+              this.basicInfoData,
+              this.inspectionItems
+            );
+          });
+        }
+      },
+
+      async initUnqualifiedProduct() {
+        const data = await getList({
+          sourceCode: this.currentRow.code
+        });
+        if (data.list[0]) {
+          this.unqualifiedProducts = data.list[0];
+        }
+
+        this.$nextTick(() => {
+          this.$refs.unqualifiedProductRef.init({
+            id: this.unqualifiedProducts?.id,
+            qualityType: this.basicInfoData.qualityType,
+            workOrderCode: this.currentRow.code,
+            qualityWorkerId: this.basicInfoData.qualityWorkerId,
+            workOrderId: this.currentRow.workOrderId,
+            qualityResults: this.basicInfoData.qualityResults
+          });
+        });
+      },
+
+      setQualifiedNumber(data) {
+        this.basicInfoData.qualifiedNumber = data.qualifiedNumber;
+        this.basicInfoData.noQualifiedNumber = data.noQualifiedNumber;
+        if (!data.noQualifiedNumber) {
+          if (data.isQualifiedNumber) {
+            this.basicInfoData.qualityResults = 1;
+          } else {
+            this.basicInfoData.qualityResults = 4;
+          }
+        }
+        if (!data.qualifiedNumber) {
+          this.basicInfoData.qualityResults = 2;
+        }
+
+        if (data.qualifiedNumber && data.noQualifiedNumber) {
+          this.basicInfoData.qualityResults = 3;
+        }
+      },
+
+      selectResult() {
+        this.$refs.unqualifiedProductRef.selectResult(
+          this.basicInfoData.qualityResults
+        );
+      },
+      getBasicInfo() {
+        return queryInspectionReportData({
+          id: this.currentRow.id,
+          type: this.type || this.currentRow.type || 0
+        }).then((res) => {
+          this.basicInfoData = res;
+          this.basicInfoData.reportCode = res.reportCode || '';
+
+          const reviewTime =
+            this.reportApprovalTaskVos?.[
+              this.reportApprovalTaskVos.length - 2
+            ]?.endTime?.split(' ')[0] || '';
+          const approvedDate =
+            this.reportApprovalTaskVos?.[
+              this.reportApprovalTaskVos.length - 3
+            ]?.endTime?.split(' ')[0] || '';
+          const reviewer =
+            this.reportApprovalTaskVos?.[this.reportApprovalTaskVos.length - 2]
+              ?.approvalUserName || '';
+          const checker =
+            this.reportApprovalTaskVos?.[this.reportApprovalTaskVos.length - 3]
+              ?.approvalUserName || '';
+          this.$set(
+            this.basicInfoData,
+            'version',
+            this.templateItem.versionSymbol +
+              this.templateItem.bigVersion +
+              this.templateItem.versionMark +
+              this.templateItem.smallVersion
+          );
+          this.$set(
+            this.basicInfoData,
+            'inspectionTime',
+            this.basicInfoData.inspectionTime?.split(' ')[0] || ''
+          );
+          this.$set(
+            this.basicInfoData,
+            'reviewTime',
+            this.basicInfoData.reviewTime?.split(' ')[0] || reviewTime || ''
+          );
+          this.$set(
+            this.basicInfoData,
+            'approvedDate',
+            this.basicInfoData.approvedDate?.split(' ')[0] || approvedDate || ''
+          );
+          this.$set(
+            this.basicInfoData,
+            'reviewer',
+            this.basicInfoData.reviewer || reviewer || ''
+          );
+          this.$set(
+            this.basicInfoData,
+            'checker',
+            this.basicInfoData.checker || checker || ''
+          );
+
+          this.$set(
+            this.basicInfoData,
+            'qualityResults',
+            this.currentRow.noQualifiedNumber == this.basicInfoData.total
+              ? 2
+              : this.currentRow.noQualifiedNumber == 0
+              ? 1
+              : 3
+          );
+          this.$set(
+            this.basicInfoData,
+            'qualifiedNumber',
+            this.currentRow.qualifiedNumber
+          );
+          this.$set(
+            this.basicInfoData,
+            'noQualifiedNumber',
+            this.currentRow.noQualifiedNumber
+          );
+          this.$set(
+            this.basicInfoData,
+            'noQualifiedNumber',
+            this.currentRow.noQualifiedNumber
+          );
+
+          this.$set(this.basicInfoData, 'qualityWorkerId', this.currentRow.id);
+          this.$set(
+            this.basicInfoData,
+            'workOrderId',
+            this.currentRow.workOrderId
+          );
+          this.$set(this.basicInfoData, 'type', this.type || 0);
+          this.$set(
+            this.basicInfoData,
+            'qualityType',
+            this.currentRow.qualityType
+          );
+          if (!this.basicInfoData.reportNumber) {
+            getCode('quality_Inspection_report_number').then((res) => {
+              this.$set(this.basicInfoData, 'reportNumber', res);
+            });
+          }
+          this.initUnqualifiedProduct();
+        });
+      },
+      getInspectionItems() {
+        return queryInspectionReportList({
+          id: this.currentRow.id,
+          type: this.type || this.currentRow.type || 0
+        }).then((res) => {
+          this.inspectionItems = res;
+        });
+      },
+      enterprisePage() {
+        return enterprisePage({
+          pageNum: 1,
+          size: 200
+        }).then((res) => {
+          if (res.list?.length > 0) {
+            this.$set(this.basicInfoData, 'groupName', res.list[0].name);
+          }
+        });
+      },
+
+      setLossNumber({
+        lossNumber,
+        lossNumberUnqualified,
+        retainedSampleQuantity,
+        retainedSampleUnqualified
+      }) {
+        this.$set(
+          this.basicInfoData,
+          'retainedSampleQuantity',
+          retainedSampleQuantity
+        );
+        this.$set(
+          this.basicInfoData,
+          'retainedSampleUnqualified',
+          retainedSampleUnqualified
+        );
+        this.$set(this.basicInfoData, 'lossNumber', lossNumber);
+        this.$set(
+          this.basicInfoData,
+          'lossNumberUnqualified',
+          lossNumberUnqualified
+        );
+      },
+      cancel() {
+        this.visible = false;
+        this.$emit('close');
+        this.unqualifiedProducts = { ...defForm };
+        this.basicInfoData = {};
+      },
+      /* 打印 */
+      print() {
+        const printSection = document.getElementById('printSection');
+        console.log('printSection', printSection.innerHTML);
+        // 创建打印任务
+        const printWindow = window.open('', '_blank');
+        printWindow.document.open();
+        printWindow.document.write('<html><head><title>打印预览</title>');
+
+        printWindow.document.write('</head><body>');
+        printWindow.document.write(printSection.innerHTML);
+        printWindow.document.write('</body></html>');
+        printWindow.document.close();
+        printWindow.onload = function () {
+          printWindow.print();
+        };
+      },
+      /* 保存编辑 */
+      save(type) {
+        this.loading = true;
+        this.isView = true;
+
+        this.$nextTick(() => {
+          const printSection = document.getElementById('printSection');
+
+          const params = {
+            id: this.currentRow.id,
+            type: this.type || this.basicInfoData.type || 0,
+            reportTemplateId:
+              this.templateItem.reportTemplateId || this.templateItem.id,
+            reportTemplateCode:
+              this.templateItem.reportTemplateCode || this.templateItem.code,
+            reportTemplateName:
+              this.templateItem.reportTemplateName || this.templateItem.name,
+            unqualifiedProducts: this.$refs.unqualifiedProductRef.getValue(),
+            reportTemplateJson: {
+              template: printSection.innerHTML,
+              basicInfoData: this.$refs.templateRef.getValue(),
+              inspectionItems: this.inspectionItems
+            }
+          };
+
+          generateReport(params)
+            .then((res) => {
+              this.loading = false;
+              this.isView = false;
+
+              this.$message({
+                message: '保存成功',
+                type: 'success'
+              });
+
+              if (type == 'submit') {
+                this.approvalSubmit(this.currentRow.id);
+              } else {
+                this.cancel();
+                this.$emit('reload');
+              }
+            })
+            .catch((err) => {
+              this.loading = false;
+              this.isView = false;
+
+              this.$message({
+                message: err.message,
+                type: 'error'
+              });
+            });
+        });
+      },
+      approvalSubmit(id) {
+        this.processSubmitDialogFlag = true;
+        this.$nextTick(async () => {
+          let params = {
+            businessId: id,
+            businessKey: 'qms_report_approval',
+            // formCreateUserId: this.basicInfoData.createUserId,
+            variables: {
+              businessCode: this.basicInfoData.code,
+              businessName: this.basicInfoData.reportNumber,
+              businessType: '检测报告单'
+            }
+          };
+          if (this.clientEnvironmentId == 5) {
+              const data = await getCategoryByCode(this.currentRow.productCode);
+            if (data && data.categoryLevelCodePath?.includes('W3-209')) {
+              params.businessKey = 'qms_report_approval1';
+            } else {
+              params.businessKey = 'qms_report_approval';
+            }
+          }
+          this.$refs.processSubmitDialogRef.init(params);
+        });
+      },
+      search() {
+        this.$emit('reload');
+        this.cancel();
+      }
+    },
+    computed: {
+      clientEnvironmentId() {
+        return this.$store.state.user.info.clientEnvironmentId;
+      },
+      targetComponent() {
+        return this.componentName;
+      }
+    }
+  };
+</script>

+ 1115 - 0
src/views/inspectionReport/components/unqualifiedProduct.vue

@@ -0,0 +1,1115 @@
+<template>
+  <div class="ele-body">
+    <el-card shadow="never" v-loading="loading">
+      <ele-pro-table
+        ref="table"
+        :columns="columns"
+        :datasource="
+          isView ? poList : poList.filter((item) => item.disposalStatus != 2)
+        "
+        :selection.sync="selection"
+        :needPage="false"
+        row-key="id"
+      >
+        <!-- 操作列 -->
+        <template v-slot:toolbar v-if="!isView">
+          <el-button type="primary" slot="reference" @click="add"
+            >新增</el-button
+          >
+          <el-button
+            type="primary"
+            slot="reference"
+            :disabled="selection.length == 0"
+            @click="disposeFn(1)"
+            >批量处置</el-button
+          >
+
+          <el-popconfirm
+            class="ele-action"
+            title="确定要删除吗?"
+            @confirm="remove"
+            style="margin-left: 10px"
+          >
+            <template v-slot:reference>
+              <el-button :disabled="selection.length == 0" type="danger"
+                >批量删除</el-button
+              >
+            </template>
+          </el-popconfirm>
+        </template>
+
+        <template v-slot:badTypeName="{ row, $index }">
+          <el-select
+            v-model="row.badTypeId"
+            placeholder="请选择不良类型"
+            size="small"
+            style="width: 100%"
+            remote
+            filterable
+            clearable
+          >
+            <el-option
+              v-for="item in badTypeList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+              @click="row.badTypeName = item.name"
+            >
+            </el-option>
+          </el-select>
+        </template>
+
+        <template v-slot:badNameName="{ row, $index }">
+          <el-select
+            v-model="row.badNameId"
+            placeholder="请选择不良名称"
+            size="small"
+            style="width: 100%"
+            remote
+            filterable
+            clearable
+          >
+            <el-option
+              v-for="item in badNameList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+              @click="row.badNameName = item.name"
+            >
+            </el-option>
+          </el-select>
+        </template>
+        <template v-slot:reasonTypeName="{ row, $index }">
+          <el-select
+            v-model="row.reasonTypeId"
+            placeholder="请选择原因类型"
+            size="small"
+            style="width: 100%"
+            remote
+            filterable
+            clearable
+          >
+            <el-option
+              v-for="item in reasonTypeList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+              @click="row.reasonTypeName = item.name"
+            >
+            </el-option>
+          </el-select>
+        </template>
+        <template v-slot:unqualifiedReason="{ row, $index }">
+          <el-input v-model="row.unqualifiedReason"></el-input>
+        </template>
+      </ele-pro-table>
+    </el-card>
+
+    <ele-modal
+      :visible.sync="dialogVisible"
+      width="35%"
+      @close="close"
+      append-to-body
+      title="处置"
+    >
+      <el-form
+        ref="disposeForm"
+        :model="disposeForm"
+        label-width="150px"
+        :rules="rules"
+      >
+        <el-form-item label="处置方式:" prop="disposeType">
+          <el-select
+            v-model="disposeForm.disposeType"
+            placeholder="请选择"
+            style="width: 100%"
+            @change="disposeTypeChange"
+          >
+            <el-option
+              v-for="item in disposeList"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+
+        <template
+          v-if="disposeForm.disposeType == 1 || disposeForm.disposeType == 2"
+        >
+          <el-form-item label="回流工序" prop="taskId" align="center">
+            <el-select
+              style="width: 100%"
+              v-model="disposeForm.taskId"
+              clearable
+            >
+              <el-option
+                v-for="item in refluxTaskList"
+                :key="item.taskId"
+                :value="item.taskId"
+                :label="item.taskTypeName"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+        </template>
+
+        <template v-if="disposeForm.disposeType == 6">
+          <el-row>
+            <el-col :span="24">
+              <el-form-item
+                label="留样数量:"
+                prop="keepSampleQuantity"
+                align="center"
+              >
+                <el-input
+                  v-model="disposeForm.keepSampleQuantity"
+                  placeholder="请输入"
+                  style="width: 100%"
+                >
+                  <template slot="append">{{
+                    current?.measureUnit
+                  }}</template></el-input
+                >
+              </el-form-item>
+            </el-col>
+            <el-col :span="6"> </el-col>
+          </el-row>
+          <el-row style="margin-top: 12px">
+            <el-col :span="24">
+              <el-form-item label="留样日期:" prop="sampleDate" align="center">
+                <el-date-picker
+                  class="w100"
+                  v-model="disposeForm.sampleDate"
+                  type="date"
+                  value-format="yyyy-MM-dd"
+                  placeholder="请输入"
+                ></el-date-picker>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6"> </el-col>
+          </el-row>
+          <el-row style="margin-top: 12px">
+            <el-col :span="24">
+              <el-form-item
+                label="留样条件:"
+                prop="sampleCondition"
+                align="center"
+              >
+                <el-input
+                  v-model="disposeForm.sampleCondition"
+                  placeholder="请输入"
+                  style="width: 100%"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row style="margin-top: 12px">
+            <el-col :span="24">
+              <el-form-item
+                label="生产商/受托生产:"
+                prop="producerManufacturer"
+                align="center"
+              >
+                <el-input
+                  v-model="disposeForm.producerManufacturer"
+                  placeholder="请输入"
+                  style="width: 100%"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row style="margin-top: 12px">
+            <el-col :span="24">
+              <el-form-item
+                label="留样地点:"
+                prop="samplePlace"
+                align="center"
+              >
+                <el-input
+                  type="textarea"
+                  v-model="disposeForm.samplePlace"
+                  placeholder="请输入"
+                  style="width: 100%"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row style="margin-top: 12px">
+            <el-col :span="24">
+              <el-form-item
+                label="留样备注:"
+                prop="sampleRemark"
+                align="center"
+              >
+                <el-input
+                  type="textarea"
+                  v-model="disposeForm.sampleRemark"
+                  placeholder="请输入"
+                  style="width: 100%"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+          </el-row>
+
+          <el-row style="margin-top: 12px">
+            <el-col :span="24"> </el-col>
+          </el-row>
+        </template>
+
+        <template
+          v-if="
+            disposeForm.disposeType == 3 ||
+            disposeForm.disposeType == 6 ||
+            disposeForm.disposeType == 9
+          "
+        >
+          <el-form-item label="入库仓库:" prop="depotId" align="center">
+            <el-select style="width: 100%" v-model="disposeForm.depotId">
+              <el-option
+                v-for="item in warehouseList"
+                :key="item.id"
+                :value="item.id"
+                :label="item.name"
+                @click.native="chooseWarehouse(item)"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+        </template>
+      </el-form>
+
+      <!-- <DictSelection
+        dictName="不良品处理类型"
+        :filterArr="all ? ['返工返修'] : []"
+        v-model="current.disposalStatus"
+      /> -->
+
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="dialogVisible = false">取 消 </el-button>
+        <el-button type="primary" @click="handleDispose">确 定</el-button>
+      </div>
+    </ele-modal>
+
+    <ele-modal
+      :visible.sync="addDialogVisible"
+      width="60%"
+      append-to-body
+      title="选择"
+    >
+      <el-table
+        :data="inventoryList"
+        @selection-change="handleAddSelectionChange"
+        row-key="id"
+        ref="table1"
+      >
+        <el-table-column
+          type="selection"
+          width="55"
+          align="center"
+          :selectable="
+            (row, index) => {
+              return !poList
+                .map((item) => item.sourceId)
+                .includes(row.sourceId);
+            }
+          "
+        ></el-table-column>
+        <el-table-column
+          type="index"
+          label="序号"
+          width="55"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="sampleCode"
+          label="样品编码"
+          width="150"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="packageNo"
+          label="包装编码"
+          width="150"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="categoryCode"
+          label="物品编码"
+          width="150"
+          align="center"
+        ></el-table-column>
+        <el-table-column
+          prop="categoryName"
+          label="物品名称"
+          width="150"
+          align="center"
+          show-overflow-tooltip
+        ></el-table-column>
+        <el-table-column
+          prop="specification"
+          label="规格"
+          width="120"
+          align="center"
+        ></el-table-column>
+        <el-table-column prop="measureQuantity" label="计量数量" align="center">
+          <template slot-scope="scope">
+            <template v-if="inventoryList.length === 1">
+              <el-input-number
+                v-model="singleQuantity"
+                :min="0"
+                :max="availableTotal"
+                style="width: 120px"
+              ></el-input-number>
+            </template>
+            <template v-else>
+              {{ scope.row.measureQuantity }}
+            </template>
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="measureUnit"
+          label="计量单位"
+          align="center"
+        ></el-table-column>
+      </el-table>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="addDialogVisible = false">取 消</el-button>
+        <el-button type="primary" @click="handleAddSave">确 定</el-button>
+      </div>
+    </ele-modal>
+  </div>
+</template>
+
+<script>
+  import Edit from '@/views/unacceptedProduct/components/edit.vue';
+  import { getWarehouseList } from '@/api/sample';
+  import dictMixins from '@/mixins/dictMixins';
+  import tabMixins from '@/mixins/tableColumnsMixin';
+  import { getById } from '@/api/unacceptedProduct/index';
+  import {
+    queryQualitySamplContent,
+    queryQualityInventory
+  } from '@/api/inspectionWork';
+  import { getList as getBadNameList } from '@/api/unacceptedProduct/unqualifiedName';
+  import { getList as getBadTypeList } from '@/api/unacceptedProduct/unqualifiedType';
+  import { getList as getReasonTypeList } from '@/api/unacceptedProduct/reasonType';
+  export default {
+    components: {
+      Edit
+    },
+    props: {
+      total: {
+        type: Number,
+        default: 0
+      },
+      isView: {
+        type: Boolean,
+        default: false
+      }
+    },
+    mixins: [dictMixins, tabMixins],
+    data() {
+      return {
+        cacheKeyUrl: 'qsm-c2e9664a-unqualifiedList-detailList',
+        key: '',
+        warehouseList: [],
+        rules: {
+          disposeType: [
+            { required: true, message: '请选择处置方式', trigger: 'change' }
+          ],
+          depotId: [
+            { required: true, message: '请选择仓库', trigger: 'change' }
+          ],
+          keepSampleQuantity: [
+            {
+              validator: (rule, value, callback) => {
+                if (
+                  this.disposeForm.disposeType === 6 &&
+                  this.current &&
+                  this.current.measureQuantity !== undefined
+                ) {
+                  const inputVal = Number(value);
+                  const maxVal = Number(this.current.measureQuantity);
+                  if (inputVal > maxVal) {
+                    return callback(new Error(`不能超过原计量数量${maxVal}`));
+                  }
+                }
+                callback();
+              },
+              trigger: 'blur'
+            }
+          ]
+        },
+        disposeType: '',
+        disposeForm: {
+          disposeType: '',
+          sampleCondition: '',
+          sampleDate: '',
+          samplePlace: '',
+          sampleRemark: '',
+          producerManufacturer: '',
+          depotId: '',
+          depotName: '',
+          taskId: '',
+          keepSampleQuantity: ''
+        },
+        loading: false,
+        selection: [],
+        poList: [],
+        id: '',
+        dialogVisible: false,
+        current: {},
+        formData: {},
+        all: false,
+        qualityType: null,
+        allList: [
+          { value: 1, label: '返工' },
+          { value: 2, label: '返修' },
+          { value: 3, label: '报废' },
+          { value: 4, label: '降级使用' },
+          { value: 5, label: '让步接收' },
+          { value: 6, label: '留样' },
+          { value: 7, label: '消耗' },
+          { value: 8, label: '回用/归批' },
+          { value: 9, label: '转试销' },
+          { value: 10, label: '退货' }
+        ],
+
+        //生产
+        disposalStatusList: [
+          {
+            value: 1,
+            label: '返工'
+          },
+          {
+            value: 2,
+            label: '返修'
+          },
+          {
+            value: 3,
+            label: '报废'
+          },
+          {
+            value: 4,
+            label: '降级使用'
+          },
+          {
+            value: 5,
+            label: '让步接收'
+          },
+          //   {
+          //     value: 6,
+          //     label: '留样'
+          //   },
+          //   {
+          //     value: 7,
+          //     label: '消耗'
+          //   },
+          {
+            value: 8,
+            label: '回用'
+          }
+        ],
+        workOrderCode: '',
+        qualityWorkerId: '',
+        qualityResults: '',
+        refluxTaskList: [],
+        sampleList: [],
+        inventoryList: [],
+        oldList: [],
+        addDialogVisible: false,
+        addSelection: [],
+        singleQuantity: '',
+        retainedSampleQuantity: 0,
+        retainedSampleUnqualified: 0,
+        lossNumber: 0,
+        lossNumberUnqualified: 0,
+        badTypeList: [],
+        badNameList: [],
+        reasonTypeList: []
+      };
+    },
+    computed: {
+      disposeList() {
+        if (this.qualityType == 1) {
+          return this.allList.filter((item) => [5, 10].includes(item.value));
+        }
+        if (this.qualityType == 2) {
+          return this.allList.filter((item) => item.value !== 8);
+        }
+        if (this.qualityType == 2 || this.qualityType == 3) {
+          return this.allList.filter(
+            (item) => item.value !== 8 && item.value !== 10
+          );
+        } else {
+          return this.allList;
+        }
+      },
+      // 可用总数 = 总数 - 留样数 - 消耗数
+      availableTotal() {
+        if (!this.sampleList.length) return this.total;
+        const usedQuantity = this.sampleList
+          .filter((item) => item.disposeType == 6 || item.disposeType == 7)
+          .reduce((sum, item) => sum + (item.measureQuantity || 0), 0);
+        return this.total - usedQuantity;
+      },
+      // 表格列配置
+      columns() {
+        const arr = [
+          {
+            width: 45,
+            type: 'selection',
+            columnKey: 'selection',
+            reserveSelection: true,
+            align: 'center',
+            selectable: (row, index) => {
+              return row.disposalStatus != 2;
+            }
+          },
+          {
+            columnKey: 'index',
+            label: '序号',
+            type: 'index',
+            width: 55,
+            align: 'center',
+            showOverflowTooltip: true,
+            fixed: 'left'
+          },
+          {
+            label: '样品编码',
+            prop: 'sampleCode',
+            width: '150',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'categoryCode',
+            label: '物品编码',
+            width: '150',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'categoryName',
+            label: '物品名称',
+            width: '150',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            label: '规格',
+            prop: 'specification',
+            width: '120',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'brandNum',
+            label: '牌号',
+            align: 'center'
+          },
+          {
+            prop: 'modelType',
+            label: '型号',
+            align: 'center',
+            width: '120',
+            showOverflowTooltip: true
+          },
+          {
+            label: '机型',
+            prop: 'modelKey',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            label: '颜色',
+            prop: 'colorKey',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'batchNo',
+            label: '批次号',
+            align: 'center',
+            width: '120',
+            showOverflowTooltip: true
+          },
+
+          { label: '计量数量', prop: 'measureQuantity', align: 'center' },
+          { label: '计量单位', prop: 'measureUnit', align: 'center' },
+          {
+            prop: 'weight',
+            label: '重量',
+            align: 'center',
+            width: '120',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'weightUnit',
+            label: '重量单位',
+            align: 'center',
+            width: '120',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'engrave',
+            label: '刻码',
+            align: 'center'
+          },
+
+          {
+            prop: 'produceRoutingName',
+            label: '工艺路线',
+            align: 'center'
+          },
+          {
+            prop: 'produceTaskName',
+            label: '工序',
+            align: 'center'
+          },
+          {
+            prop: 'badTypeName',
+            label: '不良类型',
+            slot: 'badTypeName',
+            width: '180',
+            align: 'center'
+          },
+          {
+            prop: 'badNameName',
+            slot: 'badNameName',
+            label: '不良名称',
+            width: '180',
+            align: 'center'
+          },
+          {
+            slot: 'reasonTypeName',
+            prop: 'reasonTypeName',
+            label: '原因类型',
+            width: '180',
+            align: 'center'
+          },
+          {
+            slot: 'unqualifiedReason',
+            prop: 'unqualifiedReason',
+            label: '原因',
+            width: '180',
+            align: 'center'
+          },
+          {
+            prop: 'disposeTime',
+            label: '处置时间',
+            align: 'center',
+            width: '180',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'disposalStatus',
+            label: '处置状态',
+            align: 'center',
+            filters: [
+              { value: 0, text: '待处置' },
+              { value: 1, text: '处置中' }
+            ],
+            filterMethod: this.disposalStatusFilter,
+            width: '120',
+            fixed: 'right',
+
+            showOverflowTooltip: true,
+            formatter: (row, column, cellValue) => {
+              return this.disposalStatustList[cellValue ? cellValue : 0];
+            }
+          },
+          {
+            fixed: 'right',
+
+            prop: 'disposeType',
+            label: '处置类型',
+            align: 'center',
+            width: '120',
+            showOverflowTooltip: true,
+            formatter: (row, column, cellValue) => {
+              console.log(this.allList.find((item) => item.value == cellValue));
+
+              return this.allList.find((item) => item.value == cellValue)
+                ?.label;
+            }
+          }
+        ];
+        return arr;
+      },
+      disposalStatustList() {
+        return { 0: '待处置', 1: '处置中', 2: '处置完成' };
+      }
+    },
+    created() {
+      this.getBadTypeList();
+      this.getBadNameList();
+      this.getReasonTypeList();
+    },
+
+    methods: {
+      // 查询不良类型
+      async getBadTypeList() {
+        const res = await getBadTypeList({
+          pageNum: 1,
+          size: 999
+        });
+        this.badTypeList = res.list;
+      },
+      // 查询不良名称
+      async getBadNameList() {
+        const res = await getBadNameList({
+          pageNum: 1,
+          size: 999
+        });
+        this.badNameList = res.list;
+      },
+      // 查询原因类型
+      async getReasonTypeList() {
+        const res = await getReasonTypeList({
+          pageNum: 1,
+          size: 999
+        });
+        this.reasonTypeList = res.list;
+      },
+      async init(row) {
+        this.poList = [];
+        this.oldList = [];
+
+        this.qualityType = row.qualityType;
+        this.id = row.id;
+        this.workOrderCode = row.workOrderCode;
+        this.qualityWorkerId = row.qualityWorkerId;
+        this.qualityResults = row.qualityResults;
+        this.warehouseList = await getWarehouseList();
+        await this.getQueryQualityInventory();
+        await this.queryQualitySamplContent();
+        if (this.id) {
+          await this.datasource();
+        }
+      },
+      setQualifiedNumber() {
+        let isQualifiedNumber = true;
+        let noQualifiedNumber = this.poList
+          .filter((item) => ![5, 6, 7].includes(item.disposeType))
+          .reduce((acc, cur) => acc + cur.measureQuantity, 0);
+
+        console.log(noQualifiedNumber, 'noQualifiedNumber');
+
+        if (
+          this.poList
+            .filter((item) => item.disposeType == 5)
+            .reduce((acc, cur) => acc + cur.measureQuantity, 0) ==
+          this.availableTotal
+        ) {
+          isQualifiedNumber = false;
+        }
+        this.$emit('setQualifiedNumber', {
+          noQualifiedNumber,
+          qualifiedNumber: this.availableTotal - noQualifiedNumber,
+          isQualifiedNumber
+        });
+      },
+      selectResult(qualityResults) {
+        if (qualityResults == 1) {
+          this.poList = JSON.parse(JSON.stringify(this.oldList));
+          this.poList = this.poList.map((item) => {
+            if (item.disposalStatus != 2) {
+              item.disposalStatus = 1;
+              item.disposeType = 5;
+            }
+
+            return item;
+          });
+        }
+        if (qualityResults == 4) {
+          this.poList = [
+            ...JSON.parse(JSON.stringify(this.inventoryList)),
+            ...JSON.parse(
+              JSON.stringify(
+                this.oldList.filter((item) => item.disposalStatus == 2)
+              )
+            )
+          ];
+          this.poList = this.poList.map((item) => {
+            if (item.disposalStatus != 2) {
+              item.disposalStatus = 1;
+              item.disposeType = 5;
+            }
+            return item;
+          });
+        }
+        if (qualityResults == 2) {
+          this.poList = [
+            ...JSON.parse(JSON.stringify(this.inventoryList)),
+            ...JSON.parse(
+              JSON.stringify(
+                this.oldList.filter((item) => item.disposalStatus == 2)
+              )
+            )
+          ];
+        }
+        if (qualityResults == 3) {
+          this.poList = JSON.parse(JSON.stringify(this.oldList));
+        }
+        this.qualityResults = qualityResults;
+        this.setQualifiedNumber();
+      },
+      disposeTypeChange() {
+        if (this.disposeForm.disposeType == 6) {
+          this.disposeForm.keepSampleQuantity =
+            this.current?.measureQuantity || '';
+        }
+      },
+      // /* 表格数据源 */
+      async datasource() {
+        const arr = await getById(this.id);
+        this.oldList = JSON.parse(JSON.stringify(arr.poList));
+        this.poList = arr.poList;
+        this.setQualifiedNumber();
+      },
+
+      //   async getRefluxTask() {
+      //     await refluxTask({
+      //       workOrderCode: this.workOrderCode
+      //     }).then((res) => {
+      //       this.refluxTaskList = res;
+      //     });
+      //   },
+
+      remove() {
+        let ids = this.selection.map((item) => item.id);
+        console.log(ids, 'ids');
+        this.poList = this.poList.filter((item) => !ids.includes(item.id));
+        this.setQualifiedNumber();
+      },
+      close() {
+        this.dialogVisible = false;
+        this.disposeType = '';
+        this.all = false;
+        this.formData.disposalStatus = '';
+      },
+      add() {
+        this.addDialogVisible = true;
+        this.$refs.table1.clearSelection();
+        this.addSelection = [];
+        if (this.inventoryList.length === 1) {
+          this.singleQuantity = this.inventoryList[0].measureQuantity || 0;
+        }
+      },
+      handleAddSelectionChange(selection) {
+        this.addSelection = selection;
+      },
+      handleAddSave() {
+        if (this.addSelection.length === 0) {
+          this.$message.warning('请选择数据');
+          return;
+        }
+        const poListTotal = this.poList.reduce(
+          (sum, item) => sum + Number(item.measureQuantity || 0),
+          0
+        );
+        let selectedTotal = 0;
+        if (this.inventoryList.length === 1) {
+          selectedTotal =
+            this.addSelection.length * Number(this.singleQuantity || 0);
+        } else {
+          selectedTotal = this.addSelection.reduce(
+            (sum, item) => sum + Number(item.measureQuantity || 0),
+            0
+          );
+        }
+        if (poListTotal + selectedTotal > this.availableTotal) {
+          this.$message.error('选中的计量数量之和加上已有数量不能超过总数');
+          return;
+        }
+        this.addSelection.forEach((item) => {
+          const newItem = JSON.parse(JSON.stringify(item));
+          if (this.inventoryList.length === 1) {
+            newItem.measureQuantity = Number(this.singleQuantity);
+          }
+          this.poList.push(newItem);
+        });
+        this.addDialogVisible = false;
+        this.setQualifiedNumber();
+      },
+      // // 处置
+      disposeFn(type, row) {
+        if (type == 1) {
+          this.all = true;
+        } else {
+          this.current = row;
+        }
+        this.formData = { ...row };
+        this.dialogVisible = true;
+        // let ids = this.current ? [this.current.id] : this.selection.map((item) => item.id);
+      },
+
+      async getQueryQualityInventory() {
+        const res = await queryQualityInventory({
+          qualityWorkerId: this.qualityWorkerId,
+          size: -1
+        });
+
+        if (res.list.length > 0) {
+          this.inventoryList = res.list;
+        }
+        return;
+      },
+
+      async queryQualitySamplContent() {
+        const res = await queryQualitySamplContent({
+          qualityWorkerId: this.qualityWorkerId,
+          size: 1000
+        });
+        this.sampleList = res.list;
+        [
+          'retainedSampleQuantity',
+          'retainedSampleUnqualified',
+          'lossNumber',
+          'lossNumberUnqualified'
+        ].forEach((key) => {
+          this[key] = 0;
+        });
+        if (this.sampleList.length > 0 && this.inventoryList.length > 0) {
+          const processedList = this.sampleList.filter(
+            (item) => item.disposeType == 6 || item.disposeType == 7
+          );
+          processedList.forEach((sampleItem) => {
+            if (sampleItem.disposeType == 6) {
+              sampleItem.qualityResults == 2
+                ? (this.retainedSampleUnqualified += sampleItem.measureQuantity)
+                : (this.retainedSampleQuantity += sampleItem.measureQuantity);
+            } else {
+              sampleItem.qualityResults == 2
+                ? (this.lossNumberUnqualified += sampleItem.measureQuantity)
+                : (this.lossNumber += sampleItem.measureQuantity);
+            }
+            const inventoryItem = this.inventoryList.find(
+              (item) => item.sourceId === sampleItem.sourceId
+            );
+            if (inventoryItem) {
+              inventoryItem.measureQuantity = Math.max(
+                0,
+                (inventoryItem.measureQuantity || 0) -
+                  (sampleItem.measureQuantity || 0)
+              );
+            }
+          });
+          if (
+            this.inventoryList.length == 1 &&
+            !this.inventoryList[0].sourceId
+          ) {
+            this.inventoryList[0].measureQuantity =
+              this.inventoryList[0].measureQuantity -
+              this.lossNumber -
+              this.lossNumberUnqualified -
+              this.retainedSampleQuantity -
+              this.retainedSampleUnqualified;
+          }
+        }
+        this.$emit('setLossNumber', {
+          lossNumber: this.lossNumber,
+          lossNumberUnqualified: this.lossNumberUnqualified,
+          retainedSampleQuantity: this.retainedSampleQuantity,
+          retainedSampleUnqualified: this.retainedSampleUnqualified
+        });
+        return;
+      },
+      disposalStatusFilter(value, row, column) {
+        if (value == row.disposalStatus) {
+          return row;
+        }
+      },
+      async handleDispose() {
+        this.$refs['disposeForm'].validate(async (valid) => {
+          let ids = this.selection.map((item) => item.id);
+          this.poList.forEach((item, index) => {
+            if (ids.includes(item.id)) {
+              this.$set(this.poList[index], 'disposalStatus', 1);
+
+              for (const key in this.disposeForm) {
+                this.$set(this.poList[index], key, this.disposeForm[key]);
+              }
+            }
+          });
+          this.dialogVisible = false;
+          this.setQualifiedNumber();
+        });
+      },
+      validate() {},
+      getValue() {
+        let unqualifiedProducts = {};
+        if (this.poList.length > 0) {
+          unqualifiedProducts.poList = this.poList.map((item) => {
+            item.qualityResults = 1;
+            item.qualityStatus = 1;
+            item.qualityWorkOrderId = this.qualityWorkerId;
+            return item;
+          });
+          [
+            'batchNo',
+            'brandNum',
+            'categoryCode',
+            'categoryId',
+            'categoryName',
+            'factoriesId',
+            'measureUnit',
+            'modelType',
+            'produceRoutingId',
+            'produceRoutingName',
+            'productCategory'
+          ].forEach((item) => {
+            unqualifiedProducts[item] = this.poList[0][item];
+          });
+
+          unqualifiedProducts.qualityType = this.qualityType;
+          unqualifiedProducts.qualityWorkOrderId = this.qualityWorkerId;
+          unqualifiedProducts.sourceCode = this.workOrderCode;
+          unqualifiedProducts.quantity = this.poList.reduce(
+            (total, item) => total + item.measureQuantity,
+            0
+          );
+          unqualifiedProducts.sourceType = this.workOrderId ? 1 : 0;
+
+          return unqualifiedProducts;
+        }
+        return null;
+      },
+      chooseWarehouse(item) {
+        console.log(item);
+        this.disposeForm.depotId = item.id;
+        this.disposeForm.depotName = item.name;
+      }
+    },
+    watch: {
+      'disposeForm.disposeType': {
+        handler(newVal, oldVal) {
+          if (newVal == 6) {
+            this.rules.depotId = [];
+          } else {
+            this.rules.depotId = [
+              { required: true, message: '请选择仓库', trigger: 'change' }
+            ];
+          }
+          this.disposeForm.sampleCondition = '';
+          this.disposeForm.sampleDate = '';
+          this.disposeForm.samplePlace = '';
+          this.disposeForm.sampleRemark = '';
+          this.disposeForm.producerManufacturer = '';
+          this.disposeForm.depotId = '';
+          this.disposeForm.depotName = '';
+          this.disposeForm.taskId = '';
+        }
+      }
+    }
+  };
+</script>
+
+<style lang="scss" scoped>
+  .unacceptedProductSelect {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+  }
+</style>

+ 360 - 0
src/views/inspectionReport/template/inspection_report1.vue

@@ -0,0 +1,360 @@
+<template>
+  <div id="printSection" style="padding: 20px; background-color: white">
+    <h1
+      style="
+        text-align: center;
+        font-size: 24px;
+        margin-bottom: 20px;
+        font-weight: bold;
+      "
+      >检测报告</h1
+    >
+
+    <!-- 编号和报告单号 -->
+    <div style="margin-bottom: 20px">
+      <el-row>
+        <el-col :span="12">
+          <div style="display: flex; align-items: center">
+            <span style="font-weight: bold; margin-right: 10px">编号:</span>
+            <span style="flex: 1">{{ basicInfoData.code }}</span>
+          </div>
+        </el-col>
+        <el-col :span="12">
+          <div style="display: flex; align-items: center">
+            <span style="font-weight: bold; margin-right: 10px">报告单号:</span>
+            <span style="flex: 1">
+              <div v-if="isView">
+                {{ basicInfoData.reportNumber }}
+              </div>
+              <div v-else>
+                <el-input
+                  v-model="basicInfoData.reportNumber"
+                  placeholder="请输入报告编号"
+                ></el-input>
+              </div>
+            </span>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+
+    <!-- 基本信息表格 -->
+    <table
+      class="basic-info-table"
+      style="width: 100%; border-collapse: collapse; border: 1px solid #ccc"
+    >
+      <tbody>
+        <tr>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >检品名称</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px">{{
+            basicInfoData.productName
+          }}</td>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >批号/序列号</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px">{{
+            basicInfoData.batchNo
+          }}</td>
+        </tr>
+        <tr>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >规格型号</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px"
+            >{{ basicInfoData.specification }}/{{ basicInfoData.modelType }}</td
+          >
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >数量</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px">{{
+            basicInfoData.total
+          }}</td>
+        </tr>
+        <tr>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >请验日期</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px">{{
+            basicInfoData.pleaseVerifyDate
+          }}</td>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >请验部门</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px">{{
+            basicInfoData.pleaseVerifyDepartment
+          }}</td>
+        </tr>
+        <tr>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >报告日期</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px">
+            <div v-if="!isView">
+              <el-date-picker
+                v-model="basicInfoData.reportDate"
+                type="date"
+                value-format="yyyy-MM-dd"
+                placeholder="选择日期"
+              />
+            </div>
+            <div v-else>{{ basicInfoData.reportDate }}</div>
+          </td>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >有效期</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px">
+            <div v-if="!isView">
+              <el-date-picker
+                v-model="basicInfoData.expirationDate"
+                type="date"
+                value-format="yyyy-MM-dd"
+                placeholder="选择日期"
+              />
+            </div>
+            <div v-else>{{ basicInfoData.expirationDate || '/' }}</div>
+          </td>
+        </tr>
+        <tr>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >来源</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px">
+            <div v-if="!isView">
+              <el-input
+                v-model="basicInfoData.source"
+                placeholder="请输入来源"
+              ></el-input>
+            </div>
+            <div v-else>{{ basicInfoData.source }}</div>
+          </td>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >储存条件</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px">
+            <div v-if="!isView">
+              <el-input
+                v-model="basicInfoData.storageCondition"
+                placeholder="请输入储存条件"
+              ></el-input>
+            </div>
+            <div v-else>{{ basicInfoData.storageCondition }}</div>
+          </td>
+        </tr>
+        <tr>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >检验依据</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px" colspan="3">
+            <div v-if="!isView">
+              <el-input
+                v-model="basicInfoData.inspectionBasis"
+                placeholder="请输入检验依据"
+              ></el-input>
+            </div>
+            <div v-else>{{ basicInfoData.inspectionBasis }}</div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+
+    <!-- 检验项目表格 -->
+    <table
+      class="inspection-items-table"
+      style="
+        width: 100%;
+        border-collapse: collapse;
+        border: 1px solid #ccc;
+        margin-bottom: 20px;
+      "
+    >
+      <thead>
+        <tr>
+          <th style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >检验项目</th
+          >
+          <th style="border: 1px solid #ccc; padding: 8px">标准规定</th>
+          <th style="border: 1px solid #ccc; padding: 8px; width: 150px"
+            >检测内容</th
+          >
+          <th style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >检验结果</th
+          >
+        </tr>
+      </thead>
+      <tbody>
+        <tr v-for="(item, index) in inspectionItems" :key="index">
+          <td
+            style="
+              width: 150px;
+              border: 1px solid #ccc;
+              padding: 8px;
+              min-height: 37px;
+            "
+            >{{ item.item }}</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px">{{
+            item.standardRegulations
+          }}</td>
+          <td style="border: 1px solid #ccc; padding: 8px; width: 150px">{{
+            item.qualityResultContent
+          }}</td>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px">{{
+            item.results
+          }}</td>
+        </tr>
+        <tr>
+          <td style="width: 120px; border: 1px solid #ccc; padding: 8px"
+            >结论</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px" :colspan="2">
+            <div v-if="!isView">
+              <el-input
+                v-model="basicInfoData.conclusion"
+                placeholder="请输入结论"
+              ></el-input>
+            </div>
+            <div v-else>{{ basicInfoData.conclusion || '' }}</div>
+          </td>
+        </tr>
+        <tr>
+          <td style="width: 120px; border: 1px solid #ccc; padding: 8px"
+            >备注</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px" :colspan="2">
+            <div v-if="!isView">
+              <el-input
+                v-model="basicInfoData.remarks"
+                placeholder="请输入备注"
+              ></el-input>
+            </div>
+            <div v-else>{{ basicInfoData.remarks || '' }}</div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <!-- 签名区域 -->
+    <div style="margin-top: 20px">
+      <el-row :gutter="20" style="display: flex; align-items: center">
+        <el-col :span="8">
+          <div style="display: flex; align-items: center; margin-bottom: 10px">
+            <span style="font-weight: bold; min-width: 100px"
+              >检验员/日期:</span
+            >
+            <span style="flex: 1">
+              <div v-if="!isView" style="display: flex; align-items: center">
+                <el-input
+                  v-model="basicInfoData.inspector"
+                  placeholder="请输入检验员"
+                  style="width: 140px"
+                ></el-input>
+                <el-date-picker
+                  v-model="basicInfoData.inspectionTime"
+                  type="date"
+                  value-format="yyyy-MM-dd"
+                  placeholder="选择日期"
+                  style="width: 160px"
+                />
+              </div>
+              <div v-else
+                >{{ basicInfoData.inspector || '' }} /
+                {{ basicInfoData.inspectionTime || '' }}</div
+              >
+            </span>
+          </div>
+        </el-col>
+        <el-col :span="8">
+          <div style="display: flex; align-items: center; margin-bottom: 10px">
+            <span style="font-weight: bold; min-width: 100px"
+              >复核人/日期:</span
+            >
+            <span style="flex: 1">
+              <div v-if="!isView" style="display: flex; align-items: center">
+                <el-input
+                  v-model="basicInfoData.reviewer"
+                  placeholder="请输入复核人"
+                  style="width: 140px"
+                ></el-input>
+                <el-date-picker
+                  v-model="basicInfoData.reviewTime"
+                  type="date"
+                  value-format="yyyy-MM-dd"
+                  placeholder="选择日期"
+                  style="width: 160px"
+                />
+              </div>
+              <div v-else
+                >{{ basicInfoData.reviewer || '' }} /
+                {{ basicInfoData.reviewTime || '' }}</div
+              >
+            </span>
+          </div>
+        </el-col>
+        <el-col :span="8">
+          <div style="display: flex; align-items: center; margin-bottom: 10px">
+            <span style="font-weight: bold; min-width: 100px"
+              >审核人/日期:</span
+            >
+            <span style="flex: 1">
+              <div v-if="!isView" style="display: flex; align-items: center">
+                <el-input
+                  v-model="basicInfoData.checker"
+                  placeholder="请输入审核人"
+                  style="width: 140px"
+                ></el-input>
+                <el-date-picker
+                  v-model="basicInfoData.approvedDate"
+                  type="date"
+                  value-format="yyyy-MM-dd"
+                  placeholder="选择日期"
+                  style="width: 160px"
+                />
+              </div>
+              <div v-else
+                >{{ basicInfoData.checker || '' }} /
+                {{ basicInfoData.approvedDate || '' }}</div
+              >
+            </span>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+  </div>
+</template>
+
+<script>
+  export default {
+    name: 'QualityReport',
+    props: {
+      isView: {
+        type: Boolean,
+        default: false
+      }
+    },
+
+    components: {},
+    data() {
+      return {
+        // 基本信息数据
+        basicInfoData: {},
+
+        // 检验项目
+        inspectionItems: []
+      };
+    },
+    created() {},
+    methods: {
+      /* 打开质检报告 */
+      async init(basicInfoData, inspectionItems) {
+        this.$set(this, 'basicInfoData', basicInfoData);
+        this.$set(this, 'inspectionItems', inspectionItems);
+      },
+
+      /* 保存编辑 */
+      getValue() {
+        return this.basicInfoData;
+      }
+    }
+  };
+</script>

+ 381 - 0
src/views/inspectionReport/template/inspection_report2.vue

@@ -0,0 +1,381 @@
+<template>
+  <div id="printSection" style="padding: 20px; background-color: white">
+    <h2 style="text-align: center; font-size: 24px; margin-bottom: 10px"
+      >{{ basicInfoData.groupName||'湖南特瑞精密医疗器器械有限公司' }}</h2
+    >
+    <h1
+      style="
+        text-align: center;
+        font-size: 24px;
+        margin-bottom: 20px;
+        font-weight: bold;
+      "
+      >成品检验报告</h1
+    >
+
+    <!-- 编号和报告单号 -->
+    <div style="margin-bottom: 5px">
+      <el-row style="display: flex">
+        <el-col :span="12" style="width: 50%">
+          <div style="display: flex; align-items: center">
+            <span style="font-weight: bold; margin-right: 10px">报告编号:</span>
+            <span style="flex: 1">{{ basicInfoData.code }}</span>
+          </div>
+        </el-col>
+        <el-col :span="12" style="width: 50%">
+          <div
+            style="
+              display: flex;
+              align-items: center;
+              justify-content: flex-end;
+            "
+          >
+            <span style="font-weight: bold; margin-right: 10px">表单编号:</span>
+
+            <span v-if="isView">
+              {{ basicInfoData.reportNumber }}
+            </span>
+            <span v-else>
+              <el-input
+                v-model="basicInfoData.reportNumber"
+                placeholder="请输入报告编号"
+              ></el-input>
+            </span>
+            <span style="font-weight: bold; margin-left: 3px">
+              <span> 版本:</span>
+
+              <span v-if="isView">
+                {{ basicInfoData.version }}
+              </span>
+              <span v-else>
+                <el-input
+                  style="width: 120px"
+                  v-model="basicInfoData.version"
+                  placeholder="请输入版本"
+                ></el-input>
+              </span>
+            </span>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+
+    <!-- 基本信息表格 -->
+    <table
+      class="basic-info-table"
+      style="width: 100%; border-collapse: collapse; border: 1px solid #ccc"
+    >
+      <tbody>
+        <tr>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >产品名称</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px">{{
+            basicInfoData.productName
+          }}</td>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >生产批号</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px">{{
+            basicInfoData.batchNo
+          }}</td>
+        </tr>
+        <tr>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >规格型号</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px"
+            >{{ basicInfoData.specification }}/{{ basicInfoData.modelType }}</td
+          >
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >灭菌批号</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px"
+            ><span style="flex: 1">
+              <div v-if="isView">
+                {{ basicInfoData.reportNumber1 }}
+              </div>
+              <div v-else>
+                <el-input
+                  v-model="basicInfoData.reportNumber1"
+                  placeholder="请输入报告编号"
+                ></el-input>
+              </div> </span
+          ></td>
+        </tr>
+        <tr>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >产品数量</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px"
+            >{{ basicInfoData.total }}{{ basicInfoData.sampleMeasureUnit }}</td
+          >
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >抽检数量</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px"
+            >{{ basicInfoData.sampleQuantity
+            }}{{ basicInfoData.sampleMeasureUnit }}</td
+          >
+        </tr>
+        <tr>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >包装规格</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px">{{
+            basicInfoData.packingSpecification
+          }}</td>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >生产日期</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px">
+            <div v-if="!isView">
+              <el-date-picker
+                v-model="basicInfoData.manufactureTime"
+                type="date"
+                value-format="yyyy-MM-dd"
+                placeholder="选择日期"
+              />
+            </div>
+            <div v-else>{{ basicInfoData.manufactureTime }}</div></td
+          >
+        </tr>
+        <tr>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >有效期</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px">
+            <div v-if="!isView">
+              <el-date-picker
+                v-model="basicInfoData.expirationDate"
+                type="date"
+                value-format="yyyy-MM-dd"
+                placeholder="选择日期"
+              />
+            </div>
+            <div v-else>{{ basicInfoData.expirationDate || '/' }}</div>
+          </td>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >报告日期</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px">
+            <div v-if="!isView">
+              <el-date-picker
+                v-model="basicInfoData.reportDate"
+                type="date"
+                value-format="yyyy-MM-dd"
+                placeholder="选择日期"
+              />
+            </div>
+            <div v-else>{{ basicInfoData.reportDate }}</div>
+          </td>
+        </tr>
+        <tr>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >检验依据</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px" colspan="3">
+            <div v-if="!isView">
+              <el-input
+                v-model="basicInfoData.inspectionBasis"
+                placeholder="请输入检验依据"
+              ></el-input>
+            </div>
+            <div v-else>{{ basicInfoData.inspectionBasis }}</div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+
+    <!-- 检验项目表格 -->
+    <table
+      class="inspection-items-table"
+      style="
+        width: 100%;
+        border-collapse: collapse;
+        border: 1px solid #ccc;
+        margin-bottom: 20px;
+      "
+    >
+      <thead>
+        <tr>
+          <th style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >检验项目</th
+          >
+          <th style="border: 1px solid #ccc; padding: 8px">标准要求</th>
+          <th style="width: 150px; border: 1px solid #ccc; padding: 8px"
+            >检验结果</th
+          >
+        </tr>
+      </thead>
+      <tbody>
+        <tr v-for="(item, index) in inspectionItems" :key="index">
+          <td
+            style="
+              width: 150px;
+              border: 1px solid #ccc;
+              padding: 8px;
+              min-height: 37px;
+            "
+            >{{ item.item }}</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px">{{
+            item.standardRegulations
+          }}</td>
+          <td style="width: 150px; border: 1px solid #ccc; padding: 8px">{{
+            item.results
+          }}</td>
+        </tr>
+        <tr>
+          <td style="width: 120px; border: 1px solid #ccc; padding: 8px"
+            >结论</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px" :colspan="2">
+            <div v-if="!isView">
+              <el-input
+                v-model="basicInfoData.conclusion"
+                placeholder="请输入结论"
+              ></el-input>
+            </div>
+            <div v-else>{{ basicInfoData.conclusion || '' }}</div>
+          </td>
+        </tr>
+        <tr>
+          <td style="width: 120px; border: 1px solid #ccc; padding: 8px"
+            >备注</td
+          >
+          <td style="border: 1px solid #ccc; padding: 8px" :colspan="2">
+            <div v-if="!isView">
+              <el-input
+                v-model="basicInfoData.remarks"
+                placeholder="请输入备注"
+              ></el-input>
+            </div>
+            <div v-else>{{ basicInfoData.remarks || '' }}</div>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <!-- 签名区域 -->
+    <div style="margin-top: 20px">
+      <el-row :gutter="20" style="display: flex; align-items: center">
+        <el-col :span="8">
+          <div style="display: flex; align-items: center; margin-bottom: 10px">
+            <span style="font-weight: bold; min-width: 100px"
+              >检验员/日期:</span
+            >
+            <span style="flex: 1">
+              <div v-if="!isView" style="display: flex; align-items: center">
+                <el-input
+                  v-model="basicInfoData.inspector"
+                  placeholder="请输入检验员"
+                  style="width: 140px"
+                ></el-input>
+                <el-date-picker
+                  v-model="basicInfoData.inspectionTime"
+                  type="date"
+                  value-format="yyyy-MM-dd"
+                  placeholder="选择日期"
+                  style="width: 160px"
+                />
+              </div>
+              <div v-else
+                >{{ basicInfoData.inspector || '' }} /
+                {{ basicInfoData.inspectionTime || '' }}</div
+              >
+            </span>
+          </div>
+        </el-col>
+        <el-col :span="8">
+          <div style="display: flex; align-items: center; margin-bottom: 10px">
+            <span style="font-weight: bold; min-width: 100px"
+              >复核人/日期:</span
+            >
+            <span style="flex: 1">
+              <div v-if="!isView" style="display: flex; align-items: center">
+                <el-input
+                  v-model="basicInfoData.reviewer"
+                  placeholder="请输入复核人"
+                  style="width: 140px"
+                ></el-input>
+                <el-date-picker
+                  v-model="basicInfoData.reviewTime"
+                  type="date"
+                  value-format="yyyy-MM-dd"
+                  placeholder="选择日期"
+                  style="width: 160px"
+                />
+              </div>
+              <div v-else
+                >{{ basicInfoData.reviewer || '' }} /
+                {{ basicInfoData.reviewTime || '' }}</div
+              >
+            </span>
+          </div>
+        </el-col>
+        <el-col :span="8">
+          <div style="display: flex; align-items: center; margin-bottom: 10px">
+            <span style="font-weight: bold; min-width: 100px"
+              >审核人/日期:</span
+            >
+            <span style="flex: 1">
+              <div v-if="!isView" style="display: flex; align-items: center">
+                <el-input
+                  v-model="basicInfoData.checker"
+                  placeholder="请输入审核人"
+                  style="width: 140px"
+                ></el-input>
+                <el-date-picker
+                  v-model="basicInfoData.approvedDate"
+                  type="date"
+                  value-format="yyyy-MM-dd"
+                  placeholder="选择日期"
+                  style="width: 160px"
+                />
+              </div>
+              <div v-else
+                >{{ basicInfoData.checker || '' }} /
+                {{ basicInfoData.approvedDate || '' }}</div
+              >
+            </span>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+  </div>
+</template>
+
+<script>
+  export default {
+    name: 'QualityReport',
+    props: {
+      isView: {
+        type: Boolean,
+        default: false
+      }
+    },
+
+    data() {
+      return {
+        // 基本信息数据
+        basicInfoData: {},
+
+        // 检验项目
+        inspectionItems: []
+      };
+    },
+
+    methods: {
+      /* 打开质检报告 */
+      async init(basicInfoData, inspectionItems) {
+        this.$set(this, 'basicInfoData', basicInfoData);
+        this.$set(this, 'inspectionItems', inspectionItems);
+      },
+
+      /* 保存编辑 */
+      getValue() {
+        return this.basicInfoData;
+      }
+    }
+  };
+</script>

+ 1049 - 0
src/views/inspectionReport/template/inspection_report3.vue

@@ -0,0 +1,1049 @@
+<template>
+  <div id="printSection" style="padding: 20px; background-color: white">
+    <div class="table1">
+      <h2 style="text-align: center; font-size: 24px; margin-bottom: 10px"
+        >湖南特瑞精密医疗器器械有限公司</h2
+      >
+      <h1
+        style="
+          text-align: center;
+          font-size: 24px;
+          margin-bottom: 20px;
+          font-weight: bold;
+        "
+        >不合格品通知及评审处置表</h1
+      >
+
+      <!-- 编号和报告单号 -->
+      <div style="margin-bottom: 5px">
+        <el-row style="display: flex">
+          <el-col :span="12" style="width: 50%">
+            <div style="display: flex; align-items: center">
+              <!-- 左侧空白占位 -->
+            </div>
+          </el-col>
+          <el-col :span="12" style="width: 50%">
+            <div
+              style="
+                display: flex;
+                align-items: center;
+                justify-content: flex-end;
+              "
+            >
+              <span style="font-weight: bold; margin-right: 10px"
+                >表单编号:</span
+              >
+              <span v-if="isView">
+                {{ basicInfoData.reportNumber }}
+              </span>
+              <span v-else>
+                <el-input
+                  v-model="basicInfoData.reportNumber"
+                  placeholder="请输入表单编号"
+                ></el-input>
+              </span>
+              <span style="font-weight: bold; margin-left: 3px">
+                <span> 版本:</span>
+                <span v-if="isView">
+                  {{ basicInfoData.version }}
+                </span>
+                <span v-else>
+                  <el-input
+                    style="width: 120px"
+                    v-model="basicInfoData.version"
+                    placeholder="请输入版本"
+                  ></el-input>
+                </span>
+              </span>
+            </div>
+          </el-col>
+        </el-row>
+      </div>
+
+      <!-- 基本信息表格 -->
+      <table
+        class="basic-info-table"
+        border="1"
+        style="width: 100%; border-collapse: collapse"
+      >
+        <tbody>
+          <tr align="center">
+            <td style="width: 150px">不合格品类别</td>
+            <td colspan="3" align="left">
+              <el-checkbox-group
+                v-model="basicInfoData.categoryList"
+                style="margin-left: 15px"
+              >
+                <el-checkbox label="原辅料"></el-checkbox>
+                <el-checkbox label="包材"></el-checkbox>
+                <el-checkbox label="中间产品"></el-checkbox>
+                <el-checkbox label="成品"></el-checkbox>
+              </el-checkbox-group>
+            </td>
+            <td style="width: 150px">处置编号</td>
+            <td style="width: 150px">
+              <el-input
+                v-if="!isView"
+                v-model="basicInfoData.disposalNumber"
+                placeholder="请输入处置编号"
+              ></el-input>
+              <span v-else>
+                {{ basicInfoData.disposalNumber }}
+              </span>
+            </td>
+          </tr>
+          <tr align="center"
+            ><td colspan="6"
+              ><span style="font-weight: 800; font-size: 16px"
+                >基本信息</span
+              ></td
+            ></tr
+          >
+          <tr align="center"
+            ><td>品名</td><td>型号规格</td><td>批号</td><td>数量</td
+            ><td colspan="2">来源</td>
+          </tr>
+          <tr align="center"
+            ><td>{{ basicInfoData.productName }}</td
+            ><td
+              >{{ basicInfoData.specification }}/{{
+                basicInfoData.modelType
+              }}</td
+            ><td>{{ basicInfoData.batchNo }}</td
+            ><td
+              >{{ basicInfoData.total
+              }}{{ basicInfoData.sampleMeasureUnit }}</td
+            ><td colspan="2">
+              <div style="display: flex">
+                <el-checkbox-group
+                  v-model="basicInfoData.sourceList"
+                  style="margin-left: 15px"
+                >
+                  <el-checkbox label="来料检"></el-checkbox>
+                  <el-checkbox label="过程检"></el-checkbox>
+                  <el-checkbox label="成品检"></el-checkbox>
+                </el-checkbox-group>
+                <el-checkbox-group
+                  v-model="basicInfoData.sourceList"
+                  style="margin-left: 15px"
+                >
+                  <el-checkbox label="库存"></el-checkbox>
+                  <el-checkbox label="交付后"></el-checkbox>
+                  <el-checkbox label="其他:"></el-checkbox>
+                </el-checkbox-group>
+
+                <input
+                  v-model="basicInfoData.sourceOther"
+                  style="
+                    border: none;
+                    border-bottom: solid 1px #000;
+                    width: 60px;
+                  "
+                />
+              </div>
+            </td>
+          </tr>
+          <tr
+            ><td colspan="6">
+              <div>
+                <div
+                  >不合格描述:(来料不合格的,应描述供应商名称、厂家批号等信息)</div
+                >
+                <div style="height: 100px"></div>
+                <div style="display: flex"
+                  >是否有其他物料/中间产品/成品受影响(
+                  <el-checkbox-group
+                    v-model="basicInfoData.affectedList"
+                    style="margin-left: 15px"
+                  >
+                    <el-checkbox label="否"></el-checkbox>,
+                    <el-checkbox label="是"></el-checkbox></el-checkbox-group
+                  >,受影响的物料/中间产品/成品批次如下):</div
+                >
+                <div style="display: flex; margin-top: 25px"
+                  ><span style="font-size: 16px; font-weight: 800"
+                    >处置建议:</span
+                  >
+                  <el-checkbox-group
+                    v-model="basicInfoData.suggestionList"
+                    style="margin-left: 15px"
+                  >
+                    <el-checkbox label="退货"></el-checkbox>
+                    <el-checkbox label="销毁"></el-checkbox>
+                    <el-checkbox label="让步接收"></el-checkbox>
+                    <el-checkbox label="挑选使用"></el-checkbox>
+                    <el-checkbox label="返工"></el-checkbox>
+                    <el-checkbox label="其他"></el-checkbox>
+                    <input
+                      v-model="basicInfoData.suggestionOther"
+                      style="
+                        border: none;
+                        border-bottom: solid 1px #000;
+                        width: 100px;
+                      "
+                    /> </el-checkbox-group
+                ></div>
+
+                <div style="margin-top: 25px">
+                  <el-row
+                    :gutter="20"
+                    style="display: flex; align-items: center"
+                  >
+                    <el-col :span="8">
+                      <div
+                        style="
+                          display: flex;
+                          align-items: center;
+                          margin-bottom: 10px;
+                        "
+                      >
+                        <span
+                          style="
+                            font-weight: bold;
+                            min-width: 100px;
+                            font-size: 16px;
+                          "
+                          >反馈部门:</span
+                        >
+                        <span style="flex: 1">
+                          <div
+                            v-if="!isView"
+                            style="display: flex; align-items: center"
+                          >
+                            <el-input
+                              v-model="basicInfoData.feedbackDepartment"
+                              placeholder="请输入反馈部门"
+                              style="width: 140px"
+                            ></el-input>
+                          </div>
+                          <div v-else>{{
+                            basicInfoData.feedbackDepartment || ''
+                          }}</div>
+                        </span>
+                      </div>
+                    </el-col>
+                    <el-col :span="8">
+                      <div
+                        style="
+                          display: flex;
+                          align-items: center;
+                          margin-bottom: 10px;
+                        "
+                      >
+                        <span style="font-weight: bold; min-width: 100px"
+                          >反馈人/日期:</span
+                        >
+                        <span style="flex: 1">
+                          <div
+                            v-if="!isView"
+                            style="display: flex; align-items: center"
+                          >
+                            <el-input
+                              v-model="basicInfoData.feedbackPerson"
+                              placeholder="请输入反馈人"
+                              style="width: 140px"
+                            ></el-input>
+                            <el-date-picker
+                              v-model="basicInfoData.feedbackDate"
+                              type="date"
+                              value-format="yyyy-MM-dd"
+                              placeholder="选择日期"
+                              style="width: 160px"
+                            />
+                          </div>
+                          <div v-else
+                            >{{ basicInfoData.feedbackPerson || '' }} /
+                            {{ basicInfoData.feedbackDate || '' }}</div
+                          >
+                        </span>
+                      </div>
+                    </el-col>
+                    <el-col :span="8">
+                      <div
+                        style="
+                          display: flex;
+                          align-items: center;
+                          margin-bottom: 10px;
+                        "
+                      >
+                        <span style="font-weight: bold; min-width: 100px">
+                          部门负责人/日期:</span
+                        >
+                        <span style="flex: 1">
+                          <div
+                            v-if="!isView"
+                            style="display: flex; align-items: center"
+                          >
+                            <el-input
+                              v-model="basicInfoData.deptHeadPerson"
+                              placeholder="请输入部门负责人"
+                              style="width: 140px"
+                            ></el-input>
+                            <el-date-picker
+                              v-model="basicInfoData.deptHeadDate"
+                              type="date"
+                              value-format="yyyy-MM-dd"
+                              placeholder="选择日期"
+                              style="width: 160px"
+                            />
+                          </div>
+                          <div v-else
+                            >{{ basicInfoData.deptHeadPerson || '' }} /
+                            {{ basicInfoData.deptHeadDate || '' }}</div
+                          >
+                        </span>
+                      </div>
+                    </el-col>
+                  </el-row>
+                </div>
+              </div>
+            </td>
+          </tr>
+          <tr
+            ><td colspan="6">
+              <div style="height: 80px"> 质量部QA确认: </div>
+              <div
+                style="
+                  display: flex;
+                  align-items: center;
+                  margin-bottom: 10px;
+                  justify-content: flex-end;
+                  width: 90%;
+                "
+              >
+                <span> 确认人/日期:</span>
+                <span>
+                  <div
+                    v-if="!isView"
+                    style="display: flex; align-items: center"
+                  >
+                    <el-input
+                      v-model="basicInfoData.qaPerson"
+                      placeholder="请输入确认人"
+                      style="width: 140px"
+                    ></el-input>
+                    <el-date-picker
+                      v-model="basicInfoData.qaDate"
+                      type="date"
+                      value-format="yyyy-MM-dd"
+                      placeholder="选择日期"
+                      style="width: 160px"
+                    />
+                  </div>
+                  <div v-else
+                    >{{ basicInfoData.qaPerson || '' }} /
+                    {{ basicInfoData.qaDate || '' }}</div
+                  >
+                </span>
+              </div>
+            </td></tr
+          >
+          <tr
+            ><td colspan="6">
+              <div style="height: 80px"
+                >委托方意见(仅适用于受托不合格品处置):
+              </div>
+              <div
+                style="
+                  display: flex;
+                  align-items: center;
+                  margin-bottom: 10px;
+                  justify-content: flex-end;
+                  width: 90%;
+                "
+              >
+                <span> 确认人/日期:</span>
+                <span>
+                  <div
+                    v-if="!isView"
+                    style="display: flex; align-items: center"
+                  >
+                    <el-input
+                      v-model="basicInfoData.consignorPerson"
+                      placeholder="请输入确认人"
+                      style="width: 140px"
+                    ></el-input>
+                    <el-date-picker
+                      v-model="basicInfoData.consignorDate"
+                      type="date"
+                      value-format="yyyy-MM-dd"
+                      placeholder="选择日期"
+                      style="width: 160px"
+                    />
+                  </div>
+                  <div v-else
+                    >{{ basicInfoData.consignorPerson || '' }} /
+                    {{ basicInfoData.consignorDate || '' }}</div
+                  >
+                </span>
+              </div>
+            </td></tr
+          >
+          <tr align="center"
+            ><td colspan="6"
+              ><span style="font-weight: 800; font-size: 16px"
+                >不合格评审</span
+              ></td
+            ></tr
+          >
+          <tr align="center"
+            ><td rowspan="6"><span style="font-size: 16px">评审意见</span></td>
+            <td>部门</td>
+            <td colspan="3">意见</td>
+            <td>评审人签字/日期</td>
+          </tr>
+          <tr align="center" v-for="value in basicInfoData.remarks">
+            <td style="width: 300px">
+              <el-input
+                v-if="!isView"
+                v-model="value.department"
+                placeholder="请输入部门"
+                style="width: 100%"
+              ></el-input>
+              <span v-else>{{ value.department }}</span>
+            </td>
+            <td colspan="3">
+              <el-input
+                v-if="!isView"
+                v-model="value.opinion"
+                placeholder="请输入意见"
+                style="width: 100%"
+              ></el-input>
+              <span v-else>{{ value.opinion }}</span>
+            </td>
+            <td>
+              <div v-if="!isView" style="display: flex; align-items: center">
+                <el-input
+                  v-model="value.sign"
+                  placeholder="请输入签字人"
+                  style="width: 140px"
+                ></el-input>
+                <el-date-picker
+                  v-model="value.date"
+                  type="date"
+                  value-format="yyyy-MM-dd"
+                  placeholder="选择日期"
+                  style="width: 160px"
+                />
+              </div>
+              <div v-else
+                >{{ value.sign || '' }} / {{ value.date || '' }}</div
+              ></td
+            >
+          </tr>
+        </tbody>
+      </table>
+    </div>
+
+    <div class="table2">
+      <h2 style="text-align: center; font-size: 24px; margin-bottom: 10px"
+        >湖南特瑞精密医疗器器械有限公司</h2
+      >
+      <h1
+        style="
+          text-align: center;
+          font-size: 24px;
+          margin-bottom: 20px;
+          font-weight: bold;
+        "
+        >不合格品通知及评审处置表</h1
+      >
+
+      <!-- 编号和报告单号(与 table1 共用字段) -->
+      <div style="margin-bottom: 5px">
+        <el-row style="display: flex">
+          <el-col :span="12" style="width: 50%">
+            <div style="display: flex; align-items: center"></div>
+          </el-col>
+          <el-col :span="12" style="width: 50%">
+            <div
+              style="
+                display: flex;
+                align-items: center;
+                justify-content: flex-end;
+              "
+            >
+              <span style="font-weight: bold; margin-right: 10px"
+                >表单编号:</span
+              >
+              <span v-if="isView">
+                {{ basicInfoData.reportNumber }}
+              </span>
+              <span v-else>
+                <el-input
+                  v-model="basicInfoData.reportNumber"
+                  placeholder="请输入表单编号"
+                ></el-input>
+              </span>
+              <span style="font-weight: bold; margin-left: 3px">
+                <span> 版本:</span>
+                <span v-if="isView">
+                  {{ basicInfoData.version }}
+                </span>
+                <span v-else>
+                  <el-input
+                    style="width: 120px"
+                    v-model="basicInfoData.version"
+                    placeholder="请输入版本"
+                  ></el-input>
+                </span>
+              </span>
+            </div>
+          </el-col>
+        </el-row>
+      </div>
+
+      <table
+        class="basic-info-table"
+        border="1"
+        style="width: 100%; border-collapse: collapse"
+      >
+        <tbody>
+          <!-- 第一行:管代审批 + 总经理审批 -->
+          <tr>
+            <td style="width: 60%; padding: 15px" colspan="2">
+              <div style="font-weight: bold; margin-bottom: 10px"
+                >管代审批:</div
+              >
+              <el-checkbox-group
+                v-model="basicInfoData.managementApprovalList"
+                style="margin-left: 10px"
+              >
+                <el-checkbox label="退货"></el-checkbox>
+                <el-checkbox label="销毁"></el-checkbox>
+                <el-checkbox label="让步接收"></el-checkbox>
+                <el-checkbox label="挑选使用"></el-checkbox>
+                <el-checkbox label="返工"></el-checkbox>
+              </el-checkbox-group>
+              <el-checkbox label="其他" style="margin-left: 10px"></el-checkbox>
+              <input
+                v-model="basicInfoData.managementOtherText"
+                style="
+                  width: 150px;
+                  border: none;
+                  border-bottom: solid 1px #000;
+                "
+              />
+              <div style="margin-top: 15px; display: flex">
+                是否采取纠正预防措施:
+                <el-checkbox
+                  v-model="basicInfoData.correctiveActionChecked"
+                ></el-checkbox>
+                ,编号:
+                <input
+                  v-model="basicInfoData.correctiveActionNo"
+                  style="
+                    width: 180px;
+                    border: none;
+                    border-bottom: solid 1px #000;
+                  "
+                />
+              </div>
+              <div style="margin-top: 15px; display: flex">
+                <el-checkbox
+                  v-model="basicInfoData.noCorrectiveReasonChecked"
+                ></el-checkbox>
+                否,理由:
+                <input
+                  v-model="basicInfoData.noCorrectiveReasonText"
+                  style="
+                    width: 180px;
+                    border: none;
+                    border-bottom: solid 1px #000;
+                  "
+                />
+              </div>
+              <div
+                style="
+                  display: flex;
+                  align-items: center;
+                  margin-top: 15px;
+                  width: 90%;
+                "
+              >
+                <span> 签名/日期:</span>
+                <span>
+                  <div
+                    v-if="!isView"
+                    style="display: flex; align-items: center"
+                  >
+                    <el-input
+                      v-model="basicInfoData.managementSign"
+                      placeholder="请输入签名"
+                      style="width: 140px"
+                    ></el-input>
+                    <el-date-picker
+                      v-model="basicInfoData.managementSignDate"
+                      type="date"
+                      value-format="yyyy-MM-dd"
+                      placeholder="选择日期"
+                      style="width: 160px"
+                    />
+                  </div>
+                  <div v-else
+                    >{{ basicInfoData.managementSign || '' }} /
+                    {{ basicInfoData.managementSignDate || '' }}</div
+                  >
+                </span>
+              </div>
+            </td>
+            <td style="width: 40%; padding: 15px">
+              <div
+                style="font-weight: bold; margin-bottom: 10px; font-size: 16px"
+              >
+                总经理/副总经理审批:
+                <span style="font-size: 12px"
+                  >(不适用于受托不合格品处置)</span
+                >
+              </div>
+              <el-checkbox-group
+                v-model="basicInfoData.generalManagerApprovalList"
+                style="margin-left: 10px"
+              >
+                <el-checkbox label="同意"></el-checkbox>
+                <el-checkbox label="不同意"></el-checkbox>
+              </el-checkbox-group>
+              <div
+                style="
+                  display: flex;
+                  align-items: center;
+                  margin-top: 15px;
+                  width: 90%;
+                "
+              >
+                <span> 签名/日期:</span>
+                <span>
+                  <div
+                    v-if="!isView"
+                    style="display: flex; align-items: center"
+                  >
+                    <el-input
+                      v-model="basicInfoData.generalManagerSign"
+                      placeholder="请输入签名"
+                      style="width: 140px"
+                    ></el-input>
+                    <el-date-picker
+                      v-model="basicInfoData.generalManagerSignDate"
+                      type="date"
+                      value-format="yyyy-MM-dd"
+                      placeholder="选择日期"
+                      style="width: 160px"
+                    />
+                  </div>
+                  <div v-else
+                    >{{ basicInfoData.generalManagerSign || '' }} /
+                    {{ basicInfoData.generalManagerSignDate || '' }}</div
+                  >
+                </span>
+              </div>
+            </td>
+          </tr>
+          <!-- 不合格品的处置记录标题 -->
+          <tr align="center"
+            ><td colspan="3"
+              ><span style="font-weight: 800; font-size: 16px"
+                >不合格品的处置记录</span
+              ></td
+            ></tr
+          >
+          <!-- 处理方法 -->
+          <tr>
+            <td colspan="3" style="padding: 15px">
+              <div style="font-weight: bold; margin-bottom: 10px">
+                处理方法:
+              </div>
+              <!-- 退货 -->
+              <div
+                style="margin-bottom: 15px; display: flex; align-items: center"
+              >
+                <el-checkbox
+                  v-model="basicInfoData.returnChecked"
+                ></el-checkbox>
+                退货,退货日期:
+                <el-date-picker
+                  v-model="basicInfoData.returnDate"
+                  type="date"
+                  value-format="yyyy-MM-dd"
+                  placeholder=" "
+                  style="width: 160px"
+                  v-if="!isView"
+                />
+                <input
+                  v-else
+                  v-model="basicInfoData.returnDate"
+                  style="
+                    width: 180px;
+                    border: none;
+                    border-bottom: solid 1px #000;
+                  "
+                />
+                ;退货单号:
+                <input
+                  v-model="basicInfoData.returnNo"
+                  style="
+                    width: 180px;
+                    border: none;
+                    border-bottom: solid 1px #000;
+                  "
+                />
+              </div>
+              <!-- 销毁 -->
+              <div>
+                <el-checkbox
+                  v-model="basicInfoData.destroyChecked"
+                ></el-checkbox>
+                销毁,方法(附销毁照片):
+                <el-checkbox-group
+                  v-model="basicInfoData.destroyMethods"
+                  style="display: inline-block; margin-left: 10px"
+                >
+                  <el-checkbox label="撕毁"></el-checkbox>
+                  <el-checkbox label="掩埋"></el-checkbox>
+                  <el-checkbox label="焚烧"></el-checkbox>
+                  <el-checkbox label="破碎"></el-checkbox>
+                  <el-checkbox label="废弃"></el-checkbox>
+                </el-checkbox-group>
+              </div>
+              <div style="margin-bottom: 25px">
+                <el-checkbox
+                  style="margin-left: 50px"
+                  label="其他"
+                ></el-checkbox>
+                <input
+                  v-model="basicInfoData.destroyOther"
+                  style="
+                    width: 50%;
+                    border: none;
+                    border-bottom: solid 1px #000;
+                  "
+                />
+              </div>
+              <!-- 让步接收 -->
+              <div style="margin-bottom: 25px">
+                <el-checkbox
+                  v-model="basicInfoData.concessionChecked"
+                ></el-checkbox>
+                让步接收,放行日期:
+                <el-date-picker
+                  v-model="basicInfoData.concessionDate"
+                  type="date"
+                  value-format="yyyy-MM-dd"
+                  v-if="!isView"
+                  placeholder="选择日期"
+                  style="width: 160px"
+                />
+                <input
+                  v-else
+                  v-model="basicInfoData.concessionDate"
+                  style="
+                    width: 180px;
+                    border: none;
+                    border-bottom: solid 1px #000;
+                  "
+                />
+              </div>
+              <!-- 挑选使用 -->
+              <div style="margin-bottom: 25px">
+                <el-checkbox
+                  v-model="basicInfoData.selectChecked"
+                ></el-checkbox>
+                挑选使用,挑选人:
+                <input
+                  v-model="basicInfoData.selectPerson"
+                  style="
+                    width: 180px;
+                    border: none;
+                    border-bottom: solid 1px #000;
+                  "
+                />
+                ;挑选日期:
+                <el-date-picker
+                  v-model="basicInfoData.selectDate"
+                  type="date"
+                  v-if="!isView"
+                  value-format="yyyy-MM-dd"
+                  placeholder="选择日期"
+                  style="width: 160px"
+                />
+                <input
+                  v-else
+                  v-model="basicInfoData.selectDate"
+                  style="
+                    width: 180px;
+                    border: none;
+                    border-bottom: solid 1px #000;
+                  "
+                />
+                ;
+                <div style="margin-top: 5px; margin-left: 24px">
+                  挑选结果:
+                  <input
+                    v-model="basicInfoData.selectResult"
+                    style="
+                      width: 50%;
+                      border: none;
+                      border-bottom: solid 1px #000;
+                    "
+                  />
+                </div>
+              </div>
+              <!-- 返工 -->
+              <div style="margin-bottom: 15px">
+                <el-checkbox
+                  v-model="basicInfoData.reworkChecked"
+                ></el-checkbox>
+                返工,返工方案:
+                <input
+                  v-model="basicInfoData.reworkPlan"
+                  style="
+                    width: 180px;
+                    border: none;
+                    border-bottom: solid 1px #000;
+                  "
+                />
+                ;返工日期:
+                <el-date-picker
+                  v-model="basicInfoData.reworkDate"
+                  type="date"
+                  v-if="!isView"
+                  value-format="yyyy-MM-dd"
+                  placeholder="选择日期"
+                  style="width: 160px"
+                />
+                <input
+                  v-else
+                  v-model="basicInfoData.reworkDate"
+                  style="
+                    width: 180px;
+                    border: none;
+                    border-bottom: solid 1px #000;
+                  "
+                />
+                ;
+                <div style="margin-top: 5px; margin-left: 24px">
+                  返工结果:
+                  <input
+                    v-model="basicInfoData.reworkResult"
+                    style="
+                      width: 50%;
+                      border: none;
+                      border-bottom: solid 1px #000;
+                    "
+                  />
+                </div>
+              </div>
+              <!-- 其他 -->
+              <div style="margin-bottom: 15px">
+                <el-checkbox v-model="basicInfoData.otherChecked"></el-checkbox>
+                其他
+                <input
+                  v-model="basicInfoData.otherRemark"
+                  style="
+                    width: 50%;
+                    border: none;
+                    border-bottom: solid 1px #000;
+                  "
+                />
+              </div>
+            </td>
+          </tr>
+          <!-- 签名区域 -->
+          <tr>
+            <td style="padding: 15px; height: 80px">
+              <div>执行人/日期:</div>
+              <div v-if="!isView" style="display: flex; align-items: center">
+                <el-input
+                  v-model="basicInfoData.executorSign"
+                  placeholder="请输入执行人"
+                  style="width: 140px"
+                ></el-input>
+                <el-date-picker
+                  v-model="basicInfoData.executorDate"
+                  type="date"
+                  value-format="yyyy-MM-dd"
+                  placeholder="选择日期"
+                  style="width: 160px"
+                />
+              </div>
+              <div v-else
+                >{{ basicInfoData.executorSign || '' }} /
+                {{ basicInfoData.executorDate || '' }}</div
+              >
+            </td>
+            <td style="padding: 15px; height: 80px">
+              <div>执行部门负责人/日期:</div>
+              <div v-if="!isView" style="display: flex; align-items: center">
+                <el-input
+                  v-model="basicInfoData.departmentSign"
+                  placeholder="请输入部门负责人"
+                  style="width: 140px"
+                ></el-input>
+                <el-date-picker
+                  v-model="basicInfoData.departmentDate"
+                  type="date"
+                  value-format="yyyy-MM-dd"
+                  placeholder="选择日期"
+                  style="width: 160px"
+                />
+              </div>
+              <div v-else
+                >{{ basicInfoData.departmentSign || '' }} /
+                {{ basicInfoData.departmentDate || '' }}</div
+              >
+            </td>
+            <td style="padding: 15px; height: 80px" colspan="2">
+              <div>监督人(质量部)/日期:</div>
+              <div v-if="!isView" style="display: flex; align-items: center">
+                <el-input
+                  v-model="basicInfoData.supervisorSign"
+                  placeholder="请输入监督人"
+                  style="width: 140px"
+                ></el-input>
+                <el-date-picker
+                  v-model="basicInfoData.supervisorDate"
+                  type="date"
+                  value-format="yyyy-MM-dd"
+                  placeholder="选择日期"
+                  style="width: 160px"
+                />
+              </div>
+              <div v-else
+                >{{ basicInfoData.supervisorSign || '' }} /
+                {{ basicInfoData.supervisorDate || '' }}</div
+              >
+            </td>
+          </tr>
+        </tbody>
+      </table>
+      注:1.当报废处置金额达5万元及以上时,必须经总经理或其授权代表审批;2.处置过程可另附照片或其他证据等。
+    </div>
+  </div>
+</template>
+
+<script>
+  export default {
+    name: 'QualityReport',
+    props: {
+      isView: {
+        type: Boolean,
+        default: false
+      }
+    },
+    data() {
+      return {
+        basicInfoData: {
+          // 表单编号、版本(两处共用)
+          reportNumber: '',
+          version: '',
+          // 处置编号
+          disposalNumber: '',
+          // 不合格品类别
+          categoryList: [],
+          // 来源
+          sourceList: [],
+          sourceOther: '',
+          // 是否受影响
+          affectedList: [],
+          // 处置建议
+          suggestionList: [],
+          suggestionOther: '',
+          // 基本信息的显示字段(非输入,但保留)
+          productName: '',
+          specification: '',
+          modelType: '',
+          batchNo: '',
+          total: '',
+          sampleMeasureUnit: '',
+          // 反馈部门、反馈人、部门负责人
+          feedbackDepartment: '',
+          feedbackPerson: '',
+          feedbackDate: '',
+          deptHeadPerson: '',
+          deptHeadDate: '',
+          // 质量部QA确认
+          qaPerson: '',
+          qaDate: '',
+          // 委托方意见
+          consignorPerson: '',
+          consignorDate: '',
+          // 评审意见表格(5行)
+          remarks: [
+            { department: '', opinion: '', sign: '', date: '' },
+            { department: '', opinion: '', sign: '', date: '' },
+            { department: '', opinion: '', sign: '', date: '' },
+            { department: '', opinion: '', sign: '', date: '' },
+            { department: '', opinion: '', sign: '', date: '' }
+          ],
+          // 管代审批
+          managementApprovalList: [],
+          managementOtherText: '',
+          correctiveActionChecked: false,
+          correctiveActionNo: '',
+          noCorrectiveReasonChecked: false,
+          noCorrectiveReasonText: '',
+          managementSign: '',
+          managementSignDate: '',
+          // 总经理审批
+          generalManagerApprovalList: [],
+          generalManagerSign: '',
+          generalManagerSignDate: '',
+          // 处置记录
+          returnChecked: false,
+          returnDate: '',
+          returnNo: '',
+          destroyChecked: false,
+          destroyMethods: [],
+          destroyOther: '',
+          concessionChecked: false,
+          concessionDate: '',
+          selectChecked: false,
+          selectPerson: '',
+          selectDate: '',
+          selectResult: '',
+          reworkChecked: false,
+          reworkPlan: '',
+          reworkDate: '',
+          reworkResult: '',
+          otherChecked: false,
+          otherRemark: '',
+          // 签名
+          executorSign: '',
+          executorDate: '',
+          departmentSign: '',
+          departmentDate: '',
+          supervisorSign: '',
+          supervisorDate: ''
+        }
+      };
+    },
+    methods: {
+      /* 打开质检报告 */
+      async init(basicInfoData) {
+        if (basicInfoData.productName) {
+          this.$set(this, 'basicInfoData', {
+            ...this.basicInfoData,
+            ...basicInfoData
+          });
+        }
+      },
+      /* 保存编辑 */
+      getValue() {
+        return this.basicInfoData;
+      }
+    }
+  };
+</script>
+
+<style lang="scss" scoped>
+  td {
+    padding: 4px;
+  }
+  @media print {
+    .table1 {
+      page-break-after: always;
+    }
+    .table2 {
+      page-break-before: always;
+    }
+  }
+</style>

+ 88 - 11
src/views/produce/components/workPlan/index.vue

@@ -136,27 +136,37 @@
             >
             <jimureportBrowse
               style="display: inline-block; margin-left: 5px"
-              v-if="row.status == 1"
+              v-if="row.status == 1 && isReportApproval != 1"
               text="质检报告"
               :businessId="row.id"
               businessCode="qmsqualityinspectionprint"
             ></jimureportBrowse>
+
             <el-dropdown
               trigger="click"
               v-if="
                 row.status == 1 &&
                 isReportApproval == 1 &&
                 (!row.reportApprovalStatus || row.reportApprovalStatus == 0) &&
-                isEmptyObject(row.reportTemplateJson)
+                isEmptyObject(row.reportTemplateJson) &&
+                $hasPermission('qms:quality_work_order:generateReport')
               "
             >
-              <el-link type="primary" :underline="false">生成检报告</el-link>
+              <el-link type="primary" :underline="false">生成检报告</el-link>
               <el-dropdown-menu slot="dropdown">
                 <el-dropdown-item
                   v-for="item in reportTemplateList"
                   :key="item.id"
                   @click.native="generateReportApproval(row, item)"
-                  >{{ item.name }}</el-dropdown-item
+                  >{{ item.name
+                  }}{{
+                    '(' +
+                    item.versionSymbol +
+                    item.bigVersion +
+                    item.versionMark +
+                    item.smallVersion +
+                    ')'
+                  }}</el-dropdown-item
                 >
               </el-dropdown-menu>
             </el-dropdown>
@@ -165,24 +175,27 @@
                 row.status == 1 &&
                 isReportApproval == 1 &&
                 row.reportApprovalStatus &&
-                !isEmptyObject(row.reportTemplateJson)
+                !isEmptyObject(row.reportTemplateJson) &&
+                $hasPermission('qms:quality_work_order:generateReport')
               "
               type="primary"
               :underline="false"
               @click="openReport(row)"
-              >查看检报告</el-link
+              >查看检报告</el-link
             >
             <!-- 质检报告审批  -->
             <el-link
               v-if="
                 row.status == 1 &&
                 isReportApproval == 1 &&
-                !row.reportApprovalStatus
+                !row.reportApprovalStatus &&
+                !isEmptyObject(row.reportTemplateJson) &&
+                $hasPermission('qms:quality_work_order:qualityReportApproval')
               "
               type="primary"
               :underline="false"
               @click="reportApprovalSubmit(row)"
-              >检报告审批</el-link
+              >检报告审批</el-link
             >
 
             <el-popconfirm
@@ -266,6 +279,11 @@
       <detailsOrder ref="detailsRef" />
 
       <addSample ref="addSampleRef" @reload="search"></addSample>
+      <ReportTemplateWrapper
+        ref="targetComponentRef"
+        :type="'0'"
+        @reload="search"
+      ></ReportTemplateWrapper>
     </div>
   </ele-modal>
 </template>
@@ -286,6 +304,7 @@
     closeWork,
     sampleCollection,
     checkByQualityWorkOrderId,
+    getDetailById,
     getQmsReportTemplatePageList
   } from '@/api/inspectionWork';
   import dictMixins from '@/mixins/dictMixins';
@@ -296,6 +315,8 @@
   import { parameterGetByCode } from '@/api/system/dictionary-data';
   import { recordingMethodList } from '@/utils/util.js';
   import { inspectionProjectStatus } from '@/enum/dict.js';
+  import { getCategoryByCode } from '@/api/main/index';
+  import ReportTemplateWrapper from '@/views/inspectionReport/components/reportTemplateWrapper.vue';
 
   export default {
     mixins: [dictMixins, tabMixins],
@@ -307,7 +328,8 @@
       detailsOrder,
       processSubmitDialog,
       addSample,
-      transfer
+      transfer,
+      ReportTemplateWrapper
     },
     data() {
       return {
@@ -383,6 +405,16 @@
               return row.qualityNames || '';
             }
           },
+          {
+            prop: 'executeUserName',
+            label: '执行人',
+            align: 'center',
+            width: 120,
+            showOverflowTooltip: true,
+            formatter: (row) => {
+              return row.executeUserName || '';
+            }
+          },
           {
             prop: 'qualityTime',
             label: '质检时间',
@@ -411,18 +443,50 @@
             width: 120,
             showOverflowTooltip: true
           },
+          {
+            prop: 'inspectionTeamName',
+            label: '送检班组',
+            align: 'center',
+            width: 120,
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'createUserName',
+            label: '送检人',
+            align: 'center',
+            width: 120,
+            showOverflowTooltip: true
+          },
           {
             prop: 'specification',
             label: '规格',
             align: 'center',
             showOverflowTooltip: true
           },
+          {
+            prop: 'modelType',
+            label: '型号',
+            align: 'center',
+            showOverflowTooltip: true
+          },
           {
             prop: 'brandNo',
             label: '牌号',
             align: 'center',
             showOverflowTooltip: true
           },
+          {
+            label: '电压等级',
+            prop: 'voltage',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'colorKey',
+            label: '颜色',
+            align: 'center',
+            showOverflowTooltip: true
+          },
           {
             prop: 'produceTaskName',
             label: '工序',
@@ -628,6 +692,9 @@
             planList: this.statusList
           }
         ];
+      },
+      clientEnvironmentId() {
+        return this.$store.state.user.info.clientEnvironmentId;
       }
     },
     methods: {
@@ -635,7 +702,8 @@
         getQmsReportTemplatePageList({
           pageNum: 1,
           pageSize: 999,
-          isEnabled: 1
+          isEnabled: 1,
+          type: 0
         })
           .then((res) => {
             this.reportTemplateList = res.list;
@@ -646,6 +714,15 @@
             // this.$message.error('获取报表模板列表失败:' + (err.message || '操作异常'));
           });
       },
+      generateReportApproval(row, item) {
+        row.reportTemplateCode = item.code;
+        this.$refs.targetComponentRef.open({ row, item }, false);
+      },
+      async openReport(row) {
+        const data = await getDetailById(row.id);
+        row.reportApprovalTaskVos = data.data.reportApprovalTaskVos || {};
+        this.$refs.targetComponentRef.open({ row, item: {} }, true);
+      },
       // datasource({ page, where, limit }) {
       //   // return getList({
       //   //   ...where,
@@ -771,7 +848,7 @@
       },
 
       isEmptyObject(obj) {
-        return Object.keys(obj).length === 0;
+        return !obj || Object.keys(obj).length === 0;
       },
 
       handleClose() {