video-flow-b/components/common/VideoShareForm.tsx
2025-10-22 18:16:35 +08:00

196 lines
6.4 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { AlertCircle, Loader2 } from 'lucide-react';
import { shareApiUrl } from '@/lib/env';
import { message } from 'antd';
interface VideoShareFormProps {
onCancel: () => void;
onShare: (videoUrl: string, videoName: string) => void;
error?: string | null;
platform?: string;
videoUrl?: string;
videoName?: string;
}
// 平台上传接口映射(统一使用小写 key
const PLATFORM_UPLOAD_API: Record<string, string> = {
'youtube': '/api/video-share/youtube/upload',
'tiktok': '/api/video-share/tiktok/upload',
'reddit': '/api/video-share/reddit/upload',
'twitter': '/api/video-share/x/upload',
// Instagram 开发中,暂不支持
// 'instagram': '/api/video-share/instagram/upload',
};
export const VideoShareForm: React.FC<VideoShareFormProps> = ({
onCancel,
onShare,
error = null,
platform = '',
videoUrl: initialVideoUrl = '',
videoName: initialVideoName = ''
}) => {
const [videoUrl, setVideoUrl] = useState(initialVideoUrl);
const [videoName, setVideoName] = useState(initialVideoName);
const [uploading, setUploading] = useState(false);
/**
* 获取用户ID
*/
const getUserId = () => {
const currentUser = JSON.parse(localStorage.getItem('currentUser') || '{}');
return currentUser.id || currentUser.userId;
};
/**
* 上传视频到指定平台
*/
const uploadVideoToPlatform = async (platformName: string, contentUrl: string, title: string) => {
const userId = getUserId();
if (!userId) {
message.error('用户ID不存在请先登录');
return false;
}
// 统一转换成小写后查找 API 路径
const normalizedPlatform = platformName.toLowerCase().replace(/\s+/g, '').replace(/[()]/g, '');
const apiPath = PLATFORM_UPLOAD_API[normalizedPlatform];
if (!apiPath) {
message.error(`平台 ${platformName} 暂不支持或正在开发中`);
return false;
}
try {
const response = await fetch(`${shareApiUrl}${apiPath}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token') || ''}`,
'ngrok-skip-browser-warning': 'true'
},
body: JSON.stringify({
user_id: userId,
content_url: contentUrl,
title: title
})
});
const data = await response.json();
if (data.successful && data.code === 0) {
message.success(`视频已成功上传到 ${platformName}`);
return true;
} else {
message.error(data.message || `上传到 ${platformName} 失败`);
return false;
}
} catch (error) {
console.error(`上传到 ${platformName} 失败:`, error);
message.error(`上传失败:${error instanceof Error ? error.message : '网络错误'}`);
return false;
}
};
const handleShare = async () => {
if (!videoUrl || !videoName) {
message.warning('请填写完整的视频信息');
return;
}
setUploading(true);
try {
const success = await uploadVideoToPlatform(platform, videoUrl, videoName);
if (success) {
onShare(videoUrl, videoName);
}
} finally {
setUploading(false);
}
};
return (
<div data-alt="video-share-form-container" className="backdrop-blur-lg bg-white/10 dark:bg-slate-900/30 rounded-lg p-6 shadow-lg">
<h2 data-alt="form-title" className="text-2xl font-bold mb-4 text-white">
{platform && ` - ${platform}`}
</h2>
{/* 错误信息显示 */}
{error && (
<div className="mb-4 p-3 bg-red-500/20 border border-red-500/50 rounded-lg flex items-start gap-2">
<AlertCircle className="w-5 h-5 text-red-400 flex-shrink-0 mt-0.5" />
<div className="flex-1">
<p className="text-red-200 text-sm font-medium"></p>
<p className="text-red-300 text-xs mt-1">{error}</p>
</div>
</div>
)}
<div data-alt="form-fields" className="space-y-4">
<div data-alt="video-url-field" className="flex flex-col gap-2">
<label htmlFor="videoUrl" className="text-sm font-medium text-white/90"></label>
<Input
id="videoUrl"
value={videoUrl}
onChange={(e) => setVideoUrl(e.target.value)}
placeholder="请输入视频链接"
className="bg-white/10 border-white/20 text-white placeholder:text-white/50"
disabled={!!error}
/>
</div>
<div data-alt="video-name-field" className="flex flex-col gap-2">
<label htmlFor="videoName" className="text-sm font-medium text-white/90"></label>
<Input
id="videoName"
value={videoName}
onChange={(e) => setVideoName(e.target.value)}
placeholder="请输入视频名称"
className="bg-white/10 border-white/20 text-white placeholder:text-white/50"
disabled={!!error}
/>
</div>
</div>
<div data-alt="form-actions" className="mt-6 flex justify-end gap-2">
<Button
variant="outline"
onClick={onCancel}
data-alt="cancel-button"
className="border-white/30 text-white hover:bg-white/10"
disabled={uploading}
>
{error ? '关闭' : '取消'}
</Button>
{!error && (
<Button
onClick={handleShare}
data-alt="share-button"
className="
relative overflow-hidden
bg-gradient-to-r from-[#6AF4F9] to-[#C039F6]
hover:from-[#5ee3e8] hover:to-[#b028e5]
text-white font-medium
shadow-lg shadow-purple-500/30
hover:shadow-xl hover:shadow-purple-500/50
transition-all duration-300
disabled:opacity-50 disabled:cursor-not-allowed
disabled:shadow-none
"
disabled={!videoUrl || !videoName || uploading}
>
{uploading ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
...
</>
) : (
'一键转发'
)}
</Button>
)}
</div>
</div>
);
};