基於Antd Design的Upload圖片牆元件封裝
阿新 • • 發佈:2018-12-09
基於專案需求,對Upload元件進行了封裝,主要有以下一些功能:
a. 可以動態配置儲存的方式,可以上傳到文件庫、Redis快取以及base64儲存在資料庫中
b. 動態配置允許上傳多少張,超過限制不顯示上傳按鈕
c.動態配置允許上傳圖片的大小,超過限制大小不允許上傳
d.支援圖片的預覽以及刪除
e.支援在父元件中獲取已上傳的圖片資訊
....
一、NHPicturesWall.js
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'; /** * @author weishihuai * @date 2018/9/12 * @time 9:52 * @Description: 圖片牆上傳元件 * * 圖片牆元件說明: * onSuccess: this.onImageUploadSuccess.bind(this), 圖片上傳成功之後執行的方法,在父元件中可以拿到已經上傳成功的圖片資訊 * onRemove: this.onImageRemove.bind(this), 圖片刪除成功之後執行的方法,在父元件中可以拿到刪除之後已經上傳成功的圖片資訊 * onChangeFieldValue: this.onChangeValue.bind(this), 值改變時執行的方法,主要是用於回顯圖片 * numberOfLimit: 2, 圖片牆允許上傳的圖片張數,超過numberOfLimit上傳按鈕自動隱藏 * numberOfSize: 5, 圖片牆允許上傳圖片大小的最大值,超過numberOfSize的話直接不上傳 * saveType: 'fileDoc', 圖片牆圖片儲存的格式,預設為fileDoc儲存在文件庫,redis為快取在redis,base64儲存在記憶體中,如果需要永久儲存檔案,建議儲存在文件庫 * id: 'imageUrl5', 必填,註冊FormItem必須使用這個ID * cacheTime: 30, Redis快取時長,單位:分鐘 超過30分鐘redis會刪除快取的圖片資訊 * imageList: [{ 儲存模式不需要傳該屬性,如果是編輯模式下,需要回顯已經儲存的圖片資訊,注意一下每種形式下需要構造如下型別資料才能正常顯示出來: * uuid: 'f3b96094-31dd-4694-92e6-14419af5940c', 對應圖片檔案的唯一標識 * uid: 'f3b96094-31dd-4694-92e6-14419af5940c', 對應圖片檔案的唯一標識 * name: 'timp1.png', 圖片檔案的名稱 * url: 'api/zhxg-yxxt/proData/downloadRedisCacheImage?uuid=0264a687-baa4-490f-92f5-e988dcd8d976', 圖片下載的路徑,指定之後自動去下載 * status: 'done', 狀態 * response: 'custom error message' 可以自定義一些錯誤資訊提示 * }] */ class NHPicturesWall extends React.Component { constructor(props) { super(props); this.state = { uploadedImageList: [], //已上傳圖片 previewImageVisible: false, //是否預覽圖片標識 previewImageUrl: '', //預覽圖片的URL previewImageName: '', //預覽圖片的名稱 } } //設定props預設值 static defaultProps = { id: '', //getFieldDecorator註冊FormItem的標識,必須唯一 saveType: 'fileDoc', //儲存的型別,預設為fileDoc文件庫,可選的值: 'fileDoc'(上傳文件庫)、'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, //是否禁用 onSuccess: () => { //上傳成功回撥 }, onRemove: () => { //刪除成功回撥 }, onChangeFieldValue: () => { }, //值改變時的回撥 base64UploadUrl: '', //base64上傳圖片路徑 }; //元件render之前組裝已經上傳成功的圖片資訊uploadedImageList,主要用於回顯圖片 componentWillMount() { let id = this.props.id; if (this.props.imageList.length > 0) { let uploadedImageList = []; this.props.imageList.map((file) => { uploadedImageList.push({ uuid: file.uuid, uid: file.uid, name: file.name, url: file.url, status: file.status, response: file.reponse }); }); this.setState({ uploadedImageList: uploadedImageList }); let imageList = []; uploadedImageList.map((file) => { let obj = { uuid: file.uuid, name: file.name }; imageList.push(obj); }); if (this.props.onChangeFieldValue && typeof this.props.onChangeFieldValue === "function") { this.props.onChangeFieldValue(id, 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 || 'fileDoc'; 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 === 'fileDoc') { //上傳到文件庫 //上傳成功(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); } 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); } 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 = response.data; } return file; }); this.getUploadedImage(fileList); } 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) => { let uploadedImageList = []; fileList.map((file) => { let obj = { uuid: file.uuid, name: file.name }; uploadedImageList.push(obj); }); //父元件回撥方法,在父元件可以拿到已經上傳成功的圖片資訊 if (this.props.onSuccess && typeof this.props.onSuccess === "function") { this.props.onSuccess(this.props.id, 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, name: file.name }; imageList.push(obj); }); //將刪除之後的圖片更新到父元件中 if (this.props.onRemove && typeof this.props.onRemove === 'function') { this.props.onRemove(this.props.id, imageList); } }; render() { const {previewImageVisible, previewImageUrl, uploadedImageList, previewImageName} = this.state; const numberOfLimit = this.props.numberOfLimit || 1; //預設最多上傳一張圖片 const saveType = this.props.saveType || 'fileDoc'; //預設上傳到文件庫 const redisCacheTime = this.props.cacheTime || 30; //Redis預設儲存時長,單位:分鐘 const uploadButton = ( <div> <Icon type='plus'/> <div className="ant-upload-text">Upload</div> </div> ); //根據saveType構造上傳的url const action = saveType === 'fileDoc' ? 'api/docrepo/upload' : saveType === 'redis' ? baseUrl + '/proData/uploadRedis' : this.props.base64UploadUrl; //請求傳送的資料 let requestData = saveType === 'fileDoc' ? { 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 ( <div className="clearfix"> <Upload{...params}> {uploadedImageList.length >= numberOfLimit ? null : uploadButton} </Upload> <Modal visible={previewImageVisible} footer={null} onCancel={this.handlePreviewCancel}> <img alt={previewImageName} style={{width: '100%'}} src={previewImageUrl}/> </Modal> </div> ); } } //屬性檢查 NHPicturesWall.PropTypes = { id: PropTypes.string, //FormItem唯一標識 saveType: PropTypes.string, //儲存的型別 imageList: PropTypes.array, //初始化圖片資訊 cacheTime: PropTypes.number, //Redis快取時間 numberOfLimit: PropTypes.number, //允許上傳的圖片張數 numberOfSize: PropTypes.number, //允許上傳的圖片大小 disabled: PropTypes.bool, //是否禁用 base64UploadUrl: PropTypes.string, //base64圖片上傳路徑 onSuccess: PropTypes.func, //上傳成功回撥 onRemove: PropTypes.func, //刪除成功回撥 onChangeFieldValue: PropTypes.func, //值改變回調 }; export default NHPicturesWall;
二、NHPicturesWall.css
.ant-upload-select-picture-card i {
font-size: 32px;
color: #999;
}
.ant-upload-select-picture-card .ant-upload-text {
margin-top: 8px;
color: #666;
}
三、PicturesWallContainer.js
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 className={css.main_right_content} style={{height: getSize().windowH - 123}}> <Scrollbars autoHide> <TestPicturesWall/> </Scrollbars> </div> ); } }
四、index.css
/*右側主內容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%;
}
五、TestPicturesWall.js
import React from 'react'; import {Button, Form} from 'antd'; import NHPicturesWall from '../../common/NHPicturesWall/NHPicturesWall'; import {createUuid} from '../../../utils/NHCore'; const FormItem = Form.Item; /** * @author weishihuai * @date 2018/9/13 * @time 14:01 * @Description: 測試圖片牆示例 * * 圖片牆使用說明: * onSuccess: this.onImageUploadSuccess.bind(this), 圖片上傳成功之後執行的方法,在父元件中可以拿到已經上傳成功的圖片資訊 * onRemove: this.onImageRemove.bind(this), 圖片刪除成功之後執行的方法,在父元件中可以拿到刪除之後剩餘已上傳成功的圖片資訊 * onChangeFieldValue: this.onChangeValue.bind(this), 值改變時執行的方法,主要是用於回顯圖片 * numberOfLimit: 2, 圖片牆允許上傳的圖片張數,超過numberOfLimit上傳按鈕自動隱藏 * numberOfSize: 5, 圖片牆允許上傳圖片大小的最大值,超過numberOfSize的話直接不上傳 * saveType: 'fileDoc', 圖片牆圖片儲存的格式,預設為fileDoc儲存在文件庫,redis為快取在redis,base64儲存在記憶體中,如果需要永久儲存檔案,建議儲存在文件庫 * id: 'imageUrl5', 註冊FormItem必須使用這個ID * cacheTime: 30, Redis快取時長,單位:分鐘 超過30分鐘redis會刪除快取的圖片資訊 * imageList: [{ 儲存模式不需要傳該屬性,如果是編輯模式下,需要回顯已經儲存的圖片資訊,注意一下每種形式下需要構造如下型別資料才能正常顯示出來: * uuid: 'f3b96094-31dd-4694-92e6-14419af5940c', 對應圖片檔案的唯一標識 * uid: 'f3b96094-31dd-4694-92e6-14419af5940c', 對應圖片檔案的唯一標識 * name: 'timp1.png', 圖片檔案的名稱 * url: 'api/zhxg-yxxt/proData/downloadRedisCacheImage?uuid=0264a687-baa4-490f-92f5-e988dcd8d976', 圖片下載的路徑,指定之後自動去下載 * status: 'done', 狀態 * response: 'success' 可以自定義一些錯誤資訊提示 * }] */ class TestPicturesWall extends React.Component { //圖片上傳成功之後的回撥方法 onImageUploadSuccess = (id, uploadedImageList) => { let fieldsObj = {}; fieldsObj[id] = uploadedImageList; this.props.form.setFieldsValue(fieldsObj); }; //圖片刪除成功自後的回撥方法 onImageRemove = (id, uploadedImageList) => { let fieldsObj = {}; fieldsObj[id] = uploadedImageList; this.props.form.setFieldsValue(fieldsObj); }; //FormItem值改變執行的方法 onChangeValue = (id, imageList) => { let fieldsObj = {}; fieldsObj[id] = imageList; this.props.form.setFieldsValue(fieldsObj); }; //表單提交事件 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}, } }; //fileDoc上傳到文件庫 const fileDocUploadParams = { onSuccess: this.onImageUploadSuccess.bind(this), onRemove: this.onImageRemove.bind(this), onChangeFieldValue: this.onChangeValue.bind(this), numberOfLimit: 3, numberOfSize: 10, id: 'imageUrl1', saveType: 'fileDoc' }; //快取Redis const redisUploadParams = { onSuccess: this.onImageUploadSuccess.bind(this), onRemove: this.onImageRemove.bind(this), onChangeFieldValue: this.onChangeValue.bind(this), numberOfLimit: 2, numberOfSize: 5, saveType: 'redis', id: 'imageUrl2', cacheTime: 30, //redis快取時長,單位:分鐘.(超過30分鐘redis會刪除快取的圖片資訊) }; //Base64儲存資料庫 const base64UploadParams = { onSuccess: this.onImageUploadSuccess.bind(this), onRemove: this.onImageRemove.bind(this), onChangeFieldValue: this.onChangeValue.bind(this), numberOfLimit: 3, numberOfSize: 10, saveType: 'base64', id: 'imageUrl3', base64UploadUrl: 'api/zhxg-yxxt/fwwgl/sytp/uploadPicture' //base64指定上傳路徑 }; const fileDocUploadParams2 = { onSuccess: this.onImageUploadSuccess.bind(this), onRemove: this.onImageRemove.bind(this), onChangeFieldValue: this.onChangeValue.bind(this), numberOfLimit: 3, numberOfSize: 10, saveType: 'fileDoc', id: 'imageUrl4', imageList: [ { uuid: 'f3b96094-31dd-4694-92e6-14419af5940c', uid: 'f3b96094-31dd-4694-92e6-14419af5940c', name: 'timp1.png', url: '/api/docrepo/download?attachmentId=f3b96094-31dd-4694-92e6-14419af5940c', status: 'done', response: '' }, { uuid: '67dc4599-6c3b-40f0-9b2a-b6ed8a2e3e37', uid: '67dc4599-6c3b-40f0-9b2a-b6ed8a2e3e37', name: 'timp1.png', url: '/api/docrepo/download?attachmentId=67dc4599-6c3b-40f0-9b2a-b6ed8a2e3e37', status: 'done', response: '' } ], }; const redisUploadParams2 = { onSuccess: this.onImageUploadSuccess.bind(this), onRemove: this.onImageRemove.bind(this), onChangeFieldValue: this.onChangeValue.bind(this), numberOfLimit: 2, numberOfSize: 5, saveType: 'redis', id: 'imageUrl5', cacheTime: 30, //redis快取時長,單位:分鐘 超過30分鐘redis會刪除快取的圖片資訊 imageList: [ { uuid: 'f3b96094-31dd-4694-92e6-14419af5940c', uid: 'f3b96094-31dd-4694-92e6-14419af5940c', name: 'timp1.png', url: 'api/zhxg-yxxt/proData/downloadRedisCacheImage?uuid=0264a687-baa4-490f-92f5-e988dcd8d976', status: 'done', response: '' } ], }; const base64UploadParams2 = { onSuccess: this.onImageUploadSuccess.bind(this), onRemove: this.onImageRemove.bind(this), onChangeFieldValue: this.onChangeValue.bind(this), numberOfLimit: 3, numberOfSize: 10, id: 'imageUrl6', saveType: 'base64', base64UploadUrl: 'api/zhxg-yxxt/fwwgl/sytp/uploadPicture', //base64指定上傳路徑 imageList: [ { uuid: 'Y712xvXW[1536808009954].jpg', uid: 'Y712xvXW[1536808009954].jpg', name: 'timp1.png', url: 'api/zhxg-yxxt/fwwgl/sytp/getPicture/235d761c-fe3f-420d-a4d2-055966aa49f6?uuid=' + createUuid(), status: 'done', response: '' } ], }; return ( <Form layout="horizontal" onSubmit={this.handleSubmit}> <FormItem {...formItemLayout} label="圖片牆(上傳到文件庫)" > {getFieldDecorator('imageUrl1', {rules: [{required: true, message: '請上傳圖片!'}]})( <NHPicturesWall {...fileDocUploadParams}/> )} </FormItem> <FormItem {...formItemLayout} label="圖片牆(快取Redis)" > {getFieldDecorator('imageUrl2', {rules: [{required: true, message: '請上傳圖片!'}]})( <NHPicturesWall {...redisUploadParams}/> )} </FormItem> <FormItem {...formItemLayout} label="圖片牆(Base64儲存資料庫)" > {getFieldDecorator('imageUrl3', {rules: [{required: true, message: '請上傳圖片!'}]})( <NHPicturesWall {...base64UploadParams}/> )} </FormItem> <FormItem {...formItemLayout} label="圖片牆(上傳到文件庫)--回顯圖片" > {getFieldDecorator('imageUrl4', {rules: [{required: true, message: '請上傳圖片!'}]})( <NHPicturesWall {...fileDocUploadParams2}/> )} </FormItem> <FormItem {...formItemLayout} label="圖片牆(快取Redis)--回顯圖片" > {getFieldDecorator('imageUrl5', {rules: [{required: true, message: '請上傳圖片!'}]})( <NHPicturesWall {...redisUploadParams2}/> )} </FormItem> <FormItem {...formItemLayout} label="圖片牆(Base64儲存資料庫)--回顯圖片" > {getFieldDecorator('imageUrl6', {rules: [{required: true, message: '請上傳圖片!'}]})( <NHPicturesWall {...base64UploadParams2}/> )} </FormItem> <FormItem {...tailFormItemLayout}> <Button type="primary" htmlType="submit">儲存</Button> </FormItem> </Form> ) } } export default Form.create()(TestPicturesWall);