feat: iframe code block;perf: workflow selector type (#3076)
* feat: iframe code block * perf: workflow selector type
This commit is contained in:
21
projects/app/src/components/Markdown/codeBlock/Iframe.tsx
Normal file
21
projects/app/src/components/Markdown/codeBlock/Iframe.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import React, { useEffect, useRef, useCallback, useState } from 'react';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
|
||||
const MermaidBlock = ({ code }: { code: string }) => {
|
||||
return (
|
||||
<Box w={'100%'}>
|
||||
<iframe
|
||||
src={code}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
minHeight: '40vh',
|
||||
border: 'none'
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default MermaidBlock;
|
||||
@@ -22,6 +22,7 @@ const CodeLight = dynamic(() => import('./CodeLight'), { ssr: false });
|
||||
const MermaidCodeBlock = dynamic(() => import('./img/MermaidCodeBlock'), { ssr: false });
|
||||
const MdImage = dynamic(() => import('./img/Image'), { ssr: false });
|
||||
const EChartsCodeBlock = dynamic(() => import('./img/EChartsCodeBlock'), { ssr: false });
|
||||
const IframeCodeBlock = dynamic(() => import('./codeBlock/Iframe'), { ssr: false });
|
||||
|
||||
const ChatGuide = dynamic(() => import('./chat/Guide'), { ssr: false });
|
||||
const QuestionGuide = dynamic(() => import('./chat/QuestionGuide'), { ssr: false });
|
||||
@@ -76,7 +77,7 @@ const Markdown = ({
|
||||
);
|
||||
|
||||
return finalText;
|
||||
}, [showAnimation, source]);
|
||||
}, [forbidZhFormat, showAnimation, source]);
|
||||
|
||||
const urlTransform = useCallback((val: string) => {
|
||||
return val;
|
||||
@@ -123,6 +124,9 @@ function Code(e: any) {
|
||||
if (codeType === CodeClassNameEnum.echarts) {
|
||||
return <EChartsCodeBlock code={strChildren} />;
|
||||
}
|
||||
if (codeType === CodeClassNameEnum.iframe) {
|
||||
return <IframeCodeBlock code={strChildren} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<CodeLight className={className} codeBlock={codeBlock} match={match}>
|
||||
|
||||
@@ -5,7 +5,8 @@ export enum CodeClassNameEnum {
|
||||
echarts = 'echarts',
|
||||
quote = 'quote',
|
||||
files = 'files',
|
||||
latex = 'latex'
|
||||
latex = 'latex',
|
||||
iframe = 'iframe'
|
||||
}
|
||||
|
||||
function htmlTableToLatex(html: string) {
|
||||
|
||||
@@ -27,6 +27,8 @@ import { WorkflowContext } from '../../../context';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { getWorkflowGlobalVariables } from '@/web/core/workflow/utils';
|
||||
import { AppContext } from '../../../../context';
|
||||
import { isReferenceValue, isReferenceValueArray } from '@fastgpt/global/core/workflow/utils';
|
||||
import { ReferenceItemValueType } from '@fastgpt/global/core/workflow/type/io';
|
||||
|
||||
const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -34,7 +36,7 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const { onChangeNode, nodeList } = useContextSelector(WorkflowContext, (v) => v);
|
||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||
|
||||
const arrayValue = inputs.find((input) => input.key === NodeInputKeyEnum.loopInputArray)?.value;
|
||||
const loopInputArray = inputs.find((input) => input.key === NodeInputKeyEnum.loopInputArray);
|
||||
|
||||
const { nodeWidth, nodeHeight } = useMemo(() => {
|
||||
return {
|
||||
@@ -49,41 +51,53 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
}, [nodeId, nodeList]);
|
||||
|
||||
// Detect and update array input type
|
||||
useEffect(() => {
|
||||
const inputsClone = cloneDeep(inputs);
|
||||
const newValueType = useMemo(() => {
|
||||
if (!loopInputArray) return WorkflowIOValueTypeEnum.arrayAny;
|
||||
|
||||
const nodeIds = nodeList.map((node) => node.nodeId);
|
||||
const globalVariables = getWorkflowGlobalVariables({
|
||||
nodes: nodeList,
|
||||
chatConfig: appDetail.chatConfig
|
||||
});
|
||||
|
||||
let arrayType: WorkflowIOValueTypeEnum | undefined;
|
||||
if (arrayValue[0]?.[0] === VARIABLE_NODE_ID) {
|
||||
arrayType = globalVariables.find((item) => item.key === arrayValue[0]?.[1])?.valueType;
|
||||
} else {
|
||||
const node = nodeList.find((node) => node.nodeId === arrayValue[0]?.[0]);
|
||||
const output = node?.outputs.find((output) => output.id === arrayValue[0]?.[1]);
|
||||
arrayType = output?.valueType;
|
||||
}
|
||||
const getValueType = (value: ReferenceItemValueType) => {
|
||||
if (value?.[0] === VARIABLE_NODE_ID) {
|
||||
return globalVariables.find((item) => item.key === value[1])?.valueType;
|
||||
} else {
|
||||
const node = nodeList.find((node) => node.nodeId === value?.[0]);
|
||||
const output = node?.outputs.find((output) => output.id === value?.[1]);
|
||||
return output?.valueType;
|
||||
}
|
||||
};
|
||||
|
||||
const arrayInput = inputsClone.find((input) => input.key === NodeInputKeyEnum.loopInputArray);
|
||||
const valueType = (() => {
|
||||
if (isReferenceValue(loopInputArray?.value, nodeIds)) {
|
||||
return getValueType(loopInputArray?.value);
|
||||
} else if (isReferenceValueArray(loopInputArray?.value, nodeIds)) {
|
||||
return getValueType(loopInputArray?.value?.[0]);
|
||||
} else {
|
||||
return WorkflowIOValueTypeEnum.arrayAny;
|
||||
}
|
||||
})();
|
||||
|
||||
if (!arrayInput) {
|
||||
return;
|
||||
}
|
||||
const type = ArrayTypeMap[valueType as keyof typeof ArrayTypeMap];
|
||||
|
||||
return type ?? WorkflowIOValueTypeEnum.arrayAny;
|
||||
}, [appDetail.chatConfig, loopInputArray, nodeList]);
|
||||
useEffect(() => {
|
||||
if (!loopInputArray) return;
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'updateInput',
|
||||
key: NodeInputKeyEnum.loopInputArray,
|
||||
value: {
|
||||
...arrayInput,
|
||||
valueType: arrayType
|
||||
? ArrayTypeMap[arrayType as keyof typeof ArrayTypeMap]
|
||||
: WorkflowIOValueTypeEnum.arrayAny
|
||||
...loopInputArray,
|
||||
valueType: newValueType
|
||||
}
|
||||
});
|
||||
}, [appDetail.chatConfig, arrayValue, inputs, nodeId, nodeList, onChangeNode]);
|
||||
}, [newValueType]);
|
||||
|
||||
// Update childrenNodeIdList
|
||||
useEffect(() => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
@@ -112,7 +126,7 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
>
|
||||
<Container position={'relative'} flex={1}>
|
||||
<IOTitle text={t('common:common.Input')} />
|
||||
<Box mb={6} maxW={'360'}>
|
||||
<Box mb={6} maxW={'500px'}>
|
||||
<RenderInput nodeId={nodeId} flowInputList={inputs} />
|
||||
</Box>
|
||||
<FormLabel required fontWeight={'medium'} mb={3} color={'myGray.600'}>
|
||||
|
||||
@@ -7,14 +7,13 @@ import RenderInput from './render/RenderInput';
|
||||
import { Box, Button, Flex, HStack } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { SmallAddIcon } from '@chakra-ui/icons';
|
||||
import { NodeInputKeyEnum, VARIABLE_NODE_ID } from '@fastgpt/global/core/workflow/constants';
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { getOneQuoteInputTemplate } from '@fastgpt/global/core/workflow/template/system/datasetConcat';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import MySlider from '@/components/Slider';
|
||||
import {
|
||||
FlowNodeInputItemType,
|
||||
ReferenceValueProps
|
||||
ReferenceItemValueType
|
||||
} from '@fastgpt/global/core/workflow/type/io.d';
|
||||
import RenderOutput from './render/RenderOutput';
|
||||
import IOTitle from '../components/IOTitle';
|
||||
@@ -24,94 +23,13 @@ import { ReferSelector, useReference } from './render/RenderInput/templates/Refe
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import ValueTypeLabel from './render/ValueTypeLabel';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { isWorkflowStartOutput } from '@fastgpt/global/core/workflow/template/system/workflowStart';
|
||||
import { getWebLLMModel } from '@/web/common/system/utils';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
|
||||
const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const { t } = useTranslation();
|
||||
const { llmModelList } = useSystemStore();
|
||||
const { nodeId, inputs, outputs } = data;
|
||||
const { nodeList, onChangeNode } = useContextSelector(WorkflowContext, (v) => v);
|
||||
|
||||
const Reference = useMemoizedFn(
|
||||
({ nodeId, inputChildren }: { nodeId: string; inputChildren: FlowNodeInputItemType }) => {
|
||||
const { t } = useTranslation();
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
|
||||
const { referenceList, formatValue } = useReference({
|
||||
nodeId,
|
||||
valueType: inputChildren.valueType,
|
||||
value: inputChildren.value
|
||||
});
|
||||
|
||||
const onSelect = useCallback(
|
||||
(e: ReferenceValueProps | ReferenceValueProps[]) => {
|
||||
const workflowStartNode = nodeList.find(
|
||||
(node) => node.flowNodeType === FlowNodeTypeEnum.workflowStart
|
||||
);
|
||||
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'replaceInput',
|
||||
key: inputChildren.key,
|
||||
value: {
|
||||
...inputChildren,
|
||||
value:
|
||||
e[0] === workflowStartNode?.id && !isWorkflowStartOutput(e[1] as string)
|
||||
? [VARIABLE_NODE_ID, e[1]]
|
||||
: e
|
||||
}
|
||||
});
|
||||
},
|
||||
[inputChildren, nodeId, nodeList, onChangeNode]
|
||||
);
|
||||
|
||||
const onDel = useCallback(() => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'delInput',
|
||||
key: inputChildren.key
|
||||
});
|
||||
}, [inputChildren.key, nodeId, onChangeNode]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex alignItems={'center'} mb={1}>
|
||||
<FormLabel required={inputChildren.required}>{t(inputChildren.label as any)}</FormLabel>
|
||||
{/* value */}
|
||||
<ValueTypeLabel
|
||||
valueType={inputChildren.valueType}
|
||||
valueDesc={inputChildren.valueDesc}
|
||||
/>
|
||||
|
||||
<MyIcon
|
||||
className="delete"
|
||||
name={'delete'}
|
||||
w={'14px'}
|
||||
color={'myGray.500'}
|
||||
cursor={'pointer'}
|
||||
ml={2}
|
||||
_hover={{ color: 'red.600' }}
|
||||
onClick={onDel}
|
||||
/>
|
||||
</Flex>
|
||||
<ReferSelector
|
||||
placeholder={t(
|
||||
(inputChildren.referencePlaceholder as any) ||
|
||||
t('common:core.module.Dataset quote.select')
|
||||
)}
|
||||
list={referenceList}
|
||||
value={formatValue}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const CustomComponent = useMemo(() => {
|
||||
const quoteList = inputs.filter((item) => item.canEdit);
|
||||
const tokenLimit = (() => {
|
||||
@@ -184,7 +102,7 @@ const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
<Box mt={2}>
|
||||
{quoteList.map((children) => (
|
||||
<Box key={children.key} _notLast={{ mb: 3 }}>
|
||||
<Reference nodeId={nodeId} inputChildren={children} />
|
||||
<VariableSelector nodeId={nodeId} inputChildren={children} />
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
@@ -192,7 +110,7 @@ const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
);
|
||||
}
|
||||
};
|
||||
}, [Reference, inputs, nodeId, nodeList, onChangeNode, t, llmModelList]);
|
||||
}, [inputs, nodeId, nodeList, onChangeNode, t]);
|
||||
|
||||
const Render = useMemo(() => {
|
||||
return (
|
||||
@@ -212,3 +130,75 @@ const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
return Render;
|
||||
};
|
||||
export default React.memo(NodeDatasetConcat);
|
||||
|
||||
const VariableSelector = ({
|
||||
nodeId,
|
||||
inputChildren
|
||||
}: {
|
||||
nodeId: string;
|
||||
inputChildren: FlowNodeInputItemType;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { onChangeNode } = useContextSelector(WorkflowContext, (v) => v);
|
||||
|
||||
const { referenceList } = useReference({
|
||||
nodeId,
|
||||
valueType: inputChildren.valueType
|
||||
});
|
||||
|
||||
const onSelect = useCallback(
|
||||
(e: ReferenceItemValueType) => {
|
||||
if (!e) return;
|
||||
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'replaceInput',
|
||||
key: inputChildren.key,
|
||||
value: {
|
||||
...inputChildren,
|
||||
value: e
|
||||
}
|
||||
});
|
||||
},
|
||||
[inputChildren, nodeId, onChangeNode]
|
||||
);
|
||||
|
||||
const onDel = useCallback(() => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'delInput',
|
||||
key: inputChildren.key
|
||||
});
|
||||
}, [inputChildren.key, nodeId, onChangeNode]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex alignItems={'center'} mb={1}>
|
||||
<FormLabel required={inputChildren.required}>{t(inputChildren.label as any)}</FormLabel>
|
||||
{/* value */}
|
||||
<ValueTypeLabel valueType={inputChildren.valueType} valueDesc={inputChildren.valueDesc} />
|
||||
|
||||
<MyIcon
|
||||
className="delete"
|
||||
name={'delete'}
|
||||
w={'14px'}
|
||||
color={'myGray.500'}
|
||||
cursor={'pointer'}
|
||||
ml={2}
|
||||
_hover={{ color: 'red.600' }}
|
||||
onClick={onDel}
|
||||
/>
|
||||
</Flex>
|
||||
<ReferSelector
|
||||
placeholder={t(
|
||||
(inputChildren.referencePlaceholder as any) ||
|
||||
t('common:core.module.Dataset quote.select')
|
||||
)}
|
||||
list={referenceList}
|
||||
value={inputChildren.value}
|
||||
onSelect={onSelect}
|
||||
isArray={false}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ import Container from '../../components/Container';
|
||||
import { MinusIcon, SmallAddIcon } from '@chakra-ui/icons';
|
||||
import { IfElseListItemType } from '@fastgpt/global/core/workflow/template/system/ifElse/type';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { ReferenceValueProps } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { ReferenceItemValueType } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { ReferSelector, useReference } from '../render/RenderInput/templates/Reference';
|
||||
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
@@ -122,7 +122,7 @@ const ListItem = ({
|
||||
<Flex gap={2} mb={2} alignItems={'center'}>
|
||||
{/* variable reference */}
|
||||
<Box minW={'250px'}>
|
||||
<Reference
|
||||
<VariableSelector
|
||||
nodeId={nodeId}
|
||||
variable={item.variable}
|
||||
onSelect={(e) => {
|
||||
@@ -302,29 +302,29 @@ const ListItem = ({
|
||||
|
||||
export default React.memo(ListItem);
|
||||
|
||||
const Reference = ({
|
||||
const VariableSelector = ({
|
||||
nodeId,
|
||||
variable,
|
||||
onSelect
|
||||
}: {
|
||||
nodeId: string;
|
||||
variable?: ReferenceValueProps;
|
||||
onSelect: (e: ReferenceValueProps) => void;
|
||||
variable?: ReferenceItemValueType;
|
||||
onSelect: (e: ReferenceItemValueType) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { referenceList, formatValue } = useReference({
|
||||
const { referenceList } = useReference({
|
||||
nodeId,
|
||||
valueType: WorkflowIOValueTypeEnum.any,
|
||||
value: variable
|
||||
valueType: WorkflowIOValueTypeEnum.any
|
||||
});
|
||||
|
||||
return (
|
||||
<ReferSelector
|
||||
placeholder={t('common:select_reference_variable')}
|
||||
list={referenceList}
|
||||
value={formatValue}
|
||||
onSelect={onSelect as any}
|
||||
value={variable}
|
||||
onSelect={onSelect}
|
||||
isArray={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -336,7 +336,7 @@ const ConditionSelect = ({
|
||||
onSelect
|
||||
}: {
|
||||
condition?: VariableConditionEnum;
|
||||
variable?: ReferenceValueProps;
|
||||
variable?: ReferenceItemValueType;
|
||||
onSelect: (e: VariableConditionEnum) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -414,7 +414,7 @@ const ConditionValueInput = ({
|
||||
onChange
|
||||
}: {
|
||||
value?: string;
|
||||
variable?: ReferenceValueProps;
|
||||
variable?: ReferenceItemValueType;
|
||||
condition?: VariableConditionEnum;
|
||||
onChange: (e: string) => void;
|
||||
}) => {
|
||||
|
||||
@@ -2,18 +2,12 @@ import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { NodeProps } from 'reactflow';
|
||||
import NodeCard from '../render/NodeCard';
|
||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Box, Button, Flex } from '@chakra-ui/react';
|
||||
import { SmallAddIcon } from '@chakra-ui/icons';
|
||||
import {
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowNodeTypeEnum
|
||||
} from '@fastgpt/global/core/workflow/node/constant';
|
||||
import Container from '../../components/Container';
|
||||
import { FlowNodeInputItemType, ReferenceValueProps } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { VARIABLE_NODE_ID, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { FlowNodeInputItemType, ReferenceValueType } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import RenderInput from '../render/RenderInput';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../../../context';
|
||||
import IOTitle from '../../components/IOTitle';
|
||||
@@ -24,7 +18,6 @@ import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||
import { isWorkflowStartOutput } from '@fastgpt/global/core/workflow/template/system/workflowStart';
|
||||
import PluginOutputEditModal, { defaultOutput } from './PluginOutputEditModal';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
|
||||
@@ -113,38 +106,27 @@ function Reference({
|
||||
content: workflowT('confirm_delete_field_tip')
|
||||
});
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
|
||||
const [editField, setEditField] = useState<FlowNodeInputItemType>();
|
||||
|
||||
const onSelect = useCallback(
|
||||
(e: ReferenceValueProps | ReferenceValueProps[]) => {
|
||||
const workflowStartNode = nodeList.find(
|
||||
(node) => node.flowNodeType === FlowNodeTypeEnum.workflowStart
|
||||
);
|
||||
|
||||
const value =
|
||||
e[0] === workflowStartNode?.id && !isWorkflowStartOutput(e[1] as string)
|
||||
? [VARIABLE_NODE_ID, e[1]]
|
||||
: e;
|
||||
|
||||
(e: ReferenceValueType) => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'updateInput',
|
||||
key: input.key,
|
||||
value: {
|
||||
...input,
|
||||
value
|
||||
value: e
|
||||
}
|
||||
});
|
||||
},
|
||||
[input, nodeId, nodeList, onChangeNode]
|
||||
[input, nodeId, onChangeNode]
|
||||
);
|
||||
|
||||
const { referenceList, formatValue } = useReference({
|
||||
const { referenceList } = useReference({
|
||||
nodeId,
|
||||
valueType: input.valueType,
|
||||
value: input.value
|
||||
valueType: input.valueType
|
||||
});
|
||||
|
||||
const onUpdateField = useCallback(
|
||||
@@ -217,7 +199,7 @@ function Reference({
|
||||
<ReferSelector
|
||||
placeholder={t((input.referencePlaceholder as any) || 'select_reference_variable')}
|
||||
list={referenceList}
|
||||
value={formatValue}
|
||||
value={input.value}
|
||||
onSelect={onSelect}
|
||||
isArray={input.valueType?.includes('array')}
|
||||
/>
|
||||
|
||||
@@ -26,14 +26,14 @@ import Container from '../components/Container';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { SmallAddIcon } from '@chakra-ui/icons';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { ReferenceValueProps } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { ReferenceItemValueType, ReferenceValueType } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { ReferSelector, useReference } from './render/RenderInput/templates/Reference';
|
||||
import { getRefData } from '@/web/core/workflow/utils';
|
||||
import { isReferenceValue } from '@fastgpt/global/core/workflow/utils';
|
||||
import { AppContext } from '@/pages/app/detail/components/context';
|
||||
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
||||
import { useCreation, useMemoizedFn } from 'ahooks';
|
||||
import { getEditorVariables } from '../../utils';
|
||||
import { isArray } from 'lodash';
|
||||
|
||||
const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const { inputs = [], nodeId } = data;
|
||||
@@ -103,18 +103,17 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
||||
(item) => item.renderType === updateItem.renderType
|
||||
);
|
||||
|
||||
const nodeIds = nodeList.map((node) => node.nodeId);
|
||||
const handleUpdate = (newValue: ReferenceValueProps | string) => {
|
||||
if (isReferenceValue(newValue, nodeIds)) {
|
||||
const handleUpdate = (newValue: ReferenceValueType | string) => {
|
||||
if (typeof newValue === 'string') {
|
||||
onUpdateList(
|
||||
updateList.map((update, i) =>
|
||||
i === index ? { ...update, value: newValue as ReferenceValueProps } : update
|
||||
i === index ? { ...update, value: ['', newValue] } : update
|
||||
)
|
||||
);
|
||||
} else {
|
||||
onUpdateList(
|
||||
updateList.map((update, i) =>
|
||||
i === index ? { ...update, value: ['', newValue as string] } : update
|
||||
i === index ? { ...update, value: newValue as ReferenceItemValueType } : update
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -124,7 +123,7 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
||||
<Container key={index} mt={4} w={'full'} mx={0}>
|
||||
<Flex alignItems={'center'}>
|
||||
<Flex w={'60px'}>{t('common:core.workflow.variable')}</Flex>
|
||||
<Reference
|
||||
<VariableSelector
|
||||
nodeId={nodeId}
|
||||
variable={updateItem.variable}
|
||||
onSelect={(value) => {
|
||||
@@ -135,7 +134,7 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
||||
...update,
|
||||
value: ['', ''],
|
||||
valueType,
|
||||
variable: value
|
||||
variable: value as ReferenceItemValueType
|
||||
};
|
||||
}
|
||||
return update;
|
||||
@@ -202,7 +201,7 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
||||
{(() => {
|
||||
if (updateItem.renderType === FlowNodeInputTypeEnum.reference) {
|
||||
return (
|
||||
<Reference
|
||||
<VariableSelector
|
||||
nodeId={nodeId}
|
||||
variable={updateItem.value}
|
||||
valueType={valueType}
|
||||
@@ -210,11 +209,14 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const inputValue = isArray(updateItem.value?.[1]) ? '' : updateItem.value?.[1];
|
||||
|
||||
if (valueType === WorkflowIOValueTypeEnum.string) {
|
||||
return (
|
||||
<Box w={'300px'}>
|
||||
<PromptEditor
|
||||
value={updateItem.value?.[1] || ''}
|
||||
value={inputValue || ''}
|
||||
onChange={handleUpdate}
|
||||
showOpenModal={false}
|
||||
variableLabels={variables}
|
||||
@@ -225,7 +227,7 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
||||
}
|
||||
if (valueType === WorkflowIOValueTypeEnum.number) {
|
||||
return (
|
||||
<NumberInput value={Number(updateItem.value?.[1]) || 0}>
|
||||
<NumberInput value={Number(inputValue) || 0}>
|
||||
<NumberInputField bg="white" onChange={(e) => handleUpdate(e.target.value)} />
|
||||
<NumberInputStepper>
|
||||
<NumberIncrementStepper />
|
||||
@@ -237,7 +239,7 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
||||
if (valueType === WorkflowIOValueTypeEnum.boolean) {
|
||||
return (
|
||||
<Switch
|
||||
defaultChecked={updateItem.value?.[1] === 'true'}
|
||||
defaultChecked={inputValue === 'true'}
|
||||
onChange={(e) => handleUpdate(String(e.target.checked))}
|
||||
/>
|
||||
);
|
||||
@@ -246,7 +248,7 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
||||
return (
|
||||
<Box w={'300px'}>
|
||||
<PromptEditor
|
||||
value={updateItem.value?.[1] || ''}
|
||||
value={inputValue || ''}
|
||||
onChange={handleUpdate}
|
||||
showOpenModal={false}
|
||||
variableLabels={variables}
|
||||
@@ -302,31 +304,31 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
||||
};
|
||||
export default React.memo(NodeVariableUpdate);
|
||||
|
||||
const Reference = ({
|
||||
const VariableSelector = ({
|
||||
nodeId,
|
||||
variable,
|
||||
valueType,
|
||||
onSelect
|
||||
}: {
|
||||
nodeId: string;
|
||||
variable?: ReferenceValueProps;
|
||||
variable?: ReferenceValueType;
|
||||
valueType?: WorkflowIOValueTypeEnum;
|
||||
onSelect: (e: ReferenceValueProps) => void;
|
||||
onSelect: (e: ReferenceValueType) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { referenceList, formatValue } = useReference({
|
||||
const { referenceList } = useReference({
|
||||
nodeId,
|
||||
valueType,
|
||||
value: variable
|
||||
valueType
|
||||
});
|
||||
|
||||
return (
|
||||
<ReferSelector
|
||||
placeholder={t('common:select_reference_variable')}
|
||||
list={referenceList}
|
||||
value={formatValue}
|
||||
onSelect={onSelect as any}
|
||||
value={variable}
|
||||
onSelect={onSelect}
|
||||
isArray={valueType?.includes('array')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,20 +5,17 @@ import { SmallAddIcon } from '@chakra-ui/icons';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import dynamic from 'next/dynamic';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { FlowNodeInputItemType, ReferenceValueProps } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { FlowNodeInputItemType, ReferenceValueType } from '@fastgpt/global/core/workflow/type/io';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '@/pages/app/detail/components/WorkflowComponents/context';
|
||||
import { defaultInput } from '../../FieldEditModal';
|
||||
import { getInputComponentProps } from '@fastgpt/global/core/workflow/node/io/utils';
|
||||
import { VARIABLE_NODE_ID } from '@fastgpt/global/core/workflow/constants';
|
||||
import { isReference, ReferSelector, useReference } from '../Reference';
|
||||
import { ReferSelector, useReference } from '../Reference';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import ValueTypeLabel from '../../../ValueTypeLabel';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import { isWorkflowStartOutput } from '@fastgpt/global/core/workflow/template/system/workflowStart';
|
||||
|
||||
const FieldEditModal = dynamic(() => import('../../FieldEditModal'));
|
||||
|
||||
@@ -126,71 +123,40 @@ function Reference({
|
||||
const [editField, setEditField] = useState<FlowNodeInputItemType>();
|
||||
|
||||
const onSelect = useCallback(
|
||||
(e: ReferenceValueProps | ReferenceValueProps[]) => {
|
||||
const workflowStartNode = nodeList.find(
|
||||
(node) => node.flowNodeType === FlowNodeTypeEnum.workflowStart
|
||||
);
|
||||
|
||||
const newValue =
|
||||
e[0] === workflowStartNode?.id && !isWorkflowStartOutput(e[1] as string)
|
||||
? [VARIABLE_NODE_ID, e[1]]
|
||||
: e;
|
||||
|
||||
(e: ReferenceValueType) => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'replaceInput',
|
||||
key: inputChildren.key,
|
||||
value: {
|
||||
...inputChildren,
|
||||
value: newValue
|
||||
value: e
|
||||
}
|
||||
});
|
||||
},
|
||||
[inputChildren, nodeId, nodeList, onChangeNode]
|
||||
[inputChildren, nodeId, onChangeNode]
|
||||
);
|
||||
|
||||
const { referenceList, formatValue } = useReference({
|
||||
const { referenceList } = useReference({
|
||||
nodeId,
|
||||
valueType: inputChildren.valueType,
|
||||
value: inputChildren.value
|
||||
valueType: inputChildren.valueType
|
||||
});
|
||||
|
||||
// handle array and non-array type conversion
|
||||
const getValueTypeChange = useCallback(
|
||||
(data: FlowNodeInputItemType, oldType: string | undefined) => {
|
||||
const newType = data.valueType;
|
||||
if (oldType === newType) return data.value;
|
||||
|
||||
if (!oldType?.includes('array') && newType?.includes('array')) {
|
||||
return Array.isArray(data.value) && data.value.every((item) => isReference(item))
|
||||
? data.value
|
||||
: [data.value];
|
||||
}
|
||||
if (oldType?.includes('array') && !newType?.includes('array')) {
|
||||
return Array.isArray(data.value) ? data.value[0] : data.value;
|
||||
}
|
||||
return data.value;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const onUpdateField = useCallback(
|
||||
({ data }: { data: FlowNodeInputItemType }) => {
|
||||
if (!data.key) return;
|
||||
|
||||
const updatedValue = getValueTypeChange(data, inputChildren.valueType);
|
||||
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'replaceInput',
|
||||
key: inputChildren.key,
|
||||
value: {
|
||||
...data,
|
||||
value: updatedValue
|
||||
value: data
|
||||
}
|
||||
});
|
||||
},
|
||||
[inputChildren, nodeId, onChangeNode, getValueTypeChange]
|
||||
[inputChildren, nodeId, onChangeNode]
|
||||
);
|
||||
const onDel = useCallback(() => {
|
||||
onChangeNode({
|
||||
@@ -234,7 +200,7 @@ function Reference({
|
||||
<ReferSelector
|
||||
placeholder={t((inputChildren.referencePlaceholder as any) || 'select_reference_variable')}
|
||||
list={referenceList}
|
||||
value={formatValue}
|
||||
value={inputChildren.value}
|
||||
onSelect={onSelect}
|
||||
isArray={inputChildren.valueType?.includes('array')}
|
||||
/>
|
||||
|
||||
@@ -1,28 +1,37 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import type { RenderInputProps } from '../type';
|
||||
import { Flex, Box, ButtonProps } from '@chakra-ui/react';
|
||||
import { Flex, Box, ButtonProps, Grid } from '@chakra-ui/react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { computedNodeInputReference } from '@/web/core/workflow/utils';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import {
|
||||
NodeOutputKeyEnum,
|
||||
VARIABLE_NODE_ID,
|
||||
WorkflowIOValueTypeEnum
|
||||
} from '@fastgpt/global/core/workflow/constants';
|
||||
import type { ReferenceValueProps } from '@fastgpt/global/core/workflow/type/io';
|
||||
import type {
|
||||
ReferenceArrayValueType,
|
||||
ReferenceItemValueType,
|
||||
ReferenceValueType
|
||||
} from '@fastgpt/global/core/workflow/type/io';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '@/pages/app/detail/components/WorkflowComponents/context';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { AppContext } from '@/pages/app/detail/components/context';
|
||||
|
||||
const MultipleRowSelect = dynamic(
|
||||
() => import('@fastgpt/web/components/common/MySelect/MultipleRowSelect')
|
||||
const MultipleRowSelect = dynamic(() =>
|
||||
import('@fastgpt/web/components/common/MySelect/MultipleRowSelect').then(
|
||||
(v) => v.MultipleRowSelect
|
||||
)
|
||||
);
|
||||
const MultipleRowArraySelect = dynamic(() =>
|
||||
import('@fastgpt/web/components/common/MySelect/MultipleRowSelect').then(
|
||||
(v) => v.MultipleRowArraySelect
|
||||
)
|
||||
);
|
||||
const Avatar = dynamic(() => import('@fastgpt/web/components/common/Avatar'));
|
||||
|
||||
type SelectProps = {
|
||||
value?: ReferenceValueProps[];
|
||||
type CommonSelectProps = {
|
||||
placeholder?: string;
|
||||
list: {
|
||||
label: string | React.ReactNode;
|
||||
@@ -33,95 +42,27 @@ type SelectProps = {
|
||||
valueType?: WorkflowIOValueTypeEnum;
|
||||
}[];
|
||||
}[];
|
||||
onSelect: (val: ReferenceValueProps | ReferenceValueProps[]) => void;
|
||||
popDirection?: 'top' | 'bottom';
|
||||
styles?: ButtonProps;
|
||||
isArray?: boolean;
|
||||
};
|
||||
|
||||
export const isReference = (val: any) =>
|
||||
Array.isArray(val) &&
|
||||
val.length === 2 &&
|
||||
typeof val[0] === 'string' &&
|
||||
typeof val[1] === 'string';
|
||||
|
||||
const Reference = ({ item, nodeId }: RenderInputProps) => {
|
||||
const { t } = useTranslation();
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
|
||||
const onSelect = useCallback(
|
||||
(e: ReferenceValueProps | ReferenceValueProps[]) => {
|
||||
const workflowStartNode = nodeList.find(
|
||||
(node) => node.flowNodeType === FlowNodeTypeEnum.workflowStart
|
||||
);
|
||||
if (e[0] === workflowStartNode?.id && e[1] !== NodeOutputKeyEnum.userChatInput) {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'updateInput',
|
||||
key: item.key,
|
||||
value: {
|
||||
...item,
|
||||
value: [VARIABLE_NODE_ID, e[1]]
|
||||
}
|
||||
});
|
||||
} else {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'updateInput',
|
||||
key: item.key,
|
||||
value: {
|
||||
...item,
|
||||
value: e
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
[item, nodeId, nodeList, onChangeNode]
|
||||
);
|
||||
|
||||
const { referenceList, formatValue } = useReference({
|
||||
nodeId,
|
||||
valueType: item.valueType,
|
||||
value: item.value
|
||||
});
|
||||
|
||||
const popDirection = useMemo(() => {
|
||||
const node = nodeList.find((node) => node.nodeId === nodeId);
|
||||
if (!node) return 'bottom';
|
||||
return node.flowNodeType === FlowNodeTypeEnum.loop ? 'top' : 'bottom';
|
||||
}, [nodeId, nodeList]);
|
||||
|
||||
return (
|
||||
<ReferSelector
|
||||
placeholder={t((item.referencePlaceholder as any) || 'select_reference_variable')}
|
||||
list={referenceList}
|
||||
value={formatValue}
|
||||
onSelect={onSelect}
|
||||
popDirection={popDirection}
|
||||
isArray={item.valueType?.includes('array')}
|
||||
/>
|
||||
);
|
||||
type SelectProps<T extends boolean> = CommonSelectProps & {
|
||||
isArray?: T;
|
||||
value?: T extends true ? ReferenceArrayValueType : ReferenceItemValueType;
|
||||
onSelect: (val?: T extends true ? ReferenceArrayValueType : ReferenceItemValueType) => void;
|
||||
};
|
||||
|
||||
export default React.memo(Reference);
|
||||
|
||||
export const useReference = ({
|
||||
nodeId,
|
||||
valueType = WorkflowIOValueTypeEnum.any,
|
||||
value
|
||||
valueType = WorkflowIOValueTypeEnum.any
|
||||
}: {
|
||||
nodeId: string;
|
||||
valueType?: WorkflowIOValueTypeEnum;
|
||||
value?: any;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||
const edges = useContextSelector(WorkflowContext, (v) => v.edges);
|
||||
const isArray = valueType?.includes('array');
|
||||
const currentType = isArray ? valueType.replace('array', '').toLowerCase() : valueType;
|
||||
const { nodeList, edges } = useContextSelector(WorkflowContext, (v) => v);
|
||||
|
||||
// 获取可选的变量列表
|
||||
const referenceList = useMemo(() => {
|
||||
const sourceNodes = computedNodeInputReference({
|
||||
nodeId,
|
||||
@@ -133,8 +74,11 @@ export const useReference = ({
|
||||
|
||||
if (!sourceNodes) return [];
|
||||
|
||||
const isArray = valueType?.includes('array');
|
||||
const arrayItemType = isArray ? valueType.replace('array', '').toLowerCase() : valueType;
|
||||
|
||||
// 转换为 select 的数据结构
|
||||
const list: SelectProps['list'] = sourceNodes
|
||||
const list: CommonSelectProps['list'] = sourceNodes
|
||||
.map((node) => {
|
||||
return {
|
||||
label: (
|
||||
@@ -148,26 +92,16 @@ export const useReference = ({
|
||||
.filter(
|
||||
(output) =>
|
||||
valueType === WorkflowIOValueTypeEnum.any ||
|
||||
valueType === WorkflowIOValueTypeEnum.arrayAny ||
|
||||
output.valueType === WorkflowIOValueTypeEnum.any ||
|
||||
currentType === output.valueType ||
|
||||
// array
|
||||
output.valueType === valueType ||
|
||||
(valueType === WorkflowIOValueTypeEnum.arrayAny &&
|
||||
[
|
||||
WorkflowIOValueTypeEnum.arrayString,
|
||||
WorkflowIOValueTypeEnum.arrayNumber,
|
||||
WorkflowIOValueTypeEnum.arrayBoolean,
|
||||
WorkflowIOValueTypeEnum.arrayObject,
|
||||
WorkflowIOValueTypeEnum.string,
|
||||
WorkflowIOValueTypeEnum.number,
|
||||
WorkflowIOValueTypeEnum.boolean,
|
||||
WorkflowIOValueTypeEnum.object
|
||||
].includes(output.valueType as WorkflowIOValueTypeEnum))
|
||||
// Array<String> can select string
|
||||
arrayItemType === output.valueType
|
||||
)
|
||||
.filter((output) => output.id !== NodeOutputKeyEnum.addOutputParam)
|
||||
.map((output) => {
|
||||
return {
|
||||
label: t((output.label as any) || ''),
|
||||
label: t(output.label as any),
|
||||
value: output.id,
|
||||
valueType: output.valueType
|
||||
};
|
||||
@@ -177,148 +111,216 @@ export const useReference = ({
|
||||
.filter((item) => item.children.length > 0);
|
||||
|
||||
return list;
|
||||
}, [appDetail.chatConfig, currentType, edges, isArray, nodeId, nodeList, t, valueType]);
|
||||
|
||||
const formatValue = useMemo(() => {
|
||||
// convert origin reference [variableId, outputId] to new reference [[variableId, outputId], ...]
|
||||
if (isReference(value)) {
|
||||
return [value] as ReferenceValueProps[];
|
||||
} else if (Array.isArray(value) && value.every((item) => isReference(item))) {
|
||||
return value as ReferenceValueProps[];
|
||||
}
|
||||
return undefined;
|
||||
}, [value]);
|
||||
}, [appDetail.chatConfig, edges, nodeId, nodeList, t, valueType]);
|
||||
|
||||
return {
|
||||
referenceList,
|
||||
formatValue
|
||||
referenceList
|
||||
};
|
||||
};
|
||||
|
||||
const ReferSelectorComponent = ({
|
||||
const Reference = ({ item, nodeId }: RenderInputProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { onChangeNode, nodeList } = useContextSelector(WorkflowContext, (v) => v);
|
||||
const isArray = item.valueType?.includes('array') ?? false;
|
||||
|
||||
const onSelect = useCallback(
|
||||
(e: ReferenceValueType) => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'updateInput',
|
||||
key: item.key,
|
||||
value: {
|
||||
...item,
|
||||
value: e
|
||||
}
|
||||
});
|
||||
},
|
||||
[item, nodeId, onChangeNode]
|
||||
);
|
||||
|
||||
const { referenceList } = useReference({
|
||||
nodeId,
|
||||
valueType: item.valueType
|
||||
});
|
||||
|
||||
const popDirection = useMemo(() => {
|
||||
const node = nodeList.find((node) => node.nodeId === nodeId);
|
||||
if (!node) return 'bottom';
|
||||
return node.flowNodeType === FlowNodeTypeEnum.loop ? 'top' : 'bottom';
|
||||
}, [nodeId, nodeList]);
|
||||
|
||||
return (
|
||||
<ReferSelector
|
||||
placeholder={t(item.referencePlaceholder as any) || t('common:select_reference_variable')}
|
||||
list={referenceList}
|
||||
value={item.value}
|
||||
onSelect={onSelect}
|
||||
popDirection={popDirection}
|
||||
isArray={isArray}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Reference);
|
||||
|
||||
const SingleReferenceSelector = ({
|
||||
placeholder,
|
||||
value,
|
||||
list = [],
|
||||
onSelect,
|
||||
popDirection,
|
||||
isArray
|
||||
}: SelectProps) => {
|
||||
const { t } = useTranslation();
|
||||
popDirection
|
||||
}: SelectProps<false>) => {
|
||||
const getSelectValue = useCallback(
|
||||
(value: ReferenceValueType) => {
|
||||
if (!value) return [];
|
||||
|
||||
const selectValue = useMemo(() => {
|
||||
if (!value || value.every((item) => !item || item.every((subItem) => !subItem))) {
|
||||
return;
|
||||
}
|
||||
return value.map((valueItem) => {
|
||||
const firstColumn = list.find((item) => item.value === valueItem[0]);
|
||||
const firstColumn = list.find((item) => item.value === value[0]);
|
||||
if (!firstColumn) {
|
||||
return;
|
||||
return [];
|
||||
}
|
||||
const secondColumn = firstColumn.children.find((item) => item.value === valueItem[1]);
|
||||
const secondColumn = firstColumn.children.find((item) => item.value === value[1]);
|
||||
if (!secondColumn) {
|
||||
return;
|
||||
return [];
|
||||
}
|
||||
return [firstColumn, secondColumn];
|
||||
});
|
||||
}, [list, value]);
|
||||
return [firstColumn.label, secondColumn.label];
|
||||
},
|
||||
[list]
|
||||
);
|
||||
|
||||
const ItemSelector = useMemo(() => {
|
||||
const selectorVal = value as ReferenceItemValueType;
|
||||
const [nodeName, outputName] = getSelectValue(selectorVal);
|
||||
const isValidSelect = nodeName && outputName;
|
||||
|
||||
const Render = useMemo(() => {
|
||||
return (
|
||||
<MultipleRowSelect
|
||||
label={
|
||||
selectValue && selectValue.length > 0 ? (
|
||||
<Flex
|
||||
gap={2}
|
||||
flexWrap={isArray ? 'wrap' : undefined}
|
||||
alignItems={'center'}
|
||||
fontSize={'14px'}
|
||||
>
|
||||
{isArray ? (
|
||||
// [[variableId, outputId], ...]
|
||||
selectValue.map((item, index) => {
|
||||
const isInvalidItem = item === undefined;
|
||||
return (
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
key={index}
|
||||
bg={isInvalidItem ? 'red.50' : 'primary.50'}
|
||||
color={isInvalidItem ? 'red.600' : 'myGray.900'}
|
||||
py={1}
|
||||
px={1.5}
|
||||
rounded={'sm'}
|
||||
>
|
||||
isValidSelect ? (
|
||||
<Flex gap={2} alignItems={'center'} fontSize={'sm'}>
|
||||
<Flex py={1} pl={1}>
|
||||
{nodeName}
|
||||
<MyIcon name={'common/rightArrowLight'} mx={1} w={'12px'} color={'myGray.500'} />
|
||||
{outputName}
|
||||
</Flex>
|
||||
</Flex>
|
||||
) : (
|
||||
<Box fontSize={'sm'} color={'myGray.400'}>
|
||||
{placeholder}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
value={selectorVal}
|
||||
list={list}
|
||||
onSelect={onSelect as any}
|
||||
popDirection={popDirection}
|
||||
/>
|
||||
);
|
||||
}, [getSelectValue, list, onSelect, placeholder, popDirection, value]);
|
||||
|
||||
return ItemSelector;
|
||||
};
|
||||
const MultipleReferenceSelector = ({
|
||||
placeholder,
|
||||
value,
|
||||
list = [],
|
||||
onSelect,
|
||||
popDirection
|
||||
}: SelectProps<true>) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const getSelectValue = useCallback(
|
||||
(value: ReferenceValueType) => {
|
||||
if (!value) return [];
|
||||
|
||||
const firstColumn = list.find((item) => item.value === value[0]);
|
||||
if (!firstColumn) {
|
||||
return [];
|
||||
}
|
||||
const secondColumn = firstColumn.children.find((item) => item.value === value[1]);
|
||||
if (!secondColumn) {
|
||||
return [];
|
||||
}
|
||||
return [firstColumn.label, secondColumn.label];
|
||||
},
|
||||
[list]
|
||||
);
|
||||
|
||||
const ArraySelector = useMemo(() => {
|
||||
const selectorVal = value as ReferenceItemValueType[];
|
||||
|
||||
return (
|
||||
<MultipleRowArraySelect
|
||||
label={
|
||||
selectorVal && selectorVal.length > 0 ? (
|
||||
<Grid py={3} gridTemplateColumns={'1fr 1fr'} gap={2} fontSize={'sm'}>
|
||||
{selectorVal.map((item, index) => {
|
||||
const [nodeName, outputName] = getSelectValue(item);
|
||||
const isInvalidItem = !nodeName || !outputName;
|
||||
|
||||
return (
|
||||
<Flex
|
||||
alignItems={'center'}
|
||||
key={index}
|
||||
bg={isInvalidItem ? 'red.50' : 'primary.50'}
|
||||
color={isInvalidItem ? 'red.600' : 'myGray.900'}
|
||||
py={1}
|
||||
px={1.5}
|
||||
rounded={'sm'}
|
||||
>
|
||||
<Flex alignItems={'center'} flex={1}>
|
||||
{isInvalidItem ? (
|
||||
t('common:invalid_variable')
|
||||
) : (
|
||||
<>
|
||||
{item?.[0].label}
|
||||
{nodeName}
|
||||
<MyIcon
|
||||
name={'common/rightArrowLight'}
|
||||
mx={1}
|
||||
w={'12px'}
|
||||
color={'myGray.500'}
|
||||
/>
|
||||
{item?.[1].label}
|
||||
{outputName}
|
||||
</>
|
||||
)}
|
||||
<MyIcon
|
||||
name={'common/closeLight'}
|
||||
w={'16px'}
|
||||
ml={1}
|
||||
cursor={'pointer'}
|
||||
color={'myGray.500'}
|
||||
_hover={{
|
||||
color: 'primary.600'
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (isInvalidItem) {
|
||||
const filteredValue = value?.filter((_, i) => i !== index);
|
||||
onSelect(filteredValue as any);
|
||||
return;
|
||||
}
|
||||
const filteredValue = value?.filter(
|
||||
(val) => val[0] !== item?.[0].value || val[1] !== item?.[1].value
|
||||
);
|
||||
filteredValue && onSelect(filteredValue);
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
})
|
||||
) : // [variableId, outputId]
|
||||
selectValue[0] ? (
|
||||
<Flex py={1} pl={1}>
|
||||
{selectValue[0][0].label}
|
||||
<MyIcon name={'common/rightArrowLight'} mx={1} w={'12px'} color={'myGray.500'} />
|
||||
{selectValue[0][1].label}
|
||||
</Flex>
|
||||
) : (
|
||||
<Box pl={2} py={1} fontSize={'14px'}>
|
||||
{placeholder}
|
||||
</Box>
|
||||
)}
|
||||
</Flex>
|
||||
<MyIcon
|
||||
name={'common/closeLight'}
|
||||
w={'16px'}
|
||||
ml={1}
|
||||
cursor={'pointer'}
|
||||
color={'myGray.500'}
|
||||
_hover={{
|
||||
color: 'primary.600'
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onSelect(value?.filter((_, i) => i !== index));
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
) : (
|
||||
<Box pl={2} py={1} fontSize={'14px'}>
|
||||
<Box fontSize={'sm'} color={'myGray.400'}>
|
||||
{placeholder}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
value={value as any[]}
|
||||
value={selectorVal as any}
|
||||
list={list}
|
||||
onSelect={(e) => {
|
||||
onSelect(e as ReferenceValueProps);
|
||||
}}
|
||||
onSelect={onSelect as any}
|
||||
popDirection={popDirection}
|
||||
isArray={isArray}
|
||||
/>
|
||||
);
|
||||
}, [isArray, list, onSelect, placeholder, popDirection, selectValue, t, value]);
|
||||
}, [getSelectValue, list, onSelect, placeholder, popDirection, t, value]);
|
||||
|
||||
return Render;
|
||||
return ArraySelector;
|
||||
};
|
||||
export const ReferSelector = <T extends boolean>(props: SelectProps<T>) => {
|
||||
return props.isArray ? (
|
||||
<MultipleReferenceSelector {...(props as SelectProps<true>)} />
|
||||
) : (
|
||||
<SingleReferenceSelector {...(props as SelectProps<false>)} />
|
||||
);
|
||||
};
|
||||
|
||||
ReferSelectorComponent.displayName = 'ReferSelector';
|
||||
|
||||
export const ReferSelector = React.memo(ReferSelectorComponent);
|
||||
|
||||
@@ -28,7 +28,7 @@ import { TFunction } from 'next-i18next';
|
||||
import {
|
||||
FlowNodeInputItemType,
|
||||
FlowNodeOutputItemType,
|
||||
ReferenceValueProps
|
||||
ReferenceItemValueType
|
||||
} from '@fastgpt/global/core/workflow/type/io';
|
||||
import { IfElseListItemType } from '@fastgpt/global/core/workflow/template/system/ifElse/type';
|
||||
import { VariableConditionEnum } from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
|
||||
@@ -227,7 +227,7 @@ export const getRefData = ({
|
||||
nodeList,
|
||||
chatConfig
|
||||
}: {
|
||||
variable?: ReferenceValueProps;
|
||||
variable?: ReferenceItemValueType;
|
||||
nodeList: FlowNodeItemType[];
|
||||
chatConfig: AppChatConfigType;
|
||||
}) => {
|
||||
@@ -352,7 +352,7 @@ export const checkWorkflowNodeAndConnection = ({
|
||||
}
|
||||
|
||||
// New format
|
||||
return input.value.some((inputItem: ReferenceValueProps) => {
|
||||
return input.value.some((inputItem: ReferenceItemValueType) => {
|
||||
if (!Array.isArray(inputItem) || inputItem.length !== 2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user