2025-07-28 16:53:18 +08:00
|
|
|
|
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 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[]>([]);
|
|
|
|
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
|
|
if (value) {
|
|
|
|
|
|
const file: UploadFile = {
|
|
|
|
|
|
uid: "-1",
|
|
|
|
|
|
name: "video",
|
|
|
|
|
|
status: "done",
|
2025-07-29 10:06:40 +08:00
|
|
|
|
url: value || "", // 确保 URL 不为 undefined
|
2025-07-28 16:53:18 +08:00
|
|
|
|
};
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
2025-07-29 10:06:40 +08:00
|
|
|
|
return true; // 允许上传
|
2025-07-28 16:53:18 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 处理文件变化
|
2025-07-29 11:35:04 +08:00
|
|
|
|
const handleChange: UploadProps["onChange"] = info => {
|
2025-07-29 10:06:40 +08:00
|
|
|
|
console.log("VideoUpload handleChange info:", info);
|
|
|
|
|
|
|
|
|
|
|
|
// 更新 fileList,确保所有 URL 都是字符串
|
2025-07-29 11:35:04 +08:00
|
|
|
|
const updatedFileList = info.fileList.map(file => ({
|
2025-07-29 10:06:40 +08:00
|
|
|
|
...file,
|
|
|
|
|
|
url:
|
|
|
|
|
|
file.url ||
|
|
|
|
|
|
file.response?.data ||
|
|
|
|
|
|
file.response?.url ||
|
|
|
|
|
|
file.response ||
|
|
|
|
|
|
"",
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
setFileList(updatedFileList);
|
|
|
|
|
|
|
|
|
|
|
|
// 处理上传状态
|
2025-07-28 16:53:18 +08:00
|
|
|
|
if (info.file.status === "uploading") {
|
|
|
|
|
|
setLoading(true);
|
2025-07-29 10:06:40 +08:00
|
|
|
|
} else if (info.file.status === "done") {
|
|
|
|
|
|
setLoading(false);
|
|
|
|
|
|
message.success("上传成功");
|
|
|
|
|
|
|
|
|
|
|
|
// 从响应中获取上传后的URL
|
|
|
|
|
|
const uploadedUrl =
|
|
|
|
|
|
info.file.response?.data ||
|
|
|
|
|
|
info.file.response?.url ||
|
|
|
|
|
|
info.file.response ||
|
|
|
|
|
|
"";
|
|
|
|
|
|
if (uploadedUrl) {
|
|
|
|
|
|
// 调用onChange
|
|
|
|
|
|
onChange?.(uploadedUrl);
|
2025-07-28 17:18:03 +08:00
|
|
|
|
}
|
2025-07-29 10:06:40 +08:00
|
|
|
|
} else if (info.file.status === "error") {
|
2025-07-28 16:53:18 +08:00
|
|
|
|
setLoading(false);
|
2025-07-29 10:06:40 +08:00
|
|
|
|
message.error("上传失败,请重试");
|
|
|
|
|
|
} else if (info.file.status === "removed") {
|
|
|
|
|
|
// 文件被删除
|
|
|
|
|
|
onChange?.("");
|
2025-07-28 16:53:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 删除文件
|
|
|
|
|
|
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>
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2025-07-29 10:06:40 +08:00
|
|
|
|
const action = import.meta.env.VITE_API_BASE_URL + "/v1/attachment/upload";
|
|
|
|
|
|
|
2025-07-28 16:53:18 +08:00
|
|
|
|
return (
|
|
|
|
|
|
<div className={`${style["upload-container"]} ${className || ""}`}>
|
|
|
|
|
|
<Upload
|
|
|
|
|
|
name="file"
|
2025-07-29 10:06:40 +08:00
|
|
|
|
headers={{
|
|
|
|
|
|
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
|
|
|
|
|
}}
|
|
|
|
|
|
action={action}
|
2025-07-28 16:53:18 +08:00
|
|
|
|
multiple={false}
|
|
|
|
|
|
fileList={fileList}
|
|
|
|
|
|
accept="video/*"
|
|
|
|
|
|
listType="text"
|
|
|
|
|
|
showUploadList={true}
|
|
|
|
|
|
disabled={disabled || loading}
|
|
|
|
|
|
beforeUpload={beforeUpload}
|
|
|
|
|
|
onChange={handleChange}
|
|
|
|
|
|
onRemove={handleRemove}
|
2025-07-29 10:06:40 +08:00
|
|
|
|
maxCount={1}
|
2025-07-28 16:53:18 +08:00
|
|
|
|
>
|
|
|
|
|
|
{fileList.length >= 1 ? null : uploadButton}
|
|
|
|
|
|
</Upload>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export default VideoUpload;
|