Files
music-admin/pages/login.vue
poptong 7a9a1fcbe8
Some checks failed
🚀 Deploy - Demo / deployment (push) Has been cancelled
로고삭제
2025-10-01 02:32:54 +09:00

185 lines
5.0 KiB
Vue

<script setup lang="ts">
import AuthProvider from "@/views/pages/authentication/AuthProvider.vue";
import logo from "@images/logo.svg?raw";
import authV1BottomShape from "@images/svg/auth-v1-bottom-shape.svg?url";
import authV1TopShape from "@images/svg/auth-v1-top-shape.svg?url";
const form = ref({
user_id: "",
password: "",
remember: false,
});
const isPasswordVisible = ref(false);
const isLoading = ref(false);
const errorMessage = ref("");
const router = useRouter();
// 컴포넌트 마운트 시 테스트
onMounted(() => {});
const handleLogin = async () => {
if (!form.value.user_id || !form.value.password) {
errorMessage.value = "아이디와 비밀번호를 입력해주세요.";
return;
}
isLoading.value = true;
errorMessage.value = "";
try {
const response = await $fetch("/api/auth/login", {
method: "POST",
body: {
user_id: form.value.user_id,
password: form.value.password,
},
});
if (response.success) {
// 토큰을 쿠키에 저장
const token = useCookie("auth-token", {
maxAge: 60 * 60 * 24, // 24시간
secure: true,
sameSite: "strict",
});
token.value = response.token;
// 사용자 정보를 쿠키에 저장
const user = useCookie("user-info", {
maxAge: 60 * 60 * 24,
secure: true,
sameSite: "strict",
});
user.value = JSON.stringify(response.user);
// 대시보드로 리다이렉션
await router.push("/dashboard");
} else {
errorMessage.value = response.message || "로그인에 실패했습니다.";
}
} catch (error: any) {
console.error("Login error:", error);
console.error("Error details:", {
statusCode: error.statusCode,
statusMessage: error.statusMessage,
data: error.data,
});
errorMessage.value =
error.data?.statusMessage ||
error.statusMessage ||
"로그인 중 오류가 발생했습니다.";
} finally {
isLoading.value = false;
}
};
definePageMeta({
layout: "blank",
middleware: "guest",
});
</script>
<template>
<div class="auth-wrapper d-flex align-center justify-center pa-4">
<div class="position-relative my-sm-16">
<!-- 👉 Top shape -->
<VImg
:src="authV1TopShape"
class="text-primary auth-v1-top-shape d-none d-sm-block"
/>
<!-- 👉 Bottom shape -->
<VImg
:src="authV1BottomShape"
class="text-primary auth-v1-bottom-shape d-none d-sm-block"
/>
<!-- 👉 Auth Card -->
<VCard
class="auth-card"
max-width="460"
:class="$vuetify.display.smAndUp ? 'pa-6' : 'pa-0'"
>
<VCardItem class="justify-center">
<NuxtLink to="/" class="app-logo">
<!-- eslint-disable vue/no-v-html -->
<!-- <div
class="d-flex"
v-html="logo"
/>
<h1 class="app-logo-title">
sneat
</h1> -->
</NuxtLink>
</VCardItem>
<VCardText>
<h4 class="text-h4 mb-1">음악 관리 시스템👋🏻</h4>
<p class="mb-0">계정에 로그인하여 관리 시스템을 시작하세요</p>
</VCardText>
<VCardText>
<VForm @submit.prevent="handleLogin">
<VRow>
<!-- 오류 메시지 -->
<VCol v-if="errorMessage" cols="12">
<VAlert
type="error"
variant="tonal"
closable
@click:close="errorMessage = ''"
>
{{ errorMessage }}
</VAlert>
</VCol>
<!-- 아이디 -->
<VCol cols="12">
<VTextField
:id="useId()"
v-model="form.user_id"
autofocus
label="아이디"
placeholder="아이디를 입력하세요"
/>
</VCol>
<!-- 비밀번호 -->
<VCol cols="12">
<VTextField
:id="useId()"
v-model="form.password"
label="비밀번호"
placeholder="············"
:type="isPasswordVisible ? 'text' : 'password'"
autocomplete="password"
:append-inner-icon="isPasswordVisible ? 'bx-hide' : 'bx-show'"
@click:append-inner="isPasswordVisible = !isPasswordVisible"
/>
</VCol>
<VCol cols="12">
<VBtn
block
type="button"
:loading="isLoading"
:disabled="isLoading"
@click="handleLogin"
>
로그인
</VBtn>
</VCol>
</VRow>
</VForm>
</VCardText>
</VCard>
</div>
</div>
</template>
<style lang="scss">
@use "@core/scss/template/pages/page-auth";
</style>