file-upload-setting.tsx 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useCallback } from 'react'
  4. import useSWR from 'swr'
  5. import produce from 'immer'
  6. import { useTranslation } from 'react-i18next'
  7. import type { UploadFileSetting } from '../../../types'
  8. import { SupportUploadFileTypes } from '../../../types'
  9. import OptionCard from './option-card'
  10. import FileTypeItem from './file-type-item'
  11. import InputNumberWithSlider from './input-number-with-slider'
  12. import Field from '@/app/components/app/configuration/config-var/config-modal/field'
  13. import { TransferMethod } from '@/types/app'
  14. import { fetchFileUploadConfig } from '@/service/common'
  15. import { useFileSizeLimit } from '@/app/components/base/file-uploader/hooks'
  16. import { formatFileSize } from '@/utils/format'
  17. type Props = {
  18. payload: UploadFileSetting
  19. isMultiple: boolean
  20. inFeaturePanel?: boolean
  21. hideSupportFileType?: boolean
  22. onChange: (payload: UploadFileSetting) => void
  23. }
  24. const FileUploadSetting: FC<Props> = ({
  25. payload,
  26. isMultiple,
  27. inFeaturePanel = false,
  28. hideSupportFileType = false,
  29. onChange,
  30. }) => {
  31. const { t } = useTranslation()
  32. const {
  33. allowed_file_upload_methods,
  34. max_length,
  35. allowed_file_types,
  36. allowed_file_extensions,
  37. } = payload
  38. const { data: fileUploadConfigResponse } = useSWR({ url: '/files/upload' }, fetchFileUploadConfig)
  39. const { imgSizeLimit, docSizeLimit, audioSizeLimit, videoSizeLimit } = useFileSizeLimit(fileUploadConfigResponse)
  40. const handleSupportFileTypeChange = useCallback((type: SupportUploadFileTypes) => {
  41. const newPayload = produce(payload, (draft) => {
  42. if (type === SupportUploadFileTypes.custom) {
  43. if (!draft.allowed_file_types.includes(SupportUploadFileTypes.custom))
  44. draft.allowed_file_types = [SupportUploadFileTypes.custom]
  45. else
  46. draft.allowed_file_types = draft.allowed_file_types.filter(v => v !== type)
  47. }
  48. else {
  49. draft.allowed_file_types = draft.allowed_file_types.filter(v => v !== SupportUploadFileTypes.custom)
  50. if (draft.allowed_file_types.includes(type))
  51. draft.allowed_file_types = draft.allowed_file_types.filter(v => v !== type)
  52. else
  53. draft.allowed_file_types.push(type)
  54. }
  55. })
  56. onChange(newPayload)
  57. }, [onChange, payload])
  58. const handleUploadMethodChange = useCallback((method: TransferMethod) => {
  59. return () => {
  60. const newPayload = produce(payload, (draft) => {
  61. if (method === TransferMethod.all)
  62. draft.allowed_file_upload_methods = [TransferMethod.local_file, TransferMethod.remote_url]
  63. else
  64. draft.allowed_file_upload_methods = [method]
  65. })
  66. onChange(newPayload)
  67. }
  68. }, [onChange, payload])
  69. const handleCustomFileTypesChange = useCallback((customFileTypes: string[]) => {
  70. const newPayload = produce(payload, (draft) => {
  71. draft.allowed_file_extensions = customFileTypes.map((v) => {
  72. if (v.startsWith('.')) // Not start with dot
  73. return v.slice(1)
  74. return v
  75. })
  76. })
  77. onChange(newPayload)
  78. }, [onChange, payload])
  79. const handleMaxUploadNumLimitChange = useCallback((value: number) => {
  80. const newPayload = produce(payload, (draft) => {
  81. draft.max_length = value
  82. })
  83. onChange(newPayload)
  84. }, [onChange, payload])
  85. return (
  86. <div>
  87. {!inFeaturePanel && (
  88. <Field
  89. title={t('appDebug.variableConfig.file.supportFileTypes')}
  90. >
  91. <div className='space-y-1'>
  92. {
  93. [SupportUploadFileTypes.document, SupportUploadFileTypes.image, SupportUploadFileTypes.audio, SupportUploadFileTypes.video].map((type: SupportUploadFileTypes) => (
  94. <FileTypeItem
  95. key={type}
  96. type={type as SupportUploadFileTypes.image | SupportUploadFileTypes.document | SupportUploadFileTypes.audio | SupportUploadFileTypes.video}
  97. selected={allowed_file_types.includes(type)}
  98. onToggle={handleSupportFileTypeChange}
  99. />
  100. ))
  101. }
  102. <FileTypeItem
  103. type={SupportUploadFileTypes.custom}
  104. selected={allowed_file_types.includes(SupportUploadFileTypes.custom)}
  105. onToggle={handleSupportFileTypeChange}
  106. customFileTypes={allowed_file_extensions?.map(item => `.${item}`)}
  107. onCustomFileTypesChange={handleCustomFileTypesChange}
  108. />
  109. </div>
  110. </Field>
  111. )}
  112. <Field
  113. title={t('appDebug.variableConfig.uploadFileTypes')}
  114. className='mt-4'
  115. >
  116. <div className='grid grid-cols-3 gap-2'>
  117. <OptionCard
  118. title={t('appDebug.variableConfig.localUpload')}
  119. selected={allowed_file_upload_methods.length === 1 && allowed_file_upload_methods.includes(TransferMethod.local_file)}
  120. onSelect={handleUploadMethodChange(TransferMethod.local_file)}
  121. />
  122. <OptionCard
  123. title="URL"
  124. selected={allowed_file_upload_methods.length === 1 && allowed_file_upload_methods.includes(TransferMethod.remote_url)}
  125. onSelect={handleUploadMethodChange(TransferMethod.remote_url)}
  126. />
  127. <OptionCard
  128. title={t('appDebug.variableConfig.both')}
  129. selected={allowed_file_upload_methods.includes(TransferMethod.local_file) && allowed_file_upload_methods.includes(TransferMethod.remote_url)}
  130. onSelect={handleUploadMethodChange(TransferMethod.all)}
  131. />
  132. </div>
  133. </Field>
  134. {isMultiple && (
  135. <Field
  136. className='mt-4'
  137. title={t('appDebug.variableConfig.maxNumberOfUploads')!}
  138. >
  139. <div>
  140. <div className='mb-1.5 text-text-tertiary body-xs-regular'>{t('appDebug.variableConfig.maxNumberTip', {
  141. imgLimit: formatFileSize(imgSizeLimit),
  142. docLimit: formatFileSize(docSizeLimit),
  143. audioLimit: formatFileSize(audioSizeLimit),
  144. videoLimit: formatFileSize(videoSizeLimit),
  145. })}</div>
  146. <InputNumberWithSlider
  147. value={max_length}
  148. min={1}
  149. max={10}
  150. onChange={handleMaxUploadNumLimitChange}
  151. />
  152. </div>
  153. </Field>
  154. )}
  155. {inFeaturePanel && !hideSupportFileType && (
  156. <Field
  157. title={t('appDebug.variableConfig.file.supportFileTypes')}
  158. className='mt-4'
  159. >
  160. <div className='space-y-1'>
  161. {
  162. [SupportUploadFileTypes.document, SupportUploadFileTypes.image, SupportUploadFileTypes.audio, SupportUploadFileTypes.video].map((type: SupportUploadFileTypes) => (
  163. <FileTypeItem
  164. key={type}
  165. type={type as SupportUploadFileTypes.image | SupportUploadFileTypes.document | SupportUploadFileTypes.audio | SupportUploadFileTypes.video}
  166. selected={allowed_file_types.includes(type)}
  167. onToggle={handleSupportFileTypeChange}
  168. />
  169. ))
  170. }
  171. <FileTypeItem
  172. type={SupportUploadFileTypes.custom}
  173. selected={allowed_file_types.includes(SupportUploadFileTypes.custom)}
  174. onToggle={handleSupportFileTypeChange}
  175. customFileTypes={allowed_file_extensions}
  176. onCustomFileTypesChange={handleCustomFileTypesChange}
  177. />
  178. </div>
  179. </Field>
  180. )}
  181. </div>
  182. )
  183. }
  184. export default React.memo(FileUploadSetting)