返回 AiToEarn
index.tsx
1 import {
2 ForwardedRef,
3 forwardRef,
4 memo,
5 useEffect,
6 useImperativeHandle,
7 } from 'react';
8 import styles from './uploadImages.module.scss';
9 import React, { useState } from 'react';
10 import { Image, message, Upload } from 'antd';
11 import type { GetProp, UploadFile, UploadProps } from 'antd';
12 import { PlusOutlined } from '@ant-design/icons';
13 import { useUserStore } from '../../store/user';
14
15 const FILE_BASE_URL = import.meta.env.VITE_APP_FILE_HOST;
16 const VITE_APP_URL = import.meta.env.VITE_APP_URL;
17 type FileType = Parameters<GetProp<UploadProps, 'beforeUpload'>>[0];
18
19 const getBase64 = (file: FileType): Promise<string> =>
20 new Promise((resolve, reject) => {
21 const reader = new FileReader();
22 reader.readAsDataURL(file);
23 reader.onload = () => resolve(reader.result as string);
24 reader.onerror = (error) => reject(error);
25 });
26
27 export interface IUploadimagesRef {
28 clear: () => void;
29 }
30
31 export interface IUploadimagesProps extends UploadProps {
32 onUploadChange: (files: UploadFile[]) => void;
33 fileListValue?: UploadFile[];
34 }
35
36 const Uploadimages = memo(
37 forwardRef(
38 (
39 {
40 accept = '.png,.jpg,.jpeg',
41 onUploadChange,
42 ...props
43 }: IUploadimagesProps,
44 ref: ForwardedRef<IUploadimagesRef>,
45 ) => {
46 const [previewOpen, setPreviewOpen] = useState(false);
47 const [previewImage, setPreviewImage] = useState('');
48 const [fileList, setFileList] = useState<UploadFile[]>([]);
49 const token = useUserStore((state) => state.token);
50
51 const handlePreview = async (file: UploadFile) => {
52 if (!file.url && !file.preview) {
53 file.preview = await getBase64(file.originFileObj as FileType);
54 }
55
56 setPreviewImage(file.url || (file.preview as string));
57 setPreviewOpen(true);
58 };
59
60 const handleChange: UploadProps['onChange'] = ({
61 fileList: newFileList,
62 }) => {
63 newFileList = newFileList.map((v) => {
64 if (v.status === 'done') {
65 v.url = FILE_BASE_URL + v.response?.data.name;
66 return v;
67 }
68 return v;
69 });
70 setFileList(newFileList);
71 };
72
73 const imperativeHandle: IUploadimagesRef = {
74 clear() {
75 setFileList([]);
76 },
77 };
78 useImperativeHandle(ref, () => imperativeHandle);
79
80 useEffect(() => {
81 onUploadChange(fileList.filter((v) => v.url && v.status === 'done'));
82 }, [fileList]);
83
84 const uploadButton = (
85 <button
86 style={{ border: 0, background: 'none', cursor: 'pointer' }}
87 type="button"
88 >
89 <PlusOutlined />
90 <div style={{ marginTop: 8 }}>上传</div>
91 </button>
92 );
93 return (
94 <div className={styles.uploadImages}>
95 <Upload
96 {...props}
97 beforeUpload={(e) => {
98 const isAllow = accept
99 ?.split(',')
100 .some((suffix) => e.type.includes(suffix.replace('.', '')));
101 if (!isAllow) {
102 message.warning(`不允许上传 ${e.type} 的文件!`);
103 return Upload.LIST_IGNORE;
104 }
105 }}
106 accept={accept}
107 action={`${VITE_APP_URL}/oss/upload/permanent`}
108 listType="picture-card"
109 headers={{
110 Authorization: `Bearer ${token}`,
111 }}
112 fileList={fileList}
113 onPreview={handlePreview}
114 onChange={handleChange}
115 multiple={true}
116 >
117 {props.maxCount && fileList.length >= props.maxCount
118 ? null
119 : uploadButton}
120 </Upload>
121 {previewImage && (
122 <Image
123 wrapperStyle={{ display: 'none' }}
124 preview={{
125 visible: previewOpen,
126 onVisibleChange: (visible) => setPreviewOpen(visible),
127 afterOpenChange: (visible) => !visible && setPreviewImage(''),
128 }}
129 src={previewImage}
130 />
131 )}
132 </div>
133 );
134 },
135 ),
136 );
137 Uploadimages.displayName = 'Uploadimages';
138
139 export default Uploadimages;
140
140 lines Plain Text