“爆款复刻/拆镜复刻积分不足提示”
This commit is contained in:
+156
-105
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -28,7 +28,7 @@
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
<script type="module" crossorigin src="/assets/index-nrhXZrQV.js"></script>
|
<script type="module" crossorigin src="/assets/index-Dq-lhFsY.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-DviWdElm.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-DviWdElm.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import {
|
|||||||
HeartOutlined,
|
HeartOutlined,
|
||||||
CameraOutlined,
|
CameraOutlined,
|
||||||
CalculatorOutlined,
|
CalculatorOutlined,
|
||||||
|
CaretDownOutlined,
|
||||||
DollarOutlined,
|
DollarOutlined,
|
||||||
FireOutlined,
|
FireOutlined,
|
||||||
CloudOutlined,
|
CloudOutlined,
|
||||||
@@ -126,6 +127,7 @@ const StorageCard: React.FC<{ data: ResourceCapacityData | null }> = ({ data })
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
// marginTop: 12,
|
// marginTop: 12,
|
||||||
|
marginBottom: 12,
|
||||||
padding: '0 12px',
|
padding: '0 12px',
|
||||||
paddingTop: 6,
|
paddingTop: 6,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
@@ -906,33 +908,59 @@ const AppLayout: React.FC = () => {
|
|||||||
|
|
||||||
<div style={{ padding: '16px 16px', flexShrink: 0, paddingTop: 0 }}>
|
<div style={{ padding: '16px 16px', flexShrink: 0, paddingTop: 0 }}>
|
||||||
<Dropdown menu={{ items: userMenuItems, onClick: handleUserMenuClick }} placement="topRight" arrow>
|
<Dropdown menu={{ items: userMenuItems, onClick: handleUserMenuClick }} placement="topRight" arrow>
|
||||||
<div style={{
|
<div className="user-card-container" style={{
|
||||||
|
padding: '10px 14px',
|
||||||
display: 'flex', alignItems: 'center',
|
display: 'flex', alignItems: 'center',
|
||||||
justifyContent: 'flex-start',
|
justifyContent: 'flex-start',
|
||||||
gap: 12,
|
gap: 12,
|
||||||
// padding: '10px 14px',
|
|
||||||
borderRadius: 14, cursor: 'pointer',
|
borderRadius: 14, cursor: 'pointer',
|
||||||
transition: 'all 0.25s ease',
|
transition: 'all 0.35s cubic-bezier(0.4,0,0.2,1)',
|
||||||
background: 'linear-gradient(135deg, rgba(248, 250, 252, 0.9) 0%, rgba(241, 245, 249, 0.9) 100%)',
|
background: 'linear-gradient(135deg, rgba(248, 250, 252, 0.95) 0%, rgba(241, 245, 249, 0.95) 100%)',
|
||||||
boxShadow: '0 2px 12px rgba(0, 0, 0, 0.04)',
|
boxShadow: '0 2px 12px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255,255,255,0.8)',
|
||||||
|
position: 'relative',
|
||||||
|
border: '1px solid rgba(99, 102, 241, 0.15)',
|
||||||
|
animation: 'cardBreath 3s ease-in-out infinite',
|
||||||
}}
|
}}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e) => {
|
||||||
e.currentTarget.style.background = 'linear-gradient(135deg, #ffffff 0%, #f8fafc 100%)';
|
e.currentTarget.style.background = 'linear-gradient(135deg, #ffffff 0%, #f8fafc 100%)';
|
||||||
e.currentTarget.style.boxShadow = '0 4px 16px rgba(0, 0, 0, 0.08)';
|
e.currentTarget.style.boxShadow = '0 8px 24px rgba(99, 102, 241, 0.25), inset 0 1px 0 rgba(255,255,255,0.9)';
|
||||||
|
e.currentTarget.style.transform = 'translateY(-3px) scale(1.01)';
|
||||||
|
e.currentTarget.style.borderColor = 'rgba(99, 102, 241, 0.4)';
|
||||||
|
e.currentTarget.style.animation = 'none';
|
||||||
|
e.currentTarget.querySelector('.user-card-arrow')?.classList.add('arrow-hover');
|
||||||
}}
|
}}
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e) => {
|
||||||
e.currentTarget.style.background = 'linear-gradient(135deg, rgba(248, 250, 252, 0.9) 0%, rgba(241, 245, 249, 0.9) 100%)';
|
e.currentTarget.style.background = 'linear-gradient(135deg, rgba(248, 250, 252, 0.95) 0%, rgba(241, 245, 249, 0.95) 100%)';
|
||||||
e.currentTarget.style.boxShadow = '0 2px 12px rgba(0, 0, 0, 0.04)';
|
e.currentTarget.style.boxShadow = '0 2px 12px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255,255,255,0.8)';
|
||||||
|
e.currentTarget.style.transform = 'translateY(0) scale(1)';
|
||||||
|
e.currentTarget.style.borderColor = 'rgba(99, 102, 241, 0.15)';
|
||||||
|
e.currentTarget.style.animation = 'cardBreath 3s ease-in-out infinite';
|
||||||
|
e.currentTarget.querySelector('.user-card-arrow')?.classList.remove('arrow-hover');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<div className="user-card-glow" style={{
|
||||||
|
position: 'absolute',
|
||||||
|
inset: -4,
|
||||||
|
borderRadius: 18,
|
||||||
|
background: 'linear-gradient(90deg, transparent 0%, rgba(99,102,241,0.15) 25%, rgba(139,92,246,0.15) 50%, rgba(99,102,241,0.15) 75%, transparent 100%)',
|
||||||
|
backgroundSize: '200% 100%',
|
||||||
|
opacity: 0.8,
|
||||||
|
animation: 'borderShimmer 2.5s linear infinite',
|
||||||
|
pointerEvents: 'none',
|
||||||
|
}} />
|
||||||
|
|
||||||
<Avatar size={36} icon={<UserOutlined />}
|
<Avatar size={36} icon={<UserOutlined />}
|
||||||
style={{
|
style={{
|
||||||
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)',
|
boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)',
|
||||||
|
transition: 'all 0.35s cubic-bezier(0.4,0,0.2,1)',
|
||||||
|
position: 'relative',
|
||||||
|
zIndex: 1,
|
||||||
|
animation: 'avatarPulse 2s ease-in-out infinite',
|
||||||
}} />
|
}} />
|
||||||
|
|
||||||
<div style={{ flex: 1, minWidth: 0 }}>
|
<div style={{ flex: 1, minWidth: 0, position: 'relative', zIndex: 1 }}>
|
||||||
<div style={{ color: '#1e293b', fontSize: 16, fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', letterSpacing: -0.01 }}>
|
<div style={{ color: '#1e293b', fontSize: 16, fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', letterSpacing: -0.01 }}>
|
||||||
{user?.username}
|
{user?.username}
|
||||||
</div>
|
</div>
|
||||||
@@ -941,12 +969,74 @@ const AppLayout: React.FC = () => {
|
|||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: 500,
|
fontWeight: 500,
|
||||||
letterSpacing: 0,
|
letterSpacing: 0,
|
||||||
// background: 'rgba(99, 102, 241, 0.08)',
|
|
||||||
// padding: '2px 8px',
|
|
||||||
borderRadius: 6,
|
borderRadius: 6,
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
|
animation: 'creditGlow 1.5s ease-in-out infinite',
|
||||||
}}>积分: {user?.credits || 0}</div>
|
}}>积分: {user?.credits || 0}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="user-card-arrow" style={{
|
||||||
|
fontSize: 12,
|
||||||
|
color: '#6366f1',
|
||||||
|
flexShrink: 0,
|
||||||
|
position: 'relative',
|
||||||
|
zIndex: 1,
|
||||||
|
}}>
|
||||||
|
<MenuOutlined />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>{`
|
||||||
|
@keyframes arrowFlash {
|
||||||
|
0%, 100% {
|
||||||
|
transform: translateY(0) scale(1);
|
||||||
|
opacity: 0.6;
|
||||||
|
filter: drop-shadow(0 0 4px rgba(99, 102, 241, 0.4));
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateY(4px) scale(1.1);
|
||||||
|
opacity: 1;
|
||||||
|
filter: drop-shadow(0 0 12px rgba(99, 102, 241, 0.7));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes creditGlow {
|
||||||
|
0%, 100% {
|
||||||
|
text-shadow: 0 0 0 transparent;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
text-shadow: 0 0 15px rgba(99, 102, 241, 0.6), 0 0 30px rgba(99, 102, 241, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes borderShimmer {
|
||||||
|
0% { background-position: 200% center; }
|
||||||
|
100% { background-position: -200% center; }
|
||||||
|
}
|
||||||
|
@keyframes cardBreath {
|
||||||
|
0%, 100% {
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255,255,255,0.8);
|
||||||
|
border-color: rgba(99, 102, 241, 0.15);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
box-shadow: 0 4px 20px rgba(99, 102, 241, 0.1), inset 0 1px 0 rgba(255,255,255,0.8);
|
||||||
|
border-color: rgba(99, 102, 241, 0.25);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes avatarPulse {
|
||||||
|
0%, 100% {
|
||||||
|
box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
box-shadow: 0 6px 20px rgba(99, 102, 241, 0.5);
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.user-card-arrow.arrow-hover {
|
||||||
|
color: #8b5cf6 !important;
|
||||||
|
transform: rotate(180deg) scale(1.15) !important;
|
||||||
|
animation: none !important;
|
||||||
|
filter: drop-shadow(0 0 15px rgba(139, 92, 246, 0.8)) !important;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
</div>
|
</div>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Button, Typography, Collapse, Space, Modal, Input, Table, message } from 'antd';
|
import { Button, Typography, Collapse, Space, Modal, Input, Table, message, Tooltip } from 'antd';
|
||||||
import { ArrowLeftOutlined, PlayCircleOutlined, CheckCircleOutlined, EditOutlined, DownloadOutlined, SettingOutlined, LayoutOutlined } from '@ant-design/icons';
|
import { ArrowLeftOutlined, PlayCircleOutlined, CheckCircleOutlined, EditOutlined, DownloadOutlined, SettingOutlined, LayoutOutlined, WarningOutlined } from '@ant-design/icons';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import { getReplicationList, getReplicationDetail, gettwo, getthree, getfour, getEngine, updateHotOpeningVideoPromptSchema, updateImagePrompt } from '../api/index';
|
import { getReplicationList, getReplicationDetail, gettwo, getthree, getfour, getEngine, updateHotOpeningVideoPromptSchema, updateImagePrompt, calculateCredits } from '../api/index';
|
||||||
|
import { useAuthStore } from '../store/useAuthStore';
|
||||||
import VideoPromptSchemaEditor from '../components/VideoPromptSchemaEditor';
|
import VideoPromptSchemaEditor from '../components/VideoPromptSchemaEditor';
|
||||||
import { validateVideoPromptSchemaByConfig } from '../utils/videoPromptSchema';
|
import { validateVideoPromptSchemaByConfig } from '../utils/videoPromptSchema';
|
||||||
import './css/InitialInfo.css';
|
import './css/InitialInfo.css';
|
||||||
@@ -24,6 +25,7 @@ const buildMediaUrl = (url: string): string => {
|
|||||||
function InitialInfo() {
|
function InitialInfo() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { creatID } = useParams<{ creatID: string }>();
|
const { creatID } = useParams<{ creatID: string }>();
|
||||||
|
const { user } = useAuthStore();
|
||||||
|
|
||||||
const [modalVisible, setModalVisible] = useState(false);
|
const [modalVisible, setModalVisible] = useState(false);
|
||||||
const [promptText, setPromptText] = useState('');
|
const [promptText, setPromptText] = useState('');
|
||||||
@@ -55,6 +57,9 @@ function InitialInfo() {
|
|||||||
durations: [5, 8, 10, 12, 15],
|
durations: [5, 8, 10, 12, 15],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [creditCalculationData, setCreditCalculationData] = useState<any[]>([]);
|
||||||
|
const [estimatedCredits, setEstimatedCredits] = useState<number>(0);
|
||||||
|
|
||||||
// 创作记录表格数据
|
// 创作记录表格数据
|
||||||
const [tableData, setTableData] = useState<any[]>([]);
|
const [tableData, setTableData] = useState<any[]>([]);
|
||||||
// 分页状态
|
// 分页状态
|
||||||
@@ -222,27 +227,89 @@ function InitialInfo() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getEngine()
|
getEngine()
|
||||||
.then((data: any) => {
|
.then((data: any) => {
|
||||||
setEnginesele(data.engine || {});
|
const filteredVideoEngines = (data.engine?.video || []).filter((engine: any) => engine.supportsUniversalReference !== false);
|
||||||
// 默认选中第一个视频引擎
|
const filteredImageEngines = (data.engine?.image || []).filter((engine: any) => engine.supportsUniversalReference !== false);
|
||||||
if (data.engine?.video && data.engine.video.length > 0) {
|
|
||||||
setCountType(data.engine.video[0].id);
|
setEnginesele({
|
||||||
// 设置默认引擎参数
|
video: filteredVideoEngines,
|
||||||
const firstEngine = data.engine.video[0];
|
image: filteredImageEngines,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (filteredVideoEngines.length > 0) {
|
||||||
|
setCountType(filteredVideoEngines[0].id);
|
||||||
|
const firstEngine = filteredVideoEngines[0];
|
||||||
setEngineOptions({
|
setEngineOptions({
|
||||||
ratios: firstEngine.supportedRatios || ['16:9', '4:3', '1:1', '3:4', '9:16', '21:9'],
|
ratios: firstEngine.supportedRatios || ['16:9', '4:3', '1:1', '3:4', '9:16', '21:9'],
|
||||||
resolutions: firstEngine.supportedResolutions || ['480p', '720p', '1080p'],
|
resolutions: firstEngine.supportedResolutions || ['480p', '720p', '1080p'],
|
||||||
durations: firstEngine.supportedDurations || [5, 8, 10, 12, 15],
|
durations: firstEngine.supportedDurations || [5, 8, 10, 12, 15],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 默认选中第一个图片引擎
|
if (filteredImageEngines.length > 0) {
|
||||||
if (data.engine?.image && data.engine.image.length > 0) {
|
setImageEngineId(filteredImageEngines[0].id);
|
||||||
setImageEngineId(data.engine.image[0].id);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
calculateCredits().then((data: any) => {
|
||||||
|
setCreditCalculationData(data);
|
||||||
|
}).catch(() => {});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const calculateEstimatedCredits = () => {
|
||||||
|
let config: any = {};
|
||||||
|
config = creditCalculationData.find((item: any) =>
|
||||||
|
item.modelConfigId === countType &&
|
||||||
|
item.genType === 'video' &&
|
||||||
|
item.resolution === videoResolution
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!config) {
|
||||||
|
config = {
|
||||||
|
perSecondCredits: 2,
|
||||||
|
baseCredits: 60,
|
||||||
|
ratio: 1.3,
|
||||||
|
inputVideoRatio: 1.3,
|
||||||
|
inputVideoBaseCredits: 0,
|
||||||
|
inputVideoPerSecondCredits: 15,
|
||||||
|
inputImageRatio: 1,
|
||||||
|
inputImageBaseCredits: 0,
|
||||||
|
inputImagePerImageCredits: 0.0,
|
||||||
|
};
|
||||||
|
if (videoResolution === '1080p') {
|
||||||
|
config.ratio = 1.3;
|
||||||
|
} else if (videoResolution === '720p') {
|
||||||
|
config.ratio = 1.3;
|
||||||
|
} else if (videoResolution === '480p') {
|
||||||
|
config.ratio = 1.3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let total = (videoDuration * config.perSecondCredits + config.baseCredits) * config.ratio;
|
||||||
|
|
||||||
|
const inputVideoDuration = taskDetail?.videoGeneration?.inputMedia?.video?.duration || 0;
|
||||||
|
if (inputVideoDuration > 0) {
|
||||||
|
const inputVideoCost = ((config.inputVideoBaseCredits || 0) + (config.inputVideoPerSecondCredits || 0) * inputVideoDuration) * (config.inputVideoRatio || 1);
|
||||||
|
total += inputVideoCost;
|
||||||
|
}
|
||||||
|
|
||||||
|
const inputImageCount = taskDetail?.videoGeneration?.inputMedia?.image?.length || 0;
|
||||||
|
if (inputImageCount > 0) {
|
||||||
|
const inputImageCost = ((config.inputImageBaseCredits || 0) + (config.inputImagePerImageCredits || 0) * inputImageCount) * (config.inputImageRatio || 1);
|
||||||
|
total += inputImageCost;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEstimatedCredits(Number(total.toFixed(2)));
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (creditCalculationData.length > 0 && countType) {
|
||||||
|
calculateEstimatedCredits();
|
||||||
|
}
|
||||||
|
}, [creditCalculationData, countType, videoDuration, videoResolution, taskDetail]);
|
||||||
|
|
||||||
// 组件卸载时清理定时器
|
// 组件卸载时清理定时器
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
@@ -1190,14 +1257,36 @@ function InitialInfo() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Tooltip
|
||||||
type="primary"
|
title={((user?.credits || 0) < estimatedCredits) ? '积分不足,请更换参数/充值积分' : ''}
|
||||||
style={{ width: '100%', borderRadius: 10, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', height: 36, fontWeight: 500, boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)' }}
|
placement="top"
|
||||||
onClick={() => { handleNextStep(step.id); }}
|
|
||||||
disabled={step.status !== 'completed'}
|
|
||||||
>
|
>
|
||||||
下一步:生成视频提示词
|
<Button
|
||||||
</Button>
|
type="primary"
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
borderRadius: 10,
|
||||||
|
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||||
|
border: 'none',
|
||||||
|
height: 36,
|
||||||
|
fontWeight: 500,
|
||||||
|
boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)'
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
if ((user?.credits || 0) < estimatedCredits) {
|
||||||
|
message.warning('积分不足,请更换参数/充值积分');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleNextStep(step.id);
|
||||||
|
}}
|
||||||
|
disabled={step.status !== 'completed' || ((user?.credits || 0) < estimatedCredits)}
|
||||||
|
>
|
||||||
|
下一步:生成视频提示词
|
||||||
|
<span style={{ color: '#fff', marginLeft: 8 }}>
|
||||||
|
预估积分:{estimatedCredits}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{/* 步骤4: 生成视频提示词 */}
|
{/* 步骤4: 生成视频提示词 */}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Button, Typography, Collapse, Space, Modal, Input, Table, message } from 'antd';
|
import { Button, Typography, Collapse, Space, Modal, Input, Table, message, Tooltip } from 'antd';
|
||||||
import { ArrowLeftOutlined, PlayCircleOutlined, CheckCircleOutlined, EditOutlined, DownloadOutlined, SettingOutlined, LayoutOutlined } from '@ant-design/icons';
|
import { ArrowLeftOutlined, PlayCircleOutlined, CheckCircleOutlined, EditOutlined, DownloadOutlined, SettingOutlined, LayoutOutlined } from '@ant-design/icons';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import { getShotReplicationList, removeDetail, removeone, removetwo, removethree, removefour, getEngine, updateShotImagePrompt, updateShotVideoPromptSchema } from '../api/index';
|
import { getShotReplicationList, removeDetail, removeone, removetwo, removethree, removefour, getEngine, updateShotImagePrompt, updateShotVideoPromptSchema, calculateCredits } from '../api/index';
|
||||||
|
import { useAuthStore } from '../store/useAuthStore';
|
||||||
import VideoPromptSchemaEditor from '../components/VideoPromptSchemaEditor';
|
import VideoPromptSchemaEditor from '../components/VideoPromptSchemaEditor';
|
||||||
import { validateVideoPromptSchemaByConfig } from '../utils/videoPromptSchema';
|
import { validateVideoPromptSchemaByConfig } from '../utils/videoPromptSchema';
|
||||||
import './css/InitialInfo.css';
|
import './css/InitialInfo.css';
|
||||||
@@ -24,8 +25,11 @@ const buildMediaUrl = (url: string): string => {
|
|||||||
function InitialInfo() {
|
function InitialInfo() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { creatID } = useParams<{ creatID: string }>();
|
const { creatID } = useParams<{ creatID: string }>();
|
||||||
|
const { user } = useAuthStore();
|
||||||
|
|
||||||
const [modalVisible, setModalVisible] = useState(false);
|
const [modalVisible, setModalVisible] = useState(false);
|
||||||
|
const [creditCalculationData, setCreditCalculationData] = useState<any[]>([]);
|
||||||
|
const [estimatedCredits, setEstimatedCredits] = useState<number>(0);
|
||||||
const [promptText, setPromptText] = useState('');
|
const [promptText, setPromptText] = useState('');
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const [currentType, setCurrentType] = useState<string>('image');
|
const [currentType, setCurrentType] = useState<string>('image');
|
||||||
@@ -128,7 +132,7 @@ function InitialInfo() {
|
|||||||
// 当apiSteps更新时,逆向遍历找到第一个已完成或失败的步骤并展开
|
// 当apiSteps更新时,逆向遍历找到第一个已完成或失败的步骤并展开
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const completedFailedKeys = new Set<string>();
|
const completedFailedKeys = new Set<string>();
|
||||||
|
|
||||||
activeKey.forEach(key => {
|
activeKey.forEach(key => {
|
||||||
const step = steps.find(s => String(s.childId) === key);
|
const step = steps.find(s => String(s.childId) === key);
|
||||||
if (step && (step.status === 'completed' || step.status === 'failed')) {
|
if (step && (step.status === 'completed' || step.status === 'failed')) {
|
||||||
@@ -190,12 +194,17 @@ function InitialInfo() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getEngine()
|
getEngine()
|
||||||
.then((data: any) => {
|
.then((data: any) => {
|
||||||
setEnginesele(data.engine || {});
|
const filteredVideoEngines = (data.engine?.video || []).filter((engine: any) => engine.supportsUniversalReference !== false);
|
||||||
|
const filteredImageEngines = (data.engine?.image || []).filter((engine: any) => engine.supportsUniversalReference !== false);
|
||||||
|
setEnginesele({
|
||||||
|
video: filteredVideoEngines,
|
||||||
|
image: filteredImageEngines,
|
||||||
|
});
|
||||||
// 默认选中第一个视频引擎
|
// 默认选中第一个视频引擎
|
||||||
if (data.engine?.video && data.engine.video.length > 0) {
|
if (filteredVideoEngines.length > 0) {
|
||||||
setCountType(data.engine.video[0].id);
|
setCountType(filteredVideoEngines[0].id);
|
||||||
// 设置默认引擎参数
|
// 设置默认引擎参数
|
||||||
const firstEngine = data.engine.video[0];
|
const firstEngine = filteredVideoEngines[0];
|
||||||
setEngineOptions({
|
setEngineOptions({
|
||||||
ratios: firstEngine.supportedRatios || ['16:9', '4:3', '1:1', '3:4', '9:16', '21:9'],
|
ratios: firstEngine.supportedRatios || ['16:9', '4:3', '1:1', '3:4', '9:16', '21:9'],
|
||||||
resolutions: firstEngine.supportedResolutions || ['480p', '720p', '1080p'],
|
resolutions: firstEngine.supportedResolutions || ['480p', '720p', '1080p'],
|
||||||
@@ -203,8 +212,8 @@ function InitialInfo() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 默认选中第一个图片引擎
|
// 默认选中第一个图片引擎
|
||||||
if (data.engine?.image && data.engine.image.length > 0) {
|
if (filteredImageEngines.length > 0) {
|
||||||
setImageEngineId(data.engine.image[0].id);
|
setImageEngineId(filteredImageEngines[0].id);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
@@ -275,6 +284,57 @@ function InitialInfo() {
|
|||||||
}
|
}
|
||||||
}, [steps]);
|
}, [steps]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
calculateCredits().then((data: any) => {
|
||||||
|
setCreditCalculationData(data);
|
||||||
|
}).catch(() => {});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const calculateEstimatedCredits = () => {
|
||||||
|
let config: any = {};
|
||||||
|
config = creditCalculationData.find((item: any) =>
|
||||||
|
item.modelConfigId === countType &&
|
||||||
|
item.genType === 'video' &&
|
||||||
|
item.resolution === videoResolution
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!config) {
|
||||||
|
config = {
|
||||||
|
perSecondCredits: 2,
|
||||||
|
baseCredits: 60,
|
||||||
|
ratio: 1.3,
|
||||||
|
inputVideoRatio: 1.3,
|
||||||
|
inputVideoBaseCredits: 0,
|
||||||
|
inputVideoPerSecondCredits: 15,
|
||||||
|
inputImageRatio: 1,
|
||||||
|
inputImageBaseCredits: 0,
|
||||||
|
inputImagePerImageCredits: 0.0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let total = (videoDuration * config.perSecondCredits + config.baseCredits) * config.ratio;
|
||||||
|
|
||||||
|
const inputVideoDuration = taskDetail?.videoGeneration?.inputMedia?.video?.duration || 0;
|
||||||
|
if (inputVideoDuration > 0) {
|
||||||
|
const inputVideoCost = ((config.inputVideoBaseCredits || 0) + (config.inputVideoPerSecondCredits || 0) * inputVideoDuration) * (config.inputVideoRatio || 1);
|
||||||
|
total += inputVideoCost;
|
||||||
|
}
|
||||||
|
|
||||||
|
const inputImageCount = taskDetail?.videoGeneration?.inputMedia?.image?.length || 0;
|
||||||
|
if (inputImageCount > 0) {
|
||||||
|
const inputImageCost = ((config.inputImageBaseCredits || 0) + (config.inputImagePerImageCredits || 0) * inputImageCount) * (config.inputImageRatio || 1);
|
||||||
|
total += inputImageCost;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEstimatedCredits(Number(total.toFixed(2)));
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (creditCalculationData.length > 0 && countType) {
|
||||||
|
calculateEstimatedCredits();
|
||||||
|
}
|
||||||
|
}, [creditCalculationData, countType, videoDuration, videoResolution, taskDetail]);
|
||||||
|
|
||||||
const handleOpenModal = (prompt?: any, type?: string, stepId?: string | number, schemaConfigSnapshot?: any) => {
|
const handleOpenModal = (prompt?: any, type?: string, stepId?: string | number, schemaConfigSnapshot?: any) => {
|
||||||
setCurrentType(type || 'image');
|
setCurrentType(type || 'image');
|
||||||
setEditingPromptStepId(stepId ? String(stepId) : '');
|
setEditingPromptStepId(stepId ? String(stepId) : '');
|
||||||
@@ -1188,14 +1248,36 @@ function InitialInfo() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Tooltip
|
||||||
type="primary"
|
title={((user?.credits || 0) < estimatedCredits) ? '积分不足,请更换参数/充值积分' : ''}
|
||||||
style={{ width: '100%', borderRadius: 10, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', height: 36, fontWeight: 500, boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)' }}
|
placement="top"
|
||||||
onClick={() => { handleNextStep(step.id); }}
|
|
||||||
disabled={step.status !== 'completed'}
|
|
||||||
>
|
>
|
||||||
下一步:生成视频提示词
|
<Button
|
||||||
</Button>
|
type="primary"
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
borderRadius: 10,
|
||||||
|
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
|
||||||
|
border: 'none',
|
||||||
|
height: 36,
|
||||||
|
fontWeight: 500,
|
||||||
|
boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)'
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
if ((user?.credits || 0) < estimatedCredits) {
|
||||||
|
message.warning('积分不足,请更换参数/充值积分');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleNextStep(step.id);
|
||||||
|
}}
|
||||||
|
disabled={step.status !== 'completed' || ((user?.credits || 0) < estimatedCredits)}
|
||||||
|
>
|
||||||
|
下一步:生成视频提示词
|
||||||
|
<span style={{ color: '#fff', marginLeft: 8 }}>
|
||||||
|
预估积分:{estimatedCredits}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{/* 步骤4: 生成视频提示词 */}
|
{/* 步骤4: 生成视频提示词 */}
|
||||||
|
|||||||
Reference in New Issue
Block a user