Commit ddddd998 by Archer Committed by GitHub

Fix workflow detail (#3382)

* fix: loop node init

* fix: workflow detail

* fix: point table

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