mirror of
https://github.com/langgenius/dify.git
synced 2026-04-24 09:06:42 +08:00
type safe
This commit is contained in:
parent
f9db9a6d45
commit
89a1d15da3
@ -53,11 +53,11 @@ const VarList: FC<Props> = ({
|
|||||||
}), [list])
|
}), [list])
|
||||||
|
|
||||||
const { run: validateVarInput } = useDebounceFn((list: Variable[], newKey: string) => {
|
const { run: validateVarInput } = useDebounceFn((list: Variable[], newKey: string) => {
|
||||||
const { isValid, errorKey, errorMessageKey } = checkKeys([newKey], true)
|
const result = checkKeys([newKey], true)
|
||||||
if (!isValid) {
|
if (!result.isValid) {
|
||||||
setToastHandle(Toast.notify({
|
setToastHandle(Toast.notify({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: t(`varKeyError.${errorMessageKey}` as any, { ns: 'appDebug', key: errorKey }),
|
message: t(`varKeyError.${result.errorMessageKey}`, { ns: 'appDebug', key: result.errorKey }),
|
||||||
}))
|
}))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import type { WriteMode } from '../types'
|
import type { WriteMode } from '../types'
|
||||||
|
import type { Item } from '../utils'
|
||||||
import type { VarType } from '@/app/components/workflow/types'
|
import type { VarType } from '@/app/components/workflow/types'
|
||||||
import {
|
import {
|
||||||
RiArrowDownSLine,
|
RiArrowDownSLine,
|
||||||
@ -14,12 +15,7 @@ import {
|
|||||||
PortalToFollowElemTrigger,
|
PortalToFollowElemTrigger,
|
||||||
} from '@/app/components/base/portal-to-follow-elem'
|
} from '@/app/components/base/portal-to-follow-elem'
|
||||||
import { cn } from '@/utils/classnames'
|
import { cn } from '@/utils/classnames'
|
||||||
import { getOperationItems } from '../utils'
|
import { getOperationItems, isOperationItem } from '../utils'
|
||||||
|
|
||||||
type Item = {
|
|
||||||
value: string | number
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
|
|
||||||
type OperationSelectorProps = {
|
type OperationSelectorProps = {
|
||||||
value: string | number
|
value: string | number
|
||||||
@ -34,8 +30,6 @@ type OperationSelectorProps = {
|
|||||||
writeModeTypesNum?: WriteMode[]
|
writeModeTypesNum?: WriteMode[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const i18nPrefix = 'nodes.assigner'
|
|
||||||
|
|
||||||
const OperationSelector: FC<OperationSelectorProps> = ({
|
const OperationSelector: FC<OperationSelectorProps> = ({
|
||||||
value,
|
value,
|
||||||
onSelect,
|
onSelect,
|
||||||
@ -72,7 +66,7 @@ const OperationSelector: FC<OperationSelectorProps> = ({
|
|||||||
className={`system-sm-regular overflow-hidden truncate text-ellipsis
|
className={`system-sm-regular overflow-hidden truncate text-ellipsis
|
||||||
${selectedItem ? 'text-components-input-text-filled' : 'text-components-input-text-disabled'}`}
|
${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' })}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<RiArrowDownSLine className={`h-4 w-4 text-text-quaternary ${disabled && 'text-components-input-text-placeholder'} ${open && 'text-text-secondary'}`} />
|
<RiArrowDownSLine className={`h-4 w-4 text-text-quaternary ${disabled && 'text-components-input-text-placeholder'} ${open && 'text-text-secondary'}`} />
|
||||||
@ -83,10 +77,10 @@ const OperationSelector: FC<OperationSelectorProps> = ({
|
|||||||
<div className="flex w-[140px] flex-col items-start rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg">
|
<div className="flex w-[140px] flex-col items-start rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg">
|
||||||
<div className="flex flex-col items-start self-stretch p-1">
|
<div className="flex flex-col items-start self-stretch p-1">
|
||||||
<div className="flex items-start self-stretch px-3 pb-0.5 pt-1">
|
<div className="flex items-start self-stretch px-3 pb-0.5 pt-1">
|
||||||
<div className="system-xs-medium-uppercase flex grow text-text-tertiary">{t(`${i18nPrefix}.operations.title` as any, { ns: 'workflow' }) as string}</div>
|
<div className="system-xs-medium-uppercase flex grow text-text-tertiary">{t('nodes.assigner.operations.title', { ns: 'workflow' })}</div>
|
||||||
</div>
|
</div>
|
||||||
{items.map(item => (
|
{items.map(item => (
|
||||||
item.value === 'divider'
|
!isOperationItem(item)
|
||||||
? (
|
? (
|
||||||
<Divider key="divider" className="my-1" />
|
<Divider key="divider" className="my-1" />
|
||||||
)
|
)
|
||||||
@ -100,7 +94,7 @@ const OperationSelector: FC<OperationSelectorProps> = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="flex min-h-5 grow items-center gap-1 px-1">
|
<div className="flex min-h-5 grow items-center gap-1 px-1">
|
||||||
<span className="system-sm-medium flex grow text-text-secondary">{t(`${i18nPrefix}.operations.${item.name}` as any, { ns: 'workflow' }) as string}</span>
|
<span className="system-sm-medium flex grow text-text-secondary">{t(`nodes.assigner.operations.${item.name}`, { ns: 'workflow' })}</span>
|
||||||
</div>
|
</div>
|
||||||
{item.value === value && (
|
{item.value === value && (
|
||||||
<div className="flex items-center justify-center">
|
<div className="flex items-center justify-center">
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import type { AssignerNodeType } from './types'
|
import type { AssignerNodeType } from './types'
|
||||||
|
import type { OperationName } from './utils'
|
||||||
import type { Node, NodeProps } from '@/app/components/workflow/types'
|
import type { Node, NodeProps } from '@/app/components/workflow/types'
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -59,7 +60,8 @@ const NodeComponent: FC<NodeProps<AssignerNodeType>> = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Legacy version
|
// 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)
|
if (!variable || variable.length === 0)
|
||||||
return null
|
return null
|
||||||
@ -73,7 +75,7 @@ const NodeComponent: FC<NodeProps<AssignerNodeType>> = ({
|
|||||||
nodeType={node?.data.type}
|
nodeType={node?.data.type}
|
||||||
nodeTitle={node?.data.title}
|
nodeTitle={node?.data.title}
|
||||||
rightSlot={
|
rightSlot={
|
||||||
writeMode && <Badge className="!ml-auto shrink-0" text={t(`${i18nPrefix}.operations.${writeMode}` as any, { ns: 'workflow' }) as string} />
|
writeMode && <Badge className="!ml-auto shrink-0" text={t(`nodes.assigner.operations.${writeMode}`, { ns: 'workflow' })} />
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { AssignerNodeType } from './types'
|
import type { AssignerNodeType } from './types'
|
||||||
|
import type { I18nKeysByPrefix } from '@/types/i18n'
|
||||||
import { AssignerNodeInputType, WriteMode } from './types'
|
import { AssignerNodeInputType, WriteMode } from './types'
|
||||||
|
|
||||||
export const checkNodeValid = (_payload: AssignerNodeType) => {
|
export const checkNodeValid = (_payload: AssignerNodeType) => {
|
||||||
@ -11,9 +12,14 @@ export const formatOperationName = (type: string) => {
|
|||||||
return type.charAt(0).toUpperCase() + type.slice(1)
|
return type.charAt(0).toUpperCase() + type.slice(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Item = {
|
export type OperationName = I18nKeysByPrefix<'workflow', 'nodes.assigner.operations.'>
|
||||||
value: string | number
|
|
||||||
name: string
|
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 = (
|
export const getOperationItems = (
|
||||||
|
|||||||
@ -1,15 +1,21 @@
|
|||||||
|
import type { I18nKeysByPrefix } from '@/types/i18n'
|
||||||
import { TransferMethod } from '@/types/app'
|
import { TransferMethod } from '@/types/app'
|
||||||
|
|
||||||
export const CUSTOM_NODE_TYPE = 'custom'
|
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: 'image', i18nKey: 'image' },
|
||||||
{ value: 'document', i18nKey: 'doc' },
|
{ value: 'document', i18nKey: 'doc' },
|
||||||
{ value: 'audio', i18nKey: 'audio' },
|
{ value: 'audio', i18nKey: 'audio' },
|
||||||
{ value: 'video', i18nKey: 'video' },
|
{ value: 'video', i18nKey: 'video' },
|
||||||
]
|
]
|
||||||
|
|
||||||
export const TRANSFER_METHOD = [
|
export const TRANSFER_METHOD: OptionItem[] = [
|
||||||
{ value: TransferMethod.local_file, i18nKey: 'localUpload' },
|
{ value: TransferMethod.local_file, i18nKey: 'localUpload' },
|
||||||
{ value: TransferMethod.remote_url, i18nKey: 'url' },
|
{ value: TransferMethod.remote_url, i18nKey: 'url' },
|
||||||
]
|
]
|
||||||
|
|||||||
@ -17,8 +17,6 @@ import {
|
|||||||
isEmptyRelatedOperator,
|
isEmptyRelatedOperator,
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
|
|
||||||
const i18nPrefix = 'nodes.ifElse'
|
|
||||||
|
|
||||||
type ConditionValueProps = {
|
type ConditionValueProps = {
|
||||||
condition: Condition
|
condition: Condition
|
||||||
}
|
}
|
||||||
@ -34,7 +32,7 @@ const ConditionValue = ({
|
|||||||
|
|
||||||
const variableSelector = variable_selector as ValueSelector
|
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 formatValue = useCallback((c: Condition) => {
|
||||||
const notHasValue = comparisonOperatorNotRequireValue(c.comparison_operator)
|
const notHasValue = comparisonOperatorNotRequireValue(c.comparison_operator)
|
||||||
if (notHasValue)
|
if (notHasValue)
|
||||||
@ -59,7 +57,7 @@ const ConditionValue = ({
|
|||||||
if (isSelect) {
|
if (isSelect) {
|
||||||
const name = [...FILE_TYPE_OPTIONS, ...TRANSFER_METHOD].filter(item => item.value === (Array.isArray(c.value) ? c.value[0] : c.value))[0]
|
const name = [...FILE_TYPE_OPTIONS, ...TRANSFER_METHOD].filter(item => item.value === (Array.isArray(c.value) ? c.value[0] : c.value))[0]
|
||||||
return name
|
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('.')
|
const arr: string[] = b.split('.')
|
||||||
if (isSystemVar(arr))
|
if (isSystemVar(arr))
|
||||||
return `{{${b}}}`
|
return `{{${b}}}`
|
||||||
@ -88,14 +86,20 @@ const ConditionValue = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="ml-[10px] border-l border-divider-regular pl-[10px]">
|
<div className="ml-[10px] border-l border-divider-regular pl-[10px]">
|
||||||
{
|
{
|
||||||
sub_variable_condition?.conditions.map((c: Condition, index) => (
|
sub_variable_condition?.conditions.map((c: Condition, index) => {
|
||||||
<div className="relative flex h-6 items-center space-x-1" key={c.id}>
|
const comparisonOperator = c.comparison_operator
|
||||||
<div className="system-xs-medium text-text-accent">{c.key}</div>
|
const comparisonOperatorName = comparisonOperator && isComparisonOperatorNeedTranslate(comparisonOperator)
|
||||||
<div className="system-xs-medium text-text-primary">{isComparisonOperatorNeedTranslate(c.comparison_operator) ? t(`nodes.ifElse.comparisonOperator.${c.comparison_operator}` as any, { ns: 'workflow' }) as string : c.comparison_operator}</div>
|
? t(`nodes.ifElse.comparisonOperator.${comparisonOperator}`, { ns: 'workflow' })
|
||||||
{c.comparison_operator && !isEmptyRelatedOperator(c.comparison_operator) && <div className="system-xs-regular text-text-secondary">{isSelect(c) ? selectName(c) : formatValue(c)}</div>}
|
: comparisonOperator
|
||||||
{index !== sub_variable_condition.conditions.length - 1 && (<div className="absolute bottom-[-10px] right-1 z-10 text-[10px] font-medium uppercase leading-4 text-text-accent">{t(`${i18nPrefix}.${sub_variable_condition.logical_operator}` as any, { ns: 'workflow' }) as string}</div>)}
|
return (
|
||||||
</div>
|
<div className="relative flex h-6 items-center space-x-1" key={c.id}>
|
||||||
))
|
<div className="system-xs-medium text-text-accent">{c.key}</div>
|
||||||
|
<div className="system-xs-medium text-text-primary">{comparisonOperatorName}</div>
|
||||||
|
{c.comparison_operator && !isEmptyRelatedOperator(c.comparison_operator) && <div className="system-xs-regular text-text-secondary">{isSelect(c) ? selectName(c) : formatValue(c)}</div>}
|
||||||
|
{index !== sub_variable_condition.conditions.length - 1 && (<div className="absolute bottom-[-10px] right-1 z-10 text-[10px] font-medium uppercase leading-4 text-text-accent">{t(`nodes.ifElse.${sub_variable_condition.logical_operator}`, { ns: 'workflow' })}</div>)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { Branch } from '@/app/components/workflow/types'
|
import type { Branch } from '@/app/components/workflow/types'
|
||||||
|
import type { I18nKeysByPrefix } from '@/types/i18n'
|
||||||
import { VarType } from '@/app/components/workflow/types'
|
import { VarType } from '@/app/components/workflow/types'
|
||||||
import { ComparisonOperator } from './types'
|
import { ComparisonOperator } from './types'
|
||||||
|
|
||||||
@ -13,12 +14,18 @@ const notTranslateKey = [
|
|||||||
ComparisonOperator.largerThanOrEqual,
|
ComparisonOperator.largerThanOrEqual,
|
||||||
ComparisonOperator.lessThan,
|
ComparisonOperator.lessThan,
|
||||||
ComparisonOperator.lessThanOrEqual,
|
ComparisonOperator.lessThanOrEqual,
|
||||||
]
|
] as const
|
||||||
|
|
||||||
export const isComparisonOperatorNeedTranslate = (operator?: ComparisonOperator) => {
|
type NotTranslateOperator = typeof notTranslateKey[number]
|
||||||
|
export type TranslatableComparisonOperator = Exclude<ComparisonOperator, NotTranslateOperator>
|
||||||
|
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)
|
if (!operator)
|
||||||
return false
|
return false
|
||||||
return !notTranslateKey.includes(operator)
|
return !(notTranslateKey as readonly ComparisonOperator[]).includes(operator)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getOperators = (type?: VarType, file?: { key: string }) => {
|
export const getOperators = (type?: VarType, file?: { key: string }) => {
|
||||||
|
|||||||
@ -513,6 +513,8 @@
|
|||||||
"nodes.ifElse.comparisonOperator.in": "in",
|
"nodes.ifElse.comparisonOperator.in": "in",
|
||||||
"nodes.ifElse.comparisonOperator.is": "is",
|
"nodes.ifElse.comparisonOperator.is": "is",
|
||||||
"nodes.ifElse.comparisonOperator.is not": "is not",
|
"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 contains": "not contains",
|
||||||
"nodes.ifElse.comparisonOperator.not empty": "is not empty",
|
"nodes.ifElse.comparisonOperator.not empty": "is not empty",
|
||||||
"nodes.ifElse.comparisonOperator.not exists": "not exists",
|
"nodes.ifElse.comparisonOperator.not exists": "not exists",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user