whj пре 10 месеци
родитељ
комит
357973c8f5

+ 1 - 0
src/components/Jsplumb/index.vue

@@ -51,6 +51,7 @@ export default {
   methods: {
     init(details) {
       const that = this
+      if (this.instance) return
       // 初始化节点
       this.instance = newInstance({
         container: this.$refs.container,

+ 4 - 1
src/views/workplace/publish/modules/AuditDetail.vue

@@ -16,7 +16,7 @@
               <a-descriptions-item v-if="item.type==='dataSelect'" :key="item.value" :label="item.label">
                 {{ detail[item.attrs.connect[1].bind] }}
               </a-descriptions-item>
-              <a-descriptions-item v-if="item.type==='select'" :key="item.value" :label="item.label">
+              <a-descriptions-item v-else-if="item.type==='select'" :key="item.value" :label="item.label">
                 {{ getLabel(item.attrs.options,detail[item.value]) }}
               </a-descriptions-item>
               <a-descriptions-item v-else-if="item.type==='uploadFile'" :key="item.value" :label="item.label">
@@ -143,6 +143,9 @@ export default {
     this.statusMap = this.DictCache.getLabelByValueMapByType(this.DictCache.TYPE.TASK_RECORD_STATUS)
   },
   methods: {
+    getLabel(arr, key) {
+      return arr.find((item) => item.value === key).label
+    },
     base(record) {
       this.visible = true
       this.modalTitle = '详情'

+ 91 - 54
src/views/workplace/publish/modules/Detail.vue

@@ -1,64 +1,76 @@
 <template>
-  <a-card v-show="visible" title="详情" :bordered="false">
-    <div slot="extra">
+  <a-card v-show="visible" :bordered="false">
+    <div class="btn">
       <a-space class="table-page-search-submitButtons">
         <a-button type="default" @click="handleCancel()">返回</a-button>
       </a-space>
     </div>
-    <div class="detail card">
-      <title-divider title="业务信息" width="140px"></title-divider>
-      <a-descriptions :column="2" bordered>
-        <template v-for="item in descriptions">
-          <a-descriptions-item v-if="item.type==='dataSelect'" :key="item.value" :label="item.label">
-            {{ detail[item.attrs.connect[1].bind] }}
-          </a-descriptions-item>
-          <a-descriptions-item v-if="item.type==='select'" :key="item.value" :label="item.label">
-            {{ getLabel(item.attrs.options,detail[item.value]) }}
-          </a-descriptions-item>
-          <a-descriptions-item v-else-if="item.type==='uploadFile'" :key="item.value" :label="item.label">
-            <a-space direction="vertical">
-              <a style="font-size: 12px" v-for="file in detail[item.value]" :key="file.id" target="_blank" :href="file.url"><a-icon type="file" />{{ file.name }}</a>
-            </a-space>
-          </a-descriptions-item>
-          <a-descriptions-item v-else-if="item.type==='uploadImg'" :key="item.value" :label="item.label">
-            <a-space>
-              <img v-for="file in detail[item.value]" :key="file.id" :src="file.url" :alt="file.name" width="80px" @click="$refs.imgView.base(file.url)" />
-            </a-space>
-          </a-descriptions-item>
-          <a-descriptions-item v-else-if="item.type!=='divider'" :key="item.value" :label="item.label">
-            {{ detail[item.value] }}
-          </a-descriptions-item>
-        </template>
-      </a-descriptions>
-      <title-divider title="审核进度" width="140px"></title-divider>
-      <a-list item-layout="vertical" :data-source="model.recordList">
-        <a-list-item slot="renderItem" key="item.title" slot-scope="item">
-          <a-list-item-meta>
-            <template #title>
-              <div style="width:100%;height:35px;display:flex;justify-content: space-between;align-items: center;font-size: 14px;">
-                <span>{{ item.createdUserName }} - {{ statusMap[item.handleStatus] }}</span> <span>{{ item.createdTime }}</span>
-              </div>
+    <a-tabs v-model="current" @change="callback">
+      <a-tab-pane key="1" tab="业务审批">
+        <div class="detail card">
+          <title-divider title="业务信息" width="140px"></title-divider>
+          <a-descriptions :column="2" bordered>
+            <template v-for="item in descriptions">
+              <a-descriptions-item v-if="item.type==='dataSelect'" :key="item.value" :label="item.label">
+                {{ detail[item.attrs.connect[1].bind] }}
+              </a-descriptions-item>
+              <a-descriptions-item v-else-if="item.type==='select'" :key="item.value" :label="item.label">
+                {{ getLabel(item.attrs.options,detail[item.value]) }}
+              </a-descriptions-item>
+              <a-descriptions-item v-else-if="item.type==='uploadFile'" :key="item.value" :label="item.label">
+                <a-space direction="vertical">
+                  <a style="font-size: 12px" v-for="file in detail[item.value]" :key="file.id" target="_blank" :href="file.url"><a-icon type="file" />{{ file.name }}</a>
+                </a-space>
+              </a-descriptions-item>
+              <a-descriptions-item v-else-if="item.type==='uploadImg'" :key="item.value" :label="item.label">
+                <a-space>
+                  <img v-for="file in detail[item.value]" :key="file.id" :src="file.url" :alt="file.name" width="80px" @click="$refs.imgView.base(file.url)" />
+                </a-space>
+              </a-descriptions-item>
+              <a-descriptions-item v-else-if="item.type!=='divider'" :key="item.value" :label="item.label">
+                {{ detail[item.value] }}
+              </a-descriptions-item>
             </template>
+          </a-descriptions>
+          <title-divider title="审核进度" width="140px"></title-divider>
+          <a-list item-layout="vertical" :data-source="model.recordList">
+            <a-list-item slot="renderItem" key="item.title" slot-scope="item">
+              <a-list-item-meta>
+                <template #title>
+                  <div style="width:100%;height:35px;display:flex;justify-content: space-between;align-items: center;font-size: 14px;">
+                    <span>{{ item.createdUserName }} - {{ statusMap[item.handleStatus] }}</span> <span>{{ item.createdTime }}</span>
+                  </div>
+                </template>
+
+                <a-avatar :style="{ backgroundColor: colors[item.handleStatus%4], verticalAlign: 'middle' }" slot="avatar">
+                  <my-icon v-if="icons[item.handleStatus].includes('icon-')" :type="icons[item.handleStatus]"></my-icon>
+                  <a-icon v-else :type="icons[item.handleStatus]"></a-icon>
+                </a-avatar>
+              </a-list-item-meta>
+              <div>
+                <div style="font-size: 12px">{{ item.handleRemark }}</div>
+                <a-space>
+                  <img v-for="file in item.imageList" :key="file.id" :src="file.url" :alt="file.name" width="80px" @click="$refs.imgView.base(file.url)" />
+                </a-space>
+                <br>
+                <a-space direction="vertical">
+                  <a style="font-size: 12px" v-for="file in item.fileList" :key="file.id" target="_blank" :href="file.url"><a-icon type="file" />{{ file.name }}</a>
+                </a-space>
+              </div>
 
-            <a-avatar :style="{ backgroundColor: colors[item.handleStatus%4], verticalAlign: 'middle' }" slot="avatar">
-              <my-icon v-if="icons[item.handleStatus].includes('icon-')" :type="icons[item.handleStatus]"></my-icon>
-              <a-icon v-else :type="icons[item.handleStatus]"></a-icon>
-            </a-avatar>
-          </a-list-item-meta>
-          <div>
-            <div style="font-size: 12px">{{ item.handleRemark }}</div>
-            <a-space>
-              <img v-for="file in item.imageList" :key="file.id" :src="file.url" :alt="file.name" width="80px" @click="$refs.imgView.base(file.url)" />
-            </a-space>
-            <br>
-            <a-space direction="vertical">
-              <a style="font-size: 12px" v-for="file in item.fileList" :key="file.id" target="_blank" :href="file.url"><a-icon type="file" />{{ file.name }}</a>
-            </a-space>
-          </div>
+            </a-list-item>
+          </a-list>
+        </div>
+      </a-tab-pane>
+      <a-tab-pane key="2" tab="流程查看">
+        <JsPlumb ref="JsPlumb" :details="nodes">
+          <template #default="record">
+            <Node :detail="record.detail" :act="model.frontId" :config="{}"></Node>
+          </template>
+        </JsPlumb>
+      </a-tab-pane>
+    </a-tabs>
 
-        </a-list-item>
-      </a-list>
-    </div>
     <ImgView ref="imgView" />
   </a-card>
 </template>
@@ -68,11 +80,14 @@ import { verifyWorkflow } from '@/api/workflow/publish'
 import UploadFile from '@/components/Upload/CUploadFile.vue'
 import UploadImg from '@/components/Upload/CUploadImg.vue'
 import ImgView from '@/components/viewImg/index.vue'
+import JsPlumb from '@/components/Jsplumb'
+import Node from './Node.vue'
 export default {
   name: 'WorkflowTaskBomFieldDetail',
-  components: { UploadFile, UploadImg, ImgView },
+  components: { UploadFile, UploadImg, ImgView, JsPlumb, Node },
   data() {
     return {
+      current: '1',
       colors: {
         1: '#666',
         2: '#1890ff',
@@ -98,6 +113,7 @@ export default {
       model: {},
       detail: {},
       descriptions: [],
+      nodes: [],
     }
   },
   created() {
@@ -109,10 +125,12 @@ export default {
       return arr.find((item) => item.value === key).label
     },
     base(record) {
+      this.current = '1'
       this.visible = true
       this.modalTitle = '详情'
       this.model = record
       this.detail = JSON.parse(record.data.jsonString)
+      this.nodes = JSON.parse(record.flowJson)
       this.descriptions = JSON.parse(record.data.formJsonString).components
       const {
         form: { setFieldsValue },
@@ -151,6 +169,13 @@ export default {
           })
       })
     },
+    callback(val) {
+      if (val == 2) {
+        this.$nextTick(() => {
+          this.$refs.JsPlumb.init()
+        })
+      }
+    },
     handleCancel() {
       this.visible = false
       this.confirmLoading = false
@@ -184,4 +209,16 @@ export default {
     background-color: #c1c1c1;
   }
 }
+.btn {
+  position: fixed;
+  bottom: 100px;
+  left: 50%;
+  transform: translateX(50%);
+  z-index: 999;
+}
+</style>
+<style lang="less">
+.ant-descriptions-item-label {
+  color: #000;
+}
 </style>

+ 154 - 0
src/views/workplace/publish/modules/Node.vue

@@ -0,0 +1,154 @@
+<template>
+  <div class="node">
+    <div :class="{ripple:act==detail.id}"></div>
+    <div v-if="detail.type===1" class="start">
+      <div class="title">
+        发起人
+      </div>
+      <div class="card-content">
+        {{ detail.name }}
+      </div>
+    </div>
+    <div v-else-if="detail.type===2" class="term">
+      <div class="top"></div>
+      <div class="bottom"></div>
+      <div class="title">
+        条件分支
+      </div>
+      <div class="card-content">
+        {{ detail.name }}
+      </div>
+    </div>
+    <div v-else-if="detail.type===3" class="verify">
+      <div class="top"></div>
+      <div class="bottom"></div>
+      <div class="title">
+        {{ detail.name }}
+      </div>
+      <div class="card-content">
+        <div>{{detail.verifyType==1?detail.roleName:detail.userName}}</div>
+      </div>
+    </div>
+    <div v-else class="end">
+      <div class="top"></div>
+      <div class="title">
+        结束
+      </div>
+      <div class="card-content">
+        审批结束
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  props: {
+    detail: {
+      type: Object,
+      default: () => {},
+    },
+    act: {
+      type: String,
+      default: () => '',
+    },
+  },
+  watch: {
+    detail: {
+      handler(val) {
+        console.log(val)
+      },
+      deep: true,
+    },
+  },
+  data() {
+    return {}
+  },
+}
+</script>
+
+<style lang="less" scoped>
+.node {
+  width: 200px;
+  cursor: pointer;
+  box-shadow: 1px 3px 8px 0 rgba(102, 102, 102, 0.26);
+  border-radius: 0 0 10px 10px;
+  position: relative;
+  .title {
+    padding: 2px 5px;
+    overflow: hidden;
+    background: #9e9ebc;
+    color: #fff;
+    border-radius: 10px 10px 0 0;
+  }
+  .card-content {
+    background: #fff;
+    text-align: center;
+    padding: 10px;
+    min-height: 50px;
+  }
+  .term {
+    .title {
+      color: #0ab74c;
+      border-bottom: 1px solid #ccc;
+      background: #fff;
+    }
+  }
+  .verify {
+    .title {
+      background: #e99e09;
+    }
+  }
+  .end {
+    margin-bottom: 30px;
+  }
+}
+.action {
+  border-top: 1px solid #ccc;
+  font-size: 12px;
+  border-radius: 0 0 10px 10px;
+  background: #fff;
+  .btn {
+    padding: 5px;
+    text-align: center;
+  }
+}
+
+.audit {
+  background: #e99e09;
+  border: #e99e09 solid 1px;
+  &:hover {
+    background: #f7b737;
+    border: #f7b737 solid 1px;
+  }
+}
+.ripple {
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  top: 0;
+  left: 0;
+  border-radius: 10px;
+  z-index: -1;
+  animation: 1.5s linear infinite act-in;
+}
+@keyframes act-in {
+  0% {
+    // transform: scale(0.8);
+    // background-color: #b3d4fc;
+    box-shadow: 0 0 0 0 rgba(2, 144, 255, 0.7);
+  }
+
+  50% {
+    // transform: scale(1.1);
+    // background-color: #6793fb;
+    box-shadow: 0 0 0 10px rgba(2, 144, 255, 0.4);
+  }
+
+  100% {
+    // transform: scale(1.2);
+    // background-color: #b3d4fc;
+    box-shadow: 0 0 0 20px rgba(2, 144, 255, 0);
+  }
+}
+</style>