fix: undo & redo (#2493)

* fix: undo & redo

* fix

* fix
This commit is contained in:
heheer
2024-08-24 23:15:28 +08:00
committed by GitHub
parent 3e57c7f559
commit 3248e95d53
17 changed files with 207 additions and 66 deletions

View File

@@ -31,7 +31,8 @@ const CustomRightDrawer = ({
zIndex={100} zIndex={100}
maxW={maxW} maxW={maxW}
w={'100%'} w={'100%'}
h={'90vh'} top={'60px'}
bottom={0}
borderLeftRadius={'lg'} borderLeftRadius={'lg'}
border={'base'} border={'base'}
boxShadow={'2'} boxShadow={'2'}

View File

@@ -92,6 +92,7 @@
"plugin_dispatch_tip": "It is up to the model to decide which plug-ins to add additional capabilities to. If the plug-in is selected, the knowledge base call is also treated as a special plug-in.", "plugin_dispatch_tip": "It is up to the model to decide which plug-ins to add additional capabilities to. If the plug-in is selected, the knowledge base call is also treated as a special plug-in.",
"publish_channel": "Publish channel", "publish_channel": "Publish channel",
"publish_success": "Publish success", "publish_success": "Publish success",
"saved_success": "Saved success",
"search_app": "Search app", "search_app": "Search app",
"setting_app": "Settings", "setting_app": "Settings",
"setting_plugin": "Setting plugin", "setting_plugin": "Setting plugin",

View File

@@ -49,6 +49,7 @@
"workflow": { "workflow": {
"Back_to_current_version": "Back to current version", "Back_to_current_version": "Back to current version",
"My edit": "My edit", "My edit": "My edit",
"Switch_failed": "Switch failed",
"Switch_success": "switch successfully", "Switch_success": "switch successfully",
"Team cloud": "Team cloud", "Team cloud": "Team cloud",
"exit_tips": "Your changes have not been saved. Exiting directly will not save your edits." "exit_tips": "Your changes have not been saved. Exiting directly will not save your edits."

View File

@@ -91,6 +91,7 @@
"plugin_dispatch_tip": "给模型附加额外的能力,具体调用哪些插件,将由模型自主决定。\n若选择了插件知识库调用将自动作为一个特殊的插件。", "plugin_dispatch_tip": "给模型附加额外的能力,具体调用哪些插件,将由模型自主决定。\n若选择了插件知识库调用将自动作为一个特殊的插件。",
"publish_channel": "发布渠道", "publish_channel": "发布渠道",
"publish_success": "发布成功", "publish_success": "发布成功",
"saved_success": "保存成功",
"search_app": "搜索应用", "search_app": "搜索应用",
"setting_app": "应用配置", "setting_app": "应用配置",
"setting_plugin": "插件配置", "setting_plugin": "插件配置",

View File

@@ -49,6 +49,7 @@
"workflow": { "workflow": {
"Back_to_current_version": "回到初始状态", "Back_to_current_version": "回到初始状态",
"My edit": "我的编辑", "My edit": "我的编辑",
"Switch_failed": "切换失败",
"Switch_success": "切换成功", "Switch_success": "切换成功",
"Team cloud": "团队云端", "Team cloud": "团队云端",
"exit_tips": "您的更改尚未保存,「直接退出」将不会保存您的编辑记录。" "exit_tips": "您的更改尚未保存,「直接退出」将不会保存您的编辑记录。"

View File

@@ -0,0 +1,22 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { NextAPI } from '@/service/middleware/entry';
import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema';
import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
type Props = {
versionId: string;
appId: string;
};
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
const { versionId, appId } = req.query as Props;
await authApp({ req, authToken: true, appId, per: ReadPermissionVal });
const result = await MongoAppVersion.findById(versionId);
return result;
}
export default NextAPI(handler);

View File

@@ -0,0 +1,56 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { NextAPI } from '@/service/middleware/entry';
import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema';
import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
type Props = PaginationProps<{
appId: string;
}>;
export type versionListResponse = {
_id: string;
appId: string;
versionName: string;
time: Date;
isPublish: boolean | undefined;
tmbId: string;
};
type Response = PaginationResponse<versionListResponse>;
async function handler(req: NextApiRequest, res: NextApiResponse<any>): Promise<Response> {
const { current, pageSize, appId } = req.body as Props;
const [result, total] = await Promise.all([
MongoAppVersion.find(
{
appId
},
'_id appId versionName time isPublish tmbId'
)
.sort({
time: -1
})
.skip((current - 1) * pageSize)
.limit(pageSize),
MongoAppVersion.countDocuments({ appId })
]);
const versionList = result.map((item) => {
return {
_id: item._id,
appId: item.appId,
versionName: item.versionName,
time: item.time,
isPublish: item.isPublish,
tmbId: item.tmbId
};
});
return {
total,
list: versionList
};
}
export default NextAPI(handler);

View File

@@ -29,6 +29,7 @@ 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 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';
const PublishHistories = dynamic(() => import('../WorkflowPublishHistoriesSlider')); const PublishHistories = dynamic(() => import('../WorkflowPublishHistoriesSlider'));
@@ -36,6 +37,7 @@ const Header = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { isPc } = useSystem(); const { isPc } = useSystem();
const router = useRouter(); const router = useRouter();
const { toast } = useToast();
const { appDetail, onPublish, currentTab } = useContextSelector(AppContext, (v) => v); const { appDetail, onPublish, currentTab } = useContextSelector(AppContext, (v) => v);
const isV2Workflow = appDetail?.version === 'v2'; const isV2Workflow = appDetail?.version === 'v2';
@@ -61,6 +63,9 @@ const Header = () => {
} = useContextSelector(WorkflowContext, (v) => v); } = useContextSelector(WorkflowContext, (v) => v);
const isPublished = useMemo(() => { const isPublished = useMemo(() => {
/*
Find the last saved snapshot in the past and future snapshots
*/
const savedSnapshot = const savedSnapshot =
future.findLast((snapshot) => snapshot.isSaved) || past.find((snapshot) => snapshot.isSaved); future.findLast((snapshot) => snapshot.isSaved) || past.find((snapshot) => snapshot.isSaved);
@@ -97,9 +102,10 @@ const Header = () => {
//@ts-ignore //@ts-ignore
version: 'v2' version: 'v2'
}); });
// Mark the current snapshot as saved
setPast((prevPast) => setPast((prevPast) =>
prevPast.map((item, index) => prevPast.map((item, index) =>
index === prevPast.length - 1 index === 0
? { ? {
...item, ...item,
isSaved: true isSaved: true
@@ -171,6 +177,7 @@ const Header = () => {
isLoading={loading} isLoading={loading}
onClick={async () => { onClick={async () => {
await onClickSave({}); await onClickSave({});
onClose();
onBack(); onBack();
}} }}
> >
@@ -246,7 +253,7 @@ const Header = () => {
</Button> </Button>
} }
> >
{({}) => ( {({ onClose }) => (
<MyBox p={1.5}> <MyBox p={1.5}>
<MyBox <MyBox
display={'flex'} display={'flex'}
@@ -259,6 +266,10 @@ const Header = () => {
isLoading={loading} isLoading={loading}
onClick={async () => { onClick={async () => {
await onClickSave({}); await onClickSave({});
toast({
status: 'success',
title: t('app:saved_success')
});
}} }}
> >
<MyIcon name={'core/workflow/upload'} w={'16px'} mr={2} /> <MyIcon name={'core/workflow/upload'} w={'16px'} mr={2} />
@@ -275,6 +286,7 @@ const Header = () => {
if (data) { if (data) {
onSaveAndPublishModalOpen(); onSaveAndPublishModalOpen();
} }
onClose();
}} }}
> >
<MyIcon name={'core/workflow/publish'} w={'16px'} mr={2} /> <MyIcon name={'core/workflow/publish'} w={'16px'} mr={2} />
@@ -307,6 +319,8 @@ const Header = () => {
isPc, isPc,
currentTab, currentTab,
isPublished, isPublished,
onBack,
onOpen,
isOpen, isOpen,
onClose, onClose,
t, t,
@@ -314,8 +328,6 @@ const Header = () => {
isV2Workflow, isV2Workflow,
historiesDefaultData, historiesDefaultData,
isSave, isSave,
onBack,
onOpen,
onClickSave, onClickSave,
setHistoriesDefaultData, setHistoriesDefaultData,
appDetail.chatConfig, appDetail.chatConfig,
@@ -323,6 +335,7 @@ const Header = () => {
setWorkflowTestData, setWorkflowTestData,
isSaveAndPublishModalOpen, isSaveAndPublishModalOpen,
onSaveAndPublishModalClose, onSaveAndPublishModalClose,
toast,
onSaveAndPublishModalOpen onSaveAndPublishModalOpen
]); ]);

View File

@@ -113,7 +113,6 @@ const PublishHistoriesSlider = ({
maxW={'300px'} maxW={'300px'}
px={0} px={0}
showMask={false} showMask={false}
top={'60px'}
overflow={'unset'} overflow={'unset'}
> >
<Button <Button

View File

@@ -21,6 +21,7 @@ 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 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';
const Header = ({ const Header = ({
appForm, appForm,
@@ -32,6 +33,7 @@ const Header = ({
const { t } = useTranslation(); const { t } = useTranslation();
const { isPc } = useSystem(); const { isPc } = useSystem();
const router = useRouter(); const router = useRouter();
const { toast } = useToast();
const { appId, appDetail, onPublish, currentTab } = useContextSelector(AppContext, (v) => v); const { appId, appDetail, onPublish, currentTab } = useContextSelector(AppContext, (v) => v);
const { data: paths = [] } = useRequest2(() => getAppFolderPath(appId), { const { data: paths = [] } = useRequest2(() => getAppFolderPath(appId), {
@@ -75,8 +77,12 @@ const Header = ({
chatConfig: data.chatConfig, chatConfig: data.chatConfig,
type: AppTypeEnum.simple type: AppTypeEnum.simple
}); });
toast({
status: 'success',
title: t('app:publish_success')
});
}, },
[onPublish, t] [onPublish, t, toast]
); );
const [historiesDefaultData, setHistoriesDefaultData] = useState<InitProps>(); const [historiesDefaultData, setHistoriesDefaultData] = useState<InitProps>();

View File

@@ -29,6 +29,7 @@ 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 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';
const PublishHistories = dynamic(() => import('../WorkflowPublishHistoriesSlider')); const PublishHistories = dynamic(() => import('../WorkflowPublishHistoriesSlider'));
@@ -36,6 +37,7 @@ const Header = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { isPc } = useSystem(); const { isPc } = useSystem();
const router = useRouter(); const router = useRouter();
const { toast } = useToast();
const { appDetail, onPublish, currentTab } = useContextSelector(AppContext, (v) => v); const { appDetail, onPublish, currentTab } = useContextSelector(AppContext, (v) => v);
const isV2Workflow = appDetail?.version === 'v2'; const isV2Workflow = appDetail?.version === 'v2';
@@ -103,7 +105,7 @@ const Header = () => {
// Mark the current snapshot as saved // Mark the current snapshot as saved
setPast((prevPast) => setPast((prevPast) =>
prevPast.map((item, index) => prevPast.map((item, index) =>
index === prevPast.length - 1 index === 0
? { ? {
...item, ...item,
isSaved: true isSaved: true
@@ -175,6 +177,7 @@ const Header = () => {
isLoading={loading} isLoading={loading}
onClick={async () => { onClick={async () => {
await onClickSave({}); await onClickSave({});
onClose();
onBack(); onBack();
}} }}
> >
@@ -250,7 +253,7 @@ const Header = () => {
</Button> </Button>
} }
> >
{({}) => ( {({ onClose }) => (
<MyBox p={1.5}> <MyBox p={1.5}>
<MyBox <MyBox
display={'flex'} display={'flex'}
@@ -263,6 +266,10 @@ const Header = () => {
isLoading={loading} isLoading={loading}
onClick={async () => { onClick={async () => {
await onClickSave({}); await onClickSave({});
toast({
status: 'success',
title: t('app:saved_success')
});
}} }}
> >
<MyIcon name={'core/workflow/upload'} w={'16px'} mr={2} /> <MyIcon name={'core/workflow/upload'} w={'16px'} mr={2} />
@@ -279,6 +286,7 @@ const Header = () => {
if (data) { if (data) {
onSaveAndPublishModalOpen(); onSaveAndPublishModalOpen();
} }
onClose();
}} }}
> >
<MyIcon name={'core/workflow/publish'} w={'16px'} mr={2} /> <MyIcon name={'core/workflow/publish'} w={'16px'} mr={2} />
@@ -311,6 +319,8 @@ const Header = () => {
isPc, isPc,
currentTab, currentTab,
isPublished, isPublished,
onBack,
onOpen,
isOpen, isOpen,
onClose, onClose,
t, t,
@@ -318,8 +328,6 @@ const Header = () => {
isV2Workflow, isV2Workflow,
historiesDefaultData, historiesDefaultData,
isSave, isSave,
onBack,
onOpen,
onClickSave, onClickSave,
setHistoriesDefaultData, setHistoriesDefaultData,
appDetail.chatConfig, appDetail.chatConfig,
@@ -327,6 +335,7 @@ const Header = () => {
setWorkflowTestData, setWorkflowTestData,
isSaveAndPublishModalOpen, isSaveAndPublishModalOpen,
onSaveAndPublishModalClose, onSaveAndPublishModalClose,
toast,
onSaveAndPublishModalOpen onSaveAndPublishModalOpen
]); ]);

View File

@@ -17,7 +17,11 @@ const FlowController = React.memo(function FlowController() {
useEffect(() => { useEffect(() => {
const keyDownHandler = (event: KeyboardEvent) => { const keyDownHandler = (event: KeyboardEvent) => {
if (event.key === 'z' && (event.ctrlKey || event.metaKey) && event.shiftKey) { if (
(event.key === 'z' || event.key === 'Z') &&
(event.ctrlKey || event.metaKey) &&
event.shiftKey
) {
event.preventDefault(); event.preventDefault();
redo(); redo();
} else if (event.key === 'z' && (event.ctrlKey || event.metaKey)) { } else if (event.key === 'z' && (event.ctrlKey || event.metaKey)) {

View File

@@ -1,5 +1,6 @@
import { Box, Button, Input, ModalBody, ModalFooter } from '@chakra-ui/react'; import { Box, Button, Input, ModalBody, ModalFooter } from '@chakra-ui/react';
import MyModal from '@fastgpt/web/components/common/MyModal'; import MyModal from '@fastgpt/web/components/common/MyModal';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
type FormType = { type FormType = {
@@ -17,6 +18,7 @@ const SaveAndPublishModal = ({
onClickSave: (data: { isPublish: boolean; versionName: string }) => Promise<void>; onClickSave: (data: { isPublish: boolean; versionName: string }) => Promise<void>;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { toast } = useToast();
const { register, handleSubmit } = useForm<FormType>({ const { register, handleSubmit } = useForm<FormType>({
defaultValues: { defaultValues: {
versionName: '', versionName: '',
@@ -61,6 +63,10 @@ const SaveAndPublishModal = ({
isLoading={isLoading} isLoading={isLoading}
onClick={handleSubmit(async (data) => { onClick={handleSubmit(async (data) => {
await onClickSave({ ...data, isPublish: true }); await onClickSave({ ...data, isPublish: true });
toast({
status: 'success',
title: t('app:publish_success')
});
onClose(); onClose();
})} })}
> >

View File

@@ -15,7 +15,7 @@ import { RuntimeEdgeItemType, StoreEdgeItemType } from '@fastgpt/global/core/wor
import { FlowNodeChangeProps } from '@fastgpt/global/core/workflow/type/fe'; import { FlowNodeChangeProps } from '@fastgpt/global/core/workflow/type/fe';
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io'; import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
import { useToast } from '@fastgpt/web/hooks/useToast'; import { useToast } from '@fastgpt/web/hooks/useToast';
import { useLocalStorageState, useMemoizedFn, useUpdateEffect } from 'ahooks'; import { useDebounceEffect, useLocalStorageState, useMemoizedFn, useUpdateEffect } from 'ahooks';
import React, { import React, {
Dispatch, Dispatch,
SetStateAction, SetStateAction,
@@ -900,20 +900,23 @@ const WorkflowContextProvider = ({
return true; return true;
}, },
{ {
debounceWait: 500,
refreshDeps: [nodes, edges, appDetail.chatConfig, past] refreshDeps: [nodes, edges, appDetail.chatConfig, past]
} }
); );
useEffect(() => { useDebounceEffect(
if (!nodes.length) return; () => {
saveSnapshot({ if (!nodes.length) return;
pastNodes: nodes, saveSnapshot({
pastEdges: edges, pastNodes: nodes,
customTitle: formatTime2YMDHMS(new Date()), pastEdges: edges,
chatConfig: appDetail.chatConfig customTitle: formatTime2YMDHMS(new Date()),
}); chatConfig: appDetail.chatConfig
}, [nodes, edges, appDetail.chatConfig]); });
},
[nodes, edges, appDetail.chatConfig],
{ wait: 500 }
);
const undo = useCallback(() => { const undo = useCallback(() => {
if (past[1]) { if (past[1]) {

View File

@@ -1,5 +1,9 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { getPublishList, updateAppVersion } from '@/web/core/app/api/version'; import {
getAppVersionDetail,
getWorkflowVersionList,
updateAppVersion
} from '@/web/core/app/api/version';
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination'; import { useScrollPagination } 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';
@@ -19,15 +23,18 @@ import { useUserStore } from '@/web/support/user/useUserStore';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useToast } from '@fastgpt/web/hooks/useToast'; import { useToast } from '@fastgpt/web/hooks/useToast';
import { versionListResponse } from '@/pages/api/core/app/version/listWorkflow';
const WorkflowPublishHistoriesSlider = ({ onClose }: { onClose: () => void }) => { const WorkflowPublishHistoriesSlider = ({ onClose }: { onClose: () => void }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const [currentTab, setCurrentTab] = useState<'myEdit' | 'teamCloud'>('myEdit'); const [currentTab, setCurrentTab] = useState<'myEdit' | 'teamCloud'>('myEdit');
const [isLoading, setIsLoading] = useState(false);
return ( return (
<> <>
<CustomRightDrawer <CustomRightDrawer
onClose={() => onClose()} onClose={() => onClose()}
isLoading={isLoading}
title={ title={
( (
<> <>
@@ -49,10 +56,9 @@ const WorkflowPublishHistoriesSlider = ({ onClose }: { onClose: () => void }) =>
maxW={'340px'} maxW={'340px'}
px={0} px={0}
showMask={false} showMask={false}
top={'60px'}
overflow={'unset'} overflow={'unset'}
> >
{currentTab === 'myEdit' ? <MyEdit /> : <TeamCloud />} {currentTab === 'myEdit' ? <MyEdit /> : <TeamCloud setIsLoading={setIsLoading} />}
</CustomRightDrawer> </CustomRightDrawer>
</> </>
); );
@@ -163,14 +169,14 @@ const MyEdit = () => {
); );
}; };
const TeamCloud = () => { const TeamCloud = ({ setIsLoading }: { setIsLoading: (value: boolean) => void }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { appDetail } = useContextSelector(AppContext, (v) => v); const { appDetail } = useContextSelector(AppContext, (v) => v);
const { saveSnapshot, resetSnapshot } = useContextSelector(WorkflowContext, (v) => v); const { saveSnapshot, resetSnapshot } = useContextSelector(WorkflowContext, (v) => v);
const { loadAndGetTeamMembers } = useUserStore(); const { loadAndGetTeamMembers } = useUserStore();
const { feConfigs } = useSystemStore(); const { feConfigs } = useSystemStore();
const { list, ScrollList, isLoading, fetchData } = useScrollPagination(getPublishList, { const { list, ScrollList, isLoading, fetchData } = useScrollPagination(getWorkflowVersionList, {
itemHeight: 40, itemHeight: 40,
overscan: 20, overscan: 20,
@@ -188,6 +194,37 @@ const TeamCloud = () => {
const [isEditing, setIsEditing] = useState(false); const [isEditing, setIsEditing] = useState(false);
const { toast } = useToast(); const { toast } = useToast();
const onChangeVersion = async (versionItem: versionListResponse) => {
setIsLoading(true);
const versionDetail = await getAppVersionDetail(versionItem._id, versionItem.appId);
setIsLoading(false);
if (!versionDetail) return;
const state = {
nodes: versionDetail.nodes?.map((item) => storeNode2FlowNode({ item })),
edges: versionDetail.edges?.map((item) => storeEdgesRenderEdge({ edge: item })),
title: versionItem.versionName,
chatConfig: versionDetail.chatConfig
};
const res = await saveSnapshot({
pastNodes: state.nodes,
pastEdges: state.edges,
chatConfig: state.chatConfig,
customTitle: `${t('app:app.version_copy')}-${state.title}`
});
if (!res) {
return toast({
title: t('workflow:workflow.Switch_failed'),
status: 'warning'
});
}
resetSnapshot(state);
toast({
title: t('workflow:workflow.Switch_success'),
status: 'success'
});
};
return ( return (
<ScrollList isLoading={isLoading} flex={'1 0 0'} px={5}> <ScrollList isLoading={isLoading} flex={'1 0 0'} px={5}>
{list.map((data, index) => { {list.map((data, index) => {
@@ -209,28 +246,7 @@ const TeamCloud = () => {
_hover={{ _hover={{
bg: 'primary.50' bg: 'primary.50'
}} }}
onClick={async () => { onClick={() => onChangeVersion(item)}
const state = {
nodes: item.nodes?.map((item) => storeNode2FlowNode({ item })),
edges: item.edges?.map((item) => storeEdgesRenderEdge({ edge: item })),
title: item.versionName,
chatConfig: item.chatConfig
};
const res = await saveSnapshot({
pastNodes: state.nodes,
pastEdges: state.edges,
chatConfig: state.chatConfig,
customTitle: `${t('app:app.version_copy')}-${state.title}`
});
if (!res) return;
resetSnapshot(state);
toast({
title: t('workflow:workflow.Switch_success'),
status: 'success'
});
}}
> >
<MyPopover <MyPopover
trigger="hover" trigger="hover"
@@ -308,6 +324,7 @@ const TeamCloud = () => {
autoFocus autoFocus
h={8} h={8}
defaultValue={item.versionName || formatTime2YMDHMS(item.time)} defaultValue={item.versionName || formatTime2YMDHMS(item.time)}
onClick={(e) => e.stopPropagation()}
onBlur={async (e) => { onBlur={async (e) => {
setIsEditing(true); setIsEditing(true);
await updateAppVersion({ await updateAppVersion({

View File

@@ -84,7 +84,6 @@ export const AppContext = createContext<AppContextType>({
const AppContextProvider = ({ children }: { children: ReactNode }) => { const AppContextProvider = ({ children }: { children: ReactNode }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { appT } = useI18n();
const router = useRouter(); const router = useRouter();
const { appId, currentTab = TabEnum.appEdit } = router.query as { const { appId, currentTab = TabEnum.appEdit } = router.query as {
appId: string; appId: string;
@@ -151,23 +150,18 @@ const AppContextProvider = ({ children }: { children: ReactNode }) => {
})); }));
}); });
const { runAsync: onPublish } = useRequest2( const { runAsync: onPublish } = useRequest2(async (data: PostPublishAppProps) => {
async (data: PostPublishAppProps) => { await postPublishApp(appId, data);
await postPublishApp(appId, data); setAppDetail((state) => ({
setAppDetail((state) => ({ ...state,
...state, ...data,
...data, modules: data.nodes || state.modules
modules: data.nodes || state.modules }));
})); reloadAppLatestVersion();
reloadAppLatestVersion(); });
},
{
successToast: appT('publish_success')
}
);
const { openConfirm: openConfirmDel, ConfirmModal: ConfirmDelModal } = useConfirm({ const { openConfirm: openConfirmDel, ConfirmModal: ConfirmDelModal } = useConfirm({
content: appT('confirm_del_app_tip'), content: t('app:confirm_del_app_tip'),
type: 'delete' type: 'delete'
}); });
const { runAsync: deleteApp } = useRequest2( const { runAsync: deleteApp } = useRequest2(

View File

@@ -7,6 +7,7 @@ import type {
getLatestVersionResponse getLatestVersionResponse
} from '@/pages/api/core/app/version/latest'; } from '@/pages/api/core/app/version/latest';
import { UpdateAppVersionBody } from '@/pages/api/core/app/version/update'; import { UpdateAppVersionBody } from '@/pages/api/core/app/version/update';
import { versionListResponse } from '@/pages/api/core/app/version/listWorkflow';
export const getAppLatestVersion = (data: getLatestVersionQuery) => export const getAppLatestVersion = (data: getLatestVersionQuery) =>
GET<getLatestVersionResponse>('/core/app/version/latest', data); GET<getLatestVersionResponse>('/core/app/version/latest', data);
@@ -17,6 +18,12 @@ export const postPublishApp = (appId: string, data: PostPublishAppProps) =>
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 }>) =>
POST<PaginationResponse<versionListResponse>>('/core/app/version/listWorkflow', data);
export const getAppVersionDetail = (versionId: string, appId: string) =>
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);