
--- Global.ts
+++ Global.ts
... | ... | @@ -3,8 +3,8 @@ |
3 | 3 |
const BASE_DIR = __dirname; |
4 | 4 |
const LOG_BASE_DIR = `${__dirname}/server/logs`; |
5 | 5 |
const SERVICE_STATUS = process.env.NODE_ENV;//development, production |
6 |
-const PORT = 80; |
|
7 |
-const API_SERVER_HOST = 'localhost:8080'; |
|
6 |
+const PORT = 8080; |
|
7 |
+const API_SERVER_HOST = 'localhost:8081'; |
|
8 | 8 |
|
9 | 9 |
module.exports = { |
10 | 10 |
PROJECT_NAME, |
--- client/resources/css/responsive.css
+++ client/resources/css/responsive.css
... | ... | @@ -1,0 +1,7 @@ |
1 |
+@media all and (max-width: 479px) { |
|
2 |
+ #root{width: fit-content;} |
|
3 |
+ .main-warp > div { |
|
4 |
+ display: block; |
|
5 |
+ } |
|
6 |
+ .header_menu nav{display: none;} |
|
7 |
+} |
--- client/views/pages/AppRouter.js
+++ client/views/pages/AppRouter.js
... | ... | @@ -14,6 +14,7 @@ |
14 | 14 |
import ChargeManagement from '../pages/SystemManagement/ChargeManagement.vue'; |
15 | 15 |
import DataRecord from '../pages/SystemManagement/DataRecord.vue'; |
16 | 16 |
import UserAddForm from '../pages/SystemManagement/UserAddForm.vue'; |
17 |
+import UserModifyForm from '../pages/SystemManagement/UserModifyForm.vue'; |
|
17 | 18 |
import ManagerAddForm from '../pages/SystemManagement/ManagerAddForm.vue'; |
18 | 19 |
import Mypage from '../pages/User/Mypage.vue'; |
19 | 20 |
import Login from '../pages/main/Login.vue'; |
... | ... | @@ -41,6 +42,7 @@ |
41 | 42 |
{ path: '/ChargeManagement.page', name: 'ChargeManagement', component: ChargeManagement}, |
42 | 43 |
{ path: '/DataRecord.page', name: 'DataRecord', component: DataRecord}, |
43 | 44 |
{ path: '/UserAddForm.page', name: 'UserAddForm', component: UserAddForm}, |
45 |
+ { path: '/UserModifyForm.page', name: 'UserModifyForm', component: UserModifyForm}, |
|
44 | 46 |
{ path: '/ManagerAddForm.page', name: 'ManagerAddForm', component: ManagerAddForm}, |
45 | 47 |
{ path: '/IdFind.page', name: 'IdFind', component: IdFind}, |
46 | 48 |
]; |
--- client/views/pages/RealtimeStatus/RealtimeStatus.vue
+++ client/views/pages/RealtimeStatus/RealtimeStatus.vue
... | ... | @@ -15,7 +15,10 @@ |
15 | 15 |
</li> |
16 | 16 |
</ul> |
17 | 17 |
</div> |
18 |
- <div class="search"> |
|
18 |
+ <button class="hide-on-mobile" @click="toggleVisibility"> |
|
19 |
+ Click Me |
|
20 |
+ </button> |
|
21 |
+ <div class="search" v-if="isButtonVisible"> |
|
19 | 22 |
<h2>지역설정</h2> |
20 | 23 |
<div class="box"> |
21 | 24 |
<div class="flex-between m-b"> |
... | ... | @@ -207,6 +210,29 @@ |
207 | 210 |
.right { |
208 | 211 |
width: -webkit-fill-available; |
209 | 212 |
} |
213 |
+.hide-on-mobile { |
|
214 |
+ display: none; |
|
215 |
+ } |
|
216 |
+@media all and (max-width: 768px) { |
|
217 |
+ .left .wrap{display: -webkit-box;} |
|
218 |
+ .slider { |
|
219 |
+ overflow-x: hidden; |
|
220 |
+ } |
|
221 |
+ .hide-on-mobile { |
|
222 |
+ display: block; |
|
223 |
+} |
|
224 |
+ .list { |
|
225 |
+ display: flex; |
|
226 |
+ flex-wrap: nowrap; |
|
227 |
+ padding: 0; |
|
228 |
+ margin: 0; |
|
229 |
+ list-style: none; |
|
230 |
+ transform: translateX(0); |
|
231 |
+ transition: transform 0.3s ease-in-out; |
|
232 |
+ width: 1800px; |
|
233 |
+ } |
|
234 |
+ .list li{} |
|
235 |
+} |
|
210 | 236 |
</style> |
211 | 237 |
<script> |
212 | 238 |
import MapPage from "../../component/MapPage.vue"; |
... | ... | @@ -214,6 +240,7 @@ |
214 | 240 |
export default { |
215 | 241 |
data() { |
216 | 242 |
return { |
243 |
+ isButtonVisible: true, |
|
217 | 244 |
tabs: [ |
218 | 245 |
{ |
219 | 246 |
id: 1, |
... | ... | @@ -240,6 +267,9 @@ |
240 | 267 |
Map: Map, |
241 | 268 |
}, |
242 | 269 |
methods: { |
270 |
+ toggleVisibility() { |
|
271 |
+ this.isButtonVisible = !this.isButtonVisible; |
|
272 |
+ }, |
|
243 | 273 |
changeTab(tabId) { |
244 | 274 |
this.activeTab = tabId; |
245 | 275 |
}, |
--- client/views/pages/SystemManagement/UserManagement.vue
+++ client/views/pages/SystemManagement/UserManagement.vue
... | ... | @@ -61,7 +61,7 @@ |
61 | 61 |
<th>비고</th> |
62 | 62 |
</tr> |
63 | 63 |
</thead> |
64 |
- <tbody> |
|
64 |
+ <tbody> |
|
65 | 65 |
<tr v-for="(item, idx) in userList" :key="idx" @click="userSelectOnePage(item)"> |
66 | 66 |
<td data-title="NO">{{ idx + 1 }}</td> |
67 | 67 |
<td data-title="ID">{{ item.user_id }}</td> |
... | ... | @@ -152,14 +152,15 @@ |
152 | 152 |
|
153 | 153 |
//사용자 상세조회 페이지 이동 |
154 | 154 |
userSelectOnePage: function(user) { |
155 |
- this.$router.push({ path: '/', query: {}}); |
|
155 |
+ this.$router.push({ path: '/UserModifyForm.page'}); |
|
156 | 156 |
}, |
157 | 157 |
|
158 | 158 |
//사용자 등록 페이지 이동 |
159 | 159 |
userInsertPage: function() { |
160 | 160 |
this.$router.push({path: '/UserAddForm.page'}); |
161 | 161 |
} |
162 |
- }, |
|
162 |
+ }, |
|
163 |
+ |
|
163 | 164 |
watch: {}, |
164 | 165 |
computed: {}, |
165 | 166 |
components: { |
+++ client/views/pages/SystemManagement/UserModifyForm.vue
... | ... | @@ -0,0 +1,410 @@ |
1 | +<template> | |
2 | + <div class="wrap"> | |
3 | + <div class="tab-wrap"> | |
4 | + <ul> | |
5 | + <li> | |
6 | + <a href="" class="tab active">사용자 관리</a> | |
7 | + </li> | |
8 | + <li> | |
9 | + <a href="" class="tab">접속기록 조회</a> | |
10 | + </li> | |
11 | + </ul> | |
12 | + </div> | |
13 | + <div> | |
14 | + <div> | |
15 | + <h2 class="page-title">사용자 등록</h2> | |
16 | + </div> | |
17 | + <hr class="margin"> | |
18 | + <div class="form-wrap"> | |
19 | + <div class="flex m-b"> | |
20 | + <span>아이디 : </span> | |
21 | + <input type="text" placeholder="ID" v-model="user.user_id"> | |
22 | + <button type="button" class="idchk green-btn" @click="idChcek">중복확인</button> | |
23 | + </div> | |
24 | + <div class="flex m-b"> | |
25 | + <span>비밀번호 : </span> | |
26 | + <input type="password" placeholder="Password" v-model="user.user_pw" @change="passwordSyncCheck()"> | |
27 | + </div> | |
28 | + <div class="flex m-b"> | |
29 | + <span>비밀번호 확인 : </span> | |
30 | + <input type="password" placeholder="Confirm Password" v-model="passwordCheck" @change="passwordSyncCheck()"> | |
31 | + </div> | |
32 | + <div v-if="passwordCheckFlag === true" style="color:chartreuse">비밀번호 일치</div> | |
33 | + <div v-else-if="passwordCheckFlag === false" style="color:red">비밀번호 불일치</div> | |
34 | + <div class="flex m-b"> | |
35 | + <span>이름 : </span> | |
36 | + <input type="text" placeholder="Name" v-model="user.user_nm"> | |
37 | + </div> | |
38 | + <div class="flex m-b"> | |
39 | + <span>전화번호 : </span> | |
40 | + <input type="text" placeholder=" '-' 을 제외하고 입력해주세요." maxlength="13" v-model="telno" @change="phoneCheck" @keyup="getPhoneMask(telno)"> | |
41 | + <p style="color:red" v-if="telno_boolean === false"> 전화번호 9~11자리를 입력해주세요.</p> | |
42 | + </div> | |
43 | + <div class="flex m-b"> | |
44 | + <span>이메일 : </span> | |
45 | + <input type="text" placeholder=" 예) qwer@naver.com" v-model="user.user_eml" @change="emailCheck"> | |
46 | + <p style="color:red" v-if="email_boolean === false"> 잘못된 이메일 형식입니다.</p> | |
47 | + </div> | |
48 | + <div class="flex m-b"> | |
49 | + <span>관리구역 : </span> | |
50 | + <select name="" id=""> | |
51 | + <option value="">시/도</option> | |
52 | + </select> | |
53 | + <select name="" id=""> | |
54 | + <option value="">시/군/구</option> | |
55 | + </select> | |
56 | + <select name="" id=""> | |
57 | + <option value="">읍/면/동</option> | |
58 | + </select> | |
59 | + </div> | |
60 | + <div class="flex m-b"> | |
61 | + <span>비고 : </span> | |
62 | + <input type="text" placeholder="참고사항을 입력해주세요." v-model="user.rm"> | |
63 | + </div> | |
64 | + <div class="flex m-b"> | |
65 | + <span>권한 : </span> | |
66 | + <label for="radio_1"> | |
67 | + <input type="radio" name="radio" id="radio_1" value="시청관리자" v-model="user.user_author"> | |
68 | + 시청관리자 | |
69 | + </label> | |
70 | + <label for="radio_2"> | |
71 | + <input type="radio" name="radio" id="radio_2" value="지자체관리자" v-model="user.user_author"> | |
72 | + 지자체관리자 | |
73 | + </label> | |
74 | + </div> | |
75 | + | |
76 | + <div class="btn-wrap"> | |
77 | + <button type="button" class="green-btn" @click="userInsertCheck">등록</button> | |
78 | + <router-link to="/UserManagement.page"> | |
79 | + <span class="btn-2 gray-btn">취소</span> | |
80 | + </router-link> | |
81 | + </div> | |
82 | + </div> | |
83 | + </div> | |
84 | + </div> | |
85 | + | |
86 | + <div class="modal-wrap" v-if="modal_insert == true"> | |
87 | + <div class="modal-bg"> | |
88 | + </div> | |
89 | + <div class="modal"> | |
90 | + <p>사용자를 <span class="txt-point">등록</span>하시겠습니까?</p> | |
91 | + <div class="btn-wrap"> | |
92 | + <button type="button" class="green-btn" @click="userInsert">확인</button> | |
93 | + <button type="button" class="gray-btn" @click="modal_insert = false">취소</button> | |
94 | + </div> | |
95 | + </div> | |
96 | + </div> | |
97 | +</template> | |
98 | + | |
99 | +<script> | |
100 | +import axios from 'axios'; | |
101 | +import COMMON_UTIL from '../../../resources/js/commonUtil.ts'; | |
102 | + | |
103 | +export default { | |
104 | + data: () => { | |
105 | + return { | |
106 | + user: { | |
107 | + user_id: null, | |
108 | + user_pw: null, | |
109 | + user_nm: null, | |
110 | + user_telno: null, | |
111 | + user_eml: null, | |
112 | + user_author: null, | |
113 | + rm: null | |
114 | + }, | |
115 | + passwordCheck: null, | |
116 | + passwordCheckFlag: null, | |
117 | + telno_boolean: true, | |
118 | + email_boolean: true, | |
119 | + idChcek_boolean: false, | |
120 | + telno: null, | |
121 | + modal_insert: false | |
122 | + | |
123 | + }; | |
124 | + }, | |
125 | + methods: { | |
126 | + //ID 중복 검사 | |
127 | + idChcek: function() { | |
128 | + const vm = this; | |
129 | + axios({ | |
130 | + url: '/user/userSelectOne.json', | |
131 | + method: 'post', | |
132 | + herders: { | |
133 | + 'Content-Type': "application/json; charset=UTF-8", | |
134 | + }, | |
135 | + data: vm.user | |
136 | + }).then( function (response) { | |
137 | + console.log("idCheck - response : ", response.data); | |
138 | + if(response.data != null) { | |
139 | + alert("중복된 ID 입니다."); | |
140 | + vm.idChcek_boolean = false; | |
141 | + return false; | |
142 | + } else { | |
143 | + alert("사용가능한 ID 입니다."); | |
144 | + vm.idChcek_boolean = true; | |
145 | + return true; | |
146 | + } | |
147 | + }).catch( function (error) { | |
148 | + console.log("idCheck - error : ", error); | |
149 | + alert("중복검사 오류, 다시 시도해주세요."); | |
150 | + vm.idChcek_boolean = false; | |
151 | + }); | |
152 | + }, | |
153 | + | |
154 | + //비밀번호 일치 여부 검사 | |
155 | + passwordSyncCheck: function () { | |
156 | + if (this.user.user_pw != this.passwordCheck && this.user.user_pw != "" && this.passwordCheck != "" && | |
157 | + this.user.user_pw != null && this.passwordCheck != null) { | |
158 | + this.passwordCheckFlag = false; | |
159 | + } else if (this.user.user_pw === this.passwordCheck && this.user.user_pw != "" && this.passwordCheck != ""&& | |
160 | + this.user.user_pw != null && this.passwordCheck != null) { | |
161 | + this.passwordCheckFlag = true; | |
162 | + } else if (this.user.user_pw === "" || this.passwordCheck === "") { | |
163 | + this.passwordCheckFlag = null; | |
164 | + } | |
165 | + }, | |
166 | + | |
167 | + //전화번호 입력 시 자동 '-' 삽입 | |
168 | + getPhoneMask: function(telNumber) { | |
169 | + var res = COMMON_UTIL.getMask(telNumber) | |
170 | + this.telno = res | |
171 | + //서버 전송 값에는 '-' 를 제외하고 숫자만 저장 | |
172 | + this.user.user_telno = this.telno.replace(/[^0-9]/g, '') | |
173 | + }, | |
174 | + | |
175 | + emailCheck: function() { | |
176 | + this.email_boolean = true; | |
177 | + if (COMMON_UTIL.checkEmail(this.user.user_eml) === false) this.email_boolean = false; | |
178 | + }, | |
179 | + | |
180 | + phoneCheck: function() { | |
181 | + this.telno_boolean = true; | |
182 | + if (COMMON_UTIL.checkPhone(this.user.user_telno) === false) this.telno_boolean = false; | |
183 | + }, | |
184 | + | |
185 | + // 등록버튼 클릭 시 빈칸 검사 | |
186 | + userInsertCheck: function() { | |
187 | + if (COMMON_UTIL.isEmpty(this.user.user_id) === false) { | |
188 | + alert('ID를 입력해주세요.'); | |
189 | + return false; | |
190 | + } | |
191 | + | |
192 | + if (this.idChcek_boolean === false) { | |
193 | + alert("ID중복검사를 완료해주세요.") | |
194 | + return false; | |
195 | + } | |
196 | + | |
197 | + if (COMMON_UTIL.isEmpty(this.user.user_pw) === false) { | |
198 | + alert('비밀번호를 입력해주세요.'); | |
199 | + return false; | |
200 | + } | |
201 | + | |
202 | + if (this.passwordCheckFlag === false || this.passwordCheckFlag === null) { | |
203 | + alert('비밀번호가 일치하지 않습니다.'); | |
204 | + return false; | |
205 | + } | |
206 | + | |
207 | + if (COMMON_UTIL.isEmpty(this.user.user_nm) === false) { | |
208 | + alert('이름을 입력해주세요.'); | |
209 | + return false; | |
210 | + } | |
211 | + | |
212 | + if (COMMON_UTIL.isEmpty(this.user.user_telno) === false) { | |
213 | + alert('전화번호를 입력해주세요.'); | |
214 | + return false; | |
215 | + } | |
216 | + | |
217 | + if (COMMON_UTIL.checkPhone(this.user.user_telno) === false) { | |
218 | + alert('전화번호 형식을 확인해주세요.'); | |
219 | + return false; | |
220 | + } | |
221 | + | |
222 | + if (COMMON_UTIL.isEmpty(this.user.user_eml) === false) { | |
223 | + alert('이메일을 입력해주세요.'); | |
224 | + return false; | |
225 | + } | |
226 | + | |
227 | + if (COMMON_UTIL.checkEmail(this.user.user_eml) === false) { | |
228 | + alert('이메일 형식을 확인해주세요.'); | |
229 | + return false; | |
230 | + } | |
231 | + | |
232 | + // if (COMMON_UTIL.isEmpty(this.user.) === false) { | |
233 | + // alert('관리구역을 선택해주세요.'); | |
234 | + // return false; | |
235 | + // } | |
236 | + | |
237 | + if (COMMON_UTIL.isEmpty(this.user.user_author) === false) { | |
238 | + alert('권한을 선택해주세요.'); | |
239 | + return false; | |
240 | + } | |
241 | + this.modal_insert = true | |
242 | + }, | |
243 | + | |
244 | + // 사용자 등록 | |
245 | + userInsert: function() { | |
246 | + const vm = this; | |
247 | + axios({ | |
248 | + url: '/user/userInsert.json', | |
249 | + method: 'post', | |
250 | + herders: { | |
251 | + 'Content-Type': "application/json; charset=UTF-8", | |
252 | + }, | |
253 | + data: vm.user | |
254 | + }).then(function (response) { | |
255 | + console.log("userInsert - response : ", response); | |
256 | + let result = response.data; | |
257 | + if (result > 0) { | |
258 | + alert("사용자 등록을 완료 하였습니다."); | |
259 | + vm.userSelectListPage(); | |
260 | + } else { | |
261 | + alert("등록 실패, 관리자에게 문의해주세요."); | |
262 | + vm.modal_1 = false; | |
263 | + } | |
264 | + }).catch(function (error) { | |
265 | + console.log("userInsert - error : ", error); | |
266 | + alert("사용자 등록 오류, 관리자에게 문의해주세요."); | |
267 | + vm.modal_1 = false; | |
268 | + }) | |
269 | + }, | |
270 | + | |
271 | + userSelectListPage: function () { | |
272 | + this.$router.push({ path : '/UserManagement.page'}); | |
273 | + }, | |
274 | + }, | |
275 | + watch: {}, | |
276 | + computed: {}, | |
277 | + mounted() { | |
278 | + console.log("Main4 mounted"); | |
279 | + }, | |
280 | +}; | |
281 | +</script> | |
282 | + | |
283 | +<style scoped> | |
284 | +.wrap { | |
285 | + width: 155rem; | |
286 | + margin: 100px auto; | |
287 | +} | |
288 | + | |
289 | +.tab-wrap { | |
290 | + margin-bottom: 30px; | |
291 | +} | |
292 | + | |
293 | +.tab-wrap ul { | |
294 | + display: flex; | |
295 | + gap: 2rem; | |
296 | +} | |
297 | + | |
298 | +.tab { | |
299 | + display: inline-block; | |
300 | + width: 20rem; | |
301 | + height: 3rem; | |
302 | + line-height: 3rem; | |
303 | + text-align: center; | |
304 | + border-radius: 5px; | |
305 | + background: #fff; | |
306 | + color: #949292; | |
307 | + border: 1px solid #949292; | |
308 | +} | |
309 | + | |
310 | +.tab.active { | |
311 | + background: #13833b; | |
312 | + color: #fff; | |
313 | + border-color: #13833b; | |
314 | +} | |
315 | + | |
316 | +h2.page-title { | |
317 | + font-size: 24px; | |
318 | +} | |
319 | + | |
320 | +hr.margin { | |
321 | + margin: 30px 0; | |
322 | +} | |
323 | + | |
324 | +.form-wrap { | |
325 | + width: 52rem; | |
326 | + margin: 0 auto; | |
327 | +} | |
328 | + | |
329 | +.idchk { | |
330 | + margin-left: 1rem; | |
331 | +} | |
332 | + | |
333 | +.btn-wrap { | |
334 | + margin-top: 30px; | |
335 | + text-align: center; | |
336 | +} | |
337 | + | |
338 | +.btn-2 { | |
339 | + display: inline-block; | |
340 | + padding: 0.3rem 2rem; | |
341 | + font-size: 13.333px; | |
342 | + color: #000; | |
343 | +} | |
344 | + | |
345 | +.btn-wrap>*:not(:last-child) { | |
346 | + margin-right: 2rem; | |
347 | +} | |
348 | + | |
349 | +.form-wrap .flex span { | |
350 | + display: inline-block; | |
351 | + width: 10rem; | |
352 | + padding: 0 5px; | |
353 | +} | |
354 | + | |
355 | +.form-wrap input:not([type="radio"]) { | |
356 | + min-width: 30rem; | |
357 | +} | |
358 | + | |
359 | +.form-wrap select { | |
360 | + min-width: 9rem; | |
361 | +} | |
362 | + | |
363 | +.form-wrap select:not(:last-child) { | |
364 | + margin-right: 1.5rem; | |
365 | +} | |
366 | + | |
367 | +.form-wrap input[type="radio"] { | |
368 | + vertical-align: middle; | |
369 | +} | |
370 | + | |
371 | +.form-wrap label:not(:last-child) { | |
372 | + margin-right: 2rem; | |
373 | +} | |
374 | + | |
375 | +.modal-wrap { | |
376 | + position: fixed; | |
377 | + top: 0; | |
378 | + left: 0; | |
379 | + right: 0; | |
380 | + bottom: 0; | |
381 | +} | |
382 | + | |
383 | +.modal-bg { | |
384 | + position: absolute; | |
385 | + width: 100%; | |
386 | + height: 100%; | |
387 | + background-color: rgba(0, 0, 0, .6); | |
388 | +} | |
389 | + | |
390 | +.modal { | |
391 | + position: absolute; | |
392 | + top: 50%; | |
393 | + left: 50%; | |
394 | + transform: translate(-50%, -50%); | |
395 | + max-width: 350px; | |
396 | + padding: 5rem; | |
397 | + z-index: 2; | |
398 | + background-color: #fff; | |
399 | + border-radius: 1rem; | |
400 | + text-align: center; | |
401 | +} | |
402 | + | |
403 | +.modal .btn-wrap { | |
404 | + margin-top: 15px; | |
405 | + text-align: center; | |
406 | +} | |
407 | +.txt-point { | |
408 | + color: #13833b; | |
409 | +} | |
410 | +</style>(No newline at end of file) |
--- package-lock.json
+++ package-lock.json
... | ... | @@ -1,5 +1,5 @@ |
1 | 1 |
{ |
2 |
- "name": "crosswalk", |
|
2 |
+ "name": "crosswalk-1", |
|
3 | 3 |
"lockfileVersion": 2, |
4 | 4 |
"requires": true, |
5 | 5 |
"packages": { |
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?