mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-19 19:59:10 +08:00
feat: support filter variable var data sync
This commit is contained in:
parent
0e2f78b3a6
commit
976efd93a1
@ -1,31 +1,106 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ConditionOperator from '../../if-else/components/condition-list/condition-operator'
|
||||
import { VarType } from '../../../types'
|
||||
import type { Condition } from '../types'
|
||||
import { ComparisonOperator } from '../../if-else/types'
|
||||
import { comparisonOperatorNotRequireValue, getOperators } from '../../if-else/utils'
|
||||
import SubVariablePicker from './sub-variable-picker'
|
||||
import { SimpleSelect as Select } from '@/app/components/base/select'
|
||||
import Input from '@/app/components/base/input'
|
||||
import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '@/app/components/workflow/nodes/if-else/default'
|
||||
import { SimpleSelect as Select } from '@/app/components/base/select'
|
||||
|
||||
const optionNameI18NPrefix = 'workflow.nodes.ifElse.optionName'
|
||||
type Props = {
|
||||
condition: Condition
|
||||
onChange: (condition: Condition) => void
|
||||
varType: VarType
|
||||
hasSubVariable: boolean
|
||||
}
|
||||
|
||||
const FilterCondition: FC<Props> = ({
|
||||
condition,
|
||||
varType,
|
||||
onChange,
|
||||
hasSubVariable,
|
||||
}) => {
|
||||
const [input, setInput] = React.useState('')
|
||||
const { t } = useTranslation()
|
||||
const isSelect = [ComparisonOperator.in, ComparisonOperator.notIn, ComparisonOperator.allOf].includes(condition.comparison_operator)
|
||||
const selectOptions = useMemo(() => {
|
||||
if (isSelect) {
|
||||
if (condition.key === 'type' || condition.comparison_operator === ComparisonOperator.allOf) {
|
||||
return FILE_TYPE_OPTIONS.map(item => ({
|
||||
name: t(`${optionNameI18NPrefix}.${item.i18nKey}`),
|
||||
value: item.value,
|
||||
}))
|
||||
}
|
||||
if (condition.key === 'transfer_method') {
|
||||
return TRANSFER_METHOD.map(item => ({
|
||||
name: t(`${optionNameI18NPrefix}.${item.i18nKey}`),
|
||||
value: item.value,
|
||||
}))
|
||||
}
|
||||
return []
|
||||
}
|
||||
return []
|
||||
}, [condition.comparison_operator, condition.key, isSelect, t])
|
||||
const handleChange = useCallback((key: string) => {
|
||||
return (value: any) => {
|
||||
onChange({
|
||||
...condition,
|
||||
[key]: value,
|
||||
})
|
||||
}
|
||||
}, [condition, onChange])
|
||||
|
||||
const handleSubVariableChange = useCallback((value: string) => {
|
||||
onChange({
|
||||
key: value,
|
||||
comparison_operator: getOperators(varType, { key: value })[0],
|
||||
value: '',
|
||||
})
|
||||
}, [onChange, varType])
|
||||
|
||||
return (
|
||||
<div>
|
||||
{hasSubVariable && <SubVariablePicker className="mb-2" />}
|
||||
<div className='flex space-x-1'>
|
||||
<Select
|
||||
wrapperClassName='shrink-0 h-8'
|
||||
className='!text-[13px]'
|
||||
items={[
|
||||
{ value: '1', name: 'include', type: '' },
|
||||
]}
|
||||
onSelect={() => { }}
|
||||
{hasSubVariable && (
|
||||
<SubVariablePicker
|
||||
className="mb-2"
|
||||
value={condition.key}
|
||||
onChange={handleSubVariableChange}
|
||||
/>
|
||||
<Input className='grow' value={input} onChange={e => setInput(e.target.value)} />
|
||||
)}
|
||||
<div className='flex space-x-1'>
|
||||
<ConditionOperator
|
||||
varType={varType}
|
||||
value={condition.comparison_operator}
|
||||
onSelect={handleChange('comparison_operator')}
|
||||
file={hasSubVariable ? { key: condition.key } : undefined}
|
||||
/>
|
||||
{!comparisonOperatorNotRequireValue(condition.comparison_operator) && (
|
||||
<>
|
||||
{isSelect && (
|
||||
<Select
|
||||
items={selectOptions}
|
||||
defaultValue={condition.value}
|
||||
onSelect={item => handleChange('value')(item.value)}
|
||||
className='!text-[13px]'
|
||||
wrapperClassName='grow'
|
||||
placeholder='Select value'
|
||||
/>
|
||||
)}
|
||||
{!isSelect && (
|
||||
<Input
|
||||
type={((hasSubVariable && condition.key === 'size') || (!hasSubVariable && varType === VarType.number)) ? 'number' : 'text'}
|
||||
className='grow'
|
||||
value={condition.value}
|
||||
onChange={e => handleChange('value')(e.target.value)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@ -1,17 +1,28 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import { SUB_VARIABLES } from '../../if-else/default'
|
||||
import type { Item } from '@/app/components/base/select'
|
||||
import { SimpleSelect as Select } from '@/app/components/base/select'
|
||||
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
value: string
|
||||
onChange: (value: string) => void
|
||||
className?: string
|
||||
}
|
||||
|
||||
const SubVariablePicker: FC<Props> = ({
|
||||
value,
|
||||
onChange,
|
||||
className,
|
||||
}) => {
|
||||
const subVarOptions = SUB_VARIABLES.map(item => ({
|
||||
value: item,
|
||||
name: item,
|
||||
}))
|
||||
|
||||
const renderOption = ({ item }: { item: Record<string, any> }) => {
|
||||
return (
|
||||
<div className='flex items-center h-6 justify-between'>
|
||||
@ -23,15 +34,17 @@ const SubVariablePicker: FC<Props> = ({
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const handleChange = useCallback(({ value }: Item) => {
|
||||
onChange(value as string)
|
||||
}, [onChange])
|
||||
|
||||
return (
|
||||
<div className={cn(className)}>
|
||||
<Select
|
||||
items={[
|
||||
{ value: '1', name: 'name', type: 'string' },
|
||||
{ value: '2', name: 'age', type: 'number' },
|
||||
]}
|
||||
defaultValue={'1'}
|
||||
onSelect={() => { }}
|
||||
items={subVarOptions}
|
||||
defaultValue={value}
|
||||
onSelect={handleChange}
|
||||
className='!text-[13px]'
|
||||
placeholder='Select sub variable key'
|
||||
optionClassName='pl-4 pr-5 py-0'
|
||||
|
@ -25,9 +25,11 @@ const Panel: FC<NodePanelProps<ListFilterNodeType>> = ({
|
||||
const {
|
||||
readOnly,
|
||||
inputs,
|
||||
itemVarType,
|
||||
hasSubVariable,
|
||||
handleVarChanges,
|
||||
filterVar,
|
||||
handleFilterChange,
|
||||
handleLimitChange,
|
||||
handleOrderByEnabledChange,
|
||||
handleOrderByTypeChange,
|
||||
@ -53,7 +55,12 @@ const Panel: FC<NodePanelProps<ListFilterNodeType>> = ({
|
||||
title={t(`${i18nPrefix}.filterCondition`)}
|
||||
isSubTitle
|
||||
>
|
||||
<FilterCondition hasSubVariable={hasSubVariable} />
|
||||
<FilterCondition
|
||||
condition={inputs.filter_by[0]}
|
||||
onChange={handleFilterChange}
|
||||
varType={itemVarType}
|
||||
hasSubVariable={hasSubVariable}
|
||||
/>
|
||||
</Field>
|
||||
<Split />
|
||||
<Field
|
||||
@ -71,7 +78,12 @@ const Panel: FC<NodePanelProps<ListFilterNodeType>> = ({
|
||||
? (
|
||||
<div className='flex items-center justify-between'>
|
||||
{hasSubVariable && (
|
||||
<div className='grow mr-2'><SubVariablePicker /></div>
|
||||
<div className='grow mr-2'>
|
||||
<SubVariablePicker
|
||||
value={inputs.order_by.key as string}
|
||||
onChange={() => { }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className={!hasSubVariable ? 'w-full grid grid-cols-2 gap-1' : 'shrink-0 flex space-x-1'}>
|
||||
<OptionCard
|
||||
|
@ -1,3 +1,4 @@
|
||||
import type { ComparisonOperator } from '../if-else/types'
|
||||
import type { CommonNodeType, ValueSelector } from '@/app/components/workflow/types'
|
||||
|
||||
export enum OrderBy {
|
||||
@ -12,8 +13,8 @@ export type Limit = {
|
||||
|
||||
export type Condition = {
|
||||
key: string
|
||||
comparison_operator: string
|
||||
value: string
|
||||
comparison_operator: ComparisonOperator
|
||||
value: string | number
|
||||
}
|
||||
|
||||
export type ListFilterNodeType = CommonNodeType & {
|
||||
|
@ -3,7 +3,8 @@ import produce from 'immer'
|
||||
import { useStoreApi } from 'reactflow'
|
||||
import type { ValueSelector, Var } from '../../types'
|
||||
import { VarType } from '../../types'
|
||||
import type { Limit, ListFilterNodeType, OrderBy } from './types'
|
||||
import { getOperators } from '../if-else/utils'
|
||||
import type { Condition, Limit, ListFilterNodeType, OrderBy } from './types'
|
||||
import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
|
||||
import {
|
||||
useIsChatMode,
|
||||
@ -32,27 +33,65 @@ const useConfig = (id: string, payload: ListFilterNodeType) => {
|
||||
const { inputs, setInputs } = useNodeCrud<ListFilterNodeType>(id, payload)
|
||||
|
||||
const { getCurrentVariableType } = useWorkflowVariables()
|
||||
const getType = useCallback((variable?: ValueSelector) => {
|
||||
const varType = getCurrentVariableType({
|
||||
parentNode: iterationNode,
|
||||
valueSelector: inputs.variable || [],
|
||||
valueSelector: variable || inputs.variable || [],
|
||||
availableNodes,
|
||||
isChatMode,
|
||||
isConstant: false,
|
||||
})
|
||||
let itemVarType = VarType.string
|
||||
switch (varType) {
|
||||
case VarType.arrayNumber:
|
||||
itemVarType = VarType.number
|
||||
break
|
||||
case VarType.arrayString:
|
||||
itemVarType = VarType.string
|
||||
break
|
||||
case VarType.arrayFile:
|
||||
itemVarType = VarType.file
|
||||
break
|
||||
case VarType.arrayObject:
|
||||
itemVarType = VarType.object
|
||||
break
|
||||
default:
|
||||
itemVarType = varType
|
||||
}
|
||||
return { varType, itemVarType }
|
||||
}, [availableNodes, getCurrentVariableType, inputs.variable, isChatMode, iterationNode])
|
||||
|
||||
const hasSubVariable = [VarType.arrayFile, VarType.arrayObject].includes(varType)
|
||||
const { varType, itemVarType } = getType()
|
||||
|
||||
const hasSubVariable = [VarType.arrayFile].includes(varType)
|
||||
|
||||
const handleVarChanges = useCallback((variable: ValueSelector | string) => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
draft.variable = variable as ValueSelector
|
||||
const { varType, itemVarType } = getType(draft.variable)
|
||||
const isFileArray = varType === VarType.arrayFile
|
||||
|
||||
draft.filter_by = [{
|
||||
key: isFileArray ? 'name' : '',
|
||||
comparison_operator: getOperators(itemVarType, isFileArray ? { key: 'name' } : undefined)[0],
|
||||
value: '',
|
||||
}]
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}, [getType, inputs, setInputs])
|
||||
|
||||
const filterVar = useCallback((varPayload: Var) => {
|
||||
// Don't know the item struct of VarType.arrayObject, so not support it
|
||||
return [VarType.arrayNumber, VarType.arrayString, VarType.arrayFile].includes(varPayload.type)
|
||||
}, [])
|
||||
|
||||
const handleFilterChange = useCallback((condition: Condition) => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
draft.filter_by[0] = condition
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}, [inputs, setInputs])
|
||||
|
||||
const filterVar = useCallback((varPayload: Var) => {
|
||||
return [VarType.arrayNumber, VarType.arrayString, VarType.arrayFile, VarType.arrayObject].includes(varPayload.type)
|
||||
}, [])
|
||||
|
||||
const handleLimitChange = useCallback((limit: Limit) => {
|
||||
const newInputs = produce(inputs, (draft) => {
|
||||
draft.limit = limit
|
||||
@ -80,8 +119,11 @@ const useConfig = (id: string, payload: ListFilterNodeType) => {
|
||||
readOnly,
|
||||
inputs,
|
||||
filterVar,
|
||||
varType,
|
||||
itemVarType,
|
||||
hasSubVariable,
|
||||
handleVarChanges,
|
||||
handleFilterChange,
|
||||
handleLimitChange,
|
||||
handleOrderByEnabledChange,
|
||||
handleOrderByTypeChange,
|
||||
|
Loading…
x
Reference in New Issue
Block a user