update pricing section

fix: footer & header
This commit is contained in:
austnv
2026-05-27 22:00:42 +03:00
parent e4d73becc9
commit 3fb0d198a2
4 changed files with 143 additions and 41 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "uvpn.shop",
"version": "0.3.0",
"version": "0.3.1",
"private": true,
"type": "module",
"scripts": {
+3 -3
View File
@@ -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:text-white transition">Политика конфиденциальности</a>
<a href="#" class="hover:text-gray-900 dark:text-white transition">Условия использования</a>
<a href="#" class="hover:text-gray-900 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>
<a href="#" class="hover:text-gray-900 dark:hover:text-gray-400 dark:text-white transition">Поддержка</a>
</div>
<p class="text-gray-500 dark:text-gray-400 text-sm">
© 2026 uVPN. Все права защищены.
+1 -1
View File
@@ -12,7 +12,7 @@
<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('footer')" 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>
</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">
+138 -36
View File
@@ -3,45 +3,59 @@
<h2 class="text-3xl md:text-4xl font-bold text-center mb-14">
Прозрачные тарифы
</h2>
<!-- Переключатель периода -->
<div class="flex justify-center gap-2 mb-10">
<button
v-for="period in availablePeriods"
:key="period.value"
@click="selectedPeriod = period.value"
class="px-5 py-2 rounded-full text-sm font-medium transition"
:class="selectedPeriod === period.value
? 'bg-blue-600 text-white shadow-md'
: 'bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-300 dark:hover:bg-gray-600'"
>
{{ period.label }}
</button>
</div>
<!-- Сетка тарифов -->
<div class="grid md:grid-cols-3 gap-8">
<!-- Тариф Месяц -->
<div class="bg-white dark:bg-gray-900 rounded-2xl p-8 flex flex-col border border-gray-200 dark:border-gray-800 hover:border-blue-600 transition">
<h3 class="text-xl font-semibold mb-2">Месяц</h3>
<p class="text-4xl font-extrabold mb-4">$11.99<span class="text-lg font-normal text-gray-500 dark:text-gray-400">/мес</span></p>
<div
v-for="tariff in tariffCards"
:key="tariff.key"
class="bg-white dark:bg-gray-900 rounded-2xl p-8 flex flex-col border transition"
:class="[
tariff.isPopular
? 'border-2 border-blue-600 relative transform scale-105 shadow-2xl shadow-blue-600/20'
: 'border border-gray-200 dark:border-gray-800 hover:border-blue-600'
]"
>
<span
v-if="tariff.isPopular"
class="absolute top-0 right-0 bg-blue-600 dark:bg-blue-500 text-gray-900 dark:text-white text-xs font-bold px-4 py-1 rounded-bl-xl rounded-tr-xl"
>
ПОПУЛЯРНЫЙ
</span>
<h3 class="text-xl font-semibold mb-2">{{ tariff.name }}</h3>
<p class="text-4xl font-extrabold mb-4">
{{ tariff.price }}<span class="text-lg font-normal text-gray-500 dark:text-gray-400">/мес</span>
</p>
<ul class="text-gray-500 dark:text-gray-400 space-y-3 mb-8 flex-1">
<li class="flex items-center gap-2"><Check class="w-4 h-4 text-green-400" /> Все устройства</li>
<li class="flex items-center gap-2"><Check class="w-4 h-4 text-green-400" /> Безлимитный трафик</li>
<li class="flex items-center gap-2"><Check class="w-4 h-4 text-green-400" /> Поддержка 24/7</li>
<li v-for="feature in tariff.features" :key="feature" class="flex items-center gap-2">
<Check class="w-4 h-4 text-green-400" /> {{ feature }}
</li>
</ul>
<button class="w-full bg-gray-800 hover:bg-gray-700 transition py-3 rounded-xl font-medium text-white">
Выбрать
</button>
</div>
<!-- Тариф Год (выделенный) -->
<div class="bg-white dark:bg-gray-900 rounded-2xl p-8 flex flex-col border-2 border-blue-600 relative transform scale-105 shadow-2xl shadow-blue-600/20">
<span class="absolute top-0 right-0 bg-blue-600 dark:bg-blue-500 text-gray-900 dark:text-white text-xs font-bold px-4 py-1 rounded-bl-xl rounded-tr-xl">ПОПУЛЯРНЫЙ</span>
<h3 class="text-xl font-semibold mb-2">Год</h3>
<p class="text-4xl font-extrabold mb-4">$4.99<span class="text-lg font-normal text-gray-500 dark:text-gray-400">/мес</span></p>
<ul class="text-gray-500 dark:text-gray-400 space-y-3 mb-8 flex-1">
<li class="flex items-center gap-2"><Check class="w-4 h-4 text-green-400" /> Все устройства</li>
<li class="flex items-center gap-2"><Check class="w-4 h-4 text-green-400" /> Безлимитный трафик</li>
<li class="flex items-center gap-2"><Check class="w-4 h-4 text-green-400" /> Приоритетная поддержка</li>
</ul>
<button class="w-full bg-blue-600 dark:bg-blue-500 hover:bg-blue-700 transition py-3 rounded-xl font-bold text-white">
Выбрать тариф
</button>
</div>
<!-- Тариф 2 года -->
<div class="bg-white dark:bg-gray-900 rounded-2xl p-8 flex flex-col border border-gray-200 dark:border-gray-800 hover:border-blue-600 transition">
<h3 class="text-xl font-semibold mb-2">2 года</h3>
<p class="text-4xl font-extrabold mb-4">$2.99<span class="text-lg font-normal text-gray-500 dark:text-gray-400">/мес</span></p>
<ul class="text-gray-500 dark:text-gray-400 space-y-3 mb-8 flex-1">
<li class="flex items-center gap-2"><Check class="w-4 h-4 text-green-400" /> Все устройства</li>
<li class="flex items-center gap-2"><Check class="w-4 h-4 text-green-400" /> Безлимитный трафик</li>
<li class="flex items-center gap-2"><Check class="w-4 h-4 text-green-400" /> Максимальная экономия</li>
</ul>
<button class="w-full bg-gray-800 hover:bg-gray-700 transition py-3 rounded-xl font-medium text-white">
Выбрать
<button
class="w-full transition py-3 rounded-xl font-medium text-white"
:class="tariff.isPopular
? 'bg-blue-600 dark:bg-blue-500 hover:bg-blue-700 font-bold'
: 'bg-gray-800 hover:bg-gray-700'"
>
{{ tariff.isPopular ? 'Выбрать тариф' : 'Выбрать' }}
</button>
</div>
</div>
@@ -49,5 +63,93 @@
</template>
<script setup>
import { ref, computed } from 'vue'
import { Check } from '@lucide/vue'
// Исходный конфиг
const pricingConfig = {
default_tariff: "premium",
tariffs: [
{
key: "standard",
names: { ru: "Standard", en: "Standard" },
descriptions: { ru: "До 2 устройств", en: "up to 2 devices" },
prices_rub: { "1": 179.0, "3": 387.0, "6": 594.0, "12": 996.0 },
hwid_device_limit: 2,
monthly_gb: 0.0
},
{
key: "medium",
names: { ru: "Medium", en: "Medium" },
descriptions: { ru: "До 3 устройств, YouTube без рекламы", en: "Up to 3 devices, YouTube without ads" },
prices_rub: { "1": 229.0, "3": 507.0, "6": 954.0, "12": 1488.0 },
hwid_device_limit: 3,
monthly_gb: 0.0
},
{
key: "premium",
names: { ru: "Premium", en: "Premium" },
descriptions: { ru: "До 10 устройств, YouTube без рекламы, сервера РФ", en: "Up to 10 devices, YouTube without ads, RU servers" },
prices_rub: { "1": 349.0, "3": 987.0, "6": 1734.0, "12": 1992.0 },
hwid_device_limit: 10,
monthly_gb: 0.0
}
]
}
// Доступные периоды
const availablePeriods = [
{ value: 1, label: '1 мес' },
{ value: 3, label: '3 мес' },
{ value: 6, label: '6 мес' },
{ value: 12, label: '12 мес' }
]
// Выбранный период (по умолчанию 1 месяц)
const selectedPeriod = ref(1)
// Функция для получения цены за месяц
const getMonthlyPrice = (tariff, period) => {
const total = tariff.prices_rub[String(period)]
if (!total) return '—'
const monthly = total / period
return Number.isInteger(monthly) ? monthly : monthly.toFixed(2)
}
// Подготовка данных для карточек
const tariffCards = computed(() => {
const tariffs = pricingConfig.tariffs
const defaultKey = pricingConfig.default_tariff
// Разделяем тарифы: популярный (премиум) и остальные
const premiumTariff = tariffs.find(t => t.key === defaultKey)
const otherTariffs = tariffs.filter(t => t.key !== defaultKey)
// Порядок: первый обычный, премиум, второй обычный
const ordered = [
{ ...otherTariffs[0], isPopular: false },
{ ...premiumTariff, isPopular: true },
{ ...otherTariffs[1], isPopular: false }
]
return ordered.map(tariff => {
const price = getMonthlyPrice(tariff, selectedPeriod.value)
// Фичи: разбиваем описание по запятой и добавляем общие пункты
const descriptionFeatures = tariff.descriptions.ru
.split(',')
.map(s => s.trim())
.filter(Boolean)
const staticFeatures = ['Безлимитный трафик', 'Поддержка 24/7']
const features = [...new Set([...descriptionFeatures, ...staticFeatures])]
return {
key: tariff.key,
name: tariff.names.ru,
price: `${price}`,
features,
isPopular: tariff.isPopular
}
})
})
</script>