파이널 프로젝트의 전체적인 페이지 디자인을 맡아 진행 했기 때문에 그 첫번째 일로 제일 기준이 되는 네비게이션 바를 만들었다.
[완성본]
팀원들과 회의하면서 구현 된 디자인이 아래와 같으니 이정도면 꽤나 만족스러운 아웃풋이라고 할 수 있다
[App.vue]
app.vue에 NavBar 를 import 시킨다.
개인 캘린더 서비스를 하기 때문에 굳이 모든 이들에게 네비바를 보여줄 필요는 없다. 따라서 로그인 전에는 NavBar를 보여 주지 않게 설정한다.
<template>
<NavBar v-if="loginId != null" />
<RouterView />
</template>
<script>
import NavBar from '@/components/outline/nav.vue'
export default {
name: 'app',
components: { NavBar },
data() {
return {
loginId: null,
}
},
created() {
this.loginId = sessionStorage.getItem('loginId')
console.log(this.loginId);
},
methods: {
}
}
</script>
[nav.vue]
localStorage를 사용하여 네비바를 응용한다
<template>
<keep-alive>
<div>
<nav class="navbar navbar-expand-custom navbar-mainbg">
<a class="navbar-brand navbar-logo" href="/calendar" @click="toCalendar">plan + tiful</a>
<button class="navbar-toggler" type="button" aria-controls="navbarSupportedContent" aria-expanded="false"
aria-label="Toggle navigation" @click="toggleNavigation">
<i class="fas fa-bars text-white"></i>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav ml-auto" id="navBar">
<div class="hori-selector">
<div class="left"></div>
<div class="right"></div>
</div>
<li class="nav-item">
<router-link to="/mypage" class="nav-link" @click="navigateTo('/mypage', $event)">마이페이지</router-link>
</li>
<li class="nav-item active">
<router-link to="/calendar" class="nav-link" @click="navigateTo('/calendar', $event)">캘린더</router-link>
</li>
<li class="nav-item">
<router-link to="/SubscribeBoardList" class="nav-link"
@click="navigateTo('/SubscribeBoardList', $event)">구독</router-link>
</li>
<li class="nav-item">
<router-link to="/chatlist" class="nav-link" @click="navigateTo('/chatlist', $event)">채팅</router-link>
</li>
<li class="nav-item">
<router-link to="/concertlist" class="nav-link" @click="navigateTo('/concertlist', $event)">공모전</router-link>
</li>
<div class="nav-item"
style="display: flex; align-items: center; margin-right: 20px; margin-left: 150px; margin-top: 10px;">
<div style="display: flex;">
<img :src="require('@/assets/image/checklist.png')" style=" margin-right: 10px; width: 40px; height: 40px;"
@click="openMenu($event)" :color="active ? 'primary' : undefined"
v-click-outside="{ handler: closeMenu, include }" />
<div class="nav-bar-profile" style="margin-left: 5px;">
<img :src="'http://localhost:8181/members/plantiful/' + loginId" @error="replaceImg" @click="mypage"
style="width: 40px; border-radius: 50%; height: 40px;" />
</div>
<div @click="logout">
<img :src="require('@/assets/image/logout.png')"
style="margin-left: 10px; margin-right: 5px; width: 40px; height: 40px;" />
</div>
</div>
</div>
</ul>
</div>
</nav>
</div>
</keep-alive>
</template>
<script>
export default {
data() {
return {
loginId: null,
active: false,
activeLink: '',
}
},
beforeRouteLeave(to, from, next) {
this.activeLink = to.path;
next();
},
created() {
this.activeLink = this.$route.path;
let token = sessionStorage.getItem('token');
this.loginId = sessionStorage.getItem('loginId');
},
methods: {
mypage() {
location.href = "/mypage"
localStorage.setItem("activeTab", '/mypage')
},
navigateTo(route, event) {
$(".active").removeClass("active");
$(event).addClass("active");
localStorage.setItem("activeTab", route)
this.activeLink = route;
this.$router.push(route);
},
toggleNavigation() {
const navbarCollapse = document.getElementById('navbarSupportedContent');
navbarCollapse.classList.toggle('show');
},
replaceImg(e) {
e.target.src = require('@/assets/image/profile-user.png');
},
logout() {
sessionStorage.removeItem('token');
sessionStorage.removeItem('loginId');
localStorage.setItem('activeTab', '/calendar')
location.href = '/login';
},
openMenu(value) {
if (typeof value === 'boolean') {
this.active = value
} else {
this.active = !this.active
let div = document.getElementById("checkList")
div.style.top = "0px"
div.style.top = 150 + "px"
}
},
include() {
return [document.querySelector('.included')]
},
closeMenu() {
this.active = false
},
initializeNavbar() {
var activeTab = localStorage.getItem("activeTab")
let tags = document.getElementsByClassName("nav-item")
for (var i = 0; i < tags.length; i++) {
var href = tags[i].firstChild.getAttribute("href")
if (href == activeTab) {
tags[i].classList.add("active")
} else {
tags[i].classList.remove("active")
}
}
var tabsNewAnim = $('#navbarSupportedContent');
var activeItemNewAnim = tabsNewAnim.find('.active');
var activeWidthNewAnimHeight = activeItemNewAnim.innerHeight();
var activeWidthNewAnimWidth = activeItemNewAnim.innerWidth();
var itemPosNewAnimTop = activeItemNewAnim.position();
var itemPosNewAnimLeft = activeItemNewAnim.position();
$(".hori-selector").css({
"top": itemPosNewAnimTop.top + "px",
"left": itemPosNewAnimLeft.left + "px",
"height": activeWidthNewAnimHeight + "px",
"width": activeWidthNewAnimWidth + "px"
});
$("#navbarSupportedContent").on("click", "li", function (e) {
$('#navbarSupportedContent ul li').removeClass("active");
$(this).addClass('active');
var activeWidthNewAnimHeight = $(this).innerHeight();
var activeWidthNewAnimWidth = $(this).innerWidth();
var itemPosNewAnimTop = $(this).position();
var itemPosNewAnimLeft = $(this).position();
$(".hori-selector").css({
"top": itemPosNewAnimTop.top + "px",
"left": itemPosNewAnimLeft.left + "px",
"height": activeWidthNewAnimHeight + "px",
"width": activeWidthNewAnimWidth + "px"
});
});
},
toCalendar() {
localStorage.setItem("activeTab", "/calendar")
}
},
mounted() {
this.initializeNavbar();
},
};
</script>
<style scoped>
* {
margin: 0;
padding: 0;
}
.navbar-logo {
padding: 15px;
color: #fff;
font-family: 'TheJamsil5Bold';
font-weight: 300;
font-size: 25px;
}
.navbar-logo:hover {
color: #ffe77a;
}
.navbar-mainbg {
background-color: #7AC6FF;
padding: 0px;
}
#navbarSupportedContent {
overflow: hidden;
position: relative;
}
#navbarSupportedContent ul {
padding: 0px;
margin: 0px;
}
#navbarSupportedContent ul li a i {
margin-right: 10px;
}
#navbarSupportedContent li {
list-style-type: none;
float: left;
}
#navbarSupportedContent ul li a {
font-family: 'Pretendard-Regular';
font-weight: 400;
color: white;
text-decoration: none;
font-size: 18px;
display: block;
padding: 20px 20px;
transition-duration: 0.6s;
transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);
position: relative;
}
#navbarSupportedContent>ul>li.active>a {
color: #7AC6FF;
background-color: transparent;
transition: all 0.7s;
}
#navbarSupportedContent a:not(:only-child):after {
content: "";
position: absolute;
right: 20px;
top: 10px;
font-size: 14px;
font-family: "Font Awesome 5 Free";
display: inline-block;
padding-right: 3px;
vertical-align: middle;
font-weight: 900;
transition: 0.5s;
}
#navbarSupportedContent .active>a:not(:only-child):after {
transform: rotate(90deg);
}
.hori-selector {
display: inline-block;
position: absolute;
height: 100%;
top: 0px;
left: 0px;
transition-duration: 0.6s;
transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);
background-color: #fff;
border-top-left-radius: 15px;
border-top-right-radius: 15px;
margin-top: 10px;
}
.hori-selector .right,
.hori-selector .left {
position: absolute;
width: 25px;
height: 25px;
background-color: #fff;
bottom: 10px;
}
.hori-selector .right {
right: -25px;
}
.hori-selector .left {
left: -25px;
}
.hori-selector .right:before,
.hori-selector .left:before {
content: '';
position: absolute;
width: 50px;
height: 40px;
border-radius: 30%;
background-color: #7AC6FF;
}
.hori-selector .right:before {
bottom: 0;
right: -25px;
}
.hori-selector .left:before {
bottom: 0;
left: -25px;
}
@media (min-width: 992px) {
.navbar-expand-custom {
-ms-flex-flow: row nowrap;
flex-flow: row nowrap;
-ms-flex-pack: start;
justify-content: flex-start;
}
.navbar-expand-custom .navbar-nav {
-ms-flex-direction: row;
flex-direction: row;
}
.navbar-expand-custom .navbar-toggler {
display: none;
}
.navbar-expand-custom .navbar-collapse {
display: -ms-flexbox !important;
display: flex !important;
-ms-flex-preferred-size: auto;
flex-basis: auto;
}
}
@import "https://pro.fontawesome.com/releases/v5.10.0/css/all.css";
@import 'https://fonts.googleapis.com/css2?family=Itim&display=swap';
#checkList {
font-family: 'Pretendard-Regular';
font-weight: 400;
border: 4px solid #7AC6FF;
border-radius: 45px;
max-width: 100%;
width: 327px;
height: auto;
left: 75.3%;
margin-top: -5%;
overflow: hidden;
text-align: center;
background-color: white;
white-space: pre;
position: absolute;
opacity: 1;
z-index: 999;
}
input {
border-style: groove;
width: 100px;
}
button {
border-style: groove;
}
.shadow {
box-shadow: 5px 10px 10px rgba(0, 0, 0, 0.03);
}
@media (max-width: 991px) {
#navbarSupportedContent ul li a {
padding: 12px 30px;
}
.hori-selector {
margin-top: 0px;
margin-left: 10px;
border-radius: 0;
border-top-left-radius: 25px;
border-bottom-left-radius: 25px;
}
.hori-selector .left,
.hori-selector .right {
right: 10px;
}
.hori-selector .left {
top: -25px;
left: auto;
}
.hori-selector .right {
bottom: -25px;
}
.hori-selector .left:before {
left: -25px;
top: -25px;
}
.hori-selector .right:before {
bottom: -25px;
left: -25px;
}
}
</style>
'프로젝트 > Plan + tiful (플랜티플)' 카테고리의 다른 글
[Rest Api] 아임포트를 이용한 KG이니시스 결제 구현하기 (feat. vue.js) - (2) (0) | 2023.07.16 |
---|---|
[Rest Api] 아임포트를 이용한 KG이니시스 결제 구현하기 (feat. vue.js) - (1) (0) | 2023.07.15 |
[Rest Api] 카카오톡 로그인 api 구현 (7) - java 최종 코드 (0) | 2023.07.11 |
[Rest Api] 카카오톡 로그인 api 구현 (6) - vue.js 최종 코드 (0) | 2023.07.09 |
[Rest Api] vue와 Spring boot를 이용한 카카오톡 로그인 api 구현 (5) - 가져온 사용자 정보를 DB에 저장하기 (0) | 2023.07.07 |