This commit is contained in:
Kevin Wong
2026-02-03 17:42:04 +08:00
parent 9af50a9066
commit 33d8e52802
2 changed files with 171 additions and 90 deletions

View File

@@ -9,6 +9,26 @@ import { useTask } from "@/contexts/TaskContext";
import AccountSettingsDropdown from "@/components/AccountSettingsDropdown"; import AccountSettingsDropdown from "@/components/AccountSettingsDropdown";
import VideoPreviewModal from "@/components/VideoPreviewModal"; import VideoPreviewModal from "@/components/VideoPreviewModal";
import ScriptExtractionModal from "@/components/ScriptExtractionModal"; import ScriptExtractionModal from "@/components/ScriptExtractionModal";
import {
Upload,
RefreshCw,
Eye,
Trash2,
FileText,
Sparkles,
Loader2,
Volume2,
Mic,
Play,
Pause,
Pencil,
Check,
X,
Square,
Rocket,
Download,
Send,
} from "lucide-react";
const API_BASE = typeof window === 'undefined' const API_BASE = typeof window === 'undefined'
? (process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8006') ? (process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8006')
@@ -1143,18 +1163,20 @@ export default function Home() {
/> />
<label <label
htmlFor="video-upload" htmlFor="video-upload"
className={`px-2 py-1 text-xs rounded cursor-pointer transition-all whitespace-nowrap ${isUploading className={`px-2 py-1 text-xs rounded cursor-pointer transition-all whitespace-nowrap flex items-center gap-1 ${isUploading
? "bg-gray-600 cursor-not-allowed text-gray-400" ? "bg-gray-600 cursor-not-allowed text-gray-400"
: "bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 text-white" : "bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 text-white"
}`} }`}
> >
📤 <Upload className="h-3.5 w-3.5" />
</label> </label>
<button <button
onClick={fetchMaterials} onClick={fetchMaterials}
className="px-2 py-1 text-xs bg-white/10 hover:bg-white/20 rounded text-gray-300 whitespace-nowrap" className="px-2 py-1 text-xs bg-white/10 hover:bg-white/20 rounded text-gray-300 whitespace-nowrap flex items-center gap-1"
> >
🔄 <RefreshCw className="h-3.5 w-3.5" />
</button> </button>
</div> </div>
</div> </div>
@@ -1183,7 +1205,7 @@ export default function Home() {
onClick={() => setUploadError(null)} onClick={() => setUploadError(null)}
className="text-red-300 hover:text-white" className="text-red-300 hover:text-white"
> >
<X className="h-3.5 w-3.5" />
</button> </button>
</div> </div>
)} )}
@@ -1237,7 +1259,7 @@ export default function Home() {
className="p-1 text-gray-500 hover:text-white opacity-0 group-hover:opacity-100 transition-opacity" className="p-1 text-gray-500 hover:text-white opacity-0 group-hover:opacity-100 transition-opacity"
title="预览视频" title="预览视频"
> >
👁 <Eye className="h-4 w-4" />
</button> </button>
<button <button
onClick={(e) => { onClick={(e) => {
@@ -1247,7 +1269,7 @@ export default function Home() {
className="p-1 text-gray-500 hover:text-red-400 opacity-0 group-hover:opacity-100 transition-opacity" className="p-1 text-gray-500 hover:text-red-400 opacity-0 group-hover:opacity-100 transition-opacity"
title="删除素材" title="删除素材"
> >
🗑 <Trash2 className="h-4 w-4" />
</button> </button>
</div> </div>
</div> </div>
@@ -1267,7 +1289,8 @@ export default function Home() {
onClick={() => setExtractModalOpen(true)} onClick={() => setExtractModalOpen(true)}
className="px-2 py-1 text-xs rounded transition-all whitespace-nowrap bg-purple-600 hover:bg-purple-700 text-white flex items-center gap-1" className="px-2 py-1 text-xs rounded transition-all whitespace-nowrap bg-purple-600 hover:bg-purple-700 text-white flex items-center gap-1"
> >
<span>📜</span> <FileText className="h-3.5 w-3.5" />
</button> </button>
<button <button
onClick={handleGenerateMeta} onClick={handleGenerateMeta}
@@ -1277,7 +1300,17 @@ export default function Home() {
: "bg-gradient-to-r from-blue-600 to-cyan-600 hover:from-blue-700 hover:to-cyan-700 text-white" : "bg-gradient-to-r from-blue-600 to-cyan-600 hover:from-blue-700 hover:to-cyan-700 text-white"
}`} }`}
> >
{isGeneratingMeta ? "⏳ 生成中..." : "🤖 AI生成标题标签"} {isGeneratingMeta ? (
<span className="flex items-center gap-1">
<Loader2 className="h-3.5 w-3.5 animate-spin" />
...
</span>
) : (
<span className="flex items-center gap-1">
<Sparkles className="h-3.5 w-3.5" />
AI生成标题标签
</span>
)}
</button> </button>
</div> </div>
</div> </div>
@@ -1301,9 +1334,10 @@ export default function Home() {
</h2> </h2>
<button <button
onClick={() => setShowStylePreview((prev) => !prev)} onClick={() => setShowStylePreview((prev) => !prev)}
className="px-2 py-1 text-xs bg-white/10 hover:bg-white/20 rounded text-gray-300" className="px-2 py-1 text-xs bg-white/10 hover:bg-white/20 rounded text-gray-300 flex items-center gap-1"
> >
{showStylePreview ? "收起预览" : "👁️ 预览样式"} <Eye className="h-3.5 w-3.5" />
{showStylePreview ? "收起预览" : "预览样式"}
</button> </button>
</div> </div>
@@ -1490,21 +1524,23 @@ export default function Home() {
<div className="flex gap-2 mb-4"> <div className="flex gap-2 mb-4">
<button <button
onClick={() => setTtsMode('edgetts')} onClick={() => setTtsMode('edgetts')}
className={`flex-1 py-2 px-4 rounded-lg font-medium transition-all ${ttsMode === 'edgetts' className={`flex-1 py-2 px-4 rounded-lg font-medium transition-all flex items-center justify-center gap-2 ${ttsMode === 'edgetts'
? 'bg-purple-600 text-white' ? 'bg-purple-600 text-white'
: 'bg-white/10 text-gray-300 hover:bg-white/20' : 'bg-white/10 text-gray-300 hover:bg-white/20'
}`} }`}
> >
🔊 <Volume2 className="h-4 w-4" />
</button> </button>
<button <button
onClick={() => setTtsMode('voiceclone')} onClick={() => setTtsMode('voiceclone')}
className={`flex-1 py-2 px-4 rounded-lg font-medium transition-all ${ttsMode === 'voiceclone' className={`flex-1 py-2 px-4 rounded-lg font-medium transition-all flex items-center justify-center gap-2 ${ttsMode === 'voiceclone'
? 'bg-purple-600 text-white' ? 'bg-purple-600 text-white'
: 'bg-white/10 text-gray-300 hover:bg-white/20' : 'bg-white/10 text-gray-300 hover:bg-white/20'
}`} }`}
> >
🎤 <Mic className="h-4 w-4" />
</button> </button>
</div> </div>
@@ -1549,18 +1585,20 @@ export default function Home() {
/> />
<label <label
htmlFor="ref-audio-upload" htmlFor="ref-audio-upload"
className={`px-2 py-1 text-xs rounded cursor-pointer transition-all ${isUploadingRef className={`px-2 py-1 text-xs rounded cursor-pointer transition-all flex items-center gap-1 ${isUploadingRef
? "bg-gray-600 cursor-not-allowed text-gray-400" ? "bg-gray-600 cursor-not-allowed text-gray-400"
: "bg-purple-600 hover:bg-purple-700 text-white" : "bg-purple-600 hover:bg-purple-700 text-white"
}`} }`}
> >
📤 <Upload className="h-3.5 w-3.5" />
</label> </label>
<button <button
onClick={fetchRefAudios} onClick={fetchRefAudios}
className="px-2 py-1 text-xs bg-white/10 hover:bg-white/20 rounded text-gray-300" className="px-2 py-1 text-xs bg-white/10 hover:bg-white/20 rounded text-gray-300 flex items-center gap-1"
> >
🔄 <RefreshCw className="h-3.5 w-3.5" />
</button> </button>
</div> </div>
</div> </div>
@@ -1574,7 +1612,9 @@ export default function Home() {
{uploadRefError && ( {uploadRefError && (
<div className="mb-2 p-2 bg-red-500/20 text-red-200 rounded text-xs flex justify-between"> <div className="mb-2 p-2 bg-red-500/20 text-red-200 rounded text-xs flex justify-between">
<span> {uploadRefError}</span> <span> {uploadRefError}</span>
<button onClick={() => setUploadRefError(null)} className="text-red-300 hover:text-white"></button> <button onClick={() => setUploadRefError(null)} className="text-red-300 hover:text-white">
<X className="h-3.5 w-3.5" />
</button>
</div> </div>
)} )}
@@ -1612,8 +1652,12 @@ export default function Home() {
if (e.key === 'Escape') cancelEditing(e as any); if (e.key === 'Escape') cancelEditing(e as any);
}} }}
/> />
<button onClick={e => saveEditing(audio.id, e)} className="text-green-400 hover:text-green-300 text-xs"></button> <button onClick={e => saveEditing(audio.id, e)} className="text-green-400 hover:text-green-300 text-xs">
<button onClick={e => cancelEditing(e)} className="text-gray-400 hover:text-gray-300 text-xs"></button> <Check className="h-3 w-3" />
</button>
<button onClick={e => cancelEditing(e)} className="text-gray-400 hover:text-gray-300 text-xs">
<X className="h-3 w-3" />
</button>
</div> </div>
) : ( ) : (
<> <>
@@ -1625,14 +1669,18 @@ export default function Home() {
className="text-gray-400 hover:text-purple-400 text-xs" className="text-gray-400 hover:text-purple-400 text-xs"
title="试听" title="试听"
> >
{playingAudioId === audio.id ? '⏸️' : '▶️'} {playingAudioId === audio.id ? (
<Pause className="h-3.5 w-3.5" />
) : (
<Play className="h-3.5 w-3.5" />
)}
</button> </button>
<button <button
onClick={(e) => startEditing(audio, e)} onClick={(e) => startEditing(audio, e)}
className="text-gray-400 hover:text-blue-400 text-xs" className="text-gray-400 hover:text-blue-400 text-xs"
title="重命名" title="重命名"
> >
<Pencil className="h-3.5 w-3.5" />
</button> </button>
<button <button
onClick={(e) => { onClick={(e) => {
@@ -1642,7 +1690,7 @@ export default function Home() {
className="text-gray-400 hover:text-red-400 text-xs" className="text-gray-400 hover:text-red-400 text-xs"
title="删除" title="删除"
> >
🗑 <Trash2 className="h-3.5 w-3.5" />
</button> </button>
</div> </div>
</div> </div>
@@ -1662,16 +1710,18 @@ export default function Home() {
{!isRecording ? ( {!isRecording ? (
<button <button
onClick={startRecording} onClick={startRecording}
className="px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg text-sm font-medium transition-colors" className="px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg text-sm font-medium transition-colors flex items-center gap-2"
> >
<Mic className="h-4 w-4" />
</button> </button>
) : ( ) : (
<button <button
onClick={stopRecording} onClick={stopRecording}
className="px-4 py-2 bg-gray-600 hover:bg-gray-700 text-white rounded-lg text-sm font-medium transition-colors" className="px-4 py-2 bg-gray-600 hover:bg-gray-700 text-white rounded-lg text-sm font-medium transition-colors flex items-center gap-2"
> >
<Square className="h-4 w-4" />
</button> </button>
)} )}
{isRecording && ( {isRecording && (
@@ -1727,9 +1777,10 @@ export default function Home() {
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<button <button
onClick={fetchBgmList} onClick={fetchBgmList}
className="px-2 py-1 text-xs bg-white/10 hover:bg-white/20 rounded text-gray-300" className="px-2 py-1 text-xs bg-white/10 hover:bg-white/20 rounded text-gray-300 flex items-center gap-1"
> >
🔄 <RefreshCw className="h-3.5 w-3.5" />
</button> </button>
<label className="relative inline-flex items-center cursor-pointer"> <label className="relative inline-flex items-center cursor-pointer">
<input <input
@@ -1794,7 +1845,11 @@ export default function Home() {
className="p-1 text-gray-500 hover:text-purple-400 transition-colors" className="p-1 text-gray-500 hover:text-purple-400 transition-colors"
title="试听" title="试听"
> >
{playingBgmId === bgm.id ? '⏸️' : '▶️'} {playingBgmId === bgm.id ? (
<Pause className="h-4 w-4" />
) : (
<Play className="h-4 w-4" />
)}
</button> </button>
{selectedBgmId === bgm.id && ( {selectedBgmId === bgm.id && (
<span className="text-xs text-purple-300"></span> <span className="text-xs text-purple-300"></span>
@@ -1856,7 +1911,10 @@ export default function Home() {
... {currentTask?.progress || 0}% ... {currentTask?.progress || 0}%
</span> </span>
) : ( ) : (
"🚀 生成视频" <span className="flex items-center justify-center gap-2">
<Rocket className="h-5 w-5" />
</span>
)} )}
</button> </button>
</div> </div>
@@ -1908,13 +1966,15 @@ export default function Home() {
download download
className="mt-4 w-full py-3 rounded-xl bg-green-600 hover:bg-green-700 text-white font-medium flex items-center justify-center gap-2 transition-colors" className="mt-4 w-full py-3 rounded-xl bg-green-600 hover:bg-green-700 text-white font-medium flex items-center justify-center gap-2 transition-colors"
> >
<Download className="h-4 w-4" />
</a> </a>
<Link <Link
href="/publish" href="/publish"
className="mt-3 w-full py-3 rounded-xl bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 text-white font-medium flex items-center justify-center gap-2 transition-colors" className="mt-3 w-full py-3 rounded-xl bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 text-white font-medium flex items-center justify-center gap-2 transition-colors"
> >
📤 <Send className="h-4 w-4" />
</Link> </Link>
</> </>
)} )}
@@ -1928,9 +1988,10 @@ export default function Home() {
</h2> </h2>
<button <button
onClick={() => fetchGeneratedVideos()} onClick={() => fetchGeneratedVideos()}
className="px-3 py-1 text-xs bg-white/10 hover:bg-white/20 rounded text-gray-300" className="px-3 py-1 text-xs bg-white/10 hover:bg-white/20 rounded text-gray-300 flex items-center gap-1"
> >
🔄 <RefreshCw className="h-3.5 w-3.5" />
</button> </button>
</div> </div>
{generatedVideos.length === 0 ? ( {generatedVideos.length === 0 ? (
@@ -1972,7 +2033,7 @@ export default function Home() {
className="p-1 text-gray-500 hover:text-red-400 opacity-0 group-hover:opacity-100 transition-opacity" className="p-1 text-gray-500 hover:text-red-400 opacity-0 group-hover:opacity-100 transition-opacity"
title="删除视频" title="删除视频"
> >
🗑 <Trash2 className="h-4 w-4" />
</button> </button>
</div> </div>
))} ))}

View File

@@ -1,11 +1,19 @@
"use client"; "use client";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import useSWR from 'swr'; import useSWR from 'swr';
import Link from "next/link"; import Link from "next/link";
import api from "@/lib/axios"; import api from "@/lib/axios";
import { useAuth } from "@/contexts/AuthContext"; import { useAuth } from "@/contexts/AuthContext";
import AccountSettingsDropdown from "@/components/AccountSettingsDropdown"; import AccountSettingsDropdown from "@/components/AccountSettingsDropdown";
import {
ArrowLeft,
RotateCcw,
LogOut,
QrCode,
Rocket,
Clock,
} from "lucide-react";
// SWR fetcher 使用 axios自动处理 401/403 // SWR fetcher 使用 axios自动处理 401/403
const fetcher = (url: string) => api.get(url).then((res) => res.data); const fetcher = (url: string) => api.get(url).then((res) => res.data);
@@ -312,12 +320,13 @@ export default function PublishPage() {
IPAgent IPAgent
</Link> </Link>
<div className="flex items-center gap-1 sm:gap-4"> <div className="flex items-center gap-1 sm:gap-4">
<Link <Link
href="/" href="/"
className="px-2 sm:px-4 py-1 sm:py-2 text-sm sm:text-base bg-white/10 hover:bg-white/20 text-white rounded-lg transition-colors" className="px-2 sm:px-4 py-1 sm:py-2 text-sm sm:text-base bg-white/10 hover:bg-white/20 text-white rounded-lg transition-colors flex items-center gap-1"
> >
<ArrowLeft className="h-4 w-4" />
</Link>
</Link>
<span className="px-2 sm:px-4 py-1 sm:py-2 text-sm sm:text-base bg-gradient-to-r from-purple-600 to-pink-600 text-white rounded-lg font-semibold"> <span className="px-2 sm:px-4 py-1 sm:py-2 text-sm sm:text-base bg-gradient-to-r from-purple-600 to-pink-600 text-white rounded-lg font-semibold">
</span> </span>
@@ -362,26 +371,29 @@ export default function PublishPage() {
<div className="flex gap-2"> <div className="flex gap-2">
{account.logged_in ? ( {account.logged_in ? (
<> <>
<button <button
onClick={() => handleLogin(account.platform)} onClick={() => handleLogin(account.platform)}
className="px-3 py-1 bg-white/10 hover:bg-white/20 text-white text-sm rounded-lg transition-colors" className="px-3 py-1 bg-white/10 hover:bg-white/20 text-white text-sm rounded-lg transition-colors flex items-center gap-1"
> >
<RotateCcw className="h-3.5 w-3.5" />
</button>
<button </button>
onClick={() => handleLogout(account.platform)} <button
className="px-3 py-1 bg-red-500/80 hover:bg-red-600 text-white text-sm rounded-lg transition-colors" onClick={() => handleLogout(account.platform)}
> className="px-3 py-1 bg-red-500/80 hover:bg-red-600 text-white text-sm rounded-lg transition-colors flex items-center gap-1"
>
</button> <LogOut className="h-3.5 w-3.5" />
</button>
</> </>
) : ( ) : (
<button <button
onClick={() => handleLogin(account.platform)} onClick={() => handleLogin(account.platform)}
className="px-3 py-1 bg-purple-600 hover:bg-purple-700 text-white text-sm rounded-lg transition-colors" className="px-3 py-1 bg-purple-600 hover:bg-purple-700 text-white text-sm rounded-lg transition-colors flex items-center gap-1"
> >
🔐 <QrCode className="h-3.5 w-3.5" />
</button>
</button>
)} )}
</div> </div>
</div> </div>
@@ -487,32 +499,40 @@ export default function PublishPage() {
<div className="space-y-3"> <div className="space-y-3">
<div className="flex gap-3"> <div className="flex gap-3">
{/* 立即发布 - 占 3/4 */} {/* 立即发布 - 占 3/4 */}
<button <button
onClick={() => { onClick={() => {
setScheduleMode("now"); setScheduleMode("now");
handlePublish(); handlePublish();
}} }}
disabled={isPublishing || selectedPlatforms.length === 0} disabled={isPublishing || selectedPlatforms.length === 0}
className={`flex-[3] py-4 rounded-xl font-bold text-lg transition-all ${isPublishing || selectedPlatforms.length === 0 className={`flex-[3] py-4 rounded-xl font-bold text-lg transition-all flex items-center justify-center gap-2 ${isPublishing || selectedPlatforms.length === 0
? "bg-gray-600 cursor-not-allowed text-gray-400" ? "bg-gray-600 cursor-not-allowed text-gray-400"
: "bg-gradient-to-r from-green-600 to-teal-600 hover:from-green-700 hover:to-teal-700 text-white" : "bg-gradient-to-r from-green-600 to-teal-600 hover:from-green-700 hover:to-teal-700 text-white"
}`} }`}
> >
{isPublishing && scheduleMode === "now" ? "发布中..." : "🚀 立即发布"} {isPublishing && scheduleMode === "now" ? (
</button> "发布中..."
) : (
<>
<Rocket className="h-5 w-5" />
</>
)}
</button>
{/* 定时发布 - 占 1/4 */} {/* 定时发布 - 占 1/4 */}
<button <button
onClick={() => setScheduleMode(scheduleMode === "scheduled" ? "now" : "scheduled")} onClick={() => setScheduleMode(scheduleMode === "scheduled" ? "now" : "scheduled")}
disabled={isPublishing || selectedPlatforms.length === 0} disabled={isPublishing || selectedPlatforms.length === 0}
className={`flex-1 py-4 rounded-xl font-bold text-base transition-all ${isPublishing || selectedPlatforms.length === 0 className={`flex-1 py-4 rounded-xl font-bold text-base transition-all flex items-center justify-center gap-2 ${isPublishing || selectedPlatforms.length === 0
? "bg-gray-600 cursor-not-allowed text-gray-400" ? "bg-gray-600 cursor-not-allowed text-gray-400"
: scheduleMode === "scheduled" : scheduleMode === "scheduled"
? "bg-purple-600 text-white" ? "bg-purple-600 text-white"
: "bg-white/10 hover:bg-white/20 text-white" : "bg-white/10 hover:bg-white/20 text-white"
}`} }`}
> >
<Clock className="h-5 w-5" />
</button>
</button>
</div> </div>
{/* 定时发布时间选择器 */} {/* 定时发布时间选择器 */}