11import type { DragEvent } from 'react' ;
2- import React , { useCallback , useMemo , useState } from 'react' ;
2+ import React , { useCallback , useEffect , useMemo , useState } from 'react' ;
33import type { UserInputFileItemType } from '../../chat/ChatContainer/ChatBox/type' ;
44import {
55 Box ,
@@ -31,8 +31,9 @@ import { ChatBoxContext } from '../../chat/ChatContainer/ChatBox/Provider';
3131import { POST } from '@/web/common/api/request' ;
3232import { getErrText } from '@fastgpt/global/common/error/utils' ;
3333import { useRequest2 } from '@fastgpt/web/hooks/useRequest' ;
34- import { useDebounceEffect } from 'ahooks' ;
3534import { formatFileSize , parseUrlToFileType } from '@fastgpt/global/common/file/tools' ;
35+ import { ChatItemContext } from '@/web/core/chat/context/chatItemContext' ;
36+ import { PluginRunContext } from '../../chat/ChatContainer/PluginRunBox/context' ;
3637
3738const FileSelector = ( {
3839 fileUrls,
@@ -45,49 +46,86 @@ const FileSelector = ({
4546 canSelectCustomFileExtension,
4647 customFileExtensionList,
4748 canLocalUpload,
48- canUrlUpload
49+ canUrlUpload,
50+ isDisabled = false
4951} : AppFileSelectConfigType & {
50- fileUrls : string [ ] ;
51- onChange : ( e : string [ ] ) => void ;
52+ fileUrls : string [ ] | any [ ] ; // Can be string[] or file object[]
53+ onChange : ( e : any [ ] ) => void ;
5254 canLocalUpload ?: boolean ;
5355 canUrlUpload ?: boolean ;
56+ isDisabled ?: boolean ;
5457} ) => {
5558 const { feConfigs } = useSystemStore ( ) ;
5659 const { toast } = useToast ( ) ;
5760 const { t } = useTranslation ( ) ;
5861
59- const outLinkAuthData = useContextSelector ( ChatBoxContext , ( v ) => v . outLinkAuthData ) ;
60- const appId = useContextSelector ( ChatBoxContext , ( v ) => v . appId ) ;
61- const chatId = useContextSelector ( ChatBoxContext , ( v ) => v . chatId ) ;
62+ const chatBoxOutLinkAuthData = useContextSelector ( ChatBoxContext , ( v ) => v ?. outLinkAuthData ) ;
63+ const chatBoxAppId = useContextSelector ( ChatBoxContext , ( v ) => v ?. appId ) ;
64+ const chatBoxChatId = useContextSelector ( ChatBoxContext , ( v ) => v ?. chatId ) ;
65+
66+ const pluginOutLinkAuthData = useContextSelector ( PluginRunContext , ( v ) => v ?. outLinkAuthData ) ;
67+ const pluginAppId = useContextSelector ( PluginRunContext , ( v ) => v ?. appId ) ;
68+ const pluginChatId = useContextSelector ( PluginRunContext , ( v ) => v ?. chatId ) ;
69+
70+ const chatItemAppId = useContextSelector ( ChatItemContext , ( v ) => v ?. chatBoxData ?. appId ) ;
71+ const chatItemChatId = useContextSelector ( ChatItemContext , ( v ) => v ?. chatBoxData ?. chatId ) ;
72+
73+ const outLinkAuthData = useMemo (
74+ ( ) => ( {
75+ ...( chatBoxOutLinkAuthData || { } ) ,
76+ ...( pluginOutLinkAuthData || { } )
77+ } ) ,
78+ [ chatBoxOutLinkAuthData , pluginOutLinkAuthData ]
79+ ) ;
80+ const appId = useMemo (
81+ ( ) => chatBoxAppId || pluginAppId || chatItemAppId || '' ,
82+ [ chatBoxAppId , pluginAppId , chatItemAppId ]
83+ ) ;
84+ const chatId = useMemo (
85+ ( ) => chatBoxChatId || pluginChatId || chatItemChatId || '' ,
86+ [ chatBoxChatId , pluginChatId , chatItemChatId ]
87+ ) ;
88+
89+ const [ cloneFiles , setCloneFiles ] = useState < UserInputFileItemType [ ] > ( ( ) => {
90+ return fileUrls
91+ . map ( ( item ) => {
92+ const url = typeof item === 'string' ? item : item ?. url || item ?. key ;
93+ const key = typeof item === 'string' ? undefined : item ?. key ;
94+ const name = typeof item === 'string' ? undefined : item ?. name ;
95+ const type = typeof item === 'string' ? undefined : item ?. type ;
96+
97+ if ( ! url ) return null as unknown as UserInputFileItemType ;
6298
63- const [ cloneFiles , setCloneFiles ] = useState < UserInputFileItemType [ ] > (
64- fileUrls
65- . map ( ( url ) => {
6699 const fileType = parseUrlToFileType ( url ) ;
67- if ( ! fileType ) return null as unknown as UserInputFileItemType ;
100+ if ( ! fileType && ! type ) return null as unknown as UserInputFileItemType ;
68101
69102 return {
70103 id : getNanoid ( 6 ) ,
71- name : fileType . name || url ,
72- type : fileType . type ,
73- icon : getFileIcon ( fileType . name || url ) ,
74- url : fileType . url ,
104+ name : name || fileType ? .name || url ,
105+ type : type || fileType ? .type || ChatFileTypeEnum . file ,
106+ icon : getFileIcon ( name || fileType ? .name || url ) ,
107+ url : typeof item === 'string' ? fileType ?. url : item ? .url ,
75108 status : 1 ,
76- key : url . startsWith ( 'chat/' ) ? url : undefined
109+ key : key || ( typeof item === 'string' && url . startsWith ( 'chat/' ) ? url : undefined )
77110 } ;
78111 } )
79- . filter ( Boolean ) as UserInputFileItemType [ ]
80- ) ;
81- // 采用异步更新顶层的方式
82- useDebounceEffect (
83- ( ) => {
84- onChange ( cloneFiles . map ( ( file ) => file . key || file . url || '' ) . filter ( Boolean ) ) ;
85- } ,
86- [ cloneFiles ] ,
87- {
88- wait : 1000
89- }
90- ) ;
112+ . filter ( Boolean ) as UserInputFileItemType [ ] ;
113+ } ) ;
114+
115+ useEffect ( ( ) => {
116+ const fileObjects = cloneFiles
117+ . filter ( ( file ) => file . url || file . key )
118+ . map ( ( file ) => {
119+ const fileObj = {
120+ type : file . type ,
121+ name : file . name ,
122+ key : file . key ,
123+ url : file . url || file . key || ''
124+ } ;
125+ return fileObj ;
126+ } ) ;
127+ onChange ( fileObjects as any ) ;
128+ } , [ cloneFiles , onChange ] ) ;
91129
92130 const fileType = useMemo ( ( ) => {
93131 return getUploadFileType ( {
@@ -378,9 +416,10 @@ const FileSelector = ({
378416 borderColor = { 'myGray.250' }
379417 borderRadius = { 'md' }
380418 userSelect = { 'none' }
381- { ...( isMaxSelected
419+ { ...( isMaxSelected || isDisabled
382420 ? {
383- cursor : 'not-allowed'
421+ cursor : 'not-allowed' ,
422+ opacity : isDisabled ? 0.6 : 1
384423 }
385424 : {
386425 cursor : 'pointer' ,
@@ -397,10 +436,10 @@ const FileSelector = ({
397436 } ) }
398437 >
399438 < MyIcon name = { 'common/uploadFileFill' } w = { '32px' } />
400- { isMaxSelected ? (
439+ { isMaxSelected || isDisabled ? (
401440 < >
402441 < Box fontWeight = { '500' } fontSize = { 'sm' } >
403- { t ( 'file:reached_max_file_count' ) }
442+ { isDisabled ? t ( 'common:Running' ) : t ( 'file:reached_max_file_count' ) }
404443 </ Box >
405444 </ >
406445 ) : (
@@ -427,7 +466,7 @@ const FileSelector = ({
427466 zIndex = { 10 }
428467 />
429468 < Input
430- isDisabled = { isMaxSelected }
469+ isDisabled = { isMaxSelected || isDisabled }
431470 value = { urlInput }
432471 onChange = { ( e ) => setUrlInput ( e . target . value ) }
433472 onBlur = { ( e ) => handleAddUrl ( e . target . value ) }
@@ -437,7 +476,11 @@ const FileSelector = ({
437476 pl = { 8 }
438477 py = { 1.5 }
439478 placeholder = {
440- isMaxSelected ? t ( 'file:reached_max_file_count' ) : t ( 'chat:click_to_add_url' )
479+ isDisabled
480+ ? t ( 'common:Running' )
481+ : isMaxSelected
482+ ? t ( 'file:reached_max_file_count' )
483+ : t ( 'chat:click_to_add_url' )
441484 }
442485 />
443486 </ InputGroup >
@@ -476,6 +519,7 @@ const FileSelector = ({
476519 aria-label = { 'Delete file' }
477520 icon = { < MyIcon name = { 'close' } w = { '1rem' } /> }
478521 onClick = { ( ) => handleDeleteFile ( file . id ) }
522+ isDisabled = { isDisabled }
479523 />
480524 ) : (
481525 < HStack w = { '24px' } h = { '24px' } justifyContent = { 'center' } >
0 commit comments