
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>
<SanctnViewList v-if="bsrpCnsul.sanctnList.length > 0" :sanctns="bsrpCnsul.sanctnList" />
<div class="tbl-wrap row g-3 needs-validation detail">
<table>
<tbody>
<tr>
<th>출장구분</th>
<td>{{ bsrpInfo.bsrpSeNm }}</td>
</tr>
<tr>
<th>이름</th>
<td>{{ userInfo.userNm }}</td>
</tr>
<tr>
<th>부서</th>
<td>{{ userInfo.deptNm }}</td>
</tr>
<tr>
<th>직급</th>
<td>{{ userInfo.clsfNm }}</td>
</tr>
<tr>
<th>출장지</th>
<td>{{ bsrpInfo.bsrpPlace }}</td>
</tr>
<tr>
<th>출장목적</th>
<td>{{ bsrpInfo.bsrpPurps }}</td>
</tr>
<tr>
<th>동행자</th>
<td>
<template v-if="bsrpInfo.bsrpNmprList.length > 0">
<template v-for="(item, idx) of bsrpInfo.bsrpNmprList" :key="idx">
<span v-if="idx != 0">, </span>
<span>{{ item.triperNm }}</span>
</template>
</template>
</td>
</tr>
<tr>
<th>품의내용</th>
<td>
<ViewerComponent :content="bsrpCnsul.cn" />
</td>
</tr>
<tr>
<th>법인카드</th>
<td>
<template v-if="cards.length > 0">
<template v-for="(item, idx) of cards" :key="idx">
<span v-if="idx != 0">, </span>
<span>{{ item.cardNm }}</span>
</template>
</template>
</td>
</tr>
<tr>
<th>법인차량</th>
<td>
<template v-if="vhcles.length > 0">
<template v-for="(item, idx) of vhcles" :key="idx">
<span v-if="idx != 0">, </span>
<span>{{ item.vhcleNm }}</span>
</template>
</template>
</td>
</tr>
<tr>
<th>품의 신청일</th>
<td>{{ bsrpCnsul.rgsde }}</td>
</tr>
<tr v-if="sanctnStatus === 'rejected'">
<th>품의 반려사유</th>
<td>{{ getRejectionReason(bsrpCnsul.sanctnList) }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div v-if="!hasBsrpRport" class="buttons">
<template v-if="isConsultationApprover">
<button type="button" class="btn sm primary" @click="handleApproval('A')">승인</button>
<button type="button" class="btn sm btn-red" @click="showConsultationPopup = true">반려</button>
</template>
<template v-else-if="isApplicant">
<template v-if="sanctnStatus === 'waiting'">
<button type="button" class="btn sm btn-red" @click="handleDelete">신청취소</button>
<button type="button" class="btn sm secondary" @click="handleNavigation('bsrpInsert', pageId)">수정</button>
</template>
<template v-if="sanctnStatus === 'rejected'">
<button type="button" class="btn sm secondary" @click="handleNavigation('bsrpReapply', pageId)">재신청</button>
</template>
<template v-if="sanctnStatus === 'approved'">
<button type="button" class="btn sm primary" @click="handleNavigation('bsrpRportInsert', pageId)">복명서 작성</button>
</template>
</template>
<button type="button" class="btn sm tertiary" @click="handleNavigation('list')">목록</button>
</div>
<!-- 출장 복명서 -->
<BsrpRportView v-if="hasBsrpRport" :pageId="pageId" :pageMode="pageMode" :cards="cards" :users="combinedUserList" />
</div>
</div>
<ReturnPopup v-if="showConsultationPopup" @close="showConsultationPopup = false" @confirm="handleRejection" />
</template>
<script>
import ReturnPopup from '../../../component/Popup/ReturnPopup.vue';
import SanctnViewList from '../../../component/Sanctn/SanctnViewList.vue';
import ViewerComponent from '../../../component/editor/ViewerComponent.vue';
import BsrpRportView from '../../../component/bsrp/BsrpRportView.vue';
// API
import { findBsrpProc, deleteBsrpProc } from '../../../../resources/api/bsrp';
import { updateConfmAtProc } from '../../../../resources/api/sanctns';
export default {
name: 'BsrpView',
components: {
BsrpRportView,
ReturnPopup,
SanctnViewList,
ViewerComponent,
},
data() {
return {
pageId: null,
pageMode: null,
showConsultationPopup: false,
bsrpInfo: {
applcntId: null,
bsrpSeNm: null,
bsrpPlace: null,
bsrpPurps: null,
bsrpNmprList: [],
},
userInfo: {},
cards: [],
vhcles: [],
bsrpCnsul: {
cn: null,
rgsde: null,
sanctnList: [],
},
hasBsrpRport: false,
returnResn: null,
};
},
computed: {
// 결재 상태
sanctnStatus() {
const sanctnList = this.bsrpCnsul.sanctnList;
if (sanctnList.length === 0) return 'none';
// 하나라도 반려된 경우 > 반려
if (sanctnList.some(item => item.confmAt === 'R')) {
return 'rejected';
}
// 전부 승인된 경우 > 승인
if (sanctnList.every(item => item.confmAt === 'A')) {
return 'approved';
}
// 그 외 > 대기
return 'waiting';
},
// 작성자 여부
isApplicant() {
return this.bsrpInfo.applcntId === this.$store.state.userInfo.userId;
},
// 결재자 여부
isConsultationApprover() {
const sanctnList = this.bsrpCnsul.sanctnList;
const mySanctn = sanctnList.find(
item => item.confmerId == this.$store.state.userInfo.userId
);
return mySanctn && mySanctn.confmAt === 'W' && this.pageMode === 'sanctns';
},
// 출장 신청자 + 동행자 목록
combinedUserList() {
const userList = [];
// 신청자 추가
userList.push({
userId: this.userInfo.userId,
userNm: this.userInfo.userNm,
});
// 동행자 추가
if (this.bsrpInfo.bsrpNmprList && this.bsrpInfo.bsrpNmprList.length > 0) {
this.bsrpInfo.bsrpNmprList.forEach(nmpr => {
userList.push({
userId: nmpr.triperId,
userNm: nmpr.triperNm
});
});
}
return userList;
}
},
async created() {
this.pageId = this.$route.query.id;
this.pageMode = this.$route.query.type;
if (this.$isEmpty(this.pageId)) {
alert("게시물이 존재하지 않습니다.");
this.handleNavigation('list');
}
},
mounted() {
this.fetchData(); // 출장 정보 조회
},
methods: {
// 출장 정보 조회
async fetchData() {
try {
const response = await findBsrpProc(this.pageId);
const result = response.data.data;
this.bsrpInfo = result.bsrpInfoDTO;
this.userInfo = result.userViewDTO;
this.cards = result.cards;
this.vhcles = result.vhcles;
this.bsrpCnsul = result.bsrpCnsulDTO;
this.hasBsrpRport = result.hasBsrpRport;
} catch (error) {
const message = error.response?.data?.message || '데이터 조회에 실패했습니다.';
alert(message);
}
},
// 결재
async handleApproval(value) {
try {
const sanctnList = this.bsrpCnsul.sanctnList;
const sanctn = sanctnList.find(item => item.confmerId == this.$store.state.userInfo.userId);
if (!sanctn) {
alert('결재 권한이 없습니다.');
return;
}
const data = {
sanctnOrdr: sanctn.sanctnOrdr,
sanctnIem: sanctn.sanctnIem,
sanctnMbyId: this.pageId,
confmAt: value,
returnResn: this.returnResn,
};
await updateConfmAtProc(sanctn.sanctnId, data);
alert("승인했습니다.");
this.fetchData();
} catch (error) {
const message = error.response?.data?.message || '승인 처리에 실패했습니다.';
alert(message);
}
},
// 출장 취소 (완전 삭제)
async handleDelete() {
const isCheck = confirm("출장 신청을 취소하시겠습니까?");
if (!isCheck) return;
try {
await deleteBsrpProc(this.pageId);
alert("출장 신청이 취소되었습니다.");
this.handleNavigation('list');
} catch (error) {
const message = error.response?.data?.message || '삭제에 실패했습니다.';
alert(message);
}
},
// 반려 결재 핸들러
async handleRejection(reason) {
this.returnResn = reason;
await this.handleApproval('R');
this.showConsultationPopup = false;
},
// 페이지 이동 핸들러
handleNavigation(type, id) {
const routeMap = {
'list': { name: this.pageMode === 'sanctns' ? 'PendingApprovalListPage' : 'BsrpListPage' },
'view': { name: 'BsrpViewPage', query: { id } },
'bsrpInsert': { name: 'BsrpInsertPage', query: this.$isEmpty(id) ? {} : { id } },
'bsrpReapply': { name: 'BsrpInsertPage', query: { id, type: 'reapply' } },
'bsrpRportInsert': { name: 'BsrpRportInsertPage', query: this.$isEmpty(id) ? {} : { id } },
'bsrpRportReapply': { name: 'BsrpRportInsertPage', query: { id, type: 'reapply' } },
};
const route = routeMap[type];
if (route) {
this.$router.push(route);
} else {
alert("올바르지 않은 경로입니다.");
this.$router.push(routeMap['list']);
}
},
// 반려 사유 유틸리티
getRejectionReason(sanctnList) {
const rejectedItem = sanctnList.find(item => item.confmAt === 'R');
return rejectedItem?.returnResn || '-';
},
},
};
</script>