
--- client/resources/css/style.css
+++ client/resources/css/style.css
... | ... | @@ -427,7 +427,7 @@ |
427 | 427 |
width: 100%; |
428 | 428 |
} |
429 | 429 |
|
430 |
-.header .search-form input { |
|
430 |
+ .search-form input { |
|
431 | 431 |
border: 0; |
432 | 432 |
font-size: 14px; |
433 | 433 |
color: #012970; |
... | ... | @@ -438,21 +438,21 @@ |
438 | 438 |
width: 100%; |
439 | 439 |
} |
440 | 440 |
|
441 |
-.header .search-form input:focus, |
|
442 |
-.header .search-form input:hover { |
|
441 |
+ .search-form input:focus, |
|
442 |
+ .search-form input:hover { |
|
443 | 443 |
outline: none; |
444 | 444 |
box-shadow: 0 0 10px 0 rgba(1, 41, 112, 0.15); |
445 | 445 |
border: 1px solid rgba(1, 41, 112, 0.3); |
446 | 446 |
} |
447 | 447 |
|
448 |
-.header .search-form button { |
|
448 |
+ .search-form button { |
|
449 | 449 |
border: 0; |
450 | 450 |
padding: 0; |
451 | 451 |
margin-left: -30px; |
452 | 452 |
background: none; |
453 | 453 |
} |
454 | 454 |
|
455 |
-.header .search-form button i { |
|
455 |
+ .search-form button i { |
|
456 | 456 |
color: #012970; |
457 | 457 |
} |
458 | 458 |
|
... | ... | @@ -1223,4 +1223,6 @@ |
1223 | 1223 |
text-align: center; |
1224 | 1224 |
font-size: 13px; |
1225 | 1225 |
color: #012970; |
1226 |
-}(파일 끝에 줄바꿈 문자 없음) |
|
1226 |
+} |
|
1227 |
+ |
|
1228 |
+.select-member{color: #6980aa;}(파일 끝에 줄바꿈 문자 없음) |
--- client/views/layout/SideBar.vue
+++ client/views/layout/SideBar.vue
... | ... | @@ -1,84 +1,97 @@ |
1 | 1 |
<template> |
2 |
- <!-- ======= Sidebar ======= --> |
|
2 |
+ <!-- ======= Sidebar ======= --> |
|
3 | 3 |
<aside id="sidebar" class="sidebar"> |
4 | 4 |
|
5 |
-<ul class="sidebar-nav" id="sidebar-nav"> |
|
5 |
+ <ul class="sidebar-nav" id="sidebar-nav"> |
|
6 | 6 |
|
7 |
- <li class="nav-item"> |
|
8 |
- <router-link to="/" class="nav-link " active-class="active"><i class="bi bi-grid"></i> |
|
9 |
- <span>Home</span></router-link> |
|
10 |
- </li><!-- End Dashboard Nav --> |
|
7 |
+ <li class="nav-item"> |
|
8 |
+ <router-link to="/" class="nav-link " active-class="active"><i class="bi bi-grid"></i> |
|
9 |
+ <span>Home</span></router-link> |
|
10 |
+ </li><!-- End Dashboard Nav --> |
|
11 | 11 |
|
12 | 12 |
|
13 | 13 |
|
14 |
- <li class="nav-heading">Pages</li> |
|
15 |
- |
|
16 |
- <li class="nav-item"> |
|
17 |
- <router-link to="/ChuljangList" class="nav-link " active-class="active"><i class="bi bi-journal-text"></i> |
|
18 |
- <span>출장관리</span></router-link> |
|
19 |
- </li><!-- End Profile Page Nav --> |
|
20 |
- <li class="nav-item"> |
|
21 |
- <a class="nav-link collapsed" data-bs-target="#components-nav" data-bs-toggle="collapse" href="#"> |
|
14 |
+ <li class="nav-heading">Pages</li> |
|
15 |
+ <li class="nav-item"> |
|
16 |
+ <a class="nav-link collapsed" data-bs-target="#Chuljang-nav" data-bs-toggle="collapse" href="#"> |
|
22 | 17 |
<i class="bi bi-journal-text"></i> |
23 |
- <span>휴가관리</span><i class="bi bi-chevron-down ms-auto"></i> |
|
18 |
+ <span>출장관리</span><i class="bi bi-chevron-down ms-auto"></i> |
|
24 | 19 |
</a> |
25 |
- <ul id="components-nav" class="nav-content collapse " data-bs-parent="#sidebar-nav"> |
|
20 |
+ <ul id="Chuljang-nav" class="nav-content collapse " data-bs-parent="#sidebar-nav"> |
|
26 | 21 |
<li> |
27 |
- <router-link to="/HyugaInsert" class="nav-link " active-class="active"><i class="bi bi-circle"></i><span>휴가신청</span></router-link> |
|
22 |
+ <router-link to="/ChuljangInsert" class="nav-link " active-class="active"><i |
|
23 |
+ class="bi bi-circle"></i><span>출장신청</span></router-link> |
|
28 | 24 |
</li> |
29 | 25 |
<li> |
30 |
- <router-link to="/HyugaList" class="nav-link " active-class="active"><i class="bi bi-circle"></i><span>휴가내역</span></router-link> |
|
26 |
+ <router-link to="/ChuljangList" class="nav-link " active-class="active"><i |
|
27 |
+ class="bi bi-circle"></i><span>출장내역</span></router-link> |
|
31 | 28 |
</li> |
32 |
- |
|
29 |
+ |
|
33 | 30 |
</ul> |
34 | 31 |
</li> |
35 | 32 |
<li class="nav-item"> |
36 |
- <a class="nav-link collapsed" data-bs-target="#components-nav" data-bs-toggle="collapse" href="#"> |
|
33 |
+ <a class="nav-link collapsed" data-bs-target="#hyuga-nav" data-bs-toggle="collapse" href="#"> |
|
37 | 34 |
<i class="bi bi-journal-text"></i> |
38 |
- <span>프로젝트관리</span><i class="bi bi-chevron-down ms-auto"></i> |
|
35 |
+ <span>휴가관리</span><i class="bi bi-chevron-down ms-auto"></i> |
|
39 | 36 |
</a> |
40 |
- <ul id="components-nav" class="nav-content collapse " data-bs-parent="#sidebar-nav"> |
|
37 |
+ <ul id="hyuga-nav" class="nav-content collapse " data-bs-parent="#sidebar-nav"> |
|
41 | 38 |
<li> |
42 |
- <router-link to="/ProjectInsert" class="nav-link " active-class="active"><i class="bi bi-circle"></i><span>프로젝트 등록</span></router-link> |
|
39 |
+ <router-link to="/HyugaInsert" class="nav-link " active-class="active"><i |
|
40 |
+ class="bi bi-circle"></i><span>휴가신청</span></router-link> |
|
43 | 41 |
</li> |
44 |
- |
|
42 |
+ <li> |
|
43 |
+ <router-link to="/HyugaList" class="nav-link " active-class="active"><i |
|
44 |
+ class="bi bi-circle"></i><span>휴가내역</span></router-link> |
|
45 |
+ </li> |
|
46 |
+ |
|
47 |
+ </ul> |
|
48 |
+ </li> |
|
49 |
+ <li class="nav-item"> |
|
50 |
+ <a class="nav-link collapsed" data-bs-target="#project-nav" data-bs-toggle="collapse" href="#"> |
|
51 |
+ <i class="bi bi-journal-text"></i> |
|
52 |
+ <span>프로젝트관리</span><i class="bi bi-chevron-down ms-auto"></i> |
|
53 |
+ </a> |
|
54 |
+ <ul id="project-nav" class="nav-content collapse " data-bs-parent="#sidebar-nav"> |
|
55 |
+ <li> |
|
56 |
+ <router-link to="/ProjectInsert" class="nav-link " active-class="active"><i |
|
57 |
+ class="bi bi-circle"></i><span>프로젝트 등록</span></router-link> |
|
58 |
+ </li> |
|
59 |
+ |
|
45 | 60 |
<li> |
46 | 61 |
<router-link to="/ProjectList" class="nav-link " active-class="active"><i class="bi bi-journal-text"></i> |
47 | 62 |
<span>프로젝트 내역</span></router-link> |
48 | 63 |
</li> |
49 |
- |
|
64 |
+ |
|
50 | 65 |
</ul> |
51 | 66 |
</li> |
52 | 67 |
|
53 |
- <li class="nav-item"> |
|
54 |
- <router-link to="/NoticeList" class="nav-link " active-class="active"><i class="bi bi-journal-text"></i> |
|
55 |
- <span>공지사항</span></router-link> |
|
56 |
- </li><!-- End Register Page Nav --> |
|
68 |
+ <li class="nav-item"> |
|
69 |
+ <router-link to="/NoticeList" class="nav-link " active-class="active"><i class="bi bi-journal-text"></i> |
|
70 |
+ <span>공지사항</span></router-link> |
|
71 |
+ </li><!-- End Register Page Nav --> |
|
57 | 72 |
|
58 |
- <li class="nav-item"> |
|
59 |
- <router-link to="/EmployeeList" class="nav-link " active-class="active"><i class="bi bi-person"></i> |
|
60 |
- <span>직원관리</span></router-link> |
|
61 |
- </li><!-- End Login Page Nav --> |
|
73 |
+ <li class="nav-item"> |
|
74 |
+ <router-link to="/EmployeeList" class="nav-link " active-class="active"><i class="bi bi-person"></i> |
|
75 |
+ <span>직원관리</span></router-link> |
|
76 |
+ </li><!-- End Login Page Nav --> |
|
62 | 77 |
|
63 |
- <li class="nav-item"> |
|
64 |
- <router-link to="/DeptList" class="nav-link " active-class="active"><i class="bi bi-person"></i> |
|
65 |
- <span>부서관리</span></router-link> |
|
66 |
- </li><!-- End Error 404 Page Nav --> |
|
78 |
+ <li class="nav-item"> |
|
79 |
+ <router-link to="/DeptList" class="nav-link " active-class="active"><i class="bi bi-person"></i> |
|
80 |
+ <span>부서관리</span></router-link> |
|
81 |
+ </li><!-- End Error 404 Page Nav --> |
|
67 | 82 |
|
68 |
- <li class="nav-item"> |
|
69 |
- <router-link to="/PubHoliyday" class="nav-link " active-class="active"><i class="bi bi-gem"></i> |
|
70 |
- <span>공휴일관리</span></router-link> |
|
71 |
- </li><!-- End Blank Page Nav --> |
|
83 |
+ <li class="nav-item"> |
|
84 |
+ <router-link to="/PubHoliyday" class="nav-link " active-class="active"><i class="bi bi-gem"></i> |
|
85 |
+ <span>공휴일관리</span></router-link> |
|
86 |
+ </li><!-- End Blank Page Nav --> |
|
72 | 87 |
|
73 |
-</ul> |
|
88 |
+ </ul> |
|
74 | 89 |
|
75 |
-</aside><!-- End Sidebar--> |
|
76 |
- </template> |
|
77 |
- |
|
78 |
- <script > |
|
79 |
- |
|
80 |
- </script> |
|
81 |
- |
|
82 |
- <style> |
|
83 |
- |
|
84 |
- </style>(파일 끝에 줄바꿈 문자 없음) |
|
90 |
+ </aside><!-- End Sidebar--> |
|
91 |
+</template> |
|
92 |
+ |
|
93 |
+<script> |
|
94 |
+ |
|
95 |
+</script> |
|
96 |
+ |
|
97 |
+<style></style>(파일 끝에 줄바꿈 문자 없음) |
--- client/views/pages/AppRouter.js
+++ client/views/pages/AppRouter.js
... | ... | @@ -11,6 +11,7 @@ |
11 | 11 |
|
12 | 12 |
// 직원 |
13 | 13 |
import ChuljangList from '../pages/Employee/ChuljangList.vue'; |
14 |
+import ChuljangInsert from '../pages/Employee/ChuljangInsert.vue'; |
|
14 | 15 |
import HyugaList from '../pages/Employee/HyugaList.vue'; |
15 | 16 |
import HyugaInsert from '../pages/Employee/HyugaInsert.vue'; |
16 | 17 |
import ProjectList from '../pages/Employee/ProjectList.vue'; |
... | ... | @@ -28,6 +29,7 @@ |
28 | 29 |
{ path: '/Join', name: 'Join', component: Join}, |
29 | 30 |
|
30 | 31 |
{ path: '/ChuljangList', name: 'ChuljangList', component: ChuljangList }, |
32 |
+ { path: '/ChuljangInsert', name: 'ChuljangInsert', component: ChuljangInsert }, |
|
31 | 33 |
{ path: '/HyugaList', name: 'HyugaList', component: HyugaList }, |
32 | 34 |
{ path: '/HyugaInsert', name: 'HyugaInsert', component: HyugaInsert }, |
33 | 35 |
{ path: '/ProjectInsert', name: 'ProjectInsert', component: ProjectInsert }, |
+++ client/views/pages/Employee/ChuljangInsert.vue
... | ... | @@ -0,0 +1,178 @@ |
1 | +<template> | |
2 | + <div class="pagetitle"> | |
3 | + <h1>출장신청</h1> | |
4 | + </div><!-- End Page Title --> | |
5 | + | |
6 | + <section class="section"> | |
7 | + <div class="card"> | |
8 | + <div class="card-body"> | |
9 | + | |
10 | + <!-- Multi Columns Form --> | |
11 | + <form class="row g-3 pt-3" @submit.prevent="handleSubmit"> | |
12 | + | |
13 | + | |
14 | + <div class="col-md-5"> | |
15 | + <label for="startDate" class="form-label">시작일</label> | |
16 | + <div class="d-flex gap-1"> | |
17 | + <input type="date" class="form-control" id="startDate" v-model="startDate" /> | |
18 | + <!-- 시간 선택을 위한 select 사용 --> | |
19 | + <select class="form-control" id="startTime" v-model="startTime"> | |
20 | + <option value="09:00">09:00</option> | |
21 | + <option value="10:00">10:00</option> | |
22 | + <option value="11:00">11:00</option> | |
23 | + <option value="12:00">12:00</option> | |
24 | + <option value="13:00">13:00</option> | |
25 | + <option value="14:00">14:00</option> | |
26 | + <option value="15:00">15:00</option> | |
27 | + <option value="16:00">16:00</option> | |
28 | + <option value="17:00">17:00</option> | |
29 | + <option value="18:00">18:00</option> | |
30 | + </select> | |
31 | + </div> | |
32 | + </div> | |
33 | + | |
34 | + <div class="col-md-5"> | |
35 | + <label for="endDate" class="form-label">종료일</label> | |
36 | + <div class="d-flex gap-1"> | |
37 | + <input type="date" class="form-control" id="endDate" v-model="endDate" /> | |
38 | + <!-- 종료 시간을 위한 select 사용 --> | |
39 | + <select class="form-control" id="endTime" v-model="endTime"> | |
40 | + <option value="09:00">09:00</option> | |
41 | + <option value="10:00">10:00</option> | |
42 | + <option value="11:00">11:00</option> | |
43 | + <option value="12:00">12:00</option> | |
44 | + <option value="13:00">13:00</option> | |
45 | + <option value="14:00">14:00</option> | |
46 | + <option value="15:00">15:00</option> | |
47 | + <option value="16:00">16:00</option> | |
48 | + <option value="17:00">17:00</option> | |
49 | + <option value="18:00">18:00</option> | |
50 | + </select> | |
51 | + </div> | |
52 | + </div> | |
53 | + <div class="col-12"> | |
54 | + <label for="prvonsh" class="form-label">출장지</label> | |
55 | + <input type="text" class="form-control" id="prvonsh" v-model="reason" /> | |
56 | + </div> | |
57 | + <div class="col-12"> | |
58 | + <label for="prvonsh" class="form-label">출장목적</label> | |
59 | + <input type="text" class="form-control" id="prvonsh" v-model="reason" /> | |
60 | + </div> | |
61 | + <div class="col-6"> | |
62 | + <label for="prvonsh" class="form-label">동행자</label> | |
63 | + <div class="search-bar d-flex gap-2"> | |
64 | + <form class="search-form d-flex align-items-center" method="POST" action="#" | |
65 | + @submit.prevent="updateMember"> | |
66 | + <input type="text" v-model="searchQuery" name="query" placeholder="Search" title="Enter search keyword"> | |
67 | + <button type="submit" title="Search"><i class="bi bi-search"></i></button> | |
68 | + </form> | |
69 | + <input type="text" class="select-member" :value="selectedMember.join(' ')" readonly> | |
70 | + </div> | |
71 | + </div> | |
72 | + | |
73 | + <div class="text-end"> | |
74 | + <button type="submit" class="btn btn-primary">승인요청</button> | |
75 | + <button type="reset" class="btn btn-secondary">취소</button> | |
76 | + </div> | |
77 | + </form><!-- End Multi Columns Form --> | |
78 | + | |
79 | + </div> | |
80 | + </div> | |
81 | + </section> | |
82 | +</template> | |
83 | + | |
84 | +<script> | |
85 | +import { useAuthStore } from '../../stores/authStore'; | |
86 | +export default { | |
87 | + data() { | |
88 | + const today = new Date().toISOString().split('T')[0]; | |
89 | + return { | |
90 | + searchQuery: '', | |
91 | + selectedMember: [], | |
92 | + startDate: today, | |
93 | + startTime: "09:00", // 기본 시작 시간 09:00 | |
94 | + endDate: today, | |
95 | + endTime: "18:00", // 기본 종료 시간 18:00 | |
96 | + allDay: false, // 종일 여부 | |
97 | + dayCount: 1, // 사용 휴가일 계산 | |
98 | + reason: "", // 사유 | |
99 | + }; | |
100 | + }, | |
101 | + computed: { | |
102 | + // Pinia Store의 상태를 가져옵니다. | |
103 | + loginUser() { | |
104 | + const authStore = useAuthStore(); | |
105 | + return authStore.getLoginUser; | |
106 | + }, | |
107 | + }, | |
108 | + methods: { | |
109 | + updateMember() { | |
110 | + // Add the search query to the selectedMembers array if it's not empty | |
111 | + if (this.searchQuery.trim()) { | |
112 | + this.selectedMember.push(this.searchQuery.trim()); | |
113 | + } | |
114 | + // Clear the search query after adding it to selectedMembers | |
115 | + this.searchQuery = ''; | |
116 | + }, | |
117 | + // 폼 검증 메서드 | |
118 | + validateForm() { | |
119 | + // 필수 입력 필드 체크 | |
120 | + if ( | |
121 | + this.startDate && | |
122 | + this.startTime && | |
123 | + this.endDate && | |
124 | + this.endTime && | |
125 | + this.dayCount > 0 && | |
126 | + this.reason.trim() !== "" | |
127 | + ) { | |
128 | + this.isFormValid = true; | |
129 | + } else { | |
130 | + this.isFormValid = false; | |
131 | + } | |
132 | + }, | |
133 | + calculateDayCount() { | |
134 | + const start = new Date(`${this.startDate}T${this.startTime}:00`); | |
135 | + const end = new Date(`${this.endDate}T${this.endTime}:00`); | |
136 | + | |
137 | + let totalHours = (end - start) / (1000 * 60 * 60); // 밀리초를 시간 단위로 변환 | |
138 | + | |
139 | + if (this.startDate !== this.endDate) { | |
140 | + // 시작일과 종료일이 다른경우 | |
141 | + const startDateObj = new Date(this.startDate); | |
142 | + const endDateObj = new Date(this.endDate); | |
143 | + const daysDifference = (endDateObj - startDateObj) / (1000 * 60 * 60 * 24); // 두 날짜 사이의 차이를 일수로 계산 | |
144 | + if (this.startTime !== "09:00" || this.endTime !== "18:00") { | |
145 | + this.dayCount = daysDifference + 0.5; // 시간 조건이 기준에서 벗어날 경우 | |
146 | + } else { | |
147 | + this.dayCount = Math.ceil(daysDifference + 1); // 시간 조건이 기준에 맞을 경우 | |
148 | + } | |
149 | + } else { | |
150 | + // 시작일과 종료일이 같은 경우 | |
151 | + if (this.startTime !== "09:00" || this.endTime !== "18:00") { | |
152 | + this.dayCount = 0.5; // 시작 시간 또는 종료 시간이 기준과 다를 경우 0.5 | |
153 | + } else { | |
154 | + this.dayCount = 1; // 기준 시간(09:00~18:00)이 맞으면 1일로 간주 | |
155 | + } | |
156 | + } | |
157 | + | |
158 | + this.validateForm(); // dayCount 변경 후 폼 재검증 | |
159 | + }, | |
160 | + handleSubmit() { | |
161 | + this.validateForm(); // 제출 시 유효성 확인 | |
162 | + if (this.isFormValid) { | |
163 | + alert("승인 요청이 완료되었습니다."); | |
164 | + // 추가 처리 로직 (API 요청 등) | |
165 | + } else { | |
166 | + alert("모든 필드를 올바르게 작성해주세요."); | |
167 | + } | |
168 | + }, | |
169 | + }, | |
170 | + watch: { | |
171 | + startDate: 'calculateDayCount', | |
172 | + startTime: 'calculateDayCount', | |
173 | + endDate: 'calculateDayCount', | |
174 | + endTime: 'calculateDayCount', | |
175 | + reason: "validateForm", | |
176 | + }, | |
177 | +}; | |
178 | +</script> |
--- client/views/pages/Employee/ChuljangList.vue
+++ client/views/pages/Employee/ChuljangList.vue
... | ... | @@ -52,6 +52,8 @@ |
52 | 52 |
<th data-type="date" data-format="YYYY/DD/MM">시작일자</th> |
53 | 53 |
<th data-type="date" data-format="YYYY/DD/MM">종료일자</th> |
54 | 54 |
<th>출장지</th> |
55 |
+ <th>출장목적</th> |
|
56 |
+ <th>동행자</th> |
|
55 | 57 |
</tr> |
56 | 58 |
</thead> |
57 | 59 |
<!-- 동적으로 <td> 생성 --> |
... | ... | @@ -66,6 +68,10 @@ |
66 | 68 |
<td><input type="date" v-model="item.startDate" /></td> |
67 | 69 |
<td><input type="date" v-model="item.endDate" /></td> |
68 | 70 |
<td><input type="text" v-model="item.where" /></td> |
71 |
+ <td><input type="text" v-model="item.purpose" /></td> |
|
72 |
+ <td ><input type="text" v-model="item.member" /> |
|
73 |
+ |
|
74 |
+ </td> |
|
69 | 75 |
</tr> |
70 | 76 |
</tbody> |
71 | 77 |
</table> |
... | ... | @@ -89,7 +95,7 @@ |
89 | 95 |
selectedYear: '', |
90 | 96 |
selectedMonth: '', |
91 | 97 |
ChuljangData: [ |
92 |
- { startDate: '', endDate: '', where: '대구', acceptTerms: false }, |
|
98 |
+ { startDate: '', endDate: '', where: '대구', purpose: '', acceptTerms: false }, |
|
93 | 99 |
{ startDate: '', endDate: '', where: '경산', acceptTerms: false }, |
94 | 100 |
// 더 많은 데이터 추가... |
95 | 101 |
], |
--- client/views/pages/Employee/HyugaInsert.vue
+++ client/views/pages/Employee/HyugaInsert.vue
... | ... | @@ -6,10 +6,9 @@ |
6 | 6 |
<section class="section"> |
7 | 7 |
<div class="card"> |
8 | 8 |
<div class="card-body"> |
9 |
- <h5 class="card-title"></h5> |
|
10 | 9 |
|
11 | 10 |
<!-- Multi Columns Form --> |
12 |
- <form class="row g-3" @submit.prevent="handleSubmit"> |
|
11 |
+ <form class="row g-3 pt-3" @submit.prevent="handleSubmit"> |
|
13 | 12 |
<div class="col-md-9"> |
14 | 13 |
<label for="inputName5" class="form-label">구분</label> |
15 | 14 |
<select id="inputState" class="form-select"> |
... | ... | @@ -72,7 +71,7 @@ |
72 | 71 |
<input type="text" class="form-control" id="prvonsh" v-model="reason" /> |
73 | 72 |
</div> |
74 | 73 |
|
75 |
- <div class="text-center"> |
|
74 |
+ <div class="text-end"> |
|
76 | 75 |
<button type="submit" class="btn btn-primary">승인요청</button> |
77 | 76 |
<button type="reset" class="btn btn-secondary">취소</button> |
78 | 77 |
</div> |
--- client/views/pages/Employee/HyugaList.vue
+++ client/views/pages/Employee/HyugaList.vue
... | ... | @@ -8,25 +8,26 @@ |
8 | 8 |
<!-- 해당년도 연차 수 --> |
9 | 9 |
<!-- 전체 --> |
10 | 10 |
<div class="col-xxl-2 col-md-3"> |
11 |
- <button type="button" class="btn btn-light mb-2"> |
|
12 |
- 전체 <span class="badge bg-secondary text-light">12개</span> |
|
13 |
- </button> |
|
11 |
+ <button type="button" class="btn btn-secondary mb-2"> |
|
12 |
+ 전체 <span class="badge bg-white text-secondary">12개</span> |
|
13 |
+ </button> |
|
14 |
+ |
|
14 | 15 |
</div> |
15 | 16 |
<!-- End 전체 --> |
16 | 17 |
|
17 | 18 |
<!-- 사용 --> |
18 | 19 |
<div class="col-xxl-2 col-md-3"> |
19 |
- <button type="button" class="btn btn-light mb-2"> |
|
20 |
- 사용 <span class="badge bg-secondary text-light">1개</span> |
|
21 |
- </button> |
|
20 |
+ <button type="button" class="btn btn-success mb-2"> |
|
21 |
+ 사용 <span class="badge bg-white text-success">1개</span> |
|
22 |
+ </button> |
|
22 | 23 |
</div> |
23 | 24 |
<!-- End 사용 --> |
24 | 25 |
|
25 | 26 |
<!-- 미사용 --> |
26 | 27 |
<div class="col-xxl-2 col-xl-3"> |
27 |
- <button type="button" class="btn btn-light mb-2"> |
|
28 |
- 미사용 <span class="badge bg-secondary text-light">1개</span> |
|
29 |
- </button> |
|
28 |
+ <button type="button" class="btn btn-warning mb-2"> |
|
29 |
+ 미사용 <span class="badge bg-white text-warning">1개</span> |
|
30 |
+ </button> |
|
30 | 31 |
</div> |
31 | 32 |
<!-- End 미사용 --> |
32 | 33 |
|
+++ client/views/pages/Employee/ProjectInsert.vue
... | ... | @@ -0,0 +1,158 @@ |
1 | +<template> | |
2 | + <div class="pagetitle"> | |
3 | + <h1>프로젝트 등록</h1> | |
4 | + </div><!-- End Page Title --> | |
5 | + | |
6 | + <section class="section"> | |
7 | + <div class="card"> | |
8 | + <div class="card-body"> | |
9 | + | |
10 | + <!-- Multi Columns Form --> | |
11 | + <form class="row g-3 pt-3" @submit.prevent="handleSubmit"> | |
12 | + <div class="col-md-9"> | |
13 | + <label for="projectNm" class="form-label">프로젝트명</label> | |
14 | + <input type="text" class="form-control" id="projectNm" v-model="projectNm" /> | |
15 | + </div> | |
16 | + <div class="col-md-5"> | |
17 | + <label for="orgNm" class="form-label">주관사</label> | |
18 | + <input type="text" class="form-control" id="orgNm" v-model="orgNm" /> | |
19 | + </div> | |
20 | + <div class="col-md-5"> | |
21 | + <label for="inputName5" class="form-label">부서</label> | |
22 | + <select id="inputState" class="form-select"> | |
23 | + <option selected></option> | |
24 | + <option></option> | |
25 | + <option></option> | |
26 | + <option></option> | |
27 | + <option></option> | |
28 | + <option></option> | |
29 | + </select> | |
30 | + </div> | |
31 | + <div class="col-md-9"> | |
32 | + <label for="rm" class="form-label">사업금액</label> | |
33 | + <input type="text" class="form-control" id="rm" v-model="rm" /> | |
34 | + </div> | |
35 | + <div class="col-md-5"> | |
36 | + <label for="startDate" class="form-label">사업기간</label> | |
37 | + <div class="d-flex gap-1"> | |
38 | + <input type="date" class="form-control" id="BstartDate" v-model="BstartDate" />~ | |
39 | + <input type="date" class="form-control" id="BendDate" v-model="BendDate" /> | |
40 | + </div> | |
41 | + </div> | |
42 | + <div class="col-6"> | |
43 | + <label for="prvonsh" class="form-label">사업 투입인력</label> | |
44 | + <div class="search-bar d-flex gap-2"> | |
45 | + <form class="search-form d-flex align-items-center" method="POST" action="#" | |
46 | + @submit.prevent="updateMember"> | |
47 | + <input type="text" v-model="searchQuery" name="query" placeholder="Search" title="Enter search keyword"> | |
48 | + <button type="submit" title="Search"><i class="bi bi-search"></i></button> | |
49 | + </form> | |
50 | + <input type="text" class="select-member" :value="selectedMember.join(' ')" readonly> | |
51 | + </div> | |
52 | + </div> | |
53 | + <div class="col-md-5"> | |
54 | + <label for="endDate" class="form-label">실제 투입기간</label> | |
55 | + <div class="d-flex gap-1"> | |
56 | + <input type="date" class="form-control" id="MystartDate" v-model="MystartDate" />~ | |
57 | + <input type="date" class="form-control" id="MyendDate" v-model="MyendDate" /> | |
58 | + | |
59 | + </div> | |
60 | + </div> | |
61 | + <div class="col-6"> | |
62 | + <label for="prvonsh" class="form-label">실제 투입인력</label> | |
63 | + <div class="search-bar d-flex gap-2"> | |
64 | + <form class="search-form d-flex align-items-center" method="POST" action="#" | |
65 | + @submit.prevent="updateMember"> | |
66 | + <input type="text" v-model="searchQuery" name="query" placeholder="Search" title="Enter search keyword"> | |
67 | + <button type="submit" title="Search"><i class="bi bi-search"></i></button> | |
68 | + </form> | |
69 | + <input type="text" class="select-member" :value="selectedMember.join(' ')" readonly> | |
70 | + </div> | |
71 | + </div> | |
72 | + <div class="col-md-4"> | |
73 | + <label for="totalDays" class="form-label">총 투입일<span class="small"></span></label> | |
74 | + <input type="text" class="form-control" id="totalDays" v-model="totalDays" readonly /> | |
75 | + </div> | |
76 | + | |
77 | + <div class="col-12"> | |
78 | + <label for="rm" class="form-label">비고</label> | |
79 | + <input type="text" class="form-control" id="rm" v-model="rm" /> | |
80 | + </div> | |
81 | + | |
82 | + <div class="text-end"> | |
83 | + <button type="submit" class="btn btn-primary">등록</button> | |
84 | + <button type="reset" class="btn btn-secondary">취소</button> | |
85 | + </div> | |
86 | + </form><!-- End Multi Columns Form --> | |
87 | + | |
88 | + </div> | |
89 | + </div> | |
90 | + </section> | |
91 | +</template> | |
92 | + | |
93 | +<script> | |
94 | +import { useAuthStore } from '../../stores/authStore'; | |
95 | +export default { | |
96 | + data() { | |
97 | + const today = new Date().toISOString().split('T')[0]; | |
98 | + return { | |
99 | + searchQuery: '', | |
100 | + selectedMember: [], | |
101 | + BstartDate: today, | |
102 | + MystartDate: today, | |
103 | + totalDays: 0, // 사용 휴가일 계산 | |
104 | + reason: "", // 사유 | |
105 | + }; | |
106 | + }, | |
107 | + computed: { | |
108 | + // Pinia Store의 상태를 가져옵니다. | |
109 | + loginUser() { | |
110 | + const authStore = useAuthStore(); | |
111 | + return authStore.getLoginUser; | |
112 | + }, | |
113 | + }, | |
114 | + methods: { | |
115 | + updateMember() { | |
116 | + // Add the search query to the selectedMembers array if it's not empty | |
117 | + if (this.searchQuery.trim()) { | |
118 | + this.selectedMember.push(this.searchQuery.trim()); | |
119 | + } | |
120 | + // Clear the search query after adding it to selectedMembers | |
121 | + this.searchQuery = ''; | |
122 | + }, | |
123 | + // 폼 검증 메서드 | |
124 | + validateForm() { | |
125 | + // 필수 입력 필드 체크 | |
126 | + if ( | |
127 | + this.BstartDate && | |
128 | + this.BendDate && | |
129 | + this.MystartDate && | |
130 | + this.MyendDate && | |
131 | + this.totalDays > 0 | |
132 | + ) { | |
133 | + this.isFormValid = true; | |
134 | + } else { | |
135 | + this.isFormValid = false; | |
136 | + } | |
137 | + }, | |
138 | + | |
139 | + handleSubmit() { | |
140 | + this.validateForm(); // 제출 시 유효성 확인 | |
141 | + if (this.isFormValid) { | |
142 | + alert("승인 요청이 완료되었습니다."); | |
143 | + // 추가 처리 로직 (API 요청 등) | |
144 | + } else { | |
145 | + alert("모든 필드를 올바르게 작성해주세요."); | |
146 | + } | |
147 | + }, | |
148 | + }, | |
149 | + watch: { | |
150 | + BstartDate: 'calculateDayCount', | |
151 | + BendDate: 'calculateDayCount', | |
152 | + MystartDate: 'calculateDayCount', | |
153 | + MyendDate: 'calculateDayCount', | |
154 | + rm: "validateForm", | |
155 | + }, | |
156 | + | |
157 | +}; | |
158 | +</script> |
--- client/views/pages/Employee/ProjectList.vue
+++ client/views/pages/Employee/ProjectList.vue
... | ... | @@ -1,14 +1,182 @@ |
1 | 1 |
<template> |
2 |
- <div class="services"> |
|
3 |
- <h1>Our Services</h1> |
|
4 |
- <p>We provide web development and design services.</p> |
|
5 |
- </div> |
|
6 |
- </template> |
|
7 |
- |
|
8 |
- <script> |
|
9 |
- </script> |
|
10 |
- |
|
11 |
- <style scoped> |
|
2 |
+ <div class="pagetitle"> |
|
3 |
+ <h1>프로젝트 내역</h1> |
|
4 |
+ </div> |
|
5 |
+ <!-- End Page Title --> |
|
6 |
+ <section class="section"> |
|
7 |
+ <div class="row"> |
|
8 |
+ <!-- 해당년도 연차 수 --> |
|
9 |
+ <!-- 전체 --> |
|
10 |
+ <div class="col-xxl-2 col-md-3"> |
|
11 |
+ <button type="button" class="btn btn-light mb-2"> |
|
12 |
+ 전체 <span class="badge bg-secondary text-light">{{ filteredData.length }}개</span> |
|
13 |
+ </button> |
|
14 |
+ </div> |
|
15 |
+ <!-- End 전체 --> |
|
12 | 16 |
|
13 |
- </style> |
|
14 |
-(파일 끝에 줄바꿈 문자 없음) |
|
17 |
+ <!-- 사용 --> |
|
18 |
+ <div class="col-xxl-2 col-md-3"> |
|
19 |
+ <button type="button" class="btn btn-light mb-2"> |
|
20 |
+ 사용 <span class="badge bg-secondary text-light">{{ filteredData.filter(item => item.acceptTerms).length }}개</span> |
|
21 |
+ </button> |
|
22 |
+ </div> |
|
23 |
+ <!-- End 사용 --> |
|
24 |
+ |
|
25 |
+ <!-- 미사용 --> |
|
26 |
+ <div class="col-xxl-2 col-xl-3"> |
|
27 |
+ <button type="button" class="btn btn-light mb-2"> |
|
28 |
+ 미사용 <span class="badge bg-secondary text-light">{{ filteredData.filter(item => !item.acceptTerms).length }}개</span> |
|
29 |
+ </button> |
|
30 |
+ </div> |
|
31 |
+ <!-- End 미사용 --> |
|
32 |
+ |
|
33 |
+ <div class="col-lg-12"> |
|
34 |
+ <div class="card"> |
|
35 |
+ <div class="card-body"> |
|
36 |
+ <h5 class="card-title">프로젝트 내역</h5> |
|
37 |
+ <div class="d-flex pb-3 justify-content-between"> |
|
38 |
+ |
|
39 |
+ <div class="datatable-search d-flex gap-1 "> |
|
40 |
+ <div class="col-xxl-5 col-md-4"> |
|
41 |
+ <select class="form-select" v-model="selectedYear"> |
|
42 |
+ <option value="">전체</option> |
|
43 |
+ <option v-for="year in years" :key="year" :value="year">{{ year }}</option> |
|
44 |
+ </select> |
|
45 |
+ </div> |
|
46 |
+ <div class="col-xxl-5 col-md-4"> |
|
47 |
+ <select class="form-select" v-model="selectedMonth"> |
|
48 |
+ <option value="">월</option> |
|
49 |
+ <option v-for="month in months" :key="month" :value="month">{{ month }}</option> |
|
50 |
+ </select> |
|
51 |
+ </div> |
|
52 |
+ <button type="button" class="btn btn-outline-secondary col-xxl-5 col-xl-4" |
|
53 |
+ @click="filterData">조회</button> |
|
54 |
+ |
|
55 |
+ </div> |
|
56 |
+ <div class="d-flex justify-content-end "> |
|
57 |
+ <button type="button" class="btn btn-outline-success"> |
|
58 |
+ 수정 |
|
59 |
+ </button> |
|
60 |
+ <button type="button" class="btn btn-outline-danger" @click="deletePending"> |
|
61 |
+ 삭제 |
|
62 |
+ </button> |
|
63 |
+ </div> |
|
64 |
+ |
|
65 |
+ </div> |
|
66 |
+ <!-- Table --> |
|
67 |
+ <table id="myTable" class="table datatable table-hover table-bordered"> |
|
68 |
+ <!-- 동적으로 <th> 생성 --> |
|
69 |
+ <thead> |
|
70 |
+ <tr> |
|
71 |
+ <th rowspan="2">No</th> |
|
72 |
+ <th rowspan="2">프로젝트명</th> |
|
73 |
+ <th rowspan="2">주관</th> |
|
74 |
+ <th colspan="4" class="text-center">기간</th> |
|
75 |
+ <th rowspan="2">총 투입일</th> |
|
76 |
+ <th rowspan="2">비고</th> |
|
77 |
+ </tr> |
|
78 |
+ <tr> |
|
79 |
+ <th data-type="date" data-format="YYYY/DD/MM">사업시작</th> |
|
80 |
+ <th data-type="date" data-format="YYYY/DD/MM">사업종료</th> |
|
81 |
+ <th data-type="date" data-format="YYYY/DD/MM">투입시작</th> |
|
82 |
+ <th data-type="date" data-format="YYYY/DD/MM">투입종료</th> |
|
83 |
+ </tr> |
|
84 |
+ </thead> |
|
85 |
+ <!-- 동적으로 <td> 생성 --> |
|
86 |
+ <tbody> |
|
87 |
+ <tr v-for="(item, index) in filteredData" :key="index"> |
|
88 |
+ <td> |
|
89 |
+ <div class="form-check"> |
|
90 |
+ <label class="form-check-label" for="acceptTerms">{{ index + 1 }}</label> |
|
91 |
+ <input v-model="item.acceptTerms" class="form-check-input" type="checkbox" /> |
|
92 |
+ </div> |
|
93 |
+ </td> |
|
94 |
+ <td>{{ item.projectNm }}</td> |
|
95 |
+ <td>{{ item.orgNm }}</td> |
|
96 |
+ <td>{{ item.BstartDate }}</td> |
|
97 |
+ <td>{{ item.BendDate }}</td> |
|
98 |
+ <td>{{ item.MystartDate }}</td> |
|
99 |
+ <td>{{ item.MyendDate }}</td> |
|
100 |
+ <td>{{ item.totalDays }}</td> |
|
101 |
+ <td>{{ item.rm }}</td> |
|
102 |
+ </tr> |
|
103 |
+ </tbody> |
|
104 |
+ </table> |
|
105 |
+ <!-- End Table --> |
|
106 |
+ </div> |
|
107 |
+ </div> |
|
108 |
+ </div> |
|
109 |
+ </div> |
|
110 |
+ </section> |
|
111 |
+</template> |
|
112 |
+ |
|
113 |
+<script> |
|
114 |
+export default { |
|
115 |
+ data() { |
|
116 |
+ return { |
|
117 |
+ years: [2023, 2024, 2025], |
|
118 |
+ months: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], |
|
119 |
+ selectedYear: '', |
|
120 |
+ selectedMonth: '', |
|
121 |
+ ProjectData: [ |
|
122 |
+ { projectNm: '프로젝트명', orgNm: '기관명', BstartDate: '2025-01-03', BendDate: '2025-01-01', MystartDate: '2025-01-03', MyendDate: '2025-01-05', totalDays: 0, rm: '비고', acceptTerms: false }, |
|
123 |
+ { projectNm: '프로젝트명', orgNm: '기관명', BstartDate: '2024-01-03', BendDate: '2024-01-01', MystartDate: '2024-01-03', MyendDate: '2024-01-07', totalDays: 0, rm: '비고', acceptTerms: false }, |
|
124 |
+ // 다른 항목들 추가 |
|
125 |
+ ], |
|
126 |
+ filteredData: [], |
|
127 |
+ }; |
|
128 |
+ }, |
|
129 |
+ methods: { |
|
130 |
+ deletePending() { |
|
131 |
+ const selectedItems = this.ProjectData.filter(item => item.acceptTerms); |
|
132 |
+ const nonDeletableItems = selectedItems.filter(item => item.approvalStatus === true); |
|
133 |
+ if (nonDeletableItems.length > 0) { |
|
134 |
+ alert("승인된 건은 삭제할 수 없습니다."); |
|
135 |
+ return; |
|
136 |
+ } |
|
137 |
+ if (selectedItems.length > 0) { |
|
138 |
+ this.ProjectData = this.ProjectData.filter(item => !item.acceptTerms); |
|
139 |
+ alert(`${selectedItems.length}개의 항목이 삭제되었습니다.`); |
|
140 |
+ } else { |
|
141 |
+ alert("선택된 항목이 없습니다."); |
|
142 |
+ } |
|
143 |
+ }, |
|
144 |
+ filterData() { |
|
145 |
+ if (!this.selectedYear && !this.selectedMonth) { |
|
146 |
+ this.filteredData = this.ProjectData; |
|
147 |
+ } else { |
|
148 |
+ this.filteredData = this.ProjectData.filter(item => { |
|
149 |
+ const itemYear = new Date(item.BstartDate).getFullYear(); |
|
150 |
+ const itemMonth = new Date(item.BstartDate).getMonth() + 1; |
|
151 |
+ |
|
152 |
+ return ( |
|
153 |
+ (!this.selectedYear || itemYear === parseInt(this.selectedYear)) && |
|
154 |
+ (!this.selectedMonth || itemMonth === parseInt(this.selectedMonth)) |
|
155 |
+ ); |
|
156 |
+ }); |
|
157 |
+ } |
|
158 |
+ this.updateTotalDays(); |
|
159 |
+ }, |
|
160 |
+ updateTotalDays() { |
|
161 |
+ // filteredData에서 각 항목의 totalDays 계산 |
|
162 |
+ this.filteredData.forEach(item => { |
|
163 |
+ const mystartDate = new Date(item.MystartDate); |
|
164 |
+ const myendDate = new Date(item.MyendDate); |
|
165 |
+ |
|
166 |
+ // 날짜 차이 계산 (밀리초 단위로 계산 후, 일수로 변환) |
|
167 |
+ const timeDiff = myendDate - mystartDate; |
|
168 |
+ const daysDiff = timeDiff / (1000 * 60 * 60 * 24); |
|
169 |
+ item.totalDays = daysDiff >= 0 ? daysDiff : 0; |
|
170 |
+ }); |
|
171 |
+ }, |
|
172 |
+ }, |
|
173 |
+ mounted() { |
|
174 |
+ const currentYear = new Date().getFullYear(); |
|
175 |
+ this.selectedYear = currentYear; |
|
176 |
+ this.filteredData = this.ProjectData.filter(item => new Date(item.BstartDate).getFullYear() === currentYear); |
|
177 |
+ this.updateTotalDays(); |
|
178 |
+ }, |
|
179 |
+}; |
|
180 |
+</script> |
|
181 |
+ |
|
182 |
+<style scoped></style> |
--- client/views/pages/Manager/NoticeList.vue
+++ client/views/pages/Manager/NoticeList.vue
... | ... | @@ -1,20 +1,20 @@ |
1 | 1 |
<template> |
2 |
- <div class="about"> |
|
3 |
- <h1>About Us</h1> |
|
4 |
- <p>Learn more about our company and mission.</p> |
|
5 |
- </div> |
|
2 |
+ <div class="pagetitle"> |
|
3 |
+ <h1>공지사항 등록</h1> |
|
4 |
+ </div> |
|
5 |
+ <section class="section"> |
|
6 |
+ <div class="row">ㅇㅇ</div> |
|
7 |
+ </section> |
|
8 |
+ |
|
6 | 9 |
</template> |
7 | 10 |
|
8 | 11 |
<script> |
9 | 12 |
export default { |
10 |
- name: 'AboutPage', |
|
13 |
+ components: { |
|
14 |
+ }, |
|
11 | 15 |
} |
12 | 16 |
</script> |
13 | 17 |
|
14 | 18 |
<style scoped> |
15 |
- .about { |
|
16 |
- text-align: center; |
|
17 |
- margin-top: 50px; |
|
18 |
- } |
|
19 | 19 |
</style> |
20 | 20 |
(파일 끝에 줄바꿈 문자 없음) |
--- webpack.config.js
+++ webpack.config.js
... | ... | @@ -1,3 +1,4 @@ |
1 |
+const path = require('path'); |
|
1 | 2 |
const { VueLoaderPlugin } = require("vue-loader"); |
2 | 3 |
|
3 | 4 |
const {PROJECT_NAME, BASE_DIR, SERVICE_STATUS} = require('./Global'); |
... | ... | @@ -39,4 +40,9 @@ |
39 | 40 |
path: `${BASE_DIR}/client/build`, // __dirname: webpack.config.js 파일이 위치한 경로 |
40 | 41 |
filename: 'bundle.js' |
41 | 42 |
}, |
43 |
+ resolve: { |
|
44 |
+ alias: { |
|
45 |
+ 'realgrid': path.resolve(__dirname, 'node_modules/realgrid/dist/realgrid.js'), |
|
46 |
+ }, |
|
47 |
+ }, |
|
42 | 48 |
}(파일 끝에 줄바꿈 문자 없음) |
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?