Skip to main content

ทำหน้าจัดการ Profile

ย้ายฝั่งมาทำหน้า Profile บ้างก่อนจะไปหน้าตะกร้าสินค้า (จะได้เสร็จไปส่วนๆไป)

ทำ ProfileView

สิ่งที่เราจะทำ

  • ทำหน้า Profile ที่สามารถกรอก name, email และ profileImage โดย profileImage สามารถเปลี่ยนได้
  • สามารถทำการ save profile ทั้งหมด เอาไว้ได้ใน localstorage และสามารถถึงข้อมูลกลับมาได้ตอน refresh หน้าเว็บ
<script setup>
import { ref, reactive, onMounted } from 'vue'
import UserLayout from '@/layouts/UserLayout.vue'

const userForm = [
{ name: 'Email', field: 'email' },
{ name: 'Name', field: 'name' }
]

const userData = reactive({
imageUrl: 'https://mikelopster.dev/mikelopster.da6b9a03.webp',
email: '',
name: ''
})

onMounted(() => {
const savedUserProfile = localStorage.getItem('user-profile')
if (savedUserProfile) {
const userProfile = JSON.parse(savedUserProfile)
userData.imageUrl = userProfile.imageUrl
userData.email = userProfile.email
userData.name = userProfile.name
}
})

const handleFileChange = (event) => {
const file = event.target.files[0]

if (file) {
const reader = new FileReader()
reader.onload = (e) => {
userData.imageUrl = e.target.result
}
reader.readAsDataURL(file)
}
}

const updateProfile = () => {
localStorage.setItem('user-profile', JSON.stringify(userData))
}
</script>

<template>
<UserLayout>
<div
class="container mx-auto max-w-2xl p-4 bg-base-100 my-4 border border-base-200 shadow-md"
>
<h1 class="text-2xl">Your profile</h1>
<div class="flex flex-col items-center">
<div class="avatar">
<div class="w-24 rounded-full">
<img :src="userData.imageUrl" />
</div>
</div>

<input type="file" @change="handleFileChange">

<div v-for="item in userForm" class="form-control w-full">
<label class="label">
<span class="label-text">{{ item.name }}</span>
<span class="label-text-alt"></span>
</label>
<input
type="text"
placeholder="Type here"
class="input input-bordered w-full"
v-model="userData[item.field]"
/>
</div>

<button class="btn btn-primary w-full mt-4" @click="updateProfile">Update profile</button>
</div>
</div>
</UserLayout>
</template>

ผลลัพธ์ profile-02

เพิ่ม Event success ผ่าน store event.js

  • เพิ่ม Event store สำหรับ update popup message และ state สำหรับการแสดง Alert
  • เราจะใช้ toast ของ DaisyUI ในการแสดงผลออกมาว่า update ข้อมูลเรียบร้อย = สร้าง component Toast.vue ขึ้นมา
  • เรียกใช้งานจาก App.vue โดยใช้ store event.js และ Toast.vue สำหรับแสดงผล

เพิ่ม event.js

import { defineStore } from 'pinia'

export const useEventStore = defineStore('event', {
state: () => ({
alert: false,
data: {}
}),
actions: {
popupMessage (status, message) {
this.data = {
status,
message
}
this.alert = true
setTimeout(() => {
this.clearPopup()
}, 3000)
},
clearPopup () {
this.alert = false
this.data = {}
}
}
})

เพิ่ม components/Toast.vue

<script setup>
import { defineProps } from 'vue'

defineProps({
message: String,
status: String
})
</script>

<template>
<div class="toast z-10">
<div :class="`alert alert-${status}`">
<span>{{ message }} {{ status }}</span>
</div>
</div>
</template>

เพิ่ม Toast ใน App.vue

<script setup>
// เพิ่ม Event store
import { useEventStore } from '@/stores/event'
const eventStore = useEventStore()

// import Toast component
import Toast from '@/components/Toast.vue'
</script>

<template>
<div>
<Toast
v-if="eventStore.alert"
:status="eventStore.data.status"
:message="eventStore.data.message"
>
</Toast>
<RouterView />
</div>
</template>

ผลลัพธ์ profile-01

เพิ่ม Action ที่เข้าได้จาก Navbar UserLayout

  • เพิ่ม link ที่เป็นทางเข้าสำหรับหน้า profile โดยการเพิ่ม RouterLink เข้าไป

ที่ UserLayout

<script setup>
// เพิ่ม RouterLink เข้ามา
import { RouterLink, useRouter } from 'vue-router'
</script>

<template>
<div class="flex-none">
<div v-if="!isLoggedIn" class="btn btn-ghost" @click="login">
Login
</div>
<div v-else class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-ghost btn-circle avatar">
<div class="w-10 rounded-full">
<img src="https://mikelopster.dev/mikelopster.da6b9a03.webp" />
</div>
</label>
<ul tabindex="0" class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52">
<li>
<!-- เพิ่ม link มา -->
<RouterLink to="/profile" class="justify-between">
Profile
</RouterLink>
</li>
<li>
<a @click="logout">Logout</a>
</li>
</ul>
</div>
</div>
</template>

ผลลัพธ์ profile-03