
--- client/resources/api/sanctns.js
+++ client/resources/api/sanctns.js
... | ... | @@ -1,11 +1,11 @@ |
1 | 1 |
import apiClient from "./index"; |
2 | 2 |
|
3 | 3 |
// 결재 요청 목록 조회 |
4 |
-export const findMyRequestsProc = data => { |
|
5 |
- return apiClient.get('/sanctn/findMyRequests.json', { params: data }); |
|
4 |
+export const findMyApprovalRequestsProc = data => { |
|
5 |
+ return apiClient.get('/sanctn/findMyApprovalRequests.json', { params: data }); |
|
6 | 6 |
} |
7 | 7 |
|
8 |
-// 조회 - 목록 |
|
9 |
-export const findSanctnsProc = data => { |
|
10 |
- return apiClient.get('/sanctn/findSanctns.json', { params: data }); |
|
8 |
+// 승인 대기 목록 조회 |
|
9 |
+export const findPendingApprovalsProc = data => { |
|
10 |
+ return apiClient.get('/sanctn/findPendingApprovals.json', { params: data }); |
|
11 | 11 |
}(파일 끝에 줄바꿈 문자 없음) |
--- client/resources/js/cmmnPlugin.js
+++ client/resources/js/cmmnPlugin.js
... | ... | @@ -1,5 +1,5 @@ |
1 | 1 |
import store from '../../views/pages/AppStore' |
2 |
-import { findCodeListProc } from '../api/code' |
|
2 |
+import { findCodesProc } from '../api/code' |
|
3 | 3 |
|
4 | 4 |
export default { |
5 | 5 |
install(Vue) { |
... | ... | @@ -37,23 +37,6 @@ |
37 | 37 |
} |
38 | 38 |
} |
39 | 39 |
|
40 |
- // 공통코드 조회 |
|
41 |
- Vue.config.globalProperties.$findCodeList = async function (serachRequest) { |
|
42 |
- try { |
|
43 |
- const response = await findCodeListProc(serachRequest); |
|
44 |
- const result = response.data.data; |
|
45 |
- return result.codeList; |
|
46 |
- } catch (error) { |
|
47 |
- if (error.response) { |
|
48 |
- alert(error.response.data.message); |
|
49 |
- } else { |
|
50 |
- alert("에러가 발생했습니다."); |
|
51 |
- } |
|
52 |
- console.error(error.message); |
|
53 |
- throw error; |
|
54 |
- } |
|
55 |
- } |
|
56 |
- |
|
57 | 40 |
// 날짜 형식 변경 |
58 | 41 |
Vue.config.globalProperties.$formattedDate = (d, h, m) => { |
59 | 42 |
if (Vue.config.globalProperties.$isEmpty(d) || Vue.config.globalProperties.$isEmpty(h) || Vue.config.globalProperties.$isEmpty(m)) { |
... | ... | @@ -74,22 +57,66 @@ |
74 | 57 |
return startDate + " ~ " + endDate; |
75 | 58 |
} |
76 | 59 |
|
77 |
- // 공통코드 조회 (부모 코드로 자식 코드 조회) |
|
60 |
+ // 공통코드 조회 |
|
61 |
+ Vue.config.globalProperties.$findCodeList = async function (serachRequest) { |
|
62 |
+ try { |
|
63 |
+ const response = await findCodesProc(serachRequest); |
|
64 |
+ const result = response.data.data; |
|
65 |
+ return result.codeList; |
|
66 |
+ } catch (error) { |
|
67 |
+ if (error.response) { |
|
68 |
+ alert(error.response.data.message); |
|
69 |
+ } else { |
|
70 |
+ alert("에러가 발생했습니다."); |
|
71 |
+ } |
|
72 |
+ console.error(error.message); |
|
73 |
+ throw error; |
|
74 |
+ } |
|
75 |
+ } |
|
76 |
+ |
|
77 |
+ // 공통코드 조회 |
|
78 | 78 |
Vue.config.globalProperties.$findChildCodes = async (upperCode) => { |
79 | 79 |
try { |
80 | 80 |
const searchRequest = { |
81 |
- searchType: 'upperCd', |
|
81 |
+ searchType: 'UPPER_CODE', |
|
82 | 82 |
searchText: upperCode, |
83 | 83 |
}; |
84 | 84 |
|
85 |
- const codes = await Vue.config.globalProperties.$findCodeList(searchRequest); |
|
86 |
- return Array.isArray(codes) ? codes : []; |
|
85 |
+ const response = await findCodesProc(searchRequest); |
|
86 |
+ const result = response.data.data; |
|
87 |
+ |
|
88 |
+ return result.codes; |
|
87 | 89 |
} catch (error) { |
88 | 90 |
console.error(`코드 조회 실패 (${upperCode}):`, error); |
89 | 91 |
return []; |
90 | 92 |
} |
91 | 93 |
}; |
92 | 94 |
|
95 |
+ // 휴가 및 출장 구분 코드 조회 |
|
96 |
+ Vue.config.globalProperties.$sanctnIemCodes = async () => { |
|
97 |
+ const lists = []; |
|
98 |
+ |
|
99 |
+ // 휴가 구분 (연차, 반차, 공가 등) |
|
100 |
+ const vcatnTopCodes = await Vue.config.globalProperties.$findChildCodes('sanctn_mby_vcatn'); |
|
101 |
+ for (let vcatnTopCode of vcatnTopCodes) { |
|
102 |
+ const vcatnMidCodes = await Vue.config.globalProperties.$findChildCodes(vcatnTopCode.code); |
|
103 |
+ for (let vcatnMidCode of vcatnMidCodes) { |
|
104 |
+ if (parseFloat(vcatnMidCode.codeValue) === 0.5) { |
|
105 |
+ const codes = await Vue.config.globalProperties.$findChildCodes(vcatnMidCode.code); |
|
106 |
+ lists.push(...codes); |
|
107 |
+ } else { |
|
108 |
+ lists.push(vcatnMidCode); |
|
109 |
+ } |
|
110 |
+ } |
|
111 |
+ } |
|
112 |
+ |
|
113 |
+ // 출장 구분 (품의서, 복명서) |
|
114 |
+ const bsrpCodes = await Vue.config.globalProperties.$findChildCodes('bsrp_code'); |
|
115 |
+ lists.push(...bsrpCodes); |
|
116 |
+ |
|
117 |
+ return lists; |
|
118 |
+ }; |
|
119 |
+ |
|
93 | 120 |
// 기본 코드 목록 초기화 |
94 | 121 |
Vue.config.globalProperties.$defaultCodes = async () => { |
95 | 122 |
const codeGroups = { |
--- client/views/pages/Manager/approval/approvalList.vue
... | ... | @@ -1,226 +0,0 @@ |
1 | -<template> | |
2 | - <div class="col-lg-12"> | |
3 | - <div class="card"> | |
4 | - <div class="card-body"> | |
5 | - <h2 class="card-title">승인 대기 목록</h2> | |
6 | - <div class="sch-form-wrap title-wrap"> | |
7 | - <h3><img :src="h3icon" alt="">승인 대기</h3> | |
8 | - <div class="input-group"> | |
9 | - <select class="form-select" v-model="waitRequest.year" @change="fnChangeCurrentPage(1, 'wait')"> | |
10 | - <option value="">연도 전체</option> | |
11 | - <option v-for="year in years" :key="year" :value="year">{{ year }}년</option> | |
12 | - </select> | |
13 | - <select class="form-select" v-model="waitRequest.month" @change="fnChangeCurrentPage(1, 'wait')"> | |
14 | - <option value="">월 전체</option> | |
15 | - <option v-for="month in months" :key="month" :value="month">{{ month }}월</option> | |
16 | - </select> | |
17 | - <div class="sch-input"> | |
18 | - <input type="text" class="form-control" placeholder="신청자명"> | |
19 | - <button class="ico-sch"> | |
20 | - <SearchOutlined /> | |
21 | - </button> | |
22 | - </div> | |
23 | - </div> | |
24 | - </div> | |
25 | - <div class="tbl-wrap"> | |
26 | - <table id="myTable" class="tbl data"> | |
27 | - <colgroup> | |
28 | - <col style="width: 13.33%;"> | |
29 | - <col style="width: 13.33%;"> | |
30 | - <col style="width: 13.33%;"> | |
31 | - <col style="width: 30%;"> | |
32 | - <col style="width: 30%;"> | |
33 | - </colgroup> | |
34 | - <thead> | |
35 | - <tr> | |
36 | - <th>구분</th> | |
37 | - <th>결재구분</th> | |
38 | - <th>신청자</th> | |
39 | - <th>기간</th> | |
40 | - <th>신청일</th> | |
41 | - </tr> | |
42 | - </thead> | |
43 | - <tbody> | |
44 | - <tr v-for="(item, idx) in waitList" :key="idx"> | |
45 | - <td>{{ item.sanctnIemNm }}</td> | |
46 | - <td>{{ item.sanctnSeNm }}</td> | |
47 | - <td>{{ item.applcntNm }}</td> | |
48 | - <td>{{ $formattedDates(item) }}</td> | |
49 | - <td>{{ item.rgsde }}</td> | |
50 | - </tr> | |
51 | - </tbody> | |
52 | - </table> | |
53 | - </div> | |
54 | - <Pagenation :search="waitRequest" @onChange="(currentPage) => fnChangeCurrentPage(currentPage, 'wait')" /> | |
55 | - <div class="sch-form-wrap title-wrap"> | |
56 | - <h3><img :src="h3icon" alt="">승인 이력</h3> | |
57 | - <div class="input-group"> | |
58 | - <select class="form-select" v-model="confmRequest.year" @change="fnChangeCurrentPage(1, 'wait')"> | |
59 | - <option value="">연도 전체</option> | |
60 | - <option v-for="year in years" :key="year" :value="year">{{ year }}년</option> | |
61 | - </select> | |
62 | - <select class="form-select" v-model="confmRequest.month" @change="fnChangeCurrentPage(1, 'wait')"> | |
63 | - <option value="">월 전체</option> | |
64 | - <option v-for="month in months" :key="month" :value="month">{{ month }}월</option> | |
65 | - </select> | |
66 | - <select name="" id="" class="form-select"> | |
67 | - <option value="all">상태</option> | |
68 | - <option value="">승인</option> | |
69 | - <option value="">반려</option> | |
70 | - </select> | |
71 | - <div class="sch-input"> | |
72 | - <input type="text" class="form-control" placeholder="신청자명"> | |
73 | - <button class="ico-sch"> | |
74 | - <SearchOutlined /> | |
75 | - </button> | |
76 | - </div> | |
77 | - </div> | |
78 | - </div> | |
79 | - <div class="tbl-wrap"> | |
80 | - <table id="myTable" class="tbl data"> | |
81 | - <colgroup> | |
82 | - <col style="width: 13.33%;"> | |
83 | - <col style="width: 13.33%;"> | |
84 | - <col style="width: 13.33%;"> | |
85 | - <col style="width: 30%;"> | |
86 | - <col style="width: 16.66%;"> | |
87 | - <col style="width: 13.33%;"> | |
88 | - </colgroup> | |
89 | - <thead> | |
90 | - <tr> | |
91 | - <th>구분</th> | |
92 | - <th>결재구분</th> | |
93 | - <th>신청자</th> | |
94 | - <th>기간</th> | |
95 | - <th>신청일</th> | |
96 | - <th>상태</th> | |
97 | - </tr> | |
98 | - </thead> | |
99 | - <tbody> | |
100 | - <tr v-for="(item, index) in confmList" :key="index" :class="{ 'expired': true }"> | |
101 | - <td>{{ item.sanctnIemNm }}</td> | |
102 | - <td>{{ item.sanctnSeNm }}</td> | |
103 | - <td>{{ item.applcntNm }}</td> | |
104 | - <td>{{ $formattedDates(item) }}</td> | |
105 | - <td>{{ item.rgsde }}</td> | |
106 | - <td>{{ item.confmAtNm }}</td> | |
107 | - </tr> | |
108 | - </tbody> | |
109 | - </table> | |
110 | - </div> | |
111 | - <Pagenation :search="confmRequest" @onChange="(currentPage) => fnChangeCurrentPage(currentPage, 'wait')" /> | |
112 | - </div> | |
113 | - </div> | |
114 | - </div> | |
115 | -</template> | |
116 | -<script> | |
117 | -import { SearchOutlined } from '@ant-design/icons-vue'; | |
118 | -import Pagenation from '../../../component/Pagenation.vue'; | |
119 | -// API | |
120 | -import { findSanctnsProc } from '../../../../resources/api/sanctns'; | |
121 | - | |
122 | -export default { | |
123 | - components: { | |
124 | - SearchOutlined, | |
125 | - Pagenation, | |
126 | - }, | |
127 | - | |
128 | - data() { | |
129 | - return { | |
130 | - photoicon: "/client/resources/img/photo_icon.png", | |
131 | - h3icon: "/client/resources/img/h3icon.png", | |
132 | - | |
133 | - years: [2023, 2024, 2025], // 연도 목록 | |
134 | - months: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], // 월 목록 | |
135 | - | |
136 | - // 목록 조회용 | |
137 | - waitList: [], | |
138 | - waitRequest: { | |
139 | - year: '', | |
140 | - month: '', | |
141 | - sanctnDe: null, // 결재일 | |
142 | - sanctnIem: null, // 결재항목 : 출장 구분 or 휴가 종류 (휴가/출장 구분 코드) | |
143 | - sanctnMbyId: null, // 결재 주체 아이디 : 휴가/출장 아이디 | |
144 | - confmAts: "W", // 승인여부 | |
145 | - confmerId: this.$store.state.userInfo.userId, | |
146 | - }, | |
147 | - confmList: [], | |
148 | - confmRequest: { | |
149 | - year: '', | |
150 | - month: '', | |
151 | - sanctnDe: null, // 결재일 | |
152 | - sanctnIem: null, // 결재항목 : 출장 구분 or 휴가 종류 (휴가/출장 구분 코드) | |
153 | - sanctnMbyId: null, // 결재 주체 아이디 : 휴가/출장 아이디 | |
154 | - confmAts: "A,R", // 승인여부 | |
155 | - confmerId: this.$store.state.userInfo.userId, | |
156 | - }, | |
157 | - | |
158 | - // 공통코드 목록 | |
159 | - sanctnCodes: [], // 결재 종류 코드 목록 | |
160 | - sanctnMbyCodes: [], // 결재 항목 코드 목록 | |
161 | - sanctnConfmCodes: [], // 결재 상태 코드 목록 | |
162 | - }; | |
163 | - }, | |
164 | - | |
165 | - computed: {}, | |
166 | - | |
167 | - created() { }, | |
168 | - | |
169 | - mounted() { | |
170 | - this.findList('wait'); // 목록 조회 | |
171 | - this.findList('confm'); // 목록 조회 | |
172 | - }, | |
173 | - | |
174 | - methods: { | |
175 | - // 목록 조회 | |
176 | - async findList(type) { | |
177 | - const vm = this; | |
178 | - | |
179 | - try { | |
180 | - let request = {}; | |
181 | - if (type === 'wait') { | |
182 | - request = vm.waitRequest; | |
183 | - } else if (type == 'confm') { | |
184 | - request = vm.confmRequest; | |
185 | - } | |
186 | - | |
187 | - const response = await findSanctnsProc(request); | |
188 | - const result = response.data.data; | |
189 | - | |
190 | - if (type === 'wait') { | |
191 | - vm.waitList = result.lists; | |
192 | - vm.waitRequest = result.search; | |
193 | - } else if (type == 'confm') { | |
194 | - vm.confmList = result.lists; | |
195 | - vm.confmRequest = result.search; | |
196 | - } | |
197 | - } catch (error) { | |
198 | - if (error.response) { | |
199 | - alert(error.response.data.message); | |
200 | - } else { | |
201 | - alert("에러가 발생했습니다."); | |
202 | - } | |
203 | - console.error(error.message); | |
204 | - }; | |
205 | - }, | |
206 | - | |
207 | - // 페이지 이동 | |
208 | - fnChangeCurrentPage(currentPage, type) { | |
209 | - if (type === 'wait') { | |
210 | - this.waitRequest.currentPage = Number(currentPage); | |
211 | - } else if (type === 'confm') { | |
212 | - this.confmRequest.currentPage = Number(currentPage); | |
213 | - } | |
214 | - | |
215 | - this.$nextTick(() => { | |
216 | - this.findList(type); | |
217 | - }); | |
218 | - }, | |
219 | - }, | |
220 | -}; | |
221 | -</script> | |
222 | -<style scoped> | |
223 | -tr { | |
224 | - cursor: pointer; | |
225 | -} | |
226 | -</style>(파일 끝에 줄바꿈 문자 없음) |
--- client/views/pages/Manager/approval/approvalRequest.vue
... | ... | @@ -1,160 +0,0 @@ |
1 | -<template> | |
2 | - <div class="col-lg-12"> | |
3 | - <div class="card"> | |
4 | - <div class="card-body"> | |
5 | - <h2 class="card-title">결재 요청</h2> | |
6 | - <div class="sch-form-wrap"> | |
7 | - <div class="input-group"> | |
8 | - <select class="form-select" v-model="request.year" @change="fnChangeCurrentPage(1)"> | |
9 | - <option value="">연도 전체</option> | |
10 | - <option v-for="year in years" :key="year" :value="year">{{ year }}년</option> | |
11 | - </select> | |
12 | - <select class="form-select" v-model="request.month" @change="fnChangeCurrentPage(1)"> | |
13 | - <option value="">월 전체</option> | |
14 | - <option v-for="month in months" :key="month" :value="month">{{ month }}월</option> | |
15 | - </select> | |
16 | - <select name="" id="" class="form-select"> | |
17 | - <option value="" selected>전체</option> | |
18 | - <option value="">출장-복명</option> | |
19 | - <option value="">출장-품의</option> | |
20 | - <option value="">연차</option> | |
21 | - <option value="">반차-오전</option> | |
22 | - <option value="">반차-오후</option> | |
23 | - <option value="">대체휴가</option> | |
24 | - <option value="">공가</option> | |
25 | - <option value="">병가</option> | |
26 | - </select> | |
27 | - </div> | |
28 | - </div> | |
29 | - <div class="tbl-wrap"> | |
30 | - <table id="myTable" class="tbl data"> | |
31 | - <colgroup> | |
32 | - <col style="width: 13.33%;"> | |
33 | - <col style="width: 13.33%;"> | |
34 | - <col style="width: 13.33%;"> | |
35 | - <col style="width: 30%;"> | |
36 | - <col style="width: 16.66%;"> | |
37 | - <col style="width: 13.33%;"> | |
38 | - </colgroup> | |
39 | - <thead> | |
40 | - <tr> | |
41 | - <th>구분</th> | |
42 | - <th>결재구분</th> | |
43 | - <th>신청자</th> | |
44 | - <th>기간</th> | |
45 | - <th>신청일</th> | |
46 | - <th>상태</th> | |
47 | - </tr> | |
48 | - </thead> | |
49 | - <tbody> | |
50 | - <tr v-for="(item, idx) of lists" :key="idx"> | |
51 | - <td>{{ item.sanctnIemNm }}</td> | |
52 | - <td>{{ item.sanctnSeNm }}</td> | |
53 | - <td>{{ item.applcntNm }}</td> | |
54 | - <td>{{ $formattedDates(item) }}</td> | |
55 | - <td>{{ item.rgsde }}</td> | |
56 | - <td>{{ item.confmAtNm }}</td> | |
57 | - </tr> | |
58 | - </tbody> | |
59 | - </table> | |
60 | - </div> | |
61 | - <Pagenation :search="request" @onChange="fnChangeCurrentPage" /> | |
62 | - <div class="buttons"> | |
63 | - <button type="button" class="btn sm sm primary" @click="showOptions = true"> 등록 </button> | |
64 | - </div> | |
65 | - </div> | |
66 | - </div> | |
67 | - </div> | |
68 | - <div v-if="showOptions" class="popup-overlay"> | |
69 | - <div class="popup-content"> | |
70 | - <div class="card"> | |
71 | - <div class="card-body"> | |
72 | - <h2 class="card-title">신청종류선택</h2> | |
73 | - <div class="buttons"> | |
74 | - <button class="btn sm hyuga" @click="goToPage('휴가')">휴가신청</button> | |
75 | - <button class="btn sm chuljang" @click="goToPage('출장')">출장신청</button> | |
76 | - </div> | |
77 | - </div> | |
78 | - </div> | |
79 | - <button class="close-btn" @click="showOptions = false"> | |
80 | - <CloseCircleFilled /> | |
81 | - </button> | |
82 | - </div> | |
83 | - </div> | |
84 | -</template> | |
85 | -<script> | |
86 | -import { SearchOutlined, CloseCircleFilled } from '@ant-design/icons-vue'; | |
87 | -import Pagenation from '../../../component/Pagenation.vue'; | |
88 | -// API | |
89 | -import { findSanctnsProc } from '../../../../resources/api/sanctns'; | |
90 | - | |
91 | -export default { | |
92 | - components: { | |
93 | - SearchOutlined, CloseCircleFilled, | |
94 | - Pagenation, | |
95 | - }, | |
96 | - | |
97 | - data() { | |
98 | - return { | |
99 | - photoicon: "/client/resources/img/photo_icon.png", | |
100 | - showOptions: false, | |
101 | - | |
102 | - years: [], | |
103 | - months: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], | |
104 | - | |
105 | - lists: [], | |
106 | - request: { | |
107 | - year: '', | |
108 | - month: '', | |
109 | - register: this.$store.state.userInfo.userId, | |
110 | - }, | |
111 | - }; | |
112 | - }, | |
113 | - | |
114 | - created() { | |
115 | - this.generateYears(); | |
116 | - }, | |
117 | - | |
118 | - mounted() { | |
119 | - this.findList(); // 목록 조회 | |
120 | - }, | |
121 | - | |
122 | - methods: { | |
123 | - generateYears() { | |
124 | - const startYear = 2020; | |
125 | - const currentYear = new Date().getFullYear(); | |
126 | - | |
127 | - for (let year = currentYear; year >= startYear; year--) { | |
128 | - this.years.push(year); | |
129 | - } | |
130 | - }, | |
131 | - | |
132 | - // 목록 조회 | |
133 | - async findList() { | |
134 | - const vm = this; | |
135 | - try { | |
136 | - const response = await findSanctnsProc(vm.request); | |
137 | - const result = response.data.data; | |
138 | - | |
139 | - this.lists = result.lists; | |
140 | - this.request = result.search; | |
141 | - } catch (error) { | |
142 | - if (error.response) { | |
143 | - alert(error.response.data.message); | |
144 | - } else { | |
145 | - alert("에러가 발생했습니다."); | |
146 | - } | |
147 | - console.error(error.message); | |
148 | - }; | |
149 | - }, | |
150 | - | |
151 | - // 페이지 이동 | |
152 | - fnChangeCurrentPage(currentPage) { | |
153 | - this.request.currentPage = Number(currentPage); | |
154 | - this.$nextTick(() => { | |
155 | - this.findList(); | |
156 | - }); | |
157 | - }, | |
158 | - }, | |
159 | -}; | |
160 | -</script>(파일 끝에 줄바꿈 문자 없음) |
--- client/views/pages/Manager/approval/ChuljangBokmyeong.vue
+++ client/views/pages/Manager/sanctn/ChuljangBokmyeong.vue
No changes |
--- client/views/pages/Manager/approval/ChuljangPumui.vue
+++ client/views/pages/Manager/sanctn/ChuljangPumui.vue
No changes |
--- client/views/pages/Manager/approval/Hyuga.vue
+++ client/views/pages/Manager/sanctn/Hyuga.vue
No changes |
+++ client/views/pages/Manager/sanctn/MyApprovalRequestList.vue
... | ... | @@ -0,0 +1,184 @@ |
1 | +<template> | |
2 | + <div class="col-lg-12"> | |
3 | + <div class="card"> | |
4 | + <div class="card-body"> | |
5 | + <h2 class="card-title">결재 요청</h2> | |
6 | + <div class="sch-form-wrap"> | |
7 | + <div class="input-group"> | |
8 | + <select name="yearPicker" id="yearPicker" class="form-select" v-model="searchParams.year" @change="handlePageChange(1)"> | |
9 | + <option value="">연도 전체</option> | |
10 | + <option v-for="year in yearOptions" :key="year" :value="year">{{ year }}년</option> | |
11 | + </select> | |
12 | + <select name="monthPicker" id="monthPicker" class="form-select" v-model="searchParams.month" @change="handlePageChange(1)"> | |
13 | + <option value="">월 전체</option> | |
14 | + <option v-for="month in monthOptions" :key="month" :value="month">{{ month }}월</option> | |
15 | + </select> | |
16 | + <select name="sanctnIemPicker" id="sanctnIemPicker" class="form-select" v-model="searchParams.sanctnIem" @change="handlePageChange(1)"> | |
17 | + <option value="">전체</option> | |
18 | + <option v-for="(item, idx) of approvalTypeOptions" :key="idx" :value="item.code">{{ item.codeNm }}</option> | |
19 | + </select> | |
20 | + </div> | |
21 | + </div> | |
22 | + <div class="tbl-wrap"> | |
23 | + <table class="tbl data"> | |
24 | + <colgroup> | |
25 | + <col style="width: 13.33%;"> | |
26 | + <col style="width: 13.33%;"> | |
27 | + <col style="width: 13.33%;"> | |
28 | + <col style="width: 30%;"> | |
29 | + <col style="width: 16.66%;"> | |
30 | + <col style="width: 13.33%;"> | |
31 | + </colgroup> | |
32 | + <thead> | |
33 | + <tr> | |
34 | + <th>구분</th> | |
35 | + <th>결재구분</th> | |
36 | + <th>신청자</th> | |
37 | + <th>기간</th> | |
38 | + <th>신청일</th> | |
39 | + <th>상태</th> | |
40 | + </tr> | |
41 | + </thead> | |
42 | + <tbody> | |
43 | + <template v-if="approvalRequestList.length > 0"> | |
44 | + <tr v-for="(item, idx) of approvalRequestList" :key="idx" @click="handleDetailNavigation(item.sanctnMbyId)"> | |
45 | + <td>{{ item.sanctnIemNm }}</td> | |
46 | + <td>{{ item.sanctnSeNm }}</td> | |
47 | + <td>{{ item.registerNm }}</td> | |
48 | + <td>{{ $formattedDates(item) }}</td> | |
49 | + <td>{{ item.rgsde }}</td> | |
50 | + <td>{{ item.confmAtNm }}</td> | |
51 | + </tr> | |
52 | + </template> | |
53 | + <tr v-else> | |
54 | + <td colspan="6">게시물이 존재하지 않습니다.</td> | |
55 | + </tr> | |
56 | + </tbody> | |
57 | + </table> | |
58 | + </div> | |
59 | + <Pagination :search="searchParams" @onChange="handlePageChange" /> | |
60 | + <div class="buttons"> | |
61 | + <button type="button" class="btn sm sm primary" @click="isOptionsModalVisible = true">등록</button> | |
62 | + </div> | |
63 | + </div> | |
64 | + </div> | |
65 | + </div> | |
66 | + <div v-if="isOptionsModalVisible" class="popup-overlay"> | |
67 | + <div class="popup-content"> | |
68 | + <div class="card"> | |
69 | + <div class="card-body"> | |
70 | + <h2 class="card-title">신청종류선택</h2> | |
71 | + <div class="buttons"> | |
72 | + <button class="btn sm hyuga" @click="handleRegistrationNavigation('휴가')">휴가신청</button> | |
73 | + <button class="btn sm chuljang" @click="handleRegistrationNavigation('출장')">출장신청</button> | |
74 | + </div> | |
75 | + </div> | |
76 | + </div> | |
77 | + <button class="close-btn" @click="isOptionsModalVisible = false"> | |
78 | + <CloseCircleFilled /> | |
79 | + </button> | |
80 | + </div> | |
81 | + </div> | |
82 | +</template> | |
83 | +<script> | |
84 | +import { SearchOutlined, CloseCircleFilled } from '@ant-design/icons-vue'; | |
85 | +import Pagination from '../../../component/Pagination.vue'; | |
86 | +// API | |
87 | +import { findMyApprovalRequestsProc } from '../../../../resources/api/sanctns'; | |
88 | + | |
89 | +export default { | |
90 | + components: { | |
91 | + SearchOutlined, CloseCircleFilled, | |
92 | + Pagination, | |
93 | + }, | |
94 | + | |
95 | + data() { | |
96 | + return { | |
97 | + photoicon: "/client/resources/img/photo_icon.png", | |
98 | + isOptionsModalVisible: false, | |
99 | + | |
100 | + yearOptions: [], | |
101 | + monthOptions: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], | |
102 | + | |
103 | + approvalTypeOptions: [], | |
104 | + | |
105 | + approvalRequestList: [], | |
106 | + searchParams: { | |
107 | + year: '', | |
108 | + month: '', | |
109 | + sanctnIem: '', | |
110 | + currentUserId: this.$store.state.userInfo.userId, // 변경불가 (고정값) | |
111 | + }, | |
112 | + }; | |
113 | + }, | |
114 | + | |
115 | + async created() { | |
116 | + this.generateYearOptions(); | |
117 | + this.approvalTypeOptions = await this.$sanctnIemCodes(); // 휴가 및 출장 구분 코드 조회 | |
118 | + }, | |
119 | + | |
120 | + mounted() { | |
121 | + this.fetchApprovalRequestList(); // 목록 조회 | |
122 | + }, | |
123 | + | |
124 | + methods: { | |
125 | + generateYearOptions() { | |
126 | + const startYear = 2020; | |
127 | + const currentYear = new Date().getFullYear(); | |
128 | + | |
129 | + for (let year = currentYear; year >= startYear; year--) { | |
130 | + this.yearOptions.push(year); | |
131 | + } | |
132 | + }, | |
133 | + | |
134 | + // 목록 조회 | |
135 | + async fetchApprovalRequestList() { | |
136 | + const vm = this; | |
137 | + try { | |
138 | + const response = await findMyApprovalRequestsProc(vm.searchParams); | |
139 | + const result = response.data.data; | |
140 | + | |
141 | + this.approvalRequestList = result.lists; | |
142 | + this.searchParams = result.search; | |
143 | + } catch (error) { | |
144 | + this.handleError(error); | |
145 | + }; | |
146 | + }, | |
147 | + | |
148 | + // 페이지 변경 | |
149 | + handlePageChange(currentPage) { | |
150 | + this.searchParams.currentPage = Number(currentPage); | |
151 | + this.$nextTick(() => { | |
152 | + this.fetchApprovalRequestList(); | |
153 | + }); | |
154 | + }, | |
155 | + | |
156 | + // 상세 페이지 이동 | |
157 | + handleDetailNavigation(id) { | |
158 | + const approvalType = id.split('_')[0]; | |
159 | + | |
160 | + if (approvalType === "VCATN") { | |
161 | + this.$router.push({ name: 'HyugaDetail', query: { id } }); | |
162 | + } if (approvalType === "BSRP") { | |
163 | + this.$router.push({ name: 'ChuljangDetailAll', query: { id } }); | |
164 | + } | |
165 | + }, | |
166 | + | |
167 | + // 등록 페이지 이동 | |
168 | + handleRegistrationNavigation(type) { | |
169 | + if (type === "휴가") { | |
170 | + this.$router.push({ name: 'hyugaInsert' }); | |
171 | + } else if (type === "출장") { | |
172 | + this.$router.push({ name: 'ChuljangInsert' }); | |
173 | + } | |
174 | + }, | |
175 | + | |
176 | + // 에러 처리 | |
177 | + handleError(error) { | |
178 | + const message = error.response?.data?.message || "에러가 발생했습니다."; | |
179 | + alert(message); | |
180 | + console.error(error.message); | |
181 | + }, | |
182 | + }, | |
183 | +}; | |
184 | +</script>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/pages/Manager/sanctn/PendingApprovalList.vue
... | ... | @@ -0,0 +1,249 @@ |
1 | +<template> | |
2 | + <div class="col-lg-12"> | |
3 | + <div class="card"> | |
4 | + <div class="card-body"> | |
5 | + <h2 class="card-title">승인 대기 목록</h2> | |
6 | + <div class="sch-form-wrap title-wrap"> | |
7 | + <h3><img :src="sectionIcon" alt="">승인 대기</h3> | |
8 | + <div class="input-group"> | |
9 | + <select class="form-select" v-model="pendingSearchParams.year" @change="handlePageChange(1, 'pending')"> | |
10 | + <option value="">연도 전체</option> | |
11 | + <option v-for="year in yearOptions" :key="year" :value="year">{{ year }}년</option> | |
12 | + </select> | |
13 | + <select class="form-select" v-model="pendingSearchParams.month" @change="handlePageChange(1, 'pending')"> | |
14 | + <option value="">월 전체</option> | |
15 | + <option v-for="month in monthOptions" :key="month" :value="month">{{ month }}월</option> | |
16 | + </select> | |
17 | + <div class="sch-input"> | |
18 | + <input type="text" class="form-control" v-model="pendingSearchParams.searchText" placeholder="검색어를 입력하세요." @keyup.enter="handleSearch('pending')"> | |
19 | + <button button="button" class="ico-sch" @click="handleSearch('pending')"> | |
20 | + <SearchOutlined /> | |
21 | + </button> | |
22 | + </div> | |
23 | + </div> | |
24 | + </div> | |
25 | + <div class="tbl-wrap"> | |
26 | + <table id="myTable" class="tbl data"> | |
27 | + <colgroup> | |
28 | + <col style="width: 13.33%;"> | |
29 | + <col style="width: 13.33%;"> | |
30 | + <col style="width: 13.33%;"> | |
31 | + <col style="width: 30%;"> | |
32 | + <col style="width: 30%;"> | |
33 | + </colgroup> | |
34 | + <thead> | |
35 | + <tr> | |
36 | + <th>구분</th> | |
37 | + <th>결재구분</th> | |
38 | + <th>신청자</th> | |
39 | + <th>기간</th> | |
40 | + <th>신청일</th> | |
41 | + </tr> | |
42 | + </thead> | |
43 | + <tbody> | |
44 | + <tr v-for="(item, idx) in pendingApprovalList" :key="idx" @click="handleDetailNavigation(item.sanctnMbyId)"> | |
45 | + <td>{{ item.sanctnIemNm }}</td> | |
46 | + <td>{{ item.sanctnSeNm }}</td> | |
47 | + <td>{{ item.registerNm }}</td> | |
48 | + <td>{{ $formattedDates(item) }}</td> | |
49 | + <td>{{ item.rgsde }}</td> | |
50 | + </tr> | |
51 | + </tbody> | |
52 | + </table> | |
53 | + </div> | |
54 | + <Pagenation :search="pendingSearchParams" @onChange="(currentPage) => handlePageChange(currentPage, 'pending')" /> | |
55 | + <div class="sch-form-wrap title-wrap"> | |
56 | + <h3><img :src="sectionIcon" alt="">승인 이력</h3> | |
57 | + <div class="input-group"> | |
58 | + <select class="form-select" v-model="historySearchParams.year" @change="handlePageChange(1, 'history')"> | |
59 | + <option value="">연도 전체</option> | |
60 | + <option v-for="year in yearOptions" :key="year" :value="year">{{ year }}년</option> | |
61 | + </select> | |
62 | + <select class="form-select" v-model="historySearchParams.month" @change="handlePageChange(1, 'history')"> | |
63 | + <option value="">월 전체</option> | |
64 | + <option v-for="month in monthOptions" :key="month" :value="month">{{ month }}월</option> | |
65 | + </select> | |
66 | + <select name="" id="" class="form-select"> | |
67 | + <option value="all">상태</option> | |
68 | + <option value="">승인</option> | |
69 | + <option value="">반려</option> | |
70 | + </select> | |
71 | + <div class="sch-input"> | |
72 | + <input type="text" class="form-control" v-model="historySearchParams.searchText" placeholder="검색어를 입력하세요." @keyup.enter="handleSearch('history')"> | |
73 | + <button button="button" class="ico-sch" @click="handleSearch('history')"> | |
74 | + <SearchOutlined /> | |
75 | + </button> | |
76 | + </div> | |
77 | + </div> | |
78 | + </div> | |
79 | + <div class="tbl-wrap"> | |
80 | + <table id="myTable" class="tbl data"> | |
81 | + <colgroup> | |
82 | + <col style="width: 13.33%;"> | |
83 | + <col style="width: 13.33%;"> | |
84 | + <col style="width: 13.33%;"> | |
85 | + <col style="width: 30%;"> | |
86 | + <col style="width: 16.66%;"> | |
87 | + <col style="width: 13.33%;"> | |
88 | + </colgroup> | |
89 | + <thead> | |
90 | + <tr> | |
91 | + <th>구분</th> | |
92 | + <th>결재구분</th> | |
93 | + <th>신청자</th> | |
94 | + <th>기간</th> | |
95 | + <th>신청일</th> | |
96 | + <th>상태</th> | |
97 | + </tr> | |
98 | + </thead> | |
99 | + <tbody> | |
100 | + <tr v-for="(item, index) in approvalHistoryList" :key="index" class="expired" @click="handleDetailNavigation(item.sanctnMbyId)"> | |
101 | + <td>{{ item.sanctnIemNm }}</td> | |
102 | + <td>{{ item.sanctnSeNm }}</td> | |
103 | + <td>{{ item.registerNm }}</td> | |
104 | + <td>{{ $formattedDates(item) }}</td> | |
105 | + <td>{{ item.rgsde }}</td> | |
106 | + <td>{{ item.confmAtNm }}</td> | |
107 | + </tr> | |
108 | + </tbody> | |
109 | + </table> | |
110 | + </div> | |
111 | + <Pagenation :search="historySearchParams" @onChange="(currentPage) => handlePageChange(currentPage, 'history')" /> | |
112 | + </div> | |
113 | + </div> | |
114 | + </div> | |
115 | +</template> | |
116 | +<script> | |
117 | +import { SearchOutlined } from '@ant-design/icons-vue'; | |
118 | +import Pagenation from '../../../component/Pagination.vue'; | |
119 | +// API | |
120 | +import { findPendingApprovalsProc } from '../../../../resources/api/sanctns'; | |
121 | + | |
122 | +export default { | |
123 | + components: { | |
124 | + SearchOutlined, | |
125 | + Pagenation, | |
126 | + }, | |
127 | + | |
128 | + data() { | |
129 | + return { | |
130 | + sectionIcon: "/client/resources/img/h3icon.png", | |
131 | + | |
132 | + yearOptions: [2023, 2024, 2025], // 연도 목록 | |
133 | + monthOptions: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], // 월 목록 | |
134 | + | |
135 | + // 승인 대기 목록 | |
136 | + pendingApprovalList: [], | |
137 | + pendingSearchParams: { | |
138 | + year: '', | |
139 | + month: '', | |
140 | + isConfmAt: true, // 변경불가 (고정값) | |
141 | + searchText: '', | |
142 | + currentUserId: this.$store.state.userInfo.userId, // 변경불가 (고정값) | |
143 | + }, | |
144 | + // 승인 이력 목록 | |
145 | + approvalHistoryList: [], | |
146 | + historySearchParams: { | |
147 | + year: '', | |
148 | + month: '', | |
149 | + isConfmAt: false, // 변경불가 (고정값) | |
150 | + searchText: '', | |
151 | + currentUserId: this.$store.state.userInfo.userId, // 변경불가 (고정값) | |
152 | + }, | |
153 | + }; | |
154 | + }, | |
155 | + | |
156 | + created() { | |
157 | + this.generateYearOptions(); | |
158 | + }, | |
159 | + | |
160 | + mounted() { | |
161 | + this.fetchApprovalList('pending'); // 승인 대기 목록 | |
162 | + this.fetchApprovalList('history'); // 승인 이력 목록 | |
163 | + }, | |
164 | + | |
165 | + methods: { | |
166 | + generateYearOptions() { | |
167 | + const startYear = 2020; | |
168 | + const currentYear = new Date().getFullYear(); | |
169 | + | |
170 | + for (let year = currentYear; year >= startYear; year--) { | |
171 | + this.yearOptions.push(year); | |
172 | + } | |
173 | + }, | |
174 | + | |
175 | + // 목록 조회 | |
176 | + async fetchApprovalList(listType) { | |
177 | + const vm = this; | |
178 | + | |
179 | + try { | |
180 | + let searchParams = {}; | |
181 | + if (listType === 'pending') { | |
182 | + searchParams = vm.pendingSearchParams; | |
183 | + } else if (listType === 'history') { | |
184 | + searchParams = vm.historySearchParams; | |
185 | + } | |
186 | + | |
187 | + const response = await findPendingApprovalsProc(searchParams); | |
188 | + const result = response.data.data; | |
189 | + | |
190 | + if (listType === 'pending') { | |
191 | + vm.pendingApprovalList = result.lists; | |
192 | + vm.pendingSearchParams = result.search; | |
193 | + } else if (listType === 'history') { | |
194 | + vm.approvalHistoryList = result.lists; | |
195 | + vm.historySearchParams = result.search; | |
196 | + } | |
197 | + } catch (error) { | |
198 | + this.handleError(error); | |
199 | + }; | |
200 | + }, | |
201 | + | |
202 | + // 검색 실행 | |
203 | + handleSearch(listType) { | |
204 | + if (listType === 'pending') { | |
205 | + this.pendingSearchParams.currentPage = 1; | |
206 | + } else if (listType === 'history') { | |
207 | + this.historySearchParams.currentPage = 1; | |
208 | + } | |
209 | + this.fetchApprovalList(listType); | |
210 | + }, | |
211 | + | |
212 | + // 페이지 변경 | |
213 | + handlePageChange(currentPage, listType) { | |
214 | + if (listType === 'pending') { | |
215 | + this.pendingSearchParams.currentPage = Number(currentPage); | |
216 | + } else if (listType === 'history') { | |
217 | + this.historySearchParams.currentPage = Number(currentPage); | |
218 | + } | |
219 | + | |
220 | + this.$nextTick(() => { | |
221 | + this.fetchApprovalList(listType); | |
222 | + }); | |
223 | + }, | |
224 | + | |
225 | + // 상세 페이지 이동 | |
226 | + handleDetailNavigation(id) { | |
227 | + const approvalType = id.split('_')[0]; | |
228 | + | |
229 | + if (approvalType === "VCATN") { | |
230 | + this.$router.push({ name: 'HyugaDetail', query: { id, type: 'sanctns' } }); | |
231 | + } if (approvalType === "BSRP") { | |
232 | + this.$router.push({ name: 'ChuljangDetailAll', query: { id, type: 'sanctns' } }); | |
233 | + } | |
234 | + }, | |
235 | + | |
236 | + // 에러 처리 | |
237 | + handleError(error) { | |
238 | + const message = error.response?.data?.message || "에러가 발생했습니다."; | |
239 | + alert(message); | |
240 | + console.error(error.message); | |
241 | + }, | |
242 | + }, | |
243 | +}; | |
244 | +</script> | |
245 | +<style scoped> | |
246 | +tr { | |
247 | + cursor: pointer; | |
248 | +} | |
249 | +</style>(파일 끝에 줄바꿈 문자 없음) |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?