Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
D
dify
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ai-tech
dify
Commits
0fb47fed
Commit
0fb47fed
authored
Feb 27, 2024
by
Joel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: add adn update condition
parent
4519c6ab
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
216 additions
and
100 deletions
+216
-100
add-button.tsx
...components/workflow/nodes/_base/components/add-button.tsx
+28
-0
field.tsx
web/app/components/workflow/nodes/_base/components/field.tsx
+2
-2
condition-item.tsx
...ents/workflow/nodes/if-else/components/condition-item.tsx
+113
-0
condition-list.tsx
...ents/workflow/nodes/if-else/components/condition-list.tsx
+28
-86
panel.tsx
web/app/components/workflow/nodes/if-else/panel.tsx
+25
-6
use-config.ts
web/app/components/workflow/nodes/if-else/use-config.ts
+12
-6
workflow.ts
web/i18n/en-US/workflow.ts
+4
-0
workflow.ts
web/i18n/zh-Hans/workflow.ts
+4
-0
No files found.
web/app/components/workflow/nodes/_base/components/add-button.tsx
0 → 100644
View file @
0fb47fed
'use client'
import
type
{
FC
}
from
'react'
import
React
from
'react'
import
cn
from
'classnames'
import
{
Plus
}
from
'@/app/components/base/icons/src/vender/line/general'
type
Props
=
{
className
?:
string
text
:
string
onClick
:
()
=>
void
}
const
AddButton
:
FC
<
Props
>
=
({
className
,
text
,
onClick
,
})
=>
{
return
(
<
div
className=
{
cn
(
className
,
'flex items-center h-7 justify-center bg-gray-100 rounded-lg cursor-pointer text-xs font-medium text-gray-700 space-x-1'
)
}
onClick=
{
onClick
}
>
<
Plus
className=
'w-3.5 h-3.5'
/>
{
text
}
</
div
>
)
}
export
default
React
.
memo
(
AddButton
)
web/app/components/workflow/nodes/_base/components/field.tsx
View file @
0fb47fed
...
...
@@ -22,8 +22,8 @@ const Filed: FC<Props> = ({
return
(
<
div
className=
{
cn
(
inline
&&
'flex justify-between items-center'
)
}
>
<
div
className=
'flex justify-between items-center'
>
<
div
className=
'flex items-center'
>
<
div
className=
'
h-6 text-xs font-medium text-gray-5
00 uppercase'
>
{
title
}
</
div
>
<
div
className=
'flex items-center
h-6
'
>
<
div
className=
'
text-xs font-medium text-gray-7
00 uppercase'
>
{
title
}
</
div
>
{
tooltip
&&
(
<
TooltipPlus
popupContent=
'tooltip'
>
<
HelpCircle
className=
'w-3.5 h-3.5 ml-0.5 text-gray-400'
/>
...
...
web/app/components/workflow/nodes/if-else/components/condition-item.tsx
0 → 100644
View file @
0fb47fed
'use client'
import
type
{
FC
}
from
'react'
import
React
,
{
useCallback
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
cn
from
'classnames'
import
VarReferencePicker
from
'../../_base/components/variable/var-reference-picker'
import
type
{
Condition
}
from
'@/app/components/workflow/nodes/if-else/types'
import
{
LogicalOperator
}
from
'@/app/components/workflow/nodes/if-else/types'
import
type
{
ValueSelector
}
from
'@/app/components/workflow/types'
import
{
Trash03
}
from
'@/app/components/base/icons/src/vender/line/general'
import
{
RefreshCw05
}
from
'@/app/components/base/icons/src/vender/line/arrows'
const
i18nPrefix
=
'workflow.nodes.ifElse'
const
Line
=
(
<
svg
xmlns=
"http://www.w3.org/2000/svg"
width=
"163"
height=
"2"
viewBox=
"0 0 163 2"
fill=
"none"
>
<
path
d=
"M0 1H162.5"
stroke=
"url(#paint0_linear_641_36452)"
/>
<
defs
>
<
linearGradient
id=
"paint0_linear_641_36452"
x1=
"162.5"
y1=
"9.99584"
x2=
"6.6086e-06"
y2=
"9.94317"
gradientUnits=
"userSpaceOnUse"
>
<
stop
stopColor=
"#F3F4F6"
/>
<
stop
offset=
"1"
stopColor=
"#F3F4F6"
stopOpacity=
"0"
/>
</
linearGradient
>
</
defs
>
</
svg
>
)
type
ItemProps
=
{
readonly
:
boolean
payload
:
Condition
onChange
:
(
newItem
:
Condition
)
=>
void
canRemove
:
boolean
onRemove
?:
()
=>
void
isShowLogicalOperator
?:
boolean
logicalOperator
:
LogicalOperator
onLogicalOperatorToggle
:
()
=>
void
}
const
Item
:
FC
<
ItemProps
>
=
({
readonly
,
payload
,
onChange
,
canRemove
,
onRemove
=
()
=>
{
},
isShowLogicalOperator
,
logicalOperator
,
onLogicalOperatorToggle
,
})
=>
{
const
{
t
}
=
useTranslation
()
const
handleVarReferenceChange
=
useCallback
((
value
:
ValueSelector
)
=>
{
onChange
({
...
payload
,
variable_selector
:
value
,
})
},
[
onChange
,
payload
])
const
handleValueChange
=
useCallback
((
e
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
onChange
({
...
payload
,
value
:
e
.
target
.
value
,
})
},
[
onChange
,
payload
])
return
(
<
div
className=
'space-y-2'
>
{
isShowLogicalOperator
&&
(
<
div
className=
'flex items-center select-none'
>
<
div
className=
'flex items-center '
>
{
Line
}
<
div
className=
'shrink-0 mx-1 flex items-center h-[22px] pl-2 pr-1.5 border border-gray-200 rounded-lg bg-white shadow-xs space-x-0.5 text-primary-600 cursor-pointer'
onClick=
{
onLogicalOperatorToggle
}
>
<
div
className=
'text-xs font-semibold uppercase'
>
{
t
(
`${i18nPrefix}.${logicalOperator === LogicalOperator.and ? 'and' : 'or'}`
)
}
</
div
>
<
RefreshCw05
className=
'w-3 h-3'
/>
</
div
>
<
div
className=
' rotate-180'
>
{
Line
}
</
div
>
</
div
>
</
div
>
)
}
<
div
className=
'flex items-center space-x-1'
>
<
VarReferencePicker
readonly=
{
readonly
}
isShowNodeName
className=
'grow'
value=
{
payload
.
variable_selector
}
onChange=
{
handleVarReferenceChange
}
/>
<
input
readOnly=
{
readonly
}
value=
{
payload
.
value
}
onChange=
{
handleValueChange
}
placeholder=
{
t
(
`${i18nPrefix}.enterValue`
)
!
}
className=
'w-[144px] h-8 leading-8 px-2.5 rounded-lg border-0 bg-gray-100 text-gray-900 text-[13px] placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200'
type=
'text'
/>
<
div
className=
{
cn
(
canRemove
?
'text-gray-500 bg-gray-100 hover:bg-gray-200 cursor-pointer'
:
'bg-gray-25 text-gray-300'
,
'p-2 rounded-lg '
)
}
onClick=
{
canRemove
?
onRemove
:
()
=>
{
}
}
>
<
Trash03
className=
'w-4 h-4 '
/>
</
div
>
</
div
>
</
div
>
)
}
export
default
React
.
memo
(
Item
)
web/app/components/workflow/nodes/if-else/components/condition-list.tsx
View file @
0fb47fed
...
...
@@ -2,84 +2,26 @@
import
type
{
FC
}
from
'react'
import
React
,
{
useCallback
}
from
'react'
import
produce
from
'immer'
import
{
useTranslation
}
from
'react-i18next'
import
cn
from
'classnames'
import
VarReferencePicker
from
'../../_base/components/variable/var-reference-picker'
import
type
{
Condition
}
from
'@/app/components/workflow/nodes/if-else/types'
import
type
{
ValueSelector
}
from
'@/app/components/workflow/types'
import
{
Trash03
}
from
'@/app/components/base/icons/src/vender/line/general'
const
i18nPrefix
=
'workflow.nodes.ifElse'
import
Item
from
'./condition-item'
import
type
{
Condition
,
LogicalOperator
}
from
'@/app/components/workflow/nodes/if-else/types'
type
Props
=
{
className
?:
string
readonly
:
boolean
list
:
Condition
[]
onChange
:
(
newList
:
Condition
[])
=>
void
}
type
ItemProps
=
{
readonly
:
boolean
payload
:
Condition
onChange
:
(
newItem
:
Condition
)
=>
void
canRemove
:
boolean
onRemove
?:
()
=>
void
}
const
Item
:
FC
<
ItemProps
>
=
({
readonly
,
payload
,
onChange
,
canRemove
,
onRemove
=
()
=>
{
},
})
=>
{
const
{
t
}
=
useTranslation
()
const
handleVarReferenceChange
=
useCallback
((
value
:
ValueSelector
)
=>
{
onChange
({
...
payload
,
variable_selector
:
value
,
})
},
[
onChange
,
payload
])
const
handleValueChange
=
useCallback
((
e
:
React
.
ChangeEvent
<
HTMLInputElement
>
)
=>
{
onChange
({
...
payload
,
value
:
e
.
target
.
value
,
})
},
[
onChange
,
payload
])
return
(
<
div
className=
'flex items-center space-x-1'
>
<
VarReferencePicker
readonly=
{
readonly
}
isShowNodeName
className=
'grow'
value=
{
payload
.
variable_selector
}
onChange=
{
handleVarReferenceChange
}
/>
<
input
readOnly=
{
readonly
}
value=
{
payload
.
value
}
onChange=
{
handleValueChange
}
placeholder=
{
t
(
`${i18nPrefix}.enterValue`
)
!
}
className=
'w-[144px] h-8 leading-8 px-2.5 rounded-lg border-0 bg-gray-100 text-gray-900 text-[13px] placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200'
type=
'text'
/>
<
div
className=
{
cn
(
canRemove
?
'text-gray-500 bg-gray-100 hover:bg-gray-200 cursor-pointer'
:
'bg-gray-25 text-gray-300'
,
'p-2 rounded-lg '
)
}
onClick=
{
onRemove
}
>
<
Trash03
className=
'w-4 h-4 '
/>
</
div
>
</
div
>
)
logicalOperator
:
LogicalOperator
onLogicalOperatorToggle
:
()
=>
void
}
const
ConditionList
:
FC
<
Props
>
=
({
className
,
readonly
,
list
,
onChange
,
logicalOperator
,
onLogicalOperatorToggle
,
})
=>
{
const
handleItemChange
=
useCallback
((
index
:
number
)
=>
{
return
(
newItem
:
Condition
)
=>
{
...
...
@@ -99,37 +41,37 @@ const ConditionList: FC<Props> = ({
}
},
[
list
,
onChange
])
const
canRemove
=
list
.
length
>
1
if
(
list
.
length
===
0
)
return
null
return
(
<
div
>
<
div
className=
{
cn
(
className
,
'space-y-2'
)
}
>
<
Item
readonly=
{
readonly
}
payload=
{
list
[
0
]
}
onChange=
{
handleItemChange
(
0
)
}
canRemove=
{
false
}
canRemove=
{
canRemove
}
onRemove=
{
handleItemRemove
(
0
)
}
logicalOperator=
{
logicalOperator
}
onLogicalOperatorToggle=
{
onLogicalOperatorToggle
}
/>
{
list
.
length
>
1
&&
(
<>
<
div
className=
'flex items-center justify-center h-6 text-gray-500'
>
AND
</
div
>
{
list
.
slice
(
1
).
map
((
item
,
i
)
=>
(
<
Item
key=
{
item
.
id
}
readonly=
{
readonly
}
payload=
{
item
}
onChange=
{
handleItemChange
(
i
+
1
)
}
canRemove
onRemove=
{
handleItemRemove
(
i
+
1
)
}
/>
))
}
</>)
list
.
slice
(
1
).
map
((
item
,
i
)
=>
(
<
Item
key=
{
item
.
id
}
readonly=
{
readonly
}
payload=
{
item
}
onChange=
{
handleItemChange
(
i
+
1
)
}
canRemove=
{
canRemove
}
onRemove=
{
handleItemRemove
(
i
+
1
)
}
isShowLogicalOperator
logicalOperator=
{
logicalOperator
}
onLogicalOperatorToggle=
{
onLogicalOperatorToggle
}
/>
)))
}
</
div
>
)
...
...
web/app/components/workflow/nodes/if-else/panel.tsx
View file @
0fb47fed
import
type
{
FC
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
Split
from
'../_base/components/split'
import
AddButton
from
'../_base/components/add-button'
import
useConfig
from
'./use-config'
import
{
mockData
}
from
'./mock'
import
ConditionList
from
'./components/condition-list'
...
...
@@ -14,18 +16,35 @@ const Panel: FC = () => {
inputs
,
handleConditionsChange
,
handleAddCondition
,
handleLogicalOperatorToggle
,
}
=
useConfig
(
mockData
)
return
(
<
div
className=
'mt-2'
>
<
div
className=
'px-4 pb-4 space-y-4'
>
<
Field
title=
{
t
(
`${i18nPrefix}.
conditions
`
)
}
title=
{
t
(
`${i18nPrefix}.
if
`
)
}
>
<
ConditionList
readonly=
{
readOnly
}
list=
{
inputs
.
conditions
}
onChange=
{
handleConditionsChange
}
/>
<>
<
ConditionList
className=
'mt-2'
readonly=
{
readOnly
}
list=
{
inputs
.
conditions
}
onChange=
{
handleConditionsChange
}
logicalOperator=
{
inputs
.
logical_operator
}
onLogicalOperatorToggle=
{
handleLogicalOperatorToggle
}
/>
<
AddButton
className=
'mt-3'
text=
{
t
(
`${i18nPrefix}.addCondition`
)
}
onClick=
{
handleAddCondition
}
/>
</>
</
Field
>
<
Split
/>
<
Field
title=
{
t
(
`${i18nPrefix}.else`
)
}
>
<
div
className=
'leading-[18px] text-xs font-normal text-gray-400'
>
{
t
(
`${i18nPrefix}.elseDescription`
)
}
</
div
>
</
Field
>
</
div
>
</
div
>
...
...
web/app/components/workflow/nodes/if-else/use-config.ts
View file @
0fb47fed
import
{
useCallback
,
useState
}
from
'react'
import
produce
from
'immer'
import
type
{
Condition
,
IfElseNodeType
,
LogicalOperator
}
from
'./types'
import
{
ComparisonOperator
,
LogicalOperator
}
from
'./types'
import
type
{
Condition
,
IfElseNodeType
}
from
'./types'
const
useConfig
=
(
initInputs
:
IfElseNodeType
)
=>
{
const
[
inputs
,
setInputs
]
=
useState
<
IfElseNodeType
>
(
initInputs
)
...
...
@@ -14,16 +15,21 @@ const useConfig = (initInputs: IfElseNodeType) => {
})
},
[])
const
handleAddCondition
=
useCallback
((
condition
:
Condition
)
=>
{
const
handleAddCondition
=
useCallback
(()
=>
{
const
newInputs
=
produce
(
inputs
,
(
draft
)
=>
{
draft
.
conditions
.
push
(
condition
)
draft
.
conditions
.
push
({
id
:
`
${
Date
.
now
()}
`
,
variable_selector
:
[],
comparison_operator
:
ComparisonOperator
.
equal
,
value
:
''
,
})
})
setInputs
(
newInputs
)
},
[
inputs
])
const
handleLogicalOperator
Change
=
useCallback
((
newOperator
:
LogicalOperator
)
=>
{
const
handleLogicalOperator
Toggle
=
useCallback
((
)
=>
{
const
newInputs
=
produce
(
inputs
,
(
draft
)
=>
{
draft
.
logical_operator
=
newOperator
draft
.
logical_operator
=
draft
.
logical_operator
===
LogicalOperator
.
and
?
LogicalOperator
.
or
:
LogicalOperator
.
and
})
setInputs
(
newInputs
)
},
[
inputs
])
...
...
@@ -32,7 +38,7 @@ const useConfig = (initInputs: IfElseNodeType) => {
inputs
,
handleConditionsChange
,
handleAddCondition
,
handleLogicalOperator
Chang
e
,
handleLogicalOperator
Toggl
e
,
}
}
...
...
web/i18n/en-US/workflow.ts
View file @
0fb47fed
...
...
@@ -72,6 +72,9 @@ const translation = {
},
ifElse
:
{
conditions
:
'Conditions'
,
if
:
'If'
,
else
:
'Else'
,
elseDescription
:
'Used to define the logic that should be executed when the if condition is not met.'
,
and
:
'and'
,
or
:
'or'
,
comparisonOperator
:
{
...
...
@@ -87,6 +90,7 @@ const translation = {
'not null'
:
'is not null'
,
},
enterValue
:
'Enter value'
,
addCondition
:
'Add Condition'
,
},
variableAssigner
:
{
title
:
'Assign variables'
,
...
...
web/i18n/zh-Hans/workflow.ts
View file @
0fb47fed
...
...
@@ -71,6 +71,9 @@ const translation = {
},
ifElse
:
{
conditions
:
'条件'
,
if
:
'If'
,
else
:
'Else'
,
elseDescription
:
'用于定义当 if 条件不满足时应执行的逻辑。'
,
and
:
'and'
,
or
:
'or'
,
comparisonOperator
:
{
...
...
@@ -86,6 +89,7 @@ const translation = {
'not null'
:
'不为空'
,
},
enterValue
:
'输入值'
,
addCondition
:
'添加条件'
,
},
variableAssigner
:
{
title
:
'变量赋值'
,
...
...
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