刚到杭州感觉略疲惫,后台大佬(世界上最好的语言的使用者)表示我们的上传图片要做成前端直接上传七牛,然后把地址给后端。。。。emmm 原来最优秀的后台都会偷懒啊。
那我只能基于antd的upload封装出一个比较适用于图片上传七牛,oss,后台的组件了,这是 antd-upload地址
刚学会hooks并且应用于自己的项目,瞎子过河摸索着过呗
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 = () => { // 这里是在图片上传之后 调用此函数将qiNiuToken改变 以触发重新请求
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="请上传营业执照或三证合一扫描件"
action={qiniuAction}
data={
{
token: qiNiuToken
}
}
onUploadEnd={onUploadEnd}
/>
)
}
</FormItem>
接下来是重头戏 组件
// 因为Form装饰器getFieldDecorator的某些属性不支持函数组件 这里使用类组件的形式
import React, { Component } from 'react';
import { Upload, Icon, message, Modal } from 'antd';
// 图片转换base64 以及上传前的检测
import { getBase64, filterPic } from 'lib/utils';
import './index.scss';
class CustomUpload extends Component {
// imageUrl 是上传图片后转base64在页面中的显示
// previewVisible 查看大图的开关
// 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);// 这里由于要将请求的结果作为getFieldDecorator装饰器的value 调用传入的callback 实际调用的就是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 此次请求携带的参数 传七牛需要带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;
}
}
}
}