Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
赵月辉
/
fastgpt-migrated
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Unverified
Commit
c42deab6
authored
Feb 12, 2025
by
heheer
Committed by
GitHub
Feb 12, 2025
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
global variable & interactive node dnd (#3764)
parent
58f715e8
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
379 additions
and
134 deletions
+379
-134
packages/web/components/common/DndDrag/index.tsx
+26
-9
projects/app/src/components/core/app/VariableEdit.tsx
+103
-43
projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodeFormInput/index.tsx
+119
-36
projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodeIfElse/index.tsx
+3
-1
projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodeUserSelect.tsx
+128
-45
No files found.
packages/web/components/common/DndDrag/index.tsx
View file @
c42deab6
import
{
Box
}
from
'@chakra-ui/react'
;
import
{
Box
,
Tbody
}
from
'@chakra-ui/react'
;
import
React
,
{
useState
}
from
'react'
;
import
React
,
{
ReactNode
,
useState
}
from
'react'
;
import
{
import
{
DragDropContext
,
DragDropContext
,
DroppableProps
,
Droppable
,
Droppable
,
DraggableChildrenFn
,
DraggableChildrenFn
,
DragStart
,
DragStart
,
DropResult
DropResult
,
DroppableProvided
,
DroppableStateSnapshot
}
from
'react-beautiful-dnd'
;
}
from
'react-beautiful-dnd'
;
export
*
from
'react-beautiful-dnd'
;
export
*
from
'react-beautiful-dnd'
;
type
Props
<
T
=
any
>
=
{
type
Props
<
T
=
any
>
=
{
onDragEndCb
:
(
result
:
T
[])
=>
void
;
onDragEndCb
:
(
result
:
T
[])
=>
void
;
renderClone
?:
DraggableChildrenFn
;
renderClone
?:
DraggableChildrenFn
;
children
:
DroppableProps
[
'children'
];
children
:
|
((
provided
:
DroppableProvided
,
snapshot
:
DroppableStateSnapshot
)
=>
ReactNode
)
|
ReactNode
;
dataList
:
T
[];
dataList
:
T
[];
isTable
?:
boolean
;
zoom
?:
number
;
};
};
function
DndDrag
<
T
>
({
children
,
renderClone
,
onDragEndCb
,
dataList
}:
Props
<
T
>
)
{
function
DndDrag
<
T
>
({
children
,
renderClone
,
onDragEndCb
,
dataList
,
isTable
=
false
,
zoom
=
1
}:
Props
<
T
>
)
{
const
[
draggingItemHeight
,
setDraggingItemHeight
]
=
useState
(
0
);
const
[
draggingItemHeight
,
setDraggingItemHeight
]
=
useState
(
0
);
const
onDragStart
=
(
start
:
DragStart
)
=>
{
const
onDragStart
=
(
start
:
DragStart
)
=>
{
...
@@ -45,10 +57,15 @@ function DndDrag<T>({ children, renderClone, onDragEndCb, dataList }: Props<T>)
...
@@ -45,10 +57,15 @@ function DndDrag<T>({ children, renderClone, onDragEndCb, dataList }: Props<T>)
<
DragDropContext
onDragStart=
{
onDragStart
}
onDragEnd=
{
onDragEnd
}
>
<
DragDropContext
onDragStart=
{
onDragStart
}
onDragEnd=
{
onDragEnd
}
>
<
Droppable
droppableId=
"droppable"
renderClone=
{
renderClone
}
>
<
Droppable
droppableId=
"droppable"
renderClone=
{
renderClone
}
>
{
(
provided
,
snapshot
)
=>
{
{
(
provided
,
snapshot
)
=>
{
return
(
return
isTable
?
(
<
Tbody
{
...
provided
.
droppableProps
}
ref=
{
provided
.
innerRef
}
>
{
typeof
children
!==
'function'
&&
children
}
{
snapshot
.
isDraggingOver
&&
<
Box
height=
{
`${draggingItemHeight / zoom}px`
}
/>
}
</
Tbody
>
)
:
(
<
Box
{
...
provided
.
droppableProps
}
ref=
{
provided
.
innerRef
}
>
<
Box
{
...
provided
.
droppableProps
}
ref=
{
provided
.
innerRef
}
>
{
children
(
provided
,
snapshot
)
}
{
typeof
children
===
'function'
&&
children
(
provided
,
snapshot
)
}
{
snapshot
.
isDraggingOver
&&
<
Box
height=
{
`${draggingItemHeight}px`
}
/>
}
{
snapshot
.
isDraggingOver
&&
<
Box
height=
{
`${draggingItemHeight
/ zoom
}px`
}
/>
}
</
Box
>
</
Box
>
);
);
}
}
}
}
...
...
projects/app/src/components/core/app/VariableEdit.tsx
View file @
c42deab6
import
React
,
{
useCallback
,
useMemo
,
useState
}
from
'react'
;
import
React
,
{
useCallback
,
useMemo
}
from
'react'
;
import
{
import
{
Box
,
Box
,
Button
,
Button
,
Flex
,
Flex
,
Table
,
Table
,
Thead
,
Thead
,
Tbody
,
Tr
,
Tr
,
Th
,
Th
,
Td
,
Td
,
...
@@ -20,9 +19,8 @@ import {
...
@@ -20,9 +19,8 @@ import {
}
from
'@fastgpt/global/core/workflow/constants'
;
}
from
'@fastgpt/global/core/workflow/constants'
;
import
type
{
VariableItemType
}
from
'@fastgpt/global/core/app/type.d'
;
import
type
{
VariableItemType
}
from
'@fastgpt/global/core/app/type.d'
;
import
MyIcon
from
'@fastgpt/web/components/common/Icon'
;
import
MyIcon
from
'@fastgpt/web/components/common/Icon'
;
import
{
useForm
}
from
'react-hook-form'
;
import
{
useForm
,
UseFormReset
}
from
'react-hook-form'
;
import
{
customAlphabet
}
from
'nanoid'
;
import
{
customAlphabet
}
from
'nanoid'
;
const
nanoid
=
customAlphabet
(
'abcdefghijklmnopqrstuvwxyz1234567890'
,
6
);
import
MyModal
from
'@fastgpt/web/components/common/MyModal'
;
import
MyModal
from
'@fastgpt/web/components/common/MyModal'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
{
useToast
}
from
'@fastgpt/web/hooks/useToast'
;
import
{
useToast
}
from
'@fastgpt/web/hooks/useToast'
;
...
@@ -32,6 +30,14 @@ import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
...
@@ -32,6 +30,14 @@ import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import
QuestionTip
from
'@fastgpt/web/components/common/MyTooltip/QuestionTip'
;
import
QuestionTip
from
'@fastgpt/web/components/common/MyTooltip/QuestionTip'
;
import
InputTypeConfig
from
'@/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodePluginIO/InputTypeConfig'
;
import
InputTypeConfig
from
'@/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodePluginIO/InputTypeConfig'
;
import
MyIconButton
from
'@fastgpt/web/components/common/Icon/button'
;
import
MyIconButton
from
'@fastgpt/web/components/common/Icon/button'
;
import
{
useReactFlow
,
useViewport
}
from
'reactflow'
;
import
DndDrag
,
{
Draggable
,
DraggableProvided
,
DraggableStateSnapshot
}
from
'@fastgpt/web/components/common/DndDrag'
;
const
nanoid
=
customAlphabet
(
'abcdefghijklmnopqrstuvwxyz1234567890'
,
6
);
export
const
defaultVariable
:
VariableItemType
=
{
export
const
defaultVariable
:
VariableItemType
=
{
id
:
nanoid
(),
id
:
nanoid
(),
...
@@ -61,6 +67,7 @@ const VariableEdit = ({
...
@@ -61,6 +67,7 @@ const VariableEdit = ({
})
=>
{
})
=>
{
const
{
t
}
=
useTranslation
();
const
{
t
}
=
useTranslation
();
const
{
toast
}
=
useToast
();
const
{
toast
}
=
useToast
();
const
{
zoom
}
=
useViewport
();
const
form
=
useForm
<
VariableItemType
>
();
const
form
=
useForm
<
VariableItemType
>
();
const
{
setValue
,
reset
,
watch
,
getValues
}
=
form
;
const
{
setValue
,
reset
,
watch
,
getValues
}
=
form
;
...
@@ -166,7 +173,7 @@ const VariableEdit = ({
...
@@ -166,7 +173,7 @@ const VariableEdit = ({
);
);
return
(
return
(
<
Box
>
<
Box
className=
"nodrag"
>
{
/* Row box */
}
{
/* Row box */
}
<
Flex
alignItems=
{
'center'
}
>
<
Flex
alignItems=
{
'center'
}
>
<
MyIcon
name=
{
'core/app/simpleMode/variable'
}
w=
{
'20px'
}
/>
<
MyIcon
name=
{
'core/app/simpleMode/variable'
}
w=
{
'20px'
}
/>
...
@@ -200,48 +207,40 @@ const VariableEdit = ({
...
@@ -200,48 +207,40 @@ const VariableEdit = ({
<
Th
>
{
t
(
'common:common.Operation'
)
}
</
Th
>
<
Th
>
{
t
(
'common:common.Operation'
)
}
</
Th
>
</
Tr
>
</
Tr
>
</
Thead
>
</
Thead
>
<
Tbody
>
<
DndDrag
<
VariableItemType
>
{
formatVariables
.
map
((
item
,
index
)
=>
(
onDragEndCb=
{
(
list
)
=>
{
<
Tr
key=
{
item
.
id
}
>
onChange
(
list
);
<
Td
fontWeight=
{
'medium'
}
>
<
Flex
alignItems=
{
'center'
}
>
<
MyIcon
name=
{
item
.
icon
as
any
}
w=
{
'16px'
}
color=
{
'myGray.400'
}
mr=
{
2
}
/>
{
item
.
key
}
</
Flex
>
</
Td
>
<
Td
>
<
Flex
alignItems=
{
'center'
}
>
{
item
.
required
?
(
<
MyIcon
name=
{
'check'
}
w=
{
'16px'
}
color=
{
'myGray.900'
}
mr=
{
2
}
/>
)
:
(
''
)
}
</
Flex
>
</
Td
>
<
Td
>
<
Flex
>
<
MyIconButton
icon=
{
'common/settingLight'
}
onClick=
{
()
=>
{
const
formattedItem
=
{
...
item
,
list
:
item
.
enums
||
[]
};
reset
(
formattedItem
);
}
}
}
}
dataList=
{
formatVariables
}
renderClone=
{
(
provided
,
snapshot
,
rubric
)
=>
(
<
TableItem
provided=
{
provided
}
snapshot=
{
snapshot
}
item=
{
formatVariables
[
rubric
.
source
.
index
]
}
reset=
{
reset
}
onChange=
{
onChange
}
variables=
{
variables
}
/>
/>
<
MyIconButton
)
}
icon=
{
'delete'
}
isTable
hoverColor=
{
'red.500'
}
zoom=
{
zoom
}
onClick=
{
()
=>
>
onChange
(
variables
.
filter
((
variable
)
=>
variable
.
id
!==
item
.
id
))
{
formatVariables
.
map
((
item
,
index
)
=>
(
}
<
Draggable
key=
{
item
.
id
}
draggableId=
{
item
.
id
}
index=
{
index
}
>
{
(
provided
,
snapshot
)
=>
(
<
TableItem
provided=
{
provided
}
snapshot=
{
snapshot
}
item=
{
item
}
reset=
{
reset
}
onChange=
{
onChange
}
variables=
{
variables
}
key=
{
item
.
id
}
/>
/>
</
Flex
>
)
}
</
Td
>
</
Draggable
>
</
Tr
>
))
}
))
}
</
Tbody
>
</
DndDrag
>
</
Table
>
</
Table
>
</
TableContainer
>
</
TableContainer
>
)
}
)
}
...
@@ -339,4 +338,65 @@ const VariableEdit = ({
...
@@ -339,4 +338,65 @@ const VariableEdit = ({
);
);
};
};
const
TableItem
=
({
provided
,
snapshot
,
item
,
reset
,
onChange
,
variables
}:
{
provided
:
DraggableProvided
;
snapshot
:
DraggableStateSnapshot
;
item
:
VariableItemType
&
{
icon
?:
string
;
};
reset
:
UseFormReset
<
VariableItemType
>
;
onChange
:
(
data
:
VariableItemType
[])
=>
void
;
variables
:
VariableItemType
[];
})
=>
{
return
(
<
Tr
ref=
{
provided
.
innerRef
}
{
...
provided
.
draggableProps
}
{
...
provided
.
dragHandleProps
}
style=
{
{
...
provided
.
draggableProps
.
style
,
opacity
:
snapshot
.
isDragging
?
0.8
:
1
}
}
>
<
Td
fontWeight=
{
'medium'
}
>
<
Flex
alignItems=
{
'center'
}
>
<
MyIcon
name=
{
item
.
icon
as
any
}
w=
{
'16px'
}
color=
{
'myGray.400'
}
mr=
{
1
}
/>
{
item
.
key
}
</
Flex
>
</
Td
>
<
Td
>
<
Flex
alignItems=
{
'center'
}
>
{
item
.
required
?
<
MyIcon
name=
{
'check'
}
w=
{
'16px'
}
color=
{
'myGray.900'
}
mr=
{
2
}
/>
:
''
}
</
Flex
>
</
Td
>
<
Td
>
<
Flex
>
<
MyIconButton
icon=
{
'common/settingLight'
}
onClick=
{
()
=>
{
const
formattedItem
=
{
...
item
,
list
:
item
.
enums
||
[]
};
reset
(
formattedItem
);
}
}
/>
<
MyIconButton
icon=
{
'delete'
}
hoverColor=
{
'red.500'
}
onClick=
{
()
=>
onChange
(
variables
.
filter
((
variable
)
=>
variable
.
id
!==
item
.
id
))
}
/>
</
Flex
>
</
Td
>
</
Tr
>
);
};
export
default
React
.
memo
(
VariableEdit
);
export
default
React
.
memo
(
VariableEdit
);
projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodeFormInput/index.tsx
View file @
c42deab6
import
{
FlowNodeItemType
}
from
'@fastgpt/global/core/workflow/type/node'
;
import
{
FlowNodeItemType
}
from
'@fastgpt/global/core/workflow/type/node'
;
import
React
,
{
useMemo
,
useState
}
from
'react'
;
import
React
,
{
useMemo
,
useState
}
from
'react'
;
import
{
NodeProps
}
from
'reactflow'
;
import
{
NodeProps
,
useViewport
}
from
'reactflow'
;
import
NodeCard
from
'../render/NodeCard'
;
import
NodeCard
from
'../render/NodeCard'
;
import
Container
from
'../../components/Container'
;
import
Container
from
'../../components/Container'
;
import
RenderInput
from
'../render/RenderInput'
;
import
RenderInput
from
'../render/RenderInput'
;
...
@@ -38,11 +38,17 @@ import InputFormEditModal, { defaultFormInput } from './InputFormEditModal';
...
@@ -38,11 +38,17 @@ import InputFormEditModal, { defaultFormInput } from './InputFormEditModal';
import
RenderOutput
from
'../render/RenderOutput'
;
import
RenderOutput
from
'../render/RenderOutput'
;
import
FormLabel
from
'@fastgpt/web/components/common/MyBox/FormLabel'
;
import
FormLabel
from
'@fastgpt/web/components/common/MyBox/FormLabel'
;
import
MyIconButton
from
'@fastgpt/web/components/common/Icon/button'
;
import
MyIconButton
from
'@fastgpt/web/components/common/Icon/button'
;
import
DndDrag
,
{
Draggable
,
DraggableProvided
,
DraggableStateSnapshot
}
from
'@fastgpt/web/components/common/DndDrag'
;
const
NodeFormInput
=
({
data
,
selected
}:
NodeProps
<
FlowNodeItemType
>
)
=>
{
const
NodeFormInput
=
({
data
,
selected
}:
NodeProps
<
FlowNodeItemType
>
)
=>
{
const
{
nodeId
,
inputs
,
outputs
}
=
data
;
const
{
nodeId
,
inputs
,
outputs
}
=
data
;
const
{
t
}
=
useTranslation
();
const
{
t
}
=
useTranslation
();
const
onChangeNode
=
useContextSelector
(
WorkflowContext
,
(
v
)
=>
v
.
onChangeNode
);
const
onChangeNode
=
useContextSelector
(
WorkflowContext
,
(
v
)
=>
v
.
onChangeNode
);
const
{
zoom
}
=
useViewport
();
const
[
editField
,
setEditField
]
=
useState
<
UserInputFormItemType
>
();
const
[
editField
,
setEditField
]
=
useState
<
UserInputFormItemType
>
();
...
@@ -159,53 +165,79 @@ const NodeFormInput = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
...
@@ -159,53 +165,79 @@ const NodeFormInput = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
<
Th
>
{
t
(
'user:operations'
)
}
</
Th
>
<
Th
>
{
t
(
'user:operations'
)
}
</
Th
>
</
Tr
>
</
Tr
>
</
Thead
>
</
Thead
>
<
Tbody
>
<
DndDrag
<
UserInputFormItemType
>
{
inputs
.
map
((
item
,
index
)
=>
{
onDragEndCb=
{
(
list
)
=>
{
const
sortedOutputs
=
[
outputs
[
0
],
...
outputs
.
slice
(
1
).
sort
((
a
,
b
)
=>
{
const
aIndex
=
list
.
findIndex
((
item
)
=>
item
.
key
===
a
.
key
);
const
bIndex
=
list
.
findIndex
((
item
)
=>
item
.
key
===
b
.
key
);
return
aIndex
-
bIndex
;
})
];
onChangeNode
({
nodeId
,
type
:
'updateInput'
,
key
,
value
:
{
...
props
,
key
,
value
:
list
}
});
onChangeNode
({
nodeId
,
type
:
'attr'
,
key
:
'outputs'
,
value
:
sortedOutputs
});
}
}
dataList=
{
inputs
}
renderClone=
{
(
provided
,
snapshot
,
rubric
)
=>
{
const
item
=
inputs
[
rubric
.
source
.
index
];
const
icon
=
FlowNodeInputMap
[
item
.
type
as
FlowNodeInputTypeEnum
]?.
icon
;
const
icon
=
FlowNodeInputMap
[
item
.
type
as
FlowNodeInputTypeEnum
]?.
icon
;
return
(
return
(
<
Tr
key=
{
index
}
>
<
TableItem
<
Td
>
provided=
{
provided
}
<
Flex
alignItems=
{
'center'
}
fontSize=
{
'mini'
}
fontWeight=
{
'medium'
}
>
snapshot=
{
snapshot
}
{
!!
icon
&&
(
item=
{
item
}
<
MyIcon
name=
{
icon
as
any
}
w=
{
'14px'
}
mr=
{
1
}
color=
{
'myGray.400'
}
/>
icon=
{
icon
}
)
}
setEditField=
{
setEditField
}
{
item
.
label
}
onDelete=
{
onDelete
}
</
Flex
>
</
Td
>
<
Td
>
{
item
.
description
||
'-'
}
</
Td
>
<
Td
>
{
item
.
required
?
(
<
Flex
alignItems=
{
'center'
}
>
<
MyIcon
name=
{
'check'
}
w=
{
'16px'
}
color=
{
'myGray.900'
}
mr=
{
2
}
/>
</
Flex
>
)
:
(
'-'
)
}
</
Td
>
<
Td
>
<
Flex
>
<
MyIconButton
icon=
{
'common/settingLight'
}
onClick=
{
()
=>
setEditField
(
item
)
}
/>
/>
<
MyIconButton
);
icon=
{
'delete'
}
}
}
hoverColor=
{
'red.500'
}
isTable
onClick=
{
()
=>
onDelete
(
item
.
key
)
}
zoom=
{
zoom
}
>
{
inputs
.
map
((
item
,
index
)
=>
{
const
icon
=
FlowNodeInputMap
[
item
.
type
as
FlowNodeInputTypeEnum
]?.
icon
;
return
(
<
Draggable
key=
{
item
.
key
}
draggableId=
{
item
.
key
}
index=
{
index
}
>
{
(
provided
,
snapshot
)
=>
(
<
TableItem
provided=
{
provided
}
snapshot=
{
snapshot
}
key=
{
item
.
key
}
item=
{
item
}
icon=
{
icon
}
setEditField=
{
setEditField
}
onDelete=
{
onDelete
}
/>
/>
</
Flex
>
)
}
</
Td
>
</
Draggable
>
</
Tr
>
);
);
})
}
})
}
</
Tbody
>
</
DndDrag
>
</
Table
>
</
Table
>
</
TableContainer
>
</
TableContainer
>
</
Box
>
</
Box
>
);
);
}
}
}),
}),
[
t
,
editField
,
onChangeNode
,
nodeId
,
outputs
]
[
t
,
editField
,
zoom
,
onChangeNode
,
nodeId
,
outputs
]
);
);
return
(
return
(
...
@@ -223,3 +255,54 @@ const NodeFormInput = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
...
@@ -223,3 +255,54 @@ const NodeFormInput = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
};
};
export
default
React
.
memo
(
NodeFormInput
);
export
default
React
.
memo
(
NodeFormInput
);
const
TableItem
=
({
provided
,
snapshot
,
item
,
icon
,
setEditField
,
onDelete
}:
{
provided
:
DraggableProvided
;
snapshot
:
DraggableStateSnapshot
;
item
:
UserInputFormItemType
;
icon
:
string
;
setEditField
:
(
item
:
UserInputFormItemType
)
=>
void
;
onDelete
:
(
valueKey
:
string
)
=>
void
;
})
=>
{
return
(
<
Tr
ref=
{
provided
.
innerRef
}
{
...
provided
.
draggableProps
}
{
...
provided
.
dragHandleProps
}
style=
{
{
...
provided
.
draggableProps
.
style
,
opacity
:
snapshot
.
isDragging
?
0.8
:
1
}
}
>
<
Td
>
<
Flex
alignItems=
{
'center'
}
fontSize=
{
'mini'
}
fontWeight=
{
'medium'
}
whiteSpace=
{
'nowrap'
}
>
{
!!
icon
&&
<
MyIcon
name=
{
icon
as
any
}
w=
{
'14px'
}
mr=
{
1
}
color=
{
'myGray.400'
}
/>
}
{
item
.
label
}
</
Flex
>
</
Td
>
<
Td
>
{
item
.
description
||
'-'
}
</
Td
>
<
Td
>
{
item
.
required
?
(
<
Flex
alignItems=
{
'center'
}
>
<
MyIcon
name=
{
'check'
}
w=
{
'16px'
}
color=
{
'myGray.900'
}
mr=
{
2
}
/>
</
Flex
>
)
:
(
'-'
)
}
</
Td
>
<
Td
>
<
Flex
>
<
MyIconButton
icon=
{
'common/settingLight'
}
onClick=
{
()
=>
setEditField
(
item
)
}
/>
<
MyIconButton
icon=
{
'delete'
}
hoverColor=
{
'red.500'
}
onClick=
{
()
=>
onDelete
(
item
.
key
)
}
/>
</
Flex
>
</
Td
>
</
Tr
>
);
};
projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodeIfElse/index.tsx
View file @
c42deab6
...
@@ -3,7 +3,7 @@ import NodeCard from '../render/NodeCard';
...
@@ -3,7 +3,7 @@ import NodeCard from '../render/NodeCard';
import
{
useTranslation
}
from
'next-i18next'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
{
Box
,
Button
,
Flex
}
from
'@chakra-ui/react'
;
import
{
Box
,
Button
,
Flex
}
from
'@chakra-ui/react'
;
import
{
NodeInputKeyEnum
}
from
'@fastgpt/global/core/workflow/constants'
;
import
{
NodeInputKeyEnum
}
from
'@fastgpt/global/core/workflow/constants'
;
import
{
NodeProps
,
Position
}
from
'reactflow'
;
import
{
NodeProps
,
Position
,
useViewport
}
from
'reactflow'
;
import
{
FlowNodeItemType
}
from
'@fastgpt/global/core/workflow/type/node'
;
import
{
FlowNodeItemType
}
from
'@fastgpt/global/core/workflow/type/node'
;
import
{
IfElseListItemType
}
from
'@fastgpt/global/core/workflow/template/system/ifElse/type'
;
import
{
IfElseListItemType
}
from
'@fastgpt/global/core/workflow/template/system/ifElse/type'
;
import
{
useContextSelector
}
from
'use-context-selector'
;
import
{
useContextSelector
}
from
'use-context-selector'
;
...
@@ -18,6 +18,7 @@ import { IfElseResultEnum } from '@fastgpt/global/core/workflow/template/system/
...
@@ -18,6 +18,7 @@ import { IfElseResultEnum } from '@fastgpt/global/core/workflow/template/system/
const
NodeIfElse
=
({
data
,
selected
}:
NodeProps
<
FlowNodeItemType
>
)
=>
{
const
NodeIfElse
=
({
data
,
selected
}:
NodeProps
<
FlowNodeItemType
>
)
=>
{
const
{
t
}
=
useTranslation
();
const
{
t
}
=
useTranslation
();
const
{
nodeId
,
inputs
=
[]
}
=
data
;
const
{
nodeId
,
inputs
=
[]
}
=
data
;
const
{
zoom
}
=
useViewport
();
const
onChangeNode
=
useContextSelector
(
WorkflowContext
,
(
v
)
=>
v
.
onChangeNode
);
const
onChangeNode
=
useContextSelector
(
WorkflowContext
,
(
v
)
=>
v
.
onChangeNode
);
const
elseHandleId
=
getHandleId
(
nodeId
,
'source'
,
IfElseResultEnum
.
ELSE
);
const
elseHandleId
=
getHandleId
(
nodeId
,
'source'
,
IfElseResultEnum
.
ELSE
);
...
@@ -63,6 +64,7 @@ const NodeIfElse = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
...
@@ -63,6 +64,7 @@ const NodeIfElse = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
nodeId=
{
nodeId
}
nodeId=
{
nodeId
}
/>
/>
)
}
)
}
zoom=
{
zoom
}
>
>
{
(
provided
)
=>
(
{
(
provided
)
=>
(
<
Box
{
...
provided
.
droppableProps
}
ref=
{
provided
.
innerRef
}
>
<
Box
{
...
provided
.
droppableProps
}
ref=
{
provided
.
innerRef
}
>
...
...
projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodeUserSelect.tsx
View file @
c42deab6
import
React
,
{
useMemo
}
from
'react'
;
import
React
,
{
useMemo
}
from
'react'
;
import
{
NodeProps
,
Position
}
from
'reactflow'
;
import
{
NodeProps
,
Position
,
useViewport
}
from
'reactflow'
;
import
{
Box
,
Button
,
HStack
,
Input
}
from
'@chakra-ui/react'
;
import
{
Box
,
Button
,
HStack
,
Input
}
from
'@chakra-ui/react'
;
import
NodeCard
from
'./render/NodeCard'
;
import
NodeCard
from
'./render/NodeCard'
;
import
{
FlowNodeItemType
}
from
'@fastgpt/global/core/workflow/type/node.d'
;
import
{
FlowNodeItemType
}
from
'@fastgpt/global/core/workflow/type/node.d'
;
...
@@ -18,24 +18,142 @@ import { WorkflowContext } from '../../context';
...
@@ -18,24 +18,142 @@ import { WorkflowContext } from '../../context';
import
{
UserSelectOptionItemType
}
from
'@fastgpt/global/core/workflow/template/system/interactive/type'
;
import
{
UserSelectOptionItemType
}
from
'@fastgpt/global/core/workflow/template/system/interactive/type'
;
import
IOTitle
from
'../components/IOTitle'
;
import
IOTitle
from
'../components/IOTitle'
;
import
RenderOutput
from
'./render/RenderOutput'
;
import
RenderOutput
from
'./render/RenderOutput'
;
import
DndDrag
,
{
Draggable
,
DraggableProvided
,
DraggableStateSnapshot
}
from
'@fastgpt/web/components/common/DndDrag'
;
const
NodeUserSelect
=
({
data
,
selected
}:
NodeProps
<
FlowNodeItemType
>
)
=>
{
const
NodeUserSelect
=
({
data
,
selected
}:
NodeProps
<
FlowNodeItemType
>
)
=>
{
const
{
t
}
=
useTranslation
();
const
{
t
}
=
useTranslation
();
const
{
nodeId
,
inputs
,
outputs
}
=
data
;
const
{
nodeId
,
inputs
,
outputs
}
=
data
;
const
onChangeNode
=
useContextSelector
(
WorkflowContext
,
(
v
)
=>
v
.
onChangeNode
);
const
onChangeNode
=
useContextSelector
(
WorkflowContext
,
(
v
)
=>
v
.
onChangeNode
);
const
{
zoom
}
=
useViewport
();
const
CustomComponent
=
useMemo
(
const
CustomComponent
=
useMemo
(
()
=>
({
()
=>
({
[
NodeInputKeyEnum
.
userSelectOptions
]:
({
[
NodeInputKeyEnum
.
userSelectOptions
]:
(
v
:
FlowNodeInputItemType
)
=>
{
key
:
optionKey
,
const
{
key
:
optionKey
,
value
,
...
props
}
=
v
;
value
=
[],
...
props
}:
FlowNodeInputItemType
)
=>
{
const
options
=
value
as
UserSelectOptionItemType
[];
const
options
=
value
as
UserSelectOptionItemType
[];
return
(
return
(
<
Box
>
<
Box
>
<
DndDrag
<
UserSelectOptionItemType
>
onDragEndCb=
{
(
list
)
=>
{
onChangeNode
({
nodeId
,
type
:
'updateInput'
,
key
:
optionKey
,
value
:
{
...
props
,
key
:
optionKey
,
value
:
list
}
});
}
}
dataList=
{
options
}
renderClone=
{
(
provided
,
snapshot
,
rubric
)
=>
(
<
OptionItem
provided=
{
provided
}
snapshot=
{
snapshot
}
item=
{
options
[
rubric
.
source
.
index
]
}
nodeId=
{
nodeId
}
itemValue=
{
v
}
index=
{
rubric
.
source
.
index
}
/>
)
}
zoom=
{
zoom
}
>
{
(
provided
)
=>
(
<
Box
ref=
{
provided
.
innerRef
}
{
...
provided
.
droppableProps
}
>
{
options
.
map
((
item
,
i
)
=>
(
{
options
.
map
((
item
,
i
)
=>
(
<
Box
key=
{
item
.
key
}
mb=
{
4
}
>
<
Draggable
key=
{
item
.
key
}
index=
{
i
}
draggableId=
{
item
.
key
}
>
{
(
provided
,
snapshot
)
=>
(
<
OptionItem
provided=
{
provided
}
snapshot=
{
snapshot
}
item=
{
item
}
nodeId=
{
nodeId
}
itemValue=
{
v
}
index=
{
i
}
key=
{
item
.
key
}
/>
)
}
</
Draggable
>
))
}
</
Box
>
)
}
</
DndDrag
>
<
Button
fontSize=
{
'sm'
}
leftIcon=
{
<
MyIcon
name=
{
'common/addLight'
}
w=
{
4
}
/>
}
onClick=
{
()
=>
{
onChangeNode
({
nodeId
,
type
:
'updateInput'
,
key
:
optionKey
,
value
:
{
...
props
,
key
:
optionKey
,
value
:
options
.
concat
({
value
:
''
,
key
:
getNanoid
()
})
}
});
}
}
>
{
t
(
'common:core.module.Add_option'
)
}
</
Button
>
</
Box
>
);
}
}),
[
nodeId
,
onChangeNode
,
t
,
zoom
]
);
return
(
<
NodeCard
minW=
{
'400px'
}
selected=
{
selected
}
{
...
data
}
>
<
Container
>
<
RenderInput
nodeId=
{
nodeId
}
flowInputList=
{
inputs
}
CustomComponent=
{
CustomComponent
}
/>
</
Container
>
<
Container
>
<
IOTitle
text=
{
t
(
'common:common.Output'
)
}
/>
<
RenderOutput
nodeId=
{
nodeId
}
flowOutputList=
{
outputs
}
/>
</
Container
>
</
NodeCard
>
);
};
export
default
React
.
memo
(
NodeUserSelect
);
const
OptionItem
=
({
provided
,
snapshot
,
item
,
nodeId
,
itemValue
,
index
}:
{
provided
:
DraggableProvided
;
snapshot
:
DraggableStateSnapshot
;
item
:
UserSelectOptionItemType
;
nodeId
:
string
;
itemValue
:
FlowNodeInputItemType
;
index
:
number
;
})
=>
{
const
{
t
}
=
useTranslation
();
const
onChangeNode
=
useContextSelector
(
WorkflowContext
,
(
v
)
=>
v
.
onChangeNode
);
const
{
key
:
optionKey
,
value
,
...
props
}
=
itemValue
;
const
options
=
value
as
UserSelectOptionItemType
[];
return
(
<
Box
mb=
{
4
}
ref=
{
provided
.
innerRef
}
{
...
provided
.
draggableProps
}
{
...
provided
.
dragHandleProps
}
style=
{
{
...
provided
.
draggableProps
.
style
,
opacity
:
snapshot
.
isDragging
?
0.8
:
1
}
}
>
<
HStack
spacing=
{
1
}
>
<
HStack
spacing=
{
1
}
>
<
MyTooltip
label=
{
t
(
'common:common.Delete'
)
}
>
<
MyTooltip
label=
{
t
(
'common:common.Delete'
)
}
>
<
MyIcon
<
MyIcon
...
@@ -60,7 +178,7 @@ const NodeUserSelect = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
...
@@ -60,7 +178,7 @@ const NodeUserSelect = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
/>
/>
</
MyTooltip
>
</
MyTooltip
>
<
Box
color=
{
'myGray.600'
}
fontWeight=
{
'medium'
}
fontSize=
{
'sm'
}
>
<
Box
color=
{
'myGray.600'
}
fontWeight=
{
'medium'
}
fontSize=
{
'sm'
}
>
{
t
(
'common:option'
)
+
(
i
+
1
)
}
{
t
(
'common:option'
)
+
(
index
+
1
)
}
</
Box
>
</
Box
>
</
HStack
>
</
HStack
>
<
Box
position=
{
'relative'
}
mt=
{
1
}
>
<
Box
position=
{
'relative'
}
mt=
{
1
}
>
...
@@ -89,50 +207,15 @@ const NodeUserSelect = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
...
@@ -89,50 +207,15 @@ const NodeUserSelect = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
});
});
}
}
}
}
/>
/>
{
!
snapshot
.
isDragging
&&
(
<
SourceHandle
<
SourceHandle
nodeId=
{
nodeId
}
nodeId=
{
nodeId
}
handleId=
{
getHandleId
(
nodeId
,
'source'
,
item
.
key
)
}
handleId=
{
getHandleId
(
nodeId
,
'source'
,
item
.
key
)
}
position=
{
Position
.
Right
}
position=
{
Position
.
Right
}
translate=
{
[
34
,
0
]
}
translate=
{
[
34
,
0
]
}
/>
/>
)
}
</
Box
>
</
Box
>
</
Box
>
</
Box
>
))
}
<
Button
fontSize=
{
'sm'
}
leftIcon=
{
<
MyIcon
name=
{
'common/addLight'
}
w=
{
4
}
/>
}
onClick=
{
()
=>
{
onChangeNode
({
nodeId
,
type
:
'updateInput'
,
key
:
optionKey
,
value
:
{
...
props
,
key
:
optionKey
,
value
:
options
.
concat
({
value
:
''
,
key
:
getNanoid
()
})
}
});
}
}
>
{
t
(
'common:core.module.Add_option'
)
}
</
Button
>
</
Box
>
);
}
}),
[
nodeId
,
onChangeNode
,
t
]
);
return
(
<
NodeCard
minW=
{
'400px'
}
selected=
{
selected
}
{
...
data
}
>
<
Container
>
<
RenderInput
nodeId=
{
nodeId
}
flowInputList=
{
inputs
}
CustomComponent=
{
CustomComponent
}
/>
</
Container
>
<
Container
>
<
IOTitle
text=
{
t
(
'common:common.Output'
)
}
/>
<
RenderOutput
nodeId=
{
nodeId
}
flowOutputList=
{
outputs
}
/>
</
Container
>
</
NodeCard
>
);
);
};
};
export
default
React
.
memo
(
NodeUserSelect
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment