Przeglądaj źródła

Merge branch 'master' into test

liujt 6 miesięcy temu
rodzic
commit
fba1cfb85a

+ 82 - 0
src/api/bpm/index.js

@@ -0,0 +1,82 @@
+import request from '@/utils/request'
+
+export async function getActivityList(query) {
+
+  const res= await request({
+    url: '/bpm/activity/list',
+    method: 'get',
+    params: query
+  })
+
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+export async function getProcessInstance(id) {
+
+  const res= await request({
+    url: '/bpm/process-instance/get?id=' + id,
+    method: 'get',
+  })
+
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+export async function getTaskListByProcessInstanceId(processInstanceId) {
+  const res = await request({
+    url:
+      '/bpm/task/list-by-process-instance-id?processInstanceId=' +
+      processInstanceId,
+    method: 'get'
+  });
+
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+export async function getProcessDefinitionBpmnXML(id) {
+  const res = await request({
+    url: '/bpm/process-definition/get-bpmn-xml?id=' + id,
+    method: 'get'
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+export async function getTaskAssignRuleList(query) {
+  const res = await request({
+    url: '/bpm/task-assign-rule/list',
+    method: 'get',
+    params: query
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+export async function processInstanceCreateAPI(data) {
+  const res = await request({
+    url: '/bpm/process-instance/create',
+    method: 'post'
+  },data)
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+export async function getModelPage(query) {
+  const res = await request({
+    url: '/bpm/model/page',
+    method: 'get',
+    params: query
+  })
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 75 - 0
src/utils/dateUtils.js

@@ -0,0 +1,75 @@
+/**
+ * 将毫秒,转换成时间字符串。例如说,xx 分钟
+ *
+ * @param ms 毫秒
+ * @returns {string} 字符串
+ */
+export function getDate(ms) {
+  const day = Math.floor(ms / (24 * 60 * 60 * 1000));
+  const hour =  Math.floor((ms / (60 * 60 * 1000) - day * 24));
+  const minute =  Math.floor(((ms / (60 * 1000)) - day * 24 * 60 - hour * 60));
+  const second =  Math.floor((ms / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60));
+  if (day > 0) {
+    return day + "天" + hour + "小时" + minute + "分钟";
+  }
+  if (hour > 0) {
+    return hour + "小时" + minute + "分钟";
+  }
+  if (minute > 0) {
+    return minute + "分钟";
+  }
+  if (second > 0) {
+    return second + "秒";
+  } else {
+    return 0 + "秒";
+  }
+}
+
+export function beginOfDay(date) {
+  return new Date(date.getFullYear(), date.getMonth(), date.getDate());
+}
+
+export function endOfDay(date) {
+  return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999);
+}
+
+export function betweenDay(date1, date2) {
+  date1 = convertDate(date1);
+  date2 = convertDate(date2);
+  // 计算差值
+  return Math.floor((date2.getTime() - date1.getTime()) / (24 * 3600 * 1000));
+}
+
+export function formatDate(date, fmt) {
+  date = convertDate(date);
+  const o = {
+    "M+": date.getMonth() + 1, //月份
+    "d+": date.getDate(), //日
+    "H+": date.getHours(), //小时
+    "m+": date.getMinutes(), //分
+    "s+": date.getSeconds(), //秒
+    "q+": Math.floor((date.getMonth() + 3) / 3), //季度
+    "S": date.getMilliseconds() //毫秒
+  };
+  if (/(y+)/.test(fmt)) { // 年份
+    fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
+  }
+  for (const k in o) {
+    if (new RegExp("(" + k + ")").test(fmt)) {
+      fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
+    }
+  }
+  return fmt;
+}
+
+export function addTime(date, time) {
+  date = convertDate(date);
+  return new Date(date.getTime() + time);
+}
+
+export function convertDate(date) {
+  if (typeof date === 'string') {
+    return new Date(date);
+  }
+  return date;
+}

+ 288 - 0
src/views/bpm/processInstance/detail.vue

@@ -0,0 +1,288 @@
+<template>
+
+    <div class="app-container" style="padding: 15px">
+      <!-- 审批记录 -->
+      <el-card class="box-card" v-loading="tasksLoad">
+        <div slot="header" class="clearfix">
+          <span class="el-icon-picture-outline">审批记录</span>
+        </div>
+        <el-col :span="16" :offset="4">
+          <div class="block">
+            <el-timeline>
+              <el-timeline-item
+                v-for="(item, index) in tasks"
+                :key="index"
+                :icon="getTimelineItemIcon(item)"
+                :type="getTimelineItemType(item)"
+              >
+                <p style="font-weight: 700">任务:{{ item.name }}</p>
+                <el-card :body-style="{ padding: '10px' }">
+                  <label
+                    v-if="item.assigneeUser"
+                    style="font-weight: normal; margin-right: 30px"
+                  >
+                    {{index==0?' 审批人':'发起人' }}:{{ item.assigneeUser.nickname }}
+                    <el-tag type="info" size="mini">{{
+                      item.assigneeUser.deptName
+                    }}</el-tag>
+                  </label>
+                  <label style="font-weight: normal" v-if="item.createTime"
+                    >创建时间:</label
+                  >
+                  <label style="color: #8a909c; font-weight: normal">{{
+                    item.createTime
+                  }}</label>
+                  <label
+                    v-if="item.endTime"
+                    style="margin-left: 30px; font-weight: normal"
+                    >审批时间:</label
+                  >
+                  <label
+                    v-if="item.endTime"
+                    style="color: #8a909c; font-weight: normal"
+                  >
+                    {{ item.endTime }}</label
+                  >
+                  <label
+                    v-if="item.durationInMillis"
+                    style="margin-left: 30px; font-weight: normal"
+                    >耗时:</label
+                  >
+                  <label
+                    v-if="item.durationInMillis"
+                    style="color: #8a909c; font-weight: normal"
+                  >
+                    {{ getDateStar(item.durationInMillis) }}
+                  </label>
+                  <label   v-if="item.result!=1"  style="margin-left: 30px;color: #8a909c; font-weight: normal">
+                    签名:
+                  </label>
+                  <label v-if="item.result!=1">
+                    <el-image
+                      style="width: 50px; height: 50px;position: absolute;transform: translate(0px, 0px);"
+                      :src="item?.assigneeUser?.signature[0]?.url">
+                      <div slot="error" class="image-slot">
+<!--                        <i class="el-icon-picture-outline"></i>-->
+                        <span></span>
+                      </div>
+                    </el-image>
+                  </label>
+
+                  <p v-if="item.reason&&item.result!=4">
+                    <el-tag :type="getTimelineItemType(item)">{{
+                      item.reason
+                    }}</el-tag>
+                  </p>
+                </el-card>
+              </el-timeline-item>
+            </el-timeline>
+          </div>
+        </el-col>
+      </el-card>
+
+      <!-- 高亮流程图 -->
+      <el-card class="box-card" v-loading="processInstanceLoading">
+        <div slot="header" class="clearfix">
+          <span class="el-icon-picture-outline">流程图</span>
+        </div>
+        <my-process-viewer
+
+          key="designer"
+          v-model="bpmnXML"
+          v-bind="bpmnControlForm"
+          :activityData="activityList"
+          :processInstanceData="processInstance"
+          :taskData="tasks"
+        />
+      </el-card>
+    </div>
+
+</template>
+
+<script>
+  import {
+    getProcessDefinitionBpmnXML,
+    getProcessInstance,
+    getTaskListByProcessInstanceId,
+    getActivityList
+  } from '@/api/bpm/index';
+  import store from '@/store';
+  import { getDate } from '@/utils/dateUtils';
+  import dictMixins from '@/mixins/dictMixins';
+  // import Vue from 'vue';
+
+  // 流程实例的详情页,可用于审批
+  export default {
+    name: 'ProcessInstanceDetail',
+    mixins: [dictMixins],
+    props: {
+      id: {
+        default: ''
+      }
+    },
+    components: {},
+    data() {
+      return {
+        // 遮罩层
+        processInstanceLoading: true,
+        dialogVisible: false,
+
+        // 流程实例
+        // id: undefined, // 流程实例的编号
+        processInstance: {},
+
+        // BPMN 数据
+        bpmnXML: null,
+        bpmnControlForm: {
+          prefix: 'flowable'
+        },
+        activityList: [],
+
+        // 审批记录
+        tasksLoad: true,
+        tasks: []
+      };
+    },
+    created() {
+
+      this.getDetail();
+    },
+    methods: {
+
+      /** 获得流程实例 */
+      getDetail() {
+        // 获得流程实例相关
+        this.processInstanceLoading = true;
+        getProcessInstance(this.id).then((response) => {
+          if (!response) {
+            this.$message.error('查询不到流程信息!');
+            return;
+          }
+          // 设置流程信息
+          this.processInstance = response;
+
+          // //将业务表单,注册为动态组件
+          // const path = this.processInstance.processDefinition.formCustomViewPath;
+          // Vue.component("async-biz-form-component", function (resolve) {
+          //   require([`@/views${path}`], resolve);
+          // });
+
+          // 加载流程图
+          getProcessDefinitionBpmnXML(
+            this.processInstance.processDefinition.id
+          ).then((response) => {
+            this.bpmnXML = response;
+          });
+          // 加载活动列表
+          getActivityList({
+            processInstanceId: this.processInstance.id
+          }).then((response) => {
+            this.activityList = response;
+          });
+
+          // 取消加载中
+          this.processInstanceLoading = false;
+        });
+
+        // 获得流程任务列表(审批记录)
+        this.tasksLoad = true;
+        getTaskListByProcessInstanceId(this.id).then((response) => {
+          // 审批记录
+          this.tasks = [];
+          // 移除已取消的审批
+          response.forEach((task) => {
+            // if (task.result !== 4) {
+              this.tasks.push(task);
+            // }
+          });
+          // 排序,将未完成的排在前面,已完成的排在后面;
+          this.tasks.sort((a, b) => {
+            // 有已完成的情况,按照完成时间倒序
+            if (a.endTime && b.endTime) {
+              return b.endTime - a.endTime;
+            } else if (a.endTime) {
+              return 1;
+            } else if (b.endTime) {
+              return -1;
+              // 都是未完成,按照创建时间倒序
+            } else {
+              return b.createTime - a.createTime;
+            }
+          });
+
+          // 需要审核的记录
+          const userId = store.getters.userId;
+          this.tasks.forEach((task) => {
+            if (task.result !== 1 && task.result !== 6) {
+              // 只有待处理才需要
+              return;
+            }
+            if (!task.assigneeUser || task.assigneeUser.id !== userId) {
+              // 自己不是处理人
+              return;
+            }
+          });
+
+          // 取消加载中
+          this.tasksLoad = false;
+        });
+      },
+      getDateStar(ms) {
+        return getDate(ms);
+      },
+      getTimelineItemIcon(item) {
+        if (item.result === 1) {
+          return 'el-icon-time';
+        }
+        if (item.result === 2) {
+          return 'el-icon-check';
+        }
+        if (item.result === 3) {
+          return 'el-icon-close';
+        }
+        if (item.result === 4) {
+          return 'el-icon-remove-outline';
+        }
+        if (item.result === 5) {
+          return 'el-icon-back';
+        }
+        return '';
+      },
+      getTimelineItemType(item) {
+        if (item.result === 1) {
+          return 'primary';
+        }
+        if (item.result === 2) {
+          return 'success';
+        }
+        if (item.result === 3) {
+          return 'danger';
+        }
+        if (item.result === 4) {
+          return 'info';
+        }
+        if (item.result === 5) {
+          return 'warning';
+        }
+        if (item.result === 6) {
+          return 'default';
+        }
+        return '';
+      },
+      handleClose() {
+        this.dialogVisible = false;
+      }
+    }
+  };
+</script>
+
+<style lang="scss">
+  .my-process-designer {
+    height: calc(100vh - 200px);
+  }
+
+  .box-card {
+    width: 100%;
+    margin-bottom: 20px;
+  }
+</style>

+ 79 - 18
src/views/inspectionReport/template/inspection_report1.vue

@@ -7,8 +7,22 @@
         append-to-body
         :before-close="cancel"
         :maxable="true"
-    >
-        <div id="printSection" style="padding: 20px; background-color: white;">
+    >   
+        <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>
             
             <!-- 编号和报告单号 -->
@@ -206,6 +220,10 @@
                 </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>
@@ -215,7 +233,9 @@
 </template>
 
 <script>
-    import { queryInspectionReportData, queryInspectionReportList, generateReport } from '@/api/inspectionReport';
+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: {
@@ -236,18 +256,27 @@ export default {
             default: () => {}
         },
     },
+    components: {
+        bpmDetail
+    },
     data() {
         return {
-        // visible: false,
-        loading: false,
-        currentRow: {}, // 存储当前行数据
-        templateItem: {}, // 存储当前模板项数据
-        // 基本信息数据
-        basicInfoData: {},
-        
-        // 检验项目
-        inspectionItems: [],
-    
+            // visible: false,
+            loading: false,
+            currentRow: {}, // 存储当前行数据
+            templateItem: {}, // 存储当前模板项数据
+            // 基本信息数据
+            basicInfoData: {},
+            
+            // 检验项目
+            inspectionItems: [],
+            tabOptions: [
+                { key: 'main', name: '业务详情' },
+                { key: 'bpm', name: '流程详情' }
+            ],
+            activeComp: 'main',
+            processInstanceId: '',
+            reportApprovalTaskVos: [],
         }
     },
     watch: {
@@ -255,6 +284,7 @@ export default {
             handler: function (val) {
                 console.log('visible~~~', val);
                 if (val) {
+                    // this.getProcessInstance();
                     this.open(this.row, this.item);
                 }
             },
@@ -267,18 +297,35 @@ export default {
     },
     methods: {
         /* 打开质检报告 */
-        open(row, item) {
+        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] || '');
-                this.$set(this.basicInfoData, 'approvedDate', this.basicInfoData.approvedDate?.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();
@@ -287,9 +334,17 @@ export default {
         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] || '');
-                this.$set(this.basicInfoData, 'approvedDate', this.basicInfoData.approvedDate?.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() {
@@ -297,6 +352,12 @@ export default {
                 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);

+ 1 - 1
src/views/inspectionWork/index.vue

@@ -136,7 +136,7 @@
             v-if="
                 row.status == 1 &&
                 isReportApproval == 1 &&
-                (row.reportApprovalStatus || !isEmptyObject(row.reportTemplateJson)) &&
+                (row.reportApprovalStatus && !isEmptyObject(row.reportTemplateJson)) &&
                 $hasPermission('qms:quality_work_order:generateReport')
               "
             type="primary"