feat: iframe code block;perf: workflow selector type (#3076)
* feat: iframe code block * perf: workflow selector type
This commit is contained in:
@@ -5,8 +5,8 @@ import { StoreNodeItemType } from '../type/node';
|
||||
import { StoreEdgeItemType } from '../type/edge';
|
||||
import { RuntimeEdgeItemType, RuntimeNodeItemType } from './type';
|
||||
import { VARIABLE_NODE_ID } from '../constants';
|
||||
import { isReferenceValue } from '../utils';
|
||||
import { FlowNodeOutputItemType, ReferenceValueProps } from '../type/io';
|
||||
import { isReferenceValue, isReferenceValueArray } from '../utils';
|
||||
import { FlowNodeOutputItemType, ReferenceValueType } from '../type/io';
|
||||
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
|
||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
|
||||
|
||||
@@ -225,15 +225,22 @@ export const checkNodeRunStatus = ({
|
||||
return 'wait';
|
||||
};
|
||||
|
||||
/*
|
||||
Get the value of the reference variable/node output
|
||||
1. [string,string]
|
||||
2. [string,string][]
|
||||
*/
|
||||
export const getReferenceVariableValue = ({
|
||||
value,
|
||||
nodes,
|
||||
variables
|
||||
}: {
|
||||
value: ReferenceValueProps;
|
||||
value: ReferenceValueType;
|
||||
nodes: RuntimeNodeItemType[];
|
||||
variables: Record<string, any>;
|
||||
}) => {
|
||||
if (!value) return undefined;
|
||||
|
||||
const nodeIds = nodes.map((node) => node.nodeId);
|
||||
|
||||
// handle single reference value
|
||||
@@ -254,32 +261,13 @@ export const getReferenceVariableValue = ({
|
||||
}
|
||||
|
||||
// handle reference array
|
||||
if (
|
||||
Array.isArray(value) &&
|
||||
value.every(
|
||||
(val) => val?.length === 2 && typeof val[0] === 'string' && typeof val[1] === 'string'
|
||||
)
|
||||
) {
|
||||
const result = value.map((val) => {
|
||||
if (!isReferenceValue(val, nodeIds)) {
|
||||
return [val];
|
||||
}
|
||||
|
||||
const sourceNodeId = val?.[0];
|
||||
const outputId = val?.[1];
|
||||
|
||||
if (sourceNodeId === VARIABLE_NODE_ID && outputId) {
|
||||
const variableValue = variables[outputId];
|
||||
return Array.isArray(variableValue) ? variableValue : [variableValue];
|
||||
}
|
||||
|
||||
const node = nodes.find((node) => node.nodeId === sourceNodeId);
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const outputValue = node.outputs.find((output) => output.id === outputId)?.value;
|
||||
return Array.isArray(outputValue) ? outputValue : [outputValue];
|
||||
if (isReferenceValueArray(value, nodeIds)) {
|
||||
const result = value.map<any>((val) => {
|
||||
return getReferenceVariableValue({
|
||||
value: val,
|
||||
nodes,
|
||||
variables
|
||||
});
|
||||
});
|
||||
|
||||
return result.flat();
|
||||
@@ -288,6 +276,70 @@ export const getReferenceVariableValue = ({
|
||||
return value;
|
||||
};
|
||||
|
||||
// replace {{$xx.xx$}} variables for text
|
||||
export function replaceEditorVariable({
|
||||
text,
|
||||
nodes,
|
||||
variables,
|
||||
runningNode
|
||||
}: {
|
||||
text: any;
|
||||
nodes: RuntimeNodeItemType[];
|
||||
variables: Record<string, any>; // global variables
|
||||
runningNode: RuntimeNodeItemType;
|
||||
}) {
|
||||
if (typeof text !== 'string') return text;
|
||||
|
||||
const globalVariables = Object.keys(variables).map((key) => {
|
||||
return {
|
||||
nodeId: VARIABLE_NODE_ID,
|
||||
id: key,
|
||||
value: variables[key]
|
||||
};
|
||||
});
|
||||
|
||||
// Upstream node outputs
|
||||
const nodeVariables = nodes
|
||||
.map((node) => {
|
||||
return node.outputs.map((output) => {
|
||||
return {
|
||||
nodeId: node.nodeId,
|
||||
id: output.id,
|
||||
value: output.value
|
||||
};
|
||||
});
|
||||
})
|
||||
.flat();
|
||||
|
||||
// Get runningNode inputs(Will be replaced with reference)
|
||||
const customInputs = runningNode.inputs.flatMap((item) => {
|
||||
return [
|
||||
{
|
||||
id: item.key,
|
||||
value: getReferenceVariableValue({
|
||||
value: item.value,
|
||||
nodes,
|
||||
variables
|
||||
}),
|
||||
nodeId: runningNode.nodeId
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
const allVariables = [...globalVariables, ...nodeVariables, ...customInputs];
|
||||
|
||||
// Replace {{$xxx.xxx$}} to value
|
||||
for (const key in allVariables) {
|
||||
const variable = allVariables[key];
|
||||
const val = variable.value;
|
||||
const formatVal = typeof val === 'object' ? JSON.stringify(val) : String(val);
|
||||
|
||||
const regex = new RegExp(`\\{\\{\\$(${variable.nodeId}\\.${variable.id})\\$\\}\\}`, 'g');
|
||||
text = text.replace(regex, formatVal);
|
||||
}
|
||||
return text || '';
|
||||
}
|
||||
|
||||
export const textAdaptGptResponse = ({
|
||||
text,
|
||||
model = '',
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ReferenceValueProps } from 'core/workflow/type/io';
|
||||
import { ReferenceItemValueType } from '../../../type/io';
|
||||
import { VariableConditionEnum } from './constant';
|
||||
|
||||
export type IfElseConditionType = 'AND' | 'OR';
|
||||
export type ConditionListItemType = {
|
||||
variable?: ReferenceValueProps;
|
||||
variable?: ReferenceItemValueType;
|
||||
condition?: VariableConditionEnum;
|
||||
value?: string;
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { FlowNodeInputTypeEnum } from '../../../node/constant';
|
||||
import { ReferenceValueProps } from '../../..//type/io';
|
||||
import { ReferenceItemValueType, ReferenceValueType } from '../../..//type/io';
|
||||
import { WorkflowIOValueTypeEnum } from '../../../constants';
|
||||
|
||||
export type TUpdateListItem = {
|
||||
variable?: ReferenceValueProps;
|
||||
value: ReferenceValueProps;
|
||||
variable?: ReferenceItemValueType;
|
||||
value: ReferenceValueType; // input: ['',value], reference: [nodeId,outputId]
|
||||
valueType?: WorkflowIOValueTypeEnum;
|
||||
renderType: FlowNodeInputTypeEnum.input | FlowNodeInputTypeEnum.reference;
|
||||
};
|
||||
|
||||
@@ -43,6 +43,3 @@ export const WorkflowStart: FlowNodeTemplateType = {
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export const isWorkflowStartOutput = (key?: string) =>
|
||||
!!WorkflowStart.outputs.find((output) => output.key === key);
|
||||
|
||||
4
packages/global/core/workflow/type/io.d.ts
vendored
4
packages/global/core/workflow/type/io.d.ts
vendored
@@ -80,4 +80,6 @@ export type FlowNodeOutputItemType = {
|
||||
customFieldConfig?: CustomFieldConfigType;
|
||||
};
|
||||
|
||||
export type ReferenceValueProps = [string, string | undefined];
|
||||
export type ReferenceItemValueType = undefined | [string, string];
|
||||
export type ReferenceArrayValueType = undefined | [string, string][];
|
||||
export type ReferenceValueType = ReferenceItemValueType | ReferenceArrayValueType;
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
VARIABLE_NODE_ID,
|
||||
NodeOutputKeyEnum
|
||||
} from './constants';
|
||||
import { FlowNodeInputItemType, FlowNodeOutputItemType, ReferenceValueProps } from './type/io.d';
|
||||
import { FlowNodeInputItemType, FlowNodeOutputItemType } from './type/io.d';
|
||||
import { StoreNodeItemType } from './type/node';
|
||||
import type {
|
||||
VariableItemType,
|
||||
@@ -30,7 +30,6 @@ import {
|
||||
} from '../app/constants';
|
||||
import { IfElseResultEnum } from './template/system/ifElse/constant';
|
||||
import { RuntimeNodeItemType } from './runtime/type';
|
||||
import { getReferenceVariableValue } from './runtime/utils';
|
||||
import {
|
||||
Input_Template_File_Link,
|
||||
Input_Template_History,
|
||||
@@ -301,9 +300,19 @@ export const formatEditorVariablePickerIcon = (
|
||||
}));
|
||||
};
|
||||
|
||||
export const isReferenceValue = (value: any, nodeIds: string[]): boolean => {
|
||||
const validIdList = [VARIABLE_NODE_ID, ...nodeIds];
|
||||
return Array.isArray(value) && value.length === 2 && validIdList.includes(value[0]);
|
||||
export const isReferenceValue = (value: any, nodeIds: string[]): value is [string, string] => {
|
||||
if (!Array.isArray(value) || value.length !== 2 || typeof value[0] !== 'string') return false;
|
||||
|
||||
const validIdSet = new Set([VARIABLE_NODE_ID, ...nodeIds]);
|
||||
return validIdSet.has(value[0]);
|
||||
};
|
||||
export const isReferenceValueArray = (
|
||||
value: any,
|
||||
nodeIds: string[]
|
||||
): value is [string, string][] => {
|
||||
if (!Array.isArray(value) || value.length === 0) return false;
|
||||
|
||||
return value.every((item) => isReferenceValue(item, nodeIds));
|
||||
};
|
||||
|
||||
export const getElseIFLabel = (i: number) => {
|
||||
@@ -345,79 +354,6 @@ export const updatePluginInputByVariables = (
|
||||
);
|
||||
};
|
||||
|
||||
// replace {{$xx.xx$}} variables for text
|
||||
export function replaceEditorVariable({
|
||||
text,
|
||||
nodes,
|
||||
variables,
|
||||
runningNode
|
||||
}: {
|
||||
text: any;
|
||||
nodes: RuntimeNodeItemType[];
|
||||
variables: Record<string, any>; // global variables
|
||||
runningNode: RuntimeNodeItemType;
|
||||
}) {
|
||||
if (typeof text !== 'string') return text;
|
||||
|
||||
const globalVariables = Object.keys(variables).map((key) => {
|
||||
return {
|
||||
nodeId: VARIABLE_NODE_ID,
|
||||
id: key,
|
||||
value: variables[key]
|
||||
};
|
||||
});
|
||||
|
||||
// Upstream node outputs
|
||||
const nodeVariables = nodes
|
||||
.map((node) => {
|
||||
return node.outputs.map((output) => {
|
||||
return {
|
||||
nodeId: node.nodeId,
|
||||
id: output.id,
|
||||
value: output.value
|
||||
};
|
||||
});
|
||||
})
|
||||
.flat();
|
||||
|
||||
// Get runningNode inputs(Will be replaced with reference)
|
||||
const customInputs = runningNode.inputs.flatMap((item) => {
|
||||
if (Array.isArray(item.value)) {
|
||||
return [
|
||||
{
|
||||
id: item.key,
|
||||
value: getReferenceVariableValue({
|
||||
value: item.value as ReferenceValueProps,
|
||||
nodes,
|
||||
variables
|
||||
}),
|
||||
nodeId: runningNode.nodeId
|
||||
}
|
||||
];
|
||||
}
|
||||
return [
|
||||
{
|
||||
id: item.key,
|
||||
value: item.value,
|
||||
nodeId: runningNode.nodeId
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
const allVariables = [...globalVariables, ...nodeVariables, ...customInputs];
|
||||
|
||||
// Replace {{$xxx.xxx$}} to value
|
||||
for (const key in allVariables) {
|
||||
const variable = allVariables[key];
|
||||
const val = variable.value;
|
||||
const formatVal = typeof val === 'object' ? JSON.stringify(val) : String(val);
|
||||
|
||||
const regex = new RegExp(`\\{\\{\\$(${variable.nodeId}\\.${variable.id})\\$\\}\\}`, 'g');
|
||||
text = text.replace(regex, formatVal);
|
||||
}
|
||||
return text || '';
|
||||
}
|
||||
|
||||
/* Get plugin runtime input user query */
|
||||
export const getPluginRunUserQuery = ({
|
||||
pluginInputs,
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
} from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { getNanoid, replaceVariable } from '@fastgpt/global/common/string/tools';
|
||||
import { getSystemTime } from '@fastgpt/global/common/time/timezone';
|
||||
import { replaceEditorVariable } from '@fastgpt/global/core/workflow/utils';
|
||||
|
||||
import { dispatchWorkflowStart } from './init/workflowStart';
|
||||
import { dispatchChatCompletion } from './chat/oneapi';
|
||||
@@ -42,7 +41,8 @@ import { removeSystemVariable, valueTypeFormat } from './utils';
|
||||
import {
|
||||
filterWorkflowEdges,
|
||||
checkNodeRunStatus,
|
||||
textAdaptGptResponse
|
||||
textAdaptGptResponse,
|
||||
replaceEditorVariable
|
||||
} from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type';
|
||||
import { dispatchRunTools } from './agent/runTool/index';
|
||||
|
||||
@@ -14,10 +14,12 @@ import { SERVICE_LOCAL_HOST } from '../../../../common/system/tools';
|
||||
import { addLog } from '../../../../common/system/log';
|
||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import {
|
||||
textAdaptGptResponse,
|
||||
replaceEditorVariable
|
||||
} from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { getSystemPluginCb } from '../../../../../plugins/register';
|
||||
import { ContentTypes } from '@fastgpt/global/core/workflow/constants';
|
||||
import { replaceEditorVariable } from '@fastgpt/global/core/workflow/utils';
|
||||
import { uploadFileFromBase64Img } from '../../../../common/file/gridfs/controller';
|
||||
import { ReadFileBaseUrl } from '@fastgpt/global/common/file/constants';
|
||||
import { createFileToken } from '../../../../support/permission/controller';
|
||||
|
||||
@@ -4,11 +4,14 @@ import {
|
||||
SseResponseEventEnum
|
||||
} from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { getReferenceVariableValue } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import {
|
||||
getReferenceVariableValue,
|
||||
replaceEditorVariable
|
||||
} from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { TUpdateListItem } from '@fastgpt/global/core/workflow/template/system/variableUpdate/type';
|
||||
import { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { removeSystemVariable, valueTypeFormat } from '../utils';
|
||||
import { replaceEditorVariable } from '@fastgpt/global/core/workflow/utils';
|
||||
import { isReferenceValue } from '@fastgpt/global/core/workflow/utils';
|
||||
|
||||
type Props = ModuleDispatchProps<{
|
||||
[NodeInputKeyEnum.updateList]: TUpdateListItem[];
|
||||
@@ -19,15 +22,20 @@ export const dispatchUpdateVariable = async (props: Props): Promise<Response> =>
|
||||
const { params, variables, runtimeNodes, workflowStreamResponse, node } = props;
|
||||
|
||||
const { updateList } = params;
|
||||
const result = updateList.map((item) => {
|
||||
const varNodeId = item.variable?.[0];
|
||||
const varKey = item.variable?.[1];
|
||||
const nodeIds = runtimeNodes.map((node) => node.nodeId);
|
||||
|
||||
if (!varNodeId || !varKey) {
|
||||
const result = updateList.map((item) => {
|
||||
const variable = item.variable;
|
||||
|
||||
if (!isReferenceValue(variable, nodeIds)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const varNodeId = variable[0];
|
||||
const varKey = variable[1];
|
||||
|
||||
const value = (() => {
|
||||
// If first item is empty, it means it is a input value
|
||||
if (!item.value?.[0]) {
|
||||
const formatValue = valueTypeFormat(item.value?.[1], item.valueType);
|
||||
|
||||
@@ -40,16 +48,15 @@ export const dispatchUpdateVariable = async (props: Props): Promise<Response> =>
|
||||
})
|
||||
: formatValue;
|
||||
} else {
|
||||
const value = getReferenceVariableValue({
|
||||
return getReferenceVariableValue({
|
||||
value: item.value,
|
||||
variables,
|
||||
nodes: runtimeNodes
|
||||
});
|
||||
|
||||
return value;
|
||||
}
|
||||
})();
|
||||
|
||||
// Update node output
|
||||
// Global variable
|
||||
if (varNodeId === VARIABLE_NODE_ID) {
|
||||
variables[varKey] = value;
|
||||
@@ -74,6 +81,7 @@ export const dispatchUpdateVariable = async (props: Props): Promise<Response> =>
|
||||
});
|
||||
|
||||
return {
|
||||
[DispatchNodeResponseKeyEnum.newVariables]: variables,
|
||||
[DispatchNodeResponseKeyEnum.nodeResponse]: {
|
||||
updateVarResult: result
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<svg viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="36" height="36" fill="url(#paint0_linear_10832_16007)"/>
|
||||
<path d="M13.9673 10.3122C13.1868 10.8479 13.1935 11.9491 13.863 12.6185C14.3986 13.1541 15.2613 13.1384 15.9008 12.7325C16.5477 12.3219 17.268 12.0318 18.0272 11.8808C19.2374 11.6401 20.4919 11.7636 21.6319 12.2358C22.7719 12.708 23.7463 13.5077 24.4318 14.5337C25.1174 15.5597 25.4833 16.7659 25.4833 17.9999C25.4833 19.2338 25.1174 20.4401 24.4318 21.4661C23.7463 22.4921 22.7719 23.2917 21.6319 23.7639C20.4919 24.2361 19.2374 24.3597 18.0272 24.119C17.268 23.968 16.5477 23.6779 15.9008 23.2673C15.2613 22.8614 14.3986 22.8457 13.863 23.3812C13.1935 24.0507 13.1868 25.1518 13.9673 25.6876C15.0044 26.3995 16.1799 26.8976 17.4252 27.1453C19.234 27.5051 21.1088 27.3204 22.8127 26.6147C24.5165 25.9089 25.9728 24.7138 26.9974 23.1803C28.022 21.6469 28.5689 19.8441 28.5689 17.9999C28.5689 16.1557 28.022 14.3528 26.9974 12.8194C25.9728 11.286 24.5165 10.0908 22.8127 9.38509C21.1088 8.67933 19.234 8.49468 17.4252 8.85447C16.1799 9.10217 15.0044 9.60029 13.9673 10.3122Z" fill="white"/>
|
||||
<path d="M19.2443 22.5497C17.3579 22.5497 15.7397 21.4017 15.0501 19.7663H9.19726C8.22171 19.7663 7.43087 18.9754 7.43087 17.9999C7.43087 17.0243 8.22171 16.2335 9.19726 16.2335H15.0501C15.7397 14.598 17.3579 13.45 19.2443 13.45C21.7571 13.45 23.7942 15.4871 23.7942 17.9999C23.7942 20.5127 21.7571 22.5497 19.2443 22.5497Z" fill="white"/>
|
||||
<rect width="36" height="36" fill="url(#paint0_linear_11_1507)"/>
|
||||
<path d="M9.22505 16.905C8.38096 17.102 7.87904 17.9714 8.13047 18.8009L9.96306 24.8467C10.3439 26.1032 12.0411 26.2986 12.6976 25.1616L13.5842 23.6259C15.0589 24.1044 16.6267 24.2674 18.1843 24.0937C20.4842 23.8372 22.6439 22.8596 24.3544 21.301C26.0649 19.7424 27.2384 17.6826 27.7071 15.4164C28.074 13.6422 27.9941 11.8127 27.4853 10.0898C27.2506 9.29527 26.3514 8.96094 25.5963 9.30173L23.6663 10.1728C22.9113 10.5136 22.597 11.404 22.7351 12.2208C22.8554 12.9325 22.8437 13.6647 22.6957 14.38C22.4458 15.5886 21.8199 16.6872 20.9077 17.5184C19.9954 18.3497 18.8436 18.871 17.617 19.0078C17.1616 19.0586 16.7045 19.0555 16.2549 19.0002L17.0171 17.6799C17.6736 16.5428 16.6558 15.1707 15.3772 15.4692L9.22505 16.905Z" fill="white"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_10832_16007" x1="18" y1="0" x2="5.5" y2="33" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#68C0FF"/>
|
||||
<stop offset="1" stop-color="#52A2FF"/>
|
||||
<linearGradient id="paint0_linear_11_1507" x1="18" y1="0" x2="5.5" y2="33" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF8BFD"/>
|
||||
<stop offset="1" stop-color="#7394FF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -1,11 +1,10 @@
|
||||
<svg viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="36" height="36" fill="url(#paint0_linear_10829_15860)"/>
|
||||
<path d="M22.0325 10.3122C22.813 10.8479 22.8062 11.9491 22.1368 12.6185C21.6012 13.1541 20.7384 13.1384 20.099 12.7325C19.4521 12.3219 18.7317 12.0318 17.9726 11.8808C16.7623 11.6401 15.5079 11.7636 14.3679 12.2358C13.2279 12.708 12.2535 13.5077 11.5679 14.5337C10.8824 15.5597 10.5165 16.7659 10.5165 17.9999C10.5165 19.2338 10.8824 20.4401 11.5679 21.4661C12.2535 22.4921 13.2279 23.2917 14.3679 23.7639C15.5079 24.2361 16.7624 24.3597 17.9726 24.119C18.7317 23.968 19.4521 23.6779 20.099 23.2673C20.7384 22.8614 21.6012 22.8457 22.1368 23.3812C22.8062 24.0507 22.813 25.1518 22.0325 25.6876C20.9954 26.3995 19.8199 26.8976 18.5746 27.1453C16.7658 27.5051 14.8909 27.3204 13.1871 26.6147C11.4832 25.9089 10.0269 24.7138 9.00232 23.1803C7.97772 21.6469 7.43085 19.8441 7.43085 17.9999C7.43085 16.1557 7.97772 14.3528 9.00232 12.8194C10.0269 11.286 11.4832 10.0908 13.1871 9.38509C14.8909 8.67933 16.7658 8.49468 18.5746 8.85447C19.8199 9.10217 20.9954 9.60029 22.0325 10.3122Z" fill="white"/>
|
||||
<path d="M16.7554 22.5497C18.6418 22.5497 20.2601 21.4017 20.9497 19.7663H26.8025C27.778 19.7663 28.5689 18.9754 28.5689 17.9999C28.5689 17.0243 27.778 16.2335 26.8025 16.2335H20.9497C20.2601 14.598 18.6418 13.45 16.7554 13.45C14.2426 13.45 12.2056 15.4871 12.2056 17.9999C12.2056 20.5127 14.2426 22.5497 16.7554 22.5497Z" fill="white"/>
|
||||
<svg viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="36" height="36" fill="url(#paint0_linear_11_1494)"/>
|
||||
<path d="M26.7748 18.1758C27.6189 17.9788 28.1208 17.1095 27.8693 16.2799L26.0368 10.2341C25.6559 8.97762 23.9587 8.7822 23.3022 9.91926L22.4156 11.4549C20.9409 10.9764 19.3731 10.8134 17.8155 10.9871C15.5157 11.2437 13.3559 12.2212 11.6454 13.7798C9.93493 15.3384 8.76137 17.3983 8.29272 19.6644C7.92581 21.4386 8.00571 23.2681 8.51454 24.991C8.74918 25.7855 9.64845 26.1199 10.4035 25.7791L12.3335 24.908C13.0886 24.5672 13.4028 23.6768 13.2647 22.86C13.1444 22.1483 13.1562 21.4161 13.3041 20.7008C13.554 19.4922 14.1799 18.3936 15.0922 17.5624C16.0044 16.7311 17.1562 16.2098 18.3828 16.073C18.8382 16.0222 19.2953 16.0254 19.7449 16.0807L18.9827 17.4009C18.3262 18.538 19.344 19.9101 20.6226 19.6117L26.7748 18.1758Z" fill="white"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_10829_15860" x1="18" y1="0" x2="5.5" y2="33" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#68C0FF"/>
|
||||
<stop offset="1" stop-color="#52A2FF"/>
|
||||
<linearGradient id="paint0_linear_11_1494" x1="18" y1="0" x2="5.5" y2="33" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FF8BFD"/>
|
||||
<stop offset="1" stop-color="#7394FF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -1,12 +1,11 @@
|
||||
import React, { useRef, useCallback, useState } from 'react';
|
||||
import { Button, useDisclosure, Box, Flex, useOutsideClick, Checkbox } from '@chakra-ui/react';
|
||||
import { MultipleSelectProps } from './type';
|
||||
import { ListItemType, MultipleArraySelectProps, MultipleSelectProps } from './type';
|
||||
import EmptyTip from '../EmptyTip';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyIcon from '../../common/Icon';
|
||||
import { ChevronDownIcon } from '@chakra-ui/icons';
|
||||
|
||||
const MultipleRowSelect = ({
|
||||
export const MultipleRowSelect = ({
|
||||
placeholder,
|
||||
label,
|
||||
value = [],
|
||||
@@ -15,14 +14,12 @@ const MultipleRowSelect = ({
|
||||
maxH = 300,
|
||||
onSelect,
|
||||
popDirection = 'bottom',
|
||||
styles,
|
||||
isArray = false
|
||||
styles
|
||||
}: MultipleSelectProps) => {
|
||||
const { t } = useTranslation();
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
const [navigationPath, setNavigationPath] = useState<string[]>([]);
|
||||
const [cloneValue, setCloneValue] = useState(value);
|
||||
|
||||
useOutsideClick({
|
||||
ref: ref,
|
||||
@@ -31,33 +28,183 @@ const MultipleRowSelect = ({
|
||||
|
||||
const RenderList = useCallback(
|
||||
({ index, list }: { index: number; list: MultipleSelectProps['list'] }) => {
|
||||
const currentNav = navigationPath[index];
|
||||
const selectedIndex = list.findIndex((item) => item.value === currentNav);
|
||||
const selectedValue = cloneValue[index];
|
||||
const selectedIndex = list.findIndex((item) => item.value === selectedValue);
|
||||
const children = list[selectedIndex]?.children || [];
|
||||
const hasChildren = list.some((item) => item.children && item.children?.length > 0);
|
||||
|
||||
const handleSelect = (item: any) => {
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
className="nowheel"
|
||||
flex={'1 0 auto'}
|
||||
// width={0}
|
||||
px={2}
|
||||
borderLeft={index !== 0 ? 'base' : 'none'}
|
||||
maxH={`${maxH}px`}
|
||||
overflowY={'auto'}
|
||||
whiteSpace={'nowrap'}
|
||||
>
|
||||
{list.map((item) => (
|
||||
<Flex
|
||||
key={item.value}
|
||||
py={2}
|
||||
cursor={'pointer'}
|
||||
px={2}
|
||||
borderRadius={'md'}
|
||||
_hover={{
|
||||
bg: 'primary.50',
|
||||
color: 'primary.600'
|
||||
}}
|
||||
onClick={() => {
|
||||
const newValue = [...cloneValue];
|
||||
|
||||
if (item.value === selectedValue) {
|
||||
newValue[index] = undefined;
|
||||
setCloneValue(newValue);
|
||||
onSelect(newValue);
|
||||
} else {
|
||||
newValue[index] = item.value;
|
||||
setCloneValue(newValue);
|
||||
if (!hasChildren) {
|
||||
onSelect(newValue);
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
}}
|
||||
{...(item.value === selectedValue
|
||||
? {
|
||||
color: 'primary.600'
|
||||
}
|
||||
: {})}
|
||||
>
|
||||
{item.label}
|
||||
</Flex>
|
||||
))}
|
||||
{list.length === 0 && (
|
||||
<EmptyTip
|
||||
text={emptyTip ?? t('common:common.MultipleRowSelect.No data')}
|
||||
pt={1}
|
||||
pb={3}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
{children.length > 0 && <RenderList list={children} index={index + 1} />}
|
||||
</>
|
||||
);
|
||||
},
|
||||
[cloneValue]
|
||||
);
|
||||
|
||||
const onOpenSelect = useCallback(() => {
|
||||
setCloneValue(value);
|
||||
onOpen();
|
||||
}, [value, onOpen]);
|
||||
|
||||
return (
|
||||
<Box ref={ref} position={'relative'}>
|
||||
<Button
|
||||
justifyContent={'space-between'}
|
||||
width={'100%'}
|
||||
variant={'whitePrimaryOutline'}
|
||||
size={'lg'}
|
||||
fontSize={'sm'}
|
||||
px={3}
|
||||
outline={'none'}
|
||||
rightIcon={<MyIcon name={'core/chat/chevronDown'} w="1rem" color={'myGray.500'} />}
|
||||
_active={{
|
||||
transform: 'none'
|
||||
}}
|
||||
{...(isOpen
|
||||
? {
|
||||
borderColor: 'primary.600',
|
||||
color: 'primary.700',
|
||||
boxShadow: '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)'
|
||||
}
|
||||
: {
|
||||
borderColor: 'myGray.200',
|
||||
boxShadow: 'none'
|
||||
})}
|
||||
{...styles}
|
||||
onClick={() => (isOpen ? onClose() : onOpenSelect())}
|
||||
>
|
||||
<Box>{label ?? placeholder}</Box>
|
||||
</Button>
|
||||
{isOpen && (
|
||||
<Box
|
||||
position={'absolute'}
|
||||
{...(popDirection === 'top'
|
||||
? {
|
||||
transform: 'translateY(-105%)',
|
||||
top: '0'
|
||||
}
|
||||
: {
|
||||
transform: 'translateY(105%)',
|
||||
bottom: '0'
|
||||
})}
|
||||
py={2}
|
||||
bg={'white'}
|
||||
border={'1px solid #fff'}
|
||||
boxShadow={'5'}
|
||||
borderRadius={'md'}
|
||||
zIndex={1}
|
||||
minW={'100%'}
|
||||
w={'max-content'}
|
||||
>
|
||||
<Flex>
|
||||
<RenderList list={list} index={0} />
|
||||
</Flex>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export const MultipleRowArraySelect = ({
|
||||
placeholder,
|
||||
label,
|
||||
value = [],
|
||||
list,
|
||||
emptyTip,
|
||||
maxH = 300,
|
||||
onSelect,
|
||||
popDirection = 'bottom',
|
||||
styles
|
||||
}: MultipleArraySelectProps) => {
|
||||
const { t } = useTranslation();
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
const [navigationPath, setNavigationPath] = useState<string[]>([]);
|
||||
|
||||
// Close when clicking outside
|
||||
useOutsideClick({
|
||||
ref: ref,
|
||||
handler: onClose
|
||||
});
|
||||
|
||||
const RenderList = useCallback(
|
||||
({ index, list }: { index: number; list: MultipleSelectProps['list'] }) => {
|
||||
const currentNavValue = navigationPath[index];
|
||||
const selectedIndex = list.findIndex((item) => item.value === currentNavValue);
|
||||
const children = list[selectedIndex]?.children || [];
|
||||
const hasChildren = list.some((item) => item.children && item.children?.length > 0);
|
||||
|
||||
const handleSelect = (item: ListItemType) => {
|
||||
// Has children, set parent value
|
||||
if (hasChildren) {
|
||||
// Update parent menu path
|
||||
const newPath = [...navigationPath];
|
||||
newPath[index] = item.value;
|
||||
// Clear sub paths
|
||||
newPath.splice(index + 1);
|
||||
setNavigationPath(newPath);
|
||||
} else {
|
||||
if (!isArray) {
|
||||
onSelect([navigationPath[0], item.value]);
|
||||
onClose();
|
||||
} else {
|
||||
const parentValue = navigationPath[0];
|
||||
const newValues = [...value];
|
||||
const newValue = [parentValue, item.value];
|
||||
const parentValue = navigationPath[0];
|
||||
const newValues = [...value];
|
||||
const newValue = [parentValue, item.value];
|
||||
|
||||
if (newValues.some((v) => v[0] === parentValue && v[1] === item.value)) {
|
||||
onSelect(newValues.filter((v) => !(v[0] === parentValue && v[1] === item.value)));
|
||||
} else {
|
||||
onSelect([...newValues, newValue]);
|
||||
}
|
||||
if (newValues.some((v) => v[0] === parentValue && v[1] === item.value)) {
|
||||
onSelect(newValues.filter((v) => !(v[0] === parentValue && v[1] === item.value)));
|
||||
} else {
|
||||
onSelect([...newValues, newValue]);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -74,8 +221,8 @@ const MultipleRowSelect = ({
|
||||
whiteSpace={'nowrap'}
|
||||
>
|
||||
{list.map((item) => {
|
||||
const isSelected = item.value === currentNav;
|
||||
const showCheckbox = isArray && index !== 0;
|
||||
const isSelected = item.value === currentNavValue;
|
||||
const showCheckbox = !hasChildren;
|
||||
const isChecked =
|
||||
showCheckbox &&
|
||||
value.some((v) => v[1] === item.value && v[0] === navigationPath[0]);
|
||||
@@ -101,7 +248,7 @@ const MultipleRowSelect = ({
|
||||
mr={1}
|
||||
/>
|
||||
)}
|
||||
{item.label}
|
||||
<Box>{item.label}</Box>
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
@@ -117,33 +264,26 @@ const MultipleRowSelect = ({
|
||||
</>
|
||||
);
|
||||
},
|
||||
[navigationPath, value, isArray, onSelect]
|
||||
[navigationPath, value, onSelect]
|
||||
);
|
||||
|
||||
const onOpenSelect = useCallback(() => {
|
||||
setNavigationPath(isArray ? [] : [value[0]?.[0], value[0]?.[1]]);
|
||||
setNavigationPath([]);
|
||||
onOpen();
|
||||
}, [value, isArray, onOpen]);
|
||||
}, [value, onOpen]);
|
||||
|
||||
return (
|
||||
<Box ref={ref} position={'relative'}>
|
||||
<Flex
|
||||
justifyContent={'space-between'}
|
||||
alignItems={'center'}
|
||||
overflow={'auto'}
|
||||
<Button
|
||||
width={'100%'}
|
||||
variant={'whitePrimaryOutline'}
|
||||
size={'lg'}
|
||||
fontSize={'sm'}
|
||||
px={3}
|
||||
py={1}
|
||||
minH={10}
|
||||
maxH={24}
|
||||
outline={'none'}
|
||||
rightIcon={<MyIcon name={'core/chat/chevronDown'} w={4} color={'myGray.500'} />}
|
||||
border={'1px solid'}
|
||||
borderRadius={'md'}
|
||||
bg={'white'}
|
||||
rightIcon={<MyIcon name={'core/chat/chevronDown'} w="1rem" color={'myGray.500'} />}
|
||||
iconSpacing={2}
|
||||
h={'auto'}
|
||||
_active={{
|
||||
transform: 'none'
|
||||
}}
|
||||
@@ -164,20 +304,21 @@ const MultipleRowSelect = ({
|
||||
onClick={() => (isOpen ? onClose() : onOpenSelect())}
|
||||
className="nowheel"
|
||||
>
|
||||
<Box>{label ?? placeholder}</Box>
|
||||
<Flex alignItems={'center'} ml={1}>
|
||||
<ChevronDownIcon />
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Box w={'100%'} textAlign={'left'}>
|
||||
{label ?? placeholder}
|
||||
</Box>
|
||||
</Button>
|
||||
{isOpen && (
|
||||
<Box
|
||||
position={'absolute'}
|
||||
{...(popDirection === 'top'
|
||||
? {
|
||||
bottom: '45px'
|
||||
transform: 'translateY(-105%)',
|
||||
top: '0'
|
||||
}
|
||||
: {
|
||||
top: '45px'
|
||||
transform: 'translateY(105%)',
|
||||
bottom: '0'
|
||||
})}
|
||||
py={2}
|
||||
bg={'white'}
|
||||
|
||||
@@ -4,9 +4,9 @@ type ListItemType = {
|
||||
value: any;
|
||||
children?: ListItemType[];
|
||||
};
|
||||
export type MultipleSelectProps<T = any> = {
|
||||
export type MultipleSelectProps = {
|
||||
label?: string | React.ReactNode;
|
||||
value: any[];
|
||||
value?: any[];
|
||||
placeholder?: string;
|
||||
list: ListItemType[];
|
||||
emptyTip?: string;
|
||||
@@ -14,5 +14,8 @@ export type MultipleSelectProps<T = any> = {
|
||||
onSelect: (val: any[]) => void;
|
||||
styles?: ButtonProps;
|
||||
popDirection?: 'top' | 'bottom';
|
||||
isArray?: boolean;
|
||||
};
|
||||
export type MultipleArraySelectProps = Omit<MultipleSelectProps, 'value'> & {
|
||||
value?: any[][];
|
||||
onSelect: (val: any[][]) => void;
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const Button = defineStyleConfig({
|
||||
px: '2',
|
||||
py: '0',
|
||||
h: '24px',
|
||||
minH: '24px',
|
||||
fontWeight: 'medium',
|
||||
borderRadius: 'sm'
|
||||
},
|
||||
@@ -54,6 +55,7 @@ const Button = defineStyleConfig({
|
||||
px: '0',
|
||||
py: '0',
|
||||
h: '24px',
|
||||
minH: '24px',
|
||||
w: '24px',
|
||||
fontWeight: 'medium',
|
||||
borderRadius: 'sm'
|
||||
@@ -64,6 +66,7 @@ const Button = defineStyleConfig({
|
||||
py: 0,
|
||||
fontWeight: 'medium',
|
||||
h: '30px',
|
||||
minH: '30px',
|
||||
borderRadius: 'sm'
|
||||
},
|
||||
smSquare: {
|
||||
@@ -72,6 +75,7 @@ const Button = defineStyleConfig({
|
||||
py: 0,
|
||||
fontWeight: 'medium',
|
||||
h: '30px',
|
||||
minH: '30px',
|
||||
w: '30px',
|
||||
borderRadius: 'sm'
|
||||
},
|
||||
@@ -80,6 +84,7 @@ const Button = defineStyleConfig({
|
||||
px: '4',
|
||||
py: 0,
|
||||
h: '34px',
|
||||
minH: '34px',
|
||||
fontWeight: 'medium',
|
||||
borderRadius: 'sm'
|
||||
},
|
||||
@@ -88,6 +93,7 @@ const Button = defineStyleConfig({
|
||||
px: '0',
|
||||
py: 0,
|
||||
h: '34px',
|
||||
minH: '34px',
|
||||
w: '34px',
|
||||
fontWeight: 'medium',
|
||||
borderRadius: 'sm'
|
||||
@@ -97,6 +103,7 @@ const Button = defineStyleConfig({
|
||||
px: '4',
|
||||
py: 0,
|
||||
h: '40px',
|
||||
minH: '40px',
|
||||
fontWeight: 'medium',
|
||||
borderRadius: 'md'
|
||||
},
|
||||
@@ -105,6 +112,7 @@ const Button = defineStyleConfig({
|
||||
px: '0',
|
||||
py: 0,
|
||||
h: '40px',
|
||||
minH: '40px',
|
||||
w: '40px',
|
||||
fontWeight: 'medium',
|
||||
borderRadius: 'md'
|
||||
@@ -346,14 +354,14 @@ const NumberInput = numInputMultiStyle({
|
||||
sm: defineStyle({
|
||||
field: {
|
||||
h: '32px',
|
||||
borderRadius: 'md',
|
||||
borderRadius: 'sm',
|
||||
fontsize: 'sm'
|
||||
}
|
||||
}),
|
||||
lg: defineStyle({
|
||||
field: {
|
||||
h: '40px',
|
||||
borderRadius: 'md',
|
||||
borderRadius: 'sm',
|
||||
fontsize: 'sm'
|
||||
}
|
||||
})
|
||||
|
||||
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