init source
This commit is contained in:
223
pages/login.vue
Normal file
223
pages/login.vue
Normal file
@@ -0,0 +1,223 @@
|
||||
<template>
|
||||
<div class="login-bg">
|
||||
<div class="login-card">
|
||||
<h1 class="login-title">Integrated Bio Foundry Platform</h1>
|
||||
<div class="login-form">
|
||||
<h2 class="login-signin">Sign In</h2>
|
||||
|
||||
<!-- 에러 메시지 -->
|
||||
<div v-if="errorMessage" class="error-message">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
|
||||
<label class="login-label" for="userId">ID</label>
|
||||
<input
|
||||
id="userId"
|
||||
v-model="userId"
|
||||
class="login-input"
|
||||
type="text"
|
||||
placeholder="아이디를 입력하세요"
|
||||
:disabled="isLoading"
|
||||
/>
|
||||
<label class="login-label" for="password">Password</label>
|
||||
<input
|
||||
id="password"
|
||||
v-model="password"
|
||||
class="login-input"
|
||||
type="password"
|
||||
placeholder="비밀번호를 입력하세요"
|
||||
:disabled="isLoading"
|
||||
@keyup.enter="signIn"
|
||||
/>
|
||||
<button class="login-btn" :disabled="isLoading" @click="signIn">
|
||||
<span v-if="isLoading">로그인 중...</span>
|
||||
<span v-else>SIGN IN</span>
|
||||
</button>
|
||||
|
||||
<!-- 테스트 계정 안내 -->
|
||||
<div class="test-accounts">
|
||||
<p class="test-title">테스트 계정:</p>
|
||||
<p class="test-account">관리자: admin / stam1201!</p>
|
||||
<p class="test-account">일반 사용자: user / stam1201!</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useUserStore } from "~/stores/user";
|
||||
|
||||
// auth 레이아웃 사용
|
||||
definePageMeta({
|
||||
layout: "auth",
|
||||
});
|
||||
|
||||
const userId = ref("");
|
||||
const password = ref("");
|
||||
const errorMessage = ref("");
|
||||
const isLoading = ref(false);
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 이미 로그인된 경우 홈으로 리다이렉션
|
||||
onMounted(() => {
|
||||
if (userStore.isLoggedIn) {
|
||||
router.push("/");
|
||||
}
|
||||
});
|
||||
|
||||
async function signIn() {
|
||||
if (!userId.value || !password.value) {
|
||||
errorMessage.value = "아이디와 비밀번호를 입력해주세요.";
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading.value = true;
|
||||
errorMessage.value = "";
|
||||
|
||||
try {
|
||||
const result = await userStore.login(userId.value, password.value);
|
||||
|
||||
if (result.success) {
|
||||
// 로그인 성공 시 홈으로 이동
|
||||
await router.push("/");
|
||||
} else {
|
||||
errorMessage.value = result.error || "로그인에 실패했습니다.";
|
||||
}
|
||||
} catch (error) {
|
||||
errorMessage.value = "로그인 중 오류가 발생했습니다.";
|
||||
console.error("로그인 오류:", error);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.login-bg {
|
||||
width: 100vw;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f8f9fb;
|
||||
padding: 0;
|
||||
}
|
||||
.login-card {
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 16px 40px 0 rgba(44, 62, 80, 0.08);
|
||||
padding: 40px 36px 32px 36px;
|
||||
min-width: 500px;
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.login-title {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 24px;
|
||||
color: #23272f;
|
||||
font-family: "Montserrat", "Pretendard", sans-serif;
|
||||
}
|
||||
.login-form {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.login-signin {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 16px;
|
||||
color: #23272f;
|
||||
}
|
||||
.login-label {
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 4px;
|
||||
color: #6b7280;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.login-input {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 6px;
|
||||
background: #f1f5fb;
|
||||
margin-bottom: 4px;
|
||||
font-size: 1rem;
|
||||
outline: none;
|
||||
transition: border 0.2s;
|
||||
}
|
||||
.login-input:focus {
|
||||
border: 1.5px solid #4666e5;
|
||||
}
|
||||
.login-input:disabled {
|
||||
background: #f3f4f6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.login-btn {
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
padding: 10px 0;
|
||||
background: #4666e5;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.login-btn:hover:not(:disabled) {
|
||||
background: #3451b2;
|
||||
}
|
||||
.login-btn:disabled {
|
||||
background: #9ca3af;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.error-message {
|
||||
background: #fef2f2;
|
||||
border: 1px solid #fecaca;
|
||||
color: #dc2626;
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.test-accounts {
|
||||
margin-top: 24px;
|
||||
padding: 16px;
|
||||
background: #f8fafc;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
.test-title {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
color: #475569;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
.test-account {
|
||||
font-size: 0.8rem;
|
||||
color: #64748b;
|
||||
margin: 4px 0;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.login-card {
|
||||
padding: 24px 8px 20px 8px;
|
||||
min-width: 0;
|
||||
max-width: 98vw;
|
||||
}
|
||||
.login-title {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user