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 () {
|
$app->bind(ImageToolInterface::class, function () {
|
||||||
return new ImageTool(DIR_IMAGE);
|
return new ImageTool(DIR_IMAGE, HTTPS_SERVER);
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->load->model('checkout/order');
|
$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,
|
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"/>
|
<ProductImageSwiper :images="product.images"/>
|
||||||
|
|
||||||
<!-- Product info -->
|
<!-- 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">
|
<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>
|
<h1 class="text-2xl font-bold tracking-tight sm:text-3xl">{{ product.name }}</h1>
|
||||||
</div>
|
</div>
|
||||||
@@ -32,68 +33,80 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {computed, onMounted, onUnmounted, ref, watch, watchEffect} from "vue";
|
import {computed, onMounted, onUnmounted, ref, watch, watchEffect} from "vue";
|
||||||
import {$fetch} from "ofetch";
|
import {$fetch} from "ofetch";
|
||||||
import { useRoute } from 'vue-router'
|
import {useRoute} from 'vue-router'
|
||||||
import { useRouter } from 'vue-router'
|
import {useRouter} from 'vue-router'
|
||||||
import {useHapticFeedback} from 'vue-tg';
|
|
||||||
import ProductOptions from "../components/ProductOptions/ProductOptions.vue";
|
import ProductOptions from "../components/ProductOptions/ProductOptions.vue";
|
||||||
const hapticFeedback = useHapticFeedback();
|
|
||||||
import {useCartStore} from "../stores/CartStore.js";
|
import {useCartStore} from "../stores/CartStore.js";
|
||||||
import ProductImageSwiper from "../components/ProductImageSwiper.vue";
|
import ProductImageSwiper from "../components/ProductImageSwiper.vue";
|
||||||
|
import Quantity from "../components/Quantity.vue";
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const productId = computed(() => route.params.id);
|
const productId = computed(() => route.params.id);
|
||||||
const product = ref({});
|
const product = ref({});
|
||||||
|
|
||||||
const cart = useCartStore();
|
const cart = useCartStore();
|
||||||
|
|
||||||
const buttonText = computed(() => {
|
const buttonText = computed(() => {
|
||||||
const item = cart.items.find(i => i.productId === productId.value);
|
const item = cart.items.find(i => i.productId === productId.value);
|
||||||
return item && item.quantity > 0
|
return item && item.quantity > 0
|
||||||
? `В корзине: ${item.quantity} · Перейти`
|
? `Перейти в корзину`
|
||||||
: 'Добавить в корзину'
|
: 'Добавить в корзину'
|
||||||
});
|
});
|
||||||
|
|
||||||
const isInCartNow = computed(() => {
|
const isInCartNow = computed(() => {
|
||||||
const item = cart.items.find(i => i.productId === productId.value)
|
return cart.hasProduct(productId.value);
|
||||||
return item && item.quantity > 0
|
|
||||||
})
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
window.Telegram.WebApp.MainButton.setText(buttonText.value);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
const quantity = computed(() => {
|
||||||
const {data} = await $fetch(`/index.php?route=extension/tgshop/handle&api_action=product_show&id=${productId.value}`);
|
return cart.getQuantity(productId.value);
|
||||||
product.value = data;
|
});
|
||||||
|
|
||||||
const tg = window.Telegram.WebApp;
|
function actionBtnClick() {
|
||||||
tg.MainButton.show();
|
|
||||||
tg.MainButton.setText(buttonText.value);
|
|
||||||
tg.MainButton.hasShineEffect = true;
|
|
||||||
tg.MainButton.onClick(async () => {
|
|
||||||
if (cart.hasProduct(productId.value)) {
|
if (cart.hasProduct(productId.value)) {
|
||||||
router.push({name: 'cart.show'});
|
router.push({name: 'cart.show'});
|
||||||
} else {
|
} else {
|
||||||
cart.addProduct(productId.value, product.value.name, product.value.price, 1, product.value.options);
|
cart.addProduct(productId.value, product.value.name, product.value.price, 1, product.value.options);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
});
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
function setQuantity(newQuantity) {
|
||||||
const tg = window.Telegram.WebApp;
|
if (newQuantity === 0) {
|
||||||
tg.MainButton.offClick();
|
cart.removeProduct(productId.value);
|
||||||
tg.MainButton.hide();
|
} 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();
|
const carouselRef = ref();
|
||||||
let lastScrollLeft = 0;
|
let lastScrollLeft = 0;
|
||||||
|
|
||||||
function onScroll(e) {
|
function onScroll(e) {
|
||||||
const scrollLeft = e.target.scrollLeft;
|
const scrollLeft = e.target.scrollLeft;
|
||||||
const delta = Math.abs(scrollLeft - lastScrollLeft);
|
const delta = Math.abs(scrollLeft - lastScrollLeft);
|
||||||
|
|||||||
Reference in New Issue
Block a user