feat: can add sub variable

This commit is contained in:
Joel 2024-08-16 17:12:44 +08:00
parent eaa7d114dc
commit 4554ac3ef8
9 changed files with 227 additions and 113 deletions

View File

@ -5,19 +5,20 @@ import {
} from 'react' } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { RiDeleteBinLine } from '@remixicon/react' import { RiDeleteBinLine } from '@remixicon/react'
import produce from 'immer'
import type { VarType as NumberVarType } from '../../../tool/types' import type { VarType as NumberVarType } from '../../../tool/types'
import type { import type {
Condition, Condition,
HandleAddSubVariableCondition,
HandleRemoveCondition, HandleRemoveCondition,
HandleUpdateCondition, HandleUpdateCondition,
} from '../../types' } from '../../types'
import { import {
ComparisonOperator, ComparisonOperator,
LogicalOperator,
} from '../../types' } from '../../types'
import { comparisonOperatorNotRequireValue } from '../../utils' import { comparisonOperatorNotRequireValue } from '../../utils'
import ConditionNumberInput from '../condition-number-input' import ConditionNumberInput from '../condition-number-input'
import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '../../default' import { FILE_TYPE_OPTIONS, SUB_VARIABLES, TRANSFER_METHOD } from '../../default'
import ConditionWrap from '../condition-wrap' import ConditionWrap from '../condition-wrap'
import ConditionOperator from './condition-operator' import ConditionOperator from './condition-operator'
import ConditionInput from './condition-input' import ConditionInput from './condition-input'
@ -29,7 +30,6 @@ import type {
import { VarType } from '@/app/components/workflow/types' import { VarType } from '@/app/components/workflow/types'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import { SimpleSelect as Select } from '@/app/components/base/select' import { SimpleSelect as Select } from '@/app/components/base/select'
const optionNameI18NPrefix = 'workflow.nodes.ifElse.optionName' const optionNameI18NPrefix = 'workflow.nodes.ifElse.optionName'
type ConditionItemProps = { type ConditionItemProps = {
@ -37,22 +37,26 @@ type ConditionItemProps = {
caseId: string caseId: string
condition: Condition condition: Condition
file?: { key: string } file?: { key: string }
isSubVariableKey?: boolean
onRemoveCondition: HandleRemoveCondition onRemoveCondition: HandleRemoveCondition
onUpdateCondition: HandleUpdateCondition onUpdateCondition: HandleUpdateCondition
nodesOutputVars: NodeOutPutVar[] nodesOutputVars: NodeOutPutVar[]
availableNodes: Node[] availableNodes: Node[]
numberVariables: NodeOutPutVar[] numberVariables: NodeOutPutVar[]
onAddSubVariableCondition?: HandleAddSubVariableCondition
} }
const ConditionItem = ({ const ConditionItem = ({
disabled, disabled,
caseId, caseId,
condition, condition,
file, file,
isSubVariableKey,
onRemoveCondition, onRemoveCondition,
onUpdateCondition, onUpdateCondition,
nodesOutputVars, nodesOutputVars,
availableNodes, availableNodes,
numberVariables, numberVariables,
onAddSubVariableCondition,
}: ConditionItemProps) => { }: ConditionItemProps) => {
const { t } = useTranslation() const { t } = useTranslation()
@ -104,8 +108,21 @@ const ConditionItem = ({
}, [condition.comparison_operator, file?.key, isSelect, t]) }, [condition.comparison_operator, file?.key, isSelect, t])
const isSubVariable = condition.varType === VarType.arrayFile && [ComparisonOperator.contains, ComparisonOperator.notContains].includes(condition.comparison_operator!) const isSubVariable = condition.varType === VarType.arrayFile && [ComparisonOperator.contains, ComparisonOperator.notContains].includes(condition.comparison_operator!)
const isNotInput = isSelect || isSubVariable const isNotInput = isSelect || isSubVariable
const isSubVarSelect = isSubVariableKey
const subVarOptions = SUB_VARIABLES.map(item => ({
name: item,
value: item,
}))
const handleSubVarKeyChange = useCallback((key: string) => {
const newCondition = produce(condition, (draft) => {
draft.key = key
})
onUpdateCondition(caseId, condition.id, newCondition)
}, [caseId, condition, onUpdateCondition])
return ( return (
<div className='flex mb-1 last-of-type:mb-0'> <div className='flex mb-1 last-of-type:mb-0'>
<div className={cn( <div className={cn(
@ -114,10 +131,23 @@ const ConditionItem = ({
)}> )}>
<div className='flex items-center p-1'> <div className='flex items-center p-1'>
<div className='grow w-0'> <div className='grow w-0'>
{isSubVarSelect
? (
<Select
wrapperClassName='h-6'
className='pl-0 text-xs'
defaultValue={condition.value}
items={subVarOptions}
onSelect={item => handleSubVarKeyChange(item.value as string)}
/>
)
: (
<VariableTag <VariableTag
valueSelector={condition.variable_selector} valueSelector={condition.variable_selector || []}
varType={condition.varType} varType={condition.varType}
/> />
)}
</div> </div>
<div className='mx-1 w-[1px] h-3 bg-divider-regular'></div> <div className='mx-1 w-[1px] h-3 bg-divider-regular'></div>
<ConditionOperator <ConditionOperator
@ -172,13 +202,12 @@ const ConditionItem = ({
<div className='p-1'> <div className='p-1'>
<ConditionWrap <ConditionWrap
isSubVariable isSubVariable
conditionId={condition.id}
caseId={caseId}
readOnly={!!disabled} readOnly={!!disabled}
nodeId='' nodeId=''
cases={[{ cases={condition.sub_variable_condition ? [condition.sub_variable_condition] : []}
case_id: '0', handleAddSubVariableCondition={onAddSubVariableCondition}
conditions: [],
logical_operator: LogicalOperator.and,
}]}
handleRemoveCase={() => { }} handleRemoveCase={() => { }}
handleAddCondition={() => { }} handleAddCondition={() => { }}
handleUpdateCondition={() => { }} handleUpdateCondition={() => { }}

View File

@ -2,6 +2,7 @@ import { RiLoopLeftLine } from '@remixicon/react'
import { LogicalOperator } from '../../types' import { LogicalOperator } from '../../types'
import type { import type {
CaseItem, CaseItem,
HandleAddSubVariableCondition,
HandleRemoveCondition, HandleRemoveCondition,
HandleUpdateCondition, HandleUpdateCondition,
HandleUpdateConditionLogicalOperator, HandleUpdateConditionLogicalOperator,
@ -13,6 +14,7 @@ import type {
} from '@/app/components/workflow/types' } from '@/app/components/workflow/types'
type ConditionListProps = { type ConditionListProps = {
isSubVariable?: boolean
disabled?: boolean disabled?: boolean
caseItem: CaseItem caseItem: CaseItem
onUpdateCondition: HandleUpdateCondition onUpdateCondition: HandleUpdateCondition
@ -22,8 +24,10 @@ type ConditionListProps = {
availableNodes: Node[] availableNodes: Node[]
numberVariables: NodeOutPutVar[] numberVariables: NodeOutPutVar[]
varsIsVarFileAttribute: Record<string, boolean> varsIsVarFileAttribute: Record<string, boolean>
onAddSubVariableCondition?: HandleAddSubVariableCondition
} }
const ConditionList = ({ const ConditionList = ({
isSubVariable,
disabled, disabled,
caseItem, caseItem,
onUpdateCondition, onUpdateCondition,
@ -33,6 +37,7 @@ const ConditionList = ({
availableNodes, availableNodes,
numberVariables, numberVariables,
varsIsVarFileAttribute, varsIsVarFileAttribute,
onAddSubVariableCondition,
}: ConditionListProps) => { }: ConditionListProps) => {
const { conditions, logical_operator } = caseItem const { conditions, logical_operator } = caseItem
@ -67,7 +72,9 @@ const ConditionList = ({
nodesOutputVars={nodesOutputVars} nodesOutputVars={nodesOutputVars}
availableNodes={availableNodes} availableNodes={availableNodes}
numberVariables={numberVariables} numberVariables={numberVariables}
file={varsIsVarFileAttribute[condition.id] ? { key: condition.variable_selector.slice(-1)[0] } : undefined} file={varsIsVarFileAttribute[condition.id] ? { key: (condition.variable_selector || []).slice(-1)[0] } : undefined}
isSubVariableKey={isSubVariable}
onAddSubVariableCondition={onAddSubVariableCondition}
/> />
)) ))
} }

View File

@ -4,10 +4,11 @@ import React, { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { ReactSortable } from 'react-sortablejs' import { ReactSortable } from 'react-sortablejs'
import { import {
RiAddLine,
RiDeleteBinLine, RiDeleteBinLine,
RiDraggable, RiDraggable,
} from '@remixicon/react' } from '@remixicon/react'
import type { CaseItem, HandleAddCondition, HandleRemoveCondition, HandleUpdateCondition, HandleUpdateConditionLogicalOperator } from '../types' import type { CaseItem, HandleAddCondition, HandleAddSubVariableCondition, HandleRemoveCondition, HandleUpdateCondition, HandleUpdateConditionLogicalOperator } from '../types'
import type { Node, NodeOutPutVar, Var } from '../../../types' import type { Node, NodeOutPutVar, Var } from '../../../types'
import { VarType } from '../../../types' import { VarType } from '../../../types'
import { useGetAvailableVars } from '../../variable-assigner/hooks' import { useGetAvailableVars } from '../../variable-assigner/hooks'
@ -18,12 +19,15 @@ import Button from '@/app/components/base/button'
type Props = { type Props = {
isSubVariable?: boolean isSubVariable?: boolean
conditionId?: string
caseId?: string
nodeId: string nodeId: string
cases: CaseItem[] cases: CaseItem[]
readOnly: boolean readOnly: boolean
handleSortCase?: (sortedCases: (CaseItem & { id: string })[]) => void handleSortCase?: (sortedCases: (CaseItem & { id: string })[]) => void
handleRemoveCase: (caseId: string) => void handleRemoveCase: (caseId: string) => void
handleAddCondition: HandleAddCondition handleAddCondition?: HandleAddCondition
handleAddSubVariableCondition?: HandleAddSubVariableCondition
handleUpdateCondition: HandleUpdateCondition handleUpdateCondition: HandleUpdateCondition
handleRemoveCondition: HandleRemoveCondition handleRemoveCondition: HandleRemoveCondition
handleUpdateConditionLogicalOperator: HandleUpdateConditionLogicalOperator handleUpdateConditionLogicalOperator: HandleUpdateConditionLogicalOperator
@ -35,19 +39,22 @@ type Props = {
const ConditionWrap: FC<Props> = ({ const ConditionWrap: FC<Props> = ({
isSubVariable, isSubVariable,
conditionId: parentConditionId,
caseId: conditionParentCaseId,
nodeId: id, nodeId: id,
cases, cases = [],
readOnly, readOnly,
handleSortCase = () => { }, handleSortCase = () => { },
handleRemoveCase,
handleUpdateCondition, handleUpdateCondition,
handleAddCondition,
handleRemoveCondition, handleRemoveCondition,
handleAddSubVariableCondition,
handleUpdateConditionLogicalOperator, handleUpdateConditionLogicalOperator,
nodesOutputVars, nodesOutputVars,
availableNodes, availableNodes,
varsIsVarFileAttribute, varsIsVarFileAttribute,
filterVar, filterVar,
handleAddCondition,
handleRemoveCase,
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
@ -61,6 +68,7 @@ const ConditionWrap: FC<Props> = ({
}, []) }, [])
return ( return (
<>
<ReactSortable <ReactSortable
list={cases.map(caseItem => ({ ...caseItem, id: caseItem.case_id }))} list={cases.map(caseItem => ({ ...caseItem, id: caseItem.case_id }))}
setList={handleSortCase} setList={handleSortCase}
@ -74,10 +82,14 @@ const ConditionWrap: FC<Props> = ({
<div key={item.case_id}> <div key={item.case_id}>
<div <div
className={cn( className={cn(
'group relative py-1 px-3 min-h-[40px] rounded-[10px] bg-components-panel-bg', 'group relative rounded-[10px] bg-components-panel-bg',
willDeleteCaseId === item.case_id && 'bg-state-destructive-hover', willDeleteCaseId === item.case_id && 'bg-state-destructive-hover',
!isSubVariable && 'py-1 px-3 min-h-[40px] ',
isSubVariable && 'p-2 pr-1',
)} )}
> >
{!isSubVariable && (
<>
<RiDraggable className={cn( <RiDraggable className={cn(
'hidden handle absolute top-2 left-1 w-3 h-3 text-text-quaternary cursor-pointer', 'hidden handle absolute top-2 left-1 w-3 h-3 text-text-quaternary cursor-pointer',
casesLength > 1 && 'group-hover:block', casesLength > 1 && 'group-hover:block',
@ -95,6 +107,9 @@ const ConditionWrap: FC<Props> = ({
) )
} }
</div> </div>
</>
)}
{ {
!!item.conditions.length && ( !!item.conditions.length && (
<div className='mb-2'> <div className='mb-2'>
@ -108,20 +123,39 @@ const ConditionWrap: FC<Props> = ({
availableNodes={availableNodes} availableNodes={availableNodes}
numberVariables={getAvailableVars(id, '', filterNumberVar)} numberVariables={getAvailableVars(id, '', filterNumberVar)}
varsIsVarFileAttribute={varsIsVarFileAttribute} varsIsVarFileAttribute={varsIsVarFileAttribute}
onAddSubVariableCondition={handleAddSubVariableCondition}
isSubVariable={isSubVariable}
/> />
</div> </div>
) )
} }
<div className={cn( <div className={cn(
'flex items-center justify-between pl-[60px] pr-[30px]', 'flex items-center justify-between pr-[30px]',
!item.conditions.length && 'mt-1', !item.conditions.length && !isSubVariable && 'mt-1',
!item.conditions.length && isSubVariable && 'mt-2',
!isSubVariable && ' pl-[60px]',
)}> )}>
{isSubVariable
? (
<Button
size='small'
disabled={readOnly}
onClick={() => handleAddSubVariableCondition?.(conditionParentCaseId!, parentConditionId!)}
>
<RiAddLine className='mr-1 w-3.5 h-3.5' />
{t('workflow.nodes.ifElse.addSubVariable')}
</Button>
)
: (
<ConditionAdd <ConditionAdd
disabled={readOnly} disabled={readOnly}
caseId={item.case_id} caseId={item.case_id}
variables={getAvailableVars(id, '', filterVar)} variables={getAvailableVars(id, '', filterVar)}
onSelectVariable={handleAddCondition} onSelectVariable={handleAddCondition!}
/> />
)}
{ {
((index === 0 && casesLength > 1) || (index > 0)) && ( ((index === 0 && casesLength > 1) || (index > 0)) && (
<Button <Button
@ -140,11 +174,24 @@ const ConditionWrap: FC<Props> = ({
} }
</div> </div>
</div> </div>
{!isSubVariable && (
<div className='my-2 mx-3 h-[1px] bg-divider-subtle'></div> <div className='my-2 mx-3 h-[1px] bg-divider-subtle'></div>
)}
</div> </div>
)) ))
} }
</ReactSortable> </ReactSortable>
{(cases.length === 0) && (
<Button
size='small'
disabled={readOnly}
onClick={() => handleAddSubVariableCondition?.(conditionParentCaseId!, parentConditionId!)}
>
<RiAddLine className='mr-1 w-3.5 h-3.5' />
{t('workflow.nodes.ifElse.addSubVariable')}
</Button>
)}
</>
) )
} }
export default React.memo(ConditionWrap) export default React.memo(ConditionWrap)

View File

@ -74,3 +74,5 @@ export const TRANSFER_METHOD = [
{ value: TransferMethod.local_file, i18nKey: 'localUpload' }, { value: TransferMethod.local_file, i18nKey: 'localUpload' },
{ value: TransferMethod.remote_url, i18nKey: 'url' }, { value: TransferMethod.remote_url, i18nKey: 'url' },
] ]
export const SUB_VARIABLES = ['type', 'size', 'name', 'url', 'extension', 'mime_type', 'transfer_method', 'url']

View File

@ -30,6 +30,7 @@ const Panel: FC<NodePanelProps<IfElseNodeType>> = ({
handleAddCondition, handleAddCondition,
handleUpdateCondition, handleUpdateCondition,
handleRemoveCondition, handleRemoveCondition,
handleAddSubVariableCondition,
handleUpdateConditionLogicalOperator, handleUpdateConditionLogicalOperator,
nodesOutputVars, nodesOutputVars,
availableNodes, availableNodes,
@ -49,6 +50,7 @@ const Panel: FC<NodePanelProps<IfElseNodeType>> = ({
handleUpdateCondition={handleUpdateCondition} handleUpdateCondition={handleUpdateCondition}
handleRemoveCondition={handleRemoveCondition} handleRemoveCondition={handleRemoveCondition}
handleUpdateConditionLogicalOperator={handleUpdateConditionLogicalOperator} handleUpdateConditionLogicalOperator={handleUpdateConditionLogicalOperator}
handleAddSubVariableCondition={handleAddSubVariableCondition}
nodesOutputVars={nodesOutputVars} nodesOutputVars={nodesOutputVars}
availableNodes={availableNodes} availableNodes={availableNodes}
varsIsVarFileAttribute={varsIsVarFileAttribute} varsIsVarFileAttribute={varsIsVarFileAttribute}

View File

@ -33,23 +33,15 @@ export enum ComparisonOperator {
allOf = 'all of', allOf = 'all of',
} }
export type SubVariableCondition = {
id: string
path: string
type: VarType
comparison_operator?: ComparisonOperator
value: string
numberVarType?: NumberVarType
}
export type Condition = { export type Condition = {
id: string id: string
varType: VarType varType: VarType
variable_selector: ValueSelector variable_selector?: ValueSelector
key?: string // sub variable key
comparison_operator?: ComparisonOperator comparison_operator?: ComparisonOperator
value: string value: string
numberVarType?: NumberVarType numberVarType?: NumberVarType
sub_variable_condition?: SubVariableCondition[] sub_variable_condition?: CaseItem
} }
export type CaseItem = { export type CaseItem = {
@ -66,6 +58,7 @@ export type IfElseNodeType = CommonNodeType & {
} }
export type HandleAddCondition = (caseId: string, valueSelector: ValueSelector, varItem: Var) => void export type HandleAddCondition = (caseId: string, valueSelector: ValueSelector, varItem: Var) => void
export type HandleAddSubVariableCondition = (caseId: string, conditionId: string) => void
export type HandleRemoveCondition = (caseId: string, conditionId: string) => void export type HandleRemoveCondition = (caseId: string, conditionId: string) => void
export type HandleUpdateCondition = (caseId: string, conditionId: string, newCondition: Condition) => void export type HandleUpdateCondition = (caseId: string, conditionId: string, newCondition: Condition) => void
export type HandleUpdateConditionLogicalOperator = (caseId: string, value: LogicalOperator) => void export type HandleUpdateConditionLogicalOperator = (caseId: string, value: LogicalOperator) => void

View File

@ -9,6 +9,7 @@ import { LogicalOperator } from './types'
import type { import type {
CaseItem, CaseItem,
HandleAddCondition, HandleAddCondition,
HandleAddSubVariableCondition,
HandleRemoveCondition, HandleRemoveCondition,
HandleUpdateCondition, HandleUpdateCondition,
HandleUpdateConditionLogicalOperator, HandleUpdateConditionLogicalOperator,
@ -58,7 +59,7 @@ const useConfig = (id: string, payload: IfElseNodeType) => {
const conditions: Record<string, boolean> = {} const conditions: Record<string, boolean> = {}
inputs.cases?.forEach((c) => { inputs.cases?.forEach((c) => {
c.conditions.forEach((condition) => { c.conditions.forEach((condition) => {
conditions[condition.id] = getIsVarFileAttribute(condition.variable_selector) conditions[condition.id] = getIsVarFileAttribute(condition.variable_selector!)
}) })
}) })
return conditions return conditions
@ -165,6 +166,36 @@ const useConfig = (id: string, payload: IfElseNodeType) => {
setInputs(newInputs) setInputs(newInputs)
}, [inputs, setInputs]) }, [inputs, setInputs])
const handleAddSubVariableCondition = useCallback<HandleAddSubVariableCondition>((caseId: string, conditionId: string) => {
const newInputs = produce(inputs, (draft) => {
// debugger
const condition = draft.cases?.find(item => item.case_id === caseId)?.conditions.find(item => item.id === conditionId)
if (!condition)
return
if (!condition?.sub_variable_condition) {
condition.sub_variable_condition = {
case_id: uuid4(),
logical_operator: LogicalOperator.and,
conditions: [],
}
}
const subVarCondition = condition.sub_variable_condition
if (subVarCondition) {
if (!subVarCondition.conditions)
subVarCondition.conditions = []
subVarCondition.conditions.push({
id: uuid4(),
key: '',
varType: VarType.string,
comparison_operator: undefined,
value: '',
})
}
})
setInputs(newInputs)
}, [inputs, setInputs])
const handleUpdateConditionLogicalOperator = useCallback<HandleUpdateConditionLogicalOperator>((caseId, value) => { const handleUpdateConditionLogicalOperator = useCallback<HandleUpdateConditionLogicalOperator>((caseId, value) => {
const newInputs = produce(inputs, (draft) => { const newInputs = produce(inputs, (draft) => {
const targetCase = draft.cases?.find(item => item.case_id === caseId) const targetCase = draft.cases?.find(item => item.case_id === caseId)
@ -185,6 +216,7 @@ const useConfig = (id: string, payload: IfElseNodeType) => {
handleAddCondition, handleAddCondition,
handleRemoveCondition, handleRemoveCondition,
handleUpdateCondition, handleUpdateCondition,
handleAddSubVariableCondition,
handleUpdateConditionLogicalOperator, handleUpdateConditionLogicalOperator,
nodesOutputVars: availableVars, nodesOutputVars: availableVars,
availableNodes: availableNodesWithParent, availableNodes: availableNodesWithParent,

View File

@ -434,6 +434,7 @@ const translation = {
addCondition: 'Add Condition', addCondition: 'Add Condition',
conditionNotSetup: 'Condition NOT setup', conditionNotSetup: 'Condition NOT setup',
selectVariable: 'Select variable...', selectVariable: 'Select variable...',
addSubVariable: 'Sub Variable',
}, },
variableAssigner: { variableAssigner: {
title: 'Assign variables', title: 'Assign variables',

View File

@ -434,6 +434,7 @@ const translation = {
addCondition: '添加条件', addCondition: '添加条件',
conditionNotSetup: '条件未设置', conditionNotSetup: '条件未设置',
selectVariable: '选择变量', selectVariable: '选择变量',
addSubVariable: '添加子变量',
}, },
variableAssigner: { variableAssigner: {
title: '变量赋值', title: '变量赋值',