type safe

This commit is contained in:
Stephen Zhou 2025-12-27 00:48:30 +08:00
parent 89a1d15da3
commit 8698d4f0e6
No known key found for this signature in database
15 changed files with 51 additions and 35 deletions

View File

@ -8,17 +8,17 @@ type OptionItem = {
i18nKey: I18nKeysByPrefix<'workflow', 'nodes.ifElse.optionName.'>
}
export const FILE_TYPE_OPTIONS: OptionItem[] = [
export const FILE_TYPE_OPTIONS = [
{ value: 'image', i18nKey: 'image' },
{ value: 'document', i18nKey: 'doc' },
{ value: 'audio', i18nKey: 'audio' },
{ value: 'video', i18nKey: 'video' },
]
] as const satisfies readonly OptionItem[]
export const TRANSFER_METHOD: OptionItem[] = [
export const TRANSFER_METHOD = [
{ value: TransferMethod.local_file, i18nKey: 'localUpload' },
{ value: TransferMethod.remote_url, i18nKey: 'url' },
]
] as const satisfies readonly OptionItem[]
export const SUB_VARIABLES = ['type', 'size', 'name', 'url', 'extension', 'mime_type', 'transfer_method', 'related_id']
export const OUTPUT_FILE_SUB_VARIABLES = SUB_VARIABLES.filter(key => key !== 'transfer_method')

View File

@ -49,7 +49,7 @@ import ConditionInput from './condition-input'
import ConditionOperator from './condition-operator'
import ConditionVarSelector from './condition-var-selector'
const optionNameI18NPrefix = 'nodes.ifElse.optionName'
const optionNameI18NPrefix = 'nodes.ifElse.optionName' as const
type ConditionItemProps = {
className?: string
@ -168,13 +168,13 @@ const ConditionItem = ({
if (isSelect) {
if (fileAttr?.key === 'type' || condition.comparison_operator === ComparisonOperator.allOf) {
return FILE_TYPE_OPTIONS.map(item => ({
name: t(`${optionNameI18NPrefix}.${item.i18nKey}` as any, { ns: 'workflow' }) as string,
name: t(`${optionNameI18NPrefix}.${item.i18nKey}`, { ns: 'workflow' }),
value: item.value,
}))
}
if (fileAttr?.key === 'transfer_method') {
return TRANSFER_METHOD.map(item => ({
name: t(`${optionNameI18NPrefix}.${item.i18nKey}` as any, { ns: 'workflow' }) as string,
name: t(`${optionNameI18NPrefix}.${item.i18nKey}`, { ns: 'workflow' }),
value: item.value,
}))
}

View File

@ -39,7 +39,7 @@ const ConditionOperator = ({
const options = useMemo(() => {
return getOperators(varType, file).map((o) => {
return {
label: isComparisonOperatorNeedTranslate(o) ? t(`${i18nPrefix}.comparisonOperator.${o}` as any, { ns: 'workflow' }) as string : o,
label: isComparisonOperatorNeedTranslate(o) ? t(`${i18nPrefix}.comparisonOperator.${o}`, { ns: 'workflow' }) : o,
value: o,
}
})
@ -65,7 +65,7 @@ const ConditionOperator = ({
{
selectedOption
? selectedOption.label
: t(`${i18nPrefix}.select` as any, { ns: 'workflow' }) as string
: t(`${i18nPrefix}.select`, { ns: 'workflow' })
}
<RiArrowDownSLine className="ml-1 h-3.5 w-3.5" />
</Button>

View File

@ -35,7 +35,7 @@ const ConditionValue = ({
const { t } = useTranslation()
const nodes = useNodes()
const variableName = labelName || (isSystemVar(variableSelector) ? variableSelector.slice(0).join('.') : variableSelector.slice(1).join('.'))
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 notHasValue = comparisonOperatorNotRequireValue(operator)
const node: Node<CommonNodeType> | undefined = nodes.find(n => n.id === variableSelector[0]) as Node<CommonNodeType>
const isException = isExceptionVariable(variableName, node?.data.type)
@ -63,7 +63,7 @@ const ConditionValue = ({
if (isSelect) {
const name = [...FILE_TYPE_OPTIONS, ...TRANSFER_METHOD].filter(item => item.value === (Array.isArray(value) ? value[0] : 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}}}`

View File

@ -135,7 +135,7 @@ export const useNodeIterationInteractions = () => {
_isBundled: false,
_connectedSourceHandleIds: [],
_connectedTargetHandleIds: [],
title: nodesWithSameType.length > 0 ? `${t(`blocks.${childNodeType}` as any, { ns: 'workflow' }) as string} ${childNodeTypeCount[childNodeType]}` : t(`blocks.${childNodeType}` as any, { ns: 'workflow' }) as string,
title: nodesWithSameType.length > 0 ? `${t(`blocks.${childNodeType}`, { ns: 'workflow' })} ${childNodeTypeCount[childNodeType]}` : t(`blocks.${childNodeType}`, { ns: 'workflow' }),
iteration_id: newNodeId,
type: childNodeType,
},

View File

@ -42,7 +42,7 @@ const ConditionOperator = ({
const options = useMemo(() => {
return getOperators(variableType).map((o) => {
return {
label: isComparisonOperatorNeedTranslate(o) ? t(`${i18nPrefix}.comparisonOperator.${o}` as any, { ns: 'workflow' }) as string : o,
label: isComparisonOperatorNeedTranslate(o) ? t(`${i18nPrefix}.comparisonOperator.${o}`, { ns: 'workflow' }) : o,
value: o,
}
})
@ -68,7 +68,7 @@ const ConditionOperator = ({
{
selectedOption
? selectedOption.label
: t(`${i18nPrefix}.select` as any, { ns: 'workflow' }) as string
: t(`${i18nPrefix}.select`, { ns: 'workflow' })
}
<RiArrowDownSLine className="ml-1 h-3.5 w-3.5" />
</Button>

View File

@ -14,12 +14,17 @@ const notTranslateKey = [
ComparisonOperator.largerThanOrEqual,
ComparisonOperator.lessThan,
ComparisonOperator.lessThanOrEqual,
]
] as const
export const isComparisonOperatorNeedTranslate = (operator?: ComparisonOperator) => {
type NotTranslateOperator = typeof notTranslateKey[number]
export type TranslatableComparisonOperator = Exclude<ComparisonOperator, NotTranslateOperator>
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?: MetadataFilteringVariableType) => {

View File

@ -66,13 +66,13 @@ const FilterCondition: FC<Props> = ({
if (isSelect) {
if (condition.key === 'type' || condition.comparison_operator === ComparisonOperator.allOf) {
return FILE_TYPE_OPTIONS.map(item => ({
name: t(`${optionNameI18NPrefix}.${item.i18nKey}` as any, { ns: 'workflow' }) as string,
name: t(`${optionNameI18NPrefix}.${item.i18nKey}`, { ns: 'workflow' }),
value: item.value,
}))
}
if (condition.key === 'transfer_method') {
return TRANSFER_METHOD.map(item => ({
name: t(`${optionNameI18NPrefix}.${item.i18nKey}` as any, { ns: 'workflow' }) as string,
name: t(`${optionNameI18NPrefix}.${item.i18nKey}`, { ns: 'workflow' }),
value: item.value,
}))
}

View File

@ -121,7 +121,7 @@ const ConfigPromptItem: FC<Props> = ({
<Tooltip
popupContent={
<div className="max-w-[180px]">{t(`${i18nPrefix}.roleDescription.${payload.role}` as any, { ns: 'workflow' }) as string}</div>
<div className="max-w-[180px]">{payload.role && t(`${i18nPrefix}.roleDescription.${payload.role}`, { ns: 'workflow' })}</div>
}
triggerClassName="w-4 h-4"
/>

View File

@ -34,7 +34,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 +59,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: string, b: string) => {
? t(`nodes.ifElse.optionName.${name.i18nKey}`, { ns: 'workflow' }).replace(/\{\{#([^#]*)#\}\}/g, (a: string, b: string) => {
const arr: string[] = b.split('.')
if (isSystemVar(arr))
return `{{${b}}}`
@ -91,9 +91,9 @@ const ConditionValue = ({
sub_variable_condition?.conditions.map((c: Condition, index) => (
<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">{isComparisonOperatorNeedTranslate(c.comparison_operator) ? t(`nodes.ifElse.comparisonOperator.${c.comparison_operator}` as any, { ns: 'workflow' }) as string : c.comparison_operator}</div>
<div className="system-xs-medium text-text-primary">{isComparisonOperatorNeedTranslate(c.comparison_operator) ? t(`nodes.ifElse.comparisonOperator.${c.comparison_operator}`, { ns: 'workflow' }) : c.comparison_operator}</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(`${i18nPrefix}.${sub_variable_condition.logical_operator}` as any, { ns: 'workflow' }) as string}</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(`${i18nPrefix}.${sub_variable_condition.logical_operator}`, { ns: 'workflow' })}</div>)}
</div>
))
}

View File

@ -145,13 +145,13 @@ const ConditionItem = ({
if (isSelect) {
if (fileAttr?.key === 'type' || condition.comparison_operator === ComparisonOperator.allOf) {
return FILE_TYPE_OPTIONS.map(item => ({
name: t(`${optionNameI18NPrefix}.${item.i18nKey}` as any, { ns: 'workflow' }) as string,
name: t(`${optionNameI18NPrefix}.${item.i18nKey}`, { ns: 'workflow' }),
value: item.value,
}))
}
if (fileAttr?.key === 'transfer_method') {
return TRANSFER_METHOD.map(item => ({
name: t(`${optionNameI18NPrefix}.${item.i18nKey}` as any, { ns: 'workflow' }) as string,
name: t(`${optionNameI18NPrefix}.${item.i18nKey}`, { ns: 'workflow' }),
value: item.value,
}))
}

View File

@ -39,7 +39,7 @@ const ConditionOperator = ({
const options = useMemo(() => {
return getOperators(varType, file).map((o) => {
return {
label: isComparisonOperatorNeedTranslate(o) ? t(`${i18nPrefix}.comparisonOperator.${o}` as any, { ns: 'workflow' }) as string : o,
label: isComparisonOperatorNeedTranslate(o) ? t(`${i18nPrefix}.comparisonOperator.${o}`, { ns: 'workflow' }) : o,
value: o,
}
})
@ -65,7 +65,7 @@ const ConditionOperator = ({
{
selectedOption
? selectedOption.label
: t(`${i18nPrefix}.select` as any, { ns: 'workflow' }) as string
: t(`${i18nPrefix}.select`, { ns: 'workflow' })
}
<RiArrowDownSLine className="ml-1 h-3.5 w-3.5" />
</Button>

View File

@ -27,7 +27,7 @@ const ConditionValue = ({
value,
}: ConditionValueProps) => {
const { t } = useTranslation()
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 notHasValue = comparisonOperatorNotRequireValue(operator)
const formatValue = useMemo(() => {
if (notHasValue)
@ -50,7 +50,7 @@ const ConditionValue = ({
if (isSelect) {
const name = [...FILE_TYPE_OPTIONS, ...TRANSFER_METHOD].filter(item => item.value === (Array.isArray(value) ? value[0] : 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}}}`

View File

@ -1,5 +1,6 @@
import type { NodeDefault } from '../../types'
import type { LoopNodeType } from './types'
import type { I18nKeysByPrefix } from '@/types/i18n'
import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types'
import { BlockEnum } from '@/app/components/workflow/types'
import { genNodeMetaData } from '@/app/components/workflow/utils'
@ -79,17 +80,22 @@ const nodeDefault: NodeDefault<LoopNodeType> = {
},
}
type OptionItem = {
value: string
i18nKey: I18nKeysByPrefix<'workflow', 'nodes.ifElse.optionName.'>
}
export const FILE_TYPE_OPTIONS = [
{ value: 'image', i18nKey: 'image' },
{ value: 'document', i18nKey: 'doc' },
{ value: 'audio', i18nKey: 'audio' },
{ value: 'video', i18nKey: 'video' },
]
] as const satisfies readonly OptionItem[]
export const TRANSFER_METHOD = [
{ value: TransferMethod.local_file, i18nKey: 'localUpload' },
{ value: TransferMethod.remote_url, i18nKey: 'url' },
]
] as const satisfies readonly OptionItem[]
export const SUB_VARIABLES = ['type', 'size', 'name', 'url', 'extension', 'mime_type', 'transfer_method', 'related_id']
export const OUTPUT_FILE_SUB_VARIABLES = SUB_VARIABLES.filter(key => key !== 'transfer_method')

View File

@ -13,12 +13,17 @@ const notTranslateKey = [
ComparisonOperator.largerThanOrEqual,
ComparisonOperator.lessThan,
ComparisonOperator.lessThanOrEqual,
]
] as const
export const isComparisonOperatorNeedTranslate = (operator?: ComparisonOperator) => {
type NotTranslateOperator = typeof notTranslateKey[number]
export type TranslatableComparisonOperator = Exclude<ComparisonOperator, NotTranslateOperator>
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 }) => {