фикс фоток

This commit is contained in:
107 2025-05-23 13:16:13 +07:00
parent fbcf49a3a6
commit 118597a73c
2 changed files with 207 additions and 18 deletions

View File

@ -411,6 +411,37 @@ const handlePhotoUpload = async (files) => {
console.log(`[EditProfileForm] handlePhotoUpload: получено файлов: ${files.length}`);
// Проверяем форматы и размеры файлов
const allowedTypes = ['image/jpeg', 'image/png', 'image/webp', 'image/jpg'];
const maxSizeMB = 10; // Максимальный размер файла в MB
const maxSizeBytes = maxSizeMB * 1024 * 1024;
let hasInvalidType = false;
let hasInvalidSize = false;
for (const file of files) {
if (!allowedTypes.includes(file.type)) {
hasInvalidType = true;
break;
}
if (file.size > maxSizeBytes) {
hasInvalidSize = true;
break;
}
}
if (hasInvalidType) {
photoUploadErrorMessage.value = 'Можно загружать только фотографии (JPEG, PNG, WebP)';
emit('photo-upload-complete', { success: 0, errors: files.length });
return;
}
if (hasInvalidSize) {
photoUploadErrorMessage.value = `Размер файла не должен превышать ${maxSizeMB} МБ`;
emit('photo-upload-complete', { success: 0, errors: files.length });
return;
}
// Индикатор для отслеживания успешных загрузок
let successCount = 0;
let errorCount = 0;

View File

@ -122,16 +122,14 @@
</div>
</div>
</div>
</div>
<!-- Photos Section -->
</div> <!-- Photos Section -->
<div class="info-card" id="photos-section">
<div class="card-header">
<h3><i class="bi-images"></i> Мои фотографии</h3>
<div class="header-actions" v-if="isEditMode">
<button class="add-photo-btn" @click="triggerPhotoUpload">
<i class="bi-plus"></i>
Добавить фото
<h3><i class="bi-images"></i> Мои фотографии</h3> <div class="header-actions">
<button class="add-photo-btn" @click="triggerPhotoUpload" :disabled="photoActionLoading">
<span v-if="photoActionLoading" class="spinner-small"></span>
<i v-else class="bi-plus"></i>
{{ photoActionLoading ? 'Загрузка...' : 'Добавить фото' }}
</button>
</div>
</div>
@ -170,18 +168,17 @@
</div>
</div>
<!-- Empty State -->
<div v-else class="empty-photos">
<!-- Empty State --> <div v-else class="empty-photos">
<i class="bi-image"></i>
<h4>Нет фотографий</h4>
<p>Добавьте фотографии, чтобы сделать профиль привлекательнее</p>
<button v-if="isEditMode" class="action-btn primary" @click="triggerPhotoUpload">
<i class="bi-plus"></i>
Добавить первое фото
<button class="action-btn primary" @click="triggerPhotoUpload" :disabled="photoActionLoading">
<span v-if="photoActionLoading" class="spinner-small"></span>
<i v-else class="bi-plus"></i>
{{ photoActionLoading ? 'Загрузка...' : 'Добавить первое фото' }}
</button>
</div>
<!-- Photo Messages -->
<!-- Photo Messages -->
<div v-if="photoActionError" class="alert error">
<i class="bi-exclamation-circle"></i>
{{ photoActionError }}
@ -190,6 +187,10 @@
<i class="bi-check-circle"></i>
{{ photoActionSuccess }}
</div>
<div v-if="photoActionLoading" class="alert info">
<div class="spinner-small" style="border-top-color:#667eea"></div>
Загрузка фотографий...
</div>
</div>
</div>
</div>
@ -306,6 +307,7 @@ const scrollToEdit = () => {
const triggerPhotoUpload = () => {
if (fileInput.value) {
clearMessages(); // Очищаем предыдущие сообщения перед новой загрузкой
fileInput.value.click();
}
};
@ -313,10 +315,43 @@ const triggerPhotoUpload = () => {
const handlePhotoSelect = async (event) => {
const files = event.target.files;
if (!files || files.length === 0) return;
// Передаем выбранные файлы в форму редактирования
if (editFormRef.value && editFormRef.value.handlePhotoUpload) {
// Проверяем форматы и размеры файлов
const allowedTypes = ['image/jpeg', 'image/png', 'image/webp', 'image/jpg'];
const maxSizeMB = 10; // Максимальный размер файла в MB
const maxSizeBytes = maxSizeMB * 1024 * 1024;
let hasInvalidType = false;
let hasInvalidSize = false;
for (const file of files) {
if (!allowedTypes.includes(file.type)) {
hasInvalidType = true;
break;
}
if (file.size > maxSizeBytes) {
hasInvalidSize = true;
break;
}
}
if (hasInvalidType) {
photoActionError.value = 'Можно загружать только фотографии (JPEG, PNG, WebP)';
event.target.value = '';
return;
}
if (hasInvalidSize) {
photoActionError.value = `Размер файла не должен превышать ${maxSizeMB} МБ`;
event.target.value = '';
return;
}
// Если активна форма редактирования, используем её метод
if (isEditMode.value && editFormRef.value && editFormRef.value.handlePhotoUpload) {
await editFormRef.value.handlePhotoUpload(Array.from(files));
} else {
// Иначе обрабатываем загрузку фото здесь
await uploadPhotos(Array.from(files));
}
// Очищаем input
@ -387,6 +422,102 @@ const handlePhotoUploadComplete = (result) => {
}
};
// Новый метод для загрузки фотографий непосредственно из ProfileView
const uploadPhotos = async (files) => {
if (!files || files.length === 0) {
console.log('[ProfileView] uploadPhotos: нет файлов для загрузки');
return;
}
console.log(`[ProfileView] uploadPhotos: получено файлов: ${files.length}`);
// Индикатор для отслеживания успешных загрузок
let successCount = 0;
let errorCount = 0;
photoActionLoading.value = true;
clearMessages();
try {
let imageCompression;
try {
// Импортируем библиотеку для сжатия изображений динамически
imageCompression = (await import('browser-image-compression')).default;
} catch (importErr) {
console.error('[ProfileView] Ошибка при импорте библиотеки сжатия:', importErr);
throw new Error('Не удалось загрузить необходимые компоненты. Пожалуйста, обновите страницу и попробуйте снова.');
}
// Настройки для сжатия изображений
const options = {
maxSizeMB: 1,
maxWidthOrHeight: 1920,
useWebWorker: true,
};
for (let i = 0; i < files.length; i++) {
const originalFile = files[i];
try {
// Сжимаем файл
console.log(`[ProfileView] Сжатие файла ${originalFile.name}...`);
const compressedFile = await imageCompression(originalFile, options);
console.log(`[ProfileView] Файл ${originalFile.name} сжат. Оригинальный размер: ${(originalFile.size / 1024 / 1024).toFixed(2)} MB, Новый размер: ${(compressedFile.size / 1024 / 1024).toFixed(2)} MB`);
// Создаем FormData для отправки на сервер
const fd = new FormData();
fd.append('profilePhoto', compressedFile, originalFile.name);
// Отправляем файл
console.log(`[ProfileView] Отправка файла ${originalFile.name} (сжатого) на сервер...`);
const response = await api.uploadUserProfilePhoto(fd);
console.log(`[ProfileView] Ответ сервера (фото ${originalFile.name}):`, response.data);
// Увеличиваем счетчик успешных загрузок
successCount++;
// Обновляем данные пользователя после каждой успешной загрузки
await fetchUser();
console.log(`[ProfileView] Данные пользователя обновлены после загрузки фото ${originalFile.name}`);
} catch (err) {
console.error(`[ProfileView] Ошибка при сжатии или загрузке фото ${originalFile.name}:`, err);
errorCount++;
// Показываем более информативное сообщение об ошибке
if (err.message && err.message.includes('network')) {
photoActionError.value = 'Ошибка сети при загрузке фото. Пожалуйста, проверьте подключение к интернету.';
} else if (err.response && err.response.status === 413) {
photoActionError.value = 'Файл слишком большой. Пожалуйста, выберите фото меньшего размера.';
}
}
}
// Формируем сообщение для пользователя
if (successCount > 0) {
photoActionSuccess.value = `Успешно загружено фотографий: ${successCount}`;
console.log(`[ProfileView] Успешно загружено фотографий: ${successCount}`);
// Автоматически скрываем сообщение через 3 секунды
setTimeout(() => {
photoActionSuccess.value = '';
}, 3000);
// Прокручиваем к разделу с фотографиями
setTimeout(() => {
scrollToPhotos();
}, 100);
}
if (errorCount > 0) {
photoActionError.value = `Не удалось загрузить некоторые фото (${errorCount})`;
console.log(`[ProfileView] Ошибок при загрузке: ${errorCount}`);
}
} catch (err) {
console.error('[ProfileView] Общая ошибка при загрузке фото:', err);
photoActionError.value = 'Произошла ошибка при загрузке фотографий';
} finally {
photoActionLoading.value = false;
}
};
const clearMessages = () => {
photoActionError.value = '';
photoActionSuccess.value = '';
@ -959,6 +1090,9 @@ onMounted(async () => {
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.5rem;
}
.add-photo-btn:hover {
@ -966,6 +1100,11 @@ onMounted(async () => {
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
}
.add-photo-btn:active {
transform: translateY(0);
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
}
.photo-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
@ -1088,6 +1227,12 @@ onMounted(async () => {
border: 1px solid rgba(40, 167, 69, 0.2);
}
.alert.info {
background: rgba(102, 126, 234, 0.1);
color: #3c4d8d;
border: 1px solid rgba(102, 126, 234, 0.2);
}
/* Modal */
.modal-overlay {
position: fixed;
@ -1199,4 +1344,17 @@ onMounted(async () => {
padding: 1.5rem;
}
}
/* Spinner для кнопок */
.spinner-small {
width: 16px;
height: 16px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: white;
animation: spin 1s ease-in-out infinite;
margin-right: 6px;
display: inline-block;
vertical-align: middle;
}
</style>