file-upload-setting.tsx 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  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 {
  40. imgSizeLimit,
  41. docSizeLimit,
  42. audioSizeLimit,
  43. videoSizeLimit,
  44. maxFileUploadLimit,
  45. } = useFileSizeLimit(fileUploadConfigResponse)
  46. const handleSupportFileTypeChange = useCallback((type: SupportUploadFileTypes) => {
  47. const newPayload = produce(payload, (draft) => {
  48. if (type === SupportUploadFileTypes.custom) {
  49. if (!draft.allowed_file_types.includes(SupportUploadFileTypes.custom))
  50. draft.allowed_file_types = [SupportUploadFileTypes.custom]
  51. else
  52. draft.allowed_file_types = draft.allowed_file_types.filter(v => v !== type)
  53. }
  54. else {
  55. draft.allowed_file_types = draft.allowed_file_types.filter(v => v !== SupportUploadFileTypes.custom)
  56. if (draft.allowed_file_types.includes(type))
  57. draft.allowed_file_types = draft.allowed_file_types.filter(v => v !== type)
  58. else
  59. draft.allowed_file_types.push(type)
  60. }
  61. })
  62. onChange(newPayload)
  63. }, [onChange, payload])
  64. const handleUploadMethodChange = useCallback((method: TransferMethod) => {
  65. return () => {
  66. const newPayload = produce(payload, (draft) => {
  67. if (method === TransferMethod.all)
  68. draft.allowed_file_upload_methods = [TransferMethod.local_file, TransferMethod.remote_url]
  69. else
  70. draft.allowed_file_upload_methods = [method]
  71. })
  72. onChange(newPayload)
  73. }
  74. }, [onChange, payload])
  75. const handleCustomFileTypesChange = useCallback((customFileTypes: string[]) => {
  76. const newPayload = produce(payload, (draft) => {
  77. draft.allowed_file_extensions = customFileTypes.map((v) => {
  78. return v
  79. })
  80. })
  81. onChange(newPayload)
  82. }, [onChange, payload])
  83. const handleMaxUploadNumLimitChange = useCallback((value: number) => {
  84. const newPayload = produce(payload, (draft) => {
  85. draft.max_length = value
  86. })
  87. onChange(newPayload)
  88. }, [onChange, payload])
  89. return (
  90. <div>
  91. {!inFeaturePanel && (
  92. <Field
  93. title={t('appDebug.variableConfig.file.supportFileTypes')}
  94. >
  95. <div className='space-y-1'>
  96. {
  97. [SupportUploadFileTypes.document, SupportUploadFileTypes.image, SupportUploadFileTypes.audio, SupportUploadFileTypes.video].map((type: SupportUploadFileTypes) => (
  98. <FileTypeItem
  99. key={type}
  100. type={type as SupportUploadFileTypes.image | SupportUploadFileTypes.document | SupportUploadFileTypes.audio | SupportUploadFileTypes.video}
  101. selected={allowed_file_types.includes(type)}
  102. onToggle={handleSupportFileTypeChange}
  103. />
  104. ))
  105. }
  106. <FileTypeItem
  107. type={SupportUploadFileTypes.custom}
  108. selected={allowed_file_types.includes(SupportUploadFileTypes.custom)}
  109. onToggle={handleSupportFileTypeChange}
  110. customFileTypes={allowed_file_extensions}
  111. onCustomFileTypesChange={handleCustomFileTypesChange}
  112. />
  113. </div>
  114. </Field>
  115. )}
  116. <Field
  117. title={t('appDebug.variableConfig.uploadFileTypes')}
  118. className='mt-4'
  119. >
  120. <div className='grid grid-cols-3 gap-2'>
  121. <OptionCard
  122. title={t('appDebug.variableConfig.localUpload')}
  123. selected={allowed_file_upload_methods.length === 1 && allowed_file_upload_methods.includes(TransferMethod.local_file)}
  124. onSelect={handleUploadMethodChange(TransferMethod.local_file)}
  125. />
  126. <OptionCard
  127. title="URL"
  128. selected={allowed_file_upload_methods.length === 1 && allowed_file_upload_methods.includes(TransferMethod.remote_url)}
  129. onSelect={handleUploadMethodChange(TransferMethod.remote_url)}
  130. />
  131. <OptionCard
  132. title={t('appDebug.variableConfig.both')}
  133. selected={allowed_file_upload_methods.includes(TransferMethod.local_file) && allowed_file_upload_methods.includes(TransferMethod.remote_url)}
  134. onSelect={handleUploadMethodChange(TransferMethod.all)}
  135. />
  136. </div>
  137. </Field>
  138. {isMultiple && (
  139. <Field
  140. className='mt-4'
  141. title={t('appDebug.variableConfig.maxNumberOfUploads')!}
  142. >
  143. <div>
  144. <div className='mb-1.5 text-text-tertiary body-xs-regular'>{t('appDebug.variableConfig.maxNumberTip', {
  145. imgLimit: formatFileSize(imgSizeLimit),
  146. docLimit: formatFileSize(docSizeLimit),
  147. audioLimit: formatFileSize(audioSizeLimit),
  148. videoLimit: formatFileSize(videoSizeLimit),
  149. })}</div>
  150. <InputNumberWithSlider
  151. value={max_length}
  152. min={1}
  153. max={maxFileUploadLimit}
  154. onChange={handleMaxUploadNumLimitChange}
  155. />
  156. </div>
  157. </Field>
  158. )}
  159. {inFeaturePanel && !hideSupportFileType && (
  160. <Field
  161. title={t('appDebug.variableConfig.file.supportFileTypes')}
  162. className='mt-4'
  163. >
  164. <div className='space-y-1'>
  165. {
  166. [SupportUploadFileTypes.document, SupportUploadFileTypes.image, SupportUploadFileTypes.audio, SupportUploadFileTypes.video].map((type: SupportUploadFileTypes) => (
  167. <FileTypeItem
  168. key={type}
  169. type={type as SupportUploadFileTypes.image | SupportUploadFileTypes.document | SupportUploadFileTypes.audio | SupportUploadFileTypes.video}
  170. selected={allowed_file_types.includes(type)}
  171. onToggle={handleSupportFileTypeChange}
  172. />
  173. ))
  174. }
  175. <FileTypeItem
  176. type={SupportUploadFileTypes.custom}
  177. selected={allowed_file_types.includes(SupportUploadFileTypes.custom)}
  178. onToggle={handleSupportFileTypeChange}
  179. customFileTypes={allowed_file_extensions}
  180. onCustomFileTypesChange={handleCustomFileTypesChange}
  181. />
  182. </div>
  183. </Field>
  184. )}
  185. </div>
  186. )
  187. }
  188. export default React.memo(FileUploadSetting)