Skip to main content

ทำหน้า Product Management

ทำ stores/admin/product.js สำหรับลองส่งข้อมูลระหว่างหน้า

เราจะสร้าง store สำหรับจัดการ product ออกมาก่อนโดยการสร้างไฟล์ stores/admin/product.js

สิ่งที่จะทำ

  • เพิ่ม state list สำหรับเก็บ product ทั้งหมด, loaded สำหรับตัวแปรบอกว่า load ข้อมูลเรียบร้อย
  • สร้าง actions ขึ้นมาโดย
    • loadProduct () สำหรับดึงข้อมูล product ทั้งหมด จาก localstorage และ save ใส่ state list
    • getProduct (index) ดึงข้อมูล product จาก list
    • addProduct (productData) สำหรับ add ข้อมูล product เข้า list และ save ลง localstorage
    • updateProduct (index, productData) สำหรับ update ข้อมูล product เข้า list และ save ลง localstorage
    • removeProduct (index) สำหรับลบข้อมูล product จาก list และ save กลับ localstorage
import { defineStore } from 'pinia'

export const useProductStore = defineStore('product', {
state: () => ({
list: []
}),
actions: {
loadProduct () {
const productList = localStorage.getItem('product-data')
if (productList) {
this.list = JSON.parse(productList)
}
},
getProduct (index) {
return this.list[index]
},
addProduct (productData) {
productData.remainQuantity = productData.quantity
this.list.push(productData)
// save to localstorage
localStorage.setItem('product-data', JSON.stringify(this.list))
},
updateProduct (index, productData) {
this.list[index].name = productData.name
this.list[index].imageUrl = productData.imageUrl
this.list[index].quantity = productData.quantity
this.list[index].remainQuantity = productData.quantity
this.list[index].status = productData.status
this.list[index].updatedAt = (new Date).toLocaleString()
// save to localstorage
localStorage.setItem('product-data', JSON.stringify(this.list))
},
removeProduct (index) {
this.list.splice(index, 1)
// save to localstorage
localStorage.setItem('product-data', JSON.stringify(this.list))
}
}
})

เพิ่ม router product

เพิ่ม path สำหรับ file product ListView, UpdateView

import AdminProductList from '@/views/admin/product/ListView.vue'
import AdminProductUpdate from '@/views/admin/product/UpdateView.vue'

const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/admin/products',
name: 'admin-products',
component: AdminProductList
},
{
path: '/admin/products/create',
name: 'admin-products-create',
component: AdminProductUpdate
},
{
path: '/admin/products/edit/:id',
name: 'admin-products-update',
component: AdminProductUpdate
},
]
})

ทำ product/ListView.vue

ทำหน้าสำหรับแสดง Product ทั้งหมดออกมาเป็นตาราง

สิ่งที่จะทำ

  • ทำ style ตารางแสดงข้อมูล product ออกมา โดยแสดงข้อมูล ชื่อ product, ภาพ product, ราคา, จำนวน และ สถานะการเปิดปิด พร้อมกับเวลาล่าสุดที่ update
  • เพิ่ม ปุ่มสำหรับไปหน้าเพิ่ม Product
  • เพิ่ม icon สำหรับ Edit (สำหรับไปหน้าแก้ไข Product) และ Delete (สำหรับลบ Product)
  • เพิ่ม action ให้ตอนกด Delete สามารถลบ Product ได้
<script setup>
import { useProductStore } from '@/stores/admin/product'
import { useEventStore } from '@/stores/event'

import { RouterLink } from 'vue-router'

import AdminLayout from '@/layouts/AdminLayout.vue'
import TrashIcon from '@/components/icons/Trash.vue'
import EditIcon from '@/components/icons/Edit.vue'

const productStore = useProductStore()
const eventStore = useEventStore()

const removeProduct = (index) => {
productStore.removeProduct(index)
eventStore.popupMessage('success', 'DELETE Successful!')
}
</script>
<template>
<AdminLayout>
<div class="flex-1 pt-8 px-6 bg-base-100">
<div class="card w-full p-6 mt-2">
<div class="text-xl font-semibold inline-block">
Product
<div class="inline-block float-right">
<div class="inline-block float-right">
<RouterLink
to="/admin/products/create"
class="btn px-6 btn-sm normal-case btn-primary"
>
Add New
</RouterLink>
</div>
</div>
</div>
<div class="divider mt-2"></div>
<div class="h-full w-full pb-6 bg-base-100">
<div class="overflow-x-auto w-full">
<table class="table w-full">
<thead>
<tr>
<th>Name</th>
<th>Image</th>
<th>Price</th>
<th>Quantity</th>
<th>Status</th>
<th>Updated At</th>
<th></th>
</tr>
</thead>
<tbody>
<tr v-for="(product, index) in productStore.list" :key="index">
<td>
<div class="font-bold">{{ product.name }}</div>
</td>
<td>
<div class="mask mask-squircle w-12 h-12">
<img :src="product.imageUrl" />
</div>
</td>
<td>{{ product.price }}</td>
<td>{{ product.remainQuantity }} / {{ product.quantity }}</td>
<td>
<div class="badge" :class="product.status === 'open' ? 'badge-success' : 'badge-error'">
{{ product.status }}
</div>
</td>
<td>{{ product.updatedAt }}</td>
<td>
<RouterLink :to="{ name: 'admin-products-update', params: { id: index } }">
<button class="btn btn-square btn-ghost">
<EditIcon></EditIcon>
</button>
</RouterLink>
<button @click="removeProduct(index)" class="btn btn-square btn-ghost">
<TrashIcon></TrashIcon>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</AdminLayout>
</template>

ผลลัพธ์

product-01

ทำ product/UpdateView.vue

ทำหน้าสำหรับสร้างและแก้ไข Product

สิ่งที่ต้องทำ

  • เพิ่ม style สำหรับหน้าสร้าง Product ขึ้นมาโดยประกอบไปด้วย form
    • ชื่อ product เป็น text
    • ภาพ product เป็น url ภาพ (text)
    • ราคา เป็นตัวเลข (number)
    • จำนวน เป็นตัวเลข (number)
    • สถานะการเปิด / ปิด (text)
  • ตอนกด Add = ให้เกิด action เพิ่ม product
  • โดยถ้ามี parameter id ผ่าน router มา = ให้เปลี่ยนเป็นแก้ไขข้อมูลแทน (edit product) แทน
<script setup>
import { onMounted, ref, reactive, computed } from 'vue'
import { useProductStore } from '@/stores/admin/product'
import { useEventStore } from '@/stores/event'
import { RouterLink, useRoute, useRouter } from 'vue-router'

import AdminLayout from '@/layouts/AdminLayout.vue'

const productStore = useProductStore()
const eventStore = useEventStore()

const route = useRoute()
const router = useRouter()

const productId = ref(-1)
let selectedProduct = reactive({
name: '',
imageUrl: '',
quantity: 0,
about: '',
status: 'open'
})

const mode = computed(() => {
return productId.value !== -1 ? 'Edit' : 'Add'
})

onMounted(() => {
if (route.params.id) {
productId.value = route.params.id
selectedProduct = productStore.getProduct(productId.value)
}
})

const updateProduct = () => {
if (productId.value !== -1) {
// Edit mode
productStore.updateProduct(productId.value, selectedProduct)
eventStore.popupMessage('success', 'Update Product successful!')
} else {
// Create mode
productStore.addProduct(selectedProduct)
eventStore.popupMessage('success', 'Create Product successful!')
router.push({ name: 'admin-products' })
}
}
</script>

<template>
<AdminLayout>
<div class="flex pt-8 px-6">
<div class="card w-full p-6 bg-base-100 shadow-xl mt-2">
<div class="text-xl font-semibold">{{ mode }} Product</div>
<div class="divider mt-2"></div>
<div class="h-full w-full pb-6 bg-base-100">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base-content">Name</span></label>
<input
type="text"
placeholder=""
class="input input-bordered w-full"
v-model="selectedProduct.name"
/>
</div>
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base-content">
Image
</span>
</label><input
type="text"
placeholder=""
class="input input-bordered w-full"
v-model="selectedProduct.imageUrl"
/>
</div>
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base-content">
Price
</span>
</label>
<input
type="number"
placeholder=""
class="input input-bordered w-full"
v-model="selectedProduct.price"
/>
</div>
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base-content">
Quantity
</span>
</label>
<input
type="number"
placeholder=""
class="input input-bordered w-full"
v-model="selectedProduct.quantity"
/>
</div>
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base-content">
About
</span>
</label>
<textarea
class="textarea textarea-bordered w-full"
v-model="selectedProduct.about"
placeholder="detail product">
</textarea>
</div>
</div>
<div class="divider"></div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base-content">
Status
</span>
</label>
<select class="select select-bordered w-full" v-model="selectedProduct.status">
<option disabled selected>Status</option>
<option value="open">Open</option>
<option value="close">Close</option>
</select>
</div>
</div>
<div class="mt-4 flex justify-end">
<RouterLink to="/admin/products" class="btn btn-ghost">
Back
</RouterLink>
<button @click="updateProduct()" class="btn btn-primary ml-4">
{{ mode }}
</button>
</div>
</div>
</div>
</div>
</AdminLayout>
</template>

ผลลัพธ์ตอนเปิด Form มา

product-02

ผลลัพธ์ตอนมาจากส่วนแก้ไข

product-03