1
1
import React from 'react' ;
2
- import { Upload , Icon , Modal , message } from 'antd' ;
2
+ import { Upload , Icon , Modal , message , Button , Tooltip } from 'antd' ;
3
3
import globalConfig from '../../config.js' ;
4
4
import Utils from '../../utils' ;
5
5
import Logger from '../../utils/Logger.js' ;
6
6
import './index.less' ;
7
7
8
- const logger = Logger . getLogger ( 'ImageUploader ' ) ;
8
+ const logger = Logger . getLogger ( 'FileUploader ' ) ;
9
9
10
10
/**
11
- * 图片上传组件, 样式基本是从antd官网抄过来的
11
+ * 文件上传组件, 样式基本是从antd官网抄过来的
12
+ * 可以上传图片, 也可以上传普通文件, 样式会不一样, 通过props中传入的type字段判断
12
13
*
13
14
* 这个组件可以配合antd的FormItem使用, 以后可以参考
14
15
*/
15
- class ImageUploader extends React . Component {
16
+ class FileUploader extends React . Component {
16
17
17
18
// 注意这个组件不能做成PureComponent, 会有bug, 因为上传的过程中会不断触发onChange, 进而导致状态不断变化
18
19
19
20
state = {
20
- previewVisible : false , // 是否显示预览
21
+ previewVisible : false , // 是否显示图片预览modal
21
22
previewImage : '' , // 要预览的图片
22
23
fileList : [ ] , // 已经上传的文件列表
23
24
} ;
24
25
25
- // 上传按钮, 每次render的时候重新定义也可以, 但感觉拿出来只定义一次更好
26
- uploadButton = (
27
- < div >
28
- < Icon type = "plus" />
29
- < div className = "ant-upload-text" > 上传图片</ div >
30
- </ div >
31
- ) ;
32
-
33
26
componentWillMount ( ) {
34
- const { defaultValue, max, url} = this . props ;
27
+ const { defaultValue, max, url, type} = this . props ;
28
+ // 当前是要上传图片还是普通图片? 会影响后续的很多东西
29
+ const forImage = type === 'image' ;
30
+ if ( forImage ) {
31
+ this . listType = 'picture-card' ; // 对于图片类型的上传, 要显示缩略图
32
+ } else {
33
+ this . listType = 'text' ; // 对于其他类型的上传, 只显示个文件名就可以了
34
+ }
35
+
35
36
// 组件第一次加载的时候, 设置默认值
36
37
this . forceUpdateStateByValue ( defaultValue , max ) ;
38
+
37
39
// 是否自定义了图片上传的路径
38
40
if ( url ) {
39
41
if ( url . startsWith ( 'http' ) ) {
@@ -42,10 +44,31 @@ class ImageUploader extends React.Component {
42
44
this . uploadUrl = `${ globalConfig . getAPIPath ( ) } ${ url } ` ;
43
45
}
44
46
} else {
45
- this . uploadUrl = `${ globalConfig . getAPIPath ( ) } ${ globalConfig . upload . image } ` ; // 默认路径
47
+ this . uploadUrl = `${ globalConfig . getAPIPath ( ) } ${ forImage ? globalConfig . upload . image : globalConfig . upload . file } ` ; // 默认上传接口
48
+ }
49
+
50
+ // 上传时的文件大小限制
51
+ if ( this . props . sizeLimit ) {
52
+ this . sizeLimit = this . props . sizeLimit ;
53
+ } else {
54
+ // 默认的大小限制
55
+ if ( forImage ) {
56
+ this . sizeLimit = globalConfig . upload . imageSizeLimit ;
57
+ } else {
58
+ this . sizeLimit = globalConfig . upload . fileSizeLimit ;
59
+ }
60
+ }
61
+
62
+ // 允许上传的文件类型
63
+ if ( this . props . accept ) {
64
+ this . accept = this . props . accept ;
65
+ } else if ( forImage ) {
66
+ this . accept = '.jpg,.png,.gif,.jpeg' ; // 上传图片时有默认的accept
46
67
}
47
68
48
- logger . debug ( 'image upload url = %s' , this . uploadUrl ) ;
69
+ logger . debug ( 'type = %s, upload url = %s, sizeLimit = %d, accept = %s' , type , this . uploadUrl , this . sizeLimit , this . accept ) ;
70
+
71
+ this . forImage = forImage ;
49
72
}
50
73
51
74
componentWillReceiveProps ( nextProps ) {
@@ -113,7 +136,7 @@ class ImageUploader extends React.Component {
113
136
if ( Utils . isString ( value ) && value . length > 0 ) {
114
137
this . state . fileList . push ( {
115
138
uid : - 1 ,
116
- name : value . substr ( value . lastIndexOf ( '/' ) ) , // 取url中的最后一部分作为文件名, 不过这个名字其实没啥用...
139
+ name : value . substr ( value . lastIndexOf ( '/' ) + 1 ) , // 取url中的最后一部分作为文件名
117
140
status : 'done' ,
118
141
url : value ,
119
142
} ) ;
@@ -123,15 +146,15 @@ class ImageUploader extends React.Component {
123
146
// 但如果传进来一个数组, 就只取第一个元素
124
147
this . state . fileList . push ( {
125
148
uid : - 1 ,
126
- name : value [ 0 ] . substr ( value [ 0 ] . lastIndexOf ( '/' ) ) ,
149
+ name : value [ 0 ] . substr ( value [ 0 ] . lastIndexOf ( '/' ) + 1 ) ,
127
150
status : 'done' ,
128
151
url : value [ 0 ] ,
129
152
} ) ;
130
153
} else {
131
154
for ( let i = 0 ; i < value . length ; i ++ ) {
132
155
this . state . fileList . push ( {
133
156
uid : - 1 - i ,
134
- name : value [ i ] . substr ( value [ i ] . lastIndexOf ( '/' ) ) ,
157
+ name : value [ i ] . substr ( value [ i ] . lastIndexOf ( '/' ) + 1 ) ,
135
158
status : 'done' ,
136
159
url : value [ i ] ,
137
160
} ) ;
@@ -147,11 +170,9 @@ class ImageUploader extends React.Component {
147
170
* @returns {boolean }
148
171
*/
149
172
beforeUpload = ( file ) => {
150
- const sizeLimit = this . props . sizeLimit || globalConfig . upload . imageSizeLimit ;
151
- logger . debug ( 'sizeLimit = %d' , sizeLimit ) ;
152
- if ( sizeLimit ) {
153
- if ( file . size / 1024 > sizeLimit ) {
154
- message . error ( `图片过大,最大只允许${ sizeLimit } KB` ) ;
173
+ if ( this . sizeLimit ) {
174
+ if ( file . size / 1024 > this . sizeLimit ) {
175
+ message . error ( `${ this . forImage ? '图片' : '文件' } 过大,最大只允许${ this . sizeLimit } KB` ) ;
155
176
return false ;
156
177
}
157
178
}
@@ -187,31 +208,31 @@ class ImageUploader extends React.Component {
187
208
// 还要自己处理一下fileList
188
209
for ( const tmp of fileList ) {
189
210
if ( tmp . status === 'done' && ! tmp . url && tmp . response && tmp . response . success ) {
190
- tmp . url = tmp . response . data ; // 服务端返回的图片地址
211
+ tmp . url = tmp . response . data ; // 服务端返回的url
191
212
}
192
213
}
193
214
194
215
// 上传失败
195
216
if ( file . status === 'error' ) {
196
217
// debug模式下, 上传是必定失败的, 为了测试用, 给一个默认图片
197
218
if ( globalConfig . debug ) {
198
- message . info ( 'debug模式下使用测试图片' , 2.5 ) ;
219
+ message . info ( `debug模式下使用测试 ${ this . forImage ? '图片' : '文件' } ` , 2.5 ) ;
199
220
fileList . push ( {
200
221
uid : Date . now ( ) ,
201
- name : 'avatar.jpg' ,
222
+ name : this . forImage ? 'avatar.jpg' : 'mapreduce-osdi04.pdf ',
202
223
status : 'done' ,
203
- url : 'http://jxy.me/about/avatar.jpg' ,
224
+ url : this . forImage ? 'http://jxy.me/about/avatar.jpg' : 'https://static.googleusercontent.com/media/research.google.com/zh-CN//archive/mapreduce-osdi04.pdf ',
204
225
} ) ;
205
226
this . notifyFileChange ( ) ;
206
227
} else {
207
- message . error ( `图片 ${ file . name } 上传失败` , 2.5 ) ;
228
+ message . error ( `${ file . name } 上传失败` , 2.5 ) ;
208
229
}
209
230
}
210
231
// 上传成功 or 删除图片
211
232
else if ( file . status === 'done' || file . status === 'removed' ) {
212
233
this . notifyFileChange ( ) ;
213
234
}
214
- // 其实还有正在上传(uploading)的状态, 不过这里不关心
235
+ // 其实还有正在上传(uploading)/错误(error) 的状态, 不过这里不关心
215
236
216
237
// 注意对于controlled components而言, 这步setState必不可少
217
238
// 见https://github.com/ant-design/ant-design/issues/2423
@@ -251,52 +272,95 @@ class ImageUploader extends React.Component {
251
272
}
252
273
} ;
253
274
275
+ /**
276
+ * 上传按钮的样式, 跟文件类型/当前状态都有关
277
+ */
278
+ renderUploadButton ( ) {
279
+ const { fileList} = this . state ;
280
+ const disabled = fileList . length >= this . props . max ;
281
+
282
+ if ( this . forImage ) {
283
+ const button = ( < div >
284
+ < Icon type = "plus" />
285
+ < div className = "ant-upload-text" > 上传图片</ div >
286
+ </ div > ) ;
287
+ // 对于图片而言, 如果文件数量达到max, 上传按钮直接消失
288
+ if ( disabled ) {
289
+ return null ;
290
+ }
291
+ // 是否有提示语
292
+ if ( this . props . placeholder ) {
293
+ return < Tooltip title = { this . props . placeholder } mouseLeaveDelay = { 0 } >
294
+ { button }
295
+ </ Tooltip > ;
296
+ } else {
297
+ return button ;
298
+ }
299
+ } else {
300
+ // 对于普通文件而言, 如果数量达到max, 上传按钮不可用
301
+ const button = < Button disabled = { disabled } > < Icon type = "upload" /> 上传</ Button > ;
302
+ // 是否要有提示语
303
+ if ( this . props . placeholder && ! disabled ) {
304
+ return < Tooltip title = { this . props . placeholder } mouseLeaveDelay = { 0 } >
305
+ { button }
306
+ </ Tooltip > ;
307
+ } else {
308
+ return button ;
309
+ }
310
+ }
311
+ }
312
+
254
313
255
314
render ( ) {
256
315
const { previewVisible, previewImage, fileList} = this . state ;
257
316
258
317
// 我本来是写成accept="image/*"的, 但chrome下有些bug, 要很久才能弹出文件选择框
259
- // 只能用后缀名的写法了, 只支持常见的图片格式
318
+ // 只能用后缀名的写法了
260
319
return (
261
320
< div >
262
321
< Upload
263
322
action = { this . uploadUrl }
264
- listType = "picture-card"
323
+ listType = { this . listType }
265
324
fileList = { fileList }
266
- onPreview = { this . handlePreview }
325
+ onPreview = { this . forImage ? this . handlePreview : undefined }
267
326
onChange = { this . handleChange }
268
327
beforeUpload = { this . beforeUpload }
269
- accept = ".jpg,.png,.gif,.jpeg"
328
+ accept = { this . accept }
270
329
withCredentials = { globalConfig . isCrossDomain ( ) }
271
330
>
272
- { fileList . length >= this . props . max ? null : this . uploadButton }
331
+ { this . renderUploadButton ( ) }
273
332
</ Upload >
333
+ { /*只有上传图片时才需要这个预览modal*/ }
334
+ { this . forImage &&
274
335
< Modal visible = { previewVisible } footer = { null } onCancel = { this . handleCancel } >
275
- < img alt = "加载失败 " style = { { width : '100%' } } src = { previewImage } />
276
- </ Modal >
336
+ < img alt = "图片加载失败 " style = { { width : '100%' } } src = { previewImage } />
337
+ </ Modal > }
277
338
</ div >
278
339
) ;
279
340
}
280
341
281
342
}
282
343
283
- ImageUploader . propTypes = {
284
- max : React . PropTypes . number . isRequired , // 最多可以上传几张图片
285
- sizeLimit : React . PropTypes . number , // 图片的大小限制 , 单位KB
286
- onChange : React . PropTypes . func , // 图片上传后的回调函数, 传入参数是图片的url
287
- defaultValue : React . PropTypes . oneOfType ( [ // 默认值, 可以是单张图片, 也可以是一组图片
344
+ FileUploader . propTypes = {
345
+ max : React . PropTypes . number . isRequired , // 最多可以上传文件数量
346
+ sizeLimit : React . PropTypes . number , // 大小限制 , 单位KB
347
+ onChange : React . PropTypes . func , // 上传后的回调函数
348
+ defaultValue : React . PropTypes . oneOfType ( [ // 默认值, 可以是单个文件, 也可以是一组文件
288
349
React . PropTypes . string ,
289
350
React . PropTypes . array ,
290
351
] ) ,
291
352
value : React . PropTypes . oneOfType ( [ // 受控组件
292
353
React . PropTypes . string ,
293
354
React . PropTypes . array ,
294
355
] ) ,
295
- url : React . PropTypes . string , // 自定义图片的上传地址
356
+ url : React . PropTypes . string , // 自定义上传接口
357
+ type : React . PropTypes . string , // type=image表示上传图片, 否则上传普通文件
358
+ accept : React . PropTypes . string , // 上传时允许选择的文件类型, 例子:".jpg,.png,.gif"
359
+ placeholder : React . PropTypes . string , // 提示语
296
360
} ;
297
361
298
- ImageUploader . defaultProps = {
299
- max : 1 , // 默认只上传一张图片
362
+ FileUploader . defaultProps = {
363
+ max : 1 , // 默认只能上传一个文件
300
364
} ;
301
365
302
- export default ImageUploader ;
366
+ export default FileUploader ;
0 commit comments