사원리스트 모달추가

This commit is contained in:
dyhj625 2025-02-14 16:22:42 +09:00
parent fbc578c307
commit 5a4b770fa1
3 changed files with 149 additions and 31 deletions

View File

@ -1,49 +1,60 @@
<template> <template>
<div v-if="isOpen" class="modal-dialog" @click.self="closeModal"> <div v-if="isOpen" class="modal-dialog" @click.self="closeModal">
<div class="modal-content"> <div class="modal-content">
<h5 class="modal-title">📅 {{ targetUser.name }}님의 연차 부여</h5> <h5 class="modal-title">To. {{ targetUser.MEMBERNAM }} 🎁</h5>
<button class="close-btn" @click="closeModal"></button> <button class="close-btn" @click="closeModal"></button>
<div class="modal-body"> <div class="modal-body">
<p>해당 직원에게 부여할 연차 개수를 선택하세요. (최대 2)</p> <p>해당 직원에게 부여할 연차 개수를 선택하세요. (남은 개수: {{ availableQuota }})</p>
<div class="vacation-item"> <div class="vacation-control">
<button @click="decreaseCount" :disabled="grantCount <= 0">-</button> <button @click="decreaseCount" :disabled="grantCount <= 0" class="count-btn">-</button>
<span>{{ grantCount }}</span> <span class="grant-count">{{ grantCount }}</span>
<button @click="increaseCount" :disabled="grantCount >= maxQuota">+</button> <button @click="increaseCount" :disabled="grantCount >= availableQuota" class="count-btn">+</button>
</div> </div>
<p class="">현재 보유 연차: <strong>{{ remainingQuota }}</strong></p> <button class="gift-btn" @click="saveVacationGrant" :disabled="grantCount === 0">
</div> <i class="bx bx-gift"></i> <!-- 선물상자 아이콘 -->
<div class="">
<button class="save-btn" @click="saveVacationGrant" :disabled="grantCount === 0">
저장
</button> </button>
<button class="cancel-btn" @click="closeModal">취소</button>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, defineProps, defineEmits, watch } from "vue"; import { ref, defineProps, defineEmits, watch, onMounted } from "vue";
import axios from "@api"; import axios from "@api";
const props = defineProps({ const props = defineProps({
isOpen: Boolean, // isOpen: Boolean, //
targetUser: Object, // targetUser: Object, //
remainingQuota: Number, //
}); });
const emit = defineEmits(["close", "updateVacation"]); const emit = defineEmits(["close", "updateVacation"]);
const grantCount = ref(0); const grantCount = ref(0);
const maxQuota = 2; // const maxQuota = 2; // 1
const sentCount = ref(0); //
const availableQuota = ref(2); //
//
const fetchSentVacationCount = async () => {
try {
const payload = { receiverId: props.targetUser.MEMBERSEQ };
const response = await axios.get(`vacation/sent`,{ params: payload });
console.log(response.data.data[0].count)
sentCount.value = response.data.data[0].count || 0; //
availableQuota.value = Math.max(maxQuota - sentCount.value, 0); // (0 )
console.log(`✅ 보낸 개수: ${sentCount.value}, 남은 개수: ${availableQuota.value}`);
} catch (error) {
console.error("🚨 연차 전송 기록 조회 실패:", error);
availableQuota.value = maxQuota;
}
};
// //
const increaseCount = () => { const increaseCount = () => {
if (grantCount.value < maxQuota) { if (grantCount.value < availableQuota.value) {
grantCount.value++; grantCount.value++;
} }
}; };
@ -55,18 +66,24 @@
} }
}; };
// // (saveVacations API )
const saveVacationGrant = async () => { const saveVacationGrant = async () => {
try { try {
const response = await axios.post("vacation/grant", { const payload = [
senderId: props.targetUser.senderId, // ID {
receiverId: props.targetUser.MEMBERSEQ, // ID date: new Date().toISOString().split("T")[0], //
type: "700103", // ( ) type: "700103", //
count: grantCount.value, // senderId: props.targetUser.senderId, // ID
}); receiverId: props.targetUser.MEMBERSEQ, // ID
count: grantCount.value, //
},
];
const response = await axios.post("vacation/save", payload);
if (response.data && response.data.status === "OK") { if (response.data && response.data.status === "OK") {
alert("✅ 연차가 부여되었습니다."); alert("✅ 연차가 부여되었습니다.");
await fetchSentVacationCount(); //
emit("updateVacation"); // emit("updateVacation"); //
closeModal(); closeModal();
} else { } else {
@ -83,13 +100,115 @@
emit("close"); emit("close");
}; };
// //
watch(() => props.isOpen, (newVal) => { watch(
if (newVal) { () => props.isOpen,
async (newVal) => {
if (newVal && props.targetUser && props.targetUser.MEMBERSEQ) {
console.log("🟢 모달이 열렸습니다. 데이터를 로드합니다.");
grantCount.value = 0; // grantCount.value = 0; //
await fetchSentVacationCount(); //
}
}
);
// targetUser fetchSentVacationCount
watch(
() => props.targetUser,
async (newUser) => {
if (newUser && newUser.MEMBERSEQ) {
console.log(`🔄 새로운 대상(${newUser.name})이 선택되었습니다.`);
await fetchSentVacationCount();
}
},
{ deep: true }
);
//
onMounted(async () => {
if (props.isOpen && props.targetUser && props.targetUser.MEMBERSEQ) {
await fetchSentVacationCount();
} }
}); });
</script> </script>
<style scoped> <style scoped>
/* 모달 본문 */
.modal-content {
background: white;
padding: 20px;
border-radius: 12px;
width: 400px;
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.15);
text-align: center;
position: relative;
}
/* 닫기 버튼 */
.close-btn {
position: absolute;
top: 10px;
right: 10px;
background: none;
border: none;
font-size: 18px;
cursor: pointer;
}
/* 연차 개수 조정 버튼 */
.vacation-control {
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
margin-top: 15px;
}
.count-btn {
font-size: 18px;
padding: 6px 12px;
border: none;
background: #007bff;
color: white;
border-radius: 5px;
cursor: pointer;
}
.count-btn:disabled {
background: #ccc;
cursor: not-allowed;
}
/* 개수 표시 */
.grant-count {
font-size: 20px;
font-weight: bold;
color: #333;
}
/* 선물 아이콘 버튼 */
.gift-btn {
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
background: #28a745;
color: white;
border: none;
border-radius: 8px;
padding: 10px 15px;
margin-top: 15px;
cursor: pointer;
transition: 0.3s;
}
.gift-btn:hover {
background: #218838;
}
.gift-btn:disabled {
background: #ccc;
cursor: not-allowed;
}
</style> </style>

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="card-body d-flex justify-content-center"> <div class="card-body d-flex justify-content-center">
<ul class="list-unstyled d-flex align-items-center gap-7 mb-0 mt-2"> <ul class="list-unstyled d-flex align-items-center gap-3 mb-0 mt-2">
<li <li
v-for="(user, index) in sortedUserList" v-for="(user, index) in sortedUserList"
:key="index" :key="index"

View File

@ -83,7 +83,6 @@ const fetchRemainingVacation = async () => {
try { try {
const response = await axios.get("vacation/remaining"); const response = await axios.get("vacation/remaining");
if (response.status === 200) { if (response.status === 200) {
console.log("✅ 남은 연차 데이터:", response.data);
remainingVacationData.value = response.data.data.reduce((acc, vacation) => { remainingVacationData.value = response.data.data.reduce((acc, vacation) => {
acc[vacation.employeeId] = vacation.remainingQuota; acc[vacation.employeeId] = vacation.remainingQuota;
return acc; return acc;