Перенес внешний проект vue3 в сервис для фронта. Будем подымать на микросервисах.

This commit is contained in:
valitovgaziz
2025-02-06 07:07:48 +05:00
parent 9da6dfe09d
commit c88d7ca727
57 changed files with 3047 additions and 1009 deletions
-30
View File
@@ -1,30 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo
-20
View File
@@ -1,20 +0,0 @@
# Используем Node.js LTS версию в качестве базового образа
FROM node:lts-alpine as builder
# Устанавливаем зависимости
WORKDIR /app
COPY package*.json ./
RUN npm install
RUN npm install axios
# Копируем исходный код
COPY . .
# Строим продакшн-версию приложения
RUN npm run build
# Создаем финальный образ на основе Nginx
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
+4 -2
View File
@@ -1,9 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<html lang="">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/src/components/images/logo150x150.png">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>F&A</title>
<link rel="stylesheet" href="/src/assets/main.css">
<title>YalArba</title>
</head>
<body>
<div id="app"></div>
+1 -1
View File
@@ -5,4 +5,4 @@
}
},
"exclude": ["node_modules", "dist"]
}
}
+1649 -370
View File
File diff suppressed because it is too large Load Diff
+12 -5
View File
@@ -9,12 +9,19 @@
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.5.12",
"vue-router": "^4.4.5"
"axios": "^1.7.9",
"body-parser": "^1.20.3",
"cors": "^2.8.5",
"express": "^4.21.2",
"pg": "^8.13.1",
"spa": "file:",
"vue": "^3.5.13",
"vue-material-design-icons": "^5.3.1",
"vue-router": "^4.5.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.1.4",
"vite": "^5.4.10",
"vite-plugin-vue-devtools": "^7.5.4"
"@vitejs/plugin-vue": "^5.2.1",
"vite": "^6.0.5",
"vite-plugin-vue-devtools": "^7.6.8"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

+5 -65
View File
@@ -1,72 +1,12 @@
<script setup>
import { RouterLink, RouterView } from 'vue-router';
import HomeView from './views/HomeView.vue';
import { RouterLink, RouterView } from 'vue-router'
</script>
<template>
<HomeView />
<div class="main">
<RouterView />
</div>
</template>
<style scoped>
header {
line-height: 1.5;
max-height: 100vh;
}
.logo {
display: block;
margin: 0 auto 2rem;
}
nav {
width: 100%;
font-size: 12px;
text-align: center;
margin-top: 2rem;
}
nav a.router-link-exact-active {
color: var(--color-text);
}
nav a.router-link-exact-active:hover {
background-color: transparent;
}
nav a {
display: inline-block;
padding: 0 1rem;
border-left: 1px solid var(--color-border);
}
nav a:first-of-type {
border: 0;
}
@media (min-width: 1024px) {
header {
display: flex;
place-items: center;
padding-right: calc(var(--section-gap) / 2);
}
.logo {
margin: 0 2rem 0 0;
}
header .wrapper {
display: flex;
place-items: flex-start;
flex-wrap: wrap;
}
nav {
text-align: left;
margin-left: -1rem;
font-size: 1rem;
padding: 1rem 0;
margin-top: 1rem;
}
}
<style>
</style>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

+37 -4
View File
@@ -1,9 +1,42 @@
html {
/* Общие стили */
html,
body {
margin: 0;
padding: 0;
margin: 5px;
height: fit-content;
width: 100%;
font-family: Arial, sans-serif;
}
body {
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.main {
margin: 2rem;
min-height: 90vh;
min-width: 320px;
max-width: 1024px;
height: 100%;
width: 100%;
border-radius: 1rem;
display: flex;
align-items: center;
flex-direction: column;
justify-content: space-between;
}
/* Адаптивные стили для мобильных устройств */
@media only screen and (max-width: 600px) {
.main {
margin: 0.5rem;
min-height: 80vh;
min-width: auto;
max-width: none;
width: 95%; /* Можно сделать еще меньше, если требуется */
border-radius: 0.5rem;
}
}
-39
View File
@@ -1,39 +0,0 @@
<template>
<section class="about">
<h1>О нас</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eu mi ut erat rhoncus sollicitudin. Sed vel nunc quis ante malesuada finibus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec tincidunt sapien vitae risus placerat, eget blandit lectus dignissim. Suspendisse potenti. Maecenas ultricies purus non dui volutpat pharetra. Nulla facilisi. Etiam at justo sed augue rutrum sodales.
</p>
<h2>Наши сотрудники</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam convallis arcu non quam bibendum, nec mollis magna feugiat. Phasellus porttitor felis at neque commodo, in tempus tellus aliquet. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Aenean laoreet enim ut massa viverra dictum. Aliquam cursus lacinia lorem, non scelerisque nisl fringilla ut. Fusce varius libero ut odio mattis fermentum. Ut pretium diam ac metus semper, sit amet porta leo vulputate.
</p>
<h2>Чем мы занимаемся</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque ornare nulla eget ex pellentesque, at congue est accumsan. Duis gravida pulvinar hendrerit. Mauris maximus eros ut tempor lobortis. Morbi euismod interdum nisi, eu condimentum mauris molestie vitae. Praesent venenatis consequat tortor, eu iaculis nunc suscipit id. Curabitur in nibh in odio imperdiet ultrices. Vivamus ut vestibulum nisi. Integer quis augue a ligula porta vehicula. In hac habitasse platea dictumst.
</p>
</section>
</template>
<script>
export default {
name: 'About'
};
</script>
<style scoped>
.about {
padding: 20px;
text-align: justify;
}
h1, h2 {
margin-top: 30px;
}
p {
line-height: 1.6;
}
</style>
+51 -51
View File
@@ -1,53 +1,53 @@
<template>
<footer class="footer">
<nav class="footer-nav">
<ul>
<li><a href="#about">О нас</a></li>
<li><a href="#requisites">Реквизиты</a></li>
<li><a href="#vacancies">Вакансии</a></li>
<li><a href="#donates">Донаты</a></li>
</ul>
</nav>
<p class="copyright">© Все права защищены, 2024</p>
</footer>
</template>
<script>
export default {
name: 'AppFooter'
};
</script>
<style scoped>
.footer {
background-color: #f9f9f9;
padding: 20px;
text-align: center;
}
.footer-nav ul {
list-style-type: none;
padding: 0;
margin: 0;
<div class="footer">
<div class="navigation" v-on:click="goBack">
<img src="./images/icons/arrow_back_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg" alt="back">
</div>
<router-link to="/">
<img src="./images/icons/home_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg" alt="home">
</router-link>
<div class="navigation" v-on:click="goForward">
<img src="./images/icons/arrow_forward_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg" alt="forward">
</div>
</div>
</template>
<style scoped>
.footer {
height: 3rem;
background-color: darkmagenta;
border-radius: 1rem;
display: flex;
justify-content: center;
}
.footer-nav li {
margin-right: 20px;
}
.footer-nav li:last-child {
margin-right: 0;
}
.footer-nav a {
text-decoration: none;
color: #333;
}
.copyright {
font-size: 14px;
margin-top: 10px;
}
</style>
flex-direction: row;
align-items: center;
padding: 0 2rem;
margin: 0 0 0.5rem 0;
}
.navigation {
padding: 0 1rem;
margin: 0 1rem;
}
.navigation:hover {
cursor: pointer;
background-color: rgb(112, 48, 171);
}
</style>
<script>
import MenuIcon from 'vue-material-design-icons/Menu.vue';
export default {
components: {
MenuIcon,
},
name: 'footer',
methods: {
goBack() {
this.$router.go(-1);
},
goForward() {
this.$router.go(1);
}
}
};
</script>
-81
View File
@@ -1,81 +0,0 @@
<template>
<header class="header">
<!-- Логотип -->
<div class="logo">
<img src="@/assets/logo.png" alt="Логотип" />
</div>
<!-- Выпадающее меню -->
<div class="dropdown-menu">
<button @click="isOpen = !isOpen">Меню</button>
<ul v-show="isOpen" class="menu-list">
<li v-for="item in menuItems" :key="item.id">
<router-link :to="item.to">{{ item.id }} - {{ item.title }}</router-link>
</li>
</ul>
</div>
</header>
</template>
<script>
export default {
name: 'AppHeader',
data() {
return {
isOpen: false,
menuItems: [
{ id: 1, title: 'Главная', to: '#' },
{ id: 2, title: 'О нас', to: '/about' },
{ id: 3, title: 'Услуги', to: '#' },
{ id: 4, title: 'Контакты', to: '#' },
{ id: 5, title: 'Блог', to: '#' },
{ id: 6, title: 'SignUp', to: '#' }
]
};
}
};
</script>
<style scoped>
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
}
.logo img {
width: 100px;
height: auto;
}
.dropdown-menu button {
background-color: #f0f0f0;
border: none;
cursor: pointer;
padding: 8px 16px;
}
.menu-list {
list-style-type: none;
margin: 0;
padding: 0;
position: absolute;
right: 0;
background-color: white;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
z-index: 99;
}
.menu-list li a {
display: block;
text-decoration: none;
color: black;
padding: 12px 20px;
}
.menu-list li a:hover {
background-color: #eaeaea;
}
</style>
-67
View File
@@ -1,67 +0,0 @@
<template>
<div class="content-wrapper" :class="{ centered: isCentered }">
<slot>
<SearchBar @submited="queryStr" />
<SearchResults ref="sdf" />
</slot>
</div>
</template>
<script>
import SearchBar from './SearchBar.vue';
import SearchResults from './SearchResults.vue';
export default {
name: 'ContentWrapper',
props: {
// Проп для управления положением контента (центрирование или выравнивание вверх)
isCentered: {
type: Boolean,
default: true
}
},
components: {
SearchBar,
SearchResults
},
methods: {
queryStr(searchQuery) {
alert(this.$refs.sdf)
this.isCentered = false;
this.$refs.searchResultsRef.showContent = true;
this.$refs.searchResultsRef.results = [
{
"id": "asldfjk",
"title": "title1",
"description": "long description about this result"
},
{
"id": "asladsdfjk",
"title": "title2",
"description": "long description about this result"
},{
"id": "asldfasdfjk",
"title": "title3",
"description": "long description about this result"
}
];
}
}
};
</script>
<style scoped>
.content-wrapper {
display: flex;
flex-direction: column;
justify-content: flex-start; /* Выравниваем контент к верху */
align-items: stretch;
min-height: 80vh; /* Если нужно растянуть контейнер на всю высоту экрана */
}
.centered {
justify-content: center; /* Центрирование контента вертикально */
align-items: center; /* Центрирование контента горизонтально */
}
</style>
-53
View File
@@ -1,53 +0,0 @@
<template>
<div class="search-bar">
<input
type="text"
placeholder="Поиск..."
v-model="searchQuery"
@keyup.enter="onSubmit"
/>
<button @click="onSubmit">Найти</button>
</div>
</template>
<script>
export default {
name: 'SearchBar',
data() {
return {
searchQuery: ''
};
},
methods: {
onSubmit() {
this.$emit('submited', this.searchQuery);
}
}
};
</script>
<style scoped>
.search-bar {
display: flex;
gap: 10px;
align-items: center;
margin-bottom: 20px;
}
.search-bar input {
flex-grow: 1;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
.search-bar button {
padding: 10px 20px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
</style>
-62
View File
@@ -1,62 +0,0 @@
<template>
<div class="search-result-card">
<img :src="result.photo" alt="Фото" class="card-image" />
<div class="card-content">
<h3 class="title">{{ result.title }}</h3>
<p class="description">{{ result.description }}</p>
<router-link :to="`/details/${result.id}`" class="view-more">Подробнее</router-link>
</div>
</div>
</template>
<script>
export default {
name: "SearchResultCard",
props: {
result: {
type: Object,
required: true,
},
},
};
</script>
<style scoped>
.search-result-card {
border: 1px solid #ddd;
border-radius: 5px;
overflow: hidden;
background-color: #fff;
display: flex;
flex-direction: column;
justify-content: space-between;
transition: transform 0.3s ease-in-out;
}
.card-image {
width: 100%;
height: 200px;
object-fit: cover;
}
.card-content {
padding: 10px;
}
.title {
margin-top: 0;
margin-bottom: 5px;
font-weight: bold;
}
.view-more {
text-decoration: none;
color: #007bff;
align-self: flex-end;
}
.search-result-card:hover {
transform: scale(1.02);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
</style>
-45
View File
@@ -1,45 +0,0 @@
<template>
<div v-show="showContent" class="search-results">
<h2>Результаты поиска</h2>
<div class="results-container">
<SearchResultCard v-for="result in results" :key="result.id" :result="result" />
</div>
</div>
</template>
<script>
import SearchResultCard from "./SearchResultCard.vue";
export default {
data() {
return {
showContent: false,
};
},
name: "SearchResults",
components: {
SearchResultCard,
},
props: {
results: {
type: Array,
required: true,
},
},
};
</script>
<style scoped>
.search-results {
max-width:src="result.photo" 800px;
margin: 0 auto;
padding: 20px;
}
.results-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
}
</style>
-71
View File
@@ -1,71 +0,0 @@
<template>
<div class="registration-form">
<h2>Регистрация</h2>
<form @submit.prevent="handleSubmit">
<label for="name">Имя:</label>
<input type="text" id="name" v-model="formData.name" required />
<label for="phone">Телефон:</label>
<input type="tel" id="phone" v-model="formData.phone" required />
<label for="password">Пароль:</label>
<input type="password" id="password" v-model="formData.password" required />
<button type="submit">Зарегистрироваться</button>
</form>
</div>
</template>
<script setup>
import { reactive } from 'vue';
import axios from 'axios';
const formData = reactive({
name: '',
phone: '',
password: ''
});
async function handleSubmit() {
try {
const response = await axios.post('http://localhost:8080/api/signup', formData);
console.log(response.data); // Логируем ответ сервера
alert('Успешная регистрация!');
} catch (error) {
console.error(error);
alert('Ошибка при регистрации');
}
}
</script>
<style scoped>
.registration-form {
max-width: 400px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 10px;
}
label {
display: block;
margin-bottom: 5px;
}
input {
width: 100%;
padding: 8px;
margin-bottom: 15px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
background-color: #4CAF50;
color: white;
padding: 12px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
+55
View File
@@ -0,0 +1,55 @@
<template>
<div class="about">
<h1>Freedom & Adventure</h1>
<p>Приложение для туристов</p>
<p>Прошу потдержать проект</p>
<button v-on:click="donat" class="donat-coffee">Донат на кофе</button>
<img src="../images/photo_2025-01-25_05-57-24.jpg" alt="Донат QRcod" class="QRcod">
</div>
</template>
<style scoped>
.about {
background-color: #8B4513; /* коричневый цвет */
height: fit-content;
display: flex;
flex-direction: column;
align-items: center;
filter: none;
padding: 1rem;
border-radius: 1rem;
margin: 1rem;
}
.donat-coffee {
margin: 1rem;
background-color: rgb(89, 219, 109);
box-shadow: 1px 4px 8px 0 rgba(27, 45, 37, 0.2);
border-radius: 1rem;
}
.donat-coffee:hover {
cursor: pointer;
background-color: rgb(10, 150, 50);
color: white;
box-shadow: 1px 4px 8px rgb(10, 150, 50);
}
.QRcod {
height: 200px;
display: flexbox;
border-radius: 0.5rem;
}
</style>
<script>
export default {
methods: {
donat() {
window.open("https://www.tinkoff.ru/rm/r_fFXNVwPxmH.VMEQXkfWxO/Ho4MN83486", "_blank");
}
}
}
</script>
+58
View File
@@ -0,0 +1,58 @@
<template>
<div v-if="loading">Загрузка...</div>
<ul v-else-if="error">
Произошла ошибка: {{ error }}
</ul>
<ul v-else>
<li v-for="commit in commits" :key="commit.sha">
<strong>{{ commit.commit.author.date.slice(0, 11) }}:</strong> {{ commit.commit.message }} <i>{{ commit.author.login }}</i>
</li>
</ul>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
loading: true,
commits: [],
error: null,
};
},
mounted() {
this.fetchCommits();
},
methods: {
async fetchCommits() {
try {
const response = await axios.get('https://api.github.com/repos/valitovgaziz/spa_/commits', {
headers: {
Accept: 'application/vnd.github.v3+json',
},
});
this.commits = response.data;
} catch (err) {
this.error = err.message || 'Произошла неизвестная ошибка';
} finally {
this.loading = false;
}
},
},
};
</script>
<style scoped>
ul {
max-width: 1024px;
list-style-type: none;
padding: 0;
}
li {
margin: 0.5rem 0;
white-space: pre-wrap;
width: 100%;
}
</style>x``
+61
View File
@@ -0,0 +1,61 @@
<template>
<div class="developers">
<!-- Заголовок -->
<h1 class="title">Разработчики</h1>
<!-- Список разработчиков -->
<ul class="developer-list">
<li v-for="dev in developers" :key="dev.name" class="developer-item">
<a :href="dev.profileUrl" target="_blank" rel="noopener noreferrer">
{{ dev.nickname }} ({{ dev.name }})
</a>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'DevelopersList',
data() {
return {
developers: [
{ nickname: 'valitovgaziz', name: 'Валитов Газиз', profileUrl: 'https://github.com/valitovgaziz' }
// Добавьте больше разработчиков по аналогии...
]
}
}
}
</script>
<style scoped>
.developers {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.title {
text-align: center;
font-size: 24px;
margin-bottom: 20px;
}
.developer-list {
list-style-type: none;
padding-left: 0;
}
.developer-item {
margin-bottom: 10px;
}
.developer-item a {
color: #007bff;
text-decoration: none;
}
.developer-item a:hover {
text-decoration: underline;
}
</style>
+118
View File
@@ -0,0 +1,118 @@
<template>
<div class="reviews-container">
<h2>Отзывы о приложении</h2>
<!-- Форма для добавления отзыва -->
<div class="add-review">
<textarea v-model="newReview" placeholder="Напишите ваш отзыв..." rows="4"></textarea>
<button @click="submitReview" :disabled="!newReview.trim()">Оставить отзыв</button>
</div>
<!-- Список последних отзывов -->
<div class="reviews-list">
<div v-for="review in reviews" :key="review.id" class="review-item">
<p>{{ review.text }}</p>
<small>{{ formatDate(review.created_at) }}</small>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
reviews: [], // Список отзывов
newReview: '', // Текст нового отзыва
};
},
mounted() {
this.loadReviews(); // Загружаем отзывы при монтировании компонента
},
methods: {
// Загрузка отзывов из базы данных
async loadReviews() {
try {
const response = await axios.get('http://213.108.4.63:3000/api/reviews');
this.reviews = response.data.slice(0, 5); // Показываем только последние 5 отзывов
} catch (error) {
console.error('Ошибка при загрузке отзывов:', error);
}
},
// Отправка нового отзыва
async submitReview() {
if (!this.newReview.trim()) return;
try {
const response = await axios.post('http://213.108.4.63:3000/api/reviews', {
text: this.newReview,
});
this.reviews.unshift(response.data); // Добавляем новый отзыв в начало списка
this.newReview = ''; // Очищаем текстовое поле
if (this.reviews.length > 5) {
this.reviews.pop(); // Удаляем самый старый отзыв, если их больше 5
}
} catch (error) {
console.error('Ошибка при отправке отзыва:', error);
}
},
// Форматирование даты
formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleString(); // Форматируем дату в удобный формат
},
},
};
</script>
<style scoped>
.reviews-container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.add-review textarea {
width: 100%;
padding: 10px;
margin-bottom: 10px;
border-radius: 5px;
border: 1px solid #ccc;
}
.add-review button {
padding: 10px 20px;
background-color: #42b983;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.add-review button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.reviews-list {
margin-top: 20px;
}
.review-item {
background: #f9f9f9;
padding: 15px;
border-radius: 5px;
margin-bottom: 10px;
}
.review-item p {
margin: 0;
}
.review-item small {
color: #666;
font-size: 0.9em;
}
</style>
+50
View File
@@ -0,0 +1,50 @@
<template>
<div class="container">
<!-- Заголовок -->
<h1>Trip & Freedom & Adventure</h1>
<!-- Основной контент -->
<p>Разработка приложения Trip&Freedom&Adventure представляет собой вклад в создание полезного продукта для общества. Данная работа играет важную роль в профессиональном росте и развитии. Миссия этого приложения заключается в помощи пользователям в открытии новых горизонтов, планировании путешествий и наслаждении приключениями без излишних хлопот. Приложение принесет пользу как пользователям, так и мне лично, позволяя углубить мои знания в программировании, дизайне интерфейсов и разработке мобильных приложений. Участие в этом проекте также предоставляет возможность проявить креативность и воплотить идеи, способные улучшить качество жизни людей. Ежедневная работа над приложением приближает нас к созданию удобного и функционального продукта, который станет незаменимым инструментом для путешественников. Этот проект вдохновляет на постоянное движение вперёд, обучение новым навыкам и внесение вклада в технологическое и общественное развитие.</p>
</div>
</template>
<script setup lang="ts">
// Здесь может быть логика вашего компонента, если требуется
</script>
<style scoped>
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
h1 {
text-align: center;
font-size: 32px;
margin-bottom: 30px;
}
p {
line-height: 1.6;
font-size: 18px;
}
.additional-content {
margin-top: 40px;
}
@media screen and (max-width: 600px) {
.container {
padding: 10px;
}
h1 {
font-size: 24px;
}
p {
font-size: 16px;
}
}
</style>
+70
View File
@@ -0,0 +1,70 @@
<template>
<div class="header">
<router-link to="/about" id="rl-about"><img src="../images/logo150x150.png" alt="logo"></router-link>
<router-link to="/filosofy" id="rl-filosofy">Trip*Freedom*Adventure</router-link>
<Menu />
</div>
</template>
<style scoped>
.header {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
border-radius: 1rem;
background-color: aquamarine;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
height: 3rem;
}
#rl-about {
display: flex;
flex-direction: row;
align-items: center;
justify-items: center;
border-radius: 50%;
padding: 1rem;
height: 1rem;
width: 1rem;
}
#rl-about:hover {
cursor: pointer;
background-color: rgb(85, 117, 117);
}
img {
height: 1.5rem;
padding: 0 1rem 0 0;
}
#rl-filosofy {
all: unset;
box-shadow: 1px 2px 3px rgb(63, 145, 130);
border-radius: 1rem;
padding: 0.5rem;
}
#rl-filosofy:hover {
cursor: pointer;
background-color: rgb(143, 202, 202);
box-shadow: 2px 3px 4px rgb(64,146, 132);
}
</style>
<script>
import Menu from './menu.vue';
export default {
components: {
Menu,
},
name: 'header',
}
</script>
+85
View File
@@ -0,0 +1,85 @@
<template>
<div class="menu" v-on:click="OpenClose">
<img src="../images/icons/menu_24dp_E8EAED_FILL0_wght400_GRAD0_opsz24.svg" alt="menu" id="img-m">
<ul id="ul-m">
<li v-on:click="$refs.profile.$el.click()"><router-link to="/profile" ref="profile">Профиль</router-link></li>
<li v-on:click="$refs.settings.$el.click()"><router-link to="/settings" ref="settings">Настройки</router-link></li>
<li v-on:click="$refs.about.$el.click()"><router-link to="/about" ref="about">О нас</router-link></li>
<li v-on:click="$refs.support.$el.click()"><router-link to="/support" ref="support">Написать в техпотдержку</router-link></li>
<li v-on:click="$refs.feetback.$el.click()"><router-link to="/feetback" ref="feetback">Оставить отзыв</router-link></li>
<li v-on:click="$refs.registration.$el.click()"><router-link to="/registration" ref="registration">Регистрация</router-link></li>
<li v-on:click="$refs.login.$el.click()"><router-link to="/login" ref="login">Войти</router-link></li>
<li v-on:click="$refs.logout.$el.click()"><router-link to="/logout" ref="logout">Выйти</router-link></li>
</ul>
</div>
</template>
<style scoped>
.menu {
height: 100%;
position: relative;
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: center;
}
#img-m {
height: 2rem;
width: 2rem;
border: solid 1px rgb(33, 159, 181);
border-radius: 1rem;
background-color: rgb(37, 78, 78);
margin: 0 1rem 0 0;
}
#img-m:hover {
cursor: pointer;
background-color: rgb(71, 135, 105);
}
#ul-m {
position: absolute;
top: 3rem;
right: 1rem;
width: fit-content;
visibility: hidden;
margin: auto;
border: 1px solid;
}
li {
margin: 3px 0;
border: solid 1px black;
border-radius: 1rem;
padding: 0 0.5rem;
}
li:hover {
cursor: pointer;
background-color: rgb(153, 204, 204);
border-radius: 1rem;
}
</style>
<script>
export default {
name: 'menu',
methods: {
OpenClose: OpenCloseM
}
}
function OpenCloseM() {
var ul = document.querySelector('ul');
if (ul.style.visibility === 'hidden') {
ul.style.visibility = 'visible';
} else {
ul.style.visibility = 'hidden';
}
}
</script>
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="m313-440 224 224-57 56-320-320 320-320 57 56-224 224h487v80H313Z"/></svg>

After

Width:  |  Height:  |  Size: 189 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M647-440H160v-80h487L423-744l57-56 320 320-320 320-57-56 224-224Z"/></svg>

After

Width:  |  Height:  |  Size: 190 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"/></svg>

After

Width:  |  Height:  |  Size: 222 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M240-200h120v-240h240v240h120v-360L480-740 240-560v360Zm-80 80v-480l320-240 320 240v480H520v-240h-80v240H160Zm320-350Z"/></svg>

After

Width:  |  Height:  |  Size: 243 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e8eaed"><path d="M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z"/></svg>

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

+116
View File
@@ -0,0 +1,116 @@
<template>
<div class="login-form">
<h1>Логин</h1>
<form @submit.prevent="handleSubmit">
<div class="form-group">
<label for="email">Email:</label>
<input v-model.trim="email" type="email" id="email" required />
</div>
<div class="form-group">
<label for="password">Пароль:</label>
<input v-model.trim="password" type="password" id="password" required />
</div>
<button type="submit">Войти</button>
</form>
</div>
</template>
<script>
export default {
data() {
return {
email: '',
password: ''
};
},
methods: {
handleSubmit() {
if (!this.isValid(this.email, this.password)) {
alert("Пожалуйста, заполните все поля корректно.");
return;
}
this.loginUser({
email: this.email,
password: this.password
}).then(() => {
this.$router.push('/'); // Переход на главную страницу
}).catch((error) => {
console.error(error);
alert('Неверный email или пароль. Попробуйте снова.');
});
},
isValid(email, password) {
if (email.length === 0 || password.length === 0) {
return false;
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
return false;
}
if (password.length < 6) {
return false;
}
return true;
},
async loginUser(data) {
console.log("Login by this data: ", data);
// try {
// const response = await fetch('/login', {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json'
// },
// body: JSON.stringify(data)
// });
// if (!response.ok) {
// throw new Error('Неверный email или пароль');
// }
// } catch (error) {
// throw error;
// }
}
}
};
</script>
<style scoped>
.login-form {
max-width: 400px;
margin: 50px auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 10px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
}
.form-group input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
button {
padding: 10px 20px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
</style>
+17
View File
@@ -0,0 +1,17 @@
<template>
<div class="logout">
<h2>выход</h2>
</div>
</template>
<style>
.logout {
background-color: #c68686;
}
</style>
<script>
export default {
name: 'logout',
}
</script>
+123
View File
@@ -0,0 +1,123 @@
<template>
<div class="register-form">
<h1>Регистрация</h1>
<form @submit.prevent="handleSubmit">
<div class="form-group">
<label for="username">Имя пользователя:</label>
<input v-model.trim="username" type="text" id="username" required />
</div>
<div class="form-group">
<label for="email">Email:</label>
<input v-model.trim="email" type="email" id="email" required />
</div>
<div class="form-group">
<label for="password">Пароль:</label>
<input v-model.trim="password" type="password" id="password" required />
</div>
<button type="submit">Зарегистрироваться</button>
</form>
</div>
</template>
<script>
export default {
data() {
return {
username: '',
email: '',
password: ''
};
},
methods: {
handleSubmit() {
if (!this.isValid(this.username, this.email, this.password)) {
alert("Пожалуйста, заполните все поля корректно.");
return;
}
this.sendRegistrationData({
username: this.username,
email: this.email,
password: this.password
}).then(() => {
this.$router.push('/login'); // Переход на страницу логина
}).catch((error) => {
console.error(error);
alert('Что-то пошло не так. Попробуйте еще раз.');
});
},
isValid(username, email, password) {
if (username.length === 0 || email.length === 0 || password.length === 0) {
return false;
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
return false;
}
if (password.length < 6) {
return false;
}
return true;
},
async sendRegistrationData(data) {
console.log("Register by this data: ", data);
// try {
// const response = await fetch('/register', {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json'
// },
// body: JSON.stringify(data)
// });
// if (!response.ok) {
// throw new Error('Ошибка при регистрации');
// }
// } catch (error) {
// throw error;
// }
}
}
};
</script>
<style scoped>
.register-form {
max-width: 400px;
margin: 50px auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 10px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
}
.form-group input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
button {
padding: 10px 20px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
</style>
+13
View File
@@ -0,0 +1,13 @@
<template>
<div class="profile">
<h3>This is an profile</h3>
</div>
</template>
<style></style>
<script>
export default {
name: 'profile',
}
</script>
@@ -0,0 +1,14 @@
<template>
<div class="results">
<h1>Results</h1>
</div>
</template>
<style>
.results {
background-color: darkslategrey;
}
</style>
<script>
</script>
@@ -0,0 +1,120 @@
<template>
<div class="search-container">
<div class="search-input-container">
<!-- Иконка для вызова меню параметров -->
<div class="menu-icon" @click="toggleMenu">
<svg viewBox="0 0 24 24" width="24" height="24">
<path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" fill="currentColor"/>
</svg>
</div>
<!-- Поле ввода -->
<input
v-model="searchQuery"
@keyup.enter="performSearch"
class="search-input"
placeholder="Введите место для поиска"
/>
<!-- Кнопка поиска -->
<button @click="performSearch" class="search-button">
<svg viewBox="0 0 24 24" width="24" height="24">
<path d="M10 18a8 8 0 1 1 0-16 8 8 0 0 1 0 16zm8-8a7.95 7.95 0 0 1-1.757 4.95l4.95 4.95-1.414 1.414-4.95-4.95A7.95 7.95 0 0 1 10 18z" fill="currentColor"/>
</svg>
</button>
</div>
<!-- Выпадающее меню для параметров поиска -->
<div v-if="isMenuVisible" class="search-menu">
<div class="menu-item" v-for="option in searchOptions" :key="option" @click="selectOption(option)">
{{ option }}
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
searchQuery: '',
isMenuVisible: false,
searchOptions: ['Пляжи', 'Горы', 'Отели', 'Рестораны']
};
},
methods: {
performSearch() {
if (this.searchQuery.trim()) {
// Переход на страницу результатов поиска с передачей запроса
this.$router.push({
name: 'results', // Имя маршрута для страницы результатов
query: { q: this.searchQuery } // Передача поискового запроса через query-параметры
});
}
},
toggleMenu() {
this.isMenuVisible = !this.isMenuVisible;
},
selectOption(option) {
this.searchQuery = option;
this.isMenuVisible = false;
this.performSearch();
}
}
};
</script>
<style scoped>
.search-container {
position: relative;
width: 100%;
max-width: 600px;
margin: auto;
}
.search-input-container {
display: flex;
align-items: center;
background: #fff;
border-radius: 25px;
padding: 10px;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
.menu-icon {
cursor: pointer;
margin-right: 10px;
}
.search-input {
flex-grow: 1;
border: none;
outline: none;
padding: 10px;
border-radius: 20px;
}
.search-button {
background: none;
border: none;
cursor: pointer;
padding: 10px;
}
.search-menu {
position: absolute;
top: 60px;
left: 0;
right: 0;
background: #fff;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
z-index: 1000;
}
.menu-item {
padding: 10px;
cursor: pointer;
}
.menu-item:hover {
background-color: #f0f0f0;
}
</style>
+17
View File
@@ -0,0 +1,17 @@
<template>
<div class="settings">
<h1>Settings</h1>
</div>
</template>
<style>
.settings {
background-color: #856565;
}
</style>
<script>
export default {
name: 'Settings',
}
</script>
+17
View File
@@ -0,0 +1,17 @@
<template>
<div class="support">
<h3>This is an message send to support</h3>
</div>
</template>
<style scoped>
.support {
background-color: darkkhaki;
}
</style>
<script>
export default {
name: 'support',
}
</script>
+1 -1
View File
@@ -1,4 +1,4 @@
import './assets/main.css'
import './assets/main.css';
import { createApp } from 'vue'
import App from './App.vue'
+51 -11
View File
@@ -1,27 +1,67 @@
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import SignUpView from '../views/SingUpV.vue'
import About from '../views/AboutV.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/signup',
name: 'signup',
component: SignUpView,
},
{
path: '/',
name: 'home',
component: HomeView,
component: () => import('../views/HomeView.vue'),
},
{
path: '/about',
name: 'about',
component: About
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue'),
},
{
path: '/profile',
name: 'profile',
component: () => import('../views/ProfileView.vue'),
},
{
path: '/support',
name: 'support',
component: () => import('../views/SupportView.vue'),
},
{
path: '/feetback',
name: 'feetback',
component: () => import('../views/FeetbackView.vue'),
},
{
path: '/results',
name: 'results',
component: () => import('../views/ResultsView.vue'),
},
{
path: '/settings',
name: 'settings',
component: () => import('../views/SettingsView.vue'),
},
{
path: '/registration',
name: 'registration',
component: () => import('../views/RegistrationView.vue')
},
{
path: '/login',
name: 'login',
component: () => import('../views/LogInView.vue')
},
{
path: '/logout',
name: 'logout',
component: () => import('../views/LogOutView.vue')
},
{
path: '/filosofy',
name: 'filosofy',
component: () => import('../views/FilosofyView.vue')
}
],
});
})
export default router
-12
View File
@@ -1,12 +0,0 @@
<script setup>
import Header from '../components/Header.vue'
import About from '../components/AboutC.vue'
import Footer from '../components/Footer.vue'
</script>
<template>
<Header />
<About />
<Footer />
</template>
+29
View File
@@ -0,0 +1,29 @@
<template>
<Header />
<About />
<Developers />
<Commits />
<Footer />
</template>
<style></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import About from '../components/about/about.vue';
import Commits from '../components/about/commits.vue';
import Developers from '../components/about/developers.vue';
export default {
components: {
Header,
Footer,
About,
Developers,
Commits,
},
};
</script>
+23
View File
@@ -0,0 +1,23 @@
<template>
<Header />
<Feetback />
<Footer />
</template>
<style></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import Feetback from '../components/feeback/feetback.vue';
export default {
components: {
Header,
Footer,
Feetback,
},
};
</script>
+23
View File
@@ -0,0 +1,23 @@
<template>
<Header />
<Filosofy />
<Footer />
</template>
<style>
</style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import Filosofy from '../components/filosofy.vue';
export default {
components: {
Footer,
Header,
Filosofy,
},
}
</script>
+18 -7
View File
@@ -1,11 +1,22 @@
<script setup>
import Header from '../components/Header.vue'
import Footer from '../components/Footer.vue'
import Main from '../components/Main.vue'
</script>
<template>
<Header />
<Main />
<SearchLine />
<Footer />
</template>
<style src="../assets/main.css"></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import SearchLine from '../components/searchLine/searchLine.vue';
export default {
components: {
Header,
Footer,
SearchLine,
},
};
</script>
+23
View File
@@ -0,0 +1,23 @@
<template>
<Header />
<LogIn />
<Footer />
</template>
<style></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import LogIn from '../components/inout/login.vue';
export default {
components: {
Header,
Footer,
LogIn,
},
};
</script>
+23
View File
@@ -0,0 +1,23 @@
<template>
<Header />
<LogOut />
<Footer />
</template>
<style></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import LogOut from '../components/inout/logout.vue';
export default {
components: {
Header,
Footer,
LogOut,
},
};
</script>
+22
View File
@@ -0,0 +1,22 @@
<template>
<Header />
<Profile />
<Footer />
</template>
<style></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import Profile from '../components/profile.vue';
export default {
components: {
Header,
Footer,
Profile,
},
};
</script>
+23
View File
@@ -0,0 +1,23 @@
<template>
<Header />
<Registration />
<Footer />
</template>
<style></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import Registration from '../components/inout/registration.vue';
export default {
components: {
Header,
Footer,
Registration,
},
};
</script>
+27
View File
@@ -0,0 +1,27 @@
<template>
<Header />
<SearchLine />
<Results />
<Footer />
</template>
<style></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import Results from '../components/saerch_results/results.vue';
import searchLine from '@/components/searchLine/searchLine.vue';
import SearchLine from '@/components/searchLine/searchLine.vue';
export default {
components: {
Header,
Footer,
Results,
SearchLine,
},
};
</script>
+22
View File
@@ -0,0 +1,22 @@
<template>
<Header />
<Settings />
<Footer />
</template>
<style></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import Settings from '../components/settings.vue';
export default {
components: {
Header,
Footer,
Settings,
},
};
</script>
-11
View File
@@ -1,11 +0,0 @@
<script setup>
import Header from '../components/Header.vue'
import Footer from '../components/Footer.vue'
import SignUpC from '../components/SignUpC.vue'
</script>
<template>
<Header />
<SignUpC />
<Footer />
</template>
+23
View File
@@ -0,0 +1,23 @@
<template>
<Header />
<Support />
<Footer />
</template>
<style></style>
<script>
import Header from '../components/header/header.vue';
import Footer from '../components/footer.vue';
import Support from '../components/support.vue';
export default {
components: {
Header,
Footer,
Support,
},
};
</script>
+9
View File
@@ -0,0 +1,9 @@
module.exports = {
content: [
'./src/**/*.{html,js,vue}',
],
theme: {
extend: {},
},
plugins: [],
}