CzRger 4 mesi fa
parent
commit
aa7ecf2a06

+ 11 - 1
src/utils/czr-util.ts

@@ -649,4 +649,14 @@ export const extractRgbFromRgba = (rgbaString) => {
   return [46, 129, 255];
 }
 
-export const _console = (p) => console.log(p)
+export const _console = (p) => console.log(p)
+
+export const domRootHasAttr = (dom, attr) => {
+  if (!dom) {
+    return false
+  }
+  if (dom.getAttribute(attr)) {
+    return true
+  }
+  return domRootHasAttr(dom.parentElement, attr)
+}

+ 2 - 1
src/views/workflow/chart/context-menu-tool.tsx

@@ -1,6 +1,7 @@
 import {ToolsView} from "@antv/x6";
 import {createApp} from "vue";
 import contextMenuTool from './context-menu-tool.vue'
+import {domRootHasAttr} from "@/utils/czr-util";
 
 let app: any = null
 let timer: any = null;
@@ -40,7 +41,7 @@ class ContextMenuTool extends ToolsView.ToolItem {
 
 
   onMouseDown = (e, bool = false) => {
-    if (bool || !e.target.getAttribute('context-menu-tools')) {
+    if (bool || !domRootHasAttr(e.target, 'context-menu-tools')) {
       timer = window.setTimeout(() => {
         this.toggleContextMenu(false)
       },200)

+ 1 - 1
src/views/workflow/chart/context-menu-tool.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="context-menu-tool" :context-menu-tools="true">
-    <div class="del __hover" :context-menu-tools="true" @click="onDel">删除</div>
+    <div class="del __hover" @click="onDel">删除</div>
   </div>
 </template>
 

+ 105 - 0
src/views/workflow/instance/component/vars/vars-popover.vue

@@ -0,0 +1,105 @@
+<template>
+  <div>
+    <el-popover
+      :visible="state.show"
+      placement="bottom"
+      trigger="click"
+      :width="300"
+      :popper-style="{
+        padding: 0
+      }"
+    >
+      <template #reference>
+        <div @click="state.show = !state.show">
+          <slot name="default"/>
+        </div>
+      </template>
+      <div class="vars-select-block" :vars-select-block="true">
+        <div class="filter">
+          <el-input v-model="state.text" :prefix-icon="Search" placeholder="搜索变量" clearable/>
+        </div>
+        <div class="list">
+          <template v-for="item in optionsCpt">
+            <div class="list-group">
+              <div class="px-2 text-sm mb-1 text-gray-400">{{item.label}}</div>
+              <template v-for="son in item.options">
+                <div class="__hover-bg px-2 py-1" @click="$emit('setVars', son), state.show = false">
+                  <varsItem :item="son"/>
+                </div>
+              </template>
+            </div>
+          </template>
+        </div>
+      </div>
+    </el-popover>
+  </div>
+</template>
+
+<script setup lang="ts">
+import {computed, getCurrentInstance, reactive, ref, watch} from "vue";
+import {useWorkflowStore} from "@/stores";
+import { Search } from '@element-plus/icons-vue'
+import varsItem from './vars-item.vue'
+import {domRootHasAttr} from "@/utils/czr-util";
+
+const WorkflowStore = useWorkflowStore()
+const emits = defineEmits(['setVars'])
+const props = defineProps({
+  node: {},
+})
+const {proxy}: any = getCurrentInstance()
+const state: any = reactive({
+  vars: null,
+  options: [],
+  text: '',
+  show: false
+})
+watch(() => props.node, (n) => {
+  if (n) {
+    state.options = WorkflowStore.getInVars(n)
+  }
+}, {immediate: true})
+const optionsCpt = computed(() => {
+  if (!state.text) {
+    return state.options
+  }
+  return state.options.map(v => {
+    const obj = {...v}
+    obj.options = obj.options.filter(s => s.label.includes(state.text) || s.key.includes(state.text))
+    return obj
+  }).filter(v => v.options.length > 0)
+})
+const onMouseDown = (e) => {
+  if (!domRootHasAttr(e.target, 'vars-select-block')) {
+    state.show = false
+  }
+}
+watch(() => state.show, (n) => {
+  if (n) {
+    document.addEventListener('mousedown', onMouseDown)
+  }  else {
+    document.removeEventListener('mousedown', onMouseDown)
+  }
+}, {immediate: true})
+</script>
+
+<style lang="scss" scoped>
+@import "@/views/workflow/instance/component/style";
+
+.vars-select-block {
+  .filter {
+    padding: 10px;
+    border-bottom: $borderStyle;
+  }
+  .list {
+    padding: 4px;
+    display: flex;
+    flex-direction: column;
+    gap: 6px;
+    .list-group {
+      display: flex;
+      flex-direction: column;
+    }
+  }
+}
+</style>

+ 19 - 66
src/views/workflow/instance/component/vars/vars-select.vue

@@ -1,47 +1,21 @@
 <template>
   <div class="vars-select">
-    <el-popover
-      placement="bottom"
-      trigger="click"
-      :width="400"
-      :popper-style="{
-        padding: 0
-      }"
-    >
-      <template #reference>
-        <div class="vars-display __hover">
-          <varsValue :vars="state.vars"/>
-        </div>
-      </template>
-      <div class="vars-select-block">
-        <div class="filter">
-          <el-input v-model="state.text" :prefix-icon="Search" placeholder="搜索变量" clearable/>
-        </div>
-        <div class="list">
-          <template v-for="item in optionsCpt">
-            <div class="list-group">
-              <div class="px-2 text-sm mb-1 text-gray-400">{{item.label}}</div>
-              <template v-for="son in item.options">
-                <div class="__hover-bg px-2 py-1" @click="state.vars = son">
-                  <varsItem :item="son"/>
-                </div>
-              </template>
-            </div>
-          </template>
+    <varsPopover :node="node" @setVars="val => state.vars = val">
+      <div class="vars-display __hover" @mouseenter="state.hovering = true" @mouseleave="state.hovering = false">
+        <varsValue :vars="state.vars"/>
+        <div class="del" v-if="state.hovering && state.vars">
+          <SvgIcon name="czr_close_1" size="10" color="rgba(0, 0, 0, 0.5)" class="__hover" @click.stop="state.vars = null"/>
         </div>
       </div>
-    </el-popover>
+    </varsPopover>
   </div>
 </template>
 
 <script setup lang="ts">
 import {computed, getCurrentInstance, reactive, ref, watch} from "vue";
-import {useWorkflowStore} from "@/stores";
-import { Search } from '@element-plus/icons-vue'
-import varsItem from './vars-item.vue'
 import varsValue from './vars-value.vue'
+import varsPopover from './vars-popover.vue'
 
-const WorkflowStore = useWorkflowStore()
 const emits = defineEmits([])
 const props = defineProps({
   node: {}
@@ -49,24 +23,9 @@ const props = defineProps({
 const {proxy}: any = getCurrentInstance()
 const state: any = reactive({
   vars: null,
-  options: [],
-  text: ''
-})
-watch(() => props.node, (n) => {
-  if (n) {
-    state.options = WorkflowStore.getInVars(n)
-  }
-}, {immediate: true})
-const optionsCpt = computed(() => {
-  if (!state.text) {
-    return state.options
-  }
-  return state.options.map(v => {
-    const obj = {...v}
-    obj.options = obj.options.filter(s => s.label.includes(state.text) || s.key.includes(state.text))
-    return obj
-  }).filter(v => v.options.length > 0)
+  hovering: false
 })
+
 </script>
 
 <style lang="scss" scoped>
@@ -75,27 +34,21 @@ const optionsCpt = computed(() => {
 .vars-display {
   border: $borderStyle;
   width: 100%;
-  padding: 0 10px;
+  padding: 0 8px;
   border-radius: 4px;
   font-size: 12px;
-  height: 32px;
+  height: 28px;
   display: flex;
   align-items: center;
-}
-.vars-select-block {
-  .filter {
-    padding: 10px;
-    border-bottom: $borderStyle;
-  }
-  .list {
-    padding: 4px;
+  position: relative;
+  .del {
+    position: absolute;
+    right: 0;
+    width: 30px;
+    background-color: #ffffff;
     display: flex;
-    flex-direction: column;
-    gap: 6px;
-    .list-group {
-      display: flex;
-      flex-direction: column;
-    }
+    align-items: center;
+    justify-content: center;
   }
 }
 </style>

+ 17 - 0
src/views/workflow/instance/if-else/default.ts

@@ -0,0 +1,17 @@
+const nodeDefault = {
+  defaultValue: {
+    inVars: [],
+  },
+  caseValue: {
+    mode: 'and',
+    cases: []
+  },
+  conditionValue: {
+    nodeId: '',
+    type: '',
+    mode: '',
+    key: ''
+  }
+}
+
+export default nodeDefault

+ 3 - 1
src/views/workflow/instance/if-else/node/index.vue

@@ -17,7 +17,9 @@
           </template>
         </div>
         <div v-if="index < state.nodeData.ports.length - 1">
-          {{port.data.p1}}
+          <template v-for="con in port.cases">
+            <div>{{con.key}}123</div>
+          </template>
         </div>
       </div>
     </template>

+ 44 - 7
src/views/workflow/instance/if-else/panel/index.vue

@@ -4,13 +4,28 @@
       <template v-for="(port, index) in state.nodeData.ports">
         <div v-if="index < state.nodeData.ports.length - 1" class="case-item">
           <div class="cast-title">
-            <div>{{index === 0 ? 'IF' : 'ELSE IF'}}</div>
-            <div>CASR {{index + 1}}</div>
+            <div class="flex">
+              <div class="w-[60px]">CASR {{index + 1}}</div>
+              <div class="w-[60px]">{{index === 0 ? 'IF' : 'ELSE IF'}}</div>
+              <div class="mode __hover" @click="port.mode = port.mode === 'and' ? 'or' : 'and'">{{port.mode}}</div>
+            </div>
+            <varsPopover :node="props.node" class="ml-auto">
+              <CzrButton type="add" title="添加条件"/>
+            </varsPopover>
+          </div>
+          <div class="case-conditions">
+            <template v-for="con in port.cases">
+              <div class="condition-item">
+                <div>
+                  <vars-select :node="props.node"/>
+                </div>
+              </div>
+            </template>
           </div>
         </div>
       </template>
     </div>
-    <el-button type="primary" @click="onAddPort">添加条件分支</el-button>
+    <CzrButton type="add" title="添加条件分支" @click="onAddCase" class="mt-4"/>
   </div>
 </template>
 
@@ -18,6 +33,9 @@
 import {getCurrentInstance, inject, reactive, ref, watch} from "vue";
 import { v4 } from "uuid";
 import {useWorkflowStore} from "@/stores";
+import ifElseNodeDefault from "../default";
+import varsSelect from "@/views/workflow/instance/component/vars/vars-select.vue";
+import varsPopover from "@/views/workflow/instance/component/vars/vars-popover.vue";
 
 const WorkflowStore = useWorkflowStore()
 const emits = defineEmits(['reLayoutPort'])
@@ -38,12 +56,10 @@ watch(() => state.nodeData, (n) => {
     WorkflowStore.layoutPort(props.node.id, p.id)
   })
 }, {deep: true})
-const onAddPort = () => {
+const onAddCase = () => {
   const data = {
     id: v4(),
-    data: {
-      p1: ''
-    }
+    ...ifElseNodeDefault.caseValue
   }
   props.node.addPort({
     id: data.id,
@@ -57,6 +73,12 @@ const onAddPort = () => {
   })
   state.nodeData.ports.splice(state.nodeData.ports.length - 1, 0, data)
 }
+const onAddCondition = (caseIndex) => {
+  state.nodeData.ports[caseIndex].cases.push({
+    id: v4(),
+    ...ifElseNodeDefault.conditionValue
+  })
+}
 </script>
 
 <style lang="scss" scoped>
@@ -69,6 +91,21 @@ const onAddPort = () => {
     .case-item {
       border-bottom: $borderStyle;
       padding: 10px 0;
+      font-size: 13px;
+      .cast-title {
+        display: flex;
+        align-items: center;
+        .mode {
+          color: var(--czr-main-color);
+          border:  $borderStyle;
+          padding: 0 4px;
+          border-radius: 4px;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          width: 40px;
+        }
+      }
     }
   }
 }

+ 10 - 18
src/views/workflow/mockJson.ts

@@ -47,39 +47,31 @@ export const data1 = {
         "ports": [
           {
             "id": "ce2c26aa-9b40-4e32-858e-38fa4b09718d",
-            "data": {
-              "p1": "条件分支1的桩1"
-            }
+            mode: 'and',
+            cases: []
           },
           {
             "id": "a4d6eebf-3327-4e73-8ddf-4d3d822ecfa4",
-            "data": {
-              "p1": "条件分支1的桩2"
-            }
+            mode: 'and',
+            cases: []
           },
           {
             "id": "7d21639f-866a-46df-9ec2-04016527ccd0",
-            "data": {
-              "p1": ""
-            }
+            mode: 'and',
+            cases: []
           },
           {
             "id": "2ae1788e-b022-44ea-a2d4-98f8eee7cafb",
-            "data": {
-              "p1": ""
-            }
+            mode: 'and',
+            cases: []
           },
           {
             "id": "d67172bc-ace5-43a0-8d30-ce53b7ce6762",
-            "data": {
-              "p1": ""
-            }
+            mode: 'and',
+            cases: []
           },
           {
             "id": "7171167d-873b-4621-9ce1-dd45f9f4c9c3",
-            "data": {
-              "p1": "条件分支1的桩3"
-            }
           }
         ],
         "p1": "条件分支1的的参数1",