Przeglądaj źródła

Merge branch 'test' of http://110.41.163.243:9980/kd-aiot/kd-aiot-frontend-qms into lcw-dev

yusheng 11 miesięcy temu
rodzic
commit
0a56a7648f

Plik diff jest za duży
+ 0 - 0
public/json/test.json


+ 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(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));
+}

+ 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));
+}

+ 31 - 0
src/api/main/index.js

@@ -59,3 +59,34 @@ export async function getCategoryByCode(code) {
   }
   return Promise.reject(new Error(res.data.message));
 }
+
+
+// 规则记录分页查询
+export async function recordrulesPage(body) {
+  const res = await request.post(`/main/recordrules/page`, body);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// 规则记录分页查询 过程控制 /main/recordrules/processInspectionRecordRulesPage
+export async function recordrulesNotProduceTaskConfigRecordRulesPage(body) {
+  const res = await request.post(
+    `/main/recordrules/processInspectionRecordRulesPage`,
+    body
+  );
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// /main/recordrulestype/page 规则类型分页查询
+export async function recordrulesTypePage(body) {
+  const res = await request.post(`/main/recordrulestype/page`, body);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 473 - 0
src/components/releaseRules/index.vue

@@ -0,0 +1,473 @@
+<!-- 用户编辑弹窗 -->
+<template>
+  <ele-modal
+    width="80%"
+    :visible.sync="visible"
+    title="选择模板"
+    :close-on-click-modal="false"
+    :maxable="true"
+    :resizable="true"
+    append-to-body
+  >
+    <div class="ele-body">
+      <el-card shadow="never" v-loading="loading">
+        <ele-split-layout
+          width="230px"
+          allow-collapse
+          :right-style="{ overflow: 'hidden' }"
+        >
+          <div>
+            <div class="ele-border-lighter sys-organization-list">
+              <el-tree
+                :data="typeTree"
+                default-expand-all
+                @node-click="handleNodeClick"
+                highlight-current
+                ref="treeRef"
+                node-key="id"
+                :expand-on-click-node="false"
+              >
+                <span class="custom-tree-node" slot-scope="{ node, data }">
+                  <div>
+                    <span>{{
+                      data.type == 'templateStyle' ? '模板-' : ''
+                    }}</span>
+                    {{ node.label }}</div
+                  >
+                  <span> </span>
+                </span>
+              </el-tree>
+            </div>
+          </div>
+          <template v-slot:content>
+            <search @search="reload" />
+            <ele-pro-table
+              ref="table"
+              :columns="columns"
+              :datasource="datasource"
+              row-key="id"
+              :initLoad="false"
+              @cell-click="cellClick"
+            >
+              <template v-slot:action="{ row }">
+                <el-radio class="radio" v-model="radio" :label="row.id">
+                  <i></i>
+                </el-radio>
+              </template>
+            </ele-pro-table>
+          </template>
+        </ele-split-layout> </el-card
+    ></div>
+    <template v-slot:footer>
+      <el-button type="primary" @click="save"> 确定 </el-button>
+      <el-button @click="visible = false"> 返回 </el-button>
+    </template>
+  </ele-modal>
+</template>
+
+<script>
+  import search from './search.vue';
+  import dictMixins from '@/mixins/dictMixins';
+  import { recordrulesPage, recordrulesTypePage } from '@/api/main/index';
+  export default {
+    components: { search },
+    mixins: [dictMixins],
+
+    data() {
+      return {
+        visible: false,
+        radio: null,
+        current: {},
+        loading: false,
+        type: '',
+        typeTree: [],
+        treeNode: null
+      };
+    },
+    computed: {
+      columns() {
+        return [
+          {
+            columnKey: 'index',
+            label: '序号',
+            type: 'index',
+            width: 55,
+            align: 'center',
+            showOverflowTooltip: true,
+            fixed: 'left'
+          },
+          {
+            prop: 'code',
+            label: '记录规则编码',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110,
+            slot: 'code'
+          },
+          {
+            prop: 'name',
+            label: '记录规则名称',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110
+          },
+          {
+            prop: 'industryType',
+            label: '行业类别',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110,
+            formatter: (row) => {
+              return this.getDictValue('行业类别', row.industryType);
+            }
+          },
+          {
+            prop: 'businessType',
+            label: '业务分类',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110,
+            formatter: (row) => {
+              return this.getDictValue('业务类型', row.businessType);
+            }
+          },
+          {
+            prop: 'reportWorkType',
+            label: '模块划分',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110,
+            formatter: (row) => {
+              return this.getDictValue('记录规则报工类型', row.reportWorkType);
+            }
+          },
+          {
+            prop: 'classify',
+            label: '应用场景',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110,
+            formatter: (row) => {
+              return this.getDictValue('记录规则类型', row.classify);
+            }
+          },
+          {
+            prop: 'recordTemplateStyle',
+            label: '模板样式',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110,
+            formatter: (row) => {
+              return this.getDictValue(
+                '记录表模板样式',
+                row.recordTemplateStyle
+              );
+            }
+          },
+          {
+            prop: 'executeMethodName',
+            label: '执行方式',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110
+          },
+          {
+            prop: 'produceTaskName',
+            label: '工序名称',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110
+          },
+          {
+            prop: 'version',
+            label: '版本号',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110,
+            formatter: (row) => {
+              return `${row.versionSymbol}${row.bigVersion}${row.versionMark}${row.smallVersion}`;
+            }
+          },
+          {
+            prop: 'fromName',
+            label: '来源版本',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 150
+          },
+          {
+            prop: 'startDate',
+            label: '启用日期',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110,
+            formatter: (row) => {
+              return this.$util.toDateString(row.startDate, 'yyyy-MM-dd');
+            }
+          },
+          {
+            prop: 'stopDate',
+            label: '停用日期',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110,
+            formatter: (row) => {
+              return this.$util.toDateString(row.stopDate, 'yyyy-MM-dd');
+            }
+          },
+          {
+            prop: '',
+            label: '周期',
+            align: 'center',
+            showOverflowTooltip: true,
+            formatter: (row) => {
+              return (
+                row.frequencyValue +
+                this.getDictValue('规则周期', row.frequencyUnit)
+              );
+            }
+          },
+          {
+            prop: 'createUserName',
+            label: '创建人',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110
+          },
+          {
+            prop: 'createTime',
+            label: '创建时间',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110
+          },
+          {
+            prop: 'enable',
+            label: '是否启用',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110,
+            formatter: (row) => {
+              return row.enable ? '启用' : '停用';
+            }
+          },
+          {
+            prop: 'publishStatus',
+            label: '状态',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110,
+            formatter: (row) => {
+              switch (row.publishStatus) {
+                case 0:
+                  return '草稿';
+                case 1:
+                  return '已发布';
+                case 2:
+                  return '已撤销';
+                default:
+                  return '';
+              }
+            }
+          },
+          {
+            columnKey: 'action',
+            label: '操作',
+            width: 260,
+            align: 'center',
+            resizable: false,
+            slot: 'action',
+            fixed: 'left'
+          }
+        ];
+      }
+    },
+    created() {},
+    methods: {
+      open(type) {
+        this.visible = true;
+        this.type = type;
+        this.getTypeList();
+      },
+      // 类型列表
+      async getTypeList() {
+        const { list = [] } = await recordrulesTypePage({
+          pageNum: 1,
+          size: 99999
+        });
+        this.typeTree = [];
+        console.log('type list', list);
+
+        const industryMap = new Map();
+
+        list
+          .filter((item) => [this.type].includes(item.reportWorkType))
+          .forEach((item) => {
+            // industry
+            let industryNode = industryMap.get(item.industryType);
+            if (!industryNode) {
+              industryNode = {
+                id: 'industry-' + item.industryType,
+                type: 'industry',
+                value: item.industryType,
+                label: item.industryName,
+                children: [],
+                item,
+                _businessMap: new Map()
+              };
+              industryMap.set(item.industryType, industryNode);
+            }
+
+            // business
+            let businessNode = industryNode._businessMap.get(item.businessType);
+            if (!businessNode) {
+              businessNode = {
+                id: `business-${item.industryType}-${item.businessType}`,
+                type: 'business',
+                value: item.businessType,
+                label: item.businessName,
+                children: [],
+                item,
+                _reportMap: new Map()
+              };
+              industryNode._businessMap.set(item.businessType, businessNode);
+              industryNode.children.push(businessNode);
+            }
+
+            // reportWork
+            let reportNode = businessNode._reportMap.get(item.reportWorkType);
+            if (!reportNode) {
+              reportNode = {
+                id: `report-${item.industryType}-${item.businessType}-${item.reportWorkType}`,
+                type: 'reportWork',
+                value: item.reportWorkType,
+                label: item.reportWorkName,
+                children: [],
+                item,
+                _sceneMap: new Map()
+              };
+              businessNode._reportMap.set(item.reportWorkType, reportNode);
+              businessNode.children.push(reportNode);
+            }
+
+            // scene
+            let sceneNode = reportNode._sceneMap.get(item.sceneType);
+            if (!sceneNode) {
+              sceneNode = {
+                id: `scene-${item.industryType}-${item.businessType}-${item.reportWorkType}-${item.sceneType}`,
+                type: 'scene',
+                value: item.sceneType,
+                label: item.sceneName,
+                children: [],
+                item,
+                _templateMap: new Map()
+              };
+              reportNode._sceneMap.set(item.sceneType, sceneNode);
+              reportNode.children.push(sceneNode);
+            }
+
+            // template style
+            let templateNode = sceneNode._templateMap.get(
+              item.templateStyleType
+            );
+            if (!templateNode) {
+              templateNode = {
+                id: `template-${item.industryType}-${item.businessType}-${item.reportWorkType}-${item.sceneType}-${item.templateStyleType}`,
+                type: 'templateStyle',
+                value: item.templateStyleType,
+                label: item.templateStyleName,
+                item,
+                children: []
+              };
+              sceneNode._templateMap.set(item.templateStyleType, templateNode);
+              sceneNode.children.push(templateNode);
+            }
+
+            // 原始数据
+            templateNode.item = item;
+          });
+
+        const clean = (node) => {
+          delete node._businessMap;
+          delete node._reportMap;
+          delete node._sceneMap;
+          delete node._templateMap;
+          node.children && node.children.forEach(clean);
+        };
+
+        const tree = Array.from(industryMap.values());
+        tree.forEach(clean);
+
+        this.typeTree = tree; // 结果树
+
+        if (!this.treeNode) {
+          // 默认选中第一个节点
+          this.treeNode = this.typeTree.length > 0 ? this.typeTree[0] : null;
+          if (this.treeNode) {
+            // 默认选中节点
+            this.$nextTick(() => {
+              this.$refs.treeRef.setCurrentKey(this.treeNode.id);
+              this.handleNodeClick(this.treeNode);
+            });
+          }
+        }
+      },
+
+      handleNodeClick(data) {
+        this.treeNode = data;
+        this.reload();
+      },
+      /* 表格数据源 */
+      datasource({ page, limit, where, order }) {
+        const typeWhere = {};
+        if (this.treeNode && this.treeNode.item) {
+          if (this.treeNode.type === 'industry') {
+            typeWhere.industryType = this.treeNode.item.industryType;
+          } else if (this.treeNode.type === 'business') {
+            typeWhere.industryType = this.treeNode.item.industryType;
+            typeWhere.businessType = this.treeNode.item.businessType;
+          } else if (this.treeNode.type === 'reportWork') {
+            typeWhere.industryType = this.treeNode.item.industryType;
+            typeWhere.businessType = this.treeNode.item.businessType;
+            typeWhere.reportWorkType = this.treeNode.item.reportWorkType;
+          } else if (this.treeNode.type === 'scene') {
+            typeWhere.industryType = this.treeNode.item.industryType;
+            typeWhere.businessType = this.treeNode.item.businessType;
+            typeWhere.reportWorkType = this.treeNode.item.reportWorkType;
+            typeWhere.classify = this.treeNode.item.sceneType;
+          } else {
+            typeWhere.industryType = this.treeNode.item.industryType;
+            typeWhere.businessType = this.treeNode.item.businessType;
+            typeWhere.reportWorkType = this.treeNode.item.reportWorkType;
+            typeWhere.classify = this.treeNode.item.sceneType;
+            typeWhere.recordTemplateStyle =
+              this.treeNode.item.templateStyleType;
+          }
+        }
+
+        return recordrulesPage({
+          pageNum: page,
+          size: limit,
+          ...where,
+          ...typeWhere
+        });
+      },
+      // 单击获取id
+      cellClick(row) {
+        this.current = row;
+        this.radio = row.id;
+      },
+      save() {
+        this.visible = false;
+        this.$emit('success', this.current);
+        this.radio = null;
+      },
+      /* 刷新表格 */
+      reload(where) {
+        this.$refs.table.reload({ page: 1, where });
+      }
+    },
+
+    watch: {}
+  };
+</script>

+ 110 - 0
src/components/releaseRules/search.vue

@@ -0,0 +1,110 @@
+<!-- 搜索表单 -->
+<template>
+  <seekPage :seekList="seekList" :formLength="3" @search="search"></seekPage>
+</template>
+
+<script>
+  import dictMixins from '@/mixins/dictMixins';
+
+  export default {
+    props: {},
+    mixins: [dictMixins],
+    data() {
+      return {
+        typeList: [],
+        reportWorkTypeList: []
+      };
+    },
+    computed: {
+      // 表格列配置
+      seekList() {
+        return [
+          {
+            label: '规则编码:',
+            value: 'code',
+            type: 'input',
+            placeholder: '规则编码'
+          },
+          {
+            label: '规则名称:',
+            value: 'name',
+            type: 'input',
+            placeholder: '规则名称'
+          },
+          {
+            label: '应用场景',
+            value: 'classify',
+            type: 'select',
+            placeholder: '记录表分类',
+            planList: this.typeList
+          },
+
+          {
+            label: '状态:',
+            value: 'publishStatus',
+            type: 'select',
+            placeholder: '状态',
+            // 0-草稿,1-已发布,2-已撤销
+            planList: [
+              {
+                label: '草稿',
+                value: '0'
+              },
+              {
+                label: '已发布',
+                value: '1'
+              },
+              {
+                label: '已撤销',
+                value: '2'
+              }
+            ]
+          },
+          {
+            label: '是否启用:',
+            value: 'enable',
+            type: 'select',
+            placeholder: '是否启用',
+            // 0-草稿,1-已发布,2-已撤销
+            planList: [
+              {
+                label: '启用',
+                value: '1'
+              },
+              {
+                label: '停用',
+                value: '0'
+              }
+            ]
+          }
+        ];
+      }
+    },
+    mounted() {
+      this.requestDict('记录规则类型').then(() => {
+        if (this.$store.state.dict['record_sheet']) {
+          this.typeList = this.$store.state.dict['record_sheet'].map((i) => {
+            return { label: i.dictValue, value: i.dictCode };
+          });
+        }
+      });
+      this.requestDict('记录规则报工类型').then(() => {
+        if (this.$store.state.dict['record_rules_report_work_type']) {
+          this.reportWorkTypeList = this.$store.state.dict[
+            'record_rules_report_work_type'
+          ].map((i) => {
+            return { label: i.dictValue, value: i.dictCode };
+          });
+        }
+      });
+    },
+    methods: {
+      /* 搜索 */
+      search(e) {
+        this.$emit('search', {
+          ...e
+        });
+      }
+    }
+  };
+</script>

+ 223 - 0
src/components/templateDiv/customTable.vue

@@ -0,0 +1,223 @@
+<!-- 搜索表单 -->
+<template>
+  <div style="margin-top: 10px">
+    <!-- <el-button type="primary" @click="addColumn" v-if="edit">新增列</el-button>
+    <el-button type="primary" @click="addRow" v-if="edit">新增行</el-button> -->
+
+    <table style="margin-top: 10px" :id="id">
+      <thead>
+        <tr>
+          <th v-for="(item, index) in columns" class="tableTh">
+            <!-- <i
+              class="el-icon-delete delete"
+              style="display: none"
+              @click="removeColumn(index)"
+              v-if="edit"
+            ></i> -->
+            <input
+              v-model="item.value"
+              type="text"
+              class="templateInput"
+              :id="item.id"
+              :readonly="item.readonly == 2||!edit"
+              @click="inputClick(item)"
+            />
+          </th>
+          <!-- <th v-if="edit"></th> -->
+        </tr>
+      </thead>
+      <tbody>
+        <tr v-for="(row, rowIndex) in rows">
+          <td v-for="item in row.cells"
+            ><input
+              v-model="item.value"
+              type="text"
+              class="templateInput"
+              :id="item.id"
+               :readonly="item.readonly == 2||!edit"
+              @click="inputClick(item)"
+          /></td>
+          <!-- <td v-if="edit">
+            <i
+              class="sort-handle el-icon-delete delete"
+              style="color: #f56c6c"
+              @click="removeRow(rowIndex)"
+            ></i
+          ></td> -->
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</template>
+
+<script>
+  import dictMixins from '@/mixins/dictMixins';
+  import { generateRandomString } from '@/utils/util';
+
+  export default {
+    props: {},
+    mixins: [dictMixins],
+    props: {
+      id: '',
+      edit: {
+        default: true,
+        type: Boolean
+      },
+    
+    },
+    data() {
+      return {
+        form: null,
+        valueObj: {},
+        domId: '',
+        rightClickShow: false,
+        columns: [],
+        rows: []
+      };
+    },
+    methods: {
+      // 方法:添加新列
+      addColumn() {
+        this.columns.push({
+          value: `列${this.columns.length + 1}`,
+          id: generateRandomString(5),
+          readonly: 1
+        });
+        this.rows.forEach((row) => {
+          row.cells.push({
+            value: '',
+            readonly: 1,
+            id: generateRandomString(5)
+          });
+        });
+      },
+
+      // 方法:删除指定列
+      removeColumn(index) {
+        this.columns.splice(index, 1);
+        this.rows.forEach((row) => {
+          row.cells.splice(index, 1);
+        });
+      },
+
+      // 方法:添加新行
+      addRow() {
+        const newRow = {
+          cells: Array(this.columns.length)
+            .fill(null)
+            .map(() => ({
+              readonly: 1,
+              value: '',
+              id: generateRandomString(5)
+            }))
+        };
+        this.rows.push(newRow);
+      },
+      // 方法:删除指定行
+      removeRow(index) {
+        this.rows.splice(index, 1);
+      },
+
+      objInit() {
+        if (Object.keys(this.valueObj).length) {
+          for (let key in this.valueObj) {
+            this.$nextTick(() => {
+              let dom = document.getElementById(key);
+              dom.value = this.valueObj[key];
+            });
+          }
+        }
+      },
+
+      getValue() {
+        return {
+          form: null,
+          valueObj: {
+            columns: this.columns,
+            rows: this.rows
+          }
+        };
+      },
+      init({ form, valueObj }) {
+        this.form = form;
+        this.columns = valueObj.columns;
+        this.rows = valueObj.rows;
+      },
+      editInputChange(domObj) {
+        let columnsIndex = this.columns.findIndex(
+          (item) => item.id == this.domId
+        );
+        if (columnsIndex >= 0) {
+          this.$set(this.columns, columnsIndex, domObj);
+          return;
+        }
+        this.rows.forEach((item, index) => {
+          let rowsIndex = item.cells.findIndex(
+            (cells) => cells.id == this.domId
+          );
+          // console.log(rowsIndex,'rowsIndex')
+          if (rowsIndex >= 0) {
+            this.$set(this.rows[index].cells, rowsIndex, domObj);
+          }
+        });
+      },
+
+      inputClick(item) {
+        if(!this.edit){
+          return
+        }
+        this.domId = item.id;
+        this.$emit('editShow', {
+          templateDivRef: 'customTextRef' + this.id,
+          domObj: {
+            isNoWidth: true,
+            id: item.id,
+            readonly: item.readonly,
+            value: item.value
+          }
+        });
+      }
+    }
+  };
+</script>
+<style lang="scss" scoped>
+  :deep(.templateInput) {
+    width: 100%;
+    border: solid 1px #bfbfbf;
+    padding: 1px;
+    margin: 1px;
+    margin-left: 3px;
+    text-align: center;
+    background-color: #fff;
+  }
+  table {
+    width: 100%;
+    border-collapse: collapse;
+  }
+  th,
+  td {
+    border: 1px solid #ddd;
+    padding: 0;
+    text-align: center;
+  }
+  th {
+    background-color: #f2f2f2;
+  }
+  tr:nth-child(even) {
+    background-color: #f9f9f9;
+  }
+  input:focus {
+    border-color: #66afe9; /* 改变边框颜色 */
+    outline: none; /* 移除默认的轮廓线 */
+  }
+  .tableTh:hover {
+    position: relative;
+    .delete {
+      display: block !important;
+      position: absolute;
+      right: 0;
+      top: 0;
+      color: #f56c6c;
+    }
+  }
+</style>

+ 202 - 0
src/components/templateDiv/customText.vue

@@ -0,0 +1,202 @@
+<!-- 搜索表单 -->
+<template>
+  <div>
+    <!-- <el-popover
+      style="position: fixed; z-index: 2000"
+      width="130"
+      ref="popoverRef"
+      v-model="rightClickShow"
+      @click.native="rightClickShow = false"
+    >
+      <div>
+        <el-button type="primary" @click="addHtml">插入输入框</el-button>
+      </div>
+    </el-popover> -->
+    <div
+      v-html="form"
+      :contenteditable="edit"
+      class="contenteditable"
+      :ref="'valueChange'"
+      :id="id"
+      @contextmenu.prevent="onRightClick($event)"
+    >
+    </div>
+  </div>
+</template>
+
+<script>
+  import dictMixins from '@/mixins/dictMixins';
+  import { generateRandomString } from '@/utils/util';
+
+  export default {
+    props: {},
+    mixins: [dictMixins],
+    props: {
+      id: '',
+      edit: {
+        default: true,
+        type: Boolean
+      }
+    },
+    data() {
+      return {
+        form: null,
+        valueObj: {},
+        domId: '',
+        rightClickShow: false
+      };
+    },
+    methods: {
+      addHtml() {
+        this.insertInput();
+
+        this.objInit();
+      },
+      objInit() {
+        if (Object.keys(this.valueObj).length) {
+          for (let key in this.valueObj) {
+            this.$nextTick(() => {
+              let dom = document.getElementById(key);
+              dom.value = this.valueObj[key];
+            });
+          }
+        }
+      },
+      insertInput() {
+        const selection = window.getSelection();
+        if (selection.rangeCount > 0) {
+          const range = selection.getRangeAt(0);
+          console.log(range, 'range');
+          this.getParentID(range.commonAncestorContainer);
+          if (this.getParentID(range.commonAncestorContainer)) {
+            range.setStart(selection.anchorNode, selection.anchorOffset);
+            range.setEnd(selection.focusNode, selection.focusOffset);
+            range.deleteContents(); // 删除选中的内容(如果需要)
+            range.insertNode(this.getInput()); // 插入input元素
+            // 可以设置光标位置在input之后
+            range.selectNodeContents(this.getInput());
+            range.collapse(false); // 光标放在input之后
+            selection.removeAllRanges();
+            selection.addRange(range);
+          }
+
+          // return
+        }
+      },
+      getParentID(data) {
+        if (data.id == this.id) {
+          return true;
+        }
+        if (data.parentElement) {
+          return this.getParentID(data.parentElement);
+        }
+      },
+
+      getInput() {
+        let id = generateRandomString();
+        const inputElement = document.createElement('input');
+        inputElement.setAttribute('type', 'text');
+        inputElement.setAttribute('class', 'templateInput');
+        inputElement.setAttribute('id', id);
+        return inputElement;
+      },
+      getValue() {
+        // this.form = this.$refs['valueChange'].innerHTML;
+        return {
+          form: this.$refs['valueChange'].innerHTML,
+          valueObj: this.valueObj
+        };
+      },
+      init({ form, valueObj }) {
+        this.form = form;
+        this.valueObj = valueObj;
+        this.$nextTick(() => {
+          if (!this.edit) {
+            let inputs = document.querySelectorAll('.templateInput');
+            inputs.forEach((item) => {
+              item.setAttribute('readonly', 'readonly');
+            });
+          }
+        });
+        this.objInit();
+      },
+      editInputChange(domObj) {
+        let dom = document.getElementById(this.domId);
+        dom.style.width = domObj.width + 'px';
+        if (domObj.readonly == 2) {
+          dom.setAttribute('readonly', 'readonly');
+        } else {
+          dom.removeAttribute('readonly');
+        }
+        delete this.valueObj[dom.id];
+        this.valueObj[domObj.id] = dom.value;
+        dom.id = domObj.id;
+      },
+      onRightClick(PointerEvent) {
+        this.rightClickShow = true;
+        this.$nextTick(() => {
+          let y = PointerEvent.pageY;
+          let x = PointerEvent.pageX + 10;
+          if (PointerEvent.screenY >= PointerEvent.view.innerHeight) {
+            y -= 80;
+          }
+          this.$refs.popoverRef.$el.style.top = y + 'px';
+          this.$refs.popoverRef.$el.style.left = x + 'px';
+        });
+      },
+      inputChange(event) {
+        if (event.target && event.target.className == 'templateInput') {
+          this.valueObj[event.target.id] = event.target.value;
+        }
+      },
+      // inputClick(event) {
+      //   if (!this.edit) {
+      //     return;
+      //   }
+      //   if (event.target && event.target.className == 'templateInput') {
+      //     this.domId = event.target.id;
+      //     console.log(event, 'event');
+      //     // event.getAttribute('class', 'templateInput');
+      //     this.$emit('editShow', {
+      //       templateDivRef: 'customTextRef' + this.id,
+      //       domObj: {
+      //         width: event.target.offsetWidth,
+      //         id: event.target.id,
+      //         readonly: event.target.readOnly ? 2 : 1
+      //       }
+      //     });
+      //   }
+      // }
+    },
+
+    mounted() {
+      this.$nextTick(() => {
+        console.log(document.getElementById(this.id), 'dsd');
+        document
+          .getElementById(this.id)
+          .addEventListener('change', this.inputChange);
+
+        // document
+        //   .getElementById(this.id)
+        //   .addEventListener('click', this.inputClick);
+      });
+    }
+  };
+</script>
+<style lang="scss" scoped>
+  :deep(.templateInput) {
+    width: 80px;
+    border: solid 1px #bfbfbf;
+    padding: 1px;
+    margin: 1px;
+    margin-left: 3px;
+    text-align: center;
+  }
+  .contenteditable {
+    // padding: 5px;
+    width: 100%;
+    min-height: 20px;
+    padding-left: 10px;
+    // border: solid 1px #b3b0b0;
+  }
+</style>

+ 222 - 0
src/components/templateDiv/experimentationProcess.vue

@@ -0,0 +1,222 @@
+<template>
+  <div class="ele-body">
+    <!-- <el-button type="primary" @click="addHtml('customText')" v-if="edit"
+      >插入自定义文本</el-button
+    >
+    <el-button type="primary" @click="addHtml('customTable')" v-if="edit"
+      >插入表格</el-button
+    > -->
+    <!-- <el-button type="primary" @click="save()">保存</el-button> -->
+    <div
+      style="
+        display: flex;
+        width: 100%;
+        padding: 10px;
+        border: solid 1px #f1f1f1;
+        margin-top: 10px;
+      "
+    >
+      <vue-draggable
+        v-model="list"
+        group="project1"
+        :animation="300"
+        handle=".sort-handle"
+        style="flex: 1"
+      >
+        <div
+          class="demo-drag-list-item ele-cell"
+          v-for="item in list"
+          :key="item.id"
+        >
+          <div class="listItem">
+            <!-- <i
+              class="sort-handle el-icon-_nav move"
+              style="display: none"
+              v-if="edit"
+            ></i>
+            <i
+              class="sort-handle el-icon-delete delete"
+              v-if="edit"
+              style="display: none"
+              @click="del(item.id)"
+            ></i> -->
+            <customText
+              :ref="'customTextRef' + item.id"
+              style="flex: 1"
+              v-if="item.type == 'customText'"
+              :id="item.id"
+              :form="item.value"
+              :valueObj="item.valueObj"
+              @editShow="editShowFn"
+              :edit="edit"
+            ></customText>
+            <customTable
+              :ref="'customTextRef' + item.id"
+              style="flex: 1"
+              v-if="item.type == 'customTable'"
+              :id="item.id"
+              :form="item.value"
+              :valueObj="item.valueObj"
+              @editShow="editShowFn"
+              :edit="edit"
+            ></customTable>
+          </div>
+        </div>
+      </vue-draggable>
+
+      <!-- <el-card class="box-card" v-show="editShow" style="width: 320px">
+        <div slot="header" class="clearfix">
+          <span>配置</span>
+        </div>
+        <el-form label-width="100px">
+          <el-form-item label="字段标识:" prop="id">
+            <el-input
+              v-model="domObj.id"
+              placeholder=""
+              @change="editInputChange"
+            ></el-input>
+          </el-form-item>
+          <el-form-item label="宽度:" prop="width" v-if="!domObj.isNoWidth">
+            <el-input
+              v-model="domObj.width"
+              type="number"
+              placeholder=""
+              @change="editInputChange"
+            ></el-input>
+          </el-form-item>
+          <el-form-item label="是否只读:" prop="readonly">
+            <el-select
+              v-model="domObj.readonly"
+              placeholder="请选择"
+              @change="editInputChange"
+            >
+              <el-option :key="1" label="否" :value="1"> </el-option>
+              <el-option :key="2" label="是" :value="2"> </el-option>
+            </el-select>
+          </el-form-item>
+        </el-form>
+      </el-card> -->
+    </div>
+  </div>
+</template>
+
+<script>
+  import customText from './customText.vue';
+  import customTable from './customTable.vue';
+  import VueDraggable from 'vuedraggable';
+  import { generateRandomString } from '@/utils/util';
+
+  export default {
+    components: {
+      customText,
+      VueDraggable,
+      customTable
+    },
+    props: {
+      edit: {
+        default:true,
+        type:Boolean
+      }
+    },
+    computed: {},
+    data() {
+      return {
+        list: [],
+        editShow: false,
+        templateDivRef: '',
+        domObj: {}
+      };
+    },
+    mounted() {},
+    created() {
+      // if (sessionStorage['valueJSON']) {
+      //   this.list = JSON.parse(sessionStorage['valueJSON']);
+      // }
+      //  if (localStorage['valueJSON']) {
+      //   this.list = JSON.parse(localStorage['valueJSON']);
+      // }
+    },
+    methods: {
+      init(list) {
+        this.list = JSON.parse(list);
+        console.log(this.list, ' this.list');
+        if (this.list.length) {
+          this.$nextTick(() => {
+            this.list.forEach((item) => {
+              this.$refs['customTextRef' + item.id][0].init({
+                form: item.value,
+                valueObj: item.valueObj
+              });
+            });
+          });
+        }
+      },
+      editShowFn({ templateDivRef, domObj }) {
+        this.templateDivRef = templateDivRef;
+        this.$set(this, 'domObj', domObj);
+        this.editShow = true;
+      },
+      editInputChange() {
+        this.$nextTick(() => {
+          this.$refs[this.templateDivRef][0].editInputChange(this.domObj);
+        });
+      },
+      del(id) {
+        this.list = this.list.filter((item) => item.id != id);
+      },
+      addHtml(type) {
+        this.list.push({
+          id: generateRandomString(6),
+          type: type,
+          value: '',
+          valueObj: {}
+        });
+      },
+      getValue() {
+        // this.$nextTick(() => {
+        //   // sessionStorage['valueJSON'] = JSON.stringify(this.list);
+        //   // localStorage['valueJSON'] = JSON.stringify(this.list);
+        // });
+        this.list.forEach((item, index) => {
+          let { form, valueObj } =
+            this.$refs['customTextRef' + item.id][0].getValue();
+          this.$set(this.list[index], 'value', form);
+          this.$set(this.list[index], 'valueObj', valueObj);
+        });
+        return this.list;
+      }
+    }
+  };
+</script>
+
+<style scoped lang="scss">
+  .listItem {
+    padding: 5px;
+    // padding-top: 15px;
+    // border: solid 1px #f1f1f1;
+    display: flex;
+    width: 100%;
+    align-items: center;
+    margin-top: 3px;
+    position: relative;
+  }
+  .sort-handle {
+    font-size: 16px;
+    position: absolute;
+    left: 0;
+    top: 0;
+  }
+  .move {
+    cursor: move;
+  }
+  .delete {
+    cursor: pointer;
+    left: 20px;
+    color: #f56c6c;
+  }
+  .listItem:hover {
+    .sort-handle {
+      display: block !important;
+    }
+  }
+</style>

+ 12 - 6
src/enum/dict.js

@@ -19,7 +19,8 @@ export default {
   质检计划状态: 'inspection_plan_status',
   文档类型: 'doc_type',
   工种类型: 'work_type',
-  工作流任务分配自定义脚本: 'bpm_task_assign_script'
+  工作流任务分配自定义脚本: 'bpm_task_assign_script',
+  流程实例的结果: 'bpm_process_instance_result',
 };
 
 export const numberList = [
@@ -48,22 +49,27 @@ export const reviewStatus = {
 export const inspectionProjectStatus = [
   {
     label: '待检',
-    value: 0
+    value: 0,
+    type: 'info'
   },
   {
     label: '已检',
-    value: 1
+    value: 1,
+    type: 'success'
   },
   {
     label: '已派单',
-    value: 2
+    value: 2,
+    type: 'warning'
   },
   {
     label: '已请托',
-    value: 3
+    value: 3,
+    type: 'warning'
   },
   {
     label: '检验中',
-    value: 4
+    value: 4,
+    type: ''
   }
 ];

+ 11 - 0
src/utils/util.js

@@ -52,3 +52,14 @@ export function unacceptedProductStatus(v) {
   }
   return status;
 }
+//随机id
+export function generateRandomString(num = 5) {
+  let result = '';
+  const characters =
+    'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; // 包含字母和数字
+  const charactersLength = characters.length;
+  for (let i = 0; i < num; i++) {
+    result += characters.charAt(Math.floor(Math.random() * charactersLength));
+  }
+  return result;
+}

+ 3 - 3
src/views/inspectionPlan/components/new-baseInfo.vue

@@ -536,9 +536,9 @@
         inspectionStandardsList: [],
         rules: {
           name: [{ required: true, message: '请输入', trigger: 'blur' }],
-          qualityMode: [
-            { required: true, message: '请选择检验方式', trigger: 'blur' }
-          ],
+          // qualityMode: [
+          //   { required: true, message: '请选择检验方式', trigger: 'blur' }
+          // ],
           inspectionStandards: [
             { required: true, message: '请选择检验标准', trigger: 'blur' }
           ],

+ 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>

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

@@ -0,0 +1,463 @@
+<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:reportNumber="{ row }">
+          <el-link
+            type="primary"
+            :underline="false"
+            @click="openDetails(row)"
+            >{{ row.reportNumber }}</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
+      v-if="targetVisible"
+      :is="targetComponent"
+      :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: 'reportNumber',
+            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) {
+        this.isView = true;
+        this.currentRow = row;
+        this.targetComponent = row.reportTemplateCode;
+        console.log('currentRow', this.currentRow, this.targetComponent);
+        this.$nextTick(() => {
+          this.targetVisible = true;
+            console.log('targetVisible~~~~', this.targetVisible);
+        });
+      },
+      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>

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

@@ -0,0 +1,419 @@
+<template>
+    <ele-modal
+        :visible="visible"
+        v-if="visible"
+        title="质检报告"
+        width="70%"
+        append-to-body
+        :before-close="cancel"
+        :maxable="true"
+    >   
+        <div v-if="isView && processInstanceId" 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>
+        <div v-show="activeComp === 'main'" 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>
+        <bpmDetail
+            v-if="activeComp === 'bpm' && processInstanceId"
+            :id="processInstanceId"
+        ></bpmDetail>
+        <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';
+import bpmDetail from '@/views/bpm/processInstance/detail.vue';
+import { getDetailById } from '@/api/inspectionWork/index';
+export default {
+    name: 'QualityReport',
+    props: {
+        isView: {
+            type: Boolean,
+            default: false
+        },
+        visible: {
+            type: Boolean,
+            default: false
+        },
+        row: {
+            type: Object,
+            default: () => {}
+        },
+        item: {
+            type: Object,
+            default: () => {}
+        },
+    },
+    components: {
+        bpmDetail
+    },
+    data() {
+        return {
+            // visible: false,
+            loading: false,
+            currentRow: {}, // 存储当前行数据
+            templateItem: {}, // 存储当前模板项数据
+            // 基本信息数据
+            basicInfoData: {},
+            
+            // 检验项目
+            inspectionItems: [],
+            tabOptions: [
+                { key: 'main', name: '业务详情' },
+                { key: 'bpm', name: '流程详情' }
+            ],
+            activeComp: 'main',
+            processInstanceId: '',
+            reportApprovalTaskVos: [],
+        }
+    },
+    watch: {
+        visible: {
+            handler: function (val) {
+                console.log('visible~~~', val);
+                if (val) {
+                    // this.getProcessInstance();
+                    this.open(this.row, this.item);
+                }
+            },
+            deep: true,
+            immediate: true
+        }
+    },
+    created() {
+        // 页面加载时可以在这里添加数据初始化逻辑
+    },
+    methods: {
+        /* 打开质检报告 */
+        async open(row, item) {
+            this.currentRow = row;
+            this.templateItem = item || row;
+            console.log('currentRow~~~', this.currentRow, this.templateItem);
+
+            try {
+                const res =await getDetailById(this.row.id)
+                this.processInstanceId = res.data.reportProcessInstanceId;
+                this.reportApprovalTaskVos = res.data.reportApprovalTaskVos
+
+            } catch (error) {
+                console.log('error~~~~', error);
+            }
+
+            // 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 || '');
+            } else {
+                this.getBasicInfo();
+                this.getInspectionItems();
+            }
+        },
+        getBasicInfo() {
+            queryInspectionReportData(this.currentRow.id).then(res => {
+                this.basicInfoData = res;
+                
+                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 || '');
+            });
+        },
+        getInspectionItems() {
+            queryInspectionReportList(this.currentRow.id).then(res => {
+                this.inspectionItems = res;
+            });
+        },
+        getProcessInstance() {
+            getDetailById(this.row.id).then(res => {
+                console.log('res~~~~', res);
+                this.processInstanceId = res.data.reportProcessInstanceId;
+            });
+        },
+        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>

+ 621 - 0
src/views/inspectionWork/components/experimentReport.vue

@@ -0,0 +1,621 @@
+<template>
+  <el-dialog
+    title="实验报工"
+    :visible.sync="visible"
+    :before-close="handleClose"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    append-to-body
+    width="80%"
+    :maxable="true"
+  >
+    <header-title title="基础信息"> </header-title>
+    <el-form
+      label-width="115px"
+      :model="{}"
+      :rules="[]"
+      ref="formRef"
+      :disabled="type == 'detail'"
+    >
+      <el-row>
+        <el-col :span="8">
+          <el-form-item label="质检项:" prop="inspectionName">
+            {{ projectData.inspectionName }}</el-form-item
+          >
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="编码:" prop="inspectionCode">
+            {{ projectData.inspectionCode }}
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="批次号:" prop="batchNo">
+            {{ projectData.batchNo }}
+          </el-form-item>
+        </el-col></el-row
+      >
+      <header-title title="  样品信息"> </header-title>
+      <ele-pro-table
+        :needPage="false"
+        :columns="workColumns"
+        :datasource="[projectData]"
+      >
+        <template v-slot:qualityResultContent="{ row }">
+          <el-input
+            v-model="row.qualityResultContent"
+            placeholder="请输入内容"
+            :disabled="type == 'detail'"
+            @input="handleInput(row, row.qualityResultContent)"
+          >
+            <template slot="append" v-if="row.unitName">
+              {{ row.unitName }}
+            </template>
+          </el-input>
+        </template>
+        <template v-slot:sampleQuantity="{ row, $index }">
+          <el-input
+            v-model="row.sampleQuantity"
+            placeholder="请输入"
+            type="number"
+          >
+          </el-input>
+        </template>
+
+        <template v-slot:qualifiedQuantity="{ row, $index }">
+          <el-input
+            v-model="row.qualifiedQuantity"
+            placeholder="请输入"
+            :disabled="type == 'detail'"
+            type="number"
+          >
+          </el-input>
+        </template>
+        <template v-slot:noQualifiedQuantity="{ row, $index }">
+          <el-input
+            v-model="row.noQualifiedQuantity"
+            placeholder="请输入"
+            :disabled="type == 'detail'"
+            type="number"
+          >
+          </el-input>
+        </template>
+        <template v-slot:lossNumber="{ row, $index }">
+          <el-input
+            v-model="row.lossNumber"
+            placeholder="请输入"
+            :disabled="type == 'detail'"
+            type="number"
+          >
+          </el-input>
+        </template>
+        <template v-slot:lossNumberUnqualified="{ row, $index }">
+          <el-input
+            v-model="row.lossNumberUnqualified"
+            placeholder="请输入"
+            :disabled="type == 'detail'"
+            type="number"
+          >
+          </el-input>
+        </template>
+        <template v-slot:retainedSampleQuantity="{ row, $index }">
+          <el-input
+            v-model="row.retainedSampleQuantity"
+            placeholder="请输入"
+            :disabled="type == 'detail'"
+            type="number"
+          >
+          </el-input>
+        </template>
+        <template v-slot:retainedSampleUnqualified="{ row, $index }">
+          <el-input
+            v-model="row.retainedSampleUnqualified"
+            placeholder="请输入"
+            :disabled="type == 'detail'"
+            type="number"
+          >
+          </el-input> </template
+      ></ele-pro-table>
+      <header-title title="实验数据"> </header-title>
+      <el-row>
+        <el-col :span="8">
+          <el-form-item label="实验编号:" prop="industryType">
+            <el-input
+              v-model="form.retainedSampleUnqualified1"
+              placeholder="请输入"
+              :disabled="true"
+              type="number"
+            >
+            </el-input>
+          </el-form-item> </el-col
+      ></el-row>
+      <el-row>
+        <el-col :span="8">
+          <el-form-item label="实验时间:" prop="industryType">
+            <el-date-picker
+              v-model="form.retainedSampleUnqualified2"
+              type="datetime"
+              placeholder="选择日期时间"
+              style="width: 100%"
+            >
+            </el-date-picker>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="执行标准:" prop="pcode">
+            <el-input
+              :value="
+                getDictValue('质检标准类型', projectData.qualityStandardType)
+              "
+              placeholder="请输入"
+              :disabled="true"
+            >
+            </el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <header-title title="实验试/实验设备"> </header-title>
+      <el-row>
+        <el-col :span="8">
+          <el-form-item label="实验室:" prop="industryType">
+            <el-select
+              v-model="form.retainedSampleUnqualified3"
+              style="width: 100%"
+              placeholder="请选择"
+            >
+              <el-option
+                v-for="item in laboratoryList"
+                :key="item.workshopId"
+                :label="item.workshopName"
+                :value="item.workshopId"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row v-for="(item, index) in laboratoryItemList">
+        <el-col :span="24">
+          <el-row>
+            <el-col :span="8">
+              <el-form-item
+                :label="'实验设备' + (index + 1) + ':'"
+                prop="industryType"
+              >
+                <el-select
+                  v-model="item.retainedSampleUnqualified4"
+                  style="width: calc(100% - 30px)"
+                  placeholder="请选择"
+                >
+                  <el-option :label="'测试设备1'" :value="1"> </el-option>
+                  <el-option :label="'测试设备2'" :value="2"> </el-option>
+                </el-select>
+                <i
+                  class="el-icon-delete"
+                  style="
+                    color: #f56c6c;
+                    font-size: 16px;
+                    margin-left: 10px;
+                    cursor: pointer;
+                  "
+                  @click="delLaboratoryItemList(index)"
+                ></i>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </el-col>
+        <el-col :span="24">
+          <el-row>
+            <el-col :span="8">
+              <el-form-item label="开始时间:" prop="industryType">
+                <el-date-picker
+                  v-model="item.retainedSampleUnqualified5"
+                  type="datetime"
+                  placeholder="选择日期时间"
+                  style="width: 100%"
+                >
+                </el-date-picker
+              ></el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="结束时间:" prop="industryType">
+                <el-date-picker
+                  v-model="item.retainedSampleUnqualified6"
+                  type="datetime"
+                  placeholder="选择日期时间"
+                  style="width: 100%"
+                >
+                </el-date-picker
+              ></el-form-item>
+            </el-col>
+          </el-row>
+        </el-col>
+        <el-col :span="24">
+          <el-row>
+            <el-col :span="16">
+              <el-form-item label="运行参数:" prop="industryType">
+                <el-input
+                  v-model="item.retainedSampleUnqualified7"
+                  placeholder="请输入"
+                >
+                </el-input
+              ></el-form-item>
+            </el-col>
+          </el-row>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="24">
+          <el-form-item label=" " prop="industryType">
+            <el-button type="primary" @click="addlLaboratoryItemList">
+              新增实验设备
+            </el-button>
+          </el-form-item>
+        </el-col></el-row
+      >
+      <header-title title="实验过程"> </header-title>
+      <el-button type="primary" size="small" @click="releaseRulesRefOpen(5)"
+        >选择实验过程</el-button
+      >
+      <el-row>
+        <el-col :span="24">
+          <experimentationProcess
+            ref="experimentationProcess1"
+          ></experimentationProcess> </el-col
+      ></el-row>
+      <header-title title="工艺要求"> </header-title>
+      <el-input
+        type="textarea"
+        :rows="1"
+        placeholder="请输入内容"
+        v-model="projectData.defaultValue"
+      >
+      </el-input>
+
+      <el-row> <el-col :span="24"> </el-col></el-row>
+      <header-title title="实验工具"> </header-title>
+      <el-button type="primary" size="small" @click="handleAdd('unitList')"
+        >选择实验工具</el-button
+      >
+      <ele-pro-table
+        :needPage="false"
+        :columns="unitColumns"
+        :datasource="unitList"
+      >
+        <template v-slot:action="{ row, $index }">
+          <el-popconfirm
+            class="ele-action"
+            title="确定要删除此条数据吗?"
+            @confirm="remove($index, 'unitList')"
+          >
+            <template v-slot:reference>
+              <el-link type="danger" :underline="false" icon="el-icon-delete">
+                删除
+              </el-link>
+            </template>
+          </el-popconfirm>
+        </template>
+      </ele-pro-table>
+      <header-title title="实验耗材"> </header-title>
+      <el-button type="primary" size="small" @click="handleAdd('unitList1')"
+        >选择实验耗材</el-button
+      >
+      <ele-pro-table
+        :needPage="false"
+        :columns="unitColumns1"
+        :datasource="unitList1"
+      >
+        <template v-slot:remark="{ row, $index }">
+          <el-input type="textarea" v-model="row.remark"></el-input>
+        </template>
+        <template v-slot:action="{ row, $index }">
+          <el-popconfirm
+            class="ele-action"
+            title="确定要删除此条数据吗?"
+            @confirm="remove($index, 'unitList1')"
+          >
+            <template v-slot:reference>
+              <el-link type="danger" :underline="false" icon="el-icon-delete">
+                删除
+              </el-link>
+            </template>
+          </el-popconfirm>
+        </template>
+      </ele-pro-table>
+      <header-title title="实验记录"> </header-title>
+      <el-button type="primary" size="small" @click="releaseRulesRefOpen(7)"
+        >选择实验记录</el-button
+      >
+      <el-row>
+        <el-col :span="24">
+          <experimentationProcess
+            ref="experimentationProcess3"
+          ></experimentationProcess> </el-col
+      ></el-row>
+      <el-row>
+        <el-col :span="16">
+          <el-form-item label="实验结果:" prop="industryType">
+            <el-input
+              type="textarea"
+              :rows="4"
+              placeholder="请输入内容"
+              v-model="projectData.textarea"
+            >
+            </el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="8">
+          <el-form-item label="实验结论:" prop="industryType">
+            <el-select
+              v-model="projectData.retainedSampleUnqualified9"
+              placeholder="请选择"
+            >
+              <el-option :label="'合格'" :value="1"> </el-option>
+              <el-option :label="'不合格'" :value="2"> </el-option>
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="24">
+          <el-form-item label="实验图片:" prop="industryType"> </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="24">
+          <el-form-item label="附件图片:" prop="industryType"> </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <template v-slot:footer>
+      <el-button size="small" @click="handleClose">关闭</el-button>
+      <el-button type="primary" size="small" @click="save">保存</el-button>
+      <el-button type="primary" size="small" @click="submit">提交</el-button>
+    </template>
+    <releaseRules
+      ref="releaseRulesRef"
+      @success="releaseRulesSuccess"
+    ></releaseRules>
+    <ProductModal ref="productRefs" @chooseModal="chooseModal" />
+  </el-dialog>
+</template>
+
+<script>
+  import dictMixins from '@/mixins/dictMixins';
+  import experimentationProcess from '@/components/templateDiv/experimentationProcess.vue';
+  import releaseRules from '@/components/releaseRules/index.vue';
+  import { getFactoryarea } from '@/api/aps/index.js';
+  import ProductModal from '@/views/inspectionProject/components/ProductModal.vue';
+
+  export default {
+    components: { experimentationProcess, releaseRules, ProductModal },
+    props: {},
+    mixins: [dictMixins],
+
+    data() {
+      return {
+        visible: false,
+        projectData: {},
+        unitListKey: '',
+        form: {},
+        laboratoryItemList: [{}],
+        laboratoryList: [],
+        unitList: [],
+        unitList1: [],
+        unitColumns: [
+          {
+            minWidth: 200,
+            prop: 'name',
+            align: 'center',
+            label: '工具名称',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 200,
+            prop: 'modelType',
+            align: 'center',
+            label: '型号',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 200,
+            prop: 'code',
+            align: 'center',
+            label: '编号',
+            showOverflowTooltip: true
+          },
+          {
+            columnKey: 'action',
+            label: '操作',
+            align: 'center',
+            width: 100,
+            resizable: false,
+            slot: 'action',
+            fixed: 'right'
+          }
+        ],
+        unitColumns1: [
+          {
+            minWidth: 200,
+            prop: 'name',
+            align: 'center',
+            label: '耗材名称',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 200,
+            prop: 'code',
+            align: 'center',
+            label: '编号',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 200,
+            prop: 'batchNo',
+            align: 'center',
+            label: '批次号',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 200,
+            prop: 'specification',
+            align: 'center',
+            label: '浓度',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 200,
+            prop: 'batchNo',
+            align: 'center',
+            label: '来源',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 300,
+            prop: 'remark',
+            slot: 'remark',
+            align: 'center',
+            label: '过程',
+            showOverflowTooltip: true
+          },
+          {
+            columnKey: 'action',
+            label: '操作',
+            align: 'center',
+            width: 100,
+            resizable: false,
+            slot: 'action',
+            fixed: 'right'
+          }
+        ],
+        workColumns: [
+          {
+            minWidth: 120,
+            prop: 'sampleQuantity',
+            slot: 'sampleQuantity',
+            align: 'center',
+            label: '样品数',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 120,
+            prop: 'qualifiedQuantity',
+            align: 'center',
+            slot: 'qualifiedQuantity',
+            label: '合格数',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 130,
+            prop: 'noQualifiedQuantity',
+            slot: 'noQualifiedQuantity',
+            align: 'center',
+            label: '不合格数',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 140,
+            prop: 'lossNumber',
+            slot: 'lossNumber',
+            align: 'center',
+            label: '损耗数(合格品)',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 150,
+            prop: 'lossNumberUnqualified',
+            slot: 'lossNumberUnqualified',
+            align: 'center',
+            label: '损耗数(不合格品)',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 140,
+            prop: 'retainedSampleQuantity',
+            slot: 'retainedSampleQuantity',
+            align: 'center',
+            label: '留样数(合格品)',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 150,
+            prop: 'retainedSampleUnqualified',
+            slot: 'retainedSampleUnqualified',
+            align: 'center',
+            label: '留样数(不合格品)',
+            showOverflowTooltip: true
+          }
+        ]
+      };
+    },
+
+    watch: {},
+    methods: {
+      open(row) {
+        this.projectData = row;
+        this.getFactoryarea();
+        this.visible = true;
+      },
+      handleAdd(type) {
+        this.unitListKey = type;
+        if (type == 'unitList') {
+          this.$refs.productRefs.open(this.unitList);
+        } else {
+          this.$refs.productRefs.open(this.unitList1);
+        }
+      },
+      remove(index, type) {
+        if (type == 'unitList') {
+          this.unitList.splice(index, 1);
+        } else {
+          this.unitList1.splice(index, 1);
+        }
+      },
+
+      chooseModal(data) {
+        if (this.unitListKey == 'unitList') {
+          this.unitList = [...this.unitList, ...data];
+        } else {
+          this.unitList1 = [...this.unitList1, ...data];
+        }
+      },
+      delLaboratoryItemList(index) {
+        this.laboratoryItemList.splice(index, 1);
+      },
+      addlLaboratoryItemList(index) {
+        this.laboratoryItemList.push({});
+      },
+      async getFactoryarea() {
+        const data = await getFactoryarea({
+          pageNum: 1,
+          size: 1000,
+          type: 3
+        });
+        this.laboratoryList = data.list;
+        console.log(this.laboratoryList, 'data');
+      },
+      releaseRulesRefOpen(type) {
+        this.releaseRulesType = type;
+        this.$refs.releaseRulesRef.open(type);
+      },
+      releaseRulesSuccess(data) {
+        if (this.releaseRulesType == 5) {
+          this.$refs.experimentationProcess1.init(data.tempJson.tempJson);
+        }
+        if (this.releaseRulesType == 6) {
+          this.$refs.experimentationProcess2.init(data.tempJson.tempJson);
+        }
+        if (this.releaseRulesType == 7) {
+          this.$refs.experimentationProcess3.init(data.tempJson.tempJson);
+        }
+      },
+      handleClose() {
+        this.visible = false;
+      }
+    }
+  };
+</script>
+
+<style lang="scss" scoped></style>

+ 28 - 5
src/views/inspectionWork/components/inspectionProjectReport.vue

@@ -192,13 +192,25 @@
       <template v-slot:isRequired="{ column }">
         <span class="is-required">{{ column.label }}</span>
       </template>
+      <template v-slot:action="{ row }">
+        <el-link
+          type="primary"
+          :underline="false"
+          @click="experimentReport(row, 'report')"
+          v-if="row.executionMethod == 2"
+        >
+          实验
+        </el-link>
+      </template>
     </ele-pro-table>
+    <newQualityContentTabs ref="newQualityContentTabs"></newQualityContentTabs>
     <template v-slot:footer>
       <el-button @click="handleClose">取消</el-button>
       <el-button type="primary" @click="handleConfirm" v-if="type != 'detail'">
         确认
       </el-button>
     </template>
+    <experimentReport ref="experimentReport"></experimentReport>
   </ele-modal>
 </template>
 
@@ -206,6 +218,7 @@
   import dictMixins from '@/mixins/dictMixins';
   import detailMixins from '../mixins/detailMixins';
   import toolButtom from './toolButtom.vue';
+  import experimentReport from './experimentReport.vue';
   import { handleInput } from './unit.js';
   import {
     exeTaskReportWork,
@@ -214,7 +227,7 @@
   export default {
     mixins: [dictMixins, detailMixins],
 
-    components: { toolButtom },
+    components: { toolButtom, experimentReport },
 
     data() {
       return {
@@ -583,11 +596,14 @@
             showOverflowTooltip: true
           },
           {
-            minWidth: 100,
-            prop: 'action',
-            align: 'center',
+            columnKey: 'action',
             label: '操作',
-            showOverflowTooltip: true
+            width: 120,
+            align: 'center',
+            resizable: false,
+            slot: 'action',
+            showOverflowTooltip: true,
+            fixed: 'right'
           }
         ],
         qualityResultsList: [
@@ -626,6 +642,13 @@
       this.requestDict('质检标准类型');
     },
     methods: {
+      experimentReport(row, type) {
+        this.$refs.experimentReport.open(
+          { ...row, batchNo: this.form.batchNo },
+          type
+        );
+      },
+
       async open({ workData, row, list }, type, pageName) {
         this.tableData = [];
         this.pageName = pageName;

+ 3 - 3
src/views/inspectionWork/components/newQualityContentTabs.vue

@@ -278,7 +278,7 @@
             align="center"
             fixed="right"
             :show-overflow-tooltip="true"
-            v-if="form.recordingMethod == 2"
+            v-if="form.recordingMethod !=1"
           >
             <template slot-scope="scope">
               <span v-if="scope.row.qualityStatus == 0">未检</span>
@@ -293,7 +293,7 @@
             width="180"
             fixed="right"
             :show-overflow-tooltip="true"
-            v-if="form.recordingMethod == 2"
+           v-if="form.recordingMethod !=1"
           >
             <template slot-scope="scope">
               <el-select
@@ -319,7 +319,7 @@
             align="center"
             width="120"
             fixed="right"
-            v-if="type != 'detail' && form.recordingMethod == 2"
+            v-if="type != 'detail' && form.recordingMethod!=1"
           >
             <template slot-scope="scope">
               <el-link

+ 162 - 15
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"
@@ -89,7 +90,9 @@
           <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
           >
@@ -97,7 +100,10 @@
           <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
           >
@@ -105,32 +111,74 @@
             type="primary"
             :underline="false"
             v-if="row.status == 0 && row.recordingMethod == 1"
-            @click="qualityInspectionSend(row,1)"
+            @click="qualityInspectionSend(row, 1)"
             >质检项派单</el-link
           >
           <el-link
             type="primary"
             :underline="false"
             v-if="row.status == 0 && row.recordingMethod == 1"
-            @click="qualityInspectionSend(row,2)"
+            @click="qualityInspectionSend(row, 2)"
             >质检项请托</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) &&
+              isEmptyObject(row.reportTemplateJson) &&
+              $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 &&
+              !isEmptyObject(row.reportTemplateJson) &&
+              $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"
@@ -142,13 +190,41 @@
             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>
             </template>
           </el-popconfirm>
         </template>
+        <template v-slot:templateList="{ row }">
+          <div style="width: 250px; max-height: 60px; overflow: auto">
+            <el-tag
+              v-for="item in row.templateList"
+              :key="item.inspectionName"
+              :type="
+                inspectionProjectStatus.find(
+                  (ProjectStatus) => ProjectStatus.value == item.status
+                )?.type
+              "
+              effect="dark"
+            >
+              <span
+                :title="
+                  inspectionProjectStatus.find(
+                    (ProjectStatus) => ProjectStatus.value == item.status
+                  )?.label
+                "
+              >
+                {{ item.inspectionName }}
+              </span>
+            </el-tag>
+          </div>
+        </template>
       </ele-pro-table>
     </el-card>
     <Certificate
@@ -175,16 +251,27 @@
     ></process-submit-dialog>
 
     <addSample ref="addSampleRef" @reload="search"></addSample>
-    <inspectionProjectTaskSend
+        <inspectionProjectTaskSend
       ref="inspectionProjectTaskSendRef"
       @reload="search"
     ></inspectionProjectTaskSend>
+    <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>
   import Certificate from './components/Certificate.vue';
   import search from './components/search.vue';
-  import addSample from '@/views/sample/sampleRecord/components/addSample1.vue';
+  import addSample from '@/views/sample/sampleRecord/components/addSample.vue';
   import jimureportBrowse from '@/components/jimureport/browseModal.vue';
   import Transfer from './components/transfer.vue';
   import processSubmitDialog from '@/components/processSubmitDialog/processSubmitDialog.vue';
@@ -204,6 +291,10 @@
   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';
+  import { inspectionProjectStatus } from '@/enum/dict.js';
+
 
   export default {
     mixins: [dictMixins, tabMixins],
@@ -213,12 +304,17 @@
       Certificate,
       Transfer,
       processSubmitDialog,
-      addSample,
-      inspectionProjectTaskSend
+      addSample,inspectionProjectTaskSend,inspection_report1
     },
     data() {
       return {
+        inspectionProjectStatus,
         cacheKeyUrl: 'qsm-c2e9664a-inspectionWork',
+        targetComponent: null,
+        isView: false,
+        targetVisible: false,
+        currentRow: {},
+        currentItem: {},
         columns: [
           // 新增多选列
           {
@@ -387,6 +483,13 @@
             align: 'center',
             width: 160
           },
+          {
+            label: '进度',
+            prop: 'templateList',
+            slot: 'templateList',
+            align: 'center',
+            width: 250
+          },
           {
             prop: 'status',
             label: '状态',
@@ -450,7 +553,9 @@
         transferVisible: false,
         transferId: '',
         isReportApproval: 0,
-        selection: [] // 存储选中的行数据
+        selection: [], // 存储选中的行数据
+        reportTemplateList: [], // 报表模板列表
+        templateArr: ['inspection_report1', 'ABBCCC']
       };
     },
     created() {
@@ -458,6 +563,7 @@
       this.requestDict('不良品处理类型');
       this.requestDict('取样类型');
       this.getCode();
+      this.getReportTemplateList();
       this.getTnspectionPlanType();
       this.getQualityMethodCode();
     },
@@ -528,6 +634,44 @@
       }
     },
     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('item~~~', 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;
+          console.log('targetVisible~~~~', this.targetVisible);
+        });
+      },
+      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;
       },
@@ -595,8 +739,8 @@
         });
       },
       //质检项派单
-      qualityInspectionSend(row,type) {
-        this.$refs.inspectionProjectTaskSendRef.open(row,type);
+      qualityInspectionSend(row, type) {
+        this.$refs.inspectionProjectTaskSendRef.open(row, type);
       },
       async datasource({ page, where, limit }) {
         try {
@@ -617,6 +761,9 @@
           // page: 1
         });
       },
+      isEmptyObject(obj) {
+        return Object.keys(obj).length === 0;
+      },
       async openEdit(type, row) {
         const menusList = this.$store.state.user.menus;
         let found = false;

+ 9 - 4
src/views/sample/sampleRecord/components/addSample.vue

@@ -101,7 +101,7 @@
           <el-form-item label="总数:" prop="total">
             <el-input disabled v-model="form.total" placeholder=" ">
               <template slot="append">
-                {{ form.measureUnit }}
+                {{ tableList[0]?.measureUnit }}
               </template>
             </el-input>
           </el-form-item>
@@ -128,7 +128,10 @@
         :selection.sync="selection"
       >
         <template v-slot:toolbar>
-          累计取样数量:{{ workSampleQuantity }}{{form.conditionType == '1' ? form.measureUnit : form.unit}}
+          累计取样数量:{{ workSampleQuantity
+          }}{{
+            form.conditionType == '1' ? tableList[0]?.measureUnit : form.unit
+          }}
         </template>
       </ele-pro-table>
       <header-title title="请样信息"> </header-title>
@@ -147,7 +150,7 @@
           <el-form-item label="请样数量:" prop="pleaseQuantity">
             <el-input v-model="form.pleaseQuantity" placeholder="请输入">
               <template slot="append">
-                {{ form.measureUnit }}
+                {{ tableList[0]?.measureUnit }}
               </template>
             </el-input>
           </el-form-item>
@@ -651,7 +654,7 @@
           this.form.qualityWorkOrderCode = row.code;
           this.form.qualityWorkOrderName = row.name;
           this.workSampleQuantity = Number(row.sampleQuantity) || 0;
-          this.form.pleaseUnit = this.form.measureUnit;
+
           if (!data?.list.length) {
             this.form.isFirstSampling = 1;
           } else {
@@ -758,6 +761,7 @@
             listArr.find((item) => unit == item.conversionUnit)?.id
           );
         }
+        this.form.pleaseUnit = res.list[0]?.measureUnit;
         res.list.map((el) => {
           el.weightProportion = el.weight
             ? (el.weight / el.measureQuantity).toFixed(4)
@@ -1231,6 +1235,7 @@
                 // } else {
                 //   data.quantity = this.form.portion;
                 // }
+                data.measureUnit = this.tableList[0].measureUnit;
               } else {
                 data.measureUnit = data.unit;
                 data.copies = data.portion;

+ 0 - 186
src/views/sample/sampleRecord/components/addSample1.vue

@@ -1,186 +0,0 @@
-<template>
-  <ele-modal
-    :visible.sync="visible"
-    :title="title"
-    width="80%"
-    append-to-body
-    :maxable="true"
-    @close="cancel"
-  >
-    <el-button type="primary" @click="insertInput">插入文本框</el-button>
-
-    <div
-      v-html="form"
-      contenteditable
-      class="contenteditable"
-      :ref="'valueChange'"
-      id="parentElement"
-    >
-    </div>
-    <template v-slot:footer>
-      <el-button type="primary" @click="save()">确认</el-button>
-
-      <el-button @click="cancel">关闭</el-button>
-    </template>
-    <!-- <ele-modal
-      :visible.sync="editShow"
-      title="配置"
-      width="800px"
-      append-to-body
-      :maxable="true"
-    >
-      <el-form label-width="130px">
-        <el-form-item label="字段标识:" prop="id">
-          <el-input v-model="domObj.id" placeholder=""></el-input>
-        </el-form-item>
-        <el-form-item label="宽度:" prop="width">
-          <el-input
-            v-model="domObj.width"
-            type="number"
-            placeholder=""
-          ></el-input>
-        </el-form-item>
-      </el-form>
-      <template v-slot:footer>
-        <el-button type="primary" @click="domSave()">确认</el-button>
-
-        <el-button @click="editShow = false">关闭</el-button>
-      </template>
-    </ele-modal> -->
-  </ele-modal>
-</template>
-
-<script>
-  export default {
-    data() {
-      return {
-        content: '',
-        visible: false,
-        showTrue: false,
-        form: null,
-        startIndex: -1,
-        valueObj: {},
-        domObj: { id: '', width: '' },
-        domId: '',
-        editShow: false
-      };
-    },
-    computed: {},
-    created() {},
-    methods: {
-      async open(type, row) {
-        this.visible = true;
-        this.$nextTick(() => {
-          document
-            .getElementById('parentElement')
-            .addEventListener('change', (event) => {
-              if (event.target && event.target.className == 'templateInput') {
-                this.valueObj[event.target.id] = event.target.value;
-              }
-            });
-
-          document
-            .getElementById('parentElement')
-            .addEventListener('click', (event) => {
-              if (event.target && event.target.className == 'templateInput') {
-                console.log(event, 'event.target');
-                this.domId = event.target.id;
-                this.editShow = true;
-                this.domObj.width = event.target.offsetWidth;
-                this.domObj.id = event.target.id;
-                // this.valueObj[event.target.id] = event.target.value;
-              }
-            });
-        });
-      },
-      generateRandomString() {
-        let result = '';
-        const characters =
-          'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; // 包含字母和数字
-        const charactersLength = characters.length;
-        for (let i = 0; i < 5; i++) {
-          result += characters.charAt(
-            Math.floor(Math.random() * charactersLength)
-          );
-        }
-        return result;
-      },
-
-      addHtml() {
-        this.insertInput();
-        this.form = this.$refs['valueChange'].innerHTML;
-        if (Object.keys(this.valueObj).length) {
-          for (let key in this.valueObj) {
-            this.$nextTick(() => {
-              let dom = document.getElementById(key);
-              dom.value = this.valueObj[key];
-            });
-          }
-        }
-      },
-      insertInput() {
-
-        const selection = window.getSelection();
-
-        if (selection.rangeCount > 0) {
-                const range = selection.getRangeAt(0)
-                console.log(range.commonAncestorContainer.id,'range')
-                return
-
-
-          // return
-          range.setStart(selection.anchorNode, selection.anchorOffset);
-          range.setEnd(selection.focusNode, selection.focusOffset);
-          range.deleteContents(); // 删除选中的内容(如果需要)
-          range.insertNode(this.getInput()); // 插入input元素
-          // 可以设置光标位置在input之后
-          range.selectNodeContents(this.getInput());
-          range.collapse(false); // 光标放在input之后
-          selection.removeAllRanges();
-          selection.addRange(range);
-        }
-      },
-      getInput() {
-        let id = this.generateRandomString();
-        const inputElement = document.createElement('input');
-        inputElement.setAttribute('type', 'text');
-        inputElement.setAttribute('class', 'templateInput');
-        inputElement.setAttribute('id', id);
-        return inputElement;
-      },
-
-      domSave() {
-        let dom = document.getElementById(this.domId);
-        dom.style.width = this.domObj.width + 'px';
-        delete this.valueObj[dom.id];
-        this.valueObj[this.domObj.id] = dom.value;
-        dom.id = this.domObj.id;
-        console.log(this.valueObj, 'this.valueObj');
-      },
-      save() {
-        let inpus = document.querySelectorAll('.templateInput');
-        inpus.forEach((element) => {
-          this.valueObj[element.id] = element.value;
-        });
-      },
-      cancel() {
-        this.visible = false;
-      }
-    }
-  };
-</script>
-<style lang="scss" scoped>
-  :deep(.templateInput) {
-    width: 80px;
-    border: solid 1px #bfbfbf;
-    padding: 1px;
-    margin: 1px;
-    margin-left: 3px;
-  }
-  .contenteditable {
-    padding: 5px;
-    width: 100%;
-    min-height: 20px;
-    border: solid 1px #b3b0b0;
-  }
-</style>

+ 1 - 1
src/views/sample/sampleRecord/index.vue

@@ -195,7 +195,7 @@
             showOverflowTooltip: true
           },
           {
-            prop: 'measureUnit',
+            prop: 'pleaseUnit',
             label: '单位',
             align: 'center',
             minWidth: 110,

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików