Browse Source

解决冲突

hongrunxia 11 months ago
parent
commit
89809abb3a
27 changed files with 2367 additions and 684 deletions
  1. 1 1
      package.json
  2. 2 2
      public/js/config.js
  3. BIN
      src/assets/images/test1.png
  4. 4 1
      src/views/vent/monitorManager/camera/camera.api.ts
  5. 12 0
      src/views/vent/monitorManager/camera/common/Icon/index.vue
  6. 42 0
      src/views/vent/monitorManager/camera/common/Icon/treeIcon.vue
  7. 63 0
      src/views/vent/monitorManager/camera/common/cameraTree.vue
  8. 183 0
      src/views/vent/monitorManager/camera/common/treeList.vue
  9. 370 311
      src/views/vent/monitorManager/camera/index.vue
  10. 3 0
      src/views/vent/monitorManager/deviceMonitor/components/device/index.vue
  11. 485 0
      src/views/vent/monitorManager/deviceMonitor/components/device/modal/gas.modal.vue
  12. 58 0
      src/views/vent/performance/approvalPend/approvalPend.data.ts
  13. 39 0
      src/views/vent/performance/approvalPend/index.vue
  14. 58 0
      src/views/vent/performance/approved/approved.data.ts
  15. 39 0
      src/views/vent/performance/approved/index.vue
  16. 0 0
      src/views/vent/performance/completeLc/completeLc.api.ts
  17. 79 0
      src/views/vent/performance/completeLc/completeLc.data.ts
  18. 81 0
      src/views/vent/performance/completeLc/index.vue
  19. 58 0
      src/views/vent/performance/endEd/endEd.data.ts
  20. 38 0
      src/views/vent/performance/endEd/index.vue
  21. 5 4
      src/views/vent/performance/fileDetail/fileDetail.data.ts
  22. 364 365
      src/views/vent/performance/fileDetail/index.vue
  23. 52 0
      src/views/vent/performance/progressLc/HistorySp.data.ts
  24. 87 0
      src/views/vent/performance/progressLc/common/HistorySp.vue
  25. 161 0
      src/views/vent/performance/progressLc/index.vue
  26. 18 0
      src/views/vent/performance/progressLc/progressLc.api.ts
  27. 65 0
      src/views/vent/performance/progressLc/progressLc.data.ts

+ 1 - 1
package.json

@@ -77,7 +77,7 @@
     "three": "^0.162.0",
     "tinymce": "^5.10.3",
     "vditor": "^3.9.5",
-    "vue": "^3.3.4",
+    "vue": "^3.2.0",
     "vue-cropper": "^0.6.2",
     "vue-cropperjs": "^5.0.0",
     "vue-i18n": "9.2.2",

+ 2 - 2
public/js/config.js

@@ -3,8 +3,8 @@ const VUE_APP_URL = {
   webRtcUrl: '/webRtc' // rtsp服务器IP地址
 }
 const History_Type = {
-  // type: 'remote', // remote、vent  (remote 代表的是历史查询走的装备院的接口,vent是走的咱们的,目前神东的项目都用remote, 其他矿用vent)
-  type: 'vent', // remote、vent
+  type: 'remote', // remote、vent  (remote 代表的是历史查询走的装备院的接口,vent是走的咱们的,目前神东的项目都用remote, 其他矿用vent)
+  // type: 'vent', // remote、vent
   deviceType: []
 }
 

BIN
src/assets/images/test1.png


+ 4 - 1
src/views/vent/monitorManager/camera/camera.api.ts

@@ -1,7 +1,8 @@
 import { defHttp } from '/@/utils/http/axios';
 
 enum Api {
-  list = '/ventanaly-device/safety/ventanalyCamera/list',
+  getCameraDevKind='/safety/ventanalyCamera/getCameraDevKind',
+  list = '/safety/ventanalyCamera/listNew',
   getCameraUrl = '/ventanaly-device/camera/queryByCameraCode',
 }
 /**
@@ -11,3 +12,5 @@ enum Api {
 export const list = (params) => defHttp.get({ url: Api.list, params });
 
 export const cameraAddr = (params) => defHttp.get({ url: Api.getCameraUrl, params });
+
+export const getCameraDevKind = () => defHttp.get({ url: Api.getCameraDevKind, });

+ 12 - 0
src/views/vent/monitorManager/camera/common/Icon/index.vue

@@ -0,0 +1,12 @@
+<template>
+  <svg aria-hidden="true">
+    <use :xlink:href="name" />
+  </svg>
+</template>
+<script setup lang="ts">
+  import { computed } from 'vue';
+  const props = defineProps<{
+    iconName: string;
+  }>();
+  const name = computed(() => `#${props.iconName}`);
+</script>

+ 42 - 0
src/views/vent/monitorManager/camera/common/Icon/treeIcon.vue

@@ -0,0 +1,42 @@
+<template>
+  <icon class="iconfont" :iconName="iconNam" />
+</template>
+<script setup lang="ts">
+  import icon from './index.vue';
+  import { computed } from 'vue';
+  const type = {
+    file: 'icon-document',
+    // js: 'icon-javascript',
+    // json: 'icon-json',
+    // vue: 'icon-vue',
+    // scss: 'icon-sass',
+    // file: 'icon-file',
+    // md: 'icon-mdx',
+    // tsx: 'icon-react1',
+    // ts: 'icon-typescript',
+    // css: 'icon-css',
+    // png: 'icon-image',
+    // jpg: 'icon-image',
+    // jpeg: 'icon-image',
+    // webp: 'icon-image',
+    // gif: 'icon-image',
+    // html: 'icon-html',
+    // babelrc: 'icon-babel',
+  };
+  const props = defineProps({
+    title: String,
+  });
+  const iconNam = computed(() => {
+    // const suffix = (props.title || '.file').split('.').pop().toLowerCase();
+    // console.log(suffix, 'suffix');
+    // if (props.title == 'package.json') return 'icon-npm';
+    // return type[suffix];
+    return type['file'];
+  });
+</script>
+<style lang="less" scoped>
+  .iconfont {
+    width: 16px;
+    height: 16px;
+  }
+</style>

+ 63 - 0
src/views/vent/monitorManager/camera/common/cameraTree.vue

@@ -0,0 +1,63 @@
+<template>
+  <treeList
+    v-for="model in list"
+    v-bind="$attrs"
+    :model="model"
+    :key="model.id"
+    @delete-node="onDeltet"
+  >
+    <template #icon="slotProps">
+      <slot name="icon" v-bind="slotProps"></slot>
+    </template>
+    <template #operation="slotProps">
+      <slot name="operation" v-bind="slotProps"></slot>
+    </template>
+  </treeList>
+</template>
+<script setup lang="ts">
+  import { ref } from 'vue';
+  import treeList from './treeList.vue';
+  const emit = defineEmits([ 'deleteNode']);
+  interface IFileSystem {
+    id: string;
+    title: string;
+    pid: string;
+    isFolder: boolean;
+    isAdd: boolean;
+    children?: IFileSystem[];
+  }
+  const props = withDefaults(
+    defineProps<{
+      list: IFileSystem[];
+    }>(),
+    {}
+  );
+  // 递归寻找父组件
+  function findParent(pid, Tree) {
+    let targetNode = null;
+    function find(item, flattenTree) {
+      flattenTree.find((ele) => {
+        if (ele.id == pid) {
+          targetNode = ele;
+          return true;
+        } else {
+          if (ele.children) {
+            find(pid, ele.children);
+          }
+        }
+      });
+    }
+    find(pid, Tree);
+    return targetNode.children;
+  }
+  // 删除
+  const onDeltet = (node) => {
+    emit('deleteNode', {
+      ...node,
+      eventType: 'delete',
+    });
+  };
+ 
+  
+</script>
+<style scoped></style>

+ 183 - 0
src/views/vent/monitorManager/camera/common/treeList.vue

@@ -0,0 +1,183 @@
+<template>
+  <div class="vtl-node" :id="model.id" :class="{ 'vtl-leaf-node': !isFolder, 'vtl-tree-node': isFolder }">
+    <div
+      :class="treeNodeClass"
+      @mouseover="mouseOver"
+      @mouseout="mouseOut"
+      @click.stop="toggle"
+    >
+      <div class="vtl-border-text">
+        <template v-if="isFolder">
+          <slot v-if="expanded" :item="{ title: model.title, isFolder: true, expanded: true }" name="icon"> </slot>
+          <slot v-else :item="{ title: model.title, isFolder: true, expanded: false }" name="icon"></slot>
+        </template>
+        <slot v-else :item="{ title: model.title, isFolder: false }" name="icon"></slot>
+        <span class="vtl-node-content ellipsis" >
+          {{ model.title }}
+        </span>
+      </div>
+      <div class="vtl-operation" v-show="isHover && !isFolder">
+        <span @click.stop.prevent="delNode">
+          <slot name="operation" type="deleteNode"></slot>
+        </span>
+      </div>
+    </div>
+  </div>
+  <div class="vtl-tree-margin" v-show="expanded" v-if="isFolder">
+    <!-- 这里无法使用$attr来透传属性官方还未解决此bug -->
+    <treeList
+      @on-click="(depth) => $emit('onClick', depth)"
+      @delete-node="(depth) => $emit('deleteNode', depth)"
+      v-for="newmodel in model.children"
+      :selected="selected"
+      :model="newmodel"
+      :key="newmodel.id"
+    >
+      <template #icon="slotProps">
+        <slot name="icon" v-bind="slotProps"></slot>
+      </template>
+      <template #operation="slotProps">
+        <slot name="operation" v-bind="slotProps"></slot>
+      </template>
+    </treeList>
+  </div>
+</template>
+<script setup lang="ts">
+  import { computed, ref,  } from 'vue';
+  interface IFileSystem {
+    id: string;
+    title: string;
+    pid: string;
+    isFolder: boolean;
+    isAdd: boolean;
+    children?: IFileSystem[];
+  }
+  // 吐出去的事件
+  const emit = defineEmits([
+    'onClick',
+    'deleteNode',
+  ]);
+  // 拿到传入的值
+  const props = withDefaults(
+    defineProps<{
+      model: IFileSystem;
+      selected: IFileSystem;
+    }>(),
+    {
+      // draggable: false,
+    }
+  );
+  //是否移入
+  const isHover = ref(false);
+  // 是否展开
+  const expanded = ref(true);
+  // 是否是文件夹
+  const isFolder = computed(() => {
+    return props.model.isFolder;
+  });
+  const isSelected = computed(() => props.selected.id === props.model.id);
+  // 拖拽样式
+  const treeNodeClass = computed(() => {
+    return {
+      'vtl-node-main': true,
+      selected: isSelected.value,
+    };
+  });
+ 
+  // 删除目录
+  const delNode = () => {
+    emit('deleteNode', {
+      ...props.model,
+      eventType: 'delete',
+    });
+  };
+ 
+  
+  // 展开收起
+  const toggle = () => {
+    if (isFolder.value) {
+      expanded.value = !expanded.value;
+      emit('onClick', {
+        ...props.model,
+      }); //lxh
+    } else {
+      emit('onClick', {
+        ...props.model,
+      });
+    }
+  };
+  // 拖拽结束
+  const mouseOver = () => {
+    isHover.value = true;
+  };
+  // 移出
+  const mouseOut = () => {
+    isHover.value = false;
+  };
+ 
+  
+  
+</script>
+<style lang="less">
+  .vtl-node {
+    .vtl-node-main {
+      display: flex;
+      align-items: center;
+      padding: 2px 0 2px 2px;
+      cursor: pointer;
+
+      &:hover {
+        .vtl-border-text {
+          width: 80%;
+        }
+      }
+
+      .vtl-border-text {
+        display: flex; //lxh
+        flex: 1;
+        align-items: center; //lxh
+        width: 100%;
+        padding-left: 5px;
+
+        .iconfont {
+          width: 16px;
+          height: 16px;
+          vertical-align: text-bottom;
+        }
+      }
+
+      &.selected {
+        // background-color: rgba(45, 113, 134, 0.2);
+        background-color: #1c4869;
+      }
+
+   
+
+      .vtl-node-content {
+        color: #fff;
+        padding-left: 5px;
+        font-size: 14px;
+        width: 80%;
+        display: inline-block;
+        vertical-align: bottom;
+      }
+
+      &:hover {
+        .vtl-node-content {
+          color: #fff;
+          overflow: hidden;
+        }
+      }
+
+
+
+      .vtl-operation {
+        padding-right: 10px;
+      }
+    }
+  }
+
+  .vtl-tree-margin {
+    padding-left: 1em;
+  }
+</style>

+ 370 - 311
src/views/vent/monitorManager/camera/index.vue

@@ -1,354 +1,413 @@
 <template>
-  <div v-if="addrList.length > 0">
-    <div class="vent-flex-row-wrap camera-box" >
-      <div v-for="(item, index) in addrList" :key="index" class="player-box">
-        <div class="player-name">{{ item.name }}</div>
-        <div>
-          <template v-if="item.addr.startsWith('rtsp://')">
-            <video :id="`video${index}`" muted autoplay></video>
-            <div class="click-box" @dblclick="goFullScreen(`video${index}`)"></div>
-          </template>
-          <template v-else>
-            <div :id="'player'+index" ></div>
+  <div class="camera-container">
+    <div class="left-area">
+      <cameraTree :selected="selected" :list="listArr" :draggable="true" @delete-node="onDeltet" @on-click="onClick">
+        <template #icon="{ item }">
+          <template v-if="item.isFolder">
+            <SvgIcon v-if="item.expanded" size="18" name="file-open" />
+            <SvgIcon v-else size="18" name="file-close" />
           </template>
+          <treeIcon class="iconfont" :title="item.title" v-else />
+        </template>
+        <template #operation="{ type }">
+          <!-- <i class="iconfont icon-eyeoutlined"></i> -->
+          <span style="color:#ccc;font-size:12px">详情</span>
+        </template>
+      </cameraTree>
+    </div>
+    <div class="right-area" v-if="addrList.length > 0">
+      <div class="vent-flex-row-wrap camera-box">
+        <div v-for="(item, index) in addrList" :key="index" class="player-box">
+          <div class="player-name">{{ item.name }}</div>
+          <div style="padding-top:3px">
+            <template v-if="item.addr.startsWith('rtsp://')">
+              <video :id="`video${index}`" muted autoplay></video>
+              <div class="click-box" @dblclick="goFullScreen(`video${index}`)"></div>
+            </template>
+            <template v-else>
+              <div :id="'player' + index"></div>
+            </template>
+          </div>
         </div>
       </div>
+      <div class="pagination">
+        <Pagination v-model:current="current" :total="total" show-less-items @change="onChange" />
+      </div>
     </div>
-    <div class="pagination">
-      <Pagination v-model:current="current" :total="total" show-less-items @change="onChange"/>
+    <div class="camera-box" v-else>
+      <Empty />
     </div>
   </div>
-  <div class="camera-box" v-else>
-    <Empty />
-  </div>
-  
 </template>
 <script lang="ts" setup>
-  import {onMounted, onUnmounted, ref } from 'vue';
-  import { Pagination, Empty } from 'ant-design-vue';
-  import { list, cameraAddr } from './camera.api'
-  import Player,  {I18N} from 'xgplayer';
-  import ZH from 'xgplayer/es/lang/zh-cn'
-  import HlsPlugin from 'xgplayer-hls';
-  import FlvPlugin from 'xgplayer-flv';
-  import 'xgplayer/dist/index.min.css';
+import { onMounted, onUnmounted, ref, reactive, nextTick } from 'vue';
+import { Pagination, Empty } from 'ant-design-vue';
+import { list, cameraAddr, getCameraDevKind } from './camera.api'
+import Player, { I18N } from 'xgplayer';
+import ZH from 'xgplayer/es/lang/zh-cn'
+import HlsPlugin from 'xgplayer-hls';
+import FlvPlugin from 'xgplayer-flv';
+import 'xgplayer/dist/index.min.css';
+import cameraTree from './common/cameraTree.vue';
+import { SvgIcon } from '/@/components/Icon';
+import treeIcon from './common/Icon/treeIcon.vue';
 
-  I18N.use(ZH)
+//当前选中树节点
+let selected = reactive<any>({
+  id: null,
+  pid: null,
+  title: '',
+  isFolder: false,
+});
+//tree菜单列表
+let listArr = ref<any[]>([]);
+let searchParam = reactive({
+  devKind: '',
+  strType: '',
+})
 
-  const pageSize = 8
-  const current = ref(1)
-  const total = ref(0)
-  const playerList = ref([])
-  const webRtcServerList = <any[]>[]
-  let addrList = ref<{ name: string, addr: string }[]>([])
+I18N.use(ZH)
+const pageSize = 8
+const current = ref(1)
+const total = ref(0)
+const playerList = ref([])
+const webRtcServerList = <any[]>[]
+let addrList = ref<{ name: string, addr: string }[]>([])
 
-  async function getVideoAddrs(){
-    clearCamera();
-    playerList.value = []
-    const result = await list({ pageSize: pageSize, pageNo: current.value })
-    if(result && result.records && result.records.length > 0){
-      total.value = result['total']
-      const cameraList = <{ name: string, addr: string }[]>[]
-      const cameras = result.records
-      // const camerasArr: [] = [
-      //   {
-      //     name: '1111',
-      //     devicekind: 'toHKRtsp',
-      //   },
-      //   {
-      //     name: '2222',
-      //     devicekind: 'toHKRtsp',
-      //   },
-      //   {
-      //     name: '3333',
-      //     devicekind: 'toHKRtsp',
-      //   },
-      //   {
-      //     name: '4444',
-      //     devicekind: 'toHKRtsp',
-      //   },
-      //   {
-      //     name: '5555',
-      //     devicekind: 'toHKRtsp',
-      //   },
-      //   {
-      //     name: '6666',
-      //     devicekind: 'toHKRtsp',
-      //   },
-      //   {
-      //     name: '7777',
-      //     devicekind: 'toHKRtsp',
-      //   },
-      //   {
-      //     name: '8888',
-      //     devicekind: 'toHKRtsp',
-      //   },
-      //   {
-      //     name: '9999',
-      //     devicekind: 'toHKRtsp',
-      //   },
-      //   {
-      //     name: 'aaaa',
-      //     devicekind: 'toHKRtsp',
-      //   },
-      //   {
-      //     name: 'bbbb',
-      //     devicekind: 'toHKRtsp',
-      //   },
-      //   {
-      //     name: 'cccc',
-      //     devicekind: 'toHKRtsp',
-      //   },
-      // ];
-      // const cameras = []
-      // for (let index = (current.value - 1)*pageSize; index < current.value * pageSize && index < camerasArr.length ; index++) {
-      //   cameras.push(camerasArr[index]) ;
-      // }
+async function getCameraDevKindList() {
+  let res = await getCameraDevKind()
+  if (res.length != 0) {
+    res.forEach(el => {
+      el.pid = 'root'
+      el.isFolder = true
+      el.title = el.itemText
+      el.id = el.subDictId
+      el.children.forEach(v => {
+        v.pid = v.dictId
+        v.isFolder = false
+        v.title = v.itemText
+      })
+    })
+    listArr.value = res
+    selected.id = listArr['value'][0].id;
+    selected.pid = listArr['value'][0].pid;
+    selected.title = listArr['value'][0].title;
+    selected.isFolder = listArr['value'][0].isFolder;
+  }
+}
 
-      for (let i = 0; i < cameras.length; i++) {
-        const item = cameras[i];
-        
-        if (item['devicekind'] === 'toHKRtsp') {
-          // 从海康平台接口获取视频流
-          try {
-            const data = await cameraAddr({ cameraCode: item['addr'] });
-            if (data && data['url']) {
-              cameraList.push({ name: item['name'], addr: data['url'] });
-            }
-            // cameraList.push({
-            //   name: item['name'],
-            //   // addr: 'http://219.151.31.38/liveplay-kk.rtxapp.com/live/program/live/hnwshd/4000000/mnf.m3u8'
-            //   addr: 'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.mp4/.m3u8',
-            // });
-          } catch (error) {
-            
-          }
-          
-        } else {
-          if(item['addr'].includes('0.0.0.0')){
-            item['addr'] = item['addr'].replace('0.0.0.0', window.location.hostname)
+
+//点击目录
+function onClick(node) {
+  if (node.pid != 'root') {
+    console.log(node, 'node--------------')
+    selected.id = node.id;
+    selected.pid = node.pid;
+    selected.title = node.title;
+    selected.isFolder = node.isFolder;
+    searchParam.devKind = listArr.value.filter(v => v.id == node.pid)[0]['itemValue']
+    searchParam.strType = node.itemValue
+    getVideoAddrs()
+  }
+
+};
+
+async function getVideoAddrs() {
+  clearCamera();
+  playerList.value = []
+  let res = await list({ ...searchParam })
+  if (res.records.length != 0) {
+    const cameraList = <{ name: string, addr: string }[]>[]
+    const cameras = res.records
+    for (let i = 0; i < cameras.length; i++) {
+      const item = cameras[i];
+
+      if (item['devicekind'] === 'toHKRtsp') {
+        // 从海康平台接口获取视频流
+        try {
+          const data = await cameraAddr({ cameraCode: item['addr'] });
+          if (data && data['url']) {
+            cameraList.push({ name: item['name'], addr: data['url'] });
           }
-          cameraList.push({ name: item['name'], addr: item['addr'] });
+          // cameraList.push({
+          //   name: item['name'],
+          //   // addr: 'http://219.151.31.38/liveplay-kk.rtxapp.com/live/program/live/hnwshd/4000000/mnf.m3u8'
+          //   addr: 'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.mp4/.m3u8',
+          // });
+        } catch (error) {
+
+        }
+
+      } else {
+        if (item['addr'].includes('0.0.0.0')) {
+          item['addr'] = item['addr'].replace('0.0.0.0', window.location.hostname)
         }
+        cameraList.push({ name: item['name'], addr: item['addr'] });
       }
-      addrList.value = cameraList
     }
+    addrList.value = cameraList
   }
+}
 
-  function onChange(page) {
-    current.value = page;
-    getVideoAddrs().then(() => {
-      getVideo()
-    })
+function onChange(page) {
+  current.value = page;
+  getVideoAddrs().then(() => {
+    getVideo()
+  })
+}
+
+function getVideo() {
+  const ip = VUE_APP_URL.webRtcUrl;
+  for (let i = 0; i < addrList.value.length; i++) {
+    const item = addrList.value[i]
+    if (item.addr.startsWith('rtsp://')) {
+      const dom = document.getElementById('video' + i) as HTMLVideoElement
+      dom.muted = true;
+      dom.volume = 0
+      const webRtcServer = new window['WebRtcStreamer'](dom, location.protocol + ip)
+      webRtcServerList.push(webRtcServer)
+      webRtcServer.connect(item.addr)
+    } else {
+      setNoRtspVideo('player' + i, item.addr)
+    }
   }
+}
 
-  function getVideo() {
-    for(let i = 0; i < addrList.value.length; i++){
-      const item = addrList.value[i]
-      if(item.addr.startsWith('rtsp://')){
-        const dom = document.getElementById('video' + i) as HTMLVideoElement
-        dom.muted = true;
-        dom.volume = 0   
-        const webRtcServer = new window['WebRtcStreamer'](dom, VUE_APP_URL.webRtcUrl.startsWith('/') ? location.protocol + VUE_APP_URL.webRtcUrl : VUE_APP_URL.webRtcUrl)
-        webRtcServerList.push(webRtcServer)
-        webRtcServer.connect(item.addr)
-      }else{
-        setNoRtspVideo('player'+i,item.addr )
+function setNoRtspVideo(id, videoAddr) {
+  const fileExtension = videoAddr.split('.').pop();
+  // const plugins = fileExtension === 'flv' ? [ FlvPlugin ] : [ HlsPlugin ];
+  if (fileExtension === 'flv') {
+    const player = new Player({
+      lang: 'zh',
+      id: id,
+      url: videoAddr,
+      width: 354,
+      height: 245,
+      poster: '/src/assets/images/vent/noSinge.png',
+      plugins: [FlvPlugin],
+      fluid: true,
+      autoplay: true,
+      isLive: true,
+      playsinline: false,
+      screenShot: true,
+      whitelist: [''],
+      ignores: ['time'],
+      closeVideoClick: true,
+      customConfig: {
+        isClickPlayBack: false
+      },
+      flv: {
+        retryCount: 3, // 重试 3 次,默认值
+        retryDelay: 1000, // 每次重试间隔 1 秒,默认值
+        loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
+        fetchOptions: {
+          // 该参数会透传给 fetch,默认值为 undefined
+          mode: 'cors'
+        },
+        targetLatency: 10, // 直播目标延迟,默认 10 秒
+        maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
+        disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
+        maxJumpDistance: 10,
       }
-    }
+    });
+    playerList.value.push(player)
   }
+  if (fileExtension === 'm3u8') {
+    let player
+    if (document.createElement('video').canPlayType('application/vnd.apple.mpegurl')) {
+      // 原生支持 hls 播放
+      player = new Player({
+        lang: 'zh',
+        id: id,
+        url: videoAddr,
+        width: 354,
+        height: 245,
+        isLive: true,
+        autoplay: true,
+        autoplayMuted: true,
+        cors: true,
+        poster: '/src/assets/images/vent/noSinge.png',
+        hls: {
+          retryCount: 3, // 重试 3 次,默认值
+          retryDelay: 1000, // 每次重试间隔 1 秒,默认值
+          loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
+          fetchOptions: {
+            // 该参数会透传给 fetch,默认值为 undefined
+            mode: 'cors'
+          },
+          targetLatency: 10, // 直播目标延迟,默认 10 秒
+          maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
+          disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
+          maxJumpDistance: 10,
+        }
 
-  function setNoRtspVideo(id,videoAddr) {
-    const fileExtension = videoAddr.split('.').pop();
-    // const plugins = fileExtension === 'flv' ? [ FlvPlugin ] : [ HlsPlugin ];
-    if(fileExtension === 'flv'){
-      const player = new Player({
+      })
+    } else if (HlsPlugin.isSupported()) { // 第一步
+      player = new Player({
         lang: 'zh',
         id: id,
         url: videoAddr,
-        width:  421,
-        height: 292,
+        width: 354,
+        height: 245,
+        isLive: true,
+        autoplay: true,
+        autoplayMuted: true,
+        plugins: [HlsPlugin], // 第二步
         poster: '/src/assets/images/vent/noSinge.png',
-        plugins: [ FlvPlugin ] ,
-				fluid: true,
-				autoplay: true,
-				isLive: true,
-				playsinline: false,
-				screenShot: true,
-				whitelist: [''],
-				ignores: ['time'],
-				closeVideoClick: true,
-				customConfig: {
-					isClickPlayBack: false
-				},
-				flv: {
-					retryCount: 3, // 重试 3 次,默认值
-            retryDelay: 1000, // 每次重试间隔 1 秒,默认值
-            loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
-            fetchOptions: {
-                // 该参数会透传给 fetch,默认值为 undefined
-                mode: 'cors'
-            },
-            targetLatency: 10, // 直播目标延迟,默认 10 秒
-            maxLatency:  20, // 直播允许的最大延迟,默认 20 秒
-            disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
-            maxJumpDistance: 10,
-				}
-      });
-      playerList.value.push(player)
-    }
-    if(fileExtension === 'm3u8'){
-      let player
-      if (document.createElement('video').canPlayType('application/vnd.apple.mpegurl')) {
-        // 原生支持 hls 播放
-        player = new Player({
-          lang: 'zh',
-          id: id,
-          url: videoAddr,
-          width: 421,
-          height: 292,
-          isLive: true,
-          autoplay: true,
-          autoplayMuted: true,
-          cors: true,
-          poster: '/src/assets/images/vent/noSinge.png',
-          hls: {
-            retryCount: 3, // 重试 3 次,默认值
-            retryDelay: 1000, // 每次重试间隔 1 秒,默认值
-            loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
-            fetchOptions: {
-                // 该参数会透传给 fetch,默认值为 undefined
-                mode: 'cors'
-            },
-            targetLatency: 10, // 直播目标延迟,默认 10 秒
-            maxLatency:  20, // 直播允许的最大延迟,默认 20 秒
-            disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
-            maxJumpDistance: 10,
-          }
-          
-        })
-      } else if (HlsPlugin.isSupported()) { // 第一步
-        player = new Player({
-          lang: 'zh',
-          id: id,
-          url: videoAddr,
-          width: 421,
-          height: 292,
-          isLive: true,
-          autoplay: true,
-          autoplayMuted: true,
-          plugins: [HlsPlugin], // 第二步
-          poster: '/src/assets/images/vent/noSinge.png',
-          hls: {
-            retryCount: 3, // 重试 3 次,默认值
-            retryDelay: 1000, // 每次重试间隔 1 秒,默认值
-            loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
-            fetchOptions: {
-                // 该参数会透传给 fetch,默认值为 undefined
-                mode: 'cors'
-            },
-            targetLatency: 10, // 直播目标延迟,默认 10 秒
-            maxLatency:  20, // 直播允许的最大延迟,默认 20 秒
-            disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
-            maxJumpDistance: 10,
-          }
-        })
-      }
-      playerList.value.push(player)
+        hls: {
+          retryCount: 3, // 重试 3 次,默认值
+          retryDelay: 1000, // 每次重试间隔 1 秒,默认值
+          loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
+          fetchOptions: {
+            // 该参数会透传给 fetch,默认值为 undefined
+            mode: 'cors'
+          },
+          targetLatency: 10, // 直播目标延迟,默认 10 秒
+          maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
+          disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
+          maxJumpDistance: 10,
+        }
+      })
     }
+    playerList.value.push(player)
   }
-  
+}
 
-  function goFullScreen(domId) {
-    const videoDom = document.getElementById(domId) as HTMLVideoElement
-    if(videoDom.requestFullscreen){
-      videoDom.requestFullscreen()
-      videoDom.play()
-    } else if(videoDom.mozRequestFullscreen){
-      videoDom.mozRequestFullscreen()
-      videoDom.play()
-    } else if (videoDom.webkitRequestFullscreen) {
-      videoDom.webkitRequestFullscreen()
-      videoDom.play()
-    } else if (videoDom.msRequestFullscreen) {
-      videoDom.msRequestFullscreen()
-      videoDom.play()
-    }
+
+function goFullScreen(domId) {
+  const videoDom = document.getElementById(domId) as HTMLVideoElement
+  if (videoDom.requestFullscreen) {
+    videoDom.requestFullscreen()
+    videoDom.play()
+  } else if (videoDom.mozRequestFullscreen) {
+    videoDom.mozRequestFullscreen()
+    videoDom.play()
+  } else if (videoDom.webkitRequestFullscreen) {
+    videoDom.webkitRequestFullscreen()
+    videoDom.play()
+  } else if (videoDom.msRequestFullscreen) {
+    videoDom.msRequestFullscreen()
+    videoDom.play()
   }
+}
 
-  function clearCamera() {
-    const num = webRtcServerList.length
-    for (let i = 0; i < num; i++) {
-      webRtcServerList[i].disconnect()
-      webRtcServerList[i] = null
-    }
-    for(let i = 0; i < playerList.value.length; i++){
-      const player = playerList.value[i]
-      if(player.destroy)player.destroy()
-    }
+function clearCamera() {
+  const num = webRtcServerList.length
+  for (let i = 0; i < num; i++) {
+    webRtcServerList[i].disconnect()
+    webRtcServerList[i] = null
+  }
+  for (let i = 0; i < playerList.value.length; i++) {
+    const player = playerList.value[i]
+    if (player.destroy) player.destroy()
   }
+}
 
-  onMounted(async() => {
-    await getVideoAddrs()
-    getVideo()
-  })
+onMounted(async () => {
+  await getVideoAddrs()
+  // getTreeList()
+  getCameraDevKindList()
+  getVideo()
+})
 
-  onUnmounted(() => {
-    clearCamera()
-  })
+onUnmounted(() => {
+  clearCamera()
+})
 
 </script>
 <style lang="less">
-  
-  .camera-box{
-    height: 700px;
-    overflow-y: auto;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    .player-box{
-      width: 451px;
-      height: 312px;
-      padding: 10px;
-      background: url('/@/assets/images/vent/camera_bg.png');
-      background-size: cover;
-      position: relative;
-      margin: 10px;
-      
-      .player-name{
-        font-size: 14px;
-        position: absolute;
-        top: 15px;
-        right: 15px;
-        color: #fff;
-        background-color: hsla(0, 0%, 50%, .5);
-        border-radius: 2px;
-        padding: 1px 5px;
-        max-width: 120px;
-        overflow: hidden;
-        white-space: nowrap;
-        text-overflow: ellipsis;
-        z-index: 999;
-      }
-      .click-box{
-        position: absolute;
-        width: 100%;
-        height: 100%;
-        top: 0;
-        left: 0;
-      }
+.camera-container {
+  position: relative;
+  width: calc(100% - 30px);
+  height: calc(100% - 84px);
+  display: flex;
+  margin: 15px;
+  justify-content: space-between;
+  align-items: center;
+
+  .left-area {
+    width: 15%;
+    height: 100%;
+    padding: 20px;
+    border: 1px solid #99e8ff66;
+    background: #27546e1a;
+    box-shadow: 0px 0px 20px 7px rgba(145, 233, 254, 0.7) inset;
+    -moz-box-shadow: 0px 0px 20px 7px rgba(145, 233, 254, 0.7) inset;
+    -webkit-box-shadow: 0px 0px 50px 1px rgb(149 235 255 / 5%) inset;
+    box-sizing: border-box;
+
+    // lxh
+    .iconfont {
+      color: #fff;
+      font-size: 12px;
+      margin-left: 5px;
     }
   }
-  .pagination{
-    width: 100%;
-    height: 200px;
-    display: flex;
-    justify-content: center;
-    align-items: center;
+
+  .right-area {
+    width: 85%;
+    height: 100%;
+    padding: 0px 0px 0px 15px;
+    box-sizing: border-box;
+
+    .camera-box {
+      height: calc(100% - 60px);
+      display: flex;
+      justify-content: flex-start;
+      align-items: flex-start;
+      flex-wrap: wrap;
+      overflow-y: auto;
+
+      .player-box {
+        width: 375px;
+        height: 272px;
+        padding: 10px;
+        background: url('/@/assets/images/vent/camera_bg.png');
+        background-size: 100% 100%;
+        position: relative;
+        margin: 10px;
+
+        .player-name {
+          font-size: 14px;
+          position: absolute;
+          top: 15px;
+          right: 15px;
+          color: #fff;
+          background-color: hsla(0, 0%, 50%, .5);
+          border-radius: 2px;
+          padding: 1px 5px;
+          max-width: 120px;
+          overflow: hidden;
+          white-space: nowrap;
+          text-overflow: ellipsis;
+          z-index: 999;
+        }
+
+        .click-box {
+          position: absolute;
+          width: 100%;
+          height: 100%;
+          top: 0;
+          left: 0;
+        }
+      }
+    }
+
+    .pagination {
+      width: 100%;
+      height: 60px;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+    }
   }
-  // :deep(video){
-  //   width: 100% !important;
-  //   height: 100% !important;
-  //   object-fit: cover !important;
-  // }
+}
+
+
+
+:deep(video) {
+  width: 100% !important;
+  height: 100% !important;
+  object-fit: cover !important;
+}
 </style>

+ 3 - 0
src/views/vent/monitorManager/deviceMonitor/components/device/index.vue

@@ -625,6 +625,9 @@ function goDetail(record?) {
     } else if (deviceType.value.startsWith('dusting')) {
       currentModal.value = DustModal
       modalVisible.value = true;
+    }else if(deviceType.value.startsWith('gasmonitor')){//瓦斯巡检
+      currentModal.value = gasModal
+      modalVisible.value = true;
     } else if (deviceType.value.startsWith('bundletube')) {
       currentModal.value = BundleModal
       modalVisible.value = true;

+ 485 - 0
src/views/vent/monitorManager/deviceMonitor/components/device/modal/gas.modal.vue

@@ -0,0 +1,485 @@
+<template>
+    <BasicModal
+      v-bind="$attrs"
+      @register="register"
+      :title="`瓦斯巡检监测详情  ${currentTime}`"
+      width="1200px"
+      @ok="handleOk"
+      @cancel="handleCancel"
+      wrapClassName="bundle-modal"
+    >
+      <div class="fiber-modal">
+        <div class="modal-left">
+          <div
+            v-for="device in deviceList"
+            class="link-item"
+            :class="{ 'active-device-title': device.deviceID === activeDeviceID }"
+            :key="device.deviceID"
+          >
+            <span class="" @click="selectDevice(device.deviceID)">{{ device.strinstallpos }}</span>
+          </div>
+        </div>
+        <div class="modal-right">
+          <span class="base-title">实时监测参数</span>
+          <div class="right-top">
+            <div class="top-item">
+              <div class="icon">
+                <SvgIcon class="icon-style" name="coval" style="width: 62px; height: 38px; margin-top: 10px" />
+              </div>
+              <div class="item-container">
+                <div class="title">一氧化碳</div>
+                <div class="value">{{ posMonitor.coval !== undefined && posMonitor.coval !== null ? posMonitor.coval : '-' }} <span>ppm</span> </div>
+              </div>
+            </div>
+            <div class="top-item">
+              <div class="icon">
+                <SvgIcon class="icon-style" name="co2val" style="width: 72px; height: 46px" />
+              </div>
+              <div class="item-container">
+                <div class="title">二氧化碳</div>
+                <div class="value">{{ posMonitor.co2val !== undefined && posMonitor.co2val !== null ? posMonitor.co2val : '-' }} <span>%</span></div>
+              </div>
+            </div>
+            <div class="top-item">
+              <div class="icon">
+                <SvgIcon class="icon-style" name="gasval" style="width: 72px; height: 46px" />
+              </div>
+              <div class="item-container">
+                <div class="title">甲烷</div>
+                <div class="value">{{ posMonitor.gasval !== undefined && posMonitor.gasval !== null ? posMonitor.gasval : '-' }} <span>%</span></div>
+              </div>
+            </div>
+            <div class="top-item">
+              <div class="icon">
+                <SvgIcon class="icon-style" name="ch2val" style="width: 76px; height: 42px" />
+              </div>
+              <div class="item-container">
+                <div class="title">乙烯</div>
+                <div class="value">{{ posMonitor.ch2val !== undefined && posMonitor.ch2val !== null ? posMonitor.ch2val : '-' }} <span>ppm</span></div>
+              </div>
+            </div>
+            <div class="top-item">
+              <div class="icon">
+                <SvgIcon class="icon-style" name="chval" style="width: 76px; height: 42px" />
+              </div>
+              <div class="item-container">
+                <div class="title">乙炔</div>
+                <div class="value">{{ posMonitor.chval !== undefined && posMonitor.chval !== null ? posMonitor.chval : '-' }} <span>ppm</span></div>
+              </div>
+            </div>
+            <div class="top-item">
+              <div class="icon">
+                <SvgIcon class="icon-style" name="o2val" style="width: 76px; height: 50px" />
+              </div>
+              <div class="item-container">
+                <div class="title">氧气</div>
+                <div class="value">{{ posMonitor.o2val !== undefined && posMonitor.o2val !== null ? posMonitor.o2val : '-' }} <span>%</span></div>
+              </div>
+            </div>
+            <div class="top-item warning-box">
+              <div class="icon">
+                <SvgIcon class="icon-style" size="42" name="alarm-warning" style="margin-top: 5px" />
+              </div>
+              <div class="item-container">
+                <div class="title">风险等级</div>
+                <div class="warning-value">{{ posMonitor['warnLevel_str'] ? posMonitor['warnLevel_str'] : '-' }}</div>
+              </div>
+            </div>
+            <div class="top-item warning-box">
+              <div class="icon">
+                <SvgIcon class="icon-style" size="42" name="link" style="margin-top: 5px" />
+              </div>
+              <div class="item-container">
+                <div class="title">连接状态</div>
+                <div class="warning-value">{{ posMonitor['netStatus'] == 1 ? '连接' : '未连接' }}</div>
+              </div>
+            </div>
+          </div>
+          <div class="right-bottom">
+            <span class="base-title">设备监测曲线</span>
+            <div class="echarts-box">
+              <BarAndLine
+                class="echarts-line"
+                xAxisPropType="time"
+                :dataSource="historyList"
+                height="100%"
+                :chartsColumns="chartsColumns"
+                :option="echatsOption"
+                chartsType="listMonitor"
+              />
+            </div>
+          </div>
+        </div>
+      </div>
+    </BasicModal>
+  </template>
+  <script lang="ts">
+    import { defineComponent, ref, watch, shallowRef } from 'vue';
+    import { BasicModal, useModalInner } from '/@/components/Modal';
+    import BarAndLine from '/@/components/chart/BarAndLine.vue';
+    import { SvgIcon } from '/@/components/Icon';
+    import { Decoration7 as DvDecoration7, ScrollBoard as DvScrollBoard } from '@kjgl77/datav-vue3';
+    import dayjs from 'dayjs';
+  
+    export default defineComponent({
+      components: { BasicModal, BarAndLine, SvgIcon, DvScrollBoard, DvDecoration7 },
+      props: {
+        dataSource: { type: Array },
+        activeID: { type: String },
+      },
+      setup(props) {
+        const currentTime = ref(dayjs().format('YYYY-MM-DD HH:mm:ss'));
+        const modelRef = ref({});
+        const loading = ref(true);
+        const activeDeviceID = ref('');
+        const deviceList = ref<any[]>([]);
+        const historyList = ref<any[]>([]);
+        const posList = ref<any[]>([]);
+        const posMonitor = shallowRef({});
+  
+        const echatsOption = {
+          grid: {
+            top: '25%',
+            left: '30',
+            right: '45',
+            bottom: '3%',
+            containLabel: true,
+          },
+          toolbox: {
+            feature: {},
+          },
+        };
+  
+        const chartsColumns = [
+          {
+            legend: '一氧化碳',
+            seriesName: '(ppm)',
+            ymax: 10,
+            yname: 'ppm',
+            linetype: 'line',
+            yaxispos: 'left',
+            color: '#FDB146',
+            sort: 1,
+            xRotate: 0,
+            dataIndex: 'coval',
+          },
+          {
+            legend: '乙炔',
+            seriesName: '',
+            ymax: 10,
+            yname: 'ppm',
+            linetype: 'line',
+            yaxispos: 'left',
+            color: '#00FFA8',
+            sort: 1,
+            xRotate: 0,
+            dataIndex: 'chval',
+          },
+          {
+            legend: '乙烯',
+            seriesName: '',
+            ymax: 10,
+            yname: 'ppm',
+            linetype: 'line',
+            yaxispos: 'left',
+            color: '#AE19FF',
+            sort: 1,
+            xRotate: 0,
+            dataIndex: 'ch2val',
+          },
+          {
+            legend: '二氧化碳',
+            seriesName: '(%)',
+            ymax: 20,
+            yname: '%',
+            linetype: 'line',
+            yaxispos: 'right',
+            color: '#9C83D9',
+            sort: 2,
+            xRotate: 0,
+            dataIndex: 'co2val',
+          },
+          {
+            legend: '甲烷',
+            seriesName: '',
+            ymax: 20,
+            yname: '%',
+            linetype: 'line',
+            yaxispos: 'right',
+            color: '#DA3914',
+            sort: 2,
+            xRotate: 0,
+            dataIndex: 'gasval',
+          },
+          {
+            legend: '氧气',
+            seriesName: '(氧气%)',
+            ymax: 30,
+            yname: '%',
+            linetype: 'line',
+            yaxispos: 'right',
+            color: '#03C2EC',
+            sort: 3,
+            xRotate: 0,
+            dataIndex: 'o2val',
+          },
+        ];
+        const [register, { setModalProps, closeModal }] = useModalInner();
+  
+        function handleVisibleChange(visible) {
+          if (visible) {
+            loading.value = true;
+            setModalProps({ loading: true, confirmLoading: true });
+  
+            setTimeout(() => {
+              loading.value = false;
+              setModalProps({ loading: false, confirmLoading: false });
+            }, 1000);
+          }
+        }
+  
+        // 选择监测
+        function selectDevice(id) {
+          loading.value = true;
+          setModalProps({ loading: true, confirmLoading: true });
+          setTimeout(() => {
+            loading.value = false;
+            activeDeviceID.value = id;
+            setModalProps({ loading: false, confirmLoading: false });
+          }, 300);
+        }
+  
+        function handleOk(e) {
+          e.preventDefault();
+          closeModal();
+        }
+  
+        function handleCancel(e) {
+          e.preventDefault();
+          closeModal();
+        }
+  
+        watch([() => props.dataSource, () => props.activeID], ([newDataSource, newActiveID], [oldDataSource, oldActiveID]) => {
+          // if (newActiveID != oldActiveID) {
+          //   activeDeviceID.value = newActiveID as string;
+          // }
+          activeDeviceID.value = activeDeviceID.value ? activeDeviceID.value : newActiveID;
+          deviceList.value = newDataSource?.filter((item: any, index) => {
+            if ((!activeDeviceID.value && index == 0) || item.deviceID === activeDeviceID.value) {
+              // activeDeviceID.value = item.deviceID;
+              posMonitor.value = Object.assign(item, item.readData);
+              historyList.value = item['history'];
+            }
+            item.readTime = item.readTime?.substring(11);
+            return item;
+          });
+        });
+  
+        return {
+          register,
+          model: modelRef,
+          currentTime,
+          handleVisibleChange,
+          selectDevice,
+          handleOk,
+          handleCancel,
+          deviceList,
+          historyList,
+          activeDeviceID,
+          posMonitor,
+          echatsOption,
+          posList,
+          chartsColumns,
+        };
+      },
+    });
+  </script>
+  <style lang="less">
+    .bundle-modal {
+      .zxm-modal {
+        top: 30px !important;
+      }
+    }
+  </style>
+  
+  <style lang="less" scoped>
+    .fiber-modal {
+      width: 100%;
+      height: 650px;
+      display: flex;
+      flex-direction: row;
+      justify-content: space-between;
+  
+      .modal-left {
+        width: 200px;
+        height: 100%;
+        overflow-y: auto;
+        background: #ffffff11;
+        padding: 5px;
+        border-radius: 5px;
+        .active-device-title {
+          color: aqua;
+        }
+        .link-item {
+          position: relative;
+          cursor: pointer;
+          line-height: 30px;
+          padding-left: 30px;
+          span:hover {
+            color: #89ffff;
+          }
+          &::after {
+            content: '';
+            position: absolute;
+            display: block;
+            width: 8px;
+            height: 8px;
+            top: 12px;
+            left: 10px;
+            transform: rotateZ(45deg) skew(10deg, 10deg);
+            background: #45d3fd;
+          }
+        }
+      }
+      .modal-right {
+        width: calc(100% - 220px);
+        overflow-y: auto;
+        .base-title {
+          line-height: 32px;
+          position: relative;
+          padding-left: 20px;
+          &::after {
+            content: '';
+            position: absolute;
+            display: block;
+            width: 4px;
+            height: 12px;
+            top: 4px;
+            left: 10px;
+            background: #45d3fd;
+            border-radius: 4px;
+          }
+        }
+        .right-top {
+          display: flex;
+          flex-direction: row;
+          justify-content: space-between;
+          flex-wrap: wrap;
+          margin-bottom: 10px;
+          .top-item {
+            width: 220px;
+            height: 100px;
+            display: flex;
+            flex-direction: row;
+            justify-content: center;
+            border: 1px solid rgba(25, 237, 255, 0.4);
+            box-shadow: inset 0 0 10px rgba(0, 197, 255, 0.6);
+            background: rgba(0, 0, 0, 0.06666666666666667);
+            padding-top: 20px;
+            margin: 10px 0;
+            .icon {
+              margin-right: 10px;
+              margin-top: 5px;
+              color: #fdb146;
+            }
+            .item-container {
+              width: 110px;
+              display: flex;
+              flex-direction: column;
+              justify-content: center;
+              div {
+                text-align: center;
+              }
+              .title {
+                font-size: 18px;
+              }
+              .value {
+                text-shadow: 0 0 25px #00fbfe;
+                background: linear-gradient(0deg, #45d3fd, #45d3fd, #61ddb1, #61ddb1);
+                font-style: normal;
+                background-size: cover;
+                font-family: electronicFont;
+                font-size: 30px;
+                -webkit-background-clip: text;
+                background-clip: text;
+                -webkit-text-fill-color: transparent;
+                position: relative;
+                top: -8px;
+  
+                span {
+                  font-family: Arial, Helvetica, sans-serif;
+                  font-size: 18px;
+                  color: aliceblue;
+                }
+              }
+            }
+          }
+          .warning-box {
+            padding-top: 0px;
+            .icon {
+              margin-top: 20px;
+              :deep(.icon-style) {
+                width: auto;
+                color: #fdb146;
+              }
+            }
+            .warning-value {
+              font-size: 18px;
+              color: #61ddb1;
+            }
+          }
+        }
+        .right-center {
+          margin-top: 20px;
+          display: flex;
+          flex-direction: row;
+          justify-content: space-between;
+          .table-box {
+            position: relative;
+            width: 500px;
+            height: 250px;
+          }
+          .warning-box {
+            width: calc(100% - 520px);
+            .warning-container {
+              width: 100%;
+              height: convert;
+              background: #009acd00;
+              :deep(.dv-scroll-board) {
+                .row-item {
+                  height: 40px !important;
+                  line-height: 40px !important;
+                }
+                .header-item {
+                  border-top: 1px solid #91e9fe !important;
+                  border-bottom: 1px solid #91e9fe !important;
+                }
+              }
+            }
+          }
+        }
+        .right-bottom {
+          margin-top: 20px;
+          .echarts-box {
+            width: 100%;
+            height: 320px;
+            position: relative;
+            .echarts-line {
+              width: calc(100% + 80px);
+              position: absolute;
+            }
+          }
+        }
+      }
+    }
+    :deep(.zxm-table-body) {
+      border: 1px solid rgba(57, 232, 255, 0.2) !important;
+      .zxm-table-tbody > tr > td {
+        border: none !important;
+      }
+    }
+    :deep(.zxm-table-cell) {
+      border-right: none !important;
+    }
+  </style>
+  

+ 58 - 0
src/views/vent/performance/approvalPend/approvalPend.data.ts

@@ -0,0 +1,58 @@
+import { BasicColumn } from '/@/components/Table';
+export const  columns:BasicColumn[]=[
+  {
+    title: '序号',
+    width: 60,
+    align: 'center',
+    customRender: ({ index }: { index: number }) => `${index + 1}`
+  },
+  {
+    title: '文件名称',
+    dataIndex: 'areaName',
+    key: 'areaName',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '文件类型',
+    dataIndex: 'warnLevel_dictText',
+    key: 'warnLevel_dictText',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '文件来源',
+    dataIndex: 'zhType',
+    key: 'zhType',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '上传时间',
+    dataIndex: 'isLk',
+    key: 'isLk',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '审批状态',
+    dataIndex: 'remarks',
+    key: 'remarks',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '当前审批人',
+    dataIndex: 'suggestMsg',
+    key: 'suggestMsg',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '操作',
+    key: 'action',
+    width: 250,
+    slots: { customRender: 'action' },
+    align: 'center',
+  },
+]

+ 39 - 0
src/views/vent/performance/approvalPend/index.vue

@@ -0,0 +1,39 @@
+<template>
+    <div class="approvalPend">
+        <a-table size="small" :dataSource="dataSource" :columns="columns" :scroll="{ y: 730 }" :pagination="pagination"
+            @change="pageChange">
+
+            <template #action="{ record }">
+                <a-button type="link" style="color:#3DF6FF" @click="getViews">
+                    查看
+                </a-button>
+            </template>
+        </a-table>
+    </div>
+</template>
+
+<script setup lang="ts">
+import {ref,reactive,watch,nextTick} from 'vue'
+import {columns} from './approvalPend.data.ts'
+
+//待审批列表数据
+let dataSource=ref<any[]>([])
+//分页参数配置
+    let pagination = reactive({
+  current: 1, // 当前页码
+  pageSize: 10, // 每页显示条数
+  total: 0, // 总条目数,后端返回
+  // showTotal: (total, range) => `${range[0]}-${range[1]} 条,总共 ${total} 条`, // 分页右下角显示信息
+  showSizeChanger: true, // 是否可改变每页显示条数
+  pageSizeOptions: ['10', '20', '50',], // 可选的每页显示条数
+})
+
+</script>
+
+<style lang="less" scoped>
+.approvalPend {
+    position: relative;
+    width: 100%;
+    height: 100%;
+}
+</style>

+ 58 - 0
src/views/vent/performance/approved/approved.data.ts

@@ -0,0 +1,58 @@
+import { BasicColumn } from '/@/components/Table';
+export const  columns:BasicColumn[]=[
+  {
+    title: '序号',
+    width: 60,
+    align: 'center',
+    customRender: ({ index }: { index: number }) => `${index + 1}`
+  },
+  {
+    title: '文件名称',
+    dataIndex: 'areaName',
+    key: 'areaName',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '文件类型',
+    dataIndex: 'warnLevel_dictText',
+    key: 'warnLevel_dictText',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '文件来源',
+    dataIndex: 'zhType',
+    key: 'zhType',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '上传时间',
+    dataIndex: 'isLk',
+    key: 'isLk',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '审批状态',
+    dataIndex: 'remarks',
+    key: 'remarks',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '当前审批人',
+    dataIndex: 'suggestMsg',
+    key: 'suggestMsg',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '操作',
+    key: 'action',
+    width: 250,
+    slots: { customRender: 'action' },
+    align: 'center',
+  },
+]

+ 39 - 0
src/views/vent/performance/approved/index.vue

@@ -0,0 +1,39 @@
+<template>
+    <div class="approved">
+        <a-table size="small" :dataSource="dataSource" :columns="columns" :scroll="{ y: 730 }" :pagination="pagination"
+            @change="pageChange">
+
+            <template #action="{ record }">
+                <a-button type="link" style="color:#3DF6FF" @click="getViews">
+                    查看
+                </a-button>
+            </template>
+        </a-table>
+    </div>
+</template>
+
+<script setup lang="ts">
+import {ref,reactive,watch,nextTick} from 'vue'
+import {columns} from './approved.data'
+
+//已审批列表数据
+let dataSource=ref<any[]>([])
+//分页参数配置
+let pagination = reactive({
+  current: 1, // 当前页码
+  pageSize: 10, // 每页显示条数
+  total: 0, // 总条目数,后端返回
+  // showTotal: (total, range) => `${range[0]}-${range[1]} 条,总共 ${total} 条`, // 分页右下角显示信息
+  showSizeChanger: true, // 是否可改变每页显示条数
+  pageSizeOptions: ['10', '20', '50',], // 可选的每页显示条数
+})
+
+</script>
+
+<style lang="less" scoped>
+.approved{
+    position: relative;
+    width: 100%;
+    height: 100%;
+}
+</style>

+ 0 - 0
src/views/vent/performance/completeLc/completeLc.api.ts


+ 79 - 0
src/views/vent/performance/completeLc/completeLc.data.ts

@@ -0,0 +1,79 @@
+import { BasicColumn } from '/@/components/Table';
+export const  columns:BasicColumn[]=[
+  {
+    title: '序号',
+    width: 60,
+    align: 'center',
+    customRender: ({ index }: { index: number }) => `${index + 1}`
+  },
+  {
+    title: '流程名称',
+    dataIndex: 'areaName',
+    key: 'areaName',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '申请人',
+    dataIndex: 'warnLevel_dictText',
+    key: 'warnLevel_dictText',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '标识Key',
+    dataIndex: 'zhType',
+    key: 'zhType',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '版本',
+    dataIndex: 'isLk',
+    key: 'isLk',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '审批结果',
+    dataIndex: 'remarks',
+    key: 'remarks',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '原因详情',
+    dataIndex: 'suggestMsg',
+    key: 'suggestMsg',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '总耗时',
+    dataIndex: 'suggestMsg',
+    key: 'suggestMsg',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '开始时间',
+    dataIndex: 'suggestMsg',
+    key: 'suggestMsg',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '结束时间',
+    dataIndex: 'suggestMsg',
+    key: 'suggestMsg',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '操作',
+    key: 'action',
+    width: 250,
+    slots: { customRender: 'action' },
+    align: 'center',
+  },
+]

+ 81 - 0
src/views/vent/performance/completeLc/index.vue

@@ -0,0 +1,81 @@
+<template>
+    <div class="completeLc">
+        <div class="search-box">
+            <a-form :model="formSearch" layout="inline" :label-col="labelCol">
+                <a-form-item label="流程名称:">
+                    <a-input style="width:240px" placeholder="请输入..." v-model:value="formSearch.name" />
+                </a-form-item>
+                <a-form-item label="标识Key:">
+                    <a-input style="width:240px" placeholder="请输入..." v-model:value="formSearch.name" />
+                </a-form-item>
+                <a-form-item label="">
+                    <a-button style="margin-right:10px" type="primary"
+                        preIcon="ant-design:search-outlined">查询</a-button>
+                    <a-button type="primary">重置</a-button>
+                </a-form-item>
+            </a-form>
+        </div>
+        <div class="content-box">
+            <a-table size="small" :dataSource="dataSource" :columns="columns" :scroll="{ y: 730 }"
+                :pagination="pagination" @change="pageChange">
+
+                <template #action="{ record }">
+                    <a-button type="link" style="color:#3DF6FF" @click="getViews">
+                        查看
+                    </a-button>
+                </template>
+            </a-table>
+        </div>
+    </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive,  } from 'vue'
+import { columns} from './completeLc.data'
+
+const labelCol = { style: { width: '80px' } };
+//查询参数
+let formSearch = reactive({
+    name: ''
+})
+//数据列表
+let dataSource=ref<any[]>([])
+
+//分页参数配置
+let pagination = reactive({
+  current: 1, // 当前页码
+  pageSize: 10, // 每页显示条数
+  total: 0, // 总条目数,后端返回
+  // showTotal: (total, range) => `${range[0]}-${range[1]} 条,总共 ${total} 条`, // 分页右下角显示信息
+  showSizeChanger: true, // 是否可改变每页显示条数
+  pageSizeOptions: ['10', '20', '50',], // 可选的每页显示条数
+})
+</script>
+
+<style lang="less" scoped>
+.completeLc {
+    position: relative;
+    width: 100%;
+    height: 100%;
+    padding: 15px;
+    box-sizing: border-box;
+
+    .search-box {
+        height: 50px;
+    }
+
+    .content-box {
+        height: calc(100% - 40px);
+    }
+}
+
+:deep(.zxm-form-item-label > label) {
+    color: #fff;
+}
+
+:deep(.zxm-input) {
+    color: #fff;
+    background-color: #ffffff00;
+    border: 1px solid #3ad8ff77 !important;
+}
+</style>

+ 58 - 0
src/views/vent/performance/endEd/endEd.data.ts

@@ -0,0 +1,58 @@
+import { BasicColumn } from '/@/components/Table';
+export const  columns:BasicColumn[]=[
+  {
+    title: '序号',
+    width: 60,
+    align: 'center',
+    customRender: ({ index }: { index: number }) => `${index + 1}`
+  },
+  {
+    title: '文件名称',
+    dataIndex: 'areaName',
+    key: 'areaName',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '文件类型',
+    dataIndex: 'warnLevel_dictText',
+    key: 'warnLevel_dictText',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '文件来源',
+    dataIndex: 'zhType',
+    key: 'zhType',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '上传时间',
+    dataIndex: 'isLk',
+    key: 'isLk',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '审批状态',
+    dataIndex: 'remarks',
+    key: 'remarks',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '当前审批人',
+    dataIndex: 'suggestMsg',
+    key: 'suggestMsg',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '操作',
+    key: 'action',
+    width: 250,
+    slots: { customRender: 'action' },
+    align: 'center',
+  },
+]

+ 38 - 0
src/views/vent/performance/endEd/index.vue

@@ -0,0 +1,38 @@
+<template>
+    <div class="endEd">
+        <a-table size="small" :dataSource="dataSource" :columns="columns" :scroll="{ y: 730 }" :pagination="pagination"
+            @change="pageChange">
+
+            <template #action="{ record }">
+                <a-button type="link" style="color:#3DF6FF" @click="getViews">
+                    查看
+                </a-button>
+            </template>
+        </a-table>
+    </div>
+</template>
+
+<script setup lang="ts">
+import {ref,reactive,watch,nextTick} from 'vue'
+import {columns} from './endEd.data'
+
+//已结束列表数据
+let dataSource=ref<any[]>([])
+//分页参数配置
+let pagination = reactive({
+  current: 1, // 当前页码
+  pageSize: 10, // 每页显示条数
+  total: 0, // 总条目数,后端返回
+  // showTotal: (total, range) => `${range[0]}-${range[1]} 条,总共 ${total} 条`, // 分页右下角显示信息
+  showSizeChanger: true, // 是否可改变每页显示条数
+  pageSizeOptions: ['10', '20', '50',], // 可选的每页显示条数
+})
+</script>
+
+<style lang="less" scoped>
+.endEd{
+    position: relative;
+    width: 100%;
+    height: 100%;
+}
+</style>

+ 5 - 4
src/views/vent/performance/fileDetail/fileDetail.data.ts

@@ -31,6 +31,10 @@ export const columns: BasicColumn[] = [
   //   width: 120,
   // },
   {
+    title: '上传时间',
+    dataIndex: 'createTime',
+  },
+  {
     title: '审批状态',
     dataIndex: 'bpmStatus_dictText',
   },
@@ -39,8 +43,5 @@ export const columns: BasicColumn[] = [
     dataIndex: 'createBy',
     width: 120,
   },
-  {
-    title: '上传时间',
-    dataIndex: 'createTime',
-  },
+ 
 ];

+ 364 - 365
src/views/vent/performance/fileDetail/index.vue

@@ -4,29 +4,20 @@
     <div class="content">
       <div class="left-box">
         <!-- 左侧树菜单 -->
-        <fileSystem
-          :selected="selected"
-          :list="listArr"
-          :draggable="true"
-          @delete-node="onDeltet"
-          @on-click="onClick"
-          @change-name="onChangeName"
-          @addNode="onAddNode"
-        >
+        <fileSystem :selected="selected" :list="listArr" :draggable="true" @delete-node="onDeltet" @on-click="onClick"
+          @change-name="onChangeName" @addNode="onAddNode">
           <template #icon="{ item }">
             <template v-if="item.isFolder">
-              <!-- <icon v-if="item.expanded" class="iconfont" iconName="icon-24gf-folderOpen" /> -->
               <SvgIcon v-if="item.expanded" size="18" name="file-open" />
-              <!-- <icon v-else class="iconfont" iconName="icon-bg-folder" /> -->
               <SvgIcon v-else size="18" name="file-close" />
             </template>
             <treeIcon class="iconfont" :title="item.title" v-else />
           </template>
           <template #operation="{ type }">
-            <!-- <i class="iconfont icon-add_file" v-if="type == 'addFolder'"></i> -->
             <i class="iconfont icon-xinzeng" v-if="type == 'addDocument'"></i>
             <i class="iconfont icon-bianji" v-if="type == 'Editable'"></i>
-            <a-popconfirm v-if="type == 'deleteNode'" title="是否确认删除!" ok-text="确定" cancel-text="取消" @confirm="confirmDel">
+            <a-popconfirm v-if="type == 'deleteNode'" title="是否确认删除!" ok-text="确定" cancel-text="取消"
+              @confirm="confirmDel">
               <i class="iconfont icon-guanbi"></i>
             </a-popconfirm>
           </template>
@@ -39,20 +30,22 @@
           <a-button type="primary" style="float: right; margin-right: 20px" @click="openModal(true)">文件上传</a-button>
         </div>
         <div class="list">
-          <!-- <div class="bd-t"></div> -->
-          <NormalTable
-            v-if="alive"
-            :selfParam="selfParam"
-            :searchParam="fileName"
-            :nodeParam="nodeParam"
-            :columns="columns"
-            :list="getTree"
-            :deleteById="deleteById"
-            :downLoad="downLoad"
-            designScope="file-detail"
-            title="文件详情"
-          />
-          <!-- <div class="bd-b"></div> -->
+          <a-tabs class="tabs-box" v-model:activeKey="activeKey" @change="tabChange">
+            <a-tab-pane key="1" tab="全部">
+              <NormalTable v-if="alive" :selfParam="selfParam" :searchParam="fileName" :nodeParam="nodeParam"
+                :columns="columns" :list="getTree" :deleteById="deleteById" :downLoad="downLoad"
+                designScope="file-detail" title="文件详情" />
+            </a-tab-pane>
+            <a-tab-pane key="2" tab="待审批">
+              <approvalPend></approvalPend>
+            </a-tab-pane>
+            <a-tab-pane key="3" tab="审批中">
+              <approved></approved>
+            </a-tab-pane>
+            <a-tab-pane key="4" tab="已审批">
+              <endEd></endEd>
+            </a-tab-pane>
+          </a-tabs>
         </div>
       </div>
     </div>
@@ -66,7 +59,8 @@
           </a-radio-group>
         </a-form-item>
         <a-form-item label="文件类型">
-          <JDictSelectTag v-model:value="formState.fileType" placeholder="请选择文件类型" dictCode="file_type" style="width: 260px" />
+          <JDictSelectTag v-model:value="formState.fileType" placeholder="请选择文件类型" dictCode="file_type"
+            style="width: 260px" />
         </a-form-item>
         <a-form-item label="文件上传">
           <a-upload :before-upload="beforeUpload" @remove="handleRemove" :multiple="false" :file-list="fileList">
@@ -78,382 +72,387 @@
   </div>
 </template>
 <script lang="ts" setup name="system-user">
-  import customHeader from '/@/components/vent/customHeader.vue';
-  import { useRouter } from 'vue-router';
-  import { useMessage } from '/@/hooks/web/useMessage';
-  import fileSystem from './commen/fileSystem.vue';
-  import { SvgIcon } from '/@/components/Icon';
-  import treeIcon from './commen/Icon/treeIcon.vue';
-  import { ref, onMounted, reactive, nextTick, watch } from 'vue';
-  import NormalTable from '../comment/NormalTable.vue';
-  import { columns } from './fileDetail.data';
-  import { getTree, createFile, editMenu, delMenu, uploadApi, downLoad, deleteById } from './fileDetail.api';
-  import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
-
-  let selfParam = reactive({
-    //各矿参数
-    sysOrgCode: '',
-    bpmStatus: null,
-    flag: false,
-  });
-  let router = useRouter(); //路由
-  const { createMessage } = useMessage();
-  let fileName = ref('');
-  let fileList = reactive<any[]>([]); //上传文件列表
-  // let uploadParam = reactive({}); //上传文件参数
-  let nodeParam = reactive({}); //点击树节点时传递的参数
-  let alive = ref(true); //点击树节点刷新表格数据
-  let visible = ref(false); //控制上传弹窗的显示
-  let formState = reactive({
-    //上传文件类型,是否审批
-    isApprove: null,
-    fileType: '',
-  });
+import customHeader from '/@/components/vent/customHeader.vue';
+import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
+import fileSystem from './commen/fileSystem.vue';
+import treeIcon from './commen/Icon/treeIcon.vue';
+import NormalTable from '../comment/NormalTable.vue';
+import approvalPend from '../approvalPend/index.vue'
+import approved from '../approved/index.vue'
+import endEd from '../endEd/index.vue'
+import { useRouter } from 'vue-router';
+import { useMessage } from '/@/hooks/web/useMessage';
+import { SvgIcon } from '/@/components/Icon';
+import { ref, onMounted, reactive, nextTick, watch } from 'vue';
+import { columns } from './fileDetail.data';
+import { getTree, createFile, editMenu, delMenu, uploadApi, downLoad, deleteById } from './fileDetail.api';
 
-  //lxh 当前选中树节点
-  let selected = reactive<any>({
-    id: null,
-    pid: null,
-    title: '',
-    isFolder: false,
-  });
-  let flag = ref('');
-  //左侧菜单列表
-  let listArr = reactive<any[]>([]);
-  //获取要删除的节点数据
-  let delNode = reactive({});
-
-  //上传文件
-  let openModal = (val) => {
-    formState.isApprove = null;
-    formState.fileType = '';
-    fileList.length = 0;
-    visible.value = val;
-  };
-  //文件审批状态切换
-  let changeRadio = (val) => {
-    formState.isApprove = val.target.value;
-  };
-  //开始上传
-  let handleOk = () => {
-    if (formState.isApprove === null) {
-      createMessage.warning('请选择文件审批状态!');
-    } else {
-      const formData = new FormData();
-      formData.append('file', fileList[0]);
-      formData.append('parentId', selected.id);
-      formData.append('isApprove', formState.isApprove);
-      formData.append('fileType', formState.fileType);
-      uploadApi(formData).then((res) => {
-        console.log(res, '上传返回');
-        alive.value = false;
-        nextTick(() => {
-          alive.value = true;
-          visible.value = false;
-        });
-      });
-    }
-  };
-  //取消上传
-  let handleCancel = () => {
-    visible.value = false;
-  };
-  let list2trees = (data) => {
-    // 删除 所有 children,以防止多次调用
-    data.forEach(function (item) {
-      delete item.children;
-    });
-    // 将数据存储为 以 id 为 KEY 的 map 索引数据列
-    let map = {};
-    data.forEach(function (item) {
-      map[item.id] = item;
-    });
-    var val = [];
-    data.forEach(function (item) {
-      item.isFolder = true;
-      item.title = item.fileName;
-      item.pid = item.parentId;
-      // 以当前遍历项,的pid,去map对象中找到索引的id
-      var parent = map[item.pid];
-      // 好绕啊,如果找到索引,那么说明此项不在顶级当中,那么需要把此项添加到,他对应的父级中
-      if (parent) {
-        (parent.children || (parent.children = [])).push(item);
-      } else {
-        //如果没有在map中找到对应的索引ID,那么直接把 当前的item添加到 val结果集中,作为顶级
-        val.push(item);
-      }
-    });
-    return val;
-  };
-
-  //获取左侧菜单树数据
-  let getTreeList = async () => {
-    listArr.length = 0;
-    let data = await getTree({ parentId: '' });
-    let datas = data.records.filter((v) => v.fileType == null);
-    let list = list2trees(datas);
-    listArr.push(...list);
-    console.log(listArr, '树节点数据');
-    selected.id = listArr[0].id;
-    selected.pid = listArr[0].pid;
-    selected.title = listArr[0].title;
-    selected.isFolder = listArr[0].isFolder;
-  };
-
-  //点击目录
-  const onClick = (node) => {
-    selected = node;
-    if (flag.value != node.title) {
+
+let activeKey = ref('1');
+let selfParam = reactive({
+  //各矿参数
+  sysOrgCode: '',
+  bpmStatus: null,
+  flag: false,
+});
+let router = useRouter(); //路由
+const { createMessage } = useMessage();
+let fileName = ref('');
+let fileList = reactive<any[]>([]); //上传文件列表
+// let uploadParam = reactive({}); //上传文件参数
+let nodeParam = reactive({}); //点击树节点时传递的参数
+let alive = ref(true); //点击树节点刷新表格数据
+let visible = ref(false); //控制上传弹窗的显示
+let formState = reactive({
+  //上传文件类型,是否审批
+  isApprove: null,
+  fileType: '',
+});
+
+//lxh 当前选中树节点
+let selected = reactive<any>({
+  id: null,
+  pid: null,
+  title: '',
+  isFolder: false,
+});
+let flag = ref('');
+//左侧菜单列表
+let listArr = reactive<any[]>([]);
+//获取要删除的节点数据
+let delNode = reactive({});
+
+//上传文件
+let openModal = (val) => {
+  formState.isApprove = null;
+  formState.fileType = '';
+  fileList.length = 0;
+  visible.value = val;
+};
+
+//tabs选项切换
+let tabChange = (activeKeyVal) => {
+  activeKey.value = activeKeyVal;
+  if (activeKeyVal == 1) {
+    // nextTick(() => {
+    //   MonitorDataTable.value.setSelectedRowKeys([selectData.deviceID]);
+    // });
+  }
+};
+
+//文件审批状态切换
+let changeRadio = (val) => {
+  formState.isApprove = val.target.value;
+};
+//开始上传
+let handleOk = () => {
+  if (formState.isApprove === null) {
+    createMessage.warning('请选择文件审批状态!');
+  } else {
+    const formData = new FormData();
+    formData.append('file', fileList[0]);
+    formData.append('parentId', selected.id);
+    formData.append('isApprove', formState.isApprove);
+    formData.append('fileType', formState.fileType);
+    uploadApi(formData).then((res) => {
+      console.log(res, '上传返回');
       alive.value = false;
       nextTick(() => {
         alive.value = true;
-        nodeParam = node;
-        flag.value = node.title;
+        visible.value = false;
       });
-    }
-  };
-  // // 拖拽结束
-  // const drop = (node) => {
-  //   console.log(node);
-  // };
-  //添加文件
-  const onAddNode = async (node) => {
-    let data = await createFile({ fileName: node.newName, type: 'FOL', parentId: node.id });
-    console.log(data, '新增文件返回');
-    getTreeList();
-  };
-
-  //  修改名字
-  const onChangeName = (node) => {
-    editMenu({
-      id: node.id,
-      fileName: node.newName,
-      parentId: node.pid,
-    }).then((res) => {
-      getTreeList();
     });
-  };
-  // 删除
-  let onDeltet = (node) => {
-    delNode = { ...node };
-  };
-  //确定删除
-  let confirmDel = () => {
-    if (delNode.pid == 'root') {
-      createMessage.warning('根节点不能被删除!');
-    } else if (delNode.children) {
-      createMessage.warning('该节点无法被删除,请先删除该节点下的子节点!');
+  }
+};
+//取消上传
+let handleCancel = () => {
+  visible.value = false;
+};
+let list2trees = (data) => {
+  // 删除 所有 children,以防止多次调用
+  data.forEach(function (item) {
+    delete item.children;
+  });
+  // 将数据存储为 以 id 为 KEY 的 map 索引数据列
+  let map = {};
+  data.forEach(function (item) {
+    map[item.id] = item;
+  });
+  var val = [];
+  data.forEach(function (item) {
+    item.isFolder = true;
+    item.title = item.fileName;
+    item.pid = item.parentId;
+    // 以当前遍历项,的pid,去map对象中找到索引的id
+    var parent = map[item.pid];
+    // 好绕啊,如果找到索引,那么说明此项不在顶级当中,那么需要把此项添加到,他对应的父级中
+    if (parent) {
+      (parent.children || (parent.children = [])).push(item);
     } else {
-      delMenu({ id: delNode.id }).then((res) => {
-        console.log(res, '删除文件');
-        getTreeList();
-      });
+      //如果没有在map中找到对应的索引ID,那么直接把 当前的item添加到 val结果集中,作为顶级
+      val.push(item);
     }
-  };
-  //取消删除
-  //查询列表
-  let onSearch = () => {
+  });
+  return val;
+};
+
+let getTypeTableList=async ()=>{
+  let parentId=nodeParam.id || ''
+  let selectFlag=nodeParam.id ? false : true
+  let likeFileName=fileName.value || ''
+  let bpmStatus=selfParam.bpmStatus|| ''
+  let sysOrgCode=selfParam.sysOrgCode|| '' 
+  let res=await getTree({parentId:parentId,selectFlag:selectFlag,likeFileName:likeFileName,bpmStatus:bpmStatus,sysOrgCode:sysOrgCode})
+  console.log(res,'tableList--------------------')
+}
+
+//获取左侧菜单树数据
+let getTreeList = async () => {
+  listArr.length = 0;
+  let data = await getTree({ parentId: '' });
+  let datas = data.records.filter((v) => v.fileType == null);
+  let list = list2trees(datas);
+  listArr.push(...list);
+  console.log(listArr, '树节点数据');
+  selected.id = listArr[0].id;
+  selected.pid = listArr[0].pid;
+  selected.title = listArr[0].title;
+  selected.isFolder = listArr[0].isFolder;
+};
+
+//点击目录
+const onClick = (node) => {
+  selected = node;
+  if (flag.value != node.title) {
     alive.value = false;
     nextTick(() => {
       alive.value = true;
+      nodeParam = node;
+      flag.value = node.title;
     });
-  };
-  //上传文件
-  let beforeUpload = (file) => {
-    console.log(file, '选中文件');
-    fileList.length = 0;
-    let index = file.name.indexOf('.');
-    let name = file.name.substring(index + 1);
-    if (name == 'png' || name == 'jpg' || name == 'gif' || name == 'psd' || name == 'webp') {
-      createMessage.warning('禁止上传图片类型的文件!');
-    } else {
-      fileList.push(file);
-    }
-  };
-  // 文件移除
-  let handleRemove = (file) => {
-    const index = fileList.indexOf(file);
-    const newFileList = fileList.slice();
-    newFileList.splice(index, 1);
-    fileList.length = 0;
-  };
-  watch(
-    () => router.currentRoute.value,
-    (val) => {
-      if (val) {
-        console.log('各矿传参', val);
-        selfParam.bpmStatus = val.query.bpmStatus;
-        selfParam.sysOrgCode = val.query.sysOrgCode;
-        selfParam.flag = val.query.flag;
-      }
-    },
-    { immediate: true }
-  );
-  onMounted(() => {
+  }
+};
+//添加文件
+const onAddNode = async (node) => {
+  let data = await createFile({ fileName: node.newName, type: 'FOL', parentId: node.id });
+  console.log(data, '新增文件返回');
+  getTreeList();
+};
+
+//  修改名字
+const onChangeName = (node) => {
+  editMenu({
+    id: node.id,
+    fileName: node.newName,
+    parentId: node.pid,
+  }).then((res) => {
     getTreeList();
   });
+};
+// 删除
+let onDeltet = (node) => {
+  delNode = { ...node };
+};
+//确定删除
+let confirmDel = () => {
+  if (delNode.pid == 'root') {
+    createMessage.warning('根节点不能被删除!');
+  } else if (delNode.children) {
+    createMessage.warning('该节点无法被删除,请先删除该节点下的子节点!');
+  } else {
+    delMenu({ id: delNode.id }).then((res) => {
+      console.log(res, '删除文件');
+      getTreeList();
+    });
+  }
+};
+
+//查询列表
+let onSearch = () => {
+  alive.value = false;
+  nextTick(() => {
+    alive.value = true;
+  });
+};
+//上传文件
+let beforeUpload = (file) => {
+  console.log(file, '选中文件');
+  fileList.length = 0;
+  let index = file.name.indexOf('.');
+  let name = file.name.substring(index + 1);
+  if (name == 'png' || name == 'jpg' || name == 'gif' || name == 'psd' || name == 'webp') {
+    createMessage.warning('禁止上传图片类型的文件!');
+  } else {
+    fileList.push(file);
+  }
+};
+// 文件移除
+let handleRemove = (file) => {
+  const index = fileList.indexOf(file);
+  const newFileList = fileList.slice();
+  newFileList.splice(index, 1);
+  fileList.length = 0;
+};
+watch(
+  () => router.currentRoute.value,
+  (val) => {
+    console.log('各矿传参', val);
+    selfParam.bpmStatus = val.query.bpmStatus;
+    selfParam.sysOrgCode = val.query.sysOrgCode;
+    selfParam.flag = val.query.flag;
+  },
+  { immediate: true }
+);
+onMounted(() => {
+  getTreeList();
+  getTypeTableList()
+});
 </script>
 
 <style lang="less" scoped>
-  @ventSpace: zxm;
+@ventSpace: zxm;
+
+.file-details {
+  width: calc(100% - 10px);
+  height: calc(100% - 100px);
+  padding: 0px 15px 15px 15px;
+  position: relative;
+  margin-top: 100px;
+  // background: url(../../../../assets/images/files/homes/bd.png) no-repeat center;
+  // background-size: contain;
+
+  &::after {
+    display: block;
+    content: '';
+    height: 200px;
+    width: 100%;
+    position: absolute;
+    background-image: linear-gradient(#2eb2ff05, #2ea2ff00);
+    border-top: 1px solid #2eb2ff20;
+    top: 0px;
+    left: 0px;
+  }
 
-  .file-details {
-    width: calc(100% - 10px);
-    height: calc(100% - 100px);
-    padding: 15px;
+  .content {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    flex-direction: row;
+    justify-content: space-between;
+    align-items: flex-start;
     position: relative;
-    margin-top: 100px;
-    // background: url(../../../../assets/images/files/homes/bd.png) no-repeat center;
-    // background-size: contain;
-
-    &::after {
-      display: block;
-      content: '';
-      height: 200px;
-      width: 100%;
-      position: absolute;
-      background-image: linear-gradient(#2eb2ff05, #2ea2ff00);
-      border-top: 1px solid #2eb2ff20;
-      top: 0px;
-      left: 0px;
-    }
-    .content {
-      width: 100%;
-      height: calc(100% - 30px);
-      display: flex;
-      flex-direction: row;
-      justify-content: space-between;
-      align-items: flex-start;
-      position: relative;
-      z-index: 999;
-
-      .left-box {
-        width: 15%;
-        height: calc(100% - 20px);
-        margin-bottom: 20px;
-        padding: 20px;
-        border: 1px solid #99e8ff66;
-        background: #27546e1a;
-        box-shadow: 0px 0px 20px 7px rgba(145, 233, 254, 0.7) inset;
-        -moz-box-shadow: 0px 0px 20px 7px rgba(145, 233, 254, 0.7) inset;
-        -webkit-box-shadow: 0px 0px 50px 1px rgb(149 235 255 / 5%) inset;
-
-        // lxh
-        .iconfont {
-          color: #fff;
-          font-size: 12px;
-          margin-left: 5px;
-        }
-      }
+    z-index: 999;
 
-      .right-box {
-        width: 85%;
-        height: calc(100% - 20px);
-        padding: 0px 0px 0px 15px;
-        box-sizing: border-box;
-
-        .search {
-          height: 34px;
-          line-height: 34px;
-          margin-bottom: 15px;
-        }
+    .left-box {
+      width: 15%;
+      height: 100%;
+      padding: 20px;
+      border: 1px solid #99e8ff66;
+      background: #27546e1a;
+      box-shadow: 0px 0px 20px 7px rgba(145, 233, 254, 0.7) inset;
+      -moz-box-shadow: 0px 0px 20px 7px rgba(145, 233, 254, 0.7) inset;
+      -webkit-box-shadow: 0px 0px 50px 1px rgb(149 235 255 / 5%) inset;
+
+      // lxh
+      .iconfont {
+        color: #fff;
+        font-size: 12px;
+        margin-left: 5px;
       }
+    }
 
-      .list {
-        height: calc(100% - 49px);
-        position: relative;
-
-        .bd-t {
-          height: 4px;
-          width: 100%;
-          position: absolute;
-          top: 0px;
-          background: url(../../../../assets/images/files/details/lb-b.png) no-repeat;
-          background-size: 100% 100%;
-        }
-
-        .bd-b {
-          height: 4px;
-          width: 100%;
-          position: absolute;
-          bottom: 0px;
-          background: url(../../../../assets/images/files/details/lb-b.png) no-repeat;
-          background-size: 100% 100%;
-        }
+    .right-box {
+      width: 85%;
+      height: 100%;
+      padding: 0px 0px 0px 15px;
+      box-sizing: border-box;
+
+      .search {
+        height: 34px;
+        line-height: 34px;
+        margin-bottom: 15px;
       }
     }
 
-    .zxm-form {
-      padding: 10px !important;
+    .list {
+      height: calc(100% - 49px);
+      position: relative;
     }
   }
 
-  ::v-deep .jeecg-svg-icon {
-    margin-right: 5px;
+  .zxm-form {
+    padding: 10px !important;
   }
+}
 
-  ::v-deep .jeecg-basic-table-form-container {
-    padding: 0px 0px;
-  }
+::v-deep .jeecg-svg-icon {
+  margin-right: 5px;
+}
 
-  ::v-deep .zxm-btn-primary {
-    background-color: transparent;
-    border: none;
-    background: url(../../../../assets/images/files/details/btn.png) no-repeat !important;
-    background-size: 100% 100% !important;
-  }
+::v-deep .jeecg-basic-table-form-container {
+  padding: 0px 0px;
+}
 
-  ::v-deep .zxm-tree-switcher {
-    background: transparent;
-  }
+::v-deep .zxm-btn-primary {
+  background-color: transparent;
+  border: none;
+  background: url(../../../../assets/images/files/details/btn.png) no-repeat !important;
+  background-size: 100% 100% !important;
+}
 
-  ::v-deep .zxm-input {
-    width: 220px;
-    height: 28px;
-    background: transparent;
-    border: 1px solid #31bccc;
-    color: #fff;
-    margin: 0px 20px;
-    border-radius: 5px;
-  }
+::v-deep .zxm-tree-switcher {
+  background: transparent;
+}
 
-  ::v-deep .zxm-btn-group {
-    margin-right: 25px;
-  }
+::v-deep .zxm-input {
+  width: 220px;
+  height: 28px;
+  background: transparent;
+  border: 1px solid #31bccc;
+  color: #fff;
+  margin: 0px 20px;
+  border-radius: 5px;
+}
 
-  ::v-deep .zxm-upload-list-item-name {
-    color: #fff;
-  }
+::v-deep .zxm-btn-group {
+  margin-right: 25px;
+}
 
-  ::v-deep .zxm-upload-list-item:hover .zxm-upload-list-item-info {
-    background-color: transparent;
-  }
+::v-deep .zxm-upload-list-item-name {
+  color: #fff;
+}
 
-  :deep(.@{ventSpace}-table-cell-row-hover) {
-    background: #264d8833 !important;
-  }
-  :deep(.@{ventSpace}-table-row-selected) {
-    background: #268bc522 !important;
-  }
-  :deep(.@{ventSpace}-select-dropdown) {
-    border: 1px solid #ececec66;
-    // background: #ffffff !important;
-    // left: 0px !important;
-    // backdrop-filter: blur(50px);
-    // background: transparent !important;
-    // backdrop-filter: blur(50px);
-
-    .@{ventSpace}-select-item-option-selected,
-    .@{ventSpace}-select-item-option-active {
-      background-color: #ffffff33 !important;
-    }
+::v-deep .zxm-upload-list-item:hover .zxm-upload-list-item-info {
+  background-color: transparent;
+}
 
-    .@{ventSpace}-select-item:hover {
-      background-color: #ffffff33 !important;
-    }
+:deep(.@{ventSpace}-table-cell-row-hover) {
+  background: #264d8833 !important;
+}
+
+:deep(.@{ventSpace}-table-row-selected) {
+  background: #268bc522 !important;
+}
+
+:deep(.@{ventSpace}-select-dropdown) {
+  border: 1px solid #ececec66;
+  // background: #ffffff !important;
+  // left: 0px !important;
+  // backdrop-filter: blur(50px);
+  // background: transparent !important;
+  // backdrop-filter: blur(50px);
+
+  .@{ventSpace}-select-item-option-selected,
+  .@{ventSpace}-select-item-option-active {
+    background-color: #ffffff33 !important;
   }
 
-  ::v-deep .zxm-form-item-control-input {
-    width: 90%;
+  .@{ventSpace}-select-item:hover {
+    background-color: #ffffff33 !important;
   }
+}
+
+::v-deep .zxm-form-item-control-input {
+  width: 90%;
+}
 </style>

+ 52 - 0
src/views/vent/performance/progressLc/HistorySp.data.ts

@@ -0,0 +1,52 @@
+import { BasicColumn } from '/@/components/Table';
+export const  columns:BasicColumn[]=[
+  {
+    title: '序号',
+    width: 60,
+    align: 'center',
+    customRender: ({ index }: { index: number }) => `${index + 1}`
+  },
+  {
+    title: '当前环节',
+    dataIndex: 'id',
+    key: 'id',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '审批人',
+    dataIndex: 'name',
+    key: 'name',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '申请人',
+    dataIndex: 'applyer',
+    key: 'applyer',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '标识Key',
+    dataIndex: 'key',
+    key: 'key',
+    align: 'center',
+    ellipsis: true,
+  },
+ 
+  {
+    title: '状态',
+    dataIndex: 'isSuspended',
+    key: 'isSuspended',
+    align: 'center',
+    ellipsis: true,
+  },
+//   {
+//     title: '操作',
+//     key: 'action',
+//     width: 350,
+//     slots: { customRender: 'action' },
+//     align: 'center',
+//   },
+]

+ 87 - 0
src/views/vent/performance/progressLc/common/HistorySp.vue

@@ -0,0 +1,87 @@
+<template>
+    <div class="HistorySp">
+        <div class="top-box">
+            <div class="box-title">流程审批进度历史</div>
+            <div class="box-content">
+                <a-table size="small" :dataSource="dataSource" :columns="columns" :scroll="{ y: 730 }"
+                    :pagination="pagination"></a-table>
+            </div>
+        </div>
+        <div class="bottom-box">
+            <div class="box-title">实时流程图</div>
+            <div class="box-content">
+
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive } from 'vue'
+import { columns } from '../HistorySp.data'
+
+//数据列表
+let dataSource = ref<any[]>([])
+
+//分页参数配置
+let pagination = reactive({
+    current: 1, // 当前页码
+    pageSize: 10, // 每页显示条数
+    total: 0, // 总条目数,后端返回
+    // showTotal: (total, range) => `${range[0]}-${range[1]} 条,总共 ${total} 条`, // 分页右下角显示信息
+    showSizeChanger: true, // 是否可改变每页显示条数
+    pageSizeOptions: ['10', '20', '50',], // 可选的每页显示条数
+})
+
+</script>
+
+<style lang="less" scoped>
+.HistorySp {
+    position: relative;
+    width: 100%;
+    height: 100%;
+
+
+    .top-box {
+        width: 98%;
+        height: 400px;
+        margin: 15px auto;
+        // background-color: rgba(255, 255, 255, 0.0666666667);
+        box-shadow: 0 0 20px rgba(68, 180, 255, 0.2) inset;
+        border-radius: 2px;
+        border: 1px solid rgba(68, 211, 255, 0.4392156863) !important;
+        padding: 10px;
+        box-sizing: border-box;
+
+        .box-content {
+            height: calc(100% - 40px);
+        }
+    }
+
+    .bottom-box {
+        width: 98%;
+        height: 200px;
+        margin: 15px auto;
+        // background-color: rgba(255, 255, 255, 0.0666666667);
+        box-shadow: 0 0 20px rgba(68, 180, 255, 0.2) inset;
+        border-radius: 2px;
+        border: 1px solid rgba(68, 211, 255, 0.4392156863) !important;
+        padding: 10px;
+        box-sizing: border-box;
+
+        .box-content {
+            height: calc(100% - 40px);
+            background: url('../../../../../assets/images/test1.png') no-repeat center;
+            background-position: 320% 135%;
+        }
+    }
+
+    .box-title {
+        font-size: 14px;
+        height: 40px;
+        line-height: 40px;
+        color: #fff;
+    }
+
+}
+</style>

+ 161 - 0
src/views/vent/performance/progressLc/index.vue

@@ -0,0 +1,161 @@
+<template>
+    <div class="progressLc">
+        <div class="search-box">
+            <a-form :model="formSearch" layout="inline" :label-col="labelCol">
+                <a-form-item label="流程名称:">
+                    <a-input style="width:240px" placeholder="请输入..." v-model:value="formSearch.name" />
+                </a-form-item>
+                <a-form-item label="标识Key:">
+                    <a-input style="width:240px" placeholder="请输入..." v-model:value="formSearch.key" />
+                </a-form-item>
+                <a-form-item label="">
+                    <a-button style="margin-right:10px" type="primary"
+                        preIcon="ant-design:search-outlined">查询</a-button>
+                    <a-button type="primary">重置</a-button>
+                </a-form-item>
+            </a-form>
+        </div>
+        <div class="content-box">
+            <a-table size="small" :dataSource="dataSource" :columns="columns" :scroll="{ y: 730 }"
+                :pagination="pagination">
+
+                <template #action="{ record }">
+                    <a-button type="link" style="color:#3DF6FF" @click="getClick({ event: 'gq', row: record })">
+                        挂起
+                    </a-button>
+                    <a-button type="link" style="color:#3DF6FF" @click="getClick({ event: 'sp', row: record })">
+                        审批详情
+                    </a-button>
+                    <a-button type="link" style="color:#3DF6FF">
+                        表单数据
+                    </a-button>
+                    <a-button type="link" style="color:#3DF6FF">
+                        删除
+                    </a-button>
+                </template>
+            </a-table>
+        </div>
+        <!-- 审批详情弹窗 -->
+        <a-modal v-model:visible="visibleSp" width="1000px" :title="titleSp" centered destroyOnClose>
+           <HistorySp :historySpList="historySpList"></HistorySp>
+        </a-modal>
+    </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, onMounted, createVNode } from 'vue'
+import HistorySp from './common/HistorySp.vue'
+import { Modal } from 'ant-design-vue';
+import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
+import { columns } from './progressLc.data'
+import { getRunningProcess,getHistoricFlow } from './progressLc.api'
+
+const labelCol = { style: { width: '80px' } };
+//查询参数
+let formSearch = reactive({
+    name: '',
+    key: ''
+})
+//数据列表
+let dataSource = ref<any[]>([])
+
+//分页参数配置
+let pagination = reactive({
+    current: 1, // 当前页码
+    pageSize: 10, // 每页显示条数
+    total: 0, // 总条目数,后端返回
+    // showTotal: (total, range) => `${range[0]}-${range[1]} 条,总共 ${total} 条`, // 分页右下角显示信息
+    showSizeChanger: true, // 是否可改变每页显示条数
+    pageSizeOptions: ['10', '20', '50',], // 可选的每页显示条数
+})
+
+//审批详情弹窗数据
+let visibleSp = ref(false)
+let titleSp = ref('审批历史')
+//审批详情历史数据
+let historySpList=reactive<any[]>([])
+
+
+//审批详情-审批历史列表
+async function getHistoricFlowList(){
+    let res=await getHistoricFlow()
+    console.log(res,'审批历史---------------------')
+    if(res.length!=0){
+        historySpList.length=0
+        res.forEach(el=>{
+            historySpList.push({name:el.name})
+        })
+    }
+}
+
+//获取列表
+async function getRunningProcessList() {
+    let res = await getRunningProcess({ name: formSearch.name, key: formSearch.key, pageNumber: pagination.current, pageSize: pagination.pageSize })
+    console.log(res, 'tableList--------------')
+    if (res.length != 0) {
+        res.forEach(el => {
+            el.versionC = 'v.1'
+            el.isSuspended = el.isSuspended ? '未激活' : '已激活'
+        })
+        dataSource.value = res
+    }
+}
+
+//table操作
+function getClick(data) {
+    console.log(data, 'data------------------')
+    switch (data.event) {
+        case 'gq':
+            Modal.confirm({
+                title: '确认暂停挂起',
+                centered: true,
+                icon: createVNode(ExclamationCircleOutlined),
+                content: createVNode('div', { style: 'color:#fff' }, `您确认要暂停挂起流程实例${data.row.name}`),
+                okText: '确认',
+                cancelText: '取消',
+                onOk: () => {
+
+
+                    Modal.destroyAll();
+                },
+            });
+            break;
+        case 'sp':
+            visibleSp.value = true
+            break;
+    }
+}
+
+onMounted(() => {
+    getRunningProcessList()
+    getHistoricFlowList()
+})
+</script>
+
+<style lang="less" scoped>
+.progressLc {
+    position: relative;
+    width: 100%;
+    height: 100%;
+    padding: 15px;
+    box-sizing: border-box;
+
+    .search-box {
+        height: 50px;
+    }
+
+    .content-box {
+        height: calc(100% - 40px);
+    }
+}
+
+:deep(.zxm-form-item-label > label) {
+    color: #fff;
+}
+
+:deep(.zxm-input) {
+    color: #fff;
+    background-color: #ffffff00;
+    border: 1px solid #3ad8ff77 !important;
+}
+</style>

+ 18 - 0
src/views/vent/performance/progressLc/progressLc.api.ts

@@ -0,0 +1,18 @@
+import { defHttp } from '/@/utils/http/axios';
+
+enum Api {
+    getHistoricFlow = '/safety/actTask/historicFlow/15015',
+    getRunningProcess = '/safety/actProcessIns/getRunningProcess',
+}
+
+/**
+ * 左侧树接口
+ * @param params
+ */
+export const getRunningProcess = (params) => defHttp.get({ url: Api.getRunningProcess, params });
+
+/**
+ * 审批详情-审批历史
+ * @param params
+ */
+export const getHistoricFlow = () => defHttp.get({ url: Api.getHistoricFlow,});

+ 65 - 0
src/views/vent/performance/progressLc/progressLc.data.ts

@@ -0,0 +1,65 @@
+import { BasicColumn } from '/@/components/Table';
+export const  columns:BasicColumn[]=[
+  {
+    title: '序号',
+    width: 60,
+    align: 'center',
+    customRender: ({ index }: { index: number }) => `${index + 1}`
+  },
+  {
+    title: '流程实例ID',
+    dataIndex: 'id',
+    key: 'id',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '流程名称',
+    dataIndex: 'name',
+    key: 'name',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '申请人',
+    dataIndex: 'applyer',
+    key: 'applyer',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '标识Key',
+    dataIndex: 'key',
+    key: 'key',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '版本',
+    dataIndex: 'versionC',
+    key: 'versionC',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '当前环节',
+    dataIndex: 'currTaskName',
+    key: 'currTaskName',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '状态',
+    dataIndex: 'isSuspended',
+    key: 'isSuspended',
+    align: 'center',
+    ellipsis: true,
+  },
+  {
+    title: '操作',
+    key: 'action',
+    width: 350,
+    slots: { customRender: 'action' },
+    align: 'center',
+  },
+]