feat: add custom BottomButton instead of TG

This commit is contained in:
Nikita Kiselev
2025-07-21 00:48:34 +03:00
parent c282b6ea3b
commit b0cc0237af
4 changed files with 90 additions and 32 deletions

View File

@@ -57,7 +57,7 @@ class Controllerextensiontgshophandle extends Controller
});
$app->bind(ImageToolInterface::class, function () {
return new ImageTool(DIR_IMAGE);
return new ImageTool(DIR_IMAGE, HTTPS_SERVER);
});
$this->load->model('checkout/order');

View File

@@ -0,0 +1,29 @@
<template>
<div class="flex items-center text-center">
<button class="btn btn-neutral w-10 h-10" @click="inc">-</button>
<div class="w-10 h-10 flex items-center justify-center bg-neutral">{{ model }}</div>
<button class="btn btn-neutral w-10 h-10" @click="dec">+</button>
</div>
</template>
<script setup>
const model = defineModel();
const props = defineProps({
max: Number,
});
function inc() {
if (model.value - 1 >= 0) {
model.value--;
}
}
function dec() {
if (props.max && model.value + 1 > props.max) {
model.value = props.max;
return;
}
model.value++;
}
</script>

View File

@@ -23,5 +23,21 @@ export const useCartStore = defineStore('cart', {
options: options,
});
},
removeProduct(productId) {
this.items.splice(this.items.indexOf(productId), 1);
},
getQuantity(productId) {
if (this.hasProduct(productId)) {
return this.getProduct(productId).quantity;
}
return 0;
},
setQuantity(productId, quantity) {
this.getProduct(productId).quantity = quantity;
}
},
});

View File

@@ -4,7 +4,8 @@
<ProductImageSwiper :images="product.images"/>
<!-- Product info -->
<div class="mx-auto max-w-2xl px-4 pt-3 pb-16 sm:px-6 lg:grid lg:max-w-7xl lg:grid-cols-3 lg:grid-rows-[auto_auto_1fr] lg:gap-x-8 lg:px-8 lg:pt-16 lg:pb-24">
<div
class="mx-auto max-w-2xl px-4 pt-3 pb-16 sm:px-6 lg:grid lg:max-w-7xl lg:grid-cols-3 lg:grid-rows-[auto_auto_1fr] lg:gap-x-8 lg:px-8 lg:pt-16 lg:pb-24">
<div class="lg:col-span-2 lg:border-r lg:pr-8">
<h1 class="text-2xl font-bold tracking-tight sm:text-3xl">{{ product.name }}</h1>
</div>
@@ -32,6 +33,22 @@
</div>
</div>
</div>
<div class="p-2 fixed bottom-0 left-0 w-full bg-info-content z-50 flex justify-between gap-2">
<button
class="btn btn-primary flex-1"
@click="actionBtnClick"
>
{{ buttonText }}
</button>
<Quantity
v-if="quantity > 0"
:modelValue="quantity"
@update:modelValue="setQuantity"
:max="10"
/>
</div>
</div>
</template>
@@ -40,60 +57,56 @@ import {computed, onMounted, onUnmounted, ref, watch, watchEffect} from "vue";
import {$fetch} from "ofetch";
import {useRoute} from 'vue-router'
import {useRouter} from 'vue-router'
import {useHapticFeedback} from 'vue-tg';
import ProductOptions from "../components/ProductOptions/ProductOptions.vue";
const hapticFeedback = useHapticFeedback();
import {useCartStore} from "../stores/CartStore.js";
import ProductImageSwiper from "../components/ProductImageSwiper.vue";
import Quantity from "../components/Quantity.vue";
const route = useRoute();
const router = useRouter();
const productId = computed(() => route.params.id);
const product = ref({});
const cart = useCartStore();
const buttonText = computed(() => {
const item = cart.items.find(i => i.productId === productId.value);
return item && item.quantity > 0
? `В корзине: ${item.quantity} · Перейти`
? `Перейти в корзину`
: 'Добавить в корзину'
});
const isInCartNow = computed(() => {
const item = cart.items.find(i => i.productId === productId.value)
return item && item.quantity > 0
})
watchEffect(() => {
window.Telegram.WebApp.MainButton.setText(buttonText.value);
return cart.hasProduct(productId.value);
});
onMounted(async () => {
const {data} = await $fetch(`/index.php?route=extension/tgshop/handle&api_action=product_show&id=${productId.value}`);
product.value = data;
const quantity = computed(() => {
return cart.getQuantity(productId.value);
});
const tg = window.Telegram.WebApp;
tg.MainButton.show();
tg.MainButton.setText(buttonText.value);
tg.MainButton.hasShineEffect = true;
tg.MainButton.onClick(async () => {
function actionBtnClick() {
if (cart.hasProduct(productId.value)) {
router.push({name: 'cart.show'});
} else {
cart.addProduct(productId.value, product.value.name, product.value.price, 1, product.value.options);
}
});
});
}
onUnmounted(() => {
const tg = window.Telegram.WebApp;
tg.MainButton.offClick();
tg.MainButton.hide();
function setQuantity(newQuantity) {
if (newQuantity === 0) {
cart.removeProduct(productId.value);
} else {
cart.setQuantity(productId.value, newQuantity);
}
}
onMounted(async () => {
const {data} = await $fetch(`/index.php?route=extension/tgshop/handle&api_action=product_show&id=${productId.value}`);
product.value = data;
});
const carouselRef = ref();
let lastScrollLeft = 0;
function onScroll(e) {
const scrollLeft = e.target.scrollLeft;
const delta = Math.abs(scrollLeft - lastScrollLeft);