This commit is contained in:
2026-07-02 19:42:47 +08:00
parent 82db946631
commit 545c4ffc55
5 changed files with 104 additions and 1030 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -28,7 +28,7 @@
}
})();
</script>
<script type="module" crossorigin src="/assets/index-BHLdltgs.js"></script>
<script type="module" crossorigin src="/assets/index-C-7yqsyI.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Bi8mcSs8.css">
</head>
<body>
+101 -25
View File
@@ -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>
);