feat(P1): 素材库改名为视频库 + 配音素材库改配音库 #556
@@ -130,7 +130,7 @@ export interface SaveTtsToLibraryRequest {
|
||||
tag_ids?: string[]
|
||||
}
|
||||
|
||||
/** 将 TTS 合成结果保存到配音素材库 */
|
||||
/** 将 TTS 合成结果保存到配音库 */
|
||||
export const saveTtsToLibrary = async (
|
||||
jobId: string,
|
||||
data?: SaveTtsToLibraryRequest,
|
||||
|
||||
@@ -44,7 +44,7 @@ export interface PageHeadProps {
|
||||
const ROUTE_TITLE_MAP: Record<string, string> = {
|
||||
"/app/dashboard": "首页",
|
||||
"/app/generate": "一键生成",
|
||||
"/app/assets": "素材库",
|
||||
"/app/assets": "视频库",
|
||||
"/app/voices": "配音库",
|
||||
"/app/titles": "标题库",
|
||||
"/app/products": "成片库",
|
||||
@@ -62,7 +62,7 @@ const ROUTE_TITLE_MAP: Record<string, string> = {
|
||||
"/app/editing-planner": "剪辑规划",
|
||||
"/app/my-templates": "我的模板",
|
||||
"/app/voice-clone": "我的音色",
|
||||
"/app/voice-materials": "配音素材库",
|
||||
"/app/voice-materials": "配音库",
|
||||
"/app/accounts": "账号管理",
|
||||
"/app/duplication": "查重",
|
||||
"/app/duplication/results": "查重结果",
|
||||
|
||||
@@ -47,7 +47,7 @@ export const NAV_ITEMS: NavItem[] = [
|
||||
},
|
||||
{
|
||||
key: "assets",
|
||||
label: "素材库",
|
||||
label: "视频库",
|
||||
path: "/app/assets",
|
||||
icon: React.createElement(FileOutlined),
|
||||
},
|
||||
@@ -155,7 +155,7 @@ export const NAV_GROUPS: NavGroup[] = [
|
||||
items: [
|
||||
{
|
||||
key: "assets",
|
||||
label: "素材库",
|
||||
label: "视频库",
|
||||
path: "/app/assets",
|
||||
icon: React.createElement(FileOutlined),
|
||||
},
|
||||
|
||||
Executable → Regular
+23
-23
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* 素材库页面 — V21 设计系统
|
||||
* 两栏布局:左侧素材库列表(260px)+ 右侧素材网格
|
||||
* 视频库页面 — V21 设计系统
|
||||
* 两栏布局:左侧视频库列表(260px)+ 右侧素材网格
|
||||
* 使用 useQuery 对接后端真实 API(api/assets.ts)
|
||||
*/
|
||||
import React, { useMemo, useState } from "react"
|
||||
@@ -350,7 +350,7 @@ const AssetCard: React.FC<{
|
||||
const AssetLibrary: React.FC = () => {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
/* ── 获取素材库列表 ── */
|
||||
/* ── 获取视频库列表 ── */
|
||||
const { data: apiLibraries = [], isLoading: libLoading } = useQuery<AssetLibraryItem[], Error>({
|
||||
queryKey: ["asset-libraries"],
|
||||
queryFn: getAssetLibraries,
|
||||
@@ -362,7 +362,7 @@ const AssetLibrary: React.FC = () => {
|
||||
[apiLibraries],
|
||||
)
|
||||
|
||||
/* ── 当前选中的素材库 ── */
|
||||
/* ── 当前选中的视频库 ── */
|
||||
const [activeLibId, setActiveLibId] = useState<string>("")
|
||||
|
||||
// 当库列表加载完成后,自动选中第一个
|
||||
@@ -396,10 +396,10 @@ const AssetLibrary: React.FC = () => {
|
||||
mutationFn: createAssetLibrary,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["asset-libraries"] })
|
||||
message.success("素材库创建成功")
|
||||
message.success("视频库创建成功")
|
||||
},
|
||||
onError: () => {
|
||||
message.error("创建素材库失败")
|
||||
message.error("创建视频库失败")
|
||||
},
|
||||
})
|
||||
|
||||
@@ -407,10 +407,10 @@ const AssetLibrary: React.FC = () => {
|
||||
mutationFn: deleteAssetLibrary,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["asset-libraries"] })
|
||||
message.success("素材库已删除")
|
||||
message.success("视频库已删除")
|
||||
},
|
||||
onError: () => {
|
||||
message.error("删除素材库失败")
|
||||
message.error("删除视频库失败")
|
||||
},
|
||||
})
|
||||
|
||||
@@ -428,7 +428,7 @@ const AssetLibrary: React.FC = () => {
|
||||
const [uploading, setUploading] = useState(false)
|
||||
const [uploadProgress, setUploadProgress] = useState(0)
|
||||
|
||||
/* 新建素材库 */
|
||||
/* 新建视频库 */
|
||||
const [createModalOpen, setCreateModalOpen] = useState(false)
|
||||
const [newLibName, setNewLibName] = useState("")
|
||||
const [newLibKind, setNewLibKind] = useState<AssetKind>("video")
|
||||
@@ -469,7 +469,7 @@ const AssetLibrary: React.FC = () => {
|
||||
const filteredAssets = useMemo(() => {
|
||||
let list = assets
|
||||
|
||||
/* 按素材库类型过滤(如果筛选类型不是 all) */
|
||||
/* 按视频库类型过滤(如果筛选类型不是 all) */
|
||||
if (filterType !== "all") {
|
||||
list = list.filter((a) => a.kind === filterType)
|
||||
}
|
||||
@@ -521,7 +521,7 @@ const AssetLibrary: React.FC = () => {
|
||||
return
|
||||
}
|
||||
if (!effectiveLibId) {
|
||||
message.warning("请先选择或创建一个素材库")
|
||||
message.warning("请先选择或创建一个视频库")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -551,10 +551,10 @@ const AssetLibrary: React.FC = () => {
|
||||
}
|
||||
}
|
||||
|
||||
/* 新建素材库 */
|
||||
/* 新建视频库 */
|
||||
const handleCreateLibrary = async () => {
|
||||
if (!newLibName.trim()) {
|
||||
message.warning("请输入素材库名称")
|
||||
message.warning("请输入视频库名称")
|
||||
return
|
||||
}
|
||||
try {
|
||||
@@ -571,7 +571,7 @@ const AssetLibrary: React.FC = () => {
|
||||
}
|
||||
}
|
||||
|
||||
/* 删除素材库 */
|
||||
/* 删除视频库 */
|
||||
const handleDeleteLibrary = async (id: string) => {
|
||||
try {
|
||||
await deleteLibMutation.mutateAsync(id)
|
||||
@@ -827,7 +827,7 @@ const AssetLibrary: React.FC = () => {
|
||||
|
||||
{/* 两栏布局 */}
|
||||
<div className="xx-assets-layout">
|
||||
{/* ─── 左侧:素材库列表 ─── */}
|
||||
{/* ─── 左侧:视频库列表 ─── */}
|
||||
<div className="xx-asset-library-list">
|
||||
{libraries.map((lib) => (
|
||||
<div
|
||||
@@ -840,7 +840,7 @@ const AssetLibrary: React.FC = () => {
|
||||
{kindIcon(lib.kind)} {lib.name}
|
||||
</h4>
|
||||
<Popconfirm
|
||||
title={`确定删除素材库 "${lib.name}"?`}
|
||||
title={`确定删除视频库 "${lib.name}"?`}
|
||||
onConfirm={(e) => {
|
||||
e?.stopPropagation()
|
||||
handleDeleteLibrary(lib.id)
|
||||
@@ -852,7 +852,7 @@ const AssetLibrary: React.FC = () => {
|
||||
<button
|
||||
className="xx-asset-library-delete"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
title="删除素材库"
|
||||
title="删除视频库"
|
||||
>
|
||||
<DeleteOutlined />
|
||||
</button>
|
||||
@@ -864,10 +864,10 @@ const AssetLibrary: React.FC = () => {
|
||||
</div>
|
||||
))}
|
||||
|
||||
{/* 新建素材库 */}
|
||||
{/* 新建视频库 */}
|
||||
<div className="xx-asset-library-add" onClick={() => setCreateModalOpen(true)}>
|
||||
<PlusOutlined />
|
||||
新建素材库
|
||||
新建视频库
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1018,15 +1018,15 @@ const AssetLibrary: React.FC = () => {
|
||||
<div className="xx-assets-empty-icon">
|
||||
<PictureOutlined />
|
||||
</div>
|
||||
<p className="xx-assets-empty-title">暂无素材,请上传或切换素材库</p>
|
||||
<p className="xx-assets-empty-title">暂无素材,请上传或切换视频库</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ─── 新建素材库弹窗 ─── */}
|
||||
{/* ─── 新建视频库弹窗 ─── */}
|
||||
<AntModal
|
||||
title="新建素材库"
|
||||
title="新建视频库"
|
||||
open={createModalOpen}
|
||||
onCancel={() => setCreateModalOpen(false)}
|
||||
onOk={handleCreateLibrary}
|
||||
@@ -1039,7 +1039,7 @@ const AssetLibrary: React.FC = () => {
|
||||
<div>
|
||||
<div className="xx-asset-form-label">名称</div>
|
||||
<Input
|
||||
placeholder="请输入素材库名称"
|
||||
placeholder="请输入视频库名称"
|
||||
value={newLibName}
|
||||
onChange={(e) => setNewLibName(e.target.value)}
|
||||
maxLength={50}
|
||||
|
||||
Executable → Regular
+3
-3
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* 素材库页面 - V21 设计系统样式
|
||||
* 两栏布局:左侧素材库列表(260px)+ 右侧素材网格
|
||||
* 视频库页面 - V21 设计系统样式
|
||||
* 两栏布局:左侧视频库列表(260px)+ 右侧素材网格
|
||||
* 统一使用 CSS 变量,支持深色/浅色主题
|
||||
*/
|
||||
@import "../../styles/global.css";
|
||||
@@ -24,7 +24,7 @@
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
左侧素材库列表
|
||||
左侧视频库列表
|
||||
============================================================ */
|
||||
.xx-asset-library-list {
|
||||
display: flex;
|
||||
|
||||
Executable → Regular
+1
-1
@@ -203,7 +203,7 @@ const DuplicationUpload: React.FC = () => {
|
||||
<div className="dup-info-card">
|
||||
<h3>📋 查重说明</h3>
|
||||
<ul className="dup-info-list">
|
||||
<li>系统会对比您上传的视频与素材库中的已有视频</li>
|
||||
<li>系统会对比您上传的视频与视频库中的已有视频</li>
|
||||
<li>查重完成后,可查看重复片段的具体位置</li>
|
||||
<li>查重过程通常需要几分钟,取决于视频大小</li>
|
||||
<li>高相似度片段建议进行替换或裁剪</li>
|
||||
|
||||
Executable → Regular
+2
-2
@@ -560,7 +560,7 @@ const PlanClipsManager: React.FC = () => {
|
||||
|
||||
{/* 素材导入抽屉 */}
|
||||
<Drawer
|
||||
title="从素材库导入"
|
||||
title="从视频库导入"
|
||||
open={importDrawerOpen}
|
||||
onClose={() => setImportDrawerOpen(false)}
|
||||
width={480}
|
||||
@@ -611,7 +611,7 @@ const PlanClipsManager: React.FC = () => {
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<Empty description="素材库为空" />
|
||||
<Empty description="视频库为空" />
|
||||
)}
|
||||
</Drawer>
|
||||
</div>
|
||||
|
||||
@@ -47,7 +47,7 @@ interface ClipPropertiesPanelProps {
|
||||
onOpenBgmDrawer?: () => void
|
||||
/** 打开字幕样式配置 Drawer */
|
||||
onOpenSubtitleDrawer?: () => void
|
||||
/** 配音素材列表(从配音素材库 API 获取) */
|
||||
/** 配音素材列表(从配音库 API 获取) */
|
||||
voiceMaterials?: AssetItem[]
|
||||
/** 配音素材加载中 */
|
||||
voiceMaterialsLoading?: boolean
|
||||
|
||||
@@ -313,7 +313,7 @@ const TtsPanel: React.FC<TtsPanelProps> = ({ open, onClose, config, onChange })
|
||||
{config.mode === "upload" && (
|
||||
<div className="tts-upload-hint">
|
||||
<p>请在右侧面板的「配音素材」中选择已上传的配音文件。</p>
|
||||
<p>如需上传新配音,请前往配音素材库页面。</p>
|
||||
<p>如需上传新配音,请前往配音库页面。</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -245,7 +245,7 @@ const GeneratePage: React.FC = () => {
|
||||
})
|
||||
const [selectedLibraryId, setSelectedLibraryId] = useState<string>("")
|
||||
|
||||
// 自动选中第一个素材库
|
||||
// 自动选中第一个视频库
|
||||
useEffect(() => {
|
||||
if (libraries.length > 0 && !selectedLibraryId) {
|
||||
setSelectedLibraryId(libraries[0].id)
|
||||
@@ -373,7 +373,7 @@ const GeneratePage: React.FC = () => {
|
||||
message.success({
|
||||
content: (
|
||||
<span>
|
||||
已保存到配音素材库!{" "}
|
||||
已保存到配音库!{" "}
|
||||
<a
|
||||
onClick={handleGoToLibrary}
|
||||
style={{
|
||||
@@ -381,7 +381,7 @@ const GeneratePage: React.FC = () => {
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
去素材库查看
|
||||
去视频库查看
|
||||
</a>
|
||||
</span>
|
||||
),
|
||||
@@ -440,7 +440,7 @@ const GeneratePage: React.FC = () => {
|
||||
[allTags, saveTagIds],
|
||||
)
|
||||
|
||||
/** 保存成功后跳转到素材库 */
|
||||
/** 保存成功后跳转到视频库 */
|
||||
const handleGoToLibrary = useCallback(() => {
|
||||
navigate("/app/voice-materials")
|
||||
}, [navigate])
|
||||
@@ -654,7 +654,7 @@ const GeneratePage: React.FC = () => {
|
||||
return "所选模板或素材不可用,请重新选择"
|
||||
}
|
||||
if (msg.includes("asset") && (msg.includes("not found") || msg.includes("missing"))) {
|
||||
return "素材数据异常,请返回素材库重新检查"
|
||||
return "素材数据异常,请返回视频库重新检查"
|
||||
}
|
||||
// 网络/超时
|
||||
if (msg.includes("timeout") || msg.includes("network") || msg.includes("ECONN")) {
|
||||
@@ -861,13 +861,13 @@ const GeneratePage: React.FC = () => {
|
||||
onClick={() => setMaterialMode("auto")}
|
||||
type="button"
|
||||
>
|
||||
选择素材库自动匹配
|
||||
选择视频库自动匹配
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* ── 素材库选择(两种模式共用) ── */}
|
||||
{/* ── 视频库选择(两种模式共用) ── */}
|
||||
<div className="xx-form-field" style={{ marginTop: 12 }}>
|
||||
<label>选择素材库</label>
|
||||
<label>选择视频库</label>
|
||||
<select value={selectedLibraryId} onChange={(e) => setSelectedLibraryId(e.target.value)}>
|
||||
{libraries.map((lib) => (
|
||||
<option key={lib.id} value={lib.id}>
|
||||
@@ -897,7 +897,7 @@ const GeneratePage: React.FC = () => {
|
||||
<Text style={{ color: "var(--text-secondary)", padding: "16px 0" }}>加载素材中…</Text>
|
||||
) : materials.items.length === 0 ? (
|
||||
<Text style={{ color: "var(--text-secondary)", padding: "16px 0" }}>
|
||||
暂无素材,请先在素材库中上传
|
||||
暂无素材,请先在视频库中上传
|
||||
</Text>
|
||||
) : (
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
|
||||
@@ -965,7 +965,7 @@ const GeneratePage: React.FC = () => {
|
||||
<div className="xx-auto-match-body">
|
||||
<h4 className="xx-auto-match-title">智能素材匹配</h4>
|
||||
<p className="xx-auto-match-desc">
|
||||
系统将根据所选模板和标题,从素材库中自动分析并匹配最合适的素材进行视频生成。
|
||||
系统将根据所选模板和标题,从视频库中自动分析并匹配最合适的素材进行视频生成。
|
||||
无需手动挑选,AI 会综合素材质量、时长、内容相关性等维度进行智能筛选。
|
||||
</p>
|
||||
<div className="xx-auto-match-features">
|
||||
@@ -982,7 +982,7 @@ const GeneratePage: React.FC = () => {
|
||||
marginTop: 8,
|
||||
}}
|
||||
>
|
||||
扫描素材库中…
|
||||
扫描视频库中…
|
||||
</Text>
|
||||
) : (
|
||||
<Text
|
||||
@@ -992,7 +992,7 @@ const GeneratePage: React.FC = () => {
|
||||
marginTop: 8,
|
||||
}}
|
||||
>
|
||||
当前素材库共 {materials.items.length} 个素材可供匹配
|
||||
当前视频库共 {materials.items.length} 个素材可供匹配
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
@@ -1231,7 +1231,7 @@ const GeneratePage: React.FC = () => {
|
||||
<div className="xx-save-modal-overlay" onClick={() => setSaveModalOpen(false)}>
|
||||
<div className="xx-save-modal" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="xx-save-modal-header">
|
||||
<span>保存到配音素材库</span>
|
||||
<span>保存到配音库</span>
|
||||
<button className="xx-save-modal-close" onClick={() => setSaveModalOpen(false)}>
|
||||
<CloseOutlined />
|
||||
</button>
|
||||
|
||||
@@ -927,7 +927,7 @@ const VoiceMaterialLibrary: React.FC = () => {
|
||||
|
||||
// 自动创建 voice 素材库(如果不存在)
|
||||
const createLibMutation = useMutation({
|
||||
mutationFn: () => createAssetLibrary({ name: "配音素材库", kind: "voice" }),
|
||||
mutationFn: () => createAssetLibrary({ name: "配音库", kind: "voice" }),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["asset-libraries"] })
|
||||
},
|
||||
@@ -1032,7 +1032,7 @@ const VoiceMaterialLibrary: React.FC = () => {
|
||||
queryFn: getAssetLibraries,
|
||||
})
|
||||
lib = libs.find((l) => l.kind === "voice")
|
||||
if (!lib) throw new Error("无法创建配音素材库")
|
||||
if (!lib) throw new Error("无法创建配音库")
|
||||
}
|
||||
|
||||
// 2. 上传文件(带进度)
|
||||
@@ -1474,7 +1474,7 @@ const VoiceMaterialLibrary: React.FC = () => {
|
||||
await saveTtsToLibrary(ttsJobId, {
|
||||
name: ttsText.slice(0, 20) || "AI配音",
|
||||
})
|
||||
message.success("已保存到配音素材库")
|
||||
message.success("已保存到配音库")
|
||||
queryClient.invalidateQueries({ queryKey: ["assets", "voice"] })
|
||||
setTtsOpen(false)
|
||||
} catch {
|
||||
@@ -1513,7 +1513,7 @@ const VoiceMaterialLibrary: React.FC = () => {
|
||||
return (
|
||||
<div className="vmat-page">
|
||||
<PageHead
|
||||
title="配音素材库"
|
||||
title="配音库"
|
||||
description="管理配音音频素材,支持上传、试听、编辑元信息"
|
||||
actions={pageActions}
|
||||
/>
|
||||
|
||||
@@ -670,13 +670,13 @@ const VoiceLibrary: React.FC = () => {
|
||||
}) => {
|
||||
setUploadProgress(0)
|
||||
try {
|
||||
/* 获取或创建默认配音素材库 */
|
||||
/* 获取或创建默认配音库 */
|
||||
const libs = await queryClient.fetchQuery({
|
||||
queryKey: ["asset-libraries"],
|
||||
queryFn: getAssetLibraries,
|
||||
})
|
||||
const lib = libs.find((l) => l.kind === "voice")
|
||||
if (!lib) throw new Error("配音素材库不存在,请先在配音素材库页面创建")
|
||||
if (!lib) throw new Error("配音库不存在,请先在配音库页面创建")
|
||||
|
||||
/* 直传文件 */
|
||||
const { storage_key } = await uploadAssetDirect({
|
||||
@@ -769,7 +769,7 @@ const VoiceLibrary: React.FC = () => {
|
||||
try {
|
||||
await saveTtsToLibrary(ttsJobId, { name: ttsText.slice(0, 50) })
|
||||
queryClient.invalidateQueries({ queryKey: ["assets", "voice"] })
|
||||
showToast("已保存到配音素材库", "success")
|
||||
showToast("已保存到配音库", "success")
|
||||
setTtsOpen(false)
|
||||
} catch (err: unknown) {
|
||||
const msg = err instanceof Error ? err.message : "保存失败"
|
||||
@@ -1669,7 +1669,7 @@ const VoiceLibrary: React.FC = () => {
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
保存到配音素材库
|
||||
保存到配音库
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user