mirror of
https://github.com/labring/FastGPT.git
synced 2025-08-01 20:27:45 +00:00
Publish histories (#1331)
* fix http plugin edge (#95) * fix http plugin edge * use getHandleId * perf: i18n file * feat: histories list * perf: request lock * fix: ts * move box components * fix: edit form refresh --------- Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { useTheme, type BoxProps } from '@chakra-ui/react';
|
||||
import MyBox from '../common/MyBox';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
|
||||
const PageContainer = ({
|
||||
children,
|
||||
|
@@ -1,19 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Box, BoxProps } from '@chakra-ui/react';
|
||||
import Loading from '@fastgpt/web/components/common/MyLoading';
|
||||
|
||||
type Props = BoxProps & {
|
||||
isLoading?: boolean;
|
||||
text?: string;
|
||||
};
|
||||
|
||||
const MyBox = ({ text, isLoading, children, ...props }: Props) => {
|
||||
return (
|
||||
<Box position={'relative'} {...props}>
|
||||
{isLoading && <Loading fixed={false} text={text} />}
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default MyBox;
|
@@ -7,7 +7,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import dynamic from 'next/dynamic';
|
||||
import MyBox from '@/components/common/MyBox';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
import { SearchScoreTypeEnum, SearchScoreTypeMap } from '@fastgpt/global/core/dataset/constants';
|
||||
|
||||
const InputDataModal = dynamic(() => import('@/pages/dataset/detail/components/InputDataModal'));
|
||||
|
@@ -5,7 +5,7 @@ import React, { Dispatch, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import ParentPaths from '@/components/common/ParentPaths';
|
||||
import MyBox from '@/components/common/MyBox';
|
||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||
|
||||
type PathItemType = {
|
||||
parentId: string;
|
||||
|
@@ -14,7 +14,7 @@ import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { getPreviewPluginModule } from '@/web/core/plugin/api';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { moduleTemplatesList } from '@fastgpt/global/core/workflow/template/constants';
|
||||
import { workflowNodeTemplateList } from '@fastgpt/web/core/workflow/constants';
|
||||
import RowTabs from '@fastgpt/web/components/common/Tabs/RowTabs';
|
||||
import { useWorkflowStore } from '@/web/core/workflow/store/workflow';
|
||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
||||
@@ -259,7 +259,7 @@ const RenderList = React.memo(function RenderList({
|
||||
const setNodes = useContextSelector(WorkflowContext, (v) => v.setNodes);
|
||||
|
||||
const formatTemplates = useMemo<nodeTemplateListType>(() => {
|
||||
const copy: nodeTemplateListType = JSON.parse(JSON.stringify(moduleTemplatesList));
|
||||
const copy: nodeTemplateListType = JSON.parse(JSON.stringify(workflowNodeTemplateList(t)));
|
||||
templates.forEach((item) => {
|
||||
const index = copy.findIndex((template) => template.type === item.templateType);
|
||||
if (index === -1) return;
|
||||
|
@@ -0,0 +1,179 @@
|
||||
import React, { useState } from 'react';
|
||||
import { getPublishList, postRevertVersion } from '@/web/core/app/versionApi';
|
||||
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
|
||||
import CustomRightDrawer from '@fastgpt/web/components/common/MyDrawer/CustomRightDrawer';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { Box, Button, Flex } from '@chakra-ui/react';
|
||||
import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../context';
|
||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
||||
|
||||
const PublishHistoriesSlider = () => {
|
||||
const { t } = useTranslation();
|
||||
const { openConfirm, ConfirmModal } = useConfirm({
|
||||
content: t('core.workflow.publish.OnRevert version confirm')
|
||||
});
|
||||
|
||||
const { appDetail, setAppDetail } = useAppStore();
|
||||
const appId = useContextSelector(WorkflowContext, (e) => e.appId);
|
||||
const setIsShowVersionHistories = useContextSelector(
|
||||
WorkflowContext,
|
||||
(e) => e.setIsShowVersionHistories
|
||||
);
|
||||
const initData = useContextSelector(WorkflowContext, (e) => e.initData);
|
||||
|
||||
const [selectedHistoryId, setSelectedHistoryId] = useState<string>();
|
||||
|
||||
const { list, ScrollList, isLoading } = useScrollPagination(getPublishList, {
|
||||
itemHeight: 49,
|
||||
overscan: 20,
|
||||
|
||||
pageSize: 30,
|
||||
defaultParams: {
|
||||
appId
|
||||
}
|
||||
});
|
||||
|
||||
const onClose = useMemoizedFn(() => {
|
||||
setIsShowVersionHistories(false);
|
||||
});
|
||||
|
||||
const onPreview = useMemoizedFn((data: AppVersionSchemaType) => {
|
||||
setSelectedHistoryId(data._id);
|
||||
|
||||
initData({
|
||||
nodes: data.nodes,
|
||||
edges: data.edges
|
||||
});
|
||||
});
|
||||
const onCloseSlider = useMemoizedFn(() => {
|
||||
setSelectedHistoryId(undefined);
|
||||
initData({
|
||||
nodes: appDetail.modules,
|
||||
edges: appDetail.edges
|
||||
});
|
||||
onClose();
|
||||
});
|
||||
|
||||
const { mutate: onRevert, isLoading: isReverting } = useRequest({
|
||||
mutationFn: async (data: AppVersionSchemaType) => {
|
||||
if (!appId) return;
|
||||
await postRevertVersion(appId, {
|
||||
versionId: data._id,
|
||||
editNodes: appDetail.modules,
|
||||
editEdges: appDetail.edges
|
||||
});
|
||||
|
||||
setAppDetail({
|
||||
...appDetail,
|
||||
modules: data.nodes,
|
||||
edges: data.edges
|
||||
});
|
||||
|
||||
onCloseSlider();
|
||||
}
|
||||
});
|
||||
|
||||
const showLoading = isLoading || isReverting;
|
||||
|
||||
return (
|
||||
<>
|
||||
<CustomRightDrawer
|
||||
onClose={onCloseSlider}
|
||||
iconSrc="core/workflow/versionHistories"
|
||||
title={t('core.workflow.publish.histories')}
|
||||
maxW={'300px'}
|
||||
px={0}
|
||||
showMask={false}
|
||||
mt={'60px'}
|
||||
overflow={'unset'}
|
||||
>
|
||||
<Button
|
||||
mx={'20px'}
|
||||
variant={'whitePrimary'}
|
||||
mb={1}
|
||||
isDisabled={!selectedHistoryId}
|
||||
onClick={() => {
|
||||
setSelectedHistoryId(undefined);
|
||||
initData({
|
||||
nodes: appDetail.modules,
|
||||
edges: appDetail.edges
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t('core.workflow.Current workflow')}
|
||||
</Button>
|
||||
<ScrollList isLoading={showLoading} flex={'1 0 0'} px={5}>
|
||||
{list.map((data, index) => {
|
||||
const item = data.data;
|
||||
|
||||
return (
|
||||
<Flex
|
||||
key={data.index}
|
||||
alignItems={'center'}
|
||||
py={4}
|
||||
px={3}
|
||||
borderRadius={'md'}
|
||||
cursor={'pointer'}
|
||||
fontWeight={500}
|
||||
_hover={{
|
||||
bg: 'primary.50'
|
||||
}}
|
||||
{...(selectedHistoryId === item._id && {
|
||||
color: 'primary.600'
|
||||
})}
|
||||
onClick={() => onPreview(item)}
|
||||
>
|
||||
<Box
|
||||
w={'12px'}
|
||||
h={'12px'}
|
||||
borderWidth={'2px'}
|
||||
borderColor={'primary.600'}
|
||||
borderRadius={'50%'}
|
||||
position={'relative'}
|
||||
{...(index !== list.length - 1 && {
|
||||
_after: {
|
||||
content: '""',
|
||||
height: '40px',
|
||||
width: '2px',
|
||||
bgColor: 'myGray.250',
|
||||
position: 'absolute',
|
||||
top: '10px',
|
||||
left: '3px'
|
||||
}
|
||||
})}
|
||||
></Box>
|
||||
<Box ml={3} flex={'1 0 0'}>
|
||||
{formatTime2YMDHM(item.time)}
|
||||
</Box>
|
||||
{item._id === selectedHistoryId && (
|
||||
<MyTooltip label={t('core.workflow.publish.OnRevert version')}>
|
||||
<MyIcon
|
||||
name={'core/workflow/revertVersion'}
|
||||
w={'20px'}
|
||||
color={'primary.600'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
openConfirm(() => onRevert(item))();
|
||||
}}
|
||||
/>
|
||||
</MyTooltip>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
</ScrollList>
|
||||
</CustomRightDrawer>
|
||||
<ConfirmModal />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(PublishHistoriesSlider);
|
@@ -36,10 +36,12 @@ import { createContext } from 'use-context-selector';
|
||||
import { defaultRunningStatus } from './constants';
|
||||
import { checkNodeRunStatus } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus';
|
||||
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
|
||||
|
||||
type OnChange<ChangesType> = (changes: ChangesType[]) => void;
|
||||
|
||||
type WorkflowContextType = {
|
||||
appId?: string;
|
||||
mode: 'app' | 'plugin';
|
||||
basicNodeTemplates: FlowNodeTemplateType[];
|
||||
filterAppIds?: string[];
|
||||
@@ -83,7 +85,6 @@ type WorkflowContextType = {
|
||||
};
|
||||
initData: (e: { nodes: StoreNodeItemType[]; edges: StoreEdgeItemType[] }) => Promise<void>;
|
||||
|
||||
// debug
|
||||
// debug
|
||||
workflowDebugData:
|
||||
| {
|
||||
@@ -103,6 +104,10 @@ type WorkflowContextType = {
|
||||
runtimeEdges: RuntimeEdgeItemType[];
|
||||
}) => Promise<void>;
|
||||
onStopNodeDebug: () => void;
|
||||
|
||||
// version history
|
||||
isShowVersionHistories: boolean;
|
||||
setIsShowVersionHistories: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
};
|
||||
|
||||
type ContextValueProps = Pick<
|
||||
@@ -201,6 +206,10 @@ export const WorkflowContext = createContext<WorkflowContextType>({
|
||||
},
|
||||
onChangeNode: function (e: FlowNodeChangeProps): void {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
isShowVersionHistories: false,
|
||||
setIsShowVersionHistories: function (value: React.SetStateAction<boolean>): void {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -616,6 +625,10 @@ const WorkflowContextProvider = ({
|
||||
[onNextNodeDebug, onStopNodeDebug]
|
||||
);
|
||||
|
||||
/* Version histories */
|
||||
const [isShowVersionHistories, setIsShowVersionHistories] = useState(false);
|
||||
|
||||
/* event bus */
|
||||
useEffect(() => {
|
||||
eventBus.on(EventNameEnum.requestWorkflowStore, () => {
|
||||
eventBus.emit(EventNameEnum.receiveWorkflowStore, {
|
||||
@@ -630,6 +643,7 @@ const WorkflowContextProvider = ({
|
||||
return (
|
||||
<WorkflowContext.Provider
|
||||
value={{
|
||||
appId,
|
||||
reactFlowWrapper,
|
||||
...value,
|
||||
// node
|
||||
@@ -661,7 +675,11 @@ const WorkflowContextProvider = ({
|
||||
workflowDebugData,
|
||||
onNextNodeDebug,
|
||||
onStartNodeDebug,
|
||||
onStopNodeDebug
|
||||
onStopNodeDebug,
|
||||
|
||||
// version history
|
||||
isShowVersionHistories,
|
||||
setIsShowVersionHistories
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
Reference in New Issue
Block a user