изменение логики загрузки фото профиля
This commit is contained in:
parent
673823686d
commit
fbcf49a3a6
@ -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>
|
||||
|
||||
|
@ -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 = '';
|
||||
|
Loading…
x
Reference in New Issue
Block a user