1- import React , { useState , useCallback } from 'react' ;
1+ import React , { useState , useCallback , useEffect } from 'react' ;
22import { FileUpload } from './components/FileUpload' ;
33import { RegexControls } from './components/RegexControls' ;
44import { FileList } from './components/FileList' ;
@@ -19,6 +19,8 @@ function App() {
1919 const [ showGradNorm , setShowGradNorm ] = useState ( false ) ;
2020 const [ configModalOpen , setConfigModalOpen ] = useState ( false ) ;
2121 const [ configFile , setConfigFile ] = useState ( null ) ;
22+ const [ globalDragOver , setGlobalDragOver ] = useState ( false ) ;
23+ const [ dragCounter , setDragCounter ] = useState ( 0 ) ;
2224
2325 const handleFilesUploaded = useCallback ( ( files ) => {
2426 const filesWithDefaults = files . map ( file => ( {
@@ -37,6 +39,39 @@ function App() {
3739 setUploadedFiles ( prev => [ ...prev , ...filesWithDefaults ] ) ;
3840 } , [ ] ) ;
3941
42+ // 全局文件处理函数
43+ const processGlobalFiles = useCallback ( ( files ) => {
44+ const fileArray = Array . from ( files ) . filter ( file =>
45+ file . type === 'text/plain' || file . name . endsWith ( '.log' ) || file . name . endsWith ( '.txt' )
46+ ) ;
47+
48+ if ( fileArray . length === 0 ) return ;
49+
50+ const processedFiles = fileArray . map ( file => ( {
51+ file,
52+ name : file . name ,
53+ id : Math . random ( ) . toString ( 36 ) . substr ( 2 , 9 ) ,
54+ data : null ,
55+ content : null
56+ } ) ) ;
57+
58+ // Read file contents
59+ Promise . all (
60+ processedFiles . map ( fileObj =>
61+ new Promise ( ( resolve ) => {
62+ const reader = new FileReader ( ) ;
63+ reader . onload = ( e ) => {
64+ fileObj . content = e . target . result ;
65+ resolve ( fileObj ) ;
66+ } ;
67+ reader . readAsText ( fileObj . file ) ;
68+ } )
69+ )
70+ ) . then ( files => {
71+ handleFilesUploaded ( files ) ;
72+ } ) ;
73+ } , [ handleFilesUploaded ] ) ;
74+
4075 const handleFileRemove = useCallback ( ( index ) => {
4176 setUploadedFiles ( prev => prev . filter ( ( _ , i ) => i !== index ) ) ;
4277 } , [ ] ) ;
@@ -71,8 +106,110 @@ function App() {
71106 }
72107 } , [ ] ) ;
73108
109+ // 全局拖拽事件处理
110+ const handleGlobalDragEnter = useCallback ( ( e ) => {
111+ e . preventDefault ( ) ;
112+ setDragCounter ( prev => prev + 1 ) ;
113+
114+ // 检查是否包含文件
115+ if ( e . dataTransfer . types . includes ( 'Files' ) ) {
116+ setGlobalDragOver ( true ) ;
117+ }
118+ } , [ ] ) ;
119+
120+ const handleGlobalDragOver = useCallback ( ( e ) => {
121+ e . preventDefault ( ) ;
122+ // 设置拖拽效果
123+ e . dataTransfer . dropEffect = 'copy' ;
124+ } , [ ] ) ;
125+
126+ const handleGlobalDragLeave = useCallback ( ( e ) => {
127+ e . preventDefault ( ) ;
128+ setDragCounter ( prev => {
129+ const newCount = prev - 1 ;
130+ if ( newCount === 0 ) {
131+ setGlobalDragOver ( false ) ;
132+ }
133+ return newCount ;
134+ } ) ;
135+ } , [ ] ) ;
136+
137+ const handleGlobalDrop = useCallback ( ( e ) => {
138+ e . preventDefault ( ) ;
139+ setGlobalDragOver ( false ) ;
140+ setDragCounter ( 0 ) ;
141+
142+ if ( e . dataTransfer . files . length > 0 ) {
143+ processGlobalFiles ( e . dataTransfer . files ) ;
144+ }
145+ } , [ processGlobalFiles ] ) ;
146+
147+ // 添加全局拖拽监听器
148+ useEffect ( ( ) => {
149+ const handleDragEnter = ( e ) => handleGlobalDragEnter ( e ) ;
150+ const handleDragOver = ( e ) => handleGlobalDragOver ( e ) ;
151+ const handleDragLeave = ( e ) => handleGlobalDragLeave ( e ) ;
152+ const handleDrop = ( e ) => handleGlobalDrop ( e ) ;
153+
154+ document . addEventListener ( 'dragenter' , handleDragEnter ) ;
155+ document . addEventListener ( 'dragover' , handleDragOver ) ;
156+ document . addEventListener ( 'dragleave' , handleDragLeave ) ;
157+ document . addEventListener ( 'drop' , handleDrop ) ;
158+
159+ return ( ) => {
160+ document . removeEventListener ( 'dragenter' , handleDragEnter ) ;
161+ document . removeEventListener ( 'dragover' , handleDragOver ) ;
162+ document . removeEventListener ( 'dragleave' , handleDragLeave ) ;
163+ document . removeEventListener ( 'drop' , handleDrop ) ;
164+ } ;
165+ } , [ handleGlobalDragEnter , handleGlobalDragOver , handleGlobalDragLeave , handleGlobalDrop ] ) ;
166+
74167 return (
75- < div className = "min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100" >
168+ < div className = "min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 relative" >
169+ { /* 全页面拖拽覆盖层 */ }
170+ { globalDragOver && (
171+ < div
172+ className = "fixed inset-0 bg-blue-600 bg-opacity-95 z-50 flex items-center justify-center backdrop-blur-sm drag-overlay-fade-in"
173+ >
174+ < div
175+ className = "bg-white rounded-xl shadow-2xl p-8 text-center max-w-md mx-4 border-4 border-dashed border-blue-300 drag-modal-scale-in"
176+ >
177+ < div className = "mb-6" >
178+ < div className = "relative" >
179+ < svg
180+ className = "mx-auto h-20 w-20 text-blue-600 drag-icon-bounce"
181+ fill = "none"
182+ viewBox = "0 0 24 24"
183+ stroke = "currentColor"
184+ aria-hidden = "true"
185+ >
186+ < path
187+ strokeLinecap = "round"
188+ strokeLinejoin = "round"
189+ strokeWidth = { 1.5 }
190+ d = "M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
191+ />
192+ </ svg >
193+ < div className = "absolute -top-2 -right-2 w-6 h-6 bg-green-500 rounded-full flex items-center justify-center" >
194+ < svg className = "w-4 h-4 text-white" fill = "currentColor" viewBox = "0 0 20 20" >
195+ < path fillRule = "evenodd" d = "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule = "evenodd" />
196+ </ svg >
197+ </ div >
198+ </ div >
199+ </ div >
200+ < h3 className = "text-xl font-bold text-gray-900 mb-3" >
201+ 🎯 释放文件以上传
202+ </ h3 >
203+ < p className = "text-sm text-gray-600 mb-2" >
204+ 支持 < span className = "font-semibold text-blue-600" > .log</ span > 和 < span className = "font-semibold text-blue-600" > .txt</ span > 格式
205+ </ p >
206+ < p className = "text-xs text-gray-500" >
207+ 拖拽到页面任意位置即可快速上传日志文件
208+ </ p >
209+ </ div >
210+ </ div >
211+ ) }
212+
76213 < div className = "w-full px-3 py-3" >
77214 < Header />
78215
0 commit comments