diff --git a/web/app/components/base/tag-input/index.tsx b/web/app/components/base/tag-input/index.tsx index 5e29089154..478074f5b7 100644 --- a/web/app/components/base/tag-input/index.tsx +++ b/web/app/components/base/tag-input/index.tsx @@ -88,10 +88,13 @@ const TagInput: FC = ({ !disableAdd && ( setFocused(true)} onBlur={handleBlur} value={value} diff --git a/web/app/components/workflow/hooks/use-workflow.ts b/web/app/components/workflow/hooks/use-workflow.ts index a168f81994..b877d26a3f 100644 --- a/web/app/components/workflow/hooks/use-workflow.ts +++ b/web/app/components/workflow/hooks/use-workflow.ts @@ -233,13 +233,15 @@ export const useWorkflow = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [store]) - const isVarUsedInNodes = useCallback((nodeId: string, varSelector: ValueSelector) => { + const isVarUsedInNodes = useCallback((varSelector: ValueSelector) => { + const nodeId = varSelector[0] const afterNodes = getAfterNodesInSameBranch(nodeId) const effectNodes = findUsedVarNodes(varSelector, afterNodes) return effectNodes.length > 0 }, [getAfterNodesInSameBranch]) - const removeUsedVarInNodes = useCallback((nodeId: string, varSelector: ValueSelector) => { + const removeUsedVarInNodes = useCallback((varSelector: ValueSelector) => { + const nodeId = varSelector[0] const { getNodes, setNodes } = store.getState() const afterNodes = getAfterNodesInSameBranch(nodeId) const effectNodes = findUsedVarNodes(varSelector, afterNodes) @@ -257,7 +259,7 @@ export const useWorkflow = () => { const isNodeVarsUsedInNodes = useCallback((node: Node, isChatMode: boolean) => { const outputVars = getNodeOutputVars(node, isChatMode) const isUsed = outputVars.some((varSelector) => { - return isVarUsedInNodes(node.id, varSelector) + return isVarUsedInNodes(varSelector) }) return isUsed }, [isVarUsedInNodes]) diff --git a/web/app/components/workflow/nodes/_base/components/remove-effect-var-confirm.tsx b/web/app/components/workflow/nodes/_base/components/remove-effect-var-confirm.tsx new file mode 100644 index 0000000000..2e18a5f4c7 --- /dev/null +++ b/web/app/components/workflow/nodes/_base/components/remove-effect-var-confirm.tsx @@ -0,0 +1,32 @@ +'use client' +import type { FC } from 'react' +import React from 'react' +import { useTranslation } from 'react-i18next' +import Confirm from '@/app/components/base/confirm' + +type Props = { + isShow: boolean + onConfirm: () => void + onCancel: () => void +} +const i18nPrefix = 'workflow.common.effectVarConfirm' + +const RemoveVarConfirm: FC = ({ + isShow, + onConfirm, + onCancel, +}) => { + const { t } = useTranslation() + + return ( + + ) +} +export default React.memo(RemoveVarConfirm) diff --git a/web/app/components/workflow/nodes/start/panel.tsx b/web/app/components/workflow/nodes/start/panel.tsx index 4d890b52d1..b244d269bf 100644 --- a/web/app/components/workflow/nodes/start/panel.tsx +++ b/web/app/components/workflow/nodes/start/panel.tsx @@ -1,6 +1,7 @@ import type { FC } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' +import RemoveEffectVarConfirm from '../_base/components/remove-effect-var-confirm' import VarList from './components/var-list' import useConfig from './use-config' import type { StartNodeType } from './types' @@ -27,6 +28,9 @@ const Panel: FC> = ({ handleAddVariable, hideAddVarModal, handleVarListChange, + isShowRemoveVarConfirm, + hideRemoveVarConfirm, + onRemoveVarConfirm, } = useConfig(id, data) const handleAddVarConfirm = (payload: InputVar) => { @@ -96,6 +100,12 @@ const Panel: FC> = ({ onConfirm={handleAddVarConfirm} /> )} + + ) } diff --git a/web/app/components/workflow/nodes/start/use-config.ts b/web/app/components/workflow/nodes/start/use-config.ts index d77096767d..8fe352554c 100644 --- a/web/app/components/workflow/nodes/start/use-config.ts +++ b/web/app/components/workflow/nodes/start/use-config.ts @@ -1,8 +1,9 @@ -import { useCallback } from 'react' +import { useCallback, useState } from 'react' import produce from 'immer' import { useBoolean } from 'ahooks' import type { StartNodeType } from './types' -import { ChangeType, type InputVar, type MoreInfo } from '@/app/components/workflow/types' +import { ChangeType } from '@/app/components/workflow/types' +import type { InputVar, MoreInfo, ValueSelector } from '@/app/components/workflow/types' import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' import { useIsChatMode, @@ -12,7 +13,7 @@ import { const useConfig = (id: string, payload: StartNodeType) => { const { nodesReadOnly: readOnly } = useNodesReadOnly() - const { handleOutVarRenameChange, removeUsedVarInNodes } = useWorkflow() + const { handleOutVarRenameChange, isVarUsedInNodes, removeUsedVarInNodes } = useWorkflow() const isChatMode = useIsChatMode() const { inputs, setInputs } = useNodeCrud(id, payload) @@ -22,7 +23,20 @@ const useConfig = (id: string, payload: StartNodeType) => { setFalse: hideAddVarModal, }] = useBoolean(false) + const [isShowRemoveVarConfirm, { + setTrue: showRemoveVarConfirm, + setFalse: hideRemoveVarConfirm, + }] = useBoolean(false) + const [removedVar, setRemovedVar] = useState([]) const handleVarListChange = useCallback((newList: InputVar[], moreInfo?: { index: number; payload: MoreInfo }) => { + if (moreInfo?.payload?.type === ChangeType.remove) { + if (isVarUsedInNodes([id, moreInfo?.payload?.payload?.beforeKey || ''])) { + showRemoveVarConfirm() + setRemovedVar([id, moreInfo?.payload?.payload?.beforeKey || '']) + return + } + } + const newInputs = produce(inputs, (draft: any) => { draft.variables = newList }) @@ -31,9 +45,12 @@ const useConfig = (id: string, payload: StartNodeType) => { const changedVar = newList[moreInfo.index] handleOutVarRenameChange(id, [id, inputs.variables[moreInfo.index].variable], [id, changedVar.variable]) } - if (moreInfo?.payload?.type === ChangeType.remove) - removeUsedVarInNodes(id, [id, moreInfo?.payload?.payload?.beforeKey || '']) - }, [handleOutVarRenameChange, id, inputs, setInputs]) + }, [handleOutVarRenameChange, id, inputs, isVarUsedInNodes, setInputs, showRemoveVarConfirm]) + + const removeVarInNode = useCallback(() => { + removeUsedVarInNodes(removedVar) + hideRemoveVarConfirm() + }, [hideRemoveVarConfirm, removeUsedVarInNodes, removedVar]) const handleAddVariable = useCallback((payload: InputVar) => { const newInputs = produce(inputs, (draft: StartNodeType) => { @@ -50,6 +67,9 @@ const useConfig = (id: string, payload: StartNodeType) => { hideAddVarModal, handleVarListChange, handleAddVariable, + isShowRemoveVarConfirm, + hideRemoveVarConfirm, + onRemoveVarConfirm: removeVarInNode, } } diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index f08ffda235..6bb7f5030c 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -31,6 +31,10 @@ const translation = { workflowProcess: 'Workflow Process', notRunning: 'Not running yet', previewPlaceholder: 'Enter content in the box below to start debugging the Chatbot', + effectVarConfirm: { + title: 'Remove Variable', + content: 'The variable is used in other nodes. Do you still want to remove it?', + }, }, errorMsg: { fieldRequired: '{{field}} is required', diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index 0a65357af3..895d9e89c0 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -31,6 +31,10 @@ const translation = { workflowProcess: '工作流', notRunning: '尚未运行', previewPlaceholder: '在下面的框中输入内容开始调试聊天机器人', + effectVarConfirm: { + title: '移除变量', + content: '该变量在其他节点中使用。您是否仍要删除它?', + }, }, errorMsg: { fieldRequired: '{{field}} 不能为空',