赞
踩
<input type="file" accept='.png, .svg, .jpg, .jpeg, .jfif' onChange={handleGalleryChange}/>
// index.tsx type galleryFile = { // 图库文件类型 name: string, thumbUrl: string, lastModified: number } const [galleryFileList, setGalleryFileList] = useState<galleryFile[]>(local_galleryFileList) // 图库文件列表 const handleGalleryChange = (e: any) => { // 文件选择 getBase64(e.target.files[0]).then((res)=>{ const fileList = galleryFileList ? [...galleryFileList] : []; // 状态监听的是引用 所以想要监听数组的变化得深拷贝 fileList.push({ thumbUrl: res, name: e.target.files[0].name, lastModified: e.target.files[0].lastModified }) setGalleryFileList(fileList) localStorage.setItem('AICase_gallery_fileList', JSON.stringify(fileList)) }) }
// getBase64
const getBase64 = (file: RcFile): Promise<string> => // 获取base64
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result as string);
reader.onerror = (error) => reject(error);
});
// HTML结构
<div className="upload_container">
<div className="upload">
<input type="file" accept='.png, .svg, .jpg, .jpeg, .jfif' onChange={handleGalleryChange}/>
{uploadButton}
</div>
</div>
</div>
// uploadButton
const uploadButton = ( // 上传按钮
<div className='uploadBtn'>
<PlusOutlined />
<div style={{ marginTop: 8 }}>上传</div>
</div>
);
// css样式 .upload_container { width: 102px; height: 102px; margin-right: 8px; margin-bottom: 8px; box-sizing: border-box; .upload { position: relative; width: 102px; height: 102px; padding: 8px; border-radius: 8px; border: 1px dashed #d9d9d9; background-color: rgba(0, 0, 0, 0.02); box-sizing: border-box; cursor: pointer; transition: border-color 0.3s; input { position: absolute; top: 0; left: 0; width: 100%; height: 100%; cursor: pointer; opacity: 0; z-index: 1; } .uploadBtn { position: absolute; top: 25%; left: 0; text-align: center; width: 100%; height: 100%; color: rgba(0, 0, 0, 0.88); font-size: 14px; } img { width: 100%; height: 100%; object-fit: contain; } &:hover { border: 1px dashed rgba(250, 173, 20); } } }
// index.tsx {galleryFileList ? galleryFileList.map((value: galleryFile, index: number) => { return ( <div className="upload_container" onClick={(e)=>handleGalleryClick(e)} key={`upload-container-${index}`}> <div className="upload upload_active"> <img src={value.thumbUrl} alt="" width='102px' height='102px' className='uploadImg' data-name={value.name} data-lastmodified={value.lastModified} /> <div className="mask"> <CheckOutlined className='icon checkOutIcon' /> <DeleteOutlined className='icon deleteIcon'/> </div> </div> </div>) }) : ''} <div className="upload_container"> <div className="upload"> <input type="file" accept='.png, .svg, .jpg, .jpeg, .jfif' onChange={handleGalleryChange}/> {uploadButton} </div> </div>
// dataChange.ts文件 用于将File转化为Base64
import type { RcFile } from 'antd/es/upload';
export const getBase64 = (file: RcFile): Promise<string> => // 获取base64
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result as string);
reader.onerror = (error) => reject(error);
});
// index.tsx import { PlusOutlined, CheckOutlined, DeleteOutlined } from '@ant-design/icons' import type { UploadFile } from 'antd/es/upload/interface'; import React,{ useState } from 'react' import { getBase64 } from '../../utils/dataChange' import './index.scss' type galleryFile = { // 图库文件类型 name: string, thumbUrl: string, lastModified: number } const uploadButton = ( // 上传按钮 <div className='uploadBtn'> <PlusOutlined /> <div style={{ marginTop: 8 }}>上传</div> </div> ); const local_galleryFileList = JSON.parse(localStorage.getItem('AICase_gallery_fileList') as string) // 本地图库 export default function UploadList(props:{presetFileList: UploadFile[], setSpotResult: any, setIsModalOpen: any}) { // 文件上传列表 const {presetFileList, setSpotResult, setIsModalOpen} = props // 预设文件列表 人脸识别结果 modal开关设置 const [galleryFileList, setGalleryFileList] = useState<galleryFile[]>(local_galleryFileList) // 图库文件列表 const showModal = () => { // 信息展示 setIsModalOpen(true); }; const handleGalleryChange = (e: any) => { // 文件选择 getBase64(e.target.files[0]).then((res)=>{ const fileList = galleryFileList ? [...galleryFileList] : []; // 状态监听的是引用 所以想要监听数组的变化得深拷贝 fileList.push({ thumbUrl: res, name: e.target.files[0].name, lastModified: e.target.files[0].lastModified }) setGalleryFileList(fileList) localStorage.setItem('AICase_gallery_fileList', JSON.stringify(fileList)) }) } const handleGalleryClick = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => { // 图库点击处理 const target = e.target as HTMLDivElement const currentTarget = e.currentTarget as HTMLDivElement const img = currentTarget.querySelector('img') as HTMLImageElement if(target.classList.value.includes("deleteIcon")){ // 删除 const result = galleryFileList.filter((obj:galleryFile) => obj.lastModified.toString() !== img.getAttribute('data-lastmodified')) setGalleryFileList(result) } else if(target.classList.value.includes("checkOutIcon")){ // 确认 // 获取自定义属性值并比较 const isExist = presetFileList.some((obj: UploadFile) => obj.name === img.getAttribute('data-name')) setSpotResult(isExist) showModal() } } return ( <div className="upload_list"> {galleryFileList ? galleryFileList.map((value: galleryFile, index: number) => { return ( <div className="upload_container" onClick={(e)=>handleGalleryClick(e)} key={`upload-container-${index}`}> <div className="upload upload_active"> <img src={value.thumbUrl} alt="" width='102px' height='102px' className='uploadImg' data-name={value.name} data-lastmodified={value.lastModified} /> <div className="mask"> <CheckOutlined className='icon checkOutIcon' /> <DeleteOutlined className='icon deleteIcon'/> </div> </div> </div>) }) : ''} <div className="upload_container"> <div className="upload"> <input type="file" accept='.png, .svg, .jpg, .jpeg, .jfif' onChange={handleGalleryChange}/> {uploadButton} </div> </div> </div> ) }
// index.scss .upload_list { // 文件列表 display: flex; justify-content: start; align-content: flex-start; flex-wrap: wrap; width: 100%; .upload_container { width: 102px; height: 102px; margin-right: 8px; margin-bottom: 8px; box-sizing: border-box; .upload { position: relative; width: 102px; height: 102px; padding: 8px; border-radius: 8px; border: 1px dashed #d9d9d9; background-color: rgba(0, 0, 0, 0.02); box-sizing: border-box; cursor: pointer; transition: border-color 0.3s; input { position: absolute; top: 0; left: 0; width: 100%; height: 100%; cursor: pointer; opacity: 0; z-index: 1; } .uploadBtn { position: absolute; top: 25%; left: 0; text-align: center; width: 100%; height: 100%; color: rgba(0, 0, 0, 0.88); font-size: 14px; } img { width: 100%; height: 100%; object-fit: contain; } &:hover { border: 1px dashed rgba(250, 173, 20); } } .upload_active { // 上传后的文件列表 border: 1px solid #d9d9d9; [fill=currentColor] { // antd内置的颜色修改 color: rgba(255, 255, 255, 0.65); } .mask { // 遮罩层 position: absolute; top: 8px; left: 8px; width: 84px; height: 84px; background-color: rgba(0,0,0,.45); text-align: center; line-height: 84px; opacity: 0; transition: opacity .3s; .icon { // 操作icon position: relative; transition: background-color color .3s; padding: 3px; [fill=currentColor]:hover { color: #fff; } &:hover { border-radius: 3px; background-color: rgba(0,0,0,.1); } &::after { // 这个伪类是用来确保点击事件的target指向该icon而不是icon里面的什么 content: ""; position: absolute; top: 0; left: 0; width: 22px; height: 22px; background-color: rgba(255, 255, 255, 0); } } .deleteIcon { margin-left: 8px; } &:hover { opacity: 1; } } &:hover { border: 1px solid #d9d9d9; } } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。