@@ -19,6 +19,7 @@ const DynamicTab: React.FC<DynamicTabProps> = ({ funcPublish }) => {
1919 const [ content , setContent ] = useState < string > ( '' ) ;
2020 const imageInputRef = useRef < HTMLInputElement > ( null ) ;
2121 const videoInputRef = useRef < HTMLInputElement > ( null ) ;
22+ const dropAreaRef = useRef < HTMLDivElement > ( null ) ;
2223 const [ selectedPlatforms , setSelectedPlatforms ] = useState < string [ ] > ( [ ] ) ;
2324 const [ autoPublish , setAutoPublish ] = useState < boolean > ( false ) ;
2425 const [ viewerVisible , setViewerVisible ] = useState ( false ) ;
@@ -29,6 +30,24 @@ const DynamicTab: React.FC<DynamicTabProps> = ({ funcPublish }) => {
2930 setTitle ( '开发环境标题' ) ;
3031 setContent ( '开发环境内容' ) ;
3132 }
33+
34+ // 添加粘贴事件监听器
35+ document . addEventListener ( 'paste' , handlePaste ) ;
36+
37+ // 添加拖拽事件监听器
38+ const dropArea = dropAreaRef . current ;
39+ if ( dropArea ) {
40+ dropArea . addEventListener ( 'dragover' , handleDragOver ) ;
41+ dropArea . addEventListener ( 'drop' , handleDrop ) ;
42+ }
43+
44+ return ( ) => {
45+ document . removeEventListener ( 'paste' , handlePaste ) ;
46+ if ( dropArea ) {
47+ dropArea . removeEventListener ( 'dragover' , handleDragOver ) ;
48+ dropArea . removeEventListener ( 'drop' , handleDrop ) ;
49+ }
50+ } ;
3251 } , [ ] ) ;
3352
3453 const handleFileChange = ( event : React . ChangeEvent < HTMLInputElement > , fileType : 'image' | 'video' ) => {
@@ -45,9 +64,92 @@ const DynamicTab: React.FC<DynamicTabProps> = ({ funcPublish }) => {
4564 if ( fileType === 'image' ) {
4665 setImages ( ( prevImages ) => [ ...prevImages , ...newFiles ] ) ;
4766 } else {
48- setVideos ( ( prevVideos ) => [ ...prevVideos , ...newFiles ] ) ;
67+ // 只允许一个视频,如果已有视频则替换
68+ setVideos ( [ newFiles [ 0 ] ] ) ;
69+ }
70+ }
71+ } ;
72+
73+ const handlePaste = ( event : ClipboardEvent ) => {
74+ const items = event . clipboardData ?. items ;
75+ if ( ! items ) return ;
76+
77+ for ( let i = 0 ; i < items . length ; i ++ ) {
78+ const item = items [ i ] ;
79+ if ( item . kind === 'file' ) {
80+ const file = item . getAsFile ( ) ;
81+ if ( ! file ) continue ;
82+
83+ if ( file . type . startsWith ( 'image/' ) ) {
84+ setImages ( ( prevImages ) => [
85+ ...prevImages ,
86+ {
87+ name : file . name || `pasted-image-${ Date . now ( ) } .png` ,
88+ type : file . type ,
89+ size : file . size ,
90+ url : URL . createObjectURL ( file ) ,
91+ } ,
92+ ] ) ;
93+ } else if ( file . type . startsWith ( 'video/' ) ) {
94+ // 只允许一个视频
95+ if ( videos . length === 0 ) {
96+ setVideos ( [
97+ {
98+ name : file . name || `pasted-video-${ Date . now ( ) } .mp4` ,
99+ type : file . type ,
100+ size : file . size ,
101+ url : URL . createObjectURL ( file ) ,
102+ } ,
103+ ] ) ;
104+ }
105+ break ; // 处理完第一个视频后退出循环
106+ }
107+ }
108+ }
109+ } ;
110+
111+ const handleDragOver = ( event : DragEvent ) => {
112+ event . preventDefault ( ) ;
113+ event . stopPropagation ( ) ;
114+ } ;
115+
116+ const handleDrop = ( event : DragEvent ) => {
117+ event . preventDefault ( ) ;
118+ event . stopPropagation ( ) ;
119+
120+ const files = event . dataTransfer ?. files ;
121+ if ( ! files ) return ;
122+
123+ const imageFiles : FileData [ ] = [ ] ;
124+ let videoFile : FileData | null = null ;
125+
126+ for ( let i = 0 ; i < files . length ; i ++ ) {
127+ const file = files [ i ] ;
128+ if ( file . type . startsWith ( 'image/' ) ) {
129+ imageFiles . push ( {
130+ name : file . name ,
131+ type : file . type ,
132+ size : file . size ,
133+ url : URL . createObjectURL ( file ) ,
134+ } ) ;
135+ } else if ( file . type . startsWith ( 'video/' ) && ! videoFile ) {
136+ // 只取第一个视频文件
137+ videoFile = {
138+ name : file . name ,
139+ type : file . type ,
140+ size : file . size ,
141+ url : URL . createObjectURL ( file ) ,
142+ } ;
49143 }
50144 }
145+
146+ if ( imageFiles . length > 0 ) {
147+ setImages ( ( prevImages ) => [ ...prevImages , ...imageFiles ] ) ;
148+ }
149+
150+ if ( videoFile && videos . length === 0 ) {
151+ setVideos ( [ videoFile ] ) ;
152+ }
51153 } ;
52154
53155 const handlePlatformChange = ( platform : string , isSelected : boolean ) => {
@@ -65,12 +167,7 @@ const DynamicTab: React.FC<DynamicTabProps> = ({ funcPublish }) => {
65167 alert ( chrome . i18n . getMessage ( 'optionsSelectPublishPlatforms' ) ) ;
66168 return ;
67169 }
68- // const needImage = PLATFORM_NEED_IMAGE.some((platform) => selectedPlatforms.includes(platform));
69- // if (needImage && images.length === 0) {
70- // console.log('至少一张图片');
71- // alert(chrome.i18n.getMessage('optionsAtLeastOneImage'));
72- // return;
73- // }
170+
74171 const data : SyncData = {
75172 platforms : selectedPlatforms ,
76173 data : {
@@ -110,7 +207,7 @@ const DynamicTab: React.FC<DynamicTabProps> = ({ funcPublish }) => {
110207 if ( fileType === 'image' ) {
111208 setImages ( ( prevImages ) => prevImages . filter ( ( _ , i ) => i !== index ) ) ;
112209 } else {
113- setVideos ( ( prevVideos ) => prevVideos . filter ( ( _ , i ) => i !== index ) ) ;
210+ setVideos ( [ ] ) ;
114211 }
115212 } ;
116213
@@ -124,14 +221,13 @@ const DynamicTab: React.FC<DynamicTabProps> = ({ funcPublish }) => {
124221 } ;
125222
126223 return (
127- < div className = "flex flex-col gap-4" >
224+ < div className = "flex flex-col gap-4" ref = { dropAreaRef } >
128225 < Card className = "shadow-none bg-default-50" >
129226 < CardHeader className = "flex flex-col gap-4" >
130227 < Input
131228 isClearable
132229 variant = "underlined"
133230 label = { chrome . i18n . getMessage ( 'optionsEnterDynamicTitle' ) }
134- // placeholder={chrome.i18n.getMessage('optionsEnterDynamicTitle')}
135231 value = { title }
136232 onChange = { ( e ) => setTitle ( e . target . value ) }
137233 onClear = { ( ) => setTitle ( '' ) }
@@ -143,7 +239,6 @@ const DynamicTab: React.FC<DynamicTabProps> = ({ funcPublish }) => {
143239 < Textarea
144240 isClearable
145241 label = { chrome . i18n . getMessage ( 'optionsEnterDynamicContent' ) }
146- // placeholder={chrome.i18n.getMessage('optionsEnterDynamicContent')}
147242 value = { content }
148243 onChange = { ( e ) => setContent ( e . target . value ) }
149244 variant = "underlined"
@@ -177,7 +272,6 @@ const DynamicTab: React.FC<DynamicTabProps> = ({ funcPublish }) => {
177272 accept = "video/*"
178273 onChange = { ( e ) => handleFileChange ( e , 'video' ) }
179274 className = "hidden"
180- multiple
181275 />
182276 < Button
183277 isIconOnly
@@ -230,7 +324,7 @@ const DynamicTab: React.FC<DynamicTabProps> = ({ funcPublish }) => {
230324 </ Card >
231325 ) }
232326
233- < div className = "flex flex-col gap-4 bg-default-50 p-4 rounded-lg" >
327+ < div className = "flex flex-col gap-4 p-4 rounded-lg bg-default-50 " >
234328 < Switch
235329 isSelected = { autoPublish }
236330 onValueChange = { setAutoPublish }
@@ -261,7 +355,7 @@ const DynamicTab: React.FC<DynamicTabProps> = ({ funcPublish }) => {
261355 variant = "flat"
262356 disabled = { ! title || ! content || selectedPlatforms . length === 0 }
263357 className = "w-full font-medium shadow-none" >
264- < SendIcon className = "size-4 mr-2" />
358+ < SendIcon className = "mr-2 size-4 " />
265359 { chrome . i18n . getMessage ( 'optionsSyncDynamic' ) }
266360 </ Button >
267361
0 commit comments