mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 01:33:05 +00:00
feat: 工作编排
This commit is contained in:
parent
88c6084f63
commit
c986713d84
|
|
@ -1,237 +0,0 @@
|
|||
<template>
|
||||
<div className="workflow-app" id="container"></div>
|
||||
<!-- 辅助工具栏 -->
|
||||
<Control class="workflow-control" v-if="lf" :lf="lf"></Control>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import LogicFlow from '@logicflow/core'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import AppEdge from './common/edge/index'
|
||||
import Control from './common/NodeControl.vue'
|
||||
import '@logicflow/extension/lib/style/index.css'
|
||||
import '@logicflow/core/dist/style/index.css'
|
||||
const nodes: any = import.meta.glob('./nodes/**/index.ts', { eager: true })
|
||||
|
||||
defineOptions({ name: 'WorkFlow' })
|
||||
|
||||
type ShapeItem = {
|
||||
type?: string
|
||||
text?: string
|
||||
icon?: string
|
||||
label?: string
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
properties?: Record<string, any>
|
||||
callback?: (lf: LogicFlow, container?: HTMLElement) => void
|
||||
}
|
||||
|
||||
const graphData = {
|
||||
nodes: [
|
||||
{
|
||||
id: '92a94b25-453d-4a00-aa26-9fed9b487e08',
|
||||
type: 'base-node',
|
||||
x: 180,
|
||||
y: 250,
|
||||
properties: {
|
||||
height: 200,
|
||||
stepName: '基本信息',
|
||||
// input: [{ key: '输入' }],
|
||||
// output: [{ key: '输出' }],
|
||||
node_data: {
|
||||
name: '2222',
|
||||
desc: '',
|
||||
prologue:
|
||||
'您好,我是 MaxKB 小助手,您可以向我提出 MaxKB 使用问题。\n- MaxKB 主要功能有什么?\n- MaxKB 支持哪些大语言模型?\n- MaxKB 支持哪些文档类型?'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd4',
|
||||
type: 'start-node',
|
||||
x: 180,
|
||||
y: 723,
|
||||
properties: {
|
||||
height: 200,
|
||||
stepName: '开始',
|
||||
input: [{ key: '输入' }],
|
||||
output: [{ key: '输出' }]
|
||||
// node_data: { model: 'shanghai', name: '222222' }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd5',
|
||||
type: 'search-dataset-node',
|
||||
x: 600,
|
||||
y: 250,
|
||||
properties: {
|
||||
height: 200,
|
||||
stepName: '知识库检索',
|
||||
input: [{ key: '输入' }],
|
||||
output: [{ key: '输出' }]
|
||||
// node_data: { model: 'shanghai', name: '222222' }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd6',
|
||||
type: 'condition-node',
|
||||
x: 600,
|
||||
y: 850,
|
||||
properties: {
|
||||
height: 200,
|
||||
width: 600,
|
||||
stepName: '判断器',
|
||||
input: [{ key: '输入' }],
|
||||
output: [{ key: '输出' }]
|
||||
// node_data: { model: 'shanghai', name: '222222' }
|
||||
}
|
||||
}
|
||||
]
|
||||
// edges: [
|
||||
// {
|
||||
// id: 'bc7297fa-2409-4c85-9a4d-3d74c9c1e30f',
|
||||
// type: 'app-edge',
|
||||
// sourceNodeId: '92a94b25-453d-4a00-aa26-9fed9b487e08',
|
||||
// targetNodeId: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd4',
|
||||
// startPoint: { x: 230, y: 333.000005 },
|
||||
// endPoint: { x: -97, y: 596.111105 },
|
||||
// properties: {},
|
||||
// pointsList: [
|
||||
// { x: 230, y: 333.000005 },
|
||||
// { x: 340, y: 333.000005 },
|
||||
// { x: -207, y: 596.111105 },
|
||||
// { x: -97, y: 596.111105 }
|
||||
// ],
|
||||
// sourceAnchorId: '92a94b25-453d-4a00-aa26-9fed9b487e08_输出_right',
|
||||
// targetAnchorId: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd4_输入_left'
|
||||
// },
|
||||
// {
|
||||
// id: '9f5740ce-b55e-42d4-90a2-a06f34d6f5ef',
|
||||
// type: 'app-edge',
|
||||
// sourceNodeId: '92a94b25-453d-4a00-aa26-9fed9b487e08',
|
||||
// targetNodeId: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd4',
|
||||
// startPoint: { x: 230, y: 333.000005 },
|
||||
// endPoint: { x: -97, y: 596.111105 },
|
||||
// properties: {},
|
||||
// pointsList: [
|
||||
// { x: 230, y: 333.000005 },
|
||||
// { x: 340, y: 333.000005 },
|
||||
// { x: -207, y: 596.111105 },
|
||||
// { x: -97, y: 596.111105 }
|
||||
// ],
|
||||
// sourceAnchorId: '92a94b25-453d-4a00-aa26-9fed9b487e08_输出_right',
|
||||
// targetAnchorId: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd4_输入_left'
|
||||
// }
|
||||
// ]
|
||||
}
|
||||
const lf = ref()
|
||||
|
||||
onMounted(() => {
|
||||
const container: any = document.querySelector('#container')
|
||||
if (container) {
|
||||
lf.value = new LogicFlow({
|
||||
textEdit: false,
|
||||
background: {
|
||||
backgroundColor: '#f5f6f7'
|
||||
},
|
||||
grid: {
|
||||
size: 10,
|
||||
type: 'dot',
|
||||
config: {
|
||||
color: '#DEE0E3',
|
||||
thickness: 1
|
||||
}
|
||||
},
|
||||
// keyboard: {
|
||||
// enabled: true,
|
||||
// shortcuts: [
|
||||
// {
|
||||
// keys: ['backspace'],
|
||||
// callback: () => {
|
||||
// const elements = lf.value.getSelectElements(true)
|
||||
// if (
|
||||
// (elements.edges && elements.edges.length > 0) ||
|
||||
// (elements.nodes && elements.nodes.length > 0)
|
||||
// ) {
|
||||
// const r = window.confirm('确定要删除吗?')
|
||||
// if (r) {
|
||||
// lf.value.clearSelectElements()
|
||||
// elements.edges.forEach((edge: any) => {
|
||||
// lf.value.deleteEdge(edge.id)
|
||||
// })
|
||||
// elements.nodes.forEach((node: any) => {
|
||||
// lf.value.deleteNode(node.id)
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
isSilentMode: false,
|
||||
container: container
|
||||
})
|
||||
lf.value.setTheme({
|
||||
bezier: {
|
||||
stroke: '#afafaf',
|
||||
strokeWidth: 1
|
||||
}
|
||||
})
|
||||
|
||||
lf.value.batchRegister([...Object.keys(nodes).map((key) => nodes[key].default), AppEdge])
|
||||
lf.value.setDefaultEdgeType('app-edge')
|
||||
|
||||
lf.value.render(graphData)
|
||||
|
||||
// lf.value.translate(0, 0)
|
||||
}
|
||||
})
|
||||
const validate = () => {
|
||||
lf.value.graphModel.nodes.forEach((element: any) => {
|
||||
element?.validate?.()
|
||||
})
|
||||
}
|
||||
const getGraphData = () => {
|
||||
console.log(JSON.stringify(lf.value.getGraphData()))
|
||||
}
|
||||
|
||||
const onmousedown = (shapeItem: ShapeItem) => {
|
||||
if (shapeItem.type) {
|
||||
lf.value.dnd.startDrag({
|
||||
type: shapeItem.type,
|
||||
properties: shapeItem.properties,
|
||||
icon: shapeItem.icon
|
||||
})
|
||||
}
|
||||
if (shapeItem.callback) {
|
||||
shapeItem.callback(lf.value)
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
onmousedown,
|
||||
validate,
|
||||
getGraphData
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.workflow-app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.workflow-control {
|
||||
position: absolute;
|
||||
bottom: 24px;
|
||||
left: 24px;
|
||||
z-index: 2;
|
||||
}
|
||||
// .lf-dnd-text {
|
||||
// width: 200px;
|
||||
// }
|
||||
// .lf-dnd-shape {
|
||||
// height: 50px;
|
||||
// }
|
||||
// .lf-node-selected {
|
||||
// border: 1px solid #000;
|
||||
// }
|
||||
</style>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<el-collapse-transition>
|
||||
<div v-show="showPopover" class="workflow-dropdown-menu border border-r-4">
|
||||
<h5 class="title">基础组件</h5>
|
||||
<template v-for="(item, index) in shapeList" :key="index">
|
||||
<template v-for="(item, index) in menuNodes" :key="index">
|
||||
<div class="workflow-dropdown-item cursor flex p-8-12" @mousedown="onmousedown(item)">
|
||||
<component :is="iconComponent(item.icon)" class="mr-8 mt-4" :size="32" />
|
||||
<div class="pre-line">
|
||||
|
|
@ -37,8 +37,9 @@
|
|||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount, computed } from 'vue'
|
||||
import Workflow from '@/components/workflow/index.vue'
|
||||
import { shapeList, iconComponent } from '@/components/workflow/menu-data'
|
||||
import Workflow from '@/workflow/index.vue'
|
||||
import { menuNodes } from '@/workflow/common/data.ts'
|
||||
import { iconComponent } from '@/workflow/icons/utils.ts'
|
||||
|
||||
const workflowRef = ref()
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// const props = defineProps({
|
||||
// lf: Object || String || null
|
||||
// })
|
||||
</script>
|
||||
<style scoped></style>
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { iconComponent } from '../../menu-data'
|
||||
import { iconComponent } from '../icons/utils'
|
||||
|
||||
const height = ref<{
|
||||
stepContainerHeight: number
|
||||
|
|
@ -51,7 +51,7 @@ class AppNodeModel extends HtmlNodeModel {
|
|||
* 给model自定义添加字段方法
|
||||
*/
|
||||
addField(item: any) {
|
||||
this.properties.fields.unshift(item)
|
||||
this.properties.output.unshift(item)
|
||||
this.setAttributes()
|
||||
// 为了保持节点顶部位置不变,在节点变化后,对节点进行一个位移,位移距离为添加高度的一半。
|
||||
this.move(0, 24 / 2)
|
||||
|
|
@ -1,13 +1,43 @@
|
|||
const icons: any = import.meta.glob('./icons/**.vue', { eager: true })
|
||||
function iconComponent(name: string) {
|
||||
const url = `./icons/${name}.vue`
|
||||
return icons[url]?.default || null
|
||||
}
|
||||
/**
|
||||
* 说明
|
||||
* type 与 nodes 文件对应
|
||||
*/
|
||||
const shapeList = [
|
||||
export const baseNodes = [
|
||||
{
|
||||
id: 'base-node',
|
||||
type: 'base-node',
|
||||
x: 200,
|
||||
y: 270,
|
||||
properties: {
|
||||
height: 200,
|
||||
stepName: '基本信息',
|
||||
node_data: {
|
||||
name: '',
|
||||
desc: '',
|
||||
prologue: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'start-node',
|
||||
type: 'start-node',
|
||||
x: 180,
|
||||
y: 720,
|
||||
properties: {
|
||||
height: 200,
|
||||
stepName: '开始',
|
||||
output: [{ key: '' }],
|
||||
fields: [
|
||||
{
|
||||
label: '用户问题',
|
||||
value: 'question'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
export const menuNodes = [
|
||||
{
|
||||
type: 'ai-chat-node',
|
||||
text: '与 AI 大模型进行对话',
|
||||
|
|
@ -18,12 +48,18 @@ const shapeList = [
|
|||
stepName: 'AI 对话',
|
||||
input: [
|
||||
{
|
||||
key: '输入'
|
||||
key: ''
|
||||
}
|
||||
],
|
||||
output: [
|
||||
{
|
||||
key: '输出'
|
||||
key: ''
|
||||
}
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
label: 'AI 回答内容',
|
||||
value: 'content'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -45,6 +81,16 @@ const shapeList = [
|
|||
{
|
||||
key: '输出'
|
||||
}
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
label: '检索结果',
|
||||
value: 'data'
|
||||
},
|
||||
{
|
||||
label: '满足直接回答的分段内容',
|
||||
value: 'paragraph'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
@ -69,5 +115,3 @@ const shapeList = [
|
|||
}
|
||||
}
|
||||
]
|
||||
|
||||
export { shapeList, iconComponent }
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
const icons: any = import.meta.glob('./**.vue', { eager: true })
|
||||
export function iconComponent(name: string) {
|
||||
const url = `./icons/${name}.vue`
|
||||
return icons[url]?.default || null
|
||||
}
|
||||
|
|
@ -0,0 +1,322 @@
|
|||
<template>
|
||||
<div className="workflow-app" id="container"></div>
|
||||
<!-- 辅助工具栏 -->
|
||||
<Control class="workflow-control" v-if="lf" :lf="lf"></Control>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import LogicFlow from '@logicflow/core'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import AppEdge from './common/edge.ts'
|
||||
import Control from './common/NodeControl.vue'
|
||||
import { baseNodes } from '@/workflow/common/data.ts'
|
||||
import '@logicflow/extension/lib/style/index.css'
|
||||
import '@logicflow/core/dist/style/index.css'
|
||||
const nodes: any = import.meta.glob('./nodes/**/index.ts', { eager: true })
|
||||
|
||||
defineOptions({ name: 'WorkFlow' })
|
||||
|
||||
type ShapeItem = {
|
||||
type?: string
|
||||
text?: string
|
||||
icon?: string
|
||||
label?: string
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
properties?: Record<string, any>
|
||||
callback?: (lf: LogicFlow, container?: HTMLElement) => void
|
||||
}
|
||||
|
||||
const graphData = {
|
||||
nodes: [
|
||||
...baseNodes,
|
||||
{
|
||||
id: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd5',
|
||||
type: 'search-dataset-node',
|
||||
x: 600,
|
||||
y: 250,
|
||||
properties: {
|
||||
height: 200,
|
||||
stepName: '知识库检索',
|
||||
input: [{ key: '输入' }],
|
||||
output: [{ key: '输出' }],
|
||||
node_data: {
|
||||
dataset_id_list: [],
|
||||
dataset_setting: {
|
||||
top_n: 3,
|
||||
similarity: 0.6,
|
||||
max_paragraph_char_number: 5000,
|
||||
search_mode: 'embedding'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd6',
|
||||
type: 'condition-node',
|
||||
x: 810,
|
||||
y: 764,
|
||||
properties: {
|
||||
height: 200,
|
||||
width: 600,
|
||||
stepName: '判断器',
|
||||
input: [{ key: '输入' }],
|
||||
output: [{ key: '9208' }, { key: '1143' }, { key: '输出' }],
|
||||
node_data: {
|
||||
branch: [
|
||||
{
|
||||
conditions: [{ field: { node_id: 'xxx', fields: '' }, compare: '', value: '' }],
|
||||
id: '2391',
|
||||
condition: 'and'
|
||||
},
|
||||
{
|
||||
conditions: [{ field: { node_id: 'xxx', fields: '' }, compare: '', value: '' }],
|
||||
id: '1143',
|
||||
condition: 'and'
|
||||
},
|
||||
{
|
||||
conditions: [{ field: { node_id: 'xxx', fields: '' }, compare: '', value: '' }],
|
||||
id: '9208',
|
||||
condition: 'and'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '03597cb0-ed4c-4bcb-b25b-3b358f72b266',
|
||||
type: 'ai-chat-node',
|
||||
x: 1330,
|
||||
y: 690,
|
||||
properties: {
|
||||
height: '',
|
||||
stepName: 'AI 对话',
|
||||
input: [{ key: '输入' }],
|
||||
output: [{ key: '输出' }],
|
||||
node_data: { model: '', name: '' }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: '6649ee86-348c-4d68-9cad-71f0612beb05',
|
||||
type: 'ai-chat-node',
|
||||
x: 1320,
|
||||
y: 990,
|
||||
properties: {
|
||||
height: '',
|
||||
stepName: 'AI 对话',
|
||||
input: [{ key: '输入' }],
|
||||
output: [{ key: '输出' }],
|
||||
node_data: { model: '', name: '' }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'ede4a9c9-e2fa-40ac-9215-2e1ad04f09c5',
|
||||
type: 'ai-chat-node',
|
||||
x: 1360,
|
||||
y: 1300,
|
||||
properties: {
|
||||
height: '',
|
||||
stepName: 'AI 对话',
|
||||
input: [{ key: '输入' }],
|
||||
output: [{ key: '输出' }],
|
||||
node_data: { model: '', name: '' }
|
||||
}
|
||||
}
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: '8dde4baf-0965-4999-9d37-f867ab16d638',
|
||||
type: 'app-edge',
|
||||
sourceNodeId: 'start-node',
|
||||
targetNodeId: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd5',
|
||||
startPoint: { x: 340, y: 788 },
|
||||
endPoint: { x: 440, y: 469 },
|
||||
properties: {},
|
||||
pointsList: [
|
||||
{ x: 340, y: 788 },
|
||||
{ x: 450, y: 788 },
|
||||
{ x: 330, y: 469 },
|
||||
{ x: 440, y: 469 }
|
||||
],
|
||||
sourceAnchorId: 'start-node_输出_right',
|
||||
targetAnchorId: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd5_输入_left'
|
||||
},
|
||||
{
|
||||
id: 'b60de7b4-d8d2-4e7d-bba6-3738b9e523b9',
|
||||
type: 'app-edge',
|
||||
sourceNodeId: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd5',
|
||||
targetNodeId: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd6',
|
||||
startPoint: { x: 760, y: 464 },
|
||||
endPoint: { x: 520, y: 973.75 },
|
||||
properties: {},
|
||||
pointsList: [
|
||||
{ x: 760, y: 464 },
|
||||
{ x: 870, y: 464 },
|
||||
{ x: 410, y: 973.75 },
|
||||
{ x: 520, y: 973.75 }
|
||||
],
|
||||
sourceAnchorId: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd5_输出_right',
|
||||
targetAnchorId: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd6_输入_left'
|
||||
},
|
||||
{
|
||||
id: '8de0da85-b5d6-459a-8be9-4d00082baf1c',
|
||||
type: 'app-edge',
|
||||
sourceNodeId: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd6',
|
||||
targetNodeId: '03597cb0-ed4c-4bcb-b25b-3b358f72b266',
|
||||
startPoint: { x: 1100, y: 968.75 },
|
||||
endPoint: { x: 1170, y: 803 },
|
||||
properties: {},
|
||||
pointsList: [
|
||||
{ x: 1100, y: 968.75 },
|
||||
{ x: 1210, y: 968.75 },
|
||||
{ x: 1060, y: 803 },
|
||||
{ x: 1170, y: 803 }
|
||||
],
|
||||
sourceAnchorId: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd6_9208_right',
|
||||
targetAnchorId: '03597cb0-ed4c-4bcb-b25b-3b358f72b266_输入_left'
|
||||
},
|
||||
{
|
||||
id: '3e66821a-ce0a-4ef9-a6cf-ea4095158261',
|
||||
type: 'app-edge',
|
||||
sourceNodeId: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd6',
|
||||
targetNodeId: '6649ee86-348c-4d68-9cad-71f0612beb05',
|
||||
startPoint: { x: 1100, y: 992.75 },
|
||||
endPoint: { x: 1160, y: 1103 },
|
||||
properties: {},
|
||||
pointsList: [
|
||||
{ x: 1100, y: 992.75 },
|
||||
{ x: 1210, y: 992.75 },
|
||||
{ x: 1050, y: 1103 },
|
||||
{ x: 1160, y: 1103 }
|
||||
],
|
||||
sourceAnchorId: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd6_1143_right',
|
||||
targetAnchorId: '6649ee86-348c-4d68-9cad-71f0612beb05_输入_left'
|
||||
},
|
||||
{
|
||||
id: 'cc52ab90-58e7-4f54-9660-d9fb16f776ea',
|
||||
type: 'app-edge',
|
||||
sourceNodeId: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd6',
|
||||
targetNodeId: 'ede4a9c9-e2fa-40ac-9215-2e1ad04f09c5',
|
||||
startPoint: { x: 1100, y: 1016.75 },
|
||||
endPoint: { x: 1200, y: 1413 },
|
||||
properties: {},
|
||||
pointsList: [
|
||||
{ x: 1100, y: 1016.75 },
|
||||
{ x: 1210, y: 1016.75 },
|
||||
{ x: 1090, y: 1413 },
|
||||
{ x: 1200, y: 1413 }
|
||||
],
|
||||
sourceAnchorId: '34902d3d-a3ff-497f-b8e1-0c34a44d7dd6_输出_right',
|
||||
targetAnchorId: 'ede4a9c9-e2fa-40ac-9215-2e1ad04f09c5_输入_left'
|
||||
}
|
||||
]
|
||||
}
|
||||
const lf = ref()
|
||||
|
||||
onMounted(() => {
|
||||
const container: any = document.querySelector('#container')
|
||||
if (container) {
|
||||
lf.value = new LogicFlow({
|
||||
textEdit: false,
|
||||
background: {
|
||||
backgroundColor: '#f5f6f7'
|
||||
},
|
||||
grid: {
|
||||
size: 10,
|
||||
type: 'dot',
|
||||
config: {
|
||||
color: '#DEE0E3',
|
||||
thickness: 1
|
||||
}
|
||||
},
|
||||
// keyboard: {
|
||||
// enabled: true,
|
||||
// shortcuts: [
|
||||
// {
|
||||
// keys: ['backspace'],
|
||||
// callback: () => {
|
||||
// const elements = lf.value.getSelectElements(true)
|
||||
// if (
|
||||
// (elements.edges && elements.edges.length > 0) ||
|
||||
// (elements.nodes && elements.nodes.length > 0)
|
||||
// ) {
|
||||
// const r = window.confirm('确定要删除吗?')
|
||||
// if (r) {
|
||||
// lf.value.clearSelectElements()
|
||||
// elements.edges.forEach((edge: any) => {
|
||||
// lf.value.deleteEdge(edge.id)
|
||||
// })
|
||||
// elements.nodes.forEach((node: any) => {
|
||||
// lf.value.deleteNode(node.id)
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
isSilentMode: false,
|
||||
container: container
|
||||
})
|
||||
lf.value.setTheme({
|
||||
bezier: {
|
||||
stroke: '#afafaf',
|
||||
strokeWidth: 1
|
||||
}
|
||||
})
|
||||
|
||||
lf.value.batchRegister([...Object.keys(nodes).map((key) => nodes[key].default), AppEdge])
|
||||
lf.value.setDefaultEdgeType('app-edge')
|
||||
|
||||
lf.value.render(graphData)
|
||||
}
|
||||
})
|
||||
const validate = () => {
|
||||
lf.value.graphModel.nodes.forEach((element: any) => {
|
||||
element?.validate?.()
|
||||
})
|
||||
}
|
||||
const getGraphData = () => {
|
||||
console.log(JSON.stringify(lf.value.getGraphData()))
|
||||
}
|
||||
|
||||
const onmousedown = (shapeItem: ShapeItem) => {
|
||||
if (shapeItem.type) {
|
||||
lf.value.dnd.startDrag({
|
||||
type: shapeItem.type,
|
||||
properties: shapeItem.properties,
|
||||
icon: shapeItem.icon
|
||||
})
|
||||
}
|
||||
if (shapeItem.callback) {
|
||||
shapeItem.callback(lf.value)
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
onmousedown,
|
||||
validate,
|
||||
getGraphData
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.workflow-app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.workflow-control {
|
||||
position: absolute;
|
||||
bottom: 24px;
|
||||
left: 24px;
|
||||
z-index: 2;
|
||||
}
|
||||
// .lf-dnd-text {
|
||||
// width: 200px;
|
||||
// }
|
||||
// .lf-dnd-shape {
|
||||
// height: 50px;
|
||||
// }
|
||||
// .lf-node-selected {
|
||||
// border: 1px solid #000;
|
||||
// }
|
||||
</style>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import ChatNodeVue from './index.vue'
|
||||
import { AppNode, AppNodeModel } from '@/components/workflow/common/app-node/index'
|
||||
import { AppNode, AppNodeModel } from '@/workflow/common/app-node.ts'
|
||||
class ChatNode extends AppNode {
|
||||
constructor(props: any) {
|
||||
super(props, ChatNodeVue)
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
}"
|
||||
prop="name"
|
||||
>
|
||||
<el-input v-model="chat_data.name" @focus="handleFocus" />
|
||||
<el-input v-model="chat_data.name" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
</template>
|
||||
<script setup lang="ts">
|
||||
import { set } from 'lodash'
|
||||
import NodeContainer from '@/components/workflow/common/node-container/index.vue'
|
||||
import NodeContainer from '@/workflow/common/NodeContainer.vue'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
|
||||
|
|
@ -60,9 +60,7 @@ const chat_data = computed({
|
|||
}
|
||||
})
|
||||
const props = defineProps<{ nodeModel: any }>()
|
||||
const handleFocus = () => {
|
||||
props.nodeModel.isSelected = false
|
||||
}
|
||||
|
||||
const aiChatNodeFormRef = ref<FormInstance>()
|
||||
|
||||
const validate = () => {
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import BaseNodeVue from './index.vue'
|
||||
import { AppNode, AppNodeModel } from '@/components/workflow/common/app-node/index'
|
||||
import { AppNode, AppNodeModel } from '@/workflow/common/app-node.ts'
|
||||
class BaseNode extends AppNode {
|
||||
constructor(props: any) {
|
||||
super(props, BaseNodeVue)
|
||||
|
|
@ -23,7 +23,6 @@
|
|||
maxlength="64"
|
||||
placeholder="请输入应用名称"
|
||||
show-word-limit
|
||||
@focus="handleFocus"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="应用描述">
|
||||
|
|
@ -34,7 +33,6 @@
|
|||
type="textarea"
|
||||
maxlength="256"
|
||||
show-word-limit
|
||||
@focus="handleFocus"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="开场白">
|
||||
|
|
@ -51,7 +49,7 @@
|
|||
</template>
|
||||
<script setup lang="ts">
|
||||
import { set } from 'lodash'
|
||||
import NodeContainer from '@/components/workflow/common/node-container/index.vue'
|
||||
import NodeContainer from '@/workflow/common/NodeContainer.vue'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { MdEditor } from 'md-editor-v3'
|
||||
|
|
@ -76,9 +74,6 @@ const chat_data = computed({
|
|||
}
|
||||
})
|
||||
|
||||
const handleFocus = () => {
|
||||
set(props.nodeModel, 'isSelected', false)
|
||||
}
|
||||
const baseNodeFormRef = ref<FormInstance>()
|
||||
|
||||
const validate = () => {
|
||||
|
|
@ -86,7 +81,6 @@ const validate = () => {
|
|||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
set(props.nodeModel, 'validate', validate)
|
||||
})
|
||||
</script>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import ConditioNodeVue from './index.vue'
|
||||
import { AppNode, AppNodeModel } from '@/components/workflow/common/app-node/index'
|
||||
import { AppNode, AppNodeModel } from '@/workflow/common/app-node.ts'
|
||||
class ConditioNode extends AppNode {
|
||||
constructor(props: any) {
|
||||
super(props, ConditioNodeVue)
|
||||
|
|
@ -6,6 +6,10 @@
|
|||
require-asterisk-position="right"
|
||||
label-width="auto"
|
||||
ref="ConditionNodeFormRef"
|
||||
@keydown.stop
|
||||
@submit.prevent
|
||||
@click.stop
|
||||
@mousedown.stop
|
||||
>
|
||||
<template v-for="(item, index) in form_data.branch" :key="index">
|
||||
<el-card shadow="never" class="card-never mb-8" style="--el-card-padding: 12px">
|
||||
|
|
@ -26,7 +30,7 @@
|
|||
v-model="condition.field"
|
||||
placeholder="请选择变量"
|
||||
clearable
|
||||
@visible-change="changeHandle"
|
||||
@visible-change="changeHandle()"
|
||||
>
|
||||
<el-option label="Zone one" value="shanghai" />
|
||||
<el-option label="Zone two" value="beijing" />
|
||||
|
|
@ -80,7 +84,7 @@
|
|||
</template>
|
||||
<script setup lang="ts">
|
||||
import { cloneDeep, set } from 'lodash'
|
||||
import NodeContainer from '@/components/workflow/common/node-container/index.vue'
|
||||
import NodeContainer from '@/workflow/common/NodeContainer.vue'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { randomId } from '@/utils/utils'
|
||||
|
|
@ -118,7 +122,7 @@ const form_data = computed({
|
|||
const ConditionNodeFormRef = ref<FormInstance>()
|
||||
|
||||
function changeHandle() {
|
||||
console.log(props.nodeModel)
|
||||
console.log(props.nodeModel.graphModel.getNodeIncomingNode(props.nodeModel.id))
|
||||
}
|
||||
|
||||
const validate = () => {
|
||||
|
|
@ -137,7 +141,7 @@ const judgeLabel = (index: number) => {
|
|||
|
||||
function addBranch() {
|
||||
const list = cloneDeep(props.nodeModel.properties.node_data.branch)
|
||||
list.push({
|
||||
const obj = {
|
||||
conditions: [
|
||||
{
|
||||
field: { node_id: 'xxx', fields: '' },
|
||||
|
|
@ -147,7 +151,9 @@ function addBranch() {
|
|||
],
|
||||
id: randomId(),
|
||||
condition: 'and'
|
||||
})
|
||||
}
|
||||
list.push(obj)
|
||||
props.nodeModel.addField({ key: obj.id })
|
||||
set(props.nodeModel.properties.node_data, 'branch', list)
|
||||
}
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import SearchDatasetVue from './index.vue'
|
||||
import { AppNode, AppNodeModel } from '@/components/workflow/common/app-node/index'
|
||||
import { AppNode, AppNodeModel } from '@/workflow/common/app-node.ts'
|
||||
class SearchDatasetNode extends AppNode {
|
||||
constructor(props: any) {
|
||||
super(props, SearchDatasetVue)
|
||||
|
|
@ -94,7 +94,7 @@
|
|||
<script setup lang="ts">
|
||||
import { set } from 'lodash'
|
||||
import { app } from '@/main'
|
||||
import NodeContainer from '@/components/workflow/common/node-container/index.vue'
|
||||
import NodeContainer from '@/workflow/common/NodeContainer.vue'
|
||||
import AddDatasetDialog from '@/views/application/components/AddDatasetDialog.vue'
|
||||
import ParamSettingDialog from '@/views/application/components/ParamSettingDialog.vue'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import ChatNodeVue from './index.vue'
|
||||
import { AppNode, AppNodeModel } from '@/components/workflow/common/app-node/index'
|
||||
import { AppNode, AppNodeModel } from '@/workflow/common/app-node.ts'
|
||||
class ChatNode extends AppNode {
|
||||
constructor(props: any) {
|
||||
super(props, ChatNodeVue)
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
<template>
|
||||
<NodeContainer :nodeModel="nodeModel" class="start-node">
|
||||
<h5 class="title-decoration-1 mb-8">全局变量</h5>
|
||||
<div class="border-r-4 p-8-12 mb-8 layout-bg lighter">当前时 {time}</div>
|
||||
<div class="border-r-4 p-8-12 mb-8 layout-bg lighter">当前时间 {time}</div>
|
||||
<h5 class="title-decoration-1 mb-8">参数输出</h5>
|
||||
<div class="border-r-4 p-8-12 mb-8 layout-bg lighter">用户问题 {question}</div>
|
||||
</NodeContainer>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { set } from 'lodash'
|
||||
import NodeContainer from '@/components/workflow/common/node-container/index.vue'
|
||||
import NodeContainer from '@/workflow/common/NodeContainer.vue'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
|
||||
Loading…
Reference in New Issue