250 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			250 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
<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 class="register-link">
 | 
						|
          <p>
 | 
						|
            계정이 없으신가요?
 | 
						|
            <NuxtLink to="/register" class="link-text">회원 가입하기</NuxtLink>
 | 
						|
          </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 = "";
 | 
						|
 | 
						|
  const result = await userStore.login(userId.value, password.value);
 | 
						|
 | 
						|
  if (result.success) {
 | 
						|
    await router.push("/");
 | 
						|
  } else {
 | 
						|
    errorMessage.value = result.error;
 | 
						|
  }
 | 
						|
  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;
 | 
						|
}
 | 
						|
 | 
						|
.register-link {
 | 
						|
  margin-top: 16px;
 | 
						|
  text-align: center;
 | 
						|
  padding: 16px;
 | 
						|
  background: #f8fafc;
 | 
						|
  border-radius: 6px;
 | 
						|
  border: 1px solid #e2e8f0;
 | 
						|
}
 | 
						|
 | 
						|
.register-link p {
 | 
						|
  margin: 0;
 | 
						|
  font-size: 0.9rem;
 | 
						|
  color: #64748b;
 | 
						|
}
 | 
						|
 | 
						|
.link-text {
 | 
						|
  color: #4666e5;
 | 
						|
  text-decoration: none;
 | 
						|
  font-weight: 500;
 | 
						|
}
 | 
						|
 | 
						|
.link-text:hover {
 | 
						|
  text-decoration: underline;
 | 
						|
}
 | 
						|
 | 
						|
@media (max-width: 600px) {
 | 
						|
  .login-card {
 | 
						|
    padding: 24px 8px 20px 8px;
 | 
						|
    min-width: 0;
 | 
						|
    max-width: 98vw;
 | 
						|
  }
 | 
						|
  .login-title {
 | 
						|
    font-size: 1.3rem;
 | 
						|
  }
 | 
						|
}
 | 
						|
</style>
 |