|
@@ -0,0 +1,587 @@
|
|
|
+import { Button, Modal, Upload, Progress, message, Form, Input, Space } from "antd";
|
|
|
+import { UploadOutlined, ReloadOutlined, PlusOutlined } from "@ant-design/icons";
|
|
|
+import React, { useState, useEffect, useCallback } from "react";
|
|
|
+import OSSSDK from "../../../../../utils/OSSSDK";
|
|
|
+import http from "../../../../../http";
|
|
|
+import styles from "./index.module.css";
|
|
|
+import type { UploadFile, UploadProps } from "antd/es/upload/interface";
|
|
|
+import { getAccessToken } from "../../../../../http/sso";
|
|
|
+import { adFileUpload, getTempStsToken } from "../../../../../http/api";
|
|
|
+
|
|
|
+interface UploadVideoModalProps {
|
|
|
+ visible: boolean;
|
|
|
+ onClose: () => void;
|
|
|
+ onOk?: (videoInfo: any) => void;
|
|
|
+ isLoading?: boolean;
|
|
|
+}
|
|
|
+
|
|
|
+interface UploadStatus {
|
|
|
+ isUploading: boolean;
|
|
|
+ isUploaded: boolean;
|
|
|
+ isError: boolean;
|
|
|
+ errType?: string;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+interface UploadCreds {
|
|
|
+ host: string;
|
|
|
+ hosts: string[];
|
|
|
+ fileName: string;
|
|
|
+ upload: string;
|
|
|
+ accessKeyId: string;
|
|
|
+ accessKeySecret: string;
|
|
|
+ securityToken: string;
|
|
|
+ expiration: string;
|
|
|
+}
|
|
|
+
|
|
|
+const UploadVideoModal: React.FC<UploadVideoModalProps> = ({
|
|
|
+ visible,
|
|
|
+ onClose,
|
|
|
+ onOk,
|
|
|
+ isLoading = false
|
|
|
+}) => {
|
|
|
+ // 视频文件状态
|
|
|
+ const [videoFile, setVideoFile] = useState<File & { localUrl?: string } | null>(null);
|
|
|
+ const [videoUploadProgress, setVideoUploadProgress] = useState(0);
|
|
|
+ const [videoUploadSpeed, setVideoUploadSpeed] = useState('0Mb/s');
|
|
|
+ const [videoUploadStatus, setVideoUploadStatus] = useState<UploadStatus>({
|
|
|
+ isUploading: false,
|
|
|
+ isUploaded: false,
|
|
|
+ isError: false
|
|
|
+ });
|
|
|
+
|
|
|
+ // 封面文件状态
|
|
|
+ const [coverFileList, setCoverFileList] = useState<UploadFile[]>([]);
|
|
|
+ const [coverUploadStatus, setCoverUploadStatus] = useState<UploadStatus>({
|
|
|
+ isUploading: false,
|
|
|
+ isUploaded: false,
|
|
|
+ isError: false
|
|
|
+ });
|
|
|
+
|
|
|
+ // OSS相关状态
|
|
|
+ const [videoCreds, setVideoCreds] = useState<UploadCreds | null>(null);
|
|
|
+ const [videoUploader, setVideoUploader] = useState<OSSSDK | null>(null);
|
|
|
+ const [videoUrl, setVideoUrl] = useState('');
|
|
|
+
|
|
|
+ // 重试相关状态
|
|
|
+ const [speedTimer, setSpeedTimer] = useState<NodeJS.Timeout | null>(null);
|
|
|
+
|
|
|
+ // 表单引用
|
|
|
+ const [form] = Form.useForm();
|
|
|
+
|
|
|
+ // 视频预览
|
|
|
+ const [videoPreviewOpen, setVideoPreviewOpen] = useState(false);
|
|
|
+ const [isVideoHovering, setIsVideoHovering] = useState(false);
|
|
|
+
|
|
|
+ // 重置状态
|
|
|
+ const resetStates = useCallback(() => {
|
|
|
+ // 重置视频文件状态
|
|
|
+ setVideoFile(null);
|
|
|
+ setVideoUploadProgress(0);
|
|
|
+ setVideoUploadSpeed('0Mb/s');
|
|
|
+ setVideoUploadStatus({
|
|
|
+ isUploading: false,
|
|
|
+ isUploaded: false,
|
|
|
+ isError: false
|
|
|
+ });
|
|
|
+
|
|
|
+ // 重置封面文件状态
|
|
|
+ setCoverFileList([]);
|
|
|
+ setCoverUploadStatus({
|
|
|
+ isUploading: false,
|
|
|
+ isUploaded: false,
|
|
|
+ isError: false
|
|
|
+ });
|
|
|
+
|
|
|
+ // 重置OSS状态
|
|
|
+ setVideoCreds(null);
|
|
|
+ setVideoUploader(null);
|
|
|
+ setVideoUrl('');
|
|
|
+
|
|
|
+ if (speedTimer) {
|
|
|
+ clearInterval(speedTimer);
|
|
|
+ setSpeedTimer(null);
|
|
|
+ }
|
|
|
+ form.resetFields();
|
|
|
+ }, [speedTimer, form]);
|
|
|
+
|
|
|
+ // 组件卸载时清理
|
|
|
+ useEffect(() => {
|
|
|
+ return () => {
|
|
|
+ if (speedTimer) clearInterval(speedTimer);
|
|
|
+ if (videoUploader) {
|
|
|
+ videoUploader.cancelUpload();
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }, [speedTimer, videoUploader]);
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ if (videoFile) {
|
|
|
+ startVideoUpload(videoFile);
|
|
|
+ }
|
|
|
+ }, [videoFile]);
|
|
|
+
|
|
|
+ // 获取上传凭证
|
|
|
+ const getSignature = async (fileType: number, uploadId?: string): Promise<UploadCreds> => {
|
|
|
+ try {
|
|
|
+ const params: any = { fileType };
|
|
|
+ if (uploadId) {
|
|
|
+ params.uploadId = uploadId;
|
|
|
+ }
|
|
|
+ // 这里需要根据实际API接口调整
|
|
|
+ const response = await http.post<any>(getTempStsToken, params);
|
|
|
+
|
|
|
+ if (response.code === 0) {
|
|
|
+ const credsData = response.data;
|
|
|
+ if (fileType === 2) { // 视频文件
|
|
|
+ setVideoUrl(credsData.fileName);
|
|
|
+ }
|
|
|
+ return credsData;
|
|
|
+ } else {
|
|
|
+ throw new Error(response.data.msg || '获取签名失败');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取签名失败:', error);
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 初始化视频上传器
|
|
|
+ const initVideoUploader = async (creds: UploadCreds): Promise<OSSSDK> => {
|
|
|
+ if (!videoFile) {
|
|
|
+ throw new Error('视频文件不存在');
|
|
|
+ }
|
|
|
+
|
|
|
+ const uploader = new OSSSDK(videoFile, creds, (checkpoint: any[]) => {
|
|
|
+ // 更新上传进度
|
|
|
+ const progress = Number((checkpoint[checkpoint.length - 1].percent * 100).toFixed(2));
|
|
|
+ setVideoUploadProgress(progress);
|
|
|
+ });
|
|
|
+
|
|
|
+ return uploader;
|
|
|
+ };
|
|
|
+
|
|
|
+
|
|
|
+ // 开始视频上传
|
|
|
+ const startVideoUpload = async (file?: File) => {
|
|
|
+ const targetFile = file || videoFile;
|
|
|
+ if (!targetFile) {
|
|
|
+ message.error('请先选择视频文件');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ setVideoUploadStatus(prev => ({ ...prev, isUploading: true, isError: false }));
|
|
|
+
|
|
|
+ // 获取上传凭证
|
|
|
+ const uploadCreds = await getSignature(2); // 2表示视频文件
|
|
|
+ setVideoCreds(uploadCreds);
|
|
|
+
|
|
|
+ // 初始化上传器
|
|
|
+ const uploaderInstance = await initVideoUploader(uploadCreds);
|
|
|
+ setVideoUploader(uploaderInstance);
|
|
|
+
|
|
|
+ // 开始速度监控
|
|
|
+ startSpeedMonitoring(uploaderInstance);
|
|
|
+
|
|
|
+ // 开始上传
|
|
|
+ await uploaderInstance.multipartUpload();
|
|
|
+
|
|
|
+ // 上传完成
|
|
|
+ setVideoUploadStatus(prev => ({ ...prev, isUploading: false, isUploaded: true }));
|
|
|
+ setVideoUploadSpeed('');
|
|
|
+ message.success('视频上传成功');
|
|
|
+
|
|
|
+ } catch (error: any) {
|
|
|
+ console.error('视频上传失败:', error);
|
|
|
+ setVideoUploadStatus(prev => ({ ...prev, isUploading: false, isError: true, errType: 'uploadError' }));
|
|
|
+ message.error('视频上传失败,请重试');
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 封面上传处理函数
|
|
|
+ const handleCoverUploadChange: UploadProps['onChange'] = ({ fileList: newFileList }) => {
|
|
|
+ // 只保留最新的文件
|
|
|
+ setCoverFileList(newFileList.slice(-1));
|
|
|
+
|
|
|
+ // 如果上传成功,更新状态
|
|
|
+ if (newFileList.length > 0 && newFileList[0].status === 'done') {
|
|
|
+ setCoverUploadStatus(prev => ({ ...prev, isUploaded: true, isError: false }));
|
|
|
+ message.success('封面上传成功');
|
|
|
+ } else if (newFileList.length > 0 && newFileList[0].status === 'error') {
|
|
|
+ setCoverUploadStatus(prev => ({ ...prev, isError: true }));
|
|
|
+ message.error('封面上传失败');
|
|
|
+ } else if (newFileList.length > 0 && newFileList[0].status === 'uploading') {
|
|
|
+ setCoverUploadStatus(prev => ({ ...prev, isUploading: true, isError: false }));
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 封面文件验证
|
|
|
+ const checkCoverFile = (file: UploadFile) => {
|
|
|
+ if ((file.size || 0) > 5 * 1024 * 1024) {
|
|
|
+ message.error('图片大小不能超过5MB');
|
|
|
+ return Upload.LIST_IGNORE; // 阻止上传
|
|
|
+ }
|
|
|
+ return true; // 允许上传
|
|
|
+ };
|
|
|
+
|
|
|
+ // 开始速度监控
|
|
|
+ const startSpeedMonitoring = (uploaderInstance: OSSSDK) => {
|
|
|
+ const timer = uploaderInstance.getSpeed((speed: string) => {
|
|
|
+ setVideoUploadSpeed(speed + 'Mb/s');
|
|
|
+ });
|
|
|
+ setSpeedTimer(timer);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 停止速度监控
|
|
|
+ const stopSpeedMonitoring = () => {
|
|
|
+ if (speedTimer) {
|
|
|
+ clearInterval(speedTimer);
|
|
|
+ setSpeedTimer(null);
|
|
|
+ }
|
|
|
+ setVideoUploadSpeed('');
|
|
|
+ };
|
|
|
+
|
|
|
+ // 重试视频上传
|
|
|
+ const handleVideoRetry = async () => {
|
|
|
+ setVideoUploadStatus(prev => ({ ...prev, isError: false }));
|
|
|
+
|
|
|
+ if (videoUploader && videoCreds) {
|
|
|
+ try {
|
|
|
+ setVideoUploadStatus(prev => ({ ...prev, isUploading: true }));
|
|
|
+
|
|
|
+ // 重新获取凭证
|
|
|
+ const newCreds = await getSignature(2, videoCreds.upload);
|
|
|
+ setVideoCreds(newCreds);
|
|
|
+ videoUploader.updateConfig(newCreds);
|
|
|
+
|
|
|
+ // 开始速度监控
|
|
|
+ startSpeedMonitoring(videoUploader);
|
|
|
+
|
|
|
+ // 断点续传
|
|
|
+ await videoUploader.resumeMultipartUpload();
|
|
|
+
|
|
|
+ setVideoUploadStatus(prev => ({ ...prev, isUploading: false, isUploaded: true }));
|
|
|
+ setVideoUploadSpeed('');
|
|
|
+ message.success('视频上传成功');
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.error('重试视频上传失败:', error);
|
|
|
+ setVideoUploadStatus(prev => ({ ...prev, isUploading: false, isError: true }));
|
|
|
+ message.error('重试失败');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 取消视频上传
|
|
|
+ const cancelVideoUpload = () => {
|
|
|
+ if (videoUploader) {
|
|
|
+ videoUploader.cancelUpload();
|
|
|
+ }
|
|
|
+ stopSpeedMonitoring();
|
|
|
+ setVideoUploadStatus(prev => ({ ...prev, isUploading: false }));
|
|
|
+ message.info('已取消视频上传');
|
|
|
+ };
|
|
|
+
|
|
|
+ // 发布视频
|
|
|
+ const publishVideo = async () => {
|
|
|
+ if (!videoUploadStatus.isUploaded) {
|
|
|
+ message.warning('请等待视频上传完成');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const formData = form.getFieldsValue();
|
|
|
+
|
|
|
+ const publishData = {
|
|
|
+ ...formData,
|
|
|
+ videoPath: videoCreds?.fileName || videoUrl,
|
|
|
+ coverPath: coverFileList.length > 0 ? coverFileList[0].url : '',
|
|
|
+ fileExtensions: 'mp4', // 可以根据文件类型动态设置
|
|
|
+ };
|
|
|
+
|
|
|
+ // 这里需要根据实际API接口调整
|
|
|
+ const response = await http.post<any>('/contentPlatform/video/publish', publishData);
|
|
|
+
|
|
|
+ if (response.data.code === 0) {
|
|
|
+ message.success('发布成功');
|
|
|
+ onOk?.(response.data.data);
|
|
|
+ onClose();
|
|
|
+ resetStates();
|
|
|
+ } else {
|
|
|
+ message.error(response.data.msg || '发布失败');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('发布失败:', error);
|
|
|
+ message.error('发布失败,请重试');
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 视频文件上传前处理
|
|
|
+ const beforeVideoUpload = (file: File & { localUrl?: string }) => {
|
|
|
+ // 验证文件类型
|
|
|
+ const isVideo = file.type.startsWith('video/');
|
|
|
+ if (!isVideo) {
|
|
|
+ message.error('只能上传视频文件!');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证文件大小 (例如:限制500MB)
|
|
|
+ const isLt500M = file.size / 1024 / 1024 < 500;
|
|
|
+ if (!isLt500M) {
|
|
|
+ message.error('视频大小不能超过500MB!');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ file.localUrl = URL.createObjectURL(file);
|
|
|
+
|
|
|
+ setVideoFile(file);
|
|
|
+ setVideoUploadProgress(0);
|
|
|
+ setVideoUploadStatus({
|
|
|
+ isUploading: false,
|
|
|
+ isUploaded: false,
|
|
|
+ isError: false
|
|
|
+ });
|
|
|
+
|
|
|
+ return false; // 阻止自动上传
|
|
|
+ };
|
|
|
+
|
|
|
+
|
|
|
+ // 删除视频文件
|
|
|
+ const handleVideoRemove = () => {
|
|
|
+ setVideoFile(null);
|
|
|
+ setVideoUploadProgress(0);
|
|
|
+ setVideoUploadStatus({
|
|
|
+ isUploading: false,
|
|
|
+ isUploaded: false,
|
|
|
+ isError: false
|
|
|
+ });
|
|
|
+ stopSpeedMonitoring();
|
|
|
+ if (videoUploader) {
|
|
|
+ videoUploader.cancelUpload();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 删除封面文件
|
|
|
+ const handleCoverRemove = () => {
|
|
|
+ setCoverFileList([]);
|
|
|
+ setCoverUploadStatus({
|
|
|
+ isUploading: false,
|
|
|
+ isUploaded: false,
|
|
|
+ isError: false
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 获取视频进度文本
|
|
|
+ const getVideoProgressText = () => {
|
|
|
+ if (videoUploadStatus.isError) {
|
|
|
+ return '上传失败,点击重试重新上传';
|
|
|
+ } else if (!videoUploadStatus.isUploading && videoUploadStatus.isUploaded) {
|
|
|
+ return '上传完成';
|
|
|
+ } else if (!videoUploadStatus.isUploading && !videoUploadStatus.isUploaded) {
|
|
|
+ return '等待中...';
|
|
|
+ } else {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 获取封面进度文本
|
|
|
+ const getCoverProgressText = () => {
|
|
|
+ if (coverUploadStatus.isError) {
|
|
|
+ return '上传失败,请重新选择文件';
|
|
|
+ } else if (!coverUploadStatus.isUploading && coverUploadStatus.isUploaded) {
|
|
|
+ return '上传完成';
|
|
|
+ } else if (coverFileList.length > 0 && coverFileList[0].status === 'uploading') {
|
|
|
+ return '上传中...';
|
|
|
+ } else {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <Modal
|
|
|
+ open={visible}
|
|
|
+ onCancel={() => {
|
|
|
+ onClose();
|
|
|
+ resetStates();
|
|
|
+ }}
|
|
|
+ footer={null}
|
|
|
+ width={800}
|
|
|
+ title="上传视频"
|
|
|
+ destroyOnHidden
|
|
|
+ >
|
|
|
+ <div className={styles['upload-video-modal']}>
|
|
|
+ {/* 视频上传区域:上传成功后隐藏选择模块 */}
|
|
|
+ {!videoFile && (
|
|
|
+ <div className={styles['upload-section']}>
|
|
|
+ <h4>视频文件</h4>
|
|
|
+ <Upload
|
|
|
+ fileList={[]}
|
|
|
+ beforeUpload={beforeVideoUpload}
|
|
|
+ onRemove={handleVideoRemove}
|
|
|
+ accept="video/*"
|
|
|
+ maxCount={1}
|
|
|
+ showUploadList={false}
|
|
|
+ >
|
|
|
+ <Button icon={<UploadOutlined />} disabled={videoUploadStatus.isUploading}>
|
|
|
+ 选择视频文件
|
|
|
+ </Button>
|
|
|
+ </Upload>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 视频上传进度区域 */}
|
|
|
+ {videoFile && (
|
|
|
+ <div className={styles['upload-progress-section']}>
|
|
|
+ <div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
|
|
|
+ <div
|
|
|
+ style={{ width: 104, height: 104, background: '#fafafa', border: '1px dashed #d9d9d9', borderRadius: 8, display: 'flex', alignItems: 'center', justifyContent: 'center', overflow: 'hidden', position: 'relative' }}
|
|
|
+ onMouseEnter={() => setIsVideoHovering(true)}
|
|
|
+ onMouseLeave={() => setIsVideoHovering(false)}
|
|
|
+ >
|
|
|
+ <video src={videoFile.localUrl} style={{ width: '100%', height: '100%', objectFit: 'cover' }} muted />
|
|
|
+ {!videoUploadStatus.isUploading && isVideoHovering && (
|
|
|
+ <div style={{ position: 'absolute', inset: 0, background: 'rgba(0,0,0,0.35)' }}>
|
|
|
+ <div style={{ position: 'absolute', left: 0, right: 0, bottom: 0, display: 'flex', justifyContent: 'space-between', padding: 8 }}>
|
|
|
+ <Button size="small" type="primary" onClick={() => setVideoPreviewOpen(true)}>
|
|
|
+ 预览
|
|
|
+ </Button>
|
|
|
+ <Button size="small" danger onClick={handleVideoRemove}>
|
|
|
+ 删除
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div style={{ flex: 1 }}>
|
|
|
+ <div className={styles['progress-info']}>
|
|
|
+ <span className={styles['file-name']}>{videoFile.name}</span>
|
|
|
+ {videoUploadSpeed && <span className={styles['upload-speed']}>{videoUploadSpeed}</span>}
|
|
|
+ </div>
|
|
|
+ <Progress
|
|
|
+ percent={videoUploadProgress}
|
|
|
+ status={videoUploadStatus.isError ? 'exception' : 'active'}
|
|
|
+ strokeColor={videoUploadStatus.isError ? '#F2584F' : '#FF4383'}
|
|
|
+ />
|
|
|
+ <div className={styles['progress-text']}>{getVideoProgressText()}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 封面上传区域 */}
|
|
|
+ <div className={styles['upload-section']}>
|
|
|
+ <h4>封面图片</h4>
|
|
|
+ <Upload
|
|
|
+ action={adFileUpload}
|
|
|
+ headers={{
|
|
|
+ token: getAccessToken()
|
|
|
+ }}
|
|
|
+ accept="image/*"
|
|
|
+ listType="picture-card"
|
|
|
+ beforeUpload={checkCoverFile}
|
|
|
+ onChange={handleCoverUploadChange}
|
|
|
+ fileList={coverFileList}
|
|
|
+ showUploadList={{ showPreviewIcon: false }}
|
|
|
+ maxCount={1}
|
|
|
+ data={{ fileType: 'PICTURE' }}
|
|
|
+ onRemove={handleCoverRemove}
|
|
|
+ >
|
|
|
+ {coverFileList.length >= 1 ? null : (
|
|
|
+ <button style={{ border: 0, background: 'none' }} type="button">
|
|
|
+ <PlusOutlined />
|
|
|
+ <div style={{ marginTop: 8 }}>
|
|
|
+ {coverUploadStatus.isUploaded ? '已选择封面' : '上传封面'}
|
|
|
+ </div>
|
|
|
+ </button>
|
|
|
+ )}
|
|
|
+ </Upload>
|
|
|
+
|
|
|
+ {getCoverProgressText() && (
|
|
|
+ <div className={styles['progress-text']}>{getCoverProgressText()}</div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 视频信息编辑区域 */}
|
|
|
+ <div className={styles['video-info-section']}>
|
|
|
+ <Form form={form} layout="vertical">
|
|
|
+ <Form.Item
|
|
|
+ label="视频标题"
|
|
|
+ name="title"
|
|
|
+ rules={[{ required: true, message: '请输入视频标题' }]}
|
|
|
+ >
|
|
|
+ <Input placeholder="请输入视频标题" />
|
|
|
+ </Form.Item>
|
|
|
+ </Form>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 操作按钮区域 */}
|
|
|
+ <div className={styles['action-section']}>
|
|
|
+ <div className={styles['right-actions']}>
|
|
|
+ <Space>
|
|
|
+ {/* 视频上传操作 */}
|
|
|
+ {videoFile && (
|
|
|
+ <>
|
|
|
+ {videoUploadStatus.isUploading && (
|
|
|
+ <Button onClick={cancelVideoUpload}>
|
|
|
+ 取消视频上传
|
|
|
+ </Button>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {videoUploadStatus.isError && (
|
|
|
+ <Button
|
|
|
+ type="primary"
|
|
|
+ danger
|
|
|
+ icon={<ReloadOutlined />}
|
|
|
+ onClick={handleVideoRetry}
|
|
|
+ className={styles['retry-btn']}
|
|
|
+ >
|
|
|
+ 重试视频
|
|
|
+ </Button>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {!videoUploadStatus.isUploading && videoUploadStatus.isUploaded && (
|
|
|
+ <Button
|
|
|
+ danger
|
|
|
+ onClick={handleVideoRemove}
|
|
|
+ >
|
|
|
+ 删除已上传视频
|
|
|
+ </Button>
|
|
|
+ )}
|
|
|
+ </>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 发布按钮 */}
|
|
|
+ {videoUploadStatus.isUploaded && (
|
|
|
+ <Button
|
|
|
+ type="primary"
|
|
|
+ onClick={publishVideo}
|
|
|
+ loading={isLoading}
|
|
|
+ >
|
|
|
+ 发布视频
|
|
|
+ </Button>
|
|
|
+ )}
|
|
|
+ </Space>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Modal>
|
|
|
+ {/* 视频预览弹窗 */}
|
|
|
+ <Modal
|
|
|
+ open={videoPreviewOpen}
|
|
|
+ onCancel={() => setVideoPreviewOpen(false)}
|
|
|
+ footer={null}
|
|
|
+ width={720}
|
|
|
+ title="预览视频"
|
|
|
+ destroyOnHidden
|
|
|
+ >
|
|
|
+ <video src={videoFile?.localUrl} style={{ height: '100%', margin: '0 auto' }} controls />
|
|
|
+ </Modal>
|
|
|
+ </>
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+export default UploadVideoModal;
|