Merge remote-tracking branch 'origin/main' into board-comment-2
This commit is contained in:
commit
08dabf4191
2
.env.dev
2
.env.dev
@ -1,6 +1,6 @@
|
|||||||
VITE_DOMAIN = https://192.168.0.251:5173/
|
VITE_DOMAIN = https://192.168.0.251:5173/
|
||||||
# VITE_LOGIN_URL = http://localhost:10325/ms/
|
# VITE_LOGIN_URL = http://localhost:10325/ms/
|
||||||
# VITE_FILE_URL = http://localhost:10325/ms/
|
VITE_SERVER = httpS://192.168.0.251:10325/
|
||||||
VITE_API_URL = https://192.168.0.251:10325/api/
|
VITE_API_URL = https://192.168.0.251:10325/api/
|
||||||
VITE_TEST_URL = https://192.168.0.251:10325/test/
|
VITE_TEST_URL = https://192.168.0.251:10325/test/
|
||||||
VITE_KAKAO_MAP_KEY=6f092e8f45ee81186bb6d8408f66a492
|
VITE_KAKAO_MAP_KEY=6f092e8f45ee81186bb6d8408f66a492
|
||||||
@ -1,6 +1,6 @@
|
|||||||
VITE_DOMAIN = http://localhost:5173/
|
VITE_DOMAIN = http://localhost:5173/
|
||||||
# VITE_LOGIN_URL = http://localhost:10325/ms/
|
# VITE_LOGIN_URL = http://localhost:10325/ms/
|
||||||
# VITE_FILE_URL = http://localhost:10325/ms/
|
VITE_SERVER = http://localhost:10325/
|
||||||
VITE_API_URL = http://localhost:10325/api/
|
VITE_API_URL = http://localhost:10325/api/
|
||||||
VITE_TEST_URL = http://localhost:10325/test/
|
VITE_TEST_URL = http://localhost:10325/test/
|
||||||
VITE_KAKAO_MAP_KEY=6f092e8f45ee81186bb6d8408f66a492
|
VITE_KAKAO_MAP_KEY=6f092e8f45ee81186bb6d8408f66a492
|
||||||
@ -3,22 +3,33 @@
|
|||||||
|
|
||||||
/* 휴가 */
|
/* 휴가 */
|
||||||
|
|
||||||
.fc-daygrid-day-events {
|
|
||||||
max-height: 100px !important;
|
|
||||||
overflow-y: auto !important;
|
|
||||||
}
|
|
||||||
/* 이벤트 선 없게 */
|
/* 이벤트 선 없게 */
|
||||||
.fc-event {
|
.fc-event {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
/* 오전전반차 그래프 */
|
/* 오전 반차 그래프 (왼쪽 절반) */
|
||||||
.fc-daygrid-event.half-day-am {
|
.fc-daygrid-event.half-day-am {
|
||||||
width: calc(50% - 4px) !important;
|
width: 50% !important;
|
||||||
|
height: 8px !important;
|
||||||
|
border-radius: 2px !important;
|
||||||
|
font-size: 0px !important;
|
||||||
}
|
}
|
||||||
/* 오후반차 그래프프 */
|
/* 오후 반차 그래프 (오른쪽 절반) */
|
||||||
.fc-daygrid-event.half-day-pm {
|
.fc-daygrid-event.half-day-pm {
|
||||||
width: calc(50% - 4px) !important;
|
width: 50% !important;
|
||||||
margin-left: auto !important
|
height: 8px !important;
|
||||||
|
margin-left: auto !important;
|
||||||
|
border-radius: 2px !important;
|
||||||
|
font-size: 0px !important;
|
||||||
|
}
|
||||||
|
/* 연차 그래프 (풀풀) */
|
||||||
|
.fc-daygrid-event.full-day {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 8px !important;
|
||||||
|
margin-left: auto !important;
|
||||||
|
border-radius: 2px !important;
|
||||||
|
font-size: 0px !important;
|
||||||
}
|
}
|
||||||
/* 공휴일,일요일 색상 */
|
/* 공휴일,일요일 색상 */
|
||||||
.fc-day-sun .fc-daygrid-day-number,
|
.fc-day-sun .fc-daygrid-day-number,
|
||||||
@ -84,7 +95,6 @@ opacity: 0.6; /* 흐려 보이게 */
|
|||||||
|
|
||||||
|
|
||||||
/* 본인 모달 */
|
/* 본인 모달 */
|
||||||
|
|
||||||
/* 닫기 버튼 */
|
/* 닫기 버튼 */
|
||||||
.close-btn {
|
.close-btn {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -109,7 +119,6 @@ opacity: 0.6; /* 흐려 보이게 */
|
|||||||
|
|
||||||
|
|
||||||
/* 선물하기 모달 */
|
/* 선물하기 모달 */
|
||||||
|
|
||||||
/* 연차 개수 버튼 */
|
/* 연차 개수 버튼 */
|
||||||
.count-btn {
|
.count-btn {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
@ -127,7 +136,6 @@ opacity: 0.6; /* 흐려 보이게 */
|
|||||||
background: #cccccc;
|
background: #cccccc;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 버튼 컨테이너 (우측 정렬) */
|
/* 버튼 컨테이너 (우측 정렬) */
|
||||||
.custom-button-container {
|
.custom-button-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -141,18 +149,17 @@ opacity: 0.6; /* 흐려 보이게 */
|
|||||||
padding: 10px; /* 크기 조정 */
|
padding: 10px; /* 크기 조정 */
|
||||||
cursor: pointer; /* 클릭 가능하도록 변경 */
|
cursor: pointer; /* 클릭 가능하도록 변경 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 아이콘 색상 변경 (기본) */
|
/* 아이콘 색상 변경 (기본) */
|
||||||
.custom-button i {
|
.custom-button i {
|
||||||
color: #282538; /* 기본 아이콘 색상 */
|
color: #282538; /* 기본 아이콘 색상 */
|
||||||
font-size: 25px; /* 아이콘 크기 */
|
font-size: 25px; /* 아이콘 크기 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 버튼 호버 효과 */
|
/* 버튼 호버 효과 */
|
||||||
.custom-button:hover i {
|
.custom-button:hover i {
|
||||||
color: #ff0800; /* 호버 시 아이콘 색상 변경 */
|
color: #ff0800; /* 호버 시 아이콘 색상 변경 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.grayscaleImg {
|
.grayscaleImg {
|
||||||
filter: grayscale(100%);
|
filter: grayscale(100%);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,12 +7,6 @@
|
|||||||
//-----------------
|
//-----------------
|
||||||
let menu, animate;
|
let menu, animate;
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
var menu, animate;
|
|
||||||
(function () {
|
|
||||||
// Initialize menu
|
|
||||||
//-----------------
|
|
||||||
=======
|
|
||||||
let layoutMenuEl = document.querySelectorAll('#layout-menu');
|
let layoutMenuEl = document.querySelectorAll('#layout-menu');
|
||||||
layoutMenuEl.forEach(function (element) {
|
layoutMenuEl.forEach(function (element) {
|
||||||
menu = new Menu(element, {
|
menu = new Menu(element, {
|
||||||
@ -23,7 +17,6 @@ var menu, animate;
|
|||||||
window.Helpers.scrollToActive((animate = false));
|
window.Helpers.scrollToActive((animate = false));
|
||||||
window.Helpers.mainMenu = menu;
|
window.Helpers.mainMenu = menu;
|
||||||
});
|
});
|
||||||
>>>>>>> board-comment
|
|
||||||
|
|
||||||
// Initialize menu togglers and bind click on each
|
// Initialize menu togglers and bind click on each
|
||||||
let menuToggler = document.querySelectorAll('.layout-menu-toggle');
|
let menuToggler = document.querySelectorAll('.layout-menu-toggle');
|
||||||
|
|||||||
@ -28,10 +28,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineEmits, ref, defineProps } from "vue";
|
import { defineEmits, ref, defineProps, watch } from "vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
isDisabled: Boolean
|
isDisabled: Boolean,
|
||||||
|
selectedDate: String // 날짜 선택 값을 props로 받음
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(["toggleHalfDay", "addVacationRequests", "resetHalfDay"]);
|
const emit = defineEmits(["toggleHalfDay", "addVacationRequests", "resetHalfDay"]);
|
||||||
@ -39,15 +40,16 @@ const halfDayType = ref(null);
|
|||||||
|
|
||||||
const toggleHalfDay = (type) => {
|
const toggleHalfDay = (type) => {
|
||||||
halfDayType.value = type;
|
halfDayType.value = type;
|
||||||
|
|
||||||
emit("toggleHalfDay", halfDayType.value);
|
emit("toggleHalfDay", halfDayType.value);
|
||||||
|
|
||||||
// ✅ 버튼 클릭 후 1초 후 자동 비활성화
|
|
||||||
setTimeout(() => {
|
|
||||||
halfDayType.value = null;
|
|
||||||
}, 1000);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// `selectedDate`가 변경되면 반차 선택 초기화
|
||||||
|
watch(() => props.selectedDate, (newDate) => {
|
||||||
|
if (newDate) {
|
||||||
|
resetHalfDay();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 날짜 선택 후 반차 버튼 상태 초기화
|
// 날짜 선택 후 반차 버튼 상태 초기화
|
||||||
const resetHalfDay = () => {
|
const resetHalfDay = () => {
|
||||||
halfDayType.value = null;
|
halfDayType.value = null;
|
||||||
@ -89,20 +91,20 @@ defineExpose({ resetHalfDay });
|
|||||||
|
|
||||||
/* 선택된 (눌린) 버튼 */
|
/* 선택된 (눌린) 버튼 */
|
||||||
.btn.active {
|
.btn.active {
|
||||||
border: 3px solid #fff; /* 흰색 테두리 강조 */
|
border: 3px solid #fff;
|
||||||
box-shadow: 0px 4px 15px rgba(0, 0, 0, 0.3);
|
box-shadow: 0px 4px 15px rgba(0, 0, 0, 0.3);
|
||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* AM 버튼 (선택된 상태) */
|
/* AM 버튼 (선택된 상태) */
|
||||||
.btn-warning.active {
|
.btn-warning.active {
|
||||||
background-color: #ffca2c !important; /* 진한 노란색 */
|
background-color: #ffca2c !important;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* PM 버튼 (선택된 상태) */
|
/* PM 버튼 (선택된 상태) */
|
||||||
.btn-info.active {
|
.btn-info.active {
|
||||||
background-color: #0b5ed7 !important; /* 진한 파란색 */
|
background-color: #0b5ed7 !important;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mb-4 row">
|
<div class="mb-4 row">
|
||||||
<label :for="name" class="col-md-2 col-form-label">{{ title }}</label>
|
<label :for="inputId" class="col-md-2 col-form-label">{{ title }}</label>
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
<input
|
<input
|
||||||
class="form-control"
|
class="form-control"
|
||||||
type="file"
|
type="file"
|
||||||
:id="name"
|
:id="inputId"
|
||||||
|
ref="fileInput"
|
||||||
@change="changeHandler"
|
@change="changeHandler"
|
||||||
multiple
|
multiple
|
||||||
/>
|
/>
|
||||||
@ -21,7 +22,7 @@ import { ref ,computed} from 'vue';
|
|||||||
import { fileMsg } from '@/common/msgEnum';
|
import { fileMsg } from '@/common/msgEnum';
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
const prop = defineProps({
|
const props = defineProps({
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '라벨',
|
default: '라벨',
|
||||||
@ -29,7 +30,7 @@ const prop = defineProps({
|
|||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'nameplz',
|
default: 'fileInput',
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
isAlert: {
|
isAlert: {
|
||||||
@ -38,12 +39,13 @@ const prop = defineProps({
|
|||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const inputId = computed(() => props.name || 'defaultFileInput');
|
||||||
|
|
||||||
const emits = defineEmits(['update:data', 'update:isValid']);
|
const emits = defineEmits(['update:data', 'update:isValid']);
|
||||||
|
|
||||||
const MAX_TOTAL_SIZE = 5 * 1024 * 1024; // 5MB
|
const MAX_TOTAL_SIZE = 5 * 1024 * 1024; // 5MB
|
||||||
const MAX_FILE_COUNT = 5; // 최대 파일 개수
|
const MAX_FILE_COUNT = 5; // 최대 파일 개수
|
||||||
const ALLOWED_FILE_TYPES = ['image/jpeg', 'image/png', 'application/pdf']; // 허용된 파일 유형
|
const ALLOWED_FILE_TYPES = []; // 모든 파일을 허용
|
||||||
|
|
||||||
const showError = ref(false);
|
const showError = ref(false);
|
||||||
const fileMsgKey = ref(''); // 에러 메시지 키
|
const fileMsgKey = ref(''); // 에러 메시지 키
|
||||||
@ -51,9 +53,12 @@ const fileMsgKey = ref(''); // 에러 메시지 키
|
|||||||
const changeHandler = (event) => {
|
const changeHandler = (event) => {
|
||||||
const files = Array.from(event.target.files);
|
const files = Array.from(event.target.files);
|
||||||
const totalSize = files.reduce((sum, file) => sum + file.size, 0);
|
const totalSize = files.reduce((sum, file) => sum + file.size, 0);
|
||||||
const invalidFiles = files.filter(file => !ALLOWED_FILE_TYPES.includes(file.type));
|
|
||||||
|
|
||||||
// 파일 검증 로직
|
// ALLOWED_FILE_TYPES가 비어있으면 모든 파일 허용
|
||||||
|
const invalidFiles = ALLOWED_FILE_TYPES.length > 0
|
||||||
|
? files.filter(file => !ALLOWED_FILE_TYPES.includes(file.type))
|
||||||
|
: [];
|
||||||
|
|
||||||
if (totalSize > MAX_TOTAL_SIZE) {
|
if (totalSize > MAX_TOTAL_SIZE) {
|
||||||
showError.value = true;
|
showError.value = true;
|
||||||
fileMsgKey.value = 'FileMaxSizeMsg';
|
fileMsgKey.value = 'FileMaxSizeMsg';
|
||||||
|
|||||||
@ -310,6 +310,18 @@ const closeEditModal = () => {
|
|||||||
isEditModalOpen.value = false;
|
isEditModalOpen.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 변경된 내용 있는지 확인
|
||||||
|
const hasChanges = computed(() => {
|
||||||
|
return selectedProject.value.PROJCTNAM !== props.title ||
|
||||||
|
selectedProject.value.PROJCTSTR !== props.strdate ||
|
||||||
|
selectedProject.value.PROJCTEND !== props.enddate ||
|
||||||
|
selectedProject.value.PROJCTZIP !== props.addressZip ||
|
||||||
|
selectedProject.value.PROJCTARR !== props.address ||
|
||||||
|
selectedProject.value.PROJCTDTL !== props.addressdtail ||
|
||||||
|
selectedProject.value.PROJCTDES !== props.description ||
|
||||||
|
selectedProject.value.PROJCTCOL !== props.projctCol;
|
||||||
|
});
|
||||||
|
|
||||||
// 프로젝트 수정
|
// 프로젝트 수정
|
||||||
const handleUpdate = () => {
|
const handleUpdate = () => {
|
||||||
nameAlert.value = selectedProject.value.PROJCTNAM.trim() === '';
|
nameAlert.value = selectedProject.value.PROJCTNAM.trim() === '';
|
||||||
@ -318,6 +330,11 @@ const handleUpdate = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!hasChanges.value) {
|
||||||
|
toastStore.onToast('변경된 내용이 없습니다.', 'e');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$api.patch('project/update', {
|
$api.patch('project/update', {
|
||||||
projctSeq: selectedProject.value.PROJCTSEQ,
|
projctSeq: selectedProject.value.PROJCTSEQ,
|
||||||
projctNam: selectedProject.value.PROJCTNAM,
|
projctNam: selectedProject.value.PROJCTNAM,
|
||||||
@ -336,7 +353,6 @@ const handleUpdate = () => {
|
|||||||
closeEditModal();
|
closeEditModal();
|
||||||
// 상위 컴포넌트에 업데이트 알림
|
// 상위 컴포넌트에 업데이트 알림
|
||||||
emit('update');
|
emit('update');
|
||||||
window.location.reload()
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<ul class="list-unstyled users-list d-flex align-items-center gap-1">
|
<ul class="list-unstyled users-list d-flex align-items-center gap-1">
|
||||||
<li
|
<li
|
||||||
v-for="(user, index) in userList"
|
v-for="(user, index) in sortedUserList"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="avatar pull-up"
|
class="avatar pull-up"
|
||||||
:class="{ 'opacity-100': isUserDisabled(user) }"
|
:class="{ 'opacity-100': isUserDisabled(user) }"
|
||||||
@ -25,7 +25,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, nextTick } from 'vue';
|
import { onMounted, ref, nextTick, computed } from 'vue';
|
||||||
import { useUserStore } from '@s/userList';
|
import { useUserStore } from '@s/userList';
|
||||||
import $api from '@api';
|
import $api from '@api';
|
||||||
|
|
||||||
@ -41,6 +41,19 @@ const props = defineProps({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 활성화된 회원을 앞으로 정렬하는 computed 속성
|
||||||
|
const sortedUserList = computed(() => {
|
||||||
|
return [...userList.value].sort((a, b) => {
|
||||||
|
const aDisabled = isUserDisabled(a);
|
||||||
|
const bDisabled = isUserDisabled(b);
|
||||||
|
|
||||||
|
// 활성화된 사용자가 먼저 오도록 정렬
|
||||||
|
if (!aDisabled && bDisabled) return -1;
|
||||||
|
if (aDisabled && !bDisabled) return 1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// 사용자의 프로젝트 참여 상태 확인
|
// 사용자의 프로젝트 참여 상태 확인
|
||||||
const fetchProjectParticipation = async () => {
|
const fetchProjectParticipation = async () => {
|
||||||
if (props.projctSeq) {
|
if (props.projctSeq) {
|
||||||
@ -79,27 +92,33 @@ const isUserDisabled = (user) => {
|
|||||||
|
|
||||||
// 클릭 시 활성화/비활성화 및 DB 업데이트
|
// 클릭 시 활성화/비활성화 및 DB 업데이트
|
||||||
const toggleDisable = async (index) => {
|
const toggleDisable = async (index) => {
|
||||||
const user = userList.value[index];
|
const user = sortedUserList.value[index];
|
||||||
if (user) {
|
if (user) {
|
||||||
const newParticipationStatus = props.projctSeq
|
const newParticipationStatus = props.projctSeq
|
||||||
? user.PROJCTYON === '1'
|
? user.PROJCTYON === '1'
|
||||||
: !user.disabled;
|
: !user.disabled;
|
||||||
|
|
||||||
if (props.projctSeq) {
|
if (props.projctSeq) {
|
||||||
const response = await $api.patch('project/updateYon', {
|
const response = await $api.patch('project/updateYon', {
|
||||||
memberSeq: user.MEMBERSEQ,
|
memberSeq: user.MEMBERSEQ,
|
||||||
projctSeq: props.projctSeq,
|
projctSeq: props.projctSeq,
|
||||||
projctYon: newParticipationStatus ? '0' : '1'
|
projctYon: newParticipationStatus ? '0' : '1'
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
user.PROJCTYON = newParticipationStatus ? '0' : '1';
|
// 원래 userList에서 해당 사용자를 찾아 업데이트
|
||||||
|
const originalIndex = userList.value.findIndex(u => u.MEMBERSEQ === user.MEMBERSEQ);
|
||||||
|
if (originalIndex !== -1) {
|
||||||
|
userList.value[originalIndex].PROJCTYON = newParticipationStatus ? '0' : '1';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
user.disabled = newParticipationStatus;
|
// 원래 userList에서 해당 사용자를 찾아 업데이트
|
||||||
|
const originalIndex = userList.value.findIndex(u => u.MEMBERSEQ === user.MEMBERSEQ);
|
||||||
|
if (originalIndex !== -1) {
|
||||||
|
userList.value[originalIndex].disabled = newParticipationStatus;
|
||||||
emitUserListUpdate();
|
emitUserListUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// emit
|
// emit
|
||||||
|
|||||||
@ -42,7 +42,7 @@ const loadScript = src => {
|
|||||||
nextTick(async () => {
|
nextTick(async () => {
|
||||||
await wait(200);
|
await wait(200);
|
||||||
loadScript('/vendor/js/menu.js');
|
loadScript('/vendor/js/menu.js');
|
||||||
// loadScript('/js/main.js');
|
loadScript('/js/main.js');
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<style></style>
|
<style></style>
|
||||||
|
|||||||
@ -241,7 +241,7 @@
|
|||||||
|
|
||||||
const user = ref(null);
|
const user = ref(null);
|
||||||
//const baseUrl = $api.defaults.baseURL.replace(/api\/$/, '');
|
//const baseUrl = $api.defaults.baseURL.replace(/api\/$/, '');
|
||||||
const baseUrl = import.meta.env.BASE_URL;
|
const baseUrl = import.meta.env.VITE_SERVER;
|
||||||
|
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
const userStore = useUserInfoStore();
|
const userStore = useUserInfoStore();
|
||||||
|
|||||||
@ -7,9 +7,11 @@
|
|||||||
<div class="sidebar-content">
|
<div class="sidebar-content">
|
||||||
<div class="sidebar-actions text-center my-3">
|
<div class="sidebar-actions text-center my-3">
|
||||||
<HalfDayButtons
|
<HalfDayButtons
|
||||||
|
ref="halfDayButtonsRef"
|
||||||
@toggleHalfDay="toggleHalfDay"
|
@toggleHalfDay="toggleHalfDay"
|
||||||
@addVacationRequests="saveVacationChanges"
|
@addVacationRequests="saveVacationChanges"
|
||||||
:isDisabled="!hasChanges"
|
:isDisabled="!hasChanges"
|
||||||
|
:selectedDate="selectedDate"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ProfileList
|
<ProfileList
|
||||||
@ -59,7 +61,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, ref, onMounted, nextTick, computed, watch } from "vue";
|
import { reactive, ref, onMounted, nextTick, computed, watch, onBeforeUnmount } from "vue";
|
||||||
import axios from "@api";
|
import axios from "@api";
|
||||||
import FullCalendar from "@fullcalendar/vue3";
|
import FullCalendar from "@fullcalendar/vue3";
|
||||||
import dayGridPlugin from "@fullcalendar/daygrid";
|
import dayGridPlugin from "@fullcalendar/daygrid";
|
||||||
@ -80,6 +82,9 @@
|
|||||||
import { useUserInfoStore } from "@s/useUserInfoStore";
|
import { useUserInfoStore } from "@s/useUserInfoStore";
|
||||||
import { fetchHolidays } from "@c/calendar/holiday.js";
|
import { fetchHolidays } from "@c/calendar/holiday.js";
|
||||||
import { useToastStore } from '@s/toastStore';
|
import { useToastStore } from '@s/toastStore';
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const toastStore = useToastStore();
|
const toastStore = useToastStore();
|
||||||
const userStore = useUserInfoStore();
|
const userStore = useUserInfoStore();
|
||||||
@ -90,7 +95,7 @@
|
|||||||
const receivedVacations = ref([]);
|
const receivedVacations = ref([]);
|
||||||
const isModalOpen = ref(false);
|
const isModalOpen = ref(false);
|
||||||
const remainingVacationData = ref({});
|
const remainingVacationData = ref({});
|
||||||
|
const selectedDate = ref(null);
|
||||||
const lastRemainingYear = ref(new Date().getFullYear());
|
const lastRemainingYear = ref(new Date().getFullYear());
|
||||||
const lastRemainingMonth = ref(String(new Date().getMonth() + 1).padStart(2, "0"));
|
const lastRemainingMonth = ref(String(new Date().getMonth() + 1).padStart(2, "0"));
|
||||||
const isGrantModalOpen = ref(false);
|
const isGrantModalOpen = ref(false);
|
||||||
@ -106,6 +111,39 @@
|
|||||||
const fetchedEvents = ref([]);
|
const fetchedEvents = ref([]);
|
||||||
const halfDayButtonsRef = ref(null);
|
const halfDayButtonsRef = ref(null);
|
||||||
|
|
||||||
|
// 페이지 이동 시 변경 사항 확인
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
if (hasChanges.value) {
|
||||||
|
const answer = window.confirm("저장하지 않은 변경 사항이 있습니다. 이동하시겠습니까?");
|
||||||
|
if (!answer) {
|
||||||
|
return next(false); // 이동 취소
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener("beforeunload", preventUnsavedChanges);
|
||||||
|
});
|
||||||
|
|
||||||
|
function preventUnsavedChanges(event) {
|
||||||
|
if (hasChanges.value) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.returnValue = ""; // 대부분의 브라우저에서 경고 메시지 표시됨
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// `selectedDates` 변경 시 반차 버튼 초기화
|
||||||
|
watch(
|
||||||
|
() => Array.from(selectedDates.value.keys()), // 선택된 날짜 리스트 감시
|
||||||
|
(newKeys) => {
|
||||||
|
if (halfDayButtonsRef.value) {
|
||||||
|
halfDayButtonsRef.value.resetHalfDay();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
// 데이트피커 인풋 ref
|
// 데이트피커 인풋 ref
|
||||||
const calendarDatepicker = ref(null);
|
const calendarDatepicker = ref(null);
|
||||||
let fpInstance = null;
|
let fpInstance = null;
|
||||||
@ -192,6 +230,7 @@ function handleDateClick(info) {
|
|||||||
await fetchRemainingVacation();
|
await fetchRemainingVacation();
|
||||||
const currentYear = new Date().getFullYear();
|
const currentYear = new Date().getFullYear();
|
||||||
await fetchVacationHistory(currentYear);
|
await fetchVacationHistory(currentYear);
|
||||||
|
window.addEventListener("beforeunload", preventUnsavedChanges);
|
||||||
|
|
||||||
// Flatpickr 초기화 (달 선택 모드)
|
// Flatpickr 초기화 (달 선택 모드)
|
||||||
fpInstance = flatpickr(calendarDatepicker.value, {
|
fpInstance = flatpickr(calendarDatepicker.value, {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user