feat(spa): show navbar with app logo and app name
This commit is contained in:
@@ -378,24 +378,22 @@ TEXT,
|
|||||||
],
|
],
|
||||||
|
|
||||||
'module_tgshop_app_name' => [
|
'module_tgshop_app_name' => [
|
||||||
'hidden' => true,
|
|
||||||
'required' => true,
|
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
'placeholder' => 'Введите название Телеграм магазина',
|
'placeholder' => 'Без названия',
|
||||||
'help' => <<<TEXT
|
'help' => <<<TEXT
|
||||||
Отображается в заголовке Telegram Mini App при запуске, а также используется как подпись
|
Отображается в заголовке Telegram Mini App при запуске, а также используется как подпись
|
||||||
под иконкой, если пользователь добавит приложение на главный экран своего устройства.
|
под иконкой, если пользователь добавит приложение на главный экран своего устройства.
|
||||||
Рекомендуется короткое и понятное название (до 20 символов).
|
Рекомендуется короткое и понятное название (до 20 символов).
|
||||||
|
Если оставить пустым, то название выводиться не будет.
|
||||||
TEXT,
|
TEXT,
|
||||||
],
|
],
|
||||||
|
|
||||||
'module_tgshop_app_icon' => [
|
'module_tgshop_app_icon' => [
|
||||||
'hidden' => true,
|
|
||||||
'type' => 'image',
|
'type' => 'image',
|
||||||
'help' => <<<TEXT
|
'help' => <<<TEXT
|
||||||
Изображение, которое будет отображаться в Telegram Mini App и на рабочем столе устройства,
|
Изображение, которое будет отображаться в Telegram Mini App и на рабочем столе устройства,
|
||||||
если пользователь добавит приложение как ярлык. Используйте квадратное изображение PNG или SVG,
|
если пользователь добавит приложение как ярлык. Рекомендуется использовать квадратное изображение PNG или SVG,
|
||||||
размером не менее 192×192 пикселей, а лучше 512x512.
|
размером 32×32 пикселей.
|
||||||
TEXT,
|
TEXT,
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -85,9 +85,9 @@
|
|||||||
|
|
||||||
{# Image #}
|
{# Image #}
|
||||||
{% elseif item['type'] == '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) }}"
|
<img src="{{ attribute(_context, settingKey) }}"
|
||||||
data-placeholder="{{ attribute(_context, settingKey) }}"
|
data-placeholder="https://placehold.co/100x100?text=Удалено"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
<input type="hidden"
|
<input type="hidden"
|
||||||
@@ -95,8 +95,7 @@
|
|||||||
value="{{ attribute(_context, settingKey) }}"
|
value="{{ attribute(_context, settingKey) }}"
|
||||||
id="{{ settingKey }}"
|
id="{{ settingKey }}"
|
||||||
/>
|
/>
|
||||||
|
{# Textarea #}
|
||||||
{# Image #}
|
|
||||||
{% elseif item['type'] == 'textarea' %}
|
{% elseif item['type'] == 'textarea' %}
|
||||||
<textarea name="{{ settingKey }}"
|
<textarea name="{{ settingKey }}"
|
||||||
rows="{{ item['rows'] }}"
|
rows="{{ item['rows'] }}"
|
||||||
@@ -104,7 +103,6 @@
|
|||||||
id="{{ settingKey }}"
|
id="{{ settingKey }}"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
>{{ attribute(_context, settingKey) }}</textarea>
|
>{{ attribute(_context, settingKey) }}</textarea>
|
||||||
|
|
||||||
{# Products #}
|
{# Products #}
|
||||||
{% elseif item['type'] == 'products' %}
|
{% elseif item['type'] == 'products' %}
|
||||||
<input type="text" value="" placeholder="Начните вводить название товара..." id="{{ settingKey }}-input" class="form-control"/>
|
<input type="text" value="" placeholder="Начните вводить название товара..." id="{{ settingKey }}-input" class="form-control"/>
|
||||||
@@ -451,3 +449,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ footer }}
|
{{ 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['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['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;
|
$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([
|
return new JsonResponse([
|
||||||
'app_name' => $this->settings->get('app_name'),
|
'app_name' => $this->settings->get('app_name'),
|
||||||
'app_icon' => $appIcon ? $appIcon . '?_v=' . $hash : '',
|
'app_icon' => $appIcon ?? '',
|
||||||
'app_icon192' => $icons['icon192'] ?? '',
|
'app_icon192' => $icons['icon192'] ?? '',
|
||||||
'app_icon180' => $icons['icon180'] ?? '',
|
'app_icon180' => $icons['icon180'] ?? '',
|
||||||
'app_icon152' => $icons['icon152'] ?? '',
|
'app_icon152' => $icons['icon152'] ?? '',
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
<template>
|
<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">
|
<div class="app-container h-full">
|
||||||
<header class="app-header w-full" v-if="platform === 'ios'"></header>
|
<header class="app-header w-full" v-if="platform === 'ios'"></header>
|
||||||
|
|
||||||
<section class="safe-top">
|
<Navbar @drawer="toggleDrawer"/>
|
||||||
|
|
||||||
|
<section class="telecart-main-section">
|
||||||
<FullscreenViewport v-if="platform === 'ios' || platform === 'android'" />
|
<FullscreenViewport v-if="platform === 'ios' || platform === 'android'" />
|
||||||
<RouterView v-slot="{ Component, route }">
|
<RouterView v-slot="{ Component, route }">
|
||||||
<KeepAlive include="Home" :key="filtersStore.paramsHashForRouter">
|
<KeepAlive include="Home" :key="filtersStore.paramsHashForRouter">
|
||||||
@@ -14,6 +20,17 @@
|
|||||||
<Dock v-if="isAppDockShown" />
|
<Dock v-if="isAppDockShown" />
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</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>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@@ -25,6 +42,7 @@ import {useSettingsStore} from "@/stores/SettingsStore.js";
|
|||||||
import {useProductFiltersStore} from "@/stores/ProductFiltersStore.js";
|
import {useProductFiltersStore} from "@/stores/ProductFiltersStore.js";
|
||||||
import CartButton from "@/components/CartButton.vue";
|
import CartButton from "@/components/CartButton.vue";
|
||||||
import Dock from "@/components/Dock.vue";
|
import Dock from "@/components/Dock.vue";
|
||||||
|
import Navbar from "@/components/Navbar.vue";
|
||||||
|
|
||||||
const tg = useMiniApp();
|
const tg = useMiniApp();
|
||||||
const platform = ref();
|
const platform = ref();
|
||||||
@@ -39,6 +57,7 @@ const settings = useSettingsStore();
|
|||||||
const filtersStore = useProductFiltersStore();
|
const filtersStore = useProductFiltersStore();
|
||||||
const backButton = window.Telegram.WebApp.BackButton;
|
const backButton = window.Telegram.WebApp.BackButton;
|
||||||
const haptic = window.Telegram.WebApp.HapticFeedback;
|
const haptic = window.Telegram.WebApp.HapticFeedback;
|
||||||
|
const drawerOpen = ref(false);
|
||||||
|
|
||||||
const routesToHideAppDock = [
|
const routesToHideAppDock = [
|
||||||
'product.show',
|
'product.show',
|
||||||
@@ -56,6 +75,10 @@ function navigateBack() {
|
|||||||
router.back();
|
router.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleDrawer() {
|
||||||
|
drawerOpen.value = !drawerOpen.value
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => route.name,
|
() => route.name,
|
||||||
() => {
|
() => {
|
||||||
@@ -71,7 +94,7 @@ watch(
|
|||||||
);
|
);
|
||||||
|
|
||||||
function handleClickOutside(e) {
|
function handleClickOutside(e) {
|
||||||
if (!e.target.closest('input')) {
|
if (!e.target.closest('input,select,textarea')) {
|
||||||
document.activeElement?.blur();
|
document.activeElement?.blur();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="telecart-dock fixed bottom-0 w-full z-50 px-10">
|
<div class="telecart-dock fixed bottom-0 w-full z-50 px-10">
|
||||||
<div
|
<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
|
<RouterLink
|
||||||
:to="{name: 'home'}"
|
:to="{name: 'home'}"
|
||||||
:class="{'active': route.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-bullet-inactive-color: var(--color-base-content);
|
||||||
--swiper-pagination-fraction-color: var(--color-neutral-content);
|
--swiper-pagination-fraction-color: var(--color-neutral-content);
|
||||||
--product_list_title_max_lines: 1;
|
--product_list_title_max_lines: 1;
|
||||||
|
--tc-navbar-min-height: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.swiper-pagination-bullets {
|
.swiper-pagination-bullets {
|
||||||
@@ -41,14 +42,22 @@ html {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.app-header {
|
.app-header {
|
||||||
z-index: 100;
|
z-index: 60;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
background: var(--color-primary);
|
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;
|
display: flex;
|
||||||
justify-content: center;
|
flex-direction: column;
|
||||||
|
justify-content: end;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: white;
|
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 {
|
.swiper-pagination-bullets > .swiper-pagination-bullet {
|
||||||
|
|||||||
@@ -109,7 +109,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</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
|
<button
|
||||||
class="btn btn-primary select-none shadow-xl"
|
class="btn btn-primary select-none shadow-xl"
|
||||||
:disabled="cart.canCheckout === false"
|
:disabled="cart.canCheckout === false"
|
||||||
@@ -167,3 +167,9 @@ function goToCheckout() {
|
|||||||
router.push({name: 'checkout'});
|
router.push({name: 'checkout'});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.btn-checkout {
|
||||||
|
bottom: calc(var(--spacing) * 22 + var(--tg-safe-area-inset-bottom))
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="max-w-3xl mx-auto space-y-6 pb-30">
|
<div class="max-w-3xl mx-auto p-4 space-y-6 pb-20">
|
||||||
<h2 class="text-2xl text-center">
|
<h2 class="text-2xl mb-5 text-center">
|
||||||
Оформление заказа
|
Оформление заказа
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div class="card card-border bg-base-100 w-full">
|
<div class="w-full">
|
||||||
<div class="card-body">
|
|
||||||
<TgInput
|
<TgInput
|
||||||
v-model="checkout.customer.firstName"
|
v-model="checkout.customer.firstName"
|
||||||
placeholder="Введите имя"
|
placeholder="Введите имя"
|
||||||
@@ -50,7 +49,6 @@
|
|||||||
@clearError="checkout.clearError('comment')"
|
@clearError="checkout.clearError('comment')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</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">
|
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