diff --git a/backend/controllers/userController.js b/backend/controllers/userController.js index f81e6ca..7e955c9 100644 --- a/backend/controllers/userController.js +++ b/backend/controllers/userController.js @@ -335,6 +335,63 @@ const deletePhoto = async (req, res, next) => { } }; +// @desc Получить профиль пользователя по ID (для просмотра чужих профилей) +// @route GET /api/users/:userId +// @access Private +const getUserById = async (req, res, next) => { + try { + const { userId } = req.params; + const currentUserId = req.user._id; + + // Проверяем, что пользователь не запрашивает свой собственный профиль + // (для этого есть отдельный роут /auth/me) + if (currentUserId.equals(userId)) { + const error = new Error('Для получения своего профиля используйте /auth/me'); + error.statusCode = 400; + return next(error); + } + + // Находим пользователя по ID + const user = await User.findById(userId) + .select('name dateOfBirth gender bio photos location createdAt'); + + if (!user) { + const error = new Error('Пользователь не найден.'); + error.statusCode = 404; + return next(error); + } + + // Вычисляем возраст, если указана дата рождения + let age = null; + if (user.dateOfBirth) { + const birthDate = new Date(user.dateOfBirth); + const today = new Date(); + age = today.getFullYear() - birthDate.getFullYear(); + const m = today.getMonth() - birthDate.getMonth(); + if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) { + age--; + } + } + + // Формируем ответ + const userProfile = { + _id: user._id, + name: user.name, + age: age, + gender: user.gender, + bio: user.bio, + photos: user.photos, + location: user.location, + memberSince: user.createdAt + }; + + res.status(200).json(userProfile); + + } catch (error) { + console.error('[USER_CTRL] Ошибка при получении профиля пользователя:', error.message); + next(error); + } +}; module.exports = { updateUserProfile, @@ -342,5 +399,5 @@ module.exports = { uploadUserProfilePhoto, setMainPhoto, // <--- Добавлено deletePhoto, // <--- Добавлено - // getUserById, + getUserById, // <--- Добавлено }; \ No newline at end of file diff --git a/backend/routes/userRoutes.js b/backend/routes/userRoutes.js index a74052e..c23051c 100644 --- a/backend/routes/userRoutes.js +++ b/backend/routes/userRoutes.js @@ -1,6 +1,6 @@ const express = require('express'); const router = express.Router(); -const { updateUserProfile, getUsersForSwiping, uploadUserProfilePhoto, setMainPhoto, deletePhoto } = require('../controllers/userController'); +const { updateUserProfile, getUsersForSwiping, uploadUserProfilePhoto, setMainPhoto, deletePhoto, getUserById } = require('../controllers/userController'); const { protect } = require('../middleware/authMiddleware'); // Нам нужен protect для защиты маршрута const multer = require('multer'); // 1. Импортируем multer const path = require('path'); // Может понадобиться для фильтрации файлов @@ -44,6 +44,6 @@ router.delete('/profile/photo/:photoId', protect, deletePhoto); // <--- Н // Маршрут для получения профиля по ID (например, для просмотра чужих профилей, если это нужно) // GET /api/users/:id -// router.get('/:id', protect, getUserById); // protect здесь может быть опционален или с другой логикой +router.get('/:userId', protect, getUserById); // <--- НОВЫЙ МАРШРУТ module.exports = router; \ No newline at end of file diff --git a/src/router/index.js b/src/router/index.js index 55c3bf0..bcbd0ff 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -43,6 +43,13 @@ const routes = [ component: () => import('../views/ChatView.vue'), // Мы создадим этот компонент позже meta: { requiresAuth: true }, props: true // Позволяет передавать :conversationId как пропс в компонент + }, + { + path: '/user/:userId', // Просмотр профиля пользователя + name: 'UserProfile', + component: () => import('../views/UserProfileView.vue'), + meta: { requiresAuth: true }, + props: true // Позволяет передавать :userId как пропс в компонент } // ... здесь будут другие маршруты: /profile, /swipe, /chat/:id и т.д. ]; diff --git a/src/services/api.js b/src/services/api.js index a0a211b..99fa792 100644 --- a/src/services/api.js +++ b/src/services/api.js @@ -106,5 +106,10 @@ export default { }, editMessage(conversationId, messageId, newText) { return apiClient.put(`/conversations/${conversationId}/messages/${messageId}`, { text: newText }); + }, + + // Новый метод для получения профиля пользователя по ID + getUserById(userId) { + return apiClient.get(`/users/${userId}`); } }; \ No newline at end of file diff --git a/src/views/SwipeView.vue b/src/views/SwipeView.vue index c780e3f..ec0f3c0 100644 --- a/src/views/SwipeView.vue +++ b/src/views/SwipeView.vue @@ -52,6 +52,8 @@ @touchstart="index === 0 ? startTouch($event) : null" @touchmove="index === 0 ? moveTouch($event) : null" @touchend="index === 0 ? endTouch($event) : null" + @mousedown="index === 0 ? startDrag($event) : null" + @click="index === 0 ? handleCardClick($event, user) : null" >
@@ -509,6 +511,33 @@ const endDrag = () => { document.removeEventListener('mouseup', endDrag); }; +// Обработчик клика на карточку +const handleCardClick = (event, user) => { + // Проверяем, что это был именно клик, а не завершение drag/swipe операции + const dragDuration = Date.now() - dragStartTime.value; + const totalMovement = Math.sqrt( + Math.pow(dragOffset.value.x || 0, 2) + Math.pow(dragOffset.value.y || 0, 2) + ); + + // Если было реальное перетаскивание или долгое удержание, не обрабатываем как клик + if (hasActuallyMoved.value || totalMovement > 10 || dragDuration > 300) { + console.log('[SwipeView] Клик проигнорирован - было перетаскивание'); + return; + } + + // Проверяем, что клик не был на элементах управления каруселью + if (event.target.closest('.carousel-nav') || + event.target.closest('.carousel-dots') || + event.target.closest('.dot')) { + console.log('[SwipeView] Клик на элементах управления каруселью - не открываем профиль'); + return; + } + + // Открываем полный профиль пользователя + console.log('[SwipeView] Открытие профиля пользователя:', user.name, user._id); + router.push(`/user/${user._id}`); +}; + // Основные функции const fetchSuggestions = async () => { loading.value = true; diff --git a/src/views/UserProfileView.vue b/src/views/UserProfileView.vue new file mode 100644 index 0000000..fbc587e --- /dev/null +++ b/src/views/UserProfileView.vue @@ -0,0 +1,1011 @@ + + + + + \ No newline at end of file