flatten easySite directory: remove extra easySite/easySite nesting
- Moved contents of main_dc/yalarba/easySite/easySite/ up to easySite/ - Updated docker-compose.yml build context path - Deleted empty nested easySite/ directory
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div class="modal-overlay" @click="$emit('close')">
|
||||
<div class="modal-content" @click.stop>
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title">Бронирование</h2>
|
||||
<button class="modal-close" @click="$emit('close')">✕</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<!-- Контент модального окна бронирования -->
|
||||
<div class="booking-summary">
|
||||
<h3>{{ object?.title }}</h3>
|
||||
<p>{{ object?.city }}, {{ object?.address }}</p>
|
||||
</div>
|
||||
<!-- Форма бронирования -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// Определяем тип объекта недвижимости
|
||||
interface RentalObject {
|
||||
title: string;
|
||||
city?: string; // опционально, если может отсутствовать
|
||||
address?: string; // опционально
|
||||
// другие поля при необходимости
|
||||
}
|
||||
|
||||
defineProps<{
|
||||
object: RentalObject | null | undefined;
|
||||
dates: unknown;
|
||||
guests: string;
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
close: [];
|
||||
confirm: [bookingData: unknown];
|
||||
}>()
|
||||
</script>
|
||||
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<div class="gallery-overlay" @click="$emit('close')">
|
||||
<div class="gallery-content" @click.stop>
|
||||
<button class="gallery-close" @click="$emit('close')">✕</button>
|
||||
<img :src="currentImage" :alt="`Image ${currentIndex + 1}`" class="gallery-image" >
|
||||
<button class="gallery-nav prev" @click="prevImage">‹</button>
|
||||
<button class="gallery-nav next" @click="nextImage">›</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
images: string[]
|
||||
initialIndex: number
|
||||
}>()
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const emit = defineEmits<{
|
||||
close: []
|
||||
}>()
|
||||
|
||||
const currentIndex = ref(props.initialIndex)
|
||||
const currentImage = computed(() => props.images[currentIndex.value])
|
||||
|
||||
const nextImage = () => {
|
||||
currentIndex.value = (currentIndex.value + 1) % props.images.length
|
||||
}
|
||||
|
||||
const prevImage = () => {
|
||||
currentIndex.value = (currentIndex.value - 1 + props.images.length) % props.images.length
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div class="card cursor-pointer" @click="$emit('click')">
|
||||
<div class="relative">
|
||||
<img
|
||||
:src="imageSrc"
|
||||
:alt="object.title"
|
||||
class="w-full h-48 object-cover"
|
||||
>
|
||||
<div class="absolute top-2 right-2">
|
||||
<span class="badge" :class="statusBadgeClass">
|
||||
{{ statusLabel }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<h3 class="text-lg font-semibold mb-2">{{ object.title || object.short_name }}</h3>
|
||||
<p class="text-gray-600 text-sm mb-3 line-clamp-2">
|
||||
{{ object.address || 'Адрес не указан' }}
|
||||
</p>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-1">
|
||||
<span class="text-yellow-500">⭐</span>
|
||||
<span class="text-sm font-medium">{{ averageScore }}</span>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="font-bold text-primary-600">
|
||||
{{ formatPrice(object.price) }}
|
||||
</div>
|
||||
<div class="text-xs text-gray-500">{{ object.price_period || 'за единицу' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 flex items-center text-sm text-gray-500">
|
||||
<span class="mr-2">📍</span>
|
||||
<span>{{ object.address || 'Адрес не указан' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ObjectShortResponse } from '~/types/objects'
|
||||
|
||||
interface Props {
|
||||
object: ObjectShortResponse
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
defineEmits<{ click: [] }>()
|
||||
|
||||
const imageSrc = computed(() => {
|
||||
return '/images/placeholder.jpg'
|
||||
})
|
||||
|
||||
const averageScore = computed(() => {
|
||||
return props.object.tourist_average_score || props.object.entrepreneur_average_score || '—'
|
||||
})
|
||||
|
||||
const statusLabel = computed(() => {
|
||||
const labels: Record<string, string> = {
|
||||
active: 'Активен',
|
||||
draft: 'Черновик',
|
||||
moderation: 'На модерации',
|
||||
inactive: 'Неактивен',
|
||||
rejected: 'Отклонён'
|
||||
}
|
||||
return labels[props.object.status] || props.object.status
|
||||
})
|
||||
|
||||
const statusBadgeClass = computed(() => {
|
||||
const classes: Record<string, string> = {
|
||||
active: 'badge-success',
|
||||
draft: 'badge-secondary',
|
||||
moderation: 'badge-warning',
|
||||
inactive: 'badge-secondary',
|
||||
rejected: 'badge-error'
|
||||
}
|
||||
return classes[props.object.status] || 'badge-secondary'
|
||||
})
|
||||
|
||||
const formatPrice = (price: number | undefined) => {
|
||||
if (!price && price !== 0) return '—'
|
||||
return new Intl.NumberFormat('ru-RU', {
|
||||
style: 'currency',
|
||||
currency: 'RUB',
|
||||
minimumFractionDigits: 0
|
||||
}).format(price)
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,194 @@
|
||||
<template>
|
||||
<form class="space-y-6" @submit.prevent="handleSubmit">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="text-lg font-semibold">Основная информация</h3>
|
||||
</div>
|
||||
<div class="card-body space-y-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Название объекта *</label>
|
||||
<input
|
||||
v-model="formData.short_name"
|
||||
type="text"
|
||||
class="form-input"
|
||||
required
|
||||
placeholder="Короткое название">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Полное название</label>
|
||||
<input
|
||||
v-model="formData.long_name"
|
||||
type="text"
|
||||
class="form-input"
|
||||
placeholder="Полное название (если отличается)">
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Тип объекта *</label>
|
||||
<select v-model="formData.type" class="form-select" required>
|
||||
<option value="">Выберите тип</option>
|
||||
<option value="hotel">Гостиница</option>
|
||||
<option value="sanatorium">Санаторий</option>
|
||||
<option value="guest_house">Гостевой дом</option>
|
||||
<option value="tour">Тур</option>
|
||||
<option value="excursion">Экскурсия</option>
|
||||
<option value="restaurant">Ресторан</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Статус</label>
|
||||
<select v-model="formData.status" class="form-select">
|
||||
<option value="draft">Черновик</option>
|
||||
<option value="moderation">Отправить на модерацию</option>
|
||||
<option value="active">Активен</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Описание</label>
|
||||
<textarea
|
||||
v-model="formData.description" class="form-input" rows="4"
|
||||
placeholder="Подробное описание объекта"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Краткое описание</label>
|
||||
<textarea
|
||||
v-model="formData.short_description" class="form-input" rows="2"
|
||||
placeholder="Краткое описание для списка"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="text-lg font-semibold">Местоположение</h3>
|
||||
</div>
|
||||
<div class="card-body space-y-4">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Адрес</label>
|
||||
<input
|
||||
v-model="formData.address" type="text" class="form-input"
|
||||
placeholder="Полный адрес">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="text-lg font-semibold">Цены и контакты</h3>
|
||||
</div>
|
||||
<div class="card-body space-y-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Цена</label>
|
||||
<input v-model="formData.price" type="number" class="form-input" placeholder="0">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Период цены</label>
|
||||
<select v-model="formData.price_period" class="form-select">
|
||||
<option value="">Не указано</option>
|
||||
<option value="per_night">За ночь</option>
|
||||
<option value="per_person">За человека</option>
|
||||
<option value="per_tour">За тур</option>
|
||||
<option value="per_hour">За час</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Сайт</label>
|
||||
<input
|
||||
v-model="formData.site" type="url" class="form-input"
|
||||
placeholder="https://">
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Телефон</label>
|
||||
<input
|
||||
v-model="formData.phone" type="tel" class="form-input"
|
||||
placeholder="+7 (XXX) XXX-XX-XX">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Email</label>
|
||||
<input
|
||||
v-model="formData.email" type="email" class="form-input"
|
||||
placeholder="email@example.com">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-4 justify-end">
|
||||
<button type="button" class="btn btn-outline" :disabled="loading" @click="$emit('cancel')">
|
||||
Отмена
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary" :disabled="loading">
|
||||
<span v-if="loading">Сохранение...</span>
|
||||
<span v-else>{{ object ? 'Обновить' : 'Создать' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface ObjectFormData {
|
||||
short_name: string
|
||||
long_name: string
|
||||
type: string
|
||||
description: string
|
||||
short_description: string
|
||||
address: string
|
||||
price: number | null
|
||||
price_period: string
|
||||
phone: string
|
||||
email: string
|
||||
site: string
|
||||
status: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
object?: ObjectFormData | null
|
||||
loading?: boolean
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
object: null,
|
||||
loading: false
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
submit: [formData: ObjectFormData]
|
||||
cancel: []
|
||||
}>()
|
||||
|
||||
const formData = reactive<ObjectFormData>({
|
||||
short_name: '',
|
||||
long_name: '',
|
||||
type: '',
|
||||
description: '',
|
||||
short_description: '',
|
||||
address: '',
|
||||
price: null,
|
||||
price_period: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
site: '',
|
||||
status: 'draft'
|
||||
})
|
||||
|
||||
watch(() => props.object, (newObject) => {
|
||||
if (newObject) {
|
||||
Object.assign(formData, newObject)
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
const handleSubmit = () => {
|
||||
const data = { ...formData }
|
||||
if (data.price === null) {
|
||||
delete (data as Record<string, unknown>).price
|
||||
}
|
||||
if (!data.price_period) delete (data as Record<string, unknown>).price_period
|
||||
emit('submit', data)
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<form @submit.prevent="handleSubmit" class="space-y-6">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Email</label>
|
||||
<input
|
||||
v-model="form.email"
|
||||
type="email"
|
||||
class="form-input"
|
||||
placeholder="your@email.com"
|
||||
required
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Пароль</label>
|
||||
<input
|
||||
v-model="form.password"
|
||||
type="password"
|
||||
class="form-input"
|
||||
placeholder="Введите пароль"
|
||||
required
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<label class="flex items-center">
|
||||
<input type="checkbox" class="rounded border-gray-300">
|
||||
<span class="ml-2 text-sm text-gray-600">Запомнить меня</span>
|
||||
</label>
|
||||
|
||||
<a href="#" class="text-sm text-primary-600 hover:text-primary-700">
|
||||
Забыли пароль?
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary w-full"
|
||||
:disabled="loading"
|
||||
>
|
||||
<span v-if="loading">Вход...</span>
|
||||
<span v-else>Войти</span>
|
||||
</button>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const { login } = useAuth()
|
||||
|
||||
const form = ref({
|
||||
email: '',
|
||||
password: ''
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const handleSubmit = async () => {
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
await login(form.value)
|
||||
await navigateTo('/profile')
|
||||
} catch (error) {
|
||||
console.error('Login error:', error)
|
||||
// Здесь можно добавить уведомление об ошибке
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,394 @@
|
||||
<template>
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<!-- Основное содержимое футера -->
|
||||
<div class="footer-content">
|
||||
|
||||
<!-- Информация о компании -->
|
||||
<div class="footer-section">
|
||||
<div class="footer-logo">
|
||||
<div class="logo-icon">
|
||||
<span class="logo-text">ES</span>
|
||||
</div>
|
||||
<span class="logo-name">EasySite</span>
|
||||
</div>
|
||||
<p class="footer-description">
|
||||
Платформа для агрегации туристических объектов.
|
||||
</p>
|
||||
<div class="social-links">
|
||||
<a href="https://vk.com/club222248484" target="_blank" class="social-link" aria-label="ВКонтакте">
|
||||
<span class="social-icon">📘</span>
|
||||
</a>
|
||||
<a href="https://t.me/+oYymS0r6qG9lYWJi" target="_blank" class="social-link" aria-label="Telegram">
|
||||
<span class="social-icon">📧</span>
|
||||
</a>
|
||||
<a href="https://rutube.ru/channel/26509398/" target="_blank" class="social-link" aria-label="RuTube">
|
||||
<span class="social-icon">📺</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Реквизиты -->
|
||||
<div class="footer-section">
|
||||
<h3 class="footer-title"><NuxtLink href="/requisites">Реквизиты</NuxtLink></h3>
|
||||
<div class="footer-info">
|
||||
<div class="info-item">
|
||||
<span class="info-label">ОГРН:</span>
|
||||
<span class="info-value">1220200038112</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">ИНН:</span>
|
||||
<span class="info-value">0234009584</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">КПП:</span>
|
||||
<span class="info-value">023401001</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Юр. адрес:</span>
|
||||
<span class="info-value">Нет</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Контакты -->
|
||||
<div class="footer-section">
|
||||
<h3 class="footer-title"><NuxtLink href="/contact">Контакты</NuxtLink></h3>
|
||||
<div class="footer-info">
|
||||
<div class="info-item">
|
||||
<span class="info-icon">📞</span>
|
||||
<a href="tel:+79625439343" class="info-link">8 (962) 543-93-43</a>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-icon">✉️</span>
|
||||
<a href="mailto:valitovgaziz@yandex.ru" class="info-link">valitovgaziz@yandex.ru</a>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-icon">🕒</span>
|
||||
<span class="info-value">Пн-Пт: 9:00-18:00</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-icon">📍</span>
|
||||
<span class="info-value">г. УФа</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Вакансии -->
|
||||
<div class="footer-section">
|
||||
<h3 class="footer-title"><NuxtLink to="/vacations">Карьера</NuxtLink></h3>
|
||||
<div class="footer-links">
|
||||
<NuxtLink href="/vacations" class="footer-link">💼 Открытые вакансии</NuxtLink>
|
||||
<NuxtLink href="/partner" class="footer-link">👥 Стать партнером</NuxtLink>
|
||||
<NuxtLink href="/about" class="footer-link">🏢 О компании</NuxtLink>
|
||||
<NuxtLink href="/news" class="footer-link">📰 Новости</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Навигация -->
|
||||
<div class="footer-section">
|
||||
<h3 id="poleznoe" class="footer-title">Полезное</h3>
|
||||
<div class="footer-links">
|
||||
<NuxtLink to="/objects" class="footer-link">🔍 Все объекты</NuxtLink>
|
||||
<NuxtLink to="/objects/create" class="footer-link">➕ Добавить объект</NuxtLink>
|
||||
<NuxtLink href="/support" class="footer-link">❓ Помощь</NuxtLink>
|
||||
<NuxtLink href="/rules" class="footer-link">📋 Тарифные планы</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Нижняя часть футера -->
|
||||
<div class="footer-bottom">
|
||||
<div class="footer-bottom-content">
|
||||
<div class="copyright">
|
||||
© 2025 TravelEasy. Все права защищены.
|
||||
</div>
|
||||
<div class="footer-bottom-links">
|
||||
<nuxt-link href="/agreements/privacy" class="footer-bottom-link">Политика конфиденциальности</nuxt-link>
|
||||
<nuxt-link href="/agreements/userAgreement" class="footer-bottom-link">Пользовательское
|
||||
соглашение</nuxt-link>
|
||||
<a href="/sitemap.xml" class="footer-bottom-link">Карта сайта</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// Компонент футера
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.footer {
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
margin-top: auto;
|
||||
border-top: 1px solid var(--border-light);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
|
||||
gap: 3rem;
|
||||
padding: 3rem 0 2rem;
|
||||
}
|
||||
|
||||
.footer-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.footer-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.logo-icon {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
background: var(--primary-500);
|
||||
border-radius: var(--radius-lg);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
color: var(--text-inverse);
|
||||
font-weight: bold;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.logo-name {
|
||||
font-weight: bold;
|
||||
font-size: 1.5rem;
|
||||
color: var(--text-primary);
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.footer-description {
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.6;
|
||||
font-size: 0.9rem;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.social-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.social-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-light);
|
||||
border-radius: 50%;
|
||||
transition: all 0.3s ease;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.social-link:hover {
|
||||
background: var(--primary-500);
|
||||
border-color: var(--primary-500);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.social-icon {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.footer-title {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--text-primary);
|
||||
transition: text-shadow 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
.footer-title:hover {
|
||||
text-shadow: 0 0 5px #fff, /* Белая тень (основное свечение) */
|
||||
0 0 10px #fff, /* Более широкая белая тень */
|
||||
0 0 15px #00ffff, /* Голубой оттенок свечения */
|
||||
0 0 20px #00ffff; /* Ещё шире голубой оттенок */
|
||||
}
|
||||
|
||||
#poleznoe:hover {
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.footer-title-margin {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.footer-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
color: var(--text-secondary);
|
||||
min-width: 80px;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.4;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.info-icon {
|
||||
font-size: 1rem;
|
||||
min-width: 1.5rem;
|
||||
color: var(--text-tertiary);
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.info-link {
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.info-link:hover {
|
||||
color: var(--primary-500);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.footer-link {
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 0.9rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.footer-link:hover {
|
||||
color: var(--primary-500);
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
.footer-bottom {
|
||||
border-top: 1px solid var(--border-light);
|
||||
padding: 1.5rem 0;
|
||||
transition: border-color 0.3s ease;
|
||||
}
|
||||
|
||||
.footer-bottom-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.copyright {
|
||||
color: var(--text-tertiary);
|
||||
font-size: 0.875rem;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.footer-bottom-links {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.footer-bottom-link {
|
||||
color: var(--text-tertiary);
|
||||
text-decoration: none;
|
||||
font-size: 0.875rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.footer-bottom-link:hover {
|
||||
color: var(--primary-500);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Адаптивность */
|
||||
@media (max-width: 1024px) {
|
||||
.footer-content {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.footer-content {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 2rem;
|
||||
padding: 2rem 0 1.5rem;
|
||||
}
|
||||
|
||||
.footer-bottom-content {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer-bottom-links {
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.footer-logo {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.footer-section {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.social-links {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.footer-content {
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.footer-title {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.logo-name {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.footer-description {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,501 @@
|
||||
<template>
|
||||
<div class="hamburger-menu">
|
||||
<!-- Кнопка гамбургера -->
|
||||
<button
|
||||
class="hamburger-button"
|
||||
:class="{ 'active': isOpen }"
|
||||
aria-label="Меню"
|
||||
@click="toggleMenu">
|
||||
<span class="hamburger-line"/>
|
||||
<span class="hamburger-line"/>
|
||||
<span class="hamburger-line"/>
|
||||
</button>
|
||||
|
||||
<!-- Затемнение фона -->
|
||||
<div v-if="isOpen" class="menu-overlay" @click="closeMenu"/>
|
||||
|
||||
<!-- Само меню -->
|
||||
<transition name="slide-left">
|
||||
<div v-if="isOpen" class="menu-content">
|
||||
<!-- Заголовок меню -->
|
||||
<div class="menu-header">
|
||||
<div v-if="false" class="user-info"> <!-- Можно добавить позже -->
|
||||
<div class="user-avatar">👤</div>
|
||||
<div class="user-details">
|
||||
<div class="user-name">Гость</div>
|
||||
<div class="user-status">Не авторизован</div>
|
||||
</div>
|
||||
</div>
|
||||
<h3 class="menu-title">Меню</h3>
|
||||
<button class="close-button" aria-label="Закрыть" @click="closeMenu">
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Настройки тем -->
|
||||
<div class="menu-section">
|
||||
<h4 class="menu-section-title">Настройки</h4>
|
||||
|
||||
<!-- Переключатель темы -->
|
||||
<div class="menu-item toggle-item">
|
||||
<div class="toggle-label">
|
||||
<span class="toggle-icon">🌙</span>
|
||||
<span class="toggle-text">Темная тема</span>
|
||||
</div>
|
||||
<button
|
||||
aria-label="Переключить тему"
|
||||
class="theme-toggle"
|
||||
:class="{ 'active': theme === 'dark' }"
|
||||
@click="toggleTheme">
|
||||
<span class="toggle-slider"/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Переключатель шрифтов -->
|
||||
<div class="menu-item toggle-item">
|
||||
<div class="toggle-label">
|
||||
<span class="toggle-icon">🔤</span>
|
||||
<span class="toggle-text">Элегантный шрифт</span>
|
||||
</div>
|
||||
<button
|
||||
class="font-toggle"
|
||||
:class="{ 'active': fontSet === 'elegant' }"
|
||||
aria-label="Переключить шрифт"
|
||||
@click="toggleFontSet">
|
||||
<span class="toggle-slider"/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Навигация -->
|
||||
<div class="menu-section">
|
||||
<h4 class="menu-section-title">Аккаунт</h4>
|
||||
|
||||
<NuxtLink to="/profile" class="menu-item" @click="closeMenu">
|
||||
<span class="menu-icon">👤</span>
|
||||
<span class="menu-text">Профиль</span>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink to="/auth/register" class="menu-item" @click="closeMenu">
|
||||
<span class="menu-icon">📝</span>
|
||||
<span class="menu-text">Регистрация</span>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink to="/auth/login" class="menu-item" @click="closeMenu">
|
||||
<span class="menu-icon">🔑</span>
|
||||
<span class="menu-text">Вход</span>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<!-- Дополнительные ссылки -->
|
||||
<div class="menu-section">
|
||||
<h4 class="menu-section-title">Навигация</h4>
|
||||
|
||||
<NuxtLink to="/" class="menu-item" @click="closeMenu">
|
||||
<span class="menu-icon">🏠</span>
|
||||
<span class="menu-text">Главная</span>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink to="/objects" class="menu-item" @click="closeMenu">
|
||||
<span class="menu-icon">🔍</span>
|
||||
<span class="menu-text">Все объекты</span>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink to="/objects/create" class="menu-item" @click="closeMenu">
|
||||
<span class="menu-icon">➕</span>
|
||||
<span class="menu-text">Добавить объект</span>
|
||||
</NuxtLink>
|
||||
|
||||
<!-- В секции "Навигация" добавьте: -->
|
||||
<NuxtLink to="/about" class="menu-item" @click="closeMenu">
|
||||
<span class="menu-icon">ℹ️</span>
|
||||
<span class="menu-text">О компании</span>
|
||||
</NuxtLink>
|
||||
|
||||
<a href="/support" class="menu-item" @click="closeMenu">
|
||||
<span class="menu-icon">❓</span>
|
||||
<span class="menu-text">Помощь</span>
|
||||
</a>
|
||||
|
||||
<a href="/contact" class="menu-item" @click="closeMenu">
|
||||
<span class="menu-icon">📞</span>
|
||||
<span class="menu-text">Контакты</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Футер меню -->
|
||||
<div class="menu-footer">
|
||||
<div class="menu-version">v1.0.0</div>
|
||||
<div class="menu-copyright">© 2024 EasySite</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const isOpen = ref(false)
|
||||
|
||||
const { theme, toggleTheme, fontSet, toggleFontSet } = useTheme()
|
||||
|
||||
const toggleMenu = () => {
|
||||
isOpen.value = !isOpen.value
|
||||
}
|
||||
|
||||
const closeMenu = () => {
|
||||
isOpen.value = false
|
||||
}
|
||||
|
||||
// Закрытие меню при нажатии ESC
|
||||
const handleEscape = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape' && isOpen.value) {
|
||||
closeMenu()
|
||||
}
|
||||
}
|
||||
|
||||
// Закрытие меню при изменении размера окна на десктоп
|
||||
const handleResize = () => {
|
||||
if (window.innerWidth >= 1024 && isOpen.value) {
|
||||
closeMenu()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('keydown', handleEscape)
|
||||
window.addEventListener('resize', handleResize)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('keydown', handleEscape)
|
||||
window.removeEventListener('resize', handleResize)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hamburger-menu {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Кнопка гамбургера */
|
||||
.hamburger-button {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
width: 2rem;
|
||||
height: 1.5rem;
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.hamburger-button:hover {
|
||||
opacity: 0.8;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.hamburger-line {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: var(--text-primary);
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
.hamburger-button.active .hamburger-line:nth-child(1) {
|
||||
transform: rotate(45deg) translate(6px, 6px);
|
||||
}
|
||||
|
||||
.hamburger-button.active .hamburger-line:nth-child(2) {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.hamburger-button.active .hamburger-line:nth-child(3) {
|
||||
transform: rotate(-45deg) translate(6px, -6px);
|
||||
}
|
||||
|
||||
/* Затемнение фона */
|
||||
.menu-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 998;
|
||||
}
|
||||
|
||||
/* Контент меню */
|
||||
.menu-content {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 380px;
|
||||
max-width: 90vw;
|
||||
background: var(--bg-primary);
|
||||
box-shadow: -4px 0 16px rgba(0, 0, 0, 0.1);
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
border-left: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
/* Анимация появления */
|
||||
.slide-left-enter-active,
|
||||
.slide-left-leave-active {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.slide-left-enter-from,
|
||||
.slide-left-leave-to {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
/* Заголовок меню */
|
||||
.menu-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1.5rem;
|
||||
border-bottom: 1px solid var(--border-light);
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
background: var(--primary-500);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.user-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.user-status {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.menu-title {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
color: var(--text-secondary);
|
||||
padding: 0.25rem;
|
||||
border-radius: var(--radius-sm);
|
||||
transition: all 0.2s ease;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.close-button:hover {
|
||||
color: var(--text-primary);
|
||||
background: var(--bg-tertiary);
|
||||
}
|
||||
|
||||
/* Секции меню */
|
||||
.menu-section {
|
||||
padding: 1.5rem;
|
||||
border-bottom: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.menu-section:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.menu-section-title {
|
||||
font-family: var(--font-primary);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* Элементы меню */
|
||||
.menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem 0;
|
||||
text-decoration: none;
|
||||
color: var(--text-primary);
|
||||
transition: all 0.2s ease;
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
background: var(--bg-secondary);
|
||||
color: var(--primary-600);
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
font-size: 1.25rem;
|
||||
width: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.menu-text {
|
||||
font-family: var(--font-primary);
|
||||
font-weight: 500;
|
||||
font-size: 0.95rem;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Переключатели */
|
||||
.toggle-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.75rem 0;
|
||||
}
|
||||
|
||||
.toggle-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.toggle-icon {
|
||||
font-size: 1.25rem;
|
||||
width: 1.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.toggle-text {
|
||||
font-family: var(--font-primary);
|
||||
font-weight: 500;
|
||||
font-size: 0.95rem;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* Стили для тогглов */
|
||||
.theme-toggle,
|
||||
.font-toggle {
|
||||
position: relative;
|
||||
width: 3rem;
|
||||
height: 1.5rem;
|
||||
background: var(--gray-300);
|
||||
border: none;
|
||||
border-radius: 0.75rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
padding: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.theme-toggle:hover,
|
||||
.font-toggle:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.theme-toggle.active,
|
||||
.font-toggle.active {
|
||||
background: var(--primary-500);
|
||||
}
|
||||
|
||||
.toggle-slider {
|
||||
position: absolute;
|
||||
top: 0.125rem;
|
||||
left: 0.125rem;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.theme-toggle.active .toggle-slider,
|
||||
.font-toggle.active .toggle-slider {
|
||||
transform: translateX(1.5rem);
|
||||
}
|
||||
|
||||
/* Футер меню */
|
||||
.menu-footer {
|
||||
margin-top: auto;
|
||||
padding: 1.5rem;
|
||||
border-top: 1px solid var(--border-light);
|
||||
text-align: center;
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.menu-version {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-tertiary);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.menu-copyright {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
/* Адаптивность для десктопа */
|
||||
@media (min-width: 1024px) {
|
||||
.menu-content {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.hamburger-button {
|
||||
width: 2.25rem;
|
||||
height: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.menu-content {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.menu-section {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.menu-header {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.menu-footer {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,127 @@
|
||||
<template>
|
||||
<header class="header">
|
||||
<div class="container">
|
||||
<div class="header-content">
|
||||
<!-- Логотип -->
|
||||
<NuxtLink to="/about" class="logo">
|
||||
<div class="logo-icon">
|
||||
<span class="logo-text">ES</span>
|
||||
</div>
|
||||
<span class="logo-name">EasySite102</span>
|
||||
</NuxtLink>
|
||||
<div class="header-paln-menu">
|
||||
<div class="header-plan">
|
||||
<!-- Трифы -->
|
||||
<Plan />
|
||||
</div>
|
||||
<!-- Гамбургер меню для всех устройств -->
|
||||
<div class="hamburger-nav">
|
||||
<HamburgerMenu />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Plan from '~/components/layout/PlanBadge.vue'
|
||||
import HamburgerMenu from '~/components/layout/HamburgerMenu.vue'
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.header {
|
||||
background: var(--bg-primary);
|
||||
box-shadow: var(--shadow-sm);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 50;
|
||||
border-bottom: 1px solid var(--border-light);
|
||||
width: 100%;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
text-decoration: none;
|
||||
color: var(--text-primary);
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.logo:hover {
|
||||
color: var(--primary-500);
|
||||
}
|
||||
|
||||
.logo-icon {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
background: var(--primary-500);
|
||||
border-radius: var(--radius-lg);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
color: var(--text-inverse);
|
||||
font-weight: bold;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.logo-name {
|
||||
font-weight: bold;
|
||||
font-size: 1.5rem;
|
||||
white-space: nowrap;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.header-paln-menu {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.header-plan {
|
||||
margin: 0 1rem 0 0;
|
||||
}
|
||||
|
||||
.hamburger-nav {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.header-content {
|
||||
padding: 0.75rem 0;
|
||||
}
|
||||
|
||||
.logo-name {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.logo-icon {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.logo-name {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,119 @@
|
||||
<!-- components/layout/ObjectsNavigation.vue -->
|
||||
<template>
|
||||
<nav class="objects-navigation">
|
||||
<div class="nav-container">
|
||||
<!-- Основные ссылки -->
|
||||
<div class="nav-links">
|
||||
<NuxtLink to="/objects" class="nav-link" :class="{ active: $route.path === '/objects' }">
|
||||
🏢 Все объекты
|
||||
</NuxtLink>
|
||||
<NuxtLink
|
||||
to="/objects/my-objects"
|
||||
class="nav-link"
|
||||
:class="{ active: $route.path === '/objects/my-objects' }">
|
||||
📝 Мои объекты
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/objects/create" class="nav-link" :class="{ active: $route.path === '/objects/create' }">
|
||||
➕ Добавить объект
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<!-- Дополнительные действия -->
|
||||
<div class="nav-actions">
|
||||
<NuxtLink to="/" class="nav-action">
|
||||
🏠 На главную
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.objects-navigation {
|
||||
background: var(--bg-primary);
|
||||
border-bottom: 1px solid var(--border-light);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.nav-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
padding: 0.75rem 1.25rem;
|
||||
text-decoration: none;
|
||||
border-radius: var(--radius-lg);
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
border: 1px solid transparent;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
background: var(--bg-secondary);
|
||||
color: var(--primary-600);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
background: var(--primary-500);
|
||||
color: var(--text-inverse);
|
||||
border-color: var(--primary-500);
|
||||
}
|
||||
|
||||
.nav-actions {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.nav-action {
|
||||
padding: 0.5rem 1rem;
|
||||
text-decoration: none;
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-secondary);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-action:hover {
|
||||
color: var(--primary-500);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Адаптивность */
|
||||
@media (max-width: 768px) {
|
||||
.nav-container {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
padding: 0.75rem 0;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.nav-actions {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,124 @@
|
||||
<!-- components/PlanBadge.vue -->
|
||||
<template>
|
||||
<div class="plan-badge-wrapper relative inline-flex">
|
||||
<NuxtLink
|
||||
:to="'/rules'"
|
||||
class="plan-badge group"
|
||||
:class="badgeClass"
|
||||
@click.prevent="navigateToRules"
|
||||
>
|
||||
<span class="text-sm font-semibold">{{ planLabel }}</span>
|
||||
|
||||
<!-- CSS Tooltip -->
|
||||
<div class="tooltip">
|
||||
{{ tooltipText }}
|
||||
</div>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
plan: {
|
||||
type: String,
|
||||
default: 'start',
|
||||
validator: (value) => ['start', 'pro', 'vip'].includes(value)
|
||||
},
|
||||
isCurrentPlan: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const planLabels = {
|
||||
start: 'Старт',
|
||||
pro: 'Профи',
|
||||
vip: 'VIP'
|
||||
}
|
||||
|
||||
const planDescriptions = {
|
||||
start: 'Базовый план для начала работы',
|
||||
pro: 'Расширенные возможности для профессионалов',
|
||||
vip: 'Премиум решение для максимального роста'
|
||||
}
|
||||
|
||||
const planColors = {
|
||||
start: 'bg-green-100 text-green-800 border-green-200 hover:bg-green-200',
|
||||
pro: 'bg-blue-100 text-blue-800 border-blue-200 hover:bg-blue-200',
|
||||
vip: 'bg-purple-100 text-purple-800 border-purple-200 hover:bg-purple-200'
|
||||
}
|
||||
|
||||
const planLabel = computed(() => planLabels[props.plan])
|
||||
const badgeClass = computed(() => `inline-flex items-center px-3 py-1 rounded-full border cursor-pointer transition-colors duration-200 ${planColors[props.plan]}`)
|
||||
|
||||
// Текст подсказки
|
||||
const tooltipText = computed(() => {
|
||||
if (props.isCurrentPlan) {
|
||||
return `Это ваш тарифный план: ${planDescriptions[props.plan]}`
|
||||
}
|
||||
return planDescriptions[props.plan]
|
||||
})
|
||||
|
||||
// Навигация на страницу правил
|
||||
const navigateToRules = () => {
|
||||
navigateTo('/rules')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.plan-badge-wrapper {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.plan-badge {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Стили для CSS tooltip (всплывает вниз) */
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(8px);
|
||||
background: #1f2937; /* gray-800 */
|
||||
color: white;
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 0.75rem;
|
||||
white-space: nowrap;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: all 0.2s ease-in-out;
|
||||
pointer-events: none;
|
||||
z-index: 50;
|
||||
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
min-width: 180px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Стрелка для tooltip (теперь сверху) */
|
||||
.tooltip::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
border: 4px solid transparent;
|
||||
border-bottom-color: #1f2937; /* gray-800 */
|
||||
}
|
||||
|
||||
/* Показываем tooltip при наведении */
|
||||
.plan-badge:hover .tooltip {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateX(-50%) translateY(4px);
|
||||
}
|
||||
|
||||
/* Альтернативный вариант - подсказка появляется сразу без задержки */
|
||||
.plan-badge .tooltip {
|
||||
transition-delay: 0s;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,170 @@
|
||||
<!-- polacyPrivacy.vue -->
|
||||
<template>
|
||||
<div class="privacy-page">
|
||||
<div class="container">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">Политика конфиденциальности</h1>
|
||||
<div class="page-actions">
|
||||
<button class="btn btn-primary" @click="downloadDocument">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
|
||||
<path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
|
||||
</svg>
|
||||
Скачать PDF
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="document-container">
|
||||
<div class="document-content">
|
||||
<h2>ПОЛИТИКА КОНФИДЕНЦИАЛЬНОСТИ</h2>
|
||||
|
||||
<h3>Общие положения</h3>
|
||||
<p>Настоящая Политика конфиденциальности (далее – «Политика») определяет порядок обработки и защиты Обществом с ограниченной ответственностью «Информационно консультационный центр ЯЛ АРБА» (далее – «Общество», «Мы») информации о физических лицах (далее – «Пользователи», «Вы»), которая может быть получена Обществом при использовании Пользователями сайтов, мобильных приложений и иных сервисов, входящих в экосистему «ЯЛ АРБА» (далее – «Сервисы»).</p>
|
||||
<p>Используя наши Сервисы, Вы выражаете свое безоговорочное согласие с условиями данной Политики. Если Вы не согласны с этими условиями, не используйте наши Сервисы.</p>
|
||||
|
||||
<h3>1. Оператор персональных данных</h3>
|
||||
<p>Оператором ваших персональных данных является:</p>
|
||||
<p>ОБЩЕСТВО С ОГРАНИЧЕННОЙ ОТВЕТСТВЕННОСТЬЮ "ИНФОРМАЦИОННО КОНСУЛЬТАЦИОННЫЙ ЦЕНТР ЯЛ АРБА"</p>
|
||||
<p>ИНН: 0234009584</p>
|
||||
<p>ОГРН: 1220200038112</p>
|
||||
<p>Контактный адрес электронной почты: valitovgaziz@yandex.ru</p>
|
||||
|
||||
<h3>2. Состав и цели обработки персональных данных</h3>
|
||||
<p>Мы можем обрабатывать следующие категории ваших данных:</p>
|
||||
<ul>
|
||||
<li>Данные, предоставляемые при регистрации и использовании Сервисов: имя, фамилия, адрес электронной почты, номер телефона, данные аккаунта в социальных сетях (если используется для входа).</li>
|
||||
<li>Данные, создаваемые при использовании Сервисов: история поисковых запросов, бронирований, покупок, предпочтения, отзывы и рейтинги, данные о взаимодействии с контентом предпринимателей.</li>
|
||||
<li>Технические данные: IP-адрес, данные cookie-файлов, информация о браузере и устройстве, журналы доступа.</li>
|
||||
</ul>
|
||||
<p>Цели обработки:</p>
|
||||
<ul>
|
||||
<li>Предоставление доступа к функционалу Сервисов (для предпринимателей – размещение услуг, для туристов – поиск и бронирование).</li>
|
||||
<li>Связь с Пользователем для информирования о заказах, услугах и изменениях в Сервисах.</li>
|
||||
<li>Аналитика и улучшение работы Сервисов, разработка новых функций.</li>
|
||||
<li>Обеспечение безопасности и предотвращение мошенничества.</li>
|
||||
<li>Выполнение требований применимого законодательства.</li>
|
||||
</ul>
|
||||
|
||||
<h3>3. Условия обработки персональных данных</h3>
|
||||
<p>Обработка ваших персональных данных осуществляется с вашего согласия, выражаемого путем совершения конклюдентных действий (регистрация в Сервисе, отметка о согласии в чекбоксе, использование функционала). Мы принимаем все необходимые меры для защиты ваших данных от несанкционированного доступа, изменения, раскрытия или уничтожения.</p>
|
||||
|
||||
<h3>4. Передача персональных данных</h3>
|
||||
<p>Мы не передаем ваши персональные данные третьим лицам, за исключением случаев:</p>
|
||||
<ul>
|
||||
<li>Когда это необходимо для оказания услуги (например, передача данных туриста предпринимателю для выполнения бронирования).</li>
|
||||
<li>По требованию уполномоченных государственных органов в соответствии с законодательством РФ.</li>
|
||||
<li>При реорганизации Общества (например, слиянии) с уведомлением пользователей.</li>
|
||||
</ul>
|
||||
|
||||
<h3>5. Права субъекта персональных данных</h3>
|
||||
<p>Вы имеете право:</p>
|
||||
<ul>
|
||||
<li>На доступ к своим персональным данным, их уточнение и блокирование.</li>
|
||||
<li>На отзыв согласия на обработку персональных данных.</li>
|
||||
<li>На удаление своих персональных данных.</li>
|
||||
<li>На обжалование действий или бездействия Оператора в уполномоченный орган.</li>
|
||||
</ul>
|
||||
<p>Для реализации этих прав направьте запрос по контактному адресу электронной почты, указанному в разделе 1.</p>
|
||||
|
||||
<h3>6. Заключительные положения</h3>
|
||||
<p>Мы вправе вносить изменения в настоящую Политику. Новая редакция вступает в силу с момента ее размещения в соответствующем Сервисе, если иное не предусмотрено новой редакцией Политики.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'PolacyPrivacy',
|
||||
methods: {
|
||||
downloadDocument() {
|
||||
// В реальном приложении здесь будет логика скачивания документа
|
||||
alert('Функция скачивания PDF будет реализована в бэкенде');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.privacy-page {
|
||||
padding: var(--space-xl) 0;
|
||||
background: var(--bg-secondary);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: var(--space-xl);
|
||||
padding-bottom: var(--space-lg);
|
||||
border-bottom: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-family: var(--font-heading);
|
||||
font-size: var(--text-3xl);
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.page-actions {
|
||||
display: flex;
|
||||
gap: var(--space-md);
|
||||
}
|
||||
|
||||
.document-container {
|
||||
background: var(--bg-primary);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-sm);
|
||||
border: 1px solid var(--border-light);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.document-content {
|
||||
padding: var(--space-2xl);
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.document-content h2 {
|
||||
text-align: center;
|
||||
margin-bottom: var(--space-xl);
|
||||
color: var(--primary-600);
|
||||
}
|
||||
|
||||
.document-content h3 {
|
||||
margin-top: var(--space-xl);
|
||||
margin-bottom: var(--space-md);
|
||||
color: var(--primary-500);
|
||||
}
|
||||
|
||||
.document-content p {
|
||||
margin-bottom: var(--space-md);
|
||||
line-height: var(--leading-relaxed);
|
||||
}
|
||||
|
||||
.document-content ul {
|
||||
margin-bottom: var(--space-md);
|
||||
padding-left: var(--space-lg);
|
||||
}
|
||||
|
||||
.document-content li {
|
||||
margin-bottom: var(--space-xs);
|
||||
line-height: var(--leading-relaxed);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.page-header {
|
||||
flex-direction: column;
|
||||
gap: var(--space-md);
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.document-content {
|
||||
padding: var(--space-lg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,8 @@
|
||||
<!-- layouts/default.vue -->
|
||||
<template>
|
||||
<div>
|
||||
<UToast />
|
||||
<Notifications position="top-right" />
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,282 @@
|
||||
<!-- components/pricing/PricingSection.vue -->
|
||||
<template>
|
||||
<section class="pricing-section">
|
||||
<div class="container">
|
||||
<div class="pricing-header">
|
||||
<h1 class="text-hero text-center">Тарифы для вашего бизнеса</h1>
|
||||
<p class="lead text-center">Выберите оптимальное решение для продвижения ваших туристических услуг</p>
|
||||
</div>
|
||||
|
||||
<div class="pricing-grid">
|
||||
<!-- Тариф Старт -->
|
||||
<div class="pricing-card">
|
||||
<div class="pricing-card__header">
|
||||
<h3 class="h3">Старт</h3>
|
||||
<div class="pricing-card__price">
|
||||
990 ₽<span class="pricing-card__period">/месяц</span>
|
||||
</div>
|
||||
<p class="small">Идеально для начинающих</p>
|
||||
</div>
|
||||
|
||||
<div class="pricing-features">
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>1 объект/услуга</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Базовый шаблон сайта</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Система бронирования</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Базовая SEO-оптимизация</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Статистика просмотров</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Поддержка по email</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pricing-card__footer">
|
||||
<button class="btn btn-outline btn-block">Начать 14-дневный пробный период</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Тариф Профессионал (рекомендуемый) -->
|
||||
<div class="pricing-card featured">
|
||||
<div class="pricing-card__badge">Самое популярное</div>
|
||||
<div class="pricing-card__header">
|
||||
<h3 class="h3">Профессионал</h3>
|
||||
<div class="pricing-card__price">
|
||||
2 990 ₽<span class="pricing-card__period">/месяц</span>
|
||||
</div>
|
||||
<p class="small">Для растущего бизнеса</p>
|
||||
</div>
|
||||
|
||||
<div class="pricing-features">
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>До 5 объектов/услуг</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Расширенные шаблоны</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Приоритетная SEO-оптимизация</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Интеграция с календарями</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Email-рассылки для клиентов</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Подробная аналитика</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Приоритетная поддержка</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pricing-card__footer">
|
||||
<button class="btn btn-primary btn-block">Начать 14-дневный пробный период</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Тариф Бизнес -->
|
||||
<div class="pricing-card">
|
||||
<div class="pricing-card__header">
|
||||
<h3 class="h3">Бизнес</h3>
|
||||
<div class="pricing-card__price">
|
||||
7 990 ₽<span class="pricing-card__period">/месяц</span>
|
||||
</div>
|
||||
<p class="small">Для крупных компаний</p>
|
||||
</div>
|
||||
|
||||
<div class="pricing-features">
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Неограниченное количество объектов</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Кастомизация дизайна</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Продвинутое SEO + контекстная реклама</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Интеграция с CRM</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>API доступ</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Личный менеджер</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Поддержка 24/7</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pricing-card__footer">
|
||||
<button class="btn btn-outline btn-block">Начать 14-дневный пробный период</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Дополнительная информация -->
|
||||
<div class="pricing-info text-center">
|
||||
<p class="small">Все тарифы включают 14-дневный пробный период. Отмена в любой момент.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// Логика для переключения тарифов может быть добавлена здесь
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pricing-section {
|
||||
padding: var(--space-2xl) 0;
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.pricing-header {
|
||||
text-align: center;
|
||||
margin-bottom: var(--space-2xl);
|
||||
}
|
||||
|
||||
.pricing-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: var(--space-xl);
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.pricing-card {
|
||||
background: var(--bg-primary);
|
||||
border-radius: var(--radius-xl);
|
||||
padding: var(--space-2xl);
|
||||
border: 2px solid var(--border-light);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.pricing-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.pricing-card.featured {
|
||||
border-color: var(--primary-500);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.pricing-card.featured:hover {
|
||||
transform: scale(1.05) translateY(-4px);
|
||||
}
|
||||
|
||||
.pricing-card__badge {
|
||||
position: absolute;
|
||||
top: -12px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: var(--primary-500);
|
||||
color: var(--text-inverse);
|
||||
padding: var(--space-xs) var(--space-lg);
|
||||
border-radius: var(--radius-lg);
|
||||
font-size: var(--text-sm);
|
||||
font-weight: var(--font-semibold);
|
||||
}
|
||||
|
||||
.pricing-card__header {
|
||||
text-align: center;
|
||||
margin-bottom: var(--space-xl);
|
||||
}
|
||||
|
||||
.pricing-card__price {
|
||||
font-family: var(--font-heading);
|
||||
font-size: var(--text-4xl);
|
||||
font-weight: var(--font-bold);
|
||||
color: var(--text-primary);
|
||||
margin: var(--space-md) 0;
|
||||
}
|
||||
|
||||
.pricing-card__period {
|
||||
color: var(--text-tertiary);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
|
||||
.pricing-features {
|
||||
flex-grow: 1;
|
||||
margin-bottom: var(--space-xl);
|
||||
}
|
||||
|
||||
.pricing-feature {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-sm);
|
||||
margin-bottom: var(--space-sm);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.pricing-feature__icon {
|
||||
color: var(--success-500);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.pricing-card__footer {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.btn-block {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pricing-info {
|
||||
margin-top: var(--space-xl);
|
||||
padding-top: var(--space-lg);
|
||||
border-top: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
/* Адаптивность */
|
||||
@media (max-width: 768px) {
|
||||
.pricing-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-lg);
|
||||
}
|
||||
|
||||
.pricing-card.featured {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.pricing-card.featured:hover {
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,174 @@
|
||||
<!-- userAgreement.vue -->
|
||||
<template>
|
||||
<div class="agreement-page">
|
||||
<div class="container">
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">Пользовательское соглашение</h1>
|
||||
<div class="page-actions">
|
||||
<button class="btn btn-primary" @click="downloadDocument">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
|
||||
<path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
|
||||
</svg>
|
||||
Скачать PDF
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="document-container">
|
||||
<div class="document-content">
|
||||
<h2>ПОЛЬЗОВАТЕЛЬСКОЕ СОГЛАШЕНИЕ</h2>
|
||||
|
||||
<h3>Общие положения</h3>
|
||||
<p>Настоящее Пользовательское соглашение (далее – «Соглашение») регулирует отношения между Обществом с ограниченной ответственностью «Информационно консультационный центр ЯЛ АРБА» (далее – «Администрация», «Мы») и любым физическим лицом (далее – «Пользователь», «Вы») в связи с использованием сайтов, мобильных приложений и иных сервисов, входящих в экосистему «ЯЛ АРБА» (далее – «Сервисы»).</p>
|
||||
<p>Используя любой из Сервисов, Вы в полном объеме принимаете условия настоящего Соглашения. Если Вы не согласны с каким-либо условием, Вы не вправе использовать наши Сервисы.</p>
|
||||
|
||||
<h3>1. Термины</h3>
|
||||
<ul>
|
||||
<li><strong>Экосистема «ЯЛ АРБА»</strong> – совокупность взаимосвязанных цифровых платформ (сайтов, приложений), предназначенных для взаимодействия предпринимателей и туристов.</li>
|
||||
<li><strong>Предприниматель</strong> – Пользователь, размещающий в Сервисах информацию о товарах, услугах и мероприятиях для туристов.</li>
|
||||
<li><strong>Турист</strong> – Пользователь, использующий Сервисы для поиска, планирования и бронирования туристических услуг.</li>
|
||||
<li><strong>Контент</strong> – любая информация, размещаемая Пользователями в Сервисах: тексты, фото, видео, описания услуг, отзывы и т.д.</li>
|
||||
</ul>
|
||||
|
||||
<h3>2. Общие условия использования</h3>
|
||||
<p><strong>2.1.</strong> Администрация предоставляет Вам неисключительную, непередаваемую лицензию на использование функционала Сервисов для их целевого назначения.</p>
|
||||
<p><strong>2.2.</strong> Вы обязуетесь использовать Сервисы добросовестно и исключительно в законных целях.</p>
|
||||
<p><strong>2.3.</strong> Для доступа к полному функционалу Сервисов необходима регистрация. Вы несете ответственность за сохранность своих учетных данных.</p>
|
||||
|
||||
<h3>3. Функционал для Предпринимателей</h3>
|
||||
<p><strong>3.1.</strong> Размещая Контент в Сервисах, Предприниматель гарантирует, что обладает всеми необходимыми правами на него, и что он является достоверным и не нарушает законодательство РФ и права третьих лиц.</p>
|
||||
<p><strong>3.2.</strong> Предприниматель самостоятельно несет ответственность перед Туристом за качество и предоставление размещенных услуг. Администрация не является стороной в договоре между Предпринимателем и Туристом и не несет ответственности за их взаимодействие.</p>
|
||||
<p><strong>3.3.</strong> Администрация оставляет за собой право модерировать и удалять Контент Предпринимателя в случае его несоответствия условиям Соглашения.</p>
|
||||
|
||||
<h3>4. Функционал для Туристов</h3>
|
||||
<p><strong>4.1.</strong> Бронируя услуги через Сервисы, Турист вступает в прямые договорные отношения с Предпринимателем. Все претензии по качеству услуги направляются непосредственно Предпринимателю.</p>
|
||||
<p><strong>4.2.</strong> Размещая отзывы и рейтинги, Турист обязуется быть объективным и корректым. Запрещены оскорбления, клевета и спам.</p>
|
||||
|
||||
<h3>5. Интеллектуальная собственность</h3>
|
||||
<p><strong>5.1.</strong> Все объекты в составе Сервисов (дизайн, текст, графика, логотипы, программный код) являются интеллектуальной собственностью Администрации или правообладателей.</p>
|
||||
<p><strong>5.2.</strong> Размещая Контент, Вы предоставляете Администрации право на его использование в целях функционирования Сервисов (воспроизведение, публичный показ, доведение до всеобщего сведения).</p>
|
||||
|
||||
<h3>6. Ответственность и ограничения</h3>
|
||||
<p><strong>6.1.</strong> Сервисы предоставляются на условиях «как есть». Администрация не гарантирует бесперебойную и безошибочную работу Сервисов.</p>
|
||||
<p><strong>6.2.</strong> Администрация не несет ответственности за убытки, прямые или косвенные, возникшие в связи с использованием или невозможностью использования Сервисов, включая упущенную выгоду.</p>
|
||||
<p><strong>6.3.</strong> Пользователь несет ответственность за любой Контент, который он размещает, и за любые последствия такого размещения.</p>
|
||||
|
||||
<h3>7. Конфиденциальность</h3>
|
||||
<p>Обработка персональных данных Пользователя осуществляется в соответствии с отдельной Политикой конфиденциальности, размещенной в Сервисах.</p>
|
||||
|
||||
<h3>8. Разрешение споров</h3>
|
||||
<p><strong>8.1.</strong> Все споры подлежат разрешению в порядке, предусмотренном законодательством Российской Федерации.</p>
|
||||
<p><strong>8.2.</strong> Обязательный досудебный (претензионный) порядок урегулирования споров является обязательным. Срок ответа на претензию – 30 (тридцать) календарных дней.</p>
|
||||
|
||||
<h3>9. Реквизиты Администрации</h3>
|
||||
<p>ОБЩЕСТВО С ОГРАНИЧЕННОЙ ОТВЕТСТВЕННОСТЬЮ "ИНФОРМАЦИОННО КОНСУЛЬТАЦИОННЫЙ ЦЕНТР ЯЛ АРБА"</p>
|
||||
<p>ИНН 0234009584</p>
|
||||
<p>ОГРН 1220200038112</p>
|
||||
<p>Расчётный счёт: 40702810106000055253</p>
|
||||
<p>Банк: ПАО Сбербанк</p>
|
||||
<p>БИК: 048073601</p>
|
||||
<p>Корр. счёт: 30101810300000000601</p>
|
||||
<p>Контактный e-mail: valitovgaziz@yandex.ru</p>
|
||||
|
||||
<h3>10. Заключительные положения</h3>
|
||||
<p>Администрация вправе в одностороннем порядке изменять настоящее Соглашение. Изменения вступают в силу с момента их публикации в Сервисе, если иной срок не указан в соответствующей публикации.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'UserAgreement',
|
||||
methods: {
|
||||
downloadDocument() {
|
||||
// В реальном приложении здесь будет логика скачивания документа
|
||||
alert('Функция скачивания PDF будет реализована в бэкенде');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.agreement-page {
|
||||
padding: var(--space-xl) 0;
|
||||
background: var(--bg-secondary);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: var(--space-xl);
|
||||
padding-bottom: var(--space-lg);
|
||||
border-bottom: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-family: var(--font-heading);
|
||||
font-size: var(--text-3xl);
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.page-actions {
|
||||
display: flex;
|
||||
gap: var(--space-md);
|
||||
}
|
||||
|
||||
.document-container {
|
||||
background: var(--bg-primary);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-sm);
|
||||
border: 1px solid var(--border-light);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.document-content {
|
||||
padding: var(--space-2xl);
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.document-content h2 {
|
||||
text-align: center;
|
||||
margin-bottom: var(--space-xl);
|
||||
color: var(--primary-600);
|
||||
}
|
||||
|
||||
.document-content h3 {
|
||||
margin-top: var(--space-xl);
|
||||
margin-bottom: var(--space-md);
|
||||
color: var(--primary-500);
|
||||
}
|
||||
|
||||
.document-content p {
|
||||
margin-bottom: var(--space-md);
|
||||
line-height: var(--leading-relaxed);
|
||||
}
|
||||
|
||||
.document-content ul {
|
||||
margin-bottom: var(--space-md);
|
||||
padding-left: var(--space-lg);
|
||||
}
|
||||
|
||||
.document-content li {
|
||||
margin-bottom: var(--space-xs);
|
||||
line-height: var(--leading-relaxed);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.page-header {
|
||||
flex-direction: column;
|
||||
gap: var(--space-md);
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.document-content {
|
||||
padding: var(--space-lg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div class="card cursor-pointer hover:shadow-lg transition-shadow" @click="$emit('click')">
|
||||
<div class="relative">
|
||||
<img
|
||||
:src="object.images[0] || '/images/placeholder.jpg'"
|
||||
:alt="object.title"
|
||||
class="w-full h-48 object-cover"
|
||||
>
|
||||
<div class="absolute top-3 right-3">
|
||||
<span class="badge badge-primary">
|
||||
{{ getTypeLabel(object.type) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<h3 class="font-semibold text-lg mb-2 line-clamp-2">{{ object.title }}</h3>
|
||||
<p class="text-gray-600 mb-3 flex items-center gap-1">
|
||||
📍 {{ object.city }}, {{ object.country }}
|
||||
</p>
|
||||
|
||||
<p class="text-sm text-gray-600 mb-4 line-clamp-2">
|
||||
{{ object.description }}
|
||||
</p>
|
||||
|
||||
<div class="flex flex-wrap gap-1 mb-4">
|
||||
<span
|
||||
v-for="amenity in object.amenities.slice(0, 3)"
|
||||
:key="amenity"
|
||||
class="badge badge-secondary text-xs"
|
||||
>
|
||||
{{ getAmenityLabel(amenity) }}
|
||||
</span>
|
||||
<span
|
||||
v-if="object.amenities.length > 3"
|
||||
class="badge badge-outline text-xs"
|
||||
>
|
||||
+{{ object.amenities.length - 3 }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-lg font-semibold text-primary-600">
|
||||
{{ formatPrice(object.price) }}
|
||||
<span class="text-sm text-gray-500">{{ getPriceUnitLabel(object.priceUnit) }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="object.rating" class="flex items-center gap-1">
|
||||
<span class="text-yellow-500">⭐</span>
|
||||
<span class="font-medium">{{ object.rating }}</span>
|
||||
<span class="text-gray-500 text-sm">({{ object.reviewCount }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ObjectItem } from '~/composables/useObjects'
|
||||
|
||||
interface Props {
|
||||
object: ObjectItem
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
defineEmits<{
|
||||
click: []
|
||||
}>()
|
||||
|
||||
const getTypeLabel = (type: string) => {
|
||||
const types: Record<string, string> = {
|
||||
hotel: 'Отель',
|
||||
apartment: 'Апартаменты',
|
||||
villa: 'Вилла',
|
||||
camping: 'Кемпинг',
|
||||
restaurant: 'Ресторан',
|
||||
attraction: 'Достопримечательность'
|
||||
}
|
||||
return types[type] || type
|
||||
}
|
||||
|
||||
const getAmenityLabel = (amenity: string) => {
|
||||
const amenities: Record<string, string> = {
|
||||
wifi: 'Wi-Fi',
|
||||
parking: 'Парковка',
|
||||
breakfast: 'Завтрак',
|
||||
pool: 'Бассейн',
|
||||
spa: 'СПА',
|
||||
kitchen: 'Кухня',
|
||||
washing_machine: 'Стиральная машина'
|
||||
}
|
||||
return amenities[amenity] || amenity
|
||||
}
|
||||
|
||||
const getPriceUnitLabel = (unit: string) => {
|
||||
const units: Record<string, string> = {
|
||||
per_night: '/ночь',
|
||||
per_person: '/чел',
|
||||
fixed: ''
|
||||
}
|
||||
return units[unit] || unit
|
||||
}
|
||||
|
||||
const formatPrice = (price: number) => {
|
||||
return new Intl.NumberFormat('ru-RU').format(price) + ' ₽'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,90 @@
|
||||
<!-- components/pricing/FAQSection.vue -->
|
||||
<template>
|
||||
<section class="faq-section">
|
||||
<div class="container">
|
||||
<div class="faq-header">
|
||||
<h2 class="h2 text-center">Часто задаваемые вопросы</h2>
|
||||
</div>
|
||||
|
||||
<div class="faq-grid">
|
||||
<div class="faq-item">
|
||||
<h4 class="h4">Можно ли изменить тариф позже?</h4>
|
||||
<p>Да, вы можете перейти на другой тариф в любой момент. Изменения вступят в силу со следующего платежного периода.</p>
|
||||
</div>
|
||||
|
||||
<div class="faq-item">
|
||||
<h4 class="h4">Что входит в пробный период?</h4>
|
||||
<p>14-дневный пробный период включает полный доступ ко всем функциям выбранного тарифа. Никаких платежей не требуется.</p>
|
||||
</div>
|
||||
|
||||
<div class="faq-item">
|
||||
<h4 class="h4">Есть ли ограничения по трафику?</h4>
|
||||
<p>Нет, все тарифы включают неограниченный трафик. Мы обеспечиваем стабильную работу ваших сайтов при любой нагрузке.</p>
|
||||
</div>
|
||||
|
||||
<div class="faq-item">
|
||||
<h4 class="h4">Предоставляете ли вы домены?</h4>
|
||||
<p>Да, мы можем помочь с регистрацией домена или подключением существующего. Домены оплачиваются отдельно.</p>
|
||||
</div>
|
||||
|
||||
<div class="faq-item">
|
||||
<h4 class="h4">Как происходит перенос существующих сайтов?</h4>
|
||||
<p>Наша команда поможет бесплатно перенести ваши существующие сайты и данные на нашу платформу.</p>
|
||||
</div>
|
||||
|
||||
<div class="faq-item">
|
||||
<h4 class="h4">Есть ли скидки при годовой оплате?</h4>
|
||||
<p>Да, при годовой оплате мы предоставляем скидку 20% на любой тариф.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// Логика для аккордеона может быть добавлена здесь
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.faq-section {
|
||||
padding: var(--space-2xl) 0;
|
||||
background: var(--bg-primary);
|
||||
}
|
||||
|
||||
.faq-header {
|
||||
margin-bottom: var(--space-2xl);
|
||||
}
|
||||
|
||||
.faq-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
||||
gap: var(--space-xl);
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.faq-item {
|
||||
background: var(--bg-secondary);
|
||||
padding: var(--space-lg);
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
.faq-item h4 {
|
||||
color: var(--primary-600);
|
||||
margin-bottom: var(--space-sm);
|
||||
}
|
||||
|
||||
.faq-item p {
|
||||
color: var(--text-secondary);
|
||||
line-height: var(--leading-relaxed);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.faq-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-lg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,282 @@
|
||||
<!-- components/pricing/PricingSection.vue -->
|
||||
<template>
|
||||
<section class="pricing-section">
|
||||
<div class="container">
|
||||
<div class="pricing-header">
|
||||
<h1 class="text-hero text-center">Тарифы для вашего бизнеса</h1>
|
||||
<p class="lead text-center">Выберите оптимальное решение для продвижения ваших туристических услуг</p>
|
||||
</div>
|
||||
|
||||
<div class="pricing-grid">
|
||||
<!-- Тариф Старт -->
|
||||
<div class="pricing-card">
|
||||
<div class="pricing-card__header">
|
||||
<h3 class="h3">Старт</h3>
|
||||
<div class="pricing-card__price">
|
||||
990 ₽<span class="pricing-card__period">/месяц</span>
|
||||
</div>
|
||||
<p class="small">Идеально для начинающих</p>
|
||||
</div>
|
||||
|
||||
<div class="pricing-features">
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>1 объект/услуга</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Базовый шаблон сайта</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Система бронирования</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Базовая SEO-оптимизация</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Статистика просмотров</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Поддержка по email</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pricing-card__footer">
|
||||
<button class="btn btn-outline btn-block">Начать 14-дневный пробный период</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Тариф Профессионал (рекомендуемый) -->
|
||||
<div class="pricing-card featured">
|
||||
<div class="pricing-card__badge">Самое популярное</div>
|
||||
<div class="pricing-card__header">
|
||||
<h3 class="h3">Профессионал</h3>
|
||||
<div class="pricing-card__price">
|
||||
2 990 ₽<span class="pricing-card__period">/месяц</span>
|
||||
</div>
|
||||
<p class="small">Для растущего бизнеса</p>
|
||||
</div>
|
||||
|
||||
<div class="pricing-features">
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>До 5 объектов/услуг</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Расширенные шаблоны</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Приоритетная SEO-оптимизация</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Интеграция с календарями</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Email-рассылки для клиентов</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Подробная аналитика</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Приоритетная поддержка</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pricing-card__footer">
|
||||
<button class="btn btn-primary btn-block">Начать 14-дневный пробный период</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Тариф Бизнес -->
|
||||
<div class="pricing-card">
|
||||
<div class="pricing-card__header">
|
||||
<h3 class="h3">Бизнес</h3>
|
||||
<div class="pricing-card__price">
|
||||
7 990 ₽<span class="pricing-card__period">/месяц</span>
|
||||
</div>
|
||||
<p class="small">Для крупных компаний</p>
|
||||
</div>
|
||||
|
||||
<div class="pricing-features">
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Неограниченное количество объектов</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Кастомизация дизайна</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Продвинутое SEO + контекстная реклама</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Интеграция с CRM</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>API доступ</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Личный менеджер</span>
|
||||
</div>
|
||||
<div class="pricing-feature">
|
||||
<span class="pricing-feature__icon">✓</span>
|
||||
<span>Поддержка 24/7</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pricing-card__footer">
|
||||
<button class="btn btn-outline btn-block">Начать 14-дневный пробный период</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Дополнительная информация -->
|
||||
<div class="pricing-info text-center">
|
||||
<p class="small">Все тарифы включают 14-дневный пробный период. Отмена в любой момент.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// Логика для переключения тарифов может быть добавлена здесь
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pricing-section {
|
||||
padding: var(--space-2xl) 0;
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.pricing-header {
|
||||
text-align: center;
|
||||
margin-bottom: var(--space-2xl);
|
||||
}
|
||||
|
||||
.pricing-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: var(--space-xl);
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.pricing-card {
|
||||
background: var(--bg-primary);
|
||||
border-radius: var(--radius-xl);
|
||||
padding: var(--space-2xl);
|
||||
border: 2px solid var(--border-light);
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.pricing-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.pricing-card.featured {
|
||||
border-color: var(--primary-500);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.pricing-card.featured:hover {
|
||||
transform: scale(1.05) translateY(-4px);
|
||||
}
|
||||
|
||||
.pricing-card__badge {
|
||||
position: absolute;
|
||||
top: -12px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: var(--primary-500);
|
||||
color: var(--text-inverse);
|
||||
padding: var(--space-xs) var(--space-lg);
|
||||
border-radius: var(--radius-lg);
|
||||
font-size: var(--text-sm);
|
||||
font-weight: var(--font-semibold);
|
||||
}
|
||||
|
||||
.pricing-card__header {
|
||||
text-align: center;
|
||||
margin-bottom: var(--space-xl);
|
||||
}
|
||||
|
||||
.pricing-card__price {
|
||||
font-family: var(--font-heading);
|
||||
font-size: var(--text-4xl);
|
||||
font-weight: var(--font-bold);
|
||||
color: var(--text-primary);
|
||||
margin: var(--space-md) 0;
|
||||
}
|
||||
|
||||
.pricing-card__period {
|
||||
color: var(--text-tertiary);
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
|
||||
.pricing-features {
|
||||
flex-grow: 1;
|
||||
margin-bottom: var(--space-xl);
|
||||
}
|
||||
|
||||
.pricing-feature {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-sm);
|
||||
margin-bottom: var(--space-sm);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.pricing-feature__icon {
|
||||
color: var(--success-500);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.pricing-card__footer {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.btn-block {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pricing-info {
|
||||
margin-top: var(--space-xl);
|
||||
padding-top: var(--space-lg);
|
||||
border-top: 1px solid var(--border-light);
|
||||
}
|
||||
|
||||
/* Адаптивность */
|
||||
@media (max-width: 768px) {
|
||||
.pricing-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-lg);
|
||||
}
|
||||
|
||||
.pricing-card.featured {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.pricing-card.featured:hover {
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user