Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React-react+one-ui 原生实现图片上传功能(附文件上传) #19

Open
PleaseStartYourPerformance opened this issue Jan 27, 2021 · 0 comments

Comments

@PleaseStartYourPerformance
Copy link
Owner

PleaseStartYourPerformance commented Jan 27, 2021

组件代码

注意input中的accept属性

图片:

const img = ".bmp, .jpeg, .jpg, .png, .gif";

文件:

const fileTypeAccept = ".txt, .doc, .docx, .xlsx, .xls, .pptx, .ppt, .zip, .rar, .pdf, .tar, .mp4, .jpeg, .jpg, .png";
import * as React from 'react';
import './picupload.less';
import { Loading,Message } from '@baidu/one-ui';
//上传按钮的图标
import Addpic from './imgs/addpic.svg'
//重新上传图标
import Upload from './imgs/upload.svg'
//放大的图标
import Zoom from './imgs/zoom.svg'
import {getUpload } from '../api';
interface Props {
    handlePicChange: (
        picUrl: string, 
        picFileid: number, 
        picName: string, 
        picSizeText: string, 
        picSize: number) => void;
    //临时展示的url
    picUrl: string;
    //一个页面多个上传需要唯一id
    uploadId?:string | 'uploadFiles'
}
interface States {
    picInitialUrl: string;
    isPicLoading: boolean;
    isShow: boolean;
    isEnlarge: boolean;
}
export default class PicUpload extends React.PureComponent<Props, States> {
    state = {
        picInitialUrl: '',
        isPicLoading: false,
        isShow: false,
        isEnlarge: false
    }
    //选择文件后会触发
    uploadImage = async (e: React.ChangeEvent<HTMLInputElement>) => {
        this.setState({
            picInitialUrl: ''
        });
        const name = e.target.files![0].name;
        const size = e.target.files![0].size;
        const dValue = size / 1000 / 1024 - 2;
        let sizeText = '';
        if (dValue > 0){
            return Message.warning({
                content: '图片不超过2M'
            })
        }
        if (size < 1024) {
            sizeText = size + 'B';
        }
        else if (size < 1024 * 1024) {
            sizeText = (size / 1024).toFixed(2) + 'KB';
        }
        else {
            sizeText = (size / 1024 / 1024).toFixed(2) + 'MB';
        }
        //加载中
        this.setState({
            isPicLoading: true
        });
        const oFile = e.target.files[0];
        const formData = new FormData();
        formData.append("file", oFile);
        try {
            const res = await getUpload(formData);
            const {url, fileid} = res.data.data;
            this.props.handlePicChange(url, fileid, name, sizeText, size);
            this.setState({
                isPicLoading: false
            });
        } catch (error) {
            if (error.msg) {
                Message.error({
                    content: '上传失败,请重新上传'
                })
                this.setState({
                    isPicLoading: false
                });
            }
        }
    }
    //触发input点击事件
    uploadClick = () =>{
        const {uploadId} =  this.props
        const uploadFile = document.getElementById(uploadId || 'uploadFiles')
        uploadFile?.click();
    }
    render() {
        const { picUrl,uploadId } = this.props;
        const { isShow, isEnlarge } = this.state
        return <div>
            {
                this.state.isPicLoading
                    ? <div className="upload-material-wrapper">
                        <div className="upload-add">
                            <Loading size="large" className="loading-icon" />
                            <div className="add-title">上传中...</div>
                        </div>
                    </div>
                    : !this.props.picUrl
                        ? <div className="upload-material-wrapper">
                            <div className="upload-add" onClick={this.uploadClick}>
                                <img src={Addpic} className="add-img" />
                                <div className="add-title">上传图片</div>
                            </div>
                        </div>
                        : <div
                            className={`upload-material-wrapper uploaded-material-wrapper`}
                            onMouseEnter={() => {
                                this.setState({
                                    isShow: true
                                })
                            }}
                            onMouseLeave={() => {
                                this.setState({
                                    isShow: false
                                })
                            }}
                        >
                            <div className="upload-add add-pic">
                                <img src={picUrl} className="uploaded-pic" />
                            </div>
                            {
                                isShow &&
                                <div className="upload-shadow">
                                    <img
                                        src={Upload}
                                        className='upload-shadow-img'
                                        onClick={this.uploadClick}
                                    />
                                    <img
                                        src={Zoom}
                                        className='upload-shadow-img'
                                        onClick={() => {
                                            this.setState({
                                                isEnlarge: true
                                            })
                                        }}
                                    />
                                </div>
                            }
                        </div>
            }
            {/* 限制文件类型 */}
            <input
                accept=".bmp, .jpeg, .jpg, .png, .gif"
                type="file"
                id={uploadId || 'uploadFiles'}
                className="upload-img"
                onChange={this.uploadImage}
                value={this.state.picInitialUrl}
            />
            {/* 放大模块 */}
            {
                isEnlarge &&
                <div className='enlarge-img-mask'>
                    <div className='img-wrapper'>
                        <img src={picUrl} alt="" />
                        <span
                            className='close-enlarge'
                            onClick={() => {
                                this.setState({
                                    isEnlarge: false
                                })
                            }}>
                        </span>
                    </div>
                </div>
            }
        </div>
    }

};

Css

@border-color: #e0e0e0;
@hover-background: #fff;
@font-color-main: #333;
@font-color-subtext: #999;
@table-th-border-color: #eee;
.upload-img-wrapper {
    display: inline-block;
    position: relative;
}
.upload-img{
    position: absolute;
    display: none;
    // left: 0;
    // top: 0;
    // opacity: 0;
    // width: 100%;
    // height: 100%;
    // max-height: 100%;
    // overflow: hidden;
}
.upload-material-wrapper {
    position: relative;
    background: #fafafa;
    border-radius: 3px;
    width: 136px;
    text-align: center;
    &.uploaded-material-wrapper {
        text-align: left;
    }
    &.uploaded-audio-wrapper,
    &.uploaded-file-wrapper {
        width: 260px;
    }
    .upload-add {
        width: 100%;
        height: 136px;
        border: 1px solid @border-color;
        border-radius: 3px;
        cursor: pointer;
        &.add-pic {
            position: relative;
            display: flex;
            justify-content: center;
            align-items: center;
            background: @hover-background;
        }
    }
    .upload-shadow{
        background: rgba(0,0,0,0.4);
        border-radius: 0 0 3px 3px;
        width: 101%;
        height: 27px;
        position: absolute;
        bottom: 0px;

    }
    .upload-shadow-img{
        float:right;
        margin-top: 7px;
        margin-right: 10px;
        cursor:pointer;
    }
    .loading-icon {
        margin-top: 26px;
        margin-bottom: 11px;
    }
    .add-img {
        margin-top: 34px;
        margin-bottom: 17px;
    }
    .add-title {
        width: 64px;
        height: 20px;
        color:#fff;
        text-align: center;
        background: #3275FA;
        border-radius: 2px;
        font-size:12px;
        line-height: 20px;
        letter-spacing: 0;
        font-family: PingFangSC-Regular;
        margin:0 auto;
        cursor: pointer;
    }
    .uploaded-pic {
        max-width: 100%;
        max-height: 100%;
    }
    .pic-size {
        font-size: 14px;
        color: @font-color-subtext;
        line-height: 22px;
        margin: 4px 0;
        text-align: left;
    }
    .pic-name {
        color: @font-color-main;
        line-height: 22px;
        text-align: left;
        width: 352px;
        white-space: pre-wrap;
        word-break: break-all;
    }
}
.upload-material-wrapper:not(.uploaded-material-wrapper) {
    background: #fff;
    // &:hover {
    //     background: #f6f7fa;
    // }
    // &:active {
    //     background: @table-th-border-color;
    // }
}
.enlarge-img-mask{
    display: flex;
    justify-content: center;
    align-items: center;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.6);
    z-index: 999;
}
.img-wrapper{
    position: absolute;
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 10px;
    max-width: 820px;
    max-height: 520px;
}
.close-enlarge{
    position: absolute;
    top: -50px;
    right: -50px;
    width: 20px;
    height: 20px;
    background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACoAAAAqCAYAAADFw8lbAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsSAAALEgHS3X78AAABMElEQVRYw83ZbWrDMAyAYbErNKMX2Oh2oCUtO393h47Aux91IAt14w9Zsn7KwjxgC4MsIiLAEbgAn9JJAB/BdFwSB+CHe/wCYwfIMVgItoMAX/yP2RMbkPPGNArwBtx6wEaQN+B9KTg/KDDFRpAzMG0L3bDJSE9sNtIDW4y0xFYjLbBqyJZYdWQLbDOkJrY5UgNrhqzBmiNLsG7IHKw7MgXbDTIB2w9yB9sXcoWdIlg15IuWtXDNLiKNk/0oeCBjzeSDjdzLOeSnLrDPkAk1NtgUpDs2B+mGLUGaY2uQZlgNZHOsJrIZtgVSHftko3MtUg1rgazGWiKLsR7IbKwnMhlLfDRuhtzB3kfjPP5sMEfuYEcBBuC60rshN9jllK/AsCwMwDdw8kausKdgehUR+QN/8sykW52BfwAAAABJRU5ErkJggg==) no-repeat;
    background-size: 100%;
    cursor: pointer;
}

.enlarge-img-mask .img-wrapper img {
    max-width:800px;
    max-height:500px;
}

效果

image
image
image

使用

handlePicChange:中暴露出了 rl, fileid, name, sizeText, size 属性 根据接口 可修改组件

                <PicUpload
                    handlePicChange={this.handlePicChange()}
                    uploadId='S4XE'
                    picUrl={businessFiles.url}
                />

文件上传思路 和校验

图片:

export const uploadImg = (
    files: FileList,
    option?: AxiosRequestConfig
) => {
    const imgTypes = [
        'bmp',
        'cis-cod',
        'gif',
        'ief',
        'jpeg',
        'pipeg',
        'png',
        'svg',
        '+xml',
        'tiff',
        'x-cmu-raster',
        'x-cmx',
        'x-icon',
        'x-portable-anymap',
        'x-portable-bitmap',
        'x-portable-graymap',
        'x-portable-pixmap',
        'x-rgb',
        'x-xbitmap',
        'x-xpixmap',
        'x-xwindowdump'
    ].map(type => `image/${type}`).join('|');
    const rFilter = new RegExp(`^(?:${imgTypes})$`, 'i');
    if (files.length === 0) {
        Message.error({ content: "请选择文件!"});
        return Promise.reject('请选择文件!');
    }
    const oFile = files[0];
    if (!rFilter.test(oFile.type)) {
        Message.error({ content: "请选择图片!"});
        return Promise.reject('请选择图片!');
    }
    const formData = new FormData();
    formData.append("file", oFile);
    return request.upload<{}, IUpload>('/upload', formData, option);
};

文件:

// 上传文件
export const uploadFile = (
    files: FileList,
    option?: AxiosRequestConfig
) => {
    if (files.length === 0) {
        Message.error({ content: "请选择文件!"});
        return Promise.reject('请选择文件!');
    }
    const oFile = files[0];
    const formData = new FormData();
    formData.append("file", oFile);
    return request.upload<{}, IUpload>('/upload', formData, option);
};

语音:

export const uploadAudio = (
    files: FileList,
    option?: AxiosRequestConfig
) => {
    const imgTypes = [
        'MP3',
        'mp3',
        'MPEG',
        'mpeg'
    ].map(type => `audio/${type}`).join('|');
    const rFilter = new RegExp(`^(?:${imgTypes})$`, 'i');
    if (files.length === 0) {
        Message.warning({ content: "请上传语音!"});
        return Promise.reject('请上传语音!');
    }
    const oFile = files[0];
    if (!rFilter.test(oFile.type)) {
        Message.warning({ content: "请上传语音!"});
        return Promise.reject('请上传语音!');
    }
    const formData = new FormData();
    formData.append("file", oFile);
    return request.upload<{}, IUpload>('/upload', formData, option);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant