Merge branch 'main' of https://gitee.com/wg123/video-gen
This commit is contained in:
BIN
Binary file not shown.
|
After Width: | Height: | Size: 4.9 MiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 4.6 MiB |
-428
File diff suppressed because one or more lines are too long
+440
File diff suppressed because one or more lines are too long
Vendored
+36
-36
@@ -1,37 +1,37 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
|
||||||
<title>民众智创</title>
|
<title>民众智创</title>
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
var cached = localStorage.getItem('siteInfo');
|
var cached = localStorage.getItem('siteInfo');
|
||||||
if (cached) {
|
if (cached) {
|
||||||
try {
|
try {
|
||||||
var info = JSON.parse(cached);
|
var info = JSON.parse(cached);
|
||||||
if (info.siteName) {
|
if (info.siteName) {
|
||||||
document.title = info.siteName;
|
document.title = info.siteName;
|
||||||
}
|
}
|
||||||
if (info.siteLogo) {
|
if (info.siteLogo) {
|
||||||
var link = document.querySelector('link[rel="icon"]');
|
var link = document.querySelector('link[rel="icon"]');
|
||||||
if (link) {
|
if (link) {
|
||||||
link.href = info.siteLogo;
|
link.href = info.siteLogo;
|
||||||
link.type = 'image/png';
|
link.type = 'image/png';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
</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>
|
||||||
<div id="root"></div>␍
|
<div id="root"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -809,7 +813,7 @@ const AIChatPage: React.FC = () => {
|
|||||||
|
|
||||||
const handleClosePreview = () => {
|
const handleClosePreview = () => {
|
||||||
setPreviewVisible(false);
|
setPreviewVisible(false);
|
||||||
setPreviewUrl('');
|
setPreviewUrl('');
|
||||||
if (videoRef.current) {
|
if (videoRef.current) {
|
||||||
videoRef.current.pause();
|
videoRef.current.pause();
|
||||||
}
|
}
|
||||||
@@ -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,35 +1158,36 @@ 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 }} />
|
||||||
</button>
|
</button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 文本内容 */}
|
{/* 文本内容 */}
|
||||||
<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',
|
||||||
@@ -1179,9 +1214,10 @@ const AIChatPage: React.FC = () => {
|
|||||||
}}>
|
}}>
|
||||||
{msg.originalPrompt}
|
{msg.originalPrompt}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 鼠标移入显示:完整内容 */}
|
{/* 鼠标移入显示:完整内容 */}
|
||||||
<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',
|
||||||
@@ -1194,12 +1230,12 @@ const AIChatPage: React.FC = () => {
|
|||||||
{/* 根据 status 显示不同内容 */}
|
{/* 根据 status 显示不同内容 */}
|
||||||
{/* 生成中 - 显示加载动画 */}
|
{/* 生成中 - 显示加载动画 */}
|
||||||
{msg.status === 'generating' && (
|
{msg.status === 'generating' && (
|
||||||
<div style={{
|
<div style={{
|
||||||
height: 200,
|
height: 200,
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
marginTop: 12,
|
marginTop: 12,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
background: 'linear-gradient(135deg, #e0e7ff 0%, #f3e8ff 50%, #e0e7ff 100%)',
|
background: 'linear-gradient(135deg, #e0e7ff 0%, #f3e8ff 50%, #e0e7ff 100%)',
|
||||||
}}>
|
}}>
|
||||||
@@ -1231,13 +1267,13 @@ const AIChatPage: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
{/* 生成失败 - 显示失败提示 */}
|
{/* 生成失败 - 显示失败提示 */}
|
||||||
{msg.status === 'failed' && (
|
{msg.status === 'failed' && (
|
||||||
<div style={{
|
<div style={{
|
||||||
height: 200,
|
height: 200,
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
marginTop: 12,
|
marginTop: 12,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
backgroundColor: '#fafafa',
|
backgroundColor: '#fafafa',
|
||||||
border: '1px dashed #e2e8f0',
|
border: '1px dashed #e2e8f0',
|
||||||
}}>
|
}}>
|
||||||
@@ -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%' }}>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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%)',
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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={{
|
||||||
|
|||||||
@@ -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'}>
|
||||||
下一步:生成视频
|
下一步:生成视频
|
||||||
|
|||||||
Reference in New Issue
Block a user