CzRger 1 天之前
父节点
当前提交
cdbe55cff8

二进制
src/assets/images/workflow/switch.png


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

@@ -7,7 +7,7 @@
         @click="$emit('onAddNode', { type: key, e: $event })"
       >
         <img :src="value.icon" class="m-1 size-4" />
-        {{ NodeTypeObj[key].title }}
+        {{ NodeTypeObj[key]?.title }}
       </div>
     </template>
   </div>

+ 40 - 10
src/views/workflow/config.ts

@@ -7,21 +7,25 @@ import {
   NodeTypeObj,
 } from '@/views/workflow/types'
 import startNodeDefault from '@/views/workflow/instance/start/default'
-import answerNodeDefault from '@/views/workflow/instance/answer/default'
 // @ts-ignore
 import answerImg from '@/assets/images/workflow/answer.png'
-import ifElseNodeDefault from '@/views/workflow/instance/if-else/default'
+import answerNodeDefault from '@/views/workflow/instance/answer/default'
 // @ts-ignore
 import llmImg from '@/assets/images/workflow/llm.svg'
-import testNodeDefault from '@/views/workflow/instance/test/default'
+import llmNodeDefault from '@/views/workflow/instance/llm/default'
 // @ts-ignore
 import knowledgeImg from '@/assets/images/workflow/knowledge.png'
-import knowledgeNodeDefault from '@/views/workflow/instance/knowledge-retrieval/default'
+import knowledgeNodeDefault from '@/views/workflow/instance/knowledge/default'
 // @ts-ignore
 import ifElseImg from '@/assets/images/workflow/if-else.png'
-import llmNodeDefault from '@/views/workflow/instance/llm/default'
+import ifElseNodeDefault from '@/views/workflow/instance/if-else/default'
+// @ts-ignore
+import switchImg from '@/assets/images/workflow/switch.png'
+import switchNodeDefault from '@/views/workflow/instance/switch/default'
 // @ts-ignore
 import testImg from '@/assets/images/workflow/test.png'
+import testNodeDefault from '@/views/workflow/instance/test/default'
+
 import { defineAsyncComponent } from 'vue'
 
 export const lineStyle = {
@@ -113,12 +117,10 @@ export const nodeSources = {
     isAdd: true,
     icon: knowledgeImg,
     nodeCom: defineAsyncComponent(
-      () =>
-        import('@/views/workflow/instance/knowledge-retrieval/node/index.vue'),
+      () => import('@/views/workflow/instance/knowledge/node/index.vue'),
     ),
     panelCom: defineAsyncComponent(
-      () =>
-        import('@/views/workflow/instance/knowledge-retrieval/panel/index.vue'),
+      () => import('@/views/workflow/instance/knowledge/panel/index.vue'),
     ),
   },
   [NodeType.IfElse]: {
@@ -133,7 +135,6 @@ export const nodeSources = {
           ...ifElseNodeDefault.defaultValue(),
           __ports: <NodePortStruct[]>[
             {
-              id: v4(),
               ...ifElseNodeDefault.caseValue(),
             },
             {
@@ -152,6 +153,35 @@ export const nodeSources = {
       () => import('@/views/workflow/instance/if-else/panel/index.vue'),
     ),
   },
+  [NodeType.Switch]: {
+    defaultValue: () =>
+      <NodeStruct>{
+        x: 0,
+        y: 0,
+        data: <NodeDataStruct>{
+          id: v4(),
+          title: NodeTypeObj[NodeType.Switch].title,
+          desc: '',
+          ...switchNodeDefault.defaultValue(),
+          __ports: <NodePortStruct[]>[
+            {
+              ...switchNodeDefault.caseValue(),
+            },
+            {
+              ...switchNodeDefault.caseValue(),
+            },
+          ],
+        },
+      },
+    isAdd: true,
+    icon: switchImg,
+    nodeCom: defineAsyncComponent(
+      () => import('@/views/workflow/instance/switch/node/index.vue'),
+    ),
+    panelCom: defineAsyncComponent(
+      () => import('@/views/workflow/instance/switch/panel/index.vue'),
+    ),
+  },
   [NodeType.Test]: {
     defaultValue: () =>
       <NodeStruct>{

+ 2 - 2
src/views/workflow/handle.ts

@@ -221,7 +221,7 @@ export const handleNodeSubmit = (no) => {
         //   delete no.sysVars
         // }
         no.variables = no.__outVars.map((v) => {
-          const obj = {
+          const obj: any = {
             variable: v.key,
             label: v.label,
             type: v.type.toLowerCase(),
@@ -284,7 +284,7 @@ export const handleNodeSubmit = (no) => {
               logical_operator: v.mode,
               conditions:
                 v.cases?.map((c) => {
-                  const cObj = {
+                  const cObj: any = {
                     id: c.id,
                     varType: c.source.type.toLowerCase(),
                     variable_selector:

+ 7 - 3
src/views/workflow/index.vue

@@ -40,17 +40,17 @@ import workflowChart from './chart/index.vue'
 import workflowPanel from './chart/panel-index.vue'
 import envVarsPanel from './instance/component/vars/evn-index.vue'
 import { getTeleport } from '@antv/x6-vue-shape'
-import { data0, data1 } from './mockJson'
-import { useWorkflowStore } from '@/stores'
+import { useAppStore, useDictionaryStore, useWorkflowStore } from '@/stores'
 import {
   workflowDraftDetail,
   workflowDraftSave,
 } from '@/api/modules/workflow/chart'
 import { ElLoading, ElMessage } from 'element-plus'
 import { debounce } from 'lodash'
-import { YMDHms } from '@/utils/czr-util'
 import { handleNodeSubmit } from '@/views/workflow/handle'
 
+const DictionaryStore = useDictionaryStore()
+const AppStore = useAppStore()
 const TeleportContainer = getTeleport()
 const WorkflowStore = useWorkflowStore()
 const emit = defineEmits(['autoSave'])
@@ -180,7 +180,11 @@ const formatEdges = (arr) => {
 }
 onMounted(() => {
   initData()
+  initDictionary()
 })
+const initDictionary = () => {
+  DictionaryStore.initKnowledges(AppStore.tenantInfo?.id)
+}
 </script>
 
 <style lang="scss" scoped>

+ 5 - 1
src/views/workflow/instance/if-else/default.ts

@@ -1,8 +1,12 @@
 import { ConditionMode } from '@/views/workflow/types'
+import { v4 } from 'uuid'
 
 const nodeDefault = {
-  defaultValue: () => ({}),
+  defaultValue: () => ({
+    cases: [], // handle字段
+  }),
   caseValue: () => ({
+    id: v4(),
     mode: ConditionMode.And,
     cases: [],
   }),

+ 0 - 1
src/views/workflow/instance/if-else/panel/index.vue

@@ -106,7 +106,6 @@ watch(
 )
 const onAddCase = () => {
   const data = {
-    id: v4(),
     ...ifElseNodeDefault.caseValue(),
   }
   props.node.addPort({

src/views/workflow/instance/knowledge-retrieval/default.ts → src/views/workflow/instance/knowledge/default.ts


+ 1 - 3
src/views/workflow/instance/knowledge-retrieval/node/index.vue

@@ -28,10 +28,9 @@ import {
   reactive,
   ref,
 } from 'vue'
-import { useAppStore, useDictionaryStore } from '@/stores'
+import { useDictionaryStore } from '@/stores'
 
 const DictionaryStore = useDictionaryStore()
-const AppStore = useAppStore()
 const emit = defineEmits([])
 const props = defineProps({
   node: <any>{},
@@ -42,7 +41,6 @@ const state: any = reactive({
 })
 onMounted(() => {
   state.nodeData = props.node.data.workflowData
-  DictionaryStore.initKnowledges(AppStore.tenantInfo?.id)
 })
 </script>
 

+ 1 - 6
src/views/workflow/instance/knowledge-retrieval/panel/index.vue

@@ -81,12 +81,11 @@ import { getCurrentInstance, reactive, ref, watch } from 'vue'
 import varsOut from '@/views/workflow/instance/component/vars/vars-out.vue'
 import SvgIcon from '@/components/SvgIcon/index.vue'
 import varsSelect from '@/views/workflow/instance/component/vars/vars-select.vue'
-import { useAppStore, useDictionaryStore } from '@/stores'
+import { useDictionaryStore } from '@/stores'
 import knowledgeSelect from '@/views/manage/app/make/knowledge-select.vue'
 import recallConfig from '@/views/manage/knowledge/recall-config.vue'
 
 const DictionaryStore = useDictionaryStore()
-const AppStore = useAppStore()
 const emit = defineEmits([])
 const props = defineProps({
   node: <any>{},
@@ -103,15 +102,11 @@ const state: any = reactive({
     transfer: {},
   },
 })
-const initDictionary = () => {
-  DictionaryStore.initKnowledges(AppStore.tenantInfo?.id)
-}
 watch(
   () => props.node,
   (n) => {
     if (n) {
       state.nodeData = n.data.workflowData
-      initDictionary()
     }
   },
   { immediate: true },

+ 10 - 8
src/views/workflow/instance/llm/panel/index.vue

@@ -161,14 +161,16 @@ const state: any = reactive({
   llmModelOptions: [],
 })
 const initDictionary = () => {
-  pluginGetInstanceList({
-    page: 1,
-    size: 100000,
-    modeType: 'LLM',
-    status: 1,
-  }).then(({ data }: any) => {
-    state.llmModelOptions = data.records
-  })
+  if (state.llmModelOptions.length === 0) {
+    pluginGetInstanceList({
+      page: 1,
+      size: 100000,
+      modeType: 'LLM',
+      status: 1,
+    }).then(({ data }: any) => {
+      state.llmModelOptions = data.records
+    })
+  }
 }
 watch(
   () => props.node,

+ 30 - 0
src/views/workflow/instance/switch/default.ts

@@ -0,0 +1,30 @@
+import { v4 } from 'uuid'
+
+const nodeDefault = {
+  defaultValue: () => ({
+    __queryVars: {},
+    query_variable_selector: ['1758534167012', 'sys.query'],
+    modelConfig: {
+      id: v4(),
+      pluginInstanceId: '',
+      pluginInstanceName: '',
+      paramConfigs: {},
+      bizConfigs: {},
+    },
+    classes: [], // handle字段
+    memory: {
+      window: {
+        enabled: true,
+        size: 68,
+      },
+      query_prompt_template: '{{#sys.query#}}',
+    },
+    instruction: '单独{{#llm.text#}}{{#sys.dialogue_count#}}',
+  }),
+  caseValue: () => ({
+    id: v4(),
+    name: '',
+  }),
+}
+
+export default nodeDefault

+ 61 - 0
src/views/workflow/instance/switch/node/index.vue

@@ -0,0 +1,61 @@
+<template>
+  <div class="node-if-else my-2 flex flex-col gap-2">
+    <!--    <template v-for="(port, index) in state.nodeData.__ports">-->
+    <!--      <div class="break-all" :id="port.id">-->
+    <!--        <div class="port-item-head flex items-center justify-between">-->
+    <!--          <template v-if="index === 0">-->
+    <!--            <span>CASE {{ index + 1 }}</span>-->
+    <!--            <span>IF</span>-->
+    <!--          </template>-->
+    <!--          <template v-else-if="index === state.nodeData.__ports.length - 1">-->
+    <!--            <span></span>-->
+    <!--            <span>ELSE</span>-->
+    <!--          </template>-->
+    <!--          <template v-else>-->
+    <!--            <span>CASE {{ index + 1 }}</span>-->
+    <!--            <span>ELSE IF</span>-->
+    <!--          </template>-->
+    <!--        </div>-->
+    <!--        <div v-if="index < state.nodeData.__ports.length - 1">-->
+    <!--          <conditionNode :mode="port.mode" :conditions="port.cases" />-->
+    <!--        </div>-->
+    <!--      </div>-->
+    <!--    </template>-->
+  </div>
+</template>
+
+<script setup lang="ts">
+import {
+  computed,
+  getCurrentInstance,
+  inject,
+  onMounted,
+  reactive,
+  ref,
+} from 'vue'
+import conditionNode from '@/views/workflow/instance/component/condition/condition-node.vue'
+
+const emit = defineEmits([])
+const props = defineProps({
+  node: <any>{},
+})
+const { proxy }: any = getCurrentInstance()
+const state: any = reactive({
+  nodeData: {},
+})
+onMounted(() => {
+  state.nodeData = props.node.data.workflowData
+})
+</script>
+
+<style lang="scss" scoped>
+.port-item-head {
+  > span:first-child {
+    font-size: 10px;
+    opacity: 0.65;
+  }
+  > span:last-child {
+    font-size: 12px;
+  }
+}
+</style>

+ 96 - 0
src/views/workflow/instance/switch/panel/index.vue

@@ -0,0 +1,96 @@
+<template>
+  <div class="panel-block" v-if="state.nodeData"></div>
+</template>
+
+<script setup lang="ts">
+import { getCurrentInstance, inject, reactive, ref, watch } from 'vue'
+import { v4 } from 'uuid'
+import { useWorkflowStore } from '@/stores'
+import ifElseNodeDefault from '../default'
+import varsValue from '@/views/workflow/instance/component/vars/vars-value.vue'
+import varsPopover from '@/views/workflow/instance/component/vars/vars-popover.vue'
+import conditionIndex from '@/views/workflow/instance/component/condition/condition-index.vue'
+import {
+  ConditionNumber,
+  ConditionString,
+  ConditionType,
+} from '@/views/workflow/types'
+import CzrButton from '@/components/czr-ui/CzrButton.vue'
+
+const WorkflowStore = useWorkflowStore()
+const emit = defineEmits(['reLayoutPort'])
+const props = defineProps({
+  node: <any>{},
+})
+const { proxy }: any = getCurrentInstance()
+const state: any = reactive({
+  nodeData: null,
+})
+watch(
+  () => props.node,
+  (n) => {
+    if (n) {
+      state.nodeData = n.data.workflowData
+    }
+  },
+  { immediate: true },
+)
+watch(
+  () => state.nodeData,
+  (n) => {
+    n.__ports.forEach((p) => {
+      WorkflowStore.layoutPort(props.node.id, p.id)
+    })
+  },
+  { deep: true },
+)
+const onAddCase = () => {
+  const data = {
+    id: v4(),
+    ...ifElseNodeDefault.caseValue(),
+  }
+  props.node.addPort({
+    id: data.id,
+    group: 'more',
+    args: {
+      type: 'more',
+      portId: data.id,
+      nodeId: props.node.id,
+      dy: 0,
+    },
+  })
+  state.nodeData.__ports.splice(state.nodeData.__ports.length - 1, 0, data)
+}
+const onAddCondition = (caseIndex, vars) => {
+  state.nodeData.__ports[caseIndex].cases.push({
+    id: v4(),
+    source: vars,
+    method:
+      vars.type === 'Number'
+        ? ConditionNumber.Equals
+        : ConditionString.Includes,
+    type: ConditionType.Constant,
+    target: '',
+  })
+}
+</script>
+
+<style lang="scss" scoped>
+@use '@/views/workflow/instance/component/style';
+
+.panel-block {
+  .case-list {
+    display: flex;
+    flex-direction: column;
+    .case-item {
+      border-bottom: style.$borderStyle;
+      padding: 10px 0;
+      font-size: 13px;
+      .cast-title {
+        display: flex;
+        align-items: center;
+      }
+    }
+  }
+}
+</style>

+ 12 - 6
src/views/workflow/types.ts

@@ -10,8 +10,9 @@ export enum NodeType {
   LLM = 'llm',
   Answer = 'answer',
   Knowledge = 'knowledge-retrieval',
-  Test = 'test',
   IfElse = 'if-else',
+  Switch = 'question-classifier',
+  Test = 'test',
 }
 
 export const NodeTypeObj = {
@@ -31,14 +32,18 @@ export const NodeTypeObj = {
     type: NodeType.Knowledge,
     title: '知识检索',
   },
-  [NodeType.Test]: {
-    type: NodeType.Test,
-    title: '测试节点',
-  },
   [NodeType.IfElse]: {
     type: NodeType.IfElse,
     title: '条件分支',
   },
+  [NodeType.Switch]: {
+    type: NodeType.Switch,
+    title: '问题分类器',
+  },
+  [NodeType.Test]: {
+    type: NodeType.Test,
+    title: '测试节点',
+  },
 }
 
 export type NodeDataStruct = {
@@ -50,8 +55,9 @@ export type NodeDataStruct = {
     | NodeType.Answer
     | NodeType.LLM
     | NodeType.Knowledge
-    | NodeType.Test
     | NodeType.IfElse
+    | NodeType.Switch
+    | NodeType.Test
   __ports?: NodePortStruct[]
   edgeSource?: string
 }