tiefen_space_h5/components/UploadImgs/index.js

329 lines
11 KiB
JavaScript

"use client";
import React, { useEffect, useState, useRef } from "react";
import { DotLoading, Toast, Modal } from "antd-mobile";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faAdd, faClose, faPlay } from "@fortawesome/free-solid-svg-icons";
import { getVideoBase64 } from "@/utils/tools";
import { DragDropContext, Draggable, Droppable } from "@hello-pangea/dnd";
import ImagesMask from "@/components/ImagesMask";
import OwnImage from "../OwnImage";
export default function UploadImgs({
assets,
getImgs,
accept = "image/png, image/jpeg, image/jpg",
existImages = [],
type = 1,
videoSrc = null,
getVideoCover,
id = "uploadAvatarBtn",
maxLength = 9,
}) {
const imagesMaskRef = useRef(null);
const [loading, setLoading] = useState(false);
const [filesUrls, setFilesUrls] = useState([]);
const [videoUrl, setVideoUrl] = useState(null);
const [frameImage, setFrameImage] = useState({ src: null, h: 0, w: 0 });
const [columns, setColumns] = useState([]);
useEffect(() => {
if (existImages.length > 0) {
setFilesUrls(
existImages.map((it, index) => ({ url: it.url, id: `${index}` }))
);
}
if (videoSrc) {
setVideoUrl(videoSrc);
}
}, [existImages]);
useEffect(() => {
getVideoCover && getVideoCover(frameImage);
}, [frameImage]);
useEffect(() => {
if (assets && assets.length > 0) {
const list = assets.map((it, index) => ({
url: it.url || URL.createObjectURL(it),
id: `${index}`,
}));
setFilesUrls(list);
// 将filesUrls转变为每组四个的二维数组
handleMakeColumns(list);
} else {
setFilesUrls([]);
setColumns([]);
}
}, [assets]);
const handleUploadImage = async (e) => {
let file = e.target.files[0];
if (!file) return;
if (e.target.files.length + assets.length > maxLength) {
Toast.show({
icon: "fail",
content: "最多上传" + maxLength + "张图片",
position: "top",
});
return;
}
var videoUrl = URL.createObjectURL(file);
var videoObj = document.createElement("video");
const eles = Array.from(e.target.files);
if (type == 1) {
const newFils = eles.map((it, index) => ({
url: URL.createObjectURL(it),
id: it.name,
}));
setFilesUrls((old) => [...old, ...newFils]);
} 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();
}
// console.log("assets", [...assets, ...eles]);
getImgs([...assets, ...eles]);
};
const handleRemoveItem = (index) => {
let newArr = [...filesUrls];
let newAssets = [...assets];
newArr.splice(index, 1);
newAssets.splice(index, 1);
setFilesUrls(newAssets);
getImgs(newAssets);
};
const showPhotos = (images, index) => {
if (type == 1) {
imagesMaskRef.current.show(images, index);
} else {
Modal.show({
content: (
<video
autoPlay
playsInline
controls
muted={true}
controlsList="nodownload"
>
<source src={videoUrl} />
</video>
),
closeOnMaskClick: true,
bodyStyle: {
background: "none",
maxHeight: "100vh",
},
});
}
};
const creatVideoCanvas = (file) => {
if (typeof window == "undefined") return;
// const videoD = document.getElementById("video_upload");
const url = URL.createObjectURL(file);
// videoD.src = url;
getVideoBase64(url).then((src) => {
setFrameImage((old) => ({ ...old, src }));
const list = [{ url: src, id: "0" }];
setFilesUrls(list);
handleMakeColumns(list);
});
};
const handleMakeColumns = (list) => {
const newList = [];
for (let i = 0; i < list.length; i += 4) {
newList.push(list.slice(i, i + 4));
}
const newColumns = newList.map((it, index) => {
return {
id: "column-" + index,
title: "Column " + index,
items: it,
};
});
setColumns(newColumns);
};
const reorder = (list, startIndex, endIndex) => {
const result = Array.from(list);
const removing = result[startIndex];
const removed = result[endIndex];
result[startIndex] = removed;
result[endIndex] = removing;
return result;
};
const onDragEnd = (result) => {
const { source, destination, draggableId } = result;
if (!destination) return;
if (
source.droppableId === destination.droppableId &&
source.index === destination.index
) {
return;
}
const newList = reorder(
assets,
parseInt(source.droppableId.match(/\d+/)[0], 10) * 4 + source.index,
parseInt(destination.droppableId.match(/\d+/)[0], 10) * 4 +
destination.index
);
const start = columns.find((column) => column.id === source.droppableId);
const end = columns.find((column) => column.id === destination.droppableId);
const startItems = Array.from(start.items);
let endItems = Array.from(end.items);
if (destination.droppableId === source.droppableId) {
const itemToMove = startItems[source.index];
startItems.splice(source.index, 1);
startItems.splice(destination.index, 0, itemToMove);
const newColumns = columns.map((column) => {
if (column.id === start.id) {
return { ...column, items: startItems };
}
if (column.id === end.id) {
return { ...column, items: endItems };
}
return column;
});
setColumns(newColumns);
getImgs([...newList]);
} else {
const newColumns = columns.map((column) => {
if (column.id === start.id) {
startItems.splice(source.index, 1, end.items[destination.index]);
return { ...column, items: startItems };
}
if (column.id === end.id) {
endItems.splice(destination.index, 1, start.items[source.index]);
return { ...column, items: endItems };
}
return column;
});
setColumns(newColumns);
// console.log("newList", newList);
getImgs([...newList]);
}
};
return (
<div className="event-none overflow-hidden">
<DragDropContext onDragEnd={onDragEnd}>
{columns.map((column, index) => (
<Droppable
direction="horizontal"
key={column.id}
droppableId={column.id}
>
{(provided) => (
<div
className="droppable-area grid grid-cols-4 gap-1"
style={{ height: "calc(25vw - 0.75rem)" }}
{...provided.droppableProps}
ref={provided.innerRef}
>
{column.items.map((item, ind) => (
<Draggable key={item.id} draggableId={item.id} index={ind}>
{(provided) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<div
key={ind}
className="rounded relative mr-1 mb-1 h-full"
>
<div
onClick={() =>
showPhotos(filesUrls, index * 4 + ind)
}
style={{ height: "calc(25vw - 0.75rem)" }}
className="h-full pb-1"
>
<OwnImage
src={item.url}
outClassName="w-full h-full"
className="w-full h-full"
rounded="rounded"
fit="cover"
/>
</div>
<div
className="h-6 w-6 bg-[#33333380] absolute top-0 right-0 flex justify-center items-center rounded-bl z-50"
onClick={() => handleRemoveItem(index * 4 + ind)}
>
<FontAwesomeIcon icon={faClose} size="xl" />
</div>
{type == 2 && (
<div
className="absolute top-1/2 left-1/2 flex justify-center items-center -mt-2 -ml-1 z-50"
onClick={() => showPhotos(filesUrls)}
>
<FontAwesomeIcon icon={faPlay} size="xl" />
</div>
)}
</div>
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
))}
</DragDropContext>
{loading && (
<div className="rounded border-[#ffffff80] text-[#ffffff80] flex flex-col justify-center items-center">
<DotLoading />
<p>上传中</p>
</div>
)}
<div className="grid grid-cols-4 gap-1">
{type == 2 && filesUrls.length > 0 ? null : (
<>
<label htmlFor={id}>
<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={type == 1}
id={id}
style={{ display: "none" }}
// accept="image/png, image/jpeg, video/*"
accept={accept}
// capture="camera"
onChange={handleUploadImage}
/>
</>
)}
</div>
<ImagesMask ref={imagesMaskRef} isEditing={true} />
</div>
);
}