feat: add adding items to cart

This commit is contained in:
Sergey Krylov 2025-01-27 06:06:33 +03:00
parent f67d2d187e
commit 90677c7ee8
10 changed files with 158 additions and 62 deletions

View File

@ -12,7 +12,8 @@
},
"dependencies": {
"axios": "^1.7.9",
"vue": "^3.5.13"
"vue": "^3.5.13",
"vue3-toastify": "^0.2.8"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.2.1",

View File

@ -1,11 +1,12 @@
<script setup>
import { onMounted, provide, reactive, ref, watch } from 'vue'
import { computed, onMounted, provide, reactive, ref, watch } from 'vue'
import Header from '@/components/Header.vue'
import CardList from '@/components/CardList.vue'
import Drawer from '@/components/Drawer.vue'
import axios from 'axios'
import Spinner from '@/components/Spinner.vue'
import { toast } from 'vue3-toastify'
import 'vue3-toastify/dist/index.css';
const showDrawer = ref(false);
const items = ref([]);
const isLoading = ref(false);
@ -37,20 +38,63 @@ import { onMounted, provide, reactive, ref, watch } from 'vue'
onMounted(fetchItems)
watch(filters, fetchItems)
const addItemToFavorite = async (item) => {
const toggleToFavorite = async (item) => {
try {
item.isFavorite = !item.isFavorite;
await axios.patch(`${import.meta.env.VITE_API_URL}/items/${item.id}`, item);
if (item.isFavorite) {
toast("Добавлено в избранное", {
"type": "success",
"hideProgressBar": true,
"transition": "slide",
"dangerouslyHTMLString": true
})
}
} catch (e) {
console.log('Error', e)
toast("Ошибка при добавлении в избранное", {
"type": "error",
"hideProgressBar": true,
"transition": "slide",
"dangerouslyHTMLString": true
})
}
}
const toggleToCart = async (item) => {
try {
item.isAdded = !item.isAdded;
await axios.patch(`${import.meta.env.VITE_API_URL}/items/${item.id}`, item);
if (item.isAdded) {
toast("Добавлено в корзину", {
"type": "success",
"hideProgressBar": true,
"transition": "slide",
"dangerouslyHTMLString": true
})
}
} catch (e) {
console.log('Error', e)
}
}
provide('openDrawer', () => showDrawer.value = true)
provide('closeDrawer', () => showDrawer.value = false)
const cart = computed(() => items.value.filter(i => i.isAdded));
const totalPrice = computed(() => cart.value.reduce((res, cur) => {
return res + cur.price
}, 0))
provide('cart', {
cart,
toggleToCart,
totalPrice,
openDrawer: () => showDrawer.value = true,
closeDrawer: () => showDrawer.value = false,
})
</script>
<template>
<Drawer v-if="showDrawer" />
<Drawer v-if="showDrawer" :cart="cart" />
<div
class="mt-14 w-4/5 mx-auto bg-white rounded-xl shadow-xl"
@ -85,7 +129,8 @@ import { onMounted, provide, reactive, ref, watch } from 'vue'
<CardList
v-if="items.length > 0"
:items="items"
@addToFavorite="addItemToFavorite"
@toggle-to-favorite="toggleToFavorite"
@toggle-to-cart="toggleToCart"
/>
<h2 v-else>Пусто</h2>
</div>

View File

@ -1,16 +1,13 @@
<script setup>
import Card from './Card.vue'
const onClickAdd = () => {
alert('Добавлено в корзину')
}
defineProps({
items: Array
})
defineEmits([
'addToFavorite',
'toggleToFavorite',
'toggleToCart',
])
</script>
@ -25,8 +22,8 @@
:price="item.price"
:is-added="item.isAdded"
:is-favorite="item.isFavorite"
:on-click-add="onClickAdd"
:on-click-favorite="() => $emit('addToFavorite', item)"
:on-click-add="() => $emit('toggleToCart', item)"
:on-click-favorite="() => $emit('toggleToFavorite', item)"
/>
</div>
</template>

View File

@ -1,13 +1,25 @@
<script setup>
import { decimalFormatter } from '@/utils/helpers.js'
defineProps({
item: Object,
})
defineEmits([
'onRemove'
])
</script>
<template>
<div class="w-full border border-slate-100 p-4 rounded-xl flex items-center gap-4">
<img class="w-16" src="/sneakers/sneakers-1.jpg" alt="Sneaker">
<img class="w-16" :src="item.imageUrl" alt="Sneaker">
<div class="flex flex-col justify-between">
<p>Мужские кроссовки Nike Blazer Mid Suede</p>
<p>{{item.title}}</p>
<div class="flex justify-between mt-2">
<b>12 990 руб.</b>
<img src="/close.svg" alt="Close" class="cursor-pointer transition opacity-40 hover:opacity-100">
<b>{{ decimalFormatter.format(item.price) }} руб.</b>
<img @click="() => $emit('onRemove', item)" src="/close.svg" alt="Close" class="cursor-pointer transition opacity-40 hover:opacity-100">
</div>
</div>
</div>

View File

@ -1,12 +1,19 @@
<script setup lang="ts">
import CartItem from './CartItem.vue'
import {inject} from 'vue';
defineProps({
items: {
type: Array,
required: true,
},
})
const { toggleToCart } = inject('cart')
</script>
<template>
<div class="flex flex-col gap-4">
<CartItem/>
<CartItem/>
<CartItem/>
<CartItem/>
<CartItem v-for="item in items" :key="item.id" :item="item" @on-remove="toggleToCart" />
</div>
</template>

View File

@ -2,8 +2,16 @@
import DrawerHead from './DrawerHead.vue'
import CartItemList from '@/components/CartItemList.vue'
import { inject } from 'vue'
import { decimalFormatter } from '@/utils/helpers.js'
const closeDrawer = inject('closeDrawer')
const { closeDrawer, totalPrice } = inject('cart')
defineProps({
cart: {
type: Array,
required: true
}
})
</script>
<template>
@ -13,19 +21,26 @@
<div @click.stop class="bg-white w-96 h-full fixed top-0 right-0 z-20 p-8">
<DrawerHead/>
<CartItemList />
<div v-if="cart.length > 0">
<CartItemList :items="cart" />
<div class="flex flex-col gap-2 my-6">
<div class="flex justify-between gap-2">
<span>Итого:</span>
<div class="flex-1 border-b border-dashed"></div>
<b>12 990 руб.</b>
<b>{{ decimalFormatter.format(totalPrice) }} руб.</b>
</div>
<div class="flex justify-between gap-2">
<span>Налог 5%:</span>
<div class="flex-1 border-b border-dashed"></div>
<b>900 руб.</b>
<b>{{ decimalFormatter.format(totalPrice * 0.05) }} руб.</b>
</div>
<div class="flex justify-between gap-2">
<span>К оплате:</span>
<div class="flex-1 border-b border-dashed"></div>
<b>{{ decimalFormatter.format(totalPrice * 0.95) }} руб.</b>
</div>
<button class="mt-5 bg-lime-500 w-full rounded-xl py-3 text-white transition disabled:bg-slate-300 hover:bg-lime-600 active:bg-lime-700">
@ -33,4 +48,10 @@
</button>
</div>
</div>
<h2 v-else class="text-lg font-bold text-center mt-10 text-slate-500">
В корзине нет товаров
</h2>
</div>
</template>

View File

@ -1,7 +1,7 @@
<script setup>
import { inject } from 'vue'
const closeDrawer = inject('closeDrawer')
const { closeDrawer } = inject('cart')
</script>
<template>

View File

@ -1,46 +1,43 @@
<script setup>
import { inject } from 'vue'
import { decimalFormatter } from '@/utils/helpers.js'
const openDrawer = inject('openDrawer')
const { openDrawer } = inject('cart')
const {totalPrice} = inject('cart')
</script>
<template>
<header
class="flex justify-between border-b border-slate-100 px-11 py-10"
>
<header class="flex justify-between border-b border-slate-100 px-11 py-10">
<div class="flex items-center gap-4">
<img src="/logo.png" alt="Logo" class="w-10">
<img src="/logo.png" alt="Logo" class="w-10" />
<div>
<h2 class="text-xl font-bold uppercase">
Vue Sneakers
</h2>
<p class="text-slate-500">
Магазин лучших кроссовок
</p>
<h2 class="text-xl font-bold uppercase">Vue Sneakers</h2>
<p class="text-slate-500">Магазин лучших кроссовок</p>
</div>
</div>
<ul class="flex items-center gap-8">
<li @click="openDrawer" class="flex items-center gap-2 text-gray-500 hover:text-black cursor-pointer">
<img src="/cart.svg" alt="Cart">
<b>
1 205 руб.
</b>
<li
@click="openDrawer"
class="flex items-center gap-2 text-gray-500 hover:text-black cursor-pointer"
>
<img src="/cart.svg" alt="Cart" />
<b> {{ decimalFormatter.format(totalPrice)}} руб. </b>
</li>
<li class="flex items-center gap-2 text-gray-500 hover:text-black cursor-pointer">
<img src="/heart.svg" alt="Heart">
<img src="/heart.svg" alt="Heart" />
<span>Закладки</span>
</li>
<li class="flex items-center gap-2 text-gray-500 hover:text-black cursor-pointer">
<img src="/profile.svg" alt="Profile">
<img src="/profile.svg" alt="Profile" />
<span>Профиль</span>
</li>
</ul>
</header>
</template>

View File

@ -1,5 +1,8 @@
import { createApp } from 'vue'
import './assets/main.css'
import App from './App.vue'
import Vue3Toastify from 'vue3-toastify';
createApp(App).mount('#app')
const app = createApp(App);
app.use(Vue3Toastify, { autoClose: 1000 });
app.mount('#app')

View File

@ -1290,6 +1290,7 @@ __metadata:
vite: "npm:^6.0.5"
vite-plugin-vue-devtools: "npm:^7.6.8"
vue: "npm:^3.5.13"
vue3-toastify: "npm:^0.2.8"
languageName: unknown
linkType: soft
@ -4110,6 +4111,18 @@ __metadata:
languageName: node
linkType: hard
"vue3-toastify@npm:^0.2.8":
version: 0.2.8
resolution: "vue3-toastify@npm:0.2.8"
peerDependencies:
vue: ">=3.2.0"
peerDependenciesMeta:
vue:
optional: true
checksum: 10c0/14d7aadd4663cc83dff25da730e6522d1c6735f6427e7d073b1e574a699a9c54d8be9965842a934ab271a127fb3cf850891092e67760773e119caf94c35f67f5
languageName: node
linkType: hard
"vue@npm:^3.5.13":
version: 3.5.13
resolution: "vue@npm:3.5.13"