diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx index 0a9b5473e3..b5104377e1 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx @@ -53,11 +53,11 @@ const VarList: FC = ({ }), [list]) const { run: validateVarInput } = useDebounceFn((list: Variable[], newKey: string) => { - const { isValid, errorKey, errorMessageKey } = checkKeys([newKey], true) - if (!isValid) { + const result = checkKeys([newKey], true) + if (!result.isValid) { setToastHandle(Toast.notify({ type: 'error', - message: t(`varKeyError.${errorMessageKey}` as any, { ns: 'appDebug', key: errorKey }), + message: t(`varKeyError.${result.errorMessageKey}`, { ns: 'appDebug', key: result.errorKey }), })) return } diff --git a/web/app/components/workflow/nodes/assigner/components/operation-selector.tsx b/web/app/components/workflow/nodes/assigner/components/operation-selector.tsx index 95f04f8895..456731bfa1 100644 --- a/web/app/components/workflow/nodes/assigner/components/operation-selector.tsx +++ b/web/app/components/workflow/nodes/assigner/components/operation-selector.tsx @@ -1,5 +1,6 @@ import type { FC } from 'react' import type { WriteMode } from '../types' +import type { Item } from '../utils' import type { VarType } from '@/app/components/workflow/types' import { RiArrowDownSLine, @@ -14,12 +15,7 @@ import { PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' import { cn } from '@/utils/classnames' -import { getOperationItems } from '../utils' - -type Item = { - value: string | number - name: string -} +import { getOperationItems, isOperationItem } from '../utils' type OperationSelectorProps = { value: string | number @@ -34,8 +30,6 @@ type OperationSelectorProps = { writeModeTypesNum?: WriteMode[] } -const i18nPrefix = 'nodes.assigner' - const OperationSelector: FC = ({ value, onSelect, @@ -72,7 +66,7 @@ const OperationSelector: FC = ({ className={`system-sm-regular overflow-hidden truncate text-ellipsis ${selectedItem ? 'text-components-input-text-filled' : 'text-components-input-text-disabled'}`} > - {selectedItem?.name ? t(`${i18nPrefix}.operations.${selectedItem?.name}` as any, { ns: 'workflow' }) as string : t(`${i18nPrefix}.operations.title` as any, { ns: 'workflow' }) as string} + {selectedItem && isOperationItem(selectedItem) ? t(`nodes.assigner.operations.${selectedItem.name}`, { ns: 'workflow' }) : t('nodes.assigner.operations.title', { ns: 'workflow' })} @@ -83,10 +77,10 @@ const OperationSelector: FC = ({
-
{t(`${i18nPrefix}.operations.title` as any, { ns: 'workflow' }) as string}
+
{t('nodes.assigner.operations.title', { ns: 'workflow' })}
{items.map(item => ( - item.value === 'divider' + !isOperationItem(item) ? ( ) @@ -100,7 +94,7 @@ const OperationSelector: FC = ({ }} >
- {t(`${i18nPrefix}.operations.${item.name}` as any, { ns: 'workflow' }) as string} + {t(`nodes.assigner.operations.${item.name}`, { ns: 'workflow' })}
{item.value === value && (
diff --git a/web/app/components/workflow/nodes/assigner/node.tsx b/web/app/components/workflow/nodes/assigner/node.tsx index 1919cbff99..f429c4a957 100644 --- a/web/app/components/workflow/nodes/assigner/node.tsx +++ b/web/app/components/workflow/nodes/assigner/node.tsx @@ -1,5 +1,6 @@ import type { FC } from 'react' import type { AssignerNodeType } from './types' +import type { OperationName } from './utils' import type { Node, NodeProps } from '@/app/components/workflow/types' import * as React from 'react' import { useTranslation } from 'react-i18next' @@ -59,7 +60,8 @@ const NodeComponent: FC> = ({ ) } // Legacy version - const { assigned_variable_selector: variable, write_mode: writeMode } = data as any + type LegacyAssignerNodeType = { assigned_variable_selector: string[], write_mode: OperationName } + const { assigned_variable_selector: variable, write_mode: writeMode } = data as unknown as LegacyAssignerNodeType if (!variable || variable.length === 0) return null @@ -73,7 +75,7 @@ const NodeComponent: FC> = ({ nodeType={node?.data.type} nodeTitle={node?.data.title} rightSlot={ - writeMode && + writeMode && } />
diff --git a/web/app/components/workflow/nodes/assigner/utils.ts b/web/app/components/workflow/nodes/assigner/utils.ts index 50a02fae5b..8e27626834 100644 --- a/web/app/components/workflow/nodes/assigner/utils.ts +++ b/web/app/components/workflow/nodes/assigner/utils.ts @@ -1,4 +1,5 @@ import type { AssignerNodeType } from './types' +import type { I18nKeysByPrefix } from '@/types/i18n' import { AssignerNodeInputType, WriteMode } from './types' export const checkNodeValid = (_payload: AssignerNodeType) => { @@ -11,9 +12,14 @@ export const formatOperationName = (type: string) => { return type.charAt(0).toUpperCase() + type.slice(1) } -type Item = { - value: string | number - name: string +export type OperationName = I18nKeysByPrefix<'workflow', 'nodes.assigner.operations.'> + +export type Item + = | { value: 'divider', name: 'divider' } + | { value: string | number, name: OperationName } + +export function isOperationItem(item: Item): item is { value: string | number, name: OperationName } { + return item.value !== 'divider' } export const getOperationItems = ( diff --git a/web/app/components/workflow/nodes/constants.ts b/web/app/components/workflow/nodes/constants.ts index b09b27343a..0fabe9d799 100644 --- a/web/app/components/workflow/nodes/constants.ts +++ b/web/app/components/workflow/nodes/constants.ts @@ -1,15 +1,21 @@ +import type { I18nKeysByPrefix } from '@/types/i18n' import { TransferMethod } from '@/types/app' export const CUSTOM_NODE_TYPE = 'custom' -export const FILE_TYPE_OPTIONS = [ +type OptionItem = { + value: string + i18nKey: I18nKeysByPrefix<'workflow', 'nodes.ifElse.optionName.'> +} + +export const FILE_TYPE_OPTIONS: OptionItem[] = [ { value: 'image', i18nKey: 'image' }, { value: 'document', i18nKey: 'doc' }, { value: 'audio', i18nKey: 'audio' }, { value: 'video', i18nKey: 'video' }, ] -export const TRANSFER_METHOD = [ +export const TRANSFER_METHOD: OptionItem[] = [ { value: TransferMethod.local_file, i18nKey: 'localUpload' }, { value: TransferMethod.remote_url, i18nKey: 'url' }, ] diff --git a/web/app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx b/web/app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx index 97df6e2f14..e803b31f7c 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx @@ -17,8 +17,6 @@ import { isEmptyRelatedOperator, } from '../utils' -const i18nPrefix = 'nodes.ifElse' - type ConditionValueProps = { condition: Condition } @@ -34,7 +32,7 @@ const ConditionValue = ({ const variableSelector = variable_selector as ValueSelector - const operatorName = isComparisonOperatorNeedTranslate(operator) ? t(`nodes.ifElse.comparisonOperator.${operator}` as any, { ns: 'workflow' }) as string : operator + const operatorName = isComparisonOperatorNeedTranslate(operator) ? t(`nodes.ifElse.comparisonOperator.${operator}`, { ns: 'workflow' }) : operator const formatValue = useCallback((c: Condition) => { const notHasValue = comparisonOperatorNotRequireValue(c.comparison_operator) if (notHasValue) @@ -59,7 +57,7 @@ const ConditionValue = ({ if (isSelect) { const name = [...FILE_TYPE_OPTIONS, ...TRANSFER_METHOD].filter(item => item.value === (Array.isArray(c.value) ? c.value[0] : c.value))[0] return name - ? (t(`nodes.ifElse.optionName.${name.i18nKey}` as any, { ns: 'workflow' }) as string).replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { + ? t(`nodes.ifElse.optionName.${name.i18nKey}`, { ns: 'workflow' }).replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { const arr: string[] = b.split('.') if (isSystemVar(arr)) return `{{${b}}}` @@ -88,14 +86,20 @@ const ConditionValue = ({
{ - sub_variable_condition?.conditions.map((c: Condition, index) => ( -
-
{c.key}
-
{isComparisonOperatorNeedTranslate(c.comparison_operator) ? t(`nodes.ifElse.comparisonOperator.${c.comparison_operator}` as any, { ns: 'workflow' }) as string : c.comparison_operator}
- {c.comparison_operator && !isEmptyRelatedOperator(c.comparison_operator) &&
{isSelect(c) ? selectName(c) : formatValue(c)}
} - {index !== sub_variable_condition.conditions.length - 1 && (
{t(`${i18nPrefix}.${sub_variable_condition.logical_operator}` as any, { ns: 'workflow' }) as string}
)} -
- )) + sub_variable_condition?.conditions.map((c: Condition, index) => { + const comparisonOperator = c.comparison_operator + const comparisonOperatorName = comparisonOperator && isComparisonOperatorNeedTranslate(comparisonOperator) + ? t(`nodes.ifElse.comparisonOperator.${comparisonOperator}`, { ns: 'workflow' }) + : comparisonOperator + return ( +
+
{c.key}
+
{comparisonOperatorName}
+ {c.comparison_operator && !isEmptyRelatedOperator(c.comparison_operator) &&
{isSelect(c) ? selectName(c) : formatValue(c)}
} + {index !== sub_variable_condition.conditions.length - 1 && (
{t(`nodes.ifElse.${sub_variable_condition.logical_operator}`, { ns: 'workflow' })}
)} +
+ ) + }) }
diff --git a/web/app/components/workflow/nodes/if-else/utils.ts b/web/app/components/workflow/nodes/if-else/utils.ts index 167f26c913..72b0f9d42e 100644 --- a/web/app/components/workflow/nodes/if-else/utils.ts +++ b/web/app/components/workflow/nodes/if-else/utils.ts @@ -1,4 +1,5 @@ import type { Branch } from '@/app/components/workflow/types' +import type { I18nKeysByPrefix } from '@/types/i18n' import { VarType } from '@/app/components/workflow/types' import { ComparisonOperator } from './types' @@ -13,12 +14,18 @@ const notTranslateKey = [ ComparisonOperator.largerThanOrEqual, ComparisonOperator.lessThan, ComparisonOperator.lessThanOrEqual, -] +] as const -export const isComparisonOperatorNeedTranslate = (operator?: ComparisonOperator) => { +type NotTranslateOperator = typeof notTranslateKey[number] +export type TranslatableComparisonOperator = Exclude +export type IfElseOptionName = I18nKeysByPrefix<'workflow', 'nodes.ifElse.optionName.'> + +export function isComparisonOperatorNeedTranslate(operator: ComparisonOperator): operator is TranslatableComparisonOperator +export function isComparisonOperatorNeedTranslate(operator?: ComparisonOperator): operator is TranslatableComparisonOperator +export function isComparisonOperatorNeedTranslate(operator?: ComparisonOperator): operator is TranslatableComparisonOperator { if (!operator) return false - return !notTranslateKey.includes(operator) + return !(notTranslateKey as readonly ComparisonOperator[]).includes(operator) } export const getOperators = (type?: VarType, file?: { key: string }) => { diff --git a/web/i18n/en-US/workflow.json b/web/i18n/en-US/workflow.json index 6edbdb99f6..107dad5b28 100644 --- a/web/i18n/en-US/workflow.json +++ b/web/i18n/en-US/workflow.json @@ -513,6 +513,8 @@ "nodes.ifElse.comparisonOperator.in": "in", "nodes.ifElse.comparisonOperator.is": "is", "nodes.ifElse.comparisonOperator.is not": "is not", + "nodes.ifElse.comparisonOperator.is not null": "is not null", + "nodes.ifElse.comparisonOperator.is null": "is null", "nodes.ifElse.comparisonOperator.not contains": "not contains", "nodes.ifElse.comparisonOperator.not empty": "is not empty", "nodes.ifElse.comparisonOperator.not exists": "not exists",