205 lines
6.3 KiB
JavaScript
205 lines
6.3 KiB
JavaScript
"use client";
|
|
|
|
import React, { useEffect, useState } from "react";
|
|
import { DotLoading, Image, Toast, ImageViewer, Modal } from "antd-mobile";
|
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
import { faAdd, faClose, faPlay } from "@fortawesome/free-solid-svg-icons";
|
|
export default function UploadImgs({
|
|
assets,
|
|
getImgs,
|
|
accept = "image/png, image/jpeg, image/jpg",
|
|
existImages = [],
|
|
type = 1,
|
|
videoSrc = null,
|
|
getVideoCover,
|
|
}) {
|
|
const maxCount = 6;
|
|
const [loading, setLoading] = useState(false);
|
|
const [filesUrls, setFilesUrls] = useState([...existImages]);
|
|
const [videoUrl, setVideoUrl] = useState(null);
|
|
const [frameImage, setFrameImage] = useState({ src: null, h: 0, w: 0 });
|
|
useEffect(() => {
|
|
if (existImages.length > 0) {
|
|
setFilesUrls(existImages.map((it) => it.url));
|
|
// setFrameImage({...frameImage,src:existImages});
|
|
getImgs(existImages);
|
|
}
|
|
if (videoSrc) {
|
|
setVideoUrl(videoSrc);
|
|
}
|
|
}, []);
|
|
useEffect(() => {
|
|
getVideoCover && getVideoCover(frameImage);
|
|
}, [frameImage]);
|
|
|
|
const handleUploadImage = async (e) => {
|
|
let file = e.target.files[0];
|
|
if (!file) return;
|
|
var videoUrl = URL.createObjectURL(file);
|
|
var videoObj = document.createElement("video");
|
|
let newFilesUrls = [...filesUrls];
|
|
const eles = Array.from(e.target.files);
|
|
if (type == 1) {
|
|
eles.forEach((element) => {
|
|
newFilesUrls = [...newFilesUrls, URL.createObjectURL(element)];
|
|
});
|
|
setFilesUrls(newFilesUrls);
|
|
} else {
|
|
videoObj.onloadedmetadata = function (evt) {
|
|
URL.revokeObjectURL(videoUrl);
|
|
const target = evt.target;
|
|
setFrameImage({
|
|
src: videoUrl,
|
|
w: target.videoWidth,
|
|
h: target.videoHeight,
|
|
});
|
|
setLoading(true);
|
|
// let newFilesUrls = [...filesUrls];
|
|
|
|
if (type == 2) {
|
|
if (typeof window == "undefined") return;
|
|
// newFiles = [...assets, file];
|
|
// setFileList(newAssets);
|
|
creatVideoCanvas(file);
|
|
setVideoUrl(URL.createObjectURL(file));
|
|
}
|
|
// setFileList(newFiles);
|
|
setLoading(false);
|
|
};
|
|
|
|
videoObj.src = videoUrl;
|
|
videoObj.load();
|
|
}
|
|
getImgs([...assets, ...eles]);
|
|
};
|
|
const handleRemoveItem = (index) => {
|
|
let newArr = [...filesUrls];
|
|
let newAssets = [...assets];
|
|
newArr.splice(index, 1);
|
|
newAssets.splice(index, 1);
|
|
|
|
setFilesUrls(newArr);
|
|
getImgs(newAssets);
|
|
};
|
|
const showPhotos = (images, index) => {
|
|
const file = images[0];
|
|
const urls = type == 2 ? [frameImage.src] : images;
|
|
if (type == 1) {
|
|
ImageViewer.Multi.show({
|
|
images: urls,
|
|
defaultIndex: index,
|
|
});
|
|
} else {
|
|
Modal.show({
|
|
content: (
|
|
<video autoPlay>
|
|
<source src={videoUrl} />
|
|
</video>
|
|
),
|
|
closeOnMaskClick: true,
|
|
bodyStyle: {
|
|
background: "none",
|
|
maxHeight: "100vh",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
const creatVideoCanvas = (file) => {
|
|
if (typeof window == "undefined") return;
|
|
const videoD = document.getElementById("videoD");
|
|
const url = URL.createObjectURL(file);
|
|
videoD.src = url;
|
|
videoD.addEventListener("loadeddata", function () {
|
|
videoD.currentTime = 0;
|
|
videoD.pause(); // 可选:确保视频在重置后不会自动播放
|
|
const canvas = document.createElement("canvas");
|
|
canvas.width = videoD.videoWidth;
|
|
canvas.height = videoD.videoHeight;
|
|
canvas
|
|
.getContext("2d")
|
|
.drawImage(videoD, 0, 0, canvas.width, canvas.height);
|
|
const canvasImg = canvas.toDataURL();
|
|
setFrameImage((old) => ({ ...old, src: canvasImg }));
|
|
setFilesUrls([canvasImg]);
|
|
// 释放URL对象
|
|
URL.revokeObjectURL(url);
|
|
});
|
|
};
|
|
return (
|
|
<div>
|
|
<div className="grid grid-cols-4 gap-1">
|
|
{filesUrls.map((item, index) => {
|
|
return (
|
|
<div key={index} className="rounded relative">
|
|
<div
|
|
onClick={() => showPhotos(filesUrls, index)}
|
|
style={{ height: "calc(25vw - 0.75rem)" }}
|
|
>
|
|
<Image
|
|
src={item}
|
|
width="100%"
|
|
height="100%"
|
|
className="rounded"
|
|
fit="cover"
|
|
/>
|
|
</div>
|
|
|
|
<div
|
|
className="h-4 w-4 bg-[#33333380] absolute top-0 right-0 flex justify-center items-center"
|
|
onClick={() => handleRemoveItem(index)}
|
|
>
|
|
<FontAwesomeIcon icon={faClose} size="sm" />
|
|
</div>
|
|
{type == 2 && (
|
|
<div
|
|
className="absolute top-1/2 left-1/2 flex justify-center items-center -mt-1 -ml-1"
|
|
onClick={() => showPhotos(filesUrls)}
|
|
>
|
|
<FontAwesomeIcon icon={faPlay} size="sm" />
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
})}
|
|
{loading && (
|
|
<div className="rounded border-[#ffffff80] text-[#ffffff80] flex flex-col justify-center items-center">
|
|
<DotLoading />
|
|
<p>上传中</p>
|
|
</div>
|
|
)}
|
|
|
|
{type == 2 && filesUrls.length > 0 ? null : (
|
|
<>
|
|
<label htmlFor="uploadAvatarBtn">
|
|
<div
|
|
className="border-2 border-[#ffffff80] text-[#ffffff80] rounded border-dashed w-full h-full flex justify-center items-center"
|
|
style={{ minHeight: "calc(25vw - 0.75rem)" }}
|
|
>
|
|
<div style={{ maxWidth: "24px" }}>
|
|
<FontAwesomeIcon icon={faAdd} size="2xl" />
|
|
</div>
|
|
</div>
|
|
</label>
|
|
<input
|
|
type="file"
|
|
multiple="multiple"
|
|
id="uploadAvatarBtn"
|
|
style={{ display: "none" }}
|
|
// accept="image/png, image/jpeg, video/*"
|
|
accept={accept}
|
|
// capture="camera"
|
|
onChange={handleUploadImage}
|
|
/>
|
|
</>
|
|
)}
|
|
</div>
|
|
<div className="hidden">
|
|
<video id="videoD" controls autoPlay name="media">
|
|
<source />
|
|
您的浏览器不支持 Video 标签。
|
|
</video>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|