mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-21 03:35:36 +00:00
Fix workflow detail (#3382)
* fix: loop node init * fix: workflow detail * fix: point table * add null check
This commit is contained in:
@@ -40,6 +40,18 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4815' \
|
||||
|
||||
会重置应用定时执行的字段,把 null 去掉,减少索引大小。
|
||||
|
||||
----
|
||||
|
||||
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`;{{host}} 替换成**FastGPT 域名**。
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'https://{{host}}/api/admin/refreshFreeUser' \
|
||||
--header 'rootkey: {{rootkey}}' \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
重新计算一次免费版用户的时长,之前有版本升级时没有重新计算时间,导致会误发通知。
|
||||
|
||||
|
||||
## 完整更新内容
|
||||
|
||||
|
@@ -12,4 +12,9 @@ weight: 808
|
||||
|
||||
1.
|
||||
2. 新增 - 商业版支持 API 知识库和链接集合定时同步。
|
||||
3. 修复 - 站点同步知识库,链接同步时未使用选择器。
|
||||
3. 优化 - 工作流/简易模式变量初始化代码,去除监听初始化,避免因渲染顺序不一致导致的失败。
|
||||
4. 修复 - 无法自动切换默认语言。增加分享链接,强制执行一次切换默认语言。
|
||||
5. 修复 - 数组选择器自动兼容 4.8.13 以前的数据。
|
||||
6. 修复 - 站点同步知识库,链接同步时未使用选择器。
|
||||
7. 修复 - 简易模式转工作流,没有把系统配置项转化。
|
||||
8. 修复 - 插件独立运行,变量初始值未赋上。
|
@@ -29,7 +29,7 @@ export const simpleText = (text = '') => {
|
||||
replace {{variable}} to value
|
||||
*/
|
||||
export function replaceVariable(text: any, obj: Record<string, string | number>) {
|
||||
if (!(typeof text === 'string')) return text;
|
||||
if (typeof text !== 'string') return text;
|
||||
|
||||
for (const key in obj) {
|
||||
const val = obj[key];
|
||||
|
@@ -251,6 +251,7 @@ export const getReferenceVariableValue = ({
|
||||
return variables[outputId];
|
||||
}
|
||||
|
||||
// 避免 value 刚好就是二个元素的字符串数组
|
||||
const node = nodes.find((node) => node.nodeId === sourceNodeId);
|
||||
if (!node) {
|
||||
return value;
|
||||
|
@@ -193,6 +193,18 @@ export const MultipleRowArraySelect = ({
|
||||
ref: ref,
|
||||
handler: onClose
|
||||
});
|
||||
const onChange = useCallback(
|
||||
(val: any[][]) => {
|
||||
// Filter invalid value
|
||||
const validList = val.filter((item) => {
|
||||
const listItem = list.find((v) => v.value === item[0]);
|
||||
if (!listItem) return false;
|
||||
return listItem.children?.some((v) => v.value === item[1]);
|
||||
});
|
||||
onSelect(validList);
|
||||
},
|
||||
[onSelect]
|
||||
);
|
||||
|
||||
const RenderList = useCallback(
|
||||
({ index, list }: { index: number; list: MultipleSelectProps['list'] }) => {
|
||||
@@ -213,9 +225,9 @@ export const MultipleRowArraySelect = ({
|
||||
const newValue = [parentValue, item.value];
|
||||
|
||||
if (newValues.some((v) => v[0] === parentValue && v[1] === item.value)) {
|
||||
onSelect(newValues.filter((v) => !(v[0] === parentValue && v[1] === item.value)));
|
||||
onChange(newValues.filter((v) => !(v[0] === parentValue && v[1] === item.value)));
|
||||
} else {
|
||||
onSelect([...newValues, newValue]);
|
||||
onChange([...newValues, newValue]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -43,14 +43,16 @@ export const useI18nLng = () => {
|
||||
setLang(lang);
|
||||
|
||||
await i18n?.changeLanguage?.(lang);
|
||||
if (prevLang && prevLang !== lang) {
|
||||
|
||||
if (!i18n.hasResourceBundle(lang, 'common') && prevLang !== lang) {
|
||||
window?.location?.reload?.();
|
||||
}
|
||||
};
|
||||
|
||||
const setUserDefaultLng = () => {
|
||||
const setUserDefaultLng = (forceGetDefaultLng: boolean = false) => {
|
||||
if (!navigator || !localStorage) return;
|
||||
if (getLang()) return onChangeLng(getLang() as string);
|
||||
|
||||
if (getLang() && !forceGetDefaultLng) return onChangeLng(getLang() as string);
|
||||
|
||||
const lang = languageMap[navigator.language] || 'en';
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Controller, UseFormReturn } from 'react-hook-form';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { Box, Button, Card, Textarea } from '@chakra-ui/react';
|
||||
@@ -121,21 +121,7 @@ const VariableInput = ({
|
||||
const variablesForm = useContextSelector(ChatItemContext, (v) => v.variablesForm);
|
||||
const variableList = useContextSelector(ChatBoxContext, (v) => v.variableList);
|
||||
|
||||
const { setValue, handleSubmit: handleSubmitChat } = variablesForm;
|
||||
|
||||
const defaultValues = useMemo(() => {
|
||||
return variableList.reduce((acc: Record<string, any>, item) => {
|
||||
acc[item.key] = item.defaultValue;
|
||||
return acc;
|
||||
}, {});
|
||||
}, [variableList]);
|
||||
|
||||
useEffect(() => {
|
||||
const values = variablesForm.getValues('variables');
|
||||
// If form is not empty, do not reset the variables
|
||||
if (Object.values(values).filter(Boolean).length > 0) return;
|
||||
setValue('variables', defaultValues);
|
||||
}, [defaultValues, setValue, variablesForm]);
|
||||
const { handleSubmit: handleSubmitChat } = variablesForm;
|
||||
|
||||
return (
|
||||
<Box py={3}>
|
||||
|
@@ -805,7 +805,7 @@ const ChatBox = ({
|
||||
setQuestionGuide([]);
|
||||
setValue('chatStarted', false);
|
||||
abortRequest('leave');
|
||||
}, [abortRequest, setValue]);
|
||||
}, [chatId, appId, abortRequest, setValue]);
|
||||
|
||||
// Add listener
|
||||
useEffect(() => {
|
||||
|
@@ -110,17 +110,15 @@ const RenderInput = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const defaultFormValues = formatPluginInputs.reduce(
|
||||
(acc, input) => {
|
||||
acc[input.key] = input.defaultValue;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, any>
|
||||
);
|
||||
|
||||
reset({
|
||||
files: [],
|
||||
variables: defaultFormValues
|
||||
variables: formatPluginInputs.reduce(
|
||||
(acc, input) => {
|
||||
acc[input.key] = input.defaultValue;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, any>
|
||||
)
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -164,7 +162,7 @@ const RenderInput = () => {
|
||||
files: historyFileList
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [histories]);
|
||||
}, [histories, formatPluginInputs]);
|
||||
|
||||
const [uploading, setUploading] = useState(false);
|
||||
|
||||
|
@@ -172,7 +172,6 @@ const RenderPluginInput = ({
|
||||
return (
|
||||
<Textarea
|
||||
value={value}
|
||||
defaultValue={input.defaultValue}
|
||||
onChange={onChange}
|
||||
isDisabled={isDisabled}
|
||||
placeholder={t(input.placeholder as any)}
|
||||
@@ -192,7 +191,6 @@ const RenderPluginInput = ({
|
||||
isInvalid={isInvalid}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
defaultValue={input.defaultValue}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -203,7 +201,6 @@ const RenderPluginInput = ({
|
||||
onChange={onChange}
|
||||
isDisabled={isDisabled}
|
||||
isInvalid={isInvalid}
|
||||
defaultChecked={!!input.defaultValue}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -216,7 +213,6 @@ const RenderPluginInput = ({
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
isInvalid={isInvalid}
|
||||
defaultValue={input.defaultValue}
|
||||
/>
|
||||
);
|
||||
})();
|
||||
|
@@ -2,12 +2,15 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { StandardSubLevelEnum, SubModeEnum } from '@fastgpt/global/support/wallet/sub/constants';
|
||||
import React, { useMemo } from 'react';
|
||||
import { standardSubLevelMap } from '@fastgpt/global/support/wallet/sub/constants';
|
||||
import { Box, Flex, Grid } from '@chakra-ui/react';
|
||||
import { Box, Flex, Grid, useDisclosure } from '@chakra-ui/react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useRouter } from 'next/router';
|
||||
import { getAiPointUsageCardRoute } from '@/web/support/wallet/sub/constants';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
const AiPointsModal = dynamic(() =>
|
||||
import('@/pages/price/components/Points').then((mod) => mod.AiPointsModal)
|
||||
);
|
||||
|
||||
const StandardPlanContentList = ({
|
||||
level,
|
||||
@@ -18,7 +21,12 @@ const StandardPlanContentList = ({
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { subPlans } = useSystemStore();
|
||||
const router = useRouter();
|
||||
|
||||
const {
|
||||
isOpen: isOpenAiPointsModal,
|
||||
onClose: onCloseAiPointsModal,
|
||||
onOpen: onOpenAiPointsModal
|
||||
} = useDisclosure();
|
||||
|
||||
const planContent = useMemo(() => {
|
||||
const plan = subPlans?.standard?.[level];
|
||||
@@ -95,9 +103,7 @@ const StandardPlanContentList = ({
|
||||
<QuestionTip
|
||||
ml={1}
|
||||
label={t('common:support.wallet.subscription.AI points click to read tip')}
|
||||
onClick={() => {
|
||||
router.push(getAiPointUsageCardRoute());
|
||||
}}
|
||||
onClick={onOpenAiPointsModal}
|
||||
></QuestionTip>
|
||||
</Flex>
|
||||
</Flex>
|
||||
@@ -121,6 +127,7 @@ const StandardPlanContentList = ({
|
||||
<Box color={'myGray.600'}>{t('common:support.wallet.subscription.web_site_sync')}</Box>
|
||||
</Flex>
|
||||
)}
|
||||
{isOpenAiPointsModal && <AiPointsModal onClose={onCloseAiPointsModal} />}
|
||||
</Grid>
|
||||
) : null;
|
||||
};
|
||||
|
2
projects/app/src/global/core/chat/api.d.ts
vendored
2
projects/app/src/global/core/chat/api.d.ts
vendored
@@ -42,7 +42,7 @@ export type InitChatResponse = {
|
||||
appId: string;
|
||||
userAvatar?: string;
|
||||
title?: string;
|
||||
variables: Record<string, any>;
|
||||
variables?: Record<string, any>;
|
||||
app: {
|
||||
chatConfig?: AppChatConfigType;
|
||||
chatModels?: string[];
|
||||
|
@@ -38,6 +38,7 @@ async function handler(
|
||||
type: AppTypeEnum.workflow,
|
||||
modules: app.modules,
|
||||
edges: app.edges,
|
||||
chatConfig: app.chatConfig,
|
||||
teamId: app.teamId,
|
||||
tmbId
|
||||
});
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import type { NextApiResponse } from 'next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||
import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema';
|
||||
@@ -10,7 +10,6 @@ import { PostPublishAppProps } from '@/global/core/app/api';
|
||||
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { ApiRequestProps } from '@fastgpt/service/type/next';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { getScheduleTriggerApp } from '@/service/core/app/utils';
|
||||
|
||||
async function handler(
|
||||
req: ApiRequestProps<PostPublishAppProps>,
|
||||
|
@@ -52,7 +52,7 @@ async function handler(
|
||||
appId,
|
||||
title: chat?.title,
|
||||
userAvatar: undefined,
|
||||
variables: chat?.variables || {},
|
||||
variables: chat?.variables,
|
||||
app: {
|
||||
chatConfig: getAppChatConfig({
|
||||
chatConfig,
|
||||
|
@@ -43,7 +43,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
title: chat?.title,
|
||||
//@ts-ignore
|
||||
userAvatar: tmb?.userId?.avatar,
|
||||
variables: chat?.variables || {},
|
||||
variables: chat?.variables,
|
||||
app: {
|
||||
chatConfig: getAppChatConfig({
|
||||
chatConfig,
|
||||
|
@@ -50,7 +50,7 @@ async function handler(req: ApiRequestProps<InitTeamChatProps>, res: NextApiResp
|
||||
appId,
|
||||
title: chat?.title,
|
||||
userAvatar: team?.avatar,
|
||||
variables: chat?.variables || {},
|
||||
variables: chat?.variables,
|
||||
app: {
|
||||
chatConfig: getAppChatConfig({
|
||||
chatConfig,
|
||||
|
@@ -48,7 +48,8 @@ const DetailLogsModal = ({ appId, chatId, onClose }: Props) => {
|
||||
|
||||
setChatBoxData(res);
|
||||
resetVariables({
|
||||
variables: res.variables
|
||||
variables: res.variables,
|
||||
variableList: res.app?.chatConfig?.variables
|
||||
});
|
||||
|
||||
return res;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Flex,
|
||||
Box,
|
||||
@@ -31,7 +31,8 @@ import { cardStyles } from '../constants';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import Tag from '@fastgpt/web/components/common/Tag';
|
||||
import { useMount } from 'ahooks';
|
||||
|
||||
const DetailLogsModal = dynamic(() => import('./DetailLogsModal'));
|
||||
|
||||
const Logs = () => {
|
||||
@@ -41,9 +42,9 @@ const Logs = () => {
|
||||
const appId = useContextSelector(AppContext, (v) => v.appId);
|
||||
const { teamMembers, loadAndGetTeamMembers } = useUserStore();
|
||||
|
||||
useEffect(() => {
|
||||
useMount(() => {
|
||||
loadAndGetTeamMembers();
|
||||
}, []);
|
||||
});
|
||||
|
||||
const [dateRange, setDateRange] = useState<DateRangeType>({
|
||||
from: addDays(new Date(), -7),
|
||||
@@ -140,11 +141,11 @@ const Logs = () => {
|
||||
) : (
|
||||
<HStack>
|
||||
<Avatar
|
||||
src={teamMembers.find((v) => v.tmbId === item.tmbId)?.avatar}
|
||||
src={teamMembers?.find((v) => v.tmbId === item.tmbId)?.avatar}
|
||||
w="1.25rem"
|
||||
/>
|
||||
<Box fontSize={'sm'} ml={1}>
|
||||
{teamMembers.find((v) => v.tmbId === item.tmbId)?.memberName}
|
||||
{teamMembers?.find((v) => v.tmbId === item.tmbId)?.memberName}
|
||||
</Box>
|
||||
</HStack>
|
||||
)}
|
||||
|
@@ -10,8 +10,6 @@ import {
|
||||
NodePositionChange,
|
||||
XYPosition,
|
||||
useReactFlow,
|
||||
getNodesBounds,
|
||||
Rect,
|
||||
NodeRemoveChange,
|
||||
NodeSelectionChange,
|
||||
EdgeRemoveChange
|
||||
@@ -26,15 +24,12 @@ import { WorkflowContext } from '../../context';
|
||||
import { THelperLine } from '@fastgpt/global/core/workflow/type';
|
||||
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { useDebounceEffect, useMemoizedFn } from 'ahooks';
|
||||
import {
|
||||
Input_Template_Node_Height,
|
||||
Input_Template_Node_Width
|
||||
} from '@fastgpt/global/core/workflow/template/input';
|
||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import { WorkflowNodeEdgeContext, WorkflowInitContext } from '../../context/workflowInitContext';
|
||||
import { formatTime2YMDHMS } from '@fastgpt/global/common/string/time';
|
||||
import { AppContext } from '../../../context';
|
||||
import { WorkflowEventContext } from '../../context/workflowEventContext';
|
||||
import { WorkflowStatusContext } from '../../context/workflowStatusContext';
|
||||
|
||||
/*
|
||||
Compute helper lines for snapping nodes to each other
|
||||
@@ -282,18 +277,22 @@ export const useWorkflow = () => {
|
||||
const edges = useContextSelector(WorkflowNodeEdgeContext, (state) => state.edges);
|
||||
const setEdges = useContextSelector(WorkflowNodeEdgeContext, (v) => v.setEdges);
|
||||
const onEdgesChange = useContextSelector(WorkflowNodeEdgeContext, (v) => v.onEdgesChange);
|
||||
const { setConnectingEdge, nodeList, onChangeNode, pushPastSnapshot } = useContextSelector(
|
||||
WorkflowContext,
|
||||
(v) => v
|
||||
);
|
||||
|
||||
const setConnectingEdge = useContextSelector(WorkflowContext, (v) => v.setConnectingEdge);
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
const pushPastSnapshot = useContextSelector(WorkflowContext, (v) => v.pushPastSnapshot);
|
||||
|
||||
const setHoverEdgeId = useContextSelector(WorkflowEventContext, (v) => v.setHoverEdgeId);
|
||||
const setMenu = useContextSelector(WorkflowEventContext, (v) => v.setMenu);
|
||||
const resetParentNodeSizeAndPosition = useContextSelector(
|
||||
WorkflowStatusContext,
|
||||
(v) => v.resetParentNodeSizeAndPosition
|
||||
);
|
||||
|
||||
const { getIntersectingNodes } = useReactFlow();
|
||||
const { isDowningCtrl } = useKeyboard();
|
||||
|
||||
const { resetParentNodeSizeAndPosition } = useLoopNode();
|
||||
|
||||
/* helper line */
|
||||
const [helperLineHorizontal, setHelperLineHorizontal] = useState<THelperLine>();
|
||||
const [helperLineVertical, setHelperLineVertical] = useState<THelperLine>();
|
||||
@@ -669,73 +668,6 @@ export const useWorkflow = () => {
|
||||
};
|
||||
};
|
||||
|
||||
export const useLoopNode = () => {
|
||||
const nodes = useContextSelector(WorkflowInitContext, (state) => state.nodes);
|
||||
const onNodesChange = useContextSelector(WorkflowNodeEdgeContext, (state) => state.onNodesChange);
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
|
||||
const resetParentNodeSizeAndPosition = useMemoizedFn((parentId: string) => {
|
||||
const { childNodes, loopNode } = nodes.reduce(
|
||||
(acc, node) => {
|
||||
if (node.data.parentNodeId === parentId) {
|
||||
acc.childNodes.push(node);
|
||||
}
|
||||
if (node.id === parentId) {
|
||||
acc.loopNode = node;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{ childNodes: [] as Node[], loopNode: undefined as Node<FlowNodeItemType> | undefined }
|
||||
);
|
||||
|
||||
if (!loopNode) return;
|
||||
|
||||
const rect = getNodesBounds(childNodes);
|
||||
// Calculate parent node size with minimum width/height constraints
|
||||
const width = Math.max(rect.width + 80, 840);
|
||||
const height = Math.max(rect.height + 80, 600);
|
||||
|
||||
const offsetHeight =
|
||||
loopNode.data.inputs.find((input) => input.key === NodeInputKeyEnum.loopNodeInputHeight)
|
||||
?.value ?? 83;
|
||||
|
||||
// Update parentNode size and position
|
||||
onChangeNode({
|
||||
nodeId: parentId,
|
||||
type: 'updateInput',
|
||||
key: NodeInputKeyEnum.nodeWidth,
|
||||
value: {
|
||||
...Input_Template_Node_Width,
|
||||
value: width
|
||||
}
|
||||
});
|
||||
onChangeNode({
|
||||
nodeId: parentId,
|
||||
type: 'updateInput',
|
||||
key: NodeInputKeyEnum.nodeHeight,
|
||||
value: {
|
||||
...Input_Template_Node_Height,
|
||||
value: height
|
||||
}
|
||||
});
|
||||
// Update parentNode position
|
||||
onNodesChange([
|
||||
{
|
||||
id: parentId,
|
||||
type: 'position',
|
||||
position: {
|
||||
x: rect.x - 70,
|
||||
y: rect.y - offsetHeight - 240
|
||||
}
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
return {
|
||||
resetParentNodeSizeAndPosition
|
||||
};
|
||||
};
|
||||
|
||||
export default function Dom() {
|
||||
return <></>;
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@
|
||||
When the childNodes of loopFlow change, it automatically calculates the rectangular width, height, and position of the childNodes,
|
||||
thereby further updating the width and height properties of the loop node.
|
||||
*/
|
||||
|
||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import React, { useEffect, useMemo, useRef } from 'react';
|
||||
import { Background, NodeProps } from 'reactflow';
|
||||
@@ -31,8 +30,8 @@ import { getWorkflowGlobalVariables } from '@/web/core/workflow/utils';
|
||||
import { AppContext } from '../../../../context';
|
||||
import { isValidArrayReferenceValue } from '@fastgpt/global/core/workflow/utils';
|
||||
import { ReferenceArrayValueType } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { useLoopNode } from '../../hooks/useWorkflow';
|
||||
import { useSize } from 'ahooks';
|
||||
import { WorkflowStatusContext } from '../../../context/workflowStatusContext';
|
||||
|
||||
const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -40,8 +39,10 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
|
||||
|
||||
const { resetParentNodeSizeAndPosition } = useLoopNode();
|
||||
const resetParentNodeSizeAndPosition = useContextSelector(
|
||||
WorkflowStatusContext,
|
||||
(v) => v.resetParentNodeSizeAndPosition
|
||||
);
|
||||
|
||||
const {
|
||||
nodeWidth,
|
||||
@@ -50,11 +51,11 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
loopNodeInputHeight = Input_Template_LOOP_NODE_OFFSET
|
||||
} = useMemo(() => {
|
||||
return {
|
||||
nodeWidth: Number(
|
||||
inputs.find((input) => input.key === NodeInputKeyEnum.nodeWidth)?.value?.toFixed(0)
|
||||
nodeWidth: Math.round(
|
||||
Number(inputs.find((input) => input.key === NodeInputKeyEnum.nodeWidth)?.value) || 500
|
||||
),
|
||||
nodeHeight: Number(
|
||||
inputs.find((input) => input.key === NodeInputKeyEnum.nodeHeight)?.value?.toFixed(0)
|
||||
nodeHeight: Math.round(
|
||||
Number(inputs.find((input) => input.key === NodeInputKeyEnum.nodeHeight)?.value) || 500
|
||||
),
|
||||
loopInputArray: inputs.find((input) => input.key === NodeInputKeyEnum.loopInputArray),
|
||||
loopNodeInputHeight: inputs.find(
|
||||
@@ -113,7 +114,7 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
return JSON.stringify(
|
||||
nodeList.filter((node) => node.parentNodeId === nodeId).map((node) => node.nodeId)
|
||||
);
|
||||
}, [nodeId, nodeList]);
|
||||
}, [nodeId, nodeList.length]);
|
||||
useEffect(() => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
@@ -148,39 +149,54 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
}, 50);
|
||||
}, [size?.height]);
|
||||
|
||||
const Render = useMemo(() => {
|
||||
const RenderInputDom = useMemo(() => {
|
||||
return (
|
||||
<NodeCard selected={selected} maxW="full" menuForbid={{ copy: true }} {...data}>
|
||||
<Container position={'relative'} flex={1}>
|
||||
<IOTitle text={t('common:common.Input')} />
|
||||
<Box mb={6} maxW={'500px'} ref={inputBoxRef}>
|
||||
<RenderInput nodeId={nodeId} flowInputList={inputs} />
|
||||
</Box>
|
||||
<FormLabel required fontWeight={'medium'} mb={3} color={'myGray.600'}>
|
||||
{t('workflow:loop_body')}
|
||||
</FormLabel>
|
||||
<Box
|
||||
flex={1}
|
||||
position={'relative'}
|
||||
border={'base'}
|
||||
bg={'myGray.100'}
|
||||
rounded={'8px'}
|
||||
{...(!isFolded && {
|
||||
minW: nodeWidth,
|
||||
minH: nodeHeight
|
||||
})}
|
||||
>
|
||||
<Background />
|
||||
</Box>
|
||||
</Container>
|
||||
<Container>
|
||||
<RenderOutput nodeId={nodeId} flowOutputList={outputs} />
|
||||
</Container>
|
||||
</NodeCard>
|
||||
<Box mb={6} maxW={'500px'} ref={inputBoxRef}>
|
||||
<RenderInput nodeId={nodeId} flowInputList={inputs} />
|
||||
</Box>
|
||||
);
|
||||
}, [selected, isFolded, nodeWidth, nodeHeight, data, t, nodeId, inputs, outputs]);
|
||||
}, [inputs, nodeId]);
|
||||
const RenderChildrenNodes = useMemo(() => {
|
||||
return (
|
||||
<>
|
||||
<FormLabel required fontWeight={'medium'} mb={3} color={'myGray.600'}>
|
||||
{t('workflow:loop_body')}
|
||||
</FormLabel>
|
||||
<Box
|
||||
flex={1}
|
||||
position={'relative'}
|
||||
border={'base'}
|
||||
bg={'myGray.100'}
|
||||
rounded={'8px'}
|
||||
{...(!isFolded && {
|
||||
minW: nodeWidth,
|
||||
minH: nodeHeight
|
||||
})}
|
||||
>
|
||||
<Background />
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}, [isFolded, nodeHeight, nodeWidth, t]);
|
||||
|
||||
return Render;
|
||||
const MemoRenderOutput = useMemo(() => {
|
||||
return (
|
||||
<Container>
|
||||
<RenderOutput nodeId={nodeId} flowOutputList={outputs} />
|
||||
</Container>
|
||||
);
|
||||
}, [nodeId, outputs]);
|
||||
|
||||
return (
|
||||
<NodeCard selected={selected} maxW="full" menuForbid={{ copy: true }} {...data}>
|
||||
<Container position={'relative'} flex={1}>
|
||||
<IOTitle text={t('common:common.Input')} />
|
||||
{RenderInputDom}
|
||||
{RenderChildrenNodes}
|
||||
</Container>
|
||||
{MemoRenderOutput}
|
||||
</NodeCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(NodeLoop);
|
||||
|
@@ -23,13 +23,15 @@ const typeMap = {
|
||||
[WorkflowIOValueTypeEnum.arrayString]: WorkflowIOValueTypeEnum.string,
|
||||
[WorkflowIOValueTypeEnum.arrayNumber]: WorkflowIOValueTypeEnum.number,
|
||||
[WorkflowIOValueTypeEnum.arrayBoolean]: WorkflowIOValueTypeEnum.boolean,
|
||||
[WorkflowIOValueTypeEnum.arrayObject]: WorkflowIOValueTypeEnum.object
|
||||
[WorkflowIOValueTypeEnum.arrayObject]: WorkflowIOValueTypeEnum.object,
|
||||
[WorkflowIOValueTypeEnum.arrayAny]: WorkflowIOValueTypeEnum.any
|
||||
};
|
||||
|
||||
const NodeLoopStart = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const { t } = useTranslation();
|
||||
const { nodeId, outputs } = data;
|
||||
const { nodeList, onChangeNode } = useContextSelector(WorkflowContext, (v) => v);
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
|
||||
const loopStartNode = useMemo(
|
||||
() => nodeList.find((node) => node.nodeId === nodeId),
|
||||
|
@@ -88,6 +88,13 @@ const RenderInput = ({ flowInputList, nodeId, CustomComponent, mb = 5 }: Props)
|
||||
const filterInputs = useMemo(() => {
|
||||
return flowInputList.filter((input) => {
|
||||
if (input.isPro && !feConfigs?.isPlus) return false;
|
||||
|
||||
const renderType = input.renderTypeList?.[input.selectedTypeIndex || 0];
|
||||
if (renderType === FlowNodeInputTypeEnum.hidden) return false;
|
||||
|
||||
const isDynamic = !!input.canEdit;
|
||||
if (isDynamic) return false;
|
||||
|
||||
return true;
|
||||
});
|
||||
}, [feConfigs?.isPlus, flowInputList]);
|
||||
@@ -96,7 +103,6 @@ const RenderInput = ({ flowInputList, nodeId, CustomComponent, mb = 5 }: Props)
|
||||
<>
|
||||
{filterInputs.map((input) => {
|
||||
const renderType = input.renderTypeList?.[input.selectedTypeIndex || 0];
|
||||
const isDynamic = !!input.canEdit;
|
||||
|
||||
const RenderComponent = (() => {
|
||||
if (renderType === FlowNodeInputTypeEnum.custom && CustomComponent?.[input.key]) {
|
||||
@@ -109,7 +115,7 @@ const RenderInput = ({ flowInputList, nodeId, CustomComponent, mb = 5 }: Props)
|
||||
return <Component inputs={filterInputs} item={input} nodeId={nodeId} />;
|
||||
})();
|
||||
|
||||
return renderType !== FlowNodeInputTypeEnum.hidden && !isDynamic ? (
|
||||
return (
|
||||
<Box key={input.key} _notLast={{ mb }} position={'relative'}>
|
||||
{!!input.label && !hideLabelTypeList.includes(renderType) && (
|
||||
<InputLabel nodeId={nodeId} input={input} />
|
||||
@@ -120,7 +126,7 @@ const RenderInput = ({ flowInputList, nodeId, CustomComponent, mb = 5 }: Props)
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
) : null;
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
|
@@ -115,7 +115,10 @@ export const useReference = ({
|
||||
|
||||
const Reference = ({ item, nodeId }: RenderInputProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { onChangeNode, nodeList } = useContextSelector(WorkflowContext, (v) => v);
|
||||
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
|
||||
const isArray = item.valueType?.includes('array') ?? false;
|
||||
|
||||
const onSelect = useCallback(
|
||||
@@ -254,22 +257,25 @@ const MultipleReferenceSelector = ({
|
||||
});
|
||||
}, [getSelectValue, value]);
|
||||
|
||||
// useEffect(() => {
|
||||
// const validList = formatList.filter((item) => item.nodeName && item.outputName);
|
||||
// if (validList.length !== value?.length) {
|
||||
// onSelect(validList.map((item) => item.rawValue));
|
||||
// }
|
||||
// }, [formatList, onSelect, value]);
|
||||
useEffect(() => {
|
||||
// Adapt array type from old version
|
||||
if (Array.isArray(value) && typeof value[0] === 'string') {
|
||||
// @ts-ignore
|
||||
onSelect([value]);
|
||||
}
|
||||
}, [formatList, onSelect, value]);
|
||||
|
||||
const invalidList = useMemo(() => {
|
||||
return formatList.filter((item) => item.nodeName && item.outputName);
|
||||
}, [formatList]);
|
||||
|
||||
const ArraySelector = useMemo(() => {
|
||||
return (
|
||||
<MultipleRowArraySelect
|
||||
label={
|
||||
formatList.length > 0 ? (
|
||||
invalidList.length > 0 ? (
|
||||
<Grid py={3} gridTemplateColumns={'1fr 1fr'} gap={2} fontSize={'sm'}>
|
||||
{formatList.map(({ nodeName, outputName }, index) => {
|
||||
if (!nodeName || !outputName) return null;
|
||||
|
||||
{invalidList.map(({ nodeName, outputName }, index) => {
|
||||
return (
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
@@ -325,7 +331,7 @@ const MultipleReferenceSelector = ({
|
||||
popDirection={popDirection}
|
||||
/>
|
||||
);
|
||||
}, [formatList, list, onSelect, placeholder, popDirection, value]);
|
||||
}, [invalidList, list, onSelect, placeholder, popDirection, value]);
|
||||
|
||||
return ArraySelector;
|
||||
};
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { createContext } from 'use-context-selector';
|
||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { useCreation, useMemoizedFn } from 'ahooks';
|
||||
import React, { Dispatch, SetStateAction, ReactNode, useEffect, useMemo } from 'react';
|
||||
import { Edge, EdgeChange, Node, NodeChange, useEdgesState, useNodesState } from 'reactflow';
|
||||
|
||||
@@ -50,7 +50,7 @@ const WorkflowInitContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [nodes = [], setNodes, onNodesChange] = useNodesState<FlowNodeItemType>([]);
|
||||
const getNodes = useMemoizedFn(() => nodes);
|
||||
const nodeListString = JSON.stringify(nodes.map((node) => node.data));
|
||||
const nodeList = useMemo(
|
||||
const nodeList = useCreation(
|
||||
() => JSON.parse(nodeListString) as FlowNodeItemType[],
|
||||
[nodeListString]
|
||||
);
|
||||
@@ -73,7 +73,7 @@ const WorkflowInitContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
: item
|
||||
)
|
||||
);
|
||||
}, [edges.length]);
|
||||
}, [nodeList, edges.length]);
|
||||
|
||||
const actionContextValue = useMemo(
|
||||
() => ({
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { useDebounceEffect } from 'ahooks';
|
||||
import { useDebounceEffect, useMemoizedFn } from 'ahooks';
|
||||
import React, { ReactNode, useMemo, useRef, useState } from 'react';
|
||||
import { createContext, useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowInitContext, WorkflowNodeEdgeContext } from './workflowInitContext';
|
||||
@@ -7,10 +7,18 @@ import { AppContext } from '../../context';
|
||||
import { compareSnapshot } from '@/web/core/workflow/utils';
|
||||
import { useBeforeunload } from '@fastgpt/web/hooks/useBeforeunload';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { getNodesBounds, Node } from 'reactflow';
|
||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import {
|
||||
Input_Template_Node_Height,
|
||||
Input_Template_Node_Width
|
||||
} from '@fastgpt/global/core/workflow/template/input';
|
||||
|
||||
type WorkflowStatusContextType = {
|
||||
isSaved: boolean;
|
||||
leaveSaveSign: React.MutableRefObject<boolean>;
|
||||
resetParentNodeSizeAndPosition: (parentId: string) => void;
|
||||
};
|
||||
|
||||
export const WorkflowStatusContext = createContext<WorkflowStatusContextType>({
|
||||
@@ -75,12 +83,72 @@ const WorkflowStatusContextProvider = ({ children }: { children: ReactNode }) =>
|
||||
}
|
||||
});
|
||||
|
||||
const onNodesChange = useContextSelector(WorkflowNodeEdgeContext, (state) => state.onNodesChange);
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
const resetParentNodeSizeAndPosition = useMemoizedFn((parentId: string) => {
|
||||
const { childNodes, loopNode } = nodes.reduce(
|
||||
(acc, node) => {
|
||||
if (node.data.parentNodeId === parentId) {
|
||||
acc.childNodes.push(node);
|
||||
}
|
||||
if (node.id === parentId) {
|
||||
acc.loopNode = node;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{ childNodes: [] as Node[], loopNode: undefined as Node<FlowNodeItemType> | undefined }
|
||||
);
|
||||
|
||||
if (!loopNode) return;
|
||||
|
||||
const rect = getNodesBounds(childNodes);
|
||||
// Calculate parent node size with minimum width/height constraints
|
||||
const width = Math.max(rect.width + 80, 840);
|
||||
const height = Math.max(rect.height + 80, 600);
|
||||
|
||||
const offsetHeight =
|
||||
loopNode.data.inputs.find((input) => input.key === NodeInputKeyEnum.loopNodeInputHeight)
|
||||
?.value ?? 83;
|
||||
|
||||
// Update parentNode size and position
|
||||
onChangeNode({
|
||||
nodeId: parentId,
|
||||
type: 'updateInput',
|
||||
key: NodeInputKeyEnum.nodeWidth,
|
||||
value: {
|
||||
...Input_Template_Node_Width,
|
||||
value: width
|
||||
}
|
||||
});
|
||||
onChangeNode({
|
||||
nodeId: parentId,
|
||||
type: 'updateInput',
|
||||
key: NodeInputKeyEnum.nodeHeight,
|
||||
value: {
|
||||
...Input_Template_Node_Height,
|
||||
value: height
|
||||
}
|
||||
});
|
||||
// Update parentNode position
|
||||
onNodesChange([
|
||||
{
|
||||
id: parentId,
|
||||
type: 'position',
|
||||
position: {
|
||||
x: Math.round(rect.x - 70),
|
||||
y: Math.round(rect.y - offsetHeight - 240)
|
||||
}
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
const contextValue = useMemo(() => {
|
||||
return {
|
||||
isSaved,
|
||||
leaveSaveSign
|
||||
leaveSaveSign,
|
||||
resetParentNodeSizeAndPosition
|
||||
};
|
||||
}, [isSaved]);
|
||||
}, [isSaved, resetParentNodeSizeAndPosition]);
|
||||
return (
|
||||
<WorkflowStatusContext.Provider value={contextValue}>{children}</WorkflowStatusContext.Provider>
|
||||
);
|
||||
|
@@ -107,8 +107,10 @@ export const useChatTest = ({
|
||||
async () => {
|
||||
if (!appId || !chatId) return;
|
||||
const res = await getInitChatInfo({ appId, chatId });
|
||||
|
||||
resetVariables({
|
||||
variables: res.variables
|
||||
variables: res.variables,
|
||||
variableList: res.app?.chatConfig?.variables
|
||||
});
|
||||
},
|
||||
{
|
||||
|
@@ -74,7 +74,8 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
|
||||
// reset chat variables
|
||||
resetVariables({
|
||||
variables: res.variables
|
||||
variables: res.variables,
|
||||
variableList: res.app?.chatConfig?.variables
|
||||
});
|
||||
},
|
||||
{
|
||||
|
@@ -22,8 +22,7 @@ import { connectToDatabase } from '@/service/mongo';
|
||||
import NextHead from '@/components/common/NextHead';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import ChatContextProvider, { ChatContext } from '@/web/core/chat/context/chatContext';
|
||||
import { InitChatResponse } from '@/global/core/chat/api';
|
||||
import { defaultChatData, GetChatTypeEnum } from '@/global/core/chat/constants';
|
||||
import { GetChatTypeEnum } from '@/global/core/chat/constants';
|
||||
import { useMount } from 'ahooks';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
@@ -37,6 +36,8 @@ import ChatRecordContextProvider, {
|
||||
} from '@/web/core/chat/context/chatRecordContext';
|
||||
import { useChatStore } from '@/web/core/chat/context/useChatStore';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { useI18nLng } from '@fastgpt/web/hooks/useI18n';
|
||||
|
||||
const CustomPluginRunBox = dynamic(() => import('./components/CustomPluginRunBox'));
|
||||
|
||||
type Props = {
|
||||
@@ -102,7 +103,8 @@ const OutLink = (props: Props) => {
|
||||
setChatBoxData(res);
|
||||
|
||||
resetVariables({
|
||||
variables: res.variables
|
||||
variables: res.variables,
|
||||
variableList: res.app?.chatConfig?.variables
|
||||
});
|
||||
|
||||
return res;
|
||||
@@ -299,8 +301,9 @@ const OutLink = (props: Props) => {
|
||||
|
||||
const Render = (props: Props) => {
|
||||
const { shareId, authToken, customUid, appId } = props;
|
||||
const { localUId, loaded } = useShareChatStore();
|
||||
const { localUId } = useShareChatStore();
|
||||
const { source, chatId, setSource, setAppId, setOutLinkAuthData } = useChatStore();
|
||||
const { setUserDefaultLng } = useI18nLng();
|
||||
|
||||
const chatHistoryProviderParams = useMemo(() => {
|
||||
return { shareId, outLinkUid: authToken || customUid || localUId };
|
||||
@@ -317,6 +320,7 @@ const Render = (props: Props) => {
|
||||
|
||||
useMount(() => {
|
||||
setSource('share');
|
||||
setUserDefaultLng(true);
|
||||
});
|
||||
|
||||
// Set outLinkAuthData
|
||||
|
@@ -79,7 +79,8 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
|
||||
// reset chat records
|
||||
resetVariables({
|
||||
variables: res.variables
|
||||
variables: res.variables,
|
||||
variableList: res.app?.chatConfig?.variables
|
||||
});
|
||||
},
|
||||
{
|
||||
|
@@ -23,5 +23,7 @@ export const langMap = {
|
||||
|
||||
export const serviceSideProps = (content: any, ns: I18nNsType = []) => {
|
||||
const lang = content.req?.cookies?.NEXT_LOCALE || content.locale;
|
||||
return serverSideTranslations(lang, ['common', ...ns], null);
|
||||
|
||||
const extraLng = content.req?.cookies?.NEXT_LOCALE ? undefined : content.locales;
|
||||
return serverSideTranslations(lang, ['common', ...ns], null, extraLng);
|
||||
};
|
||||
|
@@ -6,7 +6,7 @@ import { ComponentRef as ChatComponentRef } from '@/components/core/chat/ChatCon
|
||||
import { useForm, UseFormReturn } from 'react-hook-form';
|
||||
import { defaultChatData } from '@/global/core/chat/constants';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
|
||||
import { AppChatConfigType, VariableItemType } from '@fastgpt/global/core/app/type';
|
||||
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
|
||||
|
||||
type ChatBoxDataType = {
|
||||
@@ -29,7 +29,10 @@ type ChatItemContextType = {
|
||||
variablesForm: UseFormReturn<ChatBoxInputFormType, any>;
|
||||
pluginRunTab: PluginRunBoxTabEnum;
|
||||
setPluginRunTab: React.Dispatch<React.SetStateAction<PluginRunBoxTabEnum>>;
|
||||
resetVariables: (props?: { variables?: Record<string, any> }) => void;
|
||||
resetVariables: (props?: {
|
||||
variables?: Record<string, any>;
|
||||
variableList?: VariableItemType[];
|
||||
}) => void;
|
||||
clearChatRecords: () => void;
|
||||
chatBoxData: ChatBoxDataType;
|
||||
setChatBoxData: React.Dispatch<React.SetStateAction<ChatBoxDataType>>;
|
||||
@@ -44,7 +47,10 @@ export const ChatItemContext = createContext<ChatItemContextType>({
|
||||
setPluginRunTab: function (value: React.SetStateAction<PluginRunBoxTabEnum>): void {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
resetVariables: function (props?: { variables?: Record<string, any> }): void {
|
||||
resetVariables: function (props?: {
|
||||
variables?: Record<string, any>;
|
||||
variableList?: VariableItemType[];
|
||||
}): void {
|
||||
throw new Error('Function not implemented.');
|
||||
},
|
||||
clearChatRecords: function (): void {
|
||||
@@ -69,27 +75,21 @@ const ChatItemContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [pluginRunTab, setPluginRunTab] = useState<PluginRunBoxTabEnum>(PluginRunBoxTabEnum.input);
|
||||
|
||||
const resetVariables = useCallback(
|
||||
(props?: { variables?: Record<string, any> }) => {
|
||||
const { variables = {} } = props || {};
|
||||
(props?: { variables?: Record<string, any>; variableList?: VariableItemType[] }) => {
|
||||
const { variables, variableList = [] } = props || {};
|
||||
|
||||
// Reset to empty input
|
||||
const data = variablesForm.getValues();
|
||||
|
||||
// Reset the old variables to empty
|
||||
const resetVariables: Record<string, any> = {};
|
||||
for (const key in data.variables) {
|
||||
resetVariables[key] = (() => {
|
||||
if (Array.isArray(data.variables[key])) {
|
||||
return [];
|
||||
}
|
||||
return '';
|
||||
})();
|
||||
let newVariableValue: Record<string, any> = {};
|
||||
if (variables) {
|
||||
variableList.forEach((item) => {
|
||||
newVariableValue[item.key] = variables[item.key];
|
||||
});
|
||||
} else {
|
||||
variableList.forEach((item) => {
|
||||
newVariableValue[item.key] = item.defaultValue;
|
||||
});
|
||||
}
|
||||
|
||||
variablesForm.setValue('variables', {
|
||||
...resetVariables,
|
||||
...variables
|
||||
});
|
||||
variablesForm.setValue('variables', newVariableValue);
|
||||
},
|
||||
[variablesForm]
|
||||
);
|
||||
|
@@ -119,6 +119,7 @@ export const storeNode2FlowNode = ({
|
||||
|
||||
selectedTypeIndex: storeInput.selectedTypeIndex ?? templateInput.selectedTypeIndex,
|
||||
value: storeInput.value ?? templateInput.value,
|
||||
valueType: storeInput.valueType ?? templateInput.valueType,
|
||||
label: storeInput.label ?? templateInput.label
|
||||
};
|
||||
})
|
||||
@@ -148,7 +149,8 @@ export const storeNode2FlowNode = ({
|
||||
|
||||
id: storeOutput.id ?? templateOutput.id,
|
||||
label: storeOutput.label ?? templateOutput.label,
|
||||
value: storeOutput.value ?? templateOutput.value
|
||||
value: storeOutput.value ?? templateOutput.value,
|
||||
valueType: storeOutput.valueType ?? templateOutput.valueType
|
||||
};
|
||||
})
|
||||
.concat(
|
||||
|
@@ -1,14 +1,6 @@
|
||||
import { getDocPath } from '@/web/common/system/doc';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
|
||||
export const AI_POINT_USAGE_CARD_ROUTE = '/price#point-card';
|
||||
export const getAiPointUsageCardRoute = () => {
|
||||
const subPlans = useSystemStore.getState().subPlans;
|
||||
return subPlans?.planDescriptionUrl
|
||||
? getDocPath(subPlans.planDescriptionUrl)
|
||||
: AI_POINT_USAGE_CARD_ROUTE;
|
||||
};
|
||||
|
||||
export const EXTRA_PLAN_CARD_ROUTE = '/price#extra-plan';
|
||||
export const getExtraPlanCardRoute = () => {
|
||||
const subPlans = useSystemStore.getState().subPlans;
|
||||
|
Reference in New Issue
Block a user