Ant Design Pro 组件踩坑记录

ProFormUploadButton 踩坑记录

最近在用ant design pro 组件做项目,在使用到ProFormUploadButton这个组件的时候,发现很多文档上没有写的东西,接下来把我的踩坑记录分享给大家.

刚开始只有一个上传的需求,我就把官网的示例直接copy了下来,如下:
<ProFormUploadButton label="upload" name="upload" action="upload.do" />
后面跟我说要一个预览的功能,,这样其我就参考ant design 的组件加成这样:
<ProFormUploadButton
        name="businessLicense"
        label="营业执照"
        max={1}
        action="upload.do"
        fieldProps={{
          multiple: false,
          name: 'file',
          listType: 'picture-card',
          accept:"image/png, image/jpeg",
          headers: {
            'timestamp': (new Date().valueOf()) + ''
          },
          onChange: (e) => {
            handleChange(e)
          },
        }}
        fileList={fileList}
        extra="只能上传jpg/png文件,且不大于3MB"
      />
测试后,发现没有前置校验,于是加了前置校验,如下:
// 上传前置校验
const beforeUpload = (file: RcFile) => {
    const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
    if (!isJpgOrPng) {
      message.error('只允许上传 JPG/PNG 格式的文件');
    }
    const isLt3M = file.size / 1024 / 1024 < 3;
    if (!isLt3M) {
      message.error('仅支持3M以下的文件');
    }
    return isJpgOrPng && isLt3M
  };
测试后,发现就算校验了,会自动跳过,还是会调接口,所以改成这样
// 上传前置校验
const beforeUpload = (file: RcFile) => {
    const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
    if (!isJpgOrPng) {
      message.error('只允许上传 JPG/PNG 格式的文件');
    }
    const isLt3M = file.size / 1024 / 1024 < 3;
    if (!isLt3M) {
      message.error('仅支持3M以下的文件');
    }
    // 如果返回了
    if ((isJpgOrPng && isLt3M) === false) {
      // 在这里记得要清空fileList
      // 返回这个参数beforeUpload为false就可以拦截住了
      return Upload.LIST_IGNORE
    } else {
      return true
    }
  };

注意:除了fileList以外, 其他所有的属性都要放到ProFormUploadButton组件的fieldProps里面,这样才能生效.

注意二: 在onchange方法里如果上传文件状态为error,还是会显示到fileList,这时候要处理一下:

if (info.file.status == 'error') {
      message.error('上传失败!请稍后再试')
      setFileList([])
    }
我以为到这里就结束了,没想到,又加了需求,说是要让图片可以旋转,放大缩小,我网上找了个js库,用的人比较多,鉴于我是react, 所以又找了个用react封装的插件,下面贴一下两个库的git地址.

react版本 tinymins/viewerjs-react: React wrapper for viewerjs. (github.com)

原版 fengyuanchen/viewerjs: JavaScript image viewer. (github.com)

贴一下整页的代码,方便自己以后回看.
import type { UploadProps } from 'antd';
import { Card, Col, Row, message, Button, Upload, Modal } from 'antd';
import type { FC} from 'react';
import { useState, useEffect, useRef } from 'react';
import type {
  ProFormInstance} from '@ant-design/pro-form';

import ProForm, {
  ProFormText,
  ProFormSelect,
  ProFormDependency,
  ProFormRadio,
  ProFormDatePicker,
  ProFormUploadButton
} from '@ant-design/pro-form';
import { PageContainer } from '@ant-design/pro-layout';
import styles from './style.less';
import { getCompanyInfoApi, saveCompanyInfoApi } from './service';
import { validatorOnlyCn, validatorPhoneAndFixedPro, validatorCerdit, validatorEnName, businessLicenseDeadLine } from '@/utils/validator';
import type { UploadChangeParam } from 'antd/lib/upload';
import type { RcFile, UploadFile } from 'antd/lib/upload/interface';
import { history } from 'umi';
import moment from 'moment';
// import ImageFunc from '@/components/ImageFunc';
import RViewerJS from 'viewerjs-react'
import 'viewerjs-react/dist/index.css'
interface TableFormDateType {
  key: string;
  workId?: string;
  name?: string;
  department?: string;
  isNew?: boolean;
  editable?: boolean;
}
const getBase64 = (file: RcFile): Promise<string> =>
  new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => resolve(reader.result as string);
    reader.onerror = error => reject(error);
  });

const FormAdvancedForm: FC<Record<string, any>> = () => {

  const [id, setId] = useState<string>('');
    // 绑定一个 ProFormInstance 实例
    const formRef = useRef<
    ProFormInstance<{
      date: string;
    }>
    >();
  const [imageName, setImageName] = useState<string>();
  const [imageUrl, setImageUrl] = useState<string>();
  const [fileList, setFileList] = useState<[]>();
  const [btnLoading, setbtnLoading] = useState<boolean>(false);
  // 为false时不校验格式
  const [noVer, setNoVer] = useState<boolean>(true);
  const set_image_url = (new_data) => {
    setImageUrl(new_data)
  }
  const handlePreview = async (file: UploadFile) => {
    if (!file.url && !file.preview) {
      file.preview = await getBase64(file.originFileObj as RcFile);
    }
    setImageUrl(file.url || (file.preview as string));
    setImageName(file.name || file.url!.substring(file.url!.lastIndexOf('/') + 1));
    setTimeout(() => { document.getElementById('imgpre').click()}, 0)
    
  };
  const getCompanyInfoFunc =  () => {
    setFileList(null)

    getCompanyInfoApi({loginName: sessionStorage.getItem('loginName') || ''}).then((res: any) => {
      if (res.code === 200) {
        // 表单回显
        // 拷贝一份数据
        const newData = JSON.parse(JSON.stringify(res.data))
        // businessLicense需要的是list格式的数据 如果不删掉会报错
        delete newData.businessLicense
        // 表单回显
        formRef.current?.setFieldsValue(newData)
        // 做成fileList的格式传回
        const flit = [
          {
            uid: moment().valueOf(),
            name: res.data.businessLicenseName,
            status: 'done',
            url: res.data.businessLicense
          }
        ]
        if (res.data.businessLicense) {
          // 回显fileList
          formRef.current?.setFieldsValue({
            businessLicense: flit
          })
          setFileList(flit)
          set_image_url(res.data.businessLicense)
          setImageName(res.data.businessLicenseName)
        }
        // res.data.id为null表示是新增
        if (res.data.id === null) {
          setId('')
          return;
        } else {
          setId(res.data.id)
        }
        // 不为null表示是编辑
        return;
      }
      message.error('查询失败!请稍后再试')
   })
  }
  const onFinish = async (values: Record<string, any>) => {
    setbtnLoading(true)
    delete values.file
    const obj = {
      id: id === '' ? undefined : id,
      code: sessionStorage.getItem('loginName'),
      businessLicense: imageUrl,
      businessLicenseName: imageName
    }
    // eslint-disable-next-line @typescript-eslint/no-unused-expressions
    values.name ? delete values.name : ''
    // eslint-disable-next-line @typescript-eslint/no-unused-expressions
    values.tel ? delete values.tel : ''
    // eslint-disable-next-line @typescript-eslint/no-unused-expressions
    values.code ? delete values.code : ''
    // eslint-disable-next-line @typescript-eslint/no-unused-expressions
    values.email ? delete values.email : ''
    
    values.businessLicenseDeadLine = values.businessLicenseDeadLine ? moment(moment(values.businessLicenseDeadLine).format('YYYY-MM-DD') + ' 23:59:59').valueOf() : undefined
    try {
      await saveCompanyInfoApi({
        ...values,
        ...obj
      }).then(res => {
        setbtnLoading(false)
        if (res.code !== 200) {
          message.error('提交失败,请稍后再试');
          return;
        }
        message.success('提交成功');
         // 在这里更改组件是否刷新的flag
        sessionStorage.setItem('destroyInactiveTabPane', 'true')
        getCompanyInfoFunc()
        history.push('/platform/filingReview/certification');
      })
    } catch {
      message.error('提交出错,请稍后再试');
      setbtnLoading(false)
    }
  };

  useEffect(() => {
    // 在这里更改组件是否刷新的flag
    sessionStorage.setItem('destroyInactiveTabPane', 'true')
    getCompanyInfoFunc()
  }, []);
 
  const beforeUpload = (file: RcFile) => {
    const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
    if (!isJpgOrPng) {
      message.error('只允许上传 JPG/PNG 格式的文件');
    }
    const isLt3M = file.size / 1024 / 1024 < 3;
    if (!isLt3M) {
      message.error('仅支持3M以下的文件');
    }
    if ((isJpgOrPng && isLt3M) === false) {
      setFileList(null)
      setImageName(undefined)
      set_image_url(undefined);
      formRef.current?.resetFields(['resetFields'])
      return Upload.LIST_IGNORE
    } else {
      return true
    }
  };
  
  const handleChange: UploadProps['onChange'] = (info: UploadChangeParam<UploadFile>) => {
   
    if (info.file.status == 'removed') {
      setFileList(null)
    }
    if (info.file.status === 'done') {
      // Get this url from response in real world.
      // 做成fileList的格式传回
      if (info.file.response.code !== 200) {
        message.error(info.file.response.msg)
        return;
      }
      let obj ={
          uid: moment().valueOf(),
          name: info.file.response.data.fileName,
          status: 'done',
          url: info.file.response.data.filePath
      }
        setFileList([obj])
        setImageName(info.file.response.data.fileName)
        set_image_url(info.file.response.data.filePath);
        return;
    }
    if (info.file.status == 'error') {
      message.error('上传失败!请稍后再试')
      setFileList([])

    }


  };
  // 点击删除的时候直接清空fileList
  const onRemove = () => {
    setFileList(null)
  }
  //选择认证失败时 进行的操作
  const failFunC = () => {
    setNoVer(false)
   

  } 

  return (
    <div>
    <ProForm<TableFormDateType>
      layout="horizontal"
      // 通过formRef进行绑定
      formRef={formRef}
      onFinish={onFinish}
      submitter={{
        // 配置按钮文本
        searchConfig: {
          submitText: '提交',
        },
        // 配置按钮的属性
        resetButtonProps: {
          style: {
            // 隐藏重置按钮
            display: 'none',
          },
        },
        // 完全自定义整个区域
        render: (props, doms) => {
          return [
            <Row key="submit">
              <Button type="primary" key="submit" loading={btnLoading} onClick={() => props.form?.submit?.()} id="submitBtn" style={{margin: '0 auto'}}>
                提交
              </Button>
            </Row>
          ];
        },
      }}
      labelCol={{span: 10}}
    >
      <PageContainer header={{title:'',breadcrumb: {}}}>
        <Card className={styles.certificationInfoCard} bordered={false}>
          <Row gutter={24}>
            <Col lg={12}>
              <ProFormText
                label={'姓名'}
                name="name"
                disabled
                placeholder={''}
              />
              <ProFormText
                label={'手机号'}
                name="tel"
                disabled
                placeholder={''}
              />
              <ProFormText
                label={'公司中文名称'}
                name="cnCompanyName"
                rules={noVer? [
                  {validator: validatorOnlyCn},
                  { required: true, message: '请输入公司中文名称' },
                  { min: 2, max: 128, message: '长度为2-128个字符'},
                ] : [{ required: true, message: '请输入公司中文名称' }]}
              />
              <ProFormText
                label={'公司电话'}
                name="companyTel"
                rules={noVer ? [
                  {validator: validatorPhoneAndFixedPro},
                  { required: true, message: '请输入公司电话' },
                  { min: 2, max: 24, message: '长度为2-24个字符'},
                ] : [{ required: true, message: '请输入公司电话' },]}
              />
            
            </Col>
            <Col lg={12} >
              <ProFormText
                label={'登陆账户'}
                name="code"
                disabled
                placeholder={''}
              />
              <ProFormText
                label={'电子邮箱'}
                name="email"
                disabled
                placeholder={''}
              />
              <ProFormText
                label={'公司英文名称'}
                name="enCompanyName"
                rules={noVer ? [
                  { max: 128, message: '长度为最多128个字符'},
                  {validator: validatorEnName},
                ] : []}
              />
              <ProFormText
                label={'统一社会信用代码'}
                name="socialCreditCode"
                rules={noVer ? [
                  { required: true, message: '请输入统一社会信用代码' },
                  {validator: validatorCerdit},
                ] : [{required: true, message: '请输入统一社会信用代码' },]}
              />
             
            </Col>
          </Row>
         
          <Row gutter={24}>
            <Col span={12}>
              {/* 公司类型 */}
              <ProFormSelect
                  label={'公司类型'}
                  name="companyType"
                  rules={[{ required: true, message: '请选择公司类型' }]}
                  options={[
                    {
                      label: '货主',
                      value: '1',
                    },
                    {
                      label: '货代',
                      value: '2',
                    },
                    {
                      label: '报关行',
                      value: '3',
                    },
                    {
                      label: '车队',
                      value: '4',
                    },
                    {
                      label: '打单行',
                      value: '5',
                    },
                    {
                      label: '拖车司机',
                      value: '6',
                    },
                    {
                      label: '船公司',
                      value: '7',
                    },
                  ]}
                  placeholder="请选择公司类型"
                />
            </Col>
            <Col span={12}> <ProFormText
                label={'公司地址'}
                name="companyAddress"
                placeholder={'请输入公司地址'}
                rules={noVer ? [
                  { required: true, message: '请输入公司地址' },
                  { min: 2, max: 50, message: '长度为2-50个汉字'}
                ] : [{ required: true, message: '请输入公司地址' },]}
              />
            </Col>
          </Row>
          <Row gutter={24}>
            {/* 营业执照期限 */}
            <Col span={12}>
              <ProFormSelect
                label="营业执照期限"
                options={[
                  {
                    value: '长期',
                    label: '长期',
                  },
                  {
                    value: '非长期',
                    label: '非长期',
                  },
                
                ]}
                name="businessLicenseStatus"
                rules={[
                  { required: true, message: '请选择' }
                ]}
              />
              </Col>
              <Col span={6}>
                  <ProFormDependency name={['businessLicenseStatus']}>
                  {({ businessLicenseStatus }) => {
                    if (businessLicenseStatus === '长期') {
                      return (
                        ''
                      );
                    }
                    return  <ProFormDatePicker
                      name="businessLicenseDeadLine"
                      placeholder={'请选择期限'}
                      rules={noVer ? [
                        { required: true, message: '请选择' },
                        { validator: businessLicenseDeadLine }
                      ] : [ { required: true, message: '请选择' },]}
                    
                  />;
                  }}
                </ProFormDependency>
                
              </Col>
          </Row>
          <Row gutter={24}>
            <Col span={24}>
            <ProFormUploadButton
                rules={[
                  { required: true, message: '请上传营业执照' },
                ]}
                labelCol={{span: 5}}
                name="businessLicense"
                label="营业执照"
                max={1}
                fieldProps={{
                  multiple: false,
                  name: 'file',
                  action: "/external/api/file/uploadFile",
                  listType: 'picture-card',
                  data:{
                    fileType: 'company_license',
                    loginName: sessionStorage.getItem('loginName') || ''
                  },
                  accept:"image/png, image/jpeg",

                  beforeUpload: (e) => {
                    return beforeUpload(e)
                  },
                  onRemove,
                  headers: {
                    'timestamp': (new Date().valueOf()) + ''
                  },
                  onChange: (e) => {
                    handleChange(e)
                  },
                  onPreview: handlePreview,
                }}
                fileList={fileList}
                // previewFile={() => {}}
                extra="只能上传jpg/png文件,且不大于3MB"
              />
              {/* 页面不显示这个组件 点击预览的时候模拟点击这个组件 效果是一样的 */}
              <RViewerJS>
                <img src={imageUrl} style={{ width: '0.5%'}} id="imgpre"/>
              </RViewerJS> 
            </Col>
          </Row>
          <Row gutter={24}>
            <Col span={17}> 
              <ProFormRadio.Group
                labelCol={{span: 7}}
                name="certificationStatus"
                label="认证状态"
                rules={[
                  { required: true, message: '请选择认证状态' }
                ]}
                options={[
                  {
                    label: '未认证',
                    value: 0,
                  },
                  {
                    label: '认证中',
                    value: 1,
                  },
                  {
                    label: '已认证',
                    value: 2,
                  },
                  {
                    label: '认证失败',
                    value: 3,
                  },
                ]}
              />
            </Col>
           <Col span={17}>
              <ProFormDependency name={['certificationStatus']}>
                  {({ certificationStatus }) => {
                    if (certificationStatus !== 3) {
                      setNoVer(true)
                      formRef.current?.resetFields(['certificationRemark'])
                      return (
                        ''
                      );
                    } else {
                      failFunC()
                    }
                    return   <ProFormText
                    label="失败原因"
                    labelCol={{span: 7}}
                    name="certificationRemark"
                    placeholder={'请输入失败原因(16字符)'}
                    rules={[
                      { required: true, message: '失败原因为必填项' },
                      { min: 0, max: 16, message: '最多输入16个字符'}
                    ]}
                  />;
                  }}
                </ProFormDependency>
           </Col>
          </Row>
        </Card>
      </PageContainer>
    </ProForm>
    </div>
  );
};

export default FormAdvancedForm;

猜你喜欢

转载自juejin.im/post/7112351348208697380
今日推荐