feat: add custom BottomButton instead of TG
This commit is contained in:
@@ -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');
|
||||
|
||||
29
spa/src/components/Quantity.vue
Normal file
29
spa/src/components/Quantity.vue
Normal 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>
|
||||
@@ -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;
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user