This commit is contained in:
2026-06-23 15:30:11 +08:00
16 changed files with 786 additions and 652 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -28,7 +28,7 @@
} }
})(); })();
</script> </script>
<script type="module" crossorigin src="/assets/index-90jrBVA8.js"></script> <script type="module" crossorigin src="/assets/index-ZwwuCCw2.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-eLRg4pQk.css"> <link rel="stylesheet" crossorigin href="/assets/index-eLRg4pQk.css">
</head> </head>
<body> <body>
+6 -2
View File
@@ -367,8 +367,12 @@ export async function generateReplication(params: any): Promise<any> {
return api.post('/hot-opening-replications/tasks', params); return api.post('/hot-opening-replications/tasks', params);
} }
// 获取爆款开头复刻任务列表 // 获取爆款开头复刻任务列表
export async function getReplicationList(page: number,page_size: number): Promise<any[]> { export async function getReplicationList(page: number, page_size: number, keyword?: string): Promise<any[]> {
return api.get(`/hot-opening-replications/tasks?page=${page}&page_size=${page_size}`); let url = `/hot-opening-replications/tasks?page=${page}&page_size=${page_size}`;
if (keyword) {
url += `&keyword=${encodeURIComponent(keyword)}`;
}
return api.get(url);
} }
// 获取爆款开头复刻任务详情 // 获取爆款开头复刻任务详情
export async function getReplicationDetail(id: string): Promise<any> { export async function getReplicationDetail(id: string): Promise<any> {
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB

@@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Alert, Input, Space, Tag, Typography } from 'antd'; import { Alert, Input, Radio, Space, Tag, Typography } from 'antd';
import type { import type {
JsonValue, JsonValue,
VideoPromptSchemaFieldConfig, VideoPromptSchemaFieldConfig,
@@ -54,10 +54,24 @@ function EditableInput({ value, maxLength, onChange }: { value: JsonValue; maxLe
return <Input {...commonProps} />; return <Input {...commonProps} />;
} }
function renderFieldTag(field: VideoPromptSchemaFieldConfig) { function BooleanRadio({ value, onChange }: { value: JsonValue; onChange: (nextValue: JsonValue) => void }) {
return field.editable ? <Tag color="processing"></Tag> : <Tag color="default"></Tag>; const boolValue = value === true || value === 'true';
return (
<Radio.Group
value={boolValue}
onChange={(e) => onChange(e.target.value)}
style={{ display: 'flex', gap: 24 }}
>
<Radio value={true} style={{ fontSize: 14, color: '#475569' }}></Radio>
<Radio value={false} style={{ fontSize: 14, color: '#475569' }}></Radio>
</Radio.Group>
);
} }
// function renderFieldTag(field: VideoPromptSchemaFieldConfig) {
// return field.editable ? <Tag color="processing">可编辑</Tag> : <Tag color="default">只读</Tag>;
// }
const VideoPromptSchemaEditor: React.FC<VideoPromptSchemaEditorProps> = ({ value, schemaConfigSnapshot, onChange }) => { const VideoPromptSchemaEditor: React.FC<VideoPromptSchemaEditorProps> = ({ value, schemaConfigSnapshot, onChange }) => {
const safeValue = isRecord(value) ? value : {}; const safeValue = isRecord(value) ? value : {};
const sections = normalizeSchemaSections(schemaConfigSnapshot); const sections = normalizeSchemaSections(schemaConfigSnapshot);
@@ -93,21 +107,28 @@ const VideoPromptSchemaEditor: React.FC<VideoPromptSchemaEditorProps> = ({ value
<div key={section.key} style={{ marginBottom: 18 }}> <div key={section.key} style={{ marginBottom: 18 }}>
<Space style={{ marginBottom: 8 }}> <Space style={{ marginBottom: 8 }}>
<Text strong style={{ color: '#334155', fontSize: 13 }}>{section.label}</Text> <Text strong style={{ color: '#334155', fontSize: 13 }}>{section.label}</Text>
{renderFieldTag(section)} {/* {renderFieldTag(section)} */}
</Space> </Space>
<div style={{ borderLeft: '3px solid #6366f1', paddingLeft: 12, marginLeft: 4 }}> <div style={{ borderLeft: '3px solid #6366f1', paddingLeft: 12, marginLeft: 4 }}>
{fields.map((field) => ( {fields.map((field) => (
<div key={field.key} style={{ marginBottom: 12 }}> <div key={field.key} style={{ marginBottom: 12 }}>
<Space style={{ marginBottom: 4 }}> <Space style={{ marginBottom: 4 }}>
<Text style={{ color: '#64748b', fontSize: 12 }}>{field.label}</Text> <Text style={{ color: '#64748b', fontSize: 12 }}>{field.label}</Text>
{renderFieldTag(field)} {/* {renderFieldTag(field)} */}
</Space> </Space>
{field.editable ? ( {field.editable ? (
<EditableInput field.type === 'boolean' ? (
value={sectionValue[field.key]} <BooleanRadio
maxLength={field.maxLength} value={sectionValue[field.key]}
onChange={(nextText) => onChange(setObjectField(safeValue, section.key, field.key, nextText))} onChange={(nextValue) => onChange(setObjectField(safeValue, section.key, field.key, nextValue))}
/> />
) : (
<EditableInput
value={sectionValue[field.key]}
maxLength={field.maxLength}
onChange={(nextText) => onChange(setObjectField(safeValue, section.key, field.key, nextText))}
/>
)
) : ( ) : (
<ReadonlyBlock value={sectionValue[field.key]} /> <ReadonlyBlock value={sectionValue[field.key]} />
)} )}
@@ -142,14 +163,21 @@ const VideoPromptSchemaEditor: React.FC<VideoPromptSchemaEditorProps> = ({ value
<div key={field.key} style={{ marginBottom: 10 }}> <div key={field.key} style={{ marginBottom: 10 }}>
<Space style={{ marginBottom: 4 }}> <Space style={{ marginBottom: 4 }}>
<Text style={{ color: '#64748b', fontSize: 12 }}>{field.label}</Text> <Text style={{ color: '#64748b', fontSize: 12 }}>{field.label}</Text>
{renderFieldTag(field)} {/* {renderFieldTag(field)} */}
</Space> </Space>
{field.editable ? ( {field.editable ? (
<EditableInput field.type === 'boolean' ? (
value={row[field.key]} <BooleanRadio
maxLength={field.maxLength} value={row[field.key]}
onChange={(nextText) => onChange(setArrayObjectField(safeValue, section.key, index, field.key, nextText))} onChange={(nextValue) => onChange(setArrayObjectField(safeValue, section.key, index, field.key, nextValue))}
/> />
) : (
<EditableInput
value={row[field.key]}
maxLength={field.maxLength}
onChange={(nextText) => onChange(setArrayObjectField(safeValue, section.key, index, field.key, nextText))}
/>
)
) : ( ) : (
<ReadonlyBlock value={row[field.key]} /> <ReadonlyBlock value={row[field.key]} />
)} )}
@@ -170,7 +198,7 @@ const VideoPromptSchemaEditor: React.FC<VideoPromptSchemaEditorProps> = ({ value
<div key={section.key} style={{ marginBottom: 18 }}> <div key={section.key} style={{ marginBottom: 18 }}>
<Space style={{ marginBottom: 8 }}> <Space style={{ marginBottom: 8 }}>
<Text strong style={{ color: '#334155', fontSize: 13 }}>{section.label}</Text> <Text strong style={{ color: '#334155', fontSize: 13 }}>{section.label}</Text>
{renderFieldTag(section)} {/* {renderFieldTag(section)} */}
</Space> </Space>
{section.editable ? ( {section.editable ? (
<EditableInput <EditableInput
@@ -191,13 +219,13 @@ const VideoPromptSchemaEditor: React.FC<VideoPromptSchemaEditorProps> = ({ value
return ( return (
<div> <div>
<Alert {/* <Alert
type="info" type="info"
showIcon showIcon
style={{ marginBottom: 14 }} style={{ marginBottom: 14 }}
message="仅可修改后台 Schema 配置允许编辑的字段" message="仅可修改后台 Schema 配置允许编辑的字段"
description="视频规格、时间段、流程条目数量、输出规格、质量控制、合规控制等锁定内容由服务端最终校验。" description="视频规格、时间段、流程条目数量、输出规格、质量控制、合规控制等锁定内容由服务端最终校验。"
/> /> */}
{sections.map((section) => { {sections.map((section) => {
if (!(section.key in safeValue)) return null; if (!(section.key in safeValue)) return null;
if (section.type === 'object') return renderObjectSection(section); if (section.type === 'object') return renderObjectSection(section);
@@ -1,6 +1,6 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Button, Modal, Slider, Spin, Typography } from 'antd'; import { Button, Modal, Slider, Spin, Typography } from 'antd';
import { PauseOutlined, PlayCircleFilled } from '@ant-design/icons'; import { PauseCircleFilled, PlayCircleFilled } from '@ant-design/icons';
const { Text } = Typography; const { Text } = Typography;
@@ -469,16 +469,29 @@ const VideoTrimPicker: React.FC<VideoTrimPickerProps> = ({
width={980} width={980}
destroyOnHidden destroyOnHidden
footer={[ footer={[
<Button key="cancel" onClick={onCancel} disabled={loading}> <Button key="cancel" onClick={onCancel} disabled={loading} style={{ borderRadius: 8, border: '1px solid rgba(99, 102, 241, 0.2)', color: '#6366f1' }}>
</Button>, </Button>,
<Button key="confirm" type="primary" onClick={handleConfirm} loading={loading} disabled={loading || disabledByDuration || !integerDuration}> <Button key="confirm" type="primary" onClick={handleConfirm} loading={loading} disabled={loading || disabledByDuration || !integerDuration} style={{ borderRadius: 8, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)' }}>
</Button>, </Button>,
]} ]}
> >
<div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}> <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
<div style={{ display: 'flex', justifyContent: 'center', background: '#fff' }}> <style>{`
.ant-slider-handle::after {
height: 80px !important;
width: 10px !important;
border-radius: 10px !important;
top: 95% !important;
transform: translateY(-50%) !important;
}
.ant-slider-handle {
height: 80px !important;
margin-top: -37px !important;
}
`}</style>
<div style={{ display: 'flex', justifyContent: 'center', background: '#fff', borderRadius: 16, overflow: 'hidden' }}>
<video <video
ref={videoRef} ref={videoRef}
src={videoUrl} src={videoUrl}
@@ -497,26 +510,26 @@ const VideoTrimPicker: React.FC<VideoTrimPickerProps> = ({
}} }}
onTimeUpdate={handleTimeUpdate} onTimeUpdate={handleTimeUpdate}
onPause={() => setPlaying(false)} onPause={() => setPlaying(false)}
style={{ width: '100%', maxHeight: 420, objectFit: 'contain', background: '#111', borderRadius: 8 }} style={{ maxHeight: 320, objectFit: 'contain', background: '#111' }}
/> />
</div> </div>
<div <div
style={{ style={{
borderRadius: 24, borderRadius: 20,
background: '#f3f6ff', background: '#f0f5ff',
padding: '26px 34px 18px', padding: '20px 24px',
}} }}
> >
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 28, marginBottom: 22 }}> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 16, marginBottom: 18 }}>
<Button <Button
type="text" type="text"
icon={playing ? <PauseOutlined /> : <PlayCircleFilled />} icon={playing ? <PauseCircleFilled /> : <PlayCircleFilled />}
onClick={handlePlaySelected} onClick={handlePlaySelected}
disabled={!integerDuration || disabledByDuration} disabled={!integerDuration || disabledByDuration}
style={{ fontSize: 22, color: '#111' }} style={{ fontSize: 20, color: '#1e293b', padding: 0 }}
/> />
<span style={{ fontSize: 26, color: '#111', letterSpacing: 1 }}> <span style={{ fontSize: 18, color: '#1e293b', fontFamily: 'monospace' }}>
{formatTime(currentTime)} / {formatTime(integerDuration)} {formatTime(currentTime)} / {formatTime(integerDuration)}
</span> </span>
</div> </div>
@@ -528,8 +541,9 @@ const VideoTrimPicker: React.FC<VideoTrimPickerProps> = ({
gridTemplateColumns: `repeat(${Math.max(frames.length, 1)}, minmax(42px, 1fr))`, gridTemplateColumns: `repeat(${Math.max(frames.length, 1)}, minmax(42px, 1fr))`,
height: 86, height: 86,
overflow: 'hidden', overflow: 'hidden',
borderRadius: 10, borderRadius: 12,
background: '#dbe2ff', background: '#fff',
position: 'relative',
}} }}
> >
{frameLoading && frames.length === 0 ? ( {frameLoading && frames.length === 0 ? (
@@ -549,7 +563,7 @@ const VideoTrimPicker: React.FC<VideoTrimPickerProps> = ({
height: 86, height: 86,
border: 'none', border: 'none',
padding: 0, padding: 0,
background: '#eef2ff', background: 'transparent',
overflow: 'hidden', overflow: 'hidden',
cursor: disabledByDuration ? 'not-allowed' : 'pointer', cursor: disabledByDuration ? 'not-allowed' : 'pointer',
position: 'relative', position: 'relative',
@@ -562,7 +576,7 @@ const VideoTrimPicker: React.FC<VideoTrimPickerProps> = ({
<Spin size="small" /> <Spin size="small" />
</div> </div>
) : ( ) : (
<div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', color: '#64748b', fontSize: 12, background: '#eef2ff' }}> <div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', color: '#64748b', fontSize: 12, background: '#fff' }}>
<span>{frame.second}s</span> <span>{frame.second}s</span>
<span style={{ fontSize: 11 }}></span> <span style={{ fontSize: 11 }}></span>
</div> </div>
@@ -586,9 +600,10 @@ const VideoTrimPicker: React.FC<VideoTrimPickerProps> = ({
</div> </div>
)} )}
</div> </div>
<div style={{ marginTop: 0, padding: '0 8px 0' }}> <div style={{ position: 'absolute', top: -6, left: 10, right: 0, padding: '0 8px 0' }}>
<Slider <Slider
range range
min={0} min={0}
@@ -601,38 +616,35 @@ const VideoTrimPicker: React.FC<VideoTrimPickerProps> = ({
disabled={!integerDuration || disabledByDuration} disabled={!integerDuration || disabledByDuration}
styles={{ styles={{
track: { track: {
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', background: '#6969dd63',
height: 6, height: 70,
borderRadius: 3, borderRadius: 3,
margin: '0 ',
}, },
rail: { rail: {
background: '#e2e8f0', // background: '#0e447e70',
height: 6, height: 70,
borderRadius: 3, borderRadius: 3,
}, },
handle: { handle: {
width: 18, width: 20,
height: 18, height: 70,
marginTop: 0, marginTop: 0,
// backgroundColor: '#fff', // backgroundColor: '#fff',
// border: '3px solid #6366f1', // border: '2px solid #6366f1',
// boxShadow: '0 2px 8px rgba(99, 102, 241, 0.3)', borderRadius: '50%',
transition: 'all 0.2s cubic-bezier(0.4, 0, 0.2, 1)',
}, },
}} }}
/> />
</div> </div>
</div> </div>
<div style={{ marginTop: 30, textAlign: 'center', color: '#64748b', fontSize: 16 }}> <div style={{ marginTop: 16, textAlign: 'center', color: '#64748b', fontSize: 14 }}>
{selectedDuration}s {selectedDuration}s
<span style={{ marginLeft: 12, fontSize: 13, color: '#94a3b8' }}>
{minDuration}s {maxDuration}s
</span>
</div> </div>
{localError && ( {localError && (
<div style={{ marginTop: 12, textAlign: 'center', color: '#ef4444', fontSize: 13 }}> <div style={{ marginTop: 12, padding: '10px 14px', borderRadius: 8, background: 'rgba(239, 68, 68, 0.08)', border: '1px solid rgba(239, 68, 68, 0.15)', color: '#ef4444', fontSize: 13, textAlign: 'center' }}>
{localError} {localError}
</div> </div>
)} )}
+158 -103
View File
@@ -15,6 +15,10 @@ import {
Modal, Modal,
} from 'antd'; } from 'antd';
import bg1 from '../assets/bg1.png';
import bg2 from '../assets/bg2.png';
import { import {
getParameters, createGenerationTask, getgen_list, getEngine, uploadImage, getParameters, createGenerationTask, getgen_list, getEngine, uploadImage,
uploadVideo, getCreditRatios, deleteHistory, calculateCredits uploadVideo, getCreditRatios, deleteHistory, calculateCredits
@@ -830,7 +834,16 @@ const AIChatPage: React.FC = () => {
// ==================== 渲染 ==================== // ==================== 渲染 ====================
return ( return (
<Layout style={{ height: 'calc(100vh - 90px)', overflow: 'auto' }}> <Layout style={{
margin: '-24px -32px -32px',
borderRadius: 20,
height: 'calc(100vh - 34px)',
background: 'linear-gradient(135deg, #f8fafc 0%, #f0f4ff 50%, #faf5ff 100%)',
backgroundImage: `url(${bg2})`,
backgroundRepeat: 'no-repeat',
backgroundSize: '100% 100%',
backgroundPosition: 'center',
}}>
{/* 左侧边栏 - 对话列表(已隐藏,保留代码) */} {/* 左侧边栏 - 对话列表(已隐藏,保留代码) */}
{false && ( {false && (
<Sider <Sider
@@ -839,8 +852,9 @@ const AIChatPage: React.FC = () => {
collapsed={collapsed} collapsed={collapsed}
width={220} width={220}
style={{ style={{
background: '#fff', background: 'rgba(255,255,255,0.7)',
borderRight: '1px solid #f0f0f0', backdropFilter: 'blur(20px)',
borderRight: '1px solid rgba(99, 102, 241, 0.08)',
}} }}
> >
<div style={{ padding: 12 }}> <div style={{ padding: 12 }}>
@@ -917,38 +931,46 @@ const AIChatPage: React.FC = () => {
)} )}
{/* 主内容区 */} {/* 主内容区 */}
<Layout style={{ flex: 1, background: '#ffffffff' }}> <Layout style={{ display: 'flex', flex: 1, background: 'transparent' }}>
{/* 头部 - 显示对话标题和模型信息 */} {/* 头部 - 显示对话标题和模型信息 */}
<Header <Header
style={{ style={{
background: '#fff', borderRadius: '20px 20px 0 0',
// padding: '0 24px',
borderBottom: '1px solid #f0f0f0', background: 'rgba(255,255,255,0.1)',
backdropFilter: 'blur(20px)',
borderBottom: '1px solid rgba(99, 102, 241, 0.08)',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-between', justifyContent: 'space-between',
padding: '0 24px',
}} }}
> >
<div> <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<Text strong style={{ fontSize: 16 }}> <div style={{ width: 36, height: 36, borderRadius: 12, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
{'开启创作'} <RobotOutlined style={{ fontSize: 18, color: '#fff' }} />
</Text> </div>
<div>
<Text strong style={{ fontSize: 16, background: 'linear-gradient(90deg, #6366f1, #8b5cf6)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>
</Text>
{/* <div style={{ width: 32, height: 2, background: 'linear-gradient(90deg, #6366f1, #8b5cf6)', borderRadius: 1, marginTop: 4 }} /> */}
</div>
</div> </div>
{/* <div style={{ display: 'flex', gap: 16, fontSize: 12, color: '#999' }}>
<span>模型: Gemini 3 Pro</span>
<span>比例: 9:16</span>
<span style={{ color: '#6366f1', fontWeight: 500 }}>消耗 15 积分</span>
</div> */}
</Header> </Header>
{/* 消息区域 */} {/* 消息区域 */}
<Content <Content
style={{ style={{
flex: 1,
margin: 0, margin: 0,
padding: 24,
boxSizing: 'border-box',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
// padding: 24,
overflow: 'hidden', overflow: 'hidden',
minHeight: 0,
}} }}
> >
{/* 空状态 - 没有对话或当前对话没有消息时显示 */} {/* 空状态 - 没有对话或当前对话没有消息时显示 */}
@@ -982,10 +1004,15 @@ const AIChatPage: React.FC = () => {
<div <div
ref={scrollContainerRef} ref={scrollContainerRef}
style={{ style={{
height: 'calc(100vh - 280px)', // 固定高度,减去头部和输入区域 flex: 1,
overflowY: 'auto', // 垂直滚动 overflowY: 'auto',
paddingBottom: 24, paddingBottom: 24,
paddingRight: 8, // 预留滚动条空间 paddingRight: 8,
// backgroundImage: `url(${bg1})`,
// backgroundRepeat: 'no-repeat',
// backgroundSize: 'cover',
// backgroundPosition: 'center',
}} }}
> >
{/* 加载更多按钮 - 在列表顶部 */} {/* 加载更多按钮 - 在列表顶部 */}
@@ -1015,30 +1042,32 @@ const AIChatPage: React.FC = () => {
<div <div
key={msg.id} key={msg.id}
style={{ style={{
width: '100%',
display: 'flex', display: 'flex',
justifyContent: 'flex-start', // 所有消息左对齐 justifyContent: 'flex-start', // 所有消息左对齐
marginBottom: 16, marginBottom: 16,
}} }}
> >
<div style={{ display: 'flex', gap: 10, maxWidth: '70%' }}> <div style={{ width: '100%', display: 'flex', gap: 10 }}>
{/* 头像 */} {/* 头像 */}
<div <div
style={{ style={{
width: 36, width: 36,
height: 36, height: 36,
borderRadius: 50, borderRadius: 12,
background: '#e0e0e0', background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
flexShrink: 0, flexShrink: 0,
boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)',
}} }}
> >
<RobotOutlined style={{ color: '#666', fontSize: 16 }} /> <RobotOutlined style={{ color: '#fff', fontSize: 16 }} />
</div> </div>
{/* 消息内容 */} {/* 消息内容 */}
<div> <div style={{ flex: 1 }}>
{/* 时间戳和参数信息 */} {/* 时间戳和参数信息 */}
<div style={{ margin: 4, fontSize: 11, color: '#999', textAlign: 'left', display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' }}> <div style={{ margin: 4, fontSize: 11, color: '#999', textAlign: 'left', display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' }}>
@@ -1080,16 +1109,20 @@ const AIChatPage: React.FC = () => {
{/* 消息气泡 */} {/* 消息气泡 */}
<div <div
style={{ style={{
background: '#fff', background: 'rgba(255,255,255,0.85)',
backdropFilter: 'blur(20px)',
borderRadius: '16px 16px 16px 4px', borderRadius: '16px 16px 16px 4px',
padding: '12px 16px', padding: '12px 16px',
width: '600px', width: '70%',
boxShadow: '0 2px 8px rgba(0,0,0,0.06)', maxWidth: 800,
boxSizing: 'border-box',
boxShadow: '0 4px 20px rgba(99, 102, 241, 0.08), 0 1px 3px rgba(0,0,0,0.04)',
position: 'relative', position: 'relative',
border: '1px solid rgba(99, 102, 241, 0.06)',
}} }}
> >
{/* 删除按钮 - 右上角 */} {/* 删除按钮 - 右上角 */}
<div style={{ position: 'absolute', top: 8, right: 8 ,zIndex: 100}}> <div style={{ position: 'absolute', top: 8, right: 8, zIndex: 100 }}>
<Popconfirm <Popconfirm
title="确定要删除吗?" title="确定要删除吗?"
onConfirm={async () => { onConfirm={async () => {
@@ -1115,8 +1148,8 @@ const AIChatPage: React.FC = () => {
height: 28, height: 28,
borderRadius: 8, borderRadius: 8,
border: 'none', border: 'none',
background: 'rgba(146, 144, 144, 1)', background: 'rgba(99, 102, 241, 0.08)',
color: '#ffffffff', color: '#6366f1',
cursor: 'pointer', cursor: 'pointer',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
@@ -1125,12 +1158,12 @@ const AIChatPage: React.FC = () => {
padding: 0, padding: 0,
}} }}
onMouseEnter={(e) => { onMouseEnter={(e) => {
e.currentTarget.style.background = '#fff2f0'; e.currentTarget.style.background = 'rgba(239, 68, 68, 0.1)';
e.currentTarget.style.color = '#ff4d4f'; e.currentTarget.style.color = '#ef4444';
}} }}
onMouseLeave={(e) => { onMouseLeave={(e) => {
e.currentTarget.style.background = 'rgba(0,0,0,0.05)'; e.currentTarget.style.background = 'rgba(99, 102, 241, 0.08)';
e.currentTarget.style.color = '#999'; e.currentTarget.style.color = '#6366f1';
}} }}
> >
<DeleteOutlined style={{ fontSize: 12 }} /> <DeleteOutlined style={{ fontSize: 12 }} />
@@ -1142,18 +1175,19 @@ const AIChatPage: React.FC = () => {
{/* 文本内容 */} {/* 文本内容 */}
<div <div
style={{ style={{
width: '100%',
position: 'relative', position: 'relative',
margin: '8px 0', margin: '8px 0',
padding: '12px 16px', padding: '12px 16px',
backgroundColor: '#f8fafc', backgroundColor: 'rgba(99, 102, 241, 0.04)',
borderRadius: 8, borderRadius: 10,
border: '1px solid #e2e8f0', border: '1px solid rgba(99, 102, 241, 0.08)',
fontSize: 13, fontSize: 13,
color: '#475569', color: '#475569',
lineHeight: 1.6, lineHeight: 1.6,
cursor: 'pointer', cursor: 'pointer',
transition: 'all 0.2s ease', transition: 'all 0.2s ease',
boxShadow: '0 1px 3px rgba(0,0,0,0.05)', boxShadow: '0 1px 3px rgba(99, 102, 241, 0.04)',
}} }}
onMouseEnter={() => { onMouseEnter={() => {
setExpandedPrompts(prev => { setExpandedPrompts(prev => {
@@ -1172,6 +1206,7 @@ const AIChatPage: React.FC = () => {
> >
{/* 默认显示:一行省略 */} {/* 默认显示:一行省略 */}
<div style={{ <div style={{
width: '100%',
overflow: 'hidden', overflow: 'hidden',
textOverflow: 'ellipsis', textOverflow: 'ellipsis',
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
@@ -1182,6 +1217,7 @@ const AIChatPage: React.FC = () => {
{/* 鼠标移入显示:完整内容 */} {/* 鼠标移入显示:完整内容 */}
<div style={{ <div style={{
width: '100%',
maxHeight: 200, maxHeight: 200,
overflowY: 'auto', overflowY: 'auto',
display: expandedPrompts.has(msg.id) ? 'block' : 'none', display: expandedPrompts.has(msg.id) ? 'block' : 'none',
@@ -1445,11 +1481,14 @@ const AIChatPage: React.FC = () => {
{/* 输入区域 - 始终显示 */} {/* 输入区域 - 始终显示 */}
<div <div
style={{ style={{
background: '#fff', background: 'rgba(255,255,255,0.1)',
backdropFilter: 'blur(20px)',
borderRadius: 24, borderRadius: 24,
padding: 16, padding: 16,
boxShadow: '0 4px 20px rgba(0,0,0,0.08), 0 1px 3px rgba(0,0,0,0.06)', boxShadow: '0 8px 32px rgba(99, 102, 241, 0.1), 0 2px 8px rgba(0,0,0,0.04)',
transition: 'all 0.3s ease', transition: 'all 0.3s ease',
border: '1px solid rgba(99, 102, 241, 0.08)',
// margin: '0 24px 24px',
}} }}
> >
{/* 已上传媒体预览 */} {/* 已上传媒体预览 */}
@@ -1499,12 +1538,13 @@ const AIChatPage: React.FC = () => {
{/* 输入框区域 */} {/* 输入框区域 */}
<div style={{ <div style={{
display: 'flex', display: 'flex',
alignItems: 'center',
gap: 12, gap: 12,
padding: '12px 16px', padding: '12px 16px',
backgroundColor: '#ffffff', backgroundColor: 'rgba(248,250,252,0.6)',
borderRadius: 16, borderRadius: 16,
border: '1px solid #e2e8f0', border: '1px solid rgba(99, 102, 241, 0.08)',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.06)', boxShadow: '0 2px 8px rgba(99, 102, 241, 0.04)',
transition: 'all 0.2s ease', transition: 'all 0.2s ease',
}}> }}>
{/* 上传按钮 */} {/* 上传按钮 */}
@@ -1518,15 +1558,15 @@ const AIChatPage: React.FC = () => {
style={{ style={{
width: 48, width: 48,
height: 48, height: 48,
borderRadius: 12, borderRadius: 14,
border: '2px dashed #cbd5e1', border: '2px dashed rgba(99, 102, 241, 0.2)',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
cursor: 'pointer', cursor: 'pointer',
transition: 'all 0.25s ease', transition: 'all 0.25s ease',
flexShrink: 0, flexShrink: 0,
backgroundColor: '#f8fafc', backgroundColor: 'rgba(255,255,255,0.7)',
}} }}
onMouseEnter={(e) => { onMouseEnter={(e) => {
e.currentTarget.style.borderColor = '#6366f1'; e.currentTarget.style.borderColor = '#6366f1';
@@ -1534,8 +1574,8 @@ const AIChatPage: React.FC = () => {
e.currentTarget.style.transform = 'scale(1.05)'; e.currentTarget.style.transform = 'scale(1.05)';
}} }}
onMouseLeave={(e) => { onMouseLeave={(e) => {
e.currentTarget.style.borderColor = '#cbd5e1'; e.currentTarget.style.borderColor = 'rgba(99, 102, 241, 0.2)';
e.currentTarget.style.backgroundColor = '#f8fafc'; e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.7)';
e.currentTarget.style.transform = 'scale(1)'; e.currentTarget.style.transform = 'scale(1)';
}} }}
> >
@@ -1557,6 +1597,7 @@ const AIChatPage: React.FC = () => {
autoSize={{ minRows: 1, maxRows: 4 }} autoSize={{ minRows: 1, maxRows: 4 }}
style={{ style={{
flex: 1, flex: 1,
height: '100%',
borderRadius: 12, borderRadius: 12,
border: 'none', border: 'none',
outline: 'none', outline: 'none',
@@ -1564,7 +1605,10 @@ const AIChatPage: React.FC = () => {
fontSize: 14, fontSize: 14,
lineHeight: 1.5, lineHeight: 1.5,
color: '#1e293b', color: '#1e293b',
resize: 'none', // resize: 'none',
// boxShadow: 'none',
// padding: '8px 12px',
}} }}
disabled={loading} disabled={loading}
/> />
@@ -1582,14 +1626,16 @@ const AIChatPage: React.FC = () => {
width: 48, width: 48,
height: 48, height: 48,
borderRadius: 14, borderRadius: 14,
boxShadow: '0 4px 12px rgba(99, 102, 241, 0.35)', background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
boxShadow: '0 4px 16px rgba(99, 102, 241, 0.4)',
transition: 'all 0.2s ease', transition: 'all 0.2s ease',
border: 'none',
}} }}
/> />
</div> </div>
{/* 底部选择器 - 媒体类型和数量 */} {/* 底部选择器 - 媒体类型和数量 */}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 12, paddingTop: 12, borderTop: '1px solid #f0f0f0' }}> <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 12, paddingTop: 12, borderTop: '1px solid rgba(99, 102, 241, 0.08)' }}>
<Space size="middle"> <Space size="middle">
{/* 文件类型选择器 */} {/* 文件类型选择器 */}
{/* <div style={{ {/* <div style={{
@@ -1608,11 +1654,11 @@ const AIChatPage: React.FC = () => {
onChange={(val) => setMediaType(val)} onChange={(val) => setMediaType(val)}
style={{ style={{
width: 100, width: 100,
outline: 'none',
background: '#f1f5f9',
border: 'none',
borderRadius: 8,
height: 34, height: 34,
outline: 'none',
border: 'none',
backgroundColor: '#f1f5f9',
borderRadius: 10,
}} }}
size="middle" size="middle"
> >
@@ -1634,16 +1680,26 @@ const AIChatPage: React.FC = () => {
className="image-settings-trigger" className="image-settings-trigger"
style={{ style={{
minWidth: 200, minWidth: 200,
padding: '4px 12px', padding: '6px 14px',
height: 34, height: 34,
borderRadius: 8, borderRadius: 10,
border: 'none', border: 'none',
backgroundColor: '#f1f5f9', backgroundColor: '#f1f5f9',
cursor: 'pointer', cursor: 'pointer',
display: 'inline-flex', display: 'inline-flex',
alignItems: 'center', alignItems: 'center',
gap: 6, gap: 6,
transition: 'all 0.2s', transition: 'all 0.2s',
boxShadow: '0 2px 8px rgba(99, 102, 241, 0.04)',
}}
onMouseEnter={(e) => {
e.currentTarget.style.borderColor = 'rgba(99, 102, 241, 0.3)';
e.currentTarget.style.boxShadow = '0 4px 12px rgba(99, 102, 241, 0.08)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.borderColor = 'rgba(99, 102, 241, 0.15)';
e.currentTarget.style.boxShadow = '0 2px 8px rgba(99, 102, 241, 0.04)';
}} }}
> >
<SettingOutlined style={{ fontSize: 14, color: '#64748b' }} /> <SettingOutlined style={{ fontSize: 14, color: '#64748b' }} />
@@ -1664,11 +1720,12 @@ const AIChatPage: React.FC = () => {
bottom: 'calc(100% + 8px)', bottom: 'calc(100% + 8px)',
left: 0, left: 0,
width: 360, width: 360,
backgroundColor: '#fff', backgroundColor: 'rgba(255,255,255,0.95)',
borderRadius: 16, backdropFilter: 'blur(20px)',
boxShadow: '0 10px 40px rgba(0,0,0,0.15)', borderRadius: 14,
boxShadow: '0 12px 48px rgba(99, 102, 241, 0.15)',
padding: 16, padding: 16,
border: 'none', border: '1px solid rgba(99, 102, 241, 0.1)',
zIndex: 9999, zIndex: 9999,
}} }}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
@@ -1679,8 +1736,8 @@ const AIChatPage: React.FC = () => {
display: 'block', display: 'block',
marginBottom: 8, marginBottom: 8,
fontSize: 12, fontSize: 12,
fontWeight: 500, fontWeight: 600,
color: '#666666', color: '#475569',
}}> }}>
</Text> </Text>
@@ -1793,11 +1850,12 @@ const AIChatPage: React.FC = () => {
bottom: 'calc(100% + 8px)', bottom: 'calc(100% + 8px)',
left: 0, left: 0,
width: 480, width: 480,
backgroundColor: '#fff', backgroundColor: 'rgba(255,255,255,0.95)',
borderRadius: 16, backdropFilter: 'blur(20px)',
boxShadow: '0 10px 40px rgba(0,0,0,0.15)', borderRadius: 14,
boxShadow: '0 12px 48px rgba(99, 102, 241, 0.15)',
padding: 16, padding: 16,
border: 'none', border: '1px solid rgba(99, 102, 241, 0.1)',
zIndex: 9999, zIndex: 9999,
}} }}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
@@ -2074,11 +2132,12 @@ const AIChatPage: React.FC = () => {
bottom: 'calc(100% + 8px)', bottom: 'calc(100% + 8px)',
left: 0, left: 0,
width: 400, width: 400,
backgroundColor: '#fff', backgroundColor: 'rgba(255,255,255,0.95)',
borderRadius: 16, backdropFilter: 'blur(20px)',
boxShadow: '0 10px 40px rgba(0,0,0,0.15)', borderRadius: 14,
boxShadow: '0 12px 48px rgba(99, 102, 241, 0.15)',
padding: 16, padding: 16,
border: 'none', border: '1px solid rgba(99, 102, 241, 0.1)',
zIndex: 9999, zIndex: 9999,
}} }}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
@@ -2265,22 +2324,12 @@ const AIChatPage: React.FC = () => {
</div> </div>
)} )}
{/* 预估积分 */} {/* 预估积分 */}
<div style={{ display: 'flex', alignItems: 'center', gap: 4 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '6px 14px', backgroundColor: 'rgba(99, 102, 241, 0.06)', borderRadius: 10, border: '1px solid rgba(99, 102, 241, 0.1)' }}>
<Text style={{ fontSize: 12, color: '#94a3b8' }}></Text> <Text style={{ fontSize: 13, color: '#6366f1', fontWeight: 500 }}></Text>
<Text style={{ fontSize: 12, color: '#666666' }}>{getEstimatedCredits()}</Text> <Text style={{ fontSize: 14, color: '#6366f1', fontWeight: 600 }}>{getEstimatedCredits()}</Text>
</div> </div>
</Space> </Space>
{/* 底部信息 */}
{/* <div style={{ display: 'flex', gap: 8, fontSize: 12, color: '#999' }}>
<span>GPT Video 2</span>
<span style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
<span style={{ width: 16, height: 16, borderRadius: 4, background: '#f0f0f0', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 10 }}>
{mediaType === 'image' ? (selectedRatio === 'auto' ? '智能' : selectedRatio) : videoAspectRatio}
</span>
<span>{countType}</span>
</span>
</div> */}
</div> </div>
</div> </div>
</Content> </Content>
@@ -2299,7 +2348,7 @@ const AIChatPage: React.FC = () => {
open={previewVisible} open={previewVisible}
onCancel={handleClosePreview} onCancel={handleClosePreview}
footer={[ footer={[
<Button key="download" type="primary" onClick={handleDownload}> <Button key="download" type="primary" onClick={handleDownload} style={{ background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', borderRadius: 10, border: 'none' }}>
</Button>, </Button>,
]} ]}
@@ -2309,14 +2358,14 @@ const AIChatPage: React.FC = () => {
<button <button
onClick={handleClosePreview} onClick={handleClosePreview}
style={{ style={{
width: 28, width: 32,
height: 28, height: 32,
borderRadius: '50%', borderRadius: '50%',
border: 'none', border: 'none',
background: '#ff4d4f', background: 'rgba(99, 102, 241, 0.1)',
cursor: 'pointer', cursor: 'pointer',
fontSize: 14, fontSize: 16,
color: '#fff', color: '#6366f1',
fontWeight: 'bold', fontWeight: 'bold',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
@@ -2324,24 +2373,27 @@ const AIChatPage: React.FC = () => {
transition: 'all 0.2s', transition: 'all 0.2s',
}} }}
onMouseEnter={(e) => { onMouseEnter={(e) => {
e.currentTarget.style.background = '#ff7875'; e.currentTarget.style.background = 'rgba(239, 68, 68, 0.1)';
e.currentTarget.style.color = '#ef4444';
e.currentTarget.style.transform = 'scale(1.1)'; e.currentTarget.style.transform = 'scale(1.1)';
}} }}
onMouseLeave={(e) => { onMouseLeave={(e) => {
e.currentTarget.style.background = '#ff4d4f'; e.currentTarget.style.background = 'rgba(99, 102, 241, 0.1)';
e.currentTarget.style.color = '#6366f1';
e.currentTarget.style.transform = 'scale(1)'; e.currentTarget.style.transform = 'scale(1)';
}} }}
> >
× ×
</button> </button>
} }
style={{ borderRadius: 16 }}
styles={{ styles={{
body: { body: {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
minHeight: '400px', minHeight: '400px',
} },
}} }}
> >
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: '100%', height: '100%' }}> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: '100%', height: '100%' }}>
@@ -2388,7 +2440,7 @@ const AIChatPage: React.FC = () => {
document.body.appendChild(link); document.body.appendChild(link);
link.click(); link.click();
document.body.removeChild(link); document.body.removeChild(link);
}}> }} style={{ background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', borderRadius: 10, border: 'none' }}>
</Button>, </Button>,
]} ]}
@@ -2398,14 +2450,14 @@ const AIChatPage: React.FC = () => {
<button <button
onClick={() => setAttachmentPreviewVisible(false)} onClick={() => setAttachmentPreviewVisible(false)}
style={{ style={{
width: 28, width: 32,
height: 28, height: 32,
borderRadius: '50%', borderRadius: '50%',
border: 'none', border: 'none',
background: '#ff4d4f', background: 'rgba(99, 102, 241, 0.1)',
cursor: 'pointer', cursor: 'pointer',
fontSize: 14, fontSize: 16,
color: '#fff', color: '#6366f1',
fontWeight: 'bold', fontWeight: 'bold',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
@@ -2413,24 +2465,27 @@ const AIChatPage: React.FC = () => {
transition: 'all 0.2s', transition: 'all 0.2s',
}} }}
onMouseEnter={(e) => { onMouseEnter={(e) => {
e.currentTarget.style.background = '#ff7875'; e.currentTarget.style.background = 'rgba(239, 68, 68, 0.1)';
e.currentTarget.style.color = '#ef4444';
e.currentTarget.style.transform = 'scale(1.1)'; e.currentTarget.style.transform = 'scale(1.1)';
}} }}
onMouseLeave={(e) => { onMouseLeave={(e) => {
e.currentTarget.style.background = '#ff4d4f'; e.currentTarget.style.background = 'rgba(99, 102, 241, 0.1)';
e.currentTarget.style.color = '#6366f1';
e.currentTarget.style.transform = 'scale(1)'; e.currentTarget.style.transform = 'scale(1)';
}} }}
> >
× ×
</button> </button>
} }
style={{ borderRadius: 16 }}
styles={{ styles={{
body: { body: {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
minHeight: '400px', minHeight: '400px',
} },
}} }}
> >
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: '100%', height: '100%' }}> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: '100%', height: '100%' }}>
+16 -6
View File
@@ -52,6 +52,7 @@ function InitialInfo() {
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(20); const [pageSize, setPageSize] = useState(20);
const [total, setTotal] = useState(0); const [total, setTotal] = useState(0);
const [searchKeyword, setSearchKeyword] = useState('');
// 任务详情数据 // 任务详情数据
const [taskDetail, setTaskDetail] = useState<any>({}); const [taskDetail, setTaskDetail] = useState<any>({});
// API 返回的步骤数据 // API 返回的步骤数据
@@ -82,8 +83,8 @@ function InitialInfo() {
// 获取列表数据的函数 // 获取列表数据的函数
const fetchList = (page: number, size: number) => { const fetchList = (page: number, size: number, keyword?: string) => {
getReplicationList(page, size).then((res: any) => { getReplicationList(page, size, keyword).then((res: any) => {
if (res.items) { if (res.items) {
setTableData(res.items); setTableData(res.items);
} }
@@ -98,7 +99,13 @@ function InitialInfo() {
const handlePageChange = (page: number, size: number) => { const handlePageChange = (page: number, size: number) => {
setCurrentPage(page); setCurrentPage(page);
setPageSize(size); setPageSize(size);
fetchList(page, size); fetchList(page, size, searchKeyword);
};
// 搜索处理
const handleSearch = () => {
setCurrentPage(1);
fetchList(1, pageSize, searchKeyword);
}; };
// 获取复刻列表数据 // 获取复刻列表数据
@@ -1072,7 +1079,7 @@ function InitialInfo() {
<> <>
<div style={{ padding: '12px 14px', background: 'rgba(255,255,255,0.6)', borderRadius: 10, border: '1px solid rgba(99, 102, 241, 0.08)', marginBottom: 4 }}> <div style={{ padding: '12px 14px', background: 'rgba(255,255,255,0.6)', borderRadius: 10, border: '1px solid rgba(99, 102, 241, 0.08)', marginBottom: 4 }}>
<Text style={{ color: '#64748b', fontSize: 13, lineHeight: 1.8 }}> <Text style={{ color: '#64748b', fontSize: 13, lineHeight: 1.8 }}>
Schema /
</Text> </Text>
</div> </div>
<Space style={{ marginTop: 16, gap: 12, width: '100%' }}> <Space style={{ marginTop: 16, gap: 12, width: '100%' }}>
@@ -1083,7 +1090,7 @@ function InitialInfo() {
style={{ flex: 1, borderRadius: 10, borderColor: 'rgba(99, 102, 241, 0.3)', color: '#6366f1', height: 36, fontWeight: 500, background: 'rgba(99, 102, 241, 0.04)' }} style={{ flex: 1, borderRadius: 10, borderColor: 'rgba(99, 102, 241, 0.3)', color: '#6366f1', height: 36, fontWeight: 500, background: 'rgba(99, 102, 241, 0.04)' }}
disabled={step.status !== 'completed' || !taskDetail?.videoGeneration?.promptSchema || !taskDetail?.videoGeneration?.schemaConfigSnapshot} disabled={step.status !== 'completed' || !taskDetail?.videoGeneration?.promptSchema || !taskDetail?.videoGeneration?.schemaConfigSnapshot}
> >
/
</Button> </Button>
<Button <Button
onClick={() => { message.info('正在生成视频,请稍候...'); createvideo(step.id, step.engineId); }} onClick={() => { message.info('正在生成视频,请稍候...'); createvideo(step.id, step.engineId); }}
@@ -1211,9 +1218,12 @@ function InitialInfo() {
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 16 }}> <div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 16 }}>
<Input <Input
placeholder="搜索产品名称" placeholder="搜索产品名称"
value={searchKeyword}
onChange={(e) => setSearchKeyword(e.target.value)}
onPressEnter={handleSearch}
style={{ width: 200, borderRadius: 8, marginRight: 8, border: '1px solid rgba(99, 102, 241, 0.15)', background: 'rgba(255,255,255,0.8)' }} style={{ width: 200, borderRadius: 8, marginRight: 8, border: '1px solid rgba(99, 102, 241, 0.15)', background: 'rgba(255,255,255,0.8)' }}
/> />
<Button type="primary" style={{ borderRadius: 8, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)' }}> <Button type="primary" onClick={handleSearch} style={{ borderRadius: 8, background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', border: 'none', boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)' }}>
</Button> </Button>
</div> </div>
+18 -6
View File
@@ -34,6 +34,7 @@ const GenerateConver: React.FC = () => {
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(20); const [pageSize, setPageSize] = useState(20);
const [total, setTotal] = useState(0); const [total, setTotal] = useState(0);
const [searchKeyword, setSearchKeyword] = useState('');
// 卡片列表专用状态 // 卡片列表专用状态
const [cardData, setCardData] = useState<any[]>([]); const [cardData, setCardData] = useState<any[]>([]);
@@ -114,8 +115,8 @@ const GenerateConver: React.FC = () => {
}; };
// 获取列表数据的函数 // 获取列表数据的函数
const fetchList = (page: number, size: number, isPolling = false) => { const fetchList = (page: number, size: number, isPolling = false, keyword?: string) => {
getReplicationList(page, size).then((res: any) => { getReplicationList(page, size, keyword).then((res: any) => {
if (res.items) { if (res.items) {
if (isPolling) { if (isPolling) {
// 轮询时:增量更新,只更新状态发生变化的项目 // 轮询时:增量更新,只更新状态发生变化的项目
@@ -139,7 +140,7 @@ const GenerateConver: React.FC = () => {
// 如果有 processing 状态且轮询未启动,启动轮询 // 如果有 processing 状态且轮询未启动,启动轮询
if (hasProcessing && !tablePollingTimer.current) { if (hasProcessing && !tablePollingTimer.current) {
tablePollingTimer.current = setInterval(() => { tablePollingTimer.current = setInterval(() => {
fetchList(currentPage, pageSize, true); fetchList(currentPage, pageSize, true, searchKeyword);
}, 5000); }, 5000);
} }
// 如果没有 processing 状态且轮询正在运行,停止轮询 // 如果没有 processing 状态且轮询正在运行,停止轮询
@@ -177,7 +178,13 @@ const GenerateConver: React.FC = () => {
const handlePageChange = (page: number, size: number) => { const handlePageChange = (page: number, size: number) => {
setCurrentPage(page); setCurrentPage(page);
setPageSize(size); setPageSize(size);
fetchList(page, size); fetchList(page, size, false, searchKeyword);
};
// 搜索处理
const handleSearch = () => {
setCurrentPage(1);
fetchList(1, pageSize, false, searchKeyword);
}; };
// 文件上传前校验 // 文件上传前校验
@@ -205,7 +212,7 @@ const GenerateConver: React.FC = () => {
resolve(false); resolve(false);
return; return;
} }
if (video.duration > 15) { if (video.duration >= 16) {
message.error('视频时长不能超过15秒'); message.error('视频时长不能超过15秒');
URL.revokeObjectURL(video.src); URL.revokeObjectURL(video.src);
resolve(false); resolve(false);
@@ -489,13 +496,14 @@ const GenerateConver: React.FC = () => {
key={item.id} key={item.id}
style={{ style={{
background: 'rgba(255,255,255,0.9)', background: 'rgba(255,255,255,0.9)',
border: '1px solid rgba(99, 102, 241, 0.6)',
backdropFilter: 'blur(10px)', backdropFilter: 'blur(10px)',
borderRadius: 16, borderRadius: 16,
overflow: 'hidden', overflow: 'hidden',
boxShadow: '0 4px 16px rgba(99, 102, 241, 0.06)', boxShadow: '0 4px 16px rgba(99, 102, 241, 0.06)',
cursor: 'pointer', cursor: 'pointer',
transition: 'transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.25s cubic-bezier(0.4, 0, 0.2, 1)', transition: 'transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.25s cubic-bezier(0.4, 0, 0.2, 1)',
border: '1px solid rgba(99, 102, 241, 0.06)', // border: '1px solid rgba(99, 102, 241, 0.06)',
}} }}
onClick={() => navigate(`/initial/${item.id}/initialinfo`)} onClick={() => navigate(`/initial/${item.id}/initialinfo`)}
onMouseEnter={(e) => { onMouseEnter={(e) => {
@@ -1146,6 +1154,9 @@ const GenerateConver: React.FC = () => {
<div style={{ display: 'flex', justifyContent: 'flex-end', margin: '16px 24px', flexShrink: 0 }}> <div style={{ display: 'flex', justifyContent: 'flex-end', margin: '16px 24px', flexShrink: 0 }}>
<Input <Input
placeholder="搜索产品名称" placeholder="搜索产品名称"
value={searchKeyword}
onChange={(e) => setSearchKeyword(e.target.value)}
onPressEnter={handleSearch}
style={{ style={{
width: 220, width: 220,
borderRadius: 10, borderRadius: 10,
@@ -1156,6 +1167,7 @@ const GenerateConver: React.FC = () => {
/> />
<Button <Button
type="primary" type="primary"
onClick={handleSearch}
style={{ style={{
borderRadius: 10, borderRadius: 10,
background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)',
+1
View File
@@ -730,6 +730,7 @@ function RemoveInfo() {
} }
placement="top" placement="top"
trigger="hover" trigger="hover"
overlayInnerStyle={{ backgroundColor: '#fff', border: '1px solid rgba(99, 102, 241, 0.1)', borderRadius: 12, boxShadow: '0 8px 24px rgba(99, 102, 241, 0.12)' }}
> >
<span style={{ marginLeft: 8, fontSize: 13, opacity: 0.9 }}>(AI )</span> <span style={{ marginLeft: 8, fontSize: 13, opacity: 0.9 }}>(AI )</span>
</Tooltip> </Tooltip>
+2 -2
View File
@@ -127,8 +127,8 @@ export default function VideoFrameExtractor() {
<div style={{ maxWidth: 1200, margin: '50px auto', position: 'relative', zIndex: 10 }}> <div style={{ maxWidth: 1200, margin: '50px auto', position: 'relative', zIndex: 10 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 32 }}> <div style={{ marginBottom: 50,height: 160, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 16,marginBottom: 50 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
<div style={{ width: 40, height: 2, background: 'linear-gradient(90deg, transparent, #6366f1, #8b5cf6, transparent)', borderRadius: 1 }} /> <div style={{ width: 40, height: 2, background: 'linear-gradient(90deg, transparent, #6366f1, #8b5cf6, transparent)', borderRadius: 1 }} />
<div> <div>
<h1 style={{ <h1 style={{
+2 -2
View File
@@ -1075,7 +1075,7 @@ function InitialInfo() {
<> <>
<div style={{ padding: '12px 14px', background: 'rgba(255,255,255,0.6)', borderRadius: 10, border: '1px solid rgba(99, 102, 241, 0.08)', marginBottom: 4 }}> <div style={{ padding: '12px 14px', background: 'rgba(255,255,255,0.6)', borderRadius: 10, border: '1px solid rgba(99, 102, 241, 0.08)', marginBottom: 4 }}>
<Text style={{ color: '#64748b', fontSize: 13, lineHeight: 1.8 }}> <Text style={{ color: '#64748b', fontSize: 13, lineHeight: 1.8 }}>
Schema /
</Text> </Text>
</div> </div>
<Space style={{ marginTop: 16, gap: 12, width: '100%' }}> <Space style={{ marginTop: 16, gap: 12, width: '100%' }}>
@@ -1086,7 +1086,7 @@ function InitialInfo() {
style={{ flex: 1, borderRadius: 10, borderColor: 'rgba(99, 102, 241, 0.3)', color: '#6366f1', height: 36, fontWeight: 500, background: 'rgba(99, 102, 241, 0.04)' }} style={{ flex: 1, borderRadius: 10, borderColor: 'rgba(99, 102, 241, 0.3)', color: '#6366f1', height: 36, fontWeight: 500, background: 'rgba(99, 102, 241, 0.04)' }}
disabled={step.status !== 'completed' || !taskDetail?.videoGeneration?.promptSchema || !taskDetail?.videoGeneration?.schemaConfigSnapshot} disabled={step.status !== 'completed' || !taskDetail?.videoGeneration?.promptSchema || !taskDetail?.videoGeneration?.schemaConfigSnapshot}
> >
/
</Button> </Button>
<Button onClick={() => { message.info('正在生成视频,请稍候...'); createvideo(step.id, step.engineId); }} type="primary" style={{ flex: 1, 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)' }} disabled={step.status !== 'completed'}> <Button onClick={() => { message.info('正在生成视频,请稍候...'); createvideo(step.id, step.engineId); }} type="primary" style={{ flex: 1, 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)' }} disabled={step.status !== 'completed'}>