
+++ client/resources/api/asset.js
... | ... | @@ -0,0 +1,41 @@ |
1 | +import apiClient from "./index"; | |
2 | + | |
3 | +// 차량 정보 등록 | |
4 | +export const saveAsSetVhcle = insertAsSetVhcleDTO => { | |
5 | + return apiClient.post(`/assetVhcle/saveAsSetVhcle.json`, insertAsSetVhcleDTO ); | |
6 | +} | |
7 | + | |
8 | +// 차량 정보 조회 | |
9 | +export const findAllAsSetVhcle = searchReqDTO => { | |
10 | + return apiClient.get(`/assetVhcle/findAllAsSetVhcle.json`, { params: searchReqDTO }); | |
11 | +} | |
12 | + | |
13 | +// 차량 정보 변경 | |
14 | +export const updateAsSetVhcle = (vhcleId, updateAsSetVhcleDTO) => { | |
15 | + return apiClient.put(`/assetVhcle/${vhcleId}/updateAsSetVhcle.json`, updateAsSetVhcleDTO); | |
16 | +} | |
17 | + | |
18 | +// 차량 정보 사용 현황 조회 | |
19 | +export const findAllAsSetVhcleHis = searchReqDTO => { | |
20 | + return apiClient.get(`/assetVhcle/findAllAsSetVhcleHis.json`, { params: searchReqDTO }); | |
21 | +} | |
22 | + | |
23 | +// 카드 정보 등록 | |
24 | +export const saveAsSetCard = insertAsSetCardDTO => { | |
25 | + return apiClient.post(`/assetCard/saveAsSetCard.json`, insertAsSetCardDTO ); | |
26 | +} | |
27 | + | |
28 | +// 카드 정보 조회 | |
29 | +export const findAllAsSetCard = searchReqHisDTO => { | |
30 | + return apiClient.get(`/assetCard/findAllAsSetCard.json`, { params: searchReqHisDTO }); | |
31 | +} | |
32 | + | |
33 | +// 카드 정보 변경 | |
34 | +export const updateAsSetCard = (cardId, updateAsSetCardDTO) => { | |
35 | + return apiClient.put(`/assetCard/${cardId}/updateAsSetCard.json`, updateAsSetCardDTO); | |
36 | +} | |
37 | + | |
38 | +// 카드 정보 사용 현황 조회 | |
39 | +export const findAllAsSetCardHis = searchReqDTO => { | |
40 | + return apiClient.get(`/assetCard/findAllAsSetCardHis.json`, { params: searchReqDTO }); | |
41 | +}(파일 끝에 줄바꿈 문자 없음) |
--- client/views/pages/AppRouter.js
+++ client/views/pages/AppRouter.js
... | ... | @@ -55,9 +55,9 @@ |
55 | 55 |
import MeetingCostList from '../pages/Manager/financial/MeetingCostList.vue'; |
56 | 56 |
//자산관리 |
57 | 57 |
import asset from '../pages/Manager/asset/asset.vue'; |
58 |
-import CarList from '../pages/Manager/asset/CarList.vue'; |
|
58 |
+import VhcleList from '../pages/Manager/asset/VhcleList.vue'; |
|
59 | 59 |
import CardList from '../pages/Manager/asset/CardList.vue'; |
60 |
-import CarInfoManagement from '../pages/Manager/asset/CarInfoManagement.vue'; |
|
60 |
+import VhcleInfoManagement from '../pages/Manager/asset/VhcleInfoManagement.vue'; |
|
61 | 61 |
import CardInfoManagement from '../pages/Manager/asset/CardInfoManagement.vue'; |
62 | 62 |
//인사관리 |
63 | 63 |
import hr from '../pages/Manager/hr/hr.vue'; |
... | ... | @@ -149,10 +149,10 @@ |
149 | 149 |
{ path: 'MeetingCostList.page', name: 'MeetingCostList', component: MeetingCostList }, |
150 | 150 |
] |
151 | 151 |
}, //재무관리 |
152 |
- { path: '/asset-management.page', name: 'asset', component: asset, redirect: '/asset-management.page/CarList.page', |
|
152 |
+ { path: '/asset-management.page', name: 'asset', component: asset, redirect: '/asset-management.page/VhcleList.page', |
|
153 | 153 |
children: [ |
154 |
- { path: 'CarList.page', name: 'CarList', component: CarList }, |
|
155 |
- { path: 'CarInfoManagement.page', name: 'CarInfoManagement', component: CarInfoManagement }, |
|
154 |
+ { path: 'VhcleList.page', name: 'VhcleList', component: VhcleList }, |
|
155 |
+ { path: 'VhcleInfoManagement.page', name: 'VhcleInfoManagement', component: VhcleInfoManagement }, |
|
156 | 156 |
{ path: 'CardList.page', name: 'CardList', component: CardList }, |
157 | 157 |
{ path: 'CardInfoManagement.page', name: 'CardInfoManagement', component: CardInfoManagement }, |
158 | 158 |
|
--- client/views/pages/Manager/asset/CarInfoManagement.vue
... | ... | @@ -1,275 +0,0 @@ |
1 | -<template> | |
2 | - <div class="card "> | |
3 | - <div class="card-body "> | |
4 | - <h2 class="card-title">차량정보 관리</h2> | |
5 | - <div class="flex align-top"> | |
6 | - <div class="sch-form-wrap search"> | |
7 | - <div class="input-group" style="display: flex;"> | |
8 | - <select name="" id="" class="form-select"> | |
9 | - <option value="">전체</option> | |
10 | - <option value="">차량명</option> | |
11 | - <option value="">차량번호</option> | |
12 | - </select> | |
13 | - <div class="sch-input"> | |
14 | - <input type="text" class="form-control"> | |
15 | - <button class="ico-sch"> | |
16 | - <SearchOutlined /> | |
17 | - </button> | |
18 | - </div> | |
19 | - </div> | |
20 | - <div class="tbl-wrap table-scroll"> | |
21 | - <table id="myTable" class="tbl data"> | |
22 | - <!-- 동적으로 <th> 생성 --> | |
23 | - <thead> | |
24 | - <tr> | |
25 | - <th>차량목록 </th> | |
26 | - </tr> | |
27 | - </thead> | |
28 | - <!-- 동적으로 <td> 생성 --> | |
29 | - <tbody> | |
30 | - <tr v-for="(item, index) in listData" :key="index"> | |
31 | - <td></td> | |
32 | - </tr> | |
33 | - </tbody> | |
34 | - </table> | |
35 | - | |
36 | - </div> | |
37 | - </div> | |
38 | - | |
39 | - <div style="width: 100%;"> | |
40 | - <div class=" sch-form-wrap title-wrap"> | |
41 | - <h3><img :src="h3icon" alt="">차량 정보</h3> | |
42 | - <div class="buttons" style="margin: 0;"> | |
43 | - <button type="submit" class="btn sm tertiary">신규</button> | |
44 | - <button type="reset" class="btn sm secondary">등록</button> | |
45 | - <button type="delete" class="btn sm btn-red">삭제</button> | |
46 | - </div> | |
47 | - </div> | |
48 | - <form class="row g-3 pt-3 needs-validation " @submit.prevent="handleSubmit" | |
49 | - style="margin-bottom: 3rem;"> | |
50 | - <div class="col-12"> | |
51 | - <div class="col-12 border-x"> | |
52 | - <label for="where" class="form-label"><p>차종<p class="require"><img :src="require" alt=""></p></p></label> | |
53 | - <input type="text" class="form-control" id="where" v-model="where" /> | |
54 | - </div> | |
55 | - <div class="col-12 border-x"> | |
56 | - <label for="where" class="form-label"><p>차량번호<p class="require"><img :src="require" alt=""></p></p></label> | |
57 | - <input type="text" class="form-control" id="where" v-model="where" /> | |
58 | - </div> | |
59 | - | |
60 | - </div> | |
61 | - <div class="col-12"> | |
62 | - <div class="col-12 border-x"> | |
63 | - <label for="where" class="form-label">연료종류</label> | |
64 | - <input type="text" class="form-control" id="where" v-model="where" /> | |
65 | - </div> | |
66 | - <div class="col-12 border-x"> | |
67 | - <label for="where" class="form-label"><p>소유형태<p class="require"><img :src="require" alt=""></p></p></label> | |
68 | - <input type="text" class="form-control" id="where" v-model="where" /> | |
69 | - </div> | |
70 | - | |
71 | - </div> | |
72 | - <div class="col-12 "> | |
73 | - <label for="prvonsh" class="form-label"><p>차량명<p class="require"><img :src="require" alt=""></p></p></label> | |
74 | - <input type="text" class="form-control textarea" id="reason" v-model="reason" /> | |
75 | - </div> | |
76 | - <div class="col-12 "> | |
77 | - <label for="prvonsh" class="form-label"><p>담당자<p class="require"><img :src="require" alt=""></p></p></label> | |
78 | - <input type="text" class="form-control textarea" id="reason" v-model="selectedname" readonly/> | |
79 | - <input type="button" class="form-control " value="검색" @click="showPopup = true" /> | |
80 | - <HrPopup v-if="showPopup" @close="showPopup = false" @select="addApproval"/> | |
81 | - </div> | |
82 | - <div class="col-12 chuljang "> | |
83 | - <label for="prvonsh" class="form-label">비고</label> | |
84 | - <input type="text" class="form-control textarea" id="reason" v-model="reason" /> | |
85 | - </div> | |
86 | - <div class="col-12 border-x input-radio"> | |
87 | - <label for="prvonsh" class="form-label"> <p>상태 | |
88 | - <p class="require"><img :src="require" alt=""></p> | |
89 | - </p></label> | |
90 | - <select class="form-select" > | |
91 | - <option value="선택">선택</option> | |
92 | - <option value=""></option> | |
93 | - <option value=""></option> | |
94 | - </select> | |
95 | - </div> | |
96 | - | |
97 | - | |
98 | - </form> | |
99 | - <div class=" sch-form-wrap title-wrap"> | |
100 | - <h3><img :src="h3icon" alt="">예약현황</h3> | |
101 | - </div> | |
102 | - <div class="tbl-wrap chk-area"> | |
103 | - <table id="myTable" class="tbl data"> | |
104 | - | |
105 | - <thead> | |
106 | - <tr> | |
107 | - <th>차종</th> | |
108 | - <th>운행자</th> | |
109 | - <th>기간</th> | |
110 | - <th>상태</th> | |
111 | - </tr> | |
112 | - </thead> | |
113 | - <!-- 동적으로 <td> 생성 --> | |
114 | - <tbody> | |
115 | - <tr v-for="(item, index) in listData" :key="index"> | |
116 | - <td>{{ item.type }}</td> | |
117 | - <td>{{ item.driver }}</td> | |
118 | - <td>{{ item.startDate }} ~ {{ item.endDate }}</td> | |
119 | - <td :class="getStatusClass(item.status)"> | |
120 | - {{ item.status }} | |
121 | - </td> | |
122 | - </tr> | |
123 | - </tbody> | |
124 | - </table> | |
125 | - | |
126 | - </div> | |
127 | - <div class="pagination"> | |
128 | - <ul> | |
129 | - <!-- 왼쪽 화살표 (이전 페이지) --> | |
130 | - <li class="arrow" :class="{ disabled: currentPage === 1 }" @click="changePage(currentPage - 1)"> | |
131 | - < | |
132 | - </li> | |
133 | - | |
134 | - <!-- 페이지 번호 --> | |
135 | - <li v-for="page in totalPages" :key="page" :class="{ active: currentPage === page }" | |
136 | - @click="changePage(page)"> | |
137 | - {{ page }} | |
138 | - </li> | |
139 | - | |
140 | - <!-- 오른쪽 화살표 (다음 페이지) --> | |
141 | - <li class="arrow" :class="{ disabled: currentPage === totalPages }" @click="changePage(currentPage + 1)"> | |
142 | - > | |
143 | - </li> | |
144 | - </ul> | |
145 | - </div> | |
146 | - </div> | |
147 | - </div> | |
148 | - </div> | |
149 | - | |
150 | - </div> | |
151 | - | |
152 | -</template> | |
153 | - | |
154 | -<script> | |
155 | -import GoogleCalendar from "../../../component/GoogleCalendar.vue" | |
156 | -import { SearchOutlined } from '@ant-design/icons-vue'; | |
157 | -import HrPopup from "../../../component/Popup/HrPopup.vue"; | |
158 | -export default { | |
159 | - data() { | |
160 | - return { | |
161 | - showPopup: false, | |
162 | - selectedname: '', | |
163 | - approvals: [], | |
164 | - require: "/client/resources/img/require.png", | |
165 | - h3icon: "/client/resources/img/h3icon.png", | |
166 | - photoicon: "/client/resources/img/photo_icon.png", | |
167 | - img1: "/client/resources/img/img.png", | |
168 | - icon1: "/client/resources/img/icon.png", | |
169 | - dateicon: "/client/resources/img/date.png", | |
170 | - startbtn: "/client/resources/img/start.png", | |
171 | - stopbtn: "/client/resources/img/stop.png", | |
172 | - moreicon: "/client/resources/img/more.png", | |
173 | - today: new Date().toLocaleDateString('ko-KR', { | |
174 | - year: 'numeric', | |
175 | - month: '2-digit', | |
176 | - day: '2-digit', | |
177 | - weekday: 'short', | |
178 | - }), | |
179 | - time: this.getCurrentTime(), | |
180 | - listData: [ | |
181 | - { | |
182 | - type: 'SUV', | |
183 | - driver: '김철수', | |
184 | - startDate: '2025-05-01', | |
185 | - endDate: '2025-05-03', | |
186 | - status: '예약', // 사용중 | |
187 | - }, | |
188 | - { | |
189 | - type: '세단', | |
190 | - driver: '이영희', | |
191 | - startDate: '2025-05-05', | |
192 | - endDate: '2025-05-06', | |
193 | - status: '예약', // 예약됨 | |
194 | - }, | |
195 | - { | |
196 | - type: '트럭', | |
197 | - driver: '박민호', | |
198 | - startDate: '2025-04-25', | |
199 | - endDate: '2025-04-28', | |
200 | - status: '반납', // 반납됨 | |
201 | - }, | |
202 | - ], | |
203 | - } | |
204 | - }, | |
205 | - components: { | |
206 | - SearchOutlined, HrPopup | |
207 | - }, | |
208 | - methods: { | |
209 | - addApproval(selectedUser) { | |
210 | - this.approvals.push({ | |
211 | - name: selectedUser.name | |
212 | - }); | |
213 | - | |
214 | - this.selectedname = selectedUser.name; // 입력창에 표시 | |
215 | - this.showPopup = false; | |
216 | - }, | |
217 | - formatBudget(amount) { | |
218 | - return new Intl.NumberFormat().format(amount) + ' 원'; | |
219 | - }, | |
220 | - isPastPeriod(period) { | |
221 | - // 예: '2025-05-01 ~ 2025-05-03' → 종료일 추출 | |
222 | - const endDateStr = period.split('~')[1]?.trim(); | |
223 | - if (!endDateStr) return false; | |
224 | - | |
225 | - const endDate = new Date(endDateStr); | |
226 | - const today = new Date(); | |
227 | - | |
228 | - // 현재 날짜보다 과거면 true | |
229 | - return endDate < today; | |
230 | - }, | |
231 | - getStatusClass(status) { | |
232 | - return status === 'active' ? 'status-active' : 'status-inactive'; | |
233 | - }, | |
234 | - getStatusClass(status) { | |
235 | - if (status === '예약') return 'status-pending'; | |
236 | - if (status === '반납') return 'status-approved'; | |
237 | - | |
238 | - // Default empty string | |
239 | - return ''; | |
240 | - }, | |
241 | - getCurrentTime() { | |
242 | - const now = new Date(); | |
243 | - const hours = String(now.getHours()).padStart(2, '0'); | |
244 | - const minutes = String(now.getMinutes()).padStart(2, '0'); | |
245 | - const seconds = String(now.getSeconds()).padStart(2, '0'); | |
246 | - return `${hours}:${minutes}:${seconds}`; | |
247 | - }, | |
248 | - getCategoryClass(category) { | |
249 | - switch (category) { | |
250 | - case '용역': return 'category-service'; | |
251 | - case '내부': return 'category-internal'; | |
252 | - case '국가과제': return 'category-government'; | |
253 | - default: return ''; | |
254 | - } | |
255 | - }, | |
256 | - }, | |
257 | - watch: { | |
258 | - | |
259 | - }, | |
260 | - computed: { | |
261 | - | |
262 | - }, | |
263 | - mounted() { | |
264 | - console.log('main mounted'); | |
265 | - setInterval(() => { | |
266 | - this.time = this.getCurrentTime(); | |
267 | - }, 1000); | |
268 | - } | |
269 | -} | |
270 | -</script> | |
271 | -<style scoped> | |
272 | -tr { | |
273 | - cursor: pointer; | |
274 | -} | |
275 | -</style>(파일 끝에 줄바꿈 문자 없음) |
--- client/views/pages/Manager/asset/CarList.vue
... | ... | @@ -1,223 +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 name="" id="" class="form-select"> | |
10 | - <option value="">전체</option> | |
11 | - <option value="">차량명</option> | |
12 | - <option value="">차량번호</option> | |
13 | - <option value="">운행자</option> | |
14 | - </select> | |
15 | - <div class="sch-input"> | |
16 | - <input type="text" class="form-control"> | |
17 | - <button class="ico-sch"><SearchOutlined /></button> | |
18 | - </div> | |
19 | - </div> | |
20 | - </div> | |
21 | - | |
22 | - <!-- Table --> | |
23 | - <div class="tbl-wrap"> | |
24 | - <table id="myTable" class="tbl data"> | |
25 | - <!-- 동적으로 <th> 생성 --> | |
26 | - <thead> | |
27 | - <tr> | |
28 | - <th>차종 </th> | |
29 | - <th>차량번호</th> | |
30 | - <th>차량명</th> | |
31 | - <th>부서</th> | |
32 | - <th>운행자</th> | |
33 | - <th>기간</th> | |
34 | - </tr> | |
35 | - </thead> | |
36 | - <!-- 동적으로 <td> 생성 --> | |
37 | - <tbody> | |
38 | - <tr v-for="(item, index) in listData" :key="index"> | |
39 | - <td>{{ item.type }}</td> | |
40 | - <td>{{ item.number }}</td> | |
41 | - <td>{{ item.name }}</td> | |
42 | - <td>{{ item.department }}</td> | |
43 | - <td>{{ item.driver }}</td> | |
44 | - <td>{{ item.startDate }} ~ {{ item.endDate }}</td> | |
45 | - </tr> | |
46 | - </tbody> | |
47 | - </table> | |
48 | - | |
49 | - </div> | |
50 | - <div class="pagination"> | |
51 | - <ul> | |
52 | - <!-- 왼쪽 화살표 (이전 페이지) --> | |
53 | - <li class="arrow" :class="{ disabled: currentPage === 1 }" @click="changePage(currentPage - 1)"> | |
54 | - < | |
55 | - </li> | |
56 | - | |
57 | - <!-- 페이지 번호 --> | |
58 | - <li v-for="page in totalPages" :key="page" :class="{ active: currentPage === page }" | |
59 | - @click="changePage(page)"> | |
60 | - {{ page }} | |
61 | - </li> | |
62 | - | |
63 | - <!-- 오른쪽 화살표 (다음 페이지) --> | |
64 | - <li class="arrow" :class="{ disabled: currentPage === totalPages }" @click="changePage(currentPage + 1)"> | |
65 | - > | |
66 | - </li> | |
67 | - </ul> | |
68 | - </div> | |
69 | - <div class="sch-form-wrap title-wrap"> | |
70 | - <h3><img :src="h3icon" alt="">사용이력</h3> | |
71 | - <div class="input-group"> | |
72 | - <select name="" id="" class="form-select"> | |
73 | - <option value="">전체</option> | |
74 | - <option value="">차량명</option> | |
75 | - <option value="">차량번호</option> | |
76 | - <option value="">운행자</option> | |
77 | - </select> | |
78 | - <div class="sch-input"> | |
79 | - <input type="text" class="form-control"> | |
80 | - <button class="ico-sch"><SearchOutlined /></button> | |
81 | - </div> | |
82 | - </div> | |
83 | - </div> | |
84 | - | |
85 | - <!-- Table --> | |
86 | - <div class="tbl-wrap"> | |
87 | - <table id="myTable" class="tbl data"> | |
88 | - <!-- 동적으로 <th> 생성 --> | |
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 | - <!-- 동적으로 <td> 생성 --> | |
100 | - <tbody> | |
101 | - <tr v-for="(item, index) in listData" :key="index"> | |
102 | - <td>{{ item.type }}</td> | |
103 | - <td>{{ item.number }}</td> | |
104 | - <td>{{ item.name }}</td> | |
105 | - <td>{{ item.department }}</td> | |
106 | - <td>{{ item.driver }}</td> | |
107 | - <td>{{ item.startDate }} ~ {{ item.endDate }}</td> | |
108 | - </tr> | |
109 | - </tbody> | |
110 | - </table> | |
111 | - | |
112 | - </div> | |
113 | - <div class="pagination"> | |
114 | - <ul> | |
115 | - <!-- 왼쪽 화살표 (이전 페이지) --> | |
116 | - <li class="arrow" :class="{ disabled: currentPage === 1 }" @click="changePage(currentPage - 1)"> | |
117 | - < | |
118 | - </li> | |
119 | - | |
120 | - <!-- 페이지 번호 --> | |
121 | - <li v-for="page in totalPages" :key="page" :class="{ active: currentPage === page }" | |
122 | - @click="changePage(page)"> | |
123 | - {{ page }} | |
124 | - </li> | |
125 | - | |
126 | - <!-- 오른쪽 화살표 (다음 페이지) --> | |
127 | - <li class="arrow" :class="{ disabled: currentPage === totalPages }" @click="changePage(currentPage + 1)"> | |
128 | - > | |
129 | - </li> | |
130 | - </ul> | |
131 | - </div> | |
132 | - </div> | |
133 | - </div> | |
134 | - </div> | |
135 | -</template> | |
136 | - | |
137 | -<script> | |
138 | -import { ref } from 'vue'; | |
139 | -import { SearchOutlined } from '@ant-design/icons-vue'; | |
140 | -export default { | |
141 | - data() { | |
142 | - return { | |
143 | - currentPage: 1, | |
144 | - totalPages: 3, | |
145 | - photoicon: "/client/resources/img/photo_icon.png", | |
146 | - h3icon: "/client/resources/img/h3icon.png", | |
147 | - // 데이터 초기화 | |
148 | - years: [2023, 2024, 2025], // 연도 목록 | |
149 | - months: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], // 월 목록 | |
150 | - selectedYear: '', | |
151 | - selectedMonth: '', | |
152 | - listData: [ | |
153 | - { | |
154 | - type: 'SUV', | |
155 | - number: '12가3456', | |
156 | - name: '쏘렌토', | |
157 | - department: '총무부', | |
158 | - driver: '김철수', | |
159 | - startDate: '2025-05-01', | |
160 | - endDate: '2025-05-10', | |
161 | - }, | |
162 | - { | |
163 | - type: '세단', | |
164 | - number: '34나7890', | |
165 | - name: '그랜저', | |
166 | - department: '영업부', | |
167 | - driver: '이영희', | |
168 | - startDate: '2025-05-03', | |
169 | - endDate: '2025-05-04', | |
170 | - },], | |
171 | - filteredData: [], | |
172 | - }; | |
173 | - }, | |
174 | - computed: { | |
175 | - }, | |
176 | - components:{ | |
177 | - SearchOutlined | |
178 | - }, | |
179 | - methods: { | |
180 | - changePage(page) { | |
181 | - if (page < 1 || page > this.totalPages) return; | |
182 | - this.currentPage = page; | |
183 | - this.$emit('page-changed', page); // 필요 시 부모에 알림 | |
184 | - }, | |
185 | - async onClickSubmit() { | |
186 | - // `useMutation` 훅을 사용하여 mutation 함수 가져오기 | |
187 | - const { mutate, onDone, onError } = useMutation(mygql); | |
188 | - | |
189 | - try { | |
190 | - const result = await mutate(); | |
191 | - console.log(result); | |
192 | - } catch (error) { | |
193 | - console.error('Mutation error:', error); | |
194 | - } | |
195 | - }, | |
196 | - registerLeave() { | |
197 | - console.log("등록 버튼 클릭됨"); | |
198 | - | |
199 | - // Vue의 반응성 문제를 피하기 위해, 새로운 객체를 추가합니다. | |
200 | - this.DeptData = [ | |
201 | - ...this.DeptData, | |
202 | - { member: '', deptNM: '', acceptTerms: false } | |
203 | - ]; | |
204 | - | |
205 | - console.log(this.DeptData); // 배열 상태 출력 | |
206 | - }, | |
207 | - getStatusClass(status) { | |
208 | - if (status === '승인') return 'status-approved'; | |
209 | - if (status === '대기') return 'status-pending'; | |
210 | - return ''; | |
211 | - }, | |
212 | - | |
213 | - }, | |
214 | - created() { | |
215 | - }, | |
216 | - mounted() { | |
217 | - | |
218 | - | |
219 | - }, | |
220 | -}; | |
221 | -</script> | |
222 | - | |
223 | -<style scoped></style>(파일 끝에 줄바꿈 문자 없음) |
--- client/views/pages/Manager/asset/CardInfoManagement.vue
+++ client/views/pages/Manager/asset/CardInfoManagement.vue
... | ... | @@ -5,12 +5,14 @@ |
5 | 5 |
<div class="flex align-top"> |
6 | 6 |
<div class="sch-form-wrap search"> |
7 | 7 |
<div class="input-group" style="display: flex;"> |
8 |
- <select name="" id="" class="form-select"> |
|
9 |
- <option value="">전체</option> |
|
8 |
+ <select v-model="searchReqDTO.searchType" id="searchType" class="form-select"> |
|
9 |
+ <option value="all">전체</option> |
|
10 |
+ <option value="cm">카드명</option> |
|
11 |
+ <option value="nm">신청자</option> |
|
10 | 12 |
</select> |
11 | 13 |
<div class="sch-input"> |
12 |
- <input type="text" class="form-control"> |
|
13 |
- <button class="ico-sch"> |
|
14 |
+ <input type="text" class="form-control" v-model="searchReqDTO.searchText" @keyup.enter="searchCards"> |
|
15 |
+ <button class="ico-sch" @click="searchCards"> |
|
14 | 16 |
<SearchOutlined /> |
15 | 17 |
</button> |
16 | 18 |
</div> |
... | ... | @@ -25,12 +27,16 @@ |
25 | 27 |
</thead> |
26 | 28 |
<!-- 동적으로 <td> 생성 --> |
27 | 29 |
<tbody> |
28 |
- <tr v-for="(item, index) in listData" :key="index"> |
|
29 |
- <td></td> |
|
30 |
+ <tr v-if="selectedCards.length === 0"> |
|
31 |
+ <td style="text-align: center;">등록된 카드정보가 없습니다.</td> |
|
32 |
+ </tr> |
|
33 |
+ <tr v-for="(item, index) in selectedCards" :key="index" @click="selectCard(item)"> |
|
34 |
+ <td> |
|
35 |
+ {{ item.cardNm }} |
|
36 |
+ </td> |
|
30 | 37 |
</tr> |
31 | 38 |
</tbody> |
32 | 39 |
</table> |
33 |
- |
|
34 | 40 |
</div> |
35 | 41 |
</div> |
36 | 42 |
|
... | ... | @@ -38,52 +44,49 @@ |
38 | 44 |
<div class=" sch-form-wrap title-wrap"> |
39 | 45 |
<h3><img :src="h3icon" alt="">카드 정보</h3> |
40 | 46 |
<div class="buttons" style="margin: 0;"> |
41 |
- <button type="submit" class="btn sm tertiary">신규</button> |
|
42 |
- <button type="reset" class="btn sm secondary">등록</button> |
|
43 |
- <button type="delete" class="btn sm btn-red">삭제</button> |
|
47 |
+ <button type="submit" class="btn sm tertiary" @click="resetCard">신규</button> |
|
48 |
+ <button type="reset" class="btn sm secondary" @click="insertByUpdateCard"> |
|
49 |
+ {{ buttonText }} |
|
50 |
+ </button> |
|
51 |
+ <button type="delete" class="btn sm btn-red" @click="deleteByUseAtCard">삭제</button> |
|
44 | 52 |
</div> |
45 | 53 |
</div> |
46 | 54 |
<form class="row g-3 pt-3 needs-validation " @submit.prevent="handleSubmit" style="margin-bottom: 3rem;"> |
47 | 55 |
|
48 | 56 |
<div class="col-12 "> |
49 |
- <label for="prvonsh" class="form-label"> |
|
57 |
+ <label for="cardNm" class="form-label"> |
|
50 | 58 |
<p>카드명 |
51 | 59 |
<p class="require"><img :src="require" alt=""></p> |
52 | 60 |
</p> |
53 | 61 |
</label> |
54 |
- <input type="text" class="form-control " id="reason" v-model="reason" /> |
|
62 |
+ <input type="text" class="form-control " id="reason" v-model="selectedCard.cardNm" /> |
|
55 | 63 |
</div> |
56 | 64 |
<div class="col-12 "> |
57 |
- <label for="prvonsh" class="form-label"> |
|
65 |
+ <label for="chargerId" class="form-label"> |
|
58 | 66 |
<p>담당자 |
59 | 67 |
<p class="require"><img :src="require" alt=""></p> |
60 | 68 |
</p> |
61 | 69 |
</label> |
62 |
- <input type="text" class="form-control " id="reason" v-model="selectedname" readonly /> |
|
70 |
+ <input type="text" class="form-control " id="reason" v-model="selectedCard.chargerId" readonly /> |
|
63 | 71 |
<input type="button" class="form-control " value="검색" @click="showPopup = true" /> |
64 | 72 |
<!-- 팝업 --> |
65 |
- <HrPopup v-if="showPopup" @close="showPopup = false" @select="addApproval"/> |
|
73 |
+ <HrPopup v-if="showPopup" @close="showPopup = false" @select="addApproval" /> |
|
66 | 74 |
</div> |
67 | 75 |
<div class="col-12 chuljang "> |
68 | 76 |
<label for="prvonsh" class="form-label">비고</label> |
69 |
- <input type="text" class="form-control textarea" id="reason" v-model="reason" /> |
|
77 |
+ <input type="text" class="form-control textarea" id="reason" v-model="selectedCard.rm" /> |
|
70 | 78 |
</div> |
71 | 79 |
<div class="col-12 border-x input-radio"> |
72 | 80 |
<label for="prvonsh" class="form-label"> |
73 |
- <p>사용여부 |
|
81 |
+ <p>상태 |
|
74 | 82 |
<p class="require"><img :src="require" alt=""></p> |
75 | 83 |
</p> |
76 | 84 |
</label> |
77 |
- <div class="chk-area"> |
|
78 |
- <div class="form-check"> |
|
79 |
- <input type="radio" name="rdo_1" id="rdo_1"> |
|
80 |
- <label for="rdo_1">사용</label> |
|
81 |
- </div> |
|
82 |
- <div class="form-check"> |
|
83 |
- <input type="radio" name="rdo_1" id="rdo_2" checked> |
|
84 |
- <label for="rdo_2">미사용</label> |
|
85 |
- </div> |
|
86 |
- </div> |
|
85 |
+ <select class="form-select" id="sttus" v-model="selectedCard.sttus"> |
|
86 |
+ <option value="N">정상</option> |
|
87 |
+ <option value="R">재발급</option> |
|
88 |
+ <option value="D">폐기</option> |
|
89 |
+ </select> |
|
87 | 90 |
</div> |
88 | 91 |
|
89 | 92 |
|
... | ... | @@ -104,10 +107,10 @@ |
104 | 107 |
</thead> |
105 | 108 |
<!-- 동적으로 <td> 생성 --> |
106 | 109 |
<tbody> |
107 |
- <tr v-for="(item, index) in listData" :key="index"> |
|
108 |
- <td>{{ item.buseo }}</td> |
|
109 |
- <td>{{ item.user }}</td> |
|
110 |
- <td>{{ item.startDate }} ~ {{ item.endDate }}</td> |
|
110 |
+ <tr v-for="(item, index) in processedCards" :key="index"> |
|
111 |
+ <td>{{ item.deptId }}</td> |
|
112 |
+ <td>{{ item.applcntId }}</td> |
|
113 |
+ <td>{{ item.formattedPeriod }}</td> |
|
111 | 114 |
<td :class="getStatusClass(item.status)"> |
112 | 115 |
{{ item.status }} |
113 | 116 |
</td> |
... | ... | @@ -116,25 +119,7 @@ |
116 | 119 |
</table> |
117 | 120 |
|
118 | 121 |
</div> |
119 |
- <div class="pagination"> |
|
120 |
- <ul> |
|
121 |
- <!-- 왼쪽 화살표 (이전 페이지) --> |
|
122 |
- <li class="arrow" :class="{ disabled: currentPage === 1 }" @click="changePage(currentPage - 1)"> |
|
123 |
- < |
|
124 |
- </li> |
|
125 |
- |
|
126 |
- <!-- 페이지 번호 --> |
|
127 |
- <li v-for="page in totalPages" :key="page" :class="{ active: currentPage === page }" |
|
128 |
- @click="changePage(page)"> |
|
129 |
- {{ page }} |
|
130 |
- </li> |
|
131 |
- |
|
132 |
- <!-- 오른쪽 화살표 (다음 페이지) --> |
|
133 |
- <li class="arrow" :class="{ disabled: currentPage === totalPages }" @click="changePage(currentPage + 1)"> |
|
134 |
- > |
|
135 |
- </li> |
|
136 |
- </ul> |
|
137 |
- </div> |
|
122 |
+ <Pagenation :search="searchReqHisDTO" @onChange="fnChangeCurrentPage" /> |
|
138 | 123 |
</div> |
139 | 124 |
</div> |
140 | 125 |
</div> |
... | ... | @@ -147,11 +132,13 @@ |
147 | 132 |
import GoogleCalendar from "../../../component/GoogleCalendar.vue" |
148 | 133 |
import HrPopup from "../../../component/Popup/HrPopup.vue"; |
149 | 134 |
import { SearchOutlined, CloseCircleFilled } from '@ant-design/icons-vue'; |
135 |
+import { saveAsSetCard, findAllAsSetCard, updateAsSetCard, findAllAsSetCardHis } from "../../../../resources/api/asset"; //카드 정보 API |
|
136 |
+import Pagenation from "../../../component/Pagenation.vue"; |
|
137 |
+ |
|
150 | 138 |
export default { |
151 | 139 |
data() { |
152 | 140 |
return { |
153 | 141 |
showPopup: false, |
154 |
- selectedname: '', |
|
155 | 142 |
approvals: [], |
156 | 143 |
require: "/client/resources/img/require.png", |
157 | 144 |
h3icon: "/client/resources/img/h3icon.png", |
... | ... | @@ -162,102 +149,246 @@ |
162 | 149 |
startbtn: "/client/resources/img/start.png", |
163 | 150 |
stopbtn: "/client/resources/img/stop.png", |
164 | 151 |
moreicon: "/client/resources/img/more.png", |
165 |
- today: new Date().toLocaleDateString('ko-KR', { |
|
166 |
- year: 'numeric', |
|
167 |
- month: '2-digit', |
|
168 |
- day: '2-digit', |
|
169 |
- weekday: 'short', |
|
170 |
- }), |
|
171 |
- time: this.getCurrentTime(), |
|
172 |
- listData: [ |
|
173 |
- { |
|
174 |
- buseo: '', |
|
175 |
- user: '김철수', |
|
176 |
- startDate: '2025-05-01', |
|
177 |
- endDate: '2025-05-03', |
|
178 |
- status: '예약', // 사용중 |
|
179 |
- }, |
|
180 |
- { |
|
181 |
- buseo: '', |
|
182 |
- user: '이영희', |
|
183 |
- startDate: '2025-05-05', |
|
184 |
- endDate: '2025-05-06', |
|
185 |
- status: '예약', // 예약됨 |
|
186 |
- }, |
|
187 |
- { |
|
188 |
- buseo: '', |
|
189 |
- user: '박민호', |
|
190 |
- startDate: '2025-04-25', |
|
191 |
- endDate: '2025-04-28', |
|
192 |
- status: '반납', // 반납됨 |
|
193 |
- }, |
|
194 |
- ], |
|
195 | 152 |
|
153 |
+ checkCard: false, |
|
154 |
+ cardId: null, |
|
155 |
+ selectedCards: [], |
|
156 |
+ selectedCardsHis: [], |
|
157 |
+ searchReqDTO: { |
|
158 |
+ searchType: "all", |
|
159 |
+ searchText: null, |
|
160 |
+ useAt: null, |
|
161 |
+ useAll: true, |
|
162 |
+ }, |
|
163 |
+ searchReqHisDTO: { |
|
164 |
+ searchType: "all", |
|
165 |
+ searchText: null, |
|
166 |
+ useAt: null, |
|
167 |
+ useAll: false, |
|
168 |
+ listType:"all", |
|
169 |
+ |
|
170 |
+ currentPage: null, |
|
171 |
+ recordSize: 4, |
|
172 |
+ pageSize: null, |
|
173 |
+ totalRecordCount: null, |
|
174 |
+ totalPageCount: null, |
|
175 |
+ startPage: null, |
|
176 |
+ endPage: null, |
|
177 |
+ limitStart: null, |
|
178 |
+ existPrevPage: null, |
|
179 |
+ existNextPage: null, |
|
180 |
+ }, |
|
181 |
+ selectedCard: { |
|
182 |
+ cardNm: null, |
|
183 |
+ chargerId: null, |
|
184 |
+ rm: null, |
|
185 |
+ sttus: "N", |
|
186 |
+ useAt: "Y" |
|
187 |
+ }, |
|
196 | 188 |
} |
197 | 189 |
}, |
198 | 190 |
components: { |
199 |
- SearchOutlined, CloseCircleFilled, HrPopup |
|
191 |
+ SearchOutlined, CloseCircleFilled, HrPopup, Pagenation |
|
200 | 192 |
}, |
201 | 193 |
methods: { |
194 |
+ // 페이지 이동 |
|
195 |
+ fnChangeCurrentPage(currentPage) { |
|
196 |
+ this.searchReqHisDTO.currentPage = Number(currentPage); |
|
197 |
+ console.log(this.searchReqHisDTO); |
|
198 |
+ this.$nextTick(() => { |
|
199 |
+ this.searchCardsHis(); |
|
200 |
+ }); |
|
201 |
+ }, |
|
202 |
+ //카드 정보 전체 조회 |
|
203 |
+ async searchCards() { |
|
204 |
+ try { |
|
205 |
+ const response = await findAllAsSetCard(this.searchReqDTO); |
|
206 |
+ if (response.status === 200) { |
|
207 |
+ this.selectedCards = response.data.data.card; // API 응답에서 카테고리 목록을 가져옴 |
|
208 |
+ } |
|
209 |
+ } catch (error) { |
|
210 |
+ console.error("검색 중 오류 발생:", error); |
|
211 |
+ } |
|
212 |
+ }, |
|
213 |
+ |
|
214 |
+ //카드 정보 사용 현황 전체 조회 |
|
215 |
+ async searchCardsHis() { |
|
216 |
+ try { |
|
217 |
+ const response = await findAllAsSetCardHis(this.searchReqHisDTO); |
|
218 |
+ if (response.status === 200) { |
|
219 |
+ this.selectedCardsHis = response.data.data.cardHis; // API 응답에서 카테고리 목록을 가져옴 |
|
220 |
+ this.searchReqHisDTO = response.data.data.search; |
|
221 |
+ } |
|
222 |
+ } catch (error) { |
|
223 |
+ console.error("검색 중 오류 발생:", error); |
|
224 |
+ } |
|
225 |
+ }, |
|
226 |
+ |
|
227 |
+ // 카드 정보 등록 수정 |
|
228 |
+ async insertByUpdateCard() { |
|
229 |
+ |
|
230 |
+ if (!this.validateCheck()) { |
|
231 |
+ return; // 유효성 검사 실패 시 함수 종료 |
|
232 |
+ } |
|
233 |
+ if (this.cardId == null && this.cardId == "") { |
|
234 |
+ try { |
|
235 |
+ const response = await saveAsSetCard(this.selectedCard); |
|
236 |
+ if (response.status === 200) { // 성공 여부 체크 |
|
237 |
+ alert("등록되었습니다."); |
|
238 |
+ window.location.reload(); |
|
239 |
+ } |
|
240 |
+ } catch (error) { |
|
241 |
+ // HTTP 오류가 발생한 경우 |
|
242 |
+ const errorMessage = error.response?.data?.message || "등록이 실패하였습니다."; |
|
243 |
+ alert(errorMessage); // 오류 메시지 표시 |
|
244 |
+ this.searchCards(); |
|
245 |
+ } |
|
246 |
+ } else { |
|
247 |
+ try { |
|
248 |
+ const response = await updateAsSetCard(this.cardId, this.selectedCard); |
|
249 |
+ if (response.status === 200) { |
|
250 |
+ alert("수정되었습니다."); |
|
251 |
+ window.location.reload(); |
|
252 |
+ } |
|
253 |
+ } catch (error) {// HTTP 오류가 발생한 경우 |
|
254 |
+ const errorMessage = error.response?.data?.message || "수정이 실패하였습니다."; |
|
255 |
+ alert(errorMessage); // 오류 메시지 표시 |
|
256 |
+ this.searchCards(); |
|
257 |
+ } |
|
258 |
+ } |
|
259 |
+ }, |
|
260 |
+ |
|
261 |
+ // 등록 유효성 체크 함수 |
|
262 |
+ validateCheck() { |
|
263 |
+ // 1. 카드명 (cardNm) 필수 체크 |
|
264 |
+ if (!this.selectedCard.cardNm || this.selectedCard.cardNm.trim() === "") { |
|
265 |
+ alert("카드명은 필수 입력 항목입니다."); |
|
266 |
+ return false; |
|
267 |
+ } |
|
268 |
+ |
|
269 |
+ // 2. 담당자 (chargerId) 필수 체크 |
|
270 |
+ if (!this.selectedCard.chargerId || this.selectedCard.chargerId.trim() === "") { |
|
271 |
+ alert("담당자는 필수 입력 항목입니다."); |
|
272 |
+ return false; |
|
273 |
+ } |
|
274 |
+ |
|
275 |
+ return true; // 모든 유효성 검사 통과 |
|
276 |
+ }, |
|
277 |
+ |
|
278 |
+ // 삭제 버튼을 눌렀을 시 삭제 |
|
279 |
+ async deleteByUseAtCard() { |
|
280 |
+ try { |
|
281 |
+ this.selectedCard.useAt = "N"; |
|
282 |
+ const response = await updateAsSetCard(this.cardId, this.selectedCard); |
|
283 |
+ if (response.status === 200) { |
|
284 |
+ alert("삭제되었습니다."); |
|
285 |
+ window.location.reload(); |
|
286 |
+ } |
|
287 |
+ } catch (error) { |
|
288 |
+ alert("삭제가 실패하였습니다."); |
|
289 |
+ } |
|
290 |
+ }, |
|
291 |
+ |
|
292 |
+ //선택한 카드의 정보를 오른쪽에 표시 |
|
293 |
+ selectCard(item) { |
|
294 |
+ this.checkCard = true; |
|
295 |
+ this.cardId = item.cardId; |
|
296 |
+ this.selectedCard = { |
|
297 |
+ cardNm: item.cardNm, |
|
298 |
+ chargerId: item.chargerId, |
|
299 |
+ rm: item.rm, |
|
300 |
+ sttus: item.sttus, |
|
301 |
+ useAt: item.useAt |
|
302 |
+ }; |
|
303 |
+ }, |
|
304 |
+ |
|
305 |
+ // 신규 버튼을 눌렀을 시 |
|
306 |
+ resetCard() { |
|
307 |
+ this.checkCard = false; |
|
308 |
+ this.selectedCard = { |
|
309 |
+ cardNm: null, |
|
310 |
+ chargerId: null, |
|
311 |
+ rm: null, |
|
312 |
+ sttus: "N", |
|
313 |
+ useAt: "Y" |
|
314 |
+ }; |
|
315 |
+ }, |
|
316 |
+ |
|
317 |
+ |
|
318 |
+ |
|
202 | 319 |
addApproval(selectedUser) { |
203 | 320 |
this.approvals.push({ |
204 | 321 |
name: selectedUser.name |
205 | 322 |
}); |
206 | 323 |
|
207 |
- this.selectedname = selectedUser.name; // 입력창에 표시 |
|
324 |
+ this.selectedCard.chargerId = selectedUser.name; // 입력창에 표시 |
|
208 | 325 |
this.showPopup = false; |
209 |
- }, |
|
210 |
- formatBudget(amount) { |
|
211 |
- return new Intl.NumberFormat().format(amount) + ' 원'; |
|
212 |
- }, |
|
213 |
- isPastPeriod(period) { |
|
214 |
- // 예: '2025-05-01 ~ 2025-05-03' → 종료일 추출 |
|
215 |
- const endDateStr = period.split('~')[1]?.trim(); |
|
216 |
- if (!endDateStr) return false; |
|
217 |
- |
|
218 |
- const endDate = new Date(endDateStr); |
|
219 |
- const today = new Date(); |
|
220 |
- |
|
221 |
- // 현재 날짜보다 과거면 true |
|
222 |
- return endDate < today; |
|
223 |
- }, |
|
224 |
- getStatusClass(status) { |
|
225 |
- return status === 'active' ? 'status-active' : 'status-inactive'; |
|
226 | 326 |
}, |
227 | 327 |
getStatusClass(status) { |
228 | 328 |
if (status === '예약') return 'status-pending'; |
329 |
+ if (status === '사용 중') return 'status-pending'; |
|
229 | 330 |
if (status === '반납') return 'status-approved'; |
230 |
- |
|
231 |
- // Default empty string |
|
232 |
- return ''; |
|
233 |
- }, |
|
234 |
- getCurrentTime() { |
|
235 |
- const now = new Date(); |
|
236 |
- const hours = String(now.getHours()).padStart(2, '0'); |
|
237 |
- const minutes = String(now.getMinutes()).padStart(2, '0'); |
|
238 |
- const seconds = String(now.getSeconds()).padStart(2, '0'); |
|
239 |
- return `${hours}:${minutes}:${seconds}`; |
|
240 |
- }, |
|
241 |
- getCategoryClass(category) { |
|
242 |
- switch (category) { |
|
243 |
- case '용역': return 'category-service'; |
|
244 |
- case '내부': return 'category-internal'; |
|
245 |
- case '국가과제': return 'category-government'; |
|
246 |
- default: return ''; |
|
247 |
- } |
|
331 |
+ return ''; // 기본값 |
|
248 | 332 |
}, |
249 | 333 |
}, |
250 | 334 |
watch: { |
251 | 335 |
|
252 | 336 |
}, |
253 | 337 |
computed: { |
338 |
+ buttonText() { |
|
339 |
+ return this.checkCard ? "수정" : "등록"; |
|
340 |
+ }, |
|
341 |
+ // selectedCardsHis 데이터를 가공하여 화면에 표시할 데이터를 만듭니다. |
|
342 |
+ processedCards() { |
|
343 |
+ const now = new Date(); // 현재 시간 |
|
254 | 344 |
|
345 |
+ // 날짜와 시간 문자열을 조합하여 Date 객체를 생성하는 헬퍼 함수 |
|
346 |
+ const createDateTime = (dateStr, hourStr, minStr) => { |
|
347 |
+ // dateStr이 "YYYY-MM-DD HH:MM:SS.ms" 형태라고 가정하고 "YYYY-MM-DD" 부분만 추출 |
|
348 |
+ const datePart = dateStr.split(' ')[0]; |
|
349 |
+ const year = parseInt(datePart.substring(0, 4), 10); |
|
350 |
+ const month = parseInt(datePart.substring(5, 7), 10) - 1; // 월은 0부터 시작 (0=1월) |
|
351 |
+ const day = parseInt(datePart.substring(8, 10), 10); |
|
352 |
+ const hour = parseInt(hourStr, 10); |
|
353 |
+ const minute = parseInt(minStr, 10); |
|
354 |
+ return new Date(year, month, day, hour, minute, 0); |
|
355 |
+ }; |
|
356 |
+ |
|
357 |
+ // Date 객체를 'YYYY-MM-DD HH:MI' 형식으로 포맷하는 헬퍼 함수 |
|
358 |
+ const formatDateTime = (dateObj) => { |
|
359 |
+ const year = dateObj.getFullYear(); |
|
360 |
+ const month = String(dateObj.getMonth() + 1).padStart(2, '0'); |
|
361 |
+ const day = String(dateObj.getDate()).padStart(2, '0'); |
|
362 |
+ const hour = String(dateObj.getHours()).padStart(2, '0'); |
|
363 |
+ const minute = String(dateObj.getMinutes()).padStart(2, '0'); |
|
364 |
+ return `${year}-${month}-${day} ${hour}:${minute}`; |
|
365 |
+ }; |
|
366 |
+ |
|
367 |
+ return this.selectedCardsHis.map(item => { |
|
368 |
+ const startDateTime = createDateTime(item.bgnde, item.beginHour, item.beginMnt); |
|
369 |
+ const endDateTime = createDateTime(item.endde, item.endHour, item.endMnt); |
|
370 |
+ |
|
371 |
+ let status = ''; |
|
372 |
+ if (now >= startDateTime && now <= endDateTime) { |
|
373 |
+ status = '사용 중'; // 현재 시간이 기간 안에 포함 |
|
374 |
+ } else if (now < startDateTime) { |
|
375 |
+ status = '예약'; // 현재 시간이 시작일시보다 이전 |
|
376 |
+ } else { // now > endDateTime |
|
377 |
+ status = '반납'; // 현재 시간이 종료일시보다 이후 |
|
378 |
+ } |
|
379 |
+ |
|
380 |
+ return { |
|
381 |
+ ...item, // 원본 아이템의 모든 속성 유지 |
|
382 |
+ formattedPeriod: `${formatDateTime(startDateTime)} ~ ${formatDateTime(endDateTime)}`, // 포맷된 기간 문자열 |
|
383 |
+ status: status // 계산된 상태 |
|
384 |
+ }; |
|
385 |
+ }); |
|
386 |
+ } |
|
255 | 387 |
}, |
256 | 388 |
mounted() { |
257 | 389 |
console.log('main mounted'); |
258 |
- setInterval(() => { |
|
259 |
- this.time = this.getCurrentTime(); |
|
260 |
- }, 1000); |
|
390 |
+ this.searchCards(); |
|
391 |
+ this.searchCardsHis(); |
|
261 | 392 |
} |
262 | 393 |
} |
263 | 394 |
</script> |
--- client/views/pages/Manager/asset/CardList.vue
+++ client/views/pages/Manager/asset/CardList.vue
... | ... | @@ -6,14 +6,17 @@ |
6 | 6 |
<div class="sch-form-wrap title-wrap"> |
7 | 7 |
<h3><img :src="h3icon" alt="">예약현황</h3> |
8 | 8 |
<div class="input-group"> |
9 |
- <select name="" id="" class="form-select"> |
|
10 |
- <option value="">전체</option> |
|
11 |
- <option value="">카드명</option> |
|
12 |
- <option value="">신청자</option> |
|
9 |
+ <select v-model="searchReqHisResDTO.searchType" id="searchType" class="form-select"> |
|
10 |
+ <option value="all">전체</option> |
|
11 |
+ <option value="cm">카드명</option> |
|
12 |
+ <option value="nm">신청자</option> |
|
13 | 13 |
</select> |
14 | 14 |
<div class="sch-input"> |
15 |
- <input type="text" class="form-control"> |
|
16 |
- <button class="ico-sch"><SearchOutlined /></button> |
|
15 |
+ <input type="text" class="form-control" v-model="searchReqHisResDTO.searchText" |
|
16 |
+ @keyup.enter="searchCardsHisRes"> |
|
17 |
+ <button class="ico-sch" @click="searchCardsHisRes"> |
|
18 |
+ <SearchOutlined /> |
|
19 |
+ </button> |
|
17 | 20 |
</div> |
18 | 21 |
</div> |
19 | 22 |
</div> |
... | ... | @@ -32,46 +35,37 @@ |
32 | 35 |
</thead> |
33 | 36 |
<!-- 동적으로 <td> 생성 --> |
34 | 37 |
<tbody> |
35 |
- <tr v-for="(item, index) in listData" :key="index"> |
|
36 |
- <td>{{ item.name }}</td> |
|
37 |
- <td>{{ item.department }}</td> |
|
38 |
- <td>{{ item.user }}</td> |
|
39 |
- <td>{{ item.startDate }} ~ {{ item.endDate }}</td> |
|
40 |
- </tr> |
|
38 |
+ <tr v-if="!(cardHisResData && cardHisResData.length)"> |
|
39 |
+ <td colspan="4" style="text-align: center;">등록된 예약현황이 없습니다.</td> |
|
40 |
+ </tr> |
|
41 |
+ <tr v-for="(item, index) in cardHisResData" :key="index"> |
|
42 |
+ <td>{{ item.cardNm }}</td> |
|
43 |
+ <td>{{ item.deptId }}</td> |
|
44 |
+ <td>{{ item.applcntId }}</td> |
|
45 |
+ <td> |
|
46 |
+ {{ item.bgnde }} {{ item.beginHour.padStart(2, '0') }}:{{ item.beginMnt.padStart(2, '0') }} ~ |
|
47 |
+ {{ item.endde }} {{ item.endHour.padStart(2, '0') }}:{{ item.endMnt.padStart(2, '0') }} |
|
48 |
+ </td> |
|
49 |
+ </tr> |
|
41 | 50 |
</tbody> |
42 | 51 |
</table> |
43 | 52 |
|
44 | 53 |
</div> |
45 |
- <div class="pagination"> |
|
46 |
- <ul> |
|
47 |
- <!-- 왼쪽 화살표 (이전 페이지) --> |
|
48 |
- <li class="arrow" :class="{ disabled: currentPage === 1 }" @click="changePage(currentPage - 1)"> |
|
49 |
- < |
|
50 |
- </li> |
|
51 |
- |
|
52 |
- <!-- 페이지 번호 --> |
|
53 |
- <li v-for="page in totalPages" :key="page" :class="{ active: currentPage === page }" |
|
54 |
- @click="changePage(page)"> |
|
55 |
- {{ page }} |
|
56 |
- </li> |
|
57 |
- |
|
58 |
- <!-- 오른쪽 화살표 (다음 페이지) --> |
|
59 |
- <li class="arrow" :class="{ disabled: currentPage === totalPages }" @click="changePage(currentPage + 1)"> |
|
60 |
- > |
|
61 |
- </li> |
|
62 |
- </ul> |
|
63 |
- </div> |
|
54 |
+ <Pagenation :search="searchReqHisResDTO" @onChange="fnChangeCurrentPageRes" /> |
|
64 | 55 |
<div class="sch-form-wrap title-wrap"> |
65 | 56 |
<h3><img :src="h3icon" alt="">사용이력</h3> |
66 | 57 |
<div class="input-group"> |
67 |
- <select name="" id="" class="form-select"> |
|
68 |
- <option value="">전체</option> |
|
69 |
- <option value="">카드명</option> |
|
70 |
- <option value="">신청자</option> |
|
58 |
+ <select v-model="searchReqHisRtnDTO.searchType" id="searchType" class="form-select"> |
|
59 |
+ <option value="all">전체</option> |
|
60 |
+ <option value="cm">카드명</option> |
|
61 |
+ <option value="nm">신청자</option> |
|
71 | 62 |
</select> |
72 | 63 |
<div class="sch-input"> |
73 |
- <input type="text" class="form-control"> |
|
74 |
- <button class="ico-sch"><SearchOutlined /></button> |
|
64 |
+ <input type="text" class="form-control" v-model="searchReqHisRtnDTO.searchText" |
|
65 |
+ @keyup.enter="searchCardsHisRtn"> |
|
66 |
+ <button class="ico-sch" @click="searchCardsHisRtn"> |
|
67 |
+ <SearchOutlined /> |
|
68 |
+ </button> |
|
75 | 69 |
</div> |
76 | 70 |
</div> |
77 | 71 |
</div> |
... | ... | @@ -80,7 +74,7 @@ |
80 | 74 |
<div class="tbl-wrap"> |
81 | 75 |
<table id="myTable" class="tbl data"> |
82 | 76 |
<!-- 동적으로 <th> 생성 --> |
83 |
- <thead> |
|
77 |
+ <thead> |
|
84 | 78 |
<tr> |
85 | 79 |
<th>카드명</th> |
86 | 80 |
<th>부서</th> |
... | ... | @@ -90,35 +84,23 @@ |
90 | 84 |
</thead> |
91 | 85 |
<!-- 동적으로 <td> 생성 --> |
92 | 86 |
<tbody> |
93 |
- <tr v-for="(item, index) in listData" :key="index"> |
|
94 |
- <td>{{ item.name }}</td> |
|
95 |
- <td>{{ item.department }}</td> |
|
96 |
- <td>{{ item.user }}</td> |
|
97 |
- <td>{{ item.startDate }} ~ {{ item.endDate }}</td> |
|
98 |
- </tr> |
|
87 |
+ <tr v-if="!(cardHisRtnData && cardHisRtnData.length)"> |
|
88 |
+ <td colspan="4" style="text-align: center;">등록된 사용이력이 없습니다.</td> |
|
89 |
+ </tr> |
|
90 |
+ <tr v-for="(item, index) in cardHisRtnData" :key="index"> |
|
91 |
+ <td>{{ item.cardNm }}</td> |
|
92 |
+ <td>{{ item.deptId }}</td> |
|
93 |
+ <td>{{ item.applcntId }}</td> |
|
94 |
+ <td> |
|
95 |
+ {{ item.bgnde }} {{ item.beginHour.padStart(2, '0') }}:{{ item.beginMnt.padStart(2, '0') }} ~ |
|
96 |
+ {{ item.endde }} {{ item.endHour.padStart(2, '0') }}:{{ item.endMnt.padStart(2, '0') }} |
|
97 |
+ </td> |
|
98 |
+ </tr> |
|
99 | 99 |
</tbody> |
100 | 100 |
</table> |
101 | 101 |
|
102 | 102 |
</div> |
103 |
- <div class="pagination"> |
|
104 |
- <ul> |
|
105 |
- <!-- 왼쪽 화살표 (이전 페이지) --> |
|
106 |
- <li class="arrow" :class="{ disabled: currentPage === 1 }" @click="changePage(currentPage - 1)"> |
|
107 |
- < |
|
108 |
- </li> |
|
109 |
- |
|
110 |
- <!-- 페이지 번호 --> |
|
111 |
- <li v-for="page in totalPages" :key="page" :class="{ active: currentPage === page }" |
|
112 |
- @click="changePage(page)"> |
|
113 |
- {{ page }} |
|
114 |
- </li> |
|
115 |
- |
|
116 |
- <!-- 오른쪽 화살표 (다음 페이지) --> |
|
117 |
- <li class="arrow" :class="{ disabled: currentPage === totalPages }" @click="changePage(currentPage + 1)"> |
|
118 |
- > |
|
119 |
- </li> |
|
120 |
- </ul> |
|
121 |
- </div> |
|
103 |
+ <Pagenation :search="searchReqHisRtnDTO" @onChange="fnChangeCurrentPageRtn" /> |
|
122 | 104 |
</div> |
123 | 105 |
</div> |
124 | 106 |
</div> |
... | ... | @@ -127,81 +109,104 @@ |
127 | 109 |
<script> |
128 | 110 |
import { ref } from 'vue'; |
129 | 111 |
import { SearchOutlined } from '@ant-design/icons-vue'; |
112 |
+import Pagenation from "../../../component/Pagenation.vue"; |
|
113 |
+import { findAllAsSetCardHis } from "../../../../resources/api/asset"; //카드 정보 API |
|
130 | 114 |
export default { |
131 | 115 |
data() { |
132 | 116 |
return { |
133 |
- currentPage: 1, |
|
134 |
- totalPages: 3, |
|
135 | 117 |
photoicon: "/client/resources/img/photo_icon.png", |
136 | 118 |
h3icon: "/client/resources/img/h3icon.png", |
137 |
- // 데이터 초기화 |
|
138 |
- years: [2023, 2024, 2025], // 연도 목록 |
|
139 |
- months: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], // 월 목록 |
|
140 |
- selectedYear: '', |
|
141 |
- selectedMonth: '', |
|
142 |
- listData: [ |
|
143 |
- { |
|
144 |
- name: '쏘렌토', |
|
145 |
- department: '총무부', |
|
146 |
- user: '김철수', |
|
147 |
- startDate: '2025-05-01', |
|
148 |
- endDate: '2025-05-10', |
|
149 |
- }, |
|
150 |
- { |
|
151 |
- name: '그랜저', |
|
152 |
- department: '영업부', |
|
153 |
- user: '이영희', |
|
154 |
- startDate: '2025-05-03', |
|
155 |
- endDate: '2025-05-04', |
|
156 |
- },], |
|
157 |
- filteredData: [], |
|
119 |
+ |
|
120 |
+ searchReqHisResDTO: { |
|
121 |
+ searchType: "all", |
|
122 |
+ searchText: null, |
|
123 |
+ useAt: null, |
|
124 |
+ useAll: false, |
|
125 |
+ listType: "res", |
|
126 |
+ |
|
127 |
+ currentPage: null, |
|
128 |
+ recordSize: 3, |
|
129 |
+ pageSize: null, |
|
130 |
+ totalRecordCount: null, |
|
131 |
+ totalPageCount: null, |
|
132 |
+ startPage: null, |
|
133 |
+ endPage: null, |
|
134 |
+ limitStart: null, |
|
135 |
+ existPrevPage: null, |
|
136 |
+ existNextPage: null, |
|
137 |
+ }, // 예약현황 |
|
138 |
+ cardHisResData: [], // 예약현황 데이터 |
|
139 |
+ searchReqHisRtnDTO: { |
|
140 |
+ searchType: "all", |
|
141 |
+ searchText: null, |
|
142 |
+ useAt: null, |
|
143 |
+ useAll: false, |
|
144 |
+ listType: "rtn", |
|
145 |
+ |
|
146 |
+ currentPage: null, |
|
147 |
+ recordSize: 3, |
|
148 |
+ pageSize: null, |
|
149 |
+ totalRecordCount: null, |
|
150 |
+ totalPageCount: null, |
|
151 |
+ startPage: null, |
|
152 |
+ endPage: null, |
|
153 |
+ limitStart: null, |
|
154 |
+ existPrevPage: null, |
|
155 |
+ existNextPage: null, |
|
156 |
+ }, // 사용현황 |
|
157 |
+ cardHisRtnData: [], // 예약현황 데이터 |
|
158 | 158 |
}; |
159 | 159 |
}, |
160 | 160 |
computed: { |
161 | 161 |
}, |
162 |
- components:{ |
|
163 |
- SearchOutlined |
|
162 |
+ components: { |
|
163 |
+ SearchOutlined, Pagenation |
|
164 | 164 |
}, |
165 | 165 |
methods: { |
166 |
- changePage(page) { |
|
167 |
- if (page < 1 || page > this.totalPages) return; |
|
168 |
- this.currentPage = page; |
|
169 |
- this.$emit('page-changed', page); // 필요 시 부모에 알림 |
|
166 |
+ // 페이지 이동 예약현황황 |
|
167 |
+ fnChangeCurrentPageRes(currentPage) { |
|
168 |
+ this.searchReqHisResDTO.currentPage = Number(currentPage); |
|
169 |
+ this.$nextTick(() => { |
|
170 |
+ this.searchCardsHisRes(); |
|
171 |
+ }); |
|
170 | 172 |
}, |
171 |
- async onClickSubmit() { |
|
172 |
- // `useMutation` 훅을 사용하여 mutation 함수 가져오기 |
|
173 |
- const { mutate, onDone, onError } = useMutation(mygql); |
|
174 |
- |
|
173 |
+ //카드 정보 사용 현황 예약현황 |
|
174 |
+ async searchCardsHisRes() { |
|
175 | 175 |
try { |
176 |
- const result = await mutate(); |
|
177 |
- console.log(result); |
|
176 |
+ const response = await findAllAsSetCardHis(this.searchReqHisResDTO); |
|
177 |
+ if (response.status === 200) { |
|
178 |
+ this.cardHisResData = response.data.data.cardHis; // API 응답에서 카테고리 목록을 가져옴 |
|
179 |
+ this.searchReqHisResDTO = response.data.data.search; |
|
180 |
+ } |
|
178 | 181 |
} catch (error) { |
179 |
- console.error('Mutation error:', error); |
|
182 |
+ console.error("검색 중 오류 발생:", error); |
|
180 | 183 |
} |
181 | 184 |
}, |
182 |
- registerLeave() { |
|
183 |
- console.log("등록 버튼 클릭됨"); |
|
184 |
- |
|
185 |
- // Vue의 반응성 문제를 피하기 위해, 새로운 객체를 추가합니다. |
|
186 |
- this.DeptData = [ |
|
187 |
- ...this.DeptData, |
|
188 |
- { member: '', deptNM: '', acceptTerms: false } |
|
189 |
- ]; |
|
190 |
- |
|
191 |
- console.log(this.DeptData); // 배열 상태 출력 |
|
185 |
+ // 페이지 이동 사용현황 |
|
186 |
+ fnChangeCurrentPageRtn(currentPage) { |
|
187 |
+ this.searchReqHisRtnDTO.currentPage = Number(currentPage); |
|
188 |
+ this.$nextTick(() => { |
|
189 |
+ this.searchCardsHisRtn(); |
|
190 |
+ }); |
|
192 | 191 |
}, |
193 |
- getStatusClass(status) { |
|
194 |
- if (status === '승인') return 'status-approved'; |
|
195 |
- if (status === '대기') return 'status-pending'; |
|
196 |
- return ''; |
|
192 |
+ //카드 정보 사용 현황 사용현황 |
|
193 |
+ async searchCardsHisRtn() { |
|
194 |
+ try { |
|
195 |
+ const response = await findAllAsSetCardHis(this.searchReqHisRtnDTO); |
|
196 |
+ if (response.status === 200) { |
|
197 |
+ this.cardHisRtnData = response.data.data.cardHis; // API 응답에서 카테고리 목록을 가져옴 |
|
198 |
+ this.searchReqHisRtnDTO = response.data.data.search; |
|
199 |
+ } |
|
200 |
+ } catch (error) { |
|
201 |
+ console.error("검색 중 오류 발생:", error); |
|
202 |
+ } |
|
197 | 203 |
}, |
198 |
- |
|
199 | 204 |
}, |
200 | 205 |
created() { |
201 | 206 |
}, |
202 | 207 |
mounted() { |
203 |
- |
|
204 |
- |
|
208 |
+ this.searchCardsHisRes(); |
|
209 |
+ this.searchCardsHisRtn(); |
|
205 | 210 |
}, |
206 | 211 |
}; |
207 | 212 |
</script> |
+++ client/views/pages/Manager/asset/VhcleInfoManagement.vue
... | ... | @@ -0,0 +1,459 @@ |
1 | +<template> | |
2 | + <div class="card "> | |
3 | + <div class="card-body "> | |
4 | + <h2 class="card-title">차량정보 관리</h2> | |
5 | + <div class="flex align-top"> | |
6 | + <div class="sch-form-wrap search"> | |
7 | + <div class="input-group" style="display: flex;"> | |
8 | + <select v-model="searchReqDTO.searchType" id="searchType" class="form-select"> | |
9 | + <option value="all">전체</option> | |
10 | + <option value="vm">차량명</option> | |
11 | + <option value="vn">차량번호</option> | |
12 | + </select> | |
13 | + <div class="sch-input"> | |
14 | + <input type="text" class="form-control" v-model="searchReqDTO.searchText" @keyup.enter="searchVhcles"> | |
15 | + <button class="ico-sch" @click="searchCards"> | |
16 | + <SearchOutlined /> | |
17 | + </button> | |
18 | + </div> | |
19 | + </div> | |
20 | + <div class="tbl-wrap table-scroll"> | |
21 | + <table id="myTable" class="tbl data"> | |
22 | + <!-- 동적으로 <th> 생성 --> | |
23 | + <thead> | |
24 | + <tr> | |
25 | + <th>차량목록 </th> | |
26 | + </tr> | |
27 | + </thead> | |
28 | + <!-- 동적으로 <td> 생성 --> | |
29 | + <tbody> | |
30 | + <tr v-if="selectedVhcles.length === 0"> | |
31 | + <td style="text-align: center;">등록된 차량량정보가 없습니다.</td> | |
32 | + </tr> | |
33 | + <tr v-for="(item, index) in selectedVhcles" :key="index" @click="selectVhcle(item)"> | |
34 | + <td> | |
35 | + {{ item.vhcleNm }} | |
36 | + </td> | |
37 | + </tr> | |
38 | + </tbody> | |
39 | + </table> | |
40 | + | |
41 | + </div> | |
42 | + </div> | |
43 | + | |
44 | + <div style="width: 100%;"> | |
45 | + <div class=" sch-form-wrap title-wrap"> | |
46 | + <h3><img :src="h3icon" alt="">차량 정보</h3> | |
47 | + <div class="buttons" style="margin: 0;"> | |
48 | + <button type="submit" class="btn sm tertiary" @click="resetVhcle">신규</button> | |
49 | + <button type="reset" class="btn sm secondary" @click="insertByUpdateVhcle"> | |
50 | + {{ buttonText }} | |
51 | + </button> | |
52 | + <button type="delete" class="btn sm btn-red" @click="deleteByUseAtVhcle">삭제</button> | |
53 | + </div> | |
54 | + </div> | |
55 | + <form class="row g-3 pt-3 needs-validation " @submit.prevent="handleSubmit" style="margin-bottom: 3rem;"> | |
56 | + <div class="col-12"> | |
57 | + <div class="col-12 border-x"> | |
58 | + <label for="vhcty" class="form-label"> | |
59 | + <p>차종 | |
60 | + <p class="require"><img :src="require" alt=""></p> | |
61 | + </p> | |
62 | + </label> | |
63 | + <input type="text" class="form-control" id="where" v-model="selectedVhcle.vhcty" /> | |
64 | + </div> | |
65 | + <div class="col-12 border-x"> | |
66 | + <label for="vhcleNo" class="form-label"> | |
67 | + <p>차량번호 | |
68 | + <p class="require"><img :src="require" alt=""></p> | |
69 | + </p> | |
70 | + </label> | |
71 | + <input type="text" class="form-control" id="where" v-model="selectedVhcle.vhcleNo" /> | |
72 | + </div> | |
73 | + | |
74 | + </div> | |
75 | + <div class="col-12"> | |
76 | + <div class="col-12 border-x"> | |
77 | + <label for="fuelKnd" class="form-label">연료종류</label> | |
78 | + <input type="text" class="form-control" id="where" v-model="selectedVhcle.fuelKnd" /> | |
79 | + </div> | |
80 | + <div class="col-12 border-x"> | |
81 | + <label for="posesnStle" class="form-label"> | |
82 | + <p>소유형태 | |
83 | + <p class="require"><img :src="require" alt=""></p> | |
84 | + </p> | |
85 | + </label> | |
86 | + <input type="text" class="form-control" id="where" v-model="selectedVhcle.posesnStle" /> | |
87 | + </div> | |
88 | + | |
89 | + </div> | |
90 | + <div class="col-12 "> | |
91 | + <label for="vhcleNm" class="form-label"> | |
92 | + <p>차량명 | |
93 | + <p class="require"><img :src="require" alt=""></p> | |
94 | + </p> | |
95 | + </label> | |
96 | + <input type="text" class="form-control textarea" id="reason" v-model="selectedVhcle.vhcleNm" /> | |
97 | + </div> | |
98 | + <div class="col-12 "> | |
99 | + <label for="chargerId" class="form-label"> | |
100 | + <p>담당자 | |
101 | + <p class="require"><img :src="require" alt=""></p> | |
102 | + </p> | |
103 | + </label> | |
104 | + <input type="text" class="form-control textarea" id="reason" v-model="selectedVhcle.chargerId" readonly /> | |
105 | + <input type="button" class="form-control " value="검색" @click="showPopup = true" /> | |
106 | + <HrPopup v-if="showPopup" @close="showPopup = false" @select="addApproval" /> | |
107 | + </div> | |
108 | + <div class="col-12 chuljang "> | |
109 | + <label for="rm" class="form-label">비고</label> | |
110 | + <input type="text" class="form-control textarea" id="reason" v-model="selectedVhcle.rm" /> | |
111 | + </div> | |
112 | + <div class="col-12 border-x input-radio"> | |
113 | + <label for="prvonsh" class="form-label"> | |
114 | + <p>상태 | |
115 | + <p class="require"><img :src="require" alt=""></p> | |
116 | + </p> | |
117 | + </label> | |
118 | + <select class="form-select" id="sttus" v-model="selectedVhcle.sttus"> | |
119 | + <option value="N">정상</option> | |
120 | + <option value="R">수리</option> | |
121 | + <option value="D">폐차차</option> | |
122 | + </select> | |
123 | + </div> | |
124 | + | |
125 | + | |
126 | + </form> | |
127 | + <div class=" sch-form-wrap title-wrap"> | |
128 | + <h3><img :src="h3icon" alt="">예약현황</h3> | |
129 | + </div> | |
130 | + <div class="tbl-wrap chk-area"> | |
131 | + <table id="myTable" class="tbl data"> | |
132 | + | |
133 | + <thead> | |
134 | + <tr> | |
135 | + <th>차종</th> | |
136 | + <th>운행자</th> | |
137 | + <th>기간</th> | |
138 | + <th>상태</th> | |
139 | + </tr> | |
140 | + </thead> | |
141 | + <!-- 동적으로 <td> 생성 --> | |
142 | + <tbody> | |
143 | + <tr v-for="(item, index) in processedVhcles" :key="index"> | |
144 | + <td>{{ item.vhcty }}</td> | |
145 | + <td>{{ item.drverId }}</td> | |
146 | + <td>{{ item.formattedPeriod }}</td> | |
147 | + <td :class="getStatusClass(item.status)"> | |
148 | + {{ item.status }} | |
149 | + </td> | |
150 | + </tr> | |
151 | + </tbody> | |
152 | + </table> | |
153 | + | |
154 | + </div> | |
155 | + <Pagenation :search="searchReqHisDTO" @onChange="fnChangeCurrentPage" /> | |
156 | + </div> | |
157 | + </div> | |
158 | + </div> | |
159 | + | |
160 | + </div> | |
161 | + | |
162 | +</template> | |
163 | + | |
164 | +<script> | |
165 | +import GoogleCalendar from "../../../component/GoogleCalendar.vue" | |
166 | +import { SearchOutlined } from '@ant-design/icons-vue'; | |
167 | +import HrPopup from "../../../component/Popup/HrPopup.vue"; | |
168 | +import { saveAsSetVhcle, findAllAsSetVhcle, updateAsSetVhcle, findAllAsSetVhcleHis } from "../../../../resources/api/asset"; //카드 정보 API | |
169 | +import Pagenation from "../../../component/Pagenation.vue"; | |
170 | + | |
171 | +export default { | |
172 | + data() { | |
173 | + return { | |
174 | + showPopup: false, | |
175 | + selectedname: '', | |
176 | + approvals: [], | |
177 | + require: "/client/resources/img/require.png", | |
178 | + h3icon: "/client/resources/img/h3icon.png", | |
179 | + photoicon: "/client/resources/img/photo_icon.png", | |
180 | + img1: "/client/resources/img/img.png", | |
181 | + icon1: "/client/resources/img/icon.png", | |
182 | + dateicon: "/client/resources/img/date.png", | |
183 | + startbtn: "/client/resources/img/start.png", | |
184 | + stopbtn: "/client/resources/img/stop.png", | |
185 | + moreicon: "/client/resources/img/more.png", | |
186 | + | |
187 | + checkVhcle: false, | |
188 | + vhcleId: null, | |
189 | + selectedVhcles: [], | |
190 | + selectedVhcleHis: [], | |
191 | + searchReqDTO: { | |
192 | + searchType: "all", | |
193 | + searchText: null, | |
194 | + useAt: null, | |
195 | + useAll: true, | |
196 | + }, | |
197 | + searchReqHisDTO: { | |
198 | + searchType: "all", | |
199 | + searchText: null, | |
200 | + useAt: null, | |
201 | + useAll: false, | |
202 | + listType:"all", | |
203 | + | |
204 | + currentPage: null, | |
205 | + recordSize: 2, | |
206 | + pageSize: null, | |
207 | + totalRecordCount: null, | |
208 | + totalPageCount: null, | |
209 | + startPage: null, | |
210 | + endPage: null, | |
211 | + limitStart: null, | |
212 | + existPrevPage: null, | |
213 | + existNextPage: null, | |
214 | + }, | |
215 | + selectedVhcle: { | |
216 | + vhcleNm: null, | |
217 | + vhcty: null, | |
218 | + vhcleNo: null, | |
219 | + fuelKnd: null, | |
220 | + posesnStle: null, | |
221 | + chargerId: null, | |
222 | + rm: null, | |
223 | + sttus: "N", | |
224 | + useAt: "Y" | |
225 | + }, | |
226 | + } | |
227 | + }, | |
228 | + components: { | |
229 | + SearchOutlined, HrPopup, Pagenation | |
230 | + }, | |
231 | + methods: { | |
232 | + // 페이지 이동 | |
233 | + fnChangeCurrentPage(currentPage) { | |
234 | + this.searchReqHisDTO.currentPage = Number(currentPage); | |
235 | + console.log(this.searchReqHisDTO); | |
236 | + this.$nextTick(() => { | |
237 | + this.searchVhclesHis(); | |
238 | + }); | |
239 | + }, | |
240 | + //차량 정보 전체 조회 | |
241 | + async searchVhcles() { | |
242 | + try { | |
243 | + const response = await findAllAsSetVhcle(this.searchReqDTO); | |
244 | + if (response.status === 200) { | |
245 | + this.selectedVhcles = response.data.data.vhcle; // API 응답에서 카테고리 목록을 가져옴 | |
246 | + } | |
247 | + } catch (error) { | |
248 | + console.error("검색 중 오류 발생:", error); | |
249 | + } | |
250 | + }, | |
251 | + //차량 정보 사용 현황 전체 조회 | |
252 | + async searchVhclesHis() { | |
253 | + try { | |
254 | + const response = await findAllAsSetVhcleHis(this.searchReqHisDTO); | |
255 | + if (response.status === 200) { | |
256 | + this.selectedVhcleHis = response.data.data.vhcle; // API 응답에서 카테고리 목록을 가져옴 | |
257 | + this.searchReqHisDTO = response.data.data.search; | |
258 | + } | |
259 | + } catch (error) { | |
260 | + console.error("검색 중 오류 발생:", error); | |
261 | + } | |
262 | + }, | |
263 | + // 차량 정보 등록 수정 | |
264 | + async insertByUpdateVhcle() { | |
265 | + | |
266 | + if (!this.validateCheck()) { | |
267 | + return; // 유효성 검사 실패 시 함수 종료 | |
268 | + } | |
269 | + | |
270 | + if (this.vhcleId == null && this.vhcleId == "") { | |
271 | + try { | |
272 | + const response = await saveAsSetVhcle(this.selectedVhcle); | |
273 | + if (response.status === 200) { // 성공 여부 체크 | |
274 | + alert("등록되었습니다."); | |
275 | + window.location.reload(); | |
276 | + } | |
277 | + } catch (error) { | |
278 | + // HTTP 오류가 발생한 경우 | |
279 | + const errorMessage = error.response?.data?.message || "등록이 실패하였습니다."; | |
280 | + alert(errorMessage); // 오류 메시지 표시 | |
281 | + this.searchVhcles(); | |
282 | + } | |
283 | + } else{ | |
284 | + try { | |
285 | + const response = await updateAsSetVhcle(this.vhcleId, this.selectedVhcle); | |
286 | + if (response.status === 200) { | |
287 | + alert("수정되었습니다."); | |
288 | + window.location.reload(); | |
289 | + } | |
290 | + } catch (error) { | |
291 | + // HTTP 오류가 발생한 경우 | |
292 | + const errorMessage = error.response?.data?.message || "수정이 실패하였습니다."; | |
293 | + alert(errorMessage); // 오류 메시지 표시 | |
294 | + this.searchVhcles(); | |
295 | + } | |
296 | + } | |
297 | + }, | |
298 | + | |
299 | + // 등록 유효성 체크 함수 | |
300 | + validateCheck() { | |
301 | + // 1. 차종 (vhcty) 필수 체크 | |
302 | + if (!this.selectedVhcle.vhcty || this.selectedVhcle.vhcty.trim() === "") { | |
303 | + alert("차종은 필수 입력 항목입니다."); | |
304 | + return false; | |
305 | + } | |
306 | + | |
307 | + // 2. 차량번호 (vhcleNo) 필수 체크 | |
308 | + if (!this.selectedVhcle.vhcleNo || this.selectedVhcle.vhcleNo.trim() === "") { | |
309 | + alert("차량번호는 필수 입력 항목입니다."); | |
310 | + return false; | |
311 | + } | |
312 | + | |
313 | + // 3. 소유형태 (posesnStle) 필수 체크 | |
314 | + if (!this.selectedVhcle.posesnStle || this.selectedVhcle.posesnStle.trim() === "") { | |
315 | + alert("소유형태는 필수 입력 항목입니다."); | |
316 | + return false; | |
317 | + } | |
318 | + | |
319 | + // 4. 차량명 (vhcleNm) 필수 체크 | |
320 | + if (!this.selectedVhcle.vhcleNm || this.selectedVhcle.vhcleNm.trim() === "") { | |
321 | + alert("차량명은 필수 입력 항목입니다."); | |
322 | + return false; | |
323 | + } | |
324 | + | |
325 | + // 5. 담당자 (chargerId) 필수 체크 | |
326 | + if (!this.selectedVhcle.chargerId || this.selectedVhcle.chargerId.trim() === "") { | |
327 | + alert("담당자는 필수 입력 항목입니다."); | |
328 | + return false; | |
329 | + } | |
330 | + return true; // 모든 유효성 검사 통과 | |
331 | + }, | |
332 | + | |
333 | + // 삭제 버튼을 눌렀을 시 삭제 | |
334 | + async deleteByUseAtVhcle() { | |
335 | + try { | |
336 | + this.selectedVhcle.useAt = "N"; | |
337 | + const response = await updateAsSetVhcle(this.vhcleId, this.selectedVhcle); | |
338 | + if (response.status === 200) { | |
339 | + alert("삭제되었습니다."); | |
340 | + } | |
341 | + } catch (error) { | |
342 | + alert("삭제가 실패하였습니다."); | |
343 | + } | |
344 | + }, | |
345 | + | |
346 | + //선택한 차량의 정보를 오른쪽에 표시 | |
347 | + selectVhcle(item) { | |
348 | + this.checkVhcle = true; | |
349 | + this.vhcleId = item.vhcleId; | |
350 | + this.selectedVhcle = { | |
351 | + vhcleNm: item.vhcleNm, | |
352 | + vhcty: item.vhcty, | |
353 | + vhcleNo: item.vhcleNo, | |
354 | + fuelKnd: item.fuelKnd, | |
355 | + posesnStle: item.posesnStle, | |
356 | + chargerId: item.chargerId, | |
357 | + rm: item.rm, | |
358 | + sttus: item.sttus, | |
359 | + useAt: item.useAt | |
360 | + }; | |
361 | + }, | |
362 | + | |
363 | + // 신규 버튼을 눌렀을 시 | |
364 | + resetVhcle() { | |
365 | + this.checkVhcle = false; | |
366 | + this.selectedVhcle = { | |
367 | + vhcleNm: null, | |
368 | + vhcty: null, | |
369 | + vhcleNo: null, | |
370 | + fuelKnd: null, | |
371 | + posesnStle: null, | |
372 | + chargerId: null, | |
373 | + rm: null, | |
374 | + sttus: "N", | |
375 | + useAt: "Y" | |
376 | + }; | |
377 | + }, | |
378 | + | |
379 | + addApproval(selectedUser) { | |
380 | + this.approvals.push({ | |
381 | + name: selectedUser.name | |
382 | + }); | |
383 | + | |
384 | + this.selectedVhcle.chargerId = selectedUser.name; // 입력창에 표시 | |
385 | + this.showPopup = false; | |
386 | + }, | |
387 | + getStatusClass(status) { | |
388 | + if (status === '예약') return 'status-pending'; | |
389 | + if (status === '사용 중') return 'status-pending'; | |
390 | + if (status === '반납') return 'status-approved'; | |
391 | + return ''; // 기본값 | |
392 | + }, | |
393 | + }, | |
394 | + watch: { | |
395 | + | |
396 | + }, | |
397 | + computed: { | |
398 | + buttonText() { | |
399 | + return this.checkVhcle ? "수정" : "등록"; | |
400 | + }, | |
401 | + // selectedCardsHis 데이터를 가공하여 화면에 표시할 데이터를 만듭니다. | |
402 | + processedVhcles() { | |
403 | + const now = new Date(); // 현재 시간 | |
404 | + | |
405 | + // 날짜와 시간 문자열을 조합하여 Date 객체를 생성하는 헬퍼 함수 | |
406 | + const createDateTime = (dateStr, hourStr, minStr) => { | |
407 | + // dateStr이 "YYYY-MM-DD HH:MM:SS.ms" 형태라고 가정하고 "YYYY-MM-DD" 부분만 추출 | |
408 | + const datePart = dateStr.split(' ')[0]; | |
409 | + const year = parseInt(datePart.substring(0, 4), 10); | |
410 | + const month = parseInt(datePart.substring(5, 7), 10) - 1; // 월은 0부터 시작 (0=1월) | |
411 | + const day = parseInt(datePart.substring(8, 10), 10); | |
412 | + const hour = parseInt(hourStr, 10); | |
413 | + const minute = parseInt(minStr, 10); | |
414 | + return new Date(year, month, day, hour, minute, 0); | |
415 | + }; | |
416 | + | |
417 | + // Date 객체를 'YYYY-MM-DD HH:MI' 형식으로 포맷하는 헬퍼 함수 | |
418 | + const formatDateTime = (dateObj) => { | |
419 | + const year = dateObj.getFullYear(); | |
420 | + const month = String(dateObj.getMonth() + 1).padStart(2, '0'); | |
421 | + const day = String(dateObj.getDate()).padStart(2, '0'); | |
422 | + const hour = String(dateObj.getHours()).padStart(2, '0'); | |
423 | + const minute = String(dateObj.getMinutes()).padStart(2, '0'); | |
424 | + return `${year}-${month}-${day} ${hour}:${minute}`; | |
425 | + }; | |
426 | + | |
427 | + return this.selectedVhcleHis.map(item => { | |
428 | + const startDateTime = createDateTime(item.bgnde, item.beginHour, item.beginMnt); | |
429 | + const endDateTime = createDateTime(item.endde, item.endHour, item.endMnt); | |
430 | + | |
431 | + let status = ''; | |
432 | + if (now >= startDateTime && now <= endDateTime) { | |
433 | + status = '사용 중'; // 현재 시간이 기간 안에 포함 | |
434 | + } else if (now < startDateTime) { | |
435 | + status = '예약'; // 현재 시간이 시작일시보다 이전 | |
436 | + } else { // now > endDateTime | |
437 | + status = '반납'; // 현재 시간이 종료일시보다 이후 | |
438 | + } | |
439 | + | |
440 | + return { | |
441 | + ...item, // 원본 아이템의 모든 속성 유지 | |
442 | + formattedPeriod: `${formatDateTime(startDateTime)} ~ ${formatDateTime(endDateTime)}`, // 포맷된 기간 문자열 | |
443 | + status: status // 계산된 상태 | |
444 | + }; | |
445 | + }); | |
446 | + } | |
447 | + }, | |
448 | + mounted() { | |
449 | + console.log('main mounted'); | |
450 | + this.searchVhcles(); | |
451 | + this.searchVhclesHis(); | |
452 | + } | |
453 | +} | |
454 | +</script> | |
455 | +<style scoped> | |
456 | +tr { | |
457 | + cursor: pointer; | |
458 | +} | |
459 | +</style>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/pages/Manager/asset/VhcleList.vue
... | ... | @@ -0,0 +1,224 @@ |
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 v-model="searchReqHisResDTO.searchType" id="searchType" class="form-select"> | |
10 | + <option value="all">전체</option> | |
11 | + <option value="vm">차량명</option> | |
12 | + <option value="vn">차량번호</option> | |
13 | + <option value="dr">운행자</option> | |
14 | + </select> | |
15 | + <div class="sch-input"> | |
16 | + <input type="text" class="form-control" v-model="searchReqHisResDTO.searchText" | |
17 | + @keyup.enter="searchVhclesHisRes"> | |
18 | + <button class="ico-sch" @click="searchVhclesHisRes"> | |
19 | + <SearchOutlined /> | |
20 | + </button> | |
21 | + </div> | |
22 | + </div> | |
23 | + </div> | |
24 | + | |
25 | + <!-- Table --> | |
26 | + <div class="tbl-wrap"> | |
27 | + <table id="myTable" class="tbl data"> | |
28 | + <!-- 동적으로 <th> 생성 --> | |
29 | + <thead> | |
30 | + <tr> | |
31 | + <th>차종 </th> | |
32 | + <th>차량번호</th> | |
33 | + <th>차량명</th> | |
34 | + <th>부서</th> | |
35 | + <th>운행자</th> | |
36 | + <th>기간</th> | |
37 | + </tr> | |
38 | + </thead> | |
39 | + <!-- 동적으로 <td> 생성 --> | |
40 | + <tbody> | |
41 | + <tr v-if="!(vhcleHisResData && vhcleHisResData.length)"> | |
42 | + <td colspan="6" style="text-align: center;">등록된 예약현황이 없습니다.</td> | |
43 | + </tr> | |
44 | + <tr v-for="(item, index) in vhcleHisResData" :key="index"> | |
45 | + <td>{{ item.vhcty }}</td> | |
46 | + <td>{{ item.vhcleNo }}</td> | |
47 | + <td>{{ item.vhcleNm }}</td> | |
48 | + <td>{{ item.deptId }}</td> | |
49 | + <td>{{ item.drverId }}</td> | |
50 | + <td> | |
51 | + {{ item.bgnde }} {{ item.beginHour.padStart(2, '0') }}:{{ item.beginMnt.padStart(2, '0') }} ~ | |
52 | + {{ item.endde }} {{ item.endHour.padStart(2, '0') }}:{{ item.endMnt.padStart(2, '0') }} | |
53 | + </td> | |
54 | + </tr> | |
55 | + </tbody> | |
56 | + </table> | |
57 | + | |
58 | + </div> | |
59 | + <Pagenation :search="searchReqHisResDTO" @onChange="fnChangeCurrentPageRes" /> | |
60 | + <div class="sch-form-wrap title-wrap"> | |
61 | + <h3><img :src="h3icon" alt="">사용이력</h3> | |
62 | + <div class="input-group"> | |
63 | + <select v-model="searchReqHisRtnDTO.searchType" id="searchType" class="form-select"> | |
64 | + <option value="all">전체</option> | |
65 | + <option value="vm">차량명</option> | |
66 | + <option value="vn">차량번호</option> | |
67 | + <option value="dr">운행자</option> | |
68 | + </select> | |
69 | + <div class="sch-input"> | |
70 | + <input type="text" class="form-control" v-model="searchReqHisRtnDTO.searchText" | |
71 | + @keyup.enter="searchVhclesHisRtn"> | |
72 | + <button class="ico-sch" @click="searchVhclesHisRtn"> | |
73 | + <SearchOutlined /> | |
74 | + </button> | |
75 | + </div> | |
76 | + </div> | |
77 | + </div> | |
78 | + | |
79 | + <!-- Table --> | |
80 | + <div class="tbl-wrap"> | |
81 | + <table id="myTable" class="tbl data"> | |
82 | + <!-- 동적으로 <th> 생성 --> | |
83 | + <thead> | |
84 | + <tr> | |
85 | + <th>차종 </th> | |
86 | + <th>차량번호</th> | |
87 | + <th>차량명</th> | |
88 | + <th>부서</th> | |
89 | + <th>운행자</th> | |
90 | + <th>기간</th> | |
91 | + </tr> | |
92 | + </thead> | |
93 | + <!-- 동적으로 <td> 생성 --> | |
94 | + <tbody> | |
95 | + <tr v-if="!(vhcleHisRtnData && vhcleHisRtnData.length)"> | |
96 | + <td colspan="6" style="text-align: center;">등록된 예약현황이 없습니다.</td> | |
97 | + </tr> | |
98 | + <tr v-for="(item, index) in vhcleHisRtnData" :key="index"> | |
99 | + <td>{{ item.vhcty }}</td> | |
100 | + <td>{{ item.vhcleNo }}</td> | |
101 | + <td>{{ item.vhcleNm }}</td> | |
102 | + <td>{{ item.deptId }}</td> | |
103 | + <td>{{ item.drverId }}</td> | |
104 | + <td> | |
105 | + {{ item.bgnde }} {{ item.beginHour.padStart(2, '0') }}:{{ item.beginMnt.padStart(2, '0') }} ~ | |
106 | + {{ item.endde }} {{ item.endHour.padStart(2, '0') }}:{{ item.endMnt.padStart(2, '0') }} | |
107 | + </td> | |
108 | + </tr> | |
109 | + </tbody> | |
110 | + </table> | |
111 | + | |
112 | + </div> | |
113 | + <Pagenation :search="searchReqHisRtnDTO" @onChange="fnChangeCurrentPageRtn" /> | |
114 | + </div> | |
115 | + </div> | |
116 | + </div> | |
117 | +</template> | |
118 | + | |
119 | +<script> | |
120 | +import { ref } from 'vue'; | |
121 | +import { SearchOutlined } from '@ant-design/icons-vue'; | |
122 | +import Pagenation from "../../../component/Pagenation.vue"; | |
123 | +import { findAllAsSetVhcleHis } from "../../../../resources/api/asset"; //차량 정보 API | |
124 | +export default { | |
125 | + data() { | |
126 | + return { | |
127 | + photoicon: "/client/resources/img/photo_icon.png", | |
128 | + h3icon: "/client/resources/img/h3icon.png", | |
129 | + | |
130 | + searchReqHisResDTO: { | |
131 | + searchType: "all", | |
132 | + searchText: null, | |
133 | + useAt: null, | |
134 | + useAll: false, | |
135 | + listType: "res", | |
136 | + | |
137 | + currentPage: null, | |
138 | + recordSize: 3, | |
139 | + pageSize: null, | |
140 | + totalRecordCount: null, | |
141 | + totalPageCount: null, | |
142 | + startPage: null, | |
143 | + endPage: null, | |
144 | + limitStart: null, | |
145 | + existPrevPage: null, | |
146 | + existNextPage: null, | |
147 | + }, // 예약현황 | |
148 | + vhcleHisResData: [], // 예약현황 데이터 | |
149 | + searchReqHisRtnDTO: { | |
150 | + searchType: "all", | |
151 | + searchText: null, | |
152 | + useAt: null, | |
153 | + useAll: false, | |
154 | + listType: "rtn", | |
155 | + | |
156 | + currentPage: null, | |
157 | + recordSize: 3, | |
158 | + pageSize: null, | |
159 | + totalRecordCount: null, | |
160 | + totalPageCount: null, | |
161 | + startPage: null, | |
162 | + endPage: null, | |
163 | + limitStart: null, | |
164 | + existPrevPage: null, | |
165 | + existNextPage: null, | |
166 | + }, // 사용현황 | |
167 | + vhcleHisRtnData: [], // 예약현황 데이터 | |
168 | + }; | |
169 | + }, | |
170 | + computed: { | |
171 | + }, | |
172 | + components: { | |
173 | + SearchOutlined, Pagenation | |
174 | + }, | |
175 | + methods: { | |
176 | + // 페이지 이동 예약현황황 | |
177 | + fnChangeCurrentPageRes(currentPage) { | |
178 | + this.searchReqHisResDTO.currentPage = Number(currentPage); | |
179 | + this.$nextTick(() => { | |
180 | + this.searchVhclesHisRes(); | |
181 | + }); | |
182 | + }, | |
183 | + //차량량 정보 사용 현황 예약현황 | |
184 | + async searchVhclesHisRes() { | |
185 | + try { | |
186 | + const response = await findAllAsSetVhcleHis(this.searchReqHisResDTO); | |
187 | + if (response.status === 200) { | |
188 | + this.vhcleHisResData = response.data.data.vhcle; // API 응답에서 카테고리 목록을 가져옴 | |
189 | + this.searchReqHisResDTO = response.data.data.search; | |
190 | + } | |
191 | + } catch (error) { | |
192 | + console.error("검색 중 오류 발생:", error); | |
193 | + } | |
194 | + }, | |
195 | + // 페이지 이동 사용현황 | |
196 | + fnChangeCurrentPageRtn(currentPage) { | |
197 | + this.searchReqHisRtnDTO.currentPage = Number(currentPage); | |
198 | + this.$nextTick(() => { | |
199 | + this.searchVhclesHisRtn(); | |
200 | + }); | |
201 | + }, | |
202 | + //차량 정보 사용 현황 사용현황 | |
203 | + async searchVhclesHisRtn() { | |
204 | + try { | |
205 | + const response = await findAllAsSetVhcleHis(this.searchReqHisRtnDTO); | |
206 | + if (response.status === 200) { | |
207 | + this.vhcleHisRtnData = response.data.data.vhcle; // API 응답에서 카테고리 목록을 가져옴 | |
208 | + this.searchReqHisRtnDTO = response.data.data.search; | |
209 | + } | |
210 | + } catch (error) { | |
211 | + console.error("검색 중 오류 발생:", error); | |
212 | + } | |
213 | + }, | |
214 | + }, | |
215 | + created() { | |
216 | + }, | |
217 | + mounted() { | |
218 | + this.searchVhclesHisRes(); | |
219 | + this.searchVhclesHisRtn(); | |
220 | + }, | |
221 | +}; | |
222 | +</script> | |
223 | + | |
224 | +<style scoped></style>(파일 끝에 줄바꿈 문자 없음) |
--- client/views/pages/Manager/asset/asset.vue
+++ client/views/pages/Manager/asset/asset.vue
... | ... | @@ -15,52 +15,59 @@ |
15 | 15 |
</div> |
16 | 16 |
|
17 | 17 |
|
18 |
- <details class="menu-box" open> |
|
19 |
- <summary><p>법인차량</p><div class="icon"><img :src="topmenuicon" alt=""></div></summary> |
|
20 |
- <ul> |
|
21 |
- <li> <router-link :to="{ name: 'CarList' }" exact-active-class="active-link" v-slot="{ isExactActive }"> |
|
22 |
- <p>사용현황</p> |
|
23 |
- <div class="icon" v-if="isExactActive"> |
|
24 |
- <img :src="menuicon" alt=""> |
|
25 |
- </div> |
|
26 |
- </router-link></li> |
|
27 |
- <li> |
|
28 |
- <router-link :to="{ name: 'CarInfoManagement' }" exact-active-class="active-link" v-slot="{ isExactActive }"> |
|
29 |
- <p>차량정보 관리</p> |
|
30 |
- <div class="icon" v-if="isExactActive"> |
|
31 |
- <img :src="menuicon" alt=""> |
|
32 |
- </div> |
|
33 |
- </router-link> |
|
34 |
- </li> |
|
35 |
- |
|
36 |
- |
|
37 |
- </ul> |
|
18 |
+ <details class="menu-box" :open="$route.name.toLowerCase().includes('vhcle')"> |
|
19 |
+ <summary> |
|
20 |
+ <p>법인차량</p> |
|
21 |
+ <div class="icon"><img :src="topmenuicon" alt=""></div> |
|
22 |
+ </summary> |
|
23 |
+ <ul> |
|
24 |
+ <li> <router-link :to="{ name: 'VhcleList' }" exact-active-class="active-link" v-slot="{ isExactActive }"> |
|
25 |
+ <p>사용현황</p> |
|
26 |
+ <div class="icon" v-if="isExactActive"> |
|
27 |
+ <img :src="menuicon" alt=""> |
|
28 |
+ </div> |
|
29 |
+ </router-link></li> |
|
30 |
+ <li> |
|
31 |
+ <router-link :to="{ name: 'VhcleInfoManagement' }" exact-active-class="active-link" |
|
32 |
+ v-slot="{ isExactActive }"> |
|
33 |
+ <p>차량정보 관리</p> |
|
34 |
+ <div class="icon" v-if="isExactActive"> |
|
35 |
+ <img :src="menuicon" alt=""> |
|
36 |
+ </div> |
|
37 |
+ </router-link> |
|
38 |
+ </li> |
|
39 |
+ |
|
40 |
+ |
|
41 |
+ </ul> |
|
38 | 42 |
</details> |
39 |
- <details class="menu-box"> |
|
40 |
- <summary><p>법인카드</p><div class="icon"><img :src="topmenuicon" alt=""></div></summary> |
|
41 |
- <ul> |
|
42 |
- <li> <router-link :to="{ name: 'CardList' }" exact-active-class="active-link" v-slot="{ isExactActive }"> |
|
43 |
- <p>사용현황</p> |
|
44 |
- <div class="icon" v-if="isExactActive"> |
|
45 |
- <img :src="menuicon" alt=""> |
|
46 |
- </div> |
|
47 |
- </router-link></li> |
|
48 |
- <li> <router-link :to="{ name: 'CardInfoManagement' }" exact-active-class="active-link" v-slot="{ isExactActive }"> |
|
49 |
- <p>카드정보 관리</p> |
|
50 |
- <div class="icon" v-if="isExactActive"> |
|
51 |
- <img :src="menuicon" alt=""> |
|
52 |
- </div> |
|
53 |
- </router-link></li> |
|
54 |
- |
|
55 |
- |
|
56 |
- </ul> |
|
43 |
+ <details class="menu-box" :open="$route.name.toLowerCase().includes('card')"> |
|
44 |
+ <summary> |
|
45 |
+ <p>법인카드</p> |
|
46 |
+ <div class="icon"><img :src="topmenuicon" alt=""></div> |
|
47 |
+ </summary> |
|
48 |
+ <ul> |
|
49 |
+ <li> <router-link :to="{ name: 'CardList' }" exact-active-class="active-link" v-slot="{ isExactActive }"> |
|
50 |
+ <p>사용현황</p> |
|
51 |
+ <div class="icon" v-if="isExactActive"> |
|
52 |
+ <img :src="menuicon" alt=""> |
|
53 |
+ </div> |
|
54 |
+ </router-link></li> |
|
55 |
+ <li> <router-link :to="{ name: 'CardInfoManagement' }" exact-active-class="active-link" |
|
56 |
+ v-slot="{ isExactActive }"> |
|
57 |
+ <p>카드정보 관리</p> |
|
58 |
+ <div class="icon" v-if="isExactActive"> |
|
59 |
+ <img :src="menuicon" alt=""> |
|
60 |
+ </div> |
|
61 |
+ </router-link></li> |
|
62 |
+ |
|
63 |
+ |
|
64 |
+ </ul> |
|
57 | 65 |
</details> |
58 | 66 |
</div> |
59 | 67 |
</div> |
60 | 68 |
<!-- End Page Title --> |
61 | 69 |
<div class="content"> |
62 | 70 |
<router-view></router-view> |
63 |
- |
|
64 | 71 |
</div> |
65 | 72 |
</template> |
66 | 73 |
|
... | ... | @@ -88,6 +95,57 @@ |
88 | 95 |
computed: { |
89 | 96 |
}, |
90 | 97 |
methods: { |
98 |
+ async onClickSubmit() { |
|
99 |
+ // `useMutation` 훅을 사용하여 mutation 함수 가져오기 |
|
100 |
+ const { mutate, onDone, onError } = useMutation(mygql); |
|
101 |
+ |
|
102 |
+ try { |
|
103 |
+ const result = await mutate(); |
|
104 |
+ console.log(result); |
|
105 |
+ } catch (error) { |
|
106 |
+ console.error('Mutation error:', error); |
|
107 |
+ } |
|
108 |
+ }, |
|
109 |
+ registerLeave() { |
|
110 |
+ console.log("등록 버튼 클릭됨"); |
|
111 |
+ |
|
112 |
+ // Vue의 반응성 문제를 피하기 위해, 새로운 객체를 추가합니다. |
|
113 |
+ this.DeptData = [ |
|
114 |
+ ...this.DeptData, |
|
115 |
+ { member: '', deptNM: '', acceptTerms: false } |
|
116 |
+ ]; |
|
117 |
+ |
|
118 |
+ console.log(this.DeptData); // 배열 상태 출력 |
|
119 |
+ }, |
|
120 |
+ saveChanges() { |
|
121 |
+ // 로컬스토리지에 DeptData 저장 |
|
122 |
+ localStorage.setItem('DeptData', JSON.stringify(this.DeptData)); |
|
123 |
+ console.log('데이터가 로컬스토리지에 저장되었습니다.'); |
|
124 |
+ }, |
|
125 |
+ deletePending() { |
|
126 |
+ // 선택된 항목만 필터링하여 삭제 |
|
127 |
+ const selectedItems = this.DeptData.filter(item => item.acceptTerms); |
|
128 |
+ |
|
129 |
+ // 승인된 항목이 없으면 삭제 진행 |
|
130 |
+ if (selectedItems.length > 0) { |
|
131 |
+ this.DeptData = this.DeptData.filter(item => !item.acceptTerms); |
|
132 |
+ alert(`${selectedItems.length}개의 항목이 삭제되었습니다.`); |
|
133 |
+ } else { |
|
134 |
+ alert("선택된 항목이 없습니다."); |
|
135 |
+ } |
|
136 |
+ }, |
|
137 |
+ // 날짜 필터 적용 |
|
138 |
+ filterData() { |
|
139 |
+ this.filteredData = this.DeptData.filter(item => { |
|
140 |
+ const itemYear = new Date(item.startDate).getFullYear(); |
|
141 |
+ const itemMonth = new Date(item.startDate).getMonth() + 1; // 월은 0부터 시작하므로 1을 더해줍니다. |
|
142 |
+ |
|
143 |
+ return ( |
|
144 |
+ (!this.selectedYear || itemYear === parseInt(this.selectedYear)) && |
|
145 |
+ (!this.selectedMonth || itemMonth === parseInt(this.selectedMonth)) |
|
146 |
+ ); |
|
147 |
+ }); |
|
148 |
+ }, |
|
91 | 149 |
|
92 | 150 |
// 페이지 변경 |
93 | 151 |
changePage(page) { |
... | ... | @@ -95,9 +153,18 @@ |
95 | 153 |
}, |
96 | 154 |
}, |
97 | 155 |
created() { |
156 |
+ // 로컬스토리지에서 기존 데이터가 있으면 불러오기 |
|
157 |
+ const storedData = localStorage.getItem('DeptData'); |
|
158 |
+ console.log(storedData); |
|
159 |
+ if (storedData) { |
|
160 |
+ this.DeptData = JSON.parse(storedData); |
|
161 |
+ } |
|
98 | 162 |
}, |
99 | 163 |
mounted() { |
100 | 164 |
|
165 |
+ // 처음에는 모든 데이터를 표시 |
|
166 |
+ this.filteredData = this.DeptData; |
|
167 |
+ |
|
101 | 168 |
}, |
102 | 169 |
}; |
103 | 170 |
</script> |
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?