Merge branch 'main' into wordDict
This commit is contained in:
commit
d6883c3a34
@ -37,7 +37,7 @@ $api.interceptors.response.use(
|
|||||||
},
|
},
|
||||||
function (error) {
|
function (error) {
|
||||||
const toastStore = useToastStore();
|
const toastStore = useToastStore();
|
||||||
//const currentPage = error.config.headers['X-Page-Route'];
|
const currentPage = error.config.headers['X-Page-Route'];
|
||||||
// 오류 응답 처리
|
// 오류 응답 처리
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
switch (error.response.status) {
|
switch (error.response.status) {
|
||||||
|
|||||||
@ -68,7 +68,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, defineEmits, ref } from 'vue';
|
import { defineProps, defineEmits, ref } from 'vue';
|
||||||
import BoardProfile from './BoardProfile.vue';
|
import BoardProfile from './BoardProfile.vue';
|
||||||
import BoardCommentArea from './BoardComentArea.vue';
|
import BoardCommentArea from './BoardCommentArea.vue';
|
||||||
import PlusButton from '../button/PlusBtn.vue';
|
import PlusButton from '../button/PlusBtn.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
<div class="d-flex justify-content-start align-items-top">
|
<div class="d-flex justify-content-start align-items-top">
|
||||||
<!-- 프로필섹션 -->
|
<!-- 프로필섹션 -->
|
||||||
<div class="avatar-wrapper">
|
<div class="avatar-wrapper">
|
||||||
<div class="avatar me-4">
|
<div v-if="!unknown" class="avatar me-4">
|
||||||
<img src="/img/avatars/11.png" alt="Avatar" class="rounded-circle">
|
<img src="/img/avatars/11.png" alt="Avatar" class="rounded-circle">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -13,8 +13,9 @@
|
|||||||
<div class="w-100">
|
<div class="w-100">
|
||||||
<textarea
|
<textarea
|
||||||
class="form-control"
|
class="form-control"
|
||||||
placeholder="주제에 대한 생각을 자유롭게 댓글로 표현해 주세요. 여러분의 다양한 의견을 기다립니다."
|
placeholder="댓글 달기"
|
||||||
rows="3"
|
rows="3"
|
||||||
|
v-model="comment"
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -23,7 +24,7 @@
|
|||||||
<div class="d-flex justify-content-between flex-wrap mt-4">
|
<div class="d-flex justify-content-between flex-wrap mt-4">
|
||||||
<div class="d-flex flex-wrap align-items-center">
|
<div class="d-flex flex-wrap align-items-center">
|
||||||
<!-- 익명 체크박스 (익명게시판일 경우에만)-->
|
<!-- 익명 체크박스 (익명게시판일 경우에만)-->
|
||||||
<div class="form-check form-check-inline mb-0 me-4">
|
<div v-if="unknown" class="form-check form-check-inline mb-0 me-4">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@ -40,15 +41,16 @@
|
|||||||
type="password"
|
type="password"
|
||||||
id="basic-default-password"
|
id="basic-default-password"
|
||||||
class="form-control flex-grow-1"
|
class="form-control flex-grow-1"
|
||||||
placeholder=""
|
v-model="password"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 답변 쓰기 버튼 -->
|
<!-- 답변 쓰기 버튼 -->
|
||||||
<div class="ms-auto mt-3 mt-md-0">
|
<div class="ms-auto mt-3 mt-md-0">
|
||||||
<button class="btn btn-primary">
|
<button class="btn btn-primary" @click="handleCommentSubmit">
|
||||||
<i class="icon-base bx bx-check"></i>
|
<!-- <i class="icon-base bx bx-check"></i> -->
|
||||||
|
확인
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -57,6 +59,40 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
import { ref, defineEmits, defineProps, computed, watch } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
unknown: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
parentId: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const comment = ref('');
|
||||||
|
const password = ref('');
|
||||||
const isCheck = ref(false);
|
const isCheck = ref(false);
|
||||||
|
|
||||||
|
const emit = defineEmits(['submitComment']);
|
||||||
|
|
||||||
|
watch(() => props.unknown, (newVal) => {
|
||||||
|
if (!newVal) {
|
||||||
|
isCheck.value = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleCommentSubmit() {
|
||||||
|
emit('submitComment', {
|
||||||
|
comment: comment.value,
|
||||||
|
password: password.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
comment.value = '';
|
||||||
|
password.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@ -63,7 +63,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, ref } from 'vue';
|
import { defineProps, ref } from 'vue';
|
||||||
import UserList from '@c/user/UserList.vue';
|
import UserList from '@c/user/UserList.vue';
|
||||||
import CenterModal from '../modal/CenterModal.vue';
|
import CenterModal from '@c/modal/CenterModal.vue';
|
||||||
import $api from '@api';
|
import $api from '@api';
|
||||||
|
|
||||||
// Props 정의
|
// Props 정의
|
||||||
|
|||||||
@ -34,7 +34,7 @@
|
|||||||
:is-label="true"
|
:is-label="true"
|
||||||
:is-common="true"
|
:is-common="true"
|
||||||
:data="allColors"
|
:data="allColors"
|
||||||
v-model="selectedProject.projctcolor"
|
v-model="selectedProject.PROJCTCOL"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormInput
|
<FormInput
|
||||||
@ -85,19 +85,23 @@
|
|||||||
import FormInput from '@c/input/FormInput.vue';
|
import FormInput from '@c/input/FormInput.vue';
|
||||||
import FormSelect from '@c/input/FormSelect.vue';
|
import FormSelect from '@c/input/FormSelect.vue';
|
||||||
import commonApi from '@/common/commonApi';
|
import commonApi from '@/common/commonApi';
|
||||||
import ArrInput from '../input/ArrInput.vue';
|
import ArrInput from '@c/input/ArrInput.vue';
|
||||||
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
||||||
|
import { useToastStore } from '@s/toastStore';
|
||||||
|
|
||||||
|
const toastStore = useToastStore();
|
||||||
|
|
||||||
const projectList = ref([]);
|
const projectList = ref([]);
|
||||||
const isModalOpen = ref(false);
|
const isModalOpen = ref(false);
|
||||||
|
let originalColor = ref('');
|
||||||
|
|
||||||
const userStore = useUserInfoStore();
|
const userStore = useUserInfoStore();
|
||||||
const user = ref(null);
|
const user = ref(null);
|
||||||
|
|
||||||
const nameAlert = ref(false);
|
const nameAlert = ref(false);
|
||||||
const selectedProject = ref({
|
const selectedProject = ref({
|
||||||
|
PROJCTSEQ:'',
|
||||||
PROJCTNAM: '',
|
PROJCTNAM: '',
|
||||||
projctcolor: '',
|
|
||||||
PROJCTSTR: '',
|
PROJCTSTR: '',
|
||||||
PROJCTEND: '',
|
PROJCTEND: '',
|
||||||
PROJCTZIP: '',
|
PROJCTZIP: '',
|
||||||
@ -105,6 +109,7 @@
|
|||||||
PROJCTDTL: '',
|
PROJCTDTL: '',
|
||||||
PROJCTDES: '',
|
PROJCTDES: '',
|
||||||
PROJCTCOL: '',
|
PROJCTCOL: '',
|
||||||
|
projctcolor:'',
|
||||||
});
|
});
|
||||||
|
|
||||||
const { colorList } = commonApi({
|
const { colorList } = commonApi({
|
||||||
@ -124,13 +129,14 @@
|
|||||||
const getProjectList = () => {
|
const getProjectList = () => {
|
||||||
$api.get('project/select').then(res => {
|
$api.get('project/select').then(res => {
|
||||||
projectList.value = res.data.data.projectList;
|
projectList.value = res.data.data.projectList;
|
||||||
console.log(projectList.value);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const openModal = (post) => {
|
const openModal = (post) => {
|
||||||
isModalOpen.value = true;
|
isModalOpen.value = true;
|
||||||
|
|
||||||
|
originalColor.value = post.PROJCTCOL;
|
||||||
selectedProject.value = { ...post };
|
selectedProject.value = { ...post };
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -156,20 +162,23 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(projectList.PROJCTSEQ)
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
|
|
||||||
|
console.log(selectedProject.value.PROJCTCOL)
|
||||||
|
console.log(originalColor.value)
|
||||||
|
|
||||||
$api.patch('project/update', {
|
$api.patch('project/update', {
|
||||||
projctSeq: projectList.PROJCTSEQ,
|
projctSeq: selectedProject.value.PROJCTSEQ,
|
||||||
projctNam: selectedProject.value.PROJCTNAM,
|
projctNam: selectedProject.value.PROJCTNAM,
|
||||||
projctCol: selectedProject.value.projctcolor,
|
projctCol: selectedProject.value.PROJCTCOL,
|
||||||
projctArr: selectedProject.value.PROJCTARR,
|
projctArr: selectedProject.value.PROJCTARR,
|
||||||
projctDtl: selectedProject.value.PROJCTDTL,
|
projctDtl: selectedProject.value.PROJCTDTL,
|
||||||
projctZip: selectedProject.value.PROJCTZIP,
|
projctZip: selectedProject.value.PROJCTZIP,
|
||||||
projctStr: selectedProject.value.PROJCTSTR,
|
projctStr: selectedProject.value.PROJCTSTR,
|
||||||
projctEnd: selectedProject.value.PROJCTEND,
|
projctEnd: selectedProject.value.PROJCTEND || null,
|
||||||
projctDes: selectedProject.value.PROJCTDES,
|
projctDes: selectedProject.value.PROJCTDES,
|
||||||
projctUmb: user.value.name,
|
projctUmb: user.value.name,
|
||||||
|
originalColor: originalColor.value === selectedProject.value.PROJCTCOL ? null : originalColor.value
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
toastStore.onToast('수정이 완료 되었습니다.', 's');
|
toastStore.onToast('수정이 완료 되었습니다.', 's');
|
||||||
|
|||||||
@ -8,13 +8,13 @@
|
|||||||
<p>해당 직원에게 부여할 연차 개수를 선택하세요. (남은 개수: {{ availableQuota }}개)</p>
|
<p>해당 직원에게 부여할 연차 개수를 선택하세요. (남은 개수: {{ availableQuota }}개)</p>
|
||||||
|
|
||||||
<div class="vacation-control">
|
<div class="vacation-control">
|
||||||
<button @click="decreaseCount" :disabled="grantCount <= 0" class="count-btn">-</button>
|
<button @click="decreaseCount" :disabled="grantCount < 2" class="count-btn">-</button>
|
||||||
<span class="grant-count">{{ grantCount }}</span>
|
<span class="grant-count">{{ grantCount }}</span>
|
||||||
<button @click="increaseCount" :disabled="grantCount >= availableQuota" class="count-btn">+</button>
|
<button @click="increaseCount" :disabled="grantCount >= availableQuota" class="count-btn">+</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="gift-btn" @click="saveVacationGrant" :disabled="grantCount === 0">
|
<button class="gift-btn" @click="saveVacationGrant" :disabled="grantCount === 0">
|
||||||
<i class="bx bx-gift"></i> <!-- 선물상자 아이콘 -->
|
<i class="bx bx-gift"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -26,65 +26,60 @@
|
|||||||
import axios from "@api";
|
import axios from "@api";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
isOpen: Boolean, // 모달 상태
|
isOpen: Boolean,
|
||||||
targetUser: Object, // 선택한 사용자 정보
|
targetUser: Object,
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(["close", "updateVacation"]);
|
const emit = defineEmits(["close", "updateVacation"]);
|
||||||
const grantCount = ref(0);
|
const grantCount = ref(0);
|
||||||
const maxQuota = 2; // 1년에 보낼 수 있는 최대 개수
|
const maxQuota = 2;
|
||||||
const sentCount = ref(0); // 현재까지 보낸 개수
|
const sentCount = ref(0);
|
||||||
const availableQuota = ref(2); // 남은 개수
|
const availableQuota = ref(2);
|
||||||
|
|
||||||
// ✅ 해당 사용자에게 이미 보낸 연차 개수 조회
|
|
||||||
const fetchSentVacationCount = async () => {
|
const fetchSentVacationCount = async () => {
|
||||||
try {
|
try {
|
||||||
const payload = { receiverId: props.targetUser.MEMBERSEQ };
|
const payload = { receiverId: props.targetUser.MEMBERSEQ };
|
||||||
|
const response = await axios.get("vacation/sent", { params: payload });
|
||||||
const response = await axios.get(`vacation/sent`,{ params: payload });
|
sentCount.value = response.data.data[0].count || 0;
|
||||||
console.log(response.data.data[0].count)
|
availableQuota.value = Math.max(maxQuota - sentCount.value, 0);
|
||||||
sentCount.value = response.data.data[0].count || 0; // 이미 보낸 개수
|
grantCount.value = availableQuota.value; // ✅ 남은 개수로 기본값 설정
|
||||||
availableQuota.value = Math.max(maxQuota - sentCount.value, 0); // 남은 개수 (0 이하 방지)
|
|
||||||
console.log(`✅ 보낸 개수: ${sentCount.value}, 남은 개수: ${availableQuota.value}`);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("🚨 연차 전송 기록 조회 실패:", error);
|
console.error("🚨 연차 전송 기록 조회 실패:", error);
|
||||||
availableQuota.value = maxQuota;
|
availableQuota.value = maxQuota;
|
||||||
|
grantCount.value = maxQuota; // 기본값 설정
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ✅ 연차 개수 증가
|
|
||||||
const increaseCount = () => {
|
const increaseCount = () => {
|
||||||
if (grantCount.value < availableQuota.value) {
|
if (grantCount.value < availableQuota.value) {
|
||||||
grantCount.value++;
|
grantCount.value++;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ✅ 연차 개수 감소
|
|
||||||
const decreaseCount = () => {
|
const decreaseCount = () => {
|
||||||
if (grantCount.value > 0) {
|
if (grantCount.value > 0) {
|
||||||
grantCount.value--;
|
grantCount.value--;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ✅ 연차 부여 저장 요청 (saveVacations API 호출)
|
|
||||||
const saveVacationGrant = async () => {
|
const saveVacationGrant = async () => {
|
||||||
try {
|
try {
|
||||||
const payload = [
|
const payload = [
|
||||||
{
|
{
|
||||||
date: new Date().toISOString().split("T")[0], // 오늘 날짜
|
date: new Date().toISOString().split("T")[0],
|
||||||
type: "700103", // 연차 코드
|
type: "700103",
|
||||||
senderId: props.targetUser.senderId, // 보내는 사람 ID
|
senderId: props.targetUser.senderId,
|
||||||
receiverId: props.targetUser.MEMBERSEQ, // 받는 사람 ID
|
receiverId: props.targetUser.MEMBERSEQ,
|
||||||
count: grantCount.value, // 부여 개수
|
count: grantCount.value,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const response = await axios.post("vacation/save", payload);
|
const response = await axios.post("vacation", payload);
|
||||||
|
console.log(response)
|
||||||
if (response.data && response.data.status === "OK") {
|
if (response.data && response.data.status === "OK") {
|
||||||
alert("✅ 연차가 부여되었습니다.");
|
alert("✅ 연차가 부여되었습니다.");
|
||||||
await fetchSentVacationCount(); // ✅ 보낸 개수 업데이트
|
await fetchSentVacationCount();
|
||||||
emit("updateVacation"); // ✅ 연차 정보 갱신 요청
|
emit("updateVacation");
|
||||||
closeModal();
|
closeModal();
|
||||||
} else {
|
} else {
|
||||||
alert("🚨 연차 추가 중 오류가 발생했습니다.");
|
alert("🚨 연차 추가 중 오류가 발생했습니다.");
|
||||||
@ -95,36 +90,30 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ✅ 모달 닫기
|
|
||||||
const closeModal = () => {
|
const closeModal = () => {
|
||||||
emit("close");
|
emit("close");
|
||||||
};
|
};
|
||||||
|
|
||||||
// ✅ 모달이 열릴 때 초기 값 설정 및 보낸 개수 조회
|
|
||||||
watch(
|
watch(
|
||||||
() => props.isOpen,
|
() => props.isOpen,
|
||||||
async (newVal) => {
|
async (newVal) => {
|
||||||
if (newVal && props.targetUser && props.targetUser.MEMBERSEQ) {
|
if (newVal && props.targetUser && props.targetUser.MEMBERSEQ) {
|
||||||
console.log("🟢 모달이 열렸습니다. 데이터를 로드합니다.");
|
console.log("🟢 모달이 열렸습니다. 데이터를 로드합니다.");
|
||||||
grantCount.value = 0; // 초기화
|
await fetchSentVacationCount();
|
||||||
await fetchSentVacationCount(); // 보낸 개수 불러오기
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// ✅ targetUser가 변경될 때도 fetchSentVacationCount 호출
|
|
||||||
watch(
|
watch(
|
||||||
() => props.targetUser,
|
() => props.targetUser,
|
||||||
async (newUser) => {
|
async (newUser) => {
|
||||||
if (newUser && newUser.MEMBERSEQ) {
|
if (newUser && newUser.MEMBERSEQ) {
|
||||||
console.log(`🔄 새로운 대상(${newUser.name})이 선택되었습니다.`);
|
|
||||||
await fetchSentVacationCount();
|
await fetchSentVacationCount();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ deep: true }
|
{ deep: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
// ✅ 컴포넌트가 마운트될 때도 보낸 개수 불러오기
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
if (props.isOpen && props.targetUser && props.targetUser.MEMBERSEQ) {
|
if (props.isOpen && props.targetUser && props.targetUser.MEMBERSEQ) {
|
||||||
await fetchSentVacationCount();
|
await fetchSentVacationCount();
|
||||||
@ -132,6 +121,7 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
/* 모달 본문 */
|
/* 모달 본문 */
|
||||||
|
|||||||
@ -1,165 +1,186 @@
|
|||||||
<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 modal-scroll">
|
<div class="modal-content 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>
|
||||||
|
|
||||||
<!-- 연차 사용 및 받은 연차 리스트 -->
|
<!-- 연차 사용 및 받은 연차 리스트 -->
|
||||||
<div class="modal-body" v-if="mergedVacations.length > 0">
|
<div class="modal-body" v-if="mergedVacations.length > 0">
|
||||||
<ol class="vacation-list">
|
<ol class="vacation-list">
|
||||||
<li
|
<li v-for="(vacation, index) in mergedVacations" :key="index" class="vacation-item">
|
||||||
v-for="(vacation, index) in mergedVacations"
|
<span v-if="vacation.type === 'used'" class="vacation-index">
|
||||||
:key="index"
|
{{ getVacationIndex(index) }})
|
||||||
class="vacation-item"
|
</span>
|
||||||
>
|
<span :class="vacation.type === 'used' ? 'minus-symbol' : 'plus-symbol'">
|
||||||
<span v-if="vacation.type === 'used'" class="vacation-index">
|
{{ vacation.type === 'used' ? '-' : '+' }}
|
||||||
{{ totalUsedVacationCount - usedVacations.findIndex(v => v.date === vacation.date) }})
|
</span>
|
||||||
</span>
|
<span
|
||||||
<span :class="vacation.type === 'used' ? 'minus-symbol' : 'plus-symbol'">
|
:style="{ color: userColors[vacation.senderId || vacation.receiverId] || '#000' }"
|
||||||
{{ vacation.type === 'used' ? '-' : '+' }}
|
class="vacation-date"
|
||||||
</span>
|
>
|
||||||
<span :style="{ color: userColors[vacation.senderId || vacation.receiverId] || '#000' }"
|
{{ formatDate(vacation.date) }}
|
||||||
class="vacation-date">{{ formatDate(vacation.date) }}</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 연차 데이터 없음 -->
|
<!-- 연차 데이터 없음 -->
|
||||||
<p v-if="mergedVacations.length === 0" class="no-data">
|
<p v-if="mergedVacations.length === 0" class="no-data">
|
||||||
🚫 사용한 연차가 없습니다.
|
🚫 사용한 연차가 없습니다.
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, defineEmits, computed } from "vue";
|
import { defineProps, defineEmits, computed } from "vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
isOpen: Boolean,
|
isOpen: Boolean,
|
||||||
myVacations: {
|
myVacations: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
receivedVacations: {
|
receivedVacations: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
userColors: {
|
userColors: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(["close"]);
|
const emit = defineEmits(["close"]);
|
||||||
|
|
||||||
// ✅ 사용한 연차 개수
|
|
||||||
const totalUsedVacationCount = computed(() => props.myVacations.length);
|
|
||||||
|
|
||||||
// ✅ 사용한 연차 + 받은 연차 통합 후 내림차순 정렬
|
// ✅ 사용한 연차 + 받은 연차 통합 후 내림차순 정렬
|
||||||
const usedVacations = computed(() => props.myVacations.map(v => ({ ...v, type: "used" })));
|
const usedVacations = computed(() =>
|
||||||
const receivedVacations = computed(() => props.receivedVacations.map(v => ({ ...v, type: "received" })));
|
props.myVacations.map(v => ({ ...v, type: "used" }))
|
||||||
|
|
||||||
const mergedVacations = computed(() => {
|
|
||||||
return [...usedVacations.value, ...receivedVacations.value].sort(
|
|
||||||
(a, b) => new Date(b.date) - new Date(a.date)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const receivedVacations = computed(() =>
|
||||||
|
props.receivedVacations.map(v => ({ ...v, type: "received" }))
|
||||||
|
);
|
||||||
|
|
||||||
|
// ✅ 정확한 정렬 및 리스트 병합
|
||||||
|
const mergedVacations = computed(() => {
|
||||||
|
return [...usedVacations.value, ...receivedVacations.value].sort(
|
||||||
|
(a, b) => new Date(b.date) - new Date(a.date)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ✅ 연차 개수 반영된 인덱스 반환 (누적 합산)
|
||||||
|
const getVacationIndex = (index) => {
|
||||||
|
let count = 0;
|
||||||
|
for (let i = 0; i <= index; i++) {
|
||||||
|
const v = mergedVacations.value[i];
|
||||||
|
count += v.used_quota; // 누적하여 더함
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
};
|
||||||
|
|
||||||
// ✅ 날짜 형식 변환 (YYYY-MM-DD)
|
// ✅ 날짜 형식 변환 (YYYY-MM-DD)
|
||||||
const formatDate = (dateString) => {
|
const formatDate = (dateString) => {
|
||||||
const date = new Date(dateString);
|
const date = new Date(dateString);
|
||||||
return date.toISOString().split("T")[0]; // YYYY-MM-DD 형식
|
return date.toISOString().split("T")[0]; // YYYY-MM-DD 형식
|
||||||
};
|
};
|
||||||
|
|
||||||
const closeModal = () => {
|
const closeModal = () => {
|
||||||
emit("close");
|
emit("close");
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 모달 스타일 */
|
/* 모달 스타일 */
|
||||||
.modal-dialog {
|
.modal-dialog {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 스크롤 가능한 모달 */
|
/* 스크롤 가능한 모달 */
|
||||||
.modal-content {
|
.modal-content {
|
||||||
max-height: 60vh;
|
max-height: 60vh;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
width: 75%;
|
width: 75%;
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
|
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 닫기 버튼 */
|
/* 닫기 버튼 */
|
||||||
.close-btn {
|
.close-btn {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 리스트 기본 스타일 */
|
/* 리스트 기본 스타일 */
|
||||||
.vacation-list {
|
.vacation-list {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 리스트 아이템 */
|
/* 리스트 아이템 */
|
||||||
.vacation-item {
|
.vacation-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background: #f9f9f9;
|
background: #f9f9f9;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 인덱스 (연차 사용 개수) */
|
/* 인덱스 (연차 사용 개수) */
|
||||||
.vacation-index {
|
.vacation-index {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* "-" 빨간색 */
|
/* "-" 빨간색 */
|
||||||
.minus-symbol {
|
.minus-symbol {
|
||||||
color: red;
|
color: red;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* "+" 파란색 */
|
/* "+" 파란색 */
|
||||||
.plus-symbol {
|
.plus-symbol {
|
||||||
color: blue;
|
color: blue;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 날짜 스타일 */
|
/* 날짜 스타일 */
|
||||||
.vacation-date {
|
.vacation-date {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #333;
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 연차 유형 스타일 */
|
||||||
|
.vacation-type {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: normal;
|
||||||
|
color: gray;
|
||||||
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 연차 데이터 없음 */
|
/* 연차 데이터 없음 */
|
||||||
.no-data {
|
.no-data {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: gray;
|
color: gray;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -190,6 +190,8 @@ const fetchGeneralPosts = async (page = 1) => {
|
|||||||
console.log(data)
|
console.log(data)
|
||||||
const totalPosts = data.data.total; // 전체 게시물 개수 받아오기
|
const totalPosts = data.data.total; // 전체 게시물 개수 받아오기
|
||||||
|
|
||||||
|
console.log('📌 API 응답 데이터:', data.data);
|
||||||
|
|
||||||
generalList.value = data.data.list.map((post, index) => ({
|
generalList.value = data.data.list.map((post, index) => ({
|
||||||
realId: post.id,
|
realId: post.id,
|
||||||
id: totalPosts - ((page - 1) * selectedSize.value) - index,
|
id: totalPosts - ((page - 1) * selectedSize.value) - index,
|
||||||
|
|||||||
@ -94,7 +94,11 @@
|
|||||||
@updateReaction="handleUpdateReaction"
|
@updateReaction="handleUpdateReaction"
|
||||||
@submitComment="handleCommentReply"
|
@submitComment="handleCommentReply"
|
||||||
/>
|
/>
|
||||||
<Pagination/>
|
<Pagination
|
||||||
|
v-if="pagination.pages"
|
||||||
|
v-bind="pagination"
|
||||||
|
@update:currentPage="handlePageChange"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -103,7 +107,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import BoardCommentArea from '@c/board/BoardComentArea.vue';
|
import BoardCommentArea from '@/components/board/BoardCommentArea.vue';
|
||||||
import BoardProfile from '@c/board/BoardProfile.vue';
|
import BoardProfile from '@c/board/BoardProfile.vue';
|
||||||
import BoardCommentList from '@c/board/BoardCommentList.vue';
|
import BoardCommentList from '@c/board/BoardCommentList.vue';
|
||||||
import BoardRecommendBtn from '@c/button/BoardRecommendBtn.vue';
|
import BoardRecommendBtn from '@c/button/BoardRecommendBtn.vue';
|
||||||
@ -141,6 +145,21 @@ const passwordAlert = ref("");
|
|||||||
const isPassword = ref(false);
|
const isPassword = ref(false);
|
||||||
const lastClickedButton = ref("");
|
const lastClickedButton = ref("");
|
||||||
|
|
||||||
|
const pagination = ref({
|
||||||
|
currentPage: 1,
|
||||||
|
pages: 1,
|
||||||
|
prePage: 0,
|
||||||
|
nextPage: 1,
|
||||||
|
isFirstPage: true,
|
||||||
|
isLastPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
hasNextPage: false,
|
||||||
|
navigatePages: 10,
|
||||||
|
navigatepageNums: [1],
|
||||||
|
navigateFirstPage: 1,
|
||||||
|
navigateLastPage: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// 게시물 상세 데이터 불러오기
|
// 게시물 상세 데이터 불러오기
|
||||||
const fetchBoardDetails = async () => {
|
const fetchBoardDetails = async () => {
|
||||||
@ -202,10 +221,13 @@ const handleUpdateReaction = async ({ boardId, commentId, isLike, isDislike }) =
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 댓글 목록 조회
|
// 댓글 목록 조회
|
||||||
const fetchComments = async () => {
|
const fetchComments = async (page = 1) => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`board/${currentBoardId.value}/comments`, {
|
const response = await axios.get(`board/${currentBoardId.value}/comments`, {
|
||||||
params: { LOCBRDSEQ: currentBoardId.value }
|
params: {
|
||||||
|
LOCBRDSEQ: currentBoardId.value,
|
||||||
|
page
|
||||||
|
}
|
||||||
});
|
});
|
||||||
console.log("목록 API 응답 데이터:", response.data);
|
console.log("목록 API 응답 데이터:", response.data);
|
||||||
|
|
||||||
@ -240,6 +262,22 @@ const fetchComments = async () => {
|
|||||||
|
|
||||||
// console.log("변환된 comments 데이터:", comments.value);
|
// console.log("변환된 comments 데이터:", comments.value);
|
||||||
|
|
||||||
|
pagination.value = {
|
||||||
|
...pagination.value,
|
||||||
|
currentPage: response.data.data.pageNum, // 현재 페이지 번호
|
||||||
|
pages: response.data.data.pages, // 전체 페이지 수
|
||||||
|
prePage: response.data.data.prePage, // 이전 페이지
|
||||||
|
nextPage: response.data.data.nextPage, // 다음 페이지
|
||||||
|
isFirstPage: response.data.data.isFirstPage, // 첫 페이지 여부
|
||||||
|
isLastPage: response.data.data.isLastPage, // 마지막 페이지 여부
|
||||||
|
hasPreviousPage: response.data.data.hasPreviousPage, // 이전 페이지 존재 여부
|
||||||
|
hasNextPage: response.data.data.hasNextPage, // 다음 페이지 존재 여부
|
||||||
|
navigatePages: response.data.data.navigatePages, // 몇 개의 페이지 버튼을 보여줄 것인지
|
||||||
|
navigatepageNums: response.data.data.navigatepageNums, // 실제 페이지 번호 목록
|
||||||
|
navigateFirstPage: response.data.data.navigateFirstPage, // 페이지네이션에서 첫 페이지 번호
|
||||||
|
navigateLastPage: response.data.data.navigateLastPage // 페이지네이션에서 마지막 페이지 번호
|
||||||
|
};
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('댓글 목록 불러오기 오류:', error);
|
console.error('댓글 목록 불러오기 오류:', error);
|
||||||
}
|
}
|
||||||
@ -372,6 +410,14 @@ const deletePost = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 페이지 변경
|
||||||
|
const handlePageChange = (page) => {
|
||||||
|
if (page !== pagination.value.currentPage) {
|
||||||
|
pagination.value.currentPage = page;
|
||||||
|
fetchComments(page);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 날짜
|
// 날짜
|
||||||
const formattedDate = (dateString) => {
|
const formattedDate = (dateString) => {
|
||||||
if (!dateString) return "날짜 없음";
|
if (!dateString) return "날짜 없음";
|
||||||
|
|||||||
@ -99,6 +99,7 @@ const handleProfileClick = async (user) => {
|
|||||||
if (user.MEMBERSEQ === userStore.user.id) {
|
if (user.MEMBERSEQ === userStore.user.id) {
|
||||||
// 내 프로필을 클릭한 경우
|
// 내 프로필을 클릭한 경우
|
||||||
const response = await axios.get(`vacation/history`);
|
const response = await axios.get(`vacation/history`);
|
||||||
|
console.log(response)
|
||||||
|
|
||||||
if (response.status === 200 && response.data) {
|
if (response.status === 200 && response.data) {
|
||||||
myVacations.value = response.data.data.usedVacations || [];
|
myVacations.value = response.data.data.usedVacations || [];
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user