mirror of https://github.com/langgenius/dify.git
feat: output var
This commit is contained in:
parent
b4437ccd2b
commit
8b8fdb48bb
|
|
@ -48,7 +48,7 @@ const Page: FC = () => {
|
|||
* 2 directAnswer 3: llm 5: questionClassifier
|
||||
* 7 Code, 8 TemplateTransform
|
||||
*/
|
||||
selectedNodeId='3'
|
||||
selectedNodeId='7'
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import produce from 'immer'
|
||||
import type { OutputVar } from '../../../code/types'
|
||||
import VarTypePicker from './var-type-picker'
|
||||
import { Trash03 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
|
||||
type Props = {
|
||||
readonly: boolean
|
||||
list: OutputVar[]
|
||||
onChange: (list: OutputVar[]) => void
|
||||
}
|
||||
|
||||
const OutputVarList: FC<Props> = ({
|
||||
readonly,
|
||||
list,
|
||||
onChange,
|
||||
}) => {
|
||||
const handleVarNameChange = useCallback((index: number) => {
|
||||
return (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newList = produce(list, (draft) => {
|
||||
draft[index].variable = e.target.value
|
||||
})
|
||||
onChange(newList)
|
||||
}
|
||||
}, [list, onChange])
|
||||
|
||||
const handleVarChange = useCallback((index: number) => {
|
||||
return (value: string) => {
|
||||
const newList = produce(list, (draft) => {
|
||||
draft[index].variable_type = value
|
||||
})
|
||||
onChange(newList)
|
||||
}
|
||||
}, [list, onChange])
|
||||
|
||||
const handleVarRemove = useCallback((index: number) => {
|
||||
return () => {
|
||||
const newList = produce(list, (draft) => {
|
||||
draft.splice(index, 1)
|
||||
})
|
||||
onChange(newList)
|
||||
}
|
||||
}, [list, onChange])
|
||||
|
||||
return (
|
||||
<div className='space-y-2'>
|
||||
{list.map((item, index) => (
|
||||
<div className='flex items-center space-x-1' key={index}>
|
||||
<input
|
||||
readOnly={readonly}
|
||||
value={item.variable}
|
||||
onChange={handleVarNameChange(index)}
|
||||
className='w-0 grow h-8 leading-8 px-2.5 rounded-lg border-0 bg-gray-100 text-gray-900 text-[13px] placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200'
|
||||
type='text' />
|
||||
<VarTypePicker
|
||||
readonly={readonly}
|
||||
value={item.variable_type}
|
||||
onChange={handleVarChange(index)}
|
||||
/>
|
||||
<div
|
||||
className='p-2 rounded-lg bg-gray-100 hover:bg-gray-200 cursor-pointer'
|
||||
onClick={handleVarRemove(index)}
|
||||
>
|
||||
<Trash03 className='w-4 h-4 text-gray-500' />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(OutputVarList)
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import cn from 'classnames'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import { Check } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
|
||||
type Props = {
|
||||
className?: string
|
||||
readonly: boolean
|
||||
value: string
|
||||
onChange: (value: string) => void
|
||||
}
|
||||
|
||||
const TYPES = ['string', 'number']
|
||||
const VarReferencePicker: FC<Props> = ({
|
||||
readonly,
|
||||
className,
|
||||
value,
|
||||
onChange,
|
||||
}) => {
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const handleChange = useCallback((type: string) => {
|
||||
return () => {
|
||||
setOpen(false)
|
||||
onChange(type)
|
||||
}
|
||||
}, [onChange])
|
||||
|
||||
return (
|
||||
<div className={cn(className, !readonly && 'cursor-pointer select-none')}>
|
||||
<PortalToFollowElem
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
placement='bottom-start'
|
||||
offset={4}
|
||||
>
|
||||
<PortalToFollowElemTrigger onClick={() => setOpen(!open)} className='w-[120px] cursor-pointer'>
|
||||
<div className='flex items-center h-8 justify-between px-2.5 rounded-lg border-0 bg-gray-100 text-gray-900 text-[13px]'>
|
||||
<div className='capitalize'>{value}</div>
|
||||
<ChevronDown className='w-3.5 h-3.5 text-gray-700' />
|
||||
</div>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent style={{
|
||||
zIndex: 100,
|
||||
}}>
|
||||
<div className='w-[120px] p-1 bg-white rounded-lg shadow-sm'>
|
||||
{TYPES.map(type => (
|
||||
<div
|
||||
key={type}
|
||||
className='flex items-center h-[30px] justify-between pl-3 pr-2 rounded-lg hover:bg-gray-100 text-gray-900 text-[13px] cursor-pointer'
|
||||
onClick={handleChange(type)}
|
||||
>
|
||||
<div className='capitalize'>{type}</div>
|
||||
{type === value && <Check className='w-4 h-4 text-primary-600' />}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(VarReferencePicker)
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import { useCallback } from 'react'
|
||||
import produce from 'immer'
|
||||
import type { OutputVar } from '../../code/types'
|
||||
type Params<T> = {
|
||||
inputs: T
|
||||
setInputs: (newInputs: T) => void
|
||||
varKey?: string
|
||||
}
|
||||
function useOutputVarList<T>({
|
||||
inputs,
|
||||
setInputs,
|
||||
varKey = 'outputs',
|
||||
}: Params<T>) {
|
||||
const handleVarListChange = useCallback((newList: OutputVar[]) => {
|
||||
const newInputs = produce(inputs, (draft: any) => {
|
||||
draft[varKey] = newList
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}, [inputs, setInputs, varKey])
|
||||
|
||||
const handleAddVariable = useCallback(() => {
|
||||
const newInputs = produce(inputs, (draft: any) => {
|
||||
draft[varKey].push({
|
||||
variable: '',
|
||||
variable_type: 'string',
|
||||
})
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}, [inputs, setInputs, varKey])
|
||||
|
||||
return {
|
||||
handleVarListChange,
|
||||
handleAddVariable,
|
||||
}
|
||||
}
|
||||
|
||||
export default useOutputVarList
|
||||
|
|
@ -4,6 +4,7 @@ import useConfig from './use-config'
|
|||
import { mockData } from './mock'
|
||||
import { CodeLanguage } from './types'
|
||||
import VarList from '@/app/components/workflow/nodes/_base/components/variable/var-list'
|
||||
import OutputVarList from '@/app/components/workflow/nodes/_base/components/variable/output-var-list'
|
||||
import AddButton from '@/app/components/base/button/add-button'
|
||||
import Field from '@/app/components/workflow/nodes/_base/components/field'
|
||||
import Split from '@/app/components/workflow/nodes/_base/components/split'
|
||||
|
|
@ -31,6 +32,8 @@ const Panel: FC = () => {
|
|||
handleAddVariable,
|
||||
handleCodeChange,
|
||||
handleCodeLanguageChange,
|
||||
handleOutputVarListChange,
|
||||
handleAddOutputVariable,
|
||||
} = useConfig(mockData)
|
||||
return (
|
||||
<div className='mt-2'>
|
||||
|
|
@ -62,7 +65,18 @@ const Panel: FC = () => {
|
|||
</div>
|
||||
<Split />
|
||||
<div className='px-4 pt-4 pb-2'>
|
||||
output var
|
||||
<Field
|
||||
title={t(`${i18nPrefix}.outputVars`)}
|
||||
operations={
|
||||
<AddButton onClick={handleAddOutputVariable} />
|
||||
}
|
||||
>
|
||||
<OutputVarList
|
||||
readonly={readOnly}
|
||||
list={inputs.outputs}
|
||||
onChange={handleOutputVarListChange}
|
||||
/>
|
||||
</Field>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,12 +5,14 @@ export enum CodeLanguage {
|
|||
javascript = 'javascript',
|
||||
}
|
||||
|
||||
export type OutputVar = {
|
||||
variable: string
|
||||
variable_type: string
|
||||
}
|
||||
|
||||
export type CodeNodeType = CommonNodeType & {
|
||||
variables: Variable[]
|
||||
code_language: CodeLanguage
|
||||
code: string
|
||||
outputs: {
|
||||
variable: string
|
||||
variable_type: string
|
||||
}[]
|
||||
outputs: OutputVar[]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useCallback, useState } from 'react'
|
||||
import useVarList from '../_base/hooks/use-var-list'
|
||||
import useOutputVarList from '../_base/hooks/use-output-var-list'
|
||||
import type { CodeLanguage, CodeNodeType } from './types'
|
||||
|
||||
const useConfig = (initInputs: CodeNodeType) => {
|
||||
|
|
@ -17,12 +18,19 @@ const useConfig = (initInputs: CodeNodeType) => {
|
|||
setInputs(prev => ({ ...prev, code_language: codeLanguage }))
|
||||
}, [setInputs])
|
||||
|
||||
const { handleVarListChange: handleOutputVarListChange, handleAddVariable: handleAddOutputVariable } = useOutputVarList<CodeNodeType>({
|
||||
inputs,
|
||||
setInputs,
|
||||
})
|
||||
|
||||
return {
|
||||
inputs,
|
||||
handleVarListChange,
|
||||
handleAddVariable,
|
||||
handleCodeChange,
|
||||
handleCodeLanguageChange,
|
||||
handleOutputVarListChange,
|
||||
handleAddOutputVariable,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ const translation = {
|
|||
},
|
||||
code: {
|
||||
inputVars: 'Input Variables',
|
||||
outputVars: 'Output Variables',
|
||||
},
|
||||
templateTransform: {
|
||||
inputVars: 'Input Variables',
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ const translation = {
|
|||
},
|
||||
code: {
|
||||
inputVars: '输入变量',
|
||||
outputVars: '输出变量',
|
||||
},
|
||||
templateTransform: {
|
||||
inputVars: '输入变量',
|
||||
|
|
|
|||
Loading…
Reference in New Issue