From 184ab8bce397c72e4070b3d0a93c05ca0df9559e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E9=B1=BC=E5=BC=80=E5=8F=91?= Date: Sun, 24 May 2026 20:22:57 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20BGM=20=E5=BC=B9=E7=AA=97=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E5=88=86=E7=B1=BB+=E5=88=97=E8=A1=A8+=E8=AF=95?= =?UTF-8?q?=E5=90=AC=E4=BA=A4=E4=BA=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 顶部分类标签栏(全部/知识科普/案例展示/促销活动/家居生活/智能家居) - 列表布局:每项含试听按钮、标题、时长、选中标记 - 试听功能:点击圆形按钮播放/暂停,播放中按钮呼吸动画 - 选中 BGM 后关闭弹窗,本地上传入口移至底部 - 弹窗关闭时自动停止音频播放 --- .../src/pages/VideoCreation/VideoCompose.tsx | 137 ++++++++++--- .../pages/VideoCreation/VideoGeneration.css | 181 ++++++++++++++---- 2 files changed, 246 insertions(+), 72 deletions(-) diff --git a/tauri-app/src/pages/VideoCreation/VideoCompose.tsx b/tauri-app/src/pages/VideoCreation/VideoCompose.tsx index 180cb00..4394db3 100644 --- a/tauri-app/src/pages/VideoCreation/VideoCompose.tsx +++ b/tauri-app/src/pages/VideoCreation/VideoCompose.tsx @@ -48,6 +48,9 @@ export default function VideoCompose() { // BGM const [bgmList, setBgmList] = useState([]); + const [bgmCategory, setBgmCategory] = useState('all'); + const [previewId, setPreviewId] = useState(null); + const audioRef = useRef(null); const bgmMusicId = useProjectStore(state => state.bgmMusicId); const bgmMusicPath = useProjectStore(state => state.bgmMusicPath); const bgmMusicTitle = useProjectStore(state => state.bgmMusicTitle); @@ -58,6 +61,34 @@ export default function VideoCompose() { const [bgmModalOpen, setBgmModalOpen] = useState(false); const localBgmRef = useRef(null); + // 弹窗关闭时停止试听 + useEffect(() => { + if (!bgmModalOpen) { + audioRef.current?.pause(); + audioRef.current = null; + setPreviewId(null); + } + }, [bgmModalOpen]); + + const handlePreview = (item: BgmMusicItem) => { + if (previewId === item.id) { + audioRef.current?.pause(); + audioRef.current = null; + setPreviewId(null); + return; + } + audioRef.current?.pause(); + const audio = new Audio(item.url || ''); + audio.play().catch(() => {}); + audio.onended = () => setPreviewId(null); + audioRef.current = audio; + setPreviewId(item.id); + }; + + const filteredBgmList = bgmCategory === 'all' + ? bgmList + : bgmList.filter(item => item.category === bgmCategory); + // 积分不足弹窗 const [showPointsModal, setShowPointsModal] = useState(false); const showRechargeModal = usePointStore((state) => state.showRechargeModal); @@ -598,21 +629,90 @@ export default function VideoCompose() { open={bgmModalOpen} onClose={() => setBgmModalOpen(false)} title="选择背景音乐" - width="440px" + width="480px" > -
-
- {/* 本地上传 */} +
+ {/* 分类标签 */} +
+ {Object.entries(CATEGORY_LABELS).map(([key, label]) => ( + + ))} +
+ + {/* BGM 列表 */} +
+ {filteredBgmList.map((item) => ( +
+ +
{ + setBgmMusic(item.id, item.title, item.url || item.filePath); + setBgmModalOpen(false); + }} + > +
{item.title}
+
+ {CATEGORY_LABELS[item.category] || item.category} + {item.duration ? ` · ${Math.floor(item.duration / 60)}:${String(Math.floor(item.duration % 60)).padStart(2, '0')}` : ''} +
+
+ {bgmMusicId === item.id && ( +
+ + + +
+ )} +
+ ))} + {filteredBgmList.length === 0 && ( +
该分类暂无背景音乐
+ )} +
+ + {/* 底部操作 */} +
+ - {/* 系统 BGM */} - {bgmList.map((item) => ( - - ))} -
-
diff --git a/tauri-app/src/pages/VideoCreation/VideoGeneration.css b/tauri-app/src/pages/VideoCreation/VideoGeneration.css index 09a1bc8..24a94ad 100644 --- a/tauri-app/src/pages/VideoCreation/VideoGeneration.css +++ b/tauri-app/src/pages/VideoCreation/VideoGeneration.css @@ -424,74 +424,171 @@ BGM 选择弹窗 ============================================ */ -.modal-bgm-grid { - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: var(--spacing-sm); - max-height: 320px; - overflow-y: auto; +.modal-bgm-tabs { + display: flex; + gap: 6px; + overflow-x: auto; + padding-bottom: 4px; + scrollbar-width: none; } -.modal-bgm-card { - position: relative; +.modal-bgm-tabs::-webkit-scrollbar { + display: none; +} + +.modal-bgm-tab { + flex-shrink: 0; + padding: 5px 12px; + border-radius: var(--radius-full); + border: 1px solid transparent; + background: var(--bg-secondary); + color: var(--text-secondary); + font-size: var(--font-xs); + font-weight: 500; + cursor: pointer; + transition: all var(--transition-fast); + white-space: nowrap; +} + +.modal-bgm-tab:hover { + background: var(--bg-hover); + color: var(--text-primary); +} + +.modal-bgm-tab.active { + background: var(--primary-light); + color: var(--primary); + border-color: var(--primary); +} + +.modal-bgm-list { display: flex; flex-direction: column; - align-items: center; - justify-content: center; gap: 6px; - padding: var(--spacing-md); - border: 2px solid transparent; + max-height: 320px; + overflow-y: auto; + padding-right: 4px; +} + +.modal-bgm-row { + display: flex; + align-items: center; + gap: 10px; + padding: 10px 12px; border-radius: var(--radius-md); + border: 1.5px solid transparent; background: var(--bg-secondary); cursor: pointer; - aspect-ratio: 1; transition: all var(--transition-fast); } -.modal-bgm-card:hover { - border-color: var(--border-color); - transform: scale(1.02); +.modal-bgm-row:hover { + background: var(--bg-hover); } -.modal-bgm-card.active { +.modal-bgm-row.active { border-color: var(--primary); - box-shadow: 0 0 0 3px var(--primary-light); -} - -.modal-bgm-upload { - color: var(--text-tertiary); -} - -.modal-bgm-upload:hover { - color: var(--primary); - border-color: var(--primary); -} - -.modal-bgm-icon { - width: 40px; - height: 40px; - border-radius: 50%; background: var(--primary-light); +} + +.modal-bgm-preview-btn { + width: 32px; + height: 32px; + border-radius: 50%; + border: none; + background: var(--bg-primary); + color: var(--text-secondary); display: flex; align-items: center; justify-content: center; - color: var(--primary); + cursor: pointer; + flex-shrink: 0; + transition: all var(--transition-fast); } -.modal-bgm-title { - font-size: var(--font-xs); +.modal-bgm-preview-btn:hover { + background: var(--primary); + color: #fff; +} + +.modal-bgm-preview-btn.playing { + background: var(--primary); + color: #fff; + animation: bgm-pulse 1.5s ease-in-out infinite; +} + +@keyframes bgm-pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.6; } +} + +.modal-bgm-info { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + gap: 2px; +} + +.modal-bgm-row-title { + font-size: var(--font-sm); font-weight: 500; color: var(--text-primary); - text-align: center; - line-height: 1.3; - max-width: 100%; + line-height: 1.4; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } -.modal-bgm-category { - font-size: 10px; +.modal-bgm-row-meta { + font-size: 11px; color: var(--text-tertiary); - text-align: center; + line-height: 1.3; +} + +.modal-bgm-check { + width: 20px; + height: 20px; + border-radius: 50%; + background: var(--primary); + color: #fff; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.modal-bgm-empty { + text-align: center; + padding: 32px; + color: var(--text-tertiary); + font-size: var(--font-sm); +} + +.modal-bgm-footer { + display: flex; + align-items: center; + justify-content: space-between; + padding-top: 8px; + border-top: 1px solid var(--border-color); +} + +.modal-bgm-upload-btn { + display: flex; + align-items: center; + gap: 6px; + padding: 6px 12px; + border-radius: var(--radius-md); + border: 1px dashed var(--border-color); + background: transparent; + color: var(--text-tertiary); + font-size: var(--font-xs); + cursor: pointer; + transition: all var(--transition-fast); +} + +.modal-bgm-upload-btn:hover { + border-color: var(--primary); + color: var(--primary); + background: var(--primary-light); }