11// FileUpload.tsx
22import React , { useState , useRef } from 'react' ;
33import { FileInfo } from "../types/types.ts" ;
4+ import { convertPDFToMarkdown , convertPDFResultToFileInfo , isPDFFile } from '../utils/pdfProcessor' ;
5+ import { Alert , LinearProgress , Typography , Box } from '@mui/material' ;
46
57interface FileUploadProps {
68 onFilesUploaded : ( files : FileInfo [ ] ) => void ;
@@ -43,9 +45,18 @@ const getFilesFromDirectory = async (directory: FileSystemDirectoryEntry): Promi
4345
4446const FileUpload : React . FC < FileUploadProps > = ( { onFilesUploaded} ) => {
4547 const [ isDragging , setIsDragging ] = useState ( false ) ;
48+ const [ isProcessingPDF , setIsProcessingPDF ] = useState ( false ) ;
49+ const [ processingStatus , setProcessingStatus ] = useState < string > ( '' ) ;
50+ const [ processingProgress , setProcessingProgress ] = useState ( 0 ) ;
4651 const fileInputRef = useRef < HTMLInputElement > ( null ) ;
4752
4853 const readFileContent = async ( file : File ) : Promise < FileInfo > => {
54+ // 检查是否为PDF文件
55+ if ( isPDFFile ( file ) ) {
56+ return await processPDFFile ( file ) ;
57+ }
58+
59+ // 处理普通文本文件
4960 return new Promise ( ( resolve ) => {
5061 const reader = new FileReader ( ) ;
5162 reader . onload = ( e ) => {
@@ -61,6 +72,57 @@ const FileUpload: React.FC<FileUploadProps> = ({onFilesUploaded}) => {
6172 } ) ;
6273 } ;
6374
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+
64126 const processEntries = async ( items : DataTransferItemList ) => {
65127 const files : File [ ] = [ ] ;
66128
@@ -105,26 +167,52 @@ const FileUpload: React.FC<FileUploadProps> = ({onFilesUploaded}) => {
105167 } ;
106168
107169 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+ ) }
128216 </ div >
129217 ) ;
130218} ;
0 commit comments