Skip to main content

ทำหน้าตะกร้าสินค้า

ทำ store cart.js

  • เพิ่ม key ใน state
    • items สำหรับเก็บข้อมูลในตะกร้า
  • สร้าง function สำหรับจัดการตะกร้าสินค้าโดยเพิ่ม function ดังนี้
    • loadCart () โหลดข้อมูลตะกร้าจาก localstorage (จะวางโหลดเอาไว้ในตำแหน่ง App แรกสุด)
    • addToCart (productData) เพิ่มข้อมูลตะกร้าสินค้า (โดยต้องไม่ใส่สินค้าซ้ำ และหากมีสินค้านั้นอยู่แล้ว เป็นการเพิ่มจำนวนไป 1 แทน)
    • updateQuantity (index, quantity) ปรับจำนวนสินค้า
    • removeItemInCart (index) ลบของออกจากตะกร้า
  • save ลง localstorage เพื่อให้สามารถเรียกใช้งานตอน refresh ได้
  • เพิ่ม getters มา 2 ตัวคือ
    • summaryPrice สำหรับแสดงราคารวมในตะกร้าสินค้า
    • quantity สำหรับแสดงจำนวนสินค้าทั้งหมดในตะกร้า (หาก 1 item มี 2 ชิ้น = 2 items)
import { defineStore } from 'pinia'

export const useUserCartStore = defineStore('user-cart', {
state: () => ({
items: []
}),
getters: {
summaryPrice (state) {
return state.items.reduce((acc, item) => acc + (item.price * item.quantity), 0)
},
quantity (state) {
return state.items.reduce((acc, item) => acc + item.quantity, 0)
}
},
actions: {
loadCart () {
const cartItem = localStorage.getItem('cart-item')
if (cartItem) {
this.items = JSON.parse(cartItem)
}
},
addToCart (productData) {
const itemIndex = this.items.findIndex(
item => item.name === productData.name
)
if (itemIndex >= 0) {
this.updateQuantity(itemIndex, this.items[itemIndex].quantity + 1)
} else {
productData.quantity = 1
this.items.push(productData)
}
localStorage.setItem('cart-item', JSON.stringify(this.items))
},
updateQuantity (index, quantity) {
this.items[index].quantity = parseInt(quantity)
localStorage.setItem('cart-item', JSON.stringify(this.items))
},
removeItemInCart (index) {
this.items.splice(index, 1)
localStorage.setItem('cart-item', JSON.stringify(this.items))
}
}
})

ทำ CartView

  • ทำ style หน้าตะกร้าสินค้า
    • เพิ่ม layout 2 column สำหรับแสดง item ในตะกร้าและสรุปภาพรวม
    • แต่ละแถวเพิ่ม item สำหรับแสดงสินค้าในตะกร้า / dropdown จำนวน และ กากบาทสำหรับลบสินค้าในตะกร้า
  • แสดงข้อมูลตะกร้าสินค้า (โดยจำลองข้อมูลจาก localstorage ก่อน)
  • เพิ่ม function
    • ปรับจำนวน item ในตะกร้าสินค้า (เลือกผ่าน dropdown จำนวน)
    • ลบ item ในตะกร้าสินค้า (กดจาก x ตรง item)
  • แสดงยอดรวมเสมอหลังจากที่มีการปรับตะกร้าสินค้าแล้ว
<script setup>
import UserLayout from '@/layouts/UserLayout.vue'

import RightIcon from '@/components/icons/Right.vue'
import CloseIcon from '@/components/icons/Close.vue'

import { computed } from 'vue'
import { RouterLink, useRouter } from 'vue-router'
import { useUserCartStore } from '@/stores/user/cart'

const userCartStore = useUserCartStore()
const router = useRouter()

const changeQuantity = (event, index) => {
userCartStore.updateQuantity(index, event.target.value)
}

const removeItemInCart = (index) => {
userCartStore.removeItemInCart(index)
if (userCartStore.items.length === 0) {
router.push({ name: 'home' })
}
}
</script>

<template>
<UserLayout>
<div class="container mx-auto my-4">
<h1 class="text-4xl mb-4">Shopping cart</h1>
<div class="flex">
<section class="flex-auto w-64">
<div v-if="userCartStore.items.length === 0" class="px-8 py-32 bg-base-200">
Cart is empty
</div>
<ul v-else class="px-8 bg-base-200">
<li v-for="(item, index) in userCartStore.items" class="flex w-full py-10" :key="index">
<div class="shrink-0">
<img class="w-48" :src="item.imageUrl">
</div>
<div class="flex flex-1 flex-col justify-between pl-4">
<div class="grid grid-cols-2 gap-6 relative">
<div>
<div class="text-xl font-bold">{{ item.name }}</div>
<div>{{ item.about }}</div>
<div>{{ item.price }} ฿</div>
</div>
<div>
<select class="p-1.5" v-model="item.quantity" @change="changeQuantity($event, index)">
<option disabled selected>Quantity</option>
<option v-for="quantity in [1,2,3,4,5]">{{ quantity }}</option>
</select>
<div @click="removeItemInCart(index)" class="absolute top-0 right-0 cursor-pointer">
<CloseIcon class="w-5"></CloseIcon>
</div>
</div>
</div>
<p class="flex">
<RightIcon class="w-5 shrink-0"></RightIcon>
<span>In stock</span>
</p>
</div>
</li>
</ul>
</section>
<section class="flex-auto w-32 bg-slate-200 p-8">
<h2 class="text-2xl">Order summary</h2>
<div class="mt-4 m-0 divide-y divide-base-200">
<div class="flex align-middle justify-between mb-2">
<div class="font-bold">ราคาสินค้าทั้งหมด</div>
<div>{{ userCartStore.summaryPrice }}</div>
</div>
<div class="flex align-middle justify-between mb-2">
<div class="font-bold">ค่าส่ง</div>
<div>0</div>
</div>
<div class="flex align-middle justify-between mb-2">
<div class="font-bold">ราคาทั้งสิ้น</div>
<div>{{ userCartStore.summaryPrice }}</div>
</div>
<RouterLink to="/checkout" class="btn btn-primary w-full">
ชำระเงิน
</RouterLink>
</div>
</section>
</div>
</div>
</UserLayout>
</template>

ลองเทส mock มาหน้าตาจะได้ประมาณนี้

cart-01

เพิ่ม addToCart HomeView และ SearchView

  • ที่ 2 หน้า (HomeView, SearchView) ให้เพิ่ม function addToCart ลงไปยังปุ่ม "Buy Now" และให้ redirect มาหน้าตะกร้า พร้อมกับเพิ่มสินค้านั้นในตะกร้าไป 1 ชิ้น

ที่ HomeView

<script setup>
// import router เข้ามาสำหรับทำ redirect
import { useRouter } from 'vue-router'

// import cart store เข้ามา
import { useUserCartStore } from '@/stores/user/cart'

const userCartStore = useUserCartStore()

const router = useRouter()

// เพิ่ม function addToCart
const addToCart = (productData) => {
userCartStore.addToCart(productData)
router.push({ name: 'cart' })
}
</script>

<template>
<UserLayout>
<!-- code ส่วน hero -->
<ProductList
:products="userProductStore.list"
:addToCart="addToCart"
> <!-- เพิ่ม addToCart -->
</ProductList>
</UserLayout>
</template>

ที่ SearchView

<script setup>
// import router เข้ามาสำหรับทำ redirect
import { useRoute, useRouter } from 'vue-router'

// import cart store เข้ามา
import { useUserCartStore } from '@/stores/user/cart'

const userCartStore = useUserCartStore()

// เพิ่ม function addToCart
const addToCart = (productData) => {
userCartStore.addToCart(productData)
router.push({ name: 'cart' })
}
</script>

<template>
<UserLayout>
<div class="m-10">
<h1 class="text-3xl">Search: <span class="font-bold">{{ searchText }}</span></h1>
</div>
<div v-if="filterProducts.length > 0">
<ProductList
:products="filterProducts"
:addToCart="addToCart"
>
</ProductList> <!-- เพิ่ม addToCart -->
</div>
<div class="m-10" v-else>
<div class="text-center text-3xl">Product not found</div>
</div>
</UserLayout>
</template>

ผลลัพธ์ (หน้า HomeView และ SearchView เหมือนกัน) cart-03

เพิ่มแสดงจำนวนตะกร้าที่ UserLayout

  • ตรงส่วนด้านบนให้ดึงข้อมูลตะกร้า และราคารวมสินค้าของตะกร้ามาแสดงตรงตำแหน่ง Badge ของตะกร้าสินค้า (ดึงจาก CartStore)
<script setup>
import { useUserCartStore } from '@/stores/user/cart'

const userCartStore = useUserCartStore()
</script>
<template>
<!-- code เฉพาะส่วน dropdown ตะกร้า -->
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-ghost btn-circle">
<div class="indicator">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" /></svg>
<span class="badge badge-sm indicator-item">{{ userCartStore.quantity }}</span>
</div>
</label>
<div tabindex="0" class="mt-3 z-[1] card card-compact dropdown-content w-52 bg-base-100 shadow">
<div class="card-body">
<span class="font-bold text-lg">{{ userCartStore.quantity }} Items</span>
<span class="text-info">Subtotal: {{ userCartStore.summaryPrice }} ฿</span>
<div class="card-actions">
<RouterLink to="/cart" class="btn btn-primary btn-block">
View cart
</RouterLink>
</div>
</div>
</div>
</div>
<!-- จบ code -->
</template>

ผลลัพธ์ cart-04