无可厚非antd已经是react项目必不可少的组件库之一了,查阅文档
这篇文章主要写一下开发中遇到的对upload组件在formItem中的二次定制封装
样式基本完全与原antd一致,支持多选上传,自定义定制尾部按钮等
代码如下
index.tsx文件内容
import React, { useState } from 'react';
import { Button, Form, Progress, Tooltip, Upload, message } from 'antd';
import { FormItemProps } from 'antd/lib/form';
import { NamePath } from 'antd/lib/form/interface';
import axios from 'axios';
import { DeleteOutlined, DownloadOutlined, EyeOutlined, PaperClipOutlined, UploadOutlined } from '@ant-design/icons';
import './index.scss';
interface FormUploadPicFieldInterface {
formItemProps?: FormItemProps //formItem其他的配置
label: string // formlabel
name: NamePath //form字段名
minNum?: number //设置最少上传数量 -1表示不必传 默认必传1个
maxNum?: number // 设置最大上传数量 -1表示随便传 默认6个
disabled?: boolean //是否只读只读模式
accept?: string[], //限制可上传的文件类型
fileSize?: number, //可以上传的文件的大小 单位Mb默认50Mb
fetchParams?: object //默认请求携带参数
axiosUrl: string, //请求接口
tip?: string //提示语
disabledShowBtn?: boolean //禁用状态下是否显示上传按钮 默认显示
}
const FormUploadField: React.FC<FormUploadPicFieldInterface> = ({
formItemProps,
name,
label,
disabled,
minNum,
maxNum,
accept,
fileSize,
fetchParams,
axiosUrl,
tip,
disabledShowBtn,
noLabel
}) => {
const validator = (rule, value) => {
if (disabled) return Promise.resolve();
if (value) {
if (minNum !== -1 && value.length < minNum) {
return Promise.reject('请至少上传' + minNum + '个文件');
}
if (maxNum !== -1 && value.length > maxNum) {
return Promise.reject('请最多上传' + maxNum + '个文件');
}
if (value.filter((item) => item.status === 'error').length > 0) {
return Promise.reject('文件上传失败,请删除重试');
}
if (value.filter((item) => item.status === 'uploading').length > 0) {
return Promise.reject('文件正在上传中');
}
} else {
if (minNum !== -1) {
return Promise.reject('请至少上传' + minNum + '个文件');
}
}
return Promise.resolve();
};
return (
<Form.Item
labelCol={{ span: 24 }}
wrapperCol={{ span: 24 }}
{...formItemProps}
label={noLabel?'':label}
name={name}
required={minNum !== -1}
rules={[{ validator: validator }]}
>
<FormUpload disabled={disabled} maxNum={maxNum} accept={accept} fileSize={fileSize} fetchParams={fetchParams}
axiosUrl={axiosUrl} tip={tip} disabledShowBtn={disabledShowBtn} />
</Form.Item>
);
};
FormUploadField.defaultProps = {
disabled: false,
minNum: 1,
maxNum: 6,
accept: [],
fileSize: 50,
fetchParams: {},
disabledShowBtn: true,
};
FormUploadField.displayName = 'FormUploadField';
export default FormUploadField;
interface FormPicProps {
onChange?: (a) => void
value?: any
disabled: boolean
maxNum: number
accept: string[]
fileSize: number
fetchParams?: object,
axiosUrl: string
tip?: string
disabledShowBtn?: boolean
}
const FormUpload: React.FC<FormPIcProps> = ({
onChange,
value,
disabled,
maxNum,
accept,
fileSize,
fetchParams,
axiosUrl,
tip,
disabledShowBtn,
}) => {
const [loadingBtn, setLoadingBtn] = useState(false);
//内容变化后触发
const uploadOnChange = (v) => {
const fileList = v.fileList.map((item) => {
if (item?.response) {
return item?.response;
}
return item;
});
onChange(fileList);
};
//上传前文件校验
const beforeUpload = (file) => {
const mime = file.name.slice(file.name.lastIndexOf('.')).toLowerCase();
if (accept.length > 0 && !accept.includes(mime)) {
message.warning(`请上传${accept}文件`);
return Upload.LIST_IGNORE;
}
if (file.size > fileSize * 1024 * 1024) {
message.warning(`请上传小于${fileSize}Mb的文件`);
return Upload.LIST_IGNORE;
}
return true;
};
//自定义上传
const customRequest = (options) => {
const { onProgress, onError, onSuccess, file } = options;
let data = new FormData();
data.append('file', file);
Object.keys(fetchParams).forEach((item) => {
data.append(item, fetchParams[item]);
});
axios({
url: axiosUrl,
method: 'post',
data: data,
onUploadProgress: (e) => {
onProgress({ percent: e.loaded / e.total * 100 | 0 });
},
}).then((r) => {
const { data } = r.data;
if(data&&data?.file_url){
onSuccess({
uid: file.uid,
name: file.name,
status: 'done',
url: data.file_url,
key: data.key,
thumbUrl: data?.thumb_url, //文件浏览地址
sys_file_url: data?.sys_file_url, //文件下载地址
});
}else{
onError({ message:'上传失败' });
}
}).catch((e) => {
onError(e);
});
};
//回显或上传成功文件下载
const onDownload = (file) => {
if (loadingBtn) {
message.warning('有文件下载中,请稍后');
return;
}
setLoadingBtn(true);
//文件下载方法
getDownload(file, {}).then(() => {
message.success('下载成功');
setLoadingBtn(false);
}).catch(() => {
message.error('下载失败');
setLoadingBtn(false);
});
};
//在线浏览
const onPreview = (file) => {
window.open(file.thumbUrl);
};
//自定义文件列表
const itemRender = (originNode, file, fileList, action) => {
return <div className={'upload-render-item'}>
{file.status === 'uploading' ?
<Progress percent={file?.percent} size={18} type={'circle'} />
: <PaperClipOutlined className={'icon-color'} />
}
<div className={'render-item-label'} onClick={action?.download}>
<Tooltip title={file?.error?.message}>
<a title={file.name} style={{
color: file?.status === 'error' ? '#ff4d4f' : file.status === 'uploading' ? '#333333' : '#4882f3',
}}>{file.name}</a >
</Tooltip>
</div>
<div>
{file?.thumbUrl &&
<Button size={'small'} type={'text'} block icon={<EyeOutlined className={'icon-color'} />}
onClick={action?.preview} />
}
{file?.url &&
<Button size={'small'} type={'text'} block icon={<DownloadOutlined className={'icon-color'} />}
onClick={action?.download} />
}
{!disabled &&
<Button size={'small'} type={'text'} danger block icon={<DeleteOutlined />} onClick={action?.remove} />
}
</div>
</div>;
};
return (
<>
<Upload name='file'
className={'form-upload'}
fileList={value}
multiple={maxNum!==1}
onChange={uploadOnChange}
disabled={disabled}
accept={accept.join(',')}
beforeUpload={beforeUpload}
customRequest={customRequest}
itemRender={itemRender}
onDownload={onDownload}
onPreview={onPreview}
>
{
(!disabled || (disabled && disabledShowBtn)) && <>
<Button disabled={disabled} icon={<UploadOutlined />}>上传</Button>
{tip && <div className='ant-upload-hint'>{tip}</div>}
</>
}
</Upload>
</>
);
};
FormUpload.defaultProps = {
value: [],
};
index.scss代码
.form-upload {
.upload-render-item {
display: flex;
align-items: center;
margin: 2.5px 0;
&:hover {
background-color: #f5f5f5;
}
.ant-progress-line{
margin-bottom: 0 !important;
}
.render-item-label {
flex: 1;
width: 0;
padding: 0 5px;
cursor: pointer;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.ant-btn-sm {
width: 22px !important;
height: 22px !important;
padding: 2px !important;
margin: 0 3px;
}
.icon-color {
color: rgba(0, 0, 0, 0.45)
}
}
}
使用案例
<FormUploadField label={'文件影像'} minNum={-1} name={'reply_files_list'} maxNum={-1} span={24}
formItemProps={{ labelCol: { span: 6 } }}
upload_type={'2'} axiosUrl={`/file/upload`}
accept={['.doc', '.docx', '.xls', '.xlsx', '.jpg', '.png', '.pdf', '.ofd']}
tip={'支持扩展名:doc, docx, xls, xlsx, jpg, png, pdf, ofd'} />