v0.4.0
add: - legal .md files - routers - home view - rewrite App.vue - update footer / header - update features
This commit is contained in:
Generated
+85
-2
@@ -1,24 +1,27 @@
|
||||
{
|
||||
"name": "uvpn.shop",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "uvpn.shop",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.1",
|
||||
"dependencies": {
|
||||
"@lucide/vue": "^1.16.0",
|
||||
"@tailwindcss/vite": "^4.3.0",
|
||||
"@tresjs/cientos": "^5.7.1",
|
||||
"@tresjs/core": "^5.8.1",
|
||||
"marked": "^18.0.5",
|
||||
"swiper": "^12.1.4",
|
||||
"tailwindcss": "^4.3.0",
|
||||
"three": "^0.184.0",
|
||||
"vue": "^3.5.32",
|
||||
"vue-router": "^4.6.4",
|
||||
"vue3-simple-icons": "^16.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.19",
|
||||
"@vitejs/plugin-vue": "^6.0.6",
|
||||
"vite": "^8.0.8",
|
||||
"vite-plugin-vue-devtools": "^8.1.1"
|
||||
@@ -1134,6 +1137,19 @@
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/typography": {
|
||||
"version": "0.5.19",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz",
|
||||
"integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"postcss-selector-parser": "6.0.10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/vite": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.0.tgz",
|
||||
@@ -1665,6 +1681,19 @@
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cssesc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"cssesc": "bin/cssesc"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
@@ -2289,6 +2318,18 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||
}
|
||||
},
|
||||
"node_modules/marked": {
|
||||
"version": "18.0.5",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-18.0.5.tgz",
|
||||
"integrity": "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"marked": "bin/marked.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
}
|
||||
},
|
||||
"node_modules/meshoptimizer": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.1.1.tgz",
|
||||
@@ -2441,6 +2482,20 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-selector-parser": {
|
||||
"version": "6.0.10",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
|
||||
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cssesc": "^3.0.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/potpack": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz",
|
||||
@@ -2799,6 +2854,13 @@
|
||||
"browserslist": ">= 4.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "8.0.14",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.14.tgz",
|
||||
@@ -3001,6 +3063,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router": {
|
||||
"version": "4.6.4",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz",
|
||||
"integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vue/devtools-api": "^6.6.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/posva"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router/node_modules/@vue/devtools-api": {
|
||||
"version": "6.6.4",
|
||||
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
||||
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vue3-simple-icons": {
|
||||
"version": "16.10.0",
|
||||
"resolved": "https://registry.npmjs.org/vue3-simple-icons/-/vue3-simple-icons-16.10.0.tgz",
|
||||
|
||||
+4
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "uvpn.shop",
|
||||
"version": "0.3.1",
|
||||
"version": "0.4.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -13,13 +13,16 @@
|
||||
"@tailwindcss/vite": "^4.3.0",
|
||||
"@tresjs/cientos": "^5.7.1",
|
||||
"@tresjs/core": "^5.8.1",
|
||||
"marked": "^18.0.5",
|
||||
"swiper": "^12.1.4",
|
||||
"tailwindcss": "^4.3.0",
|
||||
"three": "^0.184.0",
|
||||
"vue": "^3.5.32",
|
||||
"vue-router": "^4.6.4",
|
||||
"vue3-simple-icons": "^16.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.19",
|
||||
"@vitejs/plugin-vue": "^6.0.6",
|
||||
"vite": "^8.0.8",
|
||||
"vite-plugin-vue-devtools": "^8.1.1"
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
# Пользовательское соглашение
|
||||
|
||||
### 1. Общие положения
|
||||
|
||||
1.1. Настоящее Пользовательское соглашение (далее — **«Соглашение»** ) регулирует порядок использования онлайн-сервиса (далее — **«Сервис»** ), предоставляемого Администрацией.
|
||||
|
||||
1.2. Используя Сервис, включая запуск бота, регистрацию, оплату услуг или получение доступа к материалам, Пользователь подтверждает, что полностью ознакомился с условиями настоящего Соглашения и принимает их в полном объёме.
|
||||
|
||||
1.3. В случае несогласия с условиями Соглашения Пользователь обязан прекратить использование Сервиса.
|
||||
|
||||
### 2. Характер услуг и цифровых товаров
|
||||
|
||||
2.1. Сервис предоставляет цифровые товары и услуги нематериального характера, включая, но не ограничиваясь: информационные материалы, обучающие программы, консультации, цифровые продукты и сервисные услуги.
|
||||
|
||||
2.2. Материалы, предоставляемые через Сервис, могут включать:
|
||||
|
||||
* информацию из открытых источников;
|
||||
* авторские материалы Администрации и/или третьих лиц;
|
||||
* аналитические обзоры, подборки, рекомендации, структурированные данные.
|
||||
|
||||
2.3. Пользователь осознаёт и соглашается, что ценность цифровых товаров и услуг Сервиса заключается **в систематизации, анализе, форме подачи, сопровождении, поддержке и обновлениях** , а не в эксклюзивности отдельных фрагментов информации.
|
||||
|
||||
2.4. Сервис не заявляет и не гарантирует уникальность, исключительность или недоступность отдельных элементов материалов вне Сервиса.
|
||||
|
||||
### 3. Отказ от гарантий и ответственности
|
||||
|
||||
3.1. Сервис предоставляется на условиях **«AS IS» («как есть»)** .
|
||||
|
||||
3.2. Администрация не гарантирует:
|
||||
|
||||
* соответствие Сервиса ожиданиям Пользователя;
|
||||
* достижение каких-либо финансовых, коммерческих, профессиональных или иных результатов;
|
||||
* бесперебойную и безошибочную работу Сервиса.
|
||||
|
||||
3.3. Администрация не несёт ответственности за:
|
||||
|
||||
* любые прямые или косвенные убытки, включая упущенную выгоду;
|
||||
* последствия применения Пользователем полученных материалов;
|
||||
* действия или бездействие третьих лиц;
|
||||
* временные технические сбои и ограничения доступа.
|
||||
|
||||
3.4. Все решения о применении материалов, рекомендаций и услуг принимаются Пользователем **самостоятельно и на его риск** .
|
||||
|
||||
### 4. Законность использования
|
||||
|
||||
4.1. Сервис не предназначен для поощрения, организации или содействия противоправной деятельности.
|
||||
|
||||
4.2. Пользователь обязуется использовать Сервис исключительно в рамках применимого законодательства и правил третьих сторон.
|
||||
|
||||
4.3. Ответственность за законность использования материалов и услуг Сервиса полностью возлагается на Пользователя.
|
||||
|
||||
### 5. Интеллектуальная собственность
|
||||
|
||||
5.1. Все материалы, размещённые в Сервисе, охраняются законодательством об интеллектуальной собственности.
|
||||
|
||||
5.2. Пользователю запрещается копировать, распространять, перепродавать, передавать третьим лицам или иным образом использовать материалы Сервиса без разрешения правообладателя.
|
||||
|
||||
5.3. Нарушение прав интеллектуальной собственности может повлечь ограничение доступа к Сервису без компенсации.
|
||||
|
||||
### 6. Ограничение доступа
|
||||
|
||||
6.1. Администрация вправе приостановить или ограничить доступ Пользователя к Сервису в случае:
|
||||
|
||||
* нарушения условий настоящего Соглашения;
|
||||
* выявления злоупотреблений;
|
||||
* требований законодательства или платёжных провайдеров.
|
||||
|
||||
6.2. Ограничение доступа не освобождает Пользователя от обязательств, возникших ранее.
|
||||
|
||||
6.3. Администрация оставляет за собой право отказывать в обслуживании Пользователям, чьи действия могут создавать повышенные риски для Сервиса, платёжных провайдеров или третьих лиц.
|
||||
|
||||
### 7. Платежи и возвраты
|
||||
|
||||
7.1. Оплата услуг и цифровых товаров производится на условиях, указанных в Сервисе до момента оплаты.
|
||||
|
||||
7.2. В связи с нематериальным характером цифровых товаров и услуг, **возврат денежных средств после предоставления доступа не осуществляется** , за исключением случаев, указанных ниже.
|
||||
|
||||
7.3. Возврат средств возможен только если:
|
||||
|
||||
* услуга не была оказана по технической вине Сервиса;
|
||||
* доступ к цифровому товару фактически не был предоставлен.
|
||||
|
||||
7.4. Для рассмотрения вопроса о возврате Пользователь обязан обратиться в службу поддержки в течение **24 часов** с момента оплаты.
|
||||
|
||||
7.5. Решение о возврате принимается Администрацией индивидуально.
|
||||
|
||||
7.6. Пользователь подтверждает, что обязуется **не инициировать возврат платежа (chargeback)** через платёжные системы без предварительного обращения в службу поддержки Сервиса.
|
||||
|
||||
### 8. Конфиденциальность
|
||||
|
||||
8.1. Администрация может собирать минимально необходимые технические данные для обеспечения работы Сервиса.
|
||||
|
||||
8.2. Администрация принимает разумные меры для защиты данных, однако не гарантирует абсолютную безопасность передаваемой информации.
|
||||
|
||||
### 9. Изменение условий
|
||||
|
||||
9.1. Администрация вправе вносить изменения в настоящее Соглашение.
|
||||
|
||||
9.2. Актуальная версия Соглашения публикуется в Сервисе.
|
||||
|
||||
9.3. Продолжение использования Сервиса означает согласие Пользователя с обновлёнными условиями.
|
||||
|
||||
### 10. Контактная информация
|
||||
|
||||
10.1. По всем вопросам Пользователь может обратиться в службу поддержки через форму в самом боте.
|
||||
|
||||
**Используя Сервис (в том числе запуская бота и/или вводя команду /start), Пользователь подтверждает, что ознакомлен с настоящим Соглашением и принимает его условия в полном объёме.**
|
||||
@@ -0,0 +1,64 @@
|
||||
# Политика конфиденциальности
|
||||
|
||||
Политика конфиденциальности регулирует сбор, использование и защиту информации пользователей сервиса. Собираются идентификаторы аккаунта, техническая информация и история взаимодействий. Данные используются для обеспечения работы сервиса, связи с пользователем и анализа. Передача информации третьим лицам возможна только в законодательно установленных случаях или с согласия пользователя. Хранение данных осуществляется в течение необходимого срока, их защита — в разумных пределах. Пользователь самостоятельно несёт ответственность за риски, связанные с передачей данных. Администрация вправе вносить изменения в Политику без уведомления — согласие считается принятым при дальнейшем использовании сервиса.
|
||||
|
||||
**1. Общие положения**
|
||||
|
||||
1.1. Настоящая Политика конфиденциальности (далее — «Политика») регулирует порядок обработки и защиты информации, которую Пользователь передаёт при использовании сервиса (далее — «Сервис»).
|
||||
|
||||
1.2. Используя Сервис, Пользователь подтверждает своё согласие с условиями Политики. Если Пользователь не согласен с условиями — он обязан прекратить использование Сервиса.
|
||||
|
||||
---
|
||||
|
||||
**2. Сбор информации**
|
||||
|
||||
2.1. Сервис может собирать следующие типы данных:
|
||||
|
||||
* идентификаторы аккаунта (логин, ID, никнейм и т.п.);
|
||||
* техническую информацию (IP-адрес, данные о браузере, устройстве и операционной системе);
|
||||
* историю взаимодействий с Сервисом.
|
||||
* 2.2. Сервис не требует от Пользователя предоставления паспортных данных, документов, фотографий или другой личной информации, кроме минимально необходимой для работы.
|
||||
|
||||
---
|
||||
|
||||
**3. Использование информации**
|
||||
|
||||
3.1. Сервис может использовать полученную информацию исключительно для:
|
||||
|
||||
* обеспечения работы функционала;
|
||||
* связи с Пользователем (в том числе для уведомлений и поддержки);
|
||||
* анализа и улучшения работы Сервиса.
|
||||
|
||||
---
|
||||
|
||||
**4. Передача информации третьим лицам**
|
||||
|
||||
4.1. Администрация не передаёт полученные данные третьим лицам, за исключением случаев:
|
||||
|
||||
* если это требуется по закону;
|
||||
* если это необходимо для исполнения обязательств перед Пользователем (например, при работе с платёжными системами);
|
||||
* если Пользователь сам дал на это согласие.
|
||||
|
||||
---
|
||||
|
||||
**5. Хранение и защита данных**
|
||||
|
||||
5.1. Данные хранятся в течение срока, необходимого для достижения целей обработки.
|
||||
|
||||
5.2. Администрация принимает разумные меры для защиты данных, но не гарантирует абсолютную безопасность информации при передаче через интернет.
|
||||
|
||||
---
|
||||
|
||||
**6. Отказ от ответственности**
|
||||
|
||||
6.1. Пользователь понимает и соглашается, что передача информации через интернет всегда сопряжена с рисками.
|
||||
|
||||
6.2. Администрация не несёт ответственности за утрату, кражу или раскрытие данных, если это произошло по вине третьих лиц или самого Пользователя.
|
||||
|
||||
---
|
||||
|
||||
**7. Изменения в Политике**
|
||||
|
||||
7.1. Администрация вправе изменять условия Политики без предварительного уведомления.
|
||||
|
||||
7.2. Продолжение использования Сервиса после внесения изменений означает согласие Пользователя с новой редакцией Политики.
|
||||
+6
-15
@@ -1,25 +1,16 @@
|
||||
<template>
|
||||
<div class="bg-gray-50 dark:bg-gray-950 text-gray-900 dark:text-white font-sans antialiased min-h-screen">
|
||||
<div class="bg-gray-50 dark:bg-gray-950 text-gray-900 dark:text-white font-sans antialiased min-h-screen flex flex-col">
|
||||
<AppHeader />
|
||||
<HeroSection />
|
||||
<FeaturesSection />
|
||||
<CompatibilitySection />
|
||||
<PricingSection />
|
||||
<StepsSection />
|
||||
<TestimonialsSection />
|
||||
<FaqSection />
|
||||
|
||||
<main class="grow">
|
||||
<RouterView />
|
||||
</main>
|
||||
|
||||
<AppFooter />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import AppHeader from './components/AppHeader.vue'
|
||||
import HeroSection from './components/HeroSection.vue'
|
||||
import FeaturesSection from './components/FeaturesSection.vue'
|
||||
import CompatibilitySection from './components/CompatibilitySection.vue'
|
||||
import PricingSection from './components/PricingSection.vue'
|
||||
import StepsSection from './components/StepsSection.vue'
|
||||
import TestimonialsSection from './components/TestimonialsSection.vue'
|
||||
import FaqSection from './components/FaqSection.vue'
|
||||
import AppFooter from './components/AppFooter.vue'
|
||||
</script>
|
||||
@@ -1 +1,2 @@
|
||||
@import "tailwindcss";
|
||||
@plugin "@tailwindcss/typography";
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
<span class="text-lg font-bold">uVPN</span>
|
||||
</div>
|
||||
<div class="flex gap-6 text-gray-500 dark:text-gray-400 text-sm">
|
||||
<a href="#" class="hover:text-gray-900 dark:hover:text-gray-400 dark:text-white transition">Политика конфиденциальности</a>
|
||||
<a href="#" class="hover:text-gray-900 dark:hover:text-gray-400 dark:text-white transition">Условия использования</a>
|
||||
<a href="#" class="hover:text-gray-900 dark:hover:text-gray-400 dark:text-white transition">Поддержка</a>
|
||||
<router-link to="/docs/policy" class="hover:text-gray-900 dark:hover:text-white text-gray-500 dark:text-gray-400 transition hover:cursor-pointer">Политика конфиденциальности</router-link>
|
||||
<router-link to="/docs/agreement" class="hover:text-gray-900 dark:hover:text-white text-gray-500 dark:text-gray-400 hover:cursor-pointer">Условия использования</router-link>
|
||||
<a href="mailto:support@uvpn.shop" target="_blank" class="hover:text-gray-900 dark:hover:text-white text-gray-500 dark:text-gray-400 transition">Поддержка</a>
|
||||
</div>
|
||||
<p class="text-gray-500 dark:text-gray-400 text-sm">
|
||||
© 2026 uVPN. Все права защищены.
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
<template>
|
||||
<header class="flex items-center justify-between px-6 py-4 md:px-12 border-b border-gray-200 dark:border-gray-800 sticky top-0 bg-white/80 dark:bg-gray-900/80 backdrop-blur-xs z-50">
|
||||
<div class="flex items-center gap-3">
|
||||
|
||||
<!-- Логотип -->
|
||||
<router-link to="/" class="flex items-center gap-3 hover:opacity-90 transition">
|
||||
<img src="/images/logo.png" alt="uVPN logo" class="size-8" />
|
||||
<span class="text-xl font-bold tracking-tight">uVPN</span>
|
||||
</div>
|
||||
</router-link>
|
||||
|
||||
<!-- Якорные ссылки -->
|
||||
<!-- Навигация: теперь это router-link с хэшем секции -->
|
||||
<nav class="hidden md:flex items-center gap-6 text-sm text-gray-500 dark:text-gray-400">
|
||||
<a href="#hero" @click.prevent="scrollTo('hero')" class="hover:text-gray-900 dark:hover:text-gray-400 dark:text-white transition">Главная</a>
|
||||
<a href="#features" @click.prevent="scrollTo('features')" class="hover:text-gray-900 dark:hover:text-gray-400 dark:text-white transition">Преимущества</a>
|
||||
<a href="#pricing" @click.prevent="scrollTo('pricing')" class="hover:text-gray-900 dark:hover:text-gray-400 dark:text-white transition">Тарифы</a>
|
||||
<a href="#steps" @click.prevent="scrollTo('steps')" class="hover:text-gray-900 dark:hover:text-gray-400 dark:text-white transition">Как начать</a>
|
||||
<a href="#testimonials" @click.prevent="scrollTo('testimonials')" class="hover:text-gray-900 dark:hover:text-gray-400 dark:text-white transition">Отзывы</a>
|
||||
<a href="#footer" @click.prevent="scrollTo('faq')" class="hover:text-gray-900 dark:hover:text-gray-400 dark:text-white transition">FAQ</a>
|
||||
<router-link :to="{ path: '/', hash: '#hero' }" class="hover:text-gray-900 dark:hover:text-white text-gray-500 dark:text-gray-400 transition">Главная</router-link>
|
||||
<router-link :to="{ path: '/', hash: '#features' }" class="hover:text-gray-900 dark:hover:text-white text-gray-500 dark:text-gray-400 transition">Преимущества</router-link>
|
||||
<router-link :to="{ path: '/', hash: '#pricing' }" class="hover:text-gray-900 dark:hover:text-white text-gray-500 dark:text-gray-400 transition">Тарифы</router-link>
|
||||
<router-link :to="{ path: '/', hash: '#steps' }" class="hover:text-gray-900 dark:hover:text-white text-gray-500 dark:text-gray-400 transition">Как начать</router-link>
|
||||
<router-link :to="{ path: '/', hash: '#testimonials' }" class="hover:text-gray-900 dark:hover:text-white text-gray-500 dark:text-gray-400 transition">Отзывы</router-link>
|
||||
<router-link :to="{ path: '/', hash: '#faq' }" class="hover:text-gray-900 dark:hover:text-white text-gray-500 dark:text-gray-400 transition">FAQ</router-link>
|
||||
</nav>
|
||||
|
||||
<button class="bg-blue-600 dark:bg-blue-500 hover:bg-blue-700 transition px-5 py-2 rounded-lg font-medium text-sm text-white">
|
||||
<!-- Кнопка тарифов -->
|
||||
<router-link :to="{ path: '/', hash: '#pricing' }" class="bg-blue-600 dark:bg-blue-500 hover:bg-blue-700 transition px-5 py-2 rounded-lg font-medium text-sm text-white">
|
||||
Попробовать бесплатно
|
||||
</button>
|
||||
</router-link>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const scrollTo = (id) => {
|
||||
const el = document.getElementById(id)
|
||||
if (el) {
|
||||
el.scrollIntoView({ behavior: 'smooth' })
|
||||
}
|
||||
}
|
||||
// Скрипты больше не нужны, всю магию сделает роутер!
|
||||
</script>
|
||||
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<div class="max-w-4xl mx-auto px-4 py-12">
|
||||
<!-- Навигация: теперь используем router-link вместо обычных кнопок -->
|
||||
<nav class="flex space-x-6 mb-8 pb-2">
|
||||
<router-link
|
||||
to="/docs/policy"
|
||||
class="px-1 py-2 text-sm font-medium transition-colors border-b-2 -mb-2.5"
|
||||
active-class="border-blue-600 text-blue-600 dark:text-blue-400 dark:border-blue-400"
|
||||
inactive-class="border-transparent text-slate-500 hover:text-slate-700 dark:text-slate-400"
|
||||
>
|
||||
Политика конфиденциальности
|
||||
</router-link>
|
||||
|
||||
<router-link
|
||||
to="/docs/agreement"
|
||||
class="px-1 py-2 text-sm font-medium transition-colors border-b-2 -mb-2.5"
|
||||
active-class="border-blue-600 text-blue-600 dark:text-blue-400 dark:border-blue-400"
|
||||
inactive-class="border-transparent text-slate-500 hover:text-slate-700 dark:text-slate-400"
|
||||
>
|
||||
Пользовательское соглашение
|
||||
</router-link>
|
||||
</nav>
|
||||
|
||||
<!-- Область отображения Markdown (Tailwind Typography) -->
|
||||
<div
|
||||
v-if="htmlContent"
|
||||
v-html="htmlContent"
|
||||
class="prose prose-slate max-w-none dark:prose-invert"
|
||||
></div>
|
||||
|
||||
<div v-else-if="isLoading" class="text-center py-12 text-slate-500 dark:text-slate-400">
|
||||
Загрузка документа...
|
||||
</div>
|
||||
|
||||
<div v-else class="text-center py-12 text-red-500">
|
||||
Документ не найден или произошла ошибка.
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted } from 'vue'
|
||||
import { marked } from 'marked'
|
||||
|
||||
// Принимаем параметр docName из роутера
|
||||
const props = defineProps({
|
||||
docName: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const htmlContent = ref('')
|
||||
const isLoading = ref(true)
|
||||
|
||||
// Функция загрузки md-файла
|
||||
const fetchMarkdown = async (name) => {
|
||||
isLoading.value = true
|
||||
htmlContent.value = ''
|
||||
|
||||
try {
|
||||
// Загрузка из папки public/docs/
|
||||
const response = await fetch(`/docs/${name}.md`)
|
||||
if (!response.ok) throw new Error('Файл отсутствует')
|
||||
|
||||
const markdownText = await response.text()
|
||||
htmlContent.value = marked(markdownText)
|
||||
} catch (error) {
|
||||
console.error('Ошибка отрисовки md:', error)
|
||||
htmlContent.value = ''
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Загружаем файл при первой инициализации страницы
|
||||
onMounted(() => {
|
||||
fetchMarkdown(props.docName)
|
||||
})
|
||||
|
||||
// Отслеживаем изменение ссылки (например, если пользователь перешел с policy на agreement)
|
||||
watch(() => props.docName, (newName) => {
|
||||
fetchMarkdown(newName)
|
||||
})
|
||||
</script>
|
||||
@@ -38,7 +38,7 @@
|
||||
<div class="bg-white dark:bg-gray-900 rounded-2xl p-8 text-center hover:bg-gray-100 dark:hover:bg-gray-800 transition border border-gray-200 dark:border-gray-800">
|
||||
<RefreshCw class="w-12 h-12 text-blue-600 dark:text-blue-400 mx-auto mb-4" />
|
||||
<h3 class="text-xl font-semibold mb-3">Стабильная работа</h3>
|
||||
<p class="text-gray-500 dark:text-gray-400">Автоматические обновления конфигураций — VPN работает даже при новых блокировках</p>
|
||||
<p class="text-gray-500 dark:text-gray-400">Автоматические обновления конфигураций — мы первые подстраиваемся под новые условия</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
+6
-1
@@ -1,5 +1,10 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import './assets/styles.css'
|
||||
|
||||
createApp(App).mount('#app')
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
||||
@@ -0,0 +1,34 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import HomeView from '../views/HomeView.vue'
|
||||
import DocViewer from '../components/DocViewer.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
|
||||
scrollBehavior(to, from, savedPosition) {
|
||||
// Если в ссылке есть хэш (например, #pricing)
|
||||
if (to.hash) {
|
||||
return new Promise((resolve) => {
|
||||
// Даем 200 миллисекунд компонентам (Three.js/Swiper) на полную отрисовку
|
||||
setTimeout(() => {
|
||||
resolve({
|
||||
el: to.hash,
|
||||
behavior: 'smooth',
|
||||
// Корректировка отступа сверху, чтобы липкая (sticky) шапка не перекрывала заголовок блока
|
||||
offset: { x: 0, y: 80 }
|
||||
})
|
||||
}, 200)
|
||||
})
|
||||
}
|
||||
|
||||
// Если хэша нет, при переходе на новую страницу просто скроллим в самый верх
|
||||
return { top: 0 }
|
||||
},
|
||||
|
||||
routes: [
|
||||
{ path: '/', name: 'home', component: HomeView },
|
||||
{ path: '/docs/:docName', name: 'documents', component: DocViewer, props: true }
|
||||
]
|
||||
})
|
||||
|
||||
export default router
|
||||
@@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div>
|
||||
<HeroSection />
|
||||
<FeaturesSection />
|
||||
<CompatibilitySection />
|
||||
<PricingSection />
|
||||
<StepsSection />
|
||||
<TestimonialsSection />
|
||||
<FaqSection />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import HeroSection from '../components/HeroSection.vue'
|
||||
import FeaturesSection from '../components/FeaturesSection.vue'
|
||||
import CompatibilitySection from '../components/CompatibilitySection.vue'
|
||||
import PricingSection from '../components/PricingSection.vue'
|
||||
import StepsSection from '../components/StepsSection.vue'
|
||||
import TestimonialsSection from '../components/TestimonialsSection.vue'
|
||||
import FaqSection from '../components/FaqSection.vue'
|
||||
</script>
|
||||
@@ -2,7 +2,6 @@ import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import vueDevTools from 'vite-plugin-vue-devtools'
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
|
||||
|
||||
@@ -10,7 +9,6 @@ import tailwindcss from '@tailwindcss/vite'
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
vueDevTools(),
|
||||
tailwindcss(),
|
||||
],
|
||||
resolve: {
|
||||
|
||||
Reference in New Issue
Block a user