фикс фоток
This commit is contained in:
parent
fbcf49a3a6
commit
118597a73c
@ -411,6 +411,37 @@ const handlePhotoUpload = async (files) => {
|
|||||||
|
|
||||||
console.log(`[EditProfileForm] handlePhotoUpload: получено файлов: ${files.length}`);
|
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 successCount = 0;
|
||||||
let errorCount = 0;
|
let errorCount = 0;
|
||||||
|
@ -122,16 +122,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> <!-- Photos Section -->
|
||||||
|
|
||||||
<!-- Photos Section -->
|
|
||||||
<div class="info-card" id="photos-section">
|
<div class="info-card" id="photos-section">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h3><i class="bi-images"></i> Мои фотографии</h3>
|
<h3><i class="bi-images"></i> Мои фотографии</h3> <div class="header-actions">
|
||||||
<div class="header-actions" v-if="isEditMode">
|
<button class="add-photo-btn" @click="triggerPhotoUpload" :disabled="photoActionLoading">
|
||||||
<button class="add-photo-btn" @click="triggerPhotoUpload">
|
<span v-if="photoActionLoading" class="spinner-small"></span>
|
||||||
<i class="bi-plus"></i>
|
<i v-else class="bi-plus"></i>
|
||||||
Добавить фото
|
{{ photoActionLoading ? 'Загрузка...' : 'Добавить фото' }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -170,18 +168,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Empty State -->
|
<!-- Empty State --> <div v-else class="empty-photos">
|
||||||
<div v-else class="empty-photos">
|
|
||||||
<i class="bi-image"></i>
|
<i class="bi-image"></i>
|
||||||
<h4>Нет фотографий</h4>
|
<h4>Нет фотографий</h4>
|
||||||
<p>Добавьте фотографии, чтобы сделать профиль привлекательнее</p>
|
<p>Добавьте фотографии, чтобы сделать профиль привлекательнее</p>
|
||||||
<button v-if="isEditMode" class="action-btn primary" @click="triggerPhotoUpload">
|
<button class="action-btn primary" @click="triggerPhotoUpload" :disabled="photoActionLoading">
|
||||||
<i class="bi-plus"></i>
|
<span v-if="photoActionLoading" class="spinner-small"></span>
|
||||||
Добавить первое фото
|
<i v-else class="bi-plus"></i>
|
||||||
|
{{ photoActionLoading ? 'Загрузка...' : 'Добавить первое фото' }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Photo Messages -->
|
||||||
<!-- Photo Messages -->
|
|
||||||
<div v-if="photoActionError" class="alert error">
|
<div v-if="photoActionError" class="alert error">
|
||||||
<i class="bi-exclamation-circle"></i>
|
<i class="bi-exclamation-circle"></i>
|
||||||
{{ photoActionError }}
|
{{ photoActionError }}
|
||||||
@ -190,6 +187,10 @@
|
|||||||
<i class="bi-check-circle"></i>
|
<i class="bi-check-circle"></i>
|
||||||
{{ photoActionSuccess }}
|
{{ photoActionSuccess }}
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="photoActionLoading" class="alert info">
|
||||||
|
<div class="spinner-small" style="border-top-color:#667eea"></div>
|
||||||
|
Загрузка фотографий...
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -306,6 +307,7 @@ const scrollToEdit = () => {
|
|||||||
|
|
||||||
const triggerPhotoUpload = () => {
|
const triggerPhotoUpload = () => {
|
||||||
if (fileInput.value) {
|
if (fileInput.value) {
|
||||||
|
clearMessages(); // Очищаем предыдущие сообщения перед новой загрузкой
|
||||||
fileInput.value.click();
|
fileInput.value.click();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -313,10 +315,43 @@ const triggerPhotoUpload = () => {
|
|||||||
const handlePhotoSelect = async (event) => {
|
const handlePhotoSelect = async (event) => {
|
||||||
const files = event.target.files;
|
const files = event.target.files;
|
||||||
if (!files || files.length === 0) return;
|
if (!files || files.length === 0) return;
|
||||||
|
// Проверяем форматы и размеры файлов
|
||||||
|
const allowedTypes = ['image/jpeg', 'image/png', 'image/webp', 'image/jpg'];
|
||||||
|
const maxSizeMB = 10; // Максимальный размер файла в MB
|
||||||
|
const maxSizeBytes = maxSizeMB * 1024 * 1024;
|
||||||
|
|
||||||
// Передаем выбранные файлы в форму редактирования
|
let hasInvalidType = false;
|
||||||
if (editFormRef.value && editFormRef.value.handlePhotoUpload) {
|
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));
|
await editFormRef.value.handlePhotoUpload(Array.from(files));
|
||||||
|
} else {
|
||||||
|
// Иначе обрабатываем загрузку фото здесь
|
||||||
|
await uploadPhotos(Array.from(files));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Очищаем input
|
// Очищаем 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 = () => {
|
const clearMessages = () => {
|
||||||
photoActionError.value = '';
|
photoActionError.value = '';
|
||||||
photoActionSuccess.value = '';
|
photoActionSuccess.value = '';
|
||||||
@ -959,6 +1090,9 @@ onMounted(async () => {
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-photo-btn:hover {
|
.add-photo-btn:hover {
|
||||||
@ -966,6 +1100,11 @@ onMounted(async () => {
|
|||||||
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
|
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 {
|
.photo-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||||
@ -1088,6 +1227,12 @@ onMounted(async () => {
|
|||||||
border: 1px solid rgba(40, 167, 69, 0.2);
|
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 */
|
||||||
.modal-overlay {
|
.modal-overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@ -1199,4 +1344,17 @@ onMounted(async () => {
|
|||||||
padding: 1.5rem;
|
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>
|
</style>
|
Loading…
x
Reference in New Issue
Block a user