nanjingliujinyu 5 달 전
부모
커밋
79ce047a6d
3개의 변경된 파일134개의 추가작업 그리고 60개의 파일을 삭제
  1. 2 0
      ruoyi-ui/package.json
  2. 6 0
      ruoyi-ui/src/api/standardization/modeling.js
  3. 126 60
      ruoyi-ui/src/views/standardization/modeling/index.vue

+ 2 - 0
ruoyi-ui/package.json

@@ -19,6 +19,7 @@
   "dependencies": {
     "@element-plus/icons-vue": "2.3.1",
     "@tinyflow-ai/vue": "^1.0.4",
+    "@vue-flow/controls": "^1.1.3",
     "@vue-flow/core": "^1.45.0",
     "@vueup/vue-quill": "1.2.0",
     "@vueuse/core": "10.11.0",
@@ -29,6 +30,7 @@
     "element-plus": "2.7.6",
     "file-saver": "2.0.5",
     "fuse.js": "6.6.2",
+    "html2canvas": "^1.4.1",
     "js-cookie": "3.0.5",
     "jsencrypt": "3.3.2",
     "json-editor-vue3": "^1.1.1",

+ 6 - 0
ruoyi-ui/src/api/standardization/modeling.js

@@ -61,4 +61,10 @@ export function editModelingFlow(data) {
     method: 'put',
     data: data
   })
+}
+export function delFlow(id) {
+  return request({
+    url: '/app/flow/' + id,
+    method: 'delete'
+  })
 }

+ 126 - 60
ruoyi-ui/src/views/standardization/modeling/index.vue

@@ -1,9 +1,10 @@
 <template>
   <div>
     <!-- <el-button type="primary" @click="saveFlow">测试</el-button> -->
-    <div style="display: flex;margin-left: 1%;padding-top: 1%;position: absolute;z-index: 1000;cursor: pointer;justify-content: space-between;width: 98%;"> 
-      <el-icon size="large"  @click="back"><Back /></el-icon>
-      <el-button style="margin-left: auto;" type="danger" size="mini">删除</el-button>
+    <div style="display: flex;margin-left: 1%;padding-top: 0.5%;position: absolute;z-index: 1000;justify-content: space-between;width: 98%;align-items: center;"> 
+      <el-icon size="large" style="cursor: pointer" @click="back"><Back /></el-icon>
+      <el-button @click="delWholeFlow" style="margin-left: auto;" type="danger" size="mini">删除</el-button>
+      <el-button style="margin-left: 1%;" type="info" size="mini" plain @click="toImage">导出为图片</el-button>
       <el-button style="margin-left: 1%;" type="primary" size="mini" @click="saveFlow">保存</el-button>
     </div>
     <div v-if="nodeDeSer" style="height: 85vh;width: 20vw;position: absolute;float: right;z-index: 1000;right: 1%;top: 1%;border: 0.1px solid #dedfe0;border-radius: 6px;background-color: white;">
@@ -61,12 +62,15 @@
           <el-form-item  label="失败重连次数">
             <el-input-number  v-model="form.retryCount" :min="1" style="width: 50%" :max="30"/>
           </el-form-item>
+          <el-form-item  label="输出">
+            <el-input  v-model="form.output" style="width: 100%" type="textarea" />
+          </el-form-item>
         </el-form>
       </div>
       </div>
     </div>
-    <div style="display: flex;height: 85vh;width: 100%;padding-top:2%;">
-      <div style="width: 20%;margin-left: 1%;overflow-y: auto;">
+    <div style="display: flex;height: 87vh;width: 100%;padding-top:2%;justify-items: center;">
+      <div style="width: 20%;margin-left: 1%;overflow-y: auto;margin-top: 0.5%;">
         <el-input
           v-model="inputNode"
           style="width:90%;margin-left: 5%;background-color: #ebeef5;margin-top: 1%;"
@@ -74,7 +78,7 @@
           :prefix-icon="Search" 
         />
         <el-tree :expand-on-click-node="false" ref="treeRef" :filter-node-method="filterNode" :current-node-key="currentNodeKey" class="treeLeft" :data="dataTree" 
-        @node-click="handleNodeClick" node-key="id" style="margin-left: 5%;margin-top: 2%;width: 90%;background-color: transparent;" default-expand-all :key="valueKet">
+        @node-click="handleNodeClick" node-key="id" style="margin-left: 5%;margin-top: 3%;width: 90%;background-color: transparent;" default-expand-all :key="valueKet">
           <template #default="{ node, data }">
             <span  style="justify-content: space-between;display: flex;width: 100%;align-items: center;">
               <div class="custom-tree-node":draggable="true"
@@ -92,72 +96,80 @@
           </template>
         </el-tree>
       </div>
-      <VueFlow :nodes="nodes" :viewport="zoom" :edges="edges" @drop="onDrop" @dragover="onDragOver" @dragleave="onDragLeave"
-        @node-click="onNodeClick"   @connect="onConnect" fit-view-on-init>
-        <template #node-special="specialNodeProps">
-          <div v-if="specialNodeProps.data.label=='开始'" class="vue-flow__node-default" style="border: 0.2px solid #c8c9cc;border-radius: 6px;min-height: 6vh;min-width: 11vw">
-              <div style='width:100%;font-size:10px;display:flex;align-items:flex-end;height: 10px;margin-top: 2%;'>
-                <img style="width: 15px;height:15px;border-radius: 12px;" src="@/assets/images/icon-Start-v2.jpg" alt="">
-                <div style="margin-left: 3%;font-weight: 500;">
-                  开始
+      <div ref="flowContainer" style="width: 80%;">
+        <VueFlow  style="background-color: #EFEFF4;margin-top: 0.5%;" :nodes="nodes" :viewport="zoom" :edges="edges" @drop="onDrop" @dragover="onDragOver" @dragleave="onDragLeave"
+          @node-click="onNodeClick"   @connect="onConnect" fit-view-on-init>
+          <Controls :showInteractive="false" :showFitView="false"/>
+          <template #node-special="specialNodeProps">
+            <div v-if="specialNodeProps.data.label=='开始'" class="vue-flow__node-default" style="border: 0.2px solid #c8c9cc;border-radius: 6px;min-height: 6vh;min-width: 11vw">
+                <div style='width:100%;font-size:10px;display:flex;align-items:flex-end;height: 10px;margin-top: 2%;'>
+                  <img style="width: 15px;height:15px;border-radius: 12px;" src="@/assets/images/icon-Start-v2.jpg" alt="">
+                  <div style="margin-left: 3%;font-weight: 500;">
+                    开始
+                  </div>
+                  <el-tag class="ml-2" style="width: 30px;height: 15px;font-size: 7px;margin-left: 6%;" type="info">触发器</el-tag>
                 </div>
-                <el-tag class="ml-2" style="width: 30px;height: 15px;font-size: 7px;margin-left: 6%;" type="info">触发器</el-tag>
+                  <Handle type="source" :position="Position.Right"/>
               </div>
-                <Handle type="source" :position="Position.Right"/>
-            </div>
-            <div v-if="specialNodeProps.data.label=='结束'" class="vue-flow__node-default" style="border: 0.2px solid #c8c9cc;border-radius: 6px;min-height: 6vh;min-width: 11vw">
-              <div style='width:100%;font-size:10px;display:flex;align-items:flex-end;height: 10px;margin-top: 2%;'>
-                <img style="width: 15px;height:15px;border-radius: 12px;" src="@/assets/images/icon-Start-v2.jpg" alt="">
-                <div style="margin-left: 3%;font-weight: 500;">
-                  结束
-                </div>
-                <div>
+              <div v-if="specialNodeProps.data.label=='结束'" class="vue-flow__node-default" style="border: 0.2px solid #c8c9cc;border-radius: 6px;min-height: 6vh;min-width: 11vw">
+                <div style='width:100%;font-size:10px;display:flex;align-items:flex-end;height: 10px;margin-top: 2%;'>
+                  <img style="width: 15px;height:15px;border-radius: 12px;" src="@/assets/images/icon-Start-v2.jpg" alt="">
+                  <div style="margin-left: 3%;font-weight: 500;">
+                    结束
+                  </div>
+                  <div>
 
+                  </div>
                 </div>
+                  <Handle type="target" :position="Position.Left"/>
               </div>
-                <Handle type="target" :position="Position.Left"/>
-            </div>
-            <div v-if="specialNodeProps.data.label!=='结束'&&specialNodeProps.data.label!=='开始'" class="vue-flow__node-default" 
-            style="border: 0.5px solid #c8c9cc;border-radius: 6px;border-radius: 6px;min-height: 8vh;min-width: 13vw">
-              <div style='width:100%;font-size:10px;display:flex;align-items:flex-end;height: 10px;margin-top: 2%;justify-content: space-between;'>
-                <img style="width: 15px;height:15px;border-radius: 12px;" src="@/assets/images/icon-HTTP.png" alt="">
-                <div style="margin-left:3%;font-weight: 500;">
-                  {{ specialNodeProps.data.label   }}
+              <div v-if="specialNodeProps.data.label!=='结束'&&specialNodeProps.data.label!=='开始'" class="vue-flow__node-default" 
+              style="border: 0.5px solid #c8c9cc;border-radius: 6px;border-radius: 6px;min-height: 8vh;min-width: 13vw">
+                <div style='width:100%;font-size:10px;display:flex;align-items:flex-end;height: 10px;margin-top: 2%;justify-content: space-between;'>
+                  <img style="width: 15px;height:15px;border-radius: 12px;" src="@/assets/images/icon-HTTP.png" alt="">
+                  <div style="margin-left:3%;font-weight: 500;">
+                    {{ specialNodeProps.data.label   }}
+                  </div>
+                  <el-icon style="cursor: pointer;margin-left: auto;"><CaretRight /></el-icon>
+                  <el-icon @click.stop="delNode(specialNodeProps.data)" style="cursor: pointer;color: #F56C6C;margin-left: 2%;"><Delete /></el-icon>
                 </div>
-                <el-icon style="cursor: pointer;margin-left: auto;"><CaretRight /></el-icon>
-                <el-icon @click.stop="delNode(specialNodeProps.data)" style="cursor: pointer;color: #F56C6C;margin-left: 2%;"><Delete /></el-icon>
-              </div>
-              <div style="display: flex;margin-top: 3%;">
-                <el-tag class="ml-2" style="width: 35px;height: 20px;font-size: 10px;" type="warning">服务</el-tag>
-              </div>
-              <div style="display: flex;margin-top: 3%;font-size: 9px;color: #b1b3b8;flex-wrap: wrap;align-items: flex-start;">
-                <div style="word-break: break-all;min-width: 0;width: 100%;text-align: left;">
-                  {{ specialNodeProps.data.service.rqtype+ ':' }}{{ specialNodeProps.data.service.url }}
+                <div style="display: flex;margin-top: 3%;">
+                  <el-tag class="ml-2" style="width: 35px;height: 20px;font-size: 10px;" type="warning">服务</el-tag>
                 </div>
-              </div>
-              <div style="display: flex;margin-top: 3%;font-size: 9px;color: #b1b3b8;align-items: center;">
-                <div>
-                  说明:
+                <div style="display: flex;margin-top: 3%;font-size: 9px;color: #b1b3b8;flex-wrap: wrap;align-items: flex-start;">
+                  <div style="word-break: break-all;min-width: 0;width: 100%;text-align: left;">
+                    {{ specialNodeProps.data.service.rqtype+ ':' }}{{ specialNodeProps.data.service.url }}
+                  </div>
                 </div>
-                <div>
-                  {{ specialNodeProps.data.service.intro }}
+                <div style="display: flex;margin-top: 3%;font-size: 9px;color: #b1b3b8;align-items: center;">
+                  <div>
+                    说明:
+                  </div>
+                  <div>
+                    {{ specialNodeProps.data.service.intro }}
+                  </div>
                 </div>
+                  <Handle type="source" :position="Position.Right"/>
+                  <Handle type="target" :position="Position.Left"/>
               </div>
-                <Handle type="source" :position="Position.Right"/>
-                <Handle type="target" :position="Position.Left"/>
-            </div>
-        </template>
-        <template #edge-custom="specialEdgeProps">
-          <div style="height: 1px;color: red;">
+          </template>
+          <template #edge-custom="specialEdgeProps">
+            <div style="height: 1px;color: red;">
 
-          </div>
-        </template> 
-      </VueFlow>
+            </div>
+          </template> 
+        </VueFlow>
+      </div>
+      
     </div>
 
   </div>
 </template>
 <script setup>
+import html2canvas from 'html2canvas';
+import { Controls } from '@vue-flow/controls'
+import '@vue-flow/controls/dist/style.css'
+import '@vue-flow/core/dist/theme-default.css';
 import { Plus,Search } from '@element-plus/icons-vue'
 import DynamicMap from '@/components/DynamicMap/index.vue'
 import {Promotion} from '@element-plus/icons-vue'
@@ -168,7 +180,7 @@ import SpecialEdge from './components/SpecialEdge.vue'
 import {getPtServiceList,getSerDe} from "@/api/service/info.js";
 import {getModelList2} from "@/api/register/regCom.js";
 import {copyObject} from "@/utils/index.js";
-import {getModelingDe,addModelingFlow,editModelingFlow} from "@/api/standardization/modeling.js";
+import {getModelingDe,addModelingFlow,editModelingFlow,delFlow} from "@/api/standardization/modeling.js";
 import { useStore } from 'vuex';
 import {Handle, Position} from '@vue-flow/core'
 import { computed } from 'vue';
@@ -188,6 +200,8 @@ const {
   getEdges,
 } = useVueFlow()
 snapToGrid.value = true
+const flowContainer = ref(null);
+const status = ref('就绪');
 const zoom = ref();
 const inputNode = ref('');
 const treeRef = ref(null);
@@ -246,9 +260,54 @@ const store = useStore();
 watch(inputNode, (val) => {
   treeRef.value?.filter(val); // 调用树的过滤方法
 });
+function delWholeFlow(){
+  proxy.$confirm('是否删除该模型流程?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        delFlow(parFlowId.value).then(res => {
+            if(res.code === 200){
+                proxy.$message({
+                    message: '删除成功',
+                    type: 'success'
+                });
+                getList();
+            } else {
+                proxy.$message.error('删除失败');
+            }
+        })
+      })
+}
+const toImage = async () => {
+  console.log(flowContainer.value)
+  await nextTick(); 
+  status.value = '生成图片中...';
+  try {
+    const canvas = await html2canvas(flowContainer.value, {
+      backgroundColor: '#fff', // 白底背景
+      scale: 2, // 提高导出清晰度
+      useCORS: true, // 支持跨域图片
+    });
+    
+    // 生成下载链接
+    const dataUrl = canvas.toDataURL('image/png');
+    const link = document.createElement('a');
+    link.href = dataUrl;
+    link.download = 'vueflow-diagram.png'; // 文件名
+    link.click();
+    
+    status.value = '导出成功!';
+    setTimeout(() => status.value = '就绪', 3000); // 重置状态
+  } catch (error) {
+    status.value = `导出失败: ${error.message}`;
+    console.error('导出错误:', error);
+  }
+};
 function delNode(node){
   console.log(node)
-  nodes.value = nodes.value.filter(item => item.id !== node.id)
+  console.log(nodes.value)
+  nodes.value = nodes.value.filter(item => item.data.label !== node.label)
 }
 onEdgeClick(({ edge }) => {
   console.log(edges.value,edge)
@@ -275,6 +334,7 @@ function saveFlow(){
           type: 'success'
         });
       }
+      getList()
     })
   }
   else{
@@ -292,6 +352,7 @@ function saveFlow(){
           message: '保存成功',
           type: 'success'
         });
+        getList()
       }
     })
   }
@@ -304,9 +365,9 @@ onNodeClick(({event, node}) => {
       servieName.value = res.data.ptService.name
       tableDataCan.value = res.data.list
       serviceUrl.value = res.data.ptService.url
-      dataReturn.value = res.data.returnList
+      nodeDeSer.value = true
     })
-    nodeDeSer.value = true
+    
   }
   
 });
@@ -431,10 +492,15 @@ function getList() {
   getModelingDe(par).then(res => {
     if(res.data.length===0){
       isAdd.value = true
+      nodes.value = [
+        { id: '1', position: { x: -600, y: -300 }, data: { label: '开始' }, type: 'special' },
+        { id: '2', position: { x: 150, y: 100 }, data: { label: '结束' }, type: 'special' },
+      ]
     }
     else{
       isAdd.value = false
       var a = JSON.parse(JSON.parse(JSON.stringify(res.data[0].flowGraph)))
+      console.log(a)
       nodes.value = a.nodes
       edges.value = a.edges
       zoom.value = a.zoom