deleted: main_dc/yalarba/easySite/.env

modified:   main_dc/yalarba/easySite/easySite/app/components/layout/Footer.vue
	new file:   main_dc/yalarba/easySite/easySite/app/pages/contact/index.vue
	modified:   main_dc/yalarba/easySite/easySite/nuxt.config.ts
	new file:   main_dc/yalarba/easySite/easySite/server/api/__sitemap__/blog-posts.get.ts
	new file:   main_dc/yalarba/easySite/easySite/server/api/__sitemap__/urls.get.ts
add contact page and setd message from contact page
This commit is contained in:
2025-11-01 21:52:07 +05:00
parent f032836fb2
commit 69ef4a1918
6 changed files with 815 additions and 3 deletions
-2
View File
@@ -1,2 +0,0 @@
NUXT_PUBLIC_TELEGRAM_BOT_TOKEN=8470085635:AAEPZcsN3n-3FkMdr7DzxbiQ3q8mXZTGwug
NUXT_PUBLIC_TELEGRAM_CHAT_ID=559861569
@@ -53,7 +53,7 @@
<!-- Контакты -->
<div class="footer-section">
<h3 class="footer-title">Контакты</h3>
<h3 class="footer-title"><a href="/contact">Контакты</a></h3>
<div class="footer-info">
<div class="info-item">
<span class="info-icon">📞</span>
@@ -0,0 +1,744 @@
<template>
<div class="page-wrapper">
<!-- Хедер -->
<Header />
<!-- Основной контент -->
<main class="contacts-page">
<!-- Hero секция -->
<section class="contacts-hero">
<div class="container">
<div class="hero-content">
<h1 class="hero-title">Контакты</h1>
<p class="hero-subtitle">
Свяжитесь с нами любым удобным способом. Мы всегда рады помочь!
</p>
</div>
</div>
</section>
<!-- Основной контент контактов -->
<section class="contacts-content">
<div class="container">
<div class="contacts-grid">
<!-- Контактная информация -->
<div class="contact-info">
<div class="contact-card">
<div class="contact-header">
<h2 class="contact-title">Свяжитесь с нами</h2>
<p class="contact-description">
Мы готовы ответить на все ваши вопросы и помочь с выбором
</p>
</div>
<div class="contact-methods">
<!-- Телефон -->
<div class="contact-method">
<div class="method-icon">📞</div>
<div class="method-content">
<h3 class="method-title">Телефон</h3>
<a href="tel:+79625439343" class="method-value">8 (962) 543-93-43</a>
<p class="method-description">Ежедневно с 9:00 до 18:00</p>
</div>
</div>
<!-- Email -->
<div class="contact-method">
<div class="method-icon"></div>
<div class="method-content">
<h3 class="method-title">Email</h3>
<a href="mailto:valitovgaziz@yandex.ru" class="method-value">valitovgaziz@yandex.ru</a>
<p class="method-description">Отвечаем в течение 24 часов</p>
</div>
</div>
<!-- Адрес -->
<div class="contact-method">
<div class="method-icon">📍</div>
<div class="method-content">
<h3 class="method-title">Адрес</h3>
<span class="method-value">г. Уфа</span>
<p class="method-description">Республика Башкортостан</p>
</div>
</div>
<!-- Социальные сети -->
<div class="contact-method">
<div class="method-icon">🌐</div>
<div class="method-content">
<h3 class="method-title">Социальные сети</h3>
<div class="social-contacts">
<a href="https://vk.com/club222248484" target="_blank" class="social-contact">
<span class="social-icon">📘</span>
<span>ВКонтакте</span>
</a>
<a href="https://t.me/+oYymS0r6qG9lYWJi" target="_blank" class="social-contact">
<span class="social-icon">📧</span>
<span>Telegram</span>
</a>
<a href="https://rutube.ru/channel/26509398/" target="_blank" class="social-contact">
<span class="social-icon">📺</span>
<span>RuTube</span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Форма обратной связи -->
<div class="contact-form-section">
<div class="contact-form-card">
<h2 class="form-title">Напишите нам</h2>
<p class="form-description">
Заполните форму ниже, и мы свяжемся с вами в ближайшее время
</p>
<form @submit.prevent="submitForm" class="contact-form">
<div class="form-group">
<label for="name" class="form-label">Имя *</label>
<input
id="name"
v-model="form.name"
type="text"
class="form-input"
placeholder="Введите ваше имя"
required
>
</div>
<div class="form-group">
<label for="email" class="form-label">Email *</label>
<input
id="email"
v-model="form.email"
type="email"
class="form-input"
placeholder="your@email.com"
required
>
</div>
<div class="form-group">
<label for="phone" class="form-label">Телефон</label>
<input
id="phone"
v-model="form.phone"
type="tel"
class="form-input"
placeholder="+7 (999) 999-99-99"
>
</div>
<div class="form-group">
<label for="subject" class="form-label">Тема *</label>
<select
id="subject"
v-model="form.subject"
class="form-select"
required
>
<option value="" disabled selected>Выберите тему</option>
<option value="general">Общий вопрос</option>
<option value="technical">Техническая поддержка</option>
<option value="partnership">Сотрудничество</option>
<option value="feedback">Отзыв и предложения</option>
<option value="other">Другое</option>
</select>
</div>
<div class="form-group">
<label for="message" class="form-label">Сообщение *</label>
<textarea
id="message"
v-model="form.message"
class="form-input form-textarea"
placeholder="Опишите ваш вопрос или предложение..."
rows="5"
required
></textarea>
</div>
<button type="submit" class="btn btn-primary submit-button" :disabled="loading">
<span v-if="loading">Отправка...</span>
<span v-else>Отправить сообщение</span>
</button>
</form>
</div>
</div>
</div>
</div>
</section>
<!-- FAQ секция -->
<section class="faq-section">
<div class="container">
<div class="faq-header">
<h2 class="faq-title">Часто задаваемые вопросы</h2>
<p class="faq-subtitle">Ответы на самые популярные вопросы</p>
</div>
<div class="faq-grid">
<div class="faq-item">
<h3 class="faq-question">Как быстро вы отвечаете на сообщения?</h3>
<p class="faq-answer">
На письма по электронной почте мы отвечаем в течение 24 часов.
По телефону доступны ежедневно с 9:00 до 18:00.
</p>
</div>
<div class="faq-item">
<h3 class="faq-question">Можно ли встретиться лично?</h3>
<p class="faq-answer">
Да, мы организуем личные встречи по предварительной договоренности.
Свяжитесь с нами для согласования времени и места.
</p>
</div>
<div class="faq-item">
<h3 class="faq-question">Предоставляете ли вы техническую поддержку?</h3>
<p class="faq-answer">
Да, наша команда технической поддержки готова помочь с любыми вопросами
по работе платформы и созданию сайтов.
</p>
</div>
<div class="faq-item">
<h3 class="faq-question">Как стать партнером?</h3>
<p class="faq-answer">
Для обсуждения условий партнерства напишите нам на почту или
заполните форму обратной связи, выбрав тему "Сотрудничество".
</p>
</div>
</div>
</div>
</section>
</main>
<!-- Футер -->
<Footer />
</div>
</template>
<script setup lang="ts">
import Header from '~/components/layout/Header.vue'
import Footer from '~/components/layout/Footer.vue'
definePageMeta({
title: 'Контакты - EasySite'
})
// Используем композабл для Telegram
const { sendMessageToTelegram } = useTelegram()
// Состояние формы
const form = ref({
name: '',
email: '',
phone: '',
subject: '',
message: ''
})
const loading = ref(false)
// Функция для получения текста темы
const getSubjectText = (subject: string) => {
const subjects: { [key: string]: string } = {
general: '📋 Общий вопрос',
technical: '🔧 Техническая поддержка',
partnership: '🤝 Сотрудничество',
feedback: '💬 Отзыв и предложения',
other: '❓ Другое'
}
return subjects[subject] || '❓ Неизвестная тема'
}
// Функция отправки формы
const submitForm = async () => {
loading.value = true
try {
// Формируем сообщение для Telegram
const telegramMessage = `
📨 <b>НОВОЕ СООБЩЕНИЕ С САЙТА</b>
👤 <b>Имя:</b> ${form.value.name}
📧 <b>Email:</b> ${form.value.email}
📞 <b>Телефон:</b> ${form.value.phone || 'Не указан'}
🏷️ <b>Тема:</b> ${getSubjectText(form.value.subject)}
💬 <b>Сообщение:</b>
${form.value.message}
⏰ <b>Время отправки:</b> ${new Date().toLocaleString('ru-RU')}
`.trim()
// Отправляем сообщение в Telegram
const { success, error } = await sendMessageToTelegram(telegramMessage)
if (success) {
// Сброс формы после успешной отправки
form.value = {
name: '',
email: '',
phone: '',
subject: '',
message: ''
}
// Показываем уведомление об успехе
showNotification('Сообщение успешно отправлено! Мы свяжемся с вами в ближайшее время.', 'success')
} else {
throw new Error(error || 'Неизвестная ошибка при отправке')
}
} catch (error) {
console.error('Ошибка отправки формы:', error)
showNotification('Произошла ошибка при отправке формы. Пожалуйста, попробуйте еще раз.', 'error')
} finally {
loading.value = false
}
}
// Функция для показа уведомлений
const showNotification = (message: string, type: 'success' | 'error' = 'success') => {
// Можно использовать любую систему уведомлений, здесь простой alert
if (type === 'success') {
alert(`${message}`)
} else {
alert(`${message}`)
}
// В будущем можно заменить на toast-уведомления
// Например: useToast().add({ title: message, color: type === 'success' ? 'green' : 'red' })
}
</script>
<style scoped>
.page-wrapper {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.contacts-page {
flex: 1;
}
/* Hero секция */
.contacts-hero {
background: var(--hero-gradient);
color: var(--text-inverse);
position: relative;
overflow: hidden;
padding: 4rem 0;
}
.contacts-hero::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--hero-pattern);
opacity: 0.1;
}
.hero-content {
position: relative;
z-index: 10;
text-align: center;
max-width: 600px;
margin: 0 auto;
}
.hero-title {
font-family: var(--font-heading);
font-size: var(--text-4xl);
font-weight: var(--font-bold);
line-height: var(--leading-tight);
margin-bottom: var(--space-lg);
color: var(--text-inverse);
}
.hero-subtitle {
font-family: var(--font-accent);
font-size: var(--text-xl);
font-weight: var(--font-light);
line-height: var(--leading-relaxed);
opacity: 0.9;
color: var(--text-inverse);
}
/* Основной контент */
.contacts-content {
padding: var(--space-2xl) 0;
}
.contacts-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-2xl);
align-items: start;
}
/* Карточка контактной информации */
.contact-card {
background: var(--bg-primary);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-md);
border: 1px solid var(--border-light);
padding: var(--space-2xl);
height: fit-content;
}
.contact-header {
margin-bottom: var(--space-2xl);
text-align: center;
}
.contact-title {
font-family: var(--font-heading);
font-size: var(--text-2xl);
font-weight: var(--font-semibold);
color: var(--text-primary);
margin-bottom: var(--space-sm);
}
.contact-description {
color: var(--text-secondary);
line-height: var(--leading-relaxed);
}
/* Методы связи */
.contact-methods {
display: flex;
flex-direction: column;
gap: var(--space-xl);
}
.contact-method {
display: flex;
align-items: flex-start;
gap: var(--space-lg);
padding: var(--space-lg);
border-radius: var(--radius-lg);
transition: all 0.3s ease;
border: 1px solid transparent;
}
.contact-method:hover {
background: var(--bg-secondary);
border-color: var(--border-light);
transform: translateY(-2px);
}
.method-icon {
font-size: 2rem;
width: 3rem;
height: 3rem;
display: flex;
align-items: center;
justify-content: center;
background: var(--primary-50);
border-radius: var(--radius-lg);
flex-shrink: 0;
}
.method-content {
flex: 1;
}
.method-title {
font-family: var(--font-primary);
font-size: var(--text-lg);
font-weight: var(--font-semibold);
color: var(--text-primary);
margin-bottom: var(--space-xs);
}
.method-value {
font-family: var(--font-primary);
font-size: var(--text-base);
font-weight: var(--font-medium);
color: var(--primary-600);
text-decoration: none;
display: block;
margin-bottom: var(--space-xs);
transition: color 0.2s ease;
}
.method-value:hover {
color: var(--primary-700);
text-decoration: underline;
}
.method-description {
font-size: var(--text-sm);
color: var(--text-tertiary);
line-height: var(--leading-relaxed);
}
/* Социальные контакты */
.social-contacts {
display: flex;
flex-direction: column;
gap: var(--space-sm);
}
.social-contact {
display: flex;
align-items: center;
gap: var(--space-sm);
color: var(--text-secondary);
text-decoration: none;
padding: var(--space-sm);
border-radius: var(--radius-md);
transition: all 0.2s ease;
}
.social-contact:hover {
background: var(--bg-secondary);
color: var(--primary-600);
transform: translateX(4px);
}
.social-icon {
font-size: 1.25rem;
width: 1.5rem;
text-align: center;
}
/* Форма обратной связи */
.contact-form-card {
background: var(--bg-primary);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-md);
border: 1px solid var(--border-light);
padding: var(--space-2xl);
height: fit-content;
}
.form-title {
font-family: var(--font-heading);
font-size: var(--text-2xl);
font-weight: var(--font-semibold);
color: var(--text-primary);
margin-bottom: var(--space-sm);
text-align: center;
}
.form-description {
color: var(--text-secondary);
text-align: center;
margin-bottom: var(--space-2xl);
line-height: var(--leading-relaxed);
}
.contact-form {
display: flex;
flex-direction: column;
gap: var(--space-lg);
}
.form-group {
display: flex;
flex-direction: column;
gap: var(--space-xs);
}
.form-label {
font-weight: var(--font-medium);
color: var(--text-secondary);
font-size: var(--text-sm);
}
.form-input,
.form-select {
padding: var(--space-sm) var(--space-md);
border: 1px solid var(--border-medium);
border-radius: var(--radius-md);
font-size: var(--text-base);
transition: all 0.2s ease;
background: var(--bg-primary);
color: var(--text-primary);
font-family: var(--font-primary);
}
.form-input:focus,
.form-select:focus {
outline: none;
border-color: var(--primary-500);
box-shadow: 0 0 0 3px rgb(14 165 233 / 0.1);
}
.form-textarea {
resize: vertical;
min-height: 120px;
font-family: var(--font-primary);
}
.submit-button {
padding: var(--space-md) var(--space-xl);
font-size: var(--text-base);
font-weight: var(--font-semibold);
border: none;
border-radius: var(--radius-md);
cursor: pointer;
transition: all 0.3s ease;
margin-top: var(--space-md);
}
.submit-button:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.submit-button:not(:disabled):hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
/* FAQ секция */
.faq-section {
background: var(--bg-secondary);
padding: var(--space-2xl) 0;
border-top: 1px solid var(--border-light);
}
.faq-header {
text-align: center;
margin-bottom: var(--space-2xl);
}
.faq-title {
font-family: var(--font-heading);
font-size: var(--text-3xl);
font-weight: var(--font-semibold);
color: var(--text-primary);
margin-bottom: var(--space-sm);
}
.faq-subtitle {
color: var(--text-secondary);
font-size: var(--text-lg);
}
.faq-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-xl);
max-width: 1000px;
margin: 0 auto;
}
.faq-item {
background: var(--bg-primary);
border-radius: var(--radius-lg);
padding: var(--space-xl);
border: 1px solid var(--border-light);
transition: all 0.3s ease;
}
.faq-item:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.faq-question {
font-family: var(--font-primary);
font-size: var(--text-lg);
font-weight: var(--font-semibold);
color: var(--text-primary);
margin-bottom: var(--space-md);
line-height: var(--leading-tight);
}
.faq-answer {
color: var(--text-secondary);
line-height: var(--leading-relaxed);
}
/* Адаптивность */
@media (max-width: 1024px) {
.contacts-grid {
gap: var(--space-xl);
}
.faq-grid {
grid-template-columns: 1fr;
gap: var(--space-lg);
}
}
@media (max-width: 768px) {
.contacts-grid {
grid-template-columns: 1fr;
gap: var(--space-xl);
}
.contacts-hero {
padding: 3rem 0;
}
.hero-title {
font-size: var(--text-3xl);
}
.hero-subtitle {
font-size: var(--text-lg);
}
.contact-card,
.contact-form-card {
padding: var(--space-xl);
}
.contact-method {
padding: var(--space-md);
gap: var(--space-md);
}
.method-icon {
font-size: 1.5rem;
width: 2.5rem;
height: 2.5rem;
}
}
@media (max-width: 480px) {
.contacts-content {
padding: var(--space-xl) 0;
}
.contacts-hero {
padding: 2rem 0;
}
.hero-title {
font-size: var(--text-2xl);
}
.contact-card,
.contact-form-card {
padding: var(--space-lg);
}
.faq-item {
padding: var(--space-lg);
}
.contact-method {
flex-direction: column;
text-align: center;
gap: var(--space-sm);
}
.method-icon {
align-self: center;
}
}
</style>
@@ -8,6 +8,47 @@ export default defineNuxtConfig({
compatibilityVersion: 4
},
site: {
url: 'https://easysite102.ru', // Замените на ваш домен
name: 'Easy Site',
description: 'Easy web-site maker. Registration, add info, get web-site',
},
runtimeConfig: {
public: {
siteUrl: process.env.NUXT_PUBLIC_SITE_URL || 'https://localhost:3000'
}
},
sitemap: {
// Базовые настройки
siteUrl: 'https://easysite102.ru',
gzip: true,
cacheMaxAgeSeconds: 3600, // Кэширование на 1 час
// Источники данных для карты сайта
sources: [
'/api/__sitemap__/urls'
],
// Исключить определенные пути
exclude: [
'/admin/**',
'/private/**'
],
// Настройки по умолчанию для всех роутов
defaults: {
changefreq: 'daily',
priority: 0.7,
lastmod: new Date().toISOString()
},
},
features: {
// Отключаем inlineStyles для шрифтов
inlineStyles: false
@@ -17,6 +58,7 @@ export default defineNuxtConfig({
'@nuxt/image',
'@nuxt/ui',
'@nuxt/eslint',
'@nuxtjs/sitemap'
],
css: [
@@ -0,0 +1,28 @@
// server/api/__sitemap__/urls.get.ts
export default defineEventHandler(async () => {
const baseUrl = 'https://easysite102.ru'
// Статические страницы
const staticPages = [
{
loc: `${baseUrl}/`,
lastmod: new Date().toISOString(),
changefreq: 'daily' as const,
priority: 1.0
},
{
loc: `${baseUrl}/about`,
lastmod: new Date().toISOString(),
changefreq: 'monthly' as const,
priority: 0.7
},
{
loc: `${baseUrl}/contact`,
lastmod: new Date().toISOString(),
changefreq: 'monthly' as const,
priority: 0.8
}
]
return staticPages
})