整个组件功能代码-->图片压缩上传&&axios封装

// 项目中上传图片组件 and其它功能
import React, { useState, useRef } from 'react';
import { QrcodeOutlined } from '@ant-design/icons';
import '@ant-design/compatible/assets/index.css';
import { Modal, Button, message } from 'antd';
import { useObserver } from 'mobx-react';
import { useHistory, useLocation } from 'react-router-dom';
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
import QRCode from 'qrcode';
import ajax from '@/lib/axios';
import { Wrapper, SuccessWrapper, UploadImgWrapper } from './link-qrcode-style';

interface IProps {
  merchantId: string;
  branchName: string;
  merchantName: string;
  personInCharge: string;
}

const LinkQrCode = (props: IProps) => {
  const [showConfirmModal, setShowConfirmModal] = useState<boolean>(false);
  const [showSuccessModal, setShowSuccessModal] = useState<boolean>(false);
  const [showUploadModal, setShowUploadModal] = useState<boolean>(false);
  const [uploadButtonLoading, setUploadButtonLoading] = useState<boolean>(false);
  const [confirmButtonLoading, setConfirmButtonLoading] = useState<boolean>(false);
  const [qrCodeUrl, setQrCodeUrl] = useState<string>('');
  const [qrDecode, setQrDecode] = useState<string>('');
  const [uploadImgUrl, setUploadImgUrl] = useState<string>('');
  const [canvasToBlob, setCanvasToBlob] = useState<any>({});
  const history = useHistory();
  const location = useLocation();
  const fileInputRef = useRef<HTMLInputElement>(null);
  // const ua = navigator.userAgent.toLowerCase();
  // const isIos = ua.indexOf('iphone') !== -1 || ua.indexOf('ipad') !== -1;

  const uploadFileValidate = async () => {
    try {
      const makeBoundary = function() {
        return '----JQBoundary' + btoa(Math.random().toString()).substr(0, 12);
      };
      const boundary = makeBoundary();
      const formData = new FormData();
    
// 这里需要注意,formdata.append()括号里参数的名称是根据后端定的,不是固定的.
后端需要什么字端,就append什么字段,如果需要多个字段,就写多个append()
      formData.append('qrcodeImage', canvasToBlob);
      const {
        data: { data },
      } = await ajax.post('/qrcode/validate', formData, {
     
// 请求后端上传接口的方法中,参数直接是上面封装的formdata即可,不需要其它。
// 另外请求默认的content-type是application/json,上传文件接口这里需要自己定义content-type:multipart/form-data。
定义了content-type之后需要手动添加boundary,否则会报错。
        headers: { 'Content-Type': `multipart/form-data; boundary=${boundary}` },
      });
      setUploadButtonLoading(false);
      setShowUploadModal(false);
      if (data) {
        setQrDecode(data);
        setShowConfirmModal(true);
      } else {
        Modal.error({
          content: "Sorry, we can't process this picture, please make sure you use a clear picture of valid QR code.",
          onOk() {
            const to = `${location.pathname}${location.search}`;
            history.push(to);
          },
        });
      }
    } catch (e) {
      message.error(e.message);
    }
  };

  const qrCodeUploadedHandler = (imageFile: object, fileImageUrl: string) => {
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d') as any;
    const img = new Image();
    img.src = fileImageUrl;
    img.onload = () => {
      const originWidth = img.width;
      const originHeight = img.height;
      const maxWidth = 1280;
      const maxHeight = 768;
      let targetWidth = originWidth;
      let targetHeight = originHeight;
      if (originWidth > maxWidth || originHeight > maxHeight) {
        if (originWidth / originHeight > maxWidth / maxHeight) {
          // 更宽,按照宽度限定尺寸
          targetWidth = maxWidth;
          targetHeight = Math.round(maxWidth * (originHeight / originWidth));
        } else {
          targetHeight = maxHeight;
          targetWidth = Math.round(maxHeight * (originWidth / originHeight));
        }
      }
      // canvas对图片进行缩放
      canvas.width = targetWidth;
      canvas.height = targetHeight;
      // 清除画布
      context.clearRect(0, 0, targetWidth, targetHeight);
      // 图片压缩
      context.drawImage(img, 0, 0, targetWidth, targetHeight);
      canvas.toBlob(
        blob => {
          // const newImage = new File([blob as any], (imageFile as any).name, { type: (imageFile as any).type });
          setCanvasToBlob(blob);
        },
        'image/jpeg',
        0.7,
      );
    };
    img.onerror = () => console.error('Upload file of image format please.');
  };

  const handleUploadButton = (e: React.MouseEvent) => {
    e.preventDefault();
    setUploadButtonLoading(true);
    uploadFileValidate();
  };

  const handleFileChange = (e: any) => {
    const file = e.target.files[0];
    if (file !== undefined) {
      const fr = new FileReader();
      fr.readAsDataURL(file);
      fr.addEventListener('load', () => {
        setUploadImgUrl(fr.result as string); //base64
        qrCodeUploadedHandler(file, fr.result as string);
        setShowUploadModal(true);
      });
    } else {
      setUploadImgUrl('');
    }
  };

  const backToListPage = () => {
    const to = `${location.pathname}${location.search}`;
    history.push(to);
  };

  const handleLinkButton = (e: React.MouseEvent) => {
    e.preventDefault();
    if (!!fileInputRef) {
      (fileInputRef as any).current.click();
    }
  };

  const handleConfirmLink = async (e: React.MouseEvent) => {
    e.preventDefault();
    setConfirmButtonLoading(true);
    try {
      const {
        data: { data },
      } = await ajax.post('/qrcode/bind', { qrcode: qrDecode, bindObjectId: props.merchantId });
      if (data.length > 0) {
        try {
          const codeUrl = await QRCode.toDataURL(data[0]);
          setQrCodeUrl(codeUrl);
        } catch (err) {
          console.error(err);
        }
        setConfirmButtonLoading(false);
        setShowConfirmModal(false);
        setShowSuccessModal(true);
      }
    } catch (e) {
      message.error(e.message);
    }
  };

  const handleConfirmModalCancel = (e: React.MouseEvent) => {
    e.preventDefault();
    setShowConfirmModal(false);
    backToListPage();
  };

  const handleSuccessModalCancel = (e: React.MouseEvent) => {
    e.preventDefault();
    setShowSuccessModal(false);
    backToListPage();
  };

  return useObserver(() => (
    <>
      <div>
        <Button onClick={handleLinkButton} style={{ width: '100%', marginTop: 5 }} type="default" size="small">
          <QrcodeOutlined />
          Link QR code
        </Button>
        <input
          type="file"
          name="cover"
          accept="image/*"
          ref={fileInputRef}
          style={{ width: 0, height: 0 }}
          onChange={handleFileChange}
        />
      </div>
      <Modal
        destroyOnClose={true}
        title="Confirm Information"
        visible={showConfirmModal}
        style={{ top: 20 }}
        width={'80%'}
        maskClosable={false}
        footer={[
          <Button
            type="primary"
            key="confirm"
            style={{ fontSize: '30px', height: '60px' }}
            loading={confirmButtonLoading}
            htmlType="button"
            onClick={handleConfirmLink}>
            Confirm
          </Button>,
          <Button
            key="cancel"
            htmlType="button"
            style={{ fontSize: '30px', height: '60px' }}
            onClick={handleConfirmModalCancel}>
            Cancel
          </Button>,
        ]}
        onCancel={handleConfirmModalCancel}>
        <Wrapper>
          <p>
            {/* eslint-disable-next-line react/no-unescaped-entities */}
            You're linking the QR code you scaned to the following merchant. Please check the merchant information
            throughly.
          </p>
          <div className="confirm-information">
            <div className="confirm-information-item">
              <span>Merchant ID:</span>
              <p>{props.merchantId}</p>
            </div>
            <div className="confirm-information-item">
              <span>Branch Name:</span>
              <p>{props.branchName}</p>
            </div>
            <div className="confirm-information-item">
              <span>Merchant Name:</span>
              <p>{props.merchantName}</p>
            </div>
            <div className="confirm-information-item">
              <span>Person in Charge:</span>
              <p>{props.personInCharge}</p>
            </div>
          </div>
        </Wrapper>
      </Modal>
      <Modal
        destroyOnClose={true}
        visible={showSuccessModal}
        style={{ top: 20 }}
        width={'50%'}
        maskClosable={false}
        footer={null}
        onCancel={handleSuccessModalCancel}>
        <SuccessWrapper>
          <p className="top-tip">QR code has been successfully linked!</p>
          <div className="branch-name">
            <span>Branch Name:</span>
            <p>{props.branchName}</p>
          </div>
          <div className="qr-code">
            <span>QR Code:</span>
            <img src={qrCodeUrl} alt="图片加载失败" />
          </div>
        </SuccessWrapper>
      </Modal>
      <Modal
        destroyOnClose={true}
        visible={showUploadModal}
        style={{ top: 20, textAlign: 'center' }}
        width={'80%'}
        maskClosable={false}
        closable={false}
        footer={null}>
        <UploadImgWrapper>
          <img id="img" src={uploadImgUrl} style={{ width: '90%' }} alt="" />
        </UploadImgWrapper>
        <Button
          type="primary"
          key="confirm"
          loading={uploadButtonLoading}
          size="large"
          htmlType="button"
          style={{ margin: '20px', width: '90%', fontSize: '30px', height: '60px' }}
          onClick={handleUploadButton}>
          UPLOAD
        </Button>
      </Modal>
    </>
  ));
};

export default LinkQrCode;
// axios封装
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse, Canceler } from 'axios';
import Cookies from 'js-cookie';
import { message } from 'antd';

export let cancel!: Canceler;
export const ROUTE_CHANGE = 'ROUTE_CHANGE';

export const handleError = (e: AxiosError) => {
  message.error(`CODE: ${e.code}\n MESSAGE: ${e.message}`);
};

const requestInterceptor = (config: AxiosRequestConfig) => {
  const acceptLanguages = [...navigator.languages];
// config.headers['Authorization'] = xxx,这种方式写法只会重写
// headers中Authorization字段,不会重写整个headers.
// 如果config.headers= {xxx},每次请求的headers都会被重写成这里定义
// 的,会导致你在调请求方法时手动指定的headers失效。
  config.headers['Authorization'] = Cookies.get('token');
  config.headers['Accept-Language'] = acceptLanguages.join(',');
  return config;
};

const responseInterceptor = (res: AxiosResponse) => {
  if (res.data.code !== 'SUCCESS') {
    throw {
      isAxiosError: false,
      request: res.request,
      name: res.data.code,
      config: res.config,
      code: res.data.code,
      message: res.data.message,
      response: res,
    } as AxiosError;
  } else {
    return res;
  }
};

const ajax = axios.create({
  baseURL: '/api',
  timeout: 60000,
});

ajax.interceptors.response.use(responseInterceptor);
ajax.interceptors.request.use(requestInterceptor);

export default ajax;
// AxiosRequestConfig  ===> ts
export interface AxiosTransformer {
  (data: any, headers?: any): any;
}

export interface AxiosAdapter {
  (config: AxiosRequestConfig): AxiosPromise<any>;
}

export interface AxiosBasicCredentials {
  username: string;
  password: string;
}

export interface AxiosProxyConfig {
  host: string;
  port: number;
  auth?: {
    username: string;
    password:string;
  };
  protocol?: string;
}

export type Method =
  | 'get' | 'GET'
  | 'delete' | 'DELETE'
  | 'head' | 'HEAD'
  | 'options' | 'OPTIONS'
  | 'post' | 'POST'
  | 'put' | 'PUT'
  | 'patch' | 'PATCH'
  | 'link' | 'LINK'
  | 'unlink' | 'UNLINK'

export type ResponseType = 
  | 'arraybuffer' 
  | 'blob' 
  | 'document' 
  | 'json' 
  | 'text' 
  | 'stream'

export interface AxiosRequestConfig {
  url?: string;
  method?: Method;
  baseURL?: string;
  transformRequest?: AxiosTransformer | AxiosTransformer[];
  transformResponse?: AxiosTransformer | AxiosTransformer[];
  headers?: any;
  params?: any;
  paramsSerializer?: (params: any) => string;
  data?: any;
  timeout?: number;
  timeoutErrorMessage?: string;
  withCredentials?: boolean;
  adapter?: AxiosAdapter;
  auth?: AxiosBasicCredentials;
  responseType?: ResponseType;
  xsrfCookieName?: string;
  xsrfHeaderName?: string;
  onUploadProgress?: (progressEvent: any) => void;
  onDownloadProgress?: (progressEvent: any) => void;
  maxContentLength?: number;
  validateStatus?: (status: number) => boolean;
  maxRedirects?: number;
  socketPath?: string | null;
  httpAgent?: any;
  httpsAgent?: any;
  proxy?: AxiosProxyConfig | false;
  cancelToken?: CancelToken;
}

export interface AxiosResponse<T = any>  {
  data: T;
  status: number;
  statusText: string;
  headers: any;
  config: AxiosRequestConfig;
  request?: any;
}

export interface AxiosError<T = any> extends Error {
  config: AxiosRequestConfig;
  code?: string;
  request?: any;
  response?: AxiosResponse<T>;
  isAxiosError: boolean;
  toJSON: () => object;
}

export interface AxiosPromise<T = any> extends Promise<AxiosResponse<T>> {
}

export interface CancelStatic {
  new (message?: string): Cancel;
}

export interface Cancel {
  message: string;
}

export interface Canceler {
  (message?: string): void;
}

export interface CancelTokenStatic {
  new (executor: (cancel: Canceler) => void): CancelToken;
  source(): CancelTokenSource;
}

export interface CancelToken {
  promise: Promise<Cancel>;
  reason?: Cancel;
  throwIfRequested(): void;
}

export interface CancelTokenSource {
  token: CancelToken;
  cancel: Canceler;
}

export interface AxiosInterceptorManager<V> {
  use(onFulfilled?: (value: V) => V | Promise<V>, onRejected?: (error: any) => any): number;
  eject(id: number): void;
}

export interface AxiosInstance {
  (config: AxiosRequestConfig): AxiosPromise;
  (url: string, config?: AxiosRequestConfig): AxiosPromise;
  defaults: AxiosRequestConfig;
  interceptors: {
    request: AxiosInterceptorManager<AxiosRequestConfig>;
    response: AxiosInterceptorManager<AxiosResponse>;
  };
  getUri(config?: AxiosRequestConfig): string;
  request<T = any, R = AxiosResponse<T>> (config: AxiosRequestConfig): Promise<R>;
  get<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
  delete<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
  head<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
  options<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
  post<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
  put<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
  patch<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
}

export interface AxiosStatic extends AxiosInstance {
  create(config?: AxiosRequestConfig): AxiosInstance;
  Cancel: CancelStatic;
  CancelToken: CancelTokenStatic;
  isCancel(value: any): boolean;
  all<T>(values: (T | Promise<T>)[]): Promise<T[]>;
  spread<T, R>(callback: (...args: T[]) => R): (array: T[]) => R;
}

declare const Axios: AxiosStatic;

export default Axios;

 

posted @ 2020-03-23 14:21  贝子涵夕  阅读(419)  评论(0编辑  收藏  举报