383 lines
18 KiB
Vue
383 lines
18 KiB
Vue
<template>
|
|
<nav class="layout-navbar container-xxl navbar navbar-expand-xl navbar-detached align-items-center bg-navbar-theme" id="layout-navbar">
|
|
<div class="layout-menu-toggle navbar-nav align-items-xl-center me-4 me-xl-0 d-xl-none">
|
|
<a class="nav-item nav-link px-0 me-xl-6" href="javascript:void(0)">
|
|
<i class="bx bx-menu bx-md"></i>
|
|
</a>
|
|
</div>
|
|
|
|
<!-- 날씨 정보 영역 -->
|
|
<div class="navbar-nav align-items-center">
|
|
<div class="d-flex align-items-center weather-box">
|
|
<img v-if="weather.icon" :src="customIconUrl" :alt="weather.description" :class="customIconClass" />
|
|
<div class="d-flex flex-column">
|
|
<span class="weather-desc">{{ weather.description }}</span>
|
|
<span class="weather-temp" v-if="weatherReady">
|
|
최저 {{ weather.tempMin }}° / 최고 {{ weather.tempMax }}°
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex align-items-center ms-auto" id="navbar-collapse">
|
|
<ul class="navbar-nav flex-row align-items-center ms-auto">
|
|
<select class="form-select py-1 cursor-pointer" id="name" v-model="selectedProject" @change="updateSelectedProject">
|
|
<!-- 내가 참여하고 있는 프로젝트 그룹 -->
|
|
<option v-for="item in myActiveProjects" :key="item.PROJCTSEQ" :value="item.PROJCTSEQ">
|
|
{{ item.PROJCTNAM }}
|
|
</option>
|
|
<!-- 내가 참여하지 않는 프로젝트 그룹 -->
|
|
<option v-if="otherActiveProjects.length > 0" disabled>-----------</option>
|
|
<option v-for="item in otherActiveProjects" :key="item.PROJCTSEQ" :value="item.PROJCTSEQ">
|
|
{{ item.PROJCTNAM }}
|
|
</option>
|
|
</select>
|
|
|
|
<i class="cursor-pointer p-2"></i>
|
|
|
|
<!-- Notification -->
|
|
<li class="nav-item dropdown-notifications navbar-dropdown dropdown me-3 me-xl-2 p-0">
|
|
<a
|
|
class="nav-link dropdown-toggle hide-arrow p-0"
|
|
href="javascript:void(0);"
|
|
data-bs-toggle="dropdown"
|
|
data-bs-auto-close="outside"
|
|
aria-expanded="false"
|
|
>
|
|
<span class="position-relative">
|
|
<i class="bx bx-bell bx-md"></i>
|
|
<span class="badge rounded-pill bg-danger badge-dot badge-notifications border"></span>
|
|
</span>
|
|
</a>
|
|
<ul class="dropdown-menu dropdown-menu-end p-0">
|
|
<li class="dropdown-menu-header border-bottom">
|
|
<div class="dropdown-header d-flex align-items-center py-3">
|
|
<h6 class="mb-0 me-auto">Notification</h6>
|
|
<div class="d-flex align-items-center h6 mb-0">
|
|
<span class="badge bg-label-primary me-2">8 New</span>
|
|
<a
|
|
href="javascript:void(0)"
|
|
class="dropdown-notifications-all p-2"
|
|
data-bs-toggle="tooltip"
|
|
data-bs-placement="top"
|
|
title="Mark all as read"
|
|
><i class="bx bx-envelope-open text-heading"></i
|
|
></a>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
<li class="dropdown-notifications-list scrollable-container">
|
|
<ul class="list-group list-group-flush">
|
|
<li class="list-group-item list-group-item-action dropdown-notifications-item">
|
|
<div class="d-flex">
|
|
<div class="flex-shrink-0 me-3">
|
|
<div class="avatar">
|
|
<img src="/img/avatars/1.png" class="rounded-circle" />
|
|
</div>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<h6 class="small mb-0">Congratulation Lettie 🎉</h6>
|
|
<small class="mb-1 d-block text-body">Won the monthly best seller gold badge</small>
|
|
<small class="text-muted">1h ago</small>
|
|
</div>
|
|
<div class="flex-shrink-0 dropdown-notifications-actions">
|
|
<a href="javascript:void(0)" class="dropdown-notifications-read"
|
|
><span class="badge badge-dot"></span
|
|
></a>
|
|
<a href="javascript:void(0)" class="dropdown-notifications-archive"
|
|
><span class="bx bx-x"></span
|
|
></a>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
<li class="list-group-item list-group-item-action dropdown-notifications-item">
|
|
<div class="d-flex">
|
|
<div class="flex-shrink-0 me-3">
|
|
<div class="avatar">
|
|
<span class="avatar-initial rounded-circle bg-label-danger">CF</span>
|
|
</div>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<h6 class="small mb-0">Charles Franklin</h6>
|
|
<small class="mb-1 d-block text-body">Accepted your connection</small>
|
|
<small class="text-muted">12hr ago</small>
|
|
</div>
|
|
<div class="flex-shrink-0 dropdown-notifications-actions">
|
|
<a href="javascript:void(0)" class="dropdown-notifications-read"
|
|
><span class="badge badge-dot"></span
|
|
></a>
|
|
<a href="javascript:void(0)" class="dropdown-notifications-archive"
|
|
><span class="bx bx-x"></span
|
|
></a>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
<li class="list-group-item list-group-item-action dropdown-notifications-item marked-as-read">
|
|
<div class="d-flex">
|
|
<div class="flex-shrink-0 me-3">
|
|
<div class="avatar">
|
|
<img src="/img/avatars/2.png" class="rounded-circle" />
|
|
</div>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<h6 class="small mb-0">New Message ✉️</h6>
|
|
<small class="mb-1 d-block text-body">You have new message from Natalie</small>
|
|
<small class="text-muted">1h ago</small>
|
|
</div>
|
|
<div class="flex-shrink-0 dropdown-notifications-actions">
|
|
<a href="javascript:void(0)" class="dropdown-notifications-read"
|
|
><span class="badge badge-dot"></span
|
|
></a>
|
|
<a href="javascript:void(0)" class="dropdown-notifications-archive"
|
|
><span class="bx bx-x"></span
|
|
></a>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
<li class="list-group-item list-group-item-action dropdown-notifications-item">
|
|
<div class="d-flex">
|
|
<div class="flex-shrink-0 me-3">
|
|
<div class="avatar">
|
|
<span class="avatar-initial rounded-circle bg-label-success"
|
|
><i class="bx bx-cart"></i
|
|
></span>
|
|
</div>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<h6 class="small mb-0">Whoo! You have new order 🛒</h6>
|
|
<small class="mb-1 d-block text-body">ACME Inc. made new order $1,154</small>
|
|
<small class="text-muted">1 day ago</small>
|
|
</div>
|
|
<div class="flex-shrink-0 dropdown-notifications-actions">
|
|
<a href="javascript:void(0)" class="dropdown-notifications-read"
|
|
><span class="badge badge-dot"></span
|
|
></a>
|
|
<a href="javascript:void(0)" class="dropdown-notifications-archive"
|
|
><span class="bx bx-x"></span
|
|
></a>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
<li class="border-top">
|
|
<div class="d-grid p-4">
|
|
<a class="btn btn-primary btn-sm d-flex" href="javascript:void(0);">
|
|
<small class="align-middle">View all notifications</small>
|
|
</a>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
<!--/ Notification -->
|
|
<!-- User -->
|
|
<li class="nav-item navbar-dropdown dropdown-user dropdown">
|
|
<a class="nav-link dropdown-toggle hide-arrow p-1" href="javascript:void(0);" data-bs-toggle="dropdown">
|
|
<img
|
|
v-if="user"
|
|
:src="`${baseUrl}upload/img/profile/${user.profile}`"
|
|
alt="Profile Image"
|
|
class="w-px-40 h-px-40 rounded-circle border border-3 object-fit-contain"
|
|
:style="`border-color: ${user.usercolor} !important;`"
|
|
@error="$event.target.src = '/img/icons/icon.png'"
|
|
/>
|
|
</a>
|
|
<ul class="dropdown-menu dropdown-menu-end">
|
|
<li>
|
|
<a class="dropdown-item" href="javascript:void(0)" @click="goToMyPage">
|
|
<i class="bx bx-user bx-md me-3"></i><span>My Page</span>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a class="dropdown-item" href="pages-account-settings-account.html">
|
|
<i class="bx bx-cog bx-md me-3"></i><span>Settings</span>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<div class="dropdown-divider my-1"></div>
|
|
</li>
|
|
<li>
|
|
<a class="dropdown-item" @click="handleLogout" target="_blank">
|
|
<i class="bx bx-power-off bx-md me-3"></i><span>Log Out</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
</template>
|
|
<script setup>
|
|
import { useAuthStore } from '@s/useAuthStore';
|
|
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
|
import { useProjectStore } from '@/stores/useProjectStore';
|
|
import { useRouter, useRoute } from 'vue-router';
|
|
import { useThemeStore } from '@s/darkmode';
|
|
import { useWeatherStore } from '@/stores/useWeatherStore';
|
|
import { computed, onMounted, ref, watch } from 'vue';
|
|
import axios from '@api';
|
|
|
|
const baseUrl = import.meta.env.VITE_SERVER;
|
|
|
|
const authStore = useAuthStore();
|
|
const userStore = useUserInfoStore();
|
|
const projectStore = useProjectStore();
|
|
const router = useRouter();
|
|
const route = useRoute();
|
|
const weatherStore = useWeatherStore();
|
|
|
|
const user = ref(null);
|
|
const selectedProject = ref(null);
|
|
const weather = ref({});
|
|
const dailyWeatherList = ref([]);
|
|
|
|
const weatherReady = computed(() => {
|
|
return (
|
|
weather.value &&
|
|
weather.value.tempMin !== null &&
|
|
weather.value.tempMax !== null &&
|
|
!!weather.value.description
|
|
);
|
|
});
|
|
|
|
// 내가 참여하고 있는 진행 중인 프로젝트 목록
|
|
const myActiveProjects = computed(() => {
|
|
return projectStore.activeMemberProjectList || [];
|
|
});
|
|
|
|
// 내가 참여하고 있지 않은 진행 중인 프로젝트 목록
|
|
const otherActiveProjects = computed(() => {
|
|
if (!projectStore.activeProjectList) return [];
|
|
|
|
// 내 프로젝트 ID 목록
|
|
const myProjectIds = myActiveProjects.value.map(p => p.PROJCTSEQ);
|
|
|
|
// 내 프로젝트가 아닌 프로젝트만 필터링
|
|
return projectStore.activeProjectList.filter(p => !myProjectIds.includes(p.PROJCTSEQ));
|
|
});
|
|
|
|
// 프로젝트 선택 변경 시 스토어에 저장
|
|
const updateSelectedProject = () => {
|
|
if (!selectedProject.value) return;
|
|
|
|
// 모든 진행 중인 프로젝트 리스트에서 선택된 프로젝트 찾기
|
|
let selected = projectStore.activeProjectList.find(project => project.PROJCTSEQ === selectedProject.value);
|
|
|
|
if (selected) {
|
|
projectStore.setSelectedProject(selected);
|
|
}
|
|
};
|
|
|
|
// 선택된 프로젝트 변경 감지
|
|
watch(
|
|
() => projectStore.selectedProject,
|
|
newProject => {
|
|
if (newProject) {
|
|
selectedProject.value = newProject.PROJCTSEQ;
|
|
}
|
|
},
|
|
);
|
|
|
|
const customIconUrl = computed(() => {
|
|
if (weather.value.icon === '01d' || weather.value.icon === '01n') {
|
|
return '/img/icons/sunny-custom.png';
|
|
}
|
|
return `https://openweathermap.org/img/wn/${weather.value.icon}@2x.png`;
|
|
});
|
|
|
|
const customIconClass = computed(() => {
|
|
if (weather.value.icon === '01d' || weather.value.icon === '01n') {
|
|
return 'custom-sunny-icon';
|
|
}
|
|
return 'weather-icon';
|
|
});
|
|
|
|
// const { isDarkMode, switchToDarkMode, switchToLightMode } = useThemeStore();
|
|
|
|
const handleLogout = async () => {
|
|
await authStore.logout();
|
|
router.push('/login');
|
|
};
|
|
|
|
const goToMyPage = () => {
|
|
router.push('/mypage');
|
|
};
|
|
|
|
onMounted(async () => {
|
|
// if (isDarkMode) {
|
|
// switchToDarkMode();
|
|
// } else {
|
|
// switchToLightMode();
|
|
// }
|
|
|
|
await userStore.userInfo();
|
|
user.value = userStore.user;
|
|
|
|
await projectStore.loadAllProjectLists();
|
|
|
|
// 사용자가 참여하고 있는 프로젝트 목록
|
|
await projectStore.getMemberProjects();
|
|
|
|
if (myActiveProjects.value.length > 0) {
|
|
const firstProject = myActiveProjects.value[0];
|
|
selectedProject.value = firstProject.PROJCTSEQ;
|
|
projectStore.setSelectedProject(firstProject);
|
|
}
|
|
|
|
// 로그인 페이지가 아닐 때만 날씨 정보를 가져오도록
|
|
if (route.name !== 'login' && route.name !== undefined) {
|
|
// 날씨 정보 갱신
|
|
await weatherStore.getWeatherInfoWithCache();
|
|
weather.value = weatherStore.weather; // 오늘 날씨
|
|
dailyWeatherList.value = weatherStore.dailyWeatherList; // 주간 날씨
|
|
}
|
|
});
|
|
</script>
|
|
<style scoped>
|
|
.weather-icon {
|
|
width: 40%;
|
|
height: 40%;
|
|
}
|
|
.weather-desc {
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
line-height: 1.6;
|
|
}
|
|
.weather-temp {
|
|
font-size: 13px;
|
|
color: #888;
|
|
line-height: 1.2;
|
|
}
|
|
/* .weather-box {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-shrink: 0;
|
|
max-width: 3000px;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
} */
|
|
.custom-sunny-icon {
|
|
width: 50px;
|
|
height: 50px;
|
|
object-fit: contain;
|
|
flex-shrink: 0;
|
|
}
|
|
.weather-box {
|
|
display: flex;
|
|
align-items: center;
|
|
white-space: nowrap;
|
|
gap: 10px;
|
|
min-width: 160px; /* 필요시 */
|
|
}
|
|
@media (max-width: 1200px) {
|
|
.custom-sunny-icon {
|
|
width: 40px;
|
|
}
|
|
}
|
|
@media (max-width: 1100px) {
|
|
.custom-sunny-icon {
|
|
width: 30px;
|
|
}
|
|
}
|
|
</style>
|