From 5bcf6bcb11d32a61536b602c0746cfa37ac3947d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=81=B5=E5=BA=94?= Date: Fri, 3 Jul 2026 17:44:26 +0800 Subject: [PATCH 01/53] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8DAssetLibrary?= =?UTF-8?q?=E5=92=8CTemplateLibrary=E7=B1=BB=E5=9E=8B=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/pages/assets/AssetLibrary.tsx | 40 ++++++------------- .../src/pages/templates/TemplateLibrary.tsx | 1 - 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/apps/web/src/pages/assets/AssetLibrary.tsx b/apps/web/src/pages/assets/AssetLibrary.tsx index ed05ac3ee..67e0c8a69 100644 --- a/apps/web/src/pages/assets/AssetLibrary.tsx +++ b/apps/web/src/pages/assets/AssetLibrary.tsx @@ -93,24 +93,25 @@ const formatDuration = (seconds: number): string => { const mapLibrary = (item: AssetLibraryItem): LibraryItem => ({ id: item.id, name: item.name, - kind: inferKind(item.default_mime_type || "video"), + kind: item.kind || inferKind("video"), count: item.asset_count ?? 0, }); /** 将后端 ApiAssetItem 映射为前端 AssetItem */ const mapAsset = (item: ApiAssetItem): AssetItem => { const { status, label } = inferStatus( - item.quality_score, - item.classification_status, + item.quality_score ?? undefined, + item.classification_status ?? undefined, ); + const metadata = item.metadata || {}; return { id: item.id, name: item.name, kind: inferKind(item.mime_type || ""), - thumbUrl: item.thumbnail_url, + thumbUrl: metadata.thumbnail_url as string | undefined, status, statusLabel: label, - duration: item.duration != null ? formatDuration(item.duration) : undefined, + duration: metadata.duration != null ? formatDuration(metadata.duration as number) : undefined, size: item.file_size ? +(item.file_size / (1024 * 1024)).toFixed(1) : 0, createdAt: item.created_at ? new Date(item.created_at).toISOString().slice(0, 10) @@ -268,7 +269,7 @@ const AssetLibrary: React.FC = () => { refetch: refetchAssets, } = useQuery({ queryKey: ["assets", effectiveLibId], - queryFn: () => getAssets(effectiveLibId || undefined), + queryFn: () => getAssets(effectiveLibId), enabled: !!effectiveLibId, staleTime: 30_000, }); @@ -298,17 +299,6 @@ const AssetLibrary: React.FC = () => { }, }); - const deleteAssetMutation = useMutation({ - mutationFn: deleteAsset, - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ["assets"] }); - queryClient.invalidateQueries({ queryKey: ["asset-libraries"] }); - }, - onError: () => { - message.error("删除素材失败"); - }, - }); - /* 状态 */ const [selectedIds, setSelectedIds] = useState>(new Set()); @@ -326,8 +316,6 @@ const AssetLibrary: React.FC = () => { const [newLibKind, setNewLibKind] = useState("video"); /* 派生数据 */ - const activeLibrary = libraries.find((l) => l.id === effectiveLibId); - const filteredAssets = useMemo(() => { let list = assets; @@ -392,10 +380,8 @@ const AssetLibrary: React.FC = () => { if (file.size > LARGE_FILE_THRESHOLD) { message.info(`大文件 "${file.name}" 将使用直传上传`); } - await uploadAssetDirect(file, (p) => { - // 可选:显示上传进度 - if (p === 100) message.success(`"${file.name}" 上传成功`); - }); + await uploadAssetDirect({ file, library_id: effectiveLibId }); + message.success(`"${file.name}" 上传成功`); queryClient.invalidateQueries({ queryKey: ["assets"] }); queryClient.invalidateQueries({ queryKey: ["asset-libraries"] }); } catch { @@ -413,7 +399,7 @@ const AssetLibrary: React.FC = () => { return; } try { - const newLib = await createLibMutation.mutateAsync(newLibName.trim()); + const newLib = await createLibMutation.mutateAsync({ name: newLibName.trim(), kind: newLibKind }); setActiveLibId(newLib.id); setCreateModalOpen(false); setNewLibName(""); @@ -440,9 +426,9 @@ const AssetLibrary: React.FC = () => { /* 诊断 — 调用真实 API */ const handleDiagnose = async (asset: AssetItem) => { try { - const result = await getAssetDiagnosis(asset.id); - const score = result.quality_score ?? "-"; - message.success(`"${asset.name}" 诊断完成,质量分:${score}`); + const result = await getAssetDiagnosis(); + const score = result.readiness_score ?? "-"; + message.success(`"${asset.name}" 诊断完成,就绪分:${score}`); queryClient.invalidateQueries({ queryKey: ["assets"] }); } catch { message.error(`"${asset.name}" 诊断失败`); diff --git a/apps/web/src/pages/templates/TemplateLibrary.tsx b/apps/web/src/pages/templates/TemplateLibrary.tsx index 194629674..f17d1907e 100644 --- a/apps/web/src/pages/templates/TemplateLibrary.tsx +++ b/apps/web/src/pages/templates/TemplateLibrary.tsx @@ -13,7 +13,6 @@ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Button } from "@/components/ui"; import { getTemplates, - getTemplate, toggleFavoriteTemplate, type TemplateItem, } from "@/api/templates"; From 6f2b938bc061707d6ea61051350b8f3c2e7913dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=81=B5=E5=BA=94?= Date: Fri, 3 Jul 2026 17:56:29 +0800 Subject: [PATCH 02/53] =?UTF-8?q?refactor:=20=E5=89=8D=E7=AB=AF=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=B8=85=E7=90=86-=E7=A7=BB=E9=99=A4=E6=97=A0?= =?UTF-8?q?=E7=94=A8=E4=BB=A3=E7=A0=81=E5=92=8C=E9=81=97=E7=95=99demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 apps/web/app/ 遗留 Next.js demo 目录(9 个文件) 该目录是早期项目推进器 demo,与主 Vite 应用完全隔离,不被 src/ 引用 - 删除 3 个未被 import 的 editing-planner 组件 TemplatePanel.tsx / SettingsPanel.tsx / GenerateModal.tsx - 移除 3 个未使用的 npm 依赖 react-hook-form / recharts / zod(仅 package.json 声明,源码无引用) - TypeScript 零错误,Vite 构建正常通过 --- apps/web/app/components/CreateIssueForm.tsx | 145 -------- apps/web/app/components/CreateTaskForm.tsx | 198 ---------- apps/web/app/components/EditTaskForm.tsx | 167 --------- apps/web/app/globals.css | 26 -- apps/web/app/layout.tsx | 20 - apps/web/app/milestones/page.tsx | 232 ------------ apps/web/app/page.tsx | 62 --- apps/web/app/projects/page.tsx | 259 ------------- apps/web/app/tasks/[id]/page.tsx | 352 ------------------ apps/web/package.json | 3 - .../components/GenerateModal.tsx | 58 --- .../components/SettingsPanel.tsx | 329 ---------------- .../components/TemplatePanel.tsx | 132 ------- 13 files changed, 1983 deletions(-) delete mode 100644 apps/web/app/components/CreateIssueForm.tsx delete mode 100644 apps/web/app/components/CreateTaskForm.tsx delete mode 100644 apps/web/app/components/EditTaskForm.tsx delete mode 100644 apps/web/app/globals.css delete mode 100644 apps/web/app/layout.tsx delete mode 100644 apps/web/app/milestones/page.tsx delete mode 100644 apps/web/app/page.tsx delete mode 100644 apps/web/app/projects/page.tsx delete mode 100644 apps/web/app/tasks/[id]/page.tsx delete mode 100644 apps/web/src/pages/editing-planner/components/GenerateModal.tsx delete mode 100644 apps/web/src/pages/editing-planner/components/SettingsPanel.tsx delete mode 100644 apps/web/src/pages/editing-planner/components/TemplatePanel.tsx 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 ( -
- {error && ( -
- {error} -
- )} - -
- - setFormData({ ...formData, title: e.target.value })} - style={{ - width: '100%', - padding: '8px 12px', - border: '1px solid var(--border)', - borderRadius: '4px', - fontSize: '14px', - }} - placeholder="简要描述问题" - /> -
- -
- -