1
This commit is contained in:
@@ -992,6 +992,58 @@ const AIChatPage: React.FC = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const getImageDimensions = (file: File): Promise<{ width: number; height: number }> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
window.URL.revokeObjectURL(img.src);
|
||||
resolve({ width: img.naturalWidth, height: img.naturalHeight });
|
||||
};
|
||||
img.onerror = () => {
|
||||
window.URL.revokeObjectURL(img.src);
|
||||
reject(new Error('无法读取图片尺寸'));
|
||||
};
|
||||
img.src = URL.createObjectURL(file);
|
||||
});
|
||||
};
|
||||
|
||||
const getVideoDimensions = (file: File): Promise<{ width: number; height: number; duration: number }> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const video = document.createElement('video');
|
||||
video.preload = 'metadata';
|
||||
video.onloadedmetadata = () => {
|
||||
window.URL.revokeObjectURL(video.src);
|
||||
resolve({ width: video.videoWidth, height: video.videoHeight, duration: video.duration });
|
||||
};
|
||||
video.onerror = () => {
|
||||
window.URL.revokeObjectURL(video.src);
|
||||
reject(new Error('无法读取视频尺寸'));
|
||||
};
|
||||
video.src = URL.createObjectURL(file);
|
||||
});
|
||||
};
|
||||
|
||||
// 图片尺寸校验:宽高比 (0.4, 2.5),宽高长度 (300, 6000)
|
||||
const validateImageDimensions = (width: number, height: number): string | null => {
|
||||
if (width < 300 || width > 6000) return `图片宽度需在 300~6000px 之间,当前为 ${width}px`;
|
||||
if (height < 300 || height > 6000) return `图片高度需在 300~6000px 之间,当前为 ${height}px`;
|
||||
const ratio = width / height;
|
||||
if (ratio < 0.4 || ratio > 2.5) return `图片宽高比需在 0.4~2.5 之间,当前为 ${ratio.toFixed(2)}`;
|
||||
return null;
|
||||
};
|
||||
|
||||
// 视频尺寸校验:宽高比 [0.4, 2.5],宽高 [300, 6000],总像素 [409600, 8295044]
|
||||
const validateVideoDimensions = (width: number, height: number): string | null => {
|
||||
if (width < 300 || width > 6000) return `视频宽度需在 300~6000px 之间,当前为 ${width}px`;
|
||||
if (height < 300 || height > 6000) return `视频高度需在 300~6000px 之间,当前为 ${height}px`;
|
||||
const ratio = width / height;
|
||||
if (ratio < 0.4 || ratio > 2.5) return `视频宽高比需在 0.4~2.5 之间,当前为 ${ratio.toFixed(2)}`;
|
||||
const totalPixels = width * height;
|
||||
if (totalPixels < 409600) return `视频总像素数过小(${width}×${height}=${totalPixels}),需 ≥ 640×640=409600`;
|
||||
if (totalPixels > 8295044) return `视频总像素数过大(${width}×${height}=${totalPixels}),需 ≤ 3326×2494=8295044`;
|
||||
return null;
|
||||
};
|
||||
|
||||
const handleUpload = async (file: File, frameTarget?: 'first' | 'last' | File[]) => {
|
||||
const isImage = file.type.startsWith('image/');
|
||||
const isVideo = file.type.startsWith('video/');
|
||||
@@ -1059,6 +1111,21 @@ const AIChatPage: React.FC = () => {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 图片尺寸校验
|
||||
if (isImage && mediaType !== 'video') {
|
||||
try {
|
||||
const { width, height } = await getImageDimensions(file);
|
||||
const error = validateImageDimensions(width, height);
|
||||
if (error) {
|
||||
message.error(error);
|
||||
return false;
|
||||
}
|
||||
} catch {
|
||||
message.error('无法读取图片尺寸,请检查文件是否损坏');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const imageCount = currentMedia.filter((m) => m.type === 'image').length;
|
||||
if (isImage && imageCount >= maxImage) {
|
||||
message.error(`该引擎最多上传${maxImage}张图片`);
|
||||
@@ -1086,8 +1153,15 @@ const AIChatPage: React.FC = () => {
|
||||
message.error(`所有视频素材总时长不能超过 15 秒,当前 ${(existingVideoDuration + videoDuration).toFixed(1)} 秒`);
|
||||
return false;
|
||||
}
|
||||
// 视频尺寸校验
|
||||
const { width, height } = await getVideoDimensions(file);
|
||||
const error = validateVideoDimensions(width, height);
|
||||
if (error) {
|
||||
message.error(error);
|
||||
return false;
|
||||
}
|
||||
} catch {
|
||||
message.error('无法获取视频时长');
|
||||
message.error('无法获取视频信息,请检查文件是否损坏');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2016,7 +2090,7 @@ const AIChatPage: React.FC = () => {
|
||||
}}
|
||||
>
|
||||
{/* 上方输入布局 */}
|
||||
<div style={{ display: 'flex', gap: isFirstLastFrameComposer ? 18 : 18, alignItems: 'flex-start', marginBottom: 14 }}>
|
||||
<div style={{ display: 'flex', gap: isFirstLastFrameComposer ? 18 : 18, alignItems: 'flex-end', marginBottom: 14 }}>
|
||||
{/* 左侧附件区域 */}
|
||||
<div style={{ width: isFirstLastFrameComposer ? 220 : 128, minWidth: isFirstLastFrameComposer ? 220 : 128, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'flex-start', paddingTop: 2 }}>
|
||||
{/* 首尾帧模式 */}
|
||||
@@ -2173,29 +2247,31 @@ const AIChatPage: React.FC = () => {
|
||||
style={{ width: 50, height: 54, objectFit: 'cover', borderRadius: 11, cursor: 'pointer', border: '2px solid rgba(255,255,255,0.98)', boxShadow: '0 8px 18px rgba(31, 41, 55, 0.14)' }}
|
||||
/>
|
||||
)}
|
||||
<button
|
||||
onClick={() => handleRemoveMedia(idx)}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: -5,
|
||||
right: -5,
|
||||
width: 18,
|
||||
height: 18,
|
||||
border: '1px solid rgba(255,255,255,0.9)',
|
||||
background: '#A45B5B',
|
||||
borderRadius: 50,
|
||||
cursor: 'pointer',
|
||||
color: '#fff',
|
||||
fontSize: 9,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
boxShadow: '0 4px 10px rgba(164, 91, 91, 0.28)',
|
||||
zIndex: 10,
|
||||
}}
|
||||
>
|
||||
<DeleteOutlined style={{ fontSize: 9 }} />
|
||||
</button>
|
||||
{mediaStackHovered && (
|
||||
<button
|
||||
onClick={() => handleRemoveMedia(idx)}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: -5,
|
||||
right: -5,
|
||||
width: 18,
|
||||
height: 18,
|
||||
border: '1px solid rgba(255,255,255,0.9)',
|
||||
background: '#A45B5B',
|
||||
borderRadius: 50,
|
||||
cursor: 'pointer',
|
||||
color: '#fff',
|
||||
fontSize: 9,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
boxShadow: '0 4px 10px rgba(164, 91, 91, 0.28)',
|
||||
zIndex: 10,
|
||||
}}
|
||||
>
|
||||
<DeleteOutlined style={{ fontSize: 9 }} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user