Initial commit

This commit is contained in:
2026-05-22 14:01:36 +09:00
commit caae9f50c6
18 changed files with 2111 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
<template>
<div class="login-container">
<div class="login-box">
<h2>SpecialSource 로그인</h2>
<form @submit.prevent="handleLogin">
<input v-model="username" type="text" placeholder="아이디" required />
<input v-model="password" type="password" placeholder="비밀번호" required />
<button type="submit">로그인</button>
</form>
<p v-if="errorMsg" class="error">{{ errorMsg }}</p>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import axios from 'axios'
const username = ref('')
const password = ref('')
const errorMsg = ref('')
const router = useRouter()
const handleLogin = async () => {
try {
// Vite 프록시 설정을 통해 백엔드로 POST 요청을 날립니다.
const response = await axios.post('/api/login', {
username: username.value,
password: password.value
})
// 정상적으로 토큰을 받으면 브라우저에 저장하고 메인으로 이동!
localStorage.setItem('token', response.data.token)
router.push('/')
} catch (error) {
errorMsg.value = error.response?.data || '로그인에 실패했습니다.'
}
}
</script>
<style scoped>
.login-container { display: flex; justify-content: center; align-items: center; height: 100vh; background: #0f172a; }
.login-box { background: white; padding: 40px; border-radius: 8px; width: 350px; text-align: center; }
input { width: 100%; padding: 12px; margin: 10px 0; box-sizing: border-box; border: 1px solid #cbd5e1; border-radius: 4px; }
button { width: 100%; padding: 12px; background: #3b82f6; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; }
button:hover { background: #2563eb; }
.error { color: #ef4444; margin-top: 15px; font-size: 14px; }
</style>
+65
View File
@@ -0,0 +1,65 @@
<template>
<div class="app-layout">
<aside class="sidebar">
<div class="logo">👤 Special Source</div>
<nav class="menu">
<a href="#" class="active">🏠 /메인</a>
<a href="#">📊 서비스 현황</a>
<a href="#"> 시스템 설정</a>
<button @click="handleLogout" class="btn-logout">🔓 로그아웃</button>
</nav>
</aside>
<div class="main-container">
<header class="navbar">
<h2>Flexible UI Dashboard</h2>
<div class="user-info">접속계정: <strong>admin</strong></div>
</header>
<main class="content">
<div class="card-grid">
<div class="card status">🟢 NAS Server Running (Port: 18081)</div>
<div class="card">
<h3>Vite + Vue 3</h3>
<p>초고속 HMR 빌드 엔진 탑재 완료</p>
</div>
<div class="card">
<h3>JWT Authentication</h3>
<p>보안 통신 필터 CSRF 해제 상태</p>
</div>
</div>
</main>
</div>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
const handleLogout = () => {
localStorage.removeItem('token')
router.push('/login')
}
</script>
<style scoped>
/* 📐 Flexbox & Grid 기반 플렉서블 레이아웃 */
.app-layout { display: flex; height: 100vh; width: 100vw; overflow: hidden; font-family: sans-serif; }
.sidebar { width: 240px; background-color: #1e293b; color: white; display: flex; flex-direction: column; padding: 20px; transition: all 0.3s; }
.logo { font-size: 20px; font-weight: bold; margin-bottom: 30px; padding-bottom: 15px; border-bottom: 1px solid #334155; }
.menu { display: flex; flex-direction: column; gap: 10px; height: 100%; }
.menu a { color: #94a3b8; text-decoration: none; padding: 12px; border-radius: 6px; transition: 0.2s; }
.menu a:hover, .menu a.active { color: white; background: #334155; }
.btn-logout { margin-top: auto; padding: 12px; background: #ef4444; color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: bold; }
.main-container { flex: 1; display: flex; flex-direction: column; background-color: #f1f5f9; overflow: hidden; }
.navbar { height: 60px; background: white; display: flex; justify-content: space-between; align-items: center; padding: 0 30px; border-bottom: 1px solid #e2e8f0; }
.content { flex: 1; padding: 30px; overflow-y: auto; }
/* 반응형 카드 그리드 */
.card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; }
.card { background: white; padding: 25px; border-radius: 8px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.05); border: 1px solid #e2e8f0; }
.card.status { grid-column: 1 / -1; background: #bbf7d0; color: #166534; font-weight: bold; }
</style>