154 lines
3.8 KiB
TypeScript
154 lines
3.8 KiB
TypeScript
import React, { useState } from "react";
|
||
import { Upload, message } from "antd";
|
||
import { LoadingOutlined, PlusOutlined } from "@ant-design/icons";
|
||
import type { UploadProps, UploadFile } from "antd/es/upload/interface";
|
||
import request from "@/api/request";
|
||
import style from "./index.module.scss";
|
||
|
||
interface VideoUploadProps {
|
||
value?: string;
|
||
onChange?: (url: string) => void;
|
||
disabled?: boolean;
|
||
className?: string;
|
||
}
|
||
|
||
const VideoUpload: React.FC<VideoUploadProps> = ({
|
||
value = "",
|
||
onChange,
|
||
disabled = false,
|
||
className,
|
||
}) => {
|
||
const [loading, setLoading] = useState(false);
|
||
const [fileList, setFileList] = useState<UploadFile[]>([]);
|
||
|
||
// 将value转换为fileList格式
|
||
React.useEffect(() => {
|
||
if (value) {
|
||
const file: UploadFile = {
|
||
uid: "-1",
|
||
name: "video",
|
||
status: "done",
|
||
url: value,
|
||
};
|
||
setFileList([file]);
|
||
} else {
|
||
setFileList([]);
|
||
}
|
||
}, [value]);
|
||
|
||
// 文件验证
|
||
const beforeUpload = (file: File) => {
|
||
const isVideo = file.type.startsWith("video/");
|
||
if (!isVideo) {
|
||
message.error("只能上传视频文件!");
|
||
return false;
|
||
}
|
||
|
||
const isLt50M = file.size / 1024 / 1024 < 50;
|
||
if (!isLt50M) {
|
||
message.error("视频大小不能超过50MB!");
|
||
return false;
|
||
}
|
||
|
||
return false; // 阻止自动上传
|
||
};
|
||
|
||
// 处理文件变化
|
||
const handleChange: UploadProps["onChange"] = async info => {
|
||
if (info.file.status === "uploading") {
|
||
setLoading(true);
|
||
|
||
// 在这里处理文件上传
|
||
try {
|
||
const formData = new FormData();
|
||
formData.append("file", info.file.originFileObj as File);
|
||
|
||
const response = await request(
|
||
"/v1/attachment/upload",
|
||
formData,
|
||
"POST",
|
||
);
|
||
|
||
if (response) {
|
||
const uploadedUrl =
|
||
typeof response === "string" ? response : response.url || response;
|
||
|
||
// 更新文件状态
|
||
const updatedFile = {
|
||
...info.file,
|
||
status: "done" as const,
|
||
url: uploadedUrl,
|
||
};
|
||
|
||
// 更新fileList
|
||
setFileList([updatedFile]);
|
||
// 调用onChange
|
||
onChange?.(uploadedUrl);
|
||
|
||
setLoading(false);
|
||
message.success("上传成功");
|
||
} else {
|
||
throw new Error("上传失败");
|
||
}
|
||
} catch (error) {
|
||
console.error("上传失败:", error);
|
||
message.error("上传失败,请重试");
|
||
setLoading(false);
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (info.file.status === "done") {
|
||
setLoading(false);
|
||
// 更新fileList
|
||
setFileList([info.file]);
|
||
// 调用onChange
|
||
onChange?.(info.file.url || "");
|
||
}
|
||
};
|
||
|
||
// 删除文件
|
||
const handleRemove = () => {
|
||
setFileList([]);
|
||
onChange?.("");
|
||
return true;
|
||
};
|
||
|
||
const uploadButton = (
|
||
<div className={style["upload-button"]}>
|
||
{loading ? (
|
||
<div className={style["uploading"]}>
|
||
<LoadingOutlined className={style["upload-icon"]} />
|
||
<div className={style["upload-text"]}>上传中...</div>
|
||
</div>
|
||
) : (
|
||
<>
|
||
<PlusOutlined className={style["upload-icon"]} />
|
||
<div className={style["upload-text"]}>上传视频</div>
|
||
</>
|
||
)}
|
||
</div>
|
||
);
|
||
|
||
return (
|
||
<div className={`${style["upload-container"]} ${className || ""}`}>
|
||
<Upload
|
||
name="file"
|
||
multiple={false}
|
||
fileList={fileList}
|
||
accept="video/*"
|
||
listType="text"
|
||
showUploadList={true}
|
||
disabled={disabled || loading}
|
||
beforeUpload={beforeUpload}
|
||
onChange={handleChange}
|
||
onRemove={handleRemove}
|
||
>
|
||
{fileList.length >= 1 ? null : uploadButton}
|
||
</Upload>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default VideoUpload;
|