1
This commit is contained in:
-501
File diff suppressed because one or more lines are too long
+2
-2
File diff suppressed because one or more lines are too long
-501
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -28,7 +28,7 @@
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
</script>
|
</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">
|
<link rel="stylesheet" crossorigin href="/assets/index-Bi8mcSs8.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -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 handleUpload = async (file: File, frameTarget?: 'first' | 'last' | File[]) => {
|
||||||
const isImage = file.type.startsWith('image/');
|
const isImage = file.type.startsWith('image/');
|
||||||
const isVideo = file.type.startsWith('video/');
|
const isVideo = file.type.startsWith('video/');
|
||||||
@@ -1059,6 +1111,21 @@ const AIChatPage: React.FC = () => {
|
|||||||
return false;
|
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;
|
const imageCount = currentMedia.filter((m) => m.type === 'image').length;
|
||||||
if (isImage && imageCount >= maxImage) {
|
if (isImage && imageCount >= maxImage) {
|
||||||
message.error(`该引擎最多上传${maxImage}张图片`);
|
message.error(`该引擎最多上传${maxImage}张图片`);
|
||||||
@@ -1086,8 +1153,15 @@ const AIChatPage: React.FC = () => {
|
|||||||
message.error(`所有视频素材总时长不能超过 15 秒,当前 ${(existingVideoDuration + videoDuration).toFixed(1)} 秒`);
|
message.error(`所有视频素材总时长不能超过 15 秒,当前 ${(existingVideoDuration + videoDuration).toFixed(1)} 秒`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// 视频尺寸校验
|
||||||
|
const { width, height } = await getVideoDimensions(file);
|
||||||
|
const error = validateVideoDimensions(width, height);
|
||||||
|
if (error) {
|
||||||
|
message.error(error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
message.error('无法获取视频时长');
|
message.error('无法获取视频信息,请检查文件是否损坏');
|
||||||
return false;
|
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 }}>
|
<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)' }}
|
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
|
{mediaStackHovered && (
|
||||||
onClick={() => handleRemoveMedia(idx)}
|
<button
|
||||||
style={{
|
onClick={() => handleRemoveMedia(idx)}
|
||||||
position: 'absolute',
|
style={{
|
||||||
top: -5,
|
position: 'absolute',
|
||||||
right: -5,
|
top: -5,
|
||||||
width: 18,
|
right: -5,
|
||||||
height: 18,
|
width: 18,
|
||||||
border: '1px solid rgba(255,255,255,0.9)',
|
height: 18,
|
||||||
background: '#A45B5B',
|
border: '1px solid rgba(255,255,255,0.9)',
|
||||||
borderRadius: 50,
|
background: '#A45B5B',
|
||||||
cursor: 'pointer',
|
borderRadius: 50,
|
||||||
color: '#fff',
|
cursor: 'pointer',
|
||||||
fontSize: 9,
|
color: '#fff',
|
||||||
display: 'flex',
|
fontSize: 9,
|
||||||
alignItems: 'center',
|
display: 'flex',
|
||||||
justifyContent: 'center',
|
alignItems: 'center',
|
||||||
boxShadow: '0 4px 10px rgba(164, 91, 91, 0.28)',
|
justifyContent: 'center',
|
||||||
zIndex: 10,
|
boxShadow: '0 4px 10px rgba(164, 91, 91, 0.28)',
|
||||||
}}
|
zIndex: 10,
|
||||||
>
|
}}
|
||||||
<DeleteOutlined style={{ fontSize: 9 }} />
|
>
|
||||||
</button>
|
<DeleteOutlined style={{ fontSize: 9 }} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user