最近在统一UI组件库,基于项目的需求,封装了一个专门用来上传图片的控件,主要有以下一些功能:
a. 可以动态配置保存的方式,可以上传到文档库、Redis缓存以及base64保存在数据库中;
b. 动态配置允许上传多少张,超过限制不显示上传按钮;
c. 动态配置允许上传图片的大小,超过限制大小不允许上传;
d. 支持图片的预览以及删除;
e. 支持在父组件中获取已上传的图片信息;
f. 支持动态配置回显图片信息;
g. 支持Form管理值,表单提交直接获取值进行提交;
h. onRemove回调给父组件进行删除后续处理,onChange回调给父组件进行值处理;
import React from 'react';
import {Icon, message, Modal, Upload} from 'antd';
import PropTypes from "prop-types";
import {createUuid} from '../../../utils/NHCore';
import {baseUrl} from '../../../utils/NHPrefixUrl';
import './NHPicturesWall.css';
const fileDocDownloadPath = 'api/docrepo/download?attachmentId=';
const redisDownloadPath = baseUrl + '/proData/downloadRedisCacheImage?uuid=';
/**
* @author weishihuai
* @date 2018/9/12
* @time 9:52
* @Description: 图片墙上传组件
*
* 图片墙使用说明:
* onRemove: this.onImageRemove.bind(this), 非必须, 图片删除之后回传删除图片给父组件的方法
* onChange: this.onChange.bind(this), 非必须, 值改变时执行的方法
* numberOfLimit: 2, 必须, 图片墙允许上传的图片张数,超过numberOfLimit上传按钮自动隐藏
* numberOfSize: 5, 必须, 图片墙允许上传图片大小的最大值,超过numberOfSize的话直接不上传
* saveType: 'file', 必须, 图片墙图片保存的格式,默认为‘file’保存在文档库,‘redis’为缓存在redis,‘base64’保存在内存中,如果需要永久保存文件,建议保存在文档库
* cacheTime: 30, 非必须, Redis缓存时长,单位:分钟 超过30分钟redis会删除缓存的图片信息
* imageList: ['0264a687-baa4-490f-92f5-e988dcd8d976','0264a687-baa4-490f-92f5-e988dcd8d976'] 非必须, 如果是编辑模式下,需要回显已经保存的图片信息,注意一下需要构造如下类型数据才能显示出来:
*/
class NHPicturesWall extends React.Component {
constructor(props) {
super(props);
this.state = {
uploadedImageList: [], //已上传图片
previewImageVisible: false, //是否预览图片标识
previewImageUrl: '', //预览图片的URL
previewImageName: '', //预览图片的名称
}
}
//设置props默认值
static defaultProps = {
saveType: 'file', //保存的类型,默认为file文档库,可选的值: 'file'(上传文档库)、'base64'(保存数据库)、'redis'(缓存redis)
imageList: [], //设置默认上传的图片 格式:imageList: [{ uid: '对应文档库uuid', name: '图片名称',status: 'done', url: '图片url'}]
cacheTime: 30, //设置Redis缓存的时间,单位:分钟 表示Redis超过30分钟会删除缓存的信息
numberOfLimit: 1, //最多允许上传多少张图片 默认为1张
numberOfSize: 2, //默认上传大小限制2MB
disabled: false, //是否禁用
onRemove: () => { //删除成功回调
},
onChange: () => {
}, //值改变时的回调
base64UploadUrl: '', //base64上传图片路径
};
//组件render之前组装已经上传成功的图片信息uploadedImageList,主要用于回显图片
componentWillMount() {
let saveType = this.props.saveType || 'file';
if (saveType === 'base64') {
if (this.props.imageList.length > 0) {
let uploadedImageList = [];
this.props.imageList.map((base64Code) => {
uploadedImageList.push({
uuid: base64Code,
uid: base64Code,
url: base64Code
});
});
this.setState({
uploadedImageList: uploadedImageList
});
let imageList = [];
uploadedImageList.map((file) => {
let obj = {
uuid: file.uuid,
base64Url: file.url
};
imageList.push(obj);
});
if (this.props.onChange && typeof this.props.onChange === "function") {
this.props.onChange(imageList);
}
}
} else {
if (this.props.imageList.length > 0) {
let uploadedImageList = [];
this.props.imageList.map((uuid) => {
uploadedImageList.push({
uuid: uuid,
uid: uuid,
url: saveType === 'file' ? fileDocDownloadPath + uuid : redisDownloadPath + uuid
});
});
this.setState({
uploadedImageList: uploadedImageList
});
let imageList = [];
uploadedImageList.map((file) => {
let obj = {uuid: file.uuid};
imageList.push(obj);
});
if (this.props.onChange && typeof this.props.onChange === "function") {
this.props.onChange(imageList);
}
}
}
}
//图片预览事件
handlePreview = (file) => {
this.setState({
previewImageUrl: file.url || file.thumbUrl,
previewImageName: file.name,
previewImageVisible: true
});
};
//取消图片预览事件
handlePreviewCancel = () => {
this.setState({
previewImageVisible: false
});
};
//文件上传改变事件
handleChange = (e) => {
const saveType = this.props.saveType || 'file';
let fileList = e.fileList;
let fileStatus = e.file.status;
if (fileStatus === 'uploading') { //上传中
// console.log('uploading....');
} else if (fileStatus === 'done') { //上传成功
let response = e.file.response;
if (!response) {
message.error("抱歉,文件由于未知原因上传失败!");
return;
}
let responseMeta = response.meta;
if (saveType === 'file') { //上传到文档库
//上传成功(success为true并且响应码为200)
if (responseMeta && responseMeta.success && responseMeta.statusCode === 200) {
fileList = fileList.map((file) => {
if (file.uid === e.file.uid) {
// file.uuid = response.data.ssbh;
file.uuid = response.data.bh;
}
return file;
});
this.getUploadedImage(fileList, 'file');
} else {
message.error("抱歉,文件由于未知原因上传失败!");
//过滤上传失败的图片
fileList = this.filterUploadFailFile(e.fileList, e.file);
}
} else if (saveType === 'redis') { //缓存Redis
//缓存成功(响应码为200)
if (response.code === 200) {
fileList = fileList.map((file) => {
if (file.uid === e.file.uid) {
file.uuid = response.data;
}
return file;
});
this.getUploadedImage(fileList, 'redis');
} else {
message.error("抱歉,文件由于未知原因上传失败!");
//过滤上传失败的图片
fileList = this.filterUploadFailFile(e.fileList, e.file);
}
} else if (saveType === 'base64') { //用于保存数据库
this.getImageBase64(e.file.originFileObj, (imageUrl) => {
// console.log(imageUrl);
//上传成功
if (response.code === 200) {
fileList = fileList.map((file) => {
if (file.uid === e.file.uid) {
file.uuid = imageUrl;
file.base64Url = imageUrl;
} else {
file.base64Url = file.thumbUrl || file.url;
}
return file;
});
this.getUploadedImage(fileList, 'base64');
} else {
message.error("抱歉,文件由于未知原因上传失败!");
//过滤上传失败的图片
fileList = this.filterUploadFailFile(e.fileList, e.file);
}
});
}
} else if (fileStatus === 'error') { //上传出错
message.error("抱歉,文件由于未知原因上传失败!");
//过滤上传失败的图片
fileList = this.filterUploadFailFile(e.fileList, e.file);
}
if (fileStatus) {
this.setState({
uploadedImageList: fileList
});
}
};
//获取图片Base64
getImageBase64 = (img, callback) => {
const reader = new FileReader();
reader.addEventListener('load', () => callback(reader.result));
reader.readAsDataURL(img);
};
//过滤上传失败的图片
filterUploadFailFile = (list, failUploadedFile) => {
return list.filter(file =>
file.uid !== failUploadedFile.uid
);
};
//获取上传成功的图片
getUploadedImage = (fileList, saveType) => {
if (saveType === 'base64') {
let uploadedImageList = [];
fileList.map((file) => {
let obj = {
uuid: file.uuid,
base64Url: file.base64Url
};
uploadedImageList.push(obj);
});
//父组件回调方法,在父组件可以拿到已经上传成功的图片信息
if (this.props.onChange && typeof this.props.onChange === "function") {
this.props.onChange(uploadedImageList);
}
} else {
let uploadedImageList = [];
fileList.map(file => {
let obj = {
uuid: file.uuid
};
uploadedImageList.push(obj);
});
//父组件回调方法,在父组件可以拿到已经上传成功的图片信息
if (this.props.onChange && typeof this.props.onChange === "function") {
this.props.onChange(uploadedImageList);
}
}
};
//上传文件之前的钩子,参数为准备上传的文件,若返回 false 则停止上传
//一般在beforeUpload方法内限制文件上传的格式以及大小
handelBeforeUpload = (file) => {
let fileType = file.type;
let fileName = file.name;
//判断是否支持该文件格式
let isInvalidFileType = !fileType || fileType.length < 1;
if (isInvalidFileType) {
message.error('抱歉,不支持上传该格式的文件!');
return !isInvalidFileType;
}
let availFileSuffix = ['.png', '.PNG', '.jpg', '.JPG', '.bpm', '.BPM', '.gif', '.GIF'];
let fileSuffixName = fileName.substring(file.name.lastIndexOf('.'));
let isAvailableSuffix = availFileSuffix.includes(fileSuffixName);
if (!isAvailableSuffix) {
let msg = '抱歉,只支持上传【' + availFileSuffix.join(' || ') + '】格式的文件!';
message.error(msg);
return isAvailableSuffix;
}
//限制上传文件大小(默认上传大小限制2MB)
let availSize = this.props.numberOfSize || 2;
let fileSize = file.size / 1024 / 1024;
const isOverSize = fileSize > availSize;
if (isOverSize) {
let msg = '抱歉,上传文件大小最大不能超过' + availSize + 'M!';
message.error(msg);
return !isOverSize;
}
return true;
};
//删除图片事件
handleRemove = (file) => {
let uploadedImageList = this.state.uploadedImageList;
for (let index = 0, len = uploadedImageList.length; index < len; index++) {
if (uploadedImageList[index].uid === file.uid) {
uploadedImageList.splice(index, 1);
break;
}
}
this.setState({
uploadedImageList: uploadedImageList
});
//组装数据返回给父组件,包含文档库的uuid以及文件名称
let imageList = [];
uploadedImageList.length > 0 && uploadedImageList.map((file) => {
let obj = {uuid: file.uuid};
imageList.push(obj);
});
if (this.props.onChange && typeof this.props.onChange === 'function') {
this.props.onChange(imageList);
}
//如果有需要对删除的图片做删除文档库等操作,回传给父组件进行处理
if (this.props.onRemove && typeof this.props.onRemove === 'function') {
this.props.onRemove(file.uuid);
}
};
render() {
const {previewImageVisible, previewImageUrl, uploadedImageList, previewImageName} = this.state;
const numberOfLimit = this.props.numberOfLimit || 1; //默认最多上传一张图片
const saveType = this.props.saveType || 'file'; //默认上传到文档库
const redisCacheTime = this.props.cacheTime || 30; //Redis默认保存时长,单位:分钟
const uploadButton = (
Upload
);
//根据saveType构造上传的url
const action = saveType === 'file' ? 'api/docrepo/upload' : saveType === 'redis' ? baseUrl + '/proData/uploadRedis' : this.props.base64UploadUrl;
//请求发送的数据
let requestData = saveType === 'file' ? {
uuid: createUuid(),
type: '1'
} : saveType === 'redis' ? {
'redisData': redisCacheTime
} : {};
const params = {
name: 'file',
action: action, //图片上传路径
accept: 'image/*', //接受上传的文件类型,指定为image/**的话,弹出选择文件窗口的时候只会显示图片类型文件,过滤掉.txt、.xsl等非图片文件
listType: 'picture-card', //图片墙样式
multiple: false, //是否允许多选
fileList: uploadedImageList, //已上传的图片
data: requestData, //上传所需参数
onRemove: this.handleRemove, //删除执行的方法
beforeUpload: this.handelBeforeUpload, //图片上传前执行的方法
onPreview: this.handlePreview, //预览图片执行的方法
onChange: this.handleChange, //值改变执行的方法
};
return (
{uploadedImageList.length >= numberOfLimit ? null : uploadButton}
);
}
}
//属性检查
NHPicturesWall.PropTypes = {
saveType: PropTypes.string, //保存的类型
imageList: PropTypes.array, //初始化图片信息
cacheTime: PropTypes.number, //Redis缓存时间
numberOfLimit: PropTypes.number, //允许上传的图片张数
numberOfSize: PropTypes.number, //允许上传的图片大小
disabled: PropTypes.bool, //是否禁用
base64UploadUrl: PropTypes.string, //base64图片上传路径
onRemove: PropTypes.func, //删除成功回调
onChange: PropTypes.func, //值改变回调
};
export default NHPicturesWall;
.ant-upload-select-picture-card i {
font-size: 32px;
color: #999;
}
.ant-upload-select-picture-card .ant-upload-text {
margin-top: 8px;
color: #666;
}
import React from "react";
import css from './index.css';
import TestPicturesWall from './TestPicturesWall';
import getSize from "../../../utils/getSize";
import {Scrollbars} from 'react-custom-scrollbars';
export default class PicturesWallContainer extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
);
}
}
/*右侧主内容div样式*/
.main_right_content{
margin: 12px 12px 0 12px;
padding: 16px;
background: #fff;
minHeight: 280;
overflow: hidden;
}
/*表格部分样式*/
.main_right_content .table {
width: 100%;
height: 100%;
}
import React from 'react';
import {Button, Form} from 'antd';
import NHPicturesWall from '../../common/NHPicturesWall/NHPicturesWall';
const FormItem = Form.Item;
/**
* @author weishihuai
* @date 2018/9/13
* @time 14:01
* @Description: 测试图片墙示例
*
* 图片墙使用说明:
* onRemove: this.onImageRemove.bind(this), 非必须, 图片删除之后回传删除图片给父组件的方法
* onChange: this.onChange.bind(this), 非必须, 值改变时执行的方法
* numberOfLimit: 2, 必须, 图片墙允许上传的图片张数,超过numberOfLimit上传按钮自动隐藏
* numberOfSize: 5, 必须, 图片墙允许上传图片大小的最大值,超过numberOfSize的话直接不上传
* saveType: 'file', 必须, 图片墙图片保存的格式,默认为‘file’保存在文档库,‘redis’为缓存在redis,‘base64’保存在内存中,如果需要永久保存文件,建议保存在文档库
* cacheTime: 30, 非必须, Redis缓存时长,单位:分钟 超过30分钟redis会删除缓存的图片信息
* imageList: ['0264a687-baa4-490f-92f5-e988dcd8d976','0264a687-baa4-490f-92f5-e988dcd8d976'] 非必须, 初始化图片列表,需要传入图片唯一标识组成的数组
*/
class TestPicturesWall extends React.Component {
//图片删除成功后的回调方法
onImageRemove = (uuid) => {
//这里可以拿到文件的唯一标识或者base64码,进行具体的处理
console.log(uuid);
};
//表单提交事件
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, fieldsValue) => {
if (err) {
return;
}
//Form表单FieldValues
console.log('-------fieldsValue-------', fieldsValue);
});
};
render() {
const formItemLayout = {
labelCol: {
xs: {span: 24},
sm: {span: 5},
},
wrapperCol: {
xs: {span: 24},
sm: {span: 19},
},
};
let {form} = this.props;
const {getFieldDecorator} = form;
const tailFormItemLayout = {
wrapperCol: {
xs: {span: 24},
sm: {span: 12, offset: 6},
md: {span: 10, offset: 7},
}
};
//file上传到文档库
const fileUploadParams = {
onRemove: this.onImageRemove.bind(this),
numberOfLimit: 3,
numberOfSize: 10,
saveType: 'file'
};
//缓存Redis
const redisUploadParams = {
onRemove: this.onImageRemove.bind(this),
numberOfLimit: 2,
numberOfSize: 5,
saveType: 'redis',
cacheTime: 30, //redis缓存时长,单位:分钟.(超过30分钟redis会删除缓存的图片信息)
};
//Base64保存数据库
const base64UploadParams = {
onRemove: this.onImageRemove.bind(this),
numberOfLimit: 3,
numberOfSize: 10,
saveType: 'base64',
base64UploadUrl: 'api/zhxg-yxxt/fwwgl/sytp/uploadPicture' //base64指定上传路径
};
const fileUploadParams2 = {
onRemove: this.onImageRemove.bind(this),
numberOfLimit: 3,
numberOfSize: 10,
saveType: 'file',
imageList: ['f3b96094-31dd-4694-92e6-14419af5940c', '67dc4599-6c3b-40f0-9b2a-b6ed8a2e3e37'],
};
const redisUploadParams2 = {
onRemove: this.onImageRemove.bind(this),
numberOfLimit: 2,
numberOfSize: 5,
saveType: 'redis',
cacheTime: 30, //redis缓存时长,单位:分钟 超过30分钟redis会删除缓存的图片信息
imageList: ['f3b96094-31dd-4694-92e6-14419af5940c'],
};
const base64UploadParams2 = {
onRemove: this.onImageRemove.bind(this),
numberOfLimit: 3,
numberOfSize: 10,
saveType: 'base64',
base64UploadUrl: 'api/zhxg-yxxt/fwwgl/sytp/uploadPicture', //base64指定上传路径
imageList: [
'data:image/jpeg;base64,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'
],
};
return (
)
}
}
export default Form.create()(TestPicturesWall);