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
6bb10ca1
authored
Sep 24, 2024
by
heheer
Committed by
GitHub
Sep 24, 2024
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
perf: optimize simple app history (#2782)
* simple app history * ui * extract context content into hooks
parent
7aa75f8e
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
728 additions
and
884 deletions
+728
-884
packages/web/components/common/Icon/constants.ts
+1
-0
packages/web/components/common/Icon/icons/common/line.svg
+4
-0
packages/web/components/common/MyDrawer/CustomRightDrawer.tsx
+4
-2
projects/app/src/components/common/folder/Path.tsx
+4
-4
projects/app/src/pages/app/detail/components/Plugin/Header.tsx
+15
-92
projects/app/src/pages/app/detail/components/PublishHistoriesSlider.tsx
+348
-169
projects/app/src/pages/app/detail/components/RouteTab.tsx
+6
-6
projects/app/src/pages/app/detail/components/SimpleApp/Edit.tsx
+28
-3
projects/app/src/pages/app/detail/components/SimpleApp/Header.tsx
+110
-66
projects/app/src/pages/app/detail/components/SimpleApp/index.tsx
+13
-5
projects/app/src/pages/app/detail/components/SimpleApp/useSnapshots.tsx
+47
-0
projects/app/src/pages/app/detail/components/Workflow/Header.tsx
+15
-90
projects/app/src/pages/app/detail/components/Workflow/components/SaveButton.tsx
+115
-0
projects/app/src/pages/app/detail/components/WorkflowComponents/context.tsx
+13
-2
projects/app/src/pages/app/detail/components/WorkflowPublishHistoriesSlider.tsx
+0
-351
projects/app/src/pages/app/detail/components/context.tsx
+1
-2
projects/app/src/web/core/app/api/version.ts
+4
-4
projects/app/src/web/core/workflow/utils.ts
+0
-88
No files found.
packages/web/components/common/Icon/constants.ts
View file @
6bb10ca1
...
@@ -40,6 +40,7 @@ export const iconPaths = {
...
@@ -40,6 +40,7 @@ export const iconPaths = {
'common/language/en'
:
()
=>
import
(
'./icons/common/language/en.svg'
),
'common/language/en'
:
()
=>
import
(
'./icons/common/language/en.svg'
),
'common/language/zh'
:
()
=>
import
(
'./icons/common/language/zh.svg'
),
'common/language/zh'
:
()
=>
import
(
'./icons/common/language/zh.svg'
),
'common/leftArrowLight'
:
()
=>
import
(
'./icons/common/leftArrowLight.svg'
),
'common/leftArrowLight'
:
()
=>
import
(
'./icons/common/leftArrowLight.svg'
),
'common/line'
:
()
=>
import
(
'./icons/common/line.svg'
),
'common/lineChange'
:
()
=>
import
(
'./icons/common/lineChange.svg'
),
'common/lineChange'
:
()
=>
import
(
'./icons/common/lineChange.svg'
),
'common/linkBlue'
:
()
=>
import
(
'./icons/common/linkBlue.svg'
),
'common/linkBlue'
:
()
=>
import
(
'./icons/common/linkBlue.svg'
),
'common/list'
:
()
=>
import
(
'./icons/common/list.svg'
),
'common/list'
:
()
=>
import
(
'./icons/common/list.svg'
),
...
...
packages/web/components/common/Icon/icons/common/line.svg
0 → 100644
View file @
6bb10ca1
<svg
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 5 12"
fill=
"none"
>
<path
d=
"M4.42017 1.30151L0.999965 10.6984"
stroke=
"#DFE2EA"
stroke-linecap=
"round"
/>
</svg>
\ No newline at end of file
packages/web/components/common/MyDrawer/CustomRightDrawer.tsx
View file @
6bb10ca1
...
@@ -16,6 +16,8 @@ const CustomRightDrawer = ({
...
@@ -16,6 +16,8 @@ const CustomRightDrawer = ({
iconSrc
,
iconSrc
,
title
,
title
,
maxW
=
[
'90vw'
,
'30vw'
],
maxW
=
[
'90vw'
,
'30vw'
],
top
=
16
,
bottom
=
0
,
children
,
children
,
isLoading
,
isLoading
,
showMask
=
true
,
showMask
=
true
,
...
@@ -31,8 +33,8 @@ const CustomRightDrawer = ({
...
@@ -31,8 +33,8 @@ const CustomRightDrawer = ({
zIndex=
{
100
}
zIndex=
{
100
}
maxW=
{
maxW
}
maxW=
{
maxW
}
w=
{
'100%'
}
w=
{
'100%'
}
top=
{
'60px'
}
top=
{
top
}
bottom=
{
0
}
bottom=
{
bottom
}
borderLeftRadius=
{
'lg'
}
borderLeftRadius=
{
'lg'
}
border=
{
'base'
}
border=
{
'base'
}
boxShadow=
{
'2'
}
boxShadow=
{
'2'
}
...
...
projects/app/src/components/common/folder/Path.tsx
View file @
6bb10ca1
...
@@ -2,6 +2,7 @@ import { Box, BoxProps, Flex } from '@chakra-ui/react';
...
@@ -2,6 +2,7 @@ import { Box, BoxProps, Flex } from '@chakra-ui/react';
import
{
ParentTreePathItemType
}
from
'@fastgpt/global/common/parentFolder/type'
;
import
{
ParentTreePathItemType
}
from
'@fastgpt/global/common/parentFolder/type'
;
import
React
,
{
useMemo
}
from
'react'
;
import
React
,
{
useMemo
}
from
'react'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
MyIcon
from
'@fastgpt/web/components/common/Icon'
;
const
FolderPath
=
(
props
:
{
const
FolderPath
=
(
props
:
{
paths
:
ParentTreePathItemType
[];
paths
:
ParentTreePathItemType
[];
...
@@ -35,7 +36,7 @@ const FolderPath = (props: {
...
@@ -35,7 +36,7 @@ const FolderPath = (props: {
return
paths
.
length
===
0
&&
!!
FirstPathDom
?
(
return
paths
.
length
===
0
&&
!!
FirstPathDom
?
(
<>
{
FirstPathDom
}
</>
<>
{
FirstPathDom
}
</>
)
:
(
)
:
(
<
Flex
flex=
{
1
}
ml=
{
-
1.5
}
>
<
Flex
flex=
{
1
}
>
{
concatPaths
.
map
((
item
,
i
)
=>
(
{
concatPaths
.
map
((
item
,
i
)
=>
(
<
Flex
key=
{
item
.
parentId
||
i
}
alignItems=
{
'center'
}
>
<
Flex
key=
{
item
.
parentId
||
i
}
alignItems=
{
'center'
}
>
<
Box
<
Box
...
@@ -51,6 +52,7 @@ const FolderPath = (props: {
...
@@ -51,6 +52,7 @@ const FolderPath = (props: {
}
}
:
{
:
{
cursor
:
'
pointer
',
cursor
:
'
pointer
',
fontWeight
:
'
medium
',
color
:
'
myGray
.
500',
color
:
'
myGray
.
500',
_hover
:
{
_hover
:
{
bg
:
'
myGray
.
100',
bg
:
'
myGray
.
100',
...
@@ -64,9 +66,7 @@ const FolderPath = (props: {
...
@@ -64,9 +66,7 @@ const FolderPath = (props: {
{
item
.
parentName
}
{
item
.
parentName
}
</
Box
>
</
Box
>
{
i
!==
concatPaths
.
length
-
1
&&
(
{
i
!==
concatPaths
.
length
-
1
&&
(
<
Box
mx=
{
1.5
}
color=
{
'myGray.500'
}
>
<
MyIcon
name=
{
'common/line'
}
color=
{
'myGray.500'
}
mx=
{
1
}
width=
{
'5px'
}
/>
/
</
Box
>
)
}
)
}
</
Flex
>
</
Flex
>
))
}
))
}
...
...
projects/app/src/pages/app/detail/components/Plugin/Header.tsx
View file @
6bb10ca1
import
React
,
{
useCallback
,
use
Effect
,
use
Memo
,
useState
}
from
'react'
;
import
React
,
{
useCallback
,
useMemo
,
useState
}
from
'react'
;
import
{
import
{
Box
,
Box
,
Flex
,
Flex
,
...
@@ -22,18 +22,16 @@ import { useRouter } from 'next/router';
...
@@ -22,18 +22,16 @@ import { useRouter } from 'next/router';
import
AppCard
from
'../WorkflowComponents/AppCard'
;
import
AppCard
from
'../WorkflowComponents/AppCard'
;
import
{
uiWorkflow2StoreWorkflow
}
from
'../WorkflowComponents/utils'
;
import
{
uiWorkflow2StoreWorkflow
}
from
'../WorkflowComponents/utils'
;
import
{
useSystem
}
from
'@fastgpt/web/hooks/useSystem'
;
import
{
useSystem
}
from
'@fastgpt/web/hooks/useSystem'
;
import
MyPopover
from
'@fastgpt/web/components/common/MyPopover'
;
import
MyBox
from
'@fastgpt/web/components/common/MyBox'
;
import
{
useRequest2
}
from
'@fastgpt/web/hooks/useRequest'
;
import
{
useRequest2
}
from
'@fastgpt/web/hooks/useRequest'
;
import
MyModal
from
'@fastgpt/web/components/common/MyModal'
;
import
MyModal
from
'@fastgpt/web/components/common/MyModal'
;
import
{
compareSnapshot
}
from
'@/web/core/workflow/utils'
;
import
{
compareSnapshot
}
from
'@/web/core/workflow/utils'
;
import
SaveAndPublishModal
from
'../WorkflowComponents/Flow/components/SaveAndPublish'
;
import
{
formatTime2YMDHMS
}
from
'@fastgpt/global/common/string/time'
;
import
{
formatTime2YMDHMS
}
from
'@fastgpt/global/common/string/time'
;
import
{
useToast
}
from
'@fastgpt/web/hooks/useToast'
;
import
{
useToast
}
from
'@fastgpt/web/hooks/useToast'
;
import
{
useDebounceEffect
}
from
'ahooks'
;
import
{
useDebounceEffect
}
from
'ahooks'
;
import
{
useSystemStore
}
from
'@/web/common/system/useSystemStore'
;
import
{
useSystemStore
}
from
'@/web/common/system/useSystemStore'
;
import
SaveButton
from
'../Workflow/components/SaveButton'
;
const
PublishHistories
=
dynamic
(()
=>
import
(
'../
Workflow
PublishHistoriesSlider'
));
const
PublishHistories
=
dynamic
(()
=>
import
(
'../PublishHistoriesSlider'
));
const
Header
=
()
=>
{
const
Header
=
()
=>
{
const
{
t
}
=
useTranslation
();
const
{
t
}
=
useTranslation
();
...
@@ -48,12 +46,6 @@ const Header = () => {
...
@@ -48,12 +46,6 @@ const Header = () => {
onOpen
:
onOpenBackConfirm
,
onOpen
:
onOpenBackConfirm
,
onClose
:
onCloseBackConfirm
onClose
:
onCloseBackConfirm
}
=
useDisclosure
();
}
=
useDisclosure
();
const
{
isOpen
:
isSaveAndPublishModalOpen
,
onOpen
:
onSaveAndPublishModalOpen
,
onClose
:
onSaveAndPublishModalClose
}
=
useDisclosure
();
const
[
isSave
,
setIsSave
]
=
useState
(
false
);
const
{
const
{
flowData2StoreData
,
flowData2StoreData
,
...
@@ -65,7 +57,9 @@ const Header = () => {
...
@@ -65,7 +57,9 @@ const Header = () => {
edges
,
edges
,
past
,
past
,
future
,
future
,
setPast
setPast
,
saveSnapshot
,
resetSnapshot
}
=
useContextSelector
(
WorkflowContext
,
(
v
)
=>
v
);
}
=
useContextSelector
(
WorkflowContext
,
(
v
)
=>
v
);
const
{
lastAppListRouteType
}
=
useSystemStore
();
const
{
lastAppListRouteType
}
=
useSystemStore
();
...
@@ -225,81 +219,11 @@ const Header = () => {
...
@@ -225,81 +219,11 @@ const Header = () => {
{
t
(
'common:core.workflow.Run'
)
}
{
t
(
'common:core.workflow.Run'
)
}
</
Button
>
</
Button
>
{
!
historiesDefaultData
&&
(
{
!
historiesDefaultData
&&
(
<
MyPopover
<
SaveButton
placement=
{
'bottom-end'
}
isLoading=
{
loading
}
hasArrow=
{
false
}
onClickSave=
{
onClickSave
}
offset=
{
[
2
,
4
]
}
checkData=
{
flowData2StoreDataAndCheck
}
w=
{
'116px'
}
/>
onOpenFunc=
{
()
=>
setIsSave
(
true
)
}
onCloseFunc=
{
()
=>
setIsSave
(
false
)
}
trigger=
{
'hover'
}
Trigger=
{
<
Button
size=
{
'sm'
}
rightIcon=
{
<
MyIcon
name=
{
isSave
?
'core/chat/chevronUp'
:
'core/chat/chevronDown'
}
w=
{
[
'14px'
,
'16px'
]
}
/>
}
>
<
Box
>
{
t
(
'common:common.Save'
)
}
</
Box
>
</
Button
>
}
>
{
({
onClose
})
=>
(
<
MyBox
p=
{
1.5
}
>
<
MyBox
display=
{
'flex'
}
size=
{
'md'
}
px=
{
1
}
py=
{
1.5
}
rounded=
{
'4px'
}
_hover=
{
{
color
:
'primary.600'
,
bg
:
'rgba(17, 24, 36, 0.05)'
}
}
cursor=
{
'pointer'
}
isLoading=
{
loading
}
onClick=
{
async
()
=>
{
await
onClickSave
({});
toast
({
status
:
'success'
,
title
:
t
(
'app:saved_success'
),
position
:
'top-right'
});
onClose
();
setIsSave
(
false
);
}
}
>
<
MyIcon
name=
{
'core/workflow/upload'
}
w=
{
'16px'
}
mr=
{
2
}
/>
<
Box
fontSize=
{
'sm'
}
>
{
t
(
'common:core.workflow.Save to cloud'
)
}
</
Box
>
</
MyBox
>
<
Flex
px=
{
1
}
py=
{
1.5
}
rounded=
{
'4px'
}
_hover=
{
{
color
:
'primary.600'
,
bg
:
'rgba(17, 24, 36, 0.05)'
}
}
cursor=
{
'pointer'
}
onClick=
{
()
=>
{
const
data
=
flowData2StoreDataAndCheck
();
if
(
data
)
{
onSaveAndPublishModalOpen
();
}
onClose
();
setIsSave
(
false
);
}
}
>
<
MyIcon
name=
{
'core/workflow/publish'
}
w=
{
'16px'
}
mr=
{
2
}
/>
<
Box
fontSize=
{
'sm'
}
>
{
t
(
'common:core.workflow.Save and publish'
)
}
</
Box
>
{
isSaveAndPublishModalOpen
&&
(
<
SaveAndPublishModal
isLoading=
{
loading
}
onClose=
{
onSaveAndPublishModalClose
}
onClickSave=
{
onClickSave
}
/>
)
}
</
Flex
>
</
MyBox
>
)
}
</
MyPopover
>
)
}
)
}
</
HStack
>
</
HStack
>
)
}
)
}
...
@@ -309,6 +233,9 @@ const Header = () => {
...
@@ -309,6 +233,9 @@ const Header = () => {
onClose=
{
()
=>
{
onClose=
{
()
=>
{
setHistoriesDefaultData
(
undefined
);
setHistoriesDefaultData
(
undefined
);
}
}
}
}
past=
{
past
}
saveSnapshot=
{
saveSnapshot
}
resetSnapshot=
{
resetSnapshot
}
/>
/>
)
}
)
}
<
MyModal
<
MyModal
...
@@ -356,16 +283,12 @@ const Header = () => {
...
@@ -356,16 +283,12 @@ const Header = () => {
loading
,
loading
,
isV2Workflow
,
isV2Workflow
,
historiesDefaultData
,
historiesDefaultData
,
isSave
,
onClickSave
,
onClickSave
,
setHistoriesDefaultData
,
setHistoriesDefaultData
,
appDetail
.
chatConfig
,
appDetail
.
chatConfig
,
flowData2StoreDataAndCheck
,
flowData2StoreDataAndCheck
,
setWorkflowTestData
,
setWorkflowTestData
,
isSaveAndPublishModalOpen
,
toast
onSaveAndPublishModalClose
,
toast
,
onSaveAndPublishModalOpen
]);
]);
return
Render
;
return
Render
;
...
...
projects/app/src/pages/app/detail/components/PublishHistoriesSlider.tsx
View file @
6bb10ca1
import
React
,
{
useCallback
,
useState
}
from
'react'
;
import
React
,
{
useState
}
from
'react'
;
import
{
getPublishList
,
postRevertVersion
}
from
'@/web/core/app/api/version'
;
import
{
getAppVersionDetail
,
getWorkflowVersionList
,
updateAppVersion
}
from
'@/web/core/app/api/version'
;
import
{
useVirtualScrollPagination
}
from
'@fastgpt/web/hooks/useScrollPagination'
;
import
{
useVirtualScrollPagination
}
from
'@fastgpt/web/hooks/useScrollPagination'
;
import
CustomRightDrawer
from
'@fastgpt/web/components/common/MyDrawer/CustomRightDrawer'
;
import
CustomRightDrawer
from
'@fastgpt/web/components/common/MyDrawer/CustomRightDrawer'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
{
Box
,
Button
,
Flex
}
from
'@chakra-ui/react'
;
import
{
Box
,
Button
,
Flex
,
Input
}
from
'@chakra-ui/react'
;
import
{
formatTime2YMDHM
}
from
'@fastgpt/global/common/string/time'
;
import
{
useContextSelector
}
from
'use-context-selector'
;
import
{
useContextSelector
}
from
'use-context-selector'
;
import
{
AppVersionSchemaType
}
from
'@fastgpt/global/core/app/version'
;
import
{
AppContext
}
from
'./context'
;
import
LightRowTabs
from
'@fastgpt/web/components/common/Tabs/LightRowTabs'
;
import
{
SaveSnapshotParams
,
SnapshotsType
}
from
'./WorkflowComponents/context'
;
import
{
formatTime2YMDHMS
}
from
'@fastgpt/global/common/string/time'
;
import
Avatar
from
'@fastgpt/web/components/common/Avatar'
;
import
Tag
from
'@fastgpt/web/components/common/Tag'
;
import
MyIcon
from
'@fastgpt/web/components/common/Icon'
;
import
MyIcon
from
'@fastgpt/web/components/common/Icon'
;
import
MyTooltip
from
'@fastgpt/web/components/common/MyTooltip'
;
import
MyPopover
from
'@fastgpt/web/components/common/MyPopover'
;
import
MyBox
from
'@fastgpt/web/components/common/MyBox'
;
import
{
storeEdgesRenderEdge
,
storeNode2FlowNode
}
from
'@/web/core/workflow/utils'
;
import
{
useUserStore
}
from
'@/web/support/user/useUserStore'
;
import
{
useRequest2
}
from
'@fastgpt/web/hooks/useRequest'
;
import
{
useRequest2
}
from
'@fastgpt/web/hooks/useRequest'
;
import
{
AppContext
}
from
'./context'
;
import
{
useSystemStore
}
from
'@/web/common/system/useSystemStore'
;
import
{
useI18n
}
from
'@/web/context/I18n'
;
import
{
useToast
}
from
'@fastgpt/web/hooks/useToast'
;
import
{
AppSchema
}
from
'@fastgpt/global/core/app/type'
;
import
{
versionListResponse
}
from
'@/pages/api/core/app/version/listWorkflow'
;
import
PopoverConfirm
from
'@fastgpt/web/components/common/MyPopover/PopoverConfirm'
;
export
type
InitProps
=
{
nodes
:
AppSchema
[
'modules'
];
edges
:
AppSchema
[
'edges'
];
chatConfig
:
AppSchema
[
'chatConfig'
];
};
const
PublishHistoriesSlider
=
({
const
PublishHistoriesSlider
=
({
onClose
,
onClose
,
initData
,
past
,
defaultData
saveSnapshot
,
resetSnapshot
,
top
,
bottom
}:
{
}:
{
onClose
:
()
=>
void
;
onClose
:
()
=>
void
;
initData
:
(
data
:
InitProps
)
=>
void
;
past
:
SnapshotsType
[];
defaultData
:
InitProps
;
saveSnapshot
:
(
params
:
SaveSnapshotParams
)
=>
Promise
<
boolean
>
;
resetSnapshot
:
(
state
:
SnapshotsType
)
=>
void
;
top
?:
string
|
number
;
bottom
?:
string
|
number
;
})
=>
{
})
=>
{
const
{
t
}
=
useTranslation
();
const
{
t
}
=
useTranslation
();
const
{
appT
}
=
useI18n
(
);
const
[
currentTab
,
setCurrentTab
]
=
useState
<
'myEdit'
|
'teamCloud'
>
(
'myEdit'
);
const
{
appDetail
,
setAppDetail
,
reloadAppLatestVersion
}
=
useContextSelector
(
return
(
AppContext
,
<>
(
v
)
=>
v
<
CustomRightDrawer
onClose=
{
()
=>
onClose
()
}
title=
{
(
<>
<
LightRowTabs
list=
{
[
{
label
:
t
(
'workflow:workflow.My edit'
),
value
:
'myEdit'
},
{
label
:
t
(
'workflow:workflow.Team cloud'
),
value
:
'teamCloud'
}
]
}
value=
{
currentTab
}
onChange=
{
setCurrentTab
}
inlineStyles=
{
{
px
:
0.5
,
pb
:
2
}
}
gap=
{
5
}
py=
{
0
}
fontSize=
{
'sm'
}
/>
</>
)
as
any
}
maxW=
{
'340px'
}
px=
{
0
}
showMask=
{
false
}
overflow=
{
'unset'
}
top=
{
top
}
bottom=
{
bottom
}
>
{
currentTab
===
'myEdit'
?
(
<
MyEdit
past=
{
past
}
saveSnapshot=
{
saveSnapshot
}
resetSnapshot=
{
resetSnapshot
}
/>
)
:
(
<
TeamCloud
saveSnapshot=
{
saveSnapshot
}
resetSnapshot=
{
resetSnapshot
}
/>
)
}
</
CustomRightDrawer
>
</>
);
);
const
appId
=
appDetail
.
_id
;
}
;
const
[
selectedHistoryId
,
setSelectedHistoryId
]
=
useState
<
string
>
(
);
export
default
React
.
memo
(
PublishHistoriesSlider
);
const
{
scrollDataList
,
ScrollList
,
isLoading
}
=
useVirtualScrollPagination
(
getPublishList
,
{
const
MyEdit
=
({
itemHeight
:
49
,
past
,
overscan
:
20
,
saveSnapshot
,
resetSnapshot
}:
{
past
:
SnapshotsType
[];
saveSnapshot
:
(
params
:
SaveSnapshotParams
)
=>
Promise
<
boolean
>
;
resetSnapshot
:
(
state
:
SnapshotsType
)
=>
void
;
})
=>
{
const
{
t
}
=
useTranslation
();
const
{
toast
}
=
useToast
();
pageSize
:
20
,
return
(
defaultParams
:
{
<
Flex
px=
{
5
}
flex=
{
'1 0 0'
}
flexDirection=
{
'column'
}
>
appId
{
past
.
length
>
0
&&
(
}
<
Box
py=
{
2
}
px=
{
3
}
>
});
<
Button
variant=
{
'whiteBase'
}
w=
{
'full'
}
h=
{
'30px'
}
onClick=
{
async
()
=>
{
const
initialSnapshot
=
past
[
past
.
length
-
1
];
const
onPreview
=
useCallback
(
const
res
=
await
saveSnapshot
({
(
data
:
AppVersionSchemaType
)
=>
{
pastNodes
:
initialSnapshot
.
nodes
,
setSelectedHistoryId
(
data
.
_id
);
pastEdges
:
initialSnapshot
.
edges
,
chatConfig
:
initialSnapshot
.
chatConfig
,
customTitle
:
t
(
`app:app.version_initial_copy`
)
});
initData
({
if
(
res
)
{
nodes
:
data
.
nodes
,
resetSnapshot
(
initialSnapshot
);
edges
:
data
.
edges
,
}
chatConfig
:
data
.
chatConfig
});
},
[
initData
]
);
const
onCloseSlider
=
useCallback
(
(
data
:
InitProps
)
=>
{
setSelectedHistoryId
(
undefined
);
initData
(
data
);
onClose
();
},
[
initData
,
onClose
]
);
const
{
runAsync
:
onRevert
}
=
useRequest2
(
toast
({
async
(
data
:
AppVersionSchemaType
)
=>
{
title
:
t
(
'workflow:workflow.Switch_success'
),
if
(
!
appId
)
return
;
status
:
'success'
await
postRevertVersion
(
appId
,
{
});
versionId
:
data
.
_id
,
}
}
editNodes
:
defaultData
.
nodes
,
// old workflow
>
editEdges
:
defaultData
.
edges
,
{
t
(
'app:app.version_back'
)
}
editChatConfig
:
defaultData
.
chatConfig
</
Button
>
});
</
Box
>
)
}
<
Flex
flex=
{
'1 0 0'
}
flexDirection=
{
'column'
}
overflow=
{
'auto'
}
>
{
past
.
map
((
item
,
index
)
=>
{
return
(
<
Flex
key=
{
index
}
alignItems=
{
'center'
}
py=
{
2
}
px=
{
3
}
borderRadius=
{
'md'
}
cursor=
{
'pointer'
}
fontWeight=
{
500
}
_hover=
{
{
bg
:
'primary.50'
}
}
onClick=
{
async
()
=>
{
const
res
=
await
saveSnapshot
({
pastNodes
:
item
.
nodes
,
pastEdges
:
item
.
edges
,
chatConfig
:
item
.
chatConfig
,
customTitle
:
`${t('app:app.version_copy')}-${item.title}`
});
if
(
res
)
{
resetSnapshot
(
item
);
}
setAppDetail
((
state
)
=>
({
toast
({
...
state
,
title
:
t
(
'workflow:workflow.Switch_success'
),
modules
:
data
.
nodes
,
status
:
'success'
edges
:
data
.
edges
});
}));
}
}
>
<
Box
w=
{
'12px'
}
h=
{
'12px'
}
borderWidth=
{
'2px'
}
borderColor=
{
'primary.600'
}
borderRadius=
{
'50%'
}
position=
{
'relative'
}
{
...
(
index
!==
past
.
length
-
1
&&
{
_after
:
{
content
:
'""',
height
:
'26
px
',
width
:
'2
px
',
bgColor
:
'
myGray
.
250',
position
:
'
absolute
',
top
:
'10
px
',
left
:
'3
px
'
}
})}
></
Box
>
<
Box
ml=
{
3
}
flex=
{
'1 0 0'
}
fontSize=
{
'sm'
}
overflow=
"hidden"
textOverflow=
"ellipsis"
whiteSpace=
"nowrap"
color=
{
'myGray.900'
}
>
{
item
.
title
}
</
Box
>
</
Flex
>
);
})
}
<
Box
py=
{
2
}
textAlign=
{
'center'
}
color=
{
'myGray.600'
}
fontSize=
{
'xs'
}
>
{
t
(
'common:common.No more data'
)
}
</
Box
>
</
Flex
>
</
Flex
>
);
};
onCloseSlider
(
data
);
const
TeamCloud
=
({
reloadAppLatestVersion
();
saveSnapshot
,
},
resetSnapshot
}:
{
saveSnapshot
:
(
params
:
SaveSnapshotParams
)
=>
Promise
<
boolean
>
;
resetSnapshot
:
(
state
:
SnapshotsType
)
=>
void
;
})
=>
{
const
{
t
}
=
useTranslation
();
const
{
appDetail
}
=
useContextSelector
(
AppContext
,
(
v
)
=>
v
);
const
{
loadAndGetTeamMembers
}
=
useUserStore
();
const
{
feConfigs
}
=
useSystemStore
();
const
{
scrollDataList
,
ScrollList
,
isLoading
,
fetchData
}
=
useVirtualScrollPagination
(
getWorkflowVersionList
,
{
{
successToast
:
appT
(
'version.Revert success'
)
itemHeight
:
40
,
overscan
:
20
,
pageSize
:
30
,
defaultParams
:
{
appId
:
appDetail
.
_id
}
}
}
);
);
const
{
data
:
members
=
[]
}
=
useRequest2
(
loadAndGetTeamMembers
,
{
manual
:
!
feConfigs
.
isPlus
});
const
[
editIndex
,
setEditIndex
]
=
useState
<
number
|
undefined
>
(
undefined
);
const
[
hoveredIndex
,
setHoveredIndex
]
=
useState
<
number
|
undefined
>
(
undefined
);
const
[
isEditing
,
setIsEditing
]
=
useState
(
false
);
const
{
toast
}
=
useToast
();
const
{
runAsync
:
onChangeVersion
,
loading
:
isLoadingVersion
}
=
useRequest2
(
async
(
versionItem
:
versionListResponse
)
=>
{
const
versionDetail
=
await
getAppVersionDetail
(
versionItem
.
_id
,
versionItem
.
appId
);
if
(
!
versionDetail
)
return
;
const
showLoading
=
isLoading
;
const
state
=
{
nodes
:
versionDetail
.
nodes
?.
map
((
item
)
=>
storeNode2FlowNode
({
item
,
t
})),
edges
:
versionDetail
.
edges
?.
map
((
item
)
=>
storeEdgesRenderEdge
({
edge
:
item
})),
title
:
versionItem
.
versionName
,
chatConfig
:
versionDetail
.
chatConfig
};
await
saveSnapshot
({
pastNodes
:
state
.
nodes
,
pastEdges
:
state
.
edges
,
chatConfig
:
state
.
chatConfig
,
customTitle
:
`
${
t
(
'app:app.version_copy'
)}
-
${
state
.
title
}
`
});
resetSnapshot
(
state
);
toast
({
title
:
t
(
'workflow:workflow.Switch_success'
),
status
:
'success'
});
}
);
return
(
return
(
<>
<
ScrollList
isLoading=
{
isLoading
||
isLoadingVersion
}
flex=
{
'1 0 0'
}
px=
{
5
}
>
<
CustomRightDrawer
{
scrollDataList
.
map
((
data
,
index
)
=>
{
onClose=
{
()
=>
const
item
=
data
.
data
;
onCloseSlider
({
const
firstPublishedIndex
=
scrollDataList
.
findIndex
((
data
)
=>
data
.
data
.
isPublish
);
nodes
:
defaultData
.
nodes
,
const
tmb
=
members
.
find
((
member
)
=>
member
.
tmbId
===
item
.
tmbId
);
edges
:
defaultData
.
edges
,
chatConfig
:
defaultData
.
chatConfig
return
(
})
<
Flex
}
key=
{
data
.
index
}
iconSrc=
"core/workflow/versionHistories"
alignItems=
{
'center'
}
title=
{
t
(
'common:core.workflow.publish.histories'
)
}
py=
{
editIndex
!==
index
?
2
:
1
}
maxW=
{
'300px'
}
px=
{
3
}
px=
{
0
}
borderRadius=
{
'md'
}
showMask=
{
false
}
cursor=
{
'pointer'
}
overflow=
{
'unset'
}
fontWeight=
{
500
}
>
onMouseEnter=
{
()
=>
setHoveredIndex
(
index
)
}
<
Button
onMouseLeave=
{
()
=>
setHoveredIndex
(
undefined
)
}
mx=
{
'20px'
}
_hover=
{
{
variant=
{
'whitePrimary'
}
bg
:
'primary.50'
mb=
{
2
}
}
}
isDisabled=
{
!
selectedHistoryId
}
onClick=
{
()
=>
editIndex
===
undefined
&&
onChangeVersion
(
item
)
}
onClick=
{
()
=>
{
>
setSelectedHistoryId
(
undefined
);
<
MyPopover
initData
({
trigger=
"hover"
nodes
:
defaultData
.
nodes
,
placement=
{
'bottom-end'
}
edges
:
defaultData
.
edges
,
w=
{
'208px'
}
chatConfig
:
defaultData
.
chatConfig
h=
{
'72px'
}
});
Trigger=
{
}
}
<
Box
>
>
<
Avatar
src=
{
tmb
?.
avatar
}
borderRadius=
{
'50%'
}
w=
{
'24px'
}
h=
{
'24px'
}
/>
{
appT
(
'current_settings'
)
}
</
Box
>
</
Button
>
}
<
ScrollList
isLoading=
{
showLoading
}
flex=
{
'1 0 0'
}
px=
{
5
}
>
>
{
scrollDataList
.
map
((
data
,
index
)
=>
{
{
({
onClose
})
=>
(
const
item
=
data
.
data
;
<
Flex
alignItems=
{
'center'
}
h=
{
'full'
}
pl=
{
5
}
gap=
{
3
}
>
<
Box
>
return
(
<
Avatar
src=
{
tmb
?.
avatar
}
borderRadius=
{
'50%'
}
w=
{
'36px'
}
h=
{
'36px'
}
/>
<
Flex
</
Box
>
key=
{
data
.
index
}
<
Box
>
alignItems=
{
'center'
}
<
Box
fontSize=
{
'14px'
}
color=
{
'myGray.900'
}
>
py=
{
3
}
{
tmb
?.
memberName
}
px=
{
3
}
</
Box
>
borderRadius=
{
'md'
}
<
Box
fontSize=
{
'12px'
}
color=
{
'myGray.500'
}
>
cursor=
{
'pointer'
}
{
formatTime2YMDHMS
(
item
.
time
)
}
fontWeight=
{
500
}
</
Box
>
_hover=
{
{
</
Box
>
bg
:
'primary.50'
</
Flex
>
}
}
)
}
{
...
(
selectedHistoryId
===
item
.
_id
&&
{
</
MyPopover
>
color
:
'
primary
.
600'
{
editIndex
!==
index
?
(
})}
<>
onClick=
{
()
=>
onPreview
(
item
)
}
>
<
Box
<
Box
w=
{
'12px'
}
ml=
{
3
}
h=
{
'12px'
}
flex=
{
'1 0 0'
}
borderWidth=
{
'2px'
}
fontSize=
{
'sm'
}
borderColor=
{
'primary.600'
}
display=
"flex"
borderRadius=
{
'50%'
}
alignItems=
"center"
position=
{
'relative'
}
overflow=
"hidden"
{
...
(
index
!==
scrollDataList
.
length
-
1
&&
{
textOverflow=
"ellipsis"
_after
:
{
whiteSpace=
"nowrap"
content
:
'""',
>
height
:
'40
px
',
<
Box
minWidth=
{
0
}
overflow=
"hidden"
textOverflow=
"ellipsis"
whiteSpace=
"nowrap"
>
width
:
'2
px
',
<
Box
as=
{
'span'
}
color=
{
'myGray.900'
}
>
bgColor
:
'
myGray
.
250',
{
item
.
versionName
||
formatTime2YMDHMS
(
item
.
time
)
}
position
:
'
absolute
',
</
Box
>
top
:
'10
px
',
</
Box
>
left
:
'3
px
'
{
item
.
isPublish
&&
(
}
<
Tag
})}
ml=
{
3
}
></
Box
>
flexShrink=
{
0
}
<
Box
ml=
{
3
}
flex=
{
'1 0 0'
}
>
type=
"borderSolid"
{
formatTime2YMDHM
(
item
.
time
)
}
colorSchema=
{
index
===
firstPublishedIndex
?
'green'
:
'blue'
}
>
{
index
===
firstPublishedIndex
?
t
(
'app:app.version_current'
)
:
t
(
'app:app.version_past'
)
}
</
Tag
>
)
}
</
Box
>
</
Box
>
{
item
.
_id
===
selectedHistoryId
&&
(
{
hoveredIndex
===
index
&&
(
<
PopoverConfirm
<
MyIcon
showCancel
name=
"edit"
content=
{
t
(
'common:core.workflow.publish.OnRevert version confirm'
)
}
w=
{
'18px'
}
onConfirm=
{
()
=>
onRevert
(
item
)
}
ml=
{
2
}
Trigger=
{
_hover=
{
{
color
:
'primary.600'
}
}
<
Box
>
onClick=
{
(
e
)
=>
{
<
MyTooltip
label=
{
t
(
'common:core.workflow.publish.OnRevert version'
)
}
>
e
.
stopPropagation
();
<
MyIcon
setEditIndex
(
index
);
name=
{
'core/workflow/revertVersion'
}
}
}
w=
{
'20px'
}
color=
{
'primary.600'
}
/>
</
MyTooltip
>
</
Box
>
}
/>
/>
)
}
)
}
</
Flex
>
</>
);
)
:
(
})
}
<
MyBox
ml=
{
3
}
isLoading=
{
isEditing
}
size=
{
'md'
}
>
</
ScrollList
>
<
Input
</
CustomRightDrawer
>
autoFocus
</>
h=
{
8
}
defaultValue=
{
item
.
versionName
||
formatTime2YMDHMS
(
item
.
time
)
}
onClick=
{
(
e
)
=>
e
.
stopPropagation
()
}
onBlur=
{
async
(
e
)
=>
{
setIsEditing
(
true
);
await
updateAppVersion
({
appId
:
item
.
appId
,
versionName
:
e
.
target
.
value
,
versionId
:
item
.
_id
});
await
fetchData
();
setEditIndex
(
undefined
);
setIsEditing
(
false
);
}
}
/>
</
MyBox
>
)
}
</
Flex
>
);
})
}
</
ScrollList
>
);
);
};
};
export
default
React
.
memo
(
PublishHistoriesSlider
);
projects/app/src/pages/app/detail/components/RouteTab.tsx
View file @
6bb10ca1
...
@@ -3,13 +3,11 @@ import React, { useCallback, useMemo } from 'react';
...
@@ -3,13 +3,11 @@ import React, { useCallback, useMemo } from 'react';
import
{
AppContext
,
TabEnum
}
from
'./context'
;
import
{
AppContext
,
TabEnum
}
from
'./context'
;
import
{
useRouter
}
from
'next/router'
;
import
{
useRouter
}
from
'next/router'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
{
useI18n
}
from
'@/web/context/I18n'
;
import
{
useContextSelector
}
from
'use-context-selector'
;
import
{
useContextSelector
}
from
'use-context-selector'
;
import
{
AppTypeEnum
}
from
'@fastgpt/global/core/app/constants'
;
import
{
AppTypeEnum
}
from
'@fastgpt/global/core/app/constants'
;
const
RouteTab
=
()
=>
{
const
RouteTab
=
()
=>
{
const
{
t
}
=
useTranslation
();
const
{
t
}
=
useTranslation
();
const
{
appT
}
=
useI18n
();
const
router
=
useRouter
();
const
router
=
useRouter
();
const
{
appDetail
,
currentTab
}
=
useContextSelector
(
AppContext
,
(
v
)
=>
v
);
const
{
appDetail
,
currentTab
}
=
useContextSelector
(
AppContext
,
(
v
)
=>
v
);
...
@@ -28,20 +26,21 @@ const RouteTab = () => {
...
@@ -28,20 +26,21 @@ const RouteTab = () => {
const
tabList
=
useMemo
(
const
tabList
=
useMemo
(
()
=>
[
()
=>
[
{
{
label
:
appDetail
.
type
===
AppTypeEnum
.
plugin
?
appT
(
'setting_plugin'
)
:
appT
(
'setting_app'
),
label
:
appDetail
.
type
===
AppTypeEnum
.
plugin
?
t
(
'app:setting_plugin'
)
:
t
(
'app:setting_app'
),
id
:
TabEnum
.
appEdit
id
:
TabEnum
.
appEdit
},
},
...(
appDetail
.
permission
.
hasManagePer
...(
appDetail
.
permission
.
hasManagePer
?
[
?
[
{
{
label
:
appT
(
'
publish_channel'
),
label
:
t
(
'app:
publish_channel'
),
id
:
TabEnum
.
publish
id
:
TabEnum
.
publish
},
},
{
label
:
appT
(
'
chat_logs'
),
id
:
TabEnum
.
logs
}
{
label
:
t
(
'app:
chat_logs'
),
id
:
TabEnum
.
logs
}
]
]
:
[])
:
[])
],
],
[
appDetail
.
permission
.
hasManagePer
,
appDetail
.
type
,
appT
]
[
appDetail
.
permission
.
hasManagePer
,
appDetail
.
type
]
);
);
return
(
return
(
...
@@ -51,6 +50,7 @@ const RouteTab = () => {
...
@@ -51,6 +50,7 @@ const RouteTab = () => {
key=
{
tab
.
id
}
key=
{
tab
.
id
}
px=
{
2
}
px=
{
2
}
py=
{
0.5
}
py=
{
0.5
}
fontWeight=
{
'medium'
}
{
...
(
currentTab
===
tab
.
id
{
...
(
currentTab
===
tab
.
id
?
{
?
{
color
:
'
primary
.
700'
color
:
'
primary
.
700'
...
...
projects/app/src/pages/app/detail/components/SimpleApp/Edit.tsx
View file @
6bb10ca1
...
@@ -15,21 +15,46 @@ import { cardStyles } from '../constants';
...
@@ -15,21 +15,46 @@ import { cardStyles } from '../constants';
import
styles
from
'./styles.module.scss'
;
import
styles
from
'./styles.module.scss'
;
import
{
useSystem
}
from
'@fastgpt/web/hooks/useSystem'
;
import
{
useSystem
}
from
'@fastgpt/web/hooks/useSystem'
;
import
{
storeEdgesRenderEdge
,
storeNode2FlowNode
}
from
'@/web/core/workflow/utils'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
{
uiWorkflow2StoreWorkflow
}
from
'../WorkflowComponents/utils'
;
import
{
SnapshotsType
}
from
'../WorkflowComponents/context'
;
const
Edit
=
({
const
Edit
=
({
appForm
,
appForm
,
setAppForm
setAppForm
,
past
,
saveSnapshot
}:
{
}:
{
appForm
:
AppSimpleEditFormType
;
appForm
:
AppSimpleEditFormType
;
setAppForm
:
React
.
Dispatch
<
React
.
SetStateAction
<
AppSimpleEditFormType
>>
;
setAppForm
:
React
.
Dispatch
<
React
.
SetStateAction
<
AppSimpleEditFormType
>>
;
past
:
SnapshotsType
[];
saveSnapshot
:
(
this
:
any
,
{
pastNodes
,
pastEdges
,
chatConfig
,
customTitle
,
isSaved
}:
any
)
=>
Promise
<
boolean
>
;
})
=>
{
})
=>
{
const
{
isPc
}
=
useSystem
();
const
{
isPc
}
=
useSystem
();
const
{
loadAllDatasets
}
=
useDatasetStore
();
const
{
loadAllDatasets
}
=
useDatasetStore
();
const
{
appDetail
}
=
useContextSelector
(
AppContext
,
(
v
)
=>
v
);
const
{
appDetail
}
=
useContextSelector
(
AppContext
,
(
v
)
=>
v
);
const
{
t
}
=
useTranslation
();
// show selected dataset
// show selected dataset
useMount
(()
=>
{
useMount
(()
=>
{
loadAllDatasets
();
loadAllDatasets
();
saveSnapshot
({
pastNodes
:
appDetail
.
modules
?.
map
((
item
)
=>
storeNode2FlowNode
({
item
,
t
})),
pastEdges
:
appDetail
.
edges
?.
map
((
item
)
=>
storeEdgesRenderEdge
({
edge
:
item
})),
chatConfig
:
appDetail
.
chatConfig
,
isSaved
:
true
});
if
(
past
.
length
>
0
)
{
const
storeWorkflow
=
uiWorkflow2StoreWorkflow
(
past
[
0
]);
const
currentAppForm
=
appWorkflow2Form
({
...
storeWorkflow
,
chatConfig
:
past
[
0
].
chatConfig
});
setAppForm
(
currentAppForm
);
return
;
}
setAppForm
(
setAppForm
(
appWorkflow2Form
({
appWorkflow2Form
({
nodes
:
appDetail
.
modules
,
nodes
:
appDetail
.
modules
,
...
@@ -52,7 +77,7 @@ const Edit = ({
...
@@ -52,7 +77,7 @@ const Edit = ({
display=
{
[
'block'
,
'flex'
]
}
display=
{
[
'block'
,
'flex'
]
}
flex=
{
'1 0 0'
}
flex=
{
'1 0 0'
}
h=
{
0
}
h=
{
0
}
pt=
{
[
2
,
1.5
]
}
mt=
{
[
4
,
0
]
}
gap=
{
1
}
gap=
{
1
}
borderRadius=
{
'lg'
}
borderRadius=
{
'lg'
}
overflowY=
{
[
'auto'
,
'unset'
]
}
overflowY=
{
[
'auto'
,
'unset'
]
}
...
@@ -73,7 +98,7 @@ const Edit = ({
...
@@ -73,7 +98,7 @@ const Edit = ({
</
Box
>
</
Box
>
</
Box
>
</
Box
>
{
isPc
&&
(
{
isPc
&&
(
<
Box
{
...
cardStyles
}
boxShadow=
{
'3'
}
flex=
{
'2 0 0'
}
w=
{
0
}
>
<
Box
{
...
cardStyles
}
boxShadow=
{
'3'
}
flex=
{
'2 0 0'
}
w=
{
0
}
mb=
{
3
}
>
<
ChatTest
appForm=
{
appForm
}
/>
<
ChatTest
appForm=
{
appForm
}
/>
</
Box
>
</
Box
>
)
}
)
}
...
...
projects/app/src/pages/app/detail/components/SimpleApp/Header.tsx
View file @
6bb10ca1
import
React
,
{
useCallback
,
use
Memo
,
use
State
}
from
'react'
;
import
React
,
{
useCallback
,
useState
}
from
'react'
;
import
{
useContextSelector
}
from
'use-context-selector'
;
import
{
useContextSelector
}
from
'use-context-selector'
;
import
{
AppContext
}
from
'../context'
;
import
{
AppContext
}
from
'../context'
;
import
FolderPath
from
'@/components/common/folder/Path'
;
import
FolderPath
from
'@/components/common/folder/Path'
;
import
{
useRequest2
}
from
'@fastgpt/web/hooks/useRequest'
;
import
{
useRequest2
}
from
'@fastgpt/web/hooks/useRequest'
;
import
{
getAppFolderPath
}
from
'@/web/core/app/api/app'
;
import
{
getAppFolderPath
}
from
'@/web/core/app/api/app'
;
import
{
Box
,
Button
,
Flex
,
IconButton
}
from
'@chakra-ui/react'
;
import
{
Box
,
Flex
,
IconButton
}
from
'@chakra-ui/react'
;
import
{
useRouter
}
from
'next/router'
;
import
{
useRouter
}
from
'next/router'
;
import
RouteTab
from
'../RouteTab'
;
import
RouteTab
from
'../RouteTab'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
PopoverConfirm
from
'@fastgpt/web/components/common/MyPopover/PopoverConfirm'
;
import
{
AppSimpleEditFormType
}
from
'@fastgpt/global/core/app/type'
;
import
{
AppSimpleEditFormType
}
from
'@fastgpt/global/core/app/type'
;
import
{
AppTypeEnum
}
from
'@fastgpt/global/core/app/constants'
;
import
{
AppTypeEnum
}
from
'@fastgpt/global/core/app/constants'
;
import
{
form2AppWorkflow
}
from
'@/web/core/app/utils'
;
import
{
form2AppWorkflow
}
from
'@/web/core/app/utils'
;
import
{
TabEnum
}
from
'../context'
;
import
{
TabEnum
}
from
'../context'
;
import
PublishHistoriesSlider
,
{
type
InitProps
}
from
'../PublishHistoriesSlider'
;
import
{
appWorkflow2Form
}
from
'@fastgpt/global/core/app/utils'
;
import
MyIcon
from
'@fastgpt/web/components/common/Icon'
;
import
MyIcon
from
'@fastgpt/web/components/common/Icon'
;
import
{
compareWorkflow
}
from
'@/web/core/workflow/utils'
;
import
MyTag
from
'@fastgpt/web/components/common/Tag/index'
;
import
MyTag
from
'@fastgpt/web/components/common/Tag/index'
;
import
{
publishStatusStyle
}
from
'../constants'
;
import
{
publishStatusStyle
}
from
'../constants'
;
import
MyTooltip
from
'@fastgpt/web/components/common/MyTooltip'
;
import
{
useSystem
}
from
'@fastgpt/web/hooks/useSystem'
;
import
{
useSystem
}
from
'@fastgpt/web/hooks/useSystem'
;
import
{
useToast
}
from
'@fastgpt/web/hooks/useToast'
;
import
{
formatTime2YMDHMS
}
from
'@fastgpt/global/common/string/time'
;
import
{
formatTime2YMDHMS
}
from
'@fastgpt/global/common/string/time'
;
import
{
useSystemStore
}
from
'@/web/common/system/useSystemStore'
;
import
{
useSystemStore
}
from
'@/web/common/system/useSystemStore'
;
import
{
useDatasetStore
}
from
'@/web/core/dataset/store/dataset'
;
import
{
useDatasetStore
}
from
'@/web/core/dataset/store/dataset'
;
import
SaveButton
from
'../Workflow/components/SaveButton'
;
import
dynamic
from
'next/dynamic'
;
import
{
useDebounceEffect
}
from
'ahooks'
;
import
{
InitProps
,
SnapshotsType
}
from
'../WorkflowComponents/context'
;
import
{
appWorkflow2Form
}
from
'@fastgpt/global/core/app/utils'
;
import
{
compareSnapshot
,
storeEdgesRenderEdge
,
storeNode2FlowNode
}
from
'@/web/core/workflow/utils'
;
import
{
uiWorkflow2StoreWorkflow
}
from
'../WorkflowComponents/utils'
;
const
PublishHistories
=
dynamic
(()
=>
import
(
'../PublishHistoriesSlider'
));
const
Header
=
({
const
Header
=
({
appForm
,
appForm
,
setAppForm
setAppForm
,
past
,
setPast
,
saveSnapshot
}:
{
}:
{
appForm
:
AppSimpleEditFormType
;
appForm
:
AppSimpleEditFormType
;
setAppForm
:
React
.
Dispatch
<
React
.
SetStateAction
<
AppSimpleEditFormType
>>
;
setAppForm
:
(
form
:
AppSimpleEditFormType
)
=>
void
;
past
:
SnapshotsType
[];
setPast
:
(
value
:
React
.
SetStateAction
<
SnapshotsType
[]
>
)
=>
void
;
saveSnapshot
:
(
this
:
any
,
{
pastNodes
,
pastEdges
,
chatConfig
,
customTitle
,
isSaved
}:
any
)
=>
Promise
<
boolean
>
;
})
=>
{
})
=>
{
const
{
t
}
=
useTranslation
();
const
{
t
}
=
useTranslation
();
const
{
isPc
}
=
useSystem
();
const
{
isPc
}
=
useSystem
();
const
router
=
useRouter
();
const
router
=
useRouter
();
const
{
toast
}
=
useToast
();
const
{
appId
,
appDetail
,
onSaveApp
,
currentTab
}
=
useContextSelector
(
AppContext
,
(
v
)
=>
v
);
const
{
appId
,
appDetail
,
onSaveApp
,
currentTab
}
=
useContextSelector
(
AppContext
,
(
v
)
=>
v
);
const
{
lastAppListRouteType
}
=
useSystemStore
();
const
{
lastAppListRouteType
}
=
useSystemStore
();
const
{
allDatasets
}
=
useDatasetStore
();
const
{
allDatasets
}
=
useDatasetStore
();
...
@@ -45,8 +60,10 @@ const Header = ({
...
@@ -45,8 +60,10 @@ const Header = ({
manual
:
false
,
manual
:
false
,
refreshDeps
:
[
appId
]
refreshDeps
:
[
appId
]
});
});
const
on
c
lickRoute
=
useCallback
(
const
on
C
lickRoute
=
useCallback
(
(
parentId
:
string
)
=>
{
(
parentId
:
string
)
=>
{
localStorage
.
removeItem
(
`
${
appDetail
.
_id
}
-past`
);
router
.
push
({
router
.
push
({
pathname
:
'/app/list'
,
pathname
:
'/app/list'
,
query
:
{
query
:
{
...
@@ -58,58 +75,98 @@ const Header = ({
...
@@ -58,58 +75,98 @@ const Header = ({
[
router
,
lastAppListRouteType
]
[
router
,
lastAppListRouteType
]
);
);
const
isPublished
=
useMemo
(()
=>
{
const
[
isPublished
,
setIsPublished
]
=
useState
(
false
);
const
data
=
form2AppWorkflow
(
appForm
,
t
);
return
compareWorkflow
(
{
nodes
:
appDetail
.
modules
,
edges
:
[],
chatConfig
:
appDetail
.
chatConfig
},
{
nodes
:
data
.
nodes
,
edges
:
[],
chatConfig
:
data
.
chatConfig
}
);
},
[
appDetail
.
chatConfig
,
appDetail
.
modules
,
appForm
,
allDatasets
,
t
]);
const
onSubmitPublish
=
useCallback
(
const
{
runAsync
:
onClickSave
,
loading
}
=
useRequest2
(
async
(
data
:
AppSimpleEditFormType
)
=>
{
async
({
const
{
nodes
,
edges
}
=
form2AppWorkflow
(
data
,
t
);
isPublish
,
versionName
=
formatTime2YMDHMS
(
new
Date
())
}:
{
isPublish
?:
boolean
;
versionName
?:
string
;
})
=>
{
const
{
nodes
,
edges
}
=
form2AppWorkflow
(
appForm
,
t
);
await
onSaveApp
({
await
onSaveApp
({
nodes
,
nodes
,
edges
,
edges
,
chatConfig
:
data
.
chatConfig
,
chatConfig
:
appForm
.
chatConfig
,
type
:
AppTypeEnum
.
simple
,
type
:
AppTypeEnum
.
simple
,
isPublish
:
true
,
isPublish
,
versionName
:
formatTime2YMDHMS
(
new
Date
())
versionName
});
});
toast
({
setPast
((
prevPast
)
=>
status
:
'success'
,
prevPast
.
map
((
item
,
index
)
=>
title
:
t
(
'app:publish_success'
),
index
===
0
position
:
'top-right'
?
{
...
item
,
isSaved
:
true
}
:
item
)
);
}
);
const
[
historiesDefaultData
,
setHistoriesDefaultData
]
=
useState
<
InitProps
>
();
const
resetSnapshot
=
(
data
:
SnapshotsType
)
=>
{
const
storeWorkflow
=
uiWorkflow2StoreWorkflow
(
data
);
const
currentAppForm
=
appWorkflow2Form
({
...
storeWorkflow
,
chatConfig
:
data
.
chatConfig
});
setAppForm
(
currentAppForm
);
};
useDebounceEffect
(
()
=>
{
const
data
=
form2AppWorkflow
(
appForm
,
t
);
saveSnapshot
({
pastNodes
:
data
.
nodes
?.
map
((
item
)
=>
storeNode2FlowNode
({
item
,
t
})),
pastEdges
:
data
.
edges
?.
map
((
item
)
=>
storeEdgesRenderEdge
({
edge
:
item
})),
chatConfig
:
data
.
chatConfig
});
});
},
},
[
onSaveApp
,
t
,
toast
]
[
appForm
],
{
wait
:
500
}
);
);
const
[
historiesDefaultData
,
setHistoriesDefaultData
]
=
useState
<
InitProps
>
();
useDebounceEffect
(
()
=>
{
const
savedSnapshot
=
past
.
find
((
snapshot
)
=>
snapshot
.
isSaved
);
const
data
=
form2AppWorkflow
(
appForm
,
t
);
const
val
=
compareSnapshot
(
{
nodes
:
savedSnapshot
?.
nodes
,
edges
:
[],
chatConfig
:
savedSnapshot
?.
chatConfig
},
{
nodes
:
data
.
nodes
?.
map
((
item
)
=>
storeNode2FlowNode
({
item
,
t
})),
edges
:
[],
chatConfig
:
data
.
chatConfig
}
);
setIsPublished
(
val
);
},
[
past
],
{
wait
:
500
}
);
return
(
return
(
<
Box
>
<
Box
h=
{
14
}
>
{
!
isPc
&&
(
{
!
isPc
&&
(
<
Flex
pt=
{
2
}
justifyContent=
{
'center'
}
>
<
Flex
justifyContent=
{
'center'
}
>
<
RouteTab
/>
<
RouteTab
/>
</
Flex
>
</
Flex
>
)
}
)
}
<
Flex
pt=
{
[
2
,
3
]
}
alignItems=
{
'flex-start'
}
position=
{
'relative
'
}
>
<
Flex
w=
{
'full'
}
alignItems=
{
'center'
}
position=
{
'relative'
}
h=
{
'full
'
}
>
<
Box
flex=
{
'1'
}
>
<
Box
flex=
{
'1'
}
>
<
FolderPath
<
FolderPath
rootName=
{
t
(
'app:all_apps'
)
}
rootName=
{
t
(
'app:all_apps'
)
}
paths=
{
paths
}
paths=
{
paths
}
hoverStyle=
{
{
color
:
'primary.600'
}
}
hoverStyle=
{
{
color
:
'primary.600'
}
}
onClick=
{
onclickRoute
}
onClick=
{
onClickRoute
}
fontSize=
{
'14px'
}
/>
/>
</
Box
>
</
Box
>
{
isPc
&&
(
{
isPc
&&
(
...
@@ -156,36 +213,23 @@ const Header = ({
...
@@ -156,36 +213,23 @@ const Header = ({
});
});
}
}
}
}
/>
/>
<
PopoverConfirm
<
SaveButton
isLoading=
{
loading
}
onClickSave=
{
onClickSave
}
/>
showCancel
content=
{
t
(
'common:core.app.Publish Confirm'
)
}
Trigger=
{
<
Box
>
<
MyTooltip
label=
{
t
(
'common:core.app.Publish app tip'
)
}
>
<
Button
isDisabled=
{
isPublished
}
>
{
t
(
'common:core.app.Publish'
)
}
</
Button
>
</
MyTooltip
>
</
Box
>
}
onConfirm=
{
()
=>
onSubmitPublish
(
appForm
)
}
/>
</>
</>
)
}
)
}
</
Flex
>
</
Flex
>
)
}
)
}
</
Flex
>
</
Flex
>
{
!!
historiesDefaultData
&&
(
{
historiesDefaultData
&&
currentTab
===
TabEnum
.
appEdit
&&
(
<
PublishHistoriesSlider
<
PublishHistories
initData=
{
({
nodes
,
chatConfig
})
=>
{
onClose=
{
()
=>
{
setAppForm
(
setHistoriesDefaultData
(
undefined
);
appWorkflow2Form
({
nodes
,
chatConfig
})
);
}
}
}
}
onClose=
{
()
=>
setHistoriesDefaultData
(
undefined
)
}
past=
{
past
}
defaultData=
{
historiesDefaultData
}
saveSnapshot=
{
saveSnapshot
}
resetSnapshot=
{
resetSnapshot
}
top=
{
14
}
bottom=
{
3
}
/>
/>
)
}
)
}
</
Box
>
</
Box
>
...
...
projects/app/src/pages/app/detail/components/SimpleApp/index.tsx
View file @
6bb10ca1
...
@@ -9,13 +9,15 @@ import dynamic from 'next/dynamic';
...
@@ -9,13 +9,15 @@ import dynamic from 'next/dynamic';
import
{
Box
,
Flex
}
from
'@chakra-ui/react'
;
import
{
Box
,
Flex
}
from
'@chakra-ui/react'
;
import
{
useBeforeunload
}
from
'@fastgpt/web/hooks/useBeforeunload'
;
import
{
useBeforeunload
}
from
'@fastgpt/web/hooks/useBeforeunload'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
useSnapshots
from
'./useSnapshots'
;
const
Logs
=
dynamic
(()
=>
import
(
'../Logs/index'
));
const
Logs
=
dynamic
(()
=>
import
(
'../Logs/index'
));
const
PublishChannel
=
dynamic
(()
=>
import
(
'../Publish'
));
const
PublishChannel
=
dynamic
(()
=>
import
(
'../Publish'
));
const
SimpleEdit
=
()
=>
{
const
SimpleEdit
=
()
=>
{
const
{
t
}
=
useTranslation
();
const
{
t
}
=
useTranslation
();
const
{
currentTab
}
=
useContextSelector
(
AppContext
,
(
v
)
=>
v
);
const
{
currentTab
,
appDetail
}
=
useContextSelector
(
AppContext
,
(
v
)
=>
v
);
const
{
past
,
setPast
,
saveSnapshot
}
=
useSnapshots
(
appDetail
.
_id
);
const
[
appForm
,
setAppForm
]
=
useState
(
getDefaultAppForm
());
const
[
appForm
,
setAppForm
]
=
useState
(
getDefaultAppForm
());
...
@@ -24,12 +26,18 @@ const SimpleEdit = () => {
...
@@ -24,12 +26,18 @@ const SimpleEdit = () => {
});
});
return
(
return
(
<
Flex
h=
{
'100%'
}
flexDirection=
{
'column'
}
px=
{
[
3
,
0
]
}
pr=
{
[
3
,
3
]
}
pb=
{
3
}
>
<
Flex
h=
{
'100%'
}
flexDirection=
{
'column'
}
px=
{
[
3
,
0
]
}
pr=
{
[
3
,
3
]
}
>
<
Header
appForm=
{
appForm
}
setAppForm=
{
setAppForm
}
/>
<
Header
appForm=
{
appForm
}
setAppForm=
{
setAppForm
}
past=
{
past
}
setPast=
{
setPast
}
saveSnapshot=
{
saveSnapshot
}
/>
{
currentTab
===
TabEnum
.
appEdit
?
(
{
currentTab
===
TabEnum
.
appEdit
?
(
<
Edit
appForm=
{
appForm
}
setAppForm=
{
setAppForm
}
/>
<
Edit
appForm=
{
appForm
}
setAppForm=
{
setAppForm
}
past=
{
past
}
saveSnapshot=
{
saveSnapshot
}
/>
)
:
(
)
:
(
<
Box
flex=
{
'1 0 0'
}
h=
{
0
}
mt=
{
4
}
>
<
Box
flex=
{
'1 0 0'
}
h=
{
0
}
mt=
{
[
4
,
0
]
}
>
{
currentTab
===
TabEnum
.
publish
&&
<
PublishChannel
/>
}
{
currentTab
===
TabEnum
.
publish
&&
<
PublishChannel
/>
}
{
currentTab
===
TabEnum
.
logs
&&
<
Logs
/>
}
{
currentTab
===
TabEnum
.
logs
&&
<
Logs
/>
}
</
Box
>
</
Box
>
...
...
projects/app/src/pages/app/detail/components/SimpleApp/useSnapshots.tsx
0 → 100644
View file @
6bb10ca1
import
{
useLocalStorageState
,
useMemoizedFn
}
from
'ahooks'
;
import
{
SnapshotsType
}
from
'../WorkflowComponents/context'
;
import
{
SetStateAction
}
from
'react'
;
import
{
compareSnapshot
}
from
'@/web/core/workflow/utils'
;
import
{
formatTime2YMDHMS
}
from
'@fastgpt/global/common/string/time'
;
const
useSnapshots
=
(
appId
:
string
)
=>
{
const
[
past
,
setPast
]
=
useLocalStorageState
<
SnapshotsType
[]
>
(
`
${
appId
}
-past-simple`
,
{
defaultValue
:
[],
listenStorageChange
:
true
})
as
[
SnapshotsType
[],
(
value
:
SetStateAction
<
SnapshotsType
[]
>
)
=>
void
];
const
saveSnapshot
=
useMemoizedFn
(
async
({
pastNodes
,
pastEdges
,
chatConfig
,
customTitle
,
isSaved
})
=>
{
const
pastState
=
past
[
0
];
const
isPastEqual
=
compareSnapshot
(
{
nodes
:
pastNodes
,
edges
:
pastEdges
,
chatConfig
:
chatConfig
},
{
nodes
:
pastState
?.
nodes
,
edges
:
pastState
?.
edges
,
chatConfig
:
pastState
?.
chatConfig
}
);
if
(
isPastEqual
)
return
false
;
setPast
((
past
)
=>
[
{
nodes
:
pastNodes
,
edges
:
pastEdges
,
title
:
customTitle
||
formatTime2YMDHMS
(
new
Date
()),
chatConfig
,
isSaved
},
...
past
.
slice
(
0
,
199
)
]);
return
true
;
}
);
return
{
past
,
setPast
,
saveSnapshot
};
};
export
default
useSnapshots
;
projects/app/src/pages/app/detail/components/Workflow/Header.tsx
View file @
6bb10ca1
...
@@ -22,18 +22,16 @@ import { useRouter } from 'next/router';
...
@@ -22,18 +22,16 @@ import { useRouter } from 'next/router';
import
AppCard
from
'../WorkflowComponents/AppCard'
;
import
AppCard
from
'../WorkflowComponents/AppCard'
;
import
{
uiWorkflow2StoreWorkflow
}
from
'../WorkflowComponents/utils'
;
import
{
uiWorkflow2StoreWorkflow
}
from
'../WorkflowComponents/utils'
;
import
{
useSystem
}
from
'@fastgpt/web/hooks/useSystem'
;
import
{
useSystem
}
from
'@fastgpt/web/hooks/useSystem'
;
import
MyPopover
from
'@fastgpt/web/components/common/MyPopover'
;
import
MyBox
from
'@fastgpt/web/components/common/MyBox'
;
import
{
useRequest2
}
from
'@fastgpt/web/hooks/useRequest'
;
import
{
useRequest2
}
from
'@fastgpt/web/hooks/useRequest'
;
import
MyModal
from
'@fastgpt/web/components/common/MyModal'
;
import
MyModal
from
'@fastgpt/web/components/common/MyModal'
;
import
{
compareSnapshot
}
from
'@/web/core/workflow/utils'
;
import
{
compareSnapshot
}
from
'@/web/core/workflow/utils'
;
import
SaveAndPublishModal
from
'../WorkflowComponents/Flow/components/SaveAndPublish'
;
import
{
formatTime2YMDHMS
}
from
'@fastgpt/global/common/string/time'
;
import
{
formatTime2YMDHMS
}
from
'@fastgpt/global/common/string/time'
;
import
{
useToast
}
from
'@fastgpt/web/hooks/useToast'
;
import
{
useToast
}
from
'@fastgpt/web/hooks/useToast'
;
import
{
useDebounceEffect
}
from
'ahooks'
;
import
{
useDebounceEffect
}
from
'ahooks'
;
import
{
useSystemStore
}
from
'@/web/common/system/useSystemStore'
;
import
{
useSystemStore
}
from
'@/web/common/system/useSystemStore'
;
import
SaveButton
from
'./components/SaveButton'
;
const
PublishHistories
=
dynamic
(()
=>
import
(
'../
Workflow
PublishHistoriesSlider'
));
const
PublishHistories
=
dynamic
(()
=>
import
(
'../PublishHistoriesSlider'
));
const
Header
=
()
=>
{
const
Header
=
()
=>
{
const
{
t
}
=
useTranslation
();
const
{
t
}
=
useTranslation
();
...
@@ -48,12 +46,6 @@ const Header = () => {
...
@@ -48,12 +46,6 @@ const Header = () => {
onOpen
:
onOpenBackConfirm
,
onOpen
:
onOpenBackConfirm
,
onClose
:
onCloseBackConfirm
onClose
:
onCloseBackConfirm
}
=
useDisclosure
();
}
=
useDisclosure
();
const
{
isOpen
:
isSaveAndPublishModalOpen
,
onOpen
:
onSaveAndPublishModalOpen
,
onClose
:
onSaveAndPublishModalClose
}
=
useDisclosure
();
const
[
isSave
,
setIsSave
]
=
useState
(
false
);
const
{
const
{
flowData2StoreData
,
flowData2StoreData
,
...
@@ -65,7 +57,9 @@ const Header = () => {
...
@@ -65,7 +57,9 @@ const Header = () => {
edges
,
edges
,
past
,
past
,
future
,
future
,
setPast
setPast
,
saveSnapshot
,
resetSnapshot
}
=
useContextSelector
(
WorkflowContext
,
(
v
)
=>
v
);
}
=
useContextSelector
(
WorkflowContext
,
(
v
)
=>
v
);
const
{
lastAppListRouteType
}
=
useSystemStore
();
const
{
lastAppListRouteType
}
=
useSystemStore
();
...
@@ -227,81 +221,11 @@ const Header = () => {
...
@@ -227,81 +221,11 @@ const Header = () => {
{
t
(
'common:core.workflow.Run'
)
}
{
t
(
'common:core.workflow.Run'
)
}
</
Button
>
</
Button
>
{
!
historiesDefaultData
&&
(
{
!
historiesDefaultData
&&
(
<
MyPopover
<
SaveButton
placement=
{
'bottom-end'
}
isLoading=
{
loading
}
hasArrow=
{
false
}
onClickSave=
{
onClickSave
}
offset=
{
[
2
,
4
]
}
checkData=
{
flowData2StoreDataAndCheck
}
w=
{
'116px'
}
/>
onOpenFunc=
{
()
=>
setIsSave
(
true
)
}
onCloseFunc=
{
()
=>
setIsSave
(
false
)
}
trigger=
{
'hover'
}
Trigger=
{
<
Button
size=
{
'sm'
}
rightIcon=
{
<
MyIcon
name=
{
isSave
?
'core/chat/chevronUp'
:
'core/chat/chevronDown'
}
w=
{
[
'14px'
,
'16px'
]
}
/>
}
>
<
Box
>
{
t
(
'common:common.Save'
)
}
</
Box
>
</
Button
>
}
>
{
({
onClose
})
=>
(
<
MyBox
p=
{
1.5
}
>
<
MyBox
display=
{
'flex'
}
size=
{
'md'
}
px=
{
1
}
py=
{
1.5
}
rounded=
{
'4px'
}
_hover=
{
{
color
:
'primary.600'
,
bg
:
'rgba(17, 24, 36, 0.05)'
}
}
cursor=
{
'pointer'
}
isLoading=
{
loading
}
onClick=
{
async
()
=>
{
await
onClickSave
({});
toast
({
status
:
'success'
,
title
:
t
(
'app:saved_success'
),
position
:
'top-right'
});
onClose
();
setIsSave
(
false
);
}
}
>
<
MyIcon
name=
{
'core/workflow/upload'
}
w=
{
'16px'
}
mr=
{
2
}
/>
<
Box
fontSize=
{
'sm'
}
>
{
t
(
'common:core.workflow.Save to cloud'
)
}
</
Box
>
</
MyBox
>
<
Flex
px=
{
1
}
py=
{
1.5
}
rounded=
{
'4px'
}
_hover=
{
{
color
:
'primary.600'
,
bg
:
'rgba(17, 24, 36, 0.05)'
}
}
cursor=
{
'pointer'
}
onClick=
{
()
=>
{
const
data
=
flowData2StoreDataAndCheck
();
if
(
data
)
{
onSaveAndPublishModalOpen
();
}
onClose
();
setIsSave
(
false
);
}
}
>
<
MyIcon
name=
{
'core/workflow/publish'
}
w=
{
'16px'
}
mr=
{
2
}
/>
<
Box
fontSize=
{
'sm'
}
>
{
t
(
'common:core.workflow.Save and publish'
)
}
</
Box
>
{
isSaveAndPublishModalOpen
&&
(
<
SaveAndPublishModal
isLoading=
{
loading
}
onClose=
{
onSaveAndPublishModalClose
}
onClickSave=
{
onClickSave
}
/>
)
}
</
Flex
>
</
MyBox
>
)
}
</
MyPopover
>
)
}
)
}
</
HStack
>
</
HStack
>
)
}
)
}
...
@@ -311,8 +235,12 @@ const Header = () => {
...
@@ -311,8 +235,12 @@ const Header = () => {
onClose=
{
()
=>
{
onClose=
{
()
=>
{
setHistoriesDefaultData
(
undefined
);
setHistoriesDefaultData
(
undefined
);
}
}
}
}
past=
{
past
}
saveSnapshot=
{
saveSnapshot
}
resetSnapshot=
{
resetSnapshot
}
/>
/>
)
}
)
}
<
MyModal
<
MyModal
isOpen=
{
isOpenBackConfirm
}
isOpen=
{
isOpenBackConfirm
}
onClose=
{
onCloseBackConfirm
}
onClose=
{
onCloseBackConfirm
}
...
@@ -358,16 +286,13 @@ const Header = () => {
...
@@ -358,16 +286,13 @@ const Header = () => {
loading
,
loading
,
isV2Workflow
,
isV2Workflow
,
historiesDefaultData
,
historiesDefaultData
,
isSave
,
onClickSave
,
onClickSave
,
setHistoriesDefaultData
,
setHistoriesDefaultData
,
appDetail
.
chatConfig
,
appDetail
.
chatConfig
,
flowData2StoreDataAndCheck
,
flowData2StoreDataAndCheck
,
setWorkflowTestData
,
setWorkflowTestData
,
isSaveAndPublishModalOpen
,
onSaveAndPublishModalClose
,
toast
,
toast
,
onSaveAndPublishModalOpen
past
]);
]);
return
Render
;
return
Render
;
...
...
projects/app/src/pages/app/detail/components/Workflow/components/SaveButton.tsx
0 → 100644
View file @
6bb10ca1
import
{
Box
,
Button
,
Flex
,
useDisclosure
}
from
'@chakra-ui/react'
;
import
MyPopover
from
'@fastgpt/web/components/common/MyPopover'
;
import
React
,
{
useState
}
from
'react'
;
import
MyIcon
from
'@fastgpt/web/components/common/Icon'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
MyBox
from
'@fastgpt/web/components/common/MyBox'
;
import
{
useToast
}
from
'@fastgpt/web/hooks/useToast'
;
import
SaveAndPublishModal
from
'../../WorkflowComponents/Flow/components/SaveAndPublish'
;
import
{
StoreNodeItemType
}
from
'@fastgpt/global/core/workflow/type/node'
;
import
{
StoreEdgeItemType
}
from
'@fastgpt/global/core/workflow/type/edge'
;
const
SaveButton
=
({
isLoading
,
onClickSave
,
checkData
}:
{
isLoading
:
boolean
;
onClickSave
:
(
options
:
{
isPublish
?:
boolean
;
versionName
?:
string
})
=>
Promise
<
void
>
;
checkData
?:
(
hideTip
?:
boolean
)
=>
|
{
nodes
:
StoreNodeItemType
[];
edges
:
StoreEdgeItemType
[];
}
|
undefined
;
})
=>
{
const
{
t
}
=
useTranslation
();
const
[
isSave
,
setIsSave
]
=
useState
(
false
);
const
{
toast
}
=
useToast
();
const
{
isOpen
:
isSaveAndPublishModalOpen
,
onOpen
:
onSaveAndPublishModalOpen
,
onClose
:
onSaveAndPublishModalClose
}
=
useDisclosure
();
return
(
<
MyPopover
placement=
{
'bottom-end'
}
hasArrow=
{
false
}
offset=
{
[
2
,
4
]
}
w=
{
'116px'
}
onOpenFunc=
{
()
=>
setIsSave
(
true
)
}
onCloseFunc=
{
()
=>
setIsSave
(
false
)
}
trigger=
{
'hover'
}
Trigger=
{
<
Button
size=
{
'sm'
}
rightIcon=
{
<
MyIcon
name=
{
isSave
?
'core/chat/chevronUp'
:
'core/chat/chevronDown'
}
w=
{
[
'14px'
,
'16px'
]
}
/>
}
>
<
Box
>
{
t
(
'common:common.Save'
)
}
</
Box
>
</
Button
>
}
>
{
({
onClose
})
=>
(
<
Box
p=
{
1.5
}
>
<
MyBox
display=
{
'flex'
}
size=
{
'md'
}
px=
{
1
}
py=
{
1.5
}
rounded=
{
'4px'
}
_hover=
{
{
color
:
'primary.600'
,
bg
:
'rgba(17, 24, 36, 0.05)'
}
}
cursor=
{
'pointer'
}
isLoading=
{
isLoading
}
onClick=
{
async
()
=>
{
await
onClickSave
({});
toast
({
status
:
'success'
,
title
:
t
(
'app:saved_success'
),
position
:
'top-right'
});
onClose
();
setIsSave
(
false
);
}
}
>
<
MyIcon
name=
{
'core/workflow/upload'
}
w=
{
'16px'
}
mr=
{
2
}
/>
<
Box
fontSize=
{
'sm'
}
>
{
t
(
'common:core.workflow.Save to cloud'
)
}
</
Box
>
</
MyBox
>
<
Flex
px=
{
1
}
py=
{
1.5
}
rounded=
{
'4px'
}
_hover=
{
{
color
:
'primary.600'
,
bg
:
'rgba(17, 24, 36, 0.05)'
}
}
cursor=
{
'pointer'
}
onClick=
{
()
=>
{
const
canOpen
=
!
checkData
||
checkData
();
if
(
canOpen
)
{
onSaveAndPublishModalOpen
();
}
onClose
();
setIsSave
(
false
);
}
}
>
<
MyIcon
name=
{
'core/workflow/publish'
}
w=
{
'16px'
}
mr=
{
2
}
/>
<
Box
fontSize=
{
'sm'
}
>
{
t
(
'common:core.workflow.Save and publish'
)
}
</
Box
>
{
isSaveAndPublishModalOpen
&&
(
<
SaveAndPublishModal
isLoading=
{
isLoading
}
onClose=
{
onSaveAndPublishModalClose
}
onClickSave=
{
onClickSave
}
/>
)
}
</
Flex
>
</
Box
>
)
}
</
MyPopover
>
);
};
export
default
React
.
memo
(
SaveButton
);
projects/app/src/pages/app/detail/components/WorkflowComponents/context.tsx
View file @
6bb10ca1
...
@@ -39,7 +39,7 @@ import { defaultRunningStatus } from './constants';
...
@@ -39,7 +39,7 @@ import { defaultRunningStatus } from './constants';
import
{
checkNodeRunStatus
}
from
'@fastgpt/global/core/workflow/runtime/utils'
;
import
{
checkNodeRunStatus
}
from
'@fastgpt/global/core/workflow/runtime/utils'
;
import
{
EventNameEnum
,
eventBus
}
from
'@/web/common/utils/eventbus'
;
import
{
EventNameEnum
,
eventBus
}
from
'@/web/common/utils/eventbus'
;
import
{
getHandleId
}
from
'@fastgpt/global/core/workflow/utils'
;
import
{
getHandleId
}
from
'@fastgpt/global/core/workflow/utils'
;
import
{
AppChatConfigType
}
from
'@fastgpt/global/core/app/type'
;
import
{
AppChatConfigType
,
AppSchema
}
from
'@fastgpt/global/core/app/type'
;
import
{
AppContext
}
from
'@/pages/app/detail/components/context'
;
import
{
AppContext
}
from
'@/pages/app/detail/components/context'
;
import
ChatTest
from
'./Flow/ChatTest'
;
import
ChatTest
from
'./Flow/ChatTest'
;
import
{
useDisclosure
}
from
'@chakra-ui/react'
;
import
{
useDisclosure
}
from
'@chakra-ui/react'
;
...
@@ -47,7 +47,6 @@ import { uiWorkflow2StoreWorkflow } from './utils';
...
@@ -47,7 +47,6 @@ import { uiWorkflow2StoreWorkflow } from './utils';
import
{
useTranslation
}
from
'next-i18next'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
{
useRequest2
}
from
'@fastgpt/web/hooks/useRequest'
;
import
{
useRequest2
}
from
'@fastgpt/web/hooks/useRequest'
;
import
{
formatTime2YMDHMS
,
formatTime2YMDHMW
}
from
'@fastgpt/global/common/string/time'
;
import
{
formatTime2YMDHMS
,
formatTime2YMDHMW
}
from
'@fastgpt/global/common/string/time'
;
import
type
{
InitProps
}
from
'@/pages/app/detail/components/PublishHistoriesSlider'
;
import
{
cloneDeep
}
from
'lodash'
;
import
{
cloneDeep
}
from
'lodash'
;
import
{
SetState
}
from
'ahooks/lib/createUseStorageState'
;
import
{
SetState
}
from
'ahooks/lib/createUseStorageState'
;
...
@@ -60,6 +59,18 @@ export type SnapshotsType = {
...
@@ -60,6 +59,18 @@ export type SnapshotsType = {
chatConfig
:
AppChatConfigType
;
chatConfig
:
AppChatConfigType
;
isSaved
?:
boolean
;
isSaved
?:
boolean
;
};
};
export
type
SaveSnapshotParams
=
{
pastNodes
?:
Node
[];
pastEdges
?:
Edge
[];
customTitle
?:
string
;
chatConfig
?:
AppChatConfigType
;
};
export
type
InitProps
=
{
nodes
:
AppSchema
[
'modules'
];
edges
:
AppSchema
[
'edges'
];
chatConfig
:
AppSchema
[
'chatConfig'
];
};
type
WorkflowContextType
=
{
type
WorkflowContextType
=
{
appId
?:
string
;
appId
?:
string
;
basicNodeTemplates
:
FlowNodeTemplateType
[];
basicNodeTemplates
:
FlowNodeTemplateType
[];
...
...
projects/app/src/pages/app/detail/components/WorkflowPublishHistoriesSlider.tsx
deleted
100644 → 0
View file @
7aa75f8e
import
React
,
{
useState
}
from
'react'
;
import
{
getAppVersionDetail
,
getWorkflowVersionList
,
updateAppVersion
}
from
'@/web/core/app/api/version'
;
import
{
useVirtualScrollPagination
}
from
'@fastgpt/web/hooks/useScrollPagination'
;
import
CustomRightDrawer
from
'@fastgpt/web/components/common/MyDrawer/CustomRightDrawer'
;
import
{
useTranslation
}
from
'next-i18next'
;
import
{
Box
,
Button
,
Flex
,
Input
}
from
'@chakra-ui/react'
;
import
{
useContextSelector
}
from
'use-context-selector'
;
import
{
AppContext
}
from
'./context'
;
import
LightRowTabs
from
'@fastgpt/web/components/common/Tabs/LightRowTabs'
;
import
{
WorkflowContext
}
from
'./WorkflowComponents/context'
;
import
{
formatTime2YMDHMS
}
from
'@fastgpt/global/common/string/time'
;
import
Avatar
from
'@fastgpt/web/components/common/Avatar'
;
import
Tag
from
'@fastgpt/web/components/common/Tag'
;
import
MyIcon
from
'@fastgpt/web/components/common/Icon'
;
import
MyPopover
from
'@fastgpt/web/components/common/MyPopover'
;
import
MyBox
from
'@fastgpt/web/components/common/MyBox'
;
import
{
storeEdgesRenderEdge
,
storeNode2FlowNode
}
from
'@/web/core/workflow/utils'
;
import
{
useUserStore
}
from
'@/web/support/user/useUserStore'
;
import
{
useRequest2
}
from
'@fastgpt/web/hooks/useRequest'
;
import
{
useSystemStore
}
from
'@/web/common/system/useSystemStore'
;
import
{
useToast
}
from
'@fastgpt/web/hooks/useToast'
;
import
{
versionListResponse
}
from
'@/pages/api/core/app/version/listWorkflow'
;
const
WorkflowPublishHistoriesSlider
=
({
onClose
}:
{
onClose
:
()
=>
void
})
=>
{
const
{
t
}
=
useTranslation
();
const
[
currentTab
,
setCurrentTab
]
=
useState
<
'myEdit'
|
'teamCloud'
>
(
'myEdit'
);
return
(
<>
<
CustomRightDrawer
onClose=
{
()
=>
onClose
()
}
title=
{
(
<>
<
LightRowTabs
list=
{
[
{
label
:
t
(
'workflow:workflow.My edit'
),
value
:
'myEdit'
},
{
label
:
t
(
'workflow:workflow.Team cloud'
),
value
:
'teamCloud'
}
]
}
value=
{
currentTab
}
onChange=
{
setCurrentTab
}
inlineStyles=
{
{
px
:
0.5
,
pb
:
2
}
}
gap=
{
5
}
py=
{
0
}
fontSize=
{
'sm'
}
/>
</>
)
as
any
}
maxW=
{
'340px'
}
px=
{
0
}
showMask=
{
false
}
overflow=
{
'unset'
}
>
{
currentTab
===
'myEdit'
?
<
MyEdit
/>
:
<
TeamCloud
/>
}
</
CustomRightDrawer
>
</>
);
};
export
default
React
.
memo
(
WorkflowPublishHistoriesSlider
);
const
MyEdit
=
()
=>
{
const
{
past
,
saveSnapshot
,
resetSnapshot
}
=
useContextSelector
(
WorkflowContext
,
(
v
)
=>
v
);
const
{
t
}
=
useTranslation
();
const
{
toast
}
=
useToast
();
return
(
<
Flex
px=
{
5
}
flex=
{
'1 0 0'
}
flexDirection=
{
'column'
}
>
{
past
.
length
>
0
&&
(
<
Box
py=
{
2
}
px=
{
3
}
>
<
Button
variant=
{
'whiteBase'
}
w=
{
'full'
}
h=
{
'30px'
}
onClick=
{
async
()
=>
{
const
initialSnapshot
=
past
[
past
.
length
-
1
];
const
res
=
await
saveSnapshot
({
pastNodes
:
initialSnapshot
.
nodes
,
pastEdges
:
initialSnapshot
.
edges
,
chatConfig
:
initialSnapshot
.
chatConfig
,
customTitle
:
t
(
`app:app.version_initial_copy`
)
});
if
(
res
)
{
resetSnapshot
(
initialSnapshot
);
}
toast
({
title
:
t
(
'workflow:workflow.Switch_success'
),
status
:
'success'
});
}
}
>
{
t
(
'app:app.version_back'
)
}
</
Button
>
</
Box
>
)
}
<
Flex
flex=
{
'1 0 0'
}
flexDirection=
{
'column'
}
overflow=
{
'auto'
}
>
{
past
.
map
((
item
,
index
)
=>
{
return
(
<
Flex
key=
{
index
}
alignItems=
{
'center'
}
py=
{
2
}
px=
{
3
}
borderRadius=
{
'md'
}
cursor=
{
'pointer'
}
fontWeight=
{
500
}
_hover=
{
{
bg
:
'primary.50'
}
}
onClick=
{
async
()
=>
{
const
res
=
await
saveSnapshot
({
pastNodes
:
item
.
nodes
,
pastEdges
:
item
.
edges
,
chatConfig
:
item
.
chatConfig
,
customTitle
:
`${t('app:app.version_copy')}-${item.title}`
});
if
(
res
)
{
resetSnapshot
(
item
);
}
toast
({
title
:
t
(
'workflow:workflow.Switch_success'
),
status
:
'success'
});
}
}
>
<
Box
w=
{
'12px'
}
h=
{
'12px'
}
borderWidth=
{
'2px'
}
borderColor=
{
'primary.600'
}
borderRadius=
{
'50%'
}
position=
{
'relative'
}
{
...
(
index
!==
past
.
length
-
1
&&
{
_after
:
{
content
:
'""',
height
:
'26
px
',
width
:
'2
px
',
bgColor
:
'
myGray
.
250',
position
:
'
absolute
',
top
:
'10
px
',
left
:
'3
px
'
}
})}
></
Box
>
<
Box
ml=
{
3
}
flex=
{
'1 0 0'
}
fontSize=
{
'sm'
}
overflow=
"hidden"
textOverflow=
"ellipsis"
whiteSpace=
"nowrap"
color=
{
'myGray.900'
}
>
{
item
.
title
}
</
Box
>
</
Flex
>
);
})
}
<
Box
py=
{
2
}
textAlign=
{
'center'
}
color=
{
'myGray.600'
}
fontSize=
{
'xs'
}
>
{
t
(
'common:common.No more data'
)
}
</
Box
>
</
Flex
>
</
Flex
>
);
};
const
TeamCloud
=
()
=>
{
const
{
t
}
=
useTranslation
();
const
{
appDetail
}
=
useContextSelector
(
AppContext
,
(
v
)
=>
v
);
const
{
saveSnapshot
,
resetSnapshot
}
=
useContextSelector
(
WorkflowContext
,
(
v
)
=>
v
);
const
{
loadAndGetTeamMembers
}
=
useUserStore
();
const
{
feConfigs
}
=
useSystemStore
();
const
{
scrollDataList
,
ScrollList
,
isLoading
,
fetchData
}
=
useVirtualScrollPagination
(
getWorkflowVersionList
,
{
itemHeight
:
40
,
overscan
:
20
,
pageSize
:
30
,
defaultParams
:
{
appId
:
appDetail
.
_id
}
}
);
const
{
data
:
members
=
[]
}
=
useRequest2
(
loadAndGetTeamMembers
,
{
manual
:
!
feConfigs
.
isPlus
});
const
[
editIndex
,
setEditIndex
]
=
useState
<
number
|
undefined
>
(
undefined
);
const
[
hoveredIndex
,
setHoveredIndex
]
=
useState
<
number
|
undefined
>
(
undefined
);
const
[
isEditing
,
setIsEditing
]
=
useState
(
false
);
const
{
toast
}
=
useToast
();
const
{
runAsync
:
onChangeVersion
,
loading
:
isLoadingVersion
}
=
useRequest2
(
async
(
versionItem
:
versionListResponse
)
=>
{
const
versionDetail
=
await
getAppVersionDetail
(
versionItem
.
_id
,
versionItem
.
appId
);
if
(
!
versionDetail
)
return
;
const
state
=
{
nodes
:
versionDetail
.
nodes
?.
map
((
item
)
=>
storeNode2FlowNode
({
item
,
t
})),
edges
:
versionDetail
.
edges
?.
map
((
item
)
=>
storeEdgesRenderEdge
({
edge
:
item
})),
title
:
versionItem
.
versionName
,
chatConfig
:
versionDetail
.
chatConfig
};
await
saveSnapshot
({
pastNodes
:
state
.
nodes
,
pastEdges
:
state
.
edges
,
chatConfig
:
state
.
chatConfig
,
customTitle
:
`
${
t
(
'app:app.version_copy'
)}
-
${
state
.
title
}
`
});
resetSnapshot
(
state
);
toast
({
title
:
t
(
'workflow:workflow.Switch_success'
),
status
:
'success'
});
}
);
return
(
<
ScrollList
isLoading=
{
isLoading
||
isLoadingVersion
}
flex=
{
'1 0 0'
}
px=
{
5
}
>
{
scrollDataList
.
map
((
data
,
index
)
=>
{
const
item
=
data
.
data
;
const
firstPublishedIndex
=
scrollDataList
.
findIndex
((
data
)
=>
data
.
data
.
isPublish
);
const
tmb
=
members
.
find
((
member
)
=>
member
.
tmbId
===
item
.
tmbId
);
return
(
<
Flex
key=
{
data
.
index
}
alignItems=
{
'center'
}
py=
{
editIndex
!==
index
?
2
:
1
}
px=
{
3
}
borderRadius=
{
'md'
}
cursor=
{
'pointer'
}
fontWeight=
{
500
}
onMouseEnter=
{
()
=>
setHoveredIndex
(
index
)
}
onMouseLeave=
{
()
=>
setHoveredIndex
(
undefined
)
}
_hover=
{
{
bg
:
'primary.50'
}
}
onClick=
{
()
=>
editIndex
===
undefined
&&
onChangeVersion
(
item
)
}
>
<
MyPopover
trigger=
"hover"
placement=
{
'bottom-end'
}
w=
{
'208px'
}
h=
{
'72px'
}
Trigger=
{
<
Box
>
<
Avatar
src=
{
tmb
?.
avatar
}
borderRadius=
{
'50%'
}
w=
{
'24px'
}
h=
{
'24px'
}
/>
</
Box
>
}
>
{
({
onClose
})
=>
(
<
Flex
alignItems=
{
'center'
}
h=
{
'full'
}
pl=
{
5
}
gap=
{
3
}
>
<
Box
>
<
Avatar
src=
{
tmb
?.
avatar
}
borderRadius=
{
'50%'
}
w=
{
'36px'
}
h=
{
'36px'
}
/>
</
Box
>
<
Box
>
<
Box
fontSize=
{
'14px'
}
color=
{
'myGray.900'
}
>
{
tmb
?.
memberName
}
</
Box
>
<
Box
fontSize=
{
'12px'
}
color=
{
'myGray.500'
}
>
{
formatTime2YMDHMS
(
item
.
time
)
}
</
Box
>
</
Box
>
</
Flex
>
)
}
</
MyPopover
>
{
editIndex
!==
index
?
(
<>
<
Box
ml=
{
3
}
flex=
{
'1 0 0'
}
fontSize=
{
'sm'
}
display=
"flex"
alignItems=
"center"
overflow=
"hidden"
textOverflow=
"ellipsis"
whiteSpace=
"nowrap"
>
<
Box
minWidth=
{
0
}
overflow=
"hidden"
textOverflow=
"ellipsis"
whiteSpace=
"nowrap"
>
<
Box
as=
{
'span'
}
color=
{
'myGray.900'
}
>
{
item
.
versionName
||
formatTime2YMDHMS
(
item
.
time
)
}
</
Box
>
</
Box
>
{
item
.
isPublish
&&
(
<
Tag
ml=
{
3
}
flexShrink=
{
0
}
type=
"borderSolid"
colorSchema=
{
index
===
firstPublishedIndex
?
'green'
:
'blue'
}
>
{
index
===
firstPublishedIndex
?
t
(
'app:app.version_current'
)
:
t
(
'app:app.version_past'
)
}
</
Tag
>
)
}
</
Box
>
{
hoveredIndex
===
index
&&
(
<
MyIcon
name=
"edit"
w=
{
'18px'
}
ml=
{
2
}
_hover=
{
{
color
:
'primary.600'
}
}
onClick=
{
(
e
)
=>
{
e
.
stopPropagation
();
setEditIndex
(
index
);
}
}
/>
)
}
</>
)
:
(
<
MyBox
ml=
{
3
}
isLoading=
{
isEditing
}
size=
{
'md'
}
>
<
Input
autoFocus
h=
{
8
}
defaultValue=
{
item
.
versionName
||
formatTime2YMDHMS
(
item
.
time
)
}
onClick=
{
(
e
)
=>
e
.
stopPropagation
()
}
onBlur=
{
async
(
e
)
=>
{
setIsEditing
(
true
);
await
updateAppVersion
({
appId
:
item
.
appId
,
versionName
:
e
.
target
.
value
,
versionId
:
item
.
_id
});
await
fetchData
();
setEditIndex
(
undefined
);
setIsEditing
(
false
);
}
}
/>
</
MyBox
>
)
}
</
Flex
>
);
})
}
</
ScrollList
>
);
};
projects/app/src/pages/app/detail/components/context.tsx
View file @
6bb10ca1
import
{
Dispatch
,
ReactNode
,
SetStateAction
,
useCallback
,
useState
}
from
'react'
;
import
{
Dispatch
,
ReactNode
,
SetStateAction
,
useCallback
,
use
Effect
,
use
State
}
from
'react'
;
import
{
createContext
}
from
'use-context-selector'
;
import
{
createContext
}
from
'use-context-selector'
;
import
{
defaultApp
}
from
'@/web/core/app/constants'
;
import
{
defaultApp
}
from
'@/web/core/app/constants'
;
import
{
delAppById
,
getAppDetailById
,
putAppById
}
from
'@/web/core/app/api'
;
import
{
delAppById
,
getAppDetailById
,
putAppById
}
from
'@/web/core/app/api'
;
...
@@ -11,7 +11,6 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
...
@@ -11,7 +11,6 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import
dynamic
from
'next/dynamic'
;
import
dynamic
from
'next/dynamic'
;
import
{
useDisclosure
}
from
'@chakra-ui/react'
;
import
{
useDisclosure
}
from
'@chakra-ui/react'
;
import
{
useConfirm
}
from
'@fastgpt/web/hooks/useConfirm'
;
import
{
useConfirm
}
from
'@fastgpt/web/hooks/useConfirm'
;
import
{
useI18n
}
from
'@/web/context/I18n'
;
import
type
{
StoreNodeItemType
}
from
'@fastgpt/global/core/workflow/type/node'
;
import
type
{
StoreNodeItemType
}
from
'@fastgpt/global/core/workflow/type/node'
;
import
type
{
StoreEdgeItemType
}
from
'@fastgpt/global/core/workflow/type/edge'
;
import
type
{
StoreEdgeItemType
}
from
'@fastgpt/global/core/workflow/type/edge'
;
...
...
projects/app/src/web/core/app/api/version.ts
View file @
6bb10ca1
...
@@ -15,8 +15,8 @@ export const getAppLatestVersion = (data: getLatestVersionQuery) =>
...
@@ -15,8 +15,8 @@ export const getAppLatestVersion = (data: getLatestVersionQuery) =>
export
const
postPublishApp
=
(
appId
:
string
,
data
:
PostPublishAppProps
)
=>
export
const
postPublishApp
=
(
appId
:
string
,
data
:
PostPublishAppProps
)
=>
POST
(
`/core/app/version/publish?appId=
${
appId
}
`
,
data
);
POST
(
`/core/app/version/publish?appId=
${
appId
}
`
,
data
);
export
const
getPublishList
=
(
data
:
PaginationProps
<
{
appId
:
string
}
>
)
=>
//
export const getPublishList = (data: PaginationProps<{ appId: string }>) =>
POST
<
PaginationResponse
<
AppVersionSchemaType
>>
(
'/core/app/version/list'
,
data
);
//
POST<PaginationResponse<AppVersionSchemaType>>('/core/app/version/list', data);
export
const
getWorkflowVersionList
=
(
data
:
PaginationProps
<
{
appId
:
string
}
>
)
=>
export
const
getWorkflowVersionList
=
(
data
:
PaginationProps
<
{
appId
:
string
}
>
)
=>
POST
<
PaginationResponse
<
versionListResponse
>>
(
'/core/app/version/listWorkflow'
,
data
);
POST
<
PaginationResponse
<
versionListResponse
>>
(
'/core/app/version/listWorkflow'
,
data
);
...
@@ -24,8 +24,8 @@ export const getWorkflowVersionList = (data: PaginationProps<{ appId: string }>)
...
@@ -24,8 +24,8 @@ export const getWorkflowVersionList = (data: PaginationProps<{ appId: string }>)
export
const
getAppVersionDetail
=
(
versionId
:
string
,
appId
:
string
)
=>
export
const
getAppVersionDetail
=
(
versionId
:
string
,
appId
:
string
)
=>
GET
<
AppVersionSchemaType
>
(
`/core/app/version/detail?versionId=
${
versionId
}
&appId=
${
appId
}
`
);
GET
<
AppVersionSchemaType
>
(
`/core/app/version/detail?versionId=
${
versionId
}
&appId=
${
appId
}
`
);
export
const
postRevertVersion
=
(
appId
:
string
,
data
:
PostRevertAppProps
)
=>
//
export const postRevertVersion = (appId: string, data: PostRevertAppProps) =>
POST
(
`/core/app/version/revert?appId=
${
appId
}
`
,
data
);
//
POST(`/core/app/version/revert?appId=${appId}`, data);
export
const
updateAppVersion
=
(
data
:
UpdateAppVersionBody
)
=>
export
const
updateAppVersion
=
(
data
:
UpdateAppVersionBody
)
=>
POST
(
`/core/app/version/update`
,
data
);
POST
(
`/core/app/version/update`
,
data
);
projects/app/src/web/core/workflow/utils.ts
View file @
6bb10ca1
...
@@ -438,94 +438,6 @@ export const getLatestNodeTemplate = (
...
@@ -438,94 +438,6 @@ export const getLatestNodeTemplate = (
return
updatedNode
;
return
updatedNode
;
};
};
type
WorkflowType
=
{
nodes
:
StoreNodeItemType
[];
edges
:
StoreEdgeItemType
[];
chatConfig
:
AppChatConfigType
;
};
export
const
compareWorkflow
=
(
workflow1
:
WorkflowType
,
workflow2
:
WorkflowType
)
=>
{
const
clone1
=
cloneDeep
(
workflow1
);
const
clone2
=
cloneDeep
(
workflow2
);
if
(
!
isEqual
(
clone1
.
edges
,
clone2
.
edges
))
{
console
.
log
(
'Edge not equal'
);
return
false
;
}
if
(
clone1
.
chatConfig
&&
clone2
.
chatConfig
&&
!
isEqual
(
{
welcomeText
:
clone1
.
chatConfig
?.
welcomeText
||
''
,
variables
:
clone1
.
chatConfig
?.
variables
||
[],
questionGuide
:
clone1
.
chatConfig
?.
questionGuide
||
false
,
ttsConfig
:
clone1
.
chatConfig
?.
ttsConfig
||
undefined
,
whisperConfig
:
clone1
.
chatConfig
?.
whisperConfig
||
undefined
,
scheduledTriggerConfig
:
clone1
.
chatConfig
?.
scheduledTriggerConfig
||
undefined
,
chatInputGuide
:
clone1
.
chatConfig
?.
chatInputGuide
||
undefined
,
fileSelectConfig
:
clone1
.
chatConfig
?.
fileSelectConfig
||
undefined
},
{
welcomeText
:
clone2
.
chatConfig
?.
welcomeText
||
''
,
variables
:
clone2
.
chatConfig
?.
variables
||
[],
questionGuide
:
clone2
.
chatConfig
?.
questionGuide
||
false
,
ttsConfig
:
clone2
.
chatConfig
?.
ttsConfig
||
undefined
,
whisperConfig
:
clone2
.
chatConfig
?.
whisperConfig
||
undefined
,
scheduledTriggerConfig
:
clone2
.
chatConfig
?.
scheduledTriggerConfig
||
undefined
,
chatInputGuide
:
clone2
.
chatConfig
?.
chatInputGuide
||
undefined
,
fileSelectConfig
:
clone2
.
chatConfig
?.
fileSelectConfig
||
undefined
}
)
)
{
console
.
log
(
'chatConfig not equal'
);
return
false
;
}
const
formatNodes
=
(
nodes
:
StoreNodeItemType
[])
=>
{
return
nodes
.
filter
((
node
)
=>
{
if
(
!
node
)
return
;
if
([
FlowNodeTypeEnum
.
systemConfig
].
includes
(
node
.
flowNodeType
))
return
;
return
true
;
})
.
map
((
node
)
=>
({
flowNodeType
:
node
.
flowNodeType
,
inputs
:
node
.
inputs
.
map
((
input
)
=>
({
key
:
input
.
key
,
selectedTypeIndex
:
input
.
selectedTypeIndex
??
0
,
renderTypeLis
:
input
.
renderTypeList
,
valueType
:
input
.
valueType
,
value
:
input
.
value
??
undefined
})),
outputs
:
node
.
outputs
.
map
((
item
)
=>
({
key
:
item
.
key
,
type
:
item
.
type
,
value
:
item
.
value
??
undefined
})),
name
:
node
.
name
,
intro
:
node
.
intro
,
avatar
:
node
.
avatar
,
version
:
node
.
version
,
position
:
node
.
position
}));
};
const
node1
=
formatNodes
(
clone1
.
nodes
);
const
node2
=
formatNodes
(
clone2
.
nodes
);
// console.log(node1);
// console.log(node2);
node1
.
forEach
((
node
,
i
)
=>
{
if
(
!
isEqual
(
node
,
node2
[
i
]))
{
console
.
log
(
'node not equal'
);
}
});
return
isEqual
(
node1
,
node2
);
};
export
const
compareSnapshot
=
(
export
const
compareSnapshot
=
(
snapshot1
:
{
snapshot1
:
{
nodes
:
Node
<
FlowNodeItemType
,
string
|
undefined
>
[]
|
undefined
;
nodes
:
Node
<
FlowNodeItemType
,
string
|
undefined
>
[]
|
undefined
;
...
...
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