feat: initialize gateway frontend with Vue 3, Vite, and Tailwind CSS

- Set up pnpm workspace with necessary dependencies including esbuild and vue-demi.
- Create initial HTML structure for the frontend application.
- Implement main App component with Vue Router integration.
- Add global styles using Tailwind CSS and custom theme variables.
- Define TypeScript environment for Vue components.
- Create a default layout component for consistent page structure.
- Set up Vue Router with a home view route.
- Implement authentication store using Pinia for user management.
- Create utility for tRPC client setup.
- Develop HomeView component with login functionality and server status placeholder.
- Configure TypeScript for Node with composite and strict options.
- Set up Vite configuration for Vue and Tailwind CSS integration.
This commit is contained in:
2026-01-03 10:15:40 +00:00
parent 395d937fd5
commit 821e18cb53
17 changed files with 1111 additions and 44 deletions
+11
View File
@@ -0,0 +1,11 @@
<script setup lang="ts">
import { RouterView } from 'vue-router';
</script>
<template>
<RouterView />
</template>
<style>
/* Global styles if needed */
</style>
+11
View File
@@ -0,0 +1,11 @@
@import "tailwindcss";
@theme {
--color-sammo-dark: #1a1a1a;
--color-sammo-gold: #ffd700;
}
body {
@apply bg-black text-gray-200;
font-family: 'Pretendard', -apple-system, BlinkMacSystemFont, system-ui, Roboto, 'Helvetica Neue', 'Segoe UI', 'Apple SD Gothic Neo', 'Noto Sans KR', 'Malgun Gothic', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', sans-serif;
}
+7
View File
@@ -0,0 +1,7 @@
/// <reference types="vite/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
@@ -0,0 +1,44 @@
<script setup lang="ts">
</script>
<template>
<div class="min-h-screen flex flex-col bg-black text-gray-200">
<!-- Header / Navigation -->
<header class="bg-zinc-900 border-b border-zinc-800 py-2 px-4">
<div class="max-w-6xl mx-auto flex justify-between items-center">
<div class="flex items-center space-x-6">
<h1 class="text-xl font-bold text-white">삼국지 모의전투 HiDCHe</h1>
<nav class="hidden md:flex space-x-4 text-sm">
<a href="#" class="hover:text-white">공지사항</a>
<a href="#" class="hover:text-white">커뮤니티</a>
<a href="#" class="hover:text-white">건의/제안/개발</a>
<a href="#" class="hover:text-white">신고/문의</a>
<a href="#" class="hover:text-white">자주 묻는 질문</a>
<a href="#" class="hover:text-white">패치 내역</a>
<a href="#" class="hover:text-white">Git Repo.</a>
<a href="#" class="hover:text-white">위키</a>
</nav>
</div>
<div class="flex space-x-4 text-sm">
<a href="#" class="hover:text-white">공식 오픈 </a>
<a href="#" class="hover:text-white">잡담 오픈 </a>
</div>
</div>
</header>
<!-- Main Content -->
<main class="flex-grow">
<slot />
</main>
<!-- Footer -->
<footer class="bg-zinc-900 border-t border-zinc-800 py-6 px-4 text-center text-xs text-zinc-500">
<div class="space-x-4 mb-2">
<a href="#" class="hover:text-zinc-300">개인정보처리방침</a>
<a href="#" class="hover:text-zinc-300">이용약관</a>
</div>
<p>© 2023 HideD</p>
<p class="mt-1">크롬, 엣지, 파이어폭스에 최적화되어있습니다.</p>
</footer>
</div>
</template>
+12 -1
View File
@@ -1 +1,12 @@
export {};
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import router from './router';
import './assets/main.css';
const app = createApp(App);
app.use(createPinia());
app.use(router);
app.mount('#app');
+16
View File
@@ -0,0 +1,16 @@
import { createRouter, createWebHistory } from 'vue-router';
import HomeView from '../views/HomeView.vue';
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView,
},
// 추후 추가될 페이지들
],
});
export default router;
+14
View File
@@ -0,0 +1,14 @@
import { defineStore } from 'pinia';
import { ref } from 'vue';
export const useAuthStore = defineStore('auth', () => {
const user = ref(null);
const isLoggedIn = ref(false);
function setUser(userData: any) {
user.value = userData;
isLoggedIn.value = !!userData;
}
return { user, isLoggedIn, setUser };
});
+10
View File
@@ -0,0 +1,10 @@
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from '../../../gateway-api/src/router';
export const trpc = createTRPCProxyClient<AppRouter>({
links: [
httpBatchLink({
url: '/api/trpc', // 실제 환경에 맞게 조정 필요
}),
],
});
+107
View File
@@ -0,0 +1,107 @@
<script setup lang="ts">
import { ref } from 'vue';
import DefaultLayout from '../layouts/DefaultLayout.vue';
const username = ref('');
const password = ref('');
const handleLogin = () => {
console.log('Login attempt:', username.value);
// tRPC 호출 로직이 들어갈 자리
};
const handleJoin = () => {
console.log('Join attempt');
};
</script>
<template>
<DefaultLayout>
<div class="max-w-4xl mx-auto py-12 px-4 flex flex-col items-center space-y-12">
<!-- Logo / Title -->
<div class="text-center">
<h2 class="text-4xl font-serif font-bold text-white tracking-widest">삼국지 모의전투 HiDCHe</h2>
</div>
<!-- Login Box -->
<div class="w-full max-w-md bg-zinc-800 border border-zinc-700 rounded shadow-2xl overflow-hidden">
<div class="bg-zinc-700 px-6 py-2 text-center font-bold text-white border-b border-zinc-600">
로그인
</div>
<div class="p-6 space-y-4">
<div class="flex items-center space-x-4">
<label class="w-20 text-sm font-medium">계정명</label>
<input
v-model="username"
type="text"
class="flex-grow bg-zinc-900 border border-zinc-600 rounded px-3 py-1.5 text-white focus:outline-none focus:border-blue-500"
placeholder="계정명"
/>
</div>
<div class="flex items-center space-x-4">
<label class="w-20 text-sm font-medium">비밀번호</label>
<input
v-model="password"
type="password"
class="flex-grow bg-zinc-900 border border-zinc-600 rounded px-3 py-1.5 text-white focus:outline-none focus:border-blue-500"
placeholder="비밀번호"
/>
</div>
<div class="flex space-x-2 pt-2">
<button
@click="handleJoin"
class="flex-1 bg-yellow-600 hover:bg-yellow-500 text-black font-bold py-2 rounded transition-colors flex items-center justify-center space-x-2"
>
<span>가입 & 로그인</span>
</button>
<button
@click="handleLogin"
class="flex-[2] bg-blue-700 hover:bg-blue-600 text-white font-bold py-2 rounded transition-colors"
>
로그인
</button>
</div>
</div>
</div>
<!-- Server Status Placeholder -->
<div class="w-full max-w-2xl bg-zinc-900 border border-zinc-800 rounded-lg overflow-hidden shadow-xl">
<div class="bg-zinc-800 px-4 py-2 border-b border-zinc-700 flex justify-between items-center">
<span class="font-bold text-sm"> 현황</span>
<span class="text-xs text-zinc-400">西紀 197 7 </span>
</div>
<div class="aspect-video bg-zinc-950 relative flex items-center justify-center">
<!-- Map Placeholder -->
<div class="text-zinc-700 text-lg italic">지도 이미지 현황 데이터 영역</div>
<!-- Example of a city dot if we wanted to mock it -->
<div class="absolute bottom-4 right-4 text-[10px] text-blue-400">도시명 표기 끄기</div>
</div>
<div class="p-4 bg-black text-xs space-y-1 font-mono">
<div class="flex items-start space-x-2">
<span class="text-blue-400"></span>
<span>197 7: [대회] 황제 수장의 명으로 전력전 대회가 개최됩니다! 천하의 영웅들을 모집하고 있습니다!</span>
</div>
<div class="flex items-start space-x-2">
<span class="text-cyan-400"></span>
<span>197 7: [재난] 탐라 호토에 메뚜기 떼가 발생하여 도시가 황폐해지고 있습니다.</span>
</div>
<div class="flex items-start space-x-2">
<span class="text-green-400"></span>
<span>197 7: [자금] 가을이 되어 봉록에 따라 군량이 지급됩니다.</span>
</div>
<div class="flex items-start space-x-2">
<span class="text-red-400"></span>
<span>197 4: [재난] 남피, 무안에 홍수로 인해 피해가 급증하고 있습니다.</span>
</div>
<!-- ... more logs ... -->
<div class="text-zinc-600 pt-2 italic text-center">최근 진행 상황 로그 (Placeholder)</div>
</div>
</div>
</div>
</DefaultLayout>
</template>
<style scoped>
/* Custom styles for the home view */
</style>