剛到杭州感覺略疲憊开财,后臺大佬(世界上最好的語言的使用者)表示我們的上傳圖片要做成前端直接上傳七牛,然后把地址給后端惹悄。春叫。。泣港。emmm 原來最優(yōu)秀的后臺都會偷懶啊暂殖。
那我只能基于antd的upload封裝出一個比較適用于圖片上傳七牛,oss当纱,后臺的組件了呛每,這是 antd-upload地址
剛學(xué)會hooks并且應(yīng)用于自己的項目,瞎子過河摸索著過唄
import React, { useEffect, useState } from 'react';
const [qiNiuToken, setQiNiuToken] = useState(null);
const qiniuAction = 'http://upload.qiniup.com';
const normFile = info => { // 將upload的值作為裝飾器的value
return (info.file && info.file.response) || undefined;
}
useEffect(() => { // 這里是在didmount和qiNiuToken改變的時候 重新去嘗試請求新的qiniu toekn
if (qiNiuToken) return;
const asyncRequest = async () => {
const token = await appApi.receiveQiniuToken();
setQiNiuToken(token);
}
asyncRequest();
}, [qiNiuToken])
const onUploadEnd = () => { // 這里是在圖片上傳之后 調(diào)用此函數(shù)將qiNiuToken改變 以觸發(fā)重新請求
setQiNiuToken(null);
}
<FormItem>
<Text><Text type="danger">*</Text>主辦單位證件</Text>
{
getFieldDecorator(
'cert', {
valuePropName: 'file',
getValueFromEvent: normFile,
rules: [
{
required: true,
message: '請上傳主辦單位證件!'
}
]
}
)(
<CustomUpload
className="icbc-legal-upload"
placeholder="請上傳營業(yè)執(zhí)照或三證合一掃描件"
action={qiniuAction}
data={
{
token: qiNiuToken
}
}
onUploadEnd={onUploadEnd}
/>
)
}
</FormItem>
接下來是重頭戲 組件
// 因為Form裝飾器getFieldDecorator的某些屬性不支持函數(shù)組件 這里使用類組件的形式
import React, { Component } from 'react';
import { Upload, Icon, message, Modal } from 'antd';
// 圖片轉(zhuǎn)換base64 以及上傳前的檢測
import { getBase64, filterPic } from 'lib/utils';
import './index.scss';
class CustomUpload extends Component {
// imageUrl 是上傳圖片后轉(zhuǎn)base64在頁面中的顯示
// previewVisible 查看大圖的開關(guān)
// showMask 自己寫的上傳圖片后的mask 可以查看大圖以及刪除圖片
constructor(props) {
super(props);
this.state = {
imageUrl: '',
loading: false,
previewVisible: false,
showMask: false
}
}
/**
* export const filterPic = pic => {
let content = '';
const isJpgOrPng = pic.type === 'image/jpeg' || pic.type === 'image/png';
if (!isJpgOrPng) {
content = ' 請上傳JPG/PNG文件坡氯!';
}
const isLt2M = pic.size / 1024 / 1024 < 10;
if (!isLt2M) {
content = '圖像必須小于10MB!';
}
return content;
}
*/
// 上傳圖片之前對圖片進行檢測 大小 類型
beforeUpload = file => {
const content = filterPic(file);
if (content) {
message.error(content);
return false;
}
return true;
}
// 處理上傳中的步驟 done 為上傳完成
handleChange = (info, callback) => {
if (info.file.status === 'uploading') {
this.setState({ loading: true });
return;
}
if (info.file.status === 'done') {
const { onUploadEnd } = this.props;
onUploadEnd(); // 上傳完成后通知父組件 可以做刷新七牛token的操作
callback&&callback(info);// 這里由于要將請求的結(jié)果作為getFieldDecorator裝飾器的value 調(diào)用傳入的callback 實際調(diào)用的就是getValueFromEvent
getBase64(info.file.originFileObj, imageUrl => {
this.setState({
imageUrl,
loading: false
})
});
}
};
onMouseEnter = () => { // 自己寫的一些動畫 略簡陋
const { imageUrl } = this.state;
if (imageUrl) {
this.setState({
showMask: true
})
}
}
onMouseLeave = () => {
this.setState({
showMask: false
})
}
handlePreview = file => { // 查看大圖
this.setState({
previewVisible: true
});
};
handleDelete = () => { // 刪除圖片
const { onChange } = this.props;
Modal.confirm({
title: '提示',
content: '確定要刪除此圖片?',
okText: '確定',
cancelText: '取消',
onOk: () => {
this.setState({
imageUrl: false
}, () => {
onChange({}) //刪掉裝飾器里的val
});
}
});
}
// 取消查看大圖
handleCancel = () => this.setState({ previewVisible: false });
render() {
// data 此次請求攜帶的參數(shù) 傳七牛需要帶token 由父組件傳入 action hostapi
const { className, placeholder, onChange, data = {}, action } = this.props;
const { loading, imageUrl, previewVisible, showMask } = this.state;
return (
<div className="upload-main" onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
<div className={`mask ${showMask ? 'showMask' : ''}`}>
<Icon type="eye" onClick={this.handlePreview} />
<Icon type="delete" onClick={this.handleDelete} />
</div>
<Upload
listType="picture-card"
className={`uploader ${className}`}
showUploadList={false}
action={action}
data={data}
beforeUpload={this.beforeUpload}
onChange={info => this.handleChange(info, onChange)}
>
{
imageUrl ? <img src={imageUrl} alt="pic" style={{ width: '100%', height: '100%' }} /> : <div>
<Icon type={loading ? 'loading' : 'plus'} />
<div className="ant-upload-text">{placeholder || 'Upload'}</div>
</div>
}
</Upload>
<Modal visible={previewVisible} footer={null} onCancel={this.handleCancel} width={600}>
<img alt="example" src={imageUrl} style={{ maxWidth: '100%' }} />
</Modal>
</div>
)
}
}
export default CustomUpload;
index.scss 送給連樣式都懶得寫的你
.upload-main{
position: relative;
width: 300px;
height: 202px;
.mask{
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: #000;
opacity: 0;
transition: all .3s;
visibility: hidden;
display: flex;
justify-content: center;
align-items: center;
&.showMask{
opacity: .4;
visibility: visible;
.anticon{
cursor: pointer;
margin-right: 10px;
font-size: 20px;
color: #fff;
}
}
}
}