素材案例对接,资源存储修改
This commit is contained in:
@@ -305,51 +305,7 @@ export async function getCreditRatios(): Promise<any[]> {
|
||||
return api.get('/credits/ratios');
|
||||
}
|
||||
// 引擎配置
|
||||
export async function getEngine(): Promise<any> {
|
||||
if (USE_MOCK) return {
|
||||
engine: {
|
||||
image: [
|
||||
{
|
||||
id: "0019e3dac0b795b925b",
|
||||
name: "Seedream 5.0",
|
||||
provider: "ark",
|
||||
model_name: "doubao-seedream-5-0-260128",
|
||||
supported_models: ["doubao-seedream-5-0-260128"],
|
||||
supported_sizes: {
|
||||
"2K": {
|
||||
"1:1": "2048×2048",
|
||||
"4:3": "2304×1728",
|
||||
"3:4": "1728×2304",
|
||||
"16:9": "2560×1440",
|
||||
"9:16": "1600×2848",
|
||||
"3:2": "2496×1664",
|
||||
"2:3": "1664×2496",
|
||||
"21:9": "3024×1296"
|
||||
},
|
||||
"4K": {
|
||||
"1:1": "4096×4096",
|
||||
"4:3": "4608×3456",
|
||||
"3:4": "3520×4704",
|
||||
"16:9": "5404×3040",
|
||||
"9:16": "3040×5504",
|
||||
"3:2": "4992×3328",
|
||||
"2:3": "3328×4992",
|
||||
"21:9": "6197×2656"
|
||||
}
|
||||
},
|
||||
default_size: "2K",
|
||||
priority: 10,
|
||||
maxImageCount: 4,
|
||||
maxVideoCount: 0
|
||||
}
|
||||
],
|
||||
video: [
|
||||
{ id: '0019e1697667d0eff39', name: 'Seedance 2.0', provider: 'ark', model_name: 'seedance-2-0', supportedRatios: ['16:9', '9:16', '1:1', '4:3', '3:4', '21:9'], supportedResolutions: ['480p', '720p', '1080p'], supportedDurations: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], maxImageCount: 4, maxVideoCount: 1 },
|
||||
{ id: '0019e1697667d0eff38', name: 'Seedance 2.0 fast', provider: 'ark', model_name: 'seedance-2-0-fast', supportedRatios: ['16:9', '9:16', '1:1'], supportedResolutions: ['720p', '1080p'], supportedDurations: [5, 8, 10], maxImageCount: 2, maxVideoCount: 1 },
|
||||
{ id: '0019f1b89b545d8e4b1', name: 'Seedance 2.0 mini', provider: 'ark', model_name: 'seedance-2-0-mini', supportedRatios: ['16:9', '9:16', '1:1'], supportedResolutions: ['480p', '720p'], supportedDurations: [4, 5, 6], maxImageCount: 1, maxVideoCount: 1 }
|
||||
]
|
||||
}
|
||||
};
|
||||
export async function getEngine(): Promise<any[]> {
|
||||
return api.get('/generation-ai/engines');
|
||||
}
|
||||
// ── Generation AI Tasks ────────────────────────────────────
|
||||
@@ -760,6 +716,6 @@ export async function getHomeCaseHeader(): Promise<any> {
|
||||
}
|
||||
|
||||
// 首页素材按钮资源
|
||||
export async function getHomeCaseButton(id: string,limit:number=8): Promise<any> {
|
||||
export async function getHomeCaseButton(id: string,limit:number=5): Promise<any> {
|
||||
return api.get(`/home-materials?category_id=${id}&limit_per_category=${limit}&include_empty_categories=false&response_mode=grouped&page=1&page_size=20`);
|
||||
}
|
||||
|
||||
@@ -165,9 +165,10 @@ const StorageCard: React.FC<{ data: ResourceCapacityData | null }> = ({ data })
|
||||
width: '100%',
|
||||
height: 20,
|
||||
background: '#e2e8f0',
|
||||
borderRadius: 3,
|
||||
borderRadius: 2,
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
|
||||
}}
|
||||
>
|
||||
<div style={{
|
||||
@@ -182,17 +183,16 @@ const StorageCard: React.FC<{ data: ResourceCapacityData | null }> = ({ data })
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: 6,
|
||||
fontSize: 12,
|
||||
color: '#000000ff',
|
||||
color: isOver ? '#ffffffff' : '#000000ff',
|
||||
padding: '0 8px',
|
||||
|
||||
|
||||
|
||||
}}>
|
||||
<span style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
||||
<DatabaseOutlined style={{ fontSize: 12, color: '#000000ff' }} />
|
||||
<DatabaseOutlined style={{ fontSize: 12, color: isOver ? '#ffffffff' :'#000000ff' }} />
|
||||
{rawPercent.toFixed(1)}%
|
||||
</span>
|
||||
{data.enabled ? (
|
||||
<span style={{ fontWeight: 500, color: isOver ? '#ef4444' : '#000000ff' }}>
|
||||
<span style={{ fontWeight: 500, color: isOver ? '#ffffffff' : '#000000ff' }}>
|
||||
{used.toFixed(2)} / {total.toFixed(2)} {unit}
|
||||
</span>
|
||||
) : (
|
||||
@@ -207,10 +207,13 @@ const StorageCard: React.FC<{ data: ResourceCapacityData | null }> = ({ data })
|
||||
style={{
|
||||
width: `${barPercent}%`,
|
||||
height: '100%',
|
||||
background: isOver
|
||||
? 'linear-gradient(90deg, #ef4444, #dc2626)'
|
||||
: 'linear-gradient(90deg, #6366f1, #8b5cf6)',
|
||||
borderRadius: 3,
|
||||
|
||||
background: rawPercent < 50
|
||||
? 'linear-gradient(90deg, #279951, #b7fad0)'
|
||||
: rawPercent < 85
|
||||
? 'linear-gradient(90deg, #ffd759, #fff6d4)'
|
||||
: 'linear-gradient(90deg, #ef4444, #dc2626)',
|
||||
borderRadius: 2,
|
||||
transition: 'width 0.4s ease',
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Button, Input, Tabs, Tag, Upload, message } from 'antd';
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { Button, Input, Tabs, Tag, Upload, message, Modal } from 'antd';
|
||||
import {
|
||||
FileTextOutlined,
|
||||
ScissorOutlined,
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
VideoCameraOutlined,
|
||||
PictureOutlined,
|
||||
ThunderboltOutlined,
|
||||
PlayCircleOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { getmedit ,getHomeCaseHeader,getHomeCaseButton} from '../api';
|
||||
@@ -39,13 +40,28 @@ const HomePage: React.FC = () => {
|
||||
const [activeTab, setActiveTab] = useState('project');
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [mockVideos, setMockVideos] = useState<any[]>([]);
|
||||
const [caseHeader, setCaseHeader] = useState<any[]>([]);
|
||||
const [activeCaseTab, setActiveCaseTab] = useState<string>('');
|
||||
const [caseAssets, setCaseAssets] = useState<any[]>([]);
|
||||
const [previewAsset, setPreviewAsset] = useState<any>(null);
|
||||
const previewVideoRef = useRef<HTMLVideoElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
getHomeCaseHeader().then((res) => {
|
||||
console.log(res);
|
||||
})
|
||||
getHomeCaseButton('0019f17d00fdbece98e').then((res) => {
|
||||
console.log(res);
|
||||
})
|
||||
getHomeCaseHeader().then((res: any) => {
|
||||
// console.log('caseHeader:', res);
|
||||
if (res?.items?.length > 0) {
|
||||
setCaseHeader(res.items);
|
||||
const firstId = res.items[0].id;
|
||||
setActiveCaseTab(firstId);
|
||||
// 初始请求第一个 tab 的数据
|
||||
getHomeCaseButton(firstId).then((btnRes: any) => {
|
||||
console.log('caseButton:', btnRes);
|
||||
if (btnRes?.categories?.[0]?.assets) {
|
||||
setCaseAssets(btnRes.categories[0].assets);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -83,21 +99,21 @@ const HomePage: React.FC = () => {
|
||||
{
|
||||
icon: <FileTextOutlined style={{ fontSize: 24, color: '#6366f1' }} />,
|
||||
title: '爆款开头复刻',
|
||||
description: '一键复刻热门视频开篇,快速替换自有商品素材',
|
||||
description: '上传参考视频与产品图片,一键复刻爆款视频开头',
|
||||
action: '立即创作',
|
||||
path: '/initial',
|
||||
},
|
||||
{
|
||||
icon: <ScissorOutlined style={{ fontSize: 24, color: '#f97316' }} />,
|
||||
title: '批量混剪',
|
||||
description: '多素材批量自动剪辑,智能筛选高清优质镜头片段',
|
||||
title: '拆镜复刻',
|
||||
description: '一键拆解画面分镜,助力仿拍或创作',
|
||||
action: '开始混剪',
|
||||
path: '/removelens',
|
||||
},
|
||||
{
|
||||
icon: <RobotOutlined style={{ fontSize: 24, color: '#10b981' }} />,
|
||||
title: 'AI成片',
|
||||
description: 'AI全自动快速出片,支持文案生成/上传参考素材制作',
|
||||
description: '输入想法、剧本或上传参考,智能生成视频/图片',
|
||||
action: '立即生成',
|
||||
path: '/conversation',
|
||||
},
|
||||
@@ -715,8 +731,6 @@ const HomePage: React.FC = () => {
|
||||
gap: 6,
|
||||
fontSize: 12,
|
||||
color: '#64748b',
|
||||
|
||||
|
||||
}}>
|
||||
{(() => {
|
||||
// 显示所属模块,而非媒体类型
|
||||
@@ -781,68 +795,176 @@ const HomePage: React.FC = () => {
|
||||
精选优质作品参考
|
||||
</div>
|
||||
</div>
|
||||
<div style={{
|
||||
{/* <div style={{
|
||||
fontSize: 13, color: '#6366f1', cursor: 'pointer', fontWeight: 500,
|
||||
display: 'flex', alignItems: 'center', gap: 2,
|
||||
}}>
|
||||
更多案例
|
||||
<ArrowRightOutlined style={{ fontSize: 11 }} />
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
|
||||
<div className="stagger-children" style={{ display: 'flex', gap: 14 }}>
|
||||
{materialCases.map((caseUrl, index) => (
|
||||
{/* Tab切换 */}
|
||||
<div style={{ marginBottom: 20 }}>
|
||||
<Tabs
|
||||
activeKey={activeCaseTab}
|
||||
onChange={(key) => {
|
||||
setActiveCaseTab(key);
|
||||
getHomeCaseButton(key).then((btnRes: any) => {
|
||||
if (btnRes?.categories?.[0]?.assets) {
|
||||
setCaseAssets(btnRes.categories[0].assets);
|
||||
} else {
|
||||
setCaseAssets([]);
|
||||
}
|
||||
});
|
||||
}}
|
||||
items={caseHeader.map((item: any) => ({
|
||||
key: item.id,
|
||||
label: item.name,
|
||||
}))}
|
||||
className="homepage-tabs"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* ========== 素材案例列表(与近期作品一致) ========== */}
|
||||
<div className="stagger-children" style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 16 }}>
|
||||
{caseAssets.length === 0 ? (
|
||||
<div style={{
|
||||
gridColumn: '1 / -1',
|
||||
padding: '60px 0',
|
||||
textAlign: 'center',
|
||||
color: '#94a3b8',
|
||||
fontSize: 14,
|
||||
}}>
|
||||
<PictureOutlined style={{ fontSize: 36, color: '#cbd5e1', marginBottom: 8 }} />
|
||||
<div>暂无素材</div>
|
||||
</div>
|
||||
) : caseAssets.map((asset: any, index: number) => (
|
||||
<div
|
||||
key={index}
|
||||
key={asset.id || index}
|
||||
className="project-card"
|
||||
onClick={() => setPreviewAsset(asset)}
|
||||
style={{
|
||||
flex: 1,
|
||||
aspectRatio: '16/9',
|
||||
borderRadius: 12,
|
||||
overflow: 'hidden',
|
||||
cursor: 'pointer',
|
||||
background: '#f1f5f9',
|
||||
position: 'relative',
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
const overlay = e.currentTarget.querySelector('.case-overlay') as HTMLElement | null;
|
||||
if (overlay) overlay.style.opacity = '1';
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
const overlay = e.currentTarget.querySelector('.case-overlay') as HTMLElement | null;
|
||||
if (overlay) overlay.style.opacity = '0';
|
||||
background: '#fff',
|
||||
border: '1px solid #e2e8f0',
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={caseUrl}
|
||||
alt={`素材案例 ${index + 1}`}
|
||||
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
|
||||
/>
|
||||
案例悬停遮罩(由父级 hover 触发)
|
||||
<div
|
||||
className="case-overlay"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
background: 'linear-gradient(180deg, transparent 40%, rgba(99,102,241,0.75) 100%)',
|
||||
opacity: 0,
|
||||
transition: 'opacity 0.3s',
|
||||
display: 'flex',
|
||||
alignItems: 'flex-end',
|
||||
justifyContent: 'center',
|
||||
padding: 14,
|
||||
color: '#fff',
|
||||
fontSize: 13,
|
||||
fontWeight: 600,
|
||||
letterSpacing: 0.5,
|
||||
}}
|
||||
>
|
||||
案例 {index + 1}
|
||||
{/* 媒体区域 16:9 */}
|
||||
<div style={{ position: 'relative', aspectRatio: '16/9', }}>
|
||||
{asset.mediaType === 'video' ? (
|
||||
<>
|
||||
<video
|
||||
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${asset.url}`}
|
||||
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
|
||||
muted
|
||||
playsInline
|
||||
/>
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
background: 'rgba(0,0,0,0.2)',
|
||||
}}>
|
||||
<VideoCameraOutlined style={{ fontSize: 28, color: '#fff' }} />
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<img
|
||||
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${asset.url}`}
|
||||
alt={asset.title || `素材 ${index + 1}`}
|
||||
style={{ width: '100%', height: '100%', objectFit: 'cover' }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{/* 底部信息栏 */}
|
||||
<div style={{
|
||||
padding: '10px 12px',
|
||||
background: '#f8fafc',
|
||||
}}>
|
||||
<div style={{
|
||||
fontSize: 12,
|
||||
color: '#64748b',
|
||||
textAlign: 'center',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
}}>
|
||||
{asset.title || `素材 ${index + 1}`}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ========== 预览弹窗 ========== */}
|
||||
<Modal
|
||||
open={!!previewAsset}
|
||||
onCancel={() => {
|
||||
previewVideoRef.current?.pause();
|
||||
setPreviewAsset(null);
|
||||
}}
|
||||
footer={null}
|
||||
width={760}
|
||||
centered
|
||||
className="preview-modal"
|
||||
bodyStyle={{
|
||||
padding: 0,
|
||||
background: '#fff',
|
||||
borderRadius: 16,
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{/* 固定比例容器 16:9 */}
|
||||
<div style={{
|
||||
width: '100%',
|
||||
paddingTop: '56.25%',
|
||||
position: 'relative',
|
||||
// background: '#000',
|
||||
}}>
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
{previewAsset?.mediaType === 'video' ? (
|
||||
<video
|
||||
ref={previewVideoRef}
|
||||
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${previewAsset.url}`}
|
||||
controls
|
||||
autoPlay
|
||||
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
||||
/>
|
||||
) : (
|
||||
<img
|
||||
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${previewAsset?.url}`}
|
||||
alt={previewAsset?.title}
|
||||
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{/* 底部标题栏 */}
|
||||
{previewAsset?.title && (
|
||||
<div style={{
|
||||
padding: '14px 20px',
|
||||
fontSize: 14,
|
||||
color: '#4b5563',
|
||||
fontWeight: 500,
|
||||
borderTop: '1px solid #f1f5f9',
|
||||
textAlign: 'center',
|
||||
}}>
|
||||
{previewAsset.title}
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -459,7 +459,8 @@ const GenerateConver: React.FC = () => {
|
||||
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
backgroundClip: 'text'
|
||||
backgroundClip: 'text',
|
||||
textAlign: 'center',
|
||||
}}>
|
||||
爆款开头复刻
|
||||
</h2>
|
||||
|
||||
@@ -125,7 +125,8 @@ const ProjectsPage: React.FC = () => {
|
||||
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
backgroundClip: 'text'
|
||||
backgroundClip: 'text',
|
||||
textAlign: 'center',
|
||||
}}>
|
||||
我的项目
|
||||
</h2>
|
||||
|
||||
@@ -186,7 +186,8 @@ export default function VideoFrameExtractor() {
|
||||
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
backgroundClip: 'text'
|
||||
backgroundClip: 'text',
|
||||
textAlign: 'center',
|
||||
}}>
|
||||
AI视频拆镜工作台
|
||||
</h2>
|
||||
|
||||
Reference in New Issue
Block a user