From 028ebc748465b113845110539e5a9b5f5e1cbbec Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Sun, 30 Aug 2026 15:46:36 +0800 Subject: [PATCH] =?UTF-8?q?feat(generate):=20=E8=87=AA=E5=8A=A8=E5=8C=B9?= =?UTF-8?q?=E9=85=8D=E6=A8=A1=E5=BC=8F=E4=B8=8D=E5=86=8D=E5=B1=95=E7=A4=BA?= =?UTF-8?q?AI=E5=B7=B2=E9=80=89=E7=B4=A0=E6=9D=90=E6=98=8E=E7=BB=86?= =?UTF-8?q?=EF=BC=8C=E5=8C=B9=E9=85=8D/=E6=8F=90=E4=BA=A4=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E4=B8=8D=E5=8F=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SmartMatchResults 删除「AI 已选素材(N个)」标题栏(全选/清空)、 素材卡片网格、已选汇总条;匹配成功改为一行轻量提示 「✅ AI 已帮你选好素材,可直接进入下一步」,保留 loading/空态 - 删除仅服务于结果展示的 handleToggleSmartSelect/handleSelectAllMatched/ handleClearSmartSelect/smartMatchedResults/smartSelectedTotalDuration - 保留 smartSelectedIds 自动全量选中、换一批/重新匹配、草稿保存与 clips 提交链路,生成提交的素材 id 完全不变 - 删除无引用的 SmartMatchCard.tsx 及相关 CSS(xx-link-btn 因 AiTitleGenerator 仍在使用予以保留) - 手动选择模式不受影响 --- .../components/Step2MaterialSelect.tsx | 12 +- .../components/material/SmartMatchCard.tsx | 52 ------ .../components/material/SmartMatchResults.tsx | 92 ++-------- apps/web/src/pages/generate/generate.css | 162 +----------------- .../hooks/step2-materials/useSmartMatch.ts | 51 +----- .../pages/generate/hooks/useStep2Materials.ts | 12 +- .../src/test/pages/generate/smoke.test.tsx | 1 - 7 files changed, 29 insertions(+), 353 deletions(-) delete mode 100644 apps/web/src/pages/generate/components/material/SmartMatchCard.tsx diff --git a/apps/web/src/pages/generate/components/Step2MaterialSelect.tsx b/apps/web/src/pages/generate/components/Step2MaterialSelect.tsx index 6e99582d8..b0c655435 100644 --- a/apps/web/src/pages/generate/components/Step2MaterialSelect.tsx +++ b/apps/web/src/pages/generate/components/Step2MaterialSelect.tsx @@ -82,17 +82,7 @@ const Step2MaterialSelect: React.FC = (props) => { loading={m.materialsLoading} /> - + )} diff --git a/apps/web/src/pages/generate/components/material/SmartMatchCard.tsx b/apps/web/src/pages/generate/components/material/SmartMatchCard.tsx deleted file mode 100644 index b52346bab..000000000 --- a/apps/web/src/pages/generate/components/material/SmartMatchCard.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/** - * 智能匹配卡片(Q5 简化版) - * 只展示素材缩略图、名称、时长,无匹配分数 - */ -import React from "react" -import { PlayCircleOutlined, CheckCircleFilled } from "@ant-design/icons" -import type { AssetItem } from "@/api/assets" - -interface SmartMatchCardProps { - asset: AssetItem - selected: boolean - onClick: () => void - formatDuration: (seconds: number) => string -} - -const SmartMatchCard: React.FC = ({ - asset, - selected, - onClick, - formatDuration, -}) => { - return ( -
- {/* 缩略图 */} -
- {asset.thumbnail_url ? ( - {asset.name} - ) : ( -
- -
- )} - {selected && ( -
- -
- )} - {asset.duration && ( -
{formatDuration(asset.duration)}
- )} -
- {/* 名称 */} -
-
- {asset.name} -
-
-
- ) -} - -export default SmartMatchCard diff --git a/apps/web/src/pages/generate/components/material/SmartMatchResults.tsx b/apps/web/src/pages/generate/components/material/SmartMatchResults.tsx index 67d6c55b7..e5752440a 100644 --- a/apps/web/src/pages/generate/components/material/SmartMatchResults.tsx +++ b/apps/web/src/pages/generate/components/material/SmartMatchResults.tsx @@ -1,35 +1,17 @@ /** - * 智能匹配结果区(Q5 简化版) - * 直接展示 AI 选中的素材,无匹配分数和理由 + * 智能匹配状态区 + * 仅展示匹配中 / 未匹配 / 匹配成功三种状态,不展示 AI 选中的素材明细 + * (选中的素材仍由 smartSelectedIds 驱动提交,逻辑不变) */ import React from "react" import { LoadingOutlined } from "@ant-design/icons" -import type { AssetItem } from "@/api/assets" -import SmartMatchCard from "./SmartMatchCard" interface SmartMatchResultsProps { - matchedAssets: AssetItem[] - selectedIds: string[] matching: boolean hasMatched: boolean - onToggle: (assetId: string) => void - onSelectAll: () => void - onClear: () => void - formatDuration: (seconds: number) => string - selectedTotalDuration: number } -const SmartMatchResults: React.FC = ({ - matchedAssets, - selectedIds, - matching, - hasMatched, - onToggle, - onSelectAll, - onClear, - formatDuration, - selectedTotalDuration, -}) => { +const SmartMatchResults: React.FC = ({ matching, hasMatched }) => { // 匹配中状态 if (matching) { return ( @@ -45,66 +27,26 @@ const SmartMatchResults: React.FC = ({ ) } - // 未匹配状态提示 - if (!hasMatched) { + // 匹配成功:轻量提示,不展示素材明细卡片 + if (hasMatched) { return ( -
-
💡
-
- 点击「让 AI 帮你选」,自动从视频库中选择最合适的素材 -
+
+ + + AI 已帮你选好素材,可直接进入下一步 +
) } - // 无结果 - if (matchedAssets.length === 0) return null - + // 未匹配状态提示 return ( - <> -
-
- - AI 已选素材 ({matchedAssets.length}个) - -
- - | - -
-
-
- {matchedAssets.map((asset) => { - const isSelected = selectedIds.includes(asset.id) - return ( - onToggle(asset.id)} - formatDuration={formatDuration} - /> - ) - })} -
+
+
💡
+
+ 点击「让 AI 帮你选」,自动从视频库中选择最合适的素材
- - {/* 已选素材汇总 */} - {selectedIds.length > 0 && ( -
-
- 已选 {selectedIds.length} 个素材 - - 预计总时长约 {selectedTotalDuration.toFixed(0)} 秒 - -
-
- )} - +
) } diff --git a/apps/web/src/pages/generate/generate.css b/apps/web/src/pages/generate/generate.css index d3262b37e..eb6444645 100644 --- a/apps/web/src/pages/generate/generate.css +++ b/apps/web/src/pages/generate/generate.css @@ -1409,154 +1409,15 @@ color: var(--text-tertiary, #94a3b8); } -.xx-smart-match-results { - display: flex; - flex-direction: column; - gap: 12px; -} - -.xx-smart-match-results-header { - display: flex; - justify-content: space-between; - align-items: center; -} - -.xx-smart-match-results-title { - font-size: 14px; - font-weight: 600; - color: var(--text-primary, #1e293b); -} - -.xx-smart-match-results-actions { +.xx-smart-match-success { display: flex; align-items: center; + justify-content: center; gap: 8px; - font-size: 12px; -} - -.xx-link-btn { - background: none; - border: none; - color: var(--primary-color, #4f46e5); - font-size: 12px; - cursor: pointer; - padding: 2px 4px; -} - -.xx-link-btn:hover { - text-decoration: underline; -} - -.xx-smart-match-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 10px; - max-height: 420px; - overflow-y: auto; - padding-right: 4px; -} - -.xx-smart-match-card { - background: #fff; - border: 2px solid var(--border-primary, #e2e8f0); + padding: 18px 20px; + background: #f0fdf4; + border: 1px solid #bbf7d0; border-radius: 12px; - overflow: hidden; - cursor: pointer; - transition: all 0.2s ease; -} - -.xx-smart-match-card:hover { - border-color: var(--primary-color, #4f46e5); - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); -} - -.xx-smart-match-card.selected { - border-color: var(--primary-color, #4f46e5); - background: var(--primary-soft, #eef2ff); -} - -.xx-smart-match-thumb { - position: relative; - width: 100%; - aspect-ratio: 9 / 16; - background: #f1f5f9; - overflow: hidden; -} - -.xx-smart-match-thumb img { - width: 100%; - height: 100%; - object-fit: cover; -} - -.xx-smart-match-thumb-placeholder { - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - color: var(--text-tertiary, #94a3b8); -} - -.xx-smart-match-score { - position: absolute; - top: 8px; - left: 8px; - padding: 2px 8px; - font-size: 11px; - font-weight: 600; - color: #fff; - background: linear-gradient(135deg, #4f46e5, #7c3aed); - border-radius: 12px; -} - -.xx-smart-match-check { - position: absolute; - top: 8px; - right: 8px; - width: 24px; - height: 24px; - background: var(--primary-color, #4f46e5); - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; -} - -.xx-smart-match-duration { - position: absolute; - bottom: 8px; - right: 8px; - padding: 2px 6px; - font-size: 11px; - color: #fff; - background: rgba(0, 0, 0, 0.6); - border-radius: 4px; -} - -.xx-smart-match-info { - padding: 10px 12px; -} - -.xx-smart-match-name { - font-size: 13px; - font-weight: 500; - color: var(--text-primary, #1e293b); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - margin-bottom: 4px; -} - -.xx-smart-match-reason { - font-size: 11px; - color: var(--text-secondary, #64748b); - line-height: 1.4; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; } .xx-smart-match-loading { @@ -1580,19 +1441,6 @@ text-align: center; } -.xx-smart-match-summary { - padding: 12px 16px; - background: #f0fdf4; - border: 1px solid #bbf7d0; - border-radius: 12px; -} - -.xx-smart-match-summary-header { - display: flex; - justify-content: space-between; - align-items: center; -} - /* ============================================================ AI 智能生成标题 ============================================================ */ diff --git a/apps/web/src/pages/generate/hooks/step2-materials/useSmartMatch.ts b/apps/web/src/pages/generate/hooks/step2-materials/useSmartMatch.ts index 4a4d265ce..997b4f61d 100755 --- a/apps/web/src/pages/generate/hooks/step2-materials/useSmartMatch.ts +++ b/apps/web/src/pages/generate/hooks/step2-materials/useSmartMatch.ts @@ -1,4 +1,4 @@ -import { useState, useCallback, useMemo } from "react" +import { useState, useCallback } from "react" import { message } from "antd" import type { AssetItem } from "@/api/assets" import { smartMatchAssets, isAssetUsable } from "@/api/assets" @@ -6,24 +6,21 @@ import { smartMatchAssets, isAssetUsable } from "@/api/assets" interface UseSmartMatchOptions { libraryId: string materials: { items: AssetItem[]; total: number } - smartSelectedIds: string[] onSmartSelectedIdsChange: (ids: string[]) => void } /** - * 智能素材匹配 Hook(Q5 简化版) - * 用户不手动选素材时,一键调用后端 AI 选素材 - * 后端统一选素材逻辑后续完善,当前先走前端流程简化 + * 智能素材匹配 Hook + * 用户不手动选素材时,一键调用后端 AI 选素材;匹配结果自动全量写入 + * smartSelectedIds(由上层持有),不向用户展示素材明细。 */ export function useSmartMatch({ libraryId, materials, - smartSelectedIds, onSmartSelectedIdsChange, }: UseSmartMatchOptions) { const [smartMatching, setSmartMatching] = useState(false) const [hasMatched, setHasMatched] = useState(false) - const [smartMatchedResults, setSmartMatchedResults] = useState([]) /* ── 一键智能匹配 ── */ const handleSmartMatch = useCallback(async () => { @@ -52,21 +49,17 @@ export function useSmartMatch({ if (matchedIds.length > 0) { onSmartSelectedIdsChange(matchedIds) - // 保存 API 返回的完整素材列表 - setSmartMatchedResults(matched) setHasMatched(true) message.success(`AI 已为你选择 ${matchedIds.length} 个素材`) } else { // 后端返回空结果,回退到全选可用素材 onSmartSelectedIdsChange(usableItems.map((a) => a.id)) - setSmartMatchedResults(usableItems) setHasMatched(true) message.info("AI 暂未找到匹配素材,已全选当前库素材") } } catch { // 后端 API 尚未就绪时,回退到全选当前库可用素材 onSmartSelectedIdsChange(usableItems.map((a) => a.id)) - setSmartMatchedResults(usableItems) setHasMatched(true) message.info("已为你全选当前库素材(智能匹配功能即将上线)") } finally { @@ -74,49 +67,15 @@ export function useSmartMatch({ } }, [libraryId, materials.items, onSmartSelectedIdsChange]) + /* ── 换一批 = 重新触发智能匹配 ── */ const handleRefreshMatch = useCallback(async () => { - // 换一批 = 重新触发智能匹配 await handleSmartMatch() }, [handleSmartMatch]) - const handleSelectAllMatched = useCallback(() => { - onSmartSelectedIdsChange(materials.items.filter(isAssetUsable).map((a) => a.id)) - }, [materials.items, onSmartSelectedIdsChange]) - - const handleClearSmartSelect = useCallback(() => { - onSmartSelectedIdsChange([]) - }, [onSmartSelectedIdsChange]) - - const handleToggleSmartSelect = useCallback( - (assetId: string) => { - onSmartSelectedIdsChange( - smartSelectedIds.includes(assetId) - ? smartSelectedIds.filter((id) => id !== assetId) - : [...smartSelectedIds, assetId], - ) - }, - [smartSelectedIds, onSmartSelectedIdsChange], - ) - - /* ── 计算已选素材总时长 ── */ - const smartSelectedTotalDuration = useMemo( - () => - materials.items - .filter((a) => isAssetUsable(a) && smartSelectedIds.includes(a.id)) - .reduce((sum, a) => sum + (a.duration || 0), 0), - [materials.items, smartSelectedIds], - ) - return { smartMatching, hasMatched, - smartSelectedIds, - smartMatchedResults, handleSmartMatch, - handleToggleSmartSelect, handleRefreshMatch, - handleSelectAllMatched, - handleClearSmartSelect, - smartSelectedTotalDuration, } } diff --git a/apps/web/src/pages/generate/hooks/useStep2Materials.ts b/apps/web/src/pages/generate/hooks/useStep2Materials.ts index 2aeb62e11..11ab59be5 100644 --- a/apps/web/src/pages/generate/hooks/useStep2Materials.ts +++ b/apps/web/src/pages/generate/hooks/useStep2Materials.ts @@ -7,7 +7,6 @@ import { message } from "antd" import type { TemplateSegment } from "@/api/templates/types" import type { EditPlanClip } from "@/api/template-editor" import { updateEditPlanClips, createClipsFromAssets, getEditPlanClips } from "@/api/template-editor" -import { formatDuration } from "../utils/formatDuration" import { useMaterialLibrary } from "./step2-materials/useMaterialLibrary" import { useSmartMatch } from "./step2-materials/useSmartMatch" import { useDraftAutoSave } from "./useDraftAutoSave" @@ -51,7 +50,6 @@ export function useStep2Materials({ const smartMatch = useSmartMatch({ libraryId: selectedLibraryId, materials: selectableMaterials, - smartSelectedIds, onSmartSelectedIdsChange, }) @@ -191,19 +189,11 @@ export function useStep2Materials({ // 手动选择 selectedMaterials, handleToggleMaterial, - // 智能匹配(简化版) + // 智能匹配 smartMatching: smartMatch.smartMatching, hasMatched: smartMatch.hasMatched, - smartSelectedIds: smartMatch.smartSelectedIds, - smartMatchedResults: smartMatch.smartMatchedResults, handleSmartMatch: smartMatch.handleSmartMatch, - handleToggleSmartSelect: smartMatch.handleToggleSmartSelect, handleRefreshMatch: smartMatch.handleRefreshMatch, - handleSelectAllMatched: smartMatch.handleSelectAllMatched, - handleClearSmartSelect: smartMatch.handleClearSmartSelect, - smartSelectedTotalDuration: smartMatch.smartSelectedTotalDuration, - // utils - formatDuration, } } diff --git a/apps/web/src/test/pages/generate/smoke.test.tsx b/apps/web/src/test/pages/generate/smoke.test.tsx index ef10ff4d5..475514f67 100755 --- a/apps/web/src/test/pages/generate/smoke.test.tsx +++ b/apps/web/src/test/pages/generate/smoke.test.tsx @@ -32,7 +32,6 @@ import "@/pages/generate/components/material/MaterialModeTabs" import "@/pages/generate/components/material/ManualMaterialList" import "@/pages/generate/components/material/SmartMatchInput" import "@/pages/generate/components/material/SmartMatchResults" -import "@/pages/generate/components/material/SmartMatchCard" import "@/pages/generate/components/title/AiTitleGenerator" import "@/pages/generate/components/title/AiTitleCard" import "@/pages/generate/components/title/TitleStylePanel" -- 2.54.0