diff --git a/apps/web/app/components/CreateIssueForm.tsx b/apps/web/app/components/CreateIssueForm.tsx
deleted file mode 100644
index 166ef542f..000000000
--- a/apps/web/app/components/CreateIssueForm.tsx
+++ /dev/null
@@ -1,145 +0,0 @@
-'use client';
-
-import { useState } from 'react';
-
-const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000';
-
-interface CreateIssueFormProps {
- taskId: string;
- projectId: string;
- onSuccess: () => void;
- onCancel: () => void;
-}
-
-export default function CreateIssueForm({ taskId, projectId, onSuccess, onCancel }: CreateIssueFormProps) {
- const [loading, setLoading] = useState(false);
- const [error, setError] = useState('');
- const [formData, setFormData] = useState({
- title: '',
- description: '',
- });
-
- const handleSubmit = async (e: React.FormEvent) => {
- e.preventDefault();
- setLoading(true);
- setError('');
-
- try {
- const res = await fetch(`${API_BASE}/api/v1/project-management/issues`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- task_id: taskId,
- project_id: projectId,
- ...formData,
- }),
- });
-
- if (!res.ok) {
- const data = await res.json();
- throw new Error(data.detail || '创建失败');
- }
-
- onSuccess();
- } catch (err: unknown) {
- setError(err instanceof Error ? err.message : String(err));
- } finally {
- setLoading(false);
- }
- };
-
- return (
-
- );
-}
diff --git a/apps/web/app/components/CreateTaskForm.tsx b/apps/web/app/components/CreateTaskForm.tsx
deleted file mode 100644
index d48b57c3c..000000000
--- a/apps/web/app/components/CreateTaskForm.tsx
+++ /dev/null
@@ -1,198 +0,0 @@
-'use client';
-
-import { useState } from 'react';
-import { useRouter } from 'next/navigation';
-
-const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000';
-
-interface CreateTaskFormProps {
- projectId: string;
- onSuccess?: () => void;
- onCancel?: () => void;
-}
-
-export default function CreateTaskForm({ projectId, onSuccess, onCancel }: CreateTaskFormProps) {
- const router = useRouter();
- const [loading, setLoading] = useState(false);
- const [error, setError] = useState('');
- const [formData, setFormData] = useState({
- name: '',
- description: '',
- priority: 'medium',
- assignee_user_id: '',
- parent_task_id: '',
- });
-
- const handleSubmit = async (e: React.FormEvent) => {
- e.preventDefault();
- setLoading(true);
- setError('');
-
- try {
- const res = await fetch(`${API_BASE}/api/v1/project-management/tasks`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- project_id: projectId,
- ...formData,
- }),
- });
-
- if (!res.ok) {
- const data = await res.json();
- throw new Error(data.detail || '创建失败');
- }
-
- if (onSuccess) {
- onSuccess();
- } else {
- router.push('/projects');
- }
- } catch (err: unknown) {
- setError(err instanceof Error ? err.message : String(err));
- } finally {
- setLoading(false);
- }
- };
-
- return (
-
- );
-}
diff --git a/apps/web/app/components/EditTaskForm.tsx b/apps/web/app/components/EditTaskForm.tsx
deleted file mode 100644
index d23dec9a7..000000000
--- a/apps/web/app/components/EditTaskForm.tsx
+++ /dev/null
@@ -1,167 +0,0 @@
-'use client';
-
-import { useState } from 'react';
-
-const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000';
-
-interface EditTaskFormProps {
- taskId: string;
- initialData: {
- name: string;
- description: string;
- priority: string;
- assignee_user_id: string;
- };
- onSuccess?: () => void;
- onCancel?: () => void;
-}
-
-export default function EditTaskForm({ taskId, initialData, onSuccess, onCancel }: EditTaskFormProps) {
- const [loading, setLoading] = useState(false);
- const [error, setError] = useState('');
- const [formData, setFormData] = useState(initialData);
-
- const handleSubmit = async (e: React.FormEvent) => {
- e.preventDefault();
- setLoading(true);
- setError('');
-
- try {
- const res = await fetch(`${API_BASE}/api/v1/project-management/tasks/${taskId}`, {
- method: 'PATCH',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(formData),
- });
-
- if (!res.ok) {
- const data = await res.json();
- throw new Error(data.detail || '保存失败');
- }
-
- if (onSuccess) onSuccess();
- } catch (err: unknown) {
- setError(err instanceof Error ? err.message : String(err));
- } finally {
- setLoading(false);
- }
- };
-
- return (
-
- );
-}
diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css
deleted file mode 100644
index 6209f4970..000000000
--- a/apps/web/app/globals.css
+++ /dev/null
@@ -1,26 +0,0 @@
-* {
- margin: 0;
- padding: 0;
- box-sizing: border-box;
-}
-
-body {
- font-family: -apple-system, BlinkMacSystemFont, "Microsoft YaHei", sans-serif;
- background: #F5F7FA;
- color: #1D2129;
- height: 100vh;
- overflow: hidden;
-}
-
-:root {
- --primary: #165DFF;
- --primary-hover: #0e48d1;
- --border: #E5E6EB;
- --bg-white: #fff;
- --bg-gray: #F5F7FA;
- --text-primary: #1D2129;
- --text-secondary: #6E7681;
- --success: #00B42A;
- --warning: #FF7D00;
- --error: #F53F3F;
-}
diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx
deleted file mode 100644
index 1aacc01bf..000000000
--- a/apps/web/app/layout.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import type { Metadata } from "next";
-import "./globals.css";
-
-// eslint-disable-next-line react-refresh/only-export-components
-export const metadata: Metadata = {
- title: "小虾 SaaS - 项目推进器",
- description: "AI 视频自动化剪辑系统 - 项目管理",
-};
-
-export default function RootLayout({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
- return (
-
- {children}
-
- );
-}
diff --git a/apps/web/app/milestones/page.tsx b/apps/web/app/milestones/page.tsx
deleted file mode 100644
index 7147c768e..000000000
--- a/apps/web/app/milestones/page.tsx
+++ /dev/null
@@ -1,232 +0,0 @@
-'use client';
-
-import { useEffect, useState } from 'react';
-import Link from 'next/link';
-
-interface Milestone {
- id: string;
- name: string;
- description: string;
- target_date: string | null;
- completed: boolean;
- completed_at: string | null;
- created_at: string;
-}
-
-const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000';
-
-export default function MilestonesPage() {
- const [milestones, setMilestones] = useState([]);
- const [loading, setLoading] = useState(true);
- const [error, setError] = useState('');
- const [showCreateForm, setShowCreateForm] = useState(false);
- const [formData, setFormData] = useState({ name: '', description: '' });
-
- const projectId = 'demo_project_1';
-
- useEffect(() => {
- fetchMilestones();
- }, []);
-
- const fetchMilestones = async () => {
- setLoading(true);
- try {
- const res = await fetch(`${API_BASE}/api/v1/project-management/milestones?project_id=${projectId}`);
- if (!res.ok) throw new Error('获取里程碑列表失败');
- const data = await res.json();
- setMilestones(data);
- } catch (err: unknown) {
- setError(err instanceof Error ? err.message : String(err));
- } finally {
- setLoading(false);
- }
- };
-
- const handleCreateMilestone = async (e: React.FormEvent) => {
- e.preventDefault();
- try {
- const res = await fetch(`${API_BASE}/api/v1/project-management/milestones`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- project_id: projectId,
- ...formData,
- }),
- });
- if (!res.ok) throw new Error('创建失败');
- setFormData({ name: '', description: '' });
- setShowCreateForm(false);
- fetchMilestones();
- } catch (err: unknown) {
- alert(err instanceof Error ? err.message : String(err));
- }
- };
-
- if (loading) {
- return (
-
- );
- }
-
- return (
-
- {/* Header */}
-
-
-
- 📁 项目推进器
-
- 里程碑管理
-
-
-
-
- {/* Main */}
-
- {error && (
-
- {error}
-
- )}
-
- {showCreateForm && (
-
-
新增里程碑
-
-
- )}
-
- {milestones.length === 0 ? (
-
-
暂无里程碑
-
点击右上角"+ 新增里程碑"创建第一个里程碑
-
- ) : (
-
- {milestones.map((milestone) => (
-
-
- {milestone.completed ? '🎉' : '🎯'}
-
{milestone.name}
-
- {milestone.completed ? '已完成' : '进行中'}
-
-
- {milestone.description && (
-
- {milestone.description}
-
- )}
-
- 创建时间:{new Date(milestone.created_at).toLocaleDateString('zh-CN')}
- {milestone.completed_at && (
-
- 完成时间:{new Date(milestone.completed_at).toLocaleDateString('zh-CN')}
-
- )}
-
-
- ))}
-
- )}
-
-
- );
-}
diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx
deleted file mode 100644
index aa640a2f1..000000000
--- a/apps/web/app/page.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-export default function HomePage() {
- return (
-
-
- 📁 小虾 SaaS 项目推进器
-
-
- 完整的项目管理与任务跟踪系统
-
-
-
- );
-}
diff --git a/apps/web/app/projects/page.tsx b/apps/web/app/projects/page.tsx
deleted file mode 100644
index 72d81224a..000000000
--- a/apps/web/app/projects/page.tsx
+++ /dev/null
@@ -1,259 +0,0 @@
-'use client';
-
-import { useEffect, useState } from 'react';
-import Link from 'next/link';
-import CreateTaskForm from '../components/CreateTaskForm';
-
-interface Task {
- id: string;
- name: string;
- status: string;
- priority: string;
- progress: number;
- assignee_user_id: string;
- created_at: string;
- updated_at: string;
-}
-
-const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000';
-
-export default function ProjectsPage() {
- const [tasks, setTasks] = useState([]);
- const [loading, setLoading] = useState(true);
- const [error, setError] = useState('');
- const [showCreateForm, setShowCreateForm] = useState(false);
-
- // 模拟项目ID,生产环境应该从路由或上下文获取
- const projectId = 'demo_project_1';
-
- useEffect(() => {
- fetchTasks();
- }, []);
-
- const fetchTasks = async () => {
- setLoading(true);
- try {
- const res = await fetch(`${API_BASE}/api/v1/project-management/tasks?project_id=${projectId}`);
- if (!res.ok) throw new Error('获取任务列表失败');
- const data = await res.json();
- setTasks(data);
- } catch (err: unknown) {
- setError(err instanceof Error ? err.message : String(err));
- } finally {
- setLoading(false);
- }
- };
-
- const getStatusColor = (status: string) => {
- const colors: Record = {
- pending: '#86909C',
- in_progress: '#165DFF',
- completed: '#00B42A',
- blocked: '#F53F3F',
- cancelled: '#6E7681',
- };
- return colors[status] || '#6E7681';
- };
-
- const getStatusText = (status: string) => {
- const texts: Record = {
- pending: '待开始',
- in_progress: '进行中',
- completed: '已完成',
- blocked: '阻塞',
- cancelled: '已取消',
- };
- return texts[status] || status;
- };
-
- const getPriorityText = (priority: string) => {
- const texts: Record = {
- low: '低',
- medium: '中',
- high: '高',
- urgent: '紧急',
- };
- return texts[priority] || priority;
- };
-
- if (loading && !showCreateForm) {
- return (
-
- );
- }
-
- return (
-
- {/* Header */}
-
-
-
- 📁 项目推进器
-
- Demo 项目
-
-
-
-
-
-
-
- {/* Main Content */}
-
- {error && (
-
- {error}
-
- )}
-
- {showCreateForm ? (
- {
- setShowCreateForm(false);
- fetchTasks();
- }}
- onCancel={() => setShowCreateForm(false)}
- />
- ) : tasks.length === 0 ? (
-
-
暂无任务
-
点击右上角"+ 新增任务"创建第一个任务
-
- ) : (
-
-
-
-
- | 任务名称 |
- 状态 |
- 优先级 |
- 进度 |
- 创建时间 |
-
-
-
- {tasks.map((task) => (
-
- |
-
- {task.name}
-
- |
-
-
- {getStatusText(task.status)}
-
- |
-
- {getPriorityText(task.priority)}
- |
-
-
-
-
- {task.progress}%
-
-
- |
-
- {new Date(task.created_at).toLocaleDateString('zh-CN')}
- |
-
- ))}
-
-
-
- )}
-
-
- {/* Footer */}
-
-
- );
-}
diff --git a/apps/web/app/tasks/[id]/page.tsx b/apps/web/app/tasks/[id]/page.tsx
deleted file mode 100644
index 641efab5d..000000000
--- a/apps/web/app/tasks/[id]/page.tsx
+++ /dev/null
@@ -1,352 +0,0 @@
-'use client';
-
-import { useCallback, useEffect, useState } from 'react';
-import Link from 'next/link';
-import { useParams } from 'next/navigation';
-import CreateIssueForm from '../../components/CreateIssueForm';
-import EditTaskForm from '../../components/EditTaskForm';
-
-interface Task {
- id: string;
- name: string;
- description: string;
- status: string;
- priority: string;
- progress: number;
- assignee_user_id: string;
- parent_task_id: string;
- project_id: string;
- planned_start_date: string | null;
- planned_end_date: string | null;
- actual_start_date: string | null;
- actual_end_date: string | null;
- tags: string[];
- created_at: string;
- updated_at: string;
-}
-
-interface TaskIssue {
- id: string;
- title: string;
- description: string;
- resolved: boolean;
- resolved_at: string | null;
- created_at: string;
-}
-
-const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000';
-
-export default function TaskDetailPage() {
- const params = useParams();
- const taskId = params.id as string;
-
- const [task, setTask] = useState(null);
- const [issues, setIssues] = useState([]);
- const [loading, setLoading] = useState(true);
- const [error, setError] = useState('');
- const [updating, setUpdating] = useState(false);
- const [showIssueForm, setShowIssueForm] = useState(false);
- const [showEditForm, setShowEditForm] = useState(false);
-
- const fetchTaskDetail = useCallback(async () => {
- try {
- const res = await fetch(`${API_BASE}/api/v1/project-management/tasks/${taskId}`);
- if (!res.ok) {
- if (res.status === 404) {
- throw new Error('任务不存在');
- }
- throw new Error('获取任务详情失败');
- }
- const data = await res.json();
- setTask(data);
- } catch (err: unknown) {
- setError(err instanceof Error ? err.message : String(err));
- } finally {
- setLoading(false);
- }
- }, [taskId]);
-
- const fetchTaskIssues = useCallback(async () => {
- try {
- const res = await fetch(`${API_BASE}/api/v1/project-management/issues?task_id=${taskId}`);
- if (res.ok) {
- const data = await res.json();
- setIssues(data);
- }
- } catch (err) {
- console.error('获取问题列表失败:', err);
- }
- }, [taskId]);
-
- useEffect(() => {
- fetchTaskDetail();
- fetchTaskIssues();
- }, [fetchTaskDetail, fetchTaskIssues]);
-
- const resolveIssue = async (issueId: string) => {
- setUpdating(true);
- try {
- const res = await fetch(`${API_BASE}/api/v1/project-management/issues/${issueId}/resolve`, {
- method: 'PATCH',
- });
- if (!res.ok) throw new Error('解决问题失败');
- await fetchTaskIssues();
- } catch (err: unknown) {
- alert(err instanceof Error ? err.message : String(err));
- } finally {
- setUpdating(false);
- }
- };
-
- const updateStatus = async (newStatus: string) => {
- setUpdating(true);
- try {
- const res = await fetch(`${API_BASE}/api/v1/project-management/tasks/${taskId}/status`, {
- method: 'PATCH',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ status: newStatus }),
- });
- if (!res.ok) throw new Error('更新状态失败');
- await fetchTaskDetail();
- } catch (err: unknown) {
- alert(err instanceof Error ? err.message : String(err));
- } finally {
- setUpdating(false);
- }
- };
-
- const updateProgress = async (newProgress: number) => {
- setUpdating(true);
- try {
- const res = await fetch(`${API_BASE}/api/v1/project-management/tasks/${taskId}/progress`, {
- method: 'PATCH',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ progress: newProgress }),
- });
- if (!res.ok) throw new Error('更新进度失败');
- await fetchTaskDetail();
- } catch (err: unknown) {
- alert(err instanceof Error ? err.message : String(err));
- } finally {
- setUpdating(false);
- }
- };
-
- if (loading) {
- return (
-
- );
- }
-
- return (
-
- {/* Header */}
-
-
- {/* Main */}
-
- {error && (
-
-
{error}
-
- 提示:需要在后端补充 GET /api/v1/project-management/tasks/{'{task_id}'} 接口
-
-
- )}
-
- {task && (
-
- {/* 任务基本信息 */}
-
-
-
{task.name}
-
-
-
- {showEditForm ? (
-
{
- setShowEditForm(false);
- fetchTaskDetail();
- }}
- onCancel={() => setShowEditForm(false)}
- />
- ) : (
- <>
-
- {task.description || '暂无描述'}
-
-
-
-
- 状态
-
-
-
-
优先级
-
{task.priority}
-
-
-
进度
-
- updateProgress(parseFloat(e.target.value))}
- disabled={updating}
- style={{ flex: 1, cursor: updating ? 'not-allowed' : 'pointer' }}
- />
- {task.progress}%
-
-
-
- >
- )}
-
-
- {/* 问题卡点列表 */}
-
-
-
问题卡点 ({issues.length})
-
-
-
- {showIssueForm && (
-
- {
- setShowIssueForm(false);
- fetchTaskIssues();
- }}
- onCancel={() => setShowIssueForm(false)}
- />
-
- )}
-
- {issues.length === 0 ? (
-
暂无问题
- ) : (
-
- {issues.map(issue => (
-
-
{issue.resolved ? '🟢' : '🔴'}
-
-
{issue.title}
- {issue.description && (
-
- {issue.description}
-
- )}
-
- {!issue.resolved && (
-
- )}
-
- {issue.resolved ? '已解决' : '未解决'}
-
-
- ))}
-
- )}
-
-
- )}
-
-
- );
-}
diff --git a/apps/web/package.json b/apps/web/package.json
index 89b3a4acd..8fe1514e2 100755
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -23,10 +23,7 @@
"axios": "^1.7.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
- "react-hook-form": "^7.52.0",
"react-router-dom": "^6.24.0",
- "recharts": "^3.8.1",
- "zod": "^3.23.8",
"zustand": "^4.5.2"
},
"devDependencies": {
diff --git a/apps/web/src/pages/editing-planner/components/GenerateModal.tsx b/apps/web/src/pages/editing-planner/components/GenerateModal.tsx
deleted file mode 100644
index dccd1f7c4..000000000
--- a/apps/web/src/pages/editing-planner/components/GenerateModal.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * 使用模板生成视频弹窗 — V21 设计系统
- * P1-4: voiceover_id → voiceover_duration (number)
- */
-import React from "react";
-import { Modal, Input } from "@/components/ui";
-
-interface GenerateModalProps {
- open: boolean;
- loading: boolean;
- voiceoverDuration: number | null;
- estimatedDuration: number;
- onDurationChange: (v: number | null) => void;
- onGenerate: () => void;
- onCancel: () => void;
-}
-
-const GenerateModal: React.FC = ({
- open,
- loading,
- voiceoverDuration,
- estimatedDuration,
- onDurationChange,
- onGenerate,
- onCancel,
-}) => {
- return (
-
-
-
- {
- const v = e.target.value ? Number(e.target.value) : null;
- onDurationChange(v);
- }}
- min={1}
- max={600}
- />
-
-
-
- ⏱️ 预估时长:~{estimatedDuration}s,配音时长偏差超过 ±30% 时将收到警告
-
-
- );
-};
-
-export default GenerateModal;
diff --git a/apps/web/src/pages/editing-planner/components/SettingsPanel.tsx b/apps/web/src/pages/editing-planner/components/SettingsPanel.tsx
deleted file mode 100644
index 63d185e0c..000000000
--- a/apps/web/src/pages/editing-planner/components/SettingsPanel.tsx
+++ /dev/null
@@ -1,329 +0,0 @@
-/**
- * 右侧设置面板 — V21 设计系统
- * 标题设置 / 字幕设置 / BGM 设置
- */
-import React from "react";
-import { Input, Select, Tag } from "@/components/ui";
-import type {
- TitleConfig,
- SubtitleConfig,
- BgmConfig,
-} from "@/api/editingPlanner";
-
-/* ── 常量 ── */
-const FONT_PRESETS = ["思源黑体", "站酷快乐体", "方正兰亭", "汉仪旗黑"];
-const POSITIONS = [
- { value: "top", label: "顶部" },
- { value: "center", label: "居中" },
- { value: "bottom", label: "底部" },
-];
-const SUBTITLE_FONTS = ["思源黑体", "微软雅黑", "苹方"];
-const SUBTITLE_ANIMATIONS = [
- { value: "none", label: "无" },
- { value: "fade", label: "淡入" },
- { value: "typewriter", label: "打字机" },
- { value: "slide", label: "滑动" },
-];
-
-interface SettingsPanelProps {
- titleConfig: TitleConfig;
- subtitleConfig: SubtitleConfig;
- bgmConfig: BgmConfig;
- onTitleChange: (config: TitleConfig) => void;
- onSubtitleChange: (config: SubtitleConfig) => void;
- onBgmChange: (config: BgmConfig) => void;
-}
-
-const SettingsPanel: React.FC = ({
- titleConfig,
- subtitleConfig,
- bgmConfig,
- onTitleChange,
- onSubtitleChange,
- onBgmChange,
-}) => {
- return (
-
- {/* ── 标题设置 ── */}
-
-
🔤 标题设置
-
- {/* AI 自动选择开关 */}
-
-
- AI 自动选择
-
-
-
-
- {/* 手动输入标题 */}
- {!titleConfig.ai_auto_select && (
-
-
- onTitleChange({ ...titleConfig, content: e.target.value })
- }
- rows={2}
- />
-
- )}
-
- {/* 字体预设 */}
-
-
- 字体预设
-
-
- {FONT_PRESETS.map((font) => (
-
- onTitleChange({ ...titleConfig, font_preset: font })
- }
- >
- {font}
-
- ))}
-
-
-
- {/* 颜色 + 位置 */}
-
-
-
-
- 颜色
-
-
-
-
- onTitleChange({
- ...titleConfig,
- font_color: e.target.value,
- })
- }
- />
-
-
-
-
- 位置
-
-
-
-
-
- {/* 字号 */}
-
-
-
- 字号:{titleConfig.font_size}
-
-
-
- onTitleChange({
- ...titleConfig,
- font_size: Number(e.target.value),
- })
- }
- style={{ width: "100%" }}
- />
-
-
-
- {/* ── 字幕设置 ── */}
-
-
📝 字幕设置
-
- {/* 启用开关 */}
-
-
- 启用字幕
-
-
-
-
- {subtitleConfig.enabled && (
- <>
- {/* 位置 */}
-
-
- 位置
-
-
-
- {/* 字体 */}
-
-
- 字体
-
-
-
- {/* 颜色 + 动画 */}
-
-
-
-
- 颜色
-
-
-
-
- onSubtitleChange({
- ...subtitleConfig,
- color: e.target.value,
- })
- }
- />
-
-
-
-
- 动画
-
-
-
-
-
- {/* 字号 */}
-
-
-
- 字号:{subtitleConfig.size}
-
-
-
- onSubtitleChange({
- ...subtitleConfig,
- size: Number(e.target.value),
- })
- }
- style={{ width: "100%" }}
- />
-
- >
- )}
-
-
- {/* ── BGM 设置 ── */}
-
-
🎵 BGM 设置
-
- {/* 启用开关 */}
-
-
- 启用背景音乐
-
-
-
-
- {bgmConfig.enabled && (
-
-
- 选择音乐
-
-
- )}
-
-
- );
-};
-
-export default SettingsPanel;
diff --git a/apps/web/src/pages/editing-planner/components/TemplatePanel.tsx b/apps/web/src/pages/editing-planner/components/TemplatePanel.tsx
deleted file mode 100644
index 6a7e670a3..000000000
--- a/apps/web/src/pages/editing-planner/components/TemplatePanel.tsx
+++ /dev/null
@@ -1,132 +0,0 @@
-/**
- * 左侧模板面板 — V21 设计系统
- * 搜索、分类筛选、模板卡片列表
- */
-import React from "react";
-import { Input, Select, Button, Tag } from "@/components/ui";
-import {
- MODE_LABELS,
- MODE_COLORS,
- type EditingTemplate,
- type TemplateCategory,
- type TemplateMode,
-} from "@/api/editingPlanner";
-
-/** antd Tag color → V21 Tag variant */
-const modeVariantMap: Record<
- string,
- "primary" | "success" | "warning" | "info"
-> = {
- blue: "primary",
- green: "success",
- orange: "warning",
- purple: "info",
-};
-
-interface TemplatePanelProps {
- templates: EditingTemplate[];
- categories: TemplateCategory[];
- isLoading: boolean;
- searchText: string;
- filterCategory: string;
- loadedTemplateId: string | null;
- onSearchChange: (v: string) => void;
- onCategoryChange: (v: string) => void;
- onTemplateSelect: (tpl: EditingTemplate) => void;
- onNewTemplate: () => void;
-}
-
-const TemplatePanel: React.FC = ({
- templates,
- categories,
- isLoading,
- searchText,
- filterCategory,
- loadedTemplateId,
- onSearchChange,
- onCategoryChange,
- onTemplateSelect,
- onNewTemplate,
-}) => {
- return (
-
- {/* 头部:搜索 + 筛选 */}
-
-
📂 我的模板
- onSearchChange(e.target.value)}
- allowClear
- />
-
-
- {/* 模板卡片列表 */}
-
- {isLoading ? (
-
- ) : templates.length === 0 ? (
-
-
📭
-
暂无已保存的模板
-
请先编辑并保存模板
-
- ) : (
- templates.map((tpl) => {
- const modeColor = MODE_COLORS[tpl.mode as TemplateMode] || "blue";
- const variant = modeVariantMap[modeColor] || "primary";
-
- return (
-
onTemplateSelect(tpl)}
- >
-
{tpl.name}
-
-
- {MODE_LABELS[tpl.mode as TemplateMode] || tpl.mode}
-
- {tpl.tags.slice(0, 3).map((tag) => (
-
- {tag}
-
- ))}
-
-
- {tpl.segments.length} 片段 · ~{tpl.estimated_duration}s
-
-
- );
- })
- )}
-
-
- {/* 底部:新建空白模板 */}
- {loadedTemplateId && (
-
-
-
- )}
-
- );
-};
-
-export default TemplatePanel;