
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>
<p class="require"><img :src="require" alt=""> 필수입력</p>
<div class="tbl-wrap">
<table class="tbl data">
<tbody>
<tr>
<th>출장구분 <span class="require"><img :src="require" alt=""></span></th>
<td>
<select class="form-select sm" style="width: 110px;" v-model="bsrpInfo.bsrpSe">
<option v-for="(item, idx) of cmmnCodes.bsrpCodeList" :key="idx" :value="item.code">
{{ item.codeNm }}
</option>
</select>
</td>
<th>일수</th>
<td>
<input type="text" class="form-control sm" v-model="totalDays" readonly />
</td>
</tr>
<tr>
<th>출장지 <span class="require"><img :src="require" alt=""></span></th>
<td>
<input type="text" class="form-control sm" v-model="bsrpInfo.bsrpPlace" />
</td>
<th>출장목적 <span class="require"><img :src="require" alt=""></span></th>
<td>
<input type="text" class="form-control sm" v-model="bsrpInfo.bsrpPurps" />
</td>
</tr>
<tr>
<th>출장기간 <span class="require"><img :src="require" alt=""></span></th>
<td colspan="3">
<div class="d-flex">
<div class="d-flex gap-1 mb-1">
<input type="date" class="form-control sm" v-model="bsrpInfo.bgnde" />
<input type="text" class="form-control sm" style="width: 100px;" placeholder="시" v-model="bsrpInfo.beginHour" />
<input type="text" class="form-control sm" style="width: 100px;" placeholder="분" v-model="bsrpInfo.beginMnt" />
</div>
<div class="d-flex gap-1">
<input type="date" class="form-control sm" v-model="bsrpInfo.endde" />
<input type="text" class="form-control sm" style="width: 100px;" placeholder="시" v-model="bsrpInfo.endHour" />
<input type="text" class="form-control sm" style="width: 100px;" placeholder="분" v-model="bsrpInfo.endMnt" />
</div>
</div>
</td>
</tr>
<tr>
<th>
동행자
</th>
<td>
<button type="button" title="추가" @click="isOpenNmprModal = true">
<PlusCircleFilled />
</button>
<HrPopup v-if="isOpenNmprModal" :lists="bsrpInfo.bsrpNmprList" @onSelected="fnAddNmpr" @close="isOpenNmprModal = false" />
<div class="approval-container">
<div v-for="(item, idx) of bsrpInfo.bsrpNmprList" :key="idx" class="d-flex addapproval">
<div class="d-flex align-items-center border-x">
<p>{{ item.triperNm }} {{ item.clsfNm }}</p>
<button type="button" @click="fnDelNmpr(idx)" @mousedown.stop>
<CloseOutlined />
</button>
</div>
</div>
</div>
</td>
<th>
승인자 <span class="require"><img :src="require" alt=""></span>
</th>
<td>
<button type="button" title="추가" @click="isOpenSanctnModal = true">
<PlusCircleFilled />
</button>
<HrPopup v-if="isOpenSanctnModal" :lists="bsrpCnsul.sanctnList" @onSelected="fnAddSanctn" @close="isOpenSanctnModal = false" />
<div class="approval-container">
<SanctnList v-model:lists="bsrpCnsul.sanctnList" @delSanctn="fnDelSanctn" />
</div>
</td>
</tr>
<tr>
<th>품의내용 <span class="require"><img :src="require" alt=""></span></th>
<td colspan="3" style="height: calc(100% - 550px);">
<EditorComponent v-model:contents="bsrpCnsul.cn" />
</td>
</tr>
<tr>
<th>
법인카드
<button type="button" title="추가" @click="isOpenCardModal = true">
<PlusCircleFilled />
</button>
</th>
<td>
<CorpCardPopup v-if="isOpenCardModal" :bsrpInfo="bsrpInfo" :lists="cprCardList" @close="isOpenCardModal = false" @onSelected="fnAddCard" />
<div class="approval-container">
<div v-for="(card, idx) in cprCardList" :key="idx" class="d-flex gap-2 addapproval mb-2">
<form class="d-flex align-items-center border-x">
<p>{{ card.cardNm }}</p>
<button type="button" @click="fnDelCard(idx)" class="delete-button">
<CloseOutlined />
</button>
</form>
</div>
</div>
</td>
<th>
법인차량
<button type="button" title="추가" @click="isOpenVhcleModal = true">
<PlusCircleFilled />
</button>
</th>
<td>
<CorpCarPopup v-if="isOpenVhcleModal" :bsrpInfo="bsrpInfo" :lists="cprVhcleList" @close="isOpenVhcleModal = false" @onSelected="fnAddVhcle" />
<div class="approval-container">
<div v-for="(vhcle, idx) in cprVhcleList" :key="idx" class="d-flex gap-2 addapproval mb-2">
<p>{{ vhcle.vhcleNm }}</p>
<select class="form-select" v-model="vhcle.drverId">
<option value="" disabled hidden>운전자 선택</option>
<option :value="userInfo.userId">{{ userInfo.userNm }}</option>
<option v-for="(item, idx) of bsrpInfo.bsrpNmprList" :key="idx" :value="item.userId">
{{ item.userNm }}
</option>
</select>
<button type="button" @click="fnDelVhcle(idx)" class="delete-button">
<CloseOutlined />
</button>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="buttons">
<button type="button" class="btn sm sm primary" @click="fnSave">신청</button>
<button v-if="$isEmpty(pageId)" type="button" class="btn sm sm secondary" @click="fnMoveTo('list')">목록</button>
<button v-else type="button" class="btn sm sm secondary" @click="fnMoveTo('view', pageId)">취소</button>
</div>
</div>
</div>
</template>
<script>
import { PlusCircleFilled, CloseOutlined } from '@ant-design/icons-vue';
import HrPopup from '../../../component/Popup/HrPopup.vue';
import CorpCarPopup from '../../../component/Popup/CorpCarPopup.vue';
import CorpCardPopup from '../../../component/Popup/CorpCardPopup.vue';
import SanctnList from '../../../component/Sanctn/SanctnFormList.vue';
import EditorComponent from '../../../component/editor/EditorComponent.vue';
// API
import { saveBsrpProc, bsrpProc, updateBsrpProc } from '../../../../resources/api/bsrp';
export default {
components: {
PlusCircleFilled, CloseOutlined,
HrPopup, CorpCarPopup, CorpCardPopup,
SanctnList, EditorComponent,
},
data() {
return {
require: "/client/resources/img/require.png",
pageId: null,
userInfo: this.$store.state.userInfo,
cmmnCodes: {},
isOpenNmprModal: false,
isOpenSanctnModal: false,
isOpenCardModal: false,
isOpenVhcleModal: false,
// 출장정보
bsrpInfo: {
applcntId: null, // 신청자 아이디
bsrpSe: null, // 출장구분
bsrpPlace: null, // 출장지
bsrpPurps: null, // 출장목적
bgnde: null, // 시작일
beginHour: null, // 시작시
beginMnt: null, // 시작분
endde: null, // 종료일
endHour: null, // 종료시
endMnt: null, // 종료분
bsrpNmprList: [], // 출장인원 목록
},
// 출장품의
bsrpCnsul: {
bsrpId: null, // 출장 아이디
cn: null, // 품의 내용
confmAt: null, // 승인여부
rgsde: null, // 등록일
register: null, // 등록자
updde: null, // 수정일
updusr: null, // 수정자
sanctnList: [], // 결재 목록
},
cprCardList: [], // 법인카드 목록
cprVhcleList: [], // 법인차량 목록
totalDays: 0, // 일수
};
},
computed: {},
watch: {
cmmnCodes(newVal) {
if (Object.keys(newVal).length > 0) {
this.bsrpInfo.bsrpSe = this.cmmnCodes.bsrpCodeList[0].code;
}
},
'bsrpInfo.bgnde'(newVal, oldVal) {
if (newVal !== oldVal) {
this.calcDayCnt(); // 일수 계산
}
},
'bsrpInfo.endde'(newVal, oldVal) {
if (newVal !== oldVal) {
this.calcDayCnt(); // 일수 계산
}
},
},
async created() {
this.pageId = this.$route.query.id;
this.cmmnCodes = await this.$defaultCodes();
},
mounted() {
if (!this.$isEmpty(this.pageId)) {
this.findData();
}
},
methods: {
// 상세 조회
async findData() {
try {
const response = await bsrpProc(this.pageId);
const result = response.data.data;
this.bsrpInfo = result.bsrpInfo;
this.bsrpInfo.bgnde = this.bsrpInfo.bgnde.split(' ')[0];
this.bsrpInfo.endde = this.bsrpInfo.endde.split(' ')[0];
this.bsrpCnsul = result.bsrpCnsul;
this.cprCardList = result.cprCardList;
this.cprVhcleList = result.cprVhcleList;
} catch (error) {
console.error('데이터 조회 실패:', error);
alert(error.response.data.message);
this.fnMoveTo('list');
}
},
// 일수 계산
calcDayCnt() {
if (!this.bsrpInfo.bgnde || !this.bsrpInfo.endde) {
this.totalDays = 0;
return;
}
const startDate = new Date(this.bsrpInfo.bgnde);
const endDate = new Date(this.bsrpInfo.endde);
if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
this.totalDays = 0;
return;
}
const timeDiff = endDate.getTime() - startDate.getTime();
const dayDiff = Math.floor(timeDiff / (1000 * 60 * 60 * 24)) + 1;
this.totalDays = Math.max(0, dayDiff);
},
// 동행자 추가
fnAddNmpr(user) {
const data = {
triperId: user.userId, // 출장자 아이디
deptId: user.deptId, // 부서 아이디
clsf: user.clsf, // 직급
triperNm: user.userNm, // 출장자 이름
deptNm: user.deptNm, // 부서 이름
clsfNm: user.clsfNm, // 직급 이름
};
this.bsrpInfo.bsrpNmprList.push(data);
this.isOpenNmprModal = false;
},
// 동행자 삭제
fnDelNmpr(idx) {
this.bsrpInfo.bsrpNmprList.splice(idx, 1);
},
// 승인자 추가
fnAddSanctn(user) {
const data = {
sanctnId: null, // 결재 아이디
confmerId: user.userId, // 승인자 아이디
clsf: user.clsf, // 직급
sanctnOrdr: this.bsrpCnsul.sanctnList.length + 1, // 결재 순서
sanctnIem: 'cnsul', // 결재 항목
sanctnMbyId: null, // 결재 주체 아이디
sanctnSe: this.cmmnCodes.sanctnCodeList[0].code, // 결재구분
confmerNm: user.userNm, // 승인자 이름
clsfNm: user.clsfNm, // 직급 이름
};
this.bsrpCnsul.sanctnList.push(data);
this.isOpenSanctnModal = false;
},
// 승인자 삭제
fnDelSanctn(idx) {
this.bsrpInfo.sanctnList.splice(idx, 1);
this.bsrpInfo.sanctnList.forEach((item, idx) => { item.sanctnOrdr = idx + 1; });
},
// 법인카드 추가
fnAddCard(item) {
this.cprCardList.push(item);
this.isOpenCardModal = false;
},
// 법인카드 삭제
fnDelCard(idx) {
this.cprCardList.splice(idx, 1);
},
// 법인차량 추가
fnAddVhcle(item) {
item.drverId = '';
this.cprVhcleList.push(item);
this.isOpenVhcleModal = false;
},
// 법인차량 삭제
fnDelVhcle(idx) {
this.cprVhcleList.splice(idx, 1);
},
// 유효성 검사
validateForm() {
if (this.$isEmpty(this.bsrpInfo.bsrpSe)) {
return false;
}
if (this.$isEmpty(this.bsrpInfo.bsrpPlace)) {
return false;
}
if (this.$isEmpty(this.bsrpInfo.bsrpPurps)) {
return false;
}
if (this.$isEmpty(this.bsrpInfo.bgnde) || this.$isEmpty(this.bsrpInfo.beginHour) || this.$isEmpty(this.bsrpInfo.beginMnt)) {
return false;
}
if (this.$isEmpty(this.bsrpInfo.endde) || this.$isEmpty(this.bsrpInfo.endHour) || this.$isEmpty(this.bsrpInfo.endMnt)) {
return false;
}
if (this.$isEmpty(this.bsrpInfo.bsrpNmprList) || this.bsrpInfo.bsrpNmprList.length === 0) {
return false;
}
if (this.$isEmpty(this.bsrpCnsul.cn)) {
return false;
}
return true;
},
// 신청
async fnSave() {
try {
if (!this.validateForm()) {
return;
}
let data = {
bsrpCnsulInsertDTO: this.bsrpCnsul,
cardDtlsList: this.cprCardList,
vhcleDtlsList: this.cprVhcleList,
}
if (this.$isEmpty(this.pageId)) {
data.bsrpInfoInsertDTO = this.bsrpInfo;
} else {
data.bsrpInfoUpdateDTO = this.bsrpInfo;
}
const response = this.$isEmpty(this.pageId) ? await saveBsrpProc(data) : await updateBsrpProc(data);
const message = this.$isEmpty(this.pageId) ? "등록되었습니다." : "수정되었습니다.";
alert(message);
this.fnMoveTo('view', response.data.data.pageId);
} catch (error) {
console.error('저장 실패:', error);
const message = error.response?.data?.message || "저장에 실패했습니다.";
alert(message);
}
},
fnMoveTo(type, id) {
const routes = {
'list': { name: 'ChuljangStatue' },
'view': { name: 'ChuljangDetailAll', query: { id } },
'edit': { name: 'ChuljangInsert', query: this.$isEmpty(id) ? {} : { id } },
};
if (routes[type]) {
if (!this.$isEmpty(this.pageId) && type === 'list') {
this.$router.push({ name: 'ChuljangDetailAll', query: { id: this.pageId } });
return;
}
this.$router.push(routes[type]);
} else {
alert("올바르지 않은 경로를 요청하여 목록으로 이동합니다.");
this.$router.push(routes['list']);
}
},
},
};
</script>