爆款开头复刻

This commit is contained in:
孙佳艺
2026-06-08 18:19:05 +08:00
parent f595e8dc78
commit 2a8ad389ea
6 changed files with 378 additions and 32 deletions
+11
View File
@@ -10,9 +10,15 @@ import RecordsPage from './pages/RecordsPage';
import CreditsPage from './pages/CreditsPage';
import GenerateConver from './pages/GenerateConver';
import InitialReplication from './pages/InitialReplication';
import InitialInfo from './pages/InitialInfo';
import RemoveLens from './pages/RemoveLens';
import GeneratedRecord from './pages/GeneratedRecord';
import AuthorizationPage from './pages/AuthorizationPage';
import { useAuthStore } from './store/useAuthStore';
const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
@@ -88,10 +94,15 @@ const App = () => {
<Route path="credits" element={<CreditsPage />} />
<Route path="conversation" element={<GenerateConver />} />
<Route path="initial" element={<InitialReplication />} />
<Route path="initial/:creatID/initialinfo" element={<InitialInfo />} />
<Route path="removelens" element={<RemoveLens />} />
<Route path="generated" element={<GeneratedRecord />} />
<Route path="authorization" element={<AuthorizationPage />} />
</Route>
<Route path="*" element={<Navigate to="/projects" replace />} />
</Routes>
+23 -11
View File
@@ -608,12 +608,16 @@ const AIChatPage: React.FC = () => {
// 视频参数(仅视频模式时添加)
...(mediaType === 'video' && {
duration: videoDuration,
ratio: videoAspectRatio,
aspect_ratio: videoAspectRatio,
resolution: videoResolution,
}),
};
// 设置加载状态
setLoading(true);
@@ -1132,6 +1136,7 @@ const AIChatPage: React.FC = () => {
<DeleteOutlined style={{ fontSize: 12 }} />
</button>
</Popconfirm>
</div>
{/* 文本内容 */}
@@ -1162,30 +1167,37 @@ const AIChatPage: React.FC = () => {
{/* 根据 status 显示不同内容 */}
{/* 生成中 - 显示加载动画 */}
{msg.status === 'generating' && (
<div style={{ height: 200, marginBottom: 10, marginTop: 12, borderRadius: 8, overflow: 'hidden', position: 'relative', backgroundColor: '#f1f5f9' }}>
<div style={{
height: 200,
marginBottom: 10,
marginTop: 12,
borderRadius: 8,
overflow: 'hidden',
position: 'relative',
background: 'linear-gradient(135deg, #e0e7ff 0%, #f3e8ff 50%, #e0e7ff 100%)',
}}>
<div style={{
position: 'absolute',
inset: 0,
top: 12,
left: 12,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: 16
gap: 8,
}}>
<div style={{
width: 48,
height: 48,
border: '3px solid #e2e8f0',
width: 24,
height: 24,
border: '2px solid #c7d2fe',
borderTopColor: '#6366f1',
borderRadius: '50%',
animation: 'spin 1s linear infinite'
}} />
<span style={{ fontSize: 14, color: '#64748b' }}>...</span>
<span style={{ fontSize: 13, color: '#6366f1' }}>...</span>
</div>
<div style={{
position: 'absolute',
inset: 0,
background: 'linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent)',
background: 'linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.3) 50%, transparent 100%)',
animation: 'shimmer 2s infinite'
}} />
</div>
+297
View File
@@ -0,0 +1,297 @@
import React, { useState, useEffect } from 'react';
import { Button, Typography, Collapse, Space, Modal, Input, Table } from 'antd';
import { ArrowLeftOutlined, PlayCircleOutlined, CheckCircleOutlined, EditOutlined, DownloadOutlined } from '@ant-design/icons';
import { useNavigate, useParams } from 'react-router-dom';
import './css/InitialInfo.css';
const { Title, Text } = Typography;
const { TextArea } = Input;
function InitialInfo() {
const navigate = useNavigate();
const { creatID } = useParams<{ creatID: string }>();
const [modalVisible, setModalVisible] = useState(false);
const [promptText, setPromptText] = useState('');
const [isModalOpen, setIsModalOpen] = useState(false);
// 创作记录表格数据
const [tableData, setTableData] = useState<any[]>([
{
id: 1,
image: 'https://neeko-copilot.bytedance.net/api/text_to_image?prompt=product%20image%20red%20gift%20box%20with%20hearts&image_size=square',
originalName: '进圈',
productName: '他趣',
status: '视频成功',
createTime: '2026-05-14 17:49:20',
},
{
id: 2,
image: 'https://neeko-copilot.bytedance.net/api/text_to_image?prompt=luxury%20perfume%20bottle%20golden%20elegant&image_size=square',
originalName: '香水',
productName: '面霜',
status: '视频提示词成功',
createTime: '2026-05-08 08:57:46',
},
]);
useEffect(() => {
console.log('路由参数 creatID:', creatID);
}, [creatID]);
const handleOpenModal = () => {
console.log('打开弹窗');
setPromptText('室内居家卧室场景,严格复刻原背景:人物后方是带黑色铆钉包边的浅灰色拉扣软包床头,床头前摆放蓬松白色枕头,保留柔和暖调居家氛围,禁止篡改背景元素。画面中心为上半身入镜的女性,身穿浅灰色V领长袖上衣,脸部全程用白色涂鸦完全打码,禁止生成清晰人脸;颈部佩戴目标华丽项链,替代原有细项链,佩戴位置与原项链一致,贴合颈部肌肤,光影与场景柔和光源完全统一,无半透明合成痕迹。目标项链为玫瑰金基底,镶嵌圆润白色珍珠与碎钻,中心是水滴形主钻,下方垂挂同形态水滴钻吊坠,设计精致华丽,项链为画面视觉焦点,尺寸符合颈部佩戴的合理比例。主光源来自画面面前侧偏上方,光线柔和自然,阴影清淡真实,禁止出现AI合成假阴影;整体为低饱和暖调,写实柔和质感,拍摄为平视角度,视觉优先级:颈部项链>人物上半身>背景床头与枕头。禁止出现未打码人脸,禁止项链设计偏差、佩戴错位,禁止光影色调偏离柔和暖调居家感,画面无多余字幕。');
setModalVisible(true);
};
const handleConfirm = () => {
setModalVisible(false);
console.log('修改后的提示词:', promptText);
};
const steps = [
{ id: 1, title: '原始素材', status: 'done', description: '上传原始视频素材' },
{ id: 2, title: '生成提示词', status: 'done', description: '根据素材生成描述词' },
{ id: 3, title: '生成产品融合图', status: 'done', description: '生成产品与场景融合图' },
{ id: 4, title: '生成视频提示词', status: 'done', description: '生成视频生成提示词' },
{ id: 5, title: '生成最终视频', status: 'active', description: '合成最终视频' },
];
return (
<React.Fragment>
<div style={{ height: '94vh', background: '#f8fafc' }}>
<div style={{ height: 'calc(94vh)', padding: 24, }}>
<div style={{ height: '100%', display: 'flex', justifyContent: 'space-between', gap: '2%' }}>
<div style={{ width: '70%', background: '#fff', borderRadius: 12, overflowY: 'auto' }}>
<div style={{ borderBottom: '1px solid #e2e8f0', width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '14px', boxSizing: 'border-box' }}>
<Button
type="text"
icon={<ArrowLeftOutlined />}
onClick={() => navigate(-1)}
style={{ color: '#64748b' }}
>
</Button>
<h3 style={{ margin: 0 }}>
-
</h3>
<Button type="primary" ghost style={{ borderRadius: 6, padding: '4px 12px', fontSize: 12, borderColor: '#6366f1', color: '#6366f1' }} onClick={() => setIsModalOpen(true)}>
</Button>
</div>
<div className="material_box">
<div style={{ display: 'flex' }}>
<div>
<span></span>
<div className="medio_box">
<video controls src={null} />
</div>
</div>
<div className="product_img">
<span></span>
<div className="medio_box">
<img src="https://imagecdn.gaopinimages.com/image-preview/133141727501.jpg_w.webp" alt="" />
</div>
</div>
</div>
<div>
<div>
<span></span>
<div className="medio_box">
<img src="https://imagecdn.gaopinimages.com/image-preview/133141727501.jpg_w.webp" alt="" />
</div>
</div>
</div>
<div>
<div>
<span></span>
<div className="medio_box">
<video controls src={null} />
</div>
</div>
</div>
</div>
</div>
<div style={{ width: '28%', minWidth: '400px', background: '#fff', borderRadius: 12, overflowY: 'auto' }}>
<div style={{padding:'14px'}}> </div>
<Collapse
defaultActiveKey={['2']}
ghost
bordered={false}
style={{ background: 'transparent' }}
expandIconPosition="end"
items={steps.map((step) => ({
key: String(step.id),
label: (
<div style={{ display: 'flex', alignItems: 'center', gap: 8, width: '100%', justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: 16, height: 16 }}>
{step.status === 'done' ? (
<CheckCircleOutlined style={{ fontSize: 16, color: '#22c55e' }} />
) : step.status === 'active' ? (
<div style={{ width: 8, height: 8, borderRadius: '50%', background: '#6366f1', animation: 'spinSlow 1s linear infinite' }} />
) : (
<span style={{ fontSize: 12, color: '#94a3b8' }}>{step.id}</span>
)}
</div>
<span style={{ fontSize: 14, color: step.status === 'active' ? '#6366f1' : '#1e293b', fontWeight: 500 }}>
{step.title}
</span>
{step.status === 'active' && (
<span style={{ fontSize: 12, color: '#6366f1', marginLeft: 8 }}></span>
)}
</div>
</div>
),
children: (
<div style={{ padding: '16px', background: '#f8fafc', borderRadius: 8, marginTop: 8 }}>
{step.id === 1 && <div>12312</div>}
{step.id === 2 && (
<>
<Text style={{ color: '#475569', fontSize: 13, lineHeight: 1.8, whiteSpace: 'pre-wrap' }}>
APP APP 线 APP "脱单交友" APP
</Text>
<Space style={{ marginTop: 16, gap: 12, width: '100%' }}>
<Button
type="default"
icon={<EditOutlined />}
onClick={handleOpenModal}
style={{ flex: 1, borderRadius: 8, borderColor: '#6366f1', color: '#6366f1', height: 36 }}
>
</Button>
<Button type="primary" style={{ flex: 1, borderRadius: 8, background: '#6366f1', borderColor: '#6366f1', height: 36 }}>
</Button>
</Space>
</>
)}
{step.id === 5 && (
<>
<div style={{ borderRadius: 8, overflow: 'hidden', marginBottom: 16, position: 'relative' }}>
<img
src="https://neeko-copilot.bytedance.net/api/text2image?prompt=cute%20orange%20cartoon%20character%20with%20text%20overlay%20on%20pink%20background&image_size=landscape_4_3"
alt="最终视频"
style={{ width: '100%', height: 180, objectFit: 'cover' }}
/>
<div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', display: 'flex', alignItems: 'center', justifyContent: 'center', width: 48, height: 48, background: 'rgba(255,255,255,0.9)', borderRadius: '50%' }}>
<PlayCircleOutlined style={{ fontSize: 24, color: '#6366f1', marginLeft: 2 }} />
</div>
</div>
<Space style={{ width: '100%', gap: 12 }}>
<Button type="default" icon={<EditOutlined />} style={{ flex: 1, borderRadius: 8, borderColor: '#6366f1', color: '#6366f1', height: 36 }}>
</Button>
<Button type="primary" icon={<DownloadOutlined />} style={{ flex: 1, borderRadius: 8, background: '#6366f1', borderColor: '#6366f1', height: 36 }}>
</Button>
</Space>
</>
)}
</div>
),
}))}
/>
</div>
</div>
</div>
</div>
<Modal
title="修改提示词"
open={modalVisible}
onCancel={() => setModalVisible(false)}
footer={[
<Button key="cancel" onClick={() => setModalVisible(false)}></Button>,
<Button key="confirm" type="primary" onClick={handleConfirm}></Button>,
]}
width={800}
>
<TextArea
value={promptText}
onChange={(e) => setPromptText(e.target.value)}
rows={8}
style={{ width: '100%', height: 300 }}
placeholder="请输入提示词"
/>
</Modal>
{/* 创作记录弹窗 */}
<Modal
title="创作记录"
open={isModalOpen}
onCancel={() => setIsModalOpen(false)}
width={800}
footer={null}
style={{ borderRadius: 12 }}
>
{/* 搜索区域 */}
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 16 }}>
<Input
placeholder="搜索产品名称"
style={{ width: 200, borderRadius: 6, marginRight: 8 }}
/>
<Button type="primary" style={{ borderRadius: 6 }}>
</Button>
</div>
{/* 表格 */}
<Table
columns={[
{
title: '产品图片',
dataIndex: 'image',
key: 'image',
width: 90,
render: (image: string) => (
<img
src={image}
alt="产品图片"
style={{ width: 50, height: 50, objectFit: 'cover', borderRadius: 6 }}
/>
),
},
{
title: '原产品名称',
dataIndex: 'originalName',
key: 'originalName',
},
{
title: '产品名称',
dataIndex: 'productName',
key: 'productName',
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
},
{
title: '操作',
key: 'action',
render: (_, record) => (
<button
onClick={() => navigate(`/initial/${record.id}/initialinfo`)}
style={{ color: '#6366f1', textDecoration: 'none', fontSize: 12, border: 'none', background: 'none', cursor: 'pointer' }}
>
</button>
),
},
]}
dataSource={tableData}
rowKey="id"
pagination={false}
style={{ fontSize: 13 }}
/>
</Modal>
</React.Fragment>
);
}
export default InitialInfo;
+26 -21
View File
@@ -15,31 +15,33 @@ import {
VideoCameraOutlined,
PictureOutlined,
} from '@ant-design/icons';
import { useNavigate } from 'react-router-dom';
const { Header, Content } = Layout;
const { TextArea } = Input;
const GenerateConver: React.FC = () => {
const navigate = useNavigate();
const [tableData, setTableData] = useState<any[]>(
// [
// {
// id: 1,
// image: 'https://neeko-copilot.bytedance.net/api/text_to_image?prompt=product%20image%20red%20gift%20box%20with%20hearts&image_size=square',
// originalName: '进圈',
// productName: '他趣',
// status: '视频成功',
// createTime: '2026-05-14 17:49:20',
// },
// {
// id: 2,
// image: 'https://neeko-copilot.bytedance.net/api/text_to_image?prompt=luxury%20perfume%20bottle%20golden%20elegant&image_size=square',
// originalName: '香水',
// productName: '面霜',
// status: '视频提示词成功',
// createTime: '2026-05-08 08:57:46',
// },
// ]
[
{
id: 1,
image: 'https://neeko-copilot.bytedance.net/api/text_to_image?prompt=product%20image%20red%20gift%20box%20with%20hearts&image_size=square',
originalName: '进圈',
productName: '他趣',
status: '视频成功',
createTime: '2026-05-14 17:49:20',
},
{
id: 2,
image: 'https://neeko-copilot.bytedance.net/api/text_to_image?prompt=luxury%20perfume%20bottle%20golden%20elegant&image_size=square',
originalName: '香水',
productName: '面霜',
status: '视频提示词成功',
createTime: '2026-05-08 08:57:46',
},
]
);
@@ -517,10 +519,13 @@ const GenerateConver: React.FC = () => {
{
title: '操作',
key: 'action',
render: () => (
<a href="#" style={{ color: '#6366f1', textDecoration: 'none', fontSize: 12 }}>
render: (_, record) => (
<button
onClick={() => navigate(`/initial/${record.id}/initialinfo`)}
style={{ color: '#6366f1', textDecoration: 'none', fontSize: 12, border: 'none', background: 'none', cursor: 'pointer' }}
>
</a>
</button>
),
},
]}
+3
View File
@@ -154,6 +154,9 @@ const RecordsPage: React.FC = () => {
const isGenerating = generating[record.id];
const isExpanded = expandedId === record.id;
const type: any = record.genType || 'image';
return (
<div key={record.id} className="animate-slideInCard" style={{ animationDelay: `${i * 0.04}s` }}>
{/* Record summary row */}
@@ -0,0 +1,18 @@
*{
box-sizing: border-box;
}
.material_box>div{
margin: 20px 10px;
}
.medio_box {
width: 280px;
height: 150px;
margin-top: 6px;
}
.medio_box img ,.medio_box video {
width: 100%;
height: 100%;
}
.product_img{
margin-left: 20px;
}