@svm2001

Как вывести общую сумму в корзине?

У меня есть 4 компонента во vue:
App.vue --> Drawer.vue --> CartItemList.vue --> CartItem.vue

Написал именно так, это их вложенность. Выглядит примерно так:

668043d0c7f7b282834398.png

App.vue
<script setup>
import Header from '@/components/Header.vue'
import Drawer from "@/components/Drawer.vue";
import axios from "axios";
import {onMounted, reactive, ref, watch, provide, computed} from "vue";

const cart = ref([])
const drawerOpen = ref(false)
const closeDrawer = () => drawerOpen.value = false
const openDrawer = () => drawerOpen.value = true
const totalPrice = computed(() => cart.value.reduce((total, item) => total + item.price, 0))
const salePrice = computed(() => Math.round(totalPrice.value * 5) / 100)

const addToCart = (item) => {
    cart.value.push(item)
    item.isAdded = true
}

const removeFromCart = (item) => {
    cart.value.splice(cart.value.indexOf(item), 1)
    item.isAdded = false
}


provide('cart', {
    cart,
    closeDrawer,
    openDrawer,
    addToCart,
    removeFromCart
})

</script>

<template>
    <Drawer
        v-if="drawerOpen"
        :total-price="totalPrice"
        :sale-price="salePrice"
    />
    <div class="w-4/5 m-auto bg-white rounded-3xl mt-10 mb-10" style="box-shadow: 0px 0px 14px 0px #003319; min-height: 90vh;">
        <Header :total-price="totalPrice" @openDrawer="openDrawer"/>
        <div class="p-10">
            <router-view></router-view>
        </div>
    </div>
</template>

<style>
    .router-link-active span {
        color: #003319;
        text-decoration: underline;
    }
</style>

Drawer.vue
<script setup>
import DrawerHead from "@/components/DrawerHead.vue";
import CartItemList from "@/components/CartItemList.vue";
import InfoBlock from "@/components/infoBlock.vue";
import axios from "axios";
import {inject, ref} from "vue";

const props = defineProps({
    totalPrice: Number,
    salePrice: Number,
})
const orderId = ref(null)
const { cart } = inject('cart')
const { closeDrawer } = inject('cart')
const createOrder = async () => {
    try {
        const { data } = await axios.post(`https://34402a52ffa89b7c.mokky.dev/orders`, {
            items: cart.value,
            totalPrice: props.totalPrice - props.salePrice
        })

        cart.value = []
        orderId.value = data.id

    } catch (e) {
        console.error(e)
    }
}

const cleanCart = () => {
    cart.value = []
}

const currentDate = new Date(),
    day = currentDate.getDate(),
    year = currentDate.getFullYear(),
    month = currentDate.getMonth() >= 10 ? currentDate.getMonth() + 1 : `0${currentDate.getMonth() + 1}`


</script>

<template>

    <div>
        <div
            @click="() => closeDrawer()"
            class="fixed top-0 left-0 h-full w-full bg-black z-30 opacity-70"></div>
        <div class="bg-white w-1/2 h-full fixed top-0 right-0 z-30 p-8 flex flex-col">
            <DrawerHead />

            <div v-if="!totalPrice || orderId" class="flex h-full items-center m-auto">
                <InfoBlock
                    v-if="!totalPrice && !orderId"
                    title="Корзина пустая"
                    description="Добавьте хотя бы одну пару кроссовок, чтобы сделать заказ."
                    image-url="./img/package-icon.png"
                />
                <InfoBlock
                    v-if="orderId"
                    title="Заказ оформлен!"
                    :description="`Ваш заказ №${orderId} от ${day}.${month}.${year} скоро будет передан курьерской доставке.`"
                    image-url="./img/order-success-icon.png"
                />
            </div>
            <CartItemList />
            <div v-if="totalPrice" class="flex flex-col gap-5 mt-10">
                <div class="flex gap-2">
                    <span>Итого:</span>
                    <div class="flex-1 border-b border-slate-300 border-dashed"></div>
                    <b>{{ totalPrice }} ₽</b>
                </div>
                <button
                    @click="cleanCart"
                    class="w-full rounded-xl mt-4 py-3 text-white bg-rose-500 transition hover:bg-rose-600 active:bg-rose-700 disabled:bg-slate-400">Очистить корзину</button>
                <button
                    :disabled="totalPrice ? false : true"
                    @click="createOrder"
                    class="w-full rounded-xl mt-0 py-3 text-white bg-lime-500 transition hover:bg-lime-600 active:bg-lime-700 disabled:bg-slate-400">Оформить заказ</button>
            </div>
        </div>
    </div>

</template>

CartItemList.vue
<script setup>
import CartItem from "@/components/CartItem.vue";
import {inject} from "vue";

const { cart, removeFromCart } = inject('cart')
</script>

<template>
    <div
        v-auto-animate
        class="flex flex-col gap-4 flex-1"
        style="overflow-y: auto">
        <CartItem
            :title="item.title"
            :price="item.price"
            :image-url="item.imageUrl"
            v-for="item in cart"
            :key="item.id"
            @onClickRemove="() => removeFromCart(item)"
        />
    </div>
</template>

CartItem.vue
<script setup>
import {computed, ref, watch} from "vue";
    const props = defineProps({
        id: Number,
        title: String,
        price: Number,
        imageUrl: String,
        quantity: Number,
        maxValue: Number,
        finalPrice: Number,
    })

    const quantity = ref(1)
    const maxValue = 50
    const finalPrice = computed(() => (quantity.value * props.price))
    const errorMsg = `Максимальное количество: ${maxValue}`
    const isError = ref(false)

    watch(quantity, (newValue) => {
        if (newValue === '') {
            quantity.value = 1
            isError.value = false
        } else if (newValue >= maxValue) {
            quantity.value = maxValue
            isError.value = true
        }
    })

    const emit = defineEmits(['onClickRemove'])
</script>

<template>
    <div class="flex items-center border border-slate-200 p-4 rounded-xl gap-4">
        <img
            class="w-40 h-40"
            :src="imageUrl"
            :alt="title"
        />
        <div class="flex flex-col w-full">
            <b class="">{{ title }}</b>
            <div class="flex flex-col gap-2 mt-8">
                <div>Стоимость: <b>{{ price }} ₽</b></div>
                <div>Осталось: <b>{{ maxValue }} шт.</b></div>
                <label for="" class="flex gap-4 items-center">
                    Количество:
                    <input
                        v-model="quantity"
                        class="border border-black px-2 py-1 text-black rounded-xl outline-none focus:border-lime-500 font-bold text-center"
                        type="number"
                        min="1"
                        max="100"
                        maxlength="3"
                        placeholder="1"
                    >
                    <span class="text-rose-500 text-xs font-bold" v-if="isError">{{ errorMsg }}</span>
                </label>
                <div>Итог: <b>{{ finalPrice }} ₽</b></div>
                <img
                    width="36"
                    height="36"
                    @click="emit('onClickRemove')"
                    title="Удалить из корзины"
                    class="cursor-pointer absolute transition self-end"
                    src="@/assets/img/close.svg" alt="close">
            </div>
        </div>
    </div>
</template>

Задача такая: при изменении значения инпута в корзине у каждого товара пересчитывать итоговую сумму.
  • Вопрос задан
  • 164 просмотра
Пригласить эксперта
Ответы на вопрос 1
0xD34F
@0xD34F Куратор тега Vue.js
item.isAdded = true

Это как, одни и те же объекты используются и в списке товаров, и в корзине? Можете начинать думать, как будете переделывать, чтобы такой ерунды не было. Ну а пока попробуем заставить ваш говнокод вести себя должным образом:

const quantity = ref(1)

Что, закрыли корзину и потеряли данные о количестве? Пусть количество будет свойством элемента массива, представляющего корзину, соответственно, приходит сверху, и туда же отправляется новое:

const quantity = defineModel('quantity');

В родителе ловим это новое количество и дёргаем метод обновления:

<CartItem
  v-for="n in cart"
  v-bind="n"
  :key="n.id"
  @update:quantity="updateQuantity(n, $event)"
  @remove="removeFromCart(n)"
/>

Откуда этот метод возьмётся? Оттуда же, откуда и методы добавления (этот вам придётся поправить самостоятельно, при добавлении в корзину надо задавать количество по умолчанию, типа единица) и удаления, делаем по аналогии:

const updateQuantity = (item, quantity) => item.quantity = quantity;

Ну и наконец, исправленный расчёт суммы, цену надо умножать на количество:

const totalPrice = computed(() => cart.value.reduce((acc, n) => acc + n.price * n.quantity, 0));
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы