ai对话修改
This commit is contained in:
+90
-90
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-DxvrbZVm.js"></script>
|
<script type="module" crossorigin src="/assets/index-BRAajPcM.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-D9_3MPsN.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-D9_3MPsN.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>␍
|
<div id="root"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import {
|
|||||||
ArrowUpOutlined,
|
ArrowUpOutlined,
|
||||||
DownloadOutlined,
|
DownloadOutlined,
|
||||||
ReloadOutlined,
|
ReloadOutlined,
|
||||||
|
AudioOutlined,
|
||||||
|
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
|
|
||||||
@@ -274,7 +275,7 @@ const AIChatPage: React.FC = () => {
|
|||||||
// 附件预览弹窗状态(独立弹窗)
|
// 附件预览弹窗状态(独立弹窗)
|
||||||
const [attachmentPreviewVisible, setAttachmentPreviewVisible] = useState<boolean>(false);
|
const [attachmentPreviewVisible, setAttachmentPreviewVisible] = useState<boolean>(false);
|
||||||
const [attachmentPreviewUrl, setAttachmentPreviewUrl] = useState<string>('');
|
const [attachmentPreviewUrl, setAttachmentPreviewUrl] = useState<string>('');
|
||||||
const [attachmentPreviewType, setAttachmentPreviewType] = useState<'image' | 'video'>('image');
|
const [attachmentPreviewType, setAttachmentPreviewType] = useState<'image' | 'video' | 'audio'>('image');
|
||||||
const [attachmentPreviewName, setAttachmentPreviewName] = useState<string>('');
|
const [attachmentPreviewName, setAttachmentPreviewName] = useState<string>('');
|
||||||
const attachmentPreviewVideoRef = useRef<HTMLVideoElement>(null);
|
const attachmentPreviewVideoRef = useRef<HTMLVideoElement>(null);
|
||||||
|
|
||||||
@@ -292,6 +293,7 @@ const AIChatPage: React.FC = () => {
|
|||||||
const [fblindex, setFBlindex] = useState<any>(0);
|
const [fblindex, setFBlindex] = useState<any>(0);
|
||||||
|
|
||||||
const [showEngineModal, setShowEngineModal] = useState(false);
|
const [showEngineModal, setShowEngineModal] = useState(false);
|
||||||
|
const [showMediaTypeModal, setShowMediaTypeModal] = useState(false);
|
||||||
|
|
||||||
const [creditRatios, setCreditRatios] = useState<any[]>([]);
|
const [creditRatios, setCreditRatios] = useState<any[]>([]);
|
||||||
const [cimage, setCimage] = useState<any[]>([]);
|
const [cimage, setCimage] = useState<any[]>([]);
|
||||||
@@ -428,6 +430,10 @@ const AIChatPage: React.FC = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleClickOutside = (e: MouseEvent) => {
|
const handleClickOutside = (e: MouseEvent) => {
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
|
// 关闭媒体类型选择弹窗
|
||||||
|
if (showMediaTypeModal && !target.closest('.image-settings-popover') && !target.closest('.image-settings-trigger')) {
|
||||||
|
setShowMediaTypeModal(false);
|
||||||
|
}
|
||||||
// 关闭引擎选择弹窗
|
// 关闭引擎选择弹窗
|
||||||
if (showEngineModal && !target.closest('.image-settings-popover') && !target.closest('.image-settings-trigger')) {
|
if (showEngineModal && !target.closest('.image-settings-popover') && !target.closest('.image-settings-trigger')) {
|
||||||
setShowEngineModal(false);
|
setShowEngineModal(false);
|
||||||
@@ -446,7 +452,7 @@ const AIChatPage: React.FC = () => {
|
|||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('mousedown', handleClickOutside);
|
document.removeEventListener('mousedown', handleClickOutside);
|
||||||
};
|
};
|
||||||
}, [showEngineModal, showImageSettingsModal, showVideoSettingsModal]);
|
}, [showMediaTypeModal, showEngineModal, showImageSettingsModal, showVideoSettingsModal]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (mediaType !== 'video') return;
|
if (mediaType !== 'video') return;
|
||||||
@@ -788,12 +794,8 @@ const AIChatPage: React.FC = () => {
|
|||||||
const isFirstLastFrameMode = mediaType === 'video' && referenceMode === 'first_last_frame';
|
const isFirstLastFrameMode = mediaType === 'video' && referenceMode === 'first_last_frame';
|
||||||
|
|
||||||
if (isFirstLastFrameMode) {
|
if (isFirstLastFrameMode) {
|
||||||
if (!inputValue.trim() && !firstFrame) {
|
if (!inputValue.trim()) {
|
||||||
message.warning('请输入内容或上传首帧图片');
|
message.warning('请输入内容');
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!firstFrame) {
|
|
||||||
message.warning('请上传首帧图片');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -2094,152 +2096,7 @@ const AIChatPage: React.FC = () => {
|
|||||||
<div style={{ display: 'flex', gap: isFirstLastFrameComposer ? 18 : 18, alignItems: 'flex-end', marginBottom: 14 }}>
|
<div style={{ display: 'flex', gap: isFirstLastFrameComposer ? 18 : 18, alignItems: 'flex-end', marginBottom: 14 }}>
|
||||||
{/* 左侧附件区域 */}
|
{/* 左侧附件区域 */}
|
||||||
<div style={{ width: isFirstLastFrameComposer ? 220 : 70, minWidth: isFirstLastFrameComposer ? 220 : 70, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'flex-start', paddingTop: 2, gap: 8 }}>
|
<div style={{ width: isFirstLastFrameComposer ? 220 : 70, minWidth: isFirstLastFrameComposer ? 220 : 70, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'flex-start', paddingTop: 2, gap: 8 }}>
|
||||||
{/* 层叠附件展示 - 在上传按钮上边 */}
|
|
||||||
{currentMedia.length > 0 && !(mediaType === 'video' && referenceMode === 'first_last_frame') && (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: 58,
|
|
||||||
flexShrink: 0,
|
|
||||||
position: 'relative',
|
|
||||||
}}
|
|
||||||
onMouseEnter={openMediaStackTray}
|
|
||||||
onMouseLeave={closeMediaStackTray}
|
|
||||||
>
|
|
||||||
<div style={{ position: 'relative', width: 58, height: 66 }}>
|
|
||||||
{currentMedia.slice(0, 3).map((media, idx) => {
|
|
||||||
const reversedIdx = Math.min(currentMedia.slice(0, 3).length, 3) - 1 - idx;
|
|
||||||
const offsetX = mediaStackHovered ? idx * 8 : idx * 4;
|
|
||||||
const offsetY = mediaStackHovered ? idx * 6 : idx * 3;
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={`stack-${idx}`}
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
left: offsetX,
|
|
||||||
top: offsetY,
|
|
||||||
zIndex: idx + 1,
|
|
||||||
transition: 'all 0.28s cubic-bezier(0.4, 0, 0.2, 1)',
|
|
||||||
transform: `scale(${1 - reversedIdx * 0.06})`,
|
|
||||||
opacity: mediaStackHovered ? 1 : (1 - reversedIdx * 0.15),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ position: 'relative', width: 50, height: 58 }}>
|
|
||||||
{media.type === 'image' ? (
|
|
||||||
<img
|
|
||||||
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${media.url}`}
|
|
||||||
alt={media.name}
|
|
||||||
onClick={() => {
|
|
||||||
setAttachmentPreviewUrl(media.url);
|
|
||||||
setAttachmentPreviewType('image');
|
|
||||||
setAttachmentPreviewName(media.name);
|
|
||||||
setAttachmentPreviewVisible(true);
|
|
||||||
}}
|
|
||||||
style={{ width: 50, height: 58, objectFit: 'cover', borderRadius: 12, cursor: 'pointer', border: '2px solid rgba(255,255,255,0.98)', boxShadow: '0 6px 16px rgba(31,41,55,0.2)' }}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<video
|
|
||||||
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${media.url}`}
|
|
||||||
muted
|
|
||||||
onClick={() => {
|
|
||||||
setAttachmentPreviewUrl(media.url);
|
|
||||||
setAttachmentPreviewType('video');
|
|
||||||
setAttachmentPreviewName(media.name);
|
|
||||||
setAttachmentPreviewVisible(true);
|
|
||||||
}}
|
|
||||||
style={{ width: 50, height: 58, objectFit: 'cover', borderRadius: 12, cursor: 'pointer', border: '2px solid rgba(255,255,255,0.98)', boxShadow: '0 6px 16px rgba(31,41,55,0.2)' }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{currentMedia.length > 3 && (
|
|
||||||
<div style={{ position: 'absolute', right: 0, bottom: 2, zIndex: 10, background: '#8b5cf6', color: '#fff', fontSize: 9, fontWeight: 700, padding: '2px 6px', borderRadius: 999, boxShadow: '0 3px 8px rgba(31,41,55,0.25)' }}>
|
|
||||||
+{currentMedia.length - 3}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{mediaStackHovered && currentMedia.length > 0 && (
|
|
||||||
<div
|
|
||||||
onMouseEnter={openMediaStackTray}
|
|
||||||
onMouseLeave={closeMediaStackTray}
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
left: -10,
|
|
||||||
bottom: 70,
|
|
||||||
width: 280,
|
|
||||||
padding: '12px 12px 12px',
|
|
||||||
borderRadius: 16,
|
|
||||||
background: 'rgba(255,255,255,0.98)',
|
|
||||||
border: '1px solid #E7EAF0',
|
|
||||||
boxShadow: '0 20px 48px rgba(31,41,55,0.18)',
|
|
||||||
backdropFilter: 'blur(16px)',
|
|
||||||
zIndex: 60,
|
|
||||||
pointerEvents: 'auto',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 }}>
|
|
||||||
<span style={{ fontSize: 12, color: '#344054', fontWeight: 700 }}>已上传参考素材</span>
|
|
||||||
<span style={{ fontSize: 11, color: '#98A2B3', fontWeight: 600 }}>{currentMedia.length}/{maxImageCount + maxVideoCount}</span>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: 'flex', gap: 8, overflowX: 'auto', paddingBottom: 4 }}>
|
|
||||||
{currentMedia.map((media, idx) => (
|
|
||||||
<div key={`tray-${idx}`} style={{ position: 'relative', flex: '0 0 auto', width: 54 }}>
|
|
||||||
{media.type === 'image' ? (
|
|
||||||
<img
|
|
||||||
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${media.url}`}
|
|
||||||
alt={media.name}
|
|
||||||
onClick={() => {
|
|
||||||
setAttachmentPreviewUrl(media.url);
|
|
||||||
setAttachmentPreviewType('image');
|
|
||||||
setAttachmentPreviewName(media.name);
|
|
||||||
setAttachmentPreviewVisible(true);
|
|
||||||
}}
|
|
||||||
style={{ width: 54, height: 58, objectFit: 'cover', borderRadius: 10, cursor: 'pointer', border: '1px solid #E7EAF0' }}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<video
|
|
||||||
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${media.url}`}
|
|
||||||
muted
|
|
||||||
onClick={() => {
|
|
||||||
setAttachmentPreviewUrl(media.url);
|
|
||||||
setAttachmentPreviewType('video');
|
|
||||||
setAttachmentPreviewName(media.name);
|
|
||||||
setAttachmentPreviewVisible(true);
|
|
||||||
}}
|
|
||||||
style={{ width: 54, height: 58, objectFit: 'cover', borderRadius: 10, cursor: 'pointer', border: '1px solid #E7EAF0' }}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<button
|
|
||||||
onClick={() => handleRemoveMedia(idx)}
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: 4,
|
|
||||||
right: 4,
|
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
border: 'none',
|
|
||||||
background: 'rgba(0,0,0,0.5)',
|
|
||||||
borderRadius: 50,
|
|
||||||
cursor: 'pointer',
|
|
||||||
color: '#fff',
|
|
||||||
fontSize: 10,
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
backdropFilter: 'blur(4px)',
|
|
||||||
zIndex: 10,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DeleteOutlined style={{ fontSize: 10 }} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 首尾帧模式 */}
|
{/* 首尾帧模式 */}
|
||||||
{mediaType === 'video' && referenceMode === 'first_last_frame' ? (
|
{mediaType === 'video' && referenceMode === 'first_last_frame' ? (
|
||||||
@@ -2345,63 +2202,216 @@ const AIChatPage: React.FC = () => {
|
|||||||
</div>) : (
|
</div>) : (
|
||||||
<>
|
<>
|
||||||
|
|
||||||
{/* 上传按钮 - 非首尾帧模式且还有上传空间时始终显示 */}
|
{/* 上传区域 - 图片堆叠展示,右下角圆形+按钮 */}
|
||||||
{!(mediaType === 'video' && referenceMode === 'first_last_frame') && (
|
{!(mediaType === 'video' && referenceMode === 'first_last_frame') && (
|
||||||
<div style={{ width: '100%', display: 'flex', justifyContent: 'center', minHeight: 84 }}>
|
<div
|
||||||
<Upload
|
style={{
|
||||||
accept={mediaType === 'image' ? 'image/*' : 'image/*,video/*,audio/*'}
|
position: 'relative',
|
||||||
showUploadList={false}
|
width: 64,
|
||||||
beforeUpload={handleUpload}
|
height: 72,
|
||||||
>
|
marginTop: '-10',
|
||||||
<Tooltip title={mediaType === 'image'
|
}}
|
||||||
? `图片${currentMedia.filter(m => m.type === 'image').length}/${maxImageCount}`
|
onMouseEnter={openMediaStackTray}
|
||||||
: `图片${currentMedia.filter(m => m.type === 'image').length}/${maxImageCount},视频${currentMedia.filter(m => m.type === 'video').length}/${maxVideoCount}`
|
onMouseLeave={closeMediaStackTray}
|
||||||
}>
|
>
|
||||||
<div
|
{/* 没有上传时的卡片样式 */}
|
||||||
style={{
|
{currentMedia.length === 0 && (
|
||||||
width: 54,
|
<Upload
|
||||||
height: 74,
|
accept={mediaType === 'image' ? 'image/*' : 'image/*,video/*,audio/*'}
|
||||||
borderRadius: 7,
|
showUploadList={false}
|
||||||
border: '1px solid rgba(231, 234, 240, 0.95)',
|
beforeUpload={handleUpload}
|
||||||
display: 'flex',
|
>
|
||||||
alignItems: 'center',
|
<Tooltip title={mediaType === 'image'
|
||||||
justifyContent: 'center',
|
? `图片${currentMedia.filter(m => m.type === 'image').length}/${maxImageCount}`
|
||||||
cursor: 'pointer',
|
: `图片${currentMedia.filter(m => m.type === 'image').length}/${maxImageCount},视频${currentMedia.filter(m => m.type === 'video').length}/${maxVideoCount}`
|
||||||
transition: 'all 0.25s ease',
|
}>
|
||||||
background: '#ffffff',
|
<div
|
||||||
flexDirection: 'column',
|
style={{
|
||||||
gap: 5,
|
width: 54,
|
||||||
transform: 'rotate(-7deg)',
|
height: 74,
|
||||||
boxShadow: '0 9px 20px rgba(47, 52, 64, 0.10), inset 0 1px 0 rgba(255,255,255,0.95)',
|
borderRadius: 7,
|
||||||
}}
|
border: '1px solid rgba(231, 234, 240, 0.95)',
|
||||||
onMouseEnter={(e) => {
|
display: 'flex',
|
||||||
e.currentTarget.style.borderColor = '#D7DDE7';
|
alignItems: 'center',
|
||||||
e.currentTarget.style.background = 'linear-gradient(180deg, #ffffff 0%, #F7F8FA 100%)';
|
justifyContent: 'center',
|
||||||
e.currentTarget.style.transform = 'rotate(0deg) translateY(-2px)';
|
cursor: 'pointer',
|
||||||
e.currentTarget.style.boxShadow = '0 14px 28px rgba(47, 52, 64, 0.15), inset 0 1px 0 rgba(255,255,255,0.98)';
|
transition: 'all 0.25s ease',
|
||||||
}}
|
background: '#ffffff',
|
||||||
onMouseLeave={(e) => {
|
flexDirection: 'column',
|
||||||
e.currentTarget.style.borderColor = 'rgba(231, 234, 240, 0.95)';
|
gap: 5,
|
||||||
e.currentTarget.style.background = 'linear-gradient(180deg, #ffffff 0%, #FAFBFC 100%)';
|
transform: 'rotate(-7deg)',
|
||||||
e.currentTarget.style.transform = 'rotate(-7deg)';
|
boxShadow: '0 9px 20px rgba(47, 52, 64, 0.10), inset 0 1px 0 rgba(255,255,255,0.95)',
|
||||||
e.currentTarget.style.boxShadow = '0 9px 20px rgba(47, 52, 64, 0.10), inset 0 1px 0 rgba(255,255,255,0.95)';
|
}}
|
||||||
}}
|
onMouseEnter={(e) => {
|
||||||
>
|
e.currentTarget.style.borderColor = '#D7DDE7';
|
||||||
{uploading ? (
|
e.currentTarget.style.background = 'linear-gradient(180deg, #ffffff 0%, #F7F8FA 100%)';
|
||||||
<LoadingOutlined style={{ fontSize: 17, color: '#667085' }} />
|
e.currentTarget.style.transform = 'rotate(0deg) translateY(-2px)';
|
||||||
) : (
|
e.currentTarget.style.boxShadow = '0 14px 28px rgba(47, 52, 64, 0.15), inset 0 1px 0 rgba(255,255,255,0.98)';
|
||||||
<>
|
}}
|
||||||
<PlusOutlined style={{ fontSize: 18, color: '#667085', lineHeight: 1 }} />
|
onMouseLeave={(e) => {
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 14px)', columnGap: 2, justifyContent: 'center', color: '#344054', fontSize: 12, fontWeight: 700, lineHeight: 1.05, letterSpacing: 0 }}>
|
e.currentTarget.style.borderColor = 'rgba(231, 234, 240, 0.95)';
|
||||||
<span style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 1 }}><span>参</span><span>考</span></span>
|
e.currentTarget.style.background = 'linear-gradient(180deg, #ffffff 0%, #FAFBFC 100%)';
|
||||||
<span style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 1 }}><span>内</span><span>容</span></span>
|
e.currentTarget.style.transform = 'rotate(-7deg)';
|
||||||
|
e.currentTarget.style.boxShadow = '0 9px 20px rgba(47, 52, 64, 0.10), inset 0 1px 0 rgba(255,255,255,0.95)';
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{uploading ? (
|
||||||
|
<LoadingOutlined style={{ fontSize: 17, color: '#667085' }} />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<PlusOutlined style={{ fontSize: 18, color: '#667085', lineHeight: 1 }} />
|
||||||
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 14px)', columnGap: 2, justifyContent: 'center', color: '#344054', fontSize: 12, fontWeight: 700, lineHeight: 1.05, letterSpacing: 0 }}>
|
||||||
|
<span style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 1 }}><span>参</span><span>考</span></span>
|
||||||
|
<span style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 1 }}><span>内</span><span>容</span></span>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</Upload>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 层叠附件展示 - 鼠标移入向右排列展开 */}
|
||||||
|
{currentMedia.length > 0 && (
|
||||||
|
<>
|
||||||
|
{currentMedia.map((media, idx) => {
|
||||||
|
const reversedIdx = Math.min(currentMedia.length, 3) - 1 - idx;
|
||||||
|
const offsetX = mediaStackHovered ? idx * 58 : (idx < 3 ? idx * 6 : 18);
|
||||||
|
const isVisible = !mediaStackHovered ? idx < 3 : true;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={`stack-${idx}`}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
left: offsetX,
|
||||||
|
bottom: 0,
|
||||||
|
zIndex: idx + 1,
|
||||||
|
transition: 'all 0.28s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||||
|
transform: mediaStackHovered ? `scale(1) rotate(0deg)` : `scale(${idx < 3 ? (1 - reversedIdx * 0.08) : 0}) rotate(${idx < 3 ? (-3 + idx * 1.5) : 0}deg)`,
|
||||||
|
opacity: isVisible ? (mediaStackHovered ? 1 : (1 - reversedIdx * 0.15)) : 0,
|
||||||
|
pointerEvents: isVisible ? 'auto' : 'none',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ position: 'relative', width: 52, height: 60 }}>
|
||||||
|
{media.type === 'image' ? (
|
||||||
|
<img
|
||||||
|
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${media.url}`}
|
||||||
|
alt={media.name}
|
||||||
|
onClick={() => {
|
||||||
|
setAttachmentPreviewUrl(media.url);
|
||||||
|
setAttachmentPreviewType('image');
|
||||||
|
setAttachmentPreviewName(media.name);
|
||||||
|
setAttachmentPreviewVisible(true);
|
||||||
|
}}
|
||||||
|
style={{ width: 52, height: 60, objectFit: 'cover', borderRadius: 10, cursor: 'pointer', border: '1px solid rgba(255,255,255,0.98)', boxShadow: '0 4px 12px rgba(31,41,55,0.15)' }}
|
||||||
|
/>
|
||||||
|
) : media.type === 'video' ? (
|
||||||
|
<video
|
||||||
|
src={`${import.meta.env.VITE_API_BASE || "http://localhost:8000"}${media.url}`}
|
||||||
|
muted
|
||||||
|
onClick={() => {
|
||||||
|
setAttachmentPreviewUrl(media.url);
|
||||||
|
setAttachmentPreviewType('video');
|
||||||
|
setAttachmentPreviewName(media.name);
|
||||||
|
setAttachmentPreviewVisible(true);
|
||||||
|
}}
|
||||||
|
style={{ width: 52, height: 60, objectFit: 'cover', borderRadius: 10, cursor: 'pointer', border: '1px solid rgba(255,255,255,0.98)', boxShadow: '0 4px 12px rgba(31,41,55,0.15)' }}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
onClick={() => {
|
||||||
|
setAttachmentPreviewUrl(media.url);
|
||||||
|
setAttachmentPreviewType('audio');
|
||||||
|
setAttachmentPreviewName(media.name);
|
||||||
|
setAttachmentPreviewVisible(true);
|
||||||
|
}}
|
||||||
|
style={{ width: 52, height: 60, objectFit: 'cover', borderRadius: 10, cursor: 'pointer', border: '1px solid rgba(255,255,255,0.98)', boxShadow: '0 4px 12px rgba(31,41,55,0.15)', background: 'linear-gradient(135deg, #8b5cf6 0%, #a78bfa 100%)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}
|
||||||
|
>
|
||||||
|
<AudioOutlined style={{ fontSize: 20, color: '#fff' }} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{/* 右上角删除按钮 */}
|
||||||
|
<button
|
||||||
|
onClick={() => handleRemoveMedia(idx)}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: -6,
|
||||||
|
right: -6,
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
borderRadius: 50,
|
||||||
|
background: '#1f2937',
|
||||||
|
border: 'none',
|
||||||
|
color: '#fff',
|
||||||
|
cursor: 'pointer',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
boxShadow: '0 2px 6px rgba(0,0,0,0.2)',
|
||||||
|
zIndex: 10,
|
||||||
|
transition: 'all 0.2s ease',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.background = '#dc2626';
|
||||||
|
e.currentTarget.style.transform = 'scale(1.1)';
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.background = '#1f2937';
|
||||||
|
e.currentTarget.style.transform = 'scale(1)';
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DeleteOutlined style={{ fontSize: 10 }} />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/* <span style={{ fontSize: 9, color: '#98a2b3', fontWeight: 600, lineHeight: 1 }}>{currentMedia.length}/{maxImageCount + maxVideoCount}</span> */}
|
</div>
|
||||||
</>
|
);
|
||||||
)}
|
})}
|
||||||
</div>
|
</>
|
||||||
</Tooltip>
|
)}
|
||||||
</Upload>
|
|
||||||
|
{/* 右下角圆形+上传按钮 */}
|
||||||
|
{currentMedia.length > 0 && (
|
||||||
|
<Upload
|
||||||
|
accept={mediaType === 'image' ? 'image/*' : 'image/*,video/*,audio/*'}
|
||||||
|
showUploadList={false}
|
||||||
|
beforeUpload={handleUpload}
|
||||||
|
>
|
||||||
|
<Tooltip title={mediaType === 'image'
|
||||||
|
? `图片${currentMedia.filter(m => m.type === 'image').length}/${maxImageCount}`
|
||||||
|
: `图片${currentMedia.filter(m => m.type === 'image').length}/${maxImageCount},视频${currentMedia.filter(m => m.type === 'video').length}/${maxVideoCount}`
|
||||||
|
}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
width: 28,
|
||||||
|
height: 28,
|
||||||
|
borderRadius: 50,
|
||||||
|
background: '#ffffff',
|
||||||
|
border: '1px solid rgba(231, 234, 240, 0.95)',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
cursor: 'pointer',
|
||||||
|
transition: 'all 0.2s ease',
|
||||||
|
boxShadow: '0 2px 8px rgba(47, 52, 64, 0.08)',
|
||||||
|
zIndex: 100,
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.borderColor = '#8b5cf6';
|
||||||
|
e.currentTarget.style.boxShadow = '0 4px 12px rgba(139, 92, 246, 0.2)';
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.borderColor = 'rgba(231, 234, 240, 0.95)';
|
||||||
|
e.currentTarget.style.boxShadow = '0 2px 8px rgba(47, 52, 64, 0.08)';
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PlusOutlined style={{ fontSize: 14, color: '#8b5cf6', lineHeight: 1 }} />
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</Upload>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
@@ -2521,36 +2531,144 @@ const AIChatPage: React.FC = () => {
|
|||||||
创作类型:
|
创作类型:
|
||||||
</span> */}
|
</span> */}
|
||||||
{/* 文件类型选择器 */}
|
{/* 文件类型选择器 */}
|
||||||
<Select
|
<div style={{ position: 'relative', display: 'inline-block' }}>
|
||||||
className="ai-composer-select"
|
<button
|
||||||
value={mediaType}
|
onClick={() => {
|
||||||
onChange={(val) => setMediaType(val)}
|
setShowEngineModal(false);
|
||||||
style={{
|
setShowImageSettingsModal(false);
|
||||||
minWidth: 118,
|
setShowVideoSettingsModal(false);
|
||||||
padding: '6px 14px',
|
setReferenceModeDropdownVisible(false);
|
||||||
height: 32,
|
setShowMediaTypeModal(prev => !prev);
|
||||||
outline: 'none',
|
}}
|
||||||
border: '1px solid #E7EAF0',
|
className="image-settings-trigger"
|
||||||
backgroundColor: '#ffffff',
|
style={{
|
||||||
borderRadius: 10,
|
minWidth: 118,
|
||||||
boxShadow: '0 1px 2px rgba(47, 52, 64, 0.025)',
|
padding: '5px 12px',
|
||||||
}}
|
height: 32,
|
||||||
size="middle"
|
borderRadius: 10,
|
||||||
>
|
border: '1px solid #E7EAF0',
|
||||||
<Option value="video">
|
backgroundColor: '#ffffff',
|
||||||
<VideoCameraOutlined style={{ marginRight: 6, fontSize: 14, color: '#8b5cf6' }} />
|
cursor: 'pointer',
|
||||||
<span style={{ fontSize: 13, fontWeight: 650, color: '#475467' }}>视频生成</span>
|
display: 'inline-flex',
|
||||||
</Option>
|
alignItems: 'center',
|
||||||
<Option value="image">
|
gap: 6,
|
||||||
<PictureOutlined style={{ marginRight: 6, fontSize: 14, color: '#8b5cf6' }} />
|
transition: 'all 0.2s ease',
|
||||||
<span style={{ fontSize: 13, fontWeight: 650, color: '#475467' }}>图片生成</span>
|
boxShadow: '0 1px 2px rgba(47, 52, 64, 0.025)',
|
||||||
</Option>
|
}}
|
||||||
</Select>
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.backgroundColor = '#FFFFFF';
|
||||||
|
e.currentTarget.style.borderColor = '#D7DDE7';
|
||||||
|
e.currentTarget.style.boxShadow = '0 2px 8px rgba(47, 52, 64, 0.06)';
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.backgroundColor = '#ffffff';
|
||||||
|
e.currentTarget.style.borderColor = '#E7EAF0';
|
||||||
|
e.currentTarget.style.boxShadow = '0 1px 2px rgba(47, 52, 64, 0.03)';
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{mediaType === 'video' ? (
|
||||||
|
<VideoCameraOutlined style={{ fontSize: 14, color: '#8b5cf6' }} />
|
||||||
|
) : (
|
||||||
|
<PictureOutlined style={{ fontSize: 14, color: '#8b5cf6' }} />
|
||||||
|
)}
|
||||||
|
<Text style={{
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: 500,
|
||||||
|
color: '#2f3440',
|
||||||
|
}}>
|
||||||
|
{mediaType === 'video' ? '视频生成' : '图片生成'}
|
||||||
|
</Text>
|
||||||
|
<CaretDownOutlined style={{ fontSize: 10, color: '#8b5cf6', marginLeft: 'auto' }} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{showMediaTypeModal && (
|
||||||
|
<div
|
||||||
|
className="image-settings-popover"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 'calc(100% + 8px)',
|
||||||
|
left: 0,
|
||||||
|
width: 300,
|
||||||
|
backgroundColor: '#ffffff',
|
||||||
|
backdropFilter: 'blur(16px)',
|
||||||
|
borderRadius: 16,
|
||||||
|
boxShadow: '0 20px 48px rgba(47, 52, 64, 0.12)',
|
||||||
|
padding: 16,
|
||||||
|
border: '1px solid #E7EAF0',
|
||||||
|
zIndex: 9999,
|
||||||
|
}}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<div style={{ marginBottom: 8 }}>
|
||||||
|
<Text style={{
|
||||||
|
display: 'block',
|
||||||
|
marginBottom: 8,
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 600,
|
||||||
|
color: '#475467',
|
||||||
|
}}>
|
||||||
|
创作类型
|
||||||
|
</Text>
|
||||||
|
<div style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: 4,
|
||||||
|
}}>
|
||||||
|
{[
|
||||||
|
{ value: 'video', label: '视频生成', icon: VideoCameraOutlined },
|
||||||
|
{ value: 'image', label: '图片生成', icon: PictureOutlined },
|
||||||
|
].map((item) => (
|
||||||
|
<button
|
||||||
|
key={item.value}
|
||||||
|
onClick={() => {
|
||||||
|
setMediaType(item.value);
|
||||||
|
setShowMediaTypeModal(false);
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
minHeight: 48,
|
||||||
|
borderRadius: 10,
|
||||||
|
border: mediaType === item.value
|
||||||
|
? '1px solid #8b5cf6'
|
||||||
|
: '1px solid #E7EAF0',
|
||||||
|
backgroundColor: mediaType === item.value
|
||||||
|
? '#f5f3ff'
|
||||||
|
: '#ffffff',
|
||||||
|
cursor: 'pointer',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: '8px 12px',
|
||||||
|
transition: 'all 0.2s',
|
||||||
|
textAlign: 'left',
|
||||||
|
gap: 8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<item.icon style={{ fontSize: 14, color: mediaType === item.value ? '#8b5cf6' : '#98a2b3' }} />
|
||||||
|
<span style={{
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: mediaType === item.value ? 600 : 500,
|
||||||
|
color: mediaType === item.value ? '#8b5cf6' : '#475467',
|
||||||
|
}}>
|
||||||
|
{item.label}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 引擎选择器 */}
|
{/* 引擎选择器 */}
|
||||||
<div style={{ position: 'relative', display: 'inline-block' }}>
|
<div style={{ position: 'relative', display: 'inline-block' }}>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowEngineModal(prev => !prev)}
|
onClick={() => {
|
||||||
|
setShowMediaTypeModal(false);
|
||||||
|
setShowImageSettingsModal(false);
|
||||||
|
setShowVideoSettingsModal(false);
|
||||||
|
setReferenceModeDropdownVisible(false);
|
||||||
|
setShowEngineModal(prev => !prev);
|
||||||
|
}}
|
||||||
className="image-settings-trigger"
|
className="image-settings-trigger"
|
||||||
style={{
|
style={{
|
||||||
minWidth: 174,
|
minWidth: 174,
|
||||||
@@ -2692,7 +2810,13 @@ const AIChatPage: React.FC = () => {
|
|||||||
{mediaType === 'video' && (supportsFirstLastFrame || supportsUniversalReference) && (
|
{mediaType === 'video' && (supportsFirstLastFrame || supportsUniversalReference) && (
|
||||||
<div style={{ position: 'relative', display: 'inline-block' }}>
|
<div style={{ position: 'relative', display: 'inline-block' }}>
|
||||||
<button
|
<button
|
||||||
onClick={() => setReferenceModeDropdownVisible(!referenceModeDropdownVisible)}
|
onClick={() => {
|
||||||
|
setShowMediaTypeModal(false);
|
||||||
|
setShowEngineModal(false);
|
||||||
|
setShowImageSettingsModal(false);
|
||||||
|
setShowVideoSettingsModal(false);
|
||||||
|
setReferenceModeDropdownVisible(!referenceModeDropdownVisible);
|
||||||
|
}}
|
||||||
className="image-settings-trigger"
|
className="image-settings-trigger"
|
||||||
style={{
|
style={{
|
||||||
minWidth: 118,
|
minWidth: 118,
|
||||||
@@ -2731,127 +2855,117 @@ const AIChatPage: React.FC = () => {
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
{referenceModeDropdownVisible && (
|
{referenceModeDropdownVisible && (
|
||||||
<>
|
<div
|
||||||
<div
|
className="image-settings-popover"
|
||||||
style={{
|
style={{
|
||||||
position: 'fixed',
|
position: 'absolute',
|
||||||
top: 0,
|
bottom: 'calc(100% + 8px)',
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
width: 300,
|
||||||
bottom: 0,
|
backgroundColor: '#ffffff',
|
||||||
zIndex: 9998,
|
backdropFilter: 'blur(16px)',
|
||||||
}}
|
borderRadius: 16,
|
||||||
onClick={() => setReferenceModeDropdownVisible(false)}
|
boxShadow: '0 20px 48px rgba(47, 52, 64, 0.12)',
|
||||||
/>
|
padding: 16,
|
||||||
<div
|
border: '1px solid #E7EAF0',
|
||||||
className="image-settings-popover"
|
zIndex: 9999,
|
||||||
style={{
|
}}
|
||||||
position: 'absolute',
|
onClick={(e) => e.stopPropagation()}
|
||||||
bottom: 'calc(100% + 8px)',
|
>
|
||||||
left: 0,
|
<div style={{ marginBottom: 8 }}>
|
||||||
minWidth: 160,
|
<Text style={{
|
||||||
backgroundColor: '#ffffff',
|
display: 'block',
|
||||||
backdropFilter: 'blur(16px)',
|
marginBottom: 8,
|
||||||
borderRadius: 16,
|
fontSize: 12,
|
||||||
boxShadow: '0 20px 48px rgba(47, 52, 64, 0.12)',
|
fontWeight: 600,
|
||||||
padding: 8,
|
color: '#475467',
|
||||||
border: '1px solid #E7EAF0',
|
}}>
|
||||||
zIndex: 9999,
|
参考模式
|
||||||
}}
|
</Text>
|
||||||
onClick={(e) => e.stopPropagation()}
|
<div style={{
|
||||||
>
|
display: 'flex',
|
||||||
<button
|
flexDirection: 'column',
|
||||||
onClick={() => handleReferenceModeChange('universal')}
|
gap: 4,
|
||||||
disabled={!supportsUniversalReference}
|
}}>
|
||||||
style={{
|
<button
|
||||||
width: '100%',
|
onClick={() => {
|
||||||
padding: '10px 14px',
|
handleReferenceModeChange('universal');
|
||||||
borderRadius: 10,
|
setReferenceModeDropdownVisible(false);
|
||||||
border: referenceMode === 'universal'
|
}}
|
||||||
? '1px solid rgba(139, 92, 246, 0.3)'
|
disabled={!supportsUniversalReference}
|
||||||
: '1px solid transparent',
|
style={{
|
||||||
backgroundColor: referenceMode === 'universal'
|
flex: 1,
|
||||||
? 'rgba(139, 92, 246, 0.08)'
|
minHeight: 48,
|
||||||
: 'transparent',
|
borderRadius: 10,
|
||||||
cursor: supportsUniversalReference ? 'pointer' : 'not-allowed',
|
border: referenceMode === 'universal'
|
||||||
display: 'flex',
|
? '1px solid #8b5cf6'
|
||||||
alignItems: 'center',
|
: '1px solid #E7EAF0',
|
||||||
gap: 8,
|
backgroundColor: referenceMode === 'universal'
|
||||||
transition: 'all 0.2s',
|
? '#f5f3ff'
|
||||||
textAlign: 'left',
|
: '#ffffff',
|
||||||
opacity: supportsUniversalReference ? 1 : 0.4,
|
cursor: supportsUniversalReference ? 'pointer' : 'not-allowed',
|
||||||
}}
|
display: 'flex',
|
||||||
onMouseEnter={(e) => {
|
alignItems: 'center',
|
||||||
if (supportsUniversalReference && referenceMode !== 'universal') {
|
padding: '8px 12px',
|
||||||
e.currentTarget.style.backgroundColor = 'rgba(139, 92, 246, 0.04)';
|
transition: 'all 0.2s',
|
||||||
}
|
textAlign: 'left',
|
||||||
}}
|
gap: 8,
|
||||||
onMouseLeave={(e) => {
|
opacity: supportsUniversalReference ? 1 : 0.4,
|
||||||
if (referenceMode !== 'universal') {
|
}}
|
||||||
e.currentTarget.style.backgroundColor = 'transparent';
|
>
|
||||||
}
|
<PictureOutlined style={{ fontSize: 14, color: referenceMode === 'universal' ? '#8b5cf6' : '#98a2b3' }} />
|
||||||
}}
|
<span style={{
|
||||||
>
|
fontSize: 14,
|
||||||
<PictureOutlined style={{ fontSize: 14, color: '#8b5cf6' }} />
|
fontWeight: referenceMode === 'universal' ? 600 : 500,
|
||||||
<span style={{
|
color: referenceMode === 'universal' ? '#8b5cf6' : '#475467',
|
||||||
fontSize: 13,
|
}}>
|
||||||
fontWeight: referenceMode === 'universal' ? 600 : 500,
|
全能参考
|
||||||
color: referenceMode === 'universal' ? '#8b5cf6' : '#475467',
|
</span>
|
||||||
flex: 1,
|
{!supportsUniversalReference && (
|
||||||
}}>
|
<span style={{ fontSize: 10, color: '#98a2b3', marginLeft: 'auto' }}>不支持</span>
|
||||||
全能参考
|
)}
|
||||||
</span>
|
</button>
|
||||||
{!supportsUniversalReference && (
|
<button
|
||||||
<span style={{ fontSize: 10, color: '#98a2b3' }}>不支持</span>
|
onClick={() => {
|
||||||
)}
|
handleReferenceModeChange('first_last_frame');
|
||||||
</button>
|
setReferenceModeDropdownVisible(false);
|
||||||
<button
|
}}
|
||||||
onClick={() => handleReferenceModeChange('first_last_frame')}
|
disabled={!supportsFirstLastFrame}
|
||||||
disabled={!supportsFirstLastFrame}
|
style={{
|
||||||
style={{
|
flex: 1,
|
||||||
width: '100%',
|
minHeight: 48,
|
||||||
padding: '10px 14px',
|
borderRadius: 10,
|
||||||
borderRadius: 10,
|
border: referenceMode === 'first_last_frame'
|
||||||
border: referenceMode === 'first_last_frame'
|
? '1px solid #8b5cf6'
|
||||||
? '1px solid rgba(139, 92, 246, 0.3)'
|
: '1px solid #E7EAF0',
|
||||||
: '1px solid transparent',
|
backgroundColor: referenceMode === 'first_last_frame'
|
||||||
backgroundColor: referenceMode === 'first_last_frame'
|
? '#f5f3ff'
|
||||||
? 'rgba(139, 92, 246, 0.08)'
|
: '#ffffff',
|
||||||
: 'transparent',
|
cursor: supportsFirstLastFrame ? 'pointer' : 'not-allowed',
|
||||||
cursor: supportsFirstLastFrame ? 'pointer' : 'not-allowed',
|
display: 'flex',
|
||||||
display: 'flex',
|
alignItems: 'center',
|
||||||
alignItems: 'center',
|
padding: '8px 12px',
|
||||||
gap: 8,
|
transition: 'all 0.2s',
|
||||||
transition: 'all 0.2s',
|
textAlign: 'left',
|
||||||
textAlign: 'left',
|
gap: 8,
|
||||||
opacity: supportsFirstLastFrame ? 1 : 0.4,
|
opacity: supportsFirstLastFrame ? 1 : 0.4,
|
||||||
}}
|
}}
|
||||||
onMouseEnter={(e) => {
|
>
|
||||||
if (supportsFirstLastFrame && referenceMode !== 'first_last_frame') {
|
<SwapOutlined style={{ fontSize: 14, color: referenceMode === 'first_last_frame' ? '#8b5cf6' : '#98a2b3' }} />
|
||||||
e.currentTarget.style.backgroundColor = 'rgba(139, 92, 246, 0.04)';
|
<span style={{
|
||||||
}
|
fontSize: 14,
|
||||||
}}
|
fontWeight: referenceMode === 'first_last_frame' ? 600 : 500,
|
||||||
onMouseLeave={(e) => {
|
color: referenceMode === 'first_last_frame' ? '#8b5cf6' : '#475467',
|
||||||
if (referenceMode !== 'first_last_frame') {
|
}}>
|
||||||
e.currentTarget.style.backgroundColor = 'transparent';
|
首尾帧
|
||||||
}
|
</span>
|
||||||
}}
|
{!supportsFirstLastFrame && (
|
||||||
>
|
<span style={{ fontSize: 10, color: '#98a2b3', marginLeft: 'auto' }}>不支持</span>
|
||||||
<SwapOutlined style={{ fontSize: 14, color: '#8b5cf6' }} />
|
)}
|
||||||
<span style={{
|
</button>
|
||||||
fontSize: 13,
|
</div>
|
||||||
fontWeight: referenceMode === 'first_last_frame' ? 600 : 500,
|
|
||||||
color: referenceMode === 'first_last_frame' ? '#8b5cf6' : '#475467',
|
|
||||||
flex: 1,
|
|
||||||
}}>
|
|
||||||
首尾帧
|
|
||||||
</span>
|
|
||||||
{!supportsFirstLastFrame && (
|
|
||||||
<span style={{ fontSize: 10, color: '#98a2b3' }}>不支持</span>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -2860,7 +2974,13 @@ const AIChatPage: React.FC = () => {
|
|||||||
{mediaType === 'image' && (
|
{mediaType === 'image' && (
|
||||||
<div style={{ position: 'relative', display: 'inline-block' }}>
|
<div style={{ position: 'relative', display: 'inline-block' }}>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowImageSettingsModal(!showImageSettingsModal)}
|
onClick={() => {
|
||||||
|
setShowMediaTypeModal(false);
|
||||||
|
setShowEngineModal(false);
|
||||||
|
setShowVideoSettingsModal(false);
|
||||||
|
setReferenceModeDropdownVisible(false);
|
||||||
|
setShowImageSettingsModal(!showImageSettingsModal);
|
||||||
|
}}
|
||||||
className="image-settings-trigger"
|
className="image-settings-trigger"
|
||||||
style={{
|
style={{
|
||||||
minWidth: 156,
|
minWidth: 156,
|
||||||
@@ -3164,7 +3284,13 @@ const AIChatPage: React.FC = () => {
|
|||||||
{mediaType === 'video' && (
|
{mediaType === 'video' && (
|
||||||
<div style={{ position: 'relative', display: 'inline-block' }}>
|
<div style={{ position: 'relative', display: 'inline-block' }}>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowVideoSettingsModal(!showVideoSettingsModal)}
|
onClick={() => {
|
||||||
|
setShowMediaTypeModal(false);
|
||||||
|
setShowEngineModal(false);
|
||||||
|
setShowImageSettingsModal(false);
|
||||||
|
setReferenceModeDropdownVisible(false);
|
||||||
|
setShowVideoSettingsModal(!showVideoSettingsModal);
|
||||||
|
}}
|
||||||
className="image-settings-trigger"
|
className="image-settings-trigger"
|
||||||
style={{
|
style={{
|
||||||
minWidth: 156,
|
minWidth: 156,
|
||||||
|
|||||||
Reference in New Issue
Block a user