
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
<template>
<div class="card">
<div class="card-body">
<h2 class="card-title">휴가 현황</h2>
<div class="form-card">
<h1>휴가신청서</h1>
<div v-if="detailData.sanctnList && detailData.sanctnList.length > 0" class="approval-box tbl-wrap tbl2">
<table class="tbl data">
<tbody>
<tr class="thead">
<td rowspan="2" class="th">승인자</td>
<template v-for="(item, idx) of detailData.sanctnList" :key="idx">
<td>{{ fnFindNm(item.clsf) }}</td>
</template>
</tr>
<tr>
<td v-for="(item, idx) of detailData.sanctnList" :key="idx">
<template v-if="item.confmAt === 'A'">
<p v-if="item.sanctnSe === 'sanctn_agncy'">(대결)</p>
<p class="name">{{ item.confmerInfo?.userNm || '' }}</p>
<small class="date">{{ new Date(item.sanctnDe).toISOString().split('T')[0] }}</small>
</template>
<template v-else-if="item.confmAt === 'R'">
<p class="name">반려</p>
</template>
</td>
</tr>
</tbody>
</table>
</div>
<form class="row g-3 needs-validation detail" novalidate>
<div class="col-12">
<div class="col-12 border-x">
<label for="vcatnType" class="form-label">유형</label>
<p>{{ formattedVcatnKnd }}</p>
</div>
<div class="col-12 border-x">
<label for="applicant" class="form-label">신청자</label>
<p>{{ detailData.registerInfo?.userNm || '' }}</p>
</div>
</div>
<div class="col-12">
<div class="col-12 border-x">
<label for="department" class="form-label">부서</label>
<p>{{ detailData.registerInfo?.userDeptInfo?.deptVO?.deptNm || '' }}</p>
</div>
<div class="col-12 border-x">
<label for="position" class="form-label">직급</label>
<p>{{ formattedClsf }}</p>
</div>
</div>
<div class="col-12">
<label for="period" class="form-label">기간</label>
<p>{{ formattedPeriod }}</p>
</div>
<div class="col-12 hyuga">
<label for="details" class="form-label">세부사항</label>
<ViewerComponent :content="detailData.detailCn || ''" />
</div>
<div class="col-12">
<label for="requestDate" class="form-label">신청일</label>
<p>{{ detailData.rgsde || '' }}</p>
</div>
<div class="col-12">
<label for="status" class="form-label">상태</label>
<p>{{ formattedStatus }}</p>
</div>
<div v-if="detailData.confmAt === 'R'" class="col-12 border-x return">
<label for="rejectReason" class="form-label">반려사유</label>
<template v-for="(item, idx) of detailData.sanctnList" :key="idx">
<p v-if="item.confmAt === 'R'">{{ item.returnResn || '' }}</p>
</template>
</div>
</form>
</div>
<div class="buttons">
<button v-if="detailData.confmAt === 'W' || detailData.confmAt === 'R'" class="btn btn-red" type="button" @click="deleteData">신청취소</button>
<button v-if="detailData.confmAt === 'W'" class="btn secondary" type="button" @click="fnMoveTo('edit', pageId)">수정</button>
<button v-if="detailData.confmAt === 'R'" class="btn secondary" type="button" @click="reSave">재신청</button>
<button class="btn tertiary" type="button" @click="fnMoveTo('list')">목록</button>
</div>
<ReturnPopup v-if="showPopup" @close="showPopup = false" />
</div>
</div>
</template>
<script>
import ReturnPopup from '../../../component/Popup/ReturnPopup.vue';
import ViewerComponent from '../../../component/editor/ViewerComponent.vue';
// API
import { findVcatnProc, deleteVcatnProc } from '../../../../resources/api/vcatn';
export default {
components: { ReturnPopup, ViewerComponent },
data() {
return {
require: "/client/resources/img/require.png",
pageId: null,
isRegister: false,
showPopup: false,
// 휴가 신청서 정보
detailData: {
registerInfo: {},
sanctnList: [],
confmAt: '',
vcatnKnd: '',
clsf: '',
bgnde: '',
endde: '',
detailCn: '',
rgsde: ''
},
cmmnCodes: {
vcatnKndCodeList: [],
clsfCodeList: [],
confmCodeList: []
}
};
},
computed: {
formattedVcatnKnd() {
if (!this.cmmnCodes.vcatnKndCodeList || this.cmmnCodes.vcatnKndCodeList.length === 0) return this.detailData.vcatnKnd;
const vcatnKnd = this.cmmnCodes.vcatnKndCodeList.find(v => v.code === this.detailData.vcatnKnd);
return vcatnKnd ? vcatnKnd.codeNm : this.detailData.vcatnKnd;
},
formattedClsf() {
if (!this.cmmnCodes.clsfCodeList || this.cmmnCodes.clsfCodeList.length === 0) return this.detailData.clsf;
const clsf = this.cmmnCodes.clsfCodeList.find(v => v.code === this.detailData.clsf);
return clsf ? clsf.codeNm : this.detailData.clsf;
},
formattedStatus() {
if (!this.cmmnCodes.confmCodeList || this.cmmnCodes.confmCodeList.length === 0) return this.detailData.confmAt;
const confmAt = this.cmmnCodes.confmCodeList.find(v => v.codeValue === this.detailData.confmAt);
return confmAt ? confmAt.codeNm : this.detailData.confmAt;
},
formattedPeriod() {
if (!this.detailData.bgnde || !this.detailData.endde) return '';
const formattedBgnde = this.detailData.bgnde.split(' ')[0];
const formattedBeginHour = this.detailData.beginHour?.toString().padStart(2, '0') || '00';
const formattedBeginMnt = this.detailData.beginMnt?.toString().padStart(2, '0') || '00';
const startDay = `${formattedBgnde} ${formattedBeginHour}:${formattedBeginMnt}`;
const formattedEndde = this.detailData.endde.split(' ')[0];
const formattedEndHour = this.detailData.endHour?.toString().padStart(2, '0') || '00';
const formattedEndMnt = this.detailData.endMnt?.toString().padStart(2, '0') || '00';
const endDay = `${formattedEndde} ${formattedEndHour}:${formattedEndMnt}`;
const dayCount = this.calculateDayCount();
return `${startDay} ~ ${endDay} (${dayCount}일)`;
},
},
async created() {
this.pageId = this.$route.query.id;
if (this.$isEmpty(this.pageId)) {
alert("게시물이 존재하지 않습니다.");
this.fnMoveTo('list');
}
// 코드 목록 초기화
this.cmmnCodes = await this.$defaultCodes();
},
mounted() {
this.findData(); // 상세 조회
},
methods: {
// 상세 조회
async findData() {
try {
const response = await findVcatnProc(this.pageId);
const result = response.data.data;
this.detailData = result.vo;
this.isRegister = this.$registerChk(result.vo.register);
} catch (error) {
if (error.response) {
alert(error.response.data.message);
} else {
alert("에러가 발생했습니다.");
}
console.error(error.message);
this.fnMoveTo('list');
}
},
fnFindNm(code) {
if (this.$isEmpty(code)) {
return code;
}
const clsf = this.cmmnCodes.clsfCodeList.find(v => v.code === code);
return clsf ? clsf.codeNm : code;
},
// 일수 계산
calculateDayCount() {
if (!this.detailData.bgnde || !this.detailData.endde) return 0;
let dayCnt = 1; // 기본값
// 반차인 경우
if (this.detailData.vcatnKnd === 'MORNING_HALF' || this.detailData.vcatnKnd === 'AFTERNOON_HALF') {
dayCnt = 0.5;
}
const startDate = new Date(this.detailData.bgnde);
const endDate = new Date(this.detailData.endde);
const timeDiff = endDate.getTime() - startDate.getTime();
const dayDiff = Math.floor(timeDiff / (1000 * 60 * 60 * 24)) + 1;
return dayCnt * Math.max(0, dayDiff);
},
// 삭제
async deleteData() {
try {
const response = await deleteVcatnProc(this.pageId);
this.fnMoveTo('list');
} catch (error) {
if (error.response) {
alert(error.response.data.message);
} else {
alert("에러가 발생했습니다.");
}
console.error(error.message);
this.fnMoveTo('list');
}
},
// 재신청
reSave() {
this.$router.push({ name: 'hyugaInsert', query: { id: this.pageId } });
},
// 페이지 이동
fnMoveTo(type, id) {
const routes = {
'list': { name: 'hyugaStatue' },
'view': { name: 'HyugaDetail', query: { id } },
'edit': { name: 'hyugaInsert', query: this.$isEmpty(id) ? {} : { id } },
};
if (routes[type]) {
if (!this.$isEmpty(this.pageId) && type === 'list') {
this.$router.push({ name: 'HyugaDetail', query: { id: this.pageId } });
}
this.$router.push(routes[type]);
} else {
alert("올바르지 않은 경로를 요청하여 목록으로 이동합니다.");
this.$router.push(routes['list']);
}
},
}
};
</script>