// 项目中上传图片组件 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={{ '100%', marginTop: 5 }} type="default" size="small">
<QrcodeOutlined />
Link QR code
</Button>
<input
type="file"
name="cover"
accept="image/*"
ref={fileInputRef}
style={{ 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={{ '90%' }} alt="" />
</UploadImgWrapper>
<Button
type="primary"
key="confirm"
loading={uploadButtonLoading}
size="large"
htmlType="button"
style={{ margin: '20px', '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}
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;