Compare commits

...

1 Commits

Author SHA1 Message Date
Kevin Wong
33d8e52802 更新 2026-02-03 17:42:04 +08:00
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 VideoPreviewModal from "@/components/VideoPreviewModal";
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'
? (process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8006')
@@ -1143,18 +1163,20 @@ export default function Home() {
/>
<label
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-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>
<button
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>
</div>
</div>
@@ -1183,7 +1205,7 @@ export default function Home() {
onClick={() => setUploadError(null)}
className="text-red-300 hover:text-white"
>
<X className="h-3.5 w-3.5" />
</button>
</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"
title="预览视频"
>
👁
<Eye className="h-4 w-4" />
</button>
<button
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"
title="删除素材"
>
🗑
<Trash2 className="h-4 w-4" />
</button>
</div>
</div>
@@ -1267,7 +1289,8 @@ export default function Home() {
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"
>
<span>📜</span>
<FileText className="h-3.5 w-3.5" />
</button>
<button
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"
}`}
>
{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>
</div>
</div>
@@ -1301,9 +1334,10 @@ export default function Home() {
</h2>
<button
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>
</div>
@@ -1490,21 +1524,23 @@ export default function Home() {
<div className="flex gap-2 mb-4">
<button
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-white/10 text-gray-300 hover:bg-white/20'
}`}
>
🔊
<Volume2 className="h-4 w-4" />
</button>
<button
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-white/10 text-gray-300 hover:bg-white/20'
}`}
>
🎤
<Mic className="h-4 w-4" />
</button>
</div>
@@ -1549,18 +1585,20 @@ export default function Home() {
/>
<label
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-purple-600 hover:bg-purple-700 text-white"
}`}
>
📤
<Upload className="h-3.5 w-3.5" />
</label>
<button
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>
</div>
</div>
@@ -1574,7 +1612,9 @@ export default function Home() {
{uploadRefError && (
<div className="mb-2 p-2 bg-red-500/20 text-red-200 rounded text-xs flex justify-between">
<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>
)}
@@ -1612,8 +1652,12 @@ export default function Home() {
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 => cancelEditing(e)} className="text-gray-400 hover:text-gray-300 text-xs"></button>
<button onClick={e => saveEditing(audio.id, e)} className="text-green-400 hover:text-green-300 text-xs">
<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>
) : (
<>
@@ -1625,14 +1669,18 @@ export default function Home() {
className="text-gray-400 hover:text-purple-400 text-xs"
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
onClick={(e) => startEditing(audio, e)}
className="text-gray-400 hover:text-blue-400 text-xs"
title="重命名"
>
<Pencil className="h-3.5 w-3.5" />
</button>
<button
onClick={(e) => {
@@ -1642,7 +1690,7 @@ export default function Home() {
className="text-gray-400 hover:text-red-400 text-xs"
title="删除"
>
🗑
<Trash2 className="h-3.5 w-3.5" />
</button>
</div>
</div>
@@ -1662,16 +1710,18 @@ export default function Home() {
{!isRecording ? (
<button
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
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>
)}
{isRecording && (
@@ -1727,9 +1777,10 @@ export default function Home() {
<div className="flex items-center gap-2">
<button
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>
<label className="relative inline-flex items-center cursor-pointer">
<input
@@ -1794,7 +1845,11 @@ export default function Home() {
className="p-1 text-gray-500 hover:text-purple-400 transition-colors"
title="试听"
>
{playingBgmId === bgm.id ? '⏸️' : '▶️'}
{playingBgmId === bgm.id ? (
<Pause className="h-4 w-4" />
) : (
<Play className="h-4 w-4" />
)}
</button>
{selectedBgmId === bgm.id && (
<span className="text-xs text-purple-300"></span>
@@ -1856,7 +1911,10 @@ export default function Home() {
... {currentTask?.progress || 0}%
</span>
) : (
"🚀 生成视频"
<span className="flex items-center justify-center gap-2">
<Rocket className="h-5 w-5" />
</span>
)}
</button>
</div>
@@ -1908,13 +1966,15 @@ export default function Home() {
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"
>
<Download className="h-4 w-4" />
</a>
<Link
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"
>
📤
<Send className="h-4 w-4" />
</Link>
</>
)}
@@ -1928,9 +1988,10 @@ export default function Home() {
</h2>
<button
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>
</div>
{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"
title="删除视频"
>
🗑
<Trash2 className="h-4 w-4" />
</button>
</div>
))}

View File

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