изменение логики загрузки фото профиля

This commit is contained in:
107 2025-05-23 13:01:57 +07:00
parent 673823686d
commit fbcf49a3a6
2 changed files with 96 additions and 175 deletions

View File

@ -2,12 +2,10 @@
<div class="edit-profile-container">
<!-- Header Section -->
<div class="edit-header">
<div class="header-content">
<h2 class="edit-title">
<i class="edit-icon"></i>
<div class="header-content"> <h2 class="edit-title"> <i class="edit-icon"></i>
Редактирование профиля
</h2>
<p class="edit-subtitle">Обновите информацию о себе и добавьте новые фотографии</p>
<p class="edit-subtitle">Обновите основную информацию о себе</p>
</div>
</div>
@ -163,107 +161,7 @@
Сохранить изменения
</div>
</button>
</form>
</div>
<!-- Photo Upload Section -->
<div class="section-card">
<div class="section-header">
<h3 class="section-title">
<i class="section-icon">📸</i>
Загрузка фотографий
</h3>
<p class="section-description">Добавьте новые фотографии в свой профиль</p>
</div>
<form @submit.prevent="handleMultiplePhotoUpload" class="photo-form">
<!-- File Input -->
<div class="form-group">
<label for="profilePhotos" class="file-upload-label">
<div class="file-upload-area" :class="{ 'dragover': false }">
<div class="file-upload-icon">📁</div>
<div class="file-upload-text">
<span class="file-upload-main">Выберите фотографии</span>
<span class="file-upload-sub">или перетащите файлы сюда</span>
</div>
<div class="file-upload-button">Обзор файлов</div>
</div>
<input
type="file"
class="file-input"
id="profilePhotos"
@change="onFilesSelected"
accept="image/*"
multiple
:disabled="photoLoading"
/>
</label>
</div>
<!-- Selected Files Preview -->
<div v-if="selectedFiles.length > 0" class="selected-files">
<h4 class="selected-files-title">
<i class="files-icon">📋</i>
Выбранные файлы ({{ selectedFiles.length }})
</h4>
<div class="files-list">
<div
v-for="(file, index) in selectedFiles"
:key="index"
class="file-item"
>
<div class="file-info">
<i class="file-type-icon">🖼</i>
<span class="file-name">{{ file.name }}</span>
<span class="file-size">{{ formatFileSize(file.size) }}</span>
</div>
</div>
</div>
</div>
<!-- Upload Messages -->
<div v-if="photoUploadMessages.length > 0" class="upload-messages">
<div
v-for="(message, index) in photoUploadMessages"
:key="index"
class="upload-message"
:class="message.status"
>
<i class="message-icon" v-if="message.status === 'success'"></i>
<i class="message-icon" v-else-if="message.status === 'error'"></i>
<i class="message-icon" v-else-if="message.status === 'uploading'"></i>
<i class="message-icon" v-else></i>
<span class="message-text">{{ message.message }}</span>
</div>
</div>
<!-- Global Messages -->
<div v-if="photoUploadSuccessMessage" class="alert alert-success">
<i class="alert-icon"></i>
{{ photoUploadSuccessMessage }}
</div>
<div v-if="photoUploadErrorMessage" class="alert alert-error">
<i class="alert-icon"></i>
{{ photoUploadErrorMessage }}
</div>
<!-- Upload Button -->
<button
type="submit"
class="submit-btn submit-btn-secondary"
:disabled="photoLoading || selectedFiles.length === 0"
>
<div v-if="photoLoading" class="btn-loading">
<div class="spinner"></div>
Загрузка фотографий...
</div>
<div v-else class="btn-content">
<i class="btn-icon">📤</i>
Загрузить фотографии ({{ selectedFiles.length }})
</div>
</button>
</form>
</div>
</form> </div>
</div>
</div>
</template>
@ -275,6 +173,7 @@ import api from '@/services/api';
import imageCompression from 'browser-image-compression';
import russianCities from '@/assets/russian-cities.json'; // Импортируем список городов
const emit = defineEmits(['profile-updated', 'photo-upload-complete']);
const { user, fetchUser } = useAuth();
const formData = ref({
@ -503,80 +402,82 @@ const onFilesSelected = (event) => {
}
};
// Обновляем handlePhotoUpload для загрузки нескольких файлов последовательно
const handleMultiplePhotoUpload = async () => {
if (selectedFiles.value.length === 0) {
photoUploadErrorMessage.value = 'Пожалуйста, выберите файлы для загрузки.';
// Очистим индивидуальные сообщения, если они были
photoUploadMessages.value.forEach(msg => {
if (msg.status === 'pending') msg.status = 'cancelled';
});
// Метод для загрузки фото, вызываемый из родительского компонента ProfileView
const handlePhotoUpload = async (files) => {
if (!files || files.length === 0) {
console.log('[EditProfileForm] handlePhotoUpload: нет файлов для загрузки');
return;
}
photoLoading.value = true; // Общий индикатор загрузки
photoUploadErrorMessage.value = ''; // Сбрасываем общую ошибку
for (let i = 0; i < selectedFiles.value.length; i++) {
const originalFile = selectedFiles.value[i];
const fileMessageIndex = photoUploadMessages.value.findIndex(m => m.name === originalFile.name && m.status !== 'success');
if (fileMessageIndex === -1) continue;
photoUploadMessages.value[fileMessageIndex].status = 'uploading';
photoUploadMessages.value[fileMessageIndex].message = 'Сжатие и загрузка...';
try {
// Опции сжатия
const options = {
maxSizeMB: 1, // Максимальный размер файла в MB
maxWidthOrHeight: 1920, // Максимальная ширина или высота
useWebWorker: true, // Использовать Web Worker для лучшей производительности
// Дополнительные опции можно найти в документации библиотеки
// Например, initialQuality для JPEG
}
console.log(`[EditProfileForm] handlePhotoUpload: получено файлов: ${files.length}`);
// Индикатор для отслеживания успешных загрузок
let successCount = 0;
let errorCount = 0;
photoLoading.value = true;
photoUploadErrorMessage.value = '';
photoUploadSuccessMessage.value = '';
// Настройки для сжатия изображений
const options = {
maxSizeMB: 1,
maxWidthOrHeight: 1920,
useWebWorker: true,
};
try {
for (let i = 0; i < files.length; i++) {
const originalFile = files[i];
console.log(`[EditProfileForm] Сжатие файла ${originalFile.name}...`);
const compressedFile = await imageCompression(originalFile, options);
console.log(`[EditProfileForm] Файл ${originalFile.name} сжат. Оригинальный размер: ${(originalFile.size / 1024 / 1024).toFixed(2)} MB, Новый размер: ${(compressedFile.size / 1024 / 1024).toFixed(2)} MB`);
const fd = new FormData();
// Важно: сервер ожидает имя файла, поэтому передаем его как третий аргумент
fd.append('profilePhoto', compressedFile, originalFile.name);
console.log(`[EditProfileForm] Отправка файла ${originalFile.name} (сжатого) на сервер...`);
const response = await api.uploadUserProfilePhoto(fd);
console.log(`[EditProfileForm] Ответ от сервера (фото ${originalFile.name}):`, response.data);
photoUploadMessages.value[fileMessageIndex].status = 'success';
photoUploadMessages.value[fileMessageIndex].message = response.data.message || 'Фото успешно загружено!';
await fetchUser();
console.log(`[EditProfileForm] Данные пользователя обновлены (фото ${originalFile.name})`);
} catch (err) {
console.error(`[EditProfileForm] Ошибка при сжатии или загрузке фото ${originalFile.name}:`, err);
photoUploadMessages.value[fileMessageIndex].status = 'error';
// Проверяем, является ли ошибка ошибкой сжатия
if (err instanceof Error && err.message.includes('compression')) {
photoUploadMessages.value[fileMessageIndex].message = `Ошибка при сжатии фото ${originalFile.name}.`;
} else {
photoUploadMessages.value[fileMessageIndex].message = (err.response && err.response.data && err.response.data.message)
? err.response.data.message
: `Ошибка при загрузке фото ${originalFile.name}.`;
try {
// Сжимаем файл
console.log(`[EditProfileForm] Сжатие файла ${originalFile.name}...`);
const compressedFile = await imageCompression(originalFile, options);
console.log(`[EditProfileForm] Файл ${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(`[EditProfileForm] Отправка файла ${originalFile.name} (сжатого) на сервер...`);
const response = await api.uploadUserProfilePhoto(fd);
console.log(`[EditProfileForm] Ответ сервера (фото ${originalFile.name}):`, response.data);
// Увеличиваем счетчик успешных загрузок
successCount++;
// Обновляем данные пользователя после каждой успешной загрузки
await fetchUser();
console.log(`[EditProfileForm] Данные пользователя обновлены после загрузки фото ${originalFile.name}`);
} catch (err) {
console.error(`[EditProfileForm] Ошибка при сжатии или загрузке фото ${originalFile.name}:`, err);
errorCount++;
}
}
// Формируем сообщение для пользователя
if (successCount > 0) {
photoUploadSuccessMessage.value = `Успешно загружено фотографий: ${successCount}`;
console.log(`[EditProfileForm] Успешно загружено фотографий: ${successCount}`);
// Автоматически скрываем сообщение через 3 секунды
setTimeout(() => {
photoUploadSuccessMessage.value = '';
}, 3000);
}
if (errorCount > 0) {
photoUploadErrorMessage.value = `Не удалось загрузить некоторые фото (${errorCount})`;
console.log(`[EditProfileForm] Ошибок при загрузке: ${errorCount}`);
}
// Отправляем событие родителю
emit('photo-upload-complete', { success: successCount, errors: errorCount });
} finally {
photoLoading.value = false;
}
// После завершения всех загрузок
selectedFiles.value = []; // Очищаем выбранные файлы
// Не очищаем photoInput.value здесь, т.к. input type="file" multiple сам управляет списком
// Можно сбросить значение инпута, чтобы пользователь мог выбрать те же файлы снова, если захочет
const photoInput = document.getElementById('profilePhotos'); // Убедимся, что ID правильный
if (photoInput) {
photoInput.value = '';
}
photoLoading.value = false; // Выключаем общий индикатор
};
</script>

View File

@ -195,12 +195,11 @@
</div>
</div>
</div>
</div>
<!-- Edit Mode Components -->
</div> <!-- Edit Mode Components -->
<EditProfileForm
v-if="isAuthenticated && !initialLoading && isEditMode"
@profile-updated="handleProfileUpdate"
@photo-upload-complete="handlePhotoUploadComplete"
ref="editFormRef"
/>
@ -367,6 +366,27 @@ const handleProfileUpdate = () => {
clearMessages();
};
const handlePhotoUploadComplete = (result) => {
console.log('[ProfileView] Получено событие photo-upload-complete:', result);
if (result.success > 0) {
photoActionSuccess.value = `Успешно загружено фотографий: ${result.success}`;
// Автоматически скрываем сообщение через 3 секунды
setTimeout(() => {
photoActionSuccess.value = '';
}, 3000);
// Прокручиваем к разделу с фотографиями
setTimeout(() => {
scrollToPhotos();
}, 100);
}
if (result.errors > 0) {
photoActionError.value = `Не удалось загрузить некоторые фото (${result.errors})`;
}
};
const clearMessages = () => {
photoActionError.value = '';
photoActionSuccess.value = '';