ทำหน้า Product Management
ทำ stores/admin/product.js
สำหรับลองส่งข้อมูลระหว่างหน้า
เราจะสร้าง store สำหรับจัดการ product ออกมาก่อนโดยการสร้างไฟล์ stores/admin/product.js
สิ่งที่จะทำ
- เพิ่ม state list สำหรับเก็บ product ทั้งหมด, loaded สำหรับตัวแปรบอกว่า load ข้อมูลเรียบร้อย
- สร้าง actions ขึ้นมาโดย
loadProduct ()
สำหรับดึงข้อมูล product ทั้งหมด จาก localstorage และ save ใส่ state listgetProduct (index)
ดึงข้อมูล product จาก listaddProduct (productData)
สำหรับ add ข้อมูล product เข้า list และ save ลง localstorageupdateProduct (index, productData)
สำหรับ update ข้อมูล product เข้า list และ save ลง localstorageremoveProduct (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/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 มา
ผลลัพธ์ตอนมาจากส่วนแก้ไข