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
4da50eb8
authored
Jul 15, 2026
by
DigHuang
Committed by
GitHub
Jul 15, 2026
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix(workflow): materialize editable node text (#7308)
parent
6527bebf
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
104 additions
and
55 deletions
+104
-55
projects/app/src/pageComponents/app/detail/Workflow/components/SearchButton.tsx
+1
-2
projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/components/NodeTemplates/list.tsx
+7
-7
projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/InlineEdit.tsx
+7
-10
projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/NodeCard.tsx
+6
-13
projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/templates/Reference.tsx
+1
-1
projects/app/src/web/core/app/templates.ts
+11
-11
projects/app/src/web/core/workflow/utils.ts
+18
-2
projects/app/test/web/core/app/templates.test.ts
+21
-0
projects/app/test/web/core/app/workflow/utils.test.ts
+32
-9
No files found.
projects/app/src/pageComponents/app/detail/Workflow/components/SearchButton.tsx
View file @
4da50eb8
...
@@ -45,8 +45,7 @@ const SearchButton = (props: ButtonProps) => {
...
@@ -45,8 +45,7 @@ const SearchButton = (props: ButtonProps) => {
}
}
const
searchResult
=
nodes
.
filter
((
node
)
=>
{
const
searchResult
=
nodes
.
filter
((
node
)
=>
{
const
nodeName
=
t
(
node
.
data
.
name
as
any
);
return
node
.
data
.
name
.
toLowerCase
().
includes
(
keyword
.
toLowerCase
());
return
nodeName
.
toLowerCase
().
includes
(
keyword
.
toLowerCase
());
});
});
if
(
searchResult
.
length
===
0
)
{
if
(
searchResult
.
length
===
0
)
{
...
...
projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/components/NodeTemplates/list.tsx
View file @
4da50eb8
...
@@ -362,12 +362,6 @@ const NodeTemplateList = ({
...
@@ -362,12 +362,6 @@ const NodeTemplateList = ({
const
newNode
=
nodeTemplate2FlowNode
({
const
newNode
=
nodeTemplate2FlowNode
({
template
:
{
template
:
{
...
templateNode
,
...
templateNode
,
name
:
computedNewNodeName
({
templateName
:
t
(
templateNode
.
name
as
any
),
flowNodeType
:
templateNode
.
flowNodeType
,
pluginId
:
templateNode
.
pluginId
}),
intro
:
t
(
templateNode
.
intro
as
any
),
inputs
:
templateNode
.
inputs
inputs
:
templateNode
.
inputs
.
filter
((
input
)
=>
input
.
deprecated
!==
true
)
.
filter
((
input
)
=>
input
.
deprecated
!==
true
)
.
map
((
input
)
=>
({
.
map
((
input
)
=>
({
...
@@ -400,7 +394,13 @@ const NodeTemplateList = ({
...
@@ -400,7 +394,13 @@ const NodeTemplateList = ({
position
,
position
,
selected
:
true
,
selected
:
true
,
parentNodeId
:
effectiveParentNodeId
,
parentNodeId
:
effectiveParentNodeId
,
t
t
,
formatName
:
(
templateName
)
=>
computedNewNodeName
({
templateName
,
flowNodeType
:
templateNode
.
flowNodeType
,
pluginId
:
templateNode
.
pluginId
})
});
});
const
newNodes
=
[
newNode
];
const
newNodes
=
[
newNode
];
...
...
projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/InlineEdit.tsx
View file @
4da50eb8
import
React
,
{
useState
,
useCallback
}
from
'react'
;
import
React
,
{
useState
,
useCallback
}
from
'react'
;
import
{
Box
,
Input
,
Textarea
,
type
BoxProps
}
from
'@chakra-ui/react'
;
import
{
Box
,
Input
,
Textarea
,
type
BoxProps
}
from
'@chakra-ui/react'
;
import
{
useTranslation
}
from
'next-i18next'
;
export
type
InlineEditProps
=
BoxProps
&
{
export
type
InlineEditProps
=
BoxProps
&
{
value
:
string
;
value
:
string
;
...
@@ -22,11 +21,10 @@ export const InlineEdit = React.memo(function InlineEdit({
...
@@ -22,11 +21,10 @@ export const InlineEdit = React.memo(function InlineEdit({
maxLength
,
maxLength
,
placeholder
,
placeholder
,
innerH
,
innerH
,
noOfLines
=
1
,
noOfLines
,
renderDisplay
,
renderDisplay
,
...
rest
...
rest
}:
InlineEditProps
)
{
}:
InlineEditProps
)
{
const
{
t
}
=
useTranslation
();
const
[
isEditing
,
setIsEditing
]
=
useState
(
false
);
const
[
isEditing
,
setIsEditing
]
=
useState
(
false
);
const
[
prevValue
,
setPrevValue
]
=
useState
(
value
);
const
[
prevValue
,
setPrevValue
]
=
useState
(
value
);
const
[
tempValue
,
setTempValue
]
=
useState
(
value
);
const
[
tempValue
,
setTempValue
]
=
useState
(
value
);
...
@@ -96,7 +94,7 @@ export const InlineEdit = React.memo(function InlineEdit({
...
@@ -96,7 +94,7 @@ export const InlineEdit = React.memo(function InlineEdit({
color=
{
'inherit'
}
color=
{
'inherit'
}
variant=
{
'unstyled'
}
variant=
{
'unstyled'
}
value=
{
tempValue
}
value=
{
tempValue
}
placeholder=
{
placeholder
?
t
(
placeholder
as
any
)
:
''
}
placeholder=
{
placeholder
}
maxLength=
{
maxLength
}
maxLength=
{
maxLength
}
onChange=
{
(
e
)
=>
setTempValue
(
e
.
target
.
value
)
}
onChange=
{
(
e
)
=>
setTempValue
(
e
.
target
.
value
)
}
autoFocus
autoFocus
...
@@ -129,7 +127,7 @@ export const InlineEdit = React.memo(function InlineEdit({
...
@@ -129,7 +127,7 @@ export const InlineEdit = React.memo(function InlineEdit({
</
Box
>
</
Box
>
<
Textarea
<
Textarea
value=
{
tempValue
}
value=
{
tempValue
}
placeholder=
{
placeholder
?
t
(
placeholder
as
any
)
:
''
}
placeholder=
{
placeholder
}
maxLength=
{
maxLength
}
maxLength=
{
maxLength
}
onChange=
{
(
e
)
=>
setTempValue
(
e
.
target
.
value
)
}
onChange=
{
(
e
)
=>
setTempValue
(
e
.
target
.
value
)
}
autoFocus
autoFocus
...
@@ -162,11 +160,12 @@ export const InlineEdit = React.memo(function InlineEdit({
...
@@ -162,11 +160,12 @@ export const InlineEdit = React.memo(function InlineEdit({
<
Box
<
Box
cursor=
{
'pointer'
}
cursor=
{
'pointer'
}
onClick=
{
()
=>
setIsEditing
(
true
)
}
onClick=
{
()
=>
setIsEditing
(
true
)
}
title=
{
t
(
value
as
any
)
}
title=
{
value
}
w=
{
'100%'
}
w=
{
'100%'
}
minW=
{
0
}
minW=
{
0
}
maxW=
{
'100%'
}
maxW=
{
'100%'
}
noOfLines=
{
noOfLines
}
noOfLines=
{
noOfLines
??
(
type
===
'input'
?
1
:
undefined
)
}
whiteSpace=
{
type
===
'textarea'
?
'pre-wrap'
:
undefined
}
wordBreak=
{
'break-all'
}
wordBreak=
{
'break-all'
}
borderRadius=
{
'sm'
}
borderRadius=
{
'sm'
}
border=
{
'1px solid transparent'
}
border=
{
'1px solid transparent'
}
...
@@ -186,9 +185,7 @@ export const InlineEdit = React.memo(function InlineEdit({
...
@@ -186,9 +185,7 @@ export const InlineEdit = React.memo(function InlineEdit({
color=
{
type
===
'input'
?
'myGray.900'
:
'myGray.500'
}
color=
{
type
===
'input'
?
'myGray.900'
:
'myGray.500'
}
{
...
rest
}
{
...
rest
}
>
>
{
renderDisplay
{
renderDisplay
?
renderDisplay
(
value
)
:
value
||
placeholder
||
''
}
?
renderDisplay
(
value
)
:
t
(
value
as
any
)
||
(
placeholder
?
t
(
placeholder
as
any
)
:
''
)
}
</
Box
>
</
Box
>
);
);
});
});
...
...
projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/NodeCard.tsx
View file @
4da50eb8
...
@@ -209,11 +209,11 @@ const NodeCard = (props: Props) => {
...
@@ -209,11 +209,11 @@ const NodeCard = (props: Props) => {
whiteSpace=
{
'nowrap'
}
whiteSpace=
{
'nowrap'
}
maxW=
{
'80%'
}
maxW=
{
'80%'
}
>
>
{
t
(
name
as
any
)
}
{
name
}
</
Box
>
</
Box
>
</
Flex
>
</
Flex
>
);
);
},
[
isFolded
,
avatar
,
avatarLinear
,
name
,
handleDoubleClick
,
t
]);
},
[
isFolded
,
avatar
,
avatarLinear
,
name
,
handleDoubleClick
]);
const
{
outlineColor
,
outlineWidth
}
=
useMemo
(()
=>
{
const
{
outlineColor
,
outlineWidth
}
=
useMemo
(()
=>
{
// error mode
// error mode
...
@@ -563,14 +563,9 @@ const NodeTitleSection = React.memo<{
...
@@ -563,14 +563,9 @@ const NodeTitleSection = React.memo<{
const
renderDisplay
=
useCallback
(
const
renderDisplay
=
useCallback
(
(
val
:
string
)
=>
(
(
val
:
string
)
=>
(
<
HighlightText
<
HighlightText
rawText=
{
val
}
matchText=
{
searchedText
??
''
}
mode=
{
'bg'
}
color=
{
'#ffe82d'
}
/>
rawText=
{
t
(
val
as
any
)
}
matchText=
{
searchedText
??
''
}
mode=
{
'bg'
}
color=
{
'#ffe82d'
}
/>
),
),
[
searchedText
,
t
]
[
searchedText
]
);
);
return
(
return
(
...
@@ -646,7 +641,6 @@ const NodeIntro = React.memo(function NodeIntro({
...
@@ -646,7 +641,6 @@ const NodeIntro = React.memo(function NodeIntro({
minH=
{
'20px'
}
minH=
{
'20px'
}
py=
{
'3px'
}
py=
{
'3px'
}
px=
{
'6px'
}
px=
{
'6px'
}
noOfLines=
{
1
}
/>
/>
</
Box
>
</
Box
>
);
);
...
@@ -1205,7 +1199,6 @@ const PresentationModeOverlay = React.memo(function PresentationModeOverlay({
...
@@ -1205,7 +1199,6 @@ const PresentationModeOverlay = React.memo(function PresentationModeOverlay({
isLoopNode
:
boolean
;
isLoopNode
:
boolean
;
onDoubleClick
:
()
=>
void
;
onDoubleClick
:
()
=>
void
;
})
{
})
{
const
{
t
}
=
useTranslation
();
const
[
presentationHeight
,
setPresentationHeight
]
=
useState
<
number
>
(
0
);
const
[
presentationHeight
,
setPresentationHeight
]
=
useState
<
number
>
(
0
);
const
presentationOverlayRef
=
useCallback
((
node
:
HTMLDivElement
|
null
)
=>
{
const
presentationOverlayRef
=
useCallback
((
node
:
HTMLDivElement
|
null
)
=>
{
...
@@ -1271,7 +1264,7 @@ const PresentationModeOverlay = React.memo(function PresentationModeOverlay({
...
@@ -1271,7 +1264,7 @@ const PresentationModeOverlay = React.memo(function PresentationModeOverlay({
whiteSpace=
{
'nowrap'
}
whiteSpace=
{
'nowrap'
}
maxW=
{
'80%'
}
maxW=
{
'80%'
}
>
>
{
t
(
name
as
any
)
}
{
name
}
</
Box
>
</
Box
>
)
}
)
}
{
intro
&&
presentationHeight
>
320
&&
(
{
intro
&&
presentationHeight
>
320
&&
(
...
@@ -1284,7 +1277,7 @@ const PresentationModeOverlay = React.memo(function PresentationModeOverlay({
...
@@ -1284,7 +1277,7 @@ const PresentationModeOverlay = React.memo(function PresentationModeOverlay({
whiteSpace=
{
'nowrap'
}
whiteSpace=
{
'nowrap'
}
maxW=
{
'80%'
}
maxW=
{
'80%'
}
>
>
{
t
(
intro
as
any
)
}
{
intro
}
</
Box
>
</
Box
>
)
}
)
}
</
Flex
>
</
Flex
>
...
...
projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/templates/Reference.tsx
View file @
4da50eb8
...
@@ -93,7 +93,7 @@ export const useReference = ({
...
@@ -93,7 +93,7 @@ export const useReference = ({
label
:
(
label
:
(
<
Flex
alignItems=
{
'center'
}
>
<
Flex
alignItems=
{
'center'
}
>
<
Avatar
src=
{
node
.
avatar
}
w=
{
isArray
?
'1rem'
:
'1.05rem'
}
borderRadius=
{
'xs'
}
/>
<
Avatar
src=
{
node
.
avatar
}
w=
{
isArray
?
'1rem'
:
'1.05rem'
}
borderRadius=
{
'xs'
}
/>
<
Box
ml=
{
1
}
>
{
t
(
node
.
name
as
any
)
}
</
Box
>
<
Box
ml=
{
1
}
>
{
node
.
name
}
</
Box
>
</
Flex
>
</
Flex
>
),
),
value
:
node
.
nodeId
,
value
:
node
.
nodeId
,
...
...
projects/app/src/web/core/app/templates.ts
View file @
4da50eb8
...
@@ -28,8 +28,8 @@ export const getEmptyAppsTemplate = (t: any) => {
...
@@ -28,8 +28,8 @@ export const getEmptyAppsTemplate = (t: any) => {
nodes
:
[
nodes
:
[
{
{
nodeId
:
'userGuide'
,
nodeId
:
'userGuide'
,
name
:
i18nT
(
'common:core.module.template.system_config'
),
name
:
t
(
i18nT
(
'common:core.module.template.system_config'
)
),
intro
:
i18nT
(
'common:core.module.template.config_params'
),
intro
:
t
(
i18nT
(
'common:core.module.template.config_params'
)
),
avatar
:
'core/workflow/template/systemConfig'
,
avatar
:
'core/workflow/template/systemConfig'
,
flowNodeType
:
FlowNodeTypeEnum
.
systemConfig
,
flowNodeType
:
FlowNodeTypeEnum
.
systemConfig
,
position
:
{
position
:
{
...
@@ -93,7 +93,7 @@ export const getEmptyAppsTemplate = (t: any) => {
...
@@ -93,7 +93,7 @@ export const getEmptyAppsTemplate = (t: any) => {
},
},
{
{
nodeId
:
'448745'
,
nodeId
:
'448745'
,
name
:
i18nT
(
'common:core.module.template.work_start'
),
name
:
t
(
i18nT
(
'common:core.module.template.work_start'
)
),
intro
:
''
,
intro
:
''
,
avatar
:
'core/workflow/template/workflowStart'
,
avatar
:
'core/workflow/template/workflowStart'
,
flowNodeType
:
FlowNodeTypeEnum
.
workflowStart
,
flowNodeType
:
FlowNodeTypeEnum
.
workflowStart
,
...
@@ -124,8 +124,8 @@ export const getEmptyAppsTemplate = (t: any) => {
...
@@ -124,8 +124,8 @@ export const getEmptyAppsTemplate = (t: any) => {
},
},
{
{
nodeId
:
'loOvhld2ZTKa'
,
nodeId
:
'loOvhld2ZTKa'
,
name
:
i18nT
(
'common:core.module.template.ai_chat'
),
name
:
t
(
i18nT
(
'common:core.module.template.ai_chat'
)
),
intro
:
i18nT
(
'common:core.module.template.ai_chat_intro'
),
intro
:
t
(
i18nT
(
'common:core.module.template.ai_chat_intro'
)
),
avatar
:
'core/workflow/template/aiChat'
,
avatar
:
'core/workflow/template/aiChat'
,
flowNodeType
:
FlowNodeTypeEnum
.
chatNode
,
flowNodeType
:
FlowNodeTypeEnum
.
chatNode
,
showStatus
:
true
,
showStatus
:
true
,
...
@@ -264,8 +264,8 @@ export const getEmptyAppsTemplate = (t: any) => {
...
@@ -264,8 +264,8 @@ export const getEmptyAppsTemplate = (t: any) => {
nodes
:
[
nodes
:
[
{
{
nodeId
:
'userGuide'
,
nodeId
:
'userGuide'
,
name
:
i18nT
(
'common:core.module.template.system_config'
),
name
:
t
(
i18nT
(
'common:core.module.template.system_config'
)
),
intro
:
i18nT
(
'common:core.module.template.system_config_info'
),
intro
:
t
(
i18nT
(
'common:core.module.template.system_config_info'
)
),
avatar
:
'core/workflow/template/systemConfig'
,
avatar
:
'core/workflow/template/systemConfig'
,
flowNodeType
:
FlowNodeTypeEnum
.
systemConfig
,
flowNodeType
:
FlowNodeTypeEnum
.
systemConfig
,
position
:
{
position
:
{
...
@@ -329,7 +329,7 @@ export const getEmptyAppsTemplate = (t: any) => {
...
@@ -329,7 +329,7 @@ export const getEmptyAppsTemplate = (t: any) => {
},
},
{
{
nodeId
:
'448745'
,
nodeId
:
'448745'
,
name
:
i18nT
(
'common:core.module.template.work_start'
),
name
:
t
(
i18nT
(
'common:core.module.template.work_start'
)
),
intro
:
''
,
intro
:
''
,
avatar
:
'core/workflow/template/workflowStart'
,
avatar
:
'core/workflow/template/workflowStart'
,
flowNodeType
:
FlowNodeTypeEnum
.
workflowStart
,
flowNodeType
:
FlowNodeTypeEnum
.
workflowStart
,
...
@@ -368,7 +368,7 @@ export const getEmptyAppsTemplate = (t: any) => {
...
@@ -368,7 +368,7 @@ export const getEmptyAppsTemplate = (t: any) => {
nodes
:
[
nodes
:
[
{
{
nodeId
:
'pluginInput'
,
nodeId
:
'pluginInput'
,
name
:
i18nT
(
'workflow:template.plugin_start'
),
name
:
t
(
i18nT
(
'workflow:template.plugin_start'
)
),
avatar
:
'core/workflow/template/workflowStart'
,
avatar
:
'core/workflow/template/workflowStart'
,
flowNodeType
:
FlowNodeTypeEnum
.
pluginInput
,
flowNodeType
:
FlowNodeTypeEnum
.
pluginInput
,
showStatus
:
false
,
showStatus
:
false
,
...
@@ -382,7 +382,7 @@ export const getEmptyAppsTemplate = (t: any) => {
...
@@ -382,7 +382,7 @@ export const getEmptyAppsTemplate = (t: any) => {
},
},
{
{
nodeId
:
'pluginOutput'
,
nodeId
:
'pluginOutput'
,
name
:
i18nT
(
'common:core.module.template.self_output'
),
name
:
t
(
i18nT
(
'common:core.module.template.self_output'
)
),
avatar
:
'/imgs/workflow/output.png'
,
avatar
:
'/imgs/workflow/output.png'
,
flowNodeType
:
FlowNodeTypeEnum
.
pluginOutput
,
flowNodeType
:
FlowNodeTypeEnum
.
pluginOutput
,
showStatus
:
false
,
showStatus
:
false
,
...
@@ -396,7 +396,7 @@ export const getEmptyAppsTemplate = (t: any) => {
...
@@ -396,7 +396,7 @@ export const getEmptyAppsTemplate = (t: any) => {
},
},
{
{
nodeId
:
'pluginConfig'
,
nodeId
:
'pluginConfig'
,
name
:
i18nT
(
'common:core.module.template.system_config'
),
name
:
t
(
i18nT
(
'common:core.module.template.system_config'
)
),
intro
:
''
,
intro
:
''
,
avatar
:
'core/workflow/template/systemConfig'
,
avatar
:
'core/workflow/template/systemConfig'
,
flowNodeType
:
FlowNodeTypeEnum
.
pluginConfig
,
flowNodeType
:
FlowNodeTypeEnum
.
pluginConfig
,
...
...
projects/app/src/web/core/workflow/utils.ts
View file @
4da50eb8
...
@@ -84,13 +84,18 @@ export const adaptStoreNodeInputs = (storeNode: StoreNodeItemType): FlowNodeInpu
...
@@ -84,13 +84,18 @@ export const adaptStoreNodeInputs = (storeNode: StoreNodeItemType): FlowNodeInpu
});
});
};
};
/**
* 将节点模板转换为画布节点,并按创建时语言初始化可编辑文本。
* `formatName` 在翻译完成后执行,用于基于实例名称追加重名序号。
*/
export
const
nodeTemplate2FlowNode
=
({
export
const
nodeTemplate2FlowNode
=
({
template
,
template
,
position
,
position
,
selected
,
selected
,
parentNodeId
,
parentNodeId
,
zIndex
,
zIndex
,
t
t
,
formatName
}:
{
}:
{
template
:
FlowNodeTemplateType
;
template
:
FlowNodeTemplateType
;
position
:
XYPosition
;
position
:
XYPosition
;
...
@@ -98,11 +103,15 @@ export const nodeTemplate2FlowNode = ({
...
@@ -98,11 +103,15 @@ export const nodeTemplate2FlowNode = ({
parentNodeId
?:
string
;
parentNodeId
?:
string
;
zIndex
?:
number
;
zIndex
?:
number
;
t
:
TFunction
;
t
:
TFunction
;
formatName
?:
(
name
:
string
)
=>
string
;
}):
Node
<
FlowNodeItemType
>
=>
{
}):
Node
<
FlowNodeItemType
>
=>
{
const
name
=
t
(
template
.
name
as
any
);
// replace item data
// replace item data
const
moduleItem
:
FlowNodeItemType
=
{
const
moduleItem
:
FlowNodeItemType
=
{
...
template
,
...
template
,
name
:
t
(
template
.
name
as
any
),
name
:
formatName
?.(
name
)
??
name
,
intro
:
template
.
intro
?
t
(
template
.
intro
as
any
)
:
template
.
intro
,
nodeId
:
getNanoid
(),
nodeId
:
getNanoid
(),
parentNodeId
parentNodeId
};
};
...
@@ -126,6 +135,11 @@ export const nodeTemplate2FlowNode = ({
...
@@ -126,6 +135,11 @@ export const nodeTemplate2FlowNode = ({
zIndex
zIndex
};
};
};
};
/**
* 将持久化节点恢复为画布节点,并在加载时实体化历史 i18n 文本。
* 名称或描述命中翻译 key 时使用当前语言文本,后续保存会写回实体文本。
*/
export
const
storeNode2FlowNode
=
({
export
const
storeNode2FlowNode
=
({
item
:
storeNode
,
item
:
storeNode
,
selected
=
false
,
selected
=
false
,
...
@@ -160,6 +174,8 @@ export const storeNode2FlowNode = ({
...
@@ -160,6 +174,8 @@ export const storeNode2FlowNode = ({
parentNodeId
,
parentNodeId
,
...
template
,
...
template
,
...
storeNode
,
...
storeNode
,
name
:
t
(
storeNode
.
name
as
any
),
intro
:
storeNode
.
intro
?
t
(
storeNode
.
intro
as
any
)
:
storeNode
.
intro
,
avatar
:
template
.
avatar
??
storeNode
.
avatar
,
avatar
:
template
.
avatar
??
storeNode
.
avatar
,
version
:
template
.
version
||
storeNode
.
version
,
version
:
template
.
version
||
storeNode
.
version
,
catchError
:
storeNode
.
catchError
??
template
.
catchError
,
catchError
:
storeNode
.
catchError
??
template
.
catchError
,
...
...
projects/app/test/web/core/app/templates.test.ts
0 → 100644
View file @
4da50eb8
import
{
describe
,
expect
,
it
}
from
'vitest'
;
import
{
AppTypeEnum
}
from
'@fastgpt/global/core/app/constants'
;
import
{
getEmptyAppsTemplate
}
from
'@/web/core/app/templates'
;
describe
(
'getEmptyAppsTemplate'
,
()
=>
{
it
(
'创建空应用时按当前语言初始化节点标题和描述'
,
()
=>
{
const
templates
=
getEmptyAppsTemplate
(((
key
:
string
)
=>
`translated:
${
key
}
`
)
as
any
);
const
nodes
=
[
...
templates
[
AppTypeEnum
.
simple
].
nodes
,
...
templates
[
AppTypeEnum
.
workflow
].
nodes
,
...
templates
[
AppTypeEnum
.
workflowTool
].
nodes
];
expect
(
nodes
.
every
((
node
)
=>
node
.
name
.
startsWith
(
'translated:'
))).
toBe
(
true
);
expect
(
nodes
.
filter
((
node
)
=>
node
.
intro
).
map
((
node
)
=>
node
.
intro
)).
toEqual
([
'translated:common:core.module.template.config_params'
,
'translated:common:core.module.template.ai_chat_intro'
,
'translated:common:core.module.template.system_config_info'
]);
});
});
projects/app/test/web/core/app/workflow/utils.test.ts
View file @
4da50eb8
import
{
describe
,
it
,
expect
}
from
'vitest'
;
import
{
describe
,
it
,
expect
,
vi
}
from
'vitest'
;
import
type
{
import
type
{
FlowNodeItemType
,
FlowNodeItemType
,
FlowNodeTemplateType
,
FlowNodeTemplateType
,
...
@@ -27,22 +27,31 @@ import type { FlowNodeOutputItemType } from '@fastgpt/global/core/workflow/type/
...
@@ -27,22 +27,31 @@ import type { FlowNodeOutputItemType } from '@fastgpt/global/core/workflow/type/
import
{
NodeOutputKeyEnum
,
VARIABLE_NODE_ID
}
from
'@fastgpt/global/core/workflow/constants'
;
import
{
NodeOutputKeyEnum
,
VARIABLE_NODE_ID
}
from
'@fastgpt/global/core/workflow/constants'
;
describe
(
'nodeTemplate2FlowNode'
,
()
=>
{
describe
(
'nodeTemplate2FlowNode'
,
()
=>
{
it
(
'should
convert template to flow nod
e'
,
()
=>
{
it
(
'should
initialize template text once before formatting the instance nam
e'
,
()
=>
{
const
template
:
FlowNodeTemplateType
=
{
const
template
:
FlowNodeTemplateType
=
{
id
:
'template1'
,
id
:
'template1'
,
templateType
:
'formInput'
,
templateType
:
'formInput'
,
name
:
'Test Node'
,
name
:
'workflow:template_name'
,
intro
:
'workflow:template_intro'
,
flowNodeType
:
FlowNodeTypeEnum
.
formInput
,
flowNodeType
:
FlowNodeTypeEnum
.
formInput
,
inputs
:
[],
inputs
:
[],
outputs
:
[]
outputs
:
[]
};
};
const
t
=
vi
.
fn
(
(
key
:
string
)
=>
({
'workflow:template_name'
:
'Template Name'
,
'workflow:template_intro'
:
'Template Intro'
})[
key
]
??
key
);
const
result
=
nodeTemplate2FlowNode
({
const
result
=
nodeTemplate2FlowNode
({
template
,
template
,
position
:
{
x
:
100
,
y
:
100
},
position
:
{
x
:
100
,
y
:
100
},
selected
:
true
,
selected
:
true
,
parentNodeId
:
'parent1'
,
parentNodeId
:
'parent1'
,
t
:
((
key
:
any
)
=>
key
)
as
any
t
:
t
as
any
,
formatName
:
(
name
)
=>
`
${
name
}
2`
});
});
expect
(
result
).
toMatchObject
({
expect
(
result
).
toMatchObject
({
...
@@ -50,38 +59,52 @@ describe('nodeTemplate2FlowNode', () => {
...
@@ -50,38 +59,52 @@ describe('nodeTemplate2FlowNode', () => {
position
:
{
x
:
100
,
y
:
100
},
position
:
{
x
:
100
,
y
:
100
},
selected
:
true
,
selected
:
true
,
data
:
{
data
:
{
name
:
'Test Node'
,
name
:
'Template Name 2'
,
intro
:
'Template Intro'
,
flowNodeType
:
FlowNodeTypeEnum
.
formInput
,
flowNodeType
:
FlowNodeTypeEnum
.
formInput
,
parentNodeId
:
'parent1'
parentNodeId
:
'parent1'
}
}
});
});
expect
(
result
.
id
).
toBeDefined
();
expect
(
result
.
id
).
toBeDefined
();
expect
(
t
.
mock
.
calls
.
map
(([
key
])
=>
key
)).
toEqual
([
'workflow:template_name'
,
'workflow:template_intro'
]);
});
});
});
});
describe
(
'storeNode2FlowNode'
,
()
=>
{
describe
(
'storeNode2FlowNode'
,
()
=>
{
it
(
'should
convert store node to flow node
'
,
()
=>
{
it
(
'should
materialize stored editable text when it matches an i18n key
'
,
()
=>
{
const
storeNode
:
StoreNodeItemType
=
{
const
storeNode
:
StoreNodeItemType
=
{
nodeId
:
'node1'
,
nodeId
:
'node1'
,
flowNodeType
:
FlowNodeTypeEnum
.
formInput
,
flowNodeType
:
FlowNodeTypeEnum
.
formInput
,
position
:
{
x
:
100
,
y
:
100
},
position
:
{
x
:
100
,
y
:
100
},
inputs
:
[],
inputs
:
[],
outputs
:
[],
outputs
:
[],
name
:
'Test Node'
,
name
:
'workflow:stored_name'
,
intro
:
'workflow:stored_intro'
,
version
:
'1.0'
version
:
'1.0'
};
};
const
result
=
storeNode2FlowNode
({
const
result
=
storeNode2FlowNode
({
item
:
storeNode
,
item
:
storeNode
,
selected
:
true
,
selected
:
true
,
t
:
((
key
:
any
)
=>
key
)
as
any
t
:
((
key
:
string
)
=>
({
'workflow:stored_name'
:
'Stored Name'
,
'workflow:stored_intro'
:
'Stored Intro'
})[
key
]
??
key
)
as
any
});
});
expect
(
result
).
toMatchObject
({
expect
(
result
).
toMatchObject
({
id
:
'node1'
,
id
:
'node1'
,
type
:
FlowNodeTypeEnum
.
formInput
,
type
:
FlowNodeTypeEnum
.
formInput
,
position
:
{
x
:
100
,
y
:
100
},
position
:
{
x
:
100
,
y
:
100
},
selected
:
true
selected
:
true
,
data
:
{
name
:
'Stored Name'
,
intro
:
'Stored Intro'
}
});
});
});
});
...
...
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