211 lines
8.7 KiB
Vue
211 lines
8.7 KiB
Vue
![]() |
<template>
|
|||
|
<div class="edit-profile-form mt-3">
|
|||
|
<h4>Редактировать профиль</h4>
|
|||
|
<form @submit.prevent="handleSubmit">
|
|||
|
<div class="mb-3">
|
|||
|
<label for="editName" class="form-label">Имя:</label>
|
|||
|
<input type="text" class="form-control" id="editName" v-model="formData.name" :disabled="profileLoading" />
|
|||
|
</div>
|
|||
|
<div class="mb-3">
|
|||
|
<label for="editBio" class="form-label">О себе:</label>
|
|||
|
<textarea class="form-control" id="editBio" rows="3" v-model="formData.bio" :disabled="profileLoading"></textarea>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="mb-3">
|
|||
|
<label for="editDateOfBirth" class="form-label">Дата рождения:</label>
|
|||
|
<input type="date" class="form-control" id="editDateOfBirth" v-model="formData.dateOfBirth" :disabled="profileLoading" />
|
|||
|
</div>
|
|||
|
<div class="mb-3">
|
|||
|
<label for="editGender" class="form-label">Пол:</label>
|
|||
|
<select class="form-select" id="editGender" v-model="formData.gender" :disabled="profileLoading">
|
|||
|
<option value="">Не выбрано</option>
|
|||
|
<option value="male">Мужской</option>
|
|||
|
<option value="female">Женский</option>
|
|||
|
<option value="other">Другой</option>
|
|||
|
</select>
|
|||
|
</div>
|
|||
|
|
|||
|
<div v-if="profileSuccessMessage" class="alert alert-success">{{ profileSuccessMessage }}</div>
|
|||
|
<div v-if="profileErrorMessage" class="alert alert-danger">{{ profileErrorMessage }}</div>
|
|||
|
|
|||
|
<button type="submit" class="btn btn-primary" :disabled="profileLoading">
|
|||
|
<span v-if="profileLoading" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
|||
|
{{ profileLoading ? 'Сохранение...' : 'Сохранить изменения' }}
|
|||
|
</button>
|
|||
|
</form>
|
|||
|
|
|||
|
<hr class="my-4">
|
|||
|
|
|||
|
<h4>Загрузить фото профиля</h4>
|
|||
|
<form @submit.prevent="handlePhotoUpload" class="mt-3">
|
|||
|
<div class="mb-3">
|
|||
|
<label for="profilePhoto" class="form-label">Выберите фото:</label>
|
|||
|
<input type="file" class="form-control" id="profilePhoto" @change="onFileSelected" accept="image/*" :disabled="photoLoading">
|
|||
|
</div>
|
|||
|
|
|||
|
<div v-if="previewUrl" class="mb-3">
|
|||
|
<p>Предпросмотр:</p>
|
|||
|
<img :src="previewUrl" alt="Предпросмотр фото" class="img-thumbnail" style="max-width: 200px; max-height: 200px;" />
|
|||
|
</div>
|
|||
|
|
|||
|
<div v-if="photoUploadSuccessMessage" class="alert alert-success">{{ photoUploadSuccessMessage }}</div>
|
|||
|
<div v-if="photoUploadErrorMessage" class="alert alert-danger">{{ photoUploadErrorMessage }}</div>
|
|||
|
|
|||
|
<button type="submit" class="btn btn-secondary" :disabled="photoLoading || !selectedFile">
|
|||
|
<span v-if="photoLoading" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
|||
|
{{ photoLoading ? 'Загрузка...' : 'Загрузить фото' }}
|
|||
|
</button>
|
|||
|
</form>
|
|||
|
</div>
|
|||
|
</template>
|
|||
|
|
|||
|
<script setup>
|
|||
|
import { ref, onMounted, watch } from 'vue';
|
|||
|
import { useAuth } from '@/auth';
|
|||
|
import api from '@/services/api';
|
|||
|
|
|||
|
const { user, fetchUser } = useAuth();
|
|||
|
|
|||
|
const formData = ref({
|
|||
|
name: '',
|
|||
|
bio: '',
|
|||
|
dateOfBirth: '',
|
|||
|
gender: '',
|
|||
|
});
|
|||
|
|
|||
|
const profileLoading = ref(false);
|
|||
|
const profileErrorMessage = ref('');
|
|||
|
const profileSuccessMessage = ref('');
|
|||
|
|
|||
|
const photoLoading = ref(false);
|
|||
|
const photoUploadErrorMessage = ref('');
|
|||
|
const photoUploadSuccessMessage = ref('');
|
|||
|
const selectedFile = ref(null);
|
|||
|
const previewUrl = ref(null);
|
|||
|
|
|||
|
const initializeFormData = (currentUser) => {
|
|||
|
console.log('[EditProfileForm] Инициализация формы с данными:', currentUser);
|
|||
|
if (currentUser) {
|
|||
|
formData.value.name = currentUser.name || '';
|
|||
|
formData.value.bio = currentUser.bio || '';
|
|||
|
formData.value.dateOfBirth = currentUser.dateOfBirth ? new Date(currentUser.dateOfBirth).toISOString().split('T')[0] : '';
|
|||
|
formData.value.gender = currentUser.gender || '';
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
onMounted(() => {
|
|||
|
console.log('[EditProfileForm] Компонент смонтирован');
|
|||
|
initializeFormData(user.value);
|
|||
|
});
|
|||
|
|
|||
|
watch(user, (newUser) => {
|
|||
|
console.log('[EditProfileForm] Пользователь изменился:', newUser);
|
|||
|
initializeFormData(newUser);
|
|||
|
}, { deep: true });
|
|||
|
|
|||
|
const handleSubmit = async () => {
|
|||
|
console.log('[EditProfileForm] Отправка формы профиля...');
|
|||
|
profileLoading.value = true;
|
|||
|
profileErrorMessage.value = '';
|
|||
|
profileSuccessMessage.value = '';
|
|||
|
|
|||
|
try {
|
|||
|
const dataToUpdate = {
|
|||
|
name: formData.value.name,
|
|||
|
bio: formData.value.bio,
|
|||
|
dateOfBirth: formData.value.dateOfBirth || null,
|
|||
|
gender: formData.value.gender || null,
|
|||
|
};
|
|||
|
|
|||
|
console.log('[EditProfileForm] Данные для обновления профиля:', dataToUpdate);
|
|||
|
|
|||
|
const response = await api.updateUserProfile(dataToUpdate);
|
|||
|
console.log('[EditProfileForm] Ответ от сервера (профиль):', response.data);
|
|||
|
|
|||
|
profileSuccessMessage.value = response.data.message || 'Профиль успешно обновлен!';
|
|||
|
|
|||
|
await fetchUser();
|
|||
|
console.log('[EditProfileForm] Данные пользователя обновлены (профиль)');
|
|||
|
|
|||
|
} catch (err) {
|
|||
|
console.error('[EditProfileForm] Ошибка при обновлении профиля:', err);
|
|||
|
profileErrorMessage.value = (err.response && err.response.data && err.response.data.message)
|
|||
|
? err.response.data.message
|
|||
|
: 'Ошибка при обновлении профиля.';
|
|||
|
} finally {
|
|||
|
profileLoading.value = false;
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
const onFileSelected = (event) => {
|
|||
|
const file = event.target.files[0];
|
|||
|
if (file) {
|
|||
|
selectedFile.value = file;
|
|||
|
// Создание URL для предпросмотра
|
|||
|
const reader = new FileReader();
|
|||
|
reader.onload = (e) => {
|
|||
|
previewUrl.value = e.target.result;
|
|||
|
};
|
|||
|
reader.readAsDataURL(file);
|
|||
|
photoUploadErrorMessage.value = ''; // Сброс ошибки при выборе нового файла
|
|||
|
photoUploadSuccessMessage.value = ''; // Сброс сообщения об успехе
|
|||
|
} else {
|
|||
|
selectedFile.value = null;
|
|||
|
previewUrl.value = null;
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
const handlePhotoUpload = async () => {
|
|||
|
if (!selectedFile.value) {
|
|||
|
photoUploadErrorMessage.value = 'Пожалуйста, выберите файл для загрузки.';
|
|||
|
return;
|
|||
|
}
|
|||
|
console.log('[EditProfileForm] Загрузка фото...');
|
|||
|
photoLoading.value = true;
|
|||
|
photoUploadErrorMessage.value = '';
|
|||
|
photoUploadSuccessMessage.value = '';
|
|||
|
|
|||
|
const fd = new FormData();
|
|||
|
fd.append('profilePhoto', selectedFile.value, selectedFile.value.name);
|
|||
|
|
|||
|
try {
|
|||
|
console.log('[EditProfileForm] Отправка фото на сервер:', selectedFile.value.name);
|
|||
|
const response = await api.uploadUserProfilePhoto(fd); // Предполагается, что такой метод есть в api.js
|
|||
|
console.log('[EditProfileForm] Ответ от сервера (фото):', response.data);
|
|||
|
|
|||
|
photoUploadSuccessMessage.value = response.data.message || 'Фото успешно загружено!';
|
|||
|
selectedFile.value = null;
|
|||
|
previewUrl.value = null;
|
|||
|
// Очищаем поле выбора файла визуально (браузер может не сбросить его сам)
|
|||
|
const photoInput = document.getElementById('profilePhoto');
|
|||
|
if (photoInput) {
|
|||
|
photoInput.value = '';
|
|||
|
}
|
|||
|
await fetchUser(); // Обновляем данные пользователя, включая массив photos
|
|||
|
console.log('[EditProfileForm] Данные пользователя обновлены (фото)');
|
|||
|
|
|||
|
} catch (err) {
|
|||
|
console.error('[EditProfileForm] Ошибка при загрузке фото:', err);
|
|||
|
photoUploadErrorMessage.value = (err.response && err.response.data && err.response.data.message)
|
|||
|
? err.response.data.message
|
|||
|
: 'Ошибка при загрузке фото.';
|
|||
|
} finally {
|
|||
|
photoLoading.value = false;
|
|||
|
}
|
|||
|
};
|
|||
|
</script>
|
|||
|
|
|||
|
<style scoped>
|
|||
|
.edit-profile-form {
|
|||
|
background-color: #f8f9fa;
|
|||
|
padding: 20px;
|
|||
|
border-radius: 8px;
|
|||
|
margin-bottom: 20px;
|
|||
|
}
|
|||
|
.img-thumbnail {
|
|||
|
border: 1px solid #dee2e6;
|
|||
|
padding: 0.25rem;
|
|||
|
background-color: #fff;
|
|||
|
border-radius: 0.25rem;
|
|||
|
}
|
|||
|
</style>
|