Browse Source

条件列表

CzRger 3 months ago
parent
commit
3adeb4a8b0

+ 4 - 4
src/views/workflow/chart/node-add.vue

@@ -19,9 +19,9 @@
         </div>
       </template>
       <div class="node-add">
-        <div class="node-add-item __hover" @click="onAddNode(NodeType.Test)">{{ NodeTypeTitle.Test }}</div>
-        <div class="node-add-item __hover" @click="onAddNode(NodeType.Answer)">{{ NodeTypeTitle.Answer }}</div>
-        <div class="node-add-item __hover" @click="onAddNode(NodeType.IfElse)">{{ NodeTypeTitle.IfElse }}</div>
+        <div class="node-add-item __hover" @click="onAddNode(NodeType.Test)">{{ NodeTypeObj[NodeType.Test].title }}</div>
+        <div class="node-add-item __hover" @click="onAddNode(NodeType.Answer)">{{ NodeTypeObj[NodeType.Answer].title }}</div>
+        <div class="node-add-item __hover" @click="onAddNode(NodeType.IfElse)">{{ NodeTypeObj[NodeType.IfElse].title }}</div>
       </div>
     </ElPopover>
   </template>
@@ -31,7 +31,7 @@
 import {getCurrentInstance, inject, reactive, ref} from "vue";
 import { ElPopover, ElTooltip } from 'element-plus';
 import {getNodeDefault} from "@/views/workflow/config";
-import {NodeType, NodeTypeTitle} from "@/views/workflow/types";
+import {NodeType, NodeTypeObj} from "@/views/workflow/types";
 import {handleEdge, handleNode} from "@/views/workflow/handle";
 
 const emits = defineEmits([])

+ 126 - 0
src/views/workflow/instance/component/condition/index.vue

@@ -0,0 +1,126 @@
+<template>
+  <div class="condition-main">
+    <div class="condition-mode">
+      <div/>
+      <div/>
+      <div class="__hover" @click="$emit('update:mode', mode === ConditionMode.And ? ConditionMode.Or : ConditionMode.And)">{{mode}}</div>
+    </div>
+    <div class="condition-list">
+      <template v-for="(item, index) in conditions">
+        <div class="condition-item">
+          <div class="condition-item-body">
+            <div class="top">
+              <div class="left">
+                <slot name="source" :value="item"/>
+              </div>
+              <selectPopover class="w-[60px]" :popoverWidth="40" v-model:value="item.method" :options="item.type === 'Number' ? OptionsConditionNumber : OptionsConditionString"/>
+            </div>
+            <div class="bottom">
+              <div>变量</div>
+              <div>
+                <slot name="target" :value="item"/>
+              </div>
+            </div>
+          </div>
+          <div class="condition-item-del __hover" @click="$emit('update:conditions', conditions.filter((_, i) => i !== index))">
+            <SvgIcon name="czr_del" size="14" color="#666666"/>
+          </div>
+        </div>
+      </template>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import {getCurrentInstance, reactive} from "vue";
+import {ConditionMode, OptionsConditionNumber, OptionsConditionString} from "@/views/workflow/types";
+import selectPopover from '@/views/workflow/instance/component/select-popover/index.vue'
+
+const emits = defineEmits(['update:mode', 'update:conditions'])
+const props = defineProps({
+  mode: {},
+  conditions: {default: []}
+})
+const {proxy}: any = getCurrentInstance()
+const state: any = reactive({})
+</script>
+
+<style lang="scss" scoped>
+@import "@/views/workflow/instance/component/style";
+
+.condition-main {
+  flex: 1;
+  display: flex;
+  .condition-mode {
+    height: calc(100% - 28px);
+    margin: 14px 6px 14px 0;
+    width: 44px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    position: relative;
+    >div:nth-child(1) {
+      border: $borderStyle;
+      width: 10px;
+      height: 100%;
+      border-radius: 8px 0 0 8px;
+      border-right: none;
+      position: absolute;
+      right: 4px;
+    }
+    >div:nth-child(2) {
+      z-index: 2;
+      position: absolute;
+      right: 0;
+      width: 20px;
+      height: 30px;
+      background-color: #ffffff;
+    }
+    >div:nth-child(3) {
+      z-index: 3;
+      color: var(--czr-main-color);
+      border:  $borderStyle;
+      border-radius: 4px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      width: 32px;
+      height: 22px;
+      background-color: #ffffff;
+      font-size: 12px;
+    }
+  }
+  .condition-list {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    gap: 8px;
+    .condition-item {
+      display: flex;
+      .condition-item-body {
+        border-radius: 8px;
+        background-color: $inputBg;
+        flex: 1;
+        .top {
+          border-bottom: $borderStyle;
+          padding: 6px;
+          display: flex;
+          align-items: center;
+          .left {
+            flex: 1;
+          }
+          .right {
+            width: 40px;
+          }
+        }
+        .bottom {
+          padding: 6px;
+        }
+      }
+      .condition-item-del {
+        margin: 10px 0 0 6px;
+      }
+    }
+  }
+}
+</style>

+ 76 - 0
src/views/workflow/instance/component/select-popover/index.vue

@@ -0,0 +1,76 @@
+<template>
+  <div>
+    <el-popover
+      :visible="state.show"
+      placement="bottom"
+      trigger="click"
+      :popper-style="{
+        padding: 0
+      }"
+      :width="popoverWidth"
+    >
+      <template #reference>
+        <div @click="state.show = !state.show" class="display">
+          <span>
+          {{options.filter(v => v.value === value)[0]?.label || ''}}
+          </span>
+          <SvgIcon name="czr_arrow" size="8" rotate="90"/>
+        </div>
+      </template>
+      <div class="select-popover" :select-popover="true">
+        <template v-for="item in options">
+          <div class="select-popover-item __hover" @click="$emit('update:value', item.value), state.show = false">{{item.label}}</div>
+        </template>
+      </div>
+    </el-popover>
+  </div>
+</template>
+
+<script setup lang="ts">
+import {computed, getCurrentInstance, reactive, ref, watch} from "vue";
+import {domRootHasAttr} from "@/utils/czr-util";
+
+const emits = defineEmits(['update:value'])
+const props = defineProps({
+  options: {default: []},
+  value: {default: ''},
+  popoverWidth: {default: 50},
+})
+const {proxy}: any = getCurrentInstance()
+const state: any = reactive({
+  show: false,
+})
+
+const onMouseDown = (e) => {
+  if (!domRootHasAttr(e.target, 'select-popover')) {
+    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>
+.display {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  >span {
+    flex: 1;
+    text-align: right;
+    font-size: 12px;
+  }
+  .svg-icon {
+    margin-left: 4px;
+    margin-top: 2px;
+  }
+}
+.select-popover {
+  padding: 10px;
+}
+</style>

+ 1 - 0
src/views/workflow/instance/component/style.scss

@@ -1,4 +1,5 @@
 $borderStyle: 1px solid rgba(0, 0, 0, 0.1);
+$inputBg: #c8ceda40;
 
 ._p-title {
   margin: 10px 0;

+ 7 - 5
src/views/workflow/instance/component/vars/vars-select.vue

@@ -3,7 +3,7 @@
     <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">
+        <div class="del" v-if="state.hovering && state.vars && clearable">
           <SvgIcon name="czr_close_1" size="10" color="rgba(0, 0, 0, 0.5)" class="__hover" @click.stop="state.vars = null"/>
         </div>
       </div>
@@ -18,11 +18,13 @@ import varsPopover from './vars-popover.vue'
 
 const emits = defineEmits([])
 const props = defineProps({
-  node: {}
+  node: {},
+  vars: {default: null},
+  clearable: {default: true}
 })
 const {proxy}: any = getCurrentInstance()
 const state: any = reactive({
-  vars: null,
+  vars: props.vars,
   hovering: false
 })
 
@@ -34,13 +36,13 @@ const state: any = reactive({
 .vars-display {
   border: $borderStyle;
   width: 100%;
-  padding: 0 8px;
+  padding: 4px 4px;
   border-radius: 4px;
   font-size: 12px;
-  height: 28px;
   display: flex;
   align-items: center;
   position: relative;
+  background-color: $inputBg;
   .del {
     position: absolute;
     right: 0;

+ 5 - 0
src/views/workflow/instance/component/vars/vars-value.vue

@@ -16,4 +16,9 @@ const state: any = reactive({})
 </script>
 
 <style lang="scss" scoped>
+.vars-value {
+  background-color: #ffffff;
+  border-radius: 4px;
+  padding: 2px 8px;
+}
 </style>

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

@@ -6,12 +6,6 @@ const nodeDefault = {
     mode: 'and',
     cases: []
   },
-  conditionValue: {
-    nodeId: '',
-    type: '',
-    mode: '',
-    key: ''
-  }
 }
 
 export default nodeDefault

+ 19 - 23
src/views/workflow/instance/if-else/panel/index.vue

@@ -7,20 +7,20 @@
             <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 class="mode __hover" @click="port.mode = port.mode === 'and' ? 'or' : 'and'">{{port.mode}}</div>-->
             </div>
-            <varsPopover :node="props.node" class="ml-auto">
+            <varsPopover :node="props.node" @setVars="vars => onAddCondition(index, vars)" 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 class="flex mt-2" v-if="port.cases.length > 0">
+            <condition v-model:mode="port.mode" v-model:conditions="port.cases">
+              <template #source="{value, index: conIndex}">
+                <varsPopover :node="props.node" @setVars="vars => port.cases[conIndex].source = vars" class="ml-auto">
+                  <varsValue :vars="value.source"/>
+                </varsPopover>
+              </template>
+            </condition>
           </div>
         </div>
       </template>
@@ -34,8 +34,10 @@ 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 varsValue from "@/views/workflow/instance/component/vars/vars-value.vue";
 import varsPopover from "@/views/workflow/instance/component/vars/vars-popover.vue";
+import condition from "@/views/workflow/instance/component/condition/index.vue";
+import {ConditionNumber, ConditionString, ConditionType} from "@/views/workflow/types";
 
 const WorkflowStore = useWorkflowStore()
 const emits = defineEmits(['reLayoutPort'])
@@ -73,10 +75,14 @@ const onAddCase = () => {
   })
   state.nodeData.ports.splice(state.nodeData.ports.length - 1, 0, data)
 }
-const onAddCondition = (caseIndex) => {
+const onAddCondition = (caseIndex, vars) => {
+  console.log(vars)
   state.nodeData.ports[caseIndex].cases.push({
     id: v4(),
-    ...ifElseNodeDefault.conditionValue
+    source: vars,
+    method: vars.type === 'Number' ? ConditionNumber.Equals : ConditionString.Includes,
+    type: ConditionType.Constant,
+    target: ''
   })
 }
 </script>
@@ -95,16 +101,6 @@ const onAddCondition = (caseIndex) => {
       .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;
-        }
       }
     }
   }

+ 76 - 5
src/views/workflow/types.ts

@@ -25,9 +25,80 @@ export enum NodeType {
   IfElse = 'if-else',
 }
 
-export enum NodeTypeTitle {
-  Test = '测试节点',
-  Root = '开始',
-  Answer = '直接回复',
-  IfElse = '条件分支',
+export const NodeTypeObj = {
+  [NodeType.Test]: {
+    type: NodeType.Test,
+    title: '测试节点'
+  },
+  [NodeType.Root]: {
+    type: NodeType.Root,
+    title: '开始'
+  },
+  [NodeType.Answer]: {
+    type: NodeType.Answer,
+    title: '直接回复'
+  },
+  [NodeType.IfElse]: {
+    type: NodeType.IfElse,
+    title: '条件分支'
+  },
+}
+
+export enum ConditionString {
+  Includes = 'includes',
+  NotIncludes = 'not includes',
+  StartWith = 'start with',
+  EndWith = 'end with',
+  Equals = 'equals',
+  NotEquals = 'not equals',
+  Empty = 'empty',
+  NotEmpty = 'not empty',
+}
+
+export enum ConditionNumber {
+  Equals = '=',
+  NotEquals = '≠',
+  Big = '>',
+  Small = '<',
+  BigWith = '≥',
+  SmallWith = '≤',
+  Empty = 'empty',
+  NotEmpty = 'not empty',
+}
+
+export const OptionsConditionString = [
+  {value: ConditionString.Includes, label: '包含'},
+  {value: ConditionString.NotIncludes, label: '不包含'},
+  {value: ConditionString.StartWith, label: '开始是'},
+  {value: ConditionString.EndWith, label: '结束是'},
+  {value: ConditionString.Equals, label: '是'},
+  {value: ConditionString.NotEquals, label: '不是'},
+  {value: ConditionString.Empty, label: '为空'},
+  {value: ConditionString.NotEmpty, label: '不为空'},
+]
+
+export const OptionsConditionNumber = [
+  {value: ConditionNumber.Equals, label: ConditionNumber.Equals},
+  {value: ConditionNumber.NotEquals, label: ConditionNumber.NotEquals},
+  {value: ConditionNumber.Big, label: ConditionNumber.Big},
+  {value: ConditionNumber.Small, label: ConditionNumber.Small},
+  {value: ConditionNumber.BigWith, label: ConditionNumber.BigWith},
+  {value: ConditionNumber.SmallWith, label: ConditionNumber.SmallWith},
+  {value: ConditionNumber.Empty, label: '为空'},
+  {value: ConditionNumber.NotEmpty, label: '不为空'},
+]
+
+export enum ConditionType {
+  Constant = 'constant',
+  Variable = 'variable',
+}
+
+export const OptionsConditionType = [
+  {value: ConditionType.Constant, label: '常量'},
+  {value: ConditionType.Variable, label: '变量'},
+]
+
+export enum ConditionMode {
+  And = 'and',
+  Or = 'or',
 }