This commit is contained in:
khj0414 2025-03-06 15:29:39 +09:00
commit c3b1331128
8 changed files with 119 additions and 107 deletions

View File

@ -78,14 +78,14 @@ opacity: 0.6; /* 흐려 보이게 */
border: none !important; border: none !important;
border-radius: 4px; border-radius: 4px;
} }
/* 오전 반차 (왼쪽 절반) */ /* 오전 반차 활성화 영역 (왼쪽 절반) */
.selected-event.half-day-am { .selected-event.half-day-am {
width: 50% !important; width: 50% !important;
left: 0 !important; left: 0 !important;
border-top-left-radius: 4px; border-top-left-radius: 4px;
border-bottom-left-radius: 4px; border-bottom-left-radius: 4px;
} }
/* 오후 반차 (오른쪽 절반) */ /* 오후 반차 활성화 영역 (오른쪽 절반) */
.selected-event.half-day-pm { .selected-event.half-day-pm {
width: 50% !important; width: 50% !important;
margin-left: auto !important; margin-left: auto !important;
@ -105,6 +105,30 @@ opacity: 0.6; /* 흐려 보이게 */
font-size: 18px; font-size: 18px;
cursor: pointer; cursor: pointer;
} }
.close-btn:hover {
color: #525252;
}
/* 모달 배경 투명하게 */
.vac-modal-dialog {
background: none !important;
box-shadow: none !important;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
/* 모달 본문 스타일 */
.vac-modal-content {
background: #fff;
border-radius: 8px;
box-shadow: 0px -4px 10px rgba(0, 0, 0, 0.1); /* 위쪽 그림자만 적용 */
padding: 20px;
max-width: 500px;
width: 100%;
position: relative;
}
/* 리스트 아이템 */ /* 리스트 아이템 */
.vacation-item { .vacation-item {
display: flex; display: flex;

View File

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@ -5,7 +5,7 @@
<span :class="isEssential ? 'link-danger' : 'none'">*</span> <span :class="isEssential ? 'link-danger' : 'none'">*</span>
</label> </label>
<div :class="isRow ? 'col-md-10' : 'col-md-12'" class="d-flex gap-2 align-items-center"> <div :class="isRow ? 'col-md-10' : 'col-md-12'" class="d-flex gap-2 align-items-center">
<select class="form-select" :id="name" v-model="selectData" :disabled="disabled" :style="isColor ? { color: selected } : {}"> <select class="form-select" :id="name" v-model="selectData" :disabled="disabled" :style="isColor ? { color: selected } : {}" @blur="$emit('blur')">
<option v-for="(item, i) in data" :key="i" :value="isCommon ? item.value : i" :style="isColor ? { color: item.label } : {}"> <option v-for="(item, i) in data" :key="i" :value="isCommon ? item.value : i" :style="isColor ? { color: item.label } : {}">
{{ isCommon ? item.label : item }} {{ isCommon ? item.label : item }}
</option> </option>
@ -91,7 +91,7 @@ const props = defineProps({
}, },
}); });
const emit = defineEmits(['update:data']); const emit = defineEmits(['update:data', 'blur']);
const selectData = ref(props.value); const selectData = ref(props.value);
// props.value watch // props.value watch
@ -106,6 +106,10 @@ watch(() => props.data, (newData) => {
if (props.value === '0') { if (props.value === '0') {
selectData.value = newData[0].value; selectData.value = newData[0].value;
emit('update:data', selectData.value); emit('update:data', selectData.value);
if (props.isColor) {
emit('blur');
}
} }
} }
}, { immediate: true }); }, { immediate: true });

View File

@ -1,8 +1,10 @@
<template> <template>
<div v-if="isOpen" class="modal-dialog" @click.self="closeModal"> <div v-if="isOpen" class="vac-modal-dialog" @click.self="closeModal">
<div class="modal-content p-5"> <div class="vac-modal-content p-5">
<div class="modal-header">
<h5 class="modal-title">To. {{ targetUser.MEMBERNAM }} 🎁</h5> <h5 class="modal-title">To. {{ targetUser.MEMBERNAM }} 🎁</h5>
<button class="close-btn" @click="closeModal"></button> <button class="close-btn" @click="closeModal"></button>
</div>
<div class="modal-body"> <div class="modal-body">
<p>선물할 연차 개수를 선택하세요.</p> <p>선물할 연차 개수를 선택하세요.</p>
@ -12,6 +14,7 @@
<span class="text-dark fw-bold fs-4">{{ grantCount }}</span> <span class="text-dark fw-bold fs-4">{{ grantCount }}</span>
<button @click="increaseCount" :disabled="grantCount >= availableQuota" class="count-btn">+</button> <button @click="increaseCount" :disabled="grantCount >= availableQuota" class="count-btn">+</button>
</div> </div>
<div class="custom-button-container"> <div class="custom-button-container">
<button class="custom-button" @click="saveVacationGrant" :disabled="grantCount === 0"> <button class="custom-button" @click="saveVacationGrant" :disabled="grantCount === 0">
<i class="bx bx-gift"></i> <i class="bx bx-gift"></i>
@ -20,7 +23,7 @@
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, defineProps, defineEmits, watch, onMounted } from "vue"; import { ref, defineProps, defineEmits, watch, onMounted } from "vue";
@ -121,25 +124,6 @@
</script> </script>
<style scoped> <style scoped>
/* 모달 배경 투명하게 */
.modal-dialog {
background: none !important; /* 배경 제거 */
box-shadow: none !important; /* 음영 제거 */
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
/* 모달 내용 스타일 */ </style>
.modal-content {
background: #fff; /* 기존 흰색 배경 유지 */
border-radius: 8px;
box-shadow: none !important; /* 내부 음영 제거 */
padding: 20px;
max-width: 500px;
width: 100%;
}
</style>

View File

@ -1,6 +1,6 @@
<template> <template>
<div v-if="isOpen" class="modal-dialog" @click.self="closeModal"> <div v-if="isOpen" class="vac-modal-dialog" @click.self="closeModal">
<div class="modal-content p-5 modal-scroll"> <div class="vac-modal-content p-5 modal-scroll">
<h5 class="modal-title">📅 연차 사용 내역</h5> <h5 class="modal-title">📅 연차 사용 내역</h5>
<button class="close-btn" @click="closeModal"></button> <button class="close-btn" @click="closeModal"></button>
@ -138,25 +138,4 @@ const closeModal = () => {
</script> </script>
<style scoped> <style scoped>
/* 모달 배경 투명하게 */
.modal-dialog {
background: none !important; /* 배경 제거 */
box-shadow: none !important; /* 음영 제거 */
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
/* 모달 내용 스타일 */
.modal-content {
background: #fff; /* 기존 흰색 배경 유지 */
border-radius: 8px;
box-shadow: none !important; /* 내부 음영 제거 */
padding: 20px;
max-width: 500px;
width: 100%;
}
</style> </style>

View File

@ -95,9 +95,11 @@
:is-color="true" :is-color="true"
:data="colorList" :data="colorList"
@update:data="color = $event" @update:data="color = $event"
@blur="checkColorDuplicate"
class="w-50" class="w-50"
/> />
</div> </div>
<span v-if="colorError" class="w-50 ps-1 ms-auto invalid-feedback d-block">{{ colorError }}</span>
<div class="d-flex"> <div class="d-flex">
<UserFormInput <UserFormInput
@ -187,6 +189,7 @@
const phone = ref(''); const phone = ref('');
const phoneError = ref(''); const phoneError = ref('');
const color = ref(''); // color const color = ref(''); // color
const colorError = ref('');
const mbti = ref(''); // MBTI const mbti = ref(''); // MBTI
const pwhint = ref(''); // pwhint const pwhint = ref(''); // pwhint
@ -202,6 +205,7 @@
const addressAlert = ref(false); const addressAlert = ref(false);
const phoneAlert = ref(false); const phoneAlert = ref(false);
const phoneErrorAlert = ref(false); const phoneErrorAlert = ref(false);
const colorErrorAlert = ref(false);
const toastStore = useToastStore(); const toastStore = useToastStore();
@ -247,7 +251,6 @@
// //
const checkIdDuplicate = async () => { const checkIdDuplicate = async () => {
const response = await $api.get(`/user/checkId?memberIds=${id.value}`); const response = await $api.get(`/user/checkId?memberIds=${id.value}`);
if (!response.data.data) { if (!response.data.data) {
idErrorAlert.value = true; idErrorAlert.value = true;
idError.value = '이미 사용 중인 아이디입니다.'; idError.value = '이미 사용 중인 아이디입니다.';
@ -295,9 +298,26 @@
} }
}; };
//
const checkColorDuplicate = async () => {
const response = await $api.get(`/user/checkColor?memberCol=${color.value}`);
if (response.data.data) {
colorErrorAlert.value = true;
colorError.value = '이미 사용 중인 색상입니다.';
} else {
colorErrorAlert.value = false;
colorError.value = '';
}
};
// //
const handleSubmit = async () => { const handleSubmit = async () => {
await checkColorDuplicate();
idAlert.value = id.value.trim() === ''; idAlert.value = id.value.trim() === '';
passwordAlert.value = password.value.trim() === ''; passwordAlert.value = password.value.trim() === '';
passwordcheckAlert.value = passwordcheck.value.trim() === ''; passwordcheckAlert.value = passwordcheck.value.trim() === '';
@ -317,7 +337,8 @@
} }
if (profilAlert.value || idAlert.value || idErrorAlert.value || passwordAlert.value || passwordcheckAlert.value || if (profilAlert.value || idAlert.value || idErrorAlert.value || passwordAlert.value || passwordcheckAlert.value ||
passwordcheckErrorAlert.value || pwhintResAlert.value || nameAlert.value || birthAlert.value || addressAlert.value || phoneAlert.value || phoneErrorAlert.value) { passwordcheckErrorAlert.value || pwhintResAlert.value || nameAlert.value || birthAlert.value ||
addressAlert.value || phoneAlert.value || phoneErrorAlert.value || colorErrorAlert.value) {
return; return;
} }

View File

@ -8,8 +8,8 @@
<div class="navbar-nav-right d-flex align-items-center" id="navbar-collapse"> <div class="navbar-nav-right d-flex align-items-center" id="navbar-collapse">
<ul class="navbar-nav flex-row align-items-center ms-auto"> <ul class="navbar-nav flex-row align-items-center ms-auto">
<button class="btn p-1" @click="switchToLightMode"><i class="bx bxs-sun link-warning"></i></button> <!-- <button class="btn p-1" @click="switchToLightMode"><i class="bx bxs-sun link-warning"></i></button> -->
<button class="btn p-1" @click="switchToDarkMode"><i class="bx bxs-moon"></i></button> <!-- <button class="btn p-1" @click="switchToDarkMode"><i class="bx bxs-moon"></i></button> -->
<i class="bx bx-bell bx-md bx-log-out cursor-pointer p-1" @click="handleLogout"></i> <i class="bx bx-bell bx-md bx-log-out cursor-pointer p-1" @click="handleLogout"></i>

View File

@ -148,7 +148,7 @@ watch(
const calendarDatepicker = ref(null); const calendarDatepicker = ref(null);
let fpInstance = null; let fpInstance = null;
/** 변경사항 여부 확인 */ /** 변경사항 여부 확인 */
const hasChanges = computed(() => { const hasChanges = computed(() => {
return ( return (
selectedDates.value.size > 0 || selectedDates.value.size > 0 ||
@ -157,7 +157,7 @@ const hasChanges = computed(() => {
}); });
/** selectedDates가 변경될 때 버튼 상태 즉시 업데이트 */ /** selectedDates가 변경될 때 버튼 상태 즉시 업데이트 */
watch( watch(
() => Array.from(selectedDates.value.keys()), // keys() Array () => Array.from(selectedDates.value.keys()), // keys() Array
(newKeys) => { (newKeys) => {
@ -204,7 +204,7 @@ function handleDateClick(info) {
selectedDates.value.set(clickedDateStr, type); selectedDates.value.set(clickedDateStr, type);
halfDayType.value = null; halfDayType.value = null;
updateCalendarEvents(); updateCalendarEvents();
// //
if (halfDayButtonsRef.value) { if (halfDayButtonsRef.value) {
halfDayButtonsRef.value.resetHalfDay(); halfDayButtonsRef.value.resetHalfDay();
} }
@ -417,22 +417,22 @@ function handleDateClick(info) {
halfDayType.value = halfDayType.value === type ? null : type; halfDayType.value = halfDayType.value === type ? null : type;
} }
async function fetchVacationData(year, month) { //
async function fetchVacationData(year, month) {
try { try {
const response = await axios.get(`vacation/list/${year}/${month}`); const response = await axios.get(`vacation/list/${year}/${month}`);
if (response.status === 200) { if (response.status === 200) {
const vacationList = response.data; const vacationList = response.data;
if (lastRemainingYear.value !== year) {
myVacations.value = vacationList.filter( //
(vac) => vac.MEMBERSEQ === userStore.user.id const filteredVacations = vacationList.filter(vac =>
userColors.value[vac.MEMBERSEQ] && userColors.value[vac.MEMBERSEQ] !== "#FFFFFF"
); );
lastRemainingYear.value = year;
} const events = filteredVacations.map(vac => {
const events = vacationList
.filter((vac) => !vac.LOCVACRMM)
.map((vac) => {
let dateStr = vac.LOCVACUDT ? vac.LOCVACUDT.split("T")[0] : ""; let dateStr = vac.LOCVACUDT ? vac.LOCVACUDT.split("T")[0] : "";
let backgroundColor = userColors.value[vac.MEMBERSEQ] || "#FFFFFF"; let backgroundColor = userColors.value[vac.MEMBERSEQ];
return { return {
title: getVacationType(vac.LOCVACTYP), title: getVacationType(vac.LOCVACTYP),
start: dateStr, start: dateStr,
@ -441,8 +441,8 @@ function handleDateClick(info) {
saved: true, saved: true,
memberSeq: vac.MEMBERSEQ, memberSeq: vac.MEMBERSEQ,
}; };
}) }).filter(event => event.start);
.filter((event) => event.start);
return events; return events;
} else { } else {
console.warn("📌 휴가 데이터를 불러오지 못함"); console.warn("📌 휴가 데이터를 불러오지 못함");
@ -452,7 +452,7 @@ function handleDateClick(info) {
console.error("Error fetching vacation data:", error); console.error("Error fetching vacation data:", error);
return []; return [];
} }
} }
async function saveVacationChanges() { async function saveVacationChanges() {
if (!hasChanges.value) return; if (!hasChanges.value) return;
@ -522,7 +522,7 @@ function handleDateClick(info) {
await nextTick(); await nextTick();
fullCalendarRef.value.getApi().refetchEvents(); fullCalendarRef.value.getApi().refetchEvents();
} }
/** 오늘 이후의 날짜만 클릭 가능하도록 설정 */ /** 오늘 이후의 날짜만 클릭 가능하도록 설정 */
function markClickableDates() { function markClickableDates() {
nextTick(() => { nextTick(() => {
const todayStr = new Date().toISOString().split("T")[0]; // YYYY-MM-DD const todayStr = new Date().toISOString().split("T")[0]; // YYYY-MM-DD