Merge branch 'main' of http://192.168.0.251:3000/localhost/localhost-front
This commit is contained in:
commit
a4ee364aad
@ -22,7 +22,7 @@
|
|||||||
border-radius: 2px !important;
|
border-radius: 2px !important;
|
||||||
font-size: 0px !important;
|
font-size: 0px !important;
|
||||||
}
|
}
|
||||||
/* 연차 그래프 (풀풀) */
|
/* 연차 그래프 (풀) */
|
||||||
.fc-daygrid-event.full-day {
|
.fc-daygrid-event.full-day {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
height: 8px !important;
|
height: 8px !important;
|
||||||
@ -44,7 +44,7 @@
|
|||||||
.fc-daygrid-day-number {
|
.fc-daygrid-day-number {
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
/* 데이트피커 뾰족없게게 */
|
/* 데이트피커 뾰족없게 */
|
||||||
.flatpickr-calendar:before,
|
.flatpickr-calendar:before,
|
||||||
.flatpickr-calendar:after {
|
.flatpickr-calendar:after {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
@ -65,12 +65,10 @@ background-color: rgba(0, 0, 0, 0.05); /* 연한 배경 효과 */
|
|||||||
/* 주말 (토요일, 일요일) 및 공휴일 */
|
/* 주말 (토요일, 일요일) 및 공휴일 */
|
||||||
.fc-day-sat-sun {
|
.fc-day-sat-sun {
|
||||||
cursor: not-allowed !important;
|
cursor: not-allowed !important;
|
||||||
opacity: 0.6; /* 흐려 보이게 */
|
|
||||||
}
|
}
|
||||||
/* 과거 날짜 (오늘 이전) */
|
/* 과거 날짜 (오늘 이전) */
|
||||||
.fc-daygrid-day.past {
|
.fc-daygrid-day.past {
|
||||||
cursor: not-allowed !important;
|
cursor: not-allowed !important;
|
||||||
opacity: 0.6; /* 흐려 보이게 */
|
|
||||||
}
|
}
|
||||||
/* 기본 이벤트 스타일 */
|
/* 기본 이벤트 스타일 */
|
||||||
.fc-daygrid-event {
|
.fc-daygrid-event {
|
||||||
|
|||||||
BIN
public/img/icons/loading.png
Normal file
BIN
public/img/icons/loading.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<component :is="layout">
|
<component :is="layout">
|
||||||
<template #content>
|
<template #content>
|
||||||
|
<LoadingSpinner :isLoading="loadingStore.isLoading" />
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</template>
|
</template>
|
||||||
</component>
|
</component>
|
||||||
@ -12,7 +13,10 @@ import { useRoute } from 'vue-router';
|
|||||||
import NormalLayout from './layouts/NormalLayout.vue';
|
import NormalLayout from './layouts/NormalLayout.vue';
|
||||||
import NoLayout from './layouts/NoLayout.vue';
|
import NoLayout from './layouts/NoLayout.vue';
|
||||||
import ToastModal from '@c/modal/ToastModal.vue';
|
import ToastModal from '@c/modal/ToastModal.vue';
|
||||||
|
import { useLoadingStore } from "@s/loadingStore";
|
||||||
|
import LoadingSpinner from "@v/LoadingPage.vue";
|
||||||
|
|
||||||
|
const loadingStore = useLoadingStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
const layout = computed(() => {
|
const layout = computed(() => {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useToastStore } from '@s/toastStore';
|
import { useToastStore } from '@s/toastStore';
|
||||||
|
import { useLoadingStore } from "@s/loadingStore";
|
||||||
|
|
||||||
const $api = axios.create({
|
const $api = axios.create({
|
||||||
baseURL: import.meta.env.VITE_API_URL,
|
baseURL: import.meta.env.VITE_API_URL,
|
||||||
@ -14,6 +15,9 @@ const $api = axios.create({
|
|||||||
*/
|
*/
|
||||||
$api.interceptors.request.use(
|
$api.interceptors.request.use(
|
||||||
function (config) {
|
function (config) {
|
||||||
|
const loadingStore = useLoadingStore();
|
||||||
|
loadingStore.startLoading();
|
||||||
|
|
||||||
let contentType = 'application/json';
|
let contentType = 'application/json';
|
||||||
|
|
||||||
if (config.isFormData) contentType = 'multipart/form-data';
|
if (config.isFormData) contentType = 'multipart/form-data';
|
||||||
@ -24,6 +28,8 @@ $api.interceptors.request.use(
|
|||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
function (error) {
|
function (error) {
|
||||||
|
const loadingStore = useLoadingStore();
|
||||||
|
loadingStore.stopLoading();
|
||||||
// 요청 오류가 있는 작업 수행
|
// 요청 오류가 있는 작업 수행
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
},
|
},
|
||||||
@ -32,10 +38,15 @@ $api.interceptors.request.use(
|
|||||||
// 응답 인터셉터 추가하기
|
// 응답 인터셉터 추가하기
|
||||||
$api.interceptors.response.use(
|
$api.interceptors.response.use(
|
||||||
function (response) {
|
function (response) {
|
||||||
|
const loadingStore = useLoadingStore();
|
||||||
|
loadingStore.stopLoading();
|
||||||
// 2xx 범위의 응답 처리
|
// 2xx 범위의 응답 처리
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
function (error) {
|
function (error) {
|
||||||
|
const loadingStore = useLoadingStore();
|
||||||
|
loadingStore.stopLoading();
|
||||||
|
|
||||||
const toastStore = useToastStore();
|
const toastStore = useToastStore();
|
||||||
// 오류 응답 처리
|
// 오류 응답 처리
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
|
|||||||
@ -63,8 +63,8 @@
|
|||||||
margin-left: 260px;
|
margin-left: 260px;
|
||||||
width: auto !important;
|
width: auto !important;
|
||||||
min-width: auto !important;
|
min-width: auto !important;
|
||||||
right: 24px !important;
|
right: 26px !important;
|
||||||
left: 24px !important;
|
left: 26px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 탑바 범위조정(1200px 이하) */
|
/* 탑바 범위조정(1200px 이하) */
|
||||||
@ -75,8 +75,8 @@
|
|||||||
margin-left: 0px;
|
margin-left: 0px;
|
||||||
width: auto !important;
|
width: auto !important;
|
||||||
min-width: auto !important;
|
min-width: auto !important;
|
||||||
right: 24px !important;
|
right: 26px !important;
|
||||||
left: 24px !important;
|
left: 26px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,8 +88,8 @@
|
|||||||
margin-left: 0px;
|
margin-left: 0px;
|
||||||
width: auto !important;
|
width: auto !important;
|
||||||
min-width: auto !important;
|
min-width: auto !important;
|
||||||
right: 24px !important;
|
right: 26px !important;
|
||||||
left: 24px !important;
|
left: 26px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
22
src/stores/loadingStore.js
Normal file
22
src/stores/loadingStore.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
|
||||||
|
export const useLoadingStore = defineStore("loading", () => {
|
||||||
|
const loadingCount = ref(0); // 요청 개수를 추적
|
||||||
|
|
||||||
|
const startLoading = () => {
|
||||||
|
loadingCount.value++;
|
||||||
|
};
|
||||||
|
|
||||||
|
const stopLoading = () => {
|
||||||
|
if (loadingCount.value > 0) {
|
||||||
|
setTimeout(() => {
|
||||||
|
loadingCount.value--;
|
||||||
|
}, 200); // 약간의 지연을 추가하여 응답이 동시에 도착해도 안정적으로 감소
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isLoading = computed(() => loadingCount.value > 0); // 하나라도 요청이 있으면 로딩 활성화
|
||||||
|
|
||||||
|
return { isLoading, startLoading, stopLoading };
|
||||||
|
});
|
||||||
@ -1,30 +1,58 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="loading-container">
|
<div v-if="isLoading" class="loading-overlay">
|
||||||
<div class="spinner">
|
<div class="loading-container">
|
||||||
🐰
|
<div class="spinner">
|
||||||
|
<img src="/img/icons/loading.png" class="loading-img" />
|
||||||
|
</div>
|
||||||
|
<p class="loading-text">LOADING...</p>
|
||||||
</div>
|
</div>
|
||||||
<p class="loading-text">잠시만 기다려 주세요...</p>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { defineProps } from "vue";
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
isLoading: Boolean, // 부모 컴포넌트에서 전달받는 로딩 상태
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
/* 회색 배경으로 클릭 방지 */
|
||||||
|
.loading-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.4); /* 회색 반투명 */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 9999; /* 가장 위에 위치 */
|
||||||
|
pointer-events: auto; /* 모든 클릭 방지 */
|
||||||
|
}
|
||||||
|
|
||||||
/* 로딩 컨테이너 */
|
/* 로딩 컨테이너 */
|
||||||
.loading-container {
|
.loading-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: 100vh;
|
padding: 20px;
|
||||||
background-color: #f9f9f9;
|
background: none;
|
||||||
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loading-img {
|
||||||
|
width: 80px; /* 원하는 크기로 조정 */
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
/* 빙글빙글 돌아가는 스피너 */
|
/* 빙글빙글 돌아가는 스피너 */
|
||||||
.spinner {
|
.spinner {
|
||||||
font-size: 50px;
|
font-size: 50px;
|
||||||
animation: spin 1.2s linear infinite;
|
animation: spin 2.2s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 로딩 텍스트 */
|
/* 로딩 텍스트 */
|
||||||
@ -32,8 +60,7 @@
|
|||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #555;
|
color: #ffffff;
|
||||||
font-family: "Comic Sans MS", "Arial", sans-serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 회전 애니메이션 */
|
/* 회전 애니메이션 */
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container-xxl flex-grow-1 container-p-y">
|
<div class="container-xxl flex-grow-1 container-p-y">
|
||||||
<LoadingSpinner v-if="isLoading" />
|
<div class="row">
|
||||||
<div v-else class="row">
|
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<!-- 프로필 헤더 -->
|
<!-- 프로필 헤더 -->
|
||||||
@ -137,8 +136,6 @@
|
|||||||
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
||||||
import { useToastStore } from '@s/toastStore';
|
import { useToastStore } from '@s/toastStore';
|
||||||
import axios from '@api';
|
import axios from '@api';
|
||||||
import LoadingSpinner from '@v/LoadingPage.vue';
|
|
||||||
const isLoading = ref(true);
|
|
||||||
// 게시물 데이터 상태
|
// 게시물 데이터 상태
|
||||||
const profileName = ref('');
|
const profileName = ref('');
|
||||||
const boardTitle = ref('제목 없음');
|
const boardTitle = ref('제목 없음');
|
||||||
@ -236,7 +233,6 @@
|
|||||||
// 게시물 상세 데이터 불러오기
|
// 게시물 상세 데이터 불러오기
|
||||||
const fetchBoardDetails = async () => {
|
const fetchBoardDetails = async () => {
|
||||||
try {
|
try {
|
||||||
isLoading.value = true;
|
|
||||||
const response = await axios.get(`board/${currentBoardId.value}`);
|
const response = await axios.get(`board/${currentBoardId.value}`);
|
||||||
const data = response.data.data;
|
const data = response.data.data;
|
||||||
|
|
||||||
@ -254,8 +250,6 @@
|
|||||||
attachments.value = data.attachments || [];
|
attachments.value = data.attachments || [];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert('게시물 데이터를 불러오는 중 오류가 발생했습니다.');
|
alert('게시물 데이터를 불러오는 중 오류가 발생했습니다.');
|
||||||
} finally {
|
|
||||||
isLoading.value = false;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container-xxl flex-grow-1 container-p-y">
|
<div class="container-xxl flex-grow-1 container-p-y">
|
||||||
<LoadingSpinner v-if="isLoading" />
|
<div class="row g-3">
|
||||||
<div v-else class="row g-3">
|
|
||||||
<div class="mt-8">
|
<div class="mt-8">
|
||||||
<!-- 투표 작성 -->
|
<!-- 투표 작성 -->
|
||||||
<WriteBtn @click="voteWrite" />
|
<WriteBtn @click="voteWrite" />
|
||||||
@ -52,8 +51,6 @@ import Quill from 'quill';
|
|||||||
import WriteBtn from '@c/button/WriteBtn.vue';
|
import WriteBtn from '@c/button/WriteBtn.vue';
|
||||||
import voteList from '@c/voteboard/voteCardList.vue';
|
import voteList from '@c/voteboard/voteCardList.vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import LoadingSpinner from "@v/LoadingPage.vue";
|
|
||||||
const isLoading = ref(true);
|
|
||||||
|
|
||||||
const toastStore = useToastStore();
|
const toastStore = useToastStore();
|
||||||
const category = ref('0');
|
const category = ref('0');
|
||||||
@ -81,7 +78,6 @@ const changeCheck = () =>{
|
|||||||
}
|
}
|
||||||
//투표목록
|
//투표목록
|
||||||
const getvoteList = async () => {
|
const getvoteList = async () => {
|
||||||
isLoading.value = true;
|
|
||||||
const response = await $api.get('vote/getVoteList',{
|
const response = await $api.get('vote/getVoteList',{
|
||||||
params:
|
params:
|
||||||
{
|
{
|
||||||
@ -93,7 +89,6 @@ const getvoteList = async () => {
|
|||||||
if (response.data.status === "OK") {
|
if (response.data.status === "OK") {
|
||||||
PageData.value = response.data.data;
|
PageData.value = response.data.data;
|
||||||
voteListCardData.value = response.data.data.list;
|
voteListCardData.value = response.data.data.list;
|
||||||
isLoading.value = false;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const selectHandler = () =>{
|
const selectHandler = () =>{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user