import React, { Component } from 'react'; import { PageComponent, } from '../../types/index'; import { message, Spin, Progress, Modal, Icon } from 'antd'; import QiniuUploader from './qiniuUploader' import * as Tools from 'jad-tool'; type limitType = '=='|'<='|'<'|'>'|'>='|'!='; type whProportionType = {width:number,height:number}; type imgCondition = { // 上传相关的 aspectRatio?: number; maxWidth?: number; minWidth?: number; maxHeight?: number; minHeight?: number; whProportion?:whProportionType, validateVolume?:{proportion?:whProportionType,limit:limitType,width:number,height:number}[] fileSizeMax?: number; // 单位kb fileSizeMin?: number; // 单位kb fileSizeLimitValue?: number; fileSizeLimit?: limitType; fileType?: string[]; // [.jpg, .png] maxLength?: number; // 最长 minLength?: number; // 最短 required?: boolean; imgTotalLimit?: number[]; maxNumber?: { limit: number, keys: string[] }; minNumber?: { limit: number, keys: string[] }; equal?: { limit: number, keys: string[] }; pattern?: string; defaultUploadRender?: any; } const defaultCcondition = { fileSizeMax: 1024, //单位kb fileType: ['jpg', 'png'], // [.jpg, .png] } const _getTransfer = function (event: any) { return event.dataTransfer ? event.dataTransfer : event.originalEvent.dataTransfer; // jQuery fix; } const getImgWidth = function (src: string): any { return new Promise(function (resolve, reject) { var img = new Image() img.src = src if (img.complete) { resolve({ width: img.width, height: img.height, }) } else { img.onload = function () { resolve({ width: img.width, height: img.height, }) } img.onerror = function () { reject(false) } } }) } const _haveFiles = function (types: any) { if (!types) { return false; } if (types.indexOf) { return types.indexOf('Files') !== -1; } else if (types.contains) { return types.contains('Files'); } else { return false; } } const _preventAndStop = function (event: any) { event.preventDefault(); event.stopPropagation(); } const limitToText = function(limit?:limitType){ if(!limit) return '' switch(limit){ case "==": return '等于' case '<': return '小于' case '<=': return '小于等于' case '>': return '大于' case '>=': return '大于等于' case '!=': return '不等于' default: return '' } } const limitExec = function(limit:limitType,s1:number,s2:number){ switch(limit){ case "==": return s1 == s2; case '<': return s1 < s2; case '<=': return s1 <= s2; case '>': return s1 > s2; case '>=': return s1 >= s2; case '!=': return s1 !== s2; default: return false } } export class Uploder extends Componentvoid, //must onValidate?: (task:any,config:any,image:HTMLImageElement)=>boolean, CDNToken:string, CDNUrl:string }>> { state = { //实例 uploader: null, //参数 uploadProgress: 0, dialogWidth: 500, previewVisible: false, status: 'empty', loading: false } constructor(props) { super(props) this.resetUpLoadService() } UNSAFE_componentWillReceiveProps(nextProps){ if(!this.props.CDNToken && nextProps.CDNToken){ this.resetUpLoadService() } } resetUpLoadService() { const uploader = this.initUploadService() this.setState({ uploader: uploader }) } initUploadService() { const { condition, onChange=()=>null,CDNToken,CDNUrl } = this.props if(!CDNToken || !CDNUrl) return null; const uploadService = new QiniuUploader() const config = Tools.extend({}, defaultCcondition, condition) const accept = config.fileType.map(e=>e.toLowerCase()) let ifInterrupt = false; let progressInterval; const commonMiddle = { onStart: (task) => { this.setState({ uploadProgress: 0, status: 'uploading' }) progressInterval = setInterval(() => { let { uploadProgress } = this.state if (uploadProgress >= 80) return; uploadProgress += 10; this.setState({ uploadProgress: uploadProgress, }) }, 100); }, onSuccess: (task, url) => { clearInterval(progressInterval); setTimeout(() => { this.setState({ uploadProgress: 100, status: 'done' }) }, 1000); this.setState({ loading: false }) onChange(url) }, onProgress: (task) => { this.setState({ uploadProgress: task.progress, status: 'uploading' }) }, onFinish: () => { this.setState({ loading: false, }) }, onFail: () => { this.setState({ loading: false, uploadProgress: 0, status: 'empty', }) }, onIntercept: (task): Promise => { return new Promise((resolve, reject)=>{ var validateImage = new Image(); validateImage.src = URL.createObjectURL(task._file) validateImage.onload = () => { resolve({task,config,image:validateImage}) } validateImage.onerror = () => reject() }) .then(this.validateImageTypeTransaction.bind(this)) .then(this.validateImageWHTransaction.bind(this)) .then(this.validateImageSizeTransaction.bind(this)) .then(this.validateCustomTransaction.bind(this)) //验证成功返回false .then(()=>false) //验证失败返回true .catch((err)=>{ console.error(err) message.error(Tools.toString(err),5) ifInterrupt = true; return Promise.resolve(true) }) }, onInterrupt: (task) => { //是否取消上传 return true | false if (ifInterrupt) { message.error('图片不符合规格,请重新上传') ifInterrupt = false; return true; } return false; }, }; uploadService.init(accept,CDNToken,CDNUrl) uploadService.addMiddleWares(commonMiddle); return uploadService; } /* 验证失败直接 throw error 成功则返回向下传递 */ validateImageTypeTransaction({task,config,image}){ let { fileType } = config fileType = fileType.map(e=>e.replace('.','')) let uploadtype = task.file.name.split('.').pop(); if (fileType.indexOf(uploadtype) === -1) { throw new Error('验证图片类型失败,不符合上传类型') } return {task,config,image} } validateImageWHTransaction({task,config,image}){ let { maxWidth,minWidth,maxHeight,minHeight,validateVolume,whProportion } = config const imgWidth = image.naturalWidth || image.width const imgHeiht = image.naturalHeight || image.height if (!imgWidth && !imgHeiht) { throw new Error('验证图片失败,未获取到图片宽高') } //limit if(validateVolume){ var errorMsg = '' var batchValidate = validateVolume.some(({limit,width,height,proportion})=>{ if(!limitExec(limit,imgWidth,width)){ errorMsg = `验证图片失败,图片宽度${imgWidth}不符合${limitToText(limit)}${width}限制`; return false; } if(!limitExec(limit,imgHeiht,height)){ errorMsg = `验证图片失败,图片高度${imgHeiht}不符合${limitToText(limit)}${height}限制`; return false; } if(proportion && imgWidth / imgHeiht !== proportion.width/proportion.height ){ errorMsg = `验证图片失败,图片宽度高度比例不符合${proportion.width}:${proportion.height}限制`; return false; } return true }) if(!batchValidate){ throw new Error(errorMsg) } return {task,config,image} } //default if( maxWidth && imgWidth>maxWidth ){ throw new Error(`验证图片失败,图片宽度${imgWidth}超出限制`) } if( minWidth && imgWidthmaxHeight ){ throw new Error(`验证图片失败,图片高度${imgHeiht}超出限制`) } if( minHeight && imgHeiht>minHeight ){ throw new Error(`验证图片失败,图片高度${imgHeiht}小余限制`) } if(whProportion && imgWidth / imgHeiht !== whProportion.width/whProportion.height ){ throw new Error(`验证图片失败,图片宽度高度比例不符合${whProportion.width}:${whProportion.height}限制`) } return {task,config,image} } validateImageSizeTransaction({task,config,image}){ const { fileSizeMax,fileSizeMin,fileSizeLimit,fileSizeLimitValue } = config const size = Math.ceil(task._file.size / 1024) if(fileSizeLimit && fileSizeLimitValue && !limitExec(fileSizeLimit,size,fileSizeLimitValue)){ throw new Error( `验证图片失败,图片大小${size}不符合${limitToText(fileSizeLimit)}${fileSizeLimitValue}限制`) } if(fileSizeMax && size > fileSizeMax){ throw new Error(`验证图片失败,图片大小${size}kb超出限制`) } if(fileSizeMin && size < fileSizeMin){ throw new Error(`验证图片失败,图片大小${size}kb小于限制`) } return {task,config,image} } validateCustomTransaction({task,config,image}){ const { onValidate } = this.props if(onValidate){ const result = onValidate(task,config,image) if(!result){ throw new Error(`验证图片失败`) } } return {task,config,image} } onHandleClick(e) { const { uploader } = this.state const { disabled } = this.props if(disabled){ return false; } if (!uploader) { return this.resetUpLoadService() } let { url } = this.props; if (url) { return } _preventAndStop(e) uploader.popupUplod() } onHandleFileDrop(e) { const { uploader } = this.state const { disabled } = this.props if(disabled){ return false; } if (!uploader) { return this.resetUpLoadService() } _preventAndStop(e) const transfer = _getTransfer(event); if (!_haveFiles(transfer.types)) { return; } uploader.upload(transfer.files); } onHandleDropMove(e) { _preventAndStop(e) const transfer = _getTransfer(event); if (!_haveFiles(transfer.types)) { return; } transfer.dropEffect = 'copy'; } onPreview(e) { e.stopPropagation(); let { url,value } = this.props; url = url || value getImgWidth(url).then((res) => { let dialogWidth = res.width if (dialogWidth < 50) { dialogWidth = 50 } if (dialogWidth > 1200) { dialogWidth = 1200 } this.setState({ previewVisible: true, dialogWidth }) }) } onDelete(e) { e.stopPropagation(); const { disabled,onChange=()=>null } = this.props if(disabled){ return false; } this.setState({ status: 'empty', }) onChange('') } renderProgress() { const { uploadProgress, status } = this.state if (status === 'empty' || status === 'done') { return null } return (
) } renderImgPreview(){ let { url,value,imgWrapStyle } = this.props; url = url || value return
读取失败
} renderUpload(){ let { condition,volumeText,typeText,sizeText } = this.props; let config = Tools.extend({}, defaultCcondition, condition) const { fileType,fileSizeMax,width,height,validateVolume,whProportion,fileSizeLimitValue,fileSizeLimit } = config return config.defaultUploadRender?config.defaultUploadRender(config):

点击或将文件拖拽到这里上传

支持扩展名:  {typeText? typeText: fileType.map(e => e.replace(/[^a-zA-Z]/g, '')).join(',').toLowerCase()}, 图片大小:{volumeText? volumeText: validateVolume?validateVolume.map(({limit,width,height,proportion},index)=>`${limitToText(limit)}${width}px*${height}px${index ===validateVolume.length -1?'':','}${proportion?`,宽高比:${proportion.width}:${proportion.height}${index ===validateVolume.length -1?'':','}`:''}`) :Tools.isUndefined(width)||Tools.isUndefined(height)?'不限':`${width}px*${height}px`}, {whProportion?`宽高比:${whProportion.width}:${whProportion.height},`:null} {fileSizeLimit &&fileSizeLimitValue ?`${limitToText(fileSizeLimit)}${(fileSizeLimitValue<1000?`${fileSizeLimitValue}KB`:`${((fileSizeLimitValue) / 1024).toFixed(1)}M`)}` :`小于等于${sizeText? sizeText: (fileSizeMax<1000?`${fileSizeMax}KB`:`${((fileSizeMax) / 1024).toFixed(1)}M`)}`}
} renderContent() { let { url,value } = this.props; return url || value ? this.renderImgPreview() : this.renderUpload() } render() { let { url,value,disabled=false } = this.props; const { loading, dialogWidth } = this.state url = url || value return (
{ this.renderContent() } {this.renderProgress()}
{ this.setState({ previewVisible: false }) }}>
图片加载失败
) } }