feat: 工作编排

This commit is contained in:
wangdan-fit2cloud 2024-06-06 16:36:03 +08:00
parent 88c6084f63
commit c986713d84
25 changed files with 420 additions and 277 deletions

View File

@ -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>

View File

@ -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()

View File

@ -0,0 +1,10 @@
<template>
<div></div>
</template>
<script setup lang="ts">
// const props = defineProps({
// lf: Object || String || null
// })
</script>
<style scoped></style>

View File

@ -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

View File

@ -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)

View File

@ -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 }

View File

@ -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
}

322
ui/src/workflow/index.vue Normal file
View File

@ -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>

View File

@ -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)

View File

@ -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 = () => {

View File

@ -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)

View File

@ -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>

View File

@ -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)

View File

@ -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)
}

View File

@ -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)

View File

@ -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'

View File

@ -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)

View File

@ -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'