mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-20 21:59:17 +08:00
feat: select file types
This commit is contained in:
parent
a05d16375e
commit
0be99ad01c
@ -11,10 +11,11 @@ import Field from './field'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { checkKeys, getNewVarInWorkflow } from '@/utils/var'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import type { InputVar, MoreInfo } from '@/app/components/workflow/types'
|
||||
import type { InputVar, MoreInfo, UploadFileSetting } from '@/app/components/workflow/types'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
import { ChangeType, InputVarType } from '@/app/components/workflow/types'
|
||||
import FileUploadSetting from '@/app/components/workflow/nodes/_base/components/file-upload-setting'
|
||||
|
||||
const TEXT_MAX_LENGTH = 256
|
||||
|
||||
@ -98,7 +99,7 @@ const ConfigModal: FC<IConfigModalProps> = ({
|
||||
if (isStringInput || type === InputVarType.number) {
|
||||
onConfirm(tempPayload, moreInfo)
|
||||
}
|
||||
else {
|
||||
else if (type === InputVarType.select) {
|
||||
if (options?.length === 0) {
|
||||
Toast.notify({ type: 'error', message: t('appDebug.variableConig.errorMsg.atLeastOneOption') })
|
||||
return
|
||||
@ -118,6 +119,9 @@ const ConfigModal: FC<IConfigModalProps> = ({
|
||||
}
|
||||
onConfirm(tempPayload, moreInfo)
|
||||
}
|
||||
else {
|
||||
onConfirm(tempPayload, moreInfo)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
@ -174,6 +178,14 @@ const ConfigModal: FC<IConfigModalProps> = ({
|
||||
</Field>
|
||||
)}
|
||||
|
||||
{[InputVarType.singleFile, InputVarType.multiFiles].includes(type) && (
|
||||
<FileUploadSetting
|
||||
payload={tempPayload as UploadFileSetting}
|
||||
onChange={(p: UploadFileSetting) => setTempPayload(p as InputVar)}
|
||||
isMultiple={type === InputVarType.multiFiles}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Field title={t('appDebug.variableConig.required')}>
|
||||
<Switch defaultValue={tempPayload.required} onChange={handlePayloadChange('required')} />
|
||||
</Field>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { ValueSelector } from '../../workflow/types'
|
||||
import { SupportUploadFileTypes, type ValueSelector } from '../../workflow/types'
|
||||
|
||||
export const CONTEXT_PLACEHOLDER_TEXT = '{{#context#}}'
|
||||
export const HISTORY_PLACEHOLDER_TEXT = '{{#histories#}}'
|
||||
@ -49,3 +49,10 @@ export const getInputVars = (text: string): ValueSelector[] => {
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
export const FILE_EXTS = {
|
||||
[SupportUploadFileTypes.image]: ['JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'],
|
||||
[SupportUploadFileTypes.document]: ['TXT', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'],
|
||||
[SupportUploadFileTypes.audio]: ['MP3', 'M4A', 'WAV', 'WEBM', 'AMR'],
|
||||
[SupportUploadFileTypes.video]: ['MP4', 'MOV', 'MPEG', 'MPGA'],
|
||||
}
|
||||
|
@ -0,0 +1,48 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type { SupportUploadFileTypes } from '../../../types'
|
||||
import cn from '@/utils/classnames'
|
||||
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
|
||||
|
||||
type Props = {
|
||||
type: SupportUploadFileTypes.image | SupportUploadFileTypes.document | SupportUploadFileTypes.audio | SupportUploadFileTypes.video
|
||||
selected: boolean
|
||||
onSelect: (type: SupportUploadFileTypes) => void
|
||||
}
|
||||
|
||||
const FileTypeItem: FC<Props> = ({
|
||||
type,
|
||||
selected,
|
||||
onSelect,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const handleOnSelect = useCallback(() => {
|
||||
if (!selected)
|
||||
onSelect(type)
|
||||
}, [selected, onSelect, type])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'py-2 px-3 rounded-lg bg-components-option-card-option-bg border border-components-option-card-option-border',
|
||||
selected && 'border-[1.5px] bg-components-option-card-option-selected-bg border-components-option-card-option-selected-border',
|
||||
!selected && 'cursor-pointer hover:bg-components-option-card-option-bg-hover hover:border-components-option-card-option-border-hover',
|
||||
)}
|
||||
onClick={handleOnSelect}
|
||||
>
|
||||
<div className='flex items-center'>
|
||||
{/* TODO: Wait File type icon */}
|
||||
<span className='shrink-0 w-4 h-4 bg-[#00B2EA]'></span>
|
||||
<div className='ml-2'>
|
||||
<div className='text-text-primary system-sm-medium'>{t(`appDebug.variableConig.file.${type}.name`)}</div>
|
||||
<div className='mt-1 text-text-tertiary system-2xs-regular-uppercase'>{FILE_EXTS[type].join(', ')}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(FileTypeItem)
|
@ -0,0 +1,98 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import produce from 'immer'
|
||||
import type { UploadFileSetting } from '../../../types'
|
||||
import { SupportUploadFileTypes } from '../../../types'
|
||||
import OptionCard from './option-card'
|
||||
import FileTypeItem from './file-type-item'
|
||||
import Field from '@/app/components/app/configuration/config-var/config-modal/field'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
|
||||
type Props = {
|
||||
payload: UploadFileSetting
|
||||
isMultiple: boolean
|
||||
onChange: (payload: UploadFileSetting) => void
|
||||
}
|
||||
|
||||
const FileUploadSetting: FC<Props> = ({
|
||||
payload,
|
||||
isMultiple,
|
||||
onChange,
|
||||
}) => {
|
||||
const {
|
||||
uploadMethod,
|
||||
maxUploadNumLimit,
|
||||
supportFileTypes,
|
||||
customFileTypes,
|
||||
} = payload
|
||||
|
||||
const handleSupportFileTypeChange = useCallback((type: SupportUploadFileTypes) => {
|
||||
const newPayload = produce(payload, (draft) => {
|
||||
draft.supportFileTypes = type
|
||||
})
|
||||
onChange(newPayload)
|
||||
}, [onChange, payload])
|
||||
|
||||
const handleUploadMethodChange = useCallback((method: TransferMethod) => {
|
||||
return () => {
|
||||
const newPayload = produce(payload, (draft) => {
|
||||
draft.uploadMethod = method
|
||||
})
|
||||
onChange(newPayload)
|
||||
}
|
||||
}, [onChange, payload])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Field
|
||||
title='SupportFile Types'
|
||||
>
|
||||
<div className='space-y-1'>
|
||||
{
|
||||
[SupportUploadFileTypes.image, SupportUploadFileTypes.document, SupportUploadFileTypes.audio, SupportUploadFileTypes.video].map((type: SupportUploadFileTypes) => (
|
||||
<FileTypeItem
|
||||
key={type}
|
||||
type={type as SupportUploadFileTypes.image | SupportUploadFileTypes.document | SupportUploadFileTypes.audio | SupportUploadFileTypes.video}
|
||||
selected={supportFileTypes === type}
|
||||
onSelect={handleSupportFileTypeChange}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</Field>
|
||||
<Field
|
||||
title='Upload File Types'
|
||||
>
|
||||
<div className='grid grid-cols-3 gap-2'>
|
||||
<OptionCard
|
||||
title='Local Upload'
|
||||
selected={uploadMethod === TransferMethod.local_file}
|
||||
onSelect={handleUploadMethodChange(TransferMethod.local_file)}
|
||||
/>
|
||||
<OptionCard
|
||||
title="URL"
|
||||
selected={uploadMethod === TransferMethod.remote_url}
|
||||
onSelect={handleUploadMethodChange(TransferMethod.remote_url)}
|
||||
/>
|
||||
<OptionCard
|
||||
title="Both"
|
||||
selected={uploadMethod === TransferMethod.all}
|
||||
onSelect={handleUploadMethodChange(TransferMethod.all)}
|
||||
/>
|
||||
</div>
|
||||
</Field>
|
||||
{isMultiple && (
|
||||
<Field
|
||||
title='Max number of uploads'
|
||||
>
|
||||
<div>
|
||||
<span>Max number of uploads</span>
|
||||
</div>
|
||||
</Field>
|
||||
)}
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(FileUploadSetting)
|
@ -144,7 +144,7 @@ export type InputVar = {
|
||||
hint?: string
|
||||
options?: string[]
|
||||
value_selector?: ValueSelector
|
||||
}
|
||||
} & Partial<UploadFileSetting>
|
||||
|
||||
export type ModelConfig = {
|
||||
provider: string
|
||||
@ -329,3 +329,18 @@ export type MoreInfo = {
|
||||
export type ToolWithProvider = Collection & {
|
||||
tools: Tool[]
|
||||
}
|
||||
|
||||
export enum SupportUploadFileTypes {
|
||||
image = 'image',
|
||||
document = 'document',
|
||||
audio = 'audio',
|
||||
video = 'video',
|
||||
custom = 'custom',
|
||||
}
|
||||
|
||||
export type UploadFileSetting = {
|
||||
uploadMethod: TransferMethod
|
||||
maxUploadNumLimit?: number // multiple files upload limit
|
||||
supportFileTypes: SupportUploadFileTypes
|
||||
customFileTypes?: string[]
|
||||
}
|
||||
|
@ -324,6 +324,20 @@ const translation = {
|
||||
'inputPlaceholder': 'Please input',
|
||||
'content': 'Content',
|
||||
'required': 'Required',
|
||||
'file': {
|
||||
image: {
|
||||
name: 'Image',
|
||||
},
|
||||
audio: {
|
||||
name: 'Audio',
|
||||
},
|
||||
document: {
|
||||
name: 'Document',
|
||||
},
|
||||
video: {
|
||||
name: 'Video',
|
||||
},
|
||||
},
|
||||
'errorMsg': {
|
||||
varNameRequired: 'Variable name is required',
|
||||
labelNameRequired: 'Label name is required',
|
||||
|
@ -320,6 +320,20 @@ const translation = {
|
||||
'inputPlaceholder': '请输入',
|
||||
'labelName': '显示名称',
|
||||
'required': '必填',
|
||||
'file': {
|
||||
image: {
|
||||
name: '图片',
|
||||
},
|
||||
audio: {
|
||||
name: '音频',
|
||||
},
|
||||
document: {
|
||||
name: '文档',
|
||||
},
|
||||
video: {
|
||||
name: '视频',
|
||||
},
|
||||
},
|
||||
'content': '内容',
|
||||
'errorMsg': {
|
||||
varNameRequired: '变量名称必填',
|
||||
|
Loading…
x
Reference in New Issue
Block a user