Merge branch 'main' of http://192.168.0.251:3000/localnet/localhost-front
This commit is contained in:
commit
c3b1331128
@ -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;
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.5 KiB |
@ -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 });
|
||||||
|
|||||||
@ -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>
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user