fix : main banner add
SpecialSource Frontend CD / deploy (push) Successful in 15s

This commit is contained in:
2026-05-27 10:43:16 +09:00
parent 3e22853444
commit 083f094ab4
+64 -20
View File
@@ -32,7 +32,26 @@
<main class="content-area"> <main class="content-area">
<transition name="fade" mode="out-in"> <transition name="fade" mode="out-in">
<div v-if="activeMenu === 'main'" id="videos-container" key="main"> <div v-if="activeMenu === 'main'" class="sub-page main-page" key="main">
<div class="ascii-container">
<pre class="ascii-art">
_ _
___ _ __ ___ ___(_) __ _| |___ ___ _ _ _ __ ___ ___
/ __| '_ \ / _ \/ __| |/ _` | / __|/ _ \| | | | '__/ __/ _ \
\__ \ |_) | __/ (__| | (_| | \__ \ (_) | |_| | | | (_| __/
|___/ .__/ \___|\___|_|\__,_|_|___/\___/ \__,_|_| \___\___|
|_|
___ ___ _ __ ___ _ __ __ _ _ __ _ _
/ __/ _ \| '_ ` _ \| '_ \ / _` | '_ \| | | |
| (_| (_) | | | | | | |_) | (_| | | | | |_| |
\___\___/|_| |_| |_| .__/ \__,_|_| |_|\__, |
|_| |___/
</pre>
</div>
</div>
<div v-else-if="activeMenu === 'youtube'" id="videos-container" key="youtube">
<div <div
v-for="(video, index) in videos" v-for="(video, index) in videos"
:key="video.id.videoId" :key="video.id.videoId"
@@ -63,7 +82,7 @@
class="login-input" class="login-input"
placeholder="ENTER ACCESS ID" placeholder="ENTER ACCESS ID"
@keyup.enter="handleLoginSubmit" @keyup.enter="handleLoginSubmit"
> />
</div> </div>
<div class="login-input-group"> <div class="login-input-group">
<label class="login-label">ACCESS KEY_</label> <label class="login-label">ACCESS KEY_</label>
@@ -132,22 +151,22 @@ const videos = ref([]);
const terminalRef = ref(null); const terminalRef = ref(null);
const termInstance = ref(null); const termInstance = ref(null);
// 로그인 폼 양방향 바인딩 변수 // 로그인 폼 초기값 세팅 유지
const loginId = ref('admin'); const loginId = ref('admin');
const loginPw = ref('1234'); const loginPw = ref('1234');
// 메뉴 및 인증 토큰 반응형 변수화
const activeMenu = ref('main'); const activeMenu = ref('main');
const authToken = ref(localStorage.getItem('token')); const authToken = ref(localStorage.getItem('token'));
// 🟢 [NEW] 메뉴 아이템 배열에 YOUTUBE 명시적으로 배치
const menuItems = [ const menuItems = [
{ id: 'main', name: 'HOME' }, { id: 'main', name: 'HOME' },
{ id: 'youtube', name: 'YOUTUBE' },
{ id: 'project', name: 'PROJECT' }, { id: 'project', name: 'PROJECT' },
{ id: 'about', name: 'ABOUT' }, { id: 'about', name: 'ABOUT' },
{ id: 'contact', name: 'CONTACT' } { id: 'contact', name: 'CONTACT' }
]; ];
// 실시간 로그인 여부 체크
const isLoggedIn = computed(() => !!authToken.value); const isLoggedIn = computed(() => !!authToken.value);
const changeMenu = (id) => { const changeMenu = (id) => {
@@ -157,7 +176,6 @@ const changeMenu = (id) => {
} }
}; };
// 📺 유튜브 API 세팅 (8개 출력)
const apiKey = 'AIzaSyD51LHwqoY8spjq6rsY_RlhBXzb96j7D6o'; const apiKey = 'AIzaSyD51LHwqoY8spjq6rsY_RlhBXzb96j7D6o';
const channelId = 'UC8L92ju0V88s6TcmLPb8Bkg'; const channelId = 'UC8L92ju0V88s6TcmLPb8Bkg';
const maxResults = 8; const maxResults = 8;
@@ -173,7 +191,6 @@ const fetchVideos = async () => {
const openVideo = (id) => window.open(`https://www.youtube.com/watch?v=${id}`); const openVideo = (id) => window.open(`https://www.youtube.com/watch?v=${id}`);
const handleRegister = () => alert("회원가입은 현재 관리자 승인제로 운영됩니다."); const handleRegister = () => alert("회원가입은 현재 관리자 승인제로 운영됩니다.");
// 로그아웃 로직 (새로고침 없이 실시간 상태 갱신)
const handleLogout = () => { const handleLogout = () => {
localStorage.removeItem('token'); localStorage.removeItem('token');
authToken.value = null; authToken.value = null;
@@ -183,7 +200,6 @@ const handleLogout = () => {
} }
}; };
// 🔥 [핵심 기능] 보안 로그인 처리 및 터미널 동시 출력 로직
const handleLoginSubmit = async () => { const handleLoginSubmit = async () => {
if (!loginId.value || !loginPw.value) { if (!loginId.value || !loginPw.value) {
alert("아이디와 비밀번호를 모두 입력해 주세요."); alert("아이디와 비밀번호를 모두 입력해 주세요.");
@@ -202,11 +218,9 @@ const handleLoginSubmit = async () => {
const response = await axios.post(`${apiBaseUrl}/api/login`, { const response = await axios.post(`${apiBaseUrl}/api/login`, {
username: loginId.value, username: loginId.value,
password: loginPw.value password: loginPw.value
}, }, {
{
withCredentials: true withCredentials: true
} });
);
if (response.data && response.data.token) { if (response.data && response.data.token) {
localStorage.setItem('token', response.data.token); localStorage.setItem('token', response.data.token);
@@ -278,7 +292,6 @@ onMounted(() => {
.auth-group { display: flex; align-items: center; } .auth-group { display: flex; align-items: center; }
/* 로그인/회원가입 버튼 테마 깔맞춤 고정 */
.nav-item { .nav-item {
color: #00ff00 !important; color: #00ff00 !important;
text-decoration: none; text-decoration: none;
@@ -305,11 +318,41 @@ onMounted(() => {
.content-area { .content-area {
flex: 1; padding: 20px; min-height: 400px; flex: 1; padding: 20px; min-height: 400px;
overflow-y: auto; overflow-y: auto;
display: flex;
flex-direction: column;
} }
.sub-page { max-width: 1000px; margin: 0 auto; padding-top: 15px; } .sub-page { max-width: 1000px; margin: 0 auto; padding-top: 15px; width: 100%; }
.page-title { font-family: monospace; border-bottom: 1px solid #113311; padding-bottom: 10px; margin-bottom: 20px; color: #00ff00;} .page-title { font-family: monospace; border-bottom: 1px solid #113311; padding-bottom: 10px; margin-bottom: 20px; color: #00ff00;}
/* 📺 유튜브 그리드 레이아웃 (4열 8개 무화) */ /* 🟢 [NEW] 메인 홈 아스키 전광판 전용 스타일 클래스 양식 */
.main-page {
display: flex;
justify-content: center;
align-items: center;
flex: 1;
}
.ascii-container {
width: 100%;
overflow-x: auto;
padding: 20px;
box-sizing: border-box;
}
.ascii-art {
font-family: 'Courier New', Courier, monospace !important;
color: #33ff33 !important;
line-height: 1.25;
font-size: 13px;
font-weight: bold;
text-shadow: 0 0 8px rgba(51, 255, 51, 0.8) !important;
background: transparent;
border: none;
margin: 0 auto;
white-space: pre;
display: block;
width: max-content;
}
/* 📺 유튜브 그리드 레이아웃 */
#videos-container { #videos-container {
display: grid; display: grid;
grid-template-columns: repeat(4, 1fr); grid-template-columns: repeat(4, 1fr);
@@ -339,7 +382,7 @@ iframe { width: 100%; height: 180px; border-radius: 4px; }
padding: 2px 5px; box-shadow: 0 0 5px #00ff00; z-index: 10; padding: 2px 5px; box-shadow: 0 0 5px #00ff00; z-index: 10;
} }
/* 🔐 [신규 추가] 컨텐츠 내장형 해커 로그인 박스 디자인 스타일 양식 */ /* 보안 로그인 프레임 */
.login-terminal-frame { .login-terminal-frame {
max-width: 550px; margin: 40px auto; max-width: 550px; margin: 40px auto;
border: 1px dashed #00ff00; padding: 35px; border: 1px dashed #00ff00; padding: 35px;
@@ -363,18 +406,18 @@ iframe { width: 100%; height: 180px; border-radius: 4px; }
} }
.login-submit-btn:hover { background: #00ff00; color: #000; box-shadow: 0 0 15px #00ff00; } .login-submit-btn:hover { background: #00ff00; color: #000; box-shadow: 0 0 15px #00ff00; }
/* 프로젝트 리스트 */ /* 프로젝트 리스트 및 기타 소형 요소 */
.project-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; } .project-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.project-item { border: 1px solid #113311; padding: 15px; background: #080808; font-family: monospace;} .project-item { border: 1px solid #113311; padding: 15px; background: #080808; font-family: monospace;}
.item-header { font-weight: bold; margin-bottom: 10px; color: #00ff00; } .item-header { font-weight: bold; margin-bottom: 10px; color: #00ff00; }
.about-text { font-family: monospace; line-height: 2; color: #00cc00;} .about-text { font-family: monospace; line-height: 2; color: #00cc00;}
.contact-form { font-family: monospace; line-height: 2; color: #00cc00;} .contact-form { font-family: monospace; line-height: 2; color: #00cc00;}
/* 애니메이션 트랜지션 */ /* 애니메이션 */
.fade-enter-active, .fade-leave-active { transition: opacity 0.25s ease; } .fade-enter-active, .fade-leave-active { transition: opacity 0.25s ease; }
.fade-enter-from, .fade-leave-to { opacity: 0; } .fade-enter-from, .fade-leave-to { opacity: 0; }
/* --- 하단 CRT 터미널 영역 --- */ /* 하단 CRT 터미널 */
.crt-monitor-frame { .crt-monitor-frame {
height: 300px; margin: 10px 40px 30px; height: 300px; margin: 10px 40px 30px;
border: 2px solid #1a1a1a; background: #0a0a0a; overflow: hidden; border: 2px solid #1a1a1a; background: #0a0a0a; overflow: hidden;
@@ -394,7 +437,7 @@ iframe { width: 100%; height: 180px; border-radius: 4px; }
text-shadow: 0 0 5px rgba(51, 255, 51, 0.6) !important; text-shadow: 0 0 5px rgba(51, 255, 51, 0.6) !important;
} }
/* 📱 반응형 분기점 */ /* 📱 반응형 처리 */
@media screen and (max-width: 1100px) { @media screen and (max-width: 1100px) {
#videos-container { grid-template-columns: repeat(2, 1fr); } #videos-container { grid-template-columns: repeat(2, 1fr); }
.project-grid { grid-template-columns: repeat(2, 1fr); } .project-grid { grid-template-columns: repeat(2, 1fr); }
@@ -403,6 +446,7 @@ iframe { width: 100%; height: 180px; border-radius: 4px; }
#videos-container { grid-template-columns: 1fr; } #videos-container { grid-template-columns: 1fr; }
.project-grid { grid-template-columns: 1fr; } .project-grid { grid-template-columns: 1fr; }
.auth-nav { flex-direction: column; gap: 12px; padding: 15px 20px; } .auth-nav { flex-direction: column; gap: 12px; padding: 15px 20px; }
.ascii-art { font-size: 8px; /* 모바일 대응 크기 축소 */ }
} }
@keyframes blink { 0%, 100% { opacity: 0.4; } 50% { opacity: 1; } } @keyframes blink { 0%, 100% { opacity: 0.4; } 50% { opacity: 1; } }
</style> </style>