1
1
// FileUpload.tsx
2
2
import React , { useState , useRef } from 'react' ;
3
3
import { FileInfo } from "../types/types.ts" ;
4
+ import { convertPDFToMarkdown , convertPDFResultToFileInfo , isPDFFile } from '../utils/pdfProcessor' ;
5
+ import { Alert , LinearProgress , Typography , Box } from '@mui/material' ;
4
6
5
7
interface FileUploadProps {
6
8
onFilesUploaded : ( files : FileInfo [ ] ) => void ;
@@ -43,9 +45,18 @@ const getFilesFromDirectory = async (directory: FileSystemDirectoryEntry): Promi
43
45
44
46
const FileUpload : React . FC < FileUploadProps > = ( { onFilesUploaded} ) => {
45
47
const [ isDragging , setIsDragging ] = useState ( false ) ;
48
+ const [ isProcessingPDF , setIsProcessingPDF ] = useState ( false ) ;
49
+ const [ processingStatus , setProcessingStatus ] = useState < string > ( '' ) ;
50
+ const [ processingProgress , setProcessingProgress ] = useState ( 0 ) ;
46
51
const fileInputRef = useRef < HTMLInputElement > ( null ) ;
47
52
48
53
const readFileContent = async ( file : File ) : Promise < FileInfo > => {
54
+ // 检查是否为PDF文件
55
+ if ( isPDFFile ( file ) ) {
56
+ return await processPDFFile ( file ) ;
57
+ }
58
+
59
+ // 处理普通文本文件
49
60
return new Promise ( ( resolve ) => {
50
61
const reader = new FileReader ( ) ;
51
62
reader . onload = ( e ) => {
@@ -61,6 +72,57 @@ const FileUpload: React.FC<FileUploadProps> = ({onFilesUploaded}) => {
61
72
} ) ;
62
73
} ;
63
74
75
+ const processPDFFile = async ( file : File ) : Promise < FileInfo > => {
76
+ setIsProcessingPDF ( true ) ;
77
+ setProcessingStatus ( `正在处理PDF文件: ${ file . name } ` ) ;
78
+ setProcessingProgress ( 0 ) ;
79
+
80
+ try {
81
+ // 模拟进度更新
82
+ const progressInterval = setInterval ( ( ) => {
83
+ setProcessingProgress ( prev => Math . min ( prev + 10 , 90 ) ) ;
84
+ } , 200 ) ;
85
+
86
+ const result = await convertPDFToMarkdown ( file , {
87
+ maxPages : 20 ,
88
+ preserveFormatting : true ,
89
+ includeImages : false
90
+ } ) ;
91
+
92
+ clearInterval ( progressInterval ) ;
93
+ setProcessingProgress ( 100 ) ;
94
+
95
+ if ( result . success ) {
96
+ setProcessingStatus ( `PDF处理完成: ${ result . metadata . processedPages } /${ result . metadata . pages } 页` ) ;
97
+ setTimeout ( ( ) => {
98
+ setIsProcessingPDF ( false ) ;
99
+ setProcessingStatus ( '' ) ;
100
+ setProcessingProgress ( 0 ) ;
101
+ } , 1500 ) ;
102
+
103
+ return convertPDFResultToFileInfo ( file , result ) ;
104
+ } else {
105
+ throw new Error ( result . error || 'PDF处理失败' ) ;
106
+ }
107
+ } catch ( error ) {
108
+ setProcessingStatus ( `PDF处理失败: ${ error } ` ) ;
109
+ setTimeout ( ( ) => {
110
+ setIsProcessingPDF ( false ) ;
111
+ setProcessingStatus ( '' ) ;
112
+ setProcessingProgress ( 0 ) ;
113
+ } , 3000 ) ;
114
+
115
+ // 返回错误信息作为文件内容
116
+ return {
117
+ name : file . name . replace ( '.pdf' , '_error.txt' ) ,
118
+ path : file . name ,
119
+ size : 0 ,
120
+ extension : 'txt' ,
121
+ content : `PDF处理失败: ${ error } \n\n原文件: ${ file . name } \n大小: ${ file . size } bytes`
122
+ } ;
123
+ }
124
+ } ;
125
+
64
126
const processEntries = async ( items : DataTransferItemList ) => {
65
127
const files : File [ ] = [ ] ;
66
128
@@ -105,26 +167,52 @@ const FileUpload: React.FC<FileUploadProps> = ({onFilesUploaded}) => {
105
167
} ;
106
168
107
169
return (
108
- < div
109
- className = { `drop-zone ${ isDragging ? 'dragging' : '' } ` }
110
- onDragOver = { ( e ) => {
111
- e . preventDefault ( ) ;
112
- setIsDragging ( true ) ;
113
- } }
114
- onDragLeave = { ( ) => setIsDragging ( false ) }
115
- onDrop = { handleFileDrop }
116
- onClick = { handleClick }
117
- style = { { cursor : 'pointer' } }
118
- >
119
- < input
120
- type = "file"
121
- multiple
122
- // webkitdirectory=""
123
- onChange = { handleFileSelect }
124
- className = "file-input"
125
- ref = { fileInputRef }
126
- />
127
- < p > 拖放AI参考代码文件/文件夹到这里或点击此处选择(参考上下文越完整,AI推理结果越好)</ p >
170
+ < div >
171
+ < div
172
+ className = { `drop-zone ${ isDragging ? 'dragging' : '' } ` }
173
+ onDragOver = { ( e ) => {
174
+ e . preventDefault ( ) ;
175
+ setIsDragging ( true ) ;
176
+ } }
177
+ onDragLeave = { ( ) => setIsDragging ( false ) }
178
+ onDrop = { handleFileDrop }
179
+ onClick = { ! isProcessingPDF ? handleClick : undefined }
180
+ style = { { cursor : isProcessingPDF ? 'default' : 'pointer' } }
181
+ >
182
+ < input
183
+ type = "file"
184
+ multiple
185
+ accept = ".txt,.js,.ts,.jsx,.tsx,.py,.java,.cpp,.c,.h,.cs,.php,.rb,.go,.rs,.swift,.kt,.scala,.clj,.html,.css,.scss,.sass,.less,.xml,.json,.yaml,.yml,.md,.sql,.sh,.bat,.ps1,.dockerfile,.gitignore,.env,.config,.ini,.toml,.lock,.pdf"
186
+ onChange = { handleFileSelect }
187
+ className = "file-input"
188
+ ref = { fileInputRef }
189
+ disabled = { isProcessingPDF }
190
+ />
191
+ < p >
192
+ 拖放AI参考代码文件/文件夹到这里或点击此处选择
193
+ < br />
194
+ < small style = { { color : '#666' , fontSize : '0.9em' } } >
195
+ 支持代码文件和PDF文档(PDF将自动转换为Markdown)
196
+ </ small >
197
+ </ p >
198
+ </ div >
199
+
200
+ { /* PDF处理状态显示 */ }
201
+ { isProcessingPDF && (
202
+ < Box sx = { { mt : 2 , p : 2 , border : '1px solid #ddd' , borderRadius : 1 } } >
203
+ < Typography variant = "body2" color = "primary" gutterBottom >
204
+ { processingStatus }
205
+ </ Typography >
206
+ < LinearProgress
207
+ variant = "determinate"
208
+ value = { processingProgress }
209
+ sx = { { mb : 1 } }
210
+ />
211
+ < Typography variant = "caption" color = "text.secondary" >
212
+ { processingProgress } % 完成
213
+ </ Typography >
214
+ </ Box >
215
+ ) }
128
216
</ div >
129
217
) ;
130
218
} ;
0 commit comments