fix: start node check (#5794)

* fix: start node check

* remove log

* fix: variables refresh

* fix: workflow start check

* fix: variables refresh

* perf: auto save

* perf: add log
This commit is contained in:
Archer
2025-10-21 09:51:13 +08:00
committed by GitHub
parent 44e9299d5e
commit 5054ccd487
9 changed files with 157 additions and 177 deletions

View File

@@ -8,7 +8,7 @@ import {
Textarea,
HStack
} from '@chakra-ui/react';
import React, { useCallback, useEffect, useMemo } from 'react';
import React, { useCallback, useEffect } from 'react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
@@ -61,105 +61,91 @@ const ScheduledTriggerConfig = ({
}
}, []);
const Render = useMemo(() => {
return (
<>
<Flex alignItems={'center'}>
<MyIcon name={'core/app/schedulePlan'} w={'20px'} />
<HStack ml={2} flex={1} spacing={1}>
<FormLabel color={'myGray.600'}>{t('common:core.app.Interval timer run')}</FormLabel>
<QuestionTip label={t('common:core.app.Interval timer tip')} />
</HStack>
<MyTooltip label={t('common:core.app.Config schedule plan')}>
<Button
variant={'transparentBase'}
iconSpacing={1}
size={'sm'}
mr={'-5px'}
color={'myGray.600'}
onClick={onOpen}
>
{cronString2Label(value?.cronString ?? '', t)}
</Button>
</MyTooltip>
</Flex>
return (
<>
<Flex alignItems={'center'}>
<MyIcon name={'core/app/schedulePlan'} w={'20px'} />
<HStack ml={2} flex={1} spacing={1}>
<FormLabel color={'myGray.600'}>{t('common:core.app.Interval timer run')}</FormLabel>
<QuestionTip label={t('common:core.app.Interval timer tip')} />
</HStack>
<MyTooltip label={t('common:core.app.Config schedule plan')}>
<Button
variant={'transparentBase'}
iconSpacing={1}
size={'sm'}
mr={'-5px'}
color={'myGray.600'}
onClick={onOpen}
>
{cronString2Label(value?.cronString ?? '', t)}
</Button>
</MyTooltip>
</Flex>
<MyModal
isOpen={isOpen}
onClose={onClose}
iconSrc={'core/app/schedulePlan'}
title={t('common:core.app.Interval timer config')}
overflow={'unset'}
>
<ModalBody>
<Flex justifyContent={'space-between'} alignItems={'center'}>
<FormLabel flex={'0 0 80px'}>{t('common:core.app.schedule.Open schedule')}</FormLabel>
<Switch
isChecked={isOpenSchedule}
onChange={(e) => {
if (e.target.checked) {
onUpdate({ cronString: defaultCronString });
} else {
onUpdate({ cronString: '' });
}
}}
/>
</Flex>
{isOpenSchedule && (
<>
<Flex alignItems={'center'} mt={5}>
<FormLabel flex={'0 0 80px'}>{t('app:execute_time')}</FormLabel>
<Box flex={'1 0 0'}>
<ScheduleTimeSelect
cronString={value?.cronString}
onChange={(e) => {
onUpdate({ cronString: e });
}}
/>
</Box>
</Flex>
<Flex alignItems={'center'} mt={5}>
<FormLabel flex={'0 0 80px'}>{t('app:time_zone')}</FormLabel>
<Box flex={'1 0 0'}>
<TimezoneSelect
value={timezone}
onChange={(e) => {
onUpdate({ timezone: e });
}}
/>
</Box>
</Flex>
<Box mt={5}>
<FormLabel mb={1}>{t('common:core.app.schedule.Default prompt')}</FormLabel>
<Textarea
value={defaultPrompt}
rows={8}
bg={'myGray.50'}
placeholder={t('common:core.app.schedule.Default prompt placeholder')}
<MyModal
isOpen={isOpen}
onClose={onClose}
iconSrc={'core/app/schedulePlan'}
title={t('common:core.app.Interval timer config')}
overflow={'unset'}
>
<ModalBody>
<Flex justifyContent={'space-between'} alignItems={'center'}>
<FormLabel flex={'0 0 80px'}>{t('common:core.app.schedule.Open schedule')}</FormLabel>
<Switch
isChecked={isOpenSchedule}
onChange={(e) => {
if (e.target.checked) {
onUpdate({ cronString: defaultCronString });
} else {
onUpdate({ cronString: '' });
}
}}
/>
</Flex>
{isOpenSchedule && (
<>
<Flex alignItems={'center'} mt={5}>
<FormLabel flex={'0 0 80px'}>{t('app:execute_time')}</FormLabel>
<Box flex={'1 0 0'}>
<ScheduleTimeSelect
cronString={value?.cronString}
onChange={(e) => {
onUpdate({ defaultPrompt: e.target.value });
onUpdate({ cronString: e });
}}
/>
</Box>
</>
)}
</ModalBody>
</MyModal>
</>
);
}, [
defaultPrompt,
isOpen,
isOpenSchedule,
onClose,
onOpen,
onUpdate,
t,
timezone,
value?.cronString
]);
return Render;
</Flex>
<Flex alignItems={'center'} mt={5}>
<FormLabel flex={'0 0 80px'}>{t('app:time_zone')}</FormLabel>
<Box flex={'1 0 0'}>
<TimezoneSelect
value={timezone}
onChange={(e) => {
onUpdate({ timezone: e });
}}
/>
</Box>
</Flex>
<Box mt={5}>
<FormLabel mb={1}>{t('common:core.app.schedule.Default prompt')}</FormLabel>
<Textarea
value={defaultPrompt}
rows={8}
bg={'myGray.50'}
placeholder={t('common:core.app.schedule.Default prompt placeholder')}
onChange={(e) => {
onUpdate({ defaultPrompt: e.target.value });
}}
/>
</Box>
</>
)}
</ModalBody>
</MyModal>
</>
);
};
export default React.memo(ScheduledTriggerConfig);

View File

@@ -1,4 +1,4 @@
import React, { type Dispatch, useMemo } from 'react';
import React, { type Dispatch, useCallback, useMemo } from 'react';
import { type NodeProps, useViewport } from 'reactflow';
import { Box } from '@chakra-ui/react';
import { type FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
@@ -18,12 +18,10 @@ import {
type AppDetailType,
type VariableItemType
} from '@fastgpt/global/core/app/type';
import { useMemoizedFn } from 'ahooks';
import VariableEdit from '@/components/core/app/VariableEdit';
import { AppContext } from '@/pageComponents/app/detail/context';
import WelcomeTextConfig from '@/components/core/app/WelcomeTextConfig';
import FileSelect from '@/components/core/app/FileSelect';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { userFilesInput } from '@fastgpt/global/core/workflow/template/system/workflowStart';
import Container from '../components/Container';
import AutoExecConfig from '@/components/core/app/AutoExecConfig';
@@ -44,7 +42,7 @@ const NodeUserGuide = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
systemConfigNode: data,
isPublicFetch: true
});
}, [data, appDetail]);
}, [data, appDetail.chatConfig]);
const componentsProps = useMemo(
() => ({
@@ -54,51 +52,47 @@ const NodeUserGuide = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
[chatConfig, setAppDetail]
);
const Render = useMemo(() => {
return (
<>
<NodeCard
selected={selected}
menuForbid={{
debug: true,
copy: true,
delete: true
}}
{...data}
>
<Container>
<WelcomeText {...componentsProps} />
<Box mt={2} pt={2}>
<ChatStartVariable {...componentsProps} />
</Box>
<Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
<FileSelectConfig {...componentsProps} />
</Box>
<Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
<TTSGuide {...componentsProps} />
</Box>
<Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
<WhisperGuide {...componentsProps} />
</Box>
<Box mt={3} pt={4} borderTop={'base'} borderColor={'myGray.200'}>
<QuestionGuide {...componentsProps} />
</Box>
<Box mt={4} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
<ScheduledTrigger {...componentsProps} />
</Box>
<Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
<AutoExecute {...componentsProps} />
</Box>
<Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
<QuestionInputGuide {...componentsProps} />
</Box>
</Container>
</NodeCard>
</>
);
}, [componentsProps, data, selected]);
return Render;
return (
<>
<NodeCard
selected={selected}
menuForbid={{
debug: true,
copy: true,
delete: true
}}
{...data}
>
<Container>
<WelcomeText {...componentsProps} />
<Box mt={2} pt={2}>
<ChatStartVariable {...componentsProps} />
</Box>
<Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
<FileSelectConfig {...componentsProps} />
</Box>
<Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
<TTSGuide {...componentsProps} />
</Box>
<Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
<WhisperGuide {...componentsProps} />
</Box>
<Box mt={3} pt={4} borderTop={'base'} borderColor={'myGray.200'}>
<QuestionGuide {...componentsProps} />
</Box>
<Box mt={4} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
<ScheduledTrigger {...componentsProps} />
</Box>
<Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
<AutoExecute {...componentsProps} />
</Box>
<Box mt={3} pt={3} borderTop={'base'} borderColor={'myGray.200'}>
<QuestionInputGuide {...componentsProps} />
</Box>
</Container>
</NodeCard>
</>
);
};
export default React.memo(NodeUserGuide);
@@ -124,15 +118,18 @@ function WelcomeText({ chatConfig: { welcomeText }, setAppDetail }: ComponentPro
}
function ChatStartVariable({ chatConfig: { variables = [] }, setAppDetail }: ComponentProps) {
const updateVariables = useMemoizedFn((value: VariableItemType[]) => {
setAppDetail((state) => ({
...state,
chatConfig: {
...state.chatConfig,
variables: value
}
}));
});
const updateVariables = useCallback(
(value: VariableItemType[]) => {
setAppDetail((state) => ({
...state,
chatConfig: {
...state.chatConfig,
variables: value
}
}));
},
[setAppDetail]
);
const { zoom } = useViewport();
return <VariableEdit variables={variables} onChange={(e) => updateVariables(e)} zoom={zoom} />;

View File

@@ -8,7 +8,6 @@ import IOTitle from '../components/IOTitle';
import { useTranslation } from 'next-i18next';
import { useContextSelector } from 'use-context-selector';
import { WorkflowBufferDataContext } from '../../context/workflowInitContext';
import { useCreation } from 'ahooks';
import { type FlowNodeOutputItemType } from '@fastgpt/global/core/workflow/type/io';
import { FlowNodeOutputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
@@ -16,8 +15,7 @@ import { AppContext } from '@/pageComponents/app/detail/context';
import { workflowSystemVariables } from '@/web/core/app/utils';
import {
formatEditorVariablePickerIcon,
getAppChatConfig,
getGuideModule
getAppChatConfig
} from '@fastgpt/global/core/workflow/utils';
import MyDivider from '@fastgpt/web/components/common/MyDivider';
import { useMemoEnhance } from '@fastgpt/web/hooks/useMemoEnhance';
@@ -48,9 +46,9 @@ const NodeStart = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
valueDesc: item.valueDesc
};
});
}, [systemConfigNode, appDetail.chatConfig, t]);
}, [appDetail.chatConfig, systemConfigNode, t]);
const systemVariables = useMemo(
const systemVariables = useMemoEnhance(
() =>
workflowSystemVariables.map((item) => ({
id: item.key,

View File

@@ -14,7 +14,7 @@ import React, {
useState
} from 'react';
import { createContext, useContextSelector } from 'use-context-selector';
import { useDebounceEffect } from 'ahooks';
import { useDebounceEffect, useMemoizedFn, useUnmount } from 'ahooks';
import { WorkflowBufferDataContext, WorkflowInitContext } from './workflowInitContext';
import { compareSnapshot } from '@/web/core/workflow/utils';
import { AppContext } from '@/pageComponents/app/detail/context';
@@ -105,13 +105,10 @@ export const WorkflowPersistenceProvider: React.FC<PropsWithChildren> = ({ child
}, [appDetail.chatConfig, flowData2StoreData, isSaved, onSaveApp]);
// 页面关闭前自动保存
useEffect(() => {
return () => {
if (isProduction) {
autoSaveFn();
}
};
}, [autoSaveFn]);
useUnmount(() => {
autoSaveFn();
});
useBeforeunload({
tip: t('common:core.tip.leave page'),
callback: autoSaveFn

View File

@@ -188,12 +188,7 @@ const SelectAppModal = ({
onClick={handleItemClick}
>
<Flex alignItems={'center'} w={'1.25rem'} onClick={(e) => e.stopPropagation()}>
{!isFolder && (
<Checkbox
isChecked={selected}
onChange={handleItemClick}
/>
)}
{!isFolder && <Checkbox isChecked={selected} onChange={handleItemClick} />}
</Flex>
<Avatar src={item.avatar} w="1.5rem" borderRadius={'sm'} />
<Box>{item.name}</Box>