feat(spa): show navbar with app logo and app name
This commit is contained in:
@@ -378,24 +378,22 @@ TEXT,
|
||||
],
|
||||
|
||||
'module_tgshop_app_name' => [
|
||||
'hidden' => true,
|
||||
'required' => true,
|
||||
'type' => 'text',
|
||||
'placeholder' => 'Введите название Телеграм магазина',
|
||||
'placeholder' => 'Без названия',
|
||||
'help' => <<<TEXT
|
||||
Отображается в заголовке Telegram Mini App при запуске, а также используется как подпись
|
||||
под иконкой, если пользователь добавит приложение на главный экран своего устройства.
|
||||
Рекомендуется короткое и понятное название (до 20 символов).
|
||||
Если оставить пустым, то название выводиться не будет.
|
||||
TEXT,
|
||||
],
|
||||
|
||||
'module_tgshop_app_icon' => [
|
||||
'hidden' => true,
|
||||
'type' => 'image',
|
||||
'help' => <<<TEXT
|
||||
Изображение, которое будет отображаться в Telegram Mini App и на рабочем столе устройства,
|
||||
если пользователь добавит приложение как ярлык. Используйте квадратное изображение PNG или SVG,
|
||||
размером не менее 192×192 пикселей, а лучше 512x512.
|
||||
если пользователь добавит приложение как ярлык. Рекомендуется использовать квадратное изображение PNG или SVG,
|
||||
размером 32×32 пикселей.
|
||||
TEXT,
|
||||
],
|
||||
|
||||
|
||||
@@ -85,9 +85,9 @@
|
||||
|
||||
{# Image #}
|
||||
{% elseif item['type'] == 'image' %}
|
||||
<a href="" id="thumb-image" data-toggle="image" class="img-thumbnail">
|
||||
<a href="" id="thumb-image-{{ settingKey }}" data-toggle="image" class="img-thumbnail">
|
||||
<img src="{{ attribute(_context, settingKey) }}"
|
||||
data-placeholder="{{ attribute(_context, settingKey) }}"
|
||||
data-placeholder="https://placehold.co/100x100?text=Удалено"
|
||||
/>
|
||||
</a>
|
||||
<input type="hidden"
|
||||
@@ -95,8 +95,7 @@
|
||||
value="{{ attribute(_context, settingKey) }}"
|
||||
id="{{ settingKey }}"
|
||||
/>
|
||||
|
||||
{# Image #}
|
||||
{# Textarea #}
|
||||
{% elseif item['type'] == 'textarea' %}
|
||||
<textarea name="{{ settingKey }}"
|
||||
rows="{{ item['rows'] }}"
|
||||
@@ -104,7 +103,6 @@
|
||||
id="{{ settingKey }}"
|
||||
class="form-control"
|
||||
>{{ attribute(_context, settingKey) }}</textarea>
|
||||
|
||||
{# Products #}
|
||||
{% elseif item['type'] == 'products' %}
|
||||
<input type="text" value="" placeholder="Начните вводить название товара..." id="{{ settingKey }}-input" class="form-control"/>
|
||||
@@ -451,3 +449,12 @@
|
||||
</div>
|
||||
</div>
|
||||
{{ footer }}
|
||||
|
||||
<script>
|
||||
const $element = $('#thumb-image-module_tgshop_app_icon');
|
||||
$('#button-clear').on('click', function() {
|
||||
$element.find('img').attr('src', $element.find('img').attr('data-placeholder'));
|
||||
$element.parent().find('input').val('');
|
||||
$element.popover('destroy');
|
||||
});
|
||||
</script>
|
||||
@@ -37,11 +37,12 @@ class SettingsHandler
|
||||
$icons['icon180'] = $this->imageTool->resize($appIcon, 180, 180, 'no_image.png', 'png'). '?_v=' . $hash;
|
||||
$icons['icon152'] = $this->imageTool->resize($appIcon, 152, 152, 'no_image.png', 'png'). '?_v=' . $hash;
|
||||
$icons['icon120'] = $this->imageTool->resize($appIcon, 120, 120, 'no_image.png', 'png'). '?_v=' . $hash;
|
||||
$appIcon = $this->imageTool->resize($appIcon, 32, 32, 'no_image.png', 'png'). '?_v=' . $hash;
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'app_name' => $this->settings->get('app_name'),
|
||||
'app_icon' => $appIcon ? $appIcon . '?_v=' . $hash : '',
|
||||
'app_icon' => $appIcon ?? '',
|
||||
'app_icon192' => $icons['icon192'] ?? '',
|
||||
'app_icon180' => $icons['icon180'] ?? '',
|
||||
'app_icon152' => $icons['icon152'] ?? '',
|
||||
|
||||
@@ -1,19 +1,36 @@
|
||||
<template>
|
||||
<div class="drawer h-full">
|
||||
<input id="app-drawer" type="checkbox" class="drawer-toggle" v-model="drawerOpen" />
|
||||
|
||||
<div class="drawer-content">
|
||||
<div class="app-container h-full">
|
||||
<header class="app-header w-full" v-if="platform === 'ios'"></header>
|
||||
|
||||
<section class="safe-top">
|
||||
<FullscreenViewport v-if="platform === 'ios' || platform === 'android'"/>
|
||||
<Navbar @drawer="toggleDrawer"/>
|
||||
|
||||
<section class="telecart-main-section">
|
||||
<FullscreenViewport v-if="platform === 'ios' || platform === 'android'" />
|
||||
<RouterView v-slot="{ Component, route }">
|
||||
<KeepAlive include="Home" :key="filtersStore.paramsHashForRouter">
|
||||
<component :is="Component" :key="route.fullPath"/>
|
||||
<component :is="Component" :key="route.fullPath" />
|
||||
</KeepAlive>
|
||||
</RouterView>
|
||||
|
||||
<CartButton v-if="settings.store_enabled"/>
|
||||
<Dock v-if="isAppDockShown"/>
|
||||
<CartButton v-if="settings.store_enabled" />
|
||||
<Dock v-if="isAppDockShown" />
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer-side z-50 safe-top">
|
||||
<label for="app-drawer" aria-label="close sidebar" class="drawer-overlay"></label>
|
||||
<ul class="menu bg-base-200 text-base-content min-h-full w-80 p-4">
|
||||
<li><a href="#">🏠 Главная</a></li>
|
||||
<li><a href="#">🛒 Корзина</a></li>
|
||||
<li><a @click="drawerOpen = false">❌ Закрыть</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -25,6 +42,7 @@ import {useSettingsStore} from "@/stores/SettingsStore.js";
|
||||
import {useProductFiltersStore} from "@/stores/ProductFiltersStore.js";
|
||||
import CartButton from "@/components/CartButton.vue";
|
||||
import Dock from "@/components/Dock.vue";
|
||||
import Navbar from "@/components/Navbar.vue";
|
||||
|
||||
const tg = useMiniApp();
|
||||
const platform = ref();
|
||||
@@ -39,6 +57,7 @@ const settings = useSettingsStore();
|
||||
const filtersStore = useProductFiltersStore();
|
||||
const backButton = window.Telegram.WebApp.BackButton;
|
||||
const haptic = window.Telegram.WebApp.HapticFeedback;
|
||||
const drawerOpen = ref(false);
|
||||
|
||||
const routesToHideAppDock = [
|
||||
'product.show',
|
||||
@@ -56,6 +75,10 @@ function navigateBack() {
|
||||
router.back();
|
||||
}
|
||||
|
||||
function toggleDrawer() {
|
||||
drawerOpen.value = !drawerOpen.value
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.name,
|
||||
() => {
|
||||
@@ -71,7 +94,7 @@ watch(
|
||||
);
|
||||
|
||||
function handleClickOutside(e) {
|
||||
if (!e.target.closest('input')) {
|
||||
if (!e.target.closest('input,select,textarea')) {
|
||||
document.activeElement?.blur();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="telecart-dock fixed bottom-0 w-full z-50 px-10">
|
||||
<div
|
||||
class="telecart-dock-inner flex justify-between items-center bg-base-300/10 h-full backdrop-blur-md px-2 border-base-300/90 border">
|
||||
class="telecart-dock-inner flex justify-around items-center bg-base-300/10 h-full backdrop-blur-md border-base-300/90 border">
|
||||
<RouterLink
|
||||
:to="{name: 'home'}"
|
||||
:class="{'active': route.name === 'home'}"
|
||||
|
||||
56
spa/src/components/Navbar.vue
Normal file
56
spa/src/components/Navbar.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<div class="telecart-navbar fixed navbar bg-primary text-primary-content z-50 shadow-md" :class="{'pb-0' : platform !== 'ios'}">
|
||||
<div class="navbar-start">
|
||||
<div v-if="false" class="dropdown">
|
||||
<button class="btn btn-ghost btn-circle" @click="toggleDrawer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h7" /> </svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="navbar-center">
|
||||
<RouterLink :to="{name: 'home'}" class="text-xl flex items-center">
|
||||
<div class="avatar mr-2">
|
||||
<div v-if="settings.app_icon" class="h-8 rounded-full bg-base-100">
|
||||
<img :src="settings.app_icon" class="h-8" alt=""/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ settings.app_name }}
|
||||
</RouterLink>
|
||||
</div>
|
||||
|
||||
<div class="navbar-end">
|
||||
<button v-if="false" class="btn btn-ghost btn-circle">
|
||||
<div class="indicator">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" /> </svg>
|
||||
<span class="badge badge-xs badge-secondary indicator-item">1</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useSettingsStore} from "@/stores/SettingsStore.js";
|
||||
import {useMiniApp} from "vue-tg";
|
||||
import {ref} from "vue";
|
||||
|
||||
const settings = useSettingsStore();
|
||||
const emits = defineEmits(['drawer']);
|
||||
|
||||
const tg = useMiniApp();
|
||||
const platform = ref();
|
||||
platform.value = tg.platform;
|
||||
|
||||
function toggleDrawer() {
|
||||
emits('drawer');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.telecart-navbar {
|
||||
padding-top: calc(var(--tg-content-safe-area-inset-top) + var(--tg-safe-area-inset-top));
|
||||
min-height: var(--tc-navbar-min-height);
|
||||
}
|
||||
</style>
|
||||
@@ -14,6 +14,7 @@ html {
|
||||
--swiper-pagination-bullet-inactive-color: var(--color-base-content);
|
||||
--swiper-pagination-fraction-color: var(--color-neutral-content);
|
||||
--product_list_title_max_lines: 1;
|
||||
--tc-navbar-min-height: 3rem;
|
||||
}
|
||||
|
||||
.swiper-pagination-bullets {
|
||||
@@ -41,14 +42,22 @@ html {
|
||||
}
|
||||
|
||||
.app-header {
|
||||
z-index: 100;
|
||||
z-index: 60;
|
||||
position: fixed;
|
||||
background: var(--color-primary);
|
||||
padding-top: calc(var(--tg-content-safe-area-inset-top) + var(--tg-safe-area-inset-top));
|
||||
height: calc(var(--tg-content-safe-area-inset-top) + var(--tg-safe-area-inset-top));
|
||||
min-height: calc(var(--tg-content-safe-area-inset-top) + var(--tg-safe-area-inset-top));
|
||||
max-height: calc(var(--tg-content-safe-area-inset-top) + var(--tg-safe-area-inset-top));
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
justify-content: end;
|
||||
align-items: center;
|
||||
color: white;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.telecart-main-section {
|
||||
padding-top: calc(var(--tg-content-safe-area-inset-top) + var(--tg-safe-area-inset-top) + var(--tc-navbar-min-height));
|
||||
}
|
||||
|
||||
.swiper-pagination-bullets > .swiper-pagination-bullet {
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fixed px-4 pt-4 bottom-25 left-0 w-full z-50 flex justify-end items-center gap-2">
|
||||
<div class="btn-checkout fixed px-4 pt-4 left-0 w-full z-50 flex justify-end items-center gap-2">
|
||||
<button
|
||||
class="btn btn-primary select-none shadow-xl"
|
||||
:disabled="cart.canCheckout === false"
|
||||
@@ -167,3 +167,9 @@ function goToCheckout() {
|
||||
router.push({name: 'checkout'});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.btn-checkout {
|
||||
bottom: calc(var(--spacing) * 22 + var(--tg-safe-area-inset-bottom))
|
||||
}
|
||||
</style>
|
||||
@@ -1,11 +1,10 @@
|
||||
<template>
|
||||
<div class="max-w-3xl mx-auto space-y-6 pb-30">
|
||||
<h2 class="text-2xl text-center">
|
||||
<div class="max-w-3xl mx-auto p-4 space-y-6 pb-20">
|
||||
<h2 class="text-2xl mb-5 text-center">
|
||||
Оформление заказа
|
||||
</h2>
|
||||
|
||||
<div class="card card-border bg-base-100 w-full">
|
||||
<div class="card-body">
|
||||
<div class="w-full">
|
||||
<TgInput
|
||||
v-model="checkout.customer.firstName"
|
||||
placeholder="Введите имя"
|
||||
@@ -50,7 +49,6 @@
|
||||
@clearError="checkout.clearError('comment')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="fixed px-4 pb-10 pt-4 bottom-0 left-0 w-full bg-base-200 z-50 flex flex-col justify-between items-center gap-2 border-t-1 border-t-base-300">
|
||||
|
||||
Reference in New Issue
Block a user