jichoi / calendar star
박정하 박정하 4 days ago
250807 박정하 근태관리 출장 추가, 파일 및 결재 수정
@ad506a8e7bd548a6484249484ffc235f396b80c2
client/resources/api/bsrp.js
--- client/resources/api/bsrp.js
+++ client/resources/api/bsrp.js
@@ -1,26 +1,31 @@
-import {apiClient} from "./index";
+import { apiClient } from "./index";
 
-// 등록
+// 출장 목록 조회
+export const findBsrpsProc = data => {
+  return apiClient.get('/bsrp/findBsrps.json', { params: data });
+}
+
+// 출장 상세 조회
+export const findBsrpProc = data => {
+  return apiClient.get(`/bsrp/${data}/findBsrp.json`);
+}
+
+// 출장 품의 이전 승인자 조회
+export const findLastBsrpCnsulProc = () => {
+  return apiClient.get('/bsrpCnsul/findLastBsrpCnsul.json');
+}
+
+// 출장 정보, 출장 품의 등록
 export const saveBsrpProc = data => {
   return apiClient.post('/bsrp/saveBsrp.json', data);
 }
 
-// 목록 조회
-export const bsrpsProc = data => {
-  return apiClient.get('/bsrp/bsrps.json', { params: data });
+// 출장 정보, 출장 품의 수정
+export const updateBsrpProc = (id, data) => {
+  return apiClient.put(`/bsrp/${id}/updateBsrp.json`, data);
 }
 
-// 상세 조회
-export const bsrpProc = data => {
-  return apiClient.get(`/bsrp/${data}/bsrp.json`);
-}
-
-// 수정
-export const updateBsrpProc = data => {
-  return apiClient.put('/bsrp/updateBsrp.json', data);
-}
-
-// 삭제
+// 출장 정보, 출장 품의 삭제
 export const deleteBsrpProc = data => {
   return apiClient.delete(`/bsrp/${data}/deleteBsrp.json`);
 }
(파일 끝에 줄바꿈 문자 없음)
 
client/resources/api/bsrpRport.js (added)
+++ client/resources/api/bsrpRport.js
@@ -0,0 +1,26 @@
+import { apiClient, fileClient } from "./index";
+
+// 출장 복명 상세 조회
+export const findBsrpRportProc = data => {
+  return apiClient.get(`/bsrpRport/${data}/findBsrpRport.json`);
+}
+
+// 출장 복명 이전 승인자 조회
+export const findLastBsrpRportProc = () => {
+  return apiClient.get('/bsrpRport/findLastBsrpRport.json');
+}
+
+// 출장 복명 등록
+export const saveBsrpRportProc = data => {
+  return fileClient.post('/bsrpRport/saveBsrpRport.file', data);
+}
+
+// 출장 복명 수정
+export const updateBsrpRport = data => {
+  return fileClient.put('/bsrpRport/updateBsrpRport.file', data);
+}
+
+// 출장 복명 삭제
+export const deleteBsrpRportProc = data => {
+  return apiClient.delete(`/bsrpRport/${data}/deleteBsrpRport.json`);
+}(파일 끝에 줄바꿈 문자 없음)
 
client/resources/api/file.js (added)
+++ client/resources/api/file.js
@@ -0,0 +1,6 @@
+import { apiClient } from "./index";
+
+// 파일 다운로드
+export const fileDownloadProc = (file) => {
+  return apiClient.get(`/file/${file.fileId}/${file.ordr}/fileDownload.json`, { responseType: 'blob' });
+}(파일 끝에 줄바꿈 문자 없음)
client/resources/api/sanctns.js
--- client/resources/api/sanctns.js
+++ client/resources/api/sanctns.js
@@ -8,4 +8,9 @@
 // 승인 대기 목록 조회
 export const findPendingApprovalsProc = data => {
   return apiClient.get('/sanctn/findPendingApprovals.json', { params: data });
+}
+
+// 결재
+export const updateConfmAtProc = (sanctnId, data) => {
+  return apiClient.put(`/sanctn/${sanctnId}/updateConfmAt.json`, data);
 }
(파일 끝에 줄바꿈 문자 없음)
client/views/component/Popup/ReturnPopup.vue
--- client/views/component/Popup/ReturnPopup.vue
+++ client/views/component/Popup/ReturnPopup.vue
@@ -1,48 +1,64 @@
 <template>
-    <div  class="popup-overlay" @click.self="$emit('close')">
-                <div class="popup-content">
-                  <div class="card">
-                    <div class="card-body">
-                      <h2 class="card-title">반려 사유</h2>
-                      <textarea name="" id="" class="form-control "></textarea>
-                      <div class="buttons">
-              <button class="btn sm primary"  type="submit">등록</button>
-              <button class="btn sm tertiary"  type="submit" @click="$emit('close')">취소</button>
-              </div>
-                    </div>
-                  </div>
-                  <button @click="$emit('close')" class="close-btn">
-                    <CloseCircleFilled />
-                  </button>
-                </div>
-              </div>
+  <div class="popup-overlay" @click.self="handleClose">
+    <div class="popup-content">
+      <div class="card">
+        <div class="card-body">
+          <h2 class="card-title">반려 사유</h2>
+          <textarea v-model="rejectionReason" class="form-control" placeholder="반려 사유를 입력해주세요." />
+          <div class="buttons">
+            <button type="button" class="btn sm primary" @click="handleConfirm" :disabled="!isValidReason">등록</button>
+            <button type="button" class="btn sm tertiary" @click="handleClose"> 취소 </button>
+          </div>
+        </div>
+      </div>
+      <button @click="handleClose" class="close-btn">
+        <CloseCircleFilled />
+      </button>
+    </div>
+  </div>
 </template>
 <script>
-import { SearchOutlined, CloseCircleFilled } from '@ant-design/icons-vue';
+import { CloseCircleFilled } from '@ant-design/icons-vue';
 
 export default {
-    data() {
-        return {
-        }
-    },
-    components: {
-    SearchOutlined, CloseCircleFilled
+  components: {
+    CloseCircleFilled
   },
+
+  emits: ['close', 'confirm'],
+
+  data() {
+    return {
+      rejectionReason: ''
+    }
+  },
+
+  computed: {
+    isValidReason() {
+      return this.rejectionReason.trim().length > 0;
+    }
+  },
+
   methods: {
-    selectCar(item) {
-    this.$emit('select', item); // 부모에게 데이터 전달
-  },
-    
+    handleClose() {
+      this.$emit('close');
+    },
+
+    handleConfirm() {
+      if (this.isValidReason) {
+        this.$emit('confirm', this.rejectionReason.trim());
+      }
+    },
   }
 }
 </script>
 <style scoped>
 .popup-content {
-    width: 50%;
-    
+  width: 50%;
 }
-.form-control{
-      border-color: #C7CFE3;
-      min-height: 20rem;
-      }
+
+.form-control {
+  border-color: #C7CFE3;
+  min-height: 20rem;
+}
 </style>
(파일 끝에 줄바꿈 문자 없음)
client/views/component/Sanctn/SanctnViewList.vue
--- client/views/component/Sanctn/SanctnViewList.vue
+++ client/views/component/Sanctn/SanctnViewList.vue
@@ -24,8 +24,6 @@
 </template>
 <script>
 export default {
-  name: 'SanctnViewList',
-
   props: {
     sanctns: {
       type: Array,
 
client/views/component/bsrp/BsrpRportView.vue (added)
+++ client/views/component/bsrp/BsrpRportView.vue
@@ -0,0 +1,305 @@
+<template>
+  <div class="form-card">
+    <h1>출장복명서</h1>
+    <SanctnViewList v-if="bsrpRport.sanctnList.length > 0" :sanctns="bsrpRport.sanctnList" />
+    <div class="tbl-wrap row g-3 needs-validation detail">
+      <table class="tbl">
+        <tbody>
+          <tr>
+            <th>복명내용</th>
+            <td>
+              <ViewerComponent :content="bsrpRport.cn" />
+            </td>
+          </tr>
+          <tr>
+            <th>여비계산</th>
+            <td>
+              <template v-if="bsrpRport.bsrpTrvctList.length > 0">
+                <div v-for="(item, idx) of bsrpRport.bsrpTrvctList" :key="idx">
+                  <p>
+                    <span>{{ item.seNm + '결재' }} ({{ getPaymentEntityText(item.se, item.setleMbyId) }})</span>
+                    <span>{{ item.tyNm }}</span>
+                    <span>{{ formatAmount(item.amount) }}</span>
+                    <button type="button" v-if="item.ordr" @click="handleDownload(item.ordr)">{{ getFileNm(item.ordr) }}</button>
+                    <span v-else>영수증 없음</span>
+                  </p>
+                </div>
+              </template>
+            </td>
+          </tr>
+          <tr>
+            <th>복명 신청일</th>
+            <td>{{ bsrpRport.rgsde }}</td>
+          </tr>
+          <tr v-if="reportSanctnStatus === 'rejected'">
+            <th>복명 반려사유</th>
+            <td>{{ getRejectionReason(bsrpRport.sanctnList) }}</td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+  </div>
+  <div class="buttons">
+    <template v-if="isReportApprover">
+      <button type="button" class="btn sm primary" @click="handleApproval('A')">승인</button>
+      <button type="button" class="btn sm btn-red" @click="showReportPopup = true">반려</button>
+    </template>
+    <template v-else-if="isApplicant">
+      <template v-if="reportSanctnStatus === 'waiting'">
+        <button type="button" class="btn sm btn-red" @click="handleDeleteReport">복명서 취소</button>
+        <button type="button" class="btn sm secondary" @click="handleNavigation('bsrpRportInsert', pageId)">복명서 수정</button>
+      </template>
+      <template v-if="reportSanctnStatus === 'rejected'">
+        <button type="button" class="btn sm secondary" @click="handleNavigation('bsrpRportReapply', pageId)">복명서 재신청</button>
+      </template>
+    </template>
+    <button type="button" class="btn sm tertiary" @click="handleNavigation('list')">목록</button>
+  </div>
+  <ReturnPopup v-if="showReportPopup" @close="showReportPopup = false" @confirm="handleRejection" />
+</template>
+<script>
+import ReturnPopup from '../Popup/ReturnPopup.vue';
+import SanctnViewList from '../Sanctn/SanctnViewList.vue';
+import ViewerComponent from '../editor/ViewerComponent.vue';
+// API
+import { findBsrpRportProc, deleteBsrpRportProc } from '../../../resources/api/bsrpRport';
+import { updateConfmAtProc } from '../../../resources/api/sanctns';
+import { fileDownloadProc } from '../../../resources/api/file';
+
+export default {
+  name: 'BsrpRportView',
+
+  components: {
+    ReturnPopup,
+    SanctnViewList,
+    ViewerComponent,
+  },
+
+  props: {
+    pageId: {
+      type: String,
+      default: null,
+    },
+    pageMode: {
+      type: String,
+      default: null,
+    },
+    cards: {
+      type: Array,
+      default: () => [],
+    },
+    users: {
+      type: Array,
+      default: () => [],
+    },
+  },
+
+  data() {
+    return {
+      showReportPopup: false,
+      bsrpInfo: {
+        applcntId: null,
+      },
+      bsrpRport: {
+        cn: null,
+        rgsde: null,
+        sanctnList: [],
+        bsrpTrvctList: [],
+        fileList: [],
+      },
+      returnResn: null,
+    };
+  },
+
+  computed: {
+    // 결재 상태
+    reportSanctnStatus() {
+      const sanctnList = this.bsrpRport.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.bsrpRport.register === this.$store.state.userInfo.userId;
+    },
+
+    // 결재자 여부
+    isReportApprover() {
+      const sanctnList = this.bsrpRport.sanctnList;
+      const mySanctn = sanctnList.find(
+        item => item.confmerId == this.$store.state.userInfo.userId
+      );
+      return mySanctn && mySanctn.confmAt === 'W' && this.pageMode === 'sanctns';
+    },
+  },
+
+  mounted() {
+    this.fetchData(); // 복명 정보 조회
+  },
+
+  methods: {
+    // 복명 정보 조회
+    async fetchData() {
+      try {
+        const response = await findBsrpRportProc(this.pageId);
+        const result = response.data.data;
+
+        this.bsrpRport = result;
+      } catch (error) {
+        const message = error.response?.data?.message || '데이터 조회에 실패했습니다.';
+        alert(message);
+      }
+    },
+
+    // 결재
+    async handleApproval(value) {
+      try {
+        const sanctnList = this.bsrpRport.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);
+        const message = value === 'A' ? '승인했습니다.' : '반려했습니다.';
+        alert(message);
+        this.fetchData();
+      } catch (error) {
+        const message = error.response?.data?.message || '승인 처리에 실패했습니다.';
+        alert(message);
+      }
+    },
+
+    // 복명 취소 (완전 삭제)
+    async handleDeleteReport() {
+      const isCheck = confirm("복명서를 취소하시겠습니까?");
+      if (!isCheck) return;
+
+      try {
+        await deleteBsrpRportProc(this.pageId);
+        alert("복명서가 취소되었습니다.");
+        this.fetchData();
+      } catch (error) {
+        const message = error.response?.data?.message || '복명서 취소에 실패했습니다.';
+        alert(message);
+      }
+    },
+
+    // 첨부 파일 다운로드 핸들러
+    async handleDownload(fileOrdr) {
+      const selectedFile = this.bsrpRport.fileList.find(file => file.ordr == fileOrdr);
+      if (!selectedFile) return;
+
+      let url = null;
+      let link = null;
+
+      try {
+        const response = await fileDownloadProc(selectedFile);
+
+        let filename = 'downloadFile.bin';
+        const filenameRegex = /file[Nn]ame[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
+        const matches = filenameRegex.exec(response.headers['content-disposition']);
+        if (matches != null && matches[1]) {
+          filename = matches[1].replace(/['"]/g, '');
+        }
+
+        url = window.URL.createObjectURL(new Blob([response.data]));
+        link = document.createElement('a');
+        link.href = url;
+        link.setAttribute('download', filename);
+        document.body.appendChild(link);
+        link.click();
+      } catch (error) {
+        alert('파일 다운로드 중 오류가 발생했습니다.');
+      } finally {
+        setTimeout(() => {
+          if (url) {
+            window.URL.revokeObjectURL(url);
+          }
+          if (link && link.parentNode) {
+            document.body.removeChild(link);
+          }
+        }, 100);
+      }
+    },
+
+    // 반려 결재 핸들러
+    async handleRejection(reason) {
+      this.returnResn = reason;
+      await this.handleApproval('R');
+      this.showReportPopup = 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;
+    },
+
+    // 여비 주체명 검색 유틸리티
+    getPaymentEntityText(se, id) {
+      if (se === 'PERSONAL') {
+        const user = this.users.find(user => user.userId === id);
+        return user?.userNm;
+      } else {
+        const card = this.cards.find(card => card.cardId === id);
+        return card?.cardNm;
+      }
+    },
+
+    // 금액 표기 변경 유틸리티
+    formatAmount(amount) {
+      return new Intl.NumberFormat('ko-KR').format(amount) + '원';
+    },
+
+    // 파일명 검색 유틸리티
+    getFileNm(fileOrdr) {
+      const file = this.bsrpRport.fileList.find(file => file.ordr == fileOrdr);
+      return file?.fileNm;
+    },
+  },
+};
+</script>(파일 끝에 줄바꿈 문자 없음)
client/views/layout/LeftNavBar.vue
--- client/views/layout/LeftNavBar.vue
+++ client/views/layout/LeftNavBar.vue
@@ -38,7 +38,7 @@
         <!-- 시스템 관리 -->
         <ul v-if="section.key === 'system'" class="menu-box danil">
           <li v-for="item in section.items" :key="item.name">
-              <router-link  :to="{ name: item.name }" exact-active-class="active-link">
+            <router-link :to="{ name: item.name }" exact-active-class="active-link">
               <p>{{ item.title }}</p>
               <div class="icon">
                 <img src="/client/resources/img/menuicon.png" alt="" style="vertical-align: middle;">
@@ -84,11 +84,11 @@
         ]
       },
       {
-        name: 'chuljang',
+        name: 'bsrp',
         title: '출장',
         items: [
-          { name: 'ChuljangStatue', title: '출장 현황' },
-          { name: 'ChuljangInsert', title: '출장 신청' }
+          { name: 'BsrpListPage', title: '출장 현황' },
+          { name: 'BsrpInsertPage', title: '출장 신청' }
         ]
       }
     ]
@@ -211,10 +211,9 @@
     isCurrentMenuActive(menuName) {
       const routeChecks = {
         sanctn: () => this.currentRouteName.includes('approval'),
-        attendance: () => ['attendance', 'myattendance', 'buseoattendance'].some(name =>
-          this.currentRouteName.includes(name)),
+        attendance: () => ['attendance', 'myattendance', 'buseoattendance'].some(name => this.currentRouteName.includes(name)),
         hyuga: () => this.currentRouteName.includes('hyuga'),
-        chuljang: () => this.currentRouteName.includes('chuljang'),
+        bsrp: () => this.currentRouteName.includes('bsrp'),
         project: () => this.currentRouteName.includes('project'),
         salary: () => this.currentRouteName.includes('salary'),
         expense: () => ['cost', 'meeting'].some(name => this.currentRouteName.includes(name)),
client/views/pages/AppRouter.js
--- client/views/pages/AppRouter.js
+++ client/views/pages/AppRouter.js
@@ -10,17 +10,16 @@
 import PendingApprovalListComp from '../pages/Manager/sanctn/PendingApprovalList.vue';
 
 //근태관리
-import ChuljangBokmyeongDetail from '../pages/Manager/attendance/ChuljangBokmyeongDetail.vue';
-import ChuljangDetailAll from './Manager/attendance/ChuljangDetailAll.vue';
-import ChuljangStatue from '../pages/Manager/attendance/ChuljangStatue.vue';
-import ChuljangInsert from '../pages/Manager/attendance/ChuljangInsert.vue';
 import myAttendance from '../pages/Manager/attendance/myAttendance.vue';
 import buseoAttendance from '../pages/Manager/attendance/buseoAttendance.vue';
 import AttendanceDetail from '../pages/Manager/attendance/AttendanceDetail.vue';
 import hyugaStatue from '../pages/Manager/attendance/hyugaStatue.vue';
 import HyugaInsert from '../pages/Manager/attendance/HyugaInsert.vue';
 import HyugaDetail from '../pages/Manager/attendance/HyugaDetail.vue';
-import BokmyeongInsert from '../pages/Manager/attendance/BokmyeongInsert.vue';
+import BsrpListComp from '../pages/Manager/attendance/BsrpList.vue';
+import BsrpInsertComp from '../pages/Manager/attendance/BsrpInsert.vue';
+import BsrpViewComp from './Manager/attendance/BsrpView.vue';
+import BsrpRportInsertComp from '../pages/Manager/attendance/BsrpRportInsert.vue';
 
 //업무관리
 import projectStatue from '../pages/Manager/task/projectStatue.vue';
@@ -82,11 +81,11 @@
       { path: 'hyugaStatue.page', name: 'hyugaStatue', component: hyugaStatue },
       { path: 'HyugaDetail.page', name: 'HyugaDetail', component: HyugaDetail },
       { path: 'hyugaInsert.page', name: 'hyugaInsert', component: HyugaInsert },
-      { path: 'ChuljangStatue.page', name: 'ChuljangStatue', component: ChuljangStatue },
-      { path: 'BokmyeongInsert.page', name: 'BokmyeongInsert', component: BokmyeongInsert },
-      { path: 'ChuljangInsert.page', name: 'ChuljangInsert', component: ChuljangInsert },
-      { path: 'ChuljangBokmyeongDetail.page', name: 'ChuljangBokmyeongDetail', component: ChuljangBokmyeongDetail },
-      { path: 'ChuljangDetailAll.page', name: 'ChuljangDetailAll', component: ChuljangDetailAll },
+
+      { path: 'BsrpList.page', name: 'BsrpListPage', component: BsrpListComp },
+      { path: 'BsrpView.page', name: 'BsrpViewPage', component: BsrpViewComp },
+      { path: 'BsrpInsert.page', name: 'BsrpInsertPage', component: BsrpInsertComp },
+      { path: 'BsrpRportInsert.page', name: 'BsrpRportInsertPage', component: BsrpRportInsertComp },
     ]
   },
   // 업무관리
 
client/views/pages/Manager/attendance/BokmyeongInsert.vue (deleted)
--- client/views/pages/Manager/attendance/BokmyeongInsert.vue
@@ -1,271 +0,0 @@
-<template>
-  <div class="card">
-    <div class="card-body">
-      <h2 class="card-title">출장 복명서 등록</h2>
-      <!-- Multi Columns Form -->
-      <form class="row g-3 pt-3  needs-validation detail" @submit.prevent="handleSubmit">
-
-
-        <div class="col-12">
-          <label for="where" class="form-label">출장구분</label>
-          <input type="text" class="form-control" id="where" v-model="where" readonly/>
-        </div>
-        <div class="col-12">
-          <label for="where" class="form-label">출장지</label>
-          <input type="text" class="form-control" id="where" v-model="where" readonly/>
-        </div>
-        <div class="col-12">
-          <label for="where" class="form-label">출장목적</label>
-          <input type="text" class="form-control" id="where" v-model="where" readonly/>
-        </div>
-        <div class="col-12">
-          <label for="where" class="form-label">출장기간</label>
-          <input type="text" class="form-control" id="where" v-model="where" readonly/>
-        </div>
-
-        <div class="col-12">
-          <label for="purpose" class="form-label">동행자</label>
-          <input type="text" class="form-control" id="purpose" v-model="purpose" readonly/>
-        </div>
-        <div class="col-12">
-          <label for="purpose" class="form-label">법인카드</label>
-          <input type="text" class="form-control" id="purpose" v-model="purpose" readonly/>
-        </div>
-        <div class="col-12">
-          <label for="purpose" class="form-label">법인차량</label>
-          <input type="text" class="form-control" id="purpose" v-model="purpose" readonly/>
-        </div>
-
-        <div class="col-12">
-          <label for="member" class="form-label">
-            승인자
-            <button type="button" title="추가"  @click="showPopup = true">
-              <PlusCircleFilled />
-            </button>
-          </label>
-          <HrPopup v-if="showPopup" @close="showPopup = false" @select="addApproval"/>
-          <div class="approval-container">
-            <div v-for="(approval, index) in approvals" :key="index" class="d-flex gap-2 addapproval mb-2">
-              <select class="form-select" v-model="approval.category" style="width: 110px;">
-                <option value="결재">결재</option>
-                <option value="전결">전결</option>
-                <option value="대결">대결</option>
-              </select>
-
-              <form class="d-flex align-items-center border-x">
-                <input type="text" class="form-control" v-model="approval.name" style="max-width: 150px;" />
-                <button type="button" @click="removeApproval(index)" class="delete-button">
-                  <CloseOutlined />
-                </button>
-              </form>
-            </div>
-          </div>
-        </div>
-        <div class="col-12 chuljang">
-          <label for="prvonsh" class="form-label">복명내용</label>
-          <input type="text" class="form-control textarea" id="reason" v-model="reason" />
-        </div>
-        <div class="col-12 border-x">
-          <label for="member" class="form-label">
-            여비계산
-            <button type="button" title="추가" @click="addReceipt">
-              <PlusCircleFilled />
-            </button>
-          </label>
-
-          <div class="approval-container">
-            <div v-for="(receipt, index) in receipts" :key="index" class="d-flex gap-2 addapproval mb-2">
-              <select class="form-select" style="width: 140px;">
-                <option value="">개인결제</option>
-                <option value="">법인결제</option>
-              </select>
-              <select class="form-select"  style="width: 110px;">
-                <option value="">법인</option>
-                <option value="">개인</option>
-              </select>
-              <select class="form-select"  style="width: 110px;">
-                <option value="" selected>구분</option>
-                <option value="">여비사용</option>
-              </select>
-
-              <input type="text" class="form-control" placeholder="금액입력" style="max-width: 150px;" />
-             
-                <!-- 커스텀 업로드 버튼 -->
-                <label :for="'fileUpload-' + index" class="upload-label">
-    영수증 첨부
-  </label>
-
-                <!-- 실제 파일 input (숨김 처리) -->
-                <input
-    :id="'fileUpload-' + index"
-    type="file"
-    @change="handleFileUpload(index, $event)"
-    class="hidden-file-input"
-  />
-
-
-              <!-- 선택된 파일 이름 표시 -->
-              <span v-if="receipt.fileName" class="file-name">{{ receipt.fileName }}</span>
-
-              <button type="button" @click="removeReceipt(index)" class="delete-button">
-                <CloseOutlined />
-              </button>
-            </div>
-          </div>
-        </div>
-
-      </form>
-      <div class="buttons">
-        <button type="submit" class="btn sm primary">등록</button>
-        <button type="reset" class="btn sm secondary">취소</button>
-      </div>
-
-    </div>
-  </div>
-</template>
-
-<script>
-import { PlusCircleFilled, CloseOutlined } from '@ant-design/icons-vue';
-import HrPopup from '../../../component/Popup/HrPopup.vue';
-export default {
-  data() {
-    const today = new Date().toISOString().split('T')[0];
-    return {
-      showPopup: false,
-      fileName: '',
-      startDate: today,
-      startTime: '09:00',
-      endDate: today,
-      endTime: '18:00',
-      where: '',
-      purpose: '',
-      approvals: [
-      ],
-     receipts: [
-        {
-          type: '개인결제',
-          category: '결재',
-          category1: '구분',
-        },
-      ],
-    };
-  },
-  components: {
-    PlusCircleFilled, CloseOutlined,HrPopup
-  },
-  computed: {
-    loginUser() {
-      const authStore = useAuthStore();
-      return authStore.getLoginUser;
-    },
-  },
-
-  methods: {
-    handleFileUpload(index, event) {
-    const file = event.target.files[0];
-    if (file) {
-      this.receipts[index].file = file;
-      this.receipts[index].fileName = file.name;
-    }
-  },
-    addApproval(selectedUser) {
-      this.approvals.push({
-        category: '결재',
-        name: selectedUser.name, // or other fields if needed
-      });
-      this.showPopup = false; // 팝업 닫기
-    },
-    addReceipt() {
-    this.receipts.push({
-      type: '개인결제',
-      category: '',
-      category1: '',
-      name: '',
-      file: null,
-      fileName: '',
-    });
-  },
-    // 승인자 삭제
-    removeApproval(index) {
-      this.approvals.splice(index, 1);
-    },
-    removeReceipt(index) {
-  this.receipts.splice(index, 1);
-},
-    validateForm() {
-      // 필수 입력 필드 체크
-      if (
-        this.startDate &&
-        this.startTime &&
-        this.endDate &&
-        this.endTime &&
-        this.where &&
-        this.purpose.trim() !== ""
-      ) {
-        this.isFormValid = true;
-      } else {
-        this.isFormValid = false;
-      }
-    },
-    calculateDayCount() {
-      const start = new Date(`${this.startDate}T${this.startTime}:00`);
-      const end = new Date(`${this.endDate}T${this.endTime}:00`);
-
-      let totalHours = (end - start) / (1000 * 60 * 60);  // 밀리초를 시간 단위로 변환
-
-      if (this.startDate !== this.endDate) {
-        // 시작일과 종료일이 다른경우
-        const startDateObj = new Date(this.startDate);
-        const endDateObj = new Date(this.endDate);
-        const daysDifference = (endDateObj - startDateObj) / (1000 * 60 * 60 * 24); // 두 날짜 사이의 차이를 일수로 계산
-        if (this.startTime !== "09:00" || this.endTime !== "18:00") {
-          this.dayCount = daysDifference + 0.5; // 시간 조건이 기준에서 벗어날 경우
-        } else {
-          this.dayCount = Math.ceil(daysDifference + 1); // 시간 조건이 기준에 맞을 경우
-        }
-      } else {
-        // 시작일과 종료일이 같은 경우
-        if (this.startTime !== "09:00" || this.endTime !== "18:00") {
-          this.dayCount = 0.5; // 시작 시간 또는 종료 시간이 기준과 다를 경우 0.5
-        } else {
-          this.dayCount = 1; // 기준 시간(09:00~18:00)이 맞으면 1일로 간주
-        }
-      }
-
-      this.validateForm(); // dayCount 변경 후 폼 재검증
-    },
-    handleSubmit() {
-      this.validateForm(); // 제출 시 유효성 확인
-      if (this.isFormValid) {
-        localStorage.setItem('ChuljangFormData', JSON.stringify(this.$data));
-        alert("승인 요청이 완료되었습니다.");
-        // 추가 처리 로직 (API 요청 등)
-      } else {
-        alert("모든 필드를 올바르게 작성해주세요.");
-      }
-    },
-    loadFormData() {
-      const savedData = localStorage.getItem('ChuljangFormData');
-      if (savedData) {
-        this.$data = JSON.parse(savedData);
-      }
-    },
-  },
-  mounted() {
-    // Load the saved form data when the page is loaded
-    this.loadFormData();
-  },
-  watch: {
-    startDate: 'calculateDayCount',
-    startTime: 'calculateDayCount',
-    endDate: 'calculateDayCount',
-    endTime: 'calculateDayCount',
-    where: 'validateForm',
-    purpose: "validateForm",
-  },
-};
-</script>
-
-<style scoped>
-/* 필요한 스타일 추가 */
-</style>
 
client/views/pages/Manager/attendance/BsrpInsert.vue (added)
+++ client/views/pages/Manager/attendance/BsrpInsert.vue
@@ -0,0 +1,583 @@
+<template>
+  <div class="card">
+    <div class="card-body">
+      <h2 class="card-title">출장 신청</h2>
+      <p class="require">* 필수입력</p>
+      <div class="tbl-wrap">
+        <table class="tbl data">
+          <tbody>
+            <tr>
+              <th>출장구분 *</th>
+              <td>
+                <select class="form-select sm" style="width: 110px;" v-model="bsrpInfo.bsrpSe">
+                  <option v-for="(item, idx) of bsrpCodes" :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>출장지 *</th>
+              <td>
+                <input type="text" class="form-control sm" v-model="bsrpInfo.bsrpPlace" />
+              </td>
+              <th>출장목적 *</th>
+              <td>
+                <input type="text" class="form-control sm" v-model="bsrpInfo.bsrpPurps" />
+              </td>
+            </tr>
+            <tr>
+              <th>출장기간 *</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" maxlength="2" @input="validateHour('beginHour', $event)" />
+                    <input type="text" class="form-control sm" style="width: 100px;" placeholder="분" v-model="bsrpInfo.beginMnt" maxlength="2" @input="validateMinute('beginMnt', $event)" />
+                  </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" maxlength="2" @input="validateHour('endHour', $event)" />
+                    <input type="text" class="form-control sm" style="width: 100px;" placeholder="분" v-model="bsrpInfo.endMnt" maxlength="2" @input="validateMinute('endMnt', $event)" />
+                  </div>
+                </div>
+              </td>
+            </tr>
+            <tr>
+              <th>동행자</th>
+              <td>
+                <button type="button" title="추가" @click="isOpenNmprModal = true">
+                  <PlusCircleFilled />
+                </button>
+                <HrPopup v-if="isOpenNmprModal" :selectedEmployees="bsrpInfo.bsrpNmprList" idField="triperId" :dateInfo="bsrpInfo" @select="handleCompanionAdd" @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="handleCompanionRemove(idx)" @mousedown.stop>
+                        <CloseOutlined />
+                      </button>
+                    </div>
+                  </div>
+                </div>
+              </td>
+              <th>승인자 *</th>
+              <td>
+                <button type="button" title="추가" @click="isOpenSanctnModal = true">
+                  <PlusCircleFilled />
+                </button>
+                <HrPopup v-if="isOpenSanctnModal" :selectedEmployees="bsrpCnsul.sanctnList" idField="confmerId" @select="handleApproverAdd" @close="isOpenSanctnModal = false" />
+                <div class="approval-container">
+                  <SanctnList v-model:lists="bsrpCnsul.sanctnList" @delSanctn="handleApproverRemove" />
+                </div>
+              </td>
+            </tr>
+            <tr>
+              <th>품의내용 *</th>
+              <td colspan="3" style="height: calc(100% - 550px);">
+                <EditorComponent v-model:contents="bsrpCnsul.cn" />
+              </td>
+            </tr>
+            <tr>
+              <th>법인카드</th>
+              <td>
+                <button type="button" title="추가" @click="isOpenCardModal = true">
+                  <PlusCircleFilled />
+                </button>
+                <CorpCardPopup v-if="isOpenCardModal" :bsrpInfo="bsrpInfo" :lists="cards" @close="isOpenCardModal = false" @onSelected="handleCardAdd" />
+                <div class="approval-container">
+                  <div v-for="(card, idx) in cards" :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="handleCardRemove(idx)" class="delete-button">
+                        <CloseOutlined />
+                      </button>
+                    </form>
+                  </div>
+                </div>
+              </td>
+              <th>법인차량</th>
+              <td>
+                <button type="button" title="추가" @click="isOpenVhcleModal = true">
+                  <PlusCircleFilled />
+                </button>
+                <CorpCarPopup v-if="isOpenVhcleModal" :bsrpInfo="bsrpInfo" :lists="vhcles" @close="isOpenVhcleModal = false" @onSelected="handleVehicleAdd" />
+                <div class="approval-container">
+                  <div v-for="(vhcle, idx) in vhcles" :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="handleVehicleRemove(idx)" class="delete-button">
+                      <CloseOutlined />
+                    </button>
+                  </div>
+                </div>
+              </td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+      <div class="buttons">
+        <button type="button" class="btn sm btn-red" @click="handleLoadLastApprovers">이전 승인자 불러오기</button>
+        <button type="button" class="btn sm primary" v-if="$isEmpty(pageId)" @click="handleSave">신청</button>
+        <template v-else>
+          <button type="button" class="btn sm primary" @click="handleUpdate"> {{ submitButtonText }} </button>
+          <button type="button" class="btn sm secondary" @click="handleCancel">취소</button>
+        </template>
+      </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, findBsrpProc, updateBsrpProc, findLastBsrpCnsulProc } from '../../../../resources/api/bsrp';
+
+export default {
+  name: 'BsrpInsert',
+
+  components: {
+    PlusCircleFilled, CloseOutlined,
+    HrPopup, CorpCarPopup, CorpCardPopup, SanctnList, EditorComponent,
+  },
+
+  data() {
+    return {
+      pageId: null,
+      pageMode: null,
+      userInfo: this.$store.state.userInfo,
+      isOpenNmprModal: false,
+      isOpenSanctnModal: false,
+      isOpenCardModal: false,
+      isOpenVhcleModal: false,
+      bsrpCodes: [],
+      sanctnCodes: [],
+      defaultSanctnCode: null,
+      bsrpInfo: {
+        bsrpId: null,
+        applcntId: null,
+        bsrpSe: null,
+        bsrpSeNm: null,
+        bsrpPlace: null,
+        bsrpPurps: null,
+        bgnde: null,
+        beginHour: null,
+        beginMnt: null,
+        endde: null,
+        endHour: null,
+        endMnt: null,
+        bsrpNmprList: []
+      },
+      cards: [],
+      vhcles: [],
+      bsrpCnsul: {
+        bsrpId: null,
+        cn: null,
+        rgsde: null,
+        register: null,
+        updde: null,
+        updusr: null,
+        sanctnList: []
+      },
+      totalDays: 0,
+    };
+  },
+
+  watch: {
+    // 시작일 변동 시 날짜 재계산
+    'bsrpInfo.bgnde'() {
+      this.calculateDays();
+    },
+    // 종료일 변동 시 날짜 재계산
+    'bsrpInfo.endde'() {
+      this.calculateDays();
+    },
+  },
+
+  computed: {
+    // 재신청 여부
+    isReapplyMode() {
+      return this.pageMode === 'reapply';
+    },
+
+    // 재신청 여부에 따른 등록 버튼 변경
+    submitButtonText() {
+      return this.isReapplyMode ? '재신청' : '수정';
+    }
+  },
+
+  async created() {
+    this.pageId = this.$route.query.id;
+    this.pageMode = this.$route.query.type;
+
+    this.bsrpCodes = await this.$findChildCodes('sanctn_mby_bsrp'); // 출장 구분 코드 조회
+    this.bsrpInfo.bsrpSe = this.bsrpCodes[0].code;
+
+    this.sanctnCodes = await this.$findChildCodes('sanctn_code'); // 결재 구분 코드 조회
+    this.defaultSanctnCode = this.sanctnCodes[0].code;
+  },
+
+  mounted() {
+    if (!this.$isEmpty(this.pageId)) {
+      this.fetchData(); // 출장 정보 조회
+    }
+  },
+
+  methods: {
+    // 출장 정보 조회
+    async fetchData() {
+      try {
+        const response = await findBsrpProc(this.pageId);
+        const result = response.data.data;
+
+        this.bsrpInfo = result.bsrpInfoDTO;
+        this.cards = result.cards;
+        this.vhcles = result.vhcles;
+        this.bsrpCnsul = result.bsrpCnsulDTO;
+      } catch (error) {
+        alert(error.response.data.message);
+        this.handleNavigation('list');
+      }
+    },
+
+    // 이전 승인자 조회 핸들러
+    async handleLoadLastApprovers() {
+      try {
+        const response = await findLastBsrpCnsulProc();
+        const result = response.data.data;
+
+        this.bsrpCnsul.sanctnList = result;
+      } catch (error) {
+        const message = error.response?.data?.message || "이전 승인자를 불러오는데 실패했습니다.";
+        alert(message);
+      }
+    },
+
+    // 저장 핸들러
+    async handleSave() {
+      try {
+        if (!this.validateForm()) {
+          return;
+        }
+
+        let data = this.buildSendData("insert");
+
+        const response = await saveBsrpProc(data);
+        alert("등록되었습니다.");
+
+        this.handleNavigation('view', response.data.data.pageId);
+      } catch (error) {
+        this.handleError(error);
+      }
+    },
+
+    // 수정 핸들러
+    async handleUpdate() {
+      try {
+        if (!this.validateForm()) {
+          return;
+        }
+
+        let data = this.buildSendData("update");
+
+        const response = await updateBsrpProc(this.pageId, data);
+        const message = this.isReapplyMode ? "재신청되었습니다." : "수정되었습니다.";
+        alert(message);
+
+        this.handleNavigation('view', response.data.data.pageId);
+      } catch (error) {
+        this.handleError(error);
+      }
+    },
+
+    // 동행자 추가 핸들러
+    handleCompanionAdd(item) {
+      const data = {
+        triperId: item.userId,
+        triperNm: item.userNm,
+        deptId: item.deptId,
+        deptNm: item.deptNm,
+        clsf: item.clsf,
+        clsfNm: item.clsfNm,
+      };
+
+      this.bsrpInfo.bsrpNmprList.push(data);
+      this.isOpenNmprModal = false;
+    },
+
+    // 동행자 삭제 핸들러
+    handleCompanionRemove(idx) {
+      this.bsrpInfo.bsrpNmprList.splice(idx, 1);
+    },
+
+    // 승인자 추가 핸들러
+    handleApproverAdd(user) {
+      const data = {
+        confmerId: user.userId,
+        confmerNm: user.userNm,
+        clsf: user.clsf,
+        clsfNm: user.clsfNm,
+        sanctnOrdr: this.bsrpCnsul.sanctnList.length + 1,
+        sanctnIem: 'bsrp_cnsul',
+        sanctnSe: this.defaultSanctnCode,
+      };
+
+      this.bsrpCnsul.sanctnList.push(data);
+      this.isOpenSanctnModal = false;
+    },
+
+    // 승인자 삭제 핸들러
+    handleApproverRemove(idx) {
+      this.bsrpCnsul.sanctnList.splice(idx, 1);
+      this.bsrpCnsul.sanctnList.forEach((item, index) => {
+        item.sanctnOrdr = index + 1;
+      });
+    },
+
+    // 법인 카드 추가 핸들러
+    handleCardAdd(item) {
+      this.cards.push(item);
+      this.isOpenCardModal = false;
+    },
+
+    // 법인 카드 삭제 핸들러
+    handleCardRemove(idx) {
+      this.cards.splice(idx, 1);
+    },
+
+    // 법인 차량 추가 핸들러
+    handleVehicleAdd(item) {
+      item.drverId = this.userInfo.userId;
+      this.vhcles.push(item);
+      this.isOpenVhcleModal = false;
+    },
+
+    // 법인 차량 삭제 핸들러
+    handleVehicleRemove(idx) {
+      this.vhcles.splice(idx, 1);
+    },
+
+    // 취소 핸들러
+    handleCancel() {
+      if (confirm('작성 중인 내용이 삭제됩니다. 계속하시겠습니까?')) {
+        this.handleNavigation('view', this.pageId);
+      }
+    },
+
+    // 페이지 이동 핸들러
+    handleNavigation(type, id) {
+      const routeMap = {
+        'list': { name: '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({ name: 'BsrpListPage' });
+      }
+    },
+
+    // 에러 핸들러
+    handleError(error) {
+      const message = error.response?.data?.message || "에러가 발생했습니다.";
+      alert(message);
+    },
+
+    // 일수 계산 유틸리티
+    calculateDays() {
+      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);
+    },
+
+    // 출장 시간 유효성 검사 유틸리티
+    validateHour(field, event) {
+      let value = event.target.value.replace(/[^0-9]/g, '');
+
+      if (value.length > 0) {
+        const hour = parseInt(value);
+        if (hour > 23) {
+          value = '23';
+        }
+      }
+
+      this.bsrpInfo[field] = value;
+    },
+
+    // 출장 분 유효성 검사 유틸리티
+    validateMinute(field, event) {
+      let value = event.target.value.replace(/[^0-9]/g, '');
+
+      if (value.length > 0) {
+        const minute = parseInt(value);
+        if (minute > 59) {
+          value = '59';
+        }
+      }
+
+      this.bsrpInfo[field] = value;
+    },
+
+    // 입력값 전체 유효성 검사 유틸리티
+    validateForm() {
+      if (this.$isEmpty(this.bsrpInfo.bsrpSe)) {
+        alert('출장구분을 선택해주세요.');
+        return false;
+      }
+
+      if (this.$isEmpty(this.bsrpInfo.bsrpPlace)) {
+        alert('출장지를 입력해주세요.');
+        return false;
+      }
+
+      if (this.$isEmpty(this.bsrpInfo.bsrpPurps)) {
+        alert('출장목적을 입력해주세요.');
+        return false;
+      }
+
+      if (this.$isEmpty(this.bsrpInfo.bgnde) || this.$isEmpty(this.bsrpInfo.beginHour) || this.$isEmpty(this.bsrpInfo.beginMnt)) {
+        alert('출장 시작일시를 모두 입력해주세요.');
+        return false;
+      }
+
+      if (this.$isEmpty(this.bsrpInfo.endde) || this.$isEmpty(this.bsrpInfo.endHour) || this.$isEmpty(this.bsrpInfo.endMnt)) {
+        alert('출장 종료일시를 모두 입력해주세요.');
+        return false;
+      }
+
+      if (!this.validateTimeRange(this.bsrpInfo.beginHour, this.bsrpInfo.beginMnt, '시작')) {
+        return false;
+      }
+
+      if (!this.validateTimeRange(this.bsrpInfo.endHour, this.bsrpInfo.endMnt, '종료')) {
+        return false;
+      }
+
+      if (this.$isEmpty(this.bsrpCnsul.sanctnList) || this.bsrpCnsul.sanctnList.length === 0) {
+        alert('승인자를 선택해주세요.');
+        return false;
+      }
+
+      if (this.$isEmpty(this.bsrpCnsul.cn)) {
+        alert('품의내용을 입력해주세요.');
+        return false;
+      }
+
+      return true;
+    },
+
+    // 출장 기간 유효성 검사 유틸리티
+    validateTimeRange(hour, minute, timeType) {
+      const hourNum = parseInt(hour);
+      const minuteNum = parseInt(minute);
+
+      if (isNaN(hourNum) || hourNum < 0 || hourNum > 23) {
+        alert(`${timeType} 시간은 0-23 사이의 값을 입력해주세요.`);
+        return false;
+      }
+
+      if (isNaN(minuteNum) || minuteNum < 0 || minuteNum > 59) {
+        alert(`${timeType} 분은 0-59 사이의 값을 입력해주세요.`);
+        return false;
+      }
+
+      return true;
+    },
+
+    // 데이터 가공 유틸리티
+    buildSendData(type) {
+      let bsrpInfo = {
+        applcntId: this.bsrpInfo.applcntId,
+        bsrpSe: this.bsrpInfo.bsrpSe,
+        bsrpPlace: this.bsrpInfo.bsrpPlace,
+        bsrpPurps: this.bsrpInfo.bsrpPurps,
+        bgnde: this.bsrpInfo.bgnde,
+        beginHour: this.bsrpInfo.beginHour,
+        beginMnt: this.bsrpInfo.beginMnt,
+        endde: this.bsrpInfo.endde,
+        endHour: this.bsrpInfo.endHour,
+        endMnt: this.bsrpInfo.endMnt,
+        bsrpNmprList: this.bsrpInfo.bsrpNmprList,
+      }
+
+      let cards = [];
+      for (let card of this.cards) {
+        cards.push({
+          cardId: card.cardId,
+        })
+      }
+
+      let vhcles = [];
+      for (let vhcle of this.vhcles) {
+        vhcles.push({
+          vhcleId: vhcle.vhcleId,
+          drverId: vhcle.drverId,
+        })
+      }
+
+      let sanctnList = [];
+      for (let sanctn of this.bsrpCnsul.sanctnList) {
+        sanctnList.push({
+          confmerId: sanctn.confmerId,
+          clsf: sanctn.clsf,
+          sanctnOrdr: sanctn.sanctnOrdr,
+          sanctnIem: sanctn.sanctnIem,
+          sanctnSe: sanctn.sanctnSe,
+        })
+      }
+
+      let bsrpCnsul = {
+        cn: this.bsrpCnsul.cn,
+        sanctnList: sanctnList,
+      }
+
+      if (type === "insert") {
+        return {
+          insertBsrpInfoDTO: bsrpInfo,
+          cardDtlsList: cards,
+          vhcleDtlsList: vhcles,
+          insertBsrpCnsulDTO: bsrpCnsul,
+        };
+      } else if (type === "update") {
+        return {
+          updateBsrpInfoDTO: bsrpInfo,
+          cardDtlsList: cards,
+          vhcleDtlsList: vhcles,
+          updateBsrpCnsulDTO: bsrpCnsul,
+        };
+      }
+    },
+  },
+};
+</script>(파일 끝에 줄바꿈 문자 없음)
 
client/views/pages/Manager/attendance/BsrpList.vue (added)
+++ client/views/pages/Manager/attendance/BsrpList.vue
@@ -0,0 +1,183 @@
+<template>
+  <div class="col-lg-12">
+    <div class="card">
+      <div class="card-body">
+        <h2 class="card-title">출장 현황</h2>
+        <div class="sch-form-wrap">
+          <div class="input-group">
+            <select class="form-select" v-model="searchParams.year" @change="handleSearchChange">
+              <option value="">연도 전체</option>
+              <option v-for="year in yearOptions" :key="year" :value="year">{{ year }}년</option>
+            </select>
+            <select class="form-select" v-model="searchParams.month" @change="handleSearchChange">
+              <option value="">월 전체</option>
+              <option v-for="month in monthOptions" :key="month" :value="month">{{ month }}월</option>
+            </select>
+          </div>
+        </div>
+        <div class="tbl-wrap">
+          <table id="myTable" class="tbl data">
+            <thead>
+              <tr>
+                <th>출장구분</th>
+                <th>출장지</th>
+                <th>목적</th>
+                <th>출장기간</th>
+                <th>품의서 상태</th>
+                <th>복명서 등록 여부</th>
+                <th>복명서 상태</th>
+              </tr>
+            </thead>
+            <tbody>
+              <template v-if="hasBusinessTrips">
+                <tr v-for="(item, idx) in businessTripList" :key="`${item.bsrpId}-${idx}`" @click="handleItemClick(item.bsrpId)">
+                  <td>{{ item.bsrpSeNm }}</td>
+                  <td>{{ item.bsrpPlace }}</td>
+                  <td>{{ item.bsrpPurps }}</td>
+                  <td>{{ $formattedDates(item) }}</td>
+                  <td>{{ item.cnsulConfmAtNm }}</td>
+                  <td>{{ getReportRegistrationStatus(item.hasRport) }}</td>
+                  <td>{{ getReportStatus(item) }}</td>
+                </tr>
+              </template>
+              <tr v-else>
+                <td colspan="7">게시물이 존재하지 않습니다.</td>
+              </tr>
+            </tbody>
+          </table>
+        </div>
+        <Pagination :search="searchParams" @onChange="handlePageChange" />
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+// API
+import { findBsrpsProc } from '../../../../resources/api/bsrp';
+
+export default {
+  name: 'BsrpList',
+
+  // 상수 - 라우트 맵
+  ROUTE_MAP: {
+    'list': { name: 'BsrpListPage' },
+    'view': { name: 'BsrpViewPage', query: { id: null } },
+    'bsrpInsert': { name: 'BsrpInsertPage', query: {} },
+    'bsrpReapply': { name: 'BsrpInsertPage', query: { type: 'reapply' } },
+    'bsrpRportInsert': { name: 'BsrpRportInsertPage', query: {} },
+    'bsrpRportReapply': { name: 'BsrpRportInsertPage', query: { type: 'reapply' } },
+  },
+
+  data() {
+    return {
+      searchParams: {
+        year: '',
+        month: '',
+        currentUserId: this.$store.state.userInfo.userId,
+      },
+      businessTripList: [],
+    };
+  },
+
+  computed: {
+    // 연도 옵션
+    yearOptions() {
+      const startYear = 2020;
+      const currentYear = new Date().getFullYear();
+      const years = [];
+
+      for (let year = currentYear; year >= startYear; year--) {
+        years.push(year);
+      }
+      return years;
+    },
+
+    // 월 옵션
+    monthOptions() {
+      return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
+    },
+
+    // 출장 목록 유무
+    hasBusinessTrips() {
+      return this.businessTripList.length > 0;
+    },
+  },
+
+  async mounted() {
+    await this.fetchBusinessTripList(); // 출장 목록 조회
+  },
+
+  methods: {
+    // 출장 목록 조회
+    async fetchBusinessTripList() {
+      try {
+        const response = await findBsrpsProc(this.searchParams);
+        const result = response.data.data;
+
+        this.businessTripList = result.lists;
+        this.searchParams = result.search;
+      } catch (error) {
+        this.handleError(error);
+      }
+    },
+
+    // 검색 핸들러 (검색 시 현재 페이지를 1로 변경 후 조회)
+    async handleSearchChange() {
+      await this.handlePageChange(1);
+    },
+
+    // 페이지 변경 핸들러
+    async handlePageChange(currentPage) {
+      this.searchParams.currentPage = Number(currentPage);
+      await this.$nextTick();
+      await this.fetchBusinessTripList();
+    },
+
+    // 상세 페이지 이동 핸들러
+    handleItemClick(id) {
+      this.handleNavigation('view', id);
+    },
+
+    // 페이지 이동 핸들러
+    handleNavigation(type, id) {
+      const routeMap = {
+        'list': { name: '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']);
+      }
+    },
+
+    // 에러 핸들러
+    handleError(error) {
+      const message = error.response?.data?.message || "에러가 발생했습니다.";
+      alert(message);
+    },
+
+    // 등록 여부 유틸
+    getReportRegistrationStatus(hasRport) {
+      return hasRport ? '등록' : '미등록';
+    },
+
+    // 상태 유틸
+    getReportStatus(item) {
+      return item.hasRport ? item.rportConfmAtNm : '-';
+    },
+  },
+};
+</script>
+<style scoped>
+tr {
+  cursor: pointer;
+}
+</style>(파일 끝에 줄바꿈 문자 없음)
 
client/views/pages/Manager/attendance/BsrpRportInsert.vue (added)
+++ client/views/pages/Manager/attendance/BsrpRportInsert.vue
@@ -0,0 +1,465 @@
+<template>
+  <div class="card">
+    <div class="card-body">
+      <h2 class="card-title">출장 복명서 등록</h2>
+      <p class="require">* 필수입력</p>
+      <div class="tbl-wrap">
+        <table class="tbl data">
+          <tbody>
+            <tr>
+              <th>출장구분</th>
+              <td>{{ bsrpInfo.bsrpSeNm }}</td>
+            </tr>
+            <tr>
+              <th>출장지</th>
+              <td>{{ bsrpInfo.bsrpPlace }}</td>
+            </tr>
+            <tr>
+              <th>출장목적</th>
+              <td>{{ bsrpInfo.bsrpPurps }}</td>
+            </tr>
+            <tr>
+              <th>출장기간</th>
+              <td>{{ $formattedDates(bsrpInfo) }}</td>
+            </tr>
+            <tr>
+              <th>동행자</th>
+              <td>
+                <span v-for="(item, idx) of bsrpInfo.bsrpNmprList" :key="idx"> {{ item.triperNm }}{{ idx !== bsrpInfo.bsrpNmprList.length - 1 ? ', ' : '' }} </span>
+              </td>
+            </tr>
+            <tr>
+              <th>법인카드</th>
+              <td>
+                <span v-for="(item, idx) of cards" :key="idx">{{ item.cardNm }}</span>
+              </td>
+            </tr>
+            <tr>
+              <th>법인차량</th>
+              <td>
+                <span v-for="(item, idx) of vhcles" :key="idx">{{ item.vhcleNm }}</span>
+              </td>
+            </tr>
+            <tr>
+              <th>승인자 *</th>
+              <td>
+                <button type="button" title="추가" @click="isOpenSanctnModal = true">
+                  <PlusCircleFilled />
+                </button>
+                <HrPopup v-if="isOpenSanctnModal" :selectedEmployees="bsrpRport.sanctnList" idField="confmerId" @select="handleApproverAdd" @close="isOpenSanctnModal = false" />
+                <div class="approval-container">
+                  <SanctnList v-model:lists="bsrpRport.sanctnList" @delSanctn="handleApproverRemove" />
+                </div>
+              </td>
+            </tr>
+            <tr>
+              <th>복명내용 *</th>
+              <td style="height: calc(100% - 550px);">
+                <EditorComponent v-model:contents="bsrpRport.cn" />
+              </td>
+            </tr>
+            <tr>
+              <th>여비계산</th>
+              <td>
+                <button type="button" title="추가" @click="handleExpenseAdd">
+                  <PlusCircleFilled />
+                </button>
+                <div v-for="(item, idx) of bsrpRport.bsrpTrvctList" :key="idx" class="d-flex gap-2 addapproval mb-2">
+                  <select class="form-select" v-model="item.se" style="width: 140px;">
+                    <option value="" disabled hidden>결제 방식</option>
+                    <option v-for="code of trvctTypeCodes" :key="code.code" :value="code.code"> {{ code.codeNm }} </option>
+                  </select>
+                  <select class="form-select" v-model="item.setleMbyId" style="width: 140px;">
+                    <option value="" disabled hidden>결제 주체</option>
+                    <template v-if="item.se === 'PERSONAL'">
+                      <option :value="userInfo.userId">{{ userInfo.userNm }}</option>
+                      <option v-for="nmpr of bsrpInfo.bsrpNmprList" :key="nmpr.userId" :value="nmpr.userId"> {{ nmpr.triperNm }} </option>
+                    </template>
+                    <template v-else-if="item.se === 'CORPORATE'">
+                      <option v-for="card of cards" :key="card.cardId" :value="card.cardId"> {{ card.cardNm }} </option>
+                    </template>
+                  </select>
+                  <select class="form-select" v-model="item.ty" style="width: 140px;">
+                    <option value="" disabled hidden>출장비 구분</option>
+                    <option v-for="expense of travelExpenseCodes" :key="expense.code" :value="expense.code"> {{ expense.codeNm }} </option>
+                  </select>
+                  <input type="number" class="form-control" placeholder="금액입력" v-model="item.amount" style="max-width: 150px;" />
+                  <div>
+                    <label :for="'fileUpload-' + idx" class="btn sm primary">영수증 첨부</label>
+                    <input :id="'fileUpload-' + idx" type="file" @change="handleFileUpload(idx, $event)" class="hidden-file-input" accept="image/*,.pdf" />
+                    <span v-if="item.fileName" class="file-name">{{ item.fileName }}</span>
+                  </div>
+                  <button type="button" @click="handleExpenseRemove(idx)" class="delete-button">
+                    <CloseOutlined />
+                  </button>
+                </div>
+              </td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+      <div class="buttons">
+        <button type="button" class="btn sm btn-red" @click="handleLoadLastApprovers">이전 승인자 불러오기</button>
+        <button type="button" class="btn sm primary" v-if="!this.hasBsrpRport" @click="handleSave">신청</button>
+        <template v-else>
+          <button type="button" class="btn sm primary" @click="handleUpdate"> {{ submitButtonText }} </button>
+          <button type="button" class="btn sm secondary" @click="handleCancel">취소</button>
+        </template>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import { PlusCircleFilled, CloseOutlined } from '@ant-design/icons-vue';
+import HrPopup from '../../../component/Popup/HrPopup.vue';
+import SanctnList from '../../../component/Sanctn/SanctnFormList.vue';
+import EditorComponent from '../../../component/editor/EditorComponent.vue';
+// API
+import { findBsrpProc } from '../../../../resources/api/bsrp';
+import { findBsrpRportProc, saveBsrpRportProc, updateBsrpRport, findLastBsrpRportProc } from '../../../../resources/api/bsrpRport';
+
+export default {
+  name: 'BsrpRportInsert',
+
+  components: {
+    PlusCircleFilled,
+    CloseOutlined,
+    HrPopup,
+    SanctnList,
+    EditorComponent
+  },
+
+  data() {
+    return {
+      pageId: null,
+      pageMode: null,
+      userInfo: this.$store.state.userInfo,
+      isOpenSanctnModal: false,
+      sanctnCodes: [],
+      defaultSanctnCode: null,
+      trvctTypeCodes: [],
+      travelExpenseCodes: [],
+      bsrpInfo: {
+        bsrpNmprList: []
+      },
+      cards: [],
+      vhcles: [],
+      hasBsrpRport: false,
+      bsrpRport: {
+        cn: null,
+        fileId: null,
+        bsrpTrvctList: [],
+        sanctnList: [],
+      },
+    };
+  },
+
+  computed: {
+    // 재신청 여부
+    isReapplyMode() {
+      return this.pageMode === 'reapply';
+    },
+
+    // 재신청 여부에 따른 등록 버튼 변경
+    submitButtonText() {
+      return this.isReapplyMode ? '재신청' : '수정';
+    }
+  },
+
+  async created() {
+    this.pageId = this.$route.query.id;
+    this.pageMode = this.$route.query.type;
+    if (this.$isEmpty(this.pageId)) {
+      alert("게시물이 존재하지 않습니다.");
+      this.handleNavigation('list');
+      return;
+    }
+
+    this.sanctnCodes = await this.$findChildCodes('sanctn_code');
+    this.defaultSanctnCode = this.sanctnCodes[0]?.code;
+
+    this.trvctTypeCodes = await this.$findChildCodes('BSRP_TRVCT');
+    this.travelExpenseCodes = await this.$findChildCodes('TRAVEL_EXPENSE');
+
+  },
+
+  async mounted() {
+    await this.fetchData(); // 출장 정보 조회
+  },
+
+  methods: {
+    // 출장 정보 조회
+    async fetchData() {
+      try {
+        const response = await findBsrpProc(this.pageId);
+        const result = response.data.data;
+
+        this.bsrpInfo = result.bsrpInfoDTO;
+        this.cards = result.cards;
+        this.vhcles = result.vhcles;
+        this.hasBsrpRport = result.hasBsrpRport;
+
+        if (this.hasBsrpRport) {
+          this.fetchReportData(); // 출장 복명 정보 조회
+        }
+      } catch (error) {
+        alert('데이터 조회에 실패했습니다.');
+        this.handleNavigation('list');
+      }
+    },
+
+    // 출장 복명 정보 조회
+    async fetchReportData() {
+      try {
+        const response = await findBsrpRportProc(this.pageId);
+        const result = response.data.data;
+
+        this.bsrpRport = result;
+        if (this.bsrpRport.bsrpTrvctList && this.bsrpRport.fileList) {
+          this.bsrpRport.bsrpTrvctList.forEach(trvct => {
+            const file = this.bsrpRport.fileList.find(f => f.ordr === trvct.ordr);
+            if (file) {
+              trvct.fileName = file.fileNm;
+              trvct.isExistingFile = true;
+            } else {
+              this.initializeExpenseFile(trvct);
+            }
+          });
+        }
+      } catch (error) {
+        alert('데이터 조회에 실패했습니다.');
+        this.handleNavigation('list');
+      }
+    },
+
+    // 이전 승인자 조회 핸들러
+    async handleLoadLastApprovers() {
+      try {
+        const response = await findLastBsrpRportProc();
+        const result = response.data.data;
+
+        if (result == null || result.length == 0) {
+          alert("출장 복명 기록이 존재하지 않아, 이전 승인자를 불러올 수 없습니다.");
+          return;
+        }
+
+        this.bsrpRport.sanctnList = result;
+      } catch (error) {
+        const message = error.response?.data?.message || "이전 승인자를 불러오는데 실패했습니다.";
+        alert(message);
+      }
+    },
+
+    // 저장 핸들러
+    async handleSave() {
+      if (!this.validateForm()) return;
+
+      try {
+        const formData = this.buildFormData();
+
+        this.bsrpRport.bsrpTrvctList.forEach(item => {
+          if (item.file) {
+            formData.append('files', item.file);
+          }
+        });
+
+        await saveBsrpRportProc(formData);
+        alert('출장 복명서가 등록되었습니다.');
+        this.handleNavigation('view', this.pageId);
+      } catch (error) {
+        alert(error.response?.data?.message || '저장에 실패했습니다.');
+      }
+    },
+
+    // 수정 핸들러
+    async handleUpdate() {
+      if (!this.validateForm()) return;
+
+      try {
+        const formData = this.buildFormData();
+
+        const oldFiles = [];
+        const newFileItems = [];
+
+        this.bsrpRport.bsrpTrvctList.forEach(item => {
+          if (item.isExistingFile && item.ordr > 0) {
+            oldFiles.push(String(item.ordr));
+          } else if (item.file) {
+            newFileItems.push(item);
+          }
+        });
+
+        formData.append('oldFiles', new Blob([JSON.stringify(oldFiles)], { type: 'application/json' }));
+
+        newFileItems.forEach(item => {
+          formData.append('files', item.file);
+        });
+
+        await updateBsrpRport(formData);
+        const message = this.isReapplyMode ? "재신청되었습니다." : "수정되었습니다.";
+        alert(message);
+
+        this.handleNavigation('view', this.pageId);
+      } catch (error) {
+        alert(error.response?.data?.message || '수정에 실패했습니다.');
+      }
+    },
+
+    // 승인자 추가 핸들러
+    handleApproverAdd(user) {
+      this.bsrpRport.sanctnList.push({
+        confmerId: user.userId,
+        confmerNm: user.userNm,
+        clsf: user.clsf,
+        clsfNm: user.clsfNm,
+        sanctnOrdr: this.bsrpRport.sanctnList.length + 1,
+        sanctnIem: 'bsrp_rport',
+        sanctnSe: this.defaultSanctnCode,
+      });
+      this.isOpenSanctnModal = false;
+    },
+
+    // 승인자 삭제 핸들러
+    handleApproverRemove(idx) {
+      this.bsrpRport.sanctnList.splice(idx, 1);
+      this.bsrpRport.sanctnList.forEach((item, index) => {
+        item.sanctnOrdr = index + 1;
+      });
+    },
+
+    // 여비 계산 추가 핸들러
+    handleExpenseAdd() {
+      this.bsrpRport.bsrpTrvctList.push({
+        se: this.cards.length > 0 ? 'CORPORATE' : 'PERSONAL',
+        setleMbyId: this.cards.length > 0 ? this.cards[0].cardId : this.userInfo.userId,
+        ty: '',
+        amount: null,
+        ordr: 0,
+        file: null,
+        fileName: '',
+        isExistingFile: false
+      });
+    },
+
+    // 여비 계산 삭제 핸들러
+    handleExpenseRemove(idx) {
+      this.bsrpRport.bsrpTrvctList.splice(idx, 1);
+    },
+
+    // 파일 업로드 핸들러
+    handleFileUpload(idx, event) {
+      const file = event.target.files[0];
+      if (!file) return;
+
+      const trvct = this.bsrpRport.bsrpTrvctList[idx];
+      trvct.file = file;
+      trvct.fileName = file.name;
+      trvct.isExistingFile = false;
+    },
+
+    // 취소 핸들러
+    handleCancel() {
+      if (confirm('작성 중인 내용이 삭제됩니다. 계속하시겠습니까?')) {
+        this.handleNavigation('view', this.pageId);
+      }
+    },
+
+    // 페이지 이동 핸들러
+    handleNavigation(type, id) {
+      const routeMap = {
+        'list': { name: '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']);
+      }
+    },
+
+    // 파일 정보 초기화 유틸리티
+    initializeExpenseFile(trvct) {
+      trvct.ordr = 0;
+      trvct.isExistingFile = false;
+      trvct.file = null;
+      trvct.fileName = '';
+    },
+
+    // 입력값 전체 유효성 검사 유틸리티
+    validateForm() {
+      if (!this.bsrpRport.cn?.trim()) {
+        alert('복명내용을 입력해주세요.');
+        return false;
+      }
+
+      if (this.bsrpRport.sanctnList.length === 0) {
+        alert('승인자를 선택해주세요.');
+        return false;
+      }
+
+      for (let [idx, item] of this.bsrpRport.bsrpTrvctList.entries()) {
+        const num = idx + 1;
+        if (!item.se) {
+          alert(`${num}번째 여비계산의 결제 방식을 선택해주세요.`);
+          return false;
+        }
+        if (!item.setleMbyId) {
+          alert(`${num}번째 여비계산의 결제 주체를 선택해주세요.`);
+          return false;
+        }
+        if (!item.ty) {
+          alert(`${num}번째 여비계산의 출장비 구분을 선택해주세요.`);
+          return false;
+        }
+        if (!item.amount || item.amount <= 0) {
+          alert(`${num}번째 여비계산의 금액을 입력해주세요.`);
+          return false;
+        }
+        if (!item.fileName) {
+          alert(`${num}번째 여비계산의 영수증을 첨부해주세요.`);
+          return false;
+        }
+      }
+      return true;
+    },
+
+    // 데이터 가공 유틸리티
+    buildFormData() {
+      const formData = new FormData();
+      formData.append('bsrpId', this.pageId);
+
+      const dto = {
+        cn: this.bsrpRport.cn,
+        sanctnList: this.bsrpRport.sanctnList.map(s => ({
+          confmerId: s.confmerId,
+          clsf: s.clsf,
+          sanctnOrdr: s.sanctnOrdr,
+          sanctnIem: s.sanctnIem,
+          sanctnSe: s.sanctnSe,
+        })),
+        bsrpTrvctList: this.bsrpRport.bsrpTrvctList.map(t => ({
+          se: t.se,
+          setleMbyId: t.setleMbyId,
+          ty: t.ty,
+          amount: t.amount,
+          ordr: t.ordr || 0,
+        }))
+      };
+
+      if (this.bsrpRport.fileId) {
+        dto.fileId = this.bsrpRport.fileId;
+      }
+
+      formData.append('bsrpRportDTO', new Blob([JSON.stringify(dto)], { type: 'application/json' }));
+      return formData;
+    },
+  },
+};
+</script>(파일 끝에 줄바꿈 문자 없음)
 
client/views/pages/Manager/attendance/BsrpView.vue (added)
+++ client/views/pages/Manager/attendance/BsrpView.vue
@@ -0,0 +1,322 @@
+<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>(파일 끝에 줄바꿈 문자 없음)
 
client/views/pages/Manager/attendance/ChuljangBokmyeongDetail.vue (deleted)
--- client/views/pages/Manager/attendance/ChuljangBokmyeongDetail.vue
@@ -1,142 +0,0 @@
-<template>
-<div class="card ">
-      <div class="card-body">
-          <h2 class="card-title">출장 현황</h2>
-       
-      <div class="form-card">
-        <h1>출장복명서</h1>
-        <div class="approval-box tbl-wrap tbl2">
-         <table class="tbl data">
-          <tbody>
-            <tr class="thead">
-                <td rowspan="2" class="th">승인자</td>
-                <td v-for="(approver, index) in approvers" :key="index">
-                  <p class="position">{{ approver.position }}</p>
-                </td>
-              </tr>
-              <tr>
-                <td v-for="(approver, index) in approvers" :key="index">
-                  <p class="name">{{ approver.name }}</p>
-                  <p class="date">{{ approver.date }}</p>
-                </td>
-              </tr>
-          </tbody>
-
-         </table>
-        </div>
-          <form class="row g-3 needs-validation detail"  :class="{ 'was-validated': formSubmitted }" @submit.prevent="handleRegister" novalidate>
-               
-            <div class="col-12 chuljang">
-                  <label for="yourName" class="form-label">복명내용</label>
-                  <input v-model="name" type="text" name="name" class="form-control textarea " readonly>
-            </div>
-            <div class="col-12">
-                  <label for="yourName" class="form-label">여비계산</label>
-                  <input v-model="name" type="text" name="name" class="form-control " readonly>
-            </div>
-            <div class="col-12 ">
-                  <label for="yourName" class="form-label">복명 신청일</label>
-                  <input  v-model="name" type="text" name="name" class="form-control" readonly   placeholder="2025-01-01">
-            </div>
-            <div class="col-12 border-x return">
-                  <label for="yourName" class="form-label">반려사유</label>
-                  <input  v-model="name" type="text" name="name" class="form-control" readonly   placeholder="2025-01-01">
-            </div>
-           
-                
-              </form>
-      </div>
-      <div class="buttons">
-              <button class="btn sm primary"  type="submit">승인</button>
-              <button class="btn sm  btn-red"  type="submit" @click="showPopup = true">반려</button>
-              <button class="btn sm  btn-red"  type="submit">신청취소</button>
-                <button class="btn sm secondary"  type="submit">재신청</button>
-                <button class="btn sm secondary"  type="submit">수정</button>
-                <button class="btn sm tertiary "  type="submit">목록</button>
-              </div>
-              <ReturnPopup v-if="showPopup" @close="showPopup = false"/>
-
-      </div>
-    </div>
-</template>
-
-<script>
-import ReturnPopup from '../../../component/Popup/ReturnPopup.vue';
-export default {
-  data() {
-    const today = new Date().toISOString().split('T')[0];
-    return {
-      showPopup: false,
-      startDate: today,
-      startTime: "09:00", // 기본 시작 시간 09:00
-      endDate: today,
-      endTime: "18:00", // 기본 종료 시간 18:00
-      category: "", 
-      dayCount: 1, 
-      reason: "", // 사유
-      approvers: [
-        { position: '', name: '', date: '' },
-        { position: '', name: '', date: '' },
-      ],
-      listData: [
-  {
-    type: '연차',
-    approvalType: '결재',
-    applicant: '홍길동',
-    period: '2025-05-10 ~ 2025-15-03',
-    requestDate: '2025-04-25',
-    status: '대기'
-  },   {
-    type: '반차',
-    approvalType: '전결',
-    applicant: '홍길동',
-    period: '2025-05-01 ~ 2025-05-03',
-    requestDate: '2025-04-25',
-    status: '승인'
-  }],
-    };
-  },
-  components: {
-    ReturnPopup
-  },
-  computed: {
-  },
-  methods: {
-    hasAnyApprover() {
-      return this.approvers.some(
-        (approver) =>
-          approver.name?.trim() !== '' && approver.date?.trim() !== ''
-      );
-    },
-    calculateDayCount() {
-    const start = new Date(`${this.startDate}T${this.startTime}:00`);
-    const end = new Date(`${this.endDate}T${this.endTime}:00`);
-    
-    let totalHours = (end - start) / (1000 * 60 * 60);  // 밀리초를 시간 단위로 변환
-    
-    if (this.startDate !== this.endDate) {
-      // 시작일과 종료일이 다른경우
-      const startDateObj = new Date(this.startDate);
-      const endDateObj = new Date(this.endDate);
-      const daysDifference = (endDateObj - startDateObj) / (1000 * 60 * 60 * 24); // 두 날짜 사이의 차이를 일수로 계산
-      if (this.startTime !== "09:00" || this.endTime !== "18:00") {
-        this.dayCount = daysDifference + 0.5; // 시간 조건이 기준에서 벗어날 경우
-      } else {
-        this.dayCount = Math.ceil(daysDifference + 1); // 시간 조건이 기준에 맞을 경우
-      }
-    } else {
-      // 시작일과 종료일이 같은 경우
-      if (this.startTime !== "09:00" || this.endTime !== "18:00") {
-        this.dayCount = 0.5; // 시작 시간 또는 종료 시간이 기준과 다를 경우 0.5
-      } else {
-        this.dayCount = 1; // 기준 시간(09:00~18:00)이 맞으면 1일로 간주
-      }
-    }
-
-   
-  },
-   
-    
-  },
-};
-</script>
 
client/views/pages/Manager/attendance/ChuljangDetailAll.vue (deleted)
--- client/views/pages/Manager/attendance/ChuljangDetailAll.vue
@@ -1,262 +0,0 @@
-<template>
-  <div class="card">
-    <div class="card-body">
-      <h2 class="card-title">출장 현황</h2>
-      <div class="form-card" style="margin-bottom: 20px;">
-        <h1>출장품의서</h1>
-        <SanctnViewList v-if="bsrpCnsul.sanctnList.length > 0" :sanctns="bsrpCnsul.sanctnList" />
-        <form class="row g-3 needs-validation detail" :class="{ 'was-validated': formSubmitted }" @submit.prevent="handleRegister" novalidate>
-          <div class="col-12">
-            <div class="col-12 border-x">
-              <label class="form-label">출장구분</label>
-              <p>{{ bsrpInfo.bsrpSeNm }}</p>
-            </div>
-            <div class="col-12 border-x">
-              <label class="form-label">이름</label>
-              <p>{{ user.userNm }}</p>
-            </div>
-          </div>
-          <div class="col-12">
-            <div class="col-12 border-x">
-              <label class="form-label">부서</label>
-              <p>{{ user.deptNm }}</p>
-            </div>
-            <div class="col-12 border-x">
-              <label class="form-label">직급</label>
-              <p>{{ user.clsfNm }}</p>
-            </div>
-          </div>
-          <div class="col-12">
-            <label class="form-label">출장지</label>
-            <p>{{ bsrpInfo.bsrpPlace }}</p>
-          </div>
-          <div class="col-12">
-            <label class="form-label">출장목적</label>
-            <p>{{ bsrpInfo.bsrpPurps }}</p>
-          </div>
-          <div class="col-12">
-            <label class="form-label">동행자</label>
-            <div v-if="bsrpInfo.bsrpNmprList.length > 0">
-              <span v-for="(item, idx) of bsrpInfo.bsrpNmprList" :key="idx">{{ item.triperNm }}</span>
-            </div>
-          </div>
-          <div class="col-12">
-            <label class="form-label">품의내용</label>
-            <ViewerComponent :content="bsrpCnsul.cn" />
-          </div>
-          <div class="col-12">
-            <label class="form-label">법인카드</label>
-            <div>
-              <p v-for="(item, idx) of cprCardList" :key="idx">
-                <span v-if="idx !== 0">, </span>
-                <span>{{ item.cardNm }}</span>
-              </p>
-            </div>
-          </div>
-          <div class="col-12">
-            <label class="form-label">법인차량</label>
-            <div>
-              <p v-for="(item, idx) of cprVhcleList" :key="idx">
-                <span v-if="idx !== 0">, </span>
-                <span>{{ item.vhcleNm }}</span>
-              </p>
-            </div>
-          </div>
-          <div class="col-12 border-x">
-            <label class="form-label">품의 신청일</label>
-            <p>{{ bsrpCnsul.rgsde }}</p>
-          </div>
-        </form>
-      </div>
-      <div v-if="!$isEmpty(bsrpRport.bsrpId)" class="form-card">
-        <h1>출장복명서</h1>
-        <SanctnViewList v-if="bsrpRport.sanctnList.length > 0" :sanctns="bsrpRport.sanctnList" />
-        <form class="row g-3 needs-validation detail" :class="{ 'was-validated': formSubmitted }" @submit.prevent="handleRegister" novalidate>
-          <div class="col-12">
-            <label class="form-label">복명내용</label>
-            <ViewerComponent :content="bsrpRport.cn" />
-          </div>
-          <div class="col-12">
-            <label class="form-label">여비계산</label>
-            <p>{{ bsrpInfo.applcntId }}</p>
-          </div>
-          <div class="col-12">
-            <label class="form-label">복명 신청일</label>
-            <p>{{ bsrpRport.rgsde }}</p>
-          </div>
-          <div class="col-12 border-x return">
-            <label class="form-label">반려사유</label>
-            <p>{{ bsrpInfo.applcntId }}</p>
-          </div>
-        </form>
-      </div>
-      <div class="buttons">
-        <button v-if="sanctnStatus === 'waiting' && sanctnStatus !== 'approved'" type="button" class="btn sm btn-red" @click="deleteData">신청취소</button>
-        <button v-if="sanctnStatus === 'waiting'" type="button" class="btn sm secondary" @click="fnMoveTo('edit', pageId)">수정</button>
-        <button v-if="sanctnStatus === 'rejected'" type="button" class="btn sm secondary" @click="fnMoveTo('edit', pageId)">재신청</button>
-        <button v-if="sanctnStatus === 'approved'" type="button" class="btn sm primary" @click="fnMoveTo('plus', pageId)">복명서 작성</button>
-        <button type="button" class="btn sm tertiary" @click="fnMoveTo('list')">목록</button>
-      </div>
-    </div>
-  </div>
-</template>
-<script>
-import SanctnViewList from '../../../component/Sanctn/SanctnViewList.vue';
-import ViewerComponent from '../../../component/editor/ViewerComponent.vue';
-// API
-import { bsrpProc, deleteBsrpProc } from '../../../../resources/api/bsrp';
-
-export default {
-  components: {
-    SanctnViewList, ViewerComponent,
-  },
-
-  data() {
-    return {
-      pageId: null,
-
-      // 출장 정보
-      bsrpInfo: {
-        bsrpId: null,       // 출장 아이디
-        applcntId: null,    // 신청자 아이디
-        bsrpSe: null,       // 출장구분 (공통코드 : sanctn_mby_bsrp)
-        bsrpPlace: null,    // 출장지
-        bsrpPurps: null,    // 출장목적
-        bgnde: null,        // 시작일
-        beginHour: null,    // 시작시
-        beginMnt: null,     // 시작분
-        endde: null,        // 종료일
-        endHour: null,      // 종료시
-        endMnt: null,       // 종료분
-        bsrpSeNm: null,     // 출장구분 이름
-        bsrpNmprList: [],   // 출장 인원
-      },
-      // 품의서
-      bsrpCnsul: {
-        bsrpId: null,      // 출장 아이디
-        cn: null,          // 내용
-        confmAt: null,     // 승인 여부 (W: 대기, A: 승인, R 반려)
-        rgsde: null,       // 등록일
-        register: null,    // 등록자
-        updde: null,       // 수정일
-        updusr: null,      // 수정자
-        sanctnList: [],
-      },
-      // 복명서
-      bsrpRport: {
-        bsrpId: null,      // 출장 아이디
-        cn: null,          // 내용
-        confmAt: null,     // 승인 여부 (W: 대기, A: 승인, R 반려)
-        fileId: null,      // 파일 아이디
-        rgsde: null,       // 등록일
-        register: null,    // 등록자
-        updde: null,       // 수정일
-        updusr: null,      // 수정자
-        sanctnList: [],
-      },
-      // 신청자 정보
-      user: {},
-    };
-  },
-
-  computed: {
-    // 결재 상태 확인
-    sanctnStatus() {
-      const sanctnList = this.bsrpCnsul.sanctnList;
-
-      if (sanctnList.length === 0) return 'none'; // 결재 정보 없음
-
-      // 하나라도 반려가 있으면 '반려'
-      if (sanctnList.some(item => item.sanctnSttus === 'R')) {
-        return 'rejected';
-      }
-
-      // 모든 결재가 승인이면 '승인'
-      if (sanctnList.every(item => item.sanctnSttus === 'A')) {
-        return 'approved';
-      }
-
-      // 그 외에는 '대기'
-      return 'waiting';
-    }
-  },
-
-  async created() {
-    this.pageId = this.$route.query.id;
-    if (this.$isEmpty(this.pageId)) {
-      alert("게시물이 존재하지 않습니다.");
-      this.fnMoveTo('list');
-    }
-  },
-
-  mounted() {
-    this.findDatas(); // 상세 조회
-  },
-
-  methods: {
-    // 상세 조회
-    async findDatas() {
-      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.cprCardList = result.cprCardList;
-        this.cprVhcleList = result.cprVhcleList;
-        this.user = result.user;
-
-        this.bsrpCnsul = result.bsrpCnsul;
-        this.bsrpRport = result.bsrpRport;
-      } catch (error) {
-        const message = error.response.data.message;
-        alert(message);
-      }
-    },
-
-    // 삭제
-    async deleteData() {
-      const isCheck = confirm("삭제하시겠습니까?");
-      if (!isCheck) {
-        return;
-      }
-
-      try {
-        const response = await deleteBsrpProc(this.pageId);
-
-        this.fnMoveTo('list');
-      } catch (error) {
-        if (error.response) {
-          alert(error.response.data.message);
-        } else {
-          alert("에러가 발생했습니다.");
-        }
-        console.error(error.message);
-        this.fnMoveTo('list');
-      }
-    },
-
-    // 페이지 이동
-    fnMoveTo(type, id) {
-      const routes = {
-        'list': { name: 'ChuljangStatue' },
-        'view': { name: 'ChuljangDetailAll', query: { id } },
-        'edit': { name: 'ChuljangInsert', query: this.$isEmpty(id) ? {} : { id } },
-        'plus': { 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 } });
-        }
-        this.$router.push(routes[type]);
-      } else {
-        alert("올바르지 않은 경로를 요청하여 목록으로 이동합니다.");
-        this.$router.push(routes['list']);
-      }
-    },
-  },
-};
-</script>
 
client/views/pages/Manager/attendance/ChuljangInsert.vue (deleted)
--- client/views/pages/Manager/attendance/ChuljangInsert.vue
@@ -1,432 +0,0 @@
-<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>(파일 끝에 줄바꿈 문자 없음)
 
client/views/pages/Manager/attendance/ChuljangStatue.vue (deleted)
--- client/views/pages/Manager/attendance/ChuljangStatue.vue
@@ -1,142 +0,0 @@
-<template>
-  <div class="col-lg-12">
-    <div class="card">
-      <div class="card-body">
-        <h2 class="card-title">출장 현황</h2>
-        <div class="sch-form-wrap">
-          <div class="input-group">
-            <select class="form-select" v-model="request.year" @change="fnChangeCurrentPage(1)">
-              <option value="">연도 전체</option>
-              <option v-for="year in years" :key="year" :value="year">{{ year }}년</option>
-            </select>
-            <select class="form-select" v-model="request.month" @change="fnChangeCurrentPage(1)">
-              <option value="">월 전체</option>
-              <option v-for="month in months" :key="month" :value="month">{{ month }}월</option>
-            </select>
-          </div>
-        </div>
-        <div class="tbl-wrap">
-          <table id="myTable" class="tbl data">
-            <thead>
-              <tr>
-                <th>출장구분</th>
-                <th>출장지</th>
-                <th>목적</th>
-                <th>출장기간</th>
-                <th>품의서 상태</th>
-                <th>복명서 등록 여부</th>
-                <th>복명서 상태</th>
-              </tr>
-            </thead>
-            <tbody>
-              <tr v-for="(item, idx) in lists" :key="idx" :class="{ 'expired': item.hasRport }" @click="fnMoveTo('view', item.bsrpId)">
-                <td>{{ item.bsrpSeNm }}</td>
-                <td>{{ item.bsrpPlace }}</td>
-                <td>{{ item.bsrpPurps }}</td>
-                <td>{{ item.bgnde }}</td>
-                <td>{{ item.bsrpCnsulDTO.confmAtNm }}</td>
-                <td>{{ item.hasRport ? '등록' : '미등록' }}</td>
-                <td>{{ item.hasRport ? item.bsrpRportDTO.confmAt : '-' }}</td>
-              </tr>
-            </tbody>
-          </table>
-        </div>
-        <Pagination :search="request" @onChange="fnChangeCurrentPage" />
-      </div>
-    </div>
-  </div>
-</template>
-<script>
-import { SearchOutlined } from '@ant-design/icons-vue';
-// API
-import { bsrpsProc } from '../../../../resources/api/bsrp';
-
-export default {
-  components: {
-    SearchOutlined
-  },
-
-  data() {
-    return {
-      photoicon: "/client/resources/img/photo_icon.png",
-
-      years: [],
-      months: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
-
-      cmmnCodes: {},
-      request: {
-        year: '',
-        month: '',
-      },
-      lists: [],
-    };
-  },
-
-  watch: {},
-
-  created() {
-    this.generateYears();
-  },
-
-  mounted() {
-    this.findList(); // 목록 조회
-  },
-
-  methods: {
-    generateYears() {
-      const startYear = 2020;
-      const currentYear = new Date().getFullYear();
-
-      for (let year = currentYear; year >= startYear; year--) {
-        this.years.push(year);
-      }
-    },
-
-    // 목록 조회
-    async findList() {
-      try {
-        const response = await bsrpsProc(this.request);
-        const result = response.data.data;
-
-        this.lists = result.lists;
-        this.request = result.search;
-      } catch (error) {
-        const message = error.response.data.message;
-        alert(message);
-      }
-    },
-
-    // 페이지 이동
-    fnChangeCurrentPage(currentPage) {
-      this.request.currentPage = Number(currentPage);
-      this.$nextTick(() => {
-        this.findList();
-      });
-    },
-
-    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>
-<style scoped>
-tr {
-  cursor: pointer;
-}
-</style>
 
client/views/pages/Manager/sanctn/ChuljangBokmyeong.vue (deleted)
--- client/views/pages/Manager/sanctn/ChuljangBokmyeong.vue
@@ -1,181 +0,0 @@
-<template>
-<div class="card ">
-      <div class="card-body">
-          <h2 class="card-title">승인 대기 목록</h2>
-       
-      <div class="form-card">
-        <h1>출장복명서</h1>
-        <div class="approval-box tbl-wrap tbl2">
-         <table class="tbl data">
-          <tbody>
-            <tr class="thead">
-                <td rowspan="2" class="th">승인자</td>
-                <td v-for="(approver, index) in approvers" :key="index">
-                  <p class="position">{{ approver.position }}</p>
-                </td>
-              </tr>
-              <tr>
-                <td v-for="(approver, index) in approvers" :key="index">
-                  <p class="name">{{ approver.name }}</p>
-                  <p class="date">{{ approver.date }}</p>
-                </td>
-              </tr>
-          </tbody>
-
-         </table>
-        </div>
-          <form class="row g-3 needs-validation detail"  :class="{ 'was-validated': formSubmitted }" @submit.prevent="handleRegister" novalidate>
-                <div class="col-12 ">
-                  <div class="col-12 border-x">
-                    <label for="youremail" class="form-label ">출장구분<p class="require"><img :src="require" alt=""></p></label>
-                    <input  v-model="email" type="text" name="username" class="form-control"  readonly >
-                  </div>
-    
-                  <div class="col-12 border-x">
-                    <label for="yourPassword" class="form-label">이름</label>
-                    <input  v-model="password" type="password" name="password" class="form-control"  readonly   placeholder="주식회사 테이큰 소프트"> 
-                  </div>
-               </div>
-               <div class="col-12">
-                  <div class="col-12 border-x">
-                    <label for="youremail" class="form-label">부서</label>
-                    <input  v-model="email" type="text" name="username" class="form-control"  readonly  placeholder="과장">
-                  </div>
-    
-                  <div class="col-12 border-x">
-                    <label for="yourPassword" class="form-label">직급</label>
-                    <input  v-model="password" type="password" name="password" class="form-control"  readonly  placeholder="팀장"> 
-                  </div>
-               </div>
-               <div class="col-12">
-                  <label for="yourName" class="form-label">출장지</label>
-                  <input  v-model="name" type="text" name="name" class="form-control"  readonly>
-            </div>
-            <div class="col-12">
-                  <label for="yourName" class="form-label">출장목적</label>
-                  <input v-model="name" type="text" name="name" class="form-control "  readonly>
-            </div>
-            <div class="col-12">
-                  <label for="yourName" class="form-label">동행자</label>
-                  <input v-model="name" type="text" name="name" class="form-control "  readonly>
-            </div>
-            <div class="col-12 chuljang">
-                  <label for="yourName" class="form-label">복명내용</label>
-                  <input v-model="name" type="text" name="name" class="form-control textarea "  readonly>
-            </div>
-            <div class="col-12">
-                  <label for="yourName" class="form-label">법인카드</label>
-                  <input v-model="name" type="text" name="name" class="form-control "  readonly>
-            </div>
-            <div class="col-12">
-                  <label for="yourName" class="form-label">법인차량</label>
-                  <input v-model="name" type="text" name="name" class="form-control "  readonly>
-            </div>
-            <div class="col-12">
-                  <label for="yourName" class="form-label">여비계산</label>
-                  <input v-model="name" type="text" name="name" class="form-control "  readonly>
-            </div>
-            <div class="col-12 border-x">
-                  <label for="yourName" class="form-label">복명 신청일</label>
-                  <input  v-model="name" type="text" name="name" class="form-control"  readonly   placeholder="2025-01-01">
-            </div>
-           
-                
-              </form>
-      </div>
-      <div class="buttons">
-        <button class="btn sm primary" type="submit">승인</button>
-        <button class="btn sm  btn-red" type="submit" @click="showPopup = true">반려</button>
-        <button class="btn sm tertiary " type="submit">목록</button>
-      </div>
-      <ReturnPopup v-if="showPopup" @close="showPopup = false" />
-
-
-      </div>
-    </div>
-</template>
-
-<script>
-import ReturnPopup from '../../../component/Popup/ReturnPopup.vue';
-export default {
-  data() {
-    const today = new Date().toISOString().split('T')[0];
-    return {
-      showPopup: false,
-      startDate: today,
-      startTime: "09:00", // 기본 시작 시간 09:00
-      endDate: today,
-      endTime: "18:00", // 기본 종료 시간 18:00
-      category: "", 
-      dayCount: 1, 
-      reason: "", // 사유
-      approvers: [
-        { position: '', name: '', date: '' },
-        { position: '', name: '', date: '' },
-      ],
-      listData: [
-  {
-    type: '연차',
-    approvalType: '결재',
-    applicant: '홍길동',
-    period: '2025-05-10 ~ 2025-15-03',
-    requestDate: '2025-04-25',
-    status: '대기'
-  },   {
-    type: '반차',
-    approvalType: '전결',
-    applicant: '홍길동',
-    period: '2025-05-01 ~ 2025-05-03',
-    requestDate: '2025-04-25',
-    status: '승인'
-  }],
-    };
-  },
-  components: {
-    ReturnPopup
-  },
-  computed: {
-    // Pinia Store의 상태를 가져옵니다.
-    loginUser() {
-      const authStore = useAuthStore();
-      return authStore.getLoginUser;
-    },
-  },
-  methods: {
-    hasAnyApprover() {
-      return this.approvers.some(
-        (approver) =>
-          approver.name?.trim() !== '' && approver.date?.trim() !== ''
-      );
-    },
-    calculateDayCount() {
-    const start = new Date(`${this.startDate}T${this.startTime}:00`);
-    const end = new Date(`${this.endDate}T${this.endTime}:00`);
-    
-    let totalHours = (end - start) / (1000 * 60 * 60);  // 밀리초를 시간 단위로 변환
-    
-    if (this.startDate !== this.endDate) {
-      // 시작일과 종료일이 다른경우
-      const startDateObj = new Date(this.startDate);
-      const endDateObj = new Date(this.endDate);
-      const daysDifference = (endDateObj - startDateObj) / (1000 * 60 * 60 * 24); // 두 날짜 사이의 차이를 일수로 계산
-      if (this.startTime !== "09:00" || this.endTime !== "18:00") {
-        this.dayCount = daysDifference + 0.5; // 시간 조건이 기준에서 벗어날 경우
-      } else {
-        this.dayCount = Math.ceil(daysDifference + 1); // 시간 조건이 기준에 맞을 경우
-      }
-    } else {
-      // 시작일과 종료일이 같은 경우
-      if (this.startTime !== "09:00" || this.endTime !== "18:00") {
-        this.dayCount = 0.5; // 시작 시간 또는 종료 시간이 기준과 다를 경우 0.5
-      } else {
-        this.dayCount = 1; // 기준 시간(09:00~18:00)이 맞으면 1일로 간주
-      }
-    }
-
-  },
-    
-    
-  },
-};
-</script>
 
client/views/pages/Manager/sanctn/ChuljangPumui.vue (deleted)
--- client/views/pages/Manager/sanctn/ChuljangPumui.vue
@@ -1,184 +0,0 @@
-<template>
-  <div class="card ">
-    <div class="card-body">
-      <h2 class="card-title">승인 대기 목록</h2>
-
-      <div class="form-card">
-        <h1>출장품의서</h1>
-        <div class="approval-box tbl-wrap tbl2">
-          <table class="tbl data">
-            <tbody>
-              <tr class="thead">
-                <td rowspan="2" class="th">승인자</td>
-                <td v-for="(approver, index) in approvers" :key="index">
-                  <p class="position">{{ approver.position }}</p>
-                </td>
-              </tr>
-              <tr>
-                <td v-for="(approver, index) in approvers" :key="index">
-                  <p class="name">{{ approver.name }}</p>
-                  <p class="date">{{ approver.date }}</p>
-                </td>
-              </tr>
-
-            </tbody>
-
-          </table>
-        </div>
-        <form class="row g-3 needs-validation detail" :class="{ 'was-validated': formSubmitted }"
-          @submit.prevent="handleRegister" novalidate>
-          <div class="col-12 ">
-            <div class="col-12 border-x">
-              <label for="youremail" class="form-label ">출장구분<p class="require"><img :src="require" alt=""></p></label>
-              <input v-model="email" type="text" name="username" class="form-control" id="youremail" readonly>
-            </div>
-
-            <div class="col-12 border-x">
-              <label for="yourPassword" class="form-label">이름</label>
-              <input v-model="password" type="password" name="password" class="form-control" readonly
-                placeholder="주식회사 테이큰 소프트">
-            </div>
-          </div>
-          <div class="col-12">
-            <div class="col-12 border-x">
-              <label for="youremail" class="form-label">부서</label>
-              <input v-model="email" type="text" name="username" class="form-control" readonly placeholder="과장">
-            </div>
-
-            <div class="col-12 border-x">
-              <label for="yourPassword" class="form-label">직급</label>
-              <input v-model="password" type="password" name="password" class="form-control" readonly placeholder="팀장">
-            </div>
-          </div>
-          <div class="col-12">
-            <label for="yourName" class="form-label">출장지</label>
-            <input v-model="name" type="text" name="name" class="form-control" readonly>
-          </div>
-          <div class="col-12">
-            <label for="yourName" class="form-label">출장목적</label>
-            <input v-model="name" type="text" name="name" class="form-control " readonly>
-          </div>
-          <div class="col-12">
-            <label for="yourName" class="form-label">동행자</label>
-            <input v-model="name" type="text" name="name" class="form-control " readonly>
-          </div>
-          <div class="col-12 chuljang">
-            <label for="yourName" class="form-label">내용</label>
-            <input v-model="name" type="text" name="name" class="form-control textarea " readonly>
-          </div>
-          <div class="col-12">
-            <label for="yourName" class="form-label">법인카드</label>
-            <input v-model="name" type="text" name="name" class="form-control " readonly>
-          </div>
-          <div class="col-12">
-            <label for="yourName" class="form-label">법인차량</label>
-            <input v-model="name" type="text" name="name" class="form-control " readonly>
-          </div>
-          <div class="col-12">
-            <label for="yourName" class="form-label">품의 신청일</label>
-            <input v-model="name" type="text" name="name" class="form-control " readonly>
-          </div>
-          <div class="col-12 border-x return">
-            <label for="yourName" class="form-label">반려사유</label>
-            <input v-model="name" type="text" name="name" class="form-control" readonly placeholder="2025-01-01">
-          </div>
-
-
-        </form>
-      </div>
-      <div class="buttons">
-        <button class="btn sm primary" type="submit">승인</button>
-        <button class="btn sm  btn-red" type="submit" @click="showPopup = true">반려</button>
-        <button class="btn sm tertiary " type="submit">목록</button>
-      </div>
-      <ReturnPopup v-if="showPopup" @close="showPopup = false" />
-
-    </div>
-  </div>
-</template>
-
-<script>
-import ReturnPopup from '../../../component/Popup/ReturnPopup.vue';
-export default {
-  data() {
-    const today = new Date().toISOString().split('T')[0];
-    return {
-      showPopup: false,
-      startDate: today,
-      startTime: "09:00", // 기본 시작 시간 09:00
-      endDate: today,
-      endTime: "18:00", // 기본 종료 시간 18:00
-      category: "",
-      dayCount: 1,
-      reason: "", // 사유
-      approvers: [
-        { position: '', name: '', date: '' },
-        { position: '', name: '', date: '' },
-      ],
-      listData: [
-        {
-          type: '연차',
-          approvalType: '결재',
-          applicant: '홍길동',
-          period: '2025-05-10 ~ 2025-15-03',
-          requestDate: '2025-04-25',
-          status: '대기'
-        }, {
-          type: '반차',
-          approvalType: '전결',
-          applicant: '홍길동',
-          period: '2025-05-01 ~ 2025-05-03',
-          requestDate: '2025-04-25',
-          status: '승인'
-        }],
-    };
-  },
-  components: {
-    ReturnPopup
-  },
-  computed: {
-  },
-  methods: {
-    hasAnyApprover() {
-      return this.approvers.some(
-        (approver) =>
-          approver.name?.trim() !== '' && approver.date?.trim() !== ''
-      );
-    },
-    calculateDayCount() {
-      const start = new Date(`${this.startDate}T${this.startTime}:00`);
-      const end = new Date(`${this.endDate}T${this.endTime}:00`);
-
-      let totalHours = (end - start) / (1000 * 60 * 60);  // 밀리초를 시간 단위로 변환
-
-      if (this.startDate !== this.endDate) {
-        // 시작일과 종료일이 다른경우
-        const startDateObj = new Date(this.startDate);
-        const endDateObj = new Date(this.endDate);
-        const daysDifference = (endDateObj - startDateObj) / (1000 * 60 * 60 * 24); // 두 날짜 사이의 차이를 일수로 계산
-        if (this.startTime !== "09:00" || this.endTime !== "18:00") {
-          this.dayCount = daysDifference + 0.5; // 시간 조건이 기준에서 벗어날 경우
-        } else {
-          this.dayCount = Math.ceil(daysDifference + 1); // 시간 조건이 기준에 맞을 경우
-        }
-      } else {
-        // 시작일과 종료일이 같은 경우
-        if (this.startTime !== "09:00" || this.endTime !== "18:00") {
-          this.dayCount = 0.5; // 시작 시간 또는 종료 시간이 기준과 다를 경우 0.5
-        } else {
-          this.dayCount = 1; // 기준 시간(09:00~18:00)이 맞으면 1일로 간주
-        }
-      }
-
-    },
-
-
-
-  },
-};
-</script>
-<style scoped>
-td p {
-  width: 125px;
-}
-</style>
 
client/views/pages/Manager/sanctn/Hyuga.vue (deleted)
--- client/views/pages/Manager/sanctn/Hyuga.vue
@@ -1,152 +0,0 @@
-<template>
-<div class="card ">
-      <div class="card-body">
-          <h2 class="card-title">승인 대기 목록</h2>
-       
-      <div class="form-card">
-        <h1>휴가신청서</h1>
-        <div class="approval-box tbl-wrap tbl2">
-         <table class="tbl data">
-          <tbody>
-            <tr class="thead">
-              <td rowspan="2" class="th">승인자</td>
-              <td>과장</td>
-              <td>소장</td>
-            </tr>
-            <tr>
-              <td><p class="name">홍길동</p><p class="date">2025/05/09</p></td>
-              <td><p class="name">홍길동</p><p class="date">2025/05/09</p></td>
-            </tr>
-          </tbody>
-
-         </table>
-        </div>
-          <form class="row g-3 needs-validation detail"  :class="{ 'was-validated': formSubmitted }" @submit.prevent="handleRegister" novalidate>
-                <div class="col-12 ">
-                  <div class="col-12 border-x">
-                    <label for="youremail" class="form-label ">유형<p class="require"><img :src="require" alt=""></p></label>
-                    <input  v-model="email" type="text" name="username" class="form-control"  readonly >
-                  </div>
-    
-                  <div class="col-12 border-x">
-                    <label for="yourPassword" class="form-label">신청자</label>
-                    <input  v-model="password" type="password" name="password" class="form-control"  readonly   placeholder="주식회사 테이큰 소프트"> 
-                  </div>
-               </div>
-               <div class="col-12">
-                  <div class="col-12 border-x">
-                    <label for="youremail" class="form-label">부서</label>
-                    <input  v-model="email" type="text" name="username" class="form-control"  readonly  placeholder="과장">
-                  </div>
-    
-                  <div class="col-12 border-x">
-                    <label for="yourPassword" class="form-label">직급</label>
-                    <input  v-model="password" type="password" name="password" class="form-control"  readonly  placeholder="팀장"> 
-                  </div>
-               </div>
-               <div class="col-12">
-                  <label for="yourName" class="form-label">기간</label>
-                  <input  v-model="name" type="text" name="name" class="form-control"  readonly>
-            </div>
-            <div class="col-12 hyuga">
-                  <label for="yourName" class="form-label">세부사항</label>
-                  <input v-model="name" type="text" name="name" class="form-control textarea"  readonly>
-            </div>
-            <div class="col-12 ">
-                  <label for="yourName" class="form-label">신청일</label>
-                  <input v-model="name" type="text" name="name" class="form-control "  readonly>
-            </div>
-            <div class="col-12 border-x return" >
-                  <label for="yourName" class="form-label ">반려사유</label>
-                  <input  v-model="name" type="text" name="name" class="form-control"  readonly   >
-            </div>
-           
-                
-              </form>
-      </div>
-            <div class="buttons">
-              <button class="btn sm primary"  type="button">승인</button>
-              <button class="btn sm  btn-red"  type="button" @click="showPopup = true">반려</button>
-                <button class="btn sm tertiary "  type="button">목록</button>
-              </div>
-              <ReturnPopup v-if="showPopup" @close="showPopup = false"/>
-      </div>
-    </div>
-</template>
-
-<script>
-import ReturnPopup from '../../../component/Popup/ReturnPopup.vue';
-export default {
-  data() {
-    const today = new Date().toISOString().split('T')[0];
-    return {
-      showPopup: false,
-      startDate: today,
-      startTime: "09:00", // 기본 시작 시간 09:00
-      endDate: today,
-      endTime: "18:00", // 기본 종료 시간 18:00
-      category: "", 
-      dayCount: 1, 
-      reason: "", // 사유
-      listData: [
-  {
-    type: '연차',
-    approvalType: '결재',
-    applicant: '홍길동',
-    period: '2025-05-10 ~ 2025-15-03',
-    requestDate: '2025-04-25',
-    status: '대기'
-  },   {
-    type: '반차',
-    approvalType: '전결',
-    applicant: '홍길동',
-    period: '2025-05-01 ~ 2025-05-03',
-    requestDate: '2025-04-25',
-    status: '승인'
-  }],
-    };
-  },
-  components: {
-    ReturnPopup
-  },
-  computed: {
-  
-  },
-  methods: {
-   
-    calculateDayCount() {
-    const start = new Date(`${this.startDate}T${this.startTime}:00`);
-    const end = new Date(`${this.endDate}T${this.endTime}:00`);
-    
-    let totalHours = (end - start) / (1000 * 60 * 60);  // 밀리초를 시간 단위로 변환
-    
-    if (this.startDate !== this.endDate) {
-      // 시작일과 종료일이 다른경우
-      const startDateObj = new Date(this.startDate);
-      const endDateObj = new Date(this.endDate);
-      const daysDifference = (endDateObj - startDateObj) / (1000 * 60 * 60 * 24); // 두 날짜 사이의 차이를 일수로 계산
-      if (this.startTime !== "09:00" || this.endTime !== "18:00") {
-        this.dayCount = daysDifference + 0.5; // 시간 조건이 기준에서 벗어날 경우
-      } else {
-        this.dayCount = Math.ceil(daysDifference + 1); // 시간 조건이 기준에 맞을 경우
-      }
-    } else {
-      // 시작일과 종료일이 같은 경우
-      if (this.startTime !== "09:00" || this.endTime !== "18:00") {
-        this.dayCount = 0.5; // 시작 시간 또는 종료 시간이 기준과 다를 경우 0.5
-      } else {
-        this.dayCount = 1; // 기준 시간(09:00~18:00)이 맞으면 1일로 간주
-      }
-    }
-
-    this.validateForm(); // dayCount 변경 후 폼 재검증
-  },
-    
-    
-    
-  },
-  mounted() {
-  },
-  
-};
-</script>
client/views/pages/Manager/sanctn/PendingApprovalList.vue
--- client/views/pages/Manager/sanctn/PendingApprovalList.vue
+++ client/views/pages/Manager/sanctn/PendingApprovalList.vue
@@ -1,118 +1,117 @@
 <template>
-
-    <div class="card" style="height: 100%;">
-      <div class="card-body">
-        <div>
-          <h2 class="card-title">승인 대기 목록</h2>
-          <div style="height: 50%;">
-              <div class="d-flex justify-between align-center">
-              <h3 class="sub-title">승인 대기</h3>
-              <div class="search-wrap mb10">
-                  <select class="form-select sm" v-model="pendingSearchParams.year" @change="handlePageChange(1, 'pending')">
-                    <option value="">연도 전체</option>
-                    <option v-for="year in yearOptions" :key="year" :value="year">{{ year }}년</option>
-                  </select>
-                  <select class="form-select sm" v-model="pendingSearchParams.month" @change="handlePageChange(1, 'pending')">
-                    <option value="">월 전체</option>
-                    <option v-for="month in monthOptions" :key="month" :value="month">{{ month }}월</option>
-                  </select>
-                  <input type="text" class="form-control sm" v-model="pendingSearchParams.searchText" placeholder="검색어를 입력하세요." @keyup.enter="handleSearch('pending')">
-                  <button button="button" class="ico-sch" @click="handleSearch('pending')">
-                    <SearchOutlined />
-                  </button>
-              </div>
+  <div class="card" style="height: 100%;">
+    <div class="card-body">
+      <div>
+        <h2 class="card-title">승인 대기 목록</h2>
+        <div style="height: 50%;">
+          <div class="d-flex justify-between align-center">
+            <h3 class="sub-title">승인 대기</h3>
+            <div class="search-wrap mb10">
+              <select class="form-select sm" v-model="pendingSearchParams.year" @change="handlePageChange(1, 'pending')">
+                <option value="">연도 전체</option>
+                <option v-for="year in yearOptions" :key="year" :value="year">{{ year }}년</option>
+              </select>
+              <select class="form-select sm" v-model="pendingSearchParams.month" @change="handlePageChange(1, 'pending')">
+                <option value="">월 전체</option>
+                <option v-for="month in monthOptions" :key="month" :value="month">{{ month }}월</option>
+              </select>
+              <input type="text" class="form-control sm" v-model="pendingSearchParams.searchText" placeholder="검색어를 입력하세요." @keyup.enter="handleSearch('pending')">
+              <button button="button" class="ico-sch" @click="handleSearch('pending')">
+                <SearchOutlined />
+              </button>
             </div>
-            <div class="tbl-wrap mb20">
-              <table id="myTable" class="tbl data common-radius">
-                <colgroup>
-                  <col style="width: 13.33%;">
-                  <col style="width: 13.33%;">
-                  <col style="width: 13.33%;">
-                  <col style="width: 30%;">
-                  <col style="width: 30%;">
-                </colgroup>
-                <thead>
-                  <tr>
-                    <th  style="border-radius: 1rem 0 0 0;">구분</th>
-                    <th>결재구분</th>
-                    <th>신청자</th>
-                    <th>기간</th>
-                    <th style="border-radius: 0 1rem 0 0;">신청일</th>
-                  </tr>
-                </thead>
-                <tbody>
-                  <tr v-for="(item, idx) in pendingApprovalList" :key="idx" @click="handleDetailNavigation(item.sanctnMbyId)">
-                    <td>{{ item.sanctnIemNm }}</td>
-                    <td>{{ item.sanctnSeNm }}</td>
-                    <td>{{ item.registerNm }}</td>
-                    <td>{{ $formattedDates(item) }}</td>
-                    <td>{{ item.rgsde }}</td>
-                  </tr>
-                </tbody>
-              </table>
-            </div>
-            <Pagenation :search="pendingSearchParams" @onChange="(currentPage) => handlePageChange(currentPage, 'pending')" />
           </div>
-          <div style="height: 50%;">
-            <div class="d-flex justify-between align-center">
-              <h3  class="sub-title">승인 이력</h3>
-              <div class="search-wrap mb10">
-                  <select class="form-select sm" v-model="historySearchParams.year" @change="handlePageChange(1, 'history')">
-                    <option value="">연도 전체</option>
-                    <option v-for="year in yearOptions" :key="year" :value="year">{{ year }}년</option>
-                  </select>
-                  <select class="form-select sm" v-model="historySearchParams.month" @change="handlePageChange(1, 'history')">
-                    <option value="">월 전체</option>
-                    <option v-for="month in monthOptions" :key="month" :value="month">{{ month }}월</option>
-                  </select>
-                  <select name="" id="" class="form-select sm">
-                    <option value="all">상태</option>
-                    <option value="">승인</option>
-                    <option value="">반려</option>
-                  </select>
-                    <input type="text" class="form-control sm" v-model="historySearchParams.searchText" placeholder="검색어를 입력하세요." @keyup.enter="handleSearch('history')">
-                    <button button="button" class="ico-sch" @click="handleSearch('history')">
-                      <SearchOutlined />
-                    </button>
-              </div>
-            </div>
-            <div class="tbl-wrap mb20">
-              <table id="myTable" class="tbl data">
-                <colgroup>
-                  <col style="width: 13.33%;">
-                  <col style="width: 13.33%;">
-                  <col style="width: 13.33%;">
-                  <col style="width: 30%;">
-                  <col style="width: 16.66%;">
-                  <col style="width: 13.33%;">
-                </colgroup>
-                <thead>
-                  <tr>
-                    <th  style="border-radius: 1rem 0 0 0;">구분</th>
-                    <th>결재구분</th>
-                    <th>신청자</th>
-                    <th>기간</th>
-                    <th>신청일</th>
-                    <th  style="border-radius: 0 1rem 0 0;">상태</th>
-                  </tr>
-                </thead>
-                <tbody>
-                  <tr v-for="(item, index) in approvalHistoryList" :key="index" class="expired" @click="handleDetailNavigation(item.sanctnMbyId)">
-                    <td>{{ item.sanctnIemNm }}</td>
-                    <td>{{ item.sanctnSeNm }}</td>
-                    <td>{{ item.registerNm }}</td>
-                    <td>{{ $formattedDates(item) }}</td>
-                    <td>{{ item.rgsde }}</td>
-                    <td>{{ item.confmAtNm }}</td>
-                  </tr>
-                </tbody>
-              </table>
-            </div>
-            <Pagenation :search="historySearchParams" @onChange="(currentPage) => handlePageChange(currentPage, 'history')" />
+          <div class="tbl-wrap mb20">
+            <table id="myTable" class="tbl data common-radius">
+              <colgroup>
+                <col style="width: 13.33%;">
+                <col style="width: 13.33%;">
+                <col style="width: 13.33%;">
+                <col style="width: 30%;">
+                <col style="width: 30%;">
+              </colgroup>
+              <thead>
+                <tr>
+                  <th style="border-radius: 1rem 0 0 0;">구분</th>
+                  <th>결재구분</th>
+                  <th>신청자</th>
+                  <th>기간</th>
+                  <th style="border-radius: 0 1rem 0 0;">신청일</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr v-for="(item, idx) in pendingApprovalList" :key="idx" @click="handleDetailNavigation(item.sanctnMbyId)">
+                  <td>{{ item.sanctnIemNm }}</td>
+                  <td>{{ item.sanctnSeNm }}</td>
+                  <td>{{ item.registerNm }}</td>
+                  <td>{{ $formattedDates(item) }}</td>
+                  <td>{{ item.rgsde }}</td>
+                </tr>
+              </tbody>
+            </table>
           </div>
+          <Pagenation :search="pendingSearchParams" @onChange="(currentPage) => handlePageChange(currentPage, 'pending')" />
+        </div>
+        <div style="height: 50%;">
+          <div class="d-flex justify-between align-center">
+            <h3 class="sub-title">승인 이력</h3>
+            <div class="search-wrap mb10">
+              <select class="form-select sm" v-model="historySearchParams.year" @change="handlePageChange(1, 'history')">
+                <option value="">연도 전체</option>
+                <option v-for="year in yearOptions" :key="year" :value="year">{{ year }}년</option>
+              </select>
+              <select class="form-select sm" v-model="historySearchParams.month" @change="handlePageChange(1, 'history')">
+                <option value="">월 전체</option>
+                <option v-for="month in monthOptions" :key="month" :value="month">{{ month }}월</option>
+              </select>
+              <select name="" id="" class="form-select sm">
+                <option value="all">상태</option>
+                <option value="">승인</option>
+                <option value="">반려</option>
+              </select>
+              <input type="text" class="form-control sm" v-model="historySearchParams.searchText" placeholder="검색어를 입력하세요." @keyup.enter="handleSearch('history')">
+              <button button="button" class="ico-sch" @click="handleSearch('history')">
+                <SearchOutlined />
+              </button>
+            </div>
+          </div>
+          <div class="tbl-wrap mb20">
+            <table id="myTable" class="tbl data">
+              <colgroup>
+                <col style="width: 13.33%;">
+                <col style="width: 13.33%;">
+                <col style="width: 13.33%;">
+                <col style="width: 30%;">
+                <col style="width: 16.66%;">
+                <col style="width: 13.33%;">
+              </colgroup>
+              <thead>
+                <tr>
+                  <th style="border-radius: 1rem 0 0 0;">구분</th>
+                  <th>결재구분</th>
+                  <th>신청자</th>
+                  <th>기간</th>
+                  <th>신청일</th>
+                  <th style="border-radius: 0 1rem 0 0;">상태</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr v-for="(item, index) in approvalHistoryList" :key="index" class="expired" @click="handleDetailNavigation(item.sanctnMbyId)">
+                  <td>{{ item.sanctnIemNm }}</td>
+                  <td>{{ item.sanctnSeNm }}</td>
+                  <td>{{ item.registerNm }}</td>
+                  <td>{{ $formattedDates(item) }}</td>
+                  <td>{{ item.rgsde }}</td>
+                  <td>{{ item.confmAtNm }}</td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+          <Pagenation :search="historySearchParams" @onChange="(currentPage) => handlePageChange(currentPage, 'history')" />
         </div>
       </div>
     </div>
+  </div>
 </template>
 <script>
 import { SearchOutlined } from '@ant-design/icons-vue';
@@ -230,7 +229,7 @@
       if (approvalType === "VCATN") {
         this.$router.push({ name: 'HyugaDetail', query: { id, type: 'sanctns' } });
       } if (approvalType === "BSRP") {
-        this.$router.push({ name: 'ChuljangDetailAll', query: { id, type: 'sanctns' } });
+        this.$router.push({ name: 'BsrpViewPage', query: { id, type: 'sanctns' } });
       }
     },
 
Add a comment
List