Kaynağa Gözat

Merge branch 'master' into test

liujt 6 ay önce
ebeveyn
işleme
c50ed98ea4

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

@@ -0,0 +1,46 @@
+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) {
+  const res = await request.get(`/qms/quality_work_order/queryInspectionReportData/${id}`);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+} 
+
+// 质检报告质检项列表信息
+export async function queryInspectionReportList(id) {
+  const res = await request.get(`/qms/quality_work_order/queryInspectionReportList/${id}`);
+  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(data) {
+  const res = await request.delete(`/qms/quality_work_order/deleteReport`, { data });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

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

@@ -175,3 +175,14 @@ export async function getOutboundDetail(params) {
   }
   return Promise.reject(new Error(res.data.message));
 }
+
+// /main/qmsreporttemplate/page 报表模板分页查询 get
+export async function getQmsReportTemplatePageList(params) {
+  const res = await request.get(`/main/qmsreporttemplate/page`, {
+    params
+  });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 5 - 0
src/views/inspectionProject/index.vue

@@ -17,6 +17,7 @@
         <!-- 表头工具栏 -->
         <template v-slot:toolbar>
           <el-button
+            v-if="$hasPermission('qms:inspection_item:save')"
             size="small"
             type="primary"
             icon="el-icon-plus"
@@ -27,6 +28,7 @@
           </el-button>
 
           <el-button
+            v-if="$hasPermission('qms:inspection_item:save')"
             type="primary"
             size="mini"
             icon="el-icon-upload2"
@@ -88,6 +90,7 @@
         <!-- 操作列 -->
         <template v-slot:action="{ row }">
           <el-popconfirm
+            v-if="$hasPermission('qms:inspection_item:save')"
             class="ele-action"
             title="确定要复制当前质检吗?"
             @confirm="copy(row)"
@@ -99,6 +102,7 @@
             </template>
           </el-popconfirm>
           <el-link
+            v-if="$hasPermission('qms:inspection_item:update')"
             type="primary"
             :underline="false"
             icon="el-icon-edit"
@@ -108,6 +112,7 @@
           </el-link>
 
           <el-popconfirm
+            v-if="$hasPermission('qms:inspection_item:delete')"
             class="ele-action"
             title="确定要删除当前质检吗?"
             @confirm="remove(row)"

+ 137 - 0
src/views/inspectionReport/components/detailDialog.vue

@@ -0,0 +1,137 @@
+<template>
+    <ele-modal
+        :visible.sync="visible"
+        title="质检报告"
+        width="70%"
+        append-to-body
+        :maxable="true"
+    >
+        <div id="printSection" style="padding: 20px; background-color: white;">
+            <div v-html="currentRow.reportTemplateJson?.template"></div>
+        </div>
+        <template v-slot:footer>
+            <el-button @click="cancel">关闭</el-button>
+        </template>
+    </ele-modal>
+</template>
+
+<script>
+    import { queryInspectionReportData, queryInspectionReportList, generateReport } from '@/api/inspectionReport';
+export default {
+    name: 'QualityReportDetail',
+    props: {
+        isView: {
+            type: Boolean,
+            default: false
+        },
+        visible: {
+            type: Boolean,
+            default: false
+        },
+        row: {
+            type: Object,
+            default: () => {}
+        },
+        item: {
+            type: Object,
+            default: () => {}
+        },
+    },
+    data() {
+        return {
+        // visible: false,
+        loading: false,
+        currentRow: {}, // 存储当前行数据
+        templateItem: {}, // 存储当前模板项数据
+        // 基本信息数据
+        basicInfoData: {},
+        
+        // 检验项目
+        inspectionItems: [],
+    
+        }
+    },
+    watch: {
+        visible(newVal, oldVal) {
+            if (newVal) {
+                this.open(this.row, this.item);
+            }
+        }
+    },
+    created() {
+        // 页面加载时可以在这里添加数据初始化逻辑
+    },
+    methods: {
+        /* 打开质检报告 */
+        open(row, item) {
+            this.currentRow = row;
+        },
+        getBasicInfo() {
+            queryInspectionReportData(this.currentRow.id).then(res => {
+                this.basicInfoData = res;
+            });
+        },
+        getInspectionItems() {
+            queryInspectionReportList(this.currentRow.id).then(res => {
+                this.inspectionItems = res;
+            });
+        },
+        cancel() {
+            // this.visible = false;
+            this.$emit('update:visible', false);
+        },
+        /* 保存编辑 */
+        save() {
+            this.loading = true;
+            const printSection = document.getElementById('printSection');
+            const params = {
+                id: this.currentRow.id,
+                reportTemplateId: this.templateItem.id,
+                reportTemplateCode: this.templateItem.code || this.templateItem.reportTemplateCode,
+                reportTemplateName: this.templateItem.name || this.templateItem.reportTemplateName,
+                reportTemplateJson: {
+                    template: printSection.innerHTML,
+                    basicInfoData: this.basicInfoData,
+                    inspectionItems: this.inspectionItems,
+                }
+            };
+            generateReport(params).then(res => {
+                this.loading = false;
+                this.$message({
+                    message: '保存成功',
+                    type: 'success'
+                });
+                this.$emit('reload');
+                this.cancel();
+                // this.$emit('update:visible', false);
+                // this.visible = false;
+            }).catch(err => {
+                this.loading = false;
+                this.$message({
+                    message: err.message,
+                    type: 'error'
+                });
+            });
+        },
+        /* 打印 */
+        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(
+                '<link rel="stylesheet" href="your-stylesheet-url.css" type="text/css" />'
+            );
+            printWindow.document.write('</head><body>');
+            printWindow.document.write(printSection.innerHTML);
+            printWindow.document.write('</body></html>');
+            printWindow.document.close();
+            printWindow.onload = function () {
+                printWindow.print();
+            };
+        }
+    }
+}
+</script>

+ 93 - 0
src/views/inspectionReport/components/search.vue

@@ -0,0 +1,93 @@
+<!-- 搜索表单 -->
+
+<template>
+  <el-form
+    label-width="77px"
+    class="ele-form-search"
+    @keyup.enter.native="search"
+    @submit.native.prevent
+  >
+    <el-row :gutter="15">
+      <el-col v-bind="styleResponsive ? { lg: 6, md: 10 } : { span: 6 }">
+        <el-form-item label="编码:">
+          <el-input clearable v-model="where.code" placeholder="请输入" />
+        </el-form-item>
+      </el-col>
+
+      <el-col v-bind="styleResponsive ? { lg: 6, md: 10 } : { span: 6 }">
+        <el-form-item label="工序:">
+          <el-input
+            clearable
+            v-model="where.produceTaskName"
+            placeholder="请输入"
+          />
+        </el-form-item>
+      </el-col>
+      <!--
+      <el-col v-bind="styleResponsive ? { lg: 6, md: 10 } : { span: 6}">
+        <el-form-item label="状态:" >
+          <el-select v-model="where.status" class="m-2" placeholder="请选择" size="large" style="width: 100%">
+            <el-option label="停用" :value="0" />
+            <el-option label="启用" :value="1" />
+          </el-select>
+        </el-form-item>
+      </el-col> -->
+      <!-- <el-col v-bind="styleResponsive ?{ lg: 6, md: 12 } : { span: 6 }">
+        <el-form-item label="组织机构:">
+          <auth-selection data-type="Array" v-model="where.deptIds" style="width: 100%"></auth-selection>
+        </el-form-item>
+      </el-col> -->
+      <el-col
+        style="display: flex; justify-content: flex-end"
+        v-bind="styleResponsive ? { lg: 12, md: 6 } : { span: 12 }"
+      >
+        <div class="ele-form-actions">
+          <el-button
+            type="primary"
+            icon="el-icon-search"
+            class="ele-btn-icon"
+            @click="search"
+          >
+            查询
+          </el-button>
+          <el-button @click="reset">重置</el-button>
+        </div>
+      </el-col>
+    </el-row>
+  </el-form>
+</template>
+
+<script>
+  export default {
+    data() {
+      // 默认表单数据
+      const defaultWhere = {
+        code: '',
+        produceTaskName: '',
+        deptIds: ''
+      };
+      return {
+        defaultWhere,
+        // 表单数据
+        where: { ...defaultWhere }
+      };
+    },
+    computed: {
+      // 是否开启响应式布局
+      styleResponsive() {
+        return this.$store.state.theme.styleResponsive;
+      }
+    },
+    methods: {
+      /* 搜索 */
+      search() {
+        this.$emit('search', this.where);
+      },
+      /*  重置 */
+      reset() {
+        this.where = { ...this.defaultWhere };
+        this.search();
+      }
+    }
+  };
+</script>

+ 495 - 0
src/views/inspectionReport/index.vue

@@ -0,0 +1,495 @@
+<template>
+  <div class="ele-body">
+    <el-card shadow="never">
+      <seek-page
+        :seekList="seekList"
+        @search="search"
+        ref="search"
+        :keyValue="'qms-inspectionReport-index-search'"
+      ></seek-page>
+
+      <ele-pro-table
+        ref="table"
+        :columns="columns"
+        :datasource="datasource"
+        :pageSize="20"
+        :pageSizes="[20, 30, 40, 50, 100]"
+        @columns-change="handleColumnChange"
+        :cache-key="cacheKeyUrl"
+        row-key="id"
+        @selection-change="selectionChangeHandle"
+      >
+        <!-- 表头工具栏 -->
+        <template v-slot:toolbar>
+          <!-- <el-button
+            size="small"
+            type="primary"
+            class="ele-btn-icon"
+            @click="batchClose"
+            :disabled="selection.length === 0"
+          >
+            批量关闭
+          </el-button> -->
+        </template>
+
+        <template v-slot:code="{ row }">
+          <el-link
+            type="primary"
+            :underline="false"
+            @click="openDetails(row)"
+            >{{ row.code }}</el-link
+          >
+        </template>
+
+        <template v-slot:sourceCode="{ row }">
+          <div>{{
+            row.qualityPlanCode ? row.qualityPlanCode : row.workOrderCode
+          }}</div>
+        </template>
+
+        <template v-slot:files="scope">
+          <el-link
+            v-for="link in scope.row.files"
+            :key="link.id"
+            type="primary"
+            :underline="false"
+            @click="downloadFile(link)"
+            >{{ link.name }}</el-link
+          >
+        </template>
+
+        <template v-slot:qualityType="{ row }">{{
+          getDictValue('质检计划类型', row.qualityType)
+        }}</template>
+        <template v-slot:qualityMode="{ row }">{{
+          getDictValue('取样类型', row.qualityMode)
+        }}</template>
+        <template v-slot:accessory="scope">
+          <el-link
+            v-for="link in scope.row.accessory"
+            :key="link.id"
+            type="primary"
+            :underline="false"
+            @click="downloadFile(link)"
+            >{{ link.name }}</el-link
+          >
+        </template>
+        <!-- 操作列 -->
+        <template v-slot:action="{ row }">
+          <el-link
+            type="primary"
+            :underline="false"
+            v-if="(row.reportApprovalStatus == 0 || row.reportApprovalStatus == 3) && $hasPermission('qms:inspectionReport:update')"
+            @click="openEdit(row)"
+            >编辑</el-link
+          >
+          <el-link
+            v-if="
+              isReportApproval == 1 &&
+              (row.reportApprovalStatus == 0 || row.reportApprovalStatus == 3) && $hasPermission('qms:inspectionReport:submit')
+            "
+            type="primary"
+            :underline="false"
+            @click="reportApprovalSubmit(row)"
+            >提交</el-link
+          >
+
+          <el-popconfirm
+            class="ele-action"
+            title="确定要删除吗?"
+            @confirm="deleteReportBtn(row)"
+            v-if="(row.reportApprovalStatus == 0 || row.reportApprovalStatus == 3) && $hasPermission('qms:inspectionReport:delete')"
+          >
+            <template v-slot:reference>
+              <el-link type="danger" :underline="false"> 删除 </el-link>
+            </template>
+          </el-popconfirm>
+        </template>
+      </ele-pro-table>
+    </el-card>
+
+    <component
+      :is="targetComponent"
+      ref="targetComponentRef"
+      :key="targetComponent"
+      :isView="isView"
+      :visible.sync="targetVisible"
+      :row="currentRow"
+      :item="currentItem"
+      @reload="search"
+    ></component>
+
+    <process-submit-dialog
+      api-fun-name="qmsReportApprovalAPI"
+      :processSubmitDialogFlag.sync="processSubmitDialogFlag"
+      :isCloseRefresh="false"
+      v-if="processSubmitDialogFlag"
+      ref="processSubmitDialogRef"
+      @reload="search"
+    ></process-submit-dialog>
+    <QualityReportDetail
+      ref="qualityReportDetailRef"
+      :visible.sync="detailVisible"
+      :row="currentRow"
+    ></QualityReportDetail>
+  </div>
+</template>
+<script>
+  import search from './components/search.vue';
+  import jimureportBrowse from '@/components/jimureport/browseModal.vue';
+  import processSubmitDialog from '@/components/processSubmitDialog/processSubmitDialog.vue';
+  import {
+    closeWorkList,
+  } from '@/api/inspectionWork';
+  import { queryTodo } from '@/api/bpm/task';
+  import dictMixins from '@/mixins/dictMixins';
+  import { getFile } from '@/api/system/file';
+  import { parameterGetByCode } from '@/api/main/index';
+  import tabMixins from '@/mixins/tableColumnsMixin';
+  import { getCategoryByCode } from '@/api/main/index';
+  import { getList, deleteReport } from '@/api/inspectionReport';
+  import inspection_report1 from '../inspectionReport/template/inspection_report1.vue';
+  import QualityReportDetail from '../inspectionReport/components/detailDialog.vue';
+
+  export default {
+    mixins: [dictMixins, tabMixins],
+    components: {
+      search,
+      jimureportBrowse,
+      inspection_report1,
+      processSubmitDialog,
+      QualityReportDetail
+    },
+    data() {
+      return {
+        cacheKeyUrl: 'qsm-c2e9664a-inspectionReport-index',
+        targetComponent: null,
+        targetVisible: false,
+        detailVisible: false,
+        isView: false,
+        currentRow: {},
+        currentItem: {},
+        columns: [
+          // 新增多选列
+          {
+            width: 45,
+            type: 'selection',
+            columnKey: 'selection',
+            align: 'center',
+            reserveSelection: true
+          },
+          {
+            type: 'index',
+            columnKey: 'index',
+            align: 'center',
+            label: '序号',
+            width: 55,
+            showOverflowTooltip: true,
+            fixed: 'left'
+          },
+          {
+            prop: 'reportNumber',
+            label: '报告编码',
+            slot: 'code',
+            align: 'center',
+            width: 180,
+            showOverflowTooltip: true,
+            fixed: 'left'
+          },
+          {
+            prop: 'code',
+            label: '质检工单编码',
+            // slot: 'code',
+            align: 'center',
+            width: 180,
+            showOverflowTooltip: true,
+          },
+
+          {
+            prop: 'productCode',
+            width: 120,
+            label: '产品编码',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'productName',
+            width: 120,
+            label: '产品名称',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'specification',
+            label: '规格',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'brandNo',
+            label: '牌号',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            label: '创建时间',
+            prop: 'reportDate',
+            align: 'center',
+            width: 160
+          },
+          {
+            label: '创建人',
+            prop: 'reportTemplateCreateUserName',
+            align: 'center',
+            width: 160
+          },
+          {
+            label: '审批时间',
+            prop: 'reportApprovalTime',
+            align: 'center',
+            width: 160
+          },
+          {
+            label: '审批人',
+            prop: 'reportApprovalUserName',
+            align: 'center',
+            width: 160
+          },
+          {
+            prop: 'reportApprovalStatus',
+            label: '审批状态',
+            align: 'center',
+            width: 80,
+            formatter: (row, column, cellValue) => {
+              switch (cellValue) {
+                case 0:
+                  return '未提交';
+                case 1:
+                  return '审核中';
+                case 2:
+                  return '已审核';
+                case 3:
+                  return '审核不通过';
+                case 7:
+                  return '作废';
+                default:
+                  return '';
+              }
+            },
+          },
+          {
+            columnKey: 'action',
+            label: '操作',
+            align: 'center',
+            width: 200,
+            resizable: false,
+            slot: 'action',
+            fixed: 'right'
+          }
+        ],
+        formData: {
+          certificateNumber: ''
+        },
+        processSubmitDialogFlag: false,
+        rowData: {},
+        typeList: [], //类型列表
+        qualityMode: [], //取样类型
+        statusList: [
+            { value: 0, label: '未提交' },
+            { value: 1, label: '审核中' },
+            { value: 2, label: '已审核' },
+            { value: 3, label: '审核不通过' },
+            { value: 7, label: '作废' }
+        ],
+        addOpen: false,
+        transferVisible: false,
+        transferId: '',
+        isReportApproval: 0,
+        selection: [] // 存储选中的行数据
+      };
+    },
+    created() {
+      this.getCode();
+    },
+    computed: {
+      seekList() {
+        return [
+          {
+            label: '报告编码:',
+            value: 'reportNumber',
+            type: 'input',
+            placeholder: ''
+          },
+          {
+            label: '工单编码:',
+            value: 'code',
+            type: 'input',
+            placeholder: ''
+          },
+          {
+            label: '工单名称:',
+            value: 'name',
+            type: 'input',
+            placeholder: ''
+          },
+          {
+            label: '产品编码:',
+            value: 'productCode',
+            type: 'input',
+            placeholder: ''
+          },
+          {
+            label: '产品名称:',
+            value: 'productName',
+            type: 'input',
+            placeholder: ''
+          },
+          {
+            label: '型号:',
+            value: 'modelType',
+            type: 'input',
+            placeholder: ''
+          },
+          {
+            label: '规格:',
+            value: 'specification',
+            type: 'input',
+            placeholder: ''
+          },
+          {
+            label: '牌号:',
+            value: 'brandNo',
+            type: 'input',
+            placeholder: ''
+          },
+          {
+            label: '审批状态:',
+            value: 'reportApprovalStatus',
+            type: 'select',
+            placeholder: '',
+            planList: this.statusList
+          }
+        ];
+      },
+      clientEnvironmentId() {
+        return this.$store.state.user.info.clientEnvironmentId;
+      }
+    },
+    methods: {
+      selectionChangeHandle(val) {
+        this.selection = val;
+      },
+      addSampleOpen(row) {
+        this.$refs.addSampleRef.open(row);
+      },
+      // 批量关闭
+      batchClose() {
+        const validRows = this.selection.filter(
+          (row) => row.status === 0 && row.qualityType === 2
+        );
+        if (validRows.length === 0) {
+          this.$message.warning('所选工单中没有可关闭的工单');
+          return;
+        }
+
+        this.$confirm(`确定关闭选中的 ${validRows.length} 个工单吗?`, '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(async () => {
+          try {
+            await closeWorkList(validRows.map((row) => row.id));
+            this.$refs.table.clearSelection();
+            this.search();
+          } catch (err) {
+            this.$message.error('批量关闭失败:' + (err.message || '操作异常'));
+          }
+        });
+      },
+
+      reportApprovalSubmit(res) {
+        this.processSubmitDialogFlag = true;
+        this.$nextTick(async () => {
+          let params = {   
+            businessId: res.id,
+            businessKey: 'qms_report_approval',
+            formCreateUserId: res.createUserId,
+            variables: {
+              businessCode: res.code,
+              businessName: res.reportNumber,
+              businessType: '质检报告单'
+            }
+          };
+          if (this.clientEnvironmentId == 5) {
+            const data = await getCategoryByCode(res.productCode);
+            if (data && data.categoryLevelCodePath?.includes('W3-209')) {
+              params.businessKey = 'qms_report_approval1';
+            } else {
+              params.businessKey = 'qms_report_approval';
+            }
+          }
+          this.$refs.processSubmitDialogRef.init(params);
+        });
+      },
+      getCode() {
+        parameterGetByCode({
+          code: 'qms_report_approval'
+        }).then((res) => {
+          this.isReportApproval = res.value;
+        });
+      },
+      async datasource({ page, where, limit }) {
+        try {
+          await queryTodo({});
+        } catch (err) {
+          console.error('调用queryTodo失败:', err);
+        }
+
+        return getList({
+          ...where,
+          pageNum: page,
+          size: limit
+        });
+      },
+      search(where) {
+        this.$refs.table.reload({
+          where: where
+          // page: 1
+        });
+      },
+      async openEdit(row) {
+        console.log(row);
+         this.isView = false;
+        this.targetComponent = row.reportTemplateCode;
+        this.currentRow = row;
+        this.$nextTick(() => {
+          this.targetVisible = true;
+        });
+      },
+      openDetails(row) {
+        let id = row.id;
+        this.isView = true;
+        this.currentRow = row;
+        this.targetComponent = row.reportTemplateCode;
+        this.$nextTick(() => {
+          this.targetVisible = true;
+        });
+      },
+      async deleteReportBtn(row) {
+          try {
+            await deleteReport(row.id);
+            this.$message.success('删除成功');
+            this.done();
+          } catch (err) {
+            this.$message.error('删除失败:' + (err.message || '操作异常'));
+          }
+      },
+      downloadFile(file) {
+        getFile({ objectName: file.storePath }, file.name);
+      },
+      done() {
+        this.$refs.search.search();
+      },
+    }
+  };
+</script>

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

@@ -0,0 +1,344 @@
+<template>
+    <ele-modal
+        :visible.sync="visible"
+        title="质检报告"
+        width="70%"
+        append-to-body
+        :maxable="true"
+    >
+        <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="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 v-slot:footer>
+            <el-button v-if="isView" type="primary" @click="print" v-loading="loading">打印</el-button>
+            <el-button v-if="!isView" type="primary" @click="save" v-loading="loading">确认</el-button>
+            <el-button @click="cancel">关闭</el-button>
+        </template>
+    </ele-modal>
+</template>
+
+<script>
+    import { queryInspectionReportData, queryInspectionReportList, generateReport } from '@/api/inspectionReport';
+export default {
+    name: 'QualityReport',
+    props: {
+        isView: {
+            type: Boolean,
+            default: false
+        },
+        visible: {
+            type: Boolean,
+            default: false
+        },
+        row: {
+            type: Object,
+            default: () => {}
+        },
+        item: {
+            type: Object,
+            default: () => {}
+        },
+    },
+    data() {
+        return {
+        // visible: false,
+        loading: false,
+        currentRow: {}, // 存储当前行数据
+        templateItem: {}, // 存储当前模板项数据
+        // 基本信息数据
+        basicInfoData: {},
+        
+        // 检验项目
+        inspectionItems: [],
+    
+        }
+    },
+    watch: {
+        visible(newVal, oldVal) {
+            if (newVal) {
+                this.open(this.row, this.item);
+            }
+        }
+    },
+    created() {
+        // 页面加载时可以在这里添加数据初始化逻辑
+    },
+    methods: {
+        /* 打开质检报告 */
+        open(row, item) {
+            this.currentRow = row;
+            this.templateItem = item || row;
+            console.log('currentRow~~~', this.currentRow, this.templateItem);
+            // this.visible = true;
+            if(this.currentRow.reportTemplateJson?.basicInfoData) {
+                this.basicInfoData = this.currentRow.reportTemplateJson.basicInfoData || {};
+                this.inspectionItems = this.currentRow.reportTemplateJson.inspectionItems || [];
+            } else {
+                this.getBasicInfo();
+                this.getInspectionItems();
+            }
+        },
+        getBasicInfo() {
+            queryInspectionReportData(this.currentRow.id).then(res => {
+                this.basicInfoData = res;
+            });
+        },
+        getInspectionItems() {
+            queryInspectionReportList(this.currentRow.id).then(res => {
+                this.inspectionItems = res;
+            });
+        },
+        cancel() {
+            // this.visible = false;
+            this.$emit('update:visible', false);
+        },
+        /* 保存编辑 */
+        save() {
+            this.loading = true;
+            const printSection = document.getElementById('printSection');
+            const params = {
+                id: this.currentRow.id,
+                reportTemplateId: this.templateItem.id,
+                reportTemplateCode: this.templateItem.code || this.templateItem.reportTemplateCode,
+                reportTemplateName: this.templateItem.name || this.templateItem.reportTemplateName,
+                reportTemplateJson: {
+                    template: printSection.innerHTML,
+                    basicInfoData: this.basicInfoData,
+                    inspectionItems: this.inspectionItems,
+                }
+            };
+            generateReport(params).then(res => {
+                this.loading = false;
+                this.$message({
+                    message: '保存成功',
+                    type: 'success'
+                });
+                this.$emit('reload');
+                this.cancel();
+                // this.$emit('update:visible', false);
+                // this.visible = false;
+            }).catch(err => {
+                this.loading = false;
+                this.$message({
+                    message: err.message,
+                    type: 'error'
+                });
+            });
+        },
+        /* 打印 */
+        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(
+                '<link rel="stylesheet" href="your-stylesheet-url.css" type="text/css" />'
+            );
+            printWindow.document.write('</head><body>');
+            printWindow.document.write(printSection.innerHTML);
+            printWindow.document.write('</body></html>');
+            printWindow.document.close();
+            printWindow.onload = function () {
+                printWindow.print();
+            };
+        }
+    }
+}
+</script>

+ 102 - 8
src/views/inspectionWork/index.vue

@@ -22,6 +22,7 @@
         <!-- 表头工具栏 -->
         <template v-slot:toolbar>
           <el-button
+            v-if="$hasPermission('qms:quality_work_order:close')"
             size="small"
             type="primary"
             class="ele-btn-icon"
@@ -85,7 +86,7 @@
           <el-link
             type="primary"
             :underline="false"
-            v-if="row.status == 0"
+            v-if="row.status == 0 && $hasPermission('qms:quality_work_order:update')"
             @click="openEdit('edit', row)"
             >报工</el-link
           >
@@ -93,27 +94,63 @@
           <el-link
             type="primary"
             :underline="false"
-            v-if="row.status == 0"
+            v-if="row.status == 0 && $hasPermission('qms:quality_work_order:transfer')"
             @click="openTransfer(row.id)"
             >转派</el-link
           >
 
-          <el-link type="primary" :underline="false" @click="openNumber(row)"
+          <el-link v-if="$hasPermission('qms:quality_work_order:certificate')" type="primary" :underline="false" @click="openNumber(row)"
             >合格证</el-link
           >
           <jimureportBrowse
             style="display: inline-block; margin-left: 5px"
-            v-if="row.status == 1"
+            v-if="row.status == 1 && $hasPermission('qms:quality_work_order:qualityReport')"
             text="质检报告"
             :businessId="row.id"
             businessCode="qmsqualityinspectionprint"
           ></jimureportBrowse>
+          <el-dropdown
+            trigger="click"
+            v-if="
+                row.status == 1 &&
+                isReportApproval == 1 &&
+                !row.reportApprovalStatus && row.reportApprovalStatus != 0 && 
+                $hasPermission('qms:quality_work_order:generateReport')
+              "
+          >
+            <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
+              >
+            </el-dropdown-menu>
+          </el-dropdown>
+          <el-link
+            v-if="
+                row.status == 1 &&
+                isReportApproval == 1 &&
+                row.reportApprovalStatus &&
+                $hasPermission('qms:quality_work_order:generateReport')
+              "
+            type="primary"
+            :underline="false"
+            @click="openReport(row)"
+            >查看质检报告</el-link
+          >
           <!-- 质检报告审批  -->
           <el-link
             v-if="
               row.status == 1 &&
               isReportApproval == 1 &&
-              !row.reportApprovalStatus
+              !row.reportApprovalStatus &&
+              $hasPermission('qms:quality_work_order:qualityReportApproval')
             "
             type="primary"
             :underline="false"
@@ -125,7 +162,7 @@
             class="ele-action"
             title="确定要关闭吗?"
             @confirm="close(row)"
-            v-if="row.status == 0 && row.qualityType == 2"
+            v-if="row.status == 0 && row.qualityType == 2 && $hasPermission('qms:quality_work_order:close')"
           >
             <template v-slot:reference>
               <el-link type="warning" :underline="false"> 关闭 </el-link>
@@ -158,6 +195,18 @@
     ></process-submit-dialog>
 
     <addSample ref="addSampleRef" @reload="search"></addSample>
+    <!-- <inspectionReport1 ref="qualityRef" @reload="search" :isView="true"></inspectionReport1> -->
+    <component
+      :is="targetComponent"
+      ref="targetComponentRef"
+      v-if="targetComponent"
+      :key="targetComponent"
+      :isView="isView"
+      :visible.sync="targetVisible"
+      :row="currentRow"
+      :item="currentItem"
+      @reload="search"
+    ></component>
   </div>
 </template>
 <script>
@@ -183,6 +232,8 @@
   import { parameterGetByCode } from '@/api/main/index';
   import tabMixins from '@/mixins/tableColumnsMixin';
   import { getCategoryByCode } from '@/api/main/index';
+  import { getQmsReportTemplatePageList } from '@/api/inspectionWork';
+  import inspection_report1 from '../inspectionReport/template/inspection_report1.vue';
 
   export default {
     mixins: [dictMixins, tabMixins],
@@ -192,11 +243,17 @@
       Certificate,
       Transfer,
       processSubmitDialog,
-      addSample
+      addSample,
+      inspection_report1
     },
     data() {
       return {
         cacheKeyUrl: 'qsm-c2e9664a-inspectionWork',
+        targetComponent: null,
+        isView: false,
+        targetVisible: false,
+        currentRow: {},
+        currentItem: {},
         columns: [
           // 新增多选列
           {
@@ -428,7 +485,9 @@
         transferVisible: false,
         transferId: '',
         isReportApproval: 0,
-        selection: [] // 存储选中的行数据
+        selection: [], // 存储选中的行数据
+        reportTemplateList: [], // 报表模板列表
+        templateArr: ['inspection_report1', 'ABBCCC']
       };
     },
     created() {
@@ -436,6 +495,7 @@
       this.requestDict('不良品处理类型');
       this.requestDict('取样类型');
       this.getCode();
+      this.getReportTemplateList();
       this.getTnspectionPlanType();
       this.getQualityMethodCode();
     },
@@ -506,6 +566,40 @@
       }
     },
     methods: {
+      getReportTemplateList() {
+        getQmsReportTemplatePageList({
+          pageNum: 1,
+          pageSize: 999,
+          isEnabled: 1
+        }).then((res) => {
+            this.reportTemplateList = res.list;
+            console.log('reportTemplateList', this.reportTemplateList);
+        }).catch((err) => {
+          console.error('获取报表模板列表失败:', err);
+          // this.$message.error('获取报表模板列表失败:' + (err.message || '操作异常'));
+        });
+      },
+      generateReportApproval(row, item) {
+        console.log('generateReportApproval', row, item);
+        this.targetComponent = item.code;
+        this.isView = false;
+        this.currentRow = row;
+        this.currentItem = item;
+        // this.$refs.targetComponentRef.open(item, row);
+        this.$nextTick(() => {
+        //   this.$refs.targetComponentRef.open(row, isView);
+          this.targetVisible = true;
+        });
+      },
+      openReport(row) {
+        console.log('openReport', row);
+        this.targetComponent = row.reportTemplateCode;
+        this.isView = true;
+        this.currentRow = row;
+         this.$nextTick(() => {
+          this.targetVisible = true;
+        });
+      },
       selectionChangeHandle(val) {
         this.selection = val;
       },