

20241230 하관우 front_end 최초커밋
@77e8f0e744f147eda30b5a6ae557b5d5e7b04cf1
+++ .gitignore
... | ... | @@ -0,0 +1,3 @@ |
1 | +client/build/ | |
2 | +server/logs/ | |
3 | +node_modules/(파일 끝에 줄바꿈 문자 없음) |
+++ .vscode/launch.json
... | ... | @@ -0,0 +1,15 @@ |
1 | +{ | |
2 | + // Use IntelliSense to learn about possible attributes. | |
3 | + // Hover to view descriptions of existing attributes. | |
4 | + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | |
5 | + "version": "0.2.0", | |
6 | + "configurations": [ | |
7 | + { | |
8 | + "type": "msedge", | |
9 | + "request": "launch", | |
10 | + "name": "Launch Edge against localhost", | |
11 | + "url": "http://localhost:8081", | |
12 | + "webRoot": "${workspaceFolder}" | |
13 | + } | |
14 | + ] | |
15 | +}(파일 끝에 줄바꿈 문자 없음) |
+++ Global.js
... | ... | @@ -0,0 +1,17 @@ |
1 | +const PROJECT_NAME = 'NodeJS Web Server Framework(Vue)'; | |
2 | +const PROJECT_VERSION = '1.0'; | |
3 | +const BASE_DIR = __dirname; | |
4 | +const LOG_BASE_DIR = `${__dirname}/server/logs`; | |
5 | +const SERVICE_STATUS = process.env.NODE_ENV; // development, production | |
6 | +const PORT = 8080; | |
7 | +const API_SERVER_HOST = "localhost:9090"; | |
8 | + | |
9 | +module.exports = { | |
10 | + PROJECT_NAME, | |
11 | + PROJECT_VERSION, | |
12 | + BASE_DIR, | |
13 | + LOG_BASE_DIR, | |
14 | + SERVICE_STATUS, | |
15 | + PORT, | |
16 | + API_SERVER_HOST | |
17 | +}(파일 끝에 줄바꿈 문자 없음) |
+++ client/resources/css/common.css
... | ... | @@ -0,0 +1,905 @@ |
1 | +@charset "utf-8"; | |
2 | + | |
3 | +:root { | |
4 | + /* color */ | |
5 | + --color-black: #3f454d; | |
6 | + --color-gray: #c6c6c6; | |
7 | + --color-white: #ffffff; | |
8 | + --color-orange: #fbbe28; | |
9 | + --color-light-orange: #fffcf2; | |
10 | + --color-green: #13833b; | |
11 | + --color-red: #ff5d5d; | |
12 | + --color-blue: #213f99; | |
13 | + --color-darkG: #434343; | |
14 | + --color-blueE: #e3ecff; | |
15 | + --color-skyBlue: #f5f8ff; | |
16 | +} | |
17 | + | |
18 | +/* 정렬 */ | |
19 | +.flex { | |
20 | + display: flex; | |
21 | + flex-wrap: wrap; | |
22 | +} | |
23 | + | |
24 | +.flex-column { | |
25 | + display: flex; | |
26 | + flex-direction: column; | |
27 | + height: 100%; | |
28 | +} | |
29 | + | |
30 | + | |
31 | +.justify-start { | |
32 | + justify-content: flex-start; | |
33 | +} | |
34 | + | |
35 | +.justify-center { | |
36 | + justify-content: center; | |
37 | +} | |
38 | + | |
39 | +.justify-between { | |
40 | + justify-content: space-between; | |
41 | +} | |
42 | + | |
43 | +.justify-around { | |
44 | + justify-content: space-around; | |
45 | +} | |
46 | + | |
47 | +.justify-end { | |
48 | + justify-content: flex-end; | |
49 | +} | |
50 | + | |
51 | +.align-start { | |
52 | + align-items: flex-start; | |
53 | +} | |
54 | + | |
55 | +.align-center { | |
56 | + align-items: center; | |
57 | +} | |
58 | + | |
59 | +.align-end { | |
60 | + align-items: flex-end; | |
61 | +} | |
62 | + | |
63 | + | |
64 | +.flex5, | |
65 | +.flex10, | |
66 | +.flex15, | |
67 | +.flex20, | |
68 | +.flex25, | |
69 | +.flex30, | |
70 | +.flex35, | |
71 | +.flex40, | |
72 | +.flex45, | |
73 | +.flex50, | |
74 | +.flex55, | |
75 | +.flex60, | |
76 | +.flex65, | |
77 | +.flex70, | |
78 | +.flex75, | |
79 | +.flex80, | |
80 | +.flex85, | |
81 | +.flex90, | |
82 | +.flex95, | |
83 | +.flex100 { | |
84 | + padding-left: 10px; | |
85 | + padding-right: 10px; | |
86 | +} | |
87 | + | |
88 | +.no-gutter>.flex5, | |
89 | +.no-gutter>.flex10, | |
90 | +.no-gutter>.flex15, | |
91 | +.no-gutter>.flex20, | |
92 | +.no-gutter>.flex25, | |
93 | +.no-gutter>.flex30, | |
94 | +.no-gutter>.flex35, | |
95 | +.no-gutter>.flex40, | |
96 | +.no-gutter>.flex45, | |
97 | +.no-gutter>.flex50, | |
98 | +.no-gutter>.flex55, | |
99 | +.no-gutter>.flex60, | |
100 | +.no-gutter>.flex65, | |
101 | +.no-gutter>.flex70, | |
102 | +.no-gutter>.flex75, | |
103 | +.no-gutter>.flex80, | |
104 | +.no-gutter>.flex85, | |
105 | +.no-gutter>.flex90, | |
106 | +.no-gutter>.flex95, | |
107 | +.no-gutter>.flex100 { | |
108 | + padding-left: 0; | |
109 | + padding-right: 0; | |
110 | +} | |
111 | + | |
112 | +/* 정렬 배율 */ | |
113 | +.flex5 { | |
114 | + flex: 0 0 5% | |
115 | +} | |
116 | + | |
117 | +.flex10 { | |
118 | + flex: 0 0 10% | |
119 | +} | |
120 | + | |
121 | +.flex15 { | |
122 | + flex: 0 0 15% | |
123 | +} | |
124 | + | |
125 | +.flex20 { | |
126 | + flex: 0 0 20% | |
127 | +} | |
128 | + | |
129 | +.flex25 { | |
130 | + flex: 0 0 25% | |
131 | +} | |
132 | + | |
133 | +.flex30 { | |
134 | + flex: 0 0 30% | |
135 | +} | |
136 | + | |
137 | +.flex35 { | |
138 | + flex: 0 0 35% | |
139 | +} | |
140 | + | |
141 | +.flex40 { | |
142 | + flex: 0 0 40% | |
143 | +} | |
144 | + | |
145 | +.flex45 { | |
146 | + flex: 0 0 45% | |
147 | +} | |
148 | + | |
149 | +.flex50 { | |
150 | + flex: 0 0 50% | |
151 | +} | |
152 | + | |
153 | +.flex55 { | |
154 | + flex: 0 0 55% | |
155 | +} | |
156 | + | |
157 | +.flex60 { | |
158 | + flex: 0 0 60% | |
159 | +} | |
160 | + | |
161 | +.flex65 { | |
162 | + flex: 0 0 65% | |
163 | +} | |
164 | + | |
165 | +.flex70 { | |
166 | + flex: 0 0 70% | |
167 | +} | |
168 | + | |
169 | +.flex75 { | |
170 | + flex: 0 0 75% | |
171 | +} | |
172 | + | |
173 | +.flex80 { | |
174 | + flex: 0 0 80% | |
175 | +} | |
176 | + | |
177 | +.flex85 { | |
178 | + flex: 0 0 85% | |
179 | +} | |
180 | + | |
181 | + | |
182 | +.flex90 { | |
183 | + flex: 0 0 90% | |
184 | +} | |
185 | + | |
186 | +.flex95 { | |
187 | + flex: 0 0 95% | |
188 | +} | |
189 | + | |
190 | +.flex100 { | |
191 | + flex: 0 0 100%; | |
192 | +} | |
193 | + | |
194 | +/* 너비 */ | |
195 | +.w10 { | |
196 | + width: 10%; | |
197 | +} | |
198 | + | |
199 | +.w20 { | |
200 | + width: 20%; | |
201 | +} | |
202 | + | |
203 | +.w30 { | |
204 | + width: 30%; | |
205 | +} | |
206 | + | |
207 | +.w40 { | |
208 | + width: 40%; | |
209 | +} | |
210 | + | |
211 | +.w50 { | |
212 | + width: 50%; | |
213 | +} | |
214 | + | |
215 | +.w60 { | |
216 | + width: 60%; | |
217 | +} | |
218 | + | |
219 | +.w70 { | |
220 | + width: 70%; | |
221 | +} | |
222 | + | |
223 | +.w80 { | |
224 | + width: 80%; | |
225 | +} | |
226 | + | |
227 | +.w90 { | |
228 | + width: 90%; | |
229 | +} | |
230 | + | |
231 | +.w100 { | |
232 | + width: 100%; | |
233 | +} | |
234 | + | |
235 | + | |
236 | + | |
237 | +/* 마진 */ | |
238 | +.ml0 { | |
239 | + margin-left: 0px; | |
240 | +} | |
241 | + | |
242 | +.ml5 { | |
243 | + margin-left: 5px; | |
244 | +} | |
245 | + | |
246 | +.ml10 { | |
247 | + margin-left: 10px; | |
248 | +} | |
249 | + | |
250 | +.ml20 { | |
251 | + margin-left: 20px; | |
252 | +} | |
253 | + | |
254 | +.ml30 { | |
255 | + margin-left: 30px; | |
256 | +} | |
257 | + | |
258 | +.ml40 { | |
259 | + margin-left: 40px; | |
260 | +} | |
261 | + | |
262 | +.ml50 { | |
263 | + margin-left: 50px; | |
264 | +} | |
265 | + | |
266 | +.ml60 { | |
267 | + margin-left: 60px; | |
268 | +} | |
269 | + | |
270 | +.ml70 { | |
271 | + margin-left: 70px; | |
272 | +} | |
273 | + | |
274 | +.ml80 { | |
275 | + margin-left: 80px; | |
276 | +} | |
277 | + | |
278 | +.ml90 { | |
279 | + margin-left: 90px; | |
280 | +} | |
281 | + | |
282 | +.ml100 { | |
283 | + margin-left: 100px; | |
284 | +} | |
285 | + | |
286 | +.mr0 { | |
287 | + margin-right: 0px; | |
288 | +} | |
289 | + | |
290 | +.mr5 { | |
291 | + margin-right: 5px; | |
292 | +} | |
293 | + | |
294 | +.mr10 { | |
295 | + margin-right: 10px; | |
296 | +} | |
297 | + | |
298 | +.mr20 { | |
299 | + margin-right: 20px; | |
300 | +} | |
301 | + | |
302 | +.mr30 { | |
303 | + margin-right: 30px; | |
304 | +} | |
305 | + | |
306 | +.mr40 { | |
307 | + margin-right: 40px; | |
308 | +} | |
309 | + | |
310 | +.mr50 { | |
311 | + margin-right: 50px; | |
312 | +} | |
313 | + | |
314 | +.mr60 { | |
315 | + margin-right: 60px; | |
316 | +} | |
317 | + | |
318 | +.mr70 { | |
319 | + margin-right: 70px; | |
320 | +} | |
321 | + | |
322 | +.mr80 { | |
323 | + margin-right: 80px; | |
324 | +} | |
325 | + | |
326 | +.mr90 { | |
327 | + margin-right: 90px; | |
328 | +} | |
329 | + | |
330 | +.mr100 { | |
331 | + margin-right: 100px; | |
332 | +} | |
333 | + | |
334 | +.mt0 { | |
335 | + margin-top: 0px; | |
336 | +} | |
337 | + | |
338 | +.mt5 { | |
339 | + margin-top: 5px; | |
340 | +} | |
341 | + | |
342 | +.mt10 { | |
343 | + margin-top: 10px; | |
344 | +} | |
345 | + | |
346 | +.mt20 { | |
347 | + margin-top: 20px; | |
348 | +} | |
349 | + | |
350 | +.mt30 { | |
351 | + margin-top: 30px; | |
352 | +} | |
353 | + | |
354 | +.mt40 { | |
355 | + margin-top: 40px; | |
356 | +} | |
357 | + | |
358 | +.mt50 { | |
359 | + margin-top: 50px; | |
360 | +} | |
361 | + | |
362 | +.mt60 { | |
363 | + margin-top: 60px; | |
364 | +} | |
365 | + | |
366 | +.mt70 { | |
367 | + margin-top: 70px; | |
368 | +} | |
369 | + | |
370 | +.mt80 { | |
371 | + margin-top: 80px; | |
372 | +} | |
373 | + | |
374 | +.mt90 { | |
375 | + margin-top: 90px; | |
376 | +} | |
377 | + | |
378 | +.mt100 { | |
379 | + margin-top: 100px; | |
380 | +} | |
381 | + | |
382 | +.mb0 { | |
383 | + margin-bottom: 0px; | |
384 | +} | |
385 | + | |
386 | +.mb5 { | |
387 | + margin-bottom: 5px; | |
388 | +} | |
389 | + | |
390 | +.mb10 { | |
391 | + margin-bottom: 10px; | |
392 | +} | |
393 | + | |
394 | +.mb20 { | |
395 | + margin-bottom: 20px; | |
396 | +} | |
397 | + | |
398 | +.mb30 { | |
399 | + margin-bottom: 30px; | |
400 | +} | |
401 | + | |
402 | +.mb40 { | |
403 | + margin-bottom: 40px; | |
404 | +} | |
405 | + | |
406 | +.mb50 { | |
407 | + margin-bottom: 50px; | |
408 | +} | |
409 | + | |
410 | +.mb60 { | |
411 | + margin-bottom: 60px; | |
412 | +} | |
413 | + | |
414 | +.mb70 { | |
415 | + margin-bottom: 70px; | |
416 | +} | |
417 | + | |
418 | +.mb80 { | |
419 | + margin-bottom: 80px; | |
420 | +} | |
421 | + | |
422 | +.mb90 { | |
423 | + margin-bottom: 90px; | |
424 | +} | |
425 | + | |
426 | +.mb100 { | |
427 | + margin-bottom: 100px; | |
428 | +} | |
429 | + | |
430 | +/* 패딩 */ | |
431 | +.pd0 { | |
432 | + padding: 0; | |
433 | +} | |
434 | + | |
435 | +.pd5 { | |
436 | + padding: 5px; | |
437 | +} | |
438 | + | |
439 | +.pd10 { | |
440 | + padding: 10px; | |
441 | +} | |
442 | + | |
443 | +.pd20 { | |
444 | + padding: 20px; | |
445 | +} | |
446 | + | |
447 | +.pd30 { | |
448 | + padding: 30px; | |
449 | +} | |
450 | + | |
451 | +.pd40 { | |
452 | + padding: 40px; | |
453 | +} | |
454 | + | |
455 | +.pd50 { | |
456 | + padding: 50px; | |
457 | +} | |
458 | + | |
459 | +.pd60 { | |
460 | + padding: 60px; | |
461 | +} | |
462 | + | |
463 | +.pd70 { | |
464 | + padding: 70px; | |
465 | +} | |
466 | + | |
467 | +.pd80 { | |
468 | + padding: 80px; | |
469 | +} | |
470 | + | |
471 | +.pd90 { | |
472 | + padding: 90px; | |
473 | +} | |
474 | + | |
475 | +.pd100 { | |
476 | + padding: 100px; | |
477 | +} | |
478 | + | |
479 | +.pt0 { | |
480 | + padding-top: 0; | |
481 | +} | |
482 | + | |
483 | +.pt5 { | |
484 | + padding-top: 5px; | |
485 | +} | |
486 | + | |
487 | +.pt10 { | |
488 | + padding-top: 10px; | |
489 | +} | |
490 | + | |
491 | +.pt20 { | |
492 | + padding-top: 20px; | |
493 | +} | |
494 | + | |
495 | +.pt30 { | |
496 | + padding-top: 30px; | |
497 | +} | |
498 | + | |
499 | +.pt40 { | |
500 | + padding-top: 40px; | |
501 | +} | |
502 | + | |
503 | +.pt50 { | |
504 | + padding-top: 50px; | |
505 | +} | |
506 | + | |
507 | +.pt60 { | |
508 | + padding-top: 60px; | |
509 | +} | |
510 | + | |
511 | +.pt70 { | |
512 | + padding-top: 70px; | |
513 | +} | |
514 | + | |
515 | +.pt80 { | |
516 | + padding-top: 80px; | |
517 | +} | |
518 | + | |
519 | +.pt90 { | |
520 | + padding-top: 90px; | |
521 | +} | |
522 | + | |
523 | +.pt100 { | |
524 | + padding-top: 100px; | |
525 | +} | |
526 | + | |
527 | +.pb0 { | |
528 | + padding-bottom: 0; | |
529 | +} | |
530 | + | |
531 | +.pb5 { | |
532 | + padding-bottom: 5px; | |
533 | +} | |
534 | + | |
535 | +.pb10 { | |
536 | + padding-bottom: 10px; | |
537 | +} | |
538 | + | |
539 | +.pb20 { | |
540 | + padding-bottom: 20px; | |
541 | +} | |
542 | + | |
543 | +.pb30 { | |
544 | + padding-bottom: 30px; | |
545 | +} | |
546 | + | |
547 | +.pb40 { | |
548 | + padding-bottom: 40px; | |
549 | +} | |
550 | + | |
551 | +.pb50 { | |
552 | + padding-bottom: 50px; | |
553 | +} | |
554 | + | |
555 | +.pb60 { | |
556 | + padding-bottom: 60px; | |
557 | +} | |
558 | + | |
559 | +.pb70 { | |
560 | + padding-bottom: 70px; | |
561 | +} | |
562 | + | |
563 | +.pb80 { | |
564 | + padding-bottom: 80px; | |
565 | +} | |
566 | + | |
567 | +.pb90 { | |
568 | + padding-bottom: 90px; | |
569 | +} | |
570 | + | |
571 | +.pb100 { | |
572 | + padding-bottom: 100px; | |
573 | +} | |
574 | + | |
575 | +.pl0 { | |
576 | + padding-left: 0; | |
577 | +} | |
578 | + | |
579 | +.pl5 { | |
580 | + padding-left: 5px; | |
581 | +} | |
582 | + | |
583 | +.pl10 { | |
584 | + padding-left: 10px; | |
585 | +} | |
586 | + | |
587 | +.pl20 { | |
588 | + padding-left: 20px; | |
589 | +} | |
590 | + | |
591 | +.pl30 { | |
592 | + padding-left: 30px; | |
593 | +} | |
594 | + | |
595 | +.pl40 { | |
596 | + padding-left: 40px; | |
597 | +} | |
598 | + | |
599 | +.pl50 { | |
600 | + padding-left: 50px; | |
601 | +} | |
602 | + | |
603 | +.pl60 { | |
604 | + padding-left: 60px; | |
605 | +} | |
606 | + | |
607 | +.pl70 { | |
608 | + padding-left: 70px; | |
609 | +} | |
610 | + | |
611 | +.pl80 { | |
612 | + padding-left: 80px; | |
613 | +} | |
614 | + | |
615 | +.pl90 { | |
616 | + padding-left: 90px; | |
617 | +} | |
618 | + | |
619 | +.pl100 { | |
620 | + padding-left: 100px; | |
621 | +} | |
622 | + | |
623 | + | |
624 | +.pr0 { | |
625 | + padding-right: 0; | |
626 | +} | |
627 | + | |
628 | +.pr5 { | |
629 | + padding-right: 5px; | |
630 | +} | |
631 | + | |
632 | +.pr10 { | |
633 | + padding-right: 10px; | |
634 | +} | |
635 | + | |
636 | +.pr20 { | |
637 | + padding-right: 20px; | |
638 | +} | |
639 | + | |
640 | +.pr30 { | |
641 | + padding-right: 30px; | |
642 | +} | |
643 | + | |
644 | +.pr40 { | |
645 | + padding-right: 40px; | |
646 | +} | |
647 | + | |
648 | +.pr50 { | |
649 | + padding-right: 50px; | |
650 | +} | |
651 | + | |
652 | +.pr60 { | |
653 | + padding-right: 60px; | |
654 | +} | |
655 | + | |
656 | +.pr70 { | |
657 | + padding-right: 70px; | |
658 | +} | |
659 | + | |
660 | +.pr80 { | |
661 | + padding-right: 80px; | |
662 | +} | |
663 | + | |
664 | +.pr90 { | |
665 | + padding-right: 90px; | |
666 | +} | |
667 | + | |
668 | +.pr100 { | |
669 | + padding-right: 100px; | |
670 | +} | |
671 | + | |
672 | +/* btn */ | |
673 | +.small-btn { | |
674 | + width: 120px; | |
675 | + padding: 5px 10px; | |
676 | + border-radius: 5px; | |
677 | +} | |
678 | + | |
679 | +.set-btn { | |
680 | + width: 50px; | |
681 | + padding: 5px 10px; | |
682 | + border-radius: 5px; | |
683 | +} | |
684 | + | |
685 | +.large-btn { | |
686 | + width: 100%; | |
687 | + padding: 5px 15px; | |
688 | + border-radius: 5px; | |
689 | +} | |
690 | + | |
691 | +.icon-btn { | |
692 | + padding: 5px; | |
693 | + border-radius: 50%; | |
694 | + | |
695 | +} | |
696 | + | |
697 | +.custom-toggle { | |
698 | + position: absolute; | |
699 | + bottom: 0; | |
700 | + background-color: var(--color-skyBlue); | |
701 | + margin-left: 0; | |
702 | + border: 1px solid #eee; | |
703 | + border-left: 0; | |
704 | +} | |
705 | + | |
706 | +.custom-toggle::after { | |
707 | + content: ""; | |
708 | + position: absolute; | |
709 | + left: 0; | |
710 | + width: 5px; | |
711 | + height: 5px; | |
712 | + background-color: var(--color-skyBlue); | |
713 | + bottom: 0; | |
714 | +} | |
715 | + | |
716 | +.logout-btn { | |
717 | + padding: 5px 10px; | |
718 | + color: #aaa; | |
719 | + position: relative; | |
720 | +} | |
721 | + | |
722 | +.logout-btn::before { | |
723 | + content: ""; | |
724 | + width: 1px; | |
725 | + height: 10px; | |
726 | + position: absolute; | |
727 | + top: 50%; | |
728 | + left: 0; | |
729 | + transform: translateY(-50%); | |
730 | + background-color: #aaa; | |
731 | +} | |
732 | + | |
733 | +.close-btn { | |
734 | + color: #d6def6; | |
735 | +} | |
736 | + | |
737 | +.attribute-btn { | |
738 | + position: relative; | |
739 | +} | |
740 | + | |
741 | +.blue-btn, | |
742 | +.blue-border-btn:hover { | |
743 | + background-color: var(--color-blue); | |
744 | + color: var(--color-white); | |
745 | + transition: all 0.3s ease-in-out; | |
746 | +} | |
747 | + | |
748 | +.red-btn, | |
749 | +.red-border-btn:hover { | |
750 | + background-color: var(--color-red); | |
751 | + color: var(--color-white); | |
752 | + transition: all 0.3s ease-in-out; | |
753 | +} | |
754 | + | |
755 | +.green-btn, | |
756 | +.green-border-btn:hover { | |
757 | + background-color: var(--color-green); | |
758 | + color: var(--color-white); | |
759 | + transition: all 0.3s ease-in-out; | |
760 | +} | |
761 | + | |
762 | +.orange-btn, | |
763 | +.orange-border-btn:hover { | |
764 | + background-color: var(--color-orange); | |
765 | + color: var(--color-white); | |
766 | + transition: all 0.3s ease-in-out; | |
767 | +} | |
768 | + | |
769 | +.darkg-btn, | |
770 | +.darkg-border-btn:hover { | |
771 | + background-color: var(--color-darkG); | |
772 | + color: var(--color-white); | |
773 | + transition: all 0.3s ease-in-out; | |
774 | +} | |
775 | + | |
776 | +.gray-btn, | |
777 | +.gray-border-btn:hover { | |
778 | + background-color: #eee; | |
779 | + color: #333; | |
780 | + transition: all 0.3s ease-in-out; | |
781 | +} | |
782 | + | |
783 | + | |
784 | +.blue-border-btn { | |
785 | + border: 1px solid var(--color-blue); | |
786 | + color: var(--color-blue); | |
787 | + background-color: var(--color-white); | |
788 | +} | |
789 | + | |
790 | +.red-border-btn { | |
791 | + border: 1px solid var(--color-red); | |
792 | + color: var(--color-red); | |
793 | + background-color: var(--color-white); | |
794 | +} | |
795 | + | |
796 | +.green-border-btn { | |
797 | + border: 1px solid var(--color-green); | |
798 | + color: var(--color-green); | |
799 | + background-color: var(--color-white); | |
800 | +} | |
801 | + | |
802 | +.orange-border-btn { | |
803 | + border: 1px solid var(--color-orange); | |
804 | + color: var(--color-orange); | |
805 | + background-color: #fff; | |
806 | +} | |
807 | + | |
808 | +.darkg-border-btn { | |
809 | + border: 1px solid #434343; | |
810 | + color: #434343; | |
811 | + background-color: var(--color-white); | |
812 | +} | |
813 | + | |
814 | +.gray-border-btn { | |
815 | + border: 1px solid #aaa; | |
816 | + color: #aaa; | |
817 | + background-color: var(--color-white); | |
818 | +} | |
819 | + | |
820 | +.tp-btn { | |
821 | + background-color: transparent; | |
822 | + width: 15px; | |
823 | + height: 15px; | |
824 | + margin-left: 10px; | |
825 | +} | |
826 | + | |
827 | +button:disabled { | |
828 | + background-color: #eee; | |
829 | + color: #333; | |
830 | +} | |
831 | + | |
832 | + | |
833 | +/* text 정렬 */ | |
834 | +.text-lf { | |
835 | + text-align: left; | |
836 | +} | |
837 | + | |
838 | +.text-ct { | |
839 | + text-align: center; | |
840 | +} | |
841 | + | |
842 | +.text-rg { | |
843 | + text-align: right; | |
844 | +} | |
845 | + | |
846 | +/* text color */ | |
847 | +.orange { | |
848 | + color: var(--color-orange); | |
849 | +} | |
850 | + | |
851 | +.green { | |
852 | + color: var(--color-green); | |
853 | +} | |
854 | + | |
855 | +.blue { | |
856 | + color: var(--color-blue); | |
857 | +} | |
858 | + | |
859 | +.red { | |
860 | + color: var(--color-red); | |
861 | +} | |
862 | + | |
863 | +.cursor { | |
864 | + cursor: pointer; | |
865 | +} | |
866 | + | |
867 | +/** 로딩 화면 */ | |
868 | +.loading-overlay { | |
869 | + position: fixed; | |
870 | + top: 0; | |
871 | + left: 0; | |
872 | + width: 100%; | |
873 | + height: 100%; | |
874 | + background-color: rgba(0, 0, 0, 0.5); /* 반투명 검은색 배경 */ | |
875 | + display: flex; | |
876 | + z-index: 100001; /* 다른 요소 위에 표시되도록 z-index를 높게 설정 (모달창이 100000라 1 더 높게 설정 */ | |
877 | +} | |
878 | +.loading-div { | |
879 | + position: absolute; | |
880 | + display: flex; | |
881 | + top: 50%; | |
882 | + left: 52%; | |
883 | + transform: translate(-50%, -50%); | |
884 | + color: white; | |
885 | + font-size: 32px; | |
886 | + height: 100px; | |
887 | + /* animation: bounce 0.1s infinite alternate; */ | |
888 | +} | |
889 | + | |
890 | +.loading-div .anima { | |
891 | + animation: bounce 1.2s infinite; | |
892 | +} | |
893 | + | |
894 | +/* 각 span에 대한 지연 시간 추가 */ | |
895 | +.loading-div span:nth-child(2) { animation-delay: 0.1s; } | |
896 | +.loading-div span:nth-child(3) { animation-delay: 0.4s; } | |
897 | +.loading-div span:nth-child(4) { animation-delay: 0.7s; } | |
898 | +@keyframes bounce { | |
899 | + 0%, 100% { | |
900 | + transform: translateY(0); | |
901 | + } | |
902 | + 50% { | |
903 | + transform: translateY(-15px); | |
904 | + } | |
905 | +}(파일 끝에 줄바꿈 문자 없음) |
+++ client/resources/css/component.css
... | ... | @@ -0,0 +1,805 @@ |
1 | +@charset "utf-8"; | |
2 | + | |
3 | +/* box 공통 */ | |
4 | +.container { | |
5 | + width: 100%; | |
6 | + height: 100%; | |
7 | +} | |
8 | + | |
9 | +.content-box { | |
10 | + width: 100%; | |
11 | + height: 100%; | |
12 | +} | |
13 | + | |
14 | +.w100{ | |
15 | + width: 100%; | |
16 | +} | |
17 | + | |
18 | +.left-content, | |
19 | +.right-content { | |
20 | + height: 100%; | |
21 | +} | |
22 | + | |
23 | +.content-wrap { | |
24 | + height: calc(100% - 47px); | |
25 | +} | |
26 | + | |
27 | +.content { | |
28 | + width: 100%; | |
29 | + overflow-y: auto; | |
30 | +} | |
31 | + | |
32 | +.content:last-child { | |
33 | + margin-bottom: 0; | |
34 | +} | |
35 | + | |
36 | +.content-zone { | |
37 | + height: calc(100% - 57px); | |
38 | +} | |
39 | + | |
40 | +.left-content, | |
41 | +.right-content, | |
42 | +.row, | |
43 | +.content { | |
44 | + padding: 10px 0; | |
45 | + border-radius: 10px; | |
46 | + background-color: #fff; | |
47 | + position: relative; | |
48 | +} | |
49 | + | |
50 | +.row { | |
51 | + padding: 0; | |
52 | +} | |
53 | + | |
54 | +.form-box { | |
55 | + background-color: #edf0ff; | |
56 | + border: 1px solid #dbe3fb; | |
57 | + padding: 15px; | |
58 | + border-radius: 5px; | |
59 | +} | |
60 | + | |
61 | + | |
62 | +.border { | |
63 | + border: 1px solid #eee; | |
64 | + border-radius: 5px; | |
65 | +} | |
66 | + | |
67 | +.border.active { | |
68 | + border: 1px solid var(--color-blue); | |
69 | + border-radius: 5px; | |
70 | +} | |
71 | + | |
72 | +.overflow-y { | |
73 | + overflow-y: auto; | |
74 | +} | |
75 | +.overflow-x { | |
76 | + overflow-x: auto; | |
77 | +} | |
78 | + | |
79 | +/* title 공통 */ | |
80 | +.page-titleZone { | |
81 | + margin-bottom: 10px; | |
82 | +} | |
83 | + | |
84 | +.main-title { | |
85 | + font-family: 'GmarketSansB'; | |
86 | + font-size: 2rem; | |
87 | + color: #213f99; | |
88 | +} | |
89 | + | |
90 | +.content-titleZone { | |
91 | + padding-bottom: 10px; | |
92 | + border-bottom: 1px solid #aaa; | |
93 | + margin-bottom: 10px; | |
94 | +} | |
95 | + | |
96 | +.content-titleZone2 { | |
97 | + margin-bottom: 10px; | |
98 | +} | |
99 | + | |
100 | +.box-title { | |
101 | + font-size: 1.6rem; | |
102 | + font-weight: bold; | |
103 | + position: relative; | |
104 | +} | |
105 | + | |
106 | +.content-titleZone2 > .box-title::before{ | |
107 | + content: ''; | |
108 | + width: 3px; | |
109 | + height: 100%; | |
110 | + position: absolute; | |
111 | + top: 0; | |
112 | + left: 0; | |
113 | + background-color: var(--color-blue); | |
114 | + margin-right: 1rem; | |
115 | +} | |
116 | + | |
117 | +.object-title { | |
118 | + font-size: 1.4rem; | |
119 | + font-weight: bold; | |
120 | + color: var(--color-blue); | |
121 | +} | |
122 | + | |
123 | +.detail-text{ | |
124 | + font-size: 1.3rem; | |
125 | + font-weight: 400; | |
126 | +} | |
127 | +.detail-bold{ | |
128 | + font-size: 1.3rem; | |
129 | + font-weight: 700; | |
130 | +} | |
131 | + | |
132 | + | |
133 | +/* 테이블 공통 */ | |
134 | +.table-zone { | |
135 | + padding: 15px 0; | |
136 | +} | |
137 | + | |
138 | + | |
139 | +.form-table, | |
140 | +.list-table2 { | |
141 | + border-bottom: 1px solid #bbb; | |
142 | +} | |
143 | +.sticky-table th, | |
144 | +.sticky-table td, | |
145 | +.list-table th, | |
146 | +.list-table td, | |
147 | +.list-table2 th, | |
148 | +.list-table2 td, | |
149 | +.form-table th { | |
150 | + text-align: center; | |
151 | +} | |
152 | + | |
153 | +.form-table th, | |
154 | +.form-table td, | |
155 | +.list-table2 td { | |
156 | + border-top: 1px solid #bbb; | |
157 | +} | |
158 | +.sticky-table thead th, | |
159 | +.list-table thead th, | |
160 | +.list-table2 thead th { | |
161 | + background-color: #dbe3fb; | |
162 | + color: #213f99; | |
163 | +} | |
164 | + | |
165 | +.list-table.orange thead tr, | |
166 | +.sticky-table.orange thead tr { | |
167 | + background-color: #f29600; | |
168 | + color: #fff; | |
169 | +} | |
170 | + | |
171 | +.list-table tbody tr, | |
172 | +.list-table2 tbody tr, | |
173 | +.sticky-table tbody tr { | |
174 | + cursor: pointer; | |
175 | +} | |
176 | + | |
177 | +.list-table tbody tr:hover, | |
178 | +.list-table2 tbody tr:hover, | |
179 | +.sticky-table tbody tr:hover { | |
180 | + background-color: var(--color-light-orange); | |
181 | +} | |
182 | + | |
183 | +.list-table tbody tr:nth-child(even) | |
184 | +/* .sticky-table tbody tr:nth-child(even) */ { | |
185 | + background-color: #f4f6ff; | |
186 | +} | |
187 | + | |
188 | +.list-table.orange tbody tr:nth-child(even), | |
189 | +.sticky-table.orange tbody tr:nth-child(even) { | |
190 | + background-color: #fff6e8; | |
191 | +} | |
192 | + | |
193 | +.form-table th { | |
194 | + color: #213f99; | |
195 | + text-align: center; | |
196 | + background-color: #dbe3fb; | |
197 | +} | |
198 | + | |
199 | +.sticky-table thead th{ | |
200 | + position: sticky; | |
201 | + top: 0; | |
202 | +} | |
203 | + | |
204 | +.form-table2 th, | |
205 | +.custom-subtitle { | |
206 | + color: #213f99; | |
207 | + /* text-align: center; */ | |
208 | + font-weight: 800; | |
209 | +} | |
210 | + | |
211 | +.option-table th { | |
212 | + color: #213f99; | |
213 | +} | |
214 | + | |
215 | +.list-info { | |
216 | + margin-bottom: 10px; | |
217 | +} | |
218 | + | |
219 | +.no-list { | |
220 | + text-align: center; | |
221 | + font-size: 1.3rem; | |
222 | + line-height: 120px; | |
223 | +} | |
224 | + | |
225 | +.modal-table { | |
226 | + border-bottom: 0; | |
227 | +} | |
228 | + | |
229 | +.modal-table td { | |
230 | + border-top: 0; | |
231 | +} | |
232 | + | |
233 | +/* 서치바 공통 */ | |
234 | +.searchbar-zone { | |
235 | + margin-bottom: 10px; | |
236 | +} | |
237 | + | |
238 | +/* 기본 서치바 */ | |
239 | +.search-square { | |
240 | + position: relative; | |
241 | + margin-left: 5px; | |
242 | + display: flex; | |
243 | + align-items: center; | |
244 | +} | |
245 | + | |
246 | +.square-date, | |
247 | +.square-select { | |
248 | + width: 150px; | |
249 | +} | |
250 | + | |
251 | +input[type="text"].square-input { | |
252 | + color: #646464; | |
253 | + padding: 6px 10px; | |
254 | + border-radius: 5px 0 0 5px; | |
255 | + width: 311px; | |
256 | + transition: all ease-in-out .5s; | |
257 | +} | |
258 | + | |
259 | +.square-input:hover, | |
260 | +.square-input:focus { | |
261 | + box-shadow: 0 0 1em #00000013; | |
262 | +} | |
263 | + | |
264 | +.square-input:focus { | |
265 | + outline: none; | |
266 | + background-color: #f0eeee; | |
267 | +} | |
268 | + | |
269 | +.square-input::-webkit-input-placeholder { | |
270 | + font-weight: 100; | |
271 | + color: #ccc; | |
272 | +} | |
273 | + | |
274 | + | |
275 | +.square-button { | |
276 | + margin-left: 0; | |
277 | + border-radius: 0 5px 5px 0; | |
278 | + padding: 2.5px 0; | |
279 | + /* border: none; | |
280 | + | |
281 | + position: absolute; | |
282 | + right: 5px; | |
283 | + top: 50%; | |
284 | + transform: translateY(-50%); */ | |
285 | +} | |
286 | + | |
287 | +.square-button:hover { | |
288 | + cursor: pointer; | |
289 | +} | |
290 | + | |
291 | +.square-icon { | |
292 | + color: #b4b4b4; | |
293 | +} | |
294 | + | |
295 | +input[type="text"].full-input, | |
296 | +input[type="password"].full-input, | |
297 | +.full-select { | |
298 | + width: 100%; | |
299 | +} | |
300 | + | |
301 | +input[type="text"].half-input, | |
302 | +input[type="password"].half-input, | |
303 | +.half-select { | |
304 | + width: 50%; | |
305 | +} | |
306 | + | |
307 | +/* 상세 서치바 */ | |
308 | +.search-bottom { | |
309 | + padding: 10px 0; | |
310 | +} | |
311 | + | |
312 | +.option-searchbar { | |
313 | + width: 75%; | |
314 | + margin: 0 auto; | |
315 | + padding: 15px; | |
316 | + background-color: #f8f9fe; | |
317 | + border-radius: 10px; | |
318 | +} | |
319 | + | |
320 | +.search-top { | |
321 | + padding: 15px 0; | |
322 | + border-bottom: 1px solid #aaa; | |
323 | +} | |
324 | + | |
325 | +/* radio css */ | |
326 | +.input-container label { | |
327 | + display: flex; | |
328 | + cursor: pointer; | |
329 | + font-weight: 500; | |
330 | + position: relative; | |
331 | + overflow: hidden; | |
332 | + margin-bottom: 3px; | |
333 | +} | |
334 | + | |
335 | +.input-container label input.custom-radiobox, | |
336 | +.input-container label input.custom-checkbox { | |
337 | + position: absolute; | |
338 | + left: -9999px; | |
339 | +} | |
340 | + | |
341 | +.input-container label input.custom-radiobox:checked+span { | |
342 | + background-color: #5b72b8; | |
343 | + color: #fff; | |
344 | +} | |
345 | + | |
346 | +.input-container label input.custom-checkbox:checked+span { | |
347 | + background-color: #f8bb59; | |
348 | + color: #fff; | |
349 | +} | |
350 | + | |
351 | +.input-container label input.custom-radiobox:checked+span:before { | |
352 | + box-shadow: inset 0 0 0 4px #213f99; | |
353 | +} | |
354 | + | |
355 | +.input-container label input.custom-checkbox:checked+span:before { | |
356 | + box-shadow: inset 0 0 0 4px #ff9d00; | |
357 | +} | |
358 | + | |
359 | +.input-container label span { | |
360 | + display: flex; | |
361 | + align-items: center; | |
362 | + padding: 3px 7px; | |
363 | + border-radius: 10px; | |
364 | + transition: 0.25s ease; | |
365 | + color: #333; | |
366 | +} | |
367 | + | |
368 | + | |
369 | +.input-container label.radio-label span:hover { | |
370 | + background-color: #d6d6e5; | |
371 | +} | |
372 | + | |
373 | +.input-container label.check-label span:hover { | |
374 | + background-color: #f4e3c2; | |
375 | +} | |
376 | + | |
377 | +.input-container label.radio-label span:before, | |
378 | +.input-container label.check-label span:before { | |
379 | + display: flex; | |
380 | + flex-shrink: 0; | |
381 | + content: ""; | |
382 | + background-color: #fff; | |
383 | + width: 15px; | |
384 | + height: 15px; | |
385 | + border-radius: 50%; | |
386 | + margin-right: 3px; | |
387 | + transition: 0.25s ease; | |
388 | + box-shadow: inset 0 0 0 1px #333; | |
389 | +} | |
390 | + | |
391 | +.input-container label.check-label span:before { | |
392 | + border-radius: 0%; | |
393 | +} | |
394 | + | |
395 | +/* 기타 공통 */ | |
396 | +.coupler { | |
397 | + font-size: 1.3rem; | |
398 | + margin-left: 5px; | |
399 | +} | |
400 | + | |
401 | +.option-title { | |
402 | + padding: 0 5px; | |
403 | + font-size: 1.3rem; | |
404 | + color: #213f99; | |
405 | + margin-left: 5px; | |
406 | +} | |
407 | + | |
408 | +.count-zone { | |
409 | + font-size: 1.3rem; | |
410 | +} | |
411 | + | |
412 | +.count-zone span { | |
413 | + font-weight: 900; | |
414 | + color: #213f99; | |
415 | +} | |
416 | + | |
417 | + | |
418 | +/* 모달 */ | |
419 | +.modal-wrapper { | |
420 | + background-color: rgba(0, 0, 0, 0.5); | |
421 | + position: fixed; | |
422 | + width: 100%; | |
423 | + height: 100%; | |
424 | + top: 0; | |
425 | + left: 0; | |
426 | + display: flex; | |
427 | + justify-content: center; | |
428 | + align-items: center; | |
429 | + z-index: 100000; | |
430 | +} | |
431 | + | |
432 | +.modal-container { | |
433 | + background: #fff; | |
434 | + min-width: 500px; | |
435 | + border-radius: 5px; | |
436 | + display: grid; | |
437 | + grid-template-rows: auto 1fr auto; | |
438 | + padding: 20px; | |
439 | + box-sizing: border-box; | |
440 | + max-height: 95%; | |
441 | + min-height: 500px; | |
442 | +} | |
443 | + | |
444 | +.modal-title { | |
445 | + width: 100%; | |
446 | + border-bottom: 1px solid #d4cccc; | |
447 | + padding: 0 0 10px 0; | |
448 | +} | |
449 | + | |
450 | +.modal-subtitle { | |
451 | + font-size: 1.3rem; | |
452 | + font-weight: 600; | |
453 | +} | |
454 | + | |
455 | +.modal-content-monthly { | |
456 | + width: 100%; | |
457 | + padding: 13px 0 0 0; | |
458 | + overflow-y: auto; | |
459 | + font-size: 13px; | |
460 | +} | |
461 | +.modal-content-monthly > p{font-size: 14px;} | |
462 | +.large-modal { | |
463 | + width: 90%; | |
464 | +} | |
465 | + | |
466 | +.small-modal { | |
467 | + min-width: 350px; | |
468 | + min-height: 200px; | |
469 | + /* max-width: 450px; | |
470 | + height: auto; | |
471 | + max-height: 50%; */ | |
472 | +} | |
473 | + | |
474 | +.list-modal { | |
475 | + width: 80%; | |
476 | + height: 80%; | |
477 | +} | |
478 | + | |
479 | +.alert-write { | |
480 | + font-size: 1.6rem; | |
481 | + line-height: 180%; | |
482 | +} | |
483 | + | |
484 | + | |
485 | +.modal-content-monthly::-webkit-scrollbar { | |
486 | + width: 10px; | |
487 | +} | |
488 | + | |
489 | +.modal-content-monthly::-webkit-scrollbar-thumb { | |
490 | + background-color: #6b6b6b; | |
491 | + border-radius: 10px; | |
492 | + background-clip: padding-box; | |
493 | + border: 2px solid transparent; | |
494 | +} | |
495 | + | |
496 | +.modal-content-monthly::-webkit-scrollbar-track { | |
497 | + background-color: #eee; | |
498 | + border-radius: 10px; | |
499 | + box-shadow: inset 0px 0px 5px white; | |
500 | +} | |
501 | + | |
502 | +.modal-end { | |
503 | + width: 100%; | |
504 | + padding: 15px 0 0 0; | |
505 | + border-top: 1px solid #eee; | |
506 | + gap: 20px; | |
507 | +} | |
508 | + | |
509 | +.alert-modal .modal-end button, | |
510 | +.small-modal .modal-end button { | |
511 | + margin-left: 0; | |
512 | +} | |
513 | + | |
514 | + | |
515 | +/* 탭 */ | |
516 | +.tab-nav { | |
517 | + border-top: 1px solid #eee; | |
518 | + border-bottom: 1px solid #eee; | |
519 | + padding: 10px 0; | |
520 | +} | |
521 | + | |
522 | +.tab-nav li a { | |
523 | + display: block; | |
524 | + font-size: 1.6rem; | |
525 | + text-align: center; | |
526 | + border-right: 1px solid #eee; | |
527 | + padding: 0 10px; | |
528 | +} | |
529 | + | |
530 | +.tab-nav li a.activeTab { | |
531 | + color: #213f99; | |
532 | + font-weight: 600; | |
533 | +} | |
534 | + | |
535 | +.tab-nav li:last-child a { | |
536 | + border-right: 0; | |
537 | +} | |
538 | + | |
539 | +.tab-nav2 { | |
540 | + border-bottom: 1px solid #eee; | |
541 | +} | |
542 | + | |
543 | +.tab-nav2 li a { | |
544 | + display: block; | |
545 | + font-size: 1.6rem; | |
546 | + text-align: center; | |
547 | + border: 1px solid #eee; | |
548 | + padding: 0 10px; | |
549 | + background-color: #f8f8f8; | |
550 | + padding: 10px; | |
551 | + border-radius: 5px 5px 0 0; | |
552 | + border-bottom: 5px solid #f8f8f8; | |
553 | +} | |
554 | + | |
555 | +.tab-nav2 li a.activeTab { | |
556 | + color: var(--color-blue); | |
557 | + font-weight: 600; | |
558 | + background-color: #fff; | |
559 | + border-bottom: 5px solid var(--color-blue); | |
560 | +} | |
561 | + | |
562 | +.column-nav { | |
563 | + height: 100%; | |
564 | + background-color: var(--color-blue); | |
565 | + padding: 10px 0; | |
566 | +} | |
567 | + | |
568 | +.column-nav ul li a { | |
569 | + display: block; | |
570 | + font-size: 1.1rem; | |
571 | + text-align: center; | |
572 | + padding: 10px; | |
573 | + color: var(--color-gray); | |
574 | +} | |
575 | + | |
576 | +.column-nav ul li a.activeTab { | |
577 | + color: var(--color-white); | |
578 | + font-weight: bold; | |
579 | +} | |
580 | + | |
581 | +.tab-content { | |
582 | + height: 100%; | |
583 | + overflow-y: auto; | |
584 | +} | |
585 | + | |
586 | +.tab-content2 { | |
587 | + height: calc(100% - 46px); | |
588 | +} | |
589 | + | |
590 | +.tab-content2 .content-box { | |
591 | + padding: 10px; | |
592 | +} | |
593 | + | |
594 | +.tabnav2>ul>li { | |
595 | + border: 1px solid var(--color-blue); | |
596 | + border-radius: 5px; | |
597 | + color: var(--color-blue); | |
598 | + background-color: var(--color-white); | |
599 | +} | |
600 | + | |
601 | +.tabnav2>ul>li p, | |
602 | +.tabnav3 > ul > li > p { | |
603 | + font-size: 1.3rem; | |
604 | + padding: 8px 10px; | |
605 | +} | |
606 | + | |
607 | +.tabnav2>ul>li p.activeTab2 { | |
608 | + background-color: var(--color-blue); | |
609 | + color: var(--color-white); | |
610 | +} | |
611 | + | |
612 | +.tab-content2 { | |
613 | + padding: 10px 0; | |
614 | +} | |
615 | + | |
616 | +.tabnav3{ | |
617 | + border-bottom: 1px solid var(--color-blue); | |
618 | +} | |
619 | +.tabnav3 > ul > li{ | |
620 | + border: 1px solid var(--color-blue); | |
621 | + border-bottom: 0; | |
622 | + border-radius: 5px 5px 0 0; | |
623 | + color: var(--color-blue); | |
624 | + background-color: var(--color-white); | |
625 | +} | |
626 | + | |
627 | +.tabnav3 > ul > li > p.activeOption { | |
628 | + background-color: var(--color-blue); | |
629 | + color: var(--color-white); | |
630 | + border-radius: 5px 5px 0 0; | |
631 | +} | |
632 | + | |
633 | + | |
634 | +/* 라벨 css(상세 조회랑 다름) */ | |
635 | +.chekck-type { | |
636 | + display: none; | |
637 | +} | |
638 | + | |
639 | +.chekcktype-label { | |
640 | + border-radius: 5px; | |
641 | + padding: 5px 10px; | |
642 | + background-color: #d6def6; | |
643 | + color: #213f99; | |
644 | +} | |
645 | + | |
646 | +.chekck-type:checked+label { | |
647 | + background-color: #213f99; | |
648 | + color: #fff; | |
649 | +} | |
650 | + | |
651 | + | |
652 | +/* file tree */ | |
653 | +.file-list { | |
654 | + display: block; | |
655 | + padding: 5px; | |
656 | + font-size: 1.3rem; | |
657 | +} | |
658 | + | |
659 | +.selected { | |
660 | + background-color: rgb(255, 249, 239); | |
661 | + border: 1px solid var(--color-orange); | |
662 | + color: var(--color-orange); | |
663 | + border-radius: 5px; | |
664 | +} | |
665 | + | |
666 | +.item { | |
667 | + background-color: var(--color-white); | |
668 | + border-radius: 5px; | |
669 | + padding: 5px; | |
670 | + width: 100%; | |
671 | + border: 1px solid #eee; | |
672 | +} | |
673 | + | |
674 | +.item>p { | |
675 | + font-size: 1.3rem; | |
676 | +} | |
677 | + | |
678 | + | |
679 | +/* 스타일 컴포넌트 */ | |
680 | +.color-picker { | |
681 | + width: 40px; | |
682 | + height: 40px; | |
683 | + background-color: #fff; | |
684 | + border-radius: 5px; | |
685 | + padding: 0 2px; | |
686 | + position: relative; | |
687 | +} | |
688 | + | |
689 | +.color-picker::after { | |
690 | + content: "+"; | |
691 | + position: absolute; | |
692 | + width: 20px; | |
693 | + height: 20px; | |
694 | + background-color: var(--color-white); | |
695 | + color: #6b6b6b; | |
696 | + border-radius: 20px; | |
697 | + top: 50%; | |
698 | + left: 50%; | |
699 | + transform: translate(-50%, -50%); | |
700 | + text-align: center; | |
701 | + font-size: 2rem; | |
702 | + line-height: 20px; | |
703 | +} | |
704 | + | |
705 | +.up, | |
706 | +.down { | |
707 | + border: 1px solid #aaa; | |
708 | + text-align: center; | |
709 | + padding: 1.5px 0; | |
710 | + background-color: var(--color-white); | |
711 | +} | |
712 | + | |
713 | +.up { | |
714 | + border-radius: 0 5px 5px 0; | |
715 | + border-left: none; | |
716 | +} | |
717 | + | |
718 | +.down { | |
719 | + border-radius: 5px 0 0 5px; | |
720 | + border-right: none; | |
721 | +} | |
722 | + | |
723 | +.up button, | |
724 | +.down button { | |
725 | + font-size: 2rem; | |
726 | +} | |
727 | + | |
728 | +input[type="text"].size-input { | |
729 | + border-radius: 0; | |
730 | + text-align: center; | |
731 | +} | |
732 | + | |
733 | +.color-list { | |
734 | + width: 30px; | |
735 | + height: 30px; | |
736 | + border-radius: 5px; | |
737 | +} | |
738 | + | |
739 | +.check-group:first-child{ | |
740 | + margin-left: 0; | |
741 | +} | |
742 | + | |
743 | +.style-label { | |
744 | + border: 1px solid #eee; | |
745 | + border-radius: 3px; | |
746 | + background-color: var(--color-white); | |
747 | +} | |
748 | + | |
749 | +.style-label>svg { | |
750 | + color: #aaa; | |
751 | +} | |
752 | + | |
753 | + | |
754 | +.style-input{ | |
755 | + display: none; | |
756 | +} | |
757 | +.style-input:checked + .style-label { | |
758 | + border: 1px solid var(--color-blue); | |
759 | +} | |
760 | +.style-input:checked + .style-label svg{ | |
761 | + color: var(--color-blue); | |
762 | +} | |
763 | + | |
764 | +/* 투명도 */ | |
765 | +.slider-container { | |
766 | + position: relative; | |
767 | +} | |
768 | + | |
769 | +.slider { | |
770 | + width: 100%; | |
771 | + background: linear-gradient(to right, red var(--slider-percentage), transparent var(--slider-percentage)); | |
772 | +} | |
773 | + | |
774 | +.slider::-webkit-slider-runnable-track { | |
775 | + background: linear-gradient(to right, red var(--slider-percentage), transparent var(--slider-percentage)); | |
776 | +} | |
777 | + | |
778 | +.component-title-zone { | |
779 | + display: table; | |
780 | +} | |
781 | + | |
782 | +.component-maintitle { | |
783 | + font-size: 1.8rem; | |
784 | + /* font-weight: bold; */ | |
785 | + position: relative; | |
786 | + padding-left: 10px; | |
787 | + margin-right: 10px; | |
788 | + | |
789 | +} | |
790 | + | |
791 | +.component-maintitle::before { | |
792 | + content: ""; | |
793 | + position: absolute; | |
794 | + width: 3px; | |
795 | + height: 100%; | |
796 | + background-color: var(--color-blue); | |
797 | + left: 0px; | |
798 | +} | |
799 | + | |
800 | +.component-subtitle { | |
801 | + font-size: 1.4rem; | |
802 | + color: #aaa; | |
803 | + display: table-cell; | |
804 | + vertical-align: middle; | |
805 | +}(파일 끝에 줄바꿈 문자 없음) |
+++ client/resources/css/font.css
... | ... | @@ -0,0 +1,27 @@ |
1 | +@font-face { | |
2 | + font-family: 'Pretendard'; | |
3 | + src: url('../font/PretendardVariable.woff2') format('woff'); | |
4 | + font-weight: 400; | |
5 | + font-style: normal; | |
6 | +} | |
7 | + | |
8 | +@font-face { | |
9 | + font-family: "GmarketSansM"; | |
10 | + src: url("/client/resources/font/GmarketSansMedium.woff") format("woff"); | |
11 | + font-weight: normal; | |
12 | + font-style: normal; | |
13 | +} | |
14 | + | |
15 | +@font-face { | |
16 | + font-family: "GmarketSansL"; | |
17 | + src: url("/client/resources/font/GmarketSansLight.woff") format("woff"); | |
18 | + font-weight: normal; | |
19 | + font-style: normal; | |
20 | +} | |
21 | + | |
22 | +@font-face { | |
23 | + font-family: "GmarketSansB"; | |
24 | + src: url("/client/resources/font/GmarketSansBold.woff") format("woff"); | |
25 | + font-weight: bold; | |
26 | + font-style: normal; | |
27 | +}(파일 끝에 줄바꿈 문자 없음) |
+++ client/resources/css/layout.css
... | ... | @@ -0,0 +1,199 @@ |
1 | +@charset "utf-8"; | |
2 | + | |
3 | +.dashboard-wrap { | |
4 | + width: 100%; | |
5 | + height: 100vh; | |
6 | + display: grid; | |
7 | + grid-template-columns: 270px minmax(auto, 1fr); | |
8 | + grid-template-rows: auto 1fr; | |
9 | + grid-template-areas: | |
10 | + "header header " | |
11 | + "nav main " | |
12 | + "nav main " | |
13 | +} | |
14 | + | |
15 | +.layout-wrap { | |
16 | + width: 100%; | |
17 | + min-height: 100vh; | |
18 | + position: relative; | |
19 | + | |
20 | +} | |
21 | + | |
22 | + | |
23 | +header { | |
24 | + background-color: #fff; | |
25 | + padding: 15px 30px; | |
26 | + grid-area: header; | |
27 | + position: relative; | |
28 | +} | |
29 | + | |
30 | +.logo { | |
31 | + width: 200px; | |
32 | +} | |
33 | + | |
34 | +.logo>a { | |
35 | + display: block; | |
36 | + width: 100%; | |
37 | +} | |
38 | + | |
39 | +.logo>a>img { | |
40 | + display: block; | |
41 | + width: 100%; | |
42 | +} | |
43 | + | |
44 | +.user-name { | |
45 | + font-size: 1.3rem; | |
46 | + margin-left: 5px; | |
47 | +} | |
48 | + | |
49 | +.sms, | |
50 | +.user { | |
51 | + margin-left: 10px; | |
52 | +} | |
53 | + | |
54 | +.layout-wrap header{ | |
55 | + position: absolute; | |
56 | + width: 100%; | |
57 | + top: 0; | |
58 | + left: 0; | |
59 | + z-index: 1; | |
60 | +} | |
61 | + | |
62 | + | |
63 | + | |
64 | +/* 메뉴 공통 */ | |
65 | +nav { | |
66 | + background-color: #213f99; | |
67 | + position: relative; | |
68 | +} | |
69 | + | |
70 | +nav ul li a, | |
71 | +nav ul li p { | |
72 | + padding: 5px; | |
73 | + display: block; | |
74 | + color: #fff; | |
75 | +} | |
76 | + | |
77 | +nav.side-menu ul.sub-menu, | |
78 | +nav.top-menu ul.sub-menu { | |
79 | + font-size: 1.3rem; | |
80 | + overflow: hidden; | |
81 | + transition: all 0.5s ease-in-out; | |
82 | +} | |
83 | + | |
84 | +ul.sub-menu>li { | |
85 | + padding: 10px 20px; | |
86 | + | |
87 | +} | |
88 | + | |
89 | +p.active { | |
90 | + background-color: #fff; | |
91 | + color: #213f99; | |
92 | + border-radius: 50px | |
93 | +} | |
94 | + | |
95 | +/* 사이드 메뉴 */ | |
96 | +nav.side-menu { | |
97 | + width: 100%; | |
98 | + height: 100vh; | |
99 | + border-radius: 0 70px 70px 0; | |
100 | + grid-area: nav; | |
101 | + overflow-y: auto; | |
102 | +} | |
103 | + | |
104 | +nav.side-menu::-webkit-scrollbar { | |
105 | + display: none; | |
106 | +} | |
107 | + | |
108 | +nav.side-menu::-ms-scrollbar { | |
109 | + display: none; | |
110 | +} | |
111 | + | |
112 | + | |
113 | +nav.side-menu>ul.main-menu { | |
114 | + padding: 50px 30px; | |
115 | +} | |
116 | + | |
117 | +nav.side-menu>ul.main-menu>li>div>a, | |
118 | +nav>ul>li { | |
119 | + padding: 10px 0; | |
120 | + font-size: 1.3rem; | |
121 | + font-weight: bold; | |
122 | +} | |
123 | + | |
124 | + | |
125 | + | |
126 | +/* 상단 메뉴 */ | |
127 | +.top-menu{ | |
128 | + position: absolute; | |
129 | + width: 100%; | |
130 | + top: 57px; | |
131 | + left: 0; | |
132 | + z-index: 2; | |
133 | +} | |
134 | +.top-menu>ul.main-menu { | |
135 | + display: flex; | |
136 | + justify-content: center; | |
137 | +} | |
138 | + | |
139 | +.depth1 { | |
140 | + cursor: default; | |
141 | +} | |
142 | + | |
143 | +.top-menu>ul>li { | |
144 | + /* min-width: 152px; */ | |
145 | + /* padding: 10px 30px; */ | |
146 | + text-align: center; | |
147 | + position: relative; | |
148 | +} | |
149 | + | |
150 | +.top-menu>ul>li>p { | |
151 | + padding: 5px 47px; | |
152 | +} | |
153 | + | |
154 | +.top-menu ul.sub-menu { | |
155 | + position: absolute; | |
156 | + width: 100%; | |
157 | + background-color: #fff; | |
158 | + z-index: 4; | |
159 | + bottom: 0; | |
160 | + left: 0; | |
161 | + transform: translateY(100%); | |
162 | + transition: max-height 1.8s ease-in-out, opacity 1.3s ease-in-out; | |
163 | +} | |
164 | + | |
165 | +.top-menu ul.sub-menu li a { | |
166 | + text-align: center; | |
167 | + color: #213f99; | |
168 | +} | |
169 | + | |
170 | +.navbg { | |
171 | + overflow: hidden; | |
172 | + background-color: #fff; | |
173 | + border-bottom: 2px solid #213f99; | |
174 | + position: absolute; | |
175 | + bottom: 0; | |
176 | + left: 0; | |
177 | + transform: translateY(100%); | |
178 | + width: 100%; | |
179 | + z-index: 3; | |
180 | + transition: height 0.6s ease-in-out; | |
181 | +} | |
182 | + | |
183 | + | |
184 | + | |
185 | +/* 메인 */ | |
186 | +.main { | |
187 | + padding: 20px; | |
188 | + height: 100vh; | |
189 | + grid-area: main; | |
190 | +} | |
191 | + | |
192 | +.layout-wrap .main { | |
193 | + padding: 123px 20px 20px; | |
194 | +} | |
195 | + | |
196 | +.login{ | |
197 | + width: 100%; | |
198 | + height: 100vh; | |
199 | +}(파일 끝에 줄바꿈 문자 없음) |
+++ client/resources/css/reset.css
... | ... | @@ -0,0 +1,148 @@ |
1 | +@charset "utf-8"; | |
2 | + | |
3 | +* { | |
4 | + padding: 0; | |
5 | + margin: 0; | |
6 | + box-sizing: border-box; | |
7 | +} | |
8 | + | |
9 | + | |
10 | +html, | |
11 | +body, | |
12 | +#root { | |
13 | + width: 100%; | |
14 | + min-height: 100vh; | |
15 | + font-size: 10px; | |
16 | + color: var(--color-black); | |
17 | + font-family: 'Pretendard'; | |
18 | +} | |
19 | + | |
20 | + | |
21 | +html{ | |
22 | + -ms-user-select: none; | |
23 | + -moz-user-select: -moz-none; | |
24 | + -webkit-user-select: none; | |
25 | + -khtml-user-select: none; | |
26 | + user-select: none; | |
27 | +} | |
28 | + | |
29 | +body { | |
30 | + min-width: 1356px; | |
31 | + background-color: #f7f6fb; | |
32 | +} | |
33 | + | |
34 | + | |
35 | +a { | |
36 | + color: #333; | |
37 | + text-decoration: none; | |
38 | +} | |
39 | + | |
40 | +ol, | |
41 | +ul, | |
42 | +li { | |
43 | + list-style: none; | |
44 | +} | |
45 | + | |
46 | +img, | |
47 | +svg { | |
48 | + vertical-align: middle; | |
49 | +} | |
50 | +table { | |
51 | + min-width: 100%; | |
52 | + border-collapse: collapse; | |
53 | + table-layout: fixed; | |
54 | +} | |
55 | + | |
56 | +table th, | |
57 | +table td { | |
58 | + padding: 8px; | |
59 | + font-size: 1.3rem; | |
60 | +} | |
61 | + | |
62 | +button { | |
63 | + border: none; | |
64 | + background-color: transparent; | |
65 | + font-size: 1.3rem; | |
66 | + margin-left: 5px; | |
67 | + cursor: pointer; | |
68 | +} | |
69 | + | |
70 | +label { | |
71 | + display: block; | |
72 | + font-size: 1.3rem; | |
73 | +} | |
74 | + | |
75 | +select, | |
76 | +textarea, | |
77 | +input[type="text"], | |
78 | +input[type="password"], | |
79 | +input[type="date"], | |
80 | +input[type="number"] { | |
81 | + padding: 5px 10px; | |
82 | + border: 1px solid #aaa; | |
83 | + border-radius: 5px; | |
84 | + font-size: 1.3rem; | |
85 | + position: relative; | |
86 | +} | |
87 | + | |
88 | +input:focus, | |
89 | +select:focus { | |
90 | + outline: none; | |
91 | +} | |
92 | + | |
93 | +input:disabled { | |
94 | + background: #aaa; | |
95 | + border: none; | |
96 | +} | |
97 | + | |
98 | +input[type="checkbox"], | |
99 | +input[type="radio"] { | |
100 | + vertical-align: sub; | |
101 | +} | |
102 | + | |
103 | +input[type='date'] { | |
104 | + max-width: 240px; | |
105 | + padding: 5px; | |
106 | +} | |
107 | + | |
108 | +input[type='date'].date-placeholder::before { | |
109 | + position: absolute; | |
110 | + content: attr(data-placeholder); | |
111 | + width: calc(100% - 42px); | |
112 | + display: block; | |
113 | + color: #ddd; | |
114 | + top: 7px; | |
115 | + left: 5px; | |
116 | + background-color: var(--color-white); | |
117 | +} | |
118 | + | |
119 | +button:first-child, | |
120 | +select:first-child, | |
121 | +input:first-child, | |
122 | +button.only, | |
123 | +select.only, | |
124 | +input.only { | |
125 | + margin-left: 0; | |
126 | +} | |
127 | + | |
128 | +textarea { | |
129 | + width: 100%; | |
130 | + height: 100%; | |
131 | +} | |
132 | + | |
133 | + | |
134 | +/* 스크롤바 디자인 */ | |
135 | +::-webkit-scrollbar { | |
136 | + width: 8px; | |
137 | + height: 8px; | |
138 | +} | |
139 | + | |
140 | +::-webkit-scrollbar-thumb { | |
141 | + background-color: #ededed; | |
142 | + border-radius: 10px; | |
143 | +} | |
144 | + | |
145 | +::-webkit-scrollbar-track { | |
146 | + background-color: #fff; | |
147 | + border-radius: 10px; | |
148 | +}(파일 끝에 줄바꿈 문자 없음) |
+++ client/resources/css/responsive.css
... | ... | @@ -0,0 +1,15 @@ |
1 | +/* @media screen and (min-width: 1537px){ | |
2 | + | |
3 | + | |
4 | +} | |
5 | + | |
6 | +@media screen and (min-width:1356px) and (max-width: 1536px) { | |
7 | + html{ | |
8 | + font-size: 8.5px; | |
9 | + } | |
10 | + button { | |
11 | + font-size: 1.2rem; | |
12 | + } | |
13 | + | |
14 | + | |
15 | +} */(파일 끝에 줄바꿈 문자 없음) |
+++ client/resources/css/style.css
... | ... | @@ -0,0 +1,504 @@ |
1 | +@charset "utf-8"; | |
2 | + | |
3 | +/* page 추가 */ | |
4 | +/* 작업관리 페이지*/ | |
5 | +.node-zone { | |
6 | + margin-bottom: 10px; | |
7 | + background-color: #fff; | |
8 | + padding: 10px; | |
9 | + border-radius: 5px; | |
10 | +} | |
11 | + | |
12 | +.text-over { | |
13 | + white-space: nowrap; | |
14 | + overflow: hidden; | |
15 | + text-overflow: ellipsis; | |
16 | +} | |
17 | + | |
18 | +.state span { | |
19 | + font-size: 1.3rem; | |
20 | +} | |
21 | + | |
22 | +.vue-flow__panel button { | |
23 | + margin-left: 0; | |
24 | +} | |
25 | + | |
26 | +/* 파일관리 페이지*/ | |
27 | +.file-zone { | |
28 | + height: calc(100% - 55px); | |
29 | +} | |
30 | + | |
31 | +.file-tree-zone { | |
32 | + height: calc(100% - 79px); | |
33 | +} | |
34 | + | |
35 | +.tree-wrap { | |
36 | + height: 100%; | |
37 | + /* overflow: auto; */ | |
38 | +} | |
39 | + | |
40 | +.file-table th { | |
41 | + position: sticky; | |
42 | + top: 0; | |
43 | +} | |
44 | + | |
45 | +.file-table tr .icon-btn { | |
46 | + opacity: 0; | |
47 | +} | |
48 | + | |
49 | +.file-table tr:hover, | |
50 | +.file-table tr:nth-child(even):hover { | |
51 | + background-color: var(--color-light-orange); | |
52 | +} | |
53 | + | |
54 | +.file-table tr:hover .icon-btn { | |
55 | + opacity: 1; | |
56 | + transition: all 0.5s ease-in-out; | |
57 | +} | |
58 | + | |
59 | + | |
60 | +/* 데이터활용관리 */ | |
61 | +.gall-list li { | |
62 | + padding: 0 10px; | |
63 | + max-width: 25%; | |
64 | +} | |
65 | + | |
66 | +.gall-list li>div { | |
67 | + min-height: 274px; | |
68 | + padding: 10px; | |
69 | + box-shadow: 0 0 5px #aaa; | |
70 | +} | |
71 | + | |
72 | +.gall-list li>div a { | |
73 | + display: block; | |
74 | + width: 100%; | |
75 | +} | |
76 | + | |
77 | +.gall-list li .gall-img { | |
78 | + width: 100%; | |
79 | + height: 200px; | |
80 | + text-align: center; | |
81 | + overflow: hidden; | |
82 | + margin-bottom: 10px; | |
83 | +} | |
84 | + | |
85 | +.gall-list li .gall-img img { | |
86 | + width: 100%; | |
87 | + height: 100%; | |
88 | +} | |
89 | + | |
90 | +.gall-list li .gall-title { | |
91 | + font-size: 1.6rem; | |
92 | + font-weight: 800; | |
93 | + margin-bottom: 10px; | |
94 | +} | |
95 | + | |
96 | +.gall-info { | |
97 | + width: 100%; | |
98 | +} | |
99 | + | |
100 | +.gall-list li .gall-detail { | |
101 | + font-size: 1.3rem; | |
102 | + width: 100%; | |
103 | + min-height: 50px; | |
104 | + overflow: hidden; | |
105 | + white-space: nowrap; | |
106 | + text-overflow: ellipsis; | |
107 | + word-break: break-all; | |
108 | +} | |
109 | + | |
110 | +.custom-info { | |
111 | + position: absolute; | |
112 | + width: calc(100% - 0px); | |
113 | + z-index: 1; | |
114 | +} | |
115 | + | |
116 | +.layout-option, | |
117 | +.layout { | |
118 | + padding: 15px 0; | |
119 | + height: 100%; | |
120 | +} | |
121 | + | |
122 | +.custom-info details summary, | |
123 | +.component-zone details summary, | |
124 | +.chart-zone details summary { | |
125 | + font-size: 1.6rem; | |
126 | + padding: 15px; | |
127 | + border: 1px solid #213f99; | |
128 | + background-color: rgb(243, 246, 255); | |
129 | + border-radius: 5px; | |
130 | + position: relative; | |
131 | +} | |
132 | + | |
133 | +.component-zone details summary, | |
134 | +.chart-zone details summary { | |
135 | + border: 1px solid var(--color-orange); | |
136 | + background-color: rgb(255, 249, 239); | |
137 | + color: var(--color-orange); | |
138 | +} | |
139 | + | |
140 | +.custom-info details[open] summary, | |
141 | +.component-zone details[open] summary, | |
142 | +.chart-zone details[open] summary { | |
143 | + border-radius: 5px 5px 0 0; | |
144 | +} | |
145 | + | |
146 | + | |
147 | +.custom-info .info-zone { | |
148 | + border: 1px solid #eee; | |
149 | + border-radius: 0 0 5px 5px; | |
150 | + padding: 15px; | |
151 | + background-color: #fff; | |
152 | +} | |
153 | + | |
154 | +.data-list { | |
155 | + height: calc(100% - 47px); | |
156 | + overflow-y: auto; | |
157 | + background-color: #f8f8f8; | |
158 | + border-radius: 5px; | |
159 | + padding: 10px; | |
160 | +} | |
161 | + | |
162 | +.tab-zone { | |
163 | + position: relative; | |
164 | +} | |
165 | + | |
166 | +.tab-zone, | |
167 | +.preview-zone { | |
168 | + height: 100%; | |
169 | +} | |
170 | + | |
171 | +.column-item { | |
172 | + width: 100%; | |
173 | + font-size: 1.3rem; | |
174 | + text-align: center; | |
175 | + padding: 10px; | |
176 | + border-radius: 5px; | |
177 | + border: 1px solid var(--color-orange); | |
178 | + background-color: rgb(255, 249, 239); | |
179 | + color: var(--color-orange); | |
180 | + margin-bottom: 10px; | |
181 | +} | |
182 | + | |
183 | +.column-item:last-child { | |
184 | + margin-bottom: 0; | |
185 | +} | |
186 | + | |
187 | +.component-content { | |
188 | + padding: 10px; | |
189 | + | |
190 | +} | |
191 | + | |
192 | +.layout-content>li, | |
193 | +.component-content>li { | |
194 | + margin-top: 10px; | |
195 | + margin-right: 10px; | |
196 | +} | |
197 | + | |
198 | +.layout-content>li:nth-child(3n), | |
199 | +.component-content>li:nth-child(3n) { | |
200 | + margin-right: 0px; | |
201 | +} | |
202 | + | |
203 | +.icon-content { | |
204 | + margin-left: 10px; | |
205 | + font-size: 1.3rem; | |
206 | +} | |
207 | + | |
208 | +.component-wrap { | |
209 | + width: 100%; | |
210 | + height: 100%; | |
211 | + padding: 10px; | |
212 | + border-radius: 5px; | |
213 | + background-color: #f8f8f8; | |
214 | +} | |
215 | + | |
216 | +input[type="text"].com-dbZone { | |
217 | + margin-left: 0; | |
218 | +} | |
219 | + | |
220 | +.db-input { | |
221 | + height: 100%; | |
222 | +} | |
223 | + | |
224 | +.active-layout .vertical-icon, | |
225 | +.active-layout .horizental-icon { | |
226 | + background-color: var(--color-blueE); | |
227 | + border: 3px solid var(--color-blue); | |
228 | +} | |
229 | + | |
230 | +.active-layout .horizental-icon>span, | |
231 | +.active-layout .vertical-icon>span { | |
232 | + border: 3px solid var(--color-blue); | |
233 | +} | |
234 | + | |
235 | +.component-content li img { | |
236 | + width: 100%; | |
237 | +} | |
238 | + | |
239 | +.page-info>.info-area, | |
240 | +.layout-tree ul { | |
241 | + height: calc(100% - 40px); | |
242 | + overflow-y: auto; | |
243 | +} | |
244 | + | |
245 | +.layout-option { | |
246 | + background-color: var(--color-skyBlue); | |
247 | + | |
248 | +} | |
249 | + | |
250 | +.layout-option>div:nth-child(2) { | |
251 | + overflow-y: auto; | |
252 | +} | |
253 | + | |
254 | +.section-title { | |
255 | + font-size: 1.4rem; | |
256 | + position: relative; | |
257 | +} | |
258 | + | |
259 | +.section-title::before { | |
260 | + content: ""; | |
261 | + position: absolute; | |
262 | + width: 100%; | |
263 | + height: 50%; | |
264 | + background-color: var(--color-orange); | |
265 | + left: 0; | |
266 | + bottom: 0; | |
267 | + opacity: 0.2; | |
268 | +} | |
269 | + | |
270 | +.detail-content { | |
271 | + background-color: var(--color-white); | |
272 | + border: 1px solid var(--color-blue); | |
273 | + border-radius: 0 0 5px 5px; | |
274 | + border-top: 0; | |
275 | +} | |
276 | + | |
277 | +.tab-box { | |
278 | + border: 1px solid #eee; | |
279 | + border-radius: 0 5px 0 0; | |
280 | +} | |
281 | + | |
282 | +.attribute-modal { | |
283 | + position: absolute; | |
284 | + min-width: 120px; | |
285 | + max-width: 200px; | |
286 | + bottom: 0; | |
287 | + left: 0; | |
288 | + background-color: var(--color-white); | |
289 | + transform: translateY(100%); | |
290 | + border: 1px solid #ddd; | |
291 | + z-index: 3; | |
292 | +} | |
293 | + | |
294 | +.attribute-modal>ul>li { | |
295 | + padding: 8px 5px; | |
296 | + border-top: 1px solid #eee; | |
297 | +} | |
298 | + | |
299 | +.attribute-modal>ul>li:first-child { | |
300 | + border-top: 0; | |
301 | +} | |
302 | + | |
303 | +.attribute-modal>ul>li svg, | |
304 | +.attribute-modal>ul>li p { | |
305 | + color: var(--color-darkG); | |
306 | +} | |
307 | + | |
308 | +.attribute-modal>ul>li:hover svg, | |
309 | +.attribute-modal>ul>li:hover p { | |
310 | + color: var(--color-blue); | |
311 | +} | |
312 | + | |
313 | +.editor-box { | |
314 | + background-color: var(--color-skyBlue); | |
315 | + border: 1px dashed var(--color-blue); | |
316 | +} | |
317 | + | |
318 | +.data-set { | |
319 | + height: 30%; | |
320 | +} | |
321 | + | |
322 | +.column-list { | |
323 | + height: 70%; | |
324 | +} | |
325 | + | |
326 | +#horizontal-btn, | |
327 | +#vertical-btn { | |
328 | + width: 18px; | |
329 | + height: 18px; | |
330 | +} | |
331 | + | |
332 | +#horizontal-btn>img, | |
333 | +#vertical-btn>img { | |
334 | + width: 100%; | |
335 | +} | |
336 | + | |
337 | + | |
338 | +/* 알람 */ | |
339 | + | |
340 | +.speaker { | |
341 | + text-align: center; | |
342 | + margin-bottom: 10px; | |
343 | +} | |
344 | + | |
345 | +.speaker>span { | |
346 | + display: inline-block; | |
347 | + background-color: #eee; | |
348 | +} | |
349 | + | |
350 | +.speaker>span:nth-of-type(1) { | |
351 | + width: 10px; | |
352 | + height: 10px; | |
353 | + border-radius: 10px; | |
354 | +} | |
355 | + | |
356 | +.speaker>span:nth-of-type(2) { | |
357 | + width: 60px; | |
358 | + height: 10px; | |
359 | + border-radius: 10px; | |
360 | + margin-left: 10px; | |
361 | +} | |
362 | + | |
363 | +.text-areaZone { | |
364 | + height: calc(50% - 22px); | |
365 | + margin-bottom: 10px; | |
366 | +} | |
367 | + | |
368 | +.user-list { | |
369 | + width: 100%; | |
370 | + height: calc(100% - 50% - 22px); | |
371 | +} | |
372 | + | |
373 | +.user-list .user-title { | |
374 | + font-size: 1.6rem; | |
375 | + font-weight: 800; | |
376 | + color: var(--color-blue); | |
377 | + margin-bottom: 10px; | |
378 | +} | |
379 | + | |
380 | +.user-list>ul { | |
381 | + padding: 10px; | |
382 | + height: calc(100% - 29px); | |
383 | + overflow-y: auto; | |
384 | + border-top: 1px solid #eee; | |
385 | +} | |
386 | + | |
387 | +.user-list>ul>li { | |
388 | + padding: 5px 10px; | |
389 | + margin-bottom: 10px; | |
390 | + border: 1px solid var(--color-orange); | |
391 | + background-color: rgb(255, 249, 239); | |
392 | + color: var(--color-orange); | |
393 | + border-radius: 5px; | |
394 | + font-size: 1.3rem; | |
395 | +} | |
396 | + | |
397 | +.user-list>ul>li:last-child { | |
398 | + margin-bottom: 0; | |
399 | +} | |
400 | + | |
401 | +.user-zone, | |
402 | +.log-zone { | |
403 | + height: 100vh; | |
404 | +} | |
405 | + | |
406 | +.all-user, | |
407 | +.check-user { | |
408 | + height: 100vh; | |
409 | + border: 1px solid #eee; | |
410 | +} | |
411 | + | |
412 | +.log-content { | |
413 | + height: 99%; | |
414 | + background-color: var(--color-blueE); | |
415 | + padding: 10px 10px 10px 25px; | |
416 | + overflow-y: auto; | |
417 | +} | |
418 | + | |
419 | +.log-content li { | |
420 | + font-size: 1.3rem; | |
421 | + color: var(--color-blue); | |
422 | + list-style: disc; | |
423 | + padding: 5px; | |
424 | +} | |
425 | + | |
426 | +.push-text { | |
427 | + padding: 10px; | |
428 | + font-family: 'Pretendard'; | |
429 | + font-size: 15px; | |
430 | +} | |
431 | + | |
432 | + | |
433 | +/* 요소옵션 추가 240216 김하영 */ | |
434 | +.optionBox { | |
435 | + width: 100%; | |
436 | + height: 100%; | |
437 | + border-radius: 5px; | |
438 | + padding: 2rem; | |
439 | + background-color: #f9f9f9; | |
440 | + | |
441 | +} | |
442 | + | |
443 | +.optionBoxText { | |
444 | + font-size: 1.5rem; | |
445 | + color: #213f99; | |
446 | + font-family: 'Pretendard'; | |
447 | + font-weight: 600; | |
448 | +} | |
449 | + | |
450 | +.selectBoxArea select { | |
451 | + border: 0px; | |
452 | + background-color: white; | |
453 | +} | |
454 | + | |
455 | +.optionBox { | |
456 | + font-size: 1.5rem; | |
457 | +} | |
458 | + | |
459 | +.optionSubText { | |
460 | + text-align: center; | |
461 | +} | |
462 | + | |
463 | +.optionSubBox { | |
464 | + background-color: white; | |
465 | + padding: 10px; | |
466 | + border-radius: 5px; | |
467 | + overflow-y: scroll; | |
468 | + height: 225px; | |
469 | +} | |
470 | + | |
471 | +.optionSubBox li { | |
472 | + margin-bottom: 10px; | |
473 | +} | |
474 | + | |
475 | +.cyberpunk-checkbox { | |
476 | + appearance: none; | |
477 | + width: 20px; | |
478 | + height: 20px; | |
479 | + border: 2px solid var(--color-blue); | |
480 | + border-radius: 5px; | |
481 | + background-color: transparent; | |
482 | + display: inline-block; | |
483 | + position: relative; | |
484 | + margin-right: 10px; | |
485 | + cursor: pointer; | |
486 | +} | |
487 | + | |
488 | +.cyberpunk-checkbox:before { | |
489 | + content: ""; | |
490 | + background-color: var(--color-blue); | |
491 | + display: block; | |
492 | + position: absolute; | |
493 | + top: 50%; | |
494 | + left: 50%; | |
495 | + transform: translate(-50%, -50%) scale(0); | |
496 | + width: 10px; | |
497 | + height: 10px; | |
498 | + border-radius: 3px; | |
499 | + transition: all 0.3s ease-in-out; | |
500 | +} | |
501 | + | |
502 | +.cyberpunk-checkbox:checked:before { | |
503 | + transform: translate(-50%, -50%) scale(1); | |
504 | +}(파일 끝에 줄바꿈 문자 없음) |
+++ client/resources/file/stdWrdFile.xlsx
Binary file is not shown |
+++ client/resources/font/GmarketSansBold.woff
Binary file is not shown |
+++ client/resources/font/GmarketSansLight.woff
Binary file is not shown |
+++ client/resources/font/GmarketSansMedium.woff
Binary file is not shown |
+++ client/resources/font/PretendardVariable.woff2
Binary file is not shown |
+++ client/resources/img/chartIcon/bubble_chart.png
Binary file is not shown |
+++ client/resources/img/chartIcon/clustered_chart_h.png
Binary file is not shown |
+++ client/resources/img/chartIcon/clustered_chart_v.png
Binary file is not shown |
+++ client/resources/img/chartIcon/column_chart_h.png
Binary file is not shown |
+++ client/resources/img/chartIcon/column_chart_v.png
Binary file is not shown |
+++ client/resources/img/chartIcon/dounet_chart.png
Binary file is not shown |
+++ client/resources/img/chartIcon/liin_chart_back.png
Binary file is not shown |
+++ client/resources/img/chartIcon/line_chart.png
Binary file is not shown |
+++ client/resources/img/chartIcon/mix_chart.png
Binary file is not shown |
+++ client/resources/img/chartIcon/node_line_chart.png
Binary file is not shown |
+++ client/resources/img/chartIcon/pie_chart.png
Binary file is not shown |
+++ client/resources/img/chartIcon/semicircle_chart.png
Binary file is not shown |
+++ client/resources/img/chartIcon/stacked_bar_chart.png
Binary file is not shown |
+++ client/resources/img/chartIcon/stacked_column_chart.png
Binary file is not shown |
+++ client/resources/img/chartIcon/word_chart.png
Binary file is not shown |
+++ client/resources/img/componentIcon/equipment.png
Binary file is not shown |
+++ client/resources/img/componentIcon/icon1.png
Binary file is not shown |
+++ client/resources/img/componentIcon/icon2.png
Binary file is not shown |
+++ client/resources/img/componentIcon/icon3.png
Binary file is not shown |
+++ client/resources/img/componentIcon/icon4.png
Binary file is not shown |
+++ client/resources/img/componentIcon/icon5.png
Binary file is not shown |
+++ client/resources/img/delete.png
Binary file is not shown |
+++ client/resources/img/guideImg/align.png
Binary file is not shown |
+++ client/resources/img/guideImg/horizontal.png
Binary file is not shown |
+++ client/resources/img/guideImg/justify.png
Binary file is not shown |
+++ client/resources/img/guideImg/vertical.png
Binary file is not shown |
+++ client/resources/img/icon/1.png
Binary file is not shown |
+++ client/resources/img/icon/all.png
Binary file is not shown |
+++ client/resources/img/icon/bottom.png
Binary file is not shown |
+++ client/resources/img/icon/dashed.png
Binary file is not shown |
+++ client/resources/img/icon/dobble.png
Binary file is not shown |
+++ client/resources/img/icon/dotted.png
Binary file is not shown |
+++ client/resources/img/icon/hor_a.png
Binary file is not shown |
+++ client/resources/img/icon/hor_b.png
Binary file is not shown |
+++ client/resources/img/icon/hwp.png
Binary file is not shown |
+++ client/resources/img/icon/iconCategory.png
Binary file is not shown |
+++ client/resources/img/icon/img.png
Binary file is not shown |
+++ client/resources/img/icon/lb.png
Binary file is not shown |
+++ client/resources/img/icon/left.png
Binary file is not shown |
+++ client/resources/img/icon/lt.png
Binary file is not shown |
+++ client/resources/img/icon/pdf.png
Binary file is not shown |
+++ client/resources/img/icon/ppt.png
Binary file is not shown |
+++ client/resources/img/icon/rb.png
Binary file is not shown |
+++ client/resources/img/icon/right.png
Binary file is not shown |
+++ client/resources/img/icon/rt.png
Binary file is not shown |
+++ client/resources/img/icon/solid.png
Binary file is not shown |
+++ client/resources/img/icon/top.png
Binary file is not shown |
+++ client/resources/img/icon/txt.png
Binary file is not shown |
+++ client/resources/img/icon/ver_a.png
Binary file is not shown |
+++ client/resources/img/icon/ver_b.png
Binary file is not shown |
+++ client/resources/img/icon/xls.png
Binary file is not shown |
+++ client/resources/img/loading_mini_icon.png
Binary file is not shown |
+++ client/resources/img/logo.png
Binary file is not shown |
+++ client/resources/img/logo_s.png
Binary file is not shown |
+++ client/resources/img/logo_w.png
Binary file is not shown |
+++ client/resources/img/opacity.png
Binary file is not shown |
+++ client/resources/img/option.png
Binary file is not shown |
+++ client/resources/vue-flow/controls_latest.css
... | ... | @@ -0,0 +1,37 @@ |
1 | +.vue-flow__controls { | |
2 | + box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.08); | |
3 | +} | |
4 | + | |
5 | +.vue-flow__controls-button { | |
6 | + background: #fefefe; | |
7 | + border: none; | |
8 | + border-bottom: 1px solid #eee; | |
9 | + box-sizing: content-box; | |
10 | + display: flex; | |
11 | + justify-content: center; | |
12 | + align-items: center; | |
13 | + width: 16px; | |
14 | + height: 16px; | |
15 | + cursor: pointer; | |
16 | + user-select: none; | |
17 | + padding: 5px; | |
18 | +} | |
19 | + | |
20 | +.vue-flow__controls-button svg { | |
21 | + width: 100%; | |
22 | + max-width: 12px; | |
23 | + max-height: 12px; | |
24 | +} | |
25 | + | |
26 | +.vue-flow__controls-button:hover { | |
27 | + background: #f4f4f4; | |
28 | +} | |
29 | + | |
30 | + | |
31 | +.vue-flow__controls-button:disabled { | |
32 | + pointer-events: none; | |
33 | +} | |
34 | + | |
35 | +.vue-flow__controls-button:disabled svg { | |
36 | + fill-opacity: 0.4; | |
37 | +} |
+++ client/resources/vue-flow/minimap_latest.css
... | ... | @@ -0,0 +1,15 @@ |
1 | +.vue-flow__minimap { | |
2 | + background-color: #fff; | |
3 | +} | |
4 | + | |
5 | +.vue-flow__minimap.pannable { | |
6 | + cursor: grab; | |
7 | +} | |
8 | + | |
9 | +.vue-flow__minimap.dragging { | |
10 | + cursor: grabbing; | |
11 | +} | |
12 | + | |
13 | +.vue-flow__minimap-mask.pannable { | |
14 | + cursor: grab; | |
15 | +} |
+++ client/resources/vue-flow/node-resizer_latest.css
... | ... | @@ -0,0 +1,103 @@ |
1 | +.vue-flow__resize-control { | |
2 | + position: absolute; | |
3 | +} | |
4 | + | |
5 | +.vue-flow__resize-control.left, | |
6 | +.vue-flow__resize-control.right { | |
7 | + cursor: ew-resize; | |
8 | +} | |
9 | + | |
10 | +.vue-flow__resize-control.top, | |
11 | +.vue-flow__resize-control.bottom { | |
12 | + cursor: ns-resize; | |
13 | +} | |
14 | + | |
15 | +.vue-flow__resize-control.top.left, | |
16 | +.vue-flow__resize-control.bottom.right { | |
17 | + cursor: nwse-resize; | |
18 | +} | |
19 | + | |
20 | +.vue-flow__resize-control.bottom.left, | |
21 | +.vue-flow__resize-control.top.right { | |
22 | + cursor: nesw-resize; | |
23 | +} | |
24 | + | |
25 | +/* handle styles */ | |
26 | +.vue-flow__resize-control.handle { | |
27 | + width: 4px; | |
28 | + height: 4px; | |
29 | + border: 1px solid #fff; | |
30 | + border-radius: 1px; | |
31 | + background-color: #3367d9; | |
32 | + transform: translate(-50%, -50%); | |
33 | +} | |
34 | + | |
35 | +.vue-flow__resize-control.handle.left { | |
36 | + left: 0; | |
37 | + top: 50%; | |
38 | +} | |
39 | +.vue-flow__resize-control.handle.right { | |
40 | + left: 100%; | |
41 | + top: 50%; | |
42 | +} | |
43 | +.vue-flow__resize-control.handle.top { | |
44 | + left: 50%; | |
45 | + top: 0; | |
46 | +} | |
47 | +.vue-flow__resize-control.handle.bottom { | |
48 | + left: 50%; | |
49 | + top: 100%; | |
50 | +} | |
51 | +.vue-flow__resize-control.handle.top.left { | |
52 | + left: 0; | |
53 | +} | |
54 | +.vue-flow__resize-control.handle.bottom.left { | |
55 | + left: 0; | |
56 | +} | |
57 | +.vue-flow__resize-control.handle.top.right { | |
58 | + left: 100%; | |
59 | +} | |
60 | +.vue-flow__resize-control.handle.bottom.right { | |
61 | + left: 100%; | |
62 | +} | |
63 | + | |
64 | +/* line styles */ | |
65 | +.vue-flow__resize-control.line { | |
66 | + border-color: #3367d9; | |
67 | + border-width: 0; | |
68 | + border-style: solid; | |
69 | +} | |
70 | + | |
71 | +.vue-flow__resize-control.line.left, | |
72 | +.vue-flow__resize-control.line.right { | |
73 | + width: 1px; | |
74 | + transform: translate(-50%, 0); | |
75 | + top: 0; | |
76 | + height: 100%; | |
77 | +} | |
78 | + | |
79 | +.vue-flow__resize-control.line.left { | |
80 | + left: 0; | |
81 | + border-left-width: 1px; | |
82 | +} | |
83 | +.vue-flow__resize-control.line.right { | |
84 | + left: 100%; | |
85 | + border-right-width: 1px; | |
86 | +} | |
87 | + | |
88 | +.vue-flow__resize-control.line.top, | |
89 | +.vue-flow__resize-control.line.bottom { | |
90 | + height: 1px; | |
91 | + transform: translate(0, -50%); | |
92 | + left: 0; | |
93 | + width: 100%; | |
94 | +} | |
95 | + | |
96 | +.vue-flow__resize-control.line.top { | |
97 | + top: 0; | |
98 | + border-top-width: 1px; | |
99 | +} | |
100 | +.vue-flow__resize-control.line.bottom { | |
101 | + border-bottom-width: 1px; | |
102 | + top: 100%; | |
103 | +} |
+++ client/resources/vue-flow/style.css
... | ... | @@ -0,0 +1,264 @@ |
1 | +.vue-flow { | |
2 | + position: relative; | |
3 | + width: 100%; | |
4 | + height: 100%; | |
5 | + overflow: hidden; | |
6 | + z-index: 0; | |
7 | +} | |
8 | + | |
9 | +.vue-flow__container { | |
10 | + position: absolute; | |
11 | + height: 100%; | |
12 | + width: 100%; | |
13 | + left: 0; | |
14 | + top: 0; | |
15 | +} | |
16 | + | |
17 | +.vue-flow__pane { | |
18 | + z-index: 1; | |
19 | +} | |
20 | + | |
21 | +.vue-flow__pane.draggable { | |
22 | + cursor: grab; | |
23 | + } | |
24 | + | |
25 | +.vue-flow__pane.dragging { | |
26 | + cursor: grabbing; | |
27 | + } | |
28 | + | |
29 | +.vue-flow__pane.selection { | |
30 | + cursor: pointer; | |
31 | + } | |
32 | + | |
33 | +.vue-flow__transformationpane { | |
34 | + transform-origin: 0 0; | |
35 | + z-index: 2; | |
36 | + pointer-events: none; | |
37 | +} | |
38 | + | |
39 | +.vue-flow__viewport { | |
40 | + z-index: 4; | |
41 | + overflow: clip; | |
42 | +} | |
43 | + | |
44 | +.vue-flow__selection { | |
45 | + z-index: 6; | |
46 | +} | |
47 | + | |
48 | +.vue-flow__edge-labels { | |
49 | + position: absolute; | |
50 | + width: 100%; | |
51 | + height: 100%; | |
52 | + pointer-events: none; | |
53 | + -webkit-user-select: none; | |
54 | + -moz-user-select: none; | |
55 | + user-select: none; | |
56 | +} | |
57 | + | |
58 | +.vue-flow__nodesselection-rect:focus, | |
59 | +.vue-flow__nodesselection-rect:focus-visible { | |
60 | + outline: none; | |
61 | +} | |
62 | + | |
63 | +.vue-flow .vue-flow__edges { | |
64 | + pointer-events: none; | |
65 | + overflow: visible; | |
66 | +} | |
67 | + | |
68 | +.vue-flow__edge-path, | |
69 | +.vue-flow__connection-path { | |
70 | + stroke: #b1b1b7; | |
71 | + stroke-width: 1; | |
72 | + fill: none; | |
73 | +} | |
74 | + | |
75 | +.vue-flow__edge { | |
76 | + pointer-events: visibleStroke; | |
77 | + cursor: pointer; | |
78 | +} | |
79 | + | |
80 | +.vue-flow__edge.animated path { | |
81 | + stroke-dasharray: 5; | |
82 | + animation: dashdraw 0.5s linear infinite; | |
83 | + } | |
84 | + | |
85 | +.vue-flow__edge.animated path.vue-flow__edge-interaction { | |
86 | + stroke-dasharray: none; | |
87 | + animation: none; | |
88 | + } | |
89 | + | |
90 | +.vue-flow__edge.inactive { | |
91 | + pointer-events: none; | |
92 | + } | |
93 | + | |
94 | +.vue-flow__edge.selected, | |
95 | + .vue-flow__edge:focus, | |
96 | + .vue-flow__edge:focus-visible { | |
97 | + outline: none; | |
98 | + } | |
99 | + | |
100 | +.vue-flow__edge.selected .vue-flow__edge-path, | |
101 | + .vue-flow__edge:focus .vue-flow__edge-path, | |
102 | + .vue-flow__edge:focus-visible .vue-flow__edge-path { | |
103 | + stroke: #555; | |
104 | + } | |
105 | + | |
106 | +.vue-flow__edge-textwrapper { | |
107 | + pointer-events: all; | |
108 | + } | |
109 | + | |
110 | +.vue-flow__edge-textbg { | |
111 | + fill: white; | |
112 | + } | |
113 | + | |
114 | +.vue-flow__edge-text { | |
115 | + pointer-events: none; | |
116 | + -webkit-user-select: none; | |
117 | + -moz-user-select: none; | |
118 | + user-select: none; | |
119 | + } | |
120 | + | |
121 | +.vue-flow__connection { | |
122 | + pointer-events: none; | |
123 | +} | |
124 | + | |
125 | +.vue-flow__connection .animated { | |
126 | + stroke-dasharray: 5; | |
127 | + animation: dashdraw 0.5s linear infinite; | |
128 | + } | |
129 | + | |
130 | +.vue-flow__connectionline { | |
131 | + z-index: 1001; | |
132 | +} | |
133 | + | |
134 | +.vue-flow__nodes { | |
135 | + pointer-events: none; | |
136 | + transform-origin: 0 0; | |
137 | +} | |
138 | + | |
139 | +.vue-flow__node-default, | |
140 | +.vue-flow__node-input, | |
141 | +.vue-flow__node-output { | |
142 | + border-width: 1px; | |
143 | + border-style: solid; | |
144 | + border-color: #bbb; | |
145 | +} | |
146 | + | |
147 | +.vue-flow__node-default.selected, | |
148 | + .vue-flow__node-default:focus, | |
149 | + .vue-flow__node-default:focus-visible, | |
150 | + .vue-flow__node-input.selected, | |
151 | + .vue-flow__node-input:focus, | |
152 | + .vue-flow__node-input:focus-visible, | |
153 | + .vue-flow__node-output.selected, | |
154 | + .vue-flow__node-output:focus, | |
155 | + .vue-flow__node-output:focus-visible { | |
156 | + outline: none; | |
157 | + border: 1px solid #555; | |
158 | + } | |
159 | + | |
160 | +.vue-flow__node { | |
161 | + position: absolute; | |
162 | + -webkit-user-select: none; | |
163 | + -moz-user-select: none; | |
164 | + user-select: none; | |
165 | + pointer-events: all; | |
166 | + transform-origin: 0 0; | |
167 | + box-sizing: border-box; | |
168 | + cursor: grab; | |
169 | +} | |
170 | + | |
171 | +.vue-flow__node.dragging { | |
172 | + cursor: grabbing; | |
173 | + } | |
174 | + | |
175 | +.vue-flow__nodesselection { | |
176 | + z-index: 3; | |
177 | + transform-origin: left top; | |
178 | + pointer-events: none; | |
179 | +} | |
180 | + | |
181 | +.vue-flow__nodesselection-rect { | |
182 | + position: absolute; | |
183 | + pointer-events: all; | |
184 | + cursor: grab; | |
185 | + } | |
186 | + | |
187 | +.vue-flow__nodesselection-rect.dragging { | |
188 | + cursor: grabbing; | |
189 | + } | |
190 | + | |
191 | +.vue-flow__handle { | |
192 | + position: absolute; | |
193 | + pointer-events: none; | |
194 | + min-width: 5px; | |
195 | + min-height: 5px; | |
196 | +} | |
197 | + | |
198 | +.vue-flow__handle.connectable { | |
199 | + pointer-events: all; | |
200 | + cursor: crosshair; | |
201 | + } | |
202 | + | |
203 | +.vue-flow__handle-bottom { | |
204 | + top: auto; | |
205 | + left: 50%; | |
206 | + bottom: -4px; | |
207 | + transform: translate(-50%, 0); | |
208 | + } | |
209 | + | |
210 | +.vue-flow__handle-top { | |
211 | + left: 50%; | |
212 | + top: -4px; | |
213 | + transform: translate(-50%, 0); | |
214 | + } | |
215 | + | |
216 | +.vue-flow__handle-left { | |
217 | + top: 50%; | |
218 | + left: -4px; | |
219 | + transform: translate(0, -50%); | |
220 | + } | |
221 | + | |
222 | +.vue-flow__handle-right { | |
223 | + right: -4px; | |
224 | + top: 50%; | |
225 | + transform: translate(0, -50%); | |
226 | + } | |
227 | + | |
228 | +.vue-flow__edgeupdater { | |
229 | + cursor: move; | |
230 | + pointer-events: all; | |
231 | +} | |
232 | + | |
233 | +.vue-flow__panel { | |
234 | + position: absolute; | |
235 | + z-index: 5; | |
236 | + margin: 15px; | |
237 | +} | |
238 | + | |
239 | +.vue-flow__panel.top { | |
240 | + top: 0; | |
241 | + } | |
242 | + | |
243 | +.vue-flow__panel.bottom { | |
244 | + bottom: 0; | |
245 | + } | |
246 | + | |
247 | +.vue-flow__panel.left { | |
248 | + left: 0; | |
249 | + } | |
250 | + | |
251 | +.vue-flow__panel.right { | |
252 | + right: 0; | |
253 | + } | |
254 | + | |
255 | +.vue-flow__panel.center { | |
256 | + left: 50%; | |
257 | + transform: translateX(-50%); | |
258 | + } | |
259 | + | |
260 | +@keyframes dashdraw { | |
261 | + from { | |
262 | + stroke-dashoffset: 10; | |
263 | + } | |
264 | +} |
+++ client/resources/vue-flow/theme-default.css
... | ... | @@ -0,0 +1,130 @@ |
1 | +:root { | |
2 | + --vf-node-bg: #fff; | |
3 | + --vf-node-text: #222; | |
4 | + --vf-connection-path: #b1b1b7; | |
5 | + --vf-handle: #555; | |
6 | +} | |
7 | + | |
8 | +.vue-flow__edge.updating .vue-flow__edge-path { | |
9 | + stroke: #777; | |
10 | + } | |
11 | + | |
12 | +.vue-flow__edge-text { | |
13 | + font-size: 10px; | |
14 | +} | |
15 | + | |
16 | +.vue-flow__edge-textbg { | |
17 | + fill: #fff; | |
18 | +} | |
19 | + | |
20 | +.vue-flow__connection-path { | |
21 | + stroke: var(--vf-connection-path); | |
22 | +} | |
23 | + | |
24 | +.vue-flow__node { | |
25 | + cursor: grab; | |
26 | +} | |
27 | + | |
28 | +.vue-flow__node.selectable:focus, | |
29 | + .vue-flow__node.selectable:focus-visible { | |
30 | + outline: none; | |
31 | + } | |
32 | + | |
33 | +.vue-flow__node-default, | |
34 | +.vue-flow__node-input, | |
35 | +.vue-flow__node-output { | |
36 | + padding: 10px; | |
37 | + border-radius: 3px; | |
38 | + width: 150px; | |
39 | + font-size: 12px; | |
40 | + text-align: center; | |
41 | + border-width: 1px; | |
42 | + border-style: solid; | |
43 | + color: var(--vf-node-text); | |
44 | + background-color: var(--vf-node-bg); | |
45 | + border-color: var(--vf-node-color); | |
46 | +} | |
47 | + | |
48 | +.vue-flow__node-default.selected, | |
49 | + .vue-flow__node-default.selected:hover, | |
50 | + .vue-flow__node-input.selected, | |
51 | + .vue-flow__node-input.selected:hover, | |
52 | + .vue-flow__node-output.selected, | |
53 | + .vue-flow__node-output.selected:hover { | |
54 | + box-shadow: 0 0 0 0.5px var(--vf-box-shadow); | |
55 | + } | |
56 | + | |
57 | +.vue-flow__node-default .vue-flow__handle, .vue-flow__node-input .vue-flow__handle, .vue-flow__node-output .vue-flow__handle { | |
58 | + background: var(--vf-handle); | |
59 | + } | |
60 | + | |
61 | +.vue-flow__node-default.selectable:hover, .vue-flow__node-input.selectable:hover, .vue-flow__node-output.selectable:hover { | |
62 | + box-shadow: 0 1px 4px 1px rgba(0, 0, 0, 0.08); | |
63 | + } | |
64 | + | |
65 | +.vue-flow__node-input { | |
66 | + --vf-node-color: var(--vf-node-color, #0041d0); | |
67 | + --vf-handle: var(--vf-node-color, #0041d0); | |
68 | + --vf-box-shadow: var(--vf-node-color, #0041d0); | |
69 | + | |
70 | + background: var(--vf-node-bg); | |
71 | + border-color: var(--vf-node-color, #0041d0); | |
72 | +} | |
73 | + | |
74 | +.vue-flow__node-input.selected, | |
75 | + .vue-flow__node-input:focus, | |
76 | + .vue-flow__node-input:focus-visible { | |
77 | + outline: none; | |
78 | + border: 1px solid var(--vf-node-color, #0041d0); | |
79 | + } | |
80 | + | |
81 | +.vue-flow__node-default { | |
82 | + --vf-handle: var(--vf-node-color, #1a192b); | |
83 | + --vf-box-shadow: var(--vf-node-color, #1a192b); | |
84 | + | |
85 | + background: var(--vf-node-bg); | |
86 | + border-color: var(--vf-node-color, #1a192b); | |
87 | +} | |
88 | + | |
89 | +.vue-flow__node-default.selected, | |
90 | + .vue-flow__node-default:focus, | |
91 | + .vue-flow__node-default:focus-visible { | |
92 | + outline: none; | |
93 | + border: 1px solid var(--vf-node-color, #1a192b); | |
94 | + } | |
95 | + | |
96 | +.vue-flow__node-output { | |
97 | + --vf-handle: var(--vf-node-color, #ff0072); | |
98 | + --vf-box-shadow: var(--vf-node-color, #ff0072); | |
99 | + | |
100 | + background: var(--vf-node-bg); | |
101 | + border-color: var(--vf-node-color, #ff0072); | |
102 | +} | |
103 | + | |
104 | +.vue-flow__node-output.selected, | |
105 | + .vue-flow__node-output:focus, | |
106 | + .vue-flow__node-output:focus-visible { | |
107 | + outline: none; | |
108 | + border: 1px solid var(--vf-node-color, #ff0072); | |
109 | + } | |
110 | + | |
111 | +.vue-flow__nodesselection-rect, | |
112 | +.vue-flow__selection { | |
113 | + background: rgba(0, 89, 220, 0.08); | |
114 | + border: 1px dotted rgba(0, 89, 220, 0.8); | |
115 | +} | |
116 | + | |
117 | +.vue-flow__nodesselection-rect:focus, | |
118 | + .vue-flow__nodesselection-rect:focus-visible, | |
119 | + .vue-flow__selection:focus, | |
120 | + .vue-flow__selection:focus-visible { | |
121 | + outline: none; | |
122 | + } | |
123 | + | |
124 | +.vue-flow__handle { | |
125 | + width: 6px; | |
126 | + height: 6px; | |
127 | + background: var(--vf-handle); | |
128 | + border: 1px solid #fff; | |
129 | + border-radius: 100%; | |
130 | +} |
+++ client/views/common/commonPlugin.js
... | ... | @@ -0,0 +1,372 @@ |
1 | +/** | |
2 | + * | |
3 | + * 공통 처리 플러그인 | |
4 | + */ | |
5 | + | |
6 | + | |
7 | +function prefixZero(number, length) { | |
8 | + var zero = ''; | |
9 | + number = number.toString(); | |
10 | + | |
11 | + if (number.length < length) { | |
12 | + for (let i = 0; i < length - number.length; i++) { | |
13 | + zero += '0'; | |
14 | + } | |
15 | + } | |
16 | + return zero + number; | |
17 | +} | |
18 | + | |
19 | +////////////////////////////////////////////////////////////////// | |
20 | + | |
21 | +import axios from 'axios'; | |
22 | +import { mdiMagnify, mdiFolder, mdiFolderOpen, mdiTable, mdiPlay, mdiKeyVariant, mdiTrashCan } from '@mdi/js'; | |
23 | +import moment from 'moment'; | |
24 | +import Vue from "vue"; | |
25 | + | |
26 | +export default { | |
27 | + install(Vue) { | |
28 | + | |
29 | + let alertRef = {}; | |
30 | + let commonXios = {}; | |
31 | + let defaultObject = {}; | |
32 | + | |
33 | + Vue.config.globalProperties.$setAlertRef = function (ref) { | |
34 | + alertRef = ref; | |
35 | + } | |
36 | + | |
37 | + //날짜 비교 함수 | |
38 | + Vue.config.globalProperties.$getSum = function (prevDate, currentDate) { | |
39 | + alert(prevDate) | |
40 | + alert(currentDate) | |
41 | + } | |
42 | + | |
43 | + // 모달 호출 | |
44 | + Vue.config.globalProperties.$showAlert = function (title, message) { | |
45 | + alertRef.setTitle(title); | |
46 | + alertRef.setMessage(message); | |
47 | + alertRef.showModal(); | |
48 | + } | |
49 | + | |
50 | + // confirm 창 호출 | |
51 | + Vue.config.globalProperties.$showConfirm = async function (title, message) { | |
52 | + | |
53 | + alertRef.setTitle(title); | |
54 | + alertRef.setMessage(message); | |
55 | + const resultData = await alertRef.showConfirm(); | |
56 | + return resultData; | |
57 | + } | |
58 | + | |
59 | + // confirm 창 호출 | |
60 | + Vue.config.globalProperties.$showRadioConfirm = async function (title, message) { | |
61 | + | |
62 | + alertRef.setTitle(title); | |
63 | + alertRef.setMessage(message); | |
64 | + const resultData = await alertRef.showRadioConfirm(); | |
65 | + return resultData; | |
66 | + } | |
67 | + | |
68 | + // 공통코드 호출 | |
69 | + Vue.config.globalProperties.$getCommonCode = async function (GroupCode) { | |
70 | + const promise = new Promise((resolve, reject) => { | |
71 | + axios({ | |
72 | + url: '/common/getCodeList.json', | |
73 | + method: 'post', | |
74 | + headers: { | |
75 | + 'Content-Type': 'application/json; charset=UTF-8' | |
76 | + }, | |
77 | + data: JSON.stringify({ 'groupCode': GroupCode }) | |
78 | + }).then(function (response) { | |
79 | + resolve(response.data.resultData.codeList) | |
80 | + }).catch(function (error) { | |
81 | + resolve('cancle') | |
82 | + }); | |
83 | + }); | |
84 | + | |
85 | + return promise.then( | |
86 | + (data) => { | |
87 | + return data; | |
88 | + } | |
89 | + ).catch(function (err) { | |
90 | + console.log(err) | |
91 | + return []; | |
92 | + }); | |
93 | + } | |
94 | + | |
95 | + // 데이터베이스 타입호출 | |
96 | + Vue.config.globalProperties.$getDataBaseTypeList = async function () { | |
97 | + const promise = new Promise((resolve, reject) => { | |
98 | + axios({ | |
99 | + url: '/common/getDataBaseTypeList.json', | |
100 | + method: 'post', | |
101 | + headers: { | |
102 | + 'Content-Type': 'application/json; charset=UTF-8' | |
103 | + } | |
104 | + }).then(function (response) { | |
105 | + resolve(response.data.resultData.DatabaseTypeList) | |
106 | + }).catch(function (error) { | |
107 | + resolve('cancle') | |
108 | + }); | |
109 | + }); | |
110 | + | |
111 | + return promise.then( | |
112 | + (data) => { | |
113 | + return data; | |
114 | + } | |
115 | + ).catch(function (err) { | |
116 | + console.log(err) | |
117 | + return []; | |
118 | + }); | |
119 | + } | |
120 | + | |
121 | + //시간 구하기 | |
122 | + Vue.config.globalProperties.$getFullTime = function (hour, minute, seconds) { | |
123 | + var date = new Date(); | |
124 | + var h = date.getHours(); | |
125 | + var m = date.getMinutes(); | |
126 | + var s = date.getSeconds(); | |
127 | + if (this.$isEmpty(hour) == false) { | |
128 | + h += hour; | |
129 | + } if (this.$isEmpty(minute) == false) { | |
130 | + m += minute; | |
131 | + } if (this.$isEmpty(seconds) == false) { | |
132 | + s += seconds; | |
133 | + } | |
134 | + return prefixZero(h, 2) + ":" + prefixZero(m, 2) + ":" + prefixZero(s, 2); | |
135 | + } | |
136 | + | |
137 | + // 빈값체크 | |
138 | + Vue.config.globalProperties.$isEmpty = function (data) { | |
139 | + if (data === undefined || data === null || data === "" || data.length === 0 || (data.constructor == Object && Object.keys(data).length === 0)) { | |
140 | + if ((typeof data) === "number") { | |
141 | + return false | |
142 | + } else { | |
143 | + return true; | |
144 | + } | |
145 | + } else { | |
146 | + return false; | |
147 | + } | |
148 | + } | |
149 | + | |
150 | + // 기본 검색 객체 생성 | |
151 | + Vue.config.globalProperties.$getDefaultSerchVO = function () { | |
152 | + return { | |
153 | + searchObjectList: [], | |
154 | + order: '', | |
155 | + orderASC: true, | |
156 | + currentPage: 1, | |
157 | + perPage: 10, | |
158 | + totalRows: 0 | |
159 | + } | |
160 | + } | |
161 | + | |
162 | + // 기본 검색 객체 생성 | |
163 | + Vue.config.globalProperties.$getDefaultSerchItem = function (key, type) { | |
164 | + | |
165 | + let value1 = null; | |
166 | + let value2 = null; | |
167 | + if (type === 'dates') { | |
168 | + value1 = moment().add("-1", "M").format('YYYY-MM-DD'); | |
169 | + value2 = moment().format('YYYY-MM-DD'); | |
170 | + } | |
171 | + return { | |
172 | + key: key, | |
173 | + value: value1, | |
174 | + key2: key, | |
175 | + value2: value2, | |
176 | + type: type | |
177 | + } | |
178 | + } | |
179 | + | |
180 | + // 기본 검색 객체 생성 | |
181 | + Vue.config.globalProperties.$getIconPath = function (icon) { | |
182 | + if (icon == null) { | |
183 | + icon = 'mdiMagnify'; | |
184 | + } | |
185 | + if (icon == 'mdiFolder') { | |
186 | + return mdiFolder; | |
187 | + } else if (icon == 'mdiFolder') { | |
188 | + return mdiFolderOpen; | |
189 | + } else if (icon == 'mdiTable') { | |
190 | + return mdiTable; | |
191 | + } else if (icon == 'mdiPlay') { | |
192 | + return mdiPlay; | |
193 | + } else if (icon == 'mdiKeyVariant') { | |
194 | + return mdiKeyVariant; | |
195 | + } else if (icon == 'mdiTrashCan') { | |
196 | + return mdiTrashCan; | |
197 | + } else { | |
198 | + return mdiMagnify; | |
199 | + } | |
200 | + } | |
201 | + | |
202 | + // 기본 job 관련 vo 세팅 | |
203 | + Vue.config.globalProperties.$setDefaultObject = function () { | |
204 | + axios({ | |
205 | + url: '/common/getDefaultObject.json', | |
206 | + method: 'post', | |
207 | + headers: { | |
208 | + 'Content-Type': 'application/json; charset=UTF-8' | |
209 | + } | |
210 | + }).then(function (response) { | |
211 | + defaultObject = response.data.resultData; | |
212 | + }).catch(function (error) { | |
213 | + console.log(error); | |
214 | + }); | |
215 | + } | |
216 | + | |
217 | + // default jobItm 호출 | |
218 | + Vue.config.globalProperties.$getDefaultJobGroup = function () { | |
219 | + return defaultObject; | |
220 | + } | |
221 | + | |
222 | + // default jobItm 호출 | |
223 | + Vue.config.globalProperties.$getDefaultObject = function () { | |
224 | + return defaultObject; | |
225 | + } | |
226 | + | |
227 | + // 스타일 생성 | |
228 | + Vue.config.globalProperties.$createStyleSheet = function (stylesheet) { | |
229 | + | |
230 | + // 스타일 생성 | |
231 | + let result = ''; | |
232 | + | |
233 | + // 폰트스타일 | |
234 | + if (stylesheet.fontStyle != null) { | |
235 | + | |
236 | + // 폰트 옵션 | |
237 | + result += 'font-size:' + stylesheet.fontStyle.font_size + 'px;'; | |
238 | + result += 'color : ' + stylesheet.fontStyle.font_color + ';'; | |
239 | + result += 'font-family:' + stylesheet.fontStyle.font + ';'; | |
240 | + result += 'text-align: ' + stylesheet.fontStyle.text_align + ';'; | |
241 | + result += 'vertical-align: ' + stylesheet.fontStyle.vertical_align + ';'; | |
242 | + | |
243 | + // 볼드 처리 | |
244 | + if (stylesheet.fontStyle.bold_at) result += 'font-weight: bold;' | |
245 | + // 이탤릭 처리 | |
246 | + if (stylesheet.fontStyle.italic_at) result += 'font-style: italic;' | |
247 | + // 라인 귿기 | |
248 | + if (stylesheet.fontStyle.underline_at || stylesheet.fontStyle.line_through_at) { | |
249 | + result += 'text-decoration:'; | |
250 | + if (stylesheet.fontStyle.underline_at) result += ' underline'; | |
251 | + if (stylesheet.fontStyle.line_through_at) result += ' line-through'; | |
252 | + result += ';'; | |
253 | + } | |
254 | + | |
255 | + } | |
256 | + | |
257 | + // 보더스타일 | |
258 | + if (stylesheet.borderStyle != null) { | |
259 | + result += 'border-top:' + stylesheet.borderStyle.border_item[0].border_width + 'px;'; | |
260 | + result += 'border-right:' + stylesheet.borderStyle.border_item[1].border_width + 'px;'; | |
261 | + result += 'border-bottom:' + stylesheet.borderStyle.border_item[3].border_width + 'px;'; | |
262 | + result += 'border-left:' + stylesheet.borderStyle.border_item[2].border_width + 'px;'; | |
263 | + result += 'border-radius:' + stylesheet.borderStyle.border_item[0].border_radius + 'px '; | |
264 | + | |
265 | + result += stylesheet.borderStyle.border_item[1].border_radius + 'px '; | |
266 | + result += stylesheet.borderStyle.border_item[3].border_radius + 'px '; | |
267 | + result += stylesheet.borderStyle.border_item[2].border_radius + 'px;'; | |
268 | + result += 'border-style:' + stylesheet.borderStyle.border_style + ';'; | |
269 | + result += 'border-color:' + stylesheet.borderStyle.border_color + ';'; | |
270 | + } | |
271 | + | |
272 | + // 백그라운드 | |
273 | + if (stylesheet.background_style != null) { | |
274 | + // 백그라운드 이미지 사용 여부 | |
275 | + if (stylesheet.background_style.image_at) { | |
276 | + result += 'background-image: linear-gradient(rgba(255,255,255, ' + (1 - stylesheet.background_style.opacity / 100) + ' ), rgba(255, 255, 255, ' + (1 - stylesheet.background_style.opacity / 100) + '))'; | |
277 | + result += ', url("' + stylesheet.background_style.imageUrl + '");'; | |
278 | + result += 'background-position: center;'; | |
279 | + result += 'background-repeat: no-repeat;'; | |
280 | + | |
281 | + // 채우기 설정 | |
282 | + if (stylesheet.background_style.imageType) { | |
283 | + result += 'background-size: cover;'; | |
284 | + } else { | |
285 | + result += 'background-size: contain;'; | |
286 | + } | |
287 | + | |
288 | + result += 'background-size: cover;'; | |
289 | + | |
290 | + } else { | |
291 | + let opacity = Math.floor(stylesheet.background_style.opacity * 2.55).toString(16); | |
292 | + if (opacity == '0') { | |
293 | + opacity = '00'; | |
294 | + } | |
295 | + result += 'background-color:' + stylesheet.background_style.background_color + opacity + ';'; | |
296 | + } | |
297 | + } | |
298 | + | |
299 | + return result; | |
300 | + } | |
301 | + | |
302 | + | |
303 | + | |
304 | + | |
305 | + /** | |
306 | + * 아이디 정규식(5~20자의 영문 소문자, 숫자와 특수기호(_),(-)만 사용) | |
307 | + */ | |
308 | + Vue.config.globalProperties.$idCheck = function (data) { | |
309 | + let validateId = /^[a-z0-9_-]{5,20}$/; | |
310 | + if (validateId.test(data) === true) return true; | |
311 | + return false; | |
312 | + } | |
313 | + | |
314 | + /** | |
315 | + * 비밀번호 정규식(8~16자의 영문 대문자, 소문자, 숫자, 특수문자를 사용) | |
316 | + */ | |
317 | + Vue.config.globalProperties.$pwCheck = function (data) { | |
318 | + let validatePw = /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@#$%^&+=!]).{8,16}$/; | |
319 | + if (validatePw.test(data) === true) return true; | |
320 | + return false; | |
321 | + } | |
322 | + | |
323 | + /* 이메일 형식 검사*/ | |
324 | + Vue.config.globalProperties.$email = function (email) { | |
325 | + const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; | |
326 | + try { | |
327 | + return emailPattern.test(email); | |
328 | + } catch (e) { | |
329 | + return false; | |
330 | + } | |
331 | + } | |
332 | + | |
333 | + /** | |
334 | + * IPv4 정규식 | |
335 | + */ | |
336 | + Vue.config.globalProperties.$ipv4 = function (ip) { | |
337 | + let validateIPv4 = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/; | |
338 | + if (validateIPv4.test(ip) === true) return true; | |
339 | + return false; | |
340 | + } | |
341 | + | |
342 | + /* 3글자 마다 콤마 찍기 (돈) */ | |
343 | + Vue.config.globalProperties.$comma = function (text) { | |
344 | + try { | |
345 | + return text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); | |
346 | + } catch (e) { | |
347 | + if (text === undefined || text === null || text === "" || text.length === 0) { | |
348 | + return "-"; | |
349 | + } else { | |
350 | + return text; | |
351 | + } | |
352 | + } | |
353 | + } | |
354 | + | |
355 | + /** | |
356 | + * 사업자번호 정규식(10자리) | |
357 | + */ | |
358 | + Vue.config.globalProperties.$businessNumber = function (data) { | |
359 | + let validateBusinessNumber = /^\d{10}$/; | |
360 | + if (validateBusinessNumber.test(data) === true) return true; | |
361 | + return false; | |
362 | + } | |
363 | + | |
364 | + // 날짜 형식 바꾸기 (YYYY-MM-DD HH:MM:SS) | |
365 | + Vue.config.globalProperties.$getFullTime = function (date) { | |
366 | + return moment(date).format('YYYY-MM-DD HH:mm:ss'); | |
367 | + } | |
368 | + Vue.config.globalProperties.$getFullFileTime = function (date) { | |
369 | + return moment(date).format('YYMMDD_HHmmss'); | |
370 | + } | |
371 | + } | |
372 | +}(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/common/filters.js
... | ... | @@ -0,0 +1,46 @@ |
1 | +import moment from 'moment'; | |
2 | + | |
3 | +const filters = { | |
4 | + | |
5 | + /** | |
6 | + * 아이디 정규식(5~20자의 영문 소문자, 숫자와 특수기호(_),(-)만 사용) | |
7 | + */ | |
8 | + date(value) { | |
9 | + return moment(value).format('YYYY-MM-DD'); | |
10 | + }, | |
11 | + | |
12 | + /** | |
13 | + * 아이디 정규식(5~20자의 영문 소문자, 숫자와 특수기호(_),(-)만 사용) | |
14 | + */ | |
15 | + dateTime(value) { | |
16 | + return moment(value).format('YYYY-MM-DD HH:mm:ss'); | |
17 | + }, | |
18 | + | |
19 | + bytesToSize(bytes) { | |
20 | + | |
21 | + var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; | |
22 | + | |
23 | + if (bytes == 0) return '0 Byte'; | |
24 | + | |
25 | + var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); | |
26 | + | |
27 | + return Math.trunc((bytes / Math.pow(1024, i)) * 10) / 10 + ' ' + sizes[i]; | |
28 | + | |
29 | + }, | |
30 | + | |
31 | + /** | |
32 | + * 파일명 확장자 제거 | |
33 | + */ | |
34 | + removeExtension(value) { | |
35 | + // 마지막 '.'의 위치 | |
36 | + const lastIndex = value.lastIndexOf('.'); | |
37 | + | |
38 | + // '.'이 없으면 원본 문자열을 반환 | |
39 | + if (lastIndex === -1) return value; | |
40 | + | |
41 | + // '.' 이전까지의 문자열을 반환 | |
42 | + return value.substring(0, lastIndex); | |
43 | + } | |
44 | +} | |
45 | + | |
46 | +export default filters;(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/DepartmentTree.vue
... | ... | @@ -0,0 +1,134 @@ |
1 | +<template> | |
2 | + <li class="cursor"> | |
3 | + <div | |
4 | + :class="{ | |
5 | + 'tree-container flex align-center': true, | |
6 | + selected: node.dept_code === selectedId, | |
7 | + }" | |
8 | + @click="handleClick" | |
9 | + > | |
10 | + <p> | |
11 | + <svg-icon | |
12 | + type="mdi" | |
13 | + :width="18" | |
14 | + :height="18" | |
15 | + :path="arrowPath" | |
16 | + ></svg-icon> | |
17 | + </p> | |
18 | + <p> | |
19 | + <svg-icon | |
20 | + type="mdi" | |
21 | + :width="18" | |
22 | + :height="18" | |
23 | + :path="notePath" | |
24 | + :color="'#fbbe28'" | |
25 | + ></svg-icon> | |
26 | + </p> | |
27 | + <p class="node-text">{{ node.dept_nm }}</p> | |
28 | + </div> | |
29 | + <ul | |
30 | + v-if="node.children && node.children.length > 0" | |
31 | + class="children-node" | |
32 | + :style="{ height: toggleSelect === node.dept_code ? 'auto' : '0' }" | |
33 | + > | |
34 | + <DepartmentTree | |
35 | + v-for="(child, index) in node.children" | |
36 | + :node="child" | |
37 | + :key="index" | |
38 | + @selectedNode="parentSelectedNode" | |
39 | + /> | |
40 | + </ul> | |
41 | + </li> | |
42 | +</template> | |
43 | + | |
44 | +<script> | |
45 | +import axios from "axios"; | |
46 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
47 | +import { | |
48 | + mdiNote, | |
49 | + mdiChevronRight, | |
50 | + mdiChevronDown, | |
51 | + mdiNoteOutline, | |
52 | +} from "@mdi/js"; | |
53 | + | |
54 | +export default { | |
55 | + name: "DepartmentTree", | |
56 | + props: { | |
57 | + node: { | |
58 | + type: Object, | |
59 | + default: () => ({}), | |
60 | + }, | |
61 | + }, | |
62 | + data() { | |
63 | + return { | |
64 | + toggleSelect: null, | |
65 | + notePath: mdiNote, | |
66 | + arrowPath: mdiChevronRight, | |
67 | + selectedId: null, | |
68 | + }; | |
69 | + }, | |
70 | + methods: { | |
71 | + handleClick: function () { | |
72 | + const vm = this; | |
73 | + | |
74 | + // 마지막으로 선택한 노드만 강조되도록 | |
75 | + const nodeList = document.querySelectorAll(".tree-container"); | |
76 | + nodeList.forEach((node) => { | |
77 | + node.classList.remove("selected"); | |
78 | + }); | |
79 | + | |
80 | + // 노드 선택 시 하위 노드 표시 및 가리기 | |
81 | + vm.$emit("selectedNode", vm.node); | |
82 | + if (vm.toggleSelect === vm.node.dept_code) { | |
83 | + vm.toggleSelect = null; | |
84 | + vm.selectedId = null; | |
85 | + vm.arrowPath = mdiChevronRight; | |
86 | + vm.notePath = mdiNote; | |
87 | + } else { | |
88 | + if (!vm.node.children || vm.node.children.length === 0) { | |
89 | + vm.getChildDepartment(); | |
90 | + } | |
91 | + vm.toggleSelect = vm.node.dept_code; | |
92 | + vm.selectedId = vm.node.dept_code; | |
93 | + vm.arrowPath = mdiChevronDown; | |
94 | + vm.notePath = mdiNoteOutline; | |
95 | + } | |
96 | + }, | |
97 | + parentSelectedNode(node) { | |
98 | + this.$emit("selectedNode", node); | |
99 | + }, | |
100 | + // 깊이 2 이상일 때 자식 노드 불러오기 | |
101 | + getChildDepartment: function () { | |
102 | + axios | |
103 | + .get(`/department/tree/${this.node.dept_code}`) | |
104 | + .then((response) => { | |
105 | + this.node.children.push(...response.data.resultData.childDepartments); | |
106 | + }) | |
107 | + .catch((error) => { | |
108 | + this.$showAlert( | |
109 | + "오류", | |
110 | + "목록 불러오기 오류, 관리자에게 문의바랍니다." | |
111 | + ); | |
112 | + }); | |
113 | + }, | |
114 | + }, | |
115 | + components: { | |
116 | + SvgIcon: SvgIcon, | |
117 | + }, | |
118 | +}; | |
119 | +</script> | |
120 | + | |
121 | +<style scoped> | |
122 | +.tree-container { | |
123 | + padding: 5px 10px; | |
124 | +} | |
125 | +.children-node { | |
126 | + padding: 0 0 0 10px; | |
127 | + overflow: hidden; | |
128 | + transition: max-height 0.5s ease-in-out; | |
129 | +} | |
130 | +.node-text { | |
131 | + font-size: 1.4rem; | |
132 | + margin-left: 5px; | |
133 | +} | |
134 | +</style>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/FileTree.vue
... | ... | @@ -0,0 +1,242 @@ |
1 | +<template> | |
2 | + <li @click="handleClick(idx, $event, item)" class="cursor"> | |
3 | + <div | |
4 | + :class="{ | |
5 | + 'tree-container flex align-center': true, | |
6 | + selected: item.id === selectItem.id, | |
7 | + }" | |
8 | + > | |
9 | + <p v-if="item.children.length != 0"> | |
10 | + <svg-icon | |
11 | + type="mdi" | |
12 | + :width="18" | |
13 | + :height="18" | |
14 | + :path="arrowPath" | |
15 | + ></svg-icon> | |
16 | + </p> | |
17 | + <p> | |
18 | + <svg-icon | |
19 | + type="mdi" | |
20 | + :width="18" | |
21 | + :height="18" | |
22 | + :path="folderPath" | |
23 | + :color="'#fbbe28'" | |
24 | + ></svg-icon> | |
25 | + </p> | |
26 | + <p class="node-text">{{ item.text }}</p> | |
27 | + </div> | |
28 | + <ul | |
29 | + v-if="item.children" | |
30 | + class="children-node" | |
31 | + :style="{ height: toggleSelect === idx ? 'auto' : '0' }" | |
32 | + > | |
33 | + <TreeItem | |
34 | + :connection="connection" | |
35 | + :selectedNode="parentSelectNode" | |
36 | + :closeModal="isCloseModal" | |
37 | + :selectItem="selectItem" | |
38 | + :parentSelectItem="item" | |
39 | + v-for="(child, idx) in item.children" | |
40 | + :item="child" | |
41 | + :idx="child.id" | |
42 | + :key="idx" | |
43 | + @selectFolder="emit" | |
44 | + @isLoading="handleIsLoading" | |
45 | + @selectItem="emitFolder" | |
46 | + /> | |
47 | + </ul> | |
48 | + </li> | |
49 | +</template> | |
50 | + | |
51 | +<script> | |
52 | +import axios from "axios"; | |
53 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
54 | +import { | |
55 | + mdiFolder, | |
56 | + mdiChevronRight, | |
57 | + mdiChevronDown, | |
58 | + mdiFileMultiple, | |
59 | + mdiFolderOpen, | |
60 | +} from "@mdi/js"; | |
61 | +export default { | |
62 | + props: { | |
63 | + item: { | |
64 | + type: Object, | |
65 | + default: { | |
66 | + id: null, | |
67 | + }, | |
68 | + }, | |
69 | + idx: String, | |
70 | + selectedNode: String, | |
71 | + connection: { | |
72 | + type: Object, | |
73 | + default: {}, | |
74 | + }, | |
75 | + closeModal: Boolean, | |
76 | + isLoading: Boolean, | |
77 | + selectedId: String, | |
78 | + selectItem: { | |
79 | + type: Object, | |
80 | + default: { | |
81 | + id: null, | |
82 | + }, | |
83 | + }, | |
84 | + parentSelectItem: { | |
85 | + type: Object, | |
86 | + default: { | |
87 | + id: null, | |
88 | + }, | |
89 | + }, | |
90 | + }, | |
91 | + data() { | |
92 | + return { | |
93 | + toggleSelect: null, | |
94 | + clidrunNode: false, | |
95 | + folderPath: mdiFolder, | |
96 | + arrowPath: mdiChevronRight, | |
97 | + filePath: mdiFileMultiple, | |
98 | + selectedId: null, | |
99 | + // host_code: null, | |
100 | + parentSelectNode: null, | |
101 | + isCloseModal: false, | |
102 | + isLoading: this.isLoading, | |
103 | + parentSelectedItem: {}, | |
104 | + }; | |
105 | + }, | |
106 | + methods: { | |
107 | + handleClick: function (idx, event, item) { | |
108 | + this.$emit("isLoading", true); // 부모 컴포넌트에게 로딩중임을 알림 | |
109 | + event.stopPropagation(); | |
110 | + // 클릭한 폴더가 최상위 폴더일 때 | |
111 | + if (item.id == "/home") { | |
112 | + this.parentSelectedItem = item; // 최상위 폴더일 때는 부모 폴더가 없으므로 현재 폴더 정보를 저장 | |
113 | + } | |
114 | + // 이미 열려있는 같은 폴더를 클릭했을 때 (폴더별 열림상태 관리 필요) | |
115 | + if (this.toggleSelect === idx) { | |
116 | + if (idx === this.connection.path) { | |
117 | + // 선택된 폴더가 최상위 폴더일 때 | |
118 | + if (!item.parent) { | |
119 | + this.selectedId = null; | |
120 | + this.toggleSelect = null; | |
121 | + this.parentSelectNode = null; | |
122 | + // this.parentSelectedItem = {}; | |
123 | + this.parentSelectedItem = item; // 최상위 폴더일 때는 부모 폴더가 없으므로 현재 폴더 정보를 저장 | |
124 | + } else { | |
125 | + this.selectedId = item.parent; | |
126 | + this.toggleSelect = item.parent; | |
127 | + this.parentSelectNode = item.parent; | |
128 | + this.parentSelectedItem = this.parentSelectItem; | |
129 | + } | |
130 | + this.emit(item.parent); | |
131 | + this.arrowPath = mdiChevronRight; | |
132 | + this.folderPath = mdiFolder; | |
133 | + } else { | |
134 | + this.parentSelectNode = idx; | |
135 | + if (!item.parent) { | |
136 | + this.parentSelectedItem = item; // 최상위 폴더일 때는 부모 폴더가 없으므로 현재 폴더 정보를 저장 | |
137 | + } else { | |
138 | + this.parentSelectedItem = this.parentSelectItem; | |
139 | + } | |
140 | + if (item.children.length === 0) { | |
141 | + this.getChildren(idx, item.children); | |
142 | + } else { | |
143 | + this.emit(idx); | |
144 | + } | |
145 | + this.selectedId = this.selectItem.id; | |
146 | + this.toggleSelect = idx; | |
147 | + this.arrowPath = mdiChevronDown; | |
148 | + this.folderPath = mdiFolderOpen; | |
149 | + } | |
150 | + // 다른 폴더를 클릭했을 때 (열려있지 않은 폴더를 클릭했을 때가 되어야 함) | |
151 | + } else { | |
152 | + this.parentSelectNode = idx; | |
153 | + if (!item.parent) { | |
154 | + this.parentSelectedItem = item; // 최상위 폴더일 때는 부모 폴더가 없으므로 현재 폴더 정보를 저장 | |
155 | + } else { | |
156 | + this.parentSelectedItem = this.parentSelectItem; | |
157 | + } | |
158 | + if (item.children.length === 0) { | |
159 | + this.getChildren(idx, item.children); | |
160 | + } else { | |
161 | + this.emit(idx); | |
162 | + } | |
163 | + this.selectedId = this.selectItem.id; | |
164 | + this.toggleSelect = idx; | |
165 | + this.arrowPath = mdiChevronDown; | |
166 | + this.folderPath = mdiFolderOpen; | |
167 | + } | |
168 | + this.emitFolder(item, this.parentSelectItem); | |
169 | + }, | |
170 | + | |
171 | + emit(path) { | |
172 | + this.$emit("selectFolder", path); | |
173 | + }, | |
174 | + | |
175 | + // 자식 파일 정보 조회 | |
176 | + getChildren(path, children) { | |
177 | + const vm = this; | |
178 | + vm.connection.path = path; | |
179 | + vm.connection.type = "folder"; | |
180 | + vm.connection.depth = 1; | |
181 | + | |
182 | + axios | |
183 | + .get("/files/list", { params: vm.connection }) | |
184 | + .then((response) => { | |
185 | + let childrenList = response.data.resultData.fileList; | |
186 | + childrenList.forEach((item) => { | |
187 | + children.push(item); | |
188 | + }); | |
189 | + this.emit(path); | |
190 | + }) | |
191 | + .catch((error) => {}); | |
192 | + }, | |
193 | + | |
194 | + // 로딩 상태 변경 | |
195 | + handleIsLoading(isLoadingValue) { | |
196 | + this.$emit("isLoading", true); // 자식 컴포넌트에게 로딩중임을 알림 | |
197 | + }, | |
198 | + | |
199 | + // 선택 폴더 정보 전달 | |
200 | + emitFolder(item, parentItem) { | |
201 | + this.$emit("selectItem", item, parentItem); | |
202 | + }, | |
203 | + }, | |
204 | + | |
205 | + watch: { | |
206 | + closeModal: function (newValue) { | |
207 | + if (newValue) { | |
208 | + this.toggleSelect = null; | |
209 | + this.selectedId = null; | |
210 | + this.arrowPath = mdiChevronRight; | |
211 | + this.folderPath = mdiFolder; | |
212 | + this.isCloseModal = newValue; | |
213 | + } else { | |
214 | + this.isCloseModal = newValue; | |
215 | + } | |
216 | + }, | |
217 | + }, | |
218 | + components: { | |
219 | + SvgIcon: SvgIcon, | |
220 | + }, | |
221 | + beforeCreate() { | |
222 | + this.$options.components.TreeItem = require("./FileTree.vue").default; | |
223 | + }, | |
224 | +}; | |
225 | +</script> | |
226 | + | |
227 | +<style scoped> | |
228 | +.tree-container { | |
229 | + padding: 5px 10px; | |
230 | +} | |
231 | + | |
232 | +.children-node { | |
233 | + padding: 0 0 0 10px; | |
234 | + overflow: hidden; | |
235 | + transition: max-height 0.5s ease-in-out; | |
236 | +} | |
237 | + | |
238 | +.node-text { | |
239 | + font-size: 1.4rem; | |
240 | + margin-left: 5px; | |
241 | +} | |
242 | +</style>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/FileTreeModal.vue
... | ... | @@ -0,0 +1,115 @@ |
1 | +<template> | |
2 | + <div v-show="isModalOpen" class="modal-wrapper"> | |
3 | + <div class="modal-container"> | |
4 | + <div class="modal-title"> | |
5 | + <div class="flex justify-between align-center"> | |
6 | + <h2>폴더 선택</h2> | |
7 | + <button class="close-btn" @click="closeModal()"><svg-icon type="mdi" :width="20" :height="20" :path="closePath"></svg-icon></button> | |
8 | + </div> | |
9 | + <div> | |
10 | + <h2>현재 경로</h2> | |
11 | + <p> | |
12 | + <span>{{ selectedPath }}</span> | |
13 | + </p> | |
14 | + </div> | |
15 | + </div> | |
16 | + <div class="modal-content-monthly"> | |
17 | + <TreeItem :connection="modalConnection" :closeModal="isCloseModal" v-for="(item, idx) in modalNodes" :item="item" :idx="item.id" :key="idx" @selectFolder="selectFolder" @selectItem="handleSelectItem" /> | |
18 | + </div> | |
19 | + <div class="modal-end flex justify-end"> | |
20 | + <button class="blue-btn small-btn" v-if="selectType === 'copy'" @click="submit('복사')">복사</button> | |
21 | + <button class="blue-btn small-btn" v-else-if="selectType === 'move'" @click="submit('이동')">이동</button> | |
22 | + <button class="blue-btn small-btn" v-else-if="selectType === 'choose'" @click="submit('선택')">선택</button> | |
23 | + <button class="blue-btn small-btn" v-else-if="selectType === 'update'" @click="submit('수정')">수정</button> | |
24 | + <button class="gray-btn small-btn" @click="closeModal()">취소</button> | |
25 | + </div> | |
26 | + </div> | |
27 | + </div> | |
28 | +</template> | |
29 | +<script> | |
30 | +import axios from 'axios'; | |
31 | +import SvgIcon from '@jamescoyle/vue-icon'; | |
32 | +import TreeItem from '../component/FileTree.vue'; | |
33 | +import { mdiClose } from '@mdi/js'; | |
34 | +export default { | |
35 | + props: { | |
36 | + modalNodes: Object, | |
37 | + modalConnection: { | |
38 | + type: Object, | |
39 | + default: {}, | |
40 | + }, | |
41 | + modalOpen: Boolean, | |
42 | + selectType: Number, | |
43 | + }, | |
44 | + data() { | |
45 | + return { | |
46 | + // host_code: null, | |
47 | + // parentSelectNode: null, | |
48 | + closePath: mdiClose, | |
49 | + isModalOpen: false, | |
50 | + isCloseModal: false, | |
51 | + selectedPath: null, | |
52 | + selectItem: null, | |
53 | + } | |
54 | + }, | |
55 | + methods: { | |
56 | + selectFolder(path) { | |
57 | + this.$emit('modalSelectFolder', path) | |
58 | + this.modalConnection.path = path; | |
59 | + this.selectedPath = path; | |
60 | + }, | |
61 | + | |
62 | + closeModal() { | |
63 | + this.isCloseModal = true; | |
64 | + this.isModalOpen = false; | |
65 | + this.$emit('closeTreeModal', this.isModalOpen); | |
66 | + }, | |
67 | + | |
68 | + async submit(type) { | |
69 | + this.$emit('modalSubmit', type); | |
70 | + this.$emit('modalSelectItem', this.selectItem); | |
71 | + }, | |
72 | + | |
73 | + handleSelectItem(item, parentItem) { | |
74 | + // 현재 선택된 폴더와 클릭한 폴더가 같을 때 | |
75 | + if (this.selectedPath == item.id) { | |
76 | + this.selectItem = parentItem; | |
77 | + } else { | |
78 | + this.selectItem = item; | |
79 | + } | |
80 | + }, | |
81 | + | |
82 | + }, | |
83 | + watch: { | |
84 | + 'modalOpen': function (v) { | |
85 | + this.isModalOpen = v; | |
86 | + this.isCloseModal = !v; | |
87 | + }, | |
88 | + }, | |
89 | + computed: { | |
90 | + | |
91 | + }, | |
92 | + components: { | |
93 | + 'SvgIcon': SvgIcon, | |
94 | + 'TreeItem': TreeItem, | |
95 | + }, | |
96 | + mounted() { | |
97 | + } | |
98 | +} | |
99 | +</script> | |
100 | +<style scoped> | |
101 | +.tree-container { | |
102 | + padding: 5px 10px; | |
103 | +} | |
104 | + | |
105 | +.children-node { | |
106 | + padding: 0 0 0 10px; | |
107 | + overflow: hidden; | |
108 | + transition: max-height 0.5s ease-in-out; | |
109 | +} | |
110 | + | |
111 | +.node-text { | |
112 | + font-size: 1.4rem; | |
113 | + margin-left: 5px; | |
114 | +} | |
115 | +</style>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/LayoutTree.vue
... | ... | @@ -0,0 +1,116 @@ |
1 | +<template> | |
2 | + <li class="cursor"> | |
3 | + <div :class="{ 'tree-container flex align-center': true, 'selected': selectLayout.layout_nm === splitInfo.layout_nm }" @click.stop="clickLayout(splitInfo)" > | |
4 | + <p><svg-icon type="mdi" :width="18" :height="18" :path="arrowPath"></svg-icon></p> | |
5 | + <p><svg-icon type="mdi" :width="18" :height="18" :path="folderPath" :color="'#fbbe28'"></svg-icon></p> | |
6 | + | |
7 | + <p class="node-text">{{ splitInfo.layout_nm}}</p> | |
8 | + </div> | |
9 | + <ul v-if="splitInfo.children.length > 0 " class="children-node" style="height: auto;"> | |
10 | + <TreeItem v-for="(node , indx) in splitInfo.children" :splitInfo="node" :selectLayout = 'selectLayout' :key="indx" @changeselectLayout = "changeselectLayout" /> | |
11 | + </ul> | |
12 | + | |
13 | + <!-- <ul v-if="item.children" class="children-node" :style="{ 'height': toggleSelect === idx ? 'auto' : '0' }"> | |
14 | + <TreeItem :connection="connection" :selectedNode="parentSelectNode" v-for="(child, idx) in item.children" :item="child" :idx="child.id" | |
15 | + :key="idx" @selectForder="emit"/> | |
16 | + </ul> --> | |
17 | + </li> | |
18 | +</template> | |
19 | + | |
20 | +<script> | |
21 | +import axios from 'axios'; | |
22 | +import SvgIcon from '@jamescoyle/vue-icon'; | |
23 | +import {mdiLandPlots, mdiChevronRight, mdiChevronDown, mdiRhombusSplit, mdiFolderOpen } from '@mdi/js'; | |
24 | +export default { | |
25 | + props: { | |
26 | + splitInfo:{ | |
27 | + type: Object, | |
28 | + }, | |
29 | + selectLayout : { | |
30 | + type: Object, | |
31 | + } | |
32 | + }, | |
33 | + data() { | |
34 | + return { | |
35 | + currentSelectLayout : this.selectLayout, | |
36 | + toggleSelect: false, | |
37 | + clidrunNode: false, | |
38 | + folderPath: mdiLandPlots, | |
39 | + arrowPath: mdiChevronRight, | |
40 | + filePath: mdiRhombusSplit, | |
41 | + selectedId: null, | |
42 | + // host_code: null, | |
43 | + parentSelectNode: null, | |
44 | + } | |
45 | + }, | |
46 | + methods: { | |
47 | + handleClick: function (idx, event, item) { | |
48 | + | |
49 | + }, | |
50 | + | |
51 | + emit(path){ | |
52 | + this.$emit('selectForder', path) | |
53 | + }, | |
54 | + | |
55 | + getChildren(path, children) { | |
56 | + const vm = this; | |
57 | + vm.connection.path = path | |
58 | + vm.connection.type = 'folder' | |
59 | + vm.connection.depth = 1 | |
60 | + | |
61 | + axios.get('/files/list', {params: vm.connection}) | |
62 | + .then(response => { | |
63 | + let chilerenList = response.data.resultData.fileList | |
64 | + chilerenList.forEach ( item => { | |
65 | + children.push(item) | |
66 | + }) | |
67 | + this.$emit('selectForder', path) | |
68 | + }).catch(error => { | |
69 | + | |
70 | + }) | |
71 | + }, | |
72 | + | |
73 | + clickLayout : function(splitInfo){ | |
74 | + this.toggleSelect = !this.toggleSelect; | |
75 | + this.$emit('changeselectLayout', splitInfo); | |
76 | + }, | |
77 | + | |
78 | + changeselectLayout : function(layout){ | |
79 | + this.$emit('changeselectLayout', layout); | |
80 | + }, | |
81 | + }, | |
82 | + watch: { | |
83 | + selectLayout: function (v) { | |
84 | + this.currentSelectLayout = v; | |
85 | + }, | |
86 | + }, | |
87 | + computed: { | |
88 | + | |
89 | + }, | |
90 | + components: { | |
91 | + 'SvgIcon': SvgIcon | |
92 | + }, | |
93 | + beforeCreate() { | |
94 | + this.$options.components.TreeItem = require('./LayoutTree.vue').default; | |
95 | + }, | |
96 | + mounted() { | |
97 | + } | |
98 | +} | |
99 | +</script> | |
100 | + | |
101 | +<style scoped> | |
102 | +.tree-container { | |
103 | + padding: 5px 10px; | |
104 | +} | |
105 | + | |
106 | +.children-node { | |
107 | + padding: 0 0 0 10px; | |
108 | + overflow: hidden; | |
109 | + transition: max-height 0.5s ease-in-out; | |
110 | +} | |
111 | + | |
112 | +.node-text { | |
113 | + font-size: 1.4rem; | |
114 | + margin-left: 5px; | |
115 | +} | |
116 | +</style>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/PaginationButton.vue
... | ... | @@ -0,0 +1,170 @@ |
1 | +<template lang="html"> | |
2 | + <div class="component-pagination" id="pagination"> | |
3 | + <div class="pagination-button-type"> | |
4 | + <a class="first-page" @click="excute(1)" title="첫 번 째 페이지로 이동"></a> | |
5 | + <a class="prev" @click="excute(currentPage - 1)" title="이전 페이지로 이동"></a> | |
6 | + <a @click="excute(i)" v-for="i in createRange" :class="{ 'active': currentPage == i }">{{ i }}</a> | |
7 | + <a class="next" @click="excute(currentPage + 1)" title="다음 페이지로 이동"></a> | |
8 | + <a class="end-page" @click="excute(maxEndPage)" title="마지막 페이지로 이동"></a> | |
9 | + </div> | |
10 | + </div> | |
11 | +</template> | |
12 | + | |
13 | +<script> | |
14 | +export default { | |
15 | + props: { | |
16 | + currentPage: { | |
17 | + type: Number, | |
18 | + default: 0 | |
19 | + }, | |
20 | + perPage: { | |
21 | + type: Number, | |
22 | + default: 10 | |
23 | + }, | |
24 | + totalCount: { | |
25 | + type: Number, | |
26 | + default: 0 | |
27 | + }, | |
28 | + maxRange: { | |
29 | + type: Number, | |
30 | + default: 5 | |
31 | + }, | |
32 | + click: Function | |
33 | + }, | |
34 | + emits: ['update:currentPage'], | |
35 | + data: function () { | |
36 | + return { | |
37 | + //data | |
38 | + } | |
39 | + }, | |
40 | + methods: { | |
41 | + excute: function (i) { | |
42 | + if (i >= 1 && i <= this.maxEndPage) { | |
43 | + if (i != this.currentPage) { | |
44 | + this.$emit('update:currentPage', i);//부모 currentPage에 선택한 page번호 할당 | |
45 | + this.click(i);//부모 function 실행 | |
46 | + } else { | |
47 | + return; | |
48 | + } | |
49 | + } else { | |
50 | + alert('이동할 페이지가 없습니다.'); | |
51 | + } | |
52 | + } | |
53 | + }, | |
54 | + computed: { | |
55 | + startPage: function () { | |
56 | + return Math.floor((this.currentPage - 1) / this.maxRange) * this.maxRange + 1; | |
57 | + }, | |
58 | + endPage: function () { | |
59 | + if (this.maxEndPage < this.currentEndPage) { | |
60 | + return this.maxEndPage; | |
61 | + } else { | |
62 | + return this.currentEndPage; | |
63 | + } | |
64 | + }, | |
65 | + currentEndPage: function () { | |
66 | + return this.maxRange * Math.ceil((this.currentPage / this.maxRange)); | |
67 | + }, | |
68 | + maxEndPage: function () { | |
69 | + return Math.ceil(this.totalCount / this.perPage); | |
70 | + }, | |
71 | + createRange: function () { | |
72 | + var range = []; | |
73 | + for (var i = this.startPage; i <= this.endPage; i++) { | |
74 | + range.push(i); | |
75 | + } | |
76 | + if (range.length == 0) { | |
77 | + range.push(1); | |
78 | + } | |
79 | + return range; | |
80 | + } | |
81 | + }, | |
82 | + watch: { | |
83 | + | |
84 | + }, | |
85 | + //beforeCreate: function () {}, | |
86 | + //created: function () {}, | |
87 | + //beforeUpdate: function () {}, | |
88 | + //updated: function () {}, | |
89 | + mounted: function () { | |
90 | + } | |
91 | +} | |
92 | +</script> | |
93 | + | |
94 | +<style scoped> | |
95 | +/*PAGINATION NEW START*/ | |
96 | +.component-pagination { | |
97 | + padding: 15px 0; | |
98 | +} | |
99 | + | |
100 | +.pagination-button-type { | |
101 | + display: flex; | |
102 | + align-items: center; | |
103 | + justify-content: center; | |
104 | +} | |
105 | + | |
106 | +.pagination-button-type a { | |
107 | + display: inline-block; | |
108 | + width: 30px; | |
109 | + height: 30px; | |
110 | + padding: 5px; | |
111 | + text-align: center; | |
112 | + line-height: 20px; | |
113 | + cursor: pointer; | |
114 | +} | |
115 | + | |
116 | +/* 정우추가 */ | |
117 | +.pagination-button-type a.prev:after { | |
118 | + content: "\003C"; | |
119 | + font-family: 'KoPub_Dotum'; | |
120 | + font-weight: 900; | |
121 | +} | |
122 | + | |
123 | +.pagination-button-type a.next:after { | |
124 | + content: "\003E"; | |
125 | + font-family: 'KoPub_Dotum'; | |
126 | + font-weight: 900; | |
127 | +} | |
128 | + | |
129 | +.pagination-button-type a.first-page:after { | |
130 | + content: "\003C\003C"; | |
131 | + font-family: 'KoPub_Dotum'; | |
132 | + font-weight: 900; | |
133 | +} | |
134 | + | |
135 | +.pagination-button-type a.end-page:after { | |
136 | + content: "\003E\003E"; | |
137 | + font-family: 'KoPub_Dotum'; | |
138 | + font-weight: 900; | |
139 | +} | |
140 | + | |
141 | +.pagination-button-type a.active { | |
142 | + background-color: #213f99; | |
143 | + color: #fff; | |
144 | + cursor: default !important; | |
145 | + border-radius: 50px; | |
146 | +} | |
147 | + | |
148 | +.pagination-button-type a:hover:not(.active) { | |
149 | + background-color: #213f99; | |
150 | + color: #fff; | |
151 | + border-radius: 50px; | |
152 | +} | |
153 | + | |
154 | +.pagination-button-type a:first-child { | |
155 | + border-top-left-radius: 0px; | |
156 | + border-bottom-left-radius: 0px; | |
157 | +} | |
158 | + | |
159 | +.pagination-button-type a:last-child { | |
160 | + border-top-right-radius: 0px; | |
161 | + border-bottom-right-radius: 0px; | |
162 | +} | |
163 | + | |
164 | +/*PAGINATION NEW END*/ | |
165 | + | |
166 | +@media screen and (max-width:479px) { | |
167 | + .pagination-button-type a { | |
168 | + margin: 3px; | |
169 | + } | |
170 | +}</style>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/ScheculerCreateComp.vue
... | ... | @@ -0,0 +1,885 @@ |
1 | +<template> | |
2 | + <div class="table-zone"> | |
3 | + <table class="form-table"> | |
4 | + <colgroup> | |
5 | + <col style="width: 10%" /> | |
6 | + <col style="width: 40%" /> | |
7 | + </colgroup> | |
8 | + <tbody> | |
9 | + <tr> | |
10 | + <th>제목</th> | |
11 | + <td> | |
12 | + <input | |
13 | + type="text" | |
14 | + name="" | |
15 | + id="inputid" | |
16 | + class="full-input" | |
17 | + v-model="title" | |
18 | + /> | |
19 | + </td> | |
20 | + </tr> | |
21 | + <tr> | |
22 | + <th>내용</th> | |
23 | + <td><textarea rows="4" v-model="dc"></textarea></td> | |
24 | + </tr> | |
25 | + <tr> | |
26 | + <th>작업타입</th> | |
27 | + <td> | |
28 | + <div class="input-container flex"> | |
29 | + <label class="radio-label"> | |
30 | + <input | |
31 | + type="radio" | |
32 | + name="radio2" | |
33 | + :value="false" | |
34 | + class="custom-radiobox" | |
35 | + v-model="isUsehighClassOption" | |
36 | + /> | |
37 | + <span>일반 방식</span> | |
38 | + </label> | |
39 | + <label class="radio-label"> | |
40 | + <input | |
41 | + type="radio" | |
42 | + name="radio2" | |
43 | + :value="true" | |
44 | + class="custom-radiobox" | |
45 | + v-model="isUsehighClassOption" | |
46 | + /> | |
47 | + <span>커스텀 방식</span> | |
48 | + </label> | |
49 | + </div> | |
50 | + </td> | |
51 | + </tr> | |
52 | + <tr> | |
53 | + <th>작업타입 설정</th> | |
54 | + <td> | |
55 | + <div class="input-container flex"> | |
56 | + <label class="radio-label"> | |
57 | + <input | |
58 | + type="radio" | |
59 | + name="Cyclical" | |
60 | + :value="true" | |
61 | + class="custom-radiobox" | |
62 | + v-model="isCyclical" | |
63 | + /> | |
64 | + <span>반복작업(반복주기 설정)</span> | |
65 | + </label> | |
66 | + </div> | |
67 | + </td> | |
68 | + </tr> | |
69 | + <tr> | |
70 | + <th>반복주기 설정</th> | |
71 | + <td> | |
72 | + <div | |
73 | + class="flex justify-start" | |
74 | + v-show="isUsehighClassOption == false" | |
75 | + > | |
76 | + <select | |
77 | + name="cycle" | |
78 | + v-model="cronItem" | |
79 | + :disabled="isUsehighClassOption == true" | |
80 | + style="margin-left: 5px; margin-bottom: 5px" | |
81 | + > | |
82 | + <option | |
83 | + v-for="(value, key, i) in cronItems" | |
84 | + :key="i" | |
85 | + :value="key" | |
86 | + > | |
87 | + 매 {{ value.title }} | |
88 | + </option> | |
89 | + <option value="ALL">전체</option> | |
90 | + </select> | |
91 | + <select | |
92 | + name="year" | |
93 | + v-model="cronMap.YEAR" | |
94 | + v-if="isUsehighClassOption == false" | |
95 | + v-show="cronItem == 'YEAR' || cronItem == 'ALL'" | |
96 | + style="margin-left: 5px; margin-bottom: 5px" | |
97 | + > | |
98 | + <option value="*">매년</option> | |
99 | + <option | |
100 | + v-for="(item, idx) in cronItems.YEAR.values" | |
101 | + :key="idx" | |
102 | + :value="item" | |
103 | + > | |
104 | + {{ item }}년 | |
105 | + </option> | |
106 | + </select> | |
107 | + <select | |
108 | + name="months" | |
109 | + v-model="cronMap.MONTHS" | |
110 | + v-show=" | |
111 | + cronItem == 'MONTHS' || | |
112 | + cronItem == 'YEAR' || | |
113 | + cronItem == 'ALL' | |
114 | + " | |
115 | + style="margin-left: 5px; margin-bottom: 5px" | |
116 | + > | |
117 | + <option value="*">매월</option> | |
118 | + <option | |
119 | + v-for="(item, idx) in cronItems.MONTHS.values" | |
120 | + :key="idx" | |
121 | + :value="item" | |
122 | + > | |
123 | + {{ item }}월 | |
124 | + </option> | |
125 | + </select> | |
126 | + <select | |
127 | + name="weeks" | |
128 | + v-model="cronMap.WEEKS" | |
129 | + v-show="cronItem == 'WEEKS' || cronItem == 'ALL'" | |
130 | + style="margin-left: 5px; margin-bottom: 5px" | |
131 | + > | |
132 | + <option | |
133 | + v-for="(item, idx) in cronItems.WEEKS.values" | |
134 | + :key="idx" | |
135 | + :value="idx + ''" | |
136 | + > | |
137 | + {{ item }}요일 | |
138 | + </option> | |
139 | + <option value="1,2,3,4,5">평일</option> | |
140 | + <option value="0,6">주말(토,일)</option> | |
141 | + <option value="?" v-show="cronItem != 'WEEKS'">없음</option> | |
142 | + </select> | |
143 | + <select | |
144 | + name="days" | |
145 | + v-model="cronMap.DAYS" | |
146 | + v-show=" | |
147 | + cronItem == 'DAYS' || | |
148 | + cronItem == 'MONTHS' || | |
149 | + cronItem == 'YEAR' || | |
150 | + cronItem == 'ALL' | |
151 | + " | |
152 | + style="margin-left: 5px; margin-bottom: 5px" | |
153 | + > | |
154 | + <option value="*" v-show="cronItem == 'DAYS'">매일</option> | |
155 | + <option | |
156 | + v-for="(item, idx) in cronItems.DAYS.values" | |
157 | + :key="idx" | |
158 | + :value="item" | |
159 | + > | |
160 | + {{ item }}일 | |
161 | + </option> | |
162 | + <option value="L">말일</option> | |
163 | + </select> | |
164 | + <select | |
165 | + name="hours" | |
166 | + v-model="cronMap.HOURS" | |
167 | + v-show=" | |
168 | + cronItem == 'HOURS' || | |
169 | + cronItem == 'DAYS' || | |
170 | + cronItem == 'WEEKS' || | |
171 | + cronItem == 'MONTHS' || | |
172 | + cronItem == 'YEAR' || | |
173 | + cronItem == 'ALL' | |
174 | + " | |
175 | + style="margin-left: 5px; margin-bottom: 5px" | |
176 | + > | |
177 | + <option | |
178 | + v-for="(item, idx) in cronItems.HOURS.values" | |
179 | + :key="idx" | |
180 | + :value="item" | |
181 | + > | |
182 | + {{ item }}시 | |
183 | + </option> | |
184 | + </select> | |
185 | + <select | |
186 | + name="minute" | |
187 | + v-model="cronMap.MINUTES" | |
188 | + v-show=" | |
189 | + cronItem == 'MINUTES' || | |
190 | + cronItem == 'HOURS' || | |
191 | + cronItem == 'DAYS' || | |
192 | + cronItem == 'WEEKS' || | |
193 | + cronItem == 'MONTHS' || | |
194 | + cronItem == 'YEAR' || | |
195 | + cronItem == 'ALL' | |
196 | + " | |
197 | + style="margin-left: 5px; margin-bottom: 5px" | |
198 | + > | |
199 | + <option | |
200 | + v-for="(item, idx) in cronItems.MINUTES.values" | |
201 | + :key="idx" | |
202 | + :value="item" | |
203 | + > | |
204 | + {{ item }}분 | |
205 | + </option> | |
206 | + </select> | |
207 | + <select | |
208 | + name="seconds" | |
209 | + v-model="cronMap.SECONDS" | |
210 | + v-show=" | |
211 | + cronItem == 'SECONDS' || | |
212 | + 'MINUTES' || | |
213 | + cronItem == 'HOURS' || | |
214 | + cronItem == 'DAYS' || | |
215 | + cronItem == 'WEEKS' || | |
216 | + cronItem == 'MONTHS' || | |
217 | + cronItem == 'YEAR' || | |
218 | + cronItem == 'ALL' | |
219 | + " | |
220 | + style="margin-left: 5px; margin-bottom: 5px" | |
221 | + > | |
222 | + <option | |
223 | + v-for="(item, idx) in cronItems.SECONDS.values" | |
224 | + :key="idx" | |
225 | + :value="item" | |
226 | + > | |
227 | + {{ item }}초 | |
228 | + </option> | |
229 | + </select> | |
230 | + </div> | |
231 | + <div class="flex justify-start" v-show="isUsehighClassOption"> | |
232 | + <select | |
233 | + name="cycle" | |
234 | + v-model="cronItem" | |
235 | + :disabled="isUsehighClassOption == true" | |
236 | + style="width: 100px; margin-left: 5px; margin-bottom: 5px" | |
237 | + > | |
238 | + <option | |
239 | + v-for="(value, key, i) in cronItems" | |
240 | + :key="i" | |
241 | + :value="key" | |
242 | + > | |
243 | + 매 {{ value.title }} | |
244 | + </option> | |
245 | + <option value="ALL">전체 주기</option> | |
246 | + </select> | |
247 | + <label for="year"> | |
248 | + <input | |
249 | + type="text" | |
250 | + id="year" | |
251 | + name="year" | |
252 | + v-model="cronMap.YEAR" | |
253 | + style="margin-bottom: 5px; width: 100px" | |
254 | + /> | |
255 | + </label> | |
256 | + <label for="months"> | |
257 | + <input | |
258 | + type="text" | |
259 | + id="months" | |
260 | + name="months" | |
261 | + v-model="cronMap.MONTHS" | |
262 | + style="width: 100px; margin-bottom: 5px" | |
263 | + /> | |
264 | + </label> | |
265 | + <label for="weeks"> | |
266 | + <input | |
267 | + type="text" | |
268 | + id="weeks" | |
269 | + name="weeks" | |
270 | + v-model="cronMap.WEEKS" | |
271 | + style="width: 100px; margin-bottom: 5px" | |
272 | + /> | |
273 | + </label> | |
274 | + <label for="days"> | |
275 | + <input | |
276 | + type="text" | |
277 | + id="days" | |
278 | + name="days" | |
279 | + v-model="cronMap.DAYS" | |
280 | + style="width: 100px; margin-bottom: 5px" | |
281 | + /> | |
282 | + </label> | |
283 | + <label for="hours"> | |
284 | + <input | |
285 | + type="text" | |
286 | + id="hours" | |
287 | + name="hours" | |
288 | + v-model="cronMap.HOURS" | |
289 | + style="width: 100px; margin-bottom: 5px" | |
290 | + /> | |
291 | + </label> | |
292 | + <label for="minutes"> | |
293 | + <input | |
294 | + type="text" | |
295 | + id="minutes" | |
296 | + name="minutes" | |
297 | + v-model="cronMap.MINUTES" | |
298 | + style="width: 100px; margin-bottom: 5px" | |
299 | + /> | |
300 | + </label> | |
301 | + <label for="seconds"> | |
302 | + <input | |
303 | + type="text" | |
304 | + id="seconds" | |
305 | + name="seconds" | |
306 | + v-model="cronMap.SECONDS" | |
307 | + style="width: 100px; margin-bottom: 5px" | |
308 | + /> | |
309 | + </label> | |
310 | + </div> | |
311 | + </td> | |
312 | + </tr> | |
313 | + <tr v-show="isCyclical"> | |
314 | + <th>작업타입 설정</th> | |
315 | + <td> | |
316 | + <div class="input-container flex"> | |
317 | + <p> | |
318 | + <b> {{ getCronStr }} </b><br />(표현식:{{ getCron }}) | |
319 | + </p> | |
320 | + </div> | |
321 | + </td> | |
322 | + </tr> | |
323 | + </tbody> | |
324 | + </table> | |
325 | + </div> | |
326 | +</template> | |
327 | + | |
328 | +<script> | |
329 | +//크론 설정값 초기화 | |
330 | +var cronMapInit = { | |
331 | + SECONDS: "0", | |
332 | + MINUTES: "0", | |
333 | + HOURS: "0", | |
334 | + DAYS: "*", | |
335 | + WEEKS: "?", | |
336 | + MONTHS: "*", | |
337 | + YEAR: "*", | |
338 | +}; | |
339 | + | |
340 | +export default { | |
341 | + props: { | |
342 | + schedule: { | |
343 | + type: Object, | |
344 | + default: {}, | |
345 | + }, | |
346 | + }, | |
347 | + data() { | |
348 | + return { | |
349 | + //고오급 옵션(개발자용) | |
350 | + isUsehighClassOption: false, | |
351 | + | |
352 | + //반복작업 여부 : 반복 작업(반복 주기 설정):true, 예약 작업(작업 날짜 설정 - 1회성):false | |
353 | + isCyclical: true, | |
354 | + | |
355 | + //Cron 표현식에 필요한 요소's | |
356 | + cronItems: { | |
357 | + SECONDS: { | |
358 | + title: "초", | |
359 | + values: [], | |
360 | + }, | |
361 | + MINUTES: { | |
362 | + title: "분", | |
363 | + values: [], | |
364 | + }, | |
365 | + HOURS: { | |
366 | + title: "시", | |
367 | + values: [], | |
368 | + }, | |
369 | + DAYS: { | |
370 | + title: "일", | |
371 | + values: [], | |
372 | + }, | |
373 | + WEEKS: { | |
374 | + title: "요일", | |
375 | + values: ["일", "월", "화", "수", "목", "금", "토"], //일:0, 월:1, 화:2, 수:3, 목:4, 금:5, 토:6 | |
376 | + }, | |
377 | + MONTHS: { | |
378 | + title: "월", | |
379 | + values: [], | |
380 | + }, | |
381 | + YEAR: { | |
382 | + title: "년", | |
383 | + values: [], | |
384 | + }, | |
385 | + }, | |
386 | + | |
387 | + //Cron 표현식에 필요한 요소 중 선택된 요소 | |
388 | + cronItem: "ALL", | |
389 | + | |
390 | + //작업명 | |
391 | + title: "", | |
392 | + | |
393 | + // 설명 | |
394 | + dc: "", | |
395 | + | |
396 | + //설정된 크론값 | |
397 | + cronMap: JSON.parse(JSON.stringify(cronMapInit)), | |
398 | + | |
399 | + //크론 표현식 설명 키워드 | |
400 | + cronKeyWord: { | |
401 | + "*": "매", | |
402 | + "?": "", | |
403 | + L: "마지막", | |
404 | + "/": "부터 매", | |
405 | + W: "에 가장 가까운 평일", //DAYS(일)만 사용 | |
406 | + "#": "번째 주", //WEEK(요일)만 사용 | |
407 | + }, | |
408 | + }; | |
409 | + }, | |
410 | + computed: { | |
411 | + getCron() { | |
412 | + var c = this.cronMap; | |
413 | + return ( | |
414 | + c.SECONDS + | |
415 | + " " + | |
416 | + c.MINUTES + | |
417 | + " " + | |
418 | + c.HOURS + | |
419 | + " " + | |
420 | + c.DAYS + | |
421 | + " " + | |
422 | + c.WEEKS + | |
423 | + " " + | |
424 | + c.MONTHS + | |
425 | + " " + | |
426 | + c.YEAR | |
427 | + ); | |
428 | + }, | |
429 | + | |
430 | + getCronStr() { | |
431 | + var cronStr = ""; | |
432 | + cronStr += this.getCronWord("YEAR", this.cronMap.YEAR); | |
433 | + cronStr += " " + this.getCronWord("MONTHS", this.cronMap.MONTHS); | |
434 | + if (this.cronItem != "DAYS") { | |
435 | + cronStr += " " + this.getCronWord("WEEKS", this.cronMap.WEEKS); | |
436 | + } | |
437 | + if (this.cronItem != "WEEKS") { | |
438 | + cronStr += " " + this.getCronWord("DAYS", this.cronMap.DAYS); | |
439 | + } | |
440 | + cronStr += " " + this.getCronWord("HOURS", this.cronMap.HOURS); | |
441 | + cronStr += " " + this.getCronWord("MINUTES", this.cronMap.MINUTES); | |
442 | + cronStr += " " + this.getCronWord("SECONDS", this.cronMap.SECONDS); | |
443 | + | |
444 | + return cronStr; | |
445 | + }, | |
446 | + }, | |
447 | + mounted() { | |
448 | + this.fnSarchCronItemValue(); // 반복 주기 설정을 위한 selectbox 내부 데이터(최초 1회 실행) | |
449 | + this.initEvent(); | |
450 | + this.isUsehighClassOption = false; | |
451 | + }, | |
452 | + methods: { | |
453 | + // cronItem value | |
454 | + fnSarchCronItemValue() { | |
455 | + this.fnValueLoop("SECONDS", 59); | |
456 | + this.fnValueLoop("MINUTES", 59); | |
457 | + this.fnValueLoop("HOURS", 23); | |
458 | + this.fnValueLoop("DAYS", 31); | |
459 | + this.fnValueLoop("MONTHS", 12); | |
460 | + this.fnValueLoop("YEAR", 5); | |
461 | + }, | |
462 | + fnValueLoop(type, number) { | |
463 | + let flag = number + 1; | |
464 | + if (type == "YEAR") { | |
465 | + let now = new Date(); | |
466 | + let year = now.getFullYear(); | |
467 | + for (let i = year; i < year + flag; i++) { | |
468 | + this.cronItems.YEAR.values.push(i.toString()); | |
469 | + } | |
470 | + } else { | |
471 | + for (let i = 0; i < flag; i++) { | |
472 | + this.cronItems[type].values.push(i.toString()); | |
473 | + } | |
474 | + } | |
475 | + }, | |
476 | + // 초기화 | |
477 | + initEvent() { | |
478 | + if (!this.$isEmpty(this.schedule.schdul_id)) { | |
479 | + this.cronItem = "ALL"; | |
480 | + this.title = this.schedule.sj; | |
481 | + this.dc = this.schedule.dc; | |
482 | + this.isCyclical = this.schedule.cycle_at; | |
483 | + | |
484 | + var tempcron = this.schedule.cron.split(" "); | |
485 | + this.cronMap.SECONDS = tempcron[0]; | |
486 | + this.cronMap.MINUTES = tempcron[1]; | |
487 | + this.cronMap.HOURS = tempcron[2]; | |
488 | + this.cronMap.DAYS = tempcron[3]; | |
489 | + this.cronMap.WEEKS = tempcron[4]; | |
490 | + this.cronMap.MONTHS = tempcron[5]; | |
491 | + this.cronMap.YEAR = tempcron[6]; | |
492 | + | |
493 | + this.fnJoinCronValue(); | |
494 | + } | |
495 | + }, | |
496 | + fnJoinCronValue() { | |
497 | + for (let type in this.cronItems) { | |
498 | + for (let cronItem of this.cronItems[type].values) { | |
499 | + if (this.cronMap[type] == cronItem) { | |
500 | + this.cronItem = type; | |
501 | + break; | |
502 | + } | |
503 | + } | |
504 | + } | |
505 | + }, | |
506 | + | |
507 | + //개발자 옵션 (비밀) | |
508 | + highClassSetting() { | |
509 | + if (this.isUsehighClassOption == true) { | |
510 | + this.cronMap = JSON.parse(JSON.stringify(cronMapInit)); | |
511 | + this.isUsehighClassOption = false; | |
512 | + this.cronItem = "DAYS"; | |
513 | + } else { | |
514 | + this.isUsehighClassOption = true; | |
515 | + this.cronItem = "ALL"; | |
516 | + } | |
517 | + }, | |
518 | + | |
519 | + //Cron 표현 단어 해석 | |
520 | + //cronItem: SECONDS, MINUTES, HOURS, DAYS, WEEKS, MONTHS | |
521 | + //value: 값 - ex) 3#2 | |
522 | + getCronWord(cronItem, value) { | |
523 | + if (this.$isEmpty(this.cronItems[cronItem]) == true) { | |
524 | + /* 오류 발생 여부 */ | |
525 | + return "(Error - cronItem)"; | |
526 | + } | |
527 | + | |
528 | + if (this.$isEmpty(value) == true) { | |
529 | + /* 오류 발생 여부 */ | |
530 | + return "(Error - 값 없음)"; | |
531 | + } | |
532 | + | |
533 | + //초 or 분 or 시 or 일 or 요일 or 월 | |
534 | + var unit = this.cronItems[cronItem].title; | |
535 | + | |
536 | + /****** value의 값이 cronKeyWord에 포함되어 있는지 검색 ******/ | |
537 | + var isFind = false; | |
538 | + var findInfo = { | |
539 | + key: null, //*, ?, L, /, W, # | |
540 | + index: -1, //key를 찾은 위치 | |
541 | + }; | |
542 | + for (var key in this.cronKeyWord) { | |
543 | + var index = value.indexOf(key); | |
544 | + if (index > -1) { | |
545 | + isFind = true; | |
546 | + | |
547 | + findInfo.key = key; | |
548 | + findInfo.index = index; | |
549 | + | |
550 | + break; | |
551 | + } | |
552 | + } | |
553 | + /****** value의 값이 cronKeyWord에 포함되어 있는지 검색 ******/ | |
554 | + | |
555 | + /************* 단어 만들기 *************/ | |
556 | + var word = ""; | |
557 | + | |
558 | + //cronKeyWord의 key(*, ?, L, /, W, #)값 중 value값에 포함된 단어가 있을 때 | |
559 | + if (isFind == true) { | |
560 | + //value == '*' or '?' or 'L' | |
561 | + if (this.cronKeyWord[value] != undefined) { | |
562 | + word = this.cronKeyWord[value]; | |
563 | + if (this.cronKeyWord[value] != "") { | |
564 | + word += unit; | |
565 | + } | |
566 | + } | |
567 | + | |
568 | + //value == '/' or 'W' or '#' | |
569 | + else { | |
570 | + var keyowrd = this.cronKeyWord[findInfo.key]; | |
571 | + if (key == "/") { | |
572 | + var v1 = value.substring(0, findInfo.index); | |
573 | + var v2 = value.substring(findInfo.index + 1, value.length); | |
574 | + | |
575 | + //getConvertWeek() : cronItem이 'WEEKS' 일때 -> '숫자 요일' 값을 '문자 요일' 값으로 바꿈 | |
576 | + v1 = this.getConvertWeek(cronItem, v1); | |
577 | + v2 = this.getConvertWeek(cronItem, v2); | |
578 | + | |
579 | + word = v1 + unit + " " + keyowrd + " " + (v2 + unit) + "씩"; | |
580 | + } else if (key == "W" && cronItem == "DAYS") { | |
581 | + var v = value.replace("W"); | |
582 | + word = v + unit + keyword; | |
583 | + } else if (key == "#" && cronItem == "WEEKS") { | |
584 | + var v1 = value.substring(0, findInfo.index); | |
585 | + var v2 = value.substring(findInfo.index + 1, value.length); | |
586 | + | |
587 | + //getConvertWeek() : cronItem이 'WEEKS' 일때 -> '숫자 요일' 값을 '문자 요일' 값으로 바꿈 | |
588 | + v2 = this.getConvertWeek(cronItem, v2); | |
589 | + | |
590 | + word = v1 + keyowrd + " " + (v2 + unit); | |
591 | + } else { | |
592 | + word = "(Error - '" + key + "'의 표현이 잘못됨)"; | |
593 | + } | |
594 | + } | |
595 | + } | |
596 | + | |
597 | + //날짜를 나타내는 단어 일 확률이 높음 | |
598 | + else { | |
599 | + //getConvertWeek() : cronItem이 'WEEKS' 일때 -> '숫자 요일' 값을 '문자 요일' 값으로 바꿈 | |
600 | + value = this.getConvertWeek(cronItem, value); | |
601 | + word = value + unit; | |
602 | + } | |
603 | + /************* 단어 만들기 *************/ | |
604 | + | |
605 | + return word; | |
606 | + }, | |
607 | + | |
608 | + //cronItem이 WEEKS 일때 만, index(value)로 '요일' 찾기 | |
609 | + //cronItem이 WEEKS가 아닐 때, value값을 그대로 돌려줌 | |
610 | + getConvertWeek(cronItem, value) { | |
611 | + if (cronItem == "WEEKS") { | |
612 | + try { | |
613 | + if (value.indexOf(",") > -1) { | |
614 | + var result = ""; | |
615 | + var values = value.split(","); | |
616 | + for (var i = 0; i < values.length; i++) { | |
617 | + //문자 형태의 숫자를 -> 숫자 형태로 바꿈 | |
618 | + var index = Number(values[i].trim()); | |
619 | + //'요일'을 가지고 옴 | |
620 | + result += this.cronItems.WEEKS.values[index]; | |
621 | + //마지막 전 까지만 ','를 붙여줌 | |
622 | + if (i < values.length - 1) { | |
623 | + result += ", "; | |
624 | + } | |
625 | + } | |
626 | + | |
627 | + if (result.indexOf("undefined") > -1) { | |
628 | + result = "(Error - 요일의 값의 '" + value + "' 표현이 잘못됨)"; | |
629 | + } else { | |
630 | + return result; | |
631 | + } | |
632 | + } else if (value.indexOf("-") > -1) { | |
633 | + var result = ""; | |
634 | + //'-'가 들어간 위치를 가지고옴 | |
635 | + var index = value.indexOf("-"); | |
636 | + //'-' 앞 뒤의 값을 가지고 옴 | |
637 | + var v1 = value.substring(0, index); | |
638 | + var v2 = value.substring(index + 1, value.length); | |
639 | + | |
640 | + //'-' 앞 뒤의 값을 숫자 형태로 바꿔줌 | |
641 | + v1 = Number(v1.trim()); | |
642 | + v2 = Number(v2.trim()); | |
643 | + | |
644 | + result += this.cronItems.WEEKS.values[v1]; | |
645 | + result += "-"; | |
646 | + result += this.cronItems.WEEKS.values[v2]; | |
647 | + | |
648 | + if (result.indexOf("undefined") > -1) { | |
649 | + result = "(Error - 요일의 값의 '" + value + "' 표현이 잘못됨)"; | |
650 | + } else { | |
651 | + return result; | |
652 | + } | |
653 | + } else { | |
654 | + var result = this.cronItems.WEEKS.values[value]; | |
655 | + | |
656 | + if (result.indexOf("undefined") > -1) { | |
657 | + result = "(Error - 요일의 값의 '" + value + "' 표현이 잘못됨)"; | |
658 | + } else { | |
659 | + return result; | |
660 | + } | |
661 | + } | |
662 | + } catch (e) { | |
663 | + return "(Error - 요일의 값의 '" + value + "' 표현이 잘못됨)"; | |
664 | + } | |
665 | + } else { | |
666 | + return value; | |
667 | + } | |
668 | + }, | |
669 | + }, | |
670 | + watch: { | |
671 | + schedule(v) { | |
672 | + this.initEvent(); | |
673 | + }, | |
674 | + cronItem(newValue, oldValue) { | |
675 | + //전체 주기 설정시 | |
676 | + if (newValue == "ALL") { | |
677 | + //'월'을 같이 표시해줘야 되기 때문에, '매일'을 뺌 | |
678 | + /* if (this.cronMap.DAYS == '*' || this.cronMap.DAYS == '?') { | |
679 | + this.cronMap.DAYS = '1'; | |
680 | + } */ | |
681 | + //'월', '일'이 같이 표시되기 때문에, '요일'을 기본적으로 [설정안함] | |
682 | + /* if (this.cronMap.WEEKS != '?') { | |
683 | + this.cronMap.WEEKS = '?'; | |
684 | + } */ | |
685 | + } | |
686 | + //'월' 별 주기 설정시 | |
687 | + else if (newValue == "YEAR") { | |
688 | + //'년'을 같이 표시해줘야 되기 때문에, '매일'을 뺌 | |
689 | + | |
690 | + if (this.cronMap.MONTH == "*" || this.cronMap.MONTH == "?") { | |
691 | + this.cronMap.DAYS = "1"; | |
692 | + } | |
693 | + | |
694 | + if (this.cronMap.DAYS == "*" || this.cronMap.DAYS == "?") { | |
695 | + this.cronMap.DAYS = "1"; | |
696 | + } | |
697 | + | |
698 | + //혼선을 피하기 위해 '요일'은 [설정안함] | |
699 | + this.cronMap.WEEKS = "?"; | |
700 | + } | |
701 | + | |
702 | + //'월' 별 주기 설정시 | |
703 | + else if (newValue == "MONTHS") { | |
704 | + //'월'을 같이 표시해줘야 되기 때문에, '매일'을 뺌 | |
705 | + if (this.cronMap.DAYS == "*" || this.cronMap.DAYS == "?") { | |
706 | + this.cronMap.DAYS = "1"; | |
707 | + } | |
708 | + //혼선을 피하기 위해 '요일'은 [설정안함] | |
709 | + this.cronMap.YEAR = "*"; | |
710 | + this.cronMap.WEEKS = "?"; | |
711 | + } | |
712 | + | |
713 | + //'요일' 별 주기 설정시 | |
714 | + else if (newValue == "WEEKS") { | |
715 | + //'월'의 기본 세팅 값을 '매월'로 바꿈, '일'은 [설정안함] | |
716 | + this.cronMap.YEAR = "*"; | |
717 | + this.cronMap.MONTHS = "*"; | |
718 | + this.cronMap.DAYS = "?"; | |
719 | + | |
720 | + //'요일'의 설정 값이 [설정안함]으로 되어 있을 때, -> '0:일요일'로 설정 | |
721 | + if (this.cronMap.WEEKS == "?") { | |
722 | + this.cronMap.WEEKS = "0"; | |
723 | + } | |
724 | + } | |
725 | + | |
726 | + //'일' 별 주기 설정시 | |
727 | + else if (newValue == "DAYS") { | |
728 | + //'월'의 기본 세팅 값을 '매월'로 바꿈, '요일'은 [설정안함] | |
729 | + this.cronMap.YEAR = "*"; | |
730 | + this.cronMap.MONTHS = "*"; | |
731 | + this.cronMap.WEEKS = "?"; | |
732 | + this.cronMap.DAYS = "*"; | |
733 | + | |
734 | + //'일'의 설정 값이 [설정안함]으로 되어 있을 때, -> '매일'로 설정 | |
735 | + /* if (this.cronMap.DAYS == '?') { | |
736 | + this.cronMap.DAYS = '*'; | |
737 | + } */ | |
738 | + } | |
739 | + // '시간' 별 주기 설정 시 | |
740 | + else if (newValue == "HOURS") { | |
741 | + this.cronMap.YEAR = "*"; | |
742 | + this.cronMap.MONTHS = "*"; | |
743 | + this.cronMap.WEEKS = "?"; | |
744 | + this.cronMap.DAYS = "*"; | |
745 | + } | |
746 | + // '분' 별 주기 설정 시 | |
747 | + else if (newValue == "MINUTES") { | |
748 | + this.cronMap.YEAR = "*"; | |
749 | + this.cronMap.MONTHS = "*"; | |
750 | + this.cronMap.WEEKS = "?"; | |
751 | + this.cronMap.DAYS = "*"; | |
752 | + this.cronMap.HOURS = "*"; | |
753 | + } | |
754 | + // '초' 별 주기 설정 시 | |
755 | + else if (newValue == "SECONDS") { | |
756 | + this.cronMap.YEAR = "*"; | |
757 | + this.cronMap.MONTHS = "*"; | |
758 | + this.cronMap.WEEKS = "?"; | |
759 | + this.cronMap.DAYS = "*"; | |
760 | + this.cronMap.HOURS = "*"; | |
761 | + this.cronMap.MINUTES = "*"; | |
762 | + } | |
763 | + // 그 외 | |
764 | + else { | |
765 | + this.cronMap.YEAR = "*"; | |
766 | + this.cronMap.MONTHS = "*"; | |
767 | + this.cronMap.WEEKS = "?"; | |
768 | + this.cronMap.DAYS = "*"; | |
769 | + } | |
770 | + }, | |
771 | + | |
772 | + isCyclical(v) { | |
773 | + this.$emit( | |
774 | + "get-edit-scheduling", | |
775 | + this.title, | |
776 | + this.dc, | |
777 | + this.getCronStr, | |
778 | + this.getCron, | |
779 | + v | |
780 | + ); | |
781 | + }, | |
782 | + | |
783 | + /* 데이터를 부모 컴포넌트로 전달해주기 위함 */ | |
784 | + title(newValue, oldValue) { | |
785 | + this.$emit( | |
786 | + "get-edit-scheduling", | |
787 | + this.title, | |
788 | + this.dc, | |
789 | + this.getCronStr, | |
790 | + this.getCron, | |
791 | + this.isCyclical | |
792 | + ); | |
793 | + }, | |
794 | + | |
795 | + dc(newValue, oldValue) { | |
796 | + this.$emit( | |
797 | + "get-edit-scheduling", | |
798 | + this.title, | |
799 | + this.dc, | |
800 | + this.getCronStr, | |
801 | + this.getCron, | |
802 | + this.isCyclical | |
803 | + ); | |
804 | + }, | |
805 | + | |
806 | + "cronMap.SECONDS"(newValue, oldValue) { | |
807 | + this.$emit( | |
808 | + "get-edit-scheduling", | |
809 | + this.title, | |
810 | + this.dc, | |
811 | + this.getCronStr, | |
812 | + this.getCron, | |
813 | + this.isCyclical | |
814 | + ); | |
815 | + }, | |
816 | + | |
817 | + "cronMap.MINUTES"(newValue, oldValue) { | |
818 | + this.$emit( | |
819 | + "get-edit-scheduling", | |
820 | + this.title, | |
821 | + this.dc, | |
822 | + this.getCronStr, | |
823 | + this.getCron, | |
824 | + this.isCyclical | |
825 | + ); | |
826 | + }, | |
827 | + | |
828 | + "cronMap.HOURS"(newValue, oldValue) { | |
829 | + this.$emit( | |
830 | + "get-edit-scheduling", | |
831 | + this.title, | |
832 | + this.dc, | |
833 | + this.getCronStr, | |
834 | + this.getCron, | |
835 | + this.isCyclical | |
836 | + ); | |
837 | + }, | |
838 | + | |
839 | + "cronMap.DAYS"(newValue, oldValue) { | |
840 | + this.$emit( | |
841 | + "get-edit-scheduling", | |
842 | + this.title, | |
843 | + this.dc, | |
844 | + this.getCronStr, | |
845 | + this.getCron, | |
846 | + this.isCyclical | |
847 | + ); | |
848 | + }, | |
849 | + | |
850 | + "cronMap.WEEKS"(newValue, oldValue) { | |
851 | + this.$emit( | |
852 | + "get-edit-scheduling", | |
853 | + this.title, | |
854 | + this.dc, | |
855 | + this.getCronStr, | |
856 | + this.getCron, | |
857 | + this.isCyclical | |
858 | + ); | |
859 | + }, | |
860 | + | |
861 | + "cronMap.MONTHS"(newValue, oldValue) { | |
862 | + this.$emit( | |
863 | + "get-edit-scheduling", | |
864 | + this.title, | |
865 | + this.dc, | |
866 | + this.getCronStr, | |
867 | + this.getCron, | |
868 | + this.isCyclical | |
869 | + ); | |
870 | + }, | |
871 | + | |
872 | + "cronMap.YEAR"(newValue, oldValue) { | |
873 | + this.$emit( | |
874 | + "get-edit-scheduling", | |
875 | + this.title, | |
876 | + this.dc, | |
877 | + this.getCronStr, | |
878 | + this.getCron, | |
879 | + this.isCyclical | |
880 | + ); | |
881 | + }, | |
882 | + /* 데이터를 부모 컴포넌트로 전달해주기 위함 */ | |
883 | + }, | |
884 | +}; | |
885 | +</script>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/SplitterLayout.vue
... | ... | @@ -0,0 +1,144 @@ |
1 | +<template> | |
2 | + <Splitter | |
3 | + :id="splitInfo['layoutNm']" | |
4 | + class="mb-5" | |
5 | + :layout="splitInfo['layoutType']" | |
6 | + @resize="updateSizes" | |
7 | + :resizable="true" | |
8 | + > | |
9 | + <SplitterPanel | |
10 | + :id="splitInfo['children'][0]['layoutNm']" | |
11 | + class="flex align-items-center justify-content-center" | |
12 | + :class="{ | |
13 | + active: | |
14 | + clickElement == splitInfo['children'][0]['layoutNm'] && | |
15 | + splitInfo['children'][0], | |
16 | + 'padding-1': splitInfo['children'][0]['componentNm'] != null, | |
17 | + }" | |
18 | + :size="splitInfo['sizes'][0]" | |
19 | + > | |
20 | + <!-- <SplitterLayout :is="SplitterLayout" id="SplitterLayout" :clickElement="clickElement" :layoutVal="layoutVal"></SplitterLayout> --> | |
21 | + <SplitterLayout | |
22 | + v-if=" | |
23 | + splitInfo['children'][0]['children'] !== null && | |
24 | + splitInfo['children'][0]['children'].length > 0 | |
25 | + " | |
26 | + :is="SplitterLayout" | |
27 | + :splitInfo="splitInfo['children'][0]['children'][0]" | |
28 | + :clickElement="clickElement" | |
29 | + :optionChangeClick="optionChangeClick" | |
30 | + :componentOptn="componentOptn" | |
31 | + @parentInfo="parentInfo" | |
32 | + ></SplitterLayout> | |
33 | + | |
34 | + <!-- <HorizentalThreeData ></HorizentalThreeData> --> | |
35 | + <!-- <div style="height:100%; width:100%;"> --> | |
36 | + <component | |
37 | + v-if="splitInfo['children'][0]['componentNm']" | |
38 | + :is="splitInfo['children'][0]['componentNm']" | |
39 | + :parent="splitInfo['children'][0]" | |
40 | + :optionChangeClick="optionChangeClick" | |
41 | + :componentOptn="componentOptn" | |
42 | + @parentInfo="parentInfo" | |
43 | + > | |
44 | + </component> | |
45 | + <!-- </div> --> | |
46 | + </SplitterPanel> | |
47 | + <SplitterPanel | |
48 | + v-if="splitInfo['children'][0]['layoutNm'] !== undefined" | |
49 | + :id="splitInfo['children'][1]['layoutNm']" | |
50 | + class="flex align-items-center justify-content-center" | |
51 | + :class="{ | |
52 | + active: | |
53 | + clickElement == splitInfo['children'][1]['layoutNm'] && | |
54 | + splitInfo['children'][1], | |
55 | + 'padding-1': splitInfo['children'][1]['componentNm'] != null, | |
56 | + }" | |
57 | + :size="splitInfo['sizes'][1]" | |
58 | + > | |
59 | + <!-- <SplitterLayout :is="SplitterLayout" id="SplitterLayout" :clickElement="clickElement" :layoutVal="layoutVal"></SplitterLayout> --> | |
60 | + <SplitterLayout | |
61 | + v-if=" | |
62 | + splitInfo['children'][0]['children'] !== null && | |
63 | + splitInfo['children'][0]['children'].length > 0 | |
64 | + " | |
65 | + :is="SplitterLayout" | |
66 | + :splitInfo="splitInfo['children'][1]['children'][0]" | |
67 | + :clickElement="clickElement" | |
68 | + :optionChangeClick="optionChangeClick" | |
69 | + :componentOptn="componentOptn" | |
70 | + @parentInfo="parentInfo" | |
71 | + ></SplitterLayout> | |
72 | + <component | |
73 | + v-if="splitInfo['children'][1]['componentNm']" | |
74 | + :is="splitInfo['children'][1]['componentNm']" | |
75 | + :parent="splitInfo['children'][1]" | |
76 | + :optionChangeClick="optionChangeClick" | |
77 | + :componentOptn="componentOptn" | |
78 | + @parentInfo="parentInfo" | |
79 | + ></component> | |
80 | + </SplitterPanel> | |
81 | + </Splitter> | |
82 | +</template> | |
83 | +<script> | |
84 | +import HorizentalThreeData from "./elementComponent/HorizentalThreeData.vue"; | |
85 | +import HorizentalTwoData from "./elementComponent/HorizentalTwoData.vue"; | |
86 | +import TableData from "./elementComponent/TableData.vue"; | |
87 | +import ThreeData from "./elementComponent/ThreeData.vue"; | |
88 | +import EquipmentData from "./elementComponent/EquipmentData.vue"; // 2024.02.16 PJH | |
89 | + | |
90 | +export default { | |
91 | + name: "SplitterLayout", | |
92 | + props: { | |
93 | + splitInfo: { | |
94 | + type: Object, | |
95 | + }, | |
96 | + clickElement: null, | |
97 | + optionChangeClick: { | |
98 | + type: Function, | |
99 | + }, | |
100 | + componentOptn: { | |
101 | + type: Object, | |
102 | + required: true, | |
103 | + }, | |
104 | + inputVal: { | |
105 | + type: Object, | |
106 | + required: true, | |
107 | + }, | |
108 | + }, | |
109 | + components: { | |
110 | + HorizentalThreeData, | |
111 | + HorizentalTwoData, | |
112 | + TableData, | |
113 | + ThreeData, | |
114 | + EquipmentData, // 2024.02.16 PJH | |
115 | + SplitterLayout: () => import("./SplitterLayout.vue"), | |
116 | + }, | |
117 | + methods: { | |
118 | + // newSizes는 사용자가 조정한 후의 panel 크기 비율을 배열로 제공합니다. | |
119 | + updateSizes(newSizes) { | |
120 | + this.splitInfo["sizes"] = newSizes["sizes"]; | |
121 | + }, | |
122 | + parentInfo: function (parentInfo) { | |
123 | + this.$emit("parentInfo", parentInfo); | |
124 | + }, | |
125 | + }, | |
126 | + computed: { | |
127 | + isReSize() { | |
128 | + return this.$route.path.includes("customSelectOne") ? false : true; | |
129 | + }, | |
130 | + }, | |
131 | +}; | |
132 | +</script> | |
133 | +<style scoped> | |
134 | +.active { | |
135 | + border: 3px dotted red; | |
136 | +} | |
137 | +.padding-1 { | |
138 | + padding: 1rem; | |
139 | +} | |
140 | +.p-splitter-gutter-handle, | |
141 | +.p-splitter-gutter { | |
142 | + pointer-events: none; | |
143 | +} | |
144 | +</style>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/SplitterLayoutDev.vue
... | ... | @@ -0,0 +1,173 @@ |
1 | +<template> | |
2 | + <Splitter | |
3 | + v-if="splitInfo['children'].length <= 0" | |
4 | + :id="splitInfo['layout_nm']" | |
5 | + class="mb-5" | |
6 | + :layout="splitInfo['layout_type']" | |
7 | + @resizeend="updateSizes" | |
8 | + @Click.stop="clickLayout(splitInfo)" | |
9 | + :style="this.$createStyleSheet(splitInfo.styleSheet)" | |
10 | + > | |
11 | + <SplitterPanel | |
12 | + v-if="splitInfo['children'][0] != null" | |
13 | + class="flex align-items-center justify-content-center" | |
14 | + :size="50" | |
15 | + > | |
16 | + 11 | |
17 | + </SplitterPanel> | |
18 | + <SplitterPanel | |
19 | + v-if="splitInfo['children'][1] != null" | |
20 | + class="flex align-items-center justify-content-center" | |
21 | + :size="50" | |
22 | + > | |
23 | + 22 | |
24 | + </SplitterPanel> | |
25 | + <BaseComponent | |
26 | + v-if="splitInfo.se == 'component'" | |
27 | + :component="splitInfo.component" | |
28 | + @changeselectLayout="changeselectLayout" | |
29 | + @Click.stop="clickLayout(splitInfo)" | |
30 | + ></BaseComponent> | |
31 | + </Splitter> | |
32 | + <Splitter | |
33 | + v-else | |
34 | + :id="splitInfo['layout_nm']" | |
35 | + class="mb-5" | |
36 | + :layout="splitInfo['layout_type']" | |
37 | + @resizeend="updateSizes" | |
38 | + @Click.stop="clickLayout(splitInfo)" | |
39 | + :style="this.$createStyleSheet(splitInfo.styleSheet)" | |
40 | + > | |
41 | + <SplitterPanel | |
42 | + v-if="splitInfo['children'][0] != null" | |
43 | + :id="splitInfo['children'][0]['layout_nm']" | |
44 | + class="flex align-items-center justify-content-center" | |
45 | + :class="{ | |
46 | + active: | |
47 | + selectLayout['layout_nm'] == splitInfo['children'][0]['layout_nm'] && | |
48 | + splitInfo['children'][0], | |
49 | + 'padding-1': splitInfo['children'][0]['component'] != null, | |
50 | + }" | |
51 | + :size="splitInfo['layout_size1']" | |
52 | + > | |
53 | + <template v-if="splitInfo.children[0]"> | |
54 | + <SplitterLayout | |
55 | + v-if="splitInfo.children[0].se == 'splitter'" | |
56 | + :splitInfo="splitInfo.children[0]" | |
57 | + @changeselectLayout="changeselectLayout" | |
58 | + :selectLayout="selectLayout" | |
59 | + ></SplitterLayout> | |
60 | + <BaseComponent | |
61 | + v-if="splitInfo.children[0].se == 'component'" | |
62 | + :component="splitInfo.children[0].component" | |
63 | + @changeselectLayout="changeselectLayout" | |
64 | + @Click.stop="clickLayout(splitInfo.children[0])" | |
65 | + ></BaseComponent> | |
66 | + </template> | |
67 | + </SplitterPanel> | |
68 | + <SplitterPanel | |
69 | + v-if="splitInfo['children'][1] != null" | |
70 | + :id="splitInfo['children'][1]['layout_nm']" | |
71 | + class="flex align-items-center justify-content-center" | |
72 | + :class="{ | |
73 | + active: | |
74 | + selectLayout['layout_nm'] == splitInfo['children'][1]['layout_nm'] && | |
75 | + splitInfo['children'][1], | |
76 | + 'padding-1': splitInfo['children'][1]['component'] != null, | |
77 | + }" | |
78 | + :size="splitInfo['layout_size2']" | |
79 | + > | |
80 | + <template v-if="splitInfo.children[1]"> | |
81 | + <SplitterLayout | |
82 | + v-if="splitInfo.children[1].se == 'splitter'" | |
83 | + :splitInfo="splitInfo.children[1]" | |
84 | + @changeselectLayout="changeselectLayout" | |
85 | + :selectLayout="selectLayout" | |
86 | + ></SplitterLayout> | |
87 | + <BaseComponent | |
88 | + v-if="splitInfo.children[1].se == 'component'" | |
89 | + :component="splitInfo.children[1].component" | |
90 | + @changeselectLayout="changeselectLayout" | |
91 | + @Click.stop="clickLayout(splitInfo.children[1])" | |
92 | + ></BaseComponent> | |
93 | + </template> | |
94 | + </SplitterPanel> | |
95 | + </Splitter> | |
96 | +</template> | |
97 | + | |
98 | + | |
99 | +<script> | |
100 | +import BaseComponent from "./elementComponent/BaseComponent.vue"; | |
101 | + | |
102 | +export default { | |
103 | + name: "SplitterLayout", | |
104 | + props: { | |
105 | + splitInfo: { | |
106 | + type: Object, | |
107 | + }, | |
108 | + selectLayout: { | |
109 | + type: Object, | |
110 | + default: function () { | |
111 | + return { | |
112 | + layout_nm: "", | |
113 | + layout_type: "", | |
114 | + children: [], | |
115 | + }; | |
116 | + }, | |
117 | + }, | |
118 | + createChartData: { | |
119 | + type: Object, | |
120 | + }, | |
121 | + }, | |
122 | + data() { | |
123 | + return { | |
124 | + currentSelectLayout: this.selectLayout, | |
125 | + }; | |
126 | + }, | |
127 | + | |
128 | + components: { | |
129 | + BaseComponent: BaseComponent, | |
130 | + }, | |
131 | + | |
132 | + methods: { | |
133 | + updateSizes(newSizes) { | |
134 | + // newSizes는 사용자가 조정한 후의 panel 크기 비율을 배열로 제공합니다. | |
135 | + this.splitInfo["layout_size1"] = newSizes.sizes[0]; | |
136 | + this.splitInfo["layout_size2"] = newSizes.sizes[1]; | |
137 | + this.splitInfo["sizes"] = newSizes.sizes; | |
138 | + }, | |
139 | + parentInfo: function (parentInfo) { | |
140 | + this.$emit("parentInfo", parentInfo); | |
141 | + }, | |
142 | + | |
143 | + clickLayout: function (splitInfo) { | |
144 | + this.$emit("changeselectLayout", splitInfo); | |
145 | + }, | |
146 | + | |
147 | + changeselectLayout: function (layout) { | |
148 | + this.$emit("changeselectLayout", layout); | |
149 | + }, | |
150 | + }, | |
151 | + | |
152 | + watch: { | |
153 | + splitInfo: { | |
154 | + handler: function (newVal, oldVal) {}, | |
155 | + deep: true, | |
156 | + }, | |
157 | + selectLayout: { | |
158 | + handler: function (v, old) { | |
159 | + this.currentSelectLayout = v; | |
160 | + }, | |
161 | + deep: true, | |
162 | + }, | |
163 | + }, | |
164 | +}; | |
165 | +</script> | |
166 | +<style scoped> | |
167 | +.active { | |
168 | + border: 3px dotted red; | |
169 | +} | |
170 | +.padding-1 { | |
171 | + padding: 1rem; | |
172 | +} | |
173 | +</style>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/chart/BubbleChart.vue
... | ... | @@ -0,0 +1,264 @@ |
1 | +<template> | |
2 | + <button @click="createChart">Create Chart</button> | |
3 | + <div class="chartdiv" id="chartdiv" ref="chartdiv" style="width: 100%; height: 700px;"></div> | |
4 | +</template> | |
5 | + | |
6 | +<script> | |
7 | + import * as am5 from "@amcharts/amcharts5"; | |
8 | + import * as am5xy from "@amcharts/amcharts5/xy"; | |
9 | + import * as am5percent from "@amcharts/amcharts5/percent"; | |
10 | + import am5themes_Animated from "@amcharts/amcharts5/themes/Animated"; | |
11 | + | |
12 | +export default { | |
13 | + props: { | |
14 | + datalist: { | |
15 | + type: Array, | |
16 | + default: [] | |
17 | + }, | |
18 | + chartName: { | |
19 | + type: String, | |
20 | + } | |
21 | + }, | |
22 | + data() { | |
23 | + return{ | |
24 | + jsonData: [{ | |
25 | + "y": 10, | |
26 | + "x": 14, | |
27 | + "value": 59, | |
28 | + "y2": -5, | |
29 | + "x2": -3, | |
30 | + "value2": 44 | |
31 | + }, { | |
32 | + "y": 5, | |
33 | + "x": 3, | |
34 | + "value": 50, | |
35 | + "y2": -15, | |
36 | + "x2": -8, | |
37 | + "value2": 12 | |
38 | + }, { | |
39 | + "y": -10, | |
40 | + "x": 8, | |
41 | + "value": 19, | |
42 | + "y2": -4, | |
43 | + "x2": 6, | |
44 | + "value2": 35 | |
45 | + }, { | |
46 | + "y": -6, | |
47 | + "x": 5, | |
48 | + "value": 65, | |
49 | + "y2": -5, | |
50 | + "x2": -6, | |
51 | + "value2": 168 | |
52 | + }, { | |
53 | + "y": 15, | |
54 | + "x": -4, | |
55 | + "value": 92, | |
56 | + "y2": -10, | |
57 | + "x2": -8, | |
58 | + "value2": 102 | |
59 | + }, { | |
60 | + "y": 13, | |
61 | + "x": 1, | |
62 | + "value": 8, | |
63 | + "y2": -2, | |
64 | + "x2": 0, | |
65 | + "value2": 41 | |
66 | + }, { | |
67 | + "y": 1, | |
68 | + "x": 6, | |
69 | + "value": 35, | |
70 | + "y2": 0, | |
71 | + "x2": -3, | |
72 | + "value2": 16 | |
73 | + }], | |
74 | + } | |
75 | + | |
76 | + }, | |
77 | + components: { | |
78 | + | |
79 | + }, | |
80 | + methods: { | |
81 | + // 버블차트 생성 | |
82 | + initializeChartArea:function(){ | |
83 | + let chartWarp = this.$refs["chartdiv"]; // 차트 상위 div ref 매칭 | |
84 | + chartWarp.innerHTML = ""; // 차트 상위 div 내용 초기화 (기존 차트 삭제) | |
85 | + | |
86 | + let div = document.createElement("div"); // 차트를 담을 빈 div 생성 (차트 하위 div) | |
87 | + div.style.width = "100%"; // 차트를 담을 div의 넓이 | |
88 | + div.style.height = "100%"; // 차트를 담을 div의 높이 | |
89 | + chartWarp.appendChild(div); // 차트 상위 div 안에 차트 하위 div를 추가 | |
90 | + | |
91 | + let chartArea = am5.Root.new(chartWarp.firstChild); | |
92 | + | |
93 | + chartArea.setThemes([am5themes_Animated.new(root)]); | |
94 | + | |
95 | + return chartArea | |
96 | + }, | |
97 | + createChart : function() { | |
98 | + | |
99 | + let vm = this; | |
100 | + let data = vm.jsonData; | |
101 | + let root = vm.initializeChartArea(); // 차트 초기화 | |
102 | + | |
103 | + | |
104 | + let chart = root.container.children.push(am5xy.XYChart.new(root, { | |
105 | + panX: true, | |
106 | + panY: true, | |
107 | + wheelY: "zoomXY", | |
108 | + pinchZoomX:true, | |
109 | + pinchZoomY:true | |
110 | + })); | |
111 | + | |
112 | + chart.get("colors").set("step", 2); | |
113 | + | |
114 | + // Create axes | |
115 | + // https://www.amcharts.com/docs/v5/charts/xy-chart/axes/ | |
116 | + let xAxis = chart.xAxes.push(am5xy.ValueAxis.new(root, { | |
117 | + renderer: am5xy.AxisRendererX.new(root, { minGridDistance: 50 }), | |
118 | + tooltip: am5.Tooltip.new(root, {}) | |
119 | + })); | |
120 | + | |
121 | + let yAxis = chart.yAxes.push(am5xy.ValueAxis.new(root, { | |
122 | + renderer: am5xy.AxisRendererY.new(root, {}), | |
123 | + tooltip: am5.Tooltip.new(root, {}) | |
124 | + })); | |
125 | + | |
126 | + // Create series | |
127 | + // https://www.amcharts.com/docs/v5/charts/xy-chart/series/ | |
128 | + let series0 = chart.series.push(am5xy.LineSeries.new(root, { | |
129 | + calculateAggregates: true, | |
130 | + xAxis: xAxis, | |
131 | + yAxis: yAxis, | |
132 | + valueYField: "y", | |
133 | + valueXField: "x", | |
134 | + valueField: "value", | |
135 | + tooltip: am5.Tooltip.new(root, { | |
136 | + labelText: "x: {valueX}, y: {valueY}, value: {value}" | |
137 | + }) | |
138 | + })); | |
139 | + | |
140 | + | |
141 | + // Add bullet | |
142 | + // https://www.amcharts.com/docs/v5/charts/xy-chart/series/#Bullets | |
143 | + let circleTemplate = am5.Template.new({}); | |
144 | + series0.bullets.push(function() { | |
145 | + let graphics = am5.Circle.new(root, { | |
146 | + fill: series0.get("fill"), | |
147 | + }, circleTemplate); | |
148 | + return am5.Bullet.new(root, { | |
149 | + sprite: graphics | |
150 | + }); | |
151 | + }); | |
152 | + | |
153 | + // Add heat rule | |
154 | + // https://www.amcharts.com/docs/v5/concepts/settings/heat-rules/ | |
155 | + series0.set("heatRules", [{ | |
156 | + target: circleTemplate, | |
157 | + min: 3, | |
158 | + max: 35, | |
159 | + dataField: "value", | |
160 | + key: "radius" | |
161 | + }]); | |
162 | + | |
163 | + | |
164 | + // Create second series | |
165 | + // https://www.amcharts.com/docs/v5/charts/xy-chart/series/ | |
166 | + // let series1 = chart.series.push(am5xy.LineSeries.new(root, { | |
167 | + // calculateAggregates: true, | |
168 | + // xAxis: xAxis, | |
169 | + // yAxis: yAxis, | |
170 | + // valueYField: "y2", | |
171 | + // valueXField: "x2", | |
172 | + // valueField: "value", | |
173 | + // tooltip: am5.Tooltip.new(root, { | |
174 | + // labelText: "x: {valueX}, y: {valueY}, value: {value}" | |
175 | + // }) | |
176 | + // })); | |
177 | + | |
178 | + // Add bullet | |
179 | + // https://www.amcharts.com/docs/v5/charts/xy-chart/series/#Bullets | |
180 | + // let starTemplate = am5.Template.new({}); | |
181 | + // series1.bullets.push(function() { | |
182 | + // let graphics = am5.Star.new(root, { | |
183 | + // fill: series1.get("fill"), | |
184 | + // spikes: 8, | |
185 | + // innerRadius: am5.percent(70), | |
186 | + // }, starTemplate); | |
187 | + // return am5.Bullet.new(root, { | |
188 | + // sprite: graphics | |
189 | + // }); | |
190 | + // }); | |
191 | + | |
192 | + | |
193 | + // // Add heat rule | |
194 | + // // https://www.amcharts.com/docs/v5/concepts/settings/heat-rules/ | |
195 | + // series1.set("heatRules", [{ | |
196 | + // target: starTemplate, | |
197 | + // min: 3, | |
198 | + // max: 50, | |
199 | + // dataField: "value", | |
200 | + // key: "radius" | |
201 | + // }]); | |
202 | + | |
203 | + | |
204 | + series0.strokes.template.set("strokeOpacity", 0); | |
205 | + // series1.strokes.template.set("strokeOpacity", 0); | |
206 | + | |
207 | + // Add cursor | |
208 | + // https://www.amcharts.com/docs/v5/charts/xy-chart/cursor/ | |
209 | + chart.set("cursor", am5xy.XYCursor.new(root, { | |
210 | + xAxis: xAxis, | |
211 | + yAxis: yAxis, | |
212 | + snapToSeries: [series0] | |
213 | + })); | |
214 | + | |
215 | + // Add scrollbars | |
216 | + // https://www.amcharts.com/docs/v5/charts/xy-chart/scrollbars/ | |
217 | + chart.set("scrollbarX", am5.Scrollbar.new(root, { | |
218 | + orientation: "horizontal" | |
219 | + })); | |
220 | + | |
221 | + chart.set("scrollbarY", am5.Scrollbar.new(root, { | |
222 | + orientation: "vertical" | |
223 | + })); | |
224 | + | |
225 | + series0.data.setAll(data); | |
226 | + // series1.data.setAll(data); | |
227 | + | |
228 | + | |
229 | + // Make stuff animate on load | |
230 | + // https://www.amcharts.com/docs/v5/concepts/animations/ | |
231 | + series0.appear(1000); | |
232 | + // series1.appear(1000); | |
233 | + | |
234 | + chart.appear(1000, 100); | |
235 | + root._logo.dispose(); | |
236 | + }, | |
237 | + }, | |
238 | + watch:{ | |
239 | + datalist: { | |
240 | + handler: function (newVal, oldVal) { | |
241 | + this.jsonData = newVal; | |
242 | + this.createChart(this.jsonData); | |
243 | + }, | |
244 | + deep: true | |
245 | + }, | |
246 | + | |
247 | + }, | |
248 | + mounted() { | |
249 | + // this.jsonData = this.datalist; | |
250 | + }, | |
251 | +}; | |
252 | +</script> | |
253 | + | |
254 | +<style scoped> | |
255 | +#chartdiv { | |
256 | + width: 100%; | |
257 | + height: 500px; | |
258 | +} | |
259 | +button{ | |
260 | + margin: 10px; | |
261 | + background-color: antiquewhite; | |
262 | + border-radius: 10px; | |
263 | +} | |
264 | +</style>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/chart/ChartCmmn.vue
... | ... | @@ -0,0 +1,953 @@ |
1 | +<template> | |
2 | + <!-- <div> | |
3 | + <select name="x-axis" v-model="selectedSubGroup" @change="changeSelectSubGroup"> | |
4 | + <option key="" value="" >sub 선택</option> | |
5 | + <option v-for="(column, index) in subGroupArr" :key="index" :value="column.index">{{ column.columnNm }}</option> | |
6 | + </select> | |
7 | + </div> --> | |
8 | + <div | |
9 | + class="chartdiv" | |
10 | + id="chartdiv" | |
11 | + ref="chartdiv" | |
12 | + style="width: 100%; height: 100%" | |
13 | + ></div> | |
14 | +</template> | |
15 | + | |
16 | +<script> | |
17 | +import * as am5 from "@amcharts/amcharts5"; | |
18 | +import * as am5xy from "@amcharts/amcharts5/xy"; | |
19 | +import * as am5percent from "@amcharts/amcharts5/percent"; | |
20 | +import * as am5wc from "@amcharts/amcharts5/wc"; | |
21 | +import am5themes_Animated from "@amcharts/amcharts5/themes/Animated"; | |
22 | +import Frozen from "@amcharts/amcharts5/themes/Frozen"; | |
23 | +import Dark from "@amcharts/amcharts5/themes/Dark"; // 배경이 어두워야 이쁨 | |
24 | +import Dataviz from "@amcharts/amcharts5/themes/Dataviz"; | |
25 | +import Kelly from "@amcharts/amcharts5/themes/Kelly"; | |
26 | +import Material from "@amcharts/amcharts5/themes/Material"; | |
27 | +import Micro from "@amcharts/amcharts5/themes/Micro"; | |
28 | +import Moonrise from "@amcharts/amcharts5/themes/Moonrise"; | |
29 | +import Spirited from "@amcharts/amcharts5/themes/Spirited"; | |
30 | +import chartDataTransform from "./chartDataTransform"; | |
31 | + | |
32 | +export default { | |
33 | + props: { | |
34 | + createDataObj: { | |
35 | + type: Object, | |
36 | + default: {}, | |
37 | + }, | |
38 | + }, | |
39 | + data() { | |
40 | + return { | |
41 | + // 가공된 데이터를 담을 배열 | |
42 | + chartJsondData: [], | |
43 | + // select box에서 선택 된 그룹 | |
44 | + selectedSubGroup: "", | |
45 | + | |
46 | + // group 선택에 따른 변수 | |
47 | + isSelectGroup: false, | |
48 | + subGroupArr: [], | |
49 | + groupsResult: [], | |
50 | + | |
51 | + // | |
52 | + datalist: [], | |
53 | + chartName: this.createDataObj.chart_knd, | |
54 | + selectedFieldArr: [], | |
55 | + isMultiData: this.createDataObj.isMultiData, | |
56 | + selectedCal: this.createDataObj.chart_cal, | |
57 | + selectedTheme: "", | |
58 | + // selectedGroupNm: , | |
59 | + }; | |
60 | + }, | |
61 | + components: {}, | |
62 | + methods: { | |
63 | + // 그룹 선택으로 인한 데이터 필터링 함수 | |
64 | + changeSelectSubGroup: function (event) { | |
65 | + this.selectedSubGroup = event.target.value; | |
66 | + // 필터 적용 함수 호출 | |
67 | + const filterDataList = chartDataTransform.setFilteringData( | |
68 | + this.datalist, | |
69 | + this.subGroupArr, | |
70 | + this.selectedSubGroup | |
71 | + ); | |
72 | + this.transformData(filterDataList); | |
73 | + }, | |
74 | + changeSubGroupData: function (val) { | |
75 | + if (Array.isArray(val)) { | |
76 | + this.subGroupArr = []; | |
77 | + this.subGroupArr = val | |
78 | + .map((data) => data.group) | |
79 | + .filter((value, index, self) => self.indexOf(value) === index) | |
80 | + .map((column, index) => { | |
81 | + return { | |
82 | + index: index, | |
83 | + columnNm: column, | |
84 | + }; | |
85 | + }); | |
86 | + // subGroupArr sort 처리 | |
87 | + this.subGroupArr.sort((a, b) => | |
88 | + a.columnNm < b.columnNm ? -1 : a.columnNm > b.columnNm ? 1 : 0 | |
89 | + ); | |
90 | + } | |
91 | + }, | |
92 | + //y 객체 배열 생성 | |
93 | + createYData: function () { | |
94 | + this.selectedFieldArr = this.createDataObj.valueAxis.map((field) => { | |
95 | + return { | |
96 | + valNm: field.columnNm, | |
97 | + valIndex: field.columnIdx, | |
98 | + }; | |
99 | + }); | |
100 | + }, | |
101 | + | |
102 | + // 데이터 생성 함수 | |
103 | + // dataList를 받아와 차트에 표현할 데이터로 가공 | |
104 | + transformData: function (val) { | |
105 | + if (val == null) { | |
106 | + this.chartJsondData = []; | |
107 | + this.runCreateChart(); | |
108 | + return; | |
109 | + } | |
110 | + this.createYData(); | |
111 | + if (this.selectedCal === "default") { | |
112 | + this.chartJsondData = val; | |
113 | + this.runCreateChart(); | |
114 | + return; | |
115 | + } | |
116 | + // 전체 데이터를 그룹화하여 가공 (All Data) | |
117 | + this.groupsResult = chartDataTransform.dataGrouping( | |
118 | + val, | |
119 | + this.selectedFieldArr | |
120 | + ); | |
121 | + if (!this.isSelectGroup) { | |
122 | + // groupSubArr에 group의 key를 중복을 제거하고 담음 | |
123 | + this.changeSubGroupData(this.datalist); | |
124 | + } | |
125 | + // 데이터 가공 함수 | |
126 | + this.chartJsondData = chartDataTransform.calculateSetting( | |
127 | + this.groupsResult, | |
128 | + this.selectedCal | |
129 | + ); | |
130 | + | |
131 | + this.runCreateChart(); | |
132 | + }, | |
133 | + | |
134 | + /** | |
135 | + * 차트 테마 선택 기능 | |
136 | + */ | |
137 | + choeseTheme: function (choeseTheme) { | |
138 | + if (choeseTheme === "Frozen") { | |
139 | + return Frozen; | |
140 | + } else if (choeseTheme === "Dark") { | |
141 | + return Dark; | |
142 | + } else if (choeseTheme === "Dataviz") { | |
143 | + return Dataviz; | |
144 | + } else if (choeseTheme === "Kelly") { | |
145 | + return Kelly; | |
146 | + } else if (choeseTheme === "Material") { | |
147 | + return Material; | |
148 | + } else if (choeseTheme === "Micro") { | |
149 | + return Micro; | |
150 | + } else if (choeseTheme === "Moonrise") { | |
151 | + return Moonrise; | |
152 | + } else if (choeseTheme === "Spirited") { | |
153 | + return Spirited; | |
154 | + } | |
155 | + }, | |
156 | + | |
157 | + initializeChartArea: function () { | |
158 | + let chartWarp = this.$refs["chartdiv"]; // 차트 상위 div ref 매칭 | |
159 | + if(chartWarp){ | |
160 | + chartWarp.innerHTML = ""; // 차트 상위 div 내용 초기화 (기존 차트 삭제) | |
161 | + | |
162 | + let div = document.createElement("div"); // 차트를 담을 빈 div 생성 (차트 하위 div) | |
163 | + div.style.width = "100%"; // 차트를 담을 div의 넓이 | |
164 | + div.style.height = "100%"; // 차트를 담을 div의 높이 | |
165 | + chartWarp.appendChild(div); // 차트 상위 div 안에 차트 하위 div를 추가 | |
166 | + | |
167 | + let chartArea = am5.Root.new(chartWarp.firstChild); | |
168 | + | |
169 | + let themes = this.choeseTheme(this.selectedTheme); | |
170 | + | |
171 | + if (this.selectedTheme != "") { | |
172 | + chartArea.setThemes([am5themes_Animated.new(root), themes.new(root)]); | |
173 | + } else { | |
174 | + chartArea.setThemes([am5themes_Animated.new(root)]); | |
175 | + } | |
176 | + return chartArea;} | |
177 | + }, | |
178 | + /* Create chart */ | |
179 | + // column chart | |
180 | + createColumnChart: function () { | |
181 | + // 필요 데이터 및 변수 선언 | |
182 | + let vm = this; | |
183 | + let data = vm.chartJsondData; | |
184 | + let root = vm.initializeChartArea(); // 차트 초기화 | |
185 | + | |
186 | + let chartConfig = vm.getChartConfig(vm.chartName); | |
187 | + let chart = vm.initializeChart(root, chartConfig); | |
188 | + let series = null; | |
189 | + | |
190 | + let cursor = chart.set( | |
191 | + "cursor", | |
192 | + am5xy.XYCursor.new(root, { | |
193 | + behavior: chartConfig.behavior, | |
194 | + }) | |
195 | + ); | |
196 | + // 축 라인 숨김 | |
197 | + // cursor.lineY.set("forceHidden", true); | |
198 | + // cursor.lineX.set("forceHidden", true); | |
199 | + | |
200 | + let categoryRenderer = am5xy[chartConfig.categoryAxisRenderer].new(root, { | |
201 | + minGridDistance: 30, | |
202 | + minorGridEnabled: true, | |
203 | + inversed: chartConfig.categoryInversed, | |
204 | + }); | |
205 | + | |
206 | + // 카테고리 렌더러 옵션 설정 | |
207 | + categoryRenderer.labels.template.setAll({ | |
208 | + rotation: -90, | |
209 | + centerY: am5.p50, | |
210 | + centerX: am5.p100, | |
211 | + paddingRight: 15, | |
212 | + }); | |
213 | + categoryRenderer.grid.template.setAll({ | |
214 | + stroke: am5.color(0xf15f5f), | |
215 | + location: 1, | |
216 | + }); | |
217 | + | |
218 | + // 카테고리 축 설정 | |
219 | + let categoryAxis = chart[chartConfig.categoryAxes].push( | |
220 | + am5xy.CategoryAxis.new(root, { | |
221 | + maxDeviation: 0.3, | |
222 | + categoryField: "categoryData", | |
223 | + renderer: categoryRenderer, | |
224 | + tooltip: am5.Tooltip.new(root, {}), | |
225 | + }) | |
226 | + ); | |
227 | + | |
228 | + // value 랜더러 설정 | |
229 | + let valueRenderer = am5xy[chartConfig.valueAxisRenderer].new(root, { | |
230 | + strokeOpacity: 0.1, | |
231 | + }); | |
232 | + | |
233 | + // value 축 설정 | |
234 | + let valueAxis = chart[chartConfig.valueAxes].push( | |
235 | + am5xy.ValueAxis.new(root, { | |
236 | + maxDeviation: 0.3, | |
237 | + renderer: valueRenderer, | |
238 | + }) | |
239 | + ); | |
240 | + | |
241 | + categoryAxis.data.setAll(data); | |
242 | + | |
243 | + // 시리즈 설정 | |
244 | + let seriesConfig = vm.getSeriesConfig( | |
245 | + vm.chartName, | |
246 | + categoryAxis, | |
247 | + valueAxis | |
248 | + ); | |
249 | + | |
250 | + let valuedata = | |
251 | + vm.selectedFieldArr.length > 0 ? vm.selectedFieldArr[0].valNm : ""; | |
252 | + | |
253 | + series = vm.initializeSeries(chart, seriesConfig, valuedata); | |
254 | + | |
255 | + series.columns.template.setAll({ | |
256 | + cornerRadiusTL: 5, | |
257 | + cornerRadiusTR: 5, | |
258 | + strokeOpacity: 0, | |
259 | + }); | |
260 | + series.columns.template.adapters.add("fill", function (fill, target) { | |
261 | + return chart.get("colors").getIndex(series.columns.indexOf(target)); | |
262 | + }); | |
263 | + | |
264 | + series.columns.template.adapters.add("stroke", function (stroke, target) { | |
265 | + return chart.get("colors").getIndex(series.columns.indexOf(target)); | |
266 | + }); | |
267 | + | |
268 | + series.data.setAll(data); | |
269 | + series.appear(1000); | |
270 | + root._logo.dispose(); | |
271 | + }, | |
272 | + | |
273 | + // line chart | |
274 | + createLineChart: function () { | |
275 | + let vm = this; | |
276 | + let data = vm.chartJsondData; | |
277 | + let root = vm.initializeChartArea(); // 차트 초기화 | |
278 | + | |
279 | + let chartConfig = vm.getChartConfig(vm.chartName); | |
280 | + let chart = vm.initializeChart(root, chartConfig); | |
281 | + let series = null; | |
282 | + | |
283 | + let categoryRenderer = am5xy[chartConfig.categoryAxisRenderer].new(root, { | |
284 | + minorGridEnabled: true, | |
285 | + minGridDistance: 60, | |
286 | + }); | |
287 | + categoryRenderer.grid.template.setAll({ | |
288 | + location: 1, | |
289 | + }); | |
290 | + | |
291 | + let categoryAxis = chart[chartConfig.categoryAxes].push( | |
292 | + am5xy.CategoryAxis.new(root, { | |
293 | + categoryField: "categoryData", | |
294 | + renderer: categoryRenderer, | |
295 | + tooltip: am5.Tooltip.new(root, {}), | |
296 | + }) | |
297 | + ); | |
298 | + | |
299 | + categoryAxis.data.setAll(data); | |
300 | + | |
301 | + let valueRenderer = am5xy[chartConfig.valueAxisRenderer].new(root, { | |
302 | + strokeOpacity: 0.1, | |
303 | + }); | |
304 | + | |
305 | + let valueAxis = chart[chartConfig.valueAxes].push( | |
306 | + am5xy.ValueAxis.new(root, { | |
307 | + min: 0, | |
308 | + renderer: valueRenderer, | |
309 | + }) | |
310 | + ); | |
311 | + | |
312 | + let cursor = chart.set( | |
313 | + "cursor", | |
314 | + am5xy.XYCursor.new(root, { | |
315 | + behavior: "none", | |
316 | + }) | |
317 | + ); | |
318 | + cursor.lineY.set("visible", false); | |
319 | + | |
320 | + let seriesConfig = vm.getSeriesConfig( | |
321 | + vm.chartName, | |
322 | + categoryAxis, | |
323 | + valueAxis | |
324 | + ); | |
325 | + | |
326 | + //데이터 없애는 부분 생각 필요 | |
327 | + let valuedata = | |
328 | + vm.selectedFieldArr.length > 0 ? vm.selectedFieldArr[0].valNm : "value"; | |
329 | + for (let i = 0; i < vm.selectedFieldArr.length; i++) { | |
330 | + series = vm.makeSeries( | |
331 | + root, | |
332 | + chart, | |
333 | + vm.selectedFieldArr[i].valNm, | |
334 | + seriesConfig | |
335 | + ); | |
336 | + | |
337 | + // 노드 표시 | |
338 | + if (vm.chartName == "node_line_chart") { | |
339 | + series.bullets.push(function (root) { | |
340 | + return am5xy.AxisBullet.new(root, { | |
341 | + location: 0.5, | |
342 | + sprite: am5.Circle.new(root, { | |
343 | + radius: 5, | |
344 | + fill: am5.color(0xff0000), | |
345 | + }), | |
346 | + }); | |
347 | + }); | |
348 | + } | |
349 | + | |
350 | + // 그래프 배경 색상 | |
351 | + if (vm.chartName == "liin_chart_back") { | |
352 | + series.fills.template.setAll({ | |
353 | + fillOpacity: 0.2, | |
354 | + visible: true, | |
355 | + }); | |
356 | + } | |
357 | + | |
358 | + series.data.setAll(data); | |
359 | + } | |
360 | + | |
361 | + series.appear(1000, 100); | |
362 | + | |
363 | + root._logo.dispose(); | |
364 | + }, | |
365 | + | |
366 | + // pie_chart chart | |
367 | + createPieChart: function () { | |
368 | + let data = this.chartJsondData; | |
369 | + | |
370 | + let root = this.initializeChartArea(); // 차트 초기화 | |
371 | + | |
372 | + let chart = null; | |
373 | + let series = null; | |
374 | + | |
375 | + // Create chart | |
376 | + let startAngle = null; | |
377 | + let endAngle = null; | |
378 | + let innerRadius = null; | |
379 | + | |
380 | + if (this.chartName == "semicircle_chart") { | |
381 | + startAngle = 180; | |
382 | + endAngle = 360; | |
383 | + } | |
384 | + | |
385 | + if (this.chartName == "dounet_chart") { | |
386 | + innerRadius = am5.percent(50); | |
387 | + } | |
388 | + | |
389 | + chart = root.container.children.push( | |
390 | + am5percent.PieChart.new(root, { | |
391 | + startAngle: startAngle, | |
392 | + endAngle: 360, | |
393 | + innerRadius: innerRadius, | |
394 | + }) | |
395 | + ); | |
396 | + | |
397 | + let valuedata = | |
398 | + this.selectedFieldArr.length > 0 | |
399 | + ? this.selectedFieldArr[0].valNm | |
400 | + : "value"; | |
401 | + | |
402 | + series = chart.series.push( | |
403 | + am5percent.PieSeries.new(root, { | |
404 | + valueField: valuedata, | |
405 | + categoryField: "categoryData", | |
406 | + startAngle: this.chartName == "semicircle_chart" ? 180 : null, | |
407 | + endAngle: 360, | |
408 | + }) | |
409 | + ); | |
410 | + | |
411 | + series.states.create("hidden", { | |
412 | + endAngle: -90, | |
413 | + }); | |
414 | + | |
415 | + series.data.setAll(data); | |
416 | + | |
417 | + series.appear(1000, 100); | |
418 | + | |
419 | + root._logo.dispose(); | |
420 | + }, | |
421 | + | |
422 | + // Clustered data chart | |
423 | + createClusteredChart: function () { | |
424 | + let vm = this; | |
425 | + let root = this.initializeChartArea(); // 차트 초기화' | |
426 | + let dataVal = this.chartJsondData; | |
427 | + let chartConfig = this.getChartConfig(vm.chartName); | |
428 | + let chart = this.initializeChart(root, chartConfig); | |
429 | + | |
430 | + // Add legend | |
431 | + let legend = chart.children.push( | |
432 | + am5.Legend.new(root, { | |
433 | + centerX: am5.p50, | |
434 | + x: am5.p50, | |
435 | + }) | |
436 | + ); | |
437 | + | |
438 | + let categoryRenderer = am5xy[chartConfig.categoryAxisRenderer].new(root, { | |
439 | + cellStartLocation: 0.1, | |
440 | + cellEndLocation: 0.9, | |
441 | + minorGridEnabled: true, | |
442 | + }); | |
443 | + | |
444 | + let categoryAxis = chart.xAxes.push( | |
445 | + am5xy.CategoryAxis.new(root, { | |
446 | + categoryField: "categoryData", | |
447 | + renderer: categoryRenderer, | |
448 | + tooltip: am5.Tooltip.new(root, {}), | |
449 | + }) | |
450 | + ); | |
451 | + | |
452 | + categoryRenderer.grid.template.setAll({ | |
453 | + location: 1, | |
454 | + }); | |
455 | + | |
456 | + categoryAxis.data.setAll(dataVal); | |
457 | + | |
458 | + // value 랜더러 설정 | |
459 | + let valueRenderer = am5xy[chartConfig.valueAxisRenderer].new(root, { | |
460 | + strokeOpacity: 0.1, | |
461 | + }); | |
462 | + | |
463 | + let valueAxis = chart[chartConfig.valueAxes].push( | |
464 | + am5xy.ValueAxis.new(root, { | |
465 | + renderer: valueRenderer, | |
466 | + }) | |
467 | + ); | |
468 | + | |
469 | + let seriesConfig = vm.getSeriesConfig( | |
470 | + vm.chartName, | |
471 | + categoryAxis, | |
472 | + valueAxis | |
473 | + ); | |
474 | + let series = null; | |
475 | + for (let i = 0; i < vm.selectedFieldArr.length; i++) { | |
476 | + series = vm.makeSeries( | |
477 | + root, | |
478 | + chart, | |
479 | + vm.selectedFieldArr[i].valNm, | |
480 | + seriesConfig | |
481 | + ); | |
482 | + | |
483 | + series.data.setAll(dataVal); | |
484 | + } | |
485 | + | |
486 | + chart.appear(1000, 300); | |
487 | + | |
488 | + root._logo.dispose(); //amChart 로고 삭제 | |
489 | + }, | |
490 | + createWordChart: function () { | |
491 | + let vm = this; | |
492 | + let root = vm.initializeChartArea(); // 차트 초기화 | |
493 | + | |
494 | + let valuedata = vm.selectedFieldArr.length > 0 ? vm.selectedFieldArr[0].valNm : ""; | |
495 | + | |
496 | + let series = root.container.children.push( | |
497 | + am5wc.WordCloud.new(root, { | |
498 | + categoryField: "categoryData", | |
499 | + valueField: valuedata, | |
500 | + maxFontSize: am5.percent(15), | |
501 | + }) | |
502 | + ); | |
503 | + | |
504 | + // Configure labels | |
505 | + series.labels.template.setAll({ | |
506 | + fontFamily: "Courier New", | |
507 | + }); | |
508 | + | |
509 | + series.data.setAll(this.chartJsondData); | |
510 | + root._logo.dispose(); | |
511 | + }, | |
512 | + // 음수 차트 수정 필요(아직 합치지않음) | |
513 | + createNagativeChart: function () { | |
514 | + let vm = this; | |
515 | + let root = vm.initializeChartArea(); // 차트 초기화 | |
516 | + | |
517 | + root._logo.dispose(); | |
518 | + | |
519 | + if (vm.selectedFieldArr.length < 2) { | |
520 | + return; | |
521 | + } | |
522 | + | |
523 | + chartDataTransform.convertValuesToNegative( | |
524 | + vm.chartJsondData, | |
525 | + vm.selectedFieldArr[0].valNm | |
526 | + ); | |
527 | + | |
528 | + let chart = root.container.children.push( | |
529 | + am5xy.XYChart.new(root, { | |
530 | + panX: false, | |
531 | + panY: false, | |
532 | + wheelX: "panX", | |
533 | + wheelY: "zoomX", | |
534 | + layout: root.verticalLayout, | |
535 | + arrangeTooltips: false, | |
536 | + paddingLeft: 0, | |
537 | + paddingRight: 10, | |
538 | + }) | |
539 | + ); | |
540 | + | |
541 | + // Use only absolute numbers | |
542 | + chart.getNumberFormatter().set("numberFormat", "#.#s"); | |
543 | + | |
544 | + let legend = chart.children.push( | |
545 | + am5.Legend.new(root, { | |
546 | + centerX: am5.p50, | |
547 | + x: am5.p50, | |
548 | + }) | |
549 | + ); | |
550 | + | |
551 | + let yAxis = chart.yAxes.push( | |
552 | + am5xy.CategoryAxis.new(root, { | |
553 | + categoryField: "categoryData", | |
554 | + renderer: am5xy.AxisRendererY.new(root, { | |
555 | + inversed: true, | |
556 | + cellStartLocation: 0.1, | |
557 | + cellEndLocation: 0.9, | |
558 | + minorGridEnabled: true, | |
559 | + minGridDistance: 20, | |
560 | + }), | |
561 | + }) | |
562 | + ); | |
563 | + | |
564 | + yAxis.data.setAll(this.chartJsondData); | |
565 | + | |
566 | + let xAxis = chart.xAxes.push( | |
567 | + am5xy.ValueAxis.new(root, { | |
568 | + renderer: am5xy.AxisRendererX.new(root, { | |
569 | + minGridDistance: 60, | |
570 | + strokeOpacity: 0.1, | |
571 | + }), | |
572 | + }) | |
573 | + ); | |
574 | + | |
575 | + function createSeries( | |
576 | + field, | |
577 | + labelCenterX, | |
578 | + pointerOrientation, | |
579 | + rangeValue | |
580 | + ) { | |
581 | + let series = chart.series.push( | |
582 | + am5xy.ColumnSeries.new(root, { | |
583 | + xAxis: xAxis, | |
584 | + yAxis: yAxis, | |
585 | + valueXField: field, | |
586 | + categoryYField: "categoryData", | |
587 | + sequencedInterpolation: true, | |
588 | + clustered: false, | |
589 | + tooltip: am5.Tooltip.new(root, { | |
590 | + pointerOrientation: pointerOrientation, | |
591 | + labelText: "{categoryY}: {valueX}", | |
592 | + }), | |
593 | + }) | |
594 | + ); | |
595 | + | |
596 | + series.columns.template.setAll({ | |
597 | + height: am5.p100, | |
598 | + strokeOpacity: 0, | |
599 | + fillOpacity: 0.8, | |
600 | + }); | |
601 | + | |
602 | + series.bullets.push(function () { | |
603 | + return am5.Bullet.new(root, { | |
604 | + locationX: 1, | |
605 | + locationY: 0.5, | |
606 | + sprite: am5.Label.new(root, { | |
607 | + centerY: am5.p50, | |
608 | + text: "{valueX}", | |
609 | + populateText: true, | |
610 | + centerX: labelCenterX, | |
611 | + }), | |
612 | + }); | |
613 | + }); | |
614 | + | |
615 | + series.data.setAll(vm.chartJsondData); | |
616 | + series.appear(); | |
617 | + | |
618 | + let rangeDataItem = xAxis.makeDataItem({ | |
619 | + value: rangeValue, | |
620 | + }); | |
621 | + xAxis.createAxisRange(rangeDataItem); | |
622 | + rangeDataItem.get("grid").setAll({ | |
623 | + strokeOpacity: 1, | |
624 | + stroke: series.get("stroke"), | |
625 | + }); | |
626 | + | |
627 | + let label = rangeDataItem.get("label"); | |
628 | + label.setAll({ | |
629 | + text: field.toUpperCase(), | |
630 | + fontSize: "1.1em", | |
631 | + fill: series.get("stroke"), | |
632 | + paddingTop: 10, | |
633 | + isMeasured: false, | |
634 | + centerX: labelCenterX, | |
635 | + }); | |
636 | + label.adapters.add("dy", function () { | |
637 | + return -chart.plotContainer.height(); | |
638 | + }); | |
639 | + return series; | |
640 | + } | |
641 | + | |
642 | + createSeries(vm.selectedFieldArr[0].valNm, am5.p100, "right", -3); | |
643 | + createSeries(vm.selectedFieldArr[1].valNm, 0, "left", 4); | |
644 | + | |
645 | + let cursor = chart.set( | |
646 | + "cursor", | |
647 | + am5xy.XYCursor.new(root, { | |
648 | + behavior: "zoomY", | |
649 | + }) | |
650 | + ); | |
651 | + cursor.lineY.set("forceHidden", true); | |
652 | + cursor.lineX.set("forceHidden", true); | |
653 | + | |
654 | + chart.appear(1000, 100); | |
655 | + root._logo.dispose(); //amChart 로고 삭제 | |
656 | + }, | |
657 | + // 막대 선 차트 (수정 필요) | |
658 | + createColAndLineChart: function () { | |
659 | + let vm = this; | |
660 | + let data = vm.chartJsondData; | |
661 | + let root = vm.initializeChartArea(); // 차트 초기화 | |
662 | + | |
663 | + root._logo.dispose(); //amChart 로고 삭제 | |
664 | + | |
665 | + if (data.length == 0) { | |
666 | + return; | |
667 | + } | |
668 | + | |
669 | + let income = Object.keys(data[0])[1]; | |
670 | + let expenses = Object.keys(data[0])[2]; | |
671 | + | |
672 | + let chart = root.container.children.push( | |
673 | + am5xy.XYChart.new(root, { | |
674 | + panX: false, | |
675 | + panY: false, | |
676 | + wheelX: "panX", | |
677 | + wheelY: "zoomX", | |
678 | + paddingLeft: 0, | |
679 | + layout: root.verticalLayout, | |
680 | + }) | |
681 | + ); | |
682 | + | |
683 | + let xRenderer = am5xy.AxisRendererX.new(root, { | |
684 | + minorGridEnabled: true, | |
685 | + minGridDistance: 60, | |
686 | + }); | |
687 | + let xAxis = chart.xAxes.push( | |
688 | + am5xy.CategoryAxis.new(root, { | |
689 | + categoryField: "categoryData", | |
690 | + renderer: xRenderer, | |
691 | + tooltip: am5.Tooltip.new(root, {}), | |
692 | + }) | |
693 | + ); | |
694 | + xRenderer.grid.template.setAll({ | |
695 | + location: 1, | |
696 | + }); | |
697 | + | |
698 | + xAxis.data.setAll(data); | |
699 | + | |
700 | + let yAxis = chart.yAxes.push( | |
701 | + am5xy.ValueAxis.new(root, { | |
702 | + min: 0, | |
703 | + extraMax: 0.1, | |
704 | + renderer: am5xy.AxisRendererY.new(root, { | |
705 | + strokeOpacity: 0.1, | |
706 | + }), | |
707 | + }) | |
708 | + ); | |
709 | + | |
710 | + let series1 = chart.series.push( | |
711 | + am5xy.ColumnSeries.new(root, { | |
712 | + name: income, | |
713 | + xAxis: xAxis, | |
714 | + yAxis: yAxis, | |
715 | + valueYField: income, | |
716 | + categoryXField: "categoryData", | |
717 | + tooltip: am5.Tooltip.new(root, { | |
718 | + pointerOrientation: "horizontal", | |
719 | + labelText: "{name} in {categoryX}: {valueY} {info}", | |
720 | + }), | |
721 | + }) | |
722 | + ); | |
723 | + | |
724 | + series1.columns.template.setAll({ | |
725 | + tooltipY: am5.percent(10), | |
726 | + templateField: "columnSettings", | |
727 | + }); | |
728 | + | |
729 | + series1.data.setAll(data); | |
730 | + | |
731 | + let series2 = chart.series.push( | |
732 | + am5xy.LineSeries.new(root, { | |
733 | + name: expenses, | |
734 | + xAxis: xAxis, | |
735 | + yAxis: yAxis, | |
736 | + valueYField: expenses, | |
737 | + categoryXField: "categoryData", | |
738 | + tooltip: am5.Tooltip.new(root, { | |
739 | + pointerOrientation: "horizontal", | |
740 | + labelText: "{name} in {categoryX}: {valueY} {info}", | |
741 | + }), | |
742 | + }) | |
743 | + ); | |
744 | + | |
745 | + series2.strokes.template.setAll({ | |
746 | + strokeWidth: 3, | |
747 | + templateField: "strokeSettings", | |
748 | + }); | |
749 | + | |
750 | + series2.data.setAll(data); | |
751 | + | |
752 | + series2.bullets.push(function () { | |
753 | + return am5.Bullet.new(root, { | |
754 | + sprite: am5.Circle.new(root, { | |
755 | + strokeWidth: 3, | |
756 | + stroke: series2.get("stroke"), | |
757 | + radius: 5, | |
758 | + fill: root.interfaceColors.get("background"), | |
759 | + }), | |
760 | + }); | |
761 | + }); | |
762 | + | |
763 | + chart.set("cursor", am5xy.XYCursor.new(root, {})); | |
764 | + | |
765 | + series1.appear(1000); | |
766 | + chart.appear(1000, 300); | |
767 | + root._logo.dispose(); //amChart 로고 삭제 | |
768 | + }, | |
769 | + // Create chart | |
770 | + getChartConfig: function (chartName) { | |
771 | + // 차트에 따라 설정이 달라져야함 | |
772 | + let axis = true; | |
773 | + if ( | |
774 | + chartName == "column_chart_h" || | |
775 | + chartName == "clustered_chart_h" || | |
776 | + chartName == "STACK_ROW" | |
777 | + ) { | |
778 | + axis = false; | |
779 | + } | |
780 | + | |
781 | + return { | |
782 | + // 차트 축 설정 | |
783 | + categoryAxisRenderer: axis ? "AxisRendererX" : "AxisRendererY", | |
784 | + valueAxisRenderer: axis ? "AxisRendererY" : "AxisRendererX", | |
785 | + categoryAxes: axis ? "xAxes" : "yAxes", | |
786 | + valueAxes: axis ? "yAxes" : "xAxes", | |
787 | + categoryInversed: axis ? false : true, | |
788 | + // 차트 이동 및 확대/축소 설정 | |
789 | + panX: true, | |
790 | + panY: true, | |
791 | + wheelX: "panX", | |
792 | + wheelY: axis ? "panY" : "zoomX", | |
793 | + behavior: axis ? "none" : "zoomY", | |
794 | + }; | |
795 | + }, | |
796 | + initializeChart: function (root, config) { | |
797 | + console.log(root.container.children); | |
798 | + return root.container.children.push( | |
799 | + am5xy.XYChart.new(root, { | |
800 | + panX: config.panX, | |
801 | + panY: config.panY, | |
802 | + wheelX: config.wheelX, | |
803 | + wheelY: config.wheelY, | |
804 | + }) | |
805 | + ); | |
806 | + }, | |
807 | + | |
808 | + // Create series | |
809 | + getSeriesConfig: function (chartName, categoryAxis, valueAxis) { | |
810 | + // 차트에 따라 설정이 달라져야함 | |
811 | + let axis = true; | |
812 | + let chartNm = "ColumnSeries"; | |
813 | + if ( | |
814 | + chartName == "column_chart_h" || | |
815 | + chartName == "clustered_chart_h" || | |
816 | + chartName == "STACK_ROW" | |
817 | + ) { | |
818 | + axis = false; | |
819 | + } | |
820 | + if ( | |
821 | + chartName == "line_chart" || | |
822 | + chartName == "node_line_chart" || | |
823 | + chartName == "liin_chart_back" | |
824 | + ) { | |
825 | + chartNm = "LineSeries"; | |
826 | + } | |
827 | + let isStack = false; | |
828 | + | |
829 | + if ( | |
830 | + this.chartName === "stacked_column_chart" || | |
831 | + this.chartName === "STACK_ROW" | |
832 | + ) { | |
833 | + isStack = true; | |
834 | + } | |
835 | + | |
836 | + return { | |
837 | + chartNm: chartNm, | |
838 | + isStack: isStack, | |
839 | + xAxis: axis ? categoryAxis : valueAxis, | |
840 | + yAxis: axis ? valueAxis : categoryAxis, | |
841 | + categoryField: axis ? "categoryXField" : "categoryYField", | |
842 | + valueField: axis ? "valueYField" : "valueXField", | |
843 | + categoryXY: axis ? "categoryX" : "categoryY", | |
844 | + valueXY: axis ? "valueY" : "valueX", | |
845 | + tooltipText: axis ? "{valueY}" : "{valueX}", | |
846 | + }; | |
847 | + }, | |
848 | + initializeSeries: function (chart, seriesConfig, valuedata) { | |
849 | + // 차트에 새로운 시리즈를 생성하여 추가 | |
850 | + let series = chart.series.push( | |
851 | + am5xy[seriesConfig.chartNm].new(chart.root, { | |
852 | + name: valuedata, // 이 부분은 valuedata를 이름으로 사용하고 있으나, 이는 데이터의 의도에 따라 다를 수 있습니다. | |
853 | + stacked: seriesConfig.isStack, | |
854 | + xAxis: seriesConfig.xAxis, | |
855 | + yAxis: seriesConfig.yAxis, | |
856 | + [seriesConfig.valueField]: valuedata, | |
857 | + [seriesConfig.categoryField]: "categoryData", | |
858 | + tooltip: am5.Tooltip.new(chart.root, { | |
859 | + labelText: seriesConfig.tooltipText, | |
860 | + }), | |
861 | + }) | |
862 | + ); | |
863 | + | |
864 | + return series; // 생성된 시리즈 객체를 반환 | |
865 | + }, | |
866 | + // Add series | |
867 | + // https://www.amcharts.com/docs/v5/charts/xy-chart/series/ | |
868 | + makeSeries: function (root, chart, name, seriesConfig) { | |
869 | + let series = chart.series.push( | |
870 | + am5xy[seriesConfig.chartNm].new(root, { | |
871 | + name: name, | |
872 | + stacked: seriesConfig.isStack, | |
873 | + xAxis: seriesConfig.xAxis, | |
874 | + yAxis: seriesConfig.yAxis, | |
875 | + [seriesConfig.valueField]: name, | |
876 | + [seriesConfig.categoryField]: "categoryData", | |
877 | + tooltip: am5.Tooltip.new(chart.root, { | |
878 | + labelText: seriesConfig.tooltipText, | |
879 | + }), | |
880 | + }) | |
881 | + ); | |
882 | + | |
883 | + return series; | |
884 | + }, | |
885 | + | |
886 | + // 차트 생성 실행 함수 | |
887 | + runCreateChart: function () { | |
888 | + if ( | |
889 | + this.chartName === "column_chart_v" || | |
890 | + this.chartName === "column_chart_h" | |
891 | + ) { | |
892 | + this.createColumnChart(); | |
893 | + } else if ( | |
894 | + this.chartName === "line_chart" || | |
895 | + this.chartName === "node_line_chart" || | |
896 | + this.chartName === "liin_chart_back" | |
897 | + ) { | |
898 | + this.createLineChart(); | |
899 | + } else if ( | |
900 | + this.chartName === "pie_chart" || | |
901 | + this.chartName === "dounet_chart" || | |
902 | + this.chartName === "semicircle_chart" | |
903 | + ) { | |
904 | + this.createPieChart(); | |
905 | + } else if ( | |
906 | + this.chartName === "clustered_chart_v" || | |
907 | + this.chartName === "clustered_chart_h" || | |
908 | + this.chartName === "stacked_column_chart" || | |
909 | + this.chartName === "STACK_ROW" | |
910 | + ) { | |
911 | + this.createClusteredChart(); | |
912 | + } else if (this.chartName === "word_chart") { | |
913 | + this.createWordChart(); | |
914 | + } else if (this.chartName === "stacked_bar_chart") { | |
915 | + this.createNagativeChart(); | |
916 | + } else if (this.chartName === "mix_chart") { | |
917 | + this.createColAndLineChart(); | |
918 | + } | |
919 | + }, | |
920 | + }, | |
921 | + watch: { | |
922 | + createDataObj: { | |
923 | + handler: function (newVal, oldVal) { | |
924 | + this.datalist = newVal.data_list; | |
925 | + this.chartName = newVal.chart_knd; | |
926 | + this.isMultiData = newVal.multidata_use_yn; | |
927 | + this.selectedCal = newVal.chart_cal; | |
928 | + this.transformData(this.datalist); | |
929 | + }, | |
930 | + deep: true, | |
931 | + }, | |
932 | + }, | |
933 | + mounted() { | |
934 | + this.datalist = this.createDataObj.data_list; | |
935 | + this.chartName = this.createDataObj.chart_knd; | |
936 | + this.isMultiData = this.createDataObj.multidata_use_yn; | |
937 | + this.selectedCal = this.createDataObj.chart_cal; | |
938 | + this.transformData(this.datalist); | |
939 | + }, | |
940 | +}; | |
941 | +</script> | |
942 | + | |
943 | +<style scoped> | |
944 | +#chartdiv { | |
945 | + width: 100%; | |
946 | + height: 500px; | |
947 | +} | |
948 | +button { | |
949 | + margin: 10px; | |
950 | + background-color: antiquewhite; | |
951 | + border-radius: 10px; | |
952 | +} | |
953 | +</style>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/chart/ChartPage.vue
... | ... | @@ -0,0 +1,1665 @@ |
1 | +<template> | |
2 | + <div style="width: 500px; height: 300px"> | |
3 | + <JobContainer :jobGroup="jobGroup" @getDataTable="getResult" /> | |
4 | + </div> | |
5 | + <div> | |
6 | + <button name="ChartCmmn" @click="changeChartName($event, 'column_chart_v')"> | |
7 | + 가로 막대차트 | |
8 | + </button> | |
9 | + <button name="ChartCmmn" @click="changeChartName($event, 'column_chart_h')"> | |
10 | + 세로 막대차트 | |
11 | + </button> | |
12 | + <button | |
13 | + name="ChartCmmn" | |
14 | + @click="changeChartName($event, 'clustered_chart_v')" | |
15 | + > | |
16 | + 가로 클러스터 | |
17 | + </button> | |
18 | + <button | |
19 | + name="ChartCmmn" | |
20 | + @click="changeChartName($event, 'clustered_chart_h')" | |
21 | + > | |
22 | + 세로 클러스터 | |
23 | + </button> | |
24 | + <button | |
25 | + name="WordChart" | |
26 | + @click="changeChartName($event, 'stacked_bar_chart')" | |
27 | + > | |
28 | + 음수 차트 | |
29 | + </button> | |
30 | + <button name="ChartCmmn" @click="changeChartName($event, 'line_chart')"> | |
31 | + 선 차트 | |
32 | + </button> | |
33 | + <button name="ColAndLine" @click="changeChartName($event, 'mix_chart')"> | |
34 | + 선 막대 차트 | |
35 | + </button> | |
36 | + <button name="ChartCmmn" @click="changeChartName($event, 'pie_chart')"> | |
37 | + 파이 차트 | |
38 | + </button> | |
39 | + <button name="ChartCmmn" @click="changeChartName($event, 'dounet_chart')"> | |
40 | + 도넛 차트 | |
41 | + </button> | |
42 | + <button | |
43 | + name="ChartCmmn" | |
44 | + @click="changeChartName($event, 'semicircle_chart')" | |
45 | + > | |
46 | + 반원 차트 | |
47 | + </button> | |
48 | + <button name="BubbleChart" @click="changeChartName($event, 'BUBBLE')"> | |
49 | + 버블 차트 | |
50 | + </button> | |
51 | + <button name="ChartCmmn" @click="changeChartName($event, 'word_chart')"> | |
52 | + 워드 | |
53 | + </button> | |
54 | + <button | |
55 | + name="ChartCmmn" | |
56 | + @click="changeChartName($event, 'stacked_column_chart')" | |
57 | + > | |
58 | + 스택막대차트 - COL | |
59 | + </button> | |
60 | + <button name="ChartCmmn" @click="changeChartName($event, 'STACK_ROW')"> | |
61 | + 스택막대차트 - ROW | |
62 | + </button> | |
63 | + <button | |
64 | + name="ChartCmmn" | |
65 | + @click="changeChartName($event, 'node_line_chart')" | |
66 | + > | |
67 | + 노드 선 차트 | |
68 | + </button> | |
69 | + <button | |
70 | + name="ChartCmmn" | |
71 | + @click="changeChartName($event, 'liin_chart_back')" | |
72 | + > | |
73 | + 배경 선 차트 | |
74 | + </button> | |
75 | + </div> | |
76 | + <div> | |
77 | + <select v-model="selectedTheme" @change="setThemes"> | |
78 | + <option value="">테마 선택</option> | |
79 | + <option v-for="theme in themes" :key="theme" :value="theme"> | |
80 | + {{ theme }} | |
81 | + </option> | |
82 | + </select> | |
83 | + </div> | |
84 | + <div> | |
85 | + <select name="x-axis" v-model="selectedX" @change="selectedXAxis"> | |
86 | + <option key="" value="">x축 선택</option> | |
87 | + <option | |
88 | + v-for="(column, index) in xAxisList" | |
89 | + :key="index" | |
90 | + :value="column.index" | |
91 | + > | |
92 | + {{ column.columnNm }} | |
93 | + </option> | |
94 | + </select> | |
95 | + <select name="y-axis" v-model="selectedY" @change="selectedYAxis"> | |
96 | + <option key="" value="">y축 선택</option> | |
97 | + <option | |
98 | + v-for="(column, index) in yAxisList" | |
99 | + :key="index" | |
100 | + :value="column.index" | |
101 | + > | |
102 | + {{ column.columnNm }} | |
103 | + </option> | |
104 | + </select> | |
105 | + <select name="x-axis" v-model="selectedGroup" @change="changeSelectGroup"> | |
106 | + <option key="" value="">그룹 선택</option> | |
107 | + <option | |
108 | + v-for="(column, index) in groupArr" | |
109 | + :key="index" | |
110 | + :value="column.index" | |
111 | + > | |
112 | + {{ column.columnNm }} | |
113 | + </option> | |
114 | + </select> | |
115 | + </div> | |
116 | + <div style="display: flex"> | |
117 | + <input type="radio" id="default" value="default" v-model="selectedCal" /> | |
118 | + default<br /> | |
119 | + <input type="radio" id="sum" value="sum" v-model="selectedCal" /> Sum<br /> | |
120 | + <input type="radio" id="avg" value="avg" v-model="selectedCal" /> | |
121 | + Average<br /> | |
122 | + <input type="radio" id="min" value="min" v-model="selectedCal" /> Min<br /> | |
123 | + <input type="radio" id="max" value="max" v-model="selectedCal" /> Max<br /> | |
124 | + </div> | |
125 | + <div> | |
126 | + <ul> | |
127 | + <li | |
128 | + v-for="(field, index) in selectedFieldArr" | |
129 | + :key="index" | |
130 | + :value="field.valIndex" | |
131 | + > | |
132 | + {{ field.valNm }}<button @click="deleteField(index)">x</button> | |
133 | + </li> | |
134 | + </ul> | |
135 | + </div> | |
136 | + <div class="chartdiv" id="chartdiv" style="width: 100%; height: 700px"> | |
137 | + <component | |
138 | + :is="componentName" | |
139 | + :datalist="datalist" | |
140 | + :chartName="chartName" | |
141 | + :selectedFieldArr="selectedFieldArr" | |
142 | + :isMultiData="isMultiData" | |
143 | + :selectedCal="selectedCal" | |
144 | + :selectedGroupNm="selectedGroupNm" | |
145 | + :selectedTheme="selectedTheme" | |
146 | + /> | |
147 | + </div> | |
148 | +</template> | |
149 | + | |
150 | +<script> | |
151 | +import JobContainer from "../../component/connection/jobContainer.vue"; | |
152 | + | |
153 | +// 차트 컴포넌트 import | |
154 | +import ChartCmmn from "./ChartCmmn.vue"; | |
155 | +// import ColumnLablesChart from './ColumnLablesChart.vue'; | |
156 | +// import PieChart from './PieChart.vue'; | |
157 | +// import LineChart from './LineChart.vue'; | |
158 | +// import ClusteredChart from './ClusteredChart.vue'; | |
159 | +// import ColAndLine from './ColAndLine.vue'; | |
160 | +import chartDataTransform from "./chartDataTransform"; | |
161 | +// import BubbleChart from './BubbleChart.vue'; | |
162 | + | |
163 | +export default { | |
164 | + data() { | |
165 | + return { | |
166 | + // 데이터를 담을 배열 | |
167 | + datalist: [], | |
168 | + dummyDataList: [ | |
169 | + [1, 2022, "금호읍", "16 - 16", 1, 1, 0], | |
170 | + [2, 2022, "금호읍", "18 - 18", 2, 2, 0], | |
171 | + [3, 2022, "금호읍", "19 - 19", 2, 2, 0], | |
172 | + [4, 2022, "금호읍", "20 - 20", 6, 6, 0], | |
173 | + [5, 2022, "금호읍", "21 - 21", 5, 0, 5], | |
174 | + [6, 2022, "금호읍", "22 - 22", 7, 3, 4], | |
175 | + [7, 2022, "금호읍", "23 - 23", 6, 5, 1], | |
176 | + [8, 2022, "금호읍", "24 - 24", 11, 10, 1], | |
177 | + [9, 2022, "금호읍", "25 - 25", 16, 11, 5], | |
178 | + [10, 2022, "금호읍", "26 - 26", 9, 6, 3], | |
179 | + [11, 2022, "금호읍", "27 - 27", 23, 16, 7], | |
180 | + [12, 2022, "금호읍", "28 - 28", 12, 8, 4], | |
181 | + [13, 2022, "금호읍", "29 - 29", 16, 10, 6], | |
182 | + [14, 2022, "금호읍", "30 - 30", 15, 11, 4], | |
183 | + [15, 2022, "금호읍", "31 - 31", 21, 16, 5], | |
184 | + [16, 2022, "금호읍", "32 - 32", 17, 13, 4], | |
185 | + [17, 2022, "금호읍", "33 - 33", 17, 12, 5], | |
186 | + [18, 2022, "금호읍", "34 - 34", 21, 10, 11], | |
187 | + [19, 2022, "금호읍", "35 - 35", 22, 15, 7], | |
188 | + [20, 2022, "금호읍", "36 - 36", 14, 8, 6], | |
189 | + [21, 2022, "금호읍", "37 - 37", 16, 9, 7], | |
190 | + [22, 2022, "금호읍", "38 - 38", 20, 12, 8], | |
191 | + [23, 2022, "금호읍", "39 - 39", 21, 16, 5], | |
192 | + [24, 2022, "금호읍", "40 - 40", 15, 12, 3], | |
193 | + [25, 2022, "금호읍", "41 - 41", 21, 16, 5], | |
194 | + [26, 2022, "금호읍", "42 - 42", 33, 23, 10], | |
195 | + [27, 2022, "금호읍", "43 - 43", 33, 28, 5], | |
196 | + [28, 2022, "금호읍", "44 - 44", 29, 17, 12], | |
197 | + [29, 2022, "금호읍", "45 - 45", 36, 30, 6], | |
198 | + [30, 2022, "금호읍", "46 - 46", 44, 28, 16], | |
199 | + [31, 2022, "금호읍", "47 - 47", 45, 31, 14], | |
200 | + [32, 2022, "금호읍", "48 - 48", 41, 30, 11], | |
201 | + [33, 2022, "금호읍", "49 - 49", 43, 34, 9], | |
202 | + [34, 2022, "금호읍", "50 - 50", 61, 42, 19], | |
203 | + [35, 2022, "금호읍", "51 - 51", 71, 46, 25], | |
204 | + [36, 2022, "금호읍", "52 - 52", 63, 39, 24], | |
205 | + [37, 2022, "금호읍", "53 - 53", 70, 48, 22], | |
206 | + [38, 2022, "금호읍", "54 - 54", 86, 60, 26], | |
207 | + [39, 2022, "금호읍", "55 - 55", 80, 49, 31], | |
208 | + [40, 2022, "금호읍", "56 - 56", 87, 60, 27], | |
209 | + [41, 2022, "금호읍", "57 - 57", 102, 74, 28], | |
210 | + [42, 2022, "금호읍", "58 - 58", 110, 64, 46], | |
211 | + [43, 2022, "금호읍", "59 - 59", 89, 62, 27], | |
212 | + [44, 2022, "금호읍", "60 - 60", 114, 80, 34], | |
213 | + [45, 2022, "금호읍", "61 - 61", 124, 85, 39], | |
214 | + [46, 2022, "금호읍", "62 - 62", 106, 65, 41], | |
215 | + [47, 2022, "금호읍", "63 - 63", 109, 67, 42], | |
216 | + [48, 2022, "금호읍", "64 - 64", 88, 51, 37], | |
217 | + [49, 2022, "금호읍", "65 - 65", 96, 50, 46], | |
218 | + [50, 2022, "금호읍", "66 - 66", 83, 55, 28], | |
219 | + [51, 2022, "금호읍", "67 - 67", 99, 54, 45], | |
220 | + [52, 2022, "금호읍", "68 - 68", 72, 41, 31], | |
221 | + [53, 2022, "금호읍", "69 - 69", 81, 46, 35], | |
222 | + [54, 2022, "금호읍", "70 - 70", 90, 44, 46], | |
223 | + [55, 2022, "금호읍", "71 - 71", 57, 31, 26], | |
224 | + [56, 2022, "금호읍", "72 - 72", 55, 32, 23], | |
225 | + [57, 2022, "금호읍", "73 - 73", 61, 27, 34], | |
226 | + [58, 2022, "금호읍", "74 - 74", 63, 24, 39], | |
227 | + [59, 2022, "금호읍", "75 - 75", 42, 18, 24], | |
228 | + [60, 2022, "금호읍", "76 - 76", 35, 15, 20], | |
229 | + [61, 2022, "금호읍", "77 - 77", 56, 15, 41], | |
230 | + [62, 2022, "금호읍", "78 - 78", 63, 22, 41], | |
231 | + [63, 2022, "금호읍", "79 - 79", 58, 16, 42], | |
232 | + [64, 2022, "금호읍", "80 - 80", 63, 19, 44], | |
233 | + [65, 2022, "금호읍", "81 - 81", 62, 15, 47], | |
234 | + [66, 2022, "금호읍", "82 - 82", 44, 7, 37], | |
235 | + [67, 2022, "금호읍", "83 - 83", 56, 10, 46], | |
236 | + [68, 2022, "금호읍", "84 - 84", 38, 8, 30], | |
237 | + [69, 2022, "금호읍", "85 - 85", 42, 10, 32], | |
238 | + [70, 2022, "금호읍", "86 - 86", 37, 4, 33], | |
239 | + [71, 2022, "금호읍", "87 - 87", 35, 10, 25], | |
240 | + [72, 2022, "금호읍", "88 - 88", 29, 3, 26], | |
241 | + [73, 2022, "금호읍", "89 - 89", 15, 1, 14], | |
242 | + [74, 2022, "금호읍", "90 - 90", 14, 3, 11], | |
243 | + [75, 2022, "금호읍", "91 - 91", 13, 2, 11], | |
244 | + [76, 2022, "금호읍", "92 - 92", 10, 1, 9], | |
245 | + [77, 2022, "금호읍", "93 - 93", 6, 0, 6], | |
246 | + [78, 2022, "금호읍", "94 - 94", 6, 0, 6], | |
247 | + [79, 2022, "금호읍", "95 - 95", 1, 1, 0], | |
248 | + [80, 2022, "금호읍", "102 - 102", 1, 0, 1], | |
249 | + [81, 2022, "청통면", "20 - 20", 3, 1, 2], | |
250 | + [82, 2022, "청통면", "22 - 22", 2, 2, 0], | |
251 | + [83, 2022, "청통면", "23 - 23", 6, 4, 2], | |
252 | + [84, 2022, "청통면", "24 - 24", 9, 5, 4], | |
253 | + [85, 2022, "청통면", "25 - 25", 7, 4, 3], | |
254 | + [86, 2022, "청통면", "26 - 26", 10, 8, 2], | |
255 | + [87, 2022, "청통면", "27 - 27", 11, 5, 6], | |
256 | + [88, 2022, "청통면", "28 - 28", 4, 3, 1], | |
257 | + [89, 2022, "청통면", "29 - 29", 12, 9, 3], | |
258 | + [90, 2022, "청통면", "30 - 30", 7, 7, 0], | |
259 | + [91, 2022, "청통면", "31 - 31", 4, 3, 1], | |
260 | + [92, 2022, "청통면", "32 - 32", 3, 2, 1], | |
261 | + [93, 2022, "청통면", "33 - 33", 3, 3, 0], | |
262 | + [94, 2022, "청통면", "34 - 34", 6, 4, 2], | |
263 | + [95, 2022, "청통면", "35 - 35", 6, 3, 3], | |
264 | + [96, 2022, "청통면", "36 - 36", 5, 4, 1], | |
265 | + [97, 2022, "청통면", "37 - 37", 10, 8, 2], | |
266 | + [98, 2022, "청통면", "38 - 38", 13, 9, 4], | |
267 | + [99, 2022, "청통면", "39 - 39", 6, 3, 3], | |
268 | + [100, 2022, "청통면", "40 - 40", 11, 8, 3], | |
269 | + [101, 2022, "청통면", "41 - 41", 5, 2, 3], | |
270 | + [102, 2022, "청통면", "42 - 42", 5, 4, 1], | |
271 | + [103, 2022, "청통면", "43 - 43", 7, 6, 1], | |
272 | + [104, 2022, "청통면", "44 - 44", 11, 11, 0], | |
273 | + [105, 2022, "청통면", "45 - 45", 9, 9, 0], | |
274 | + [106, 2022, "청통면", "46 - 46", 12, 10, 2], | |
275 | + [107, 2022, "청통면", "47 - 47", 14, 10, 4], | |
276 | + [108, 2022, "청통면", "48 - 48", 17, 12, 5], | |
277 | + [109, 2022, "청통면", "49 - 49", 13, 11, 2], | |
278 | + [110, 2022, "청통면", "50 - 50", 21, 16, 5], | |
279 | + [111, 2022, "청통면", "51 - 51", 19, 14, 5], | |
280 | + [112, 2022, "청통면", "52 - 52", 19, 10, 9], | |
281 | + [113, 2022, "청통면", "53 - 53", 18, 14, 4], | |
282 | + [114, 2022, "청통면", "54 - 54", 27, 22, 5], | |
283 | + [115, 2022, "청통면", "55 - 55", 38, 23, 15], | |
284 | + [116, 2022, "청통면", "56 - 56", 34, 26, 8], | |
285 | + [117, 2022, "청통면", "57 - 57", 28, 21, 7], | |
286 | + [118, 2022, "청통면", "58 - 58", 34, 22, 12], | |
287 | + [119, 2022, "청통면", "59 - 59", 32, 19, 13], | |
288 | + [120, 2022, "청통면", "60 - 60", 25, 19, 6], | |
289 | + [121, 2022, "청통면", "61 - 61", 39, 22, 17], | |
290 | + [122, 2022, "청통면", "62 - 62", 42, 25, 17], | |
291 | + [123, 2022, "청통면", "63 - 63", 37, 22, 15], | |
292 | + [124, 2022, "청통면", "64 - 64", 34, 28, 6], | |
293 | + [125, 2022, "청통면", "65 - 65", 33, 25, 8], | |
294 | + [126, 2022, "청통면", "66 - 66", 33, 21, 12], | |
295 | + [127, 2022, "청통면", "67 - 67", 32, 22, 10], | |
296 | + [128, 2022, "청통면", "68 - 68", 33, 19, 14], | |
297 | + [129, 2022, "청통면", "69 - 69", 20, 10, 10], | |
298 | + [130, 2022, "청통면", "70 - 70", 29, 15, 14], | |
299 | + [131, 2022, "청통면", "71 - 71", 21, 7, 14], | |
300 | + [132, 2022, "청통면", "72 - 72", 20, 7, 13], | |
301 | + [133, 2022, "청통면", "73 - 73", 29, 17, 12], | |
302 | + [134, 2022, "청통면", "74 - 74", 17, 9, 8], | |
303 | + [135, 2022, "청통면", "75 - 75", 27, 13, 14], | |
304 | + [136, 2022, "청통면", "76 - 76", 15, 6, 9], | |
305 | + [137, 2022, "청통면", "77 - 77", 9, 1, 8], | |
306 | + [138, 2022, "청통면", "78 - 78", 23, 4, 19], | |
307 | + [139, 2022, "청통면", "79 - 79", 21, 3, 18], | |
308 | + [140, 2022, "청통면", "80 - 80", 26, 8, 18], | |
309 | + [141, 2022, "청통면", "81 - 81", 20, 4, 16], | |
310 | + [142, 2022, "청통면", "82 - 82", 30, 6, 24], | |
311 | + [143, 2022, "청통면", "83 - 83", 20, 1, 19], | |
312 | + [144, 2022, "청통면", "84 - 84", 16, 1, 15], | |
313 | + [145, 2022, "청통면", "85 - 85", 24, 6, 18], | |
314 | + [146, 2022, "청통면", "86 - 86", 17, 3, 14], | |
315 | + [147, 2022, "청통면", "87 - 87", 13, 1, 12], | |
316 | + [148, 2022, "청통면", "88 - 88", 20, 1, 19], | |
317 | + [149, 2022, "청통면", "89 - 89", 10, 2, 8], | |
318 | + [150, 2022, "청통면", "90 - 90", 5, 0, 5], | |
319 | + [151, 2022, "청통면", "91 - 91", 8, 2, 6], | |
320 | + [152, 2022, "청통면", "92 - 92", 2, 0, 2], | |
321 | + [153, 2022, "청통면", "93 - 93", 5, 2, 3], | |
322 | + [154, 2022, "청통면", "94 - 94", 3, 0, 3], | |
323 | + [155, 2022, "청통면", "95 - 95", 4, 0, 4], | |
324 | + [156, 2022, "청통면", "96 - 96", 1, 0, 1], | |
325 | + [157, 2022, "청통면", "97 - 97", 3, 1, 2], | |
326 | + [158, 2022, "청통면", "101 - 101", 1, 0, 1], | |
327 | + [159, 2022, "신녕면", "15 - 15", 2, 1, 1], | |
328 | + [160, 2022, "신녕면", "16 - 16", 31, 14, 17], | |
329 | + [161, 2022, "신녕면", "17 - 17", 25, 12, 13], | |
330 | + [162, 2022, "신녕면", "18 - 18", 24, 13, 11], | |
331 | + [163, 2022, "신녕면", "19 - 19", 3, 2, 1], | |
332 | + [164, 2022, "신녕면", "22 - 22", 4, 3, 1], | |
333 | + [165, 2022, "신녕면", "23 - 23", 1, 1, 0], | |
334 | + [166, 2022, "신녕면", "24 - 24", 3, 3, 0], | |
335 | + [167, 2022, "신녕면", "25 - 25", 4, 2, 2], | |
336 | + [168, 2022, "신녕면", "26 - 26", 3, 2, 1], | |
337 | + [169, 2022, "신녕면", "27 - 27", 2, 2, 0], | |
338 | + [170, 2022, "신녕면", "28 - 28", 5, 3, 2], | |
339 | + [171, 2022, "신녕면", "29 - 29", 4, 4, 0], | |
340 | + [172, 2022, "신녕면", "30 - 30", 8, 4, 4], | |
341 | + [173, 2022, "신녕면", "31 - 31", 6, 5, 1], | |
342 | + [174, 2022, "신녕면", "32 - 32", 6, 5, 1], | |
343 | + [175, 2022, "신녕면", "33 - 33", 6, 4, 2], | |
344 | + [176, 2022, "신녕면", "34 - 34", 6, 4, 2], | |
345 | + [177, 2022, "신녕면", "35 - 35", 4, 3, 1], | |
346 | + [178, 2022, "신녕면", "36 - 36", 5, 3, 2], | |
347 | + [179, 2022, "신녕면", "37 - 37", 7, 4, 3], | |
348 | + [180, 2022, "신녕면", "38 - 38", 3, 3, 0], | |
349 | + [181, 2022, "신녕면", "39 - 39", 7, 7, 0], | |
350 | + [182, 2022, "신녕면", "40 - 40", 7, 5, 2], | |
351 | + [183, 2022, "신녕면", "41 - 41", 9, 8, 1], | |
352 | + [184, 2022, "신녕면", "42 - 42", 8, 5, 3], | |
353 | + [185, 2022, "신녕면", "43 - 43", 8, 5, 3], | |
354 | + [186, 2022, "신녕면", "44 - 44", 7, 4, 3], | |
355 | + [187, 2022, "신녕면", "45 - 45", 9, 6, 3], | |
356 | + [188, 2022, "신녕면", "46 - 46", 8, 6, 2], | |
357 | + [189, 2022, "신녕면", "47 - 47", 10, 8, 2], | |
358 | + [190, 2022, "신녕면", "48 - 48", 7, 6, 1], | |
359 | + [191, 2022, "신녕면", "49 - 49", 12, 8, 4], | |
360 | + [192, 2022, "신녕면", "50 - 50", 12, 7, 5], | |
361 | + [193, 2022, "신녕면", "51 - 51", 16, 10, 6], | |
362 | + [194, 2022, "신녕면", "52 - 52", 14, 10, 4], | |
363 | + [195, 2022, "신녕면", "53 - 53", 17, 11, 6], | |
364 | + [196, 2022, "신녕면", "54 - 54", 30, 11, 19], | |
365 | + [197, 2022, "신녕면", "55 - 55", 21, 16, 5], | |
366 | + [198, 2022, "신녕면", "56 - 56", 30, 21, 9], | |
367 | + [199, 2022, "신녕면", "57 - 57", 29, 16, 13], | |
368 | + [200, 2022, "신녕면", "58 - 58", 35, 28, 7], | |
369 | + [201, 2022, "신녕면", "59 - 59", 21, 12, 9], | |
370 | + [202, 2022, "신녕면", "60 - 60", 37, 26, 11], | |
371 | + [203, 2022, "신녕면", "61 - 61", 43, 26, 17], | |
372 | + [204, 2022, "신녕면", "62 - 62", 39, 21, 18], | |
373 | + [205, 2022, "신녕면", "63 - 63", 41, 20, 21], | |
374 | + [206, 2022, "신녕면", "64 - 64", 37, 23, 14], | |
375 | + [207, 2022, "신녕면", "65 - 65", 34, 20, 14], | |
376 | + [208, 2022, "신녕면", "66 - 66", 31, 15, 16], | |
377 | + [209, 2022, "신녕면", "67 - 67", 40, 21, 19], | |
378 | + [210, 2022, "신녕면", "68 - 68", 28, 12, 16], | |
379 | + [211, 2022, "신녕면", "69 - 69", 26, 11, 15], | |
380 | + [212, 2022, "신녕면", "70 - 70", 35, 16, 19], | |
381 | + [213, 2022, "신녕면", "71 - 71", 16, 9, 7], | |
382 | + [214, 2022, "신녕면", "72 - 72", 21, 11, 10], | |
383 | + [215, 2022, "신녕면", "73 - 73", 25, 7, 18], | |
384 | + [216, 2022, "신녕면", "74 - 74", 16, 6, 10], | |
385 | + [217, 2022, "신녕면", "75 - 75", 26, 9, 17], | |
386 | + [218, 2022, "신녕면", "76 - 76", 16, 6, 10], | |
387 | + [219, 2022, "신녕면", "77 - 77", 19, 9, 10], | |
388 | + [220, 2022, "신녕면", "78 - 78", 28, 4, 24], | |
389 | + [221, 2022, "신녕면", "79 - 79", 33, 6, 27], | |
390 | + [222, 2022, "신녕면", "80 - 80", 31, 7, 24], | |
391 | + [223, 2022, "신녕면", "81 - 81", 26, 7, 19], | |
392 | + [224, 2022, "신녕면", "82 - 82", 18, 1, 17], | |
393 | + [225, 2022, "신녕면", "83 - 83", 29, 4, 25], | |
394 | + [226, 2022, "신녕면", "84 - 84", 23, 1, 22], | |
395 | + [227, 2022, "신녕면", "85 - 85", 17, 0, 17], | |
396 | + [228, 2022, "신녕면", "86 - 86", 12, 3, 9], | |
397 | + [229, 2022, "신녕면", "87 - 87", 14, 2, 12], | |
398 | + [230, 2022, "신녕면", "88 - 88", 16, 1, 15], | |
399 | + [231, 2022, "신녕면", "89 - 89", 12, 2, 10], | |
400 | + [232, 2022, "신녕면", "90 - 90", 20, 1, 19], | |
401 | + [233, 2022, "신녕면", "91 - 91", 11, 2, 9], | |
402 | + [234, 2022, "신녕면", "92 - 92", 8, 0, 8], | |
403 | + [235, 2022, "신녕면", "93 - 93", 6, 0, 6], | |
404 | + [236, 2022, "신녕면", "94 - 94", 2, 0, 2], | |
405 | + [237, 2022, "신녕면", "96 - 96", 1, 0, 1], | |
406 | + [238, 2022, "신녕면", "98 - 98", 2, 0, 2], | |
407 | + [239, 2022, "신녕면", "102 - 102", 1, 1, 0], | |
408 | + [240, 2022, "화산면", "03-03", 1, 1, 0], | |
409 | + [241, 2022, "화산면", "21 - 21", 1, 1, 0], | |
410 | + [242, 2022, "화산면", "22 - 22", 3, 3, 0], | |
411 | + [243, 2022, "화산면", "23 - 23", 2, 0, 2], | |
412 | + [244, 2022, "화산면", "24 - 24", 2, 2, 0], | |
413 | + [245, 2022, "화산면", "25 - 25", 4, 3, 1], | |
414 | + [246, 2022, "화산면", "26 - 26", 4, 3, 1], | |
415 | + [247, 2022, "화산면", "27 - 27", 7, 7, 0], | |
416 | + [248, 2022, "화산면", "28 - 28", 3, 3, 0], | |
417 | + [249, 2022, "화산면", "29 - 29", 8, 4, 4], | |
418 | + [250, 2022, "화산면", "30 - 30", 3, 2, 1], | |
419 | + [251, 2022, "화산면", "31 - 31", 3, 2, 1], | |
420 | + [252, 2022, "화산면", "32 - 32", 5, 3, 2], | |
421 | + [253, 2022, "화산면", "33 - 33", 2, 2, 0], | |
422 | + [254, 2022, "화산면", "34 - 34", 4, 4, 0], | |
423 | + [255, 2022, "화산면", "35 - 35", 3, 2, 1], | |
424 | + [256, 2022, "화산면", "36 - 36", 6, 4, 2], | |
425 | + [257, 2022, "화산면", "37 - 37", 4, 4, 0], | |
426 | + [258, 2022, "화산면", "38 - 38", 5, 5, 0], | |
427 | + [259, 2022, "화산면", "39 - 39", 3, 1, 2], | |
428 | + [260, 2022, "화산면", "40 - 40", 9, 8, 1], | |
429 | + [261, 2022, "화산면", "41 - 41", 4, 2, 2], | |
430 | + [262, 2022, "화산면", "42 - 42", 5, 4, 1], | |
431 | + [263, 2022, "화산면", "43 - 43", 5, 4, 1], | |
432 | + [264, 2022, "화산면", "44 - 44", 4, 4, 0], | |
433 | + [265, 2022, "화산면", "45 - 45", 11, 7, 4], | |
434 | + [266, 2022, "화산면", "46 - 46", 13, 8, 5], | |
435 | + [267, 2022, "화산면", "47 - 47", 8, 7, 1], | |
436 | + [268, 2022, "화산면", "48 - 48", 10, 6, 4], | |
437 | + [269, 2022, "화산면", "49 - 49", 14, 10, 4], | |
438 | + [270, 2022, "화산면", "50 - 50", 15, 13, 2], | |
439 | + [271, 2022, "화산면", "51 - 51", 14, 12, 2], | |
440 | + [272, 2022, "화산면", "52 - 52", 14, 13, 1], | |
441 | + [273, 2022, "화산면", "53 - 53", 13, 9, 4], | |
442 | + [274, 2022, "화산면", "54 - 54", 15, 10, 5], | |
443 | + [275, 2022, "화산면", "55 - 55", 17, 13, 4], | |
444 | + [276, 2022, "화산면", "56 - 56", 15, 9, 6], | |
445 | + [277, 2022, "화산면", "57 - 57", 18, 11, 7], | |
446 | + [278, 2022, "화산면", "58 - 58", 28, 21, 7], | |
447 | + [279, 2022, "화산면", "59 - 59", 28, 20, 8], | |
448 | + [280, 2022, "화산면", "60 - 60", 29, 23, 6], | |
449 | + [281, 2022, "화산면", "61 - 61", 19, 14, 5], | |
450 | + [282, 2022, "화산면", "62 - 62", 22, 16, 6], | |
451 | + [283, 2022, "화산면", "63 - 63", 29, 23, 6], | |
452 | + [284, 2022, "화산면", "64 - 64", 25, 17, 8], | |
453 | + [285, 2022, "화산면", "65 - 65", 29, 20, 9], | |
454 | + [286, 2022, "화산면", "66 - 66", 34, 19, 15], | |
455 | + [287, 2022, "화산면", "67 - 67", 23, 14, 9], | |
456 | + [288, 2022, "화산면", "68 - 68", 27, 17, 10], | |
457 | + [289, 2022, "화산면", "69 - 69", 21, 10, 11], | |
458 | + [290, 2022, "화산면", "70 - 70", 20, 8, 12], | |
459 | + [291, 2022, "화산면", "71 - 71", 20, 10, 10], | |
460 | + [292, 2022, "화산면", "72 - 72", 30, 18, 12], | |
461 | + [293, 2022, "화산면", "73 - 73", 22, 11, 11], | |
462 | + [294, 2022, "화산면", "74 - 74", 17, 9, 8], | |
463 | + [295, 2022, "화산면", "75 - 75", 23, 8, 15], | |
464 | + [296, 2022, "화산면", "76 - 76", 8, 0, 8], | |
465 | + [297, 2022, "화산면", "77 - 77", 11, 2, 9], | |
466 | + [298, 2022, "화산면", "78 - 78", 26, 7, 19], | |
467 | + [299, 2022, "화산면", "79 - 79", 20, 2, 18], | |
468 | + [300, 2022, "화산면", "80 - 80", 25, 2, 23], | |
469 | + [301, 2022, "화산면", "81 - 81", 24, 2, 22], | |
470 | + [302, 2022, "화산면", "82 - 82", 18, 3, 15], | |
471 | + [303, 2022, "화산면", "83 - 83", 34, 5, 29], | |
472 | + [304, 2022, "화산면", "84 - 84", 19, 6, 13], | |
473 | + [305, 2022, "화산면", "85 - 85", 26, 3, 23], | |
474 | + [306, 2022, "화산면", "86 - 86", 20, 3, 17], | |
475 | + [307, 2022, "화산면", "87 - 87", 21, 4, 17], | |
476 | + [308, 2022, "화산면", "88 - 88", 20, 5, 15], | |
477 | + [309, 2022, "화산면", "89 - 89", 5, 0, 5], | |
478 | + [310, 2022, "화산면", "90 - 90", 7, 0, 7], | |
479 | + [311, 2022, "화산면", "91 - 91", 7, 0, 7], | |
480 | + [312, 2022, "화산면", "92 - 92", 6, 2, 4], | |
481 | + [313, 2022, "화산면", "93 - 93", 3, 1, 2], | |
482 | + [314, 2022, "화산면", "94 - 94", 3, 0, 3], | |
483 | + [315, 2022, "화산면", "95 - 95", 1, 0, 1], | |
484 | + [316, 2022, "화산면", "101 - 101", 1, 0, 1], | |
485 | + [317, 2022, "화북면", "20 - 20", 1, 0, 1], | |
486 | + [318, 2022, "화북면", "21 - 21", 1, 0, 1], | |
487 | + [319, 2022, "화북면", "23 - 23", 1, 0, 1], | |
488 | + [320, 2022, "화북면", "24 - 24", 2, 2, 0], | |
489 | + [321, 2022, "화북면", "25 - 25", 2, 1, 1], | |
490 | + [322, 2022, "화북면", "26 - 26", 1, 0, 1], | |
491 | + [323, 2022, "화북면", "27 - 27", 1, 1, 0], | |
492 | + [324, 2022, "화북면", "28 - 28", 7, 4, 3], | |
493 | + [325, 2022, "화북면", "29 - 29", 2, 0, 2], | |
494 | + [326, 2022, "화북면", "30 - 30", 1, 1, 0], | |
495 | + [327, 2022, "화북면", "32 - 32", 2, 1, 1], | |
496 | + [328, 2022, "화북면", "33 - 33", 2, 1, 1], | |
497 | + [329, 2022, "화북면", "34 - 34", 1, 1, 0], | |
498 | + [330, 2022, "화북면", "35 - 35", 4, 4, 0], | |
499 | + [331, 2022, "화북면", "36 - 36", 2, 2, 0], | |
500 | + [332, 2022, "화북면", "37 - 37", 3, 1, 2], | |
501 | + [333, 2022, "화북면", "38 - 38", 4, 4, 0], | |
502 | + [334, 2022, "화북면", "39 - 39", 1, 0, 1], | |
503 | + [335, 2022, "화북면", "40 - 40", 2, 2, 0], | |
504 | + [336, 2022, "화북면", "41 - 41", 2, 2, 0], | |
505 | + [337, 2022, "화북면", "42 - 42", 2, 1, 1], | |
506 | + [338, 2022, "화북면", "43 - 43", 6, 5, 1], | |
507 | + [339, 2022, "화북면", "44 - 44", 3, 2, 1], | |
508 | + [340, 2022, "화북면", "45 - 45", 5, 4, 1], | |
509 | + [341, 2022, "화북면", "46 - 46", 8, 6, 2], | |
510 | + [342, 2022, "화북면", "47 - 47", 6, 4, 2], | |
511 | + [343, 2022, "화북면", "48 - 48", 9, 6, 3], | |
512 | + [344, 2022, "화북면", "49 - 49", 11, 8, 3], | |
513 | + [345, 2022, "화북면", "50 - 50", 11, 9, 2], | |
514 | + [346, 2022, "화북면", "51 - 51", 8, 5, 3], | |
515 | + [347, 2022, "화북면", "52 - 52", 12, 8, 4], | |
516 | + [348, 2022, "화북면", "53 - 53", 12, 6, 6], | |
517 | + [349, 2022, "화북면", "54 - 54", 23, 14, 9], | |
518 | + [350, 2022, "화북면", "55 - 55", 24, 15, 9], | |
519 | + [351, 2022, "화북면", "56 - 56", 19, 13, 6], | |
520 | + [352, 2022, "화북면", "57 - 57", 21, 12, 9], | |
521 | + [353, 2022, "화북면", "58 - 58", 19, 13, 6], | |
522 | + [354, 2022, "화북면", "59 - 59", 18, 9, 9], | |
523 | + [355, 2022, "화북면", "60 - 60", 23, 12, 11], | |
524 | + [356, 2022, "화북면", "61 - 61", 22, 13, 9], | |
525 | + [357, 2022, "화북면", "62 - 62", 17, 10, 7], | |
526 | + [358, 2022, "화북면", "63 - 63", 38, 19, 19], | |
527 | + [359, 2022, "화북면", "64 - 64", 10, 7, 3], | |
528 | + [360, 2022, "화북면", "65 - 65", 16, 11, 5], | |
529 | + [361, 2022, "화북면", "66 - 66", 23, 11, 12], | |
530 | + [362, 2022, "화북면", "67 - 67", 15, 11, 4], | |
531 | + [363, 2022, "화북면", "68 - 68", 16, 10, 6], | |
532 | + [364, 2022, "화북면", "69 - 69", 16, 9, 7], | |
533 | + [365, 2022, "화북면", "70 - 70", 15, 12, 3], | |
534 | + [366, 2022, "화북면", "71 - 71", 11, 3, 8], | |
535 | + [367, 2022, "화북면", "72 - 72", 22, 9, 13], | |
536 | + [368, 2022, "화북면", "73 - 73", 20, 13, 7], | |
537 | + [369, 2022, "화북면", "74 - 74", 16, 7, 9], | |
538 | + [370, 2022, "화북면", "75 - 75", 28, 8, 20], | |
539 | + [371, 2022, "화북면", "76 - 76", 7, 4, 3], | |
540 | + [372, 2022, "화북면", "77 - 77", 9, 3, 6], | |
541 | + [373, 2022, "화북면", "78 - 78", 6, 2, 4], | |
542 | + [374, 2022, "화북면", "79 - 79", 14, 2, 12], | |
543 | + [375, 2022, "화북면", "80 - 80", 18, 4, 14], | |
544 | + [376, 2022, "화북면", "81 - 81", 7, 1, 6], | |
545 | + [377, 2022, "화북면", "82 - 82", 18, 3, 15], | |
546 | + [378, 2022, "화북면", "83 - 83", 14, 2, 12], | |
547 | + [379, 2022, "화북면", "84 - 84", 14, 2, 12], | |
548 | + [380, 2022, "화북면", "85 - 85", 14, 1, 13], | |
549 | + [381, 2022, "화북면", "86 - 86", 12, 1, 11], | |
550 | + [382, 2022, "화북면", "87 - 87", 13, 2, 11], | |
551 | + [383, 2022, "화북면", "88 - 88", 13, 4, 9], | |
552 | + [384, 2022, "화북면", "89 - 89", 5, 0, 5], | |
553 | + [385, 2022, "화북면", "90 - 90", 7, 4, 3], | |
554 | + [386, 2022, "화북면", "91 - 91", 9, 1, 8], | |
555 | + [387, 2022, "화북면", "92 - 92", 3, 0, 3], | |
556 | + [388, 2022, "화북면", "93 - 93", 2, 0, 2], | |
557 | + [389, 2022, "화북면", "94 - 94", 5, 0, 5], | |
558 | + [390, 2022, "화북면", "95 - 95", 1, 0, 1], | |
559 | + [391, 2022, "화북면", "96 - 96", 2, 0, 2], | |
560 | + [392, 2022, "화북면", "98 - 98", 1, 0, 1], | |
561 | + [393, 2022, "화북면", "100 - 100", 1, 0, 1], | |
562 | + [394, 2022, "화북면", "107 - 107", 1, 0, 1], | |
563 | + [395, 2022, "화남면", "14 - 14", 1, 1, 0], | |
564 | + [396, 2022, "화남면", "17 - 17", 1, 1, 0], | |
565 | + [397, 2022, "화남면", "19 - 19", 1, 1, 0], | |
566 | + [398, 2022, "화남면", "20 - 20", 1, 1, 0], | |
567 | + [399, 2022, "화남면", "23 - 23", 2, 2, 0], | |
568 | + [400, 2022, "화남면", "24 - 24", 3, 2, 1], | |
569 | + [401, 2022, "화남면", "26 - 26", 3, 3, 0], | |
570 | + [402, 2022, "화남면", "27 - 27", 3, 1, 2], | |
571 | + [403, 2022, "화남면", "28 - 28", 1, 1, 0], | |
572 | + [404, 2022, "화남면", "29 - 29", 5, 4, 1], | |
573 | + [405, 2022, "화남면", "30 - 30", 1, 0, 1], | |
574 | + [406, 2022, "화남면", "31 - 31", 3, 2, 1], | |
575 | + [407, 2022, "화남면", "32 - 32", 4, 4, 0], | |
576 | + [408, 2022, "화남면", "33 - 33", 4, 3, 1], | |
577 | + [409, 2022, "화남면", "34 - 34", 5, 3, 2], | |
578 | + [410, 2022, "화남면", "35 - 35", 3, 2, 1], | |
579 | + [411, 2022, "화남면", "36 - 36", 4, 2, 2], | |
580 | + [412, 2022, "화남면", "37 - 37", 1, 1, 0], | |
581 | + [413, 2022, "화남면", "38 - 38", 5, 4, 1], | |
582 | + [414, 2022, "화남면", "39 - 39", 3, 3, 0], | |
583 | + [415, 2022, "화남면", "40 - 40", 7, 5, 2], | |
584 | + [416, 2022, "화남면", "41 - 41", 7, 7, 0], | |
585 | + [417, 2022, "화남면", "42 - 42", 7, 6, 1], | |
586 | + [418, 2022, "화남면", "43 - 43", 10, 9, 1], | |
587 | + [419, 2022, "화남면", "44 - 44", 6, 5, 1], | |
588 | + [420, 2022, "화남면", "45 - 45", 7, 4, 3], | |
589 | + [421, 2022, "화남면", "46 - 46", 11, 9, 2], | |
590 | + [422, 2022, "화남면", "47 - 47", 7, 3, 4], | |
591 | + [423, 2022, "화남면", "48 - 48", 14, 12, 2], | |
592 | + [424, 2022, "화남면", "49 - 49", 7, 5, 2], | |
593 | + [425, 2022, "화남면", "50 - 50", 19, 14, 5], | |
594 | + [426, 2022, "화남면", "51 - 51", 15, 13, 2], | |
595 | + [427, 2022, "화남면", "52 - 52", 15, 10, 5], | |
596 | + [428, 2022, "화남면", "53 - 53", 16, 8, 8], | |
597 | + [429, 2022, "화남면", "54 - 54", 21, 17, 4], | |
598 | + [430, 2022, "화남면", "55 - 55", 23, 19, 4], | |
599 | + [431, 2022, "화남면", "56 - 56", 23, 13, 10], | |
600 | + [432, 2022, "화남면", "57 - 57", 22, 14, 8], | |
601 | + [433, 2022, "화남면", "58 - 58", 31, 23, 8], | |
602 | + [434, 2022, "화남면", "59 - 59", 25, 20, 5], | |
603 | + [435, 2022, "화남면", "60 - 60", 35, 24, 11], | |
604 | + [436, 2022, "화남면", "61 - 61", 34, 23, 11], | |
605 | + [437, 2022, "화남면", "62 - 62", 28, 17, 11], | |
606 | + [438, 2022, "화남면", "63 - 63", 32, 21, 11], | |
607 | + [439, 2022, "화남면", "64 - 64", 25, 17, 8], | |
608 | + [440, 2022, "화남면", "65 - 65", 27, 18, 9], | |
609 | + [441, 2022, "화남면", "66 - 66", 22, 16, 6], | |
610 | + [442, 2022, "화남면", "67 - 67", 26, 16, 10], | |
611 | + [443, 2022, "화남면", "68 - 68", 19, 10, 9], | |
612 | + [444, 2022, "화남면", "69 - 69", 26, 18, 8], | |
613 | + [445, 2022, "화남면", "70 - 70", 18, 10, 8], | |
614 | + [446, 2022, "화남면", "71 - 71", 22, 12, 10], | |
615 | + [447, 2022, "화남면", "72 - 72", 27, 15, 12], | |
616 | + [448, 2022, "화남면", "73 - 73", 28, 15, 13], | |
617 | + [449, 2022, "화남면", "74 - 74", 21, 6, 15], | |
618 | + [450, 2022, "화남면", "75 - 75", 13, 2, 11], | |
619 | + [451, 2022, "화남면", "76 - 76", 13, 3, 10], | |
620 | + [452, 2022, "화남면", "77 - 77", 14, 6, 8], | |
621 | + [453, 2022, "화남면", "78 - 78", 16, 4, 12], | |
622 | + [454, 2022, "화남면", "79 - 79", 12, 4, 8], | |
623 | + [455, 2022, "화남면", "80 - 80", 26, 4, 22], | |
624 | + [456, 2022, "화남면", "81 - 81", 20, 1, 19], | |
625 | + [457, 2022, "화남면", "82 - 82", 18, 4, 14], | |
626 | + [458, 2022, "화남면", "83 - 83", 22, 2, 20], | |
627 | + [459, 2022, "화남면", "84 - 84", 15, 1, 14], | |
628 | + [460, 2022, "화남면", "85 - 85", 15, 2, 13], | |
629 | + [461, 2022, "화남면", "86 - 86", 14, 3, 11], | |
630 | + [462, 2022, "화남면", "87 - 87", 18, 5, 13], | |
631 | + [463, 2022, "화남면", "88 - 88", 11, 1, 10], | |
632 | + [464, 2022, "화남면", "89 - 89", 6, 0, 6], | |
633 | + [465, 2022, "화남면", "90 - 90", 7, 2, 5], | |
634 | + [466, 2022, "화남면", "91 - 91", 7, 0, 7], | |
635 | + [467, 2022, "화남면", "92 - 92", 4, 0, 4], | |
636 | + [468, 2022, "화남면", "93 - 93", 5, 0, 5], | |
637 | + [469, 2022, "화남면", "94 - 94", 1, 1, 0], | |
638 | + [470, 2022, "화남면", "97 - 97", 2, 1, 1], | |
639 | + [471, 2022, "화남면", "101 - 101", 1, 0, 1], | |
640 | + [472, 2022, "자양면", "23 - 23", 1, 1, 0], | |
641 | + [473, 2022, "자양면", "24 - 24", 1, 0, 1], | |
642 | + [474, 2022, "자양면", "25 - 25", 3, 2, 1], | |
643 | + [475, 2022, "자양면", "26 - 26", 3, 3, 0], | |
644 | + [476, 2022, "자양면", "29 - 29", 1, 0, 1], | |
645 | + [477, 2022, "자양면", "30 - 30", 2, 1, 1], | |
646 | + [478, 2022, "자양면", "31 - 31", 1, 1, 0], | |
647 | + [479, 2022, "자양면", "32 - 32", 3, 2, 1], | |
648 | + [480, 2022, "자양면", "33 - 33", 1, 1, 0], | |
649 | + [481, 2022, "자양면", "34 - 34", 1, 0, 1], | |
650 | + [482, 2022, "자양면", "35 - 35", 1, 1, 0], | |
651 | + [483, 2022, "자양면", "36 - 36", 4, 3, 1], | |
652 | + [484, 2022, "자양면", "38 - 38", 2, 2, 0], | |
653 | + [485, 2022, "자양면", "39 - 39", 2, 0, 2], | |
654 | + [486, 2022, "자양면", "40 - 40", 4, 3, 1], | |
655 | + [487, 2022, "자양면", "41 - 41", 1, 0, 1], | |
656 | + [488, 2022, "자양면", "42 - 42", 2, 1, 1], | |
657 | + [489, 2022, "자양면", "43 - 43", 1, 1, 0], | |
658 | + [490, 2022, "자양면", "44 - 44", 2, 2, 0], | |
659 | + [491, 2022, "자양면", "45 - 45", 1, 0, 1], | |
660 | + [492, 2022, "자양면", "47 - 47", 4, 3, 1], | |
661 | + [493, 2022, "자양면", "48 - 48", 2, 2, 0], | |
662 | + [494, 2022, "자양면", "49 - 49", 6, 3, 3], | |
663 | + [495, 2022, "자양면", "50 - 50", 5, 4, 1], | |
664 | + [496, 2022, "자양면", "51 - 51", 5, 5, 0], | |
665 | + [497, 2022, "자양면", "52 - 52", 4, 3, 1], | |
666 | + [498, 2022, "자양면", "53 - 53", 8, 7, 1], | |
667 | + [499, 2022, "자양면", "54 - 54", 13, 8, 5], | |
668 | + [500, 2022, "자양면", "55 - 55", 12, 7, 5], | |
669 | + [501, 2022, "자양면", "56 - 56", 13, 8, 5], | |
670 | + [502, 2022, "자양면", "57 - 57", 15, 7, 8], | |
671 | + [503, 2022, "자양면", "58 - 58", 17, 9, 8], | |
672 | + [504, 2022, "자양면", "59 - 59", 11, 7, 4], | |
673 | + [505, 2022, "자양면", "60 - 60", 13, 10, 3], | |
674 | + [506, 2022, "자양면", "61 - 61", 14, 10, 4], | |
675 | + [507, 2022, "자양면", "62 - 62", 23, 15, 8], | |
676 | + [508, 2022, "자양면", "63 - 63", 24, 16, 8], | |
677 | + [509, 2022, "자양면", "64 - 64", 8, 6, 2], | |
678 | + [510, 2022, "자양면", "65 - 65", 12, 7, 5], | |
679 | + [511, 2022, "자양면", "66 - 66", 15, 13, 2], | |
680 | + [512, 2022, "자양면", "67 - 67", 10, 8, 2], | |
681 | + [513, 2022, "자양면", "68 - 68", 8, 4, 4], | |
682 | + [514, 2022, "자양면", "69 - 69", 8, 7, 1], | |
683 | + [515, 2022, "자양면", "70 - 70", 8, 5, 3], | |
684 | + [516, 2022, "자양면", "71 - 71", 7, 2, 5], | |
685 | + [517, 2022, "자양면", "72 - 72", 8, 5, 3], | |
686 | + [518, 2022, "자양면", "73 - 73", 11, 5, 6], | |
687 | + [519, 2022, "자양면", "74 - 74", 3, 2, 1], | |
688 | + [520, 2022, "자양면", "75 - 75", 11, 5, 6], | |
689 | + [521, 2022, "자양면", "76 - 76", 12, 6, 6], | |
690 | + [522, 2022, "자양면", "77 - 77", 15, 5, 10], | |
691 | + [523, 2022, "자양면", "78 - 78", 7, 0, 7], | |
692 | + [524, 2022, "자양면", "79 - 79", 8, 0, 8], | |
693 | + [525, 2022, "자양면", "80 - 80", 9, 1, 8], | |
694 | + [526, 2022, "자양면", "81 - 81", 15, 3, 12], | |
695 | + [527, 2022, "자양면", "82 - 82", 4, 0, 4], | |
696 | + [528, 2022, "자양면", "83 - 83", 4, 0, 4], | |
697 | + [529, 2022, "자양면", "84 - 84", 8, 3, 5], | |
698 | + [530, 2022, "자양면", "85 - 85", 10, 3, 7], | |
699 | + [531, 2022, "자양면", "86 - 86", 13, 2, 11], | |
700 | + [532, 2022, "자양면", "87 - 87", 6, 1, 5], | |
701 | + [533, 2022, "자양면", "88 - 88", 5, 0, 5], | |
702 | + [534, 2022, "자양면", "89 - 89", 2, 1, 1], | |
703 | + [535, 2022, "자양면", "90 - 90", 7, 1, 6], | |
704 | + [536, 2022, "자양면", "91 - 91", 2, 2, 0], | |
705 | + [537, 2022, "자양면", "92 - 92", 2, 0, 2], | |
706 | + [538, 2022, "자양면", "93 - 93", 4, 1, 3], | |
707 | + [539, 2022, "자양면", "94 - 94", 2, 0, 2], | |
708 | + [540, 2022, "임고면", "19 - 19", 1, 0, 1], | |
709 | + [541, 2022, "임고면", "20 - 20", 2, 2, 0], | |
710 | + [542, 2022, "임고면", "22 - 22", 4, 1, 3], | |
711 | + [543, 2022, "임고면", "23 - 23", 2, 1, 1], | |
712 | + [544, 2022, "임고면", "24 - 24", 4, 1, 3], | |
713 | + [545, 2022, "임고면", "25 - 25", 4, 2, 2], | |
714 | + [546, 2022, "임고면", "26 - 26", 3, 3, 0], | |
715 | + [547, 2022, "임고면", "27 - 27", 3, 3, 0], | |
716 | + [548, 2022, "임고면", "28 - 28", 10, 10, 0], | |
717 | + [549, 2022, "임고면", "29 - 29", 7, 4, 3], | |
718 | + [550, 2022, "임고면", "30 - 30", 5, 5, 0], | |
719 | + [551, 2022, "임고면", "31 - 31", 2, 1, 1], | |
720 | + [552, 2022, "임고면", "32 - 32", 2, 2, 0], | |
721 | + [553, 2022, "임고면", "33 - 33", 2, 2, 0], | |
722 | + [554, 2022, "임고면", "34 - 34", 6, 3, 3], | |
723 | + [555, 2022, "임고면", "35 - 35", 7, 5, 2], | |
724 | + [556, 2022, "임고면", "36 - 36", 3, 2, 1], | |
725 | + [557, 2022, "임고면", "37 - 37", 2, 2, 0], | |
726 | + [558, 2022, "임고면", "38 - 38", 7, 5, 2], | |
727 | + [559, 2022, "임고면", "39 - 39", 7, 6, 1], | |
728 | + [560, 2022, "임고면", "40 - 40", 7, 6, 1], | |
729 | + [561, 2022, "임고면", "41 - 41", 6, 5, 1], | |
730 | + [562, 2022, "임고면", "42 - 42", 3, 2, 1], | |
731 | + [563, 2022, "임고면", "43 - 43", 8, 4, 4], | |
732 | + [564, 2022, "임고면", "44 - 44", 5, 2, 3], | |
733 | + [565, 2022, "임고면", "45 - 45", 6, 4, 2], | |
734 | + [566, 2022, "임고면", "46 - 46", 10, 7, 3], | |
735 | + [567, 2022, "임고면", "47 - 47", 11, 5, 6], | |
736 | + [568, 2022, "임고면", "48 - 48", 20, 13, 7], | |
737 | + [569, 2022, "임고면", "49 - 49", 8, 7, 1], | |
738 | + [570, 2022, "임고면", "50 - 50", 20, 16, 4], | |
739 | + [571, 2022, "임고면", "51 - 51", 19, 15, 4], | |
740 | + [572, 2022, "임고면", "52 - 52", 25, 13, 12], | |
741 | + [573, 2022, "임고면", "53 - 53", 16, 10, 6], | |
742 | + [574, 2022, "임고면", "54 - 54", 23, 19, 4], | |
743 | + [575, 2022, "임고면", "55 - 55", 30, 16, 14], | |
744 | + [576, 2022, "임고면", "56 - 56", 20, 15, 5], | |
745 | + [577, 2022, "임고면", "57 - 57", 27, 18, 9], | |
746 | + [578, 2022, "임고면", "58 - 58", 30, 20, 10], | |
747 | + [579, 2022, "임고면", "59 - 59", 25, 18, 7], | |
748 | + [580, 2022, "임고면", "60 - 60", 27, 14, 13], | |
749 | + [581, 2022, "임고면", "61 - 61", 27, 18, 9], | |
750 | + [582, 2022, "임고면", "62 - 62", 34, 21, 13], | |
751 | + [583, 2022, "임고면", "63 - 63", 37, 25, 12], | |
752 | + [584, 2022, "임고면", "64 - 64", 29, 19, 10], | |
753 | + [585, 2022, "임고면", "65 - 65", 34, 25, 9], | |
754 | + [586, 2022, "임고면", "66 - 66", 32, 15, 17], | |
755 | + [587, 2022, "임고면", "67 - 67", 36, 22, 14], | |
756 | + [588, 2022, "임고면", "68 - 68", 25, 17, 8], | |
757 | + [589, 2022, "임고면", "69 - 69", 22, 10, 12], | |
758 | + [590, 2022, "임고면", "70 - 70", 27, 13, 14], | |
759 | + [591, 2022, "임고면", "71 - 71", 26, 16, 10], | |
760 | + [592, 2022, "임고면", "72 - 72", 27, 18, 9], | |
761 | + [593, 2022, "임고면", "73 - 73", 31, 10, 21], | |
762 | + [594, 2022, "임고면", "74 - 74", 29, 11, 18], | |
763 | + [595, 2022, "임고면", "75 - 75", 26, 6, 20], | |
764 | + [596, 2022, "임고면", "76 - 76", 14, 4, 10], | |
765 | + [597, 2022, "임고면", "77 - 77", 16, 2, 14], | |
766 | + [598, 2022, "임고면", "78 - 78", 24, 8, 16], | |
767 | + [599, 2022, "임고면", "79 - 79", 21, 3, 18], | |
768 | + [600, 2022, "임고면", "80 - 80", 24, 6, 18], | |
769 | + [601, 2022, "임고면", "81 - 81", 26, 5, 21], | |
770 | + [602, 2022, "임고면", "82 - 82", 22, 8, 14], | |
771 | + [603, 2022, "임고면", "83 - 83", 20, 2, 18], | |
772 | + [604, 2022, "임고면", "84 - 84", 28, 5, 23], | |
773 | + [605, 2022, "임고면", "85 - 85", 18, 1, 17], | |
774 | + [606, 2022, "임고면", "86 - 86", 23, 4, 19], | |
775 | + [607, 2022, "임고면", "87 - 87", 19, 3, 16], | |
776 | + [608, 2022, "임고면", "88 - 88", 15, 3, 12], | |
777 | + [609, 2022, "임고면", "89 - 89", 16, 2, 14], | |
778 | + [610, 2022, "임고면", "90 - 90", 14, 3, 11], | |
779 | + [611, 2022, "임고면", "91 - 91", 4, 2, 2], | |
780 | + [612, 2022, "임고면", "92 - 92", 4, 1, 3], | |
781 | + [613, 2022, "임고면", "93 - 93", 4, 2, 2], | |
782 | + [614, 2022, "임고면", "94 - 94", 4, 1, 3], | |
783 | + [615, 2022, "임고면", "95 - 95", 2, 1, 1], | |
784 | + [616, 2022, "임고면", "97 - 97", 1, 0, 1], | |
785 | + [617, 2022, "임고면", "98 - 98", 1, 0, 1], | |
786 | + [618, 2022, "임고면", "101 - 101", 1, 0, 1], | |
787 | + [619, 2022, "고경면", "20 - 20", 7, 5, 2], | |
788 | + [620, 2022, "고경면", "21 - 21", 72, 67, 5], | |
789 | + [621, 2022, "고경면", "22 - 22", 101, 94, 7], | |
790 | + [622, 2022, "고경면", "23 - 23", 81, 65, 16], | |
791 | + [623, 2022, "고경면", "24 - 24", 57, 51, 6], | |
792 | + [624, 2022, "고경면", "25 - 25", 40, 28, 12], | |
793 | + [625, 2022, "고경면", "26 - 26", 28, 23, 5], | |
794 | + [626, 2022, "고경면", "27 - 27", 19, 19, 0], | |
795 | + [627, 2022, "고경면", "28 - 28", 16, 10, 6], | |
796 | + [628, 2022, "고경면", "29 - 29", 10, 8, 2], | |
797 | + [629, 2022, "고경면", "30 - 30", 9, 5, 4], | |
798 | + [630, 2022, "고경면", "31 - 31", 14, 11, 3], | |
799 | + [631, 2022, "고경면", "32 - 32", 8, 6, 2], | |
800 | + [632, 2022, "고경면", "33 - 33", 10, 9, 1], | |
801 | + [633, 2022, "고경면", "34 - 34", 8, 7, 1], | |
802 | + [634, 2022, "고경면", "35 - 35", 5, 4, 1], | |
803 | + [635, 2022, "고경면", "36 - 36", 8, 8, 0], | |
804 | + [636, 2022, "고경면", "37 - 37", 6, 4, 2], | |
805 | + [637, 2022, "고경면", "38 - 38", 10, 8, 2], | |
806 | + [638, 2022, "고경면", "39 - 39", 14, 11, 3], | |
807 | + [639, 2022, "고경면", "40 - 40", 9, 7, 2], | |
808 | + [640, 2022, "고경면", "41 - 41", 15, 11, 4], | |
809 | + [641, 2022, "고경면", "42 - 42", 11, 8, 3], | |
810 | + [642, 2022, "고경면", "43 - 43", 7, 5, 2], | |
811 | + [643, 2022, "고경면", "44 - 44", 9, 6, 3], | |
812 | + [644, 2022, "고경면", "45 - 45", 17, 11, 6], | |
813 | + [645, 2022, "고경면", "46 - 46", 14, 11, 3], | |
814 | + [646, 2022, "고경면", "47 - 47", 19, 14, 5], | |
815 | + [647, 2022, "고경면", "48 - 48", 14, 12, 2], | |
816 | + [648, 2022, "고경면", "49 - 49", 15, 12, 3], | |
817 | + [649, 2022, "고경면", "50 - 50", 21, 15, 6], | |
818 | + [650, 2022, "고경면", "51 - 51", 32, 24, 8], | |
819 | + [651, 2022, "고경면", "52 - 52", 33, 24, 9], | |
820 | + [652, 2022, "고경면", "53 - 53", 34, 24, 10], | |
821 | + [653, 2022, "고경면", "54 - 54", 26, 18, 8], | |
822 | + [654, 2022, "고경면", "55 - 55", 36, 21, 15], | |
823 | + [655, 2022, "고경면", "56 - 56", 30, 21, 9], | |
824 | + [656, 2022, "고경면", "57 - 57", 46, 28, 18], | |
825 | + [657, 2022, "고경면", "58 - 58", 40, 21, 19], | |
826 | + [658, 2022, "고경면", "59 - 59", 47, 33, 14], | |
827 | + [659, 2022, "고경면", "60 - 60", 48, 34, 14], | |
828 | + [660, 2022, "고경면", "61 - 61", 52, 28, 24], | |
829 | + [661, 2022, "고경면", "62 - 62", 36, 24, 12], | |
830 | + [662, 2022, "고경면", "63 - 63", 58, 43, 15], | |
831 | + [663, 2022, "고경면", "64 - 64", 43, 37, 6], | |
832 | + [664, 2022, "고경면", "65 - 65", 36, 21, 15], | |
833 | + [665, 2022, "고경면", "66 - 66", 45, 33, 12], | |
834 | + [666, 2022, "고경면", "67 - 67", 51, 25, 26], | |
835 | + [667, 2022, "고경면", "68 - 68", 36, 20, 16], | |
836 | + [668, 2022, "고경면", "69 - 69", 24, 10, 14], | |
837 | + [669, 2022, "고경면", "70 - 70", 32, 21, 11], | |
838 | + [670, 2022, "고경면", "71 - 71", 30, 16, 14], | |
839 | + [671, 2022, "고경면", "72 - 72", 30, 17, 13], | |
840 | + [672, 2022, "고경면", "73 - 73", 33, 18, 15], | |
841 | + [673, 2022, "고경면", "74 - 74", 28, 9, 19], | |
842 | + [674, 2022, "고경면", "75 - 75", 33, 8, 25], | |
843 | + [675, 2022, "고경면", "76 - 76", 22, 4, 18], | |
844 | + [676, 2022, "고경면", "77 - 77", 29, 9, 20], | |
845 | + [677, 2022, "고경면", "78 - 78", 28, 9, 19], | |
846 | + [678, 2022, "고경면", "79 - 79", 27, 8, 19], | |
847 | + [679, 2022, "고경면", "80 - 80", 55, 16, 39], | |
848 | + [680, 2022, "고경면", "81 - 81", 37, 11, 26], | |
849 | + [681, 2022, "고경면", "82 - 82", 31, 5, 26], | |
850 | + [682, 2022, "고경면", "83 - 83", 36, 4, 32], | |
851 | + [683, 2022, "고경면", "84 - 84", 22, 6, 16], | |
852 | + [684, 2022, "고경면", "85 - 85", 16, 4, 12], | |
853 | + [685, 2022, "고경면", "86 - 86", 34, 5, 29], | |
854 | + [686, 2022, "고경면", "87 - 87", 23, 2, 21], | |
855 | + [687, 2022, "고경면", "88 - 88", 26, 3, 23], | |
856 | + [688, 2022, "고경면", "89 - 89", 14, 4, 10], | |
857 | + [689, 2022, "고경면", "90 - 90", 13, 1, 12], | |
858 | + [690, 2022, "고경면", "91 - 91", 9, 0, 9], | |
859 | + [691, 2022, "고경면", "92 - 92", 2, 0, 2], | |
860 | + [692, 2022, "고경면", "93 - 93", 7, 0, 7], | |
861 | + [693, 2022, "고경면", "94 - 94", 4, 0, 4], | |
862 | + [694, 2022, "고경면", "95 - 95", 1, 1, 0], | |
863 | + [695, 2022, "고경면", "96 - 96", 1, 0, 1], | |
864 | + [696, 2022, "고경면", "97 - 97", 1, 0, 1], | |
865 | + [697, 2022, "고경면", "101 - 101", 1, 0, 1], | |
866 | + [698, 2022, "북안면", "21 - 21", 2, 0, 2], | |
867 | + [699, 2022, "북안면", "22 - 22", 3, 3, 0], | |
868 | + [700, 2022, "북안면", "23 - 23", 6, 5, 1], | |
869 | + [701, 2022, "북안면", "24 - 24", 4, 3, 1], | |
870 | + [702, 2022, "북안면", "25 - 25", 10, 4, 6], | |
871 | + [703, 2022, "북안면", "26 - 26", 11, 6, 5], | |
872 | + [704, 2022, "북안면", "27 - 27", 5, 2, 3], | |
873 | + [705, 2022, "북안면", "28 - 28", 8, 4, 4], | |
874 | + [706, 2022, "북안면", "29 - 29", 5, 3, 2], | |
875 | + [707, 2022, "북안면", "30 - 30", 2, 2, 0], | |
876 | + [708, 2022, "북안면", "31 - 31", 7, 6, 1], | |
877 | + [709, 2022, "북안면", "32 - 32", 8, 5, 3], | |
878 | + [710, 2022, "북안면", "33 - 33", 4, 2, 2], | |
879 | + [711, 2022, "북안면", "34 - 34", 6, 4, 2], | |
880 | + [712, 2022, "북안면", "35 - 35", 7, 6, 1], | |
881 | + [713, 2022, "북안면", "36 - 36", 7, 5, 2], | |
882 | + [714, 2022, "북안면", "37 - 37", 5, 5, 0], | |
883 | + [715, 2022, "북안면", "38 - 38", 8, 6, 2], | |
884 | + [716, 2022, "북안면", "39 - 39", 5, 4, 1], | |
885 | + [717, 2022, "북안면", "40 - 40", 5, 4, 1], | |
886 | + [718, 2022, "북안면", "41 - 41", 12, 11, 1], | |
887 | + [719, 2022, "북안면", "42 - 42", 9, 5, 4], | |
888 | + [720, 2022, "북안면", "43 - 43", 13, 9, 4], | |
889 | + [721, 2022, "북안면", "44 - 44", 13, 9, 4], | |
890 | + [722, 2022, "북안면", "45 - 45", 10, 6, 4], | |
891 | + [723, 2022, "북안면", "46 - 46", 15, 10, 5], | |
892 | + [724, 2022, "북안면", "47 - 47", 5, 3, 2], | |
893 | + [725, 2022, "북안면", "48 - 48", 19, 16, 3], | |
894 | + [726, 2022, "북안면", "49 - 49", 20, 9, 11], | |
895 | + [727, 2022, "북안면", "50 - 50", 15, 12, 3], | |
896 | + [728, 2022, "북안면", "51 - 51", 23, 14, 9], | |
897 | + [729, 2022, "북안면", "52 - 52", 24, 20, 4], | |
898 | + [730, 2022, "북안면", "53 - 53", 18, 13, 5], | |
899 | + [731, 2022, "북안면", "54 - 54", 33, 21, 12], | |
900 | + [732, 2022, "북안면", "55 - 55", 33, 19, 14], | |
901 | + [733, 2022, "북안면", "56 - 56", 36, 29, 7], | |
902 | + [734, 2022, "북안면", "57 - 57", 27, 22, 5], | |
903 | + [735, 2022, "북안면", "58 - 58", 32, 21, 11], | |
904 | + [736, 2022, "북안면", "59 - 59", 42, 27, 15], | |
905 | + [737, 2022, "북안면", "60 - 60", 36, 26, 10], | |
906 | + [738, 2022, "북안면", "61 - 61", 56, 37, 19], | |
907 | + [739, 2022, "북안면", "62 - 62", 42, 24, 18], | |
908 | + [740, 2022, "북안면", "63 - 63", 55, 38, 17], | |
909 | + [741, 2022, "북안면", "64 - 64", 35, 26, 9], | |
910 | + [742, 2022, "북안면", "65 - 65", 49, 34, 15], | |
911 | + [743, 2022, "북안면", "66 - 66", 45, 30, 15], | |
912 | + [744, 2022, "북안면", "67 - 67", 45, 26, 19], | |
913 | + [745, 2022, "북안면", "68 - 68", 36, 24, 12], | |
914 | + [746, 2022, "북안면", "69 - 69", 32, 19, 13], | |
915 | + [747, 2022, "북안면", "70 - 70", 37, 20, 17], | |
916 | + [748, 2022, "북안면", "71 - 71", 33, 19, 14], | |
917 | + [749, 2022, "북안면", "72 - 72", 29, 13, 16], | |
918 | + [750, 2022, "북안면", "73 - 73", 25, 14, 11], | |
919 | + [751, 2022, "북안면", "74 - 74", 23, 6, 17], | |
920 | + [752, 2022, "북안면", "75 - 75", 32, 18, 14], | |
921 | + [753, 2022, "북안면", "76 - 76", 23, 12, 11], | |
922 | + [754, 2022, "북안면", "77 - 77", 19, 9, 10], | |
923 | + [755, 2022, "북안면", "78 - 78", 26, 3, 23], | |
924 | + [756, 2022, "북안면", "79 - 79", 22, 7, 15], | |
925 | + [757, 2022, "북안면", "80 - 80", 33, 10, 23], | |
926 | + [758, 2022, "북안면", "81 - 81", 30, 5, 25], | |
927 | + [759, 2022, "북안면", "82 - 82", 27, 4, 23], | |
928 | + [760, 2022, "북안면", "83 - 83", 37, 8, 29], | |
929 | + [761, 2022, "북안면", "84 - 84", 20, 3, 17], | |
930 | + [762, 2022, "북안면", "85 - 85", 34, 4, 30], | |
931 | + [763, 2022, "북안면", "86 - 86", 29, 4, 25], | |
932 | + [764, 2022, "북안면", "87 - 87", 23, 2, 21], | |
933 | + [765, 2022, "북안면", "88 - 88", 18, 5, 13], | |
934 | + [766, 2022, "북안면", "89 - 89", 17, 3, 14], | |
935 | + [767, 2022, "북안면", "90 - 90", 12, 1, 11], | |
936 | + [768, 2022, "북안면", "91 - 91", 8, 1, 7], | |
937 | + [769, 2022, "북안면", "92 - 92", 10, 1, 9], | |
938 | + [770, 2022, "북안면", "93 - 93", 4, 0, 4], | |
939 | + [771, 2022, "북안면", "94 - 94", 4, 2, 2], | |
940 | + [772, 2022, "북안면", "96 - 96", 1, 1, 0], | |
941 | + [773, 2022, "북안면", "98 - 98", 2, 0, 2], | |
942 | + [774, 2022, "대창면", "19 - 19", 2, 2, 0], | |
943 | + [775, 2022, "대창면", "20 - 20", 2, 1, 1], | |
944 | + [776, 2022, "대창면", "22 - 22", 4, 3, 1], | |
945 | + [777, 2022, "대창면", "23 - 23", 5, 3, 2], | |
946 | + [778, 2022, "대창면", "24 - 24", 1, 1, 0], | |
947 | + [779, 2022, "대창면", "26 - 26", 3, 3, 0], | |
948 | + [780, 2022, "대창면", "27 - 27", 7, 4, 3], | |
949 | + [781, 2022, "대창면", "28 - 28", 6, 4, 2], | |
950 | + [782, 2022, "대창면", "29 - 29", 4, 3, 1], | |
951 | + [783, 2022, "대창면", "30 - 30", 6, 4, 2], | |
952 | + [784, 2022, "대창면", "31 - 31", 4, 2, 2], | |
953 | + [785, 2022, "대창면", "32 - 32", 4, 3, 1], | |
954 | + [786, 2022, "대창면", "33 - 33", 5, 2, 3], | |
955 | + [787, 2022, "대창면", "34 - 34", 5, 3, 2], | |
956 | + [788, 2022, "대창면", "35 - 35", 3, 2, 1], | |
957 | + [789, 2022, "대창면", "36 - 36", 3, 2, 1], | |
958 | + [790, 2022, "대창면", "37 - 37", 9, 7, 2], | |
959 | + [791, 2022, "대창면", "38 - 38", 5, 5, 0], | |
960 | + [792, 2022, "대창면", "39 - 39", 6, 5, 1], | |
961 | + [793, 2022, "대창면", "40 - 40", 13, 8, 5], | |
962 | + [794, 2022, "대창면", "41 - 41", 8, 8, 0], | |
963 | + [795, 2022, "대창면", "42 - 42", 11, 7, 4], | |
964 | + [796, 2022, "대창면", "43 - 43", 6, 4, 2], | |
965 | + [797, 2022, "대창면", "44 - 44", 3, 0, 3], | |
966 | + [798, 2022, "대창면", "45 - 45", 3, 2, 1], | |
967 | + [799, 2022, "대창면", "46 - 46", 13, 11, 2], | |
968 | + [800, 2022, "대창면", "47 - 47", 9, 9, 0], | |
969 | + [801, 2022, "대창면", "48 - 48", 11, 8, 3], | |
970 | + [802, 2022, "대창면", "49 - 49", 13, 10, 3], | |
971 | + [803, 2022, "대창면", "50 - 50", 17, 14, 3], | |
972 | + [804, 2022, "대창면", "51 - 51", 20, 18, 2], | |
973 | + [805, 2022, "대창면", "52 - 52", 19, 12, 7], | |
974 | + [806, 2022, "대창면", "53 - 53", 22, 17, 5], | |
975 | + [807, 2022, "대창면", "54 - 54", 24, 16, 8], | |
976 | + [808, 2022, "대창면", "55 - 55", 19, 13, 6], | |
977 | + [809, 2022, "대창면", "56 - 56", 21, 19, 2], | |
978 | + [810, 2022, "대창면", "57 - 57", 20, 16, 4], | |
979 | + [811, 2022, "대창면", "58 - 58", 25, 21, 4], | |
980 | + [812, 2022, "대창면", "59 - 59", 24, 22, 2], | |
981 | + [813, 2022, "대창면", "60 - 60", 35, 23, 12], | |
982 | + [814, 2022, "대창면", "61 - 61", 32, 23, 9], | |
983 | + [815, 2022, "대창면", "62 - 62", 36, 23, 13], | |
984 | + [816, 2022, "대창면", "63 - 63", 33, 21, 12], | |
985 | + [817, 2022, "대창면", "64 - 64", 27, 13, 14], | |
986 | + [818, 2022, "대창면", "65 - 65", 35, 24, 11], | |
987 | + [819, 2022, "대창면", "66 - 66", 34, 20, 14], | |
988 | + [820, 2022, "대창면", "67 - 67", 30, 23, 7], | |
989 | + [821, 2022, "대창면", "68 - 68", 30, 17, 13], | |
990 | + [822, 2022, "대창면", "69 - 69", 17, 13, 4], | |
991 | + [823, 2022, "대창면", "70 - 70", 32, 21, 11], | |
992 | + [824, 2022, "대창면", "71 - 71", 26, 10, 16], | |
993 | + [825, 2022, "대창면", "72 - 72", 19, 9, 10], | |
994 | + [826, 2022, "대창면", "73 - 73", 23, 11, 12], | |
995 | + [827, 2022, "대창면", "74 - 74", 23, 9, 14], | |
996 | + [828, 2022, "대창면", "75 - 75", 18, 10, 8], | |
997 | + [829, 2022, "대창면", "76 - 76", 8, 2, 6], | |
998 | + [830, 2022, "대창면", "77 - 77", 12, 3, 9], | |
999 | + [831, 2022, "대창면", "78 - 78", 12, 4, 8], | |
1000 | + [832, 2022, "대창면", "79 - 79", 19, 3, 16], | |
1001 | + [833, 2022, "대창면", "80 - 80", 29, 6, 23], | |
1002 | + [834, 2022, "대창면", "81 - 81", 23, 5, 18], | |
1003 | + [835, 2022, "대창면", "82 - 82", 15, 6, 9], | |
1004 | + [836, 2022, "대창면", "83 - 83", 15, 2, 13], | |
1005 | + [837, 2022, "대창면", "84 - 84", 16, 3, 13], | |
1006 | + [838, 2022, "대창면", "85 - 85", 13, 3, 10], | |
1007 | + [839, 2022, "대창면", "86 - 86", 15, 3, 12], | |
1008 | + [840, 2022, "대창면", "87 - 87", 12, 1, 11], | |
1009 | + [841, 2022, "대창면", "88 - 88", 10, 0, 10], | |
1010 | + [842, 2022, "대창면", "89 - 89", 10, 1, 9], | |
1011 | + [843, 2022, "대창면", "90 - 90", 5, 0, 5], | |
1012 | + [844, 2022, "대창면", "91 - 91", 2, 1, 1], | |
1013 | + [845, 2022, "대창면", "92 - 92", 4, 1, 3], | |
1014 | + [846, 2022, "대창면", "93 - 93", 2, 1, 1], | |
1015 | + [847, 2022, "대창면", "94 - 94", 1, 0, 1], | |
1016 | + [848, 2022, "대창면", "95 - 95", 1, 0, 1], | |
1017 | + [849, 2022, "대창면", "98 - 98", 1, 0, 1], | |
1018 | + [850, 2022, "대창면", "100 - 100", 1, 0, 1], | |
1019 | + [851, 2022, "동부동", "04-04", 1, 1, 0], | |
1020 | + [852, 2022, "동부동", "05-05", 1, 0, 1], | |
1021 | + [853, 2022, "동부동", "12-12", 1, 0, 1], | |
1022 | + [854, 2022, "동부동", "13 - 13", 1, 0, 1], | |
1023 | + [855, 2022, "동부동", "16 - 16", 1, 0, 1], | |
1024 | + [856, 2022, "동부동", "18 - 18", 1, 0, 1], | |
1025 | + [857, 2022, "동부동", "19 - 19", 4, 3, 1], | |
1026 | + [858, 2022, "동부동", "20 - 20", 7, 5, 2], | |
1027 | + [859, 2022, "동부동", "21 - 21", 12, 8, 4], | |
1028 | + [860, 2022, "동부동", "22 - 22", 23, 15, 8], | |
1029 | + [861, 2022, "동부동", "23 - 23", 24, 13, 11], | |
1030 | + [862, 2022, "동부동", "24 - 24", 35, 22, 13], | |
1031 | + [863, 2022, "동부동", "25 - 25", 47, 25, 22], | |
1032 | + [864, 2022, "동부동", "26 - 26", 75, 47, 28], | |
1033 | + [865, 2022, "동부동", "27 - 27", 75, 41, 34], | |
1034 | + [866, 2022, "동부동", "28 - 28", 86, 53, 33], | |
1035 | + [867, 2022, "동부동", "29 - 29", 87, 57, 30], | |
1036 | + [868, 2022, "동부동", "30 - 30", 84, 54, 30], | |
1037 | + [869, 2022, "동부동", "31 - 31", 83, 60, 23], | |
1038 | + [870, 2022, "동부동", "32 - 32", 77, 55, 22], | |
1039 | + [871, 2022, "동부동", "33 - 33", 69, 50, 19], | |
1040 | + [872, 2022, "동부동", "34 - 34", 64, 40, 24], | |
1041 | + [873, 2022, "동부동", "35 - 35", 52, 32, 20], | |
1042 | + [874, 2022, "동부동", "36 - 36", 60, 46, 14], | |
1043 | + [875, 2022, "동부동", "37 - 37", 61, 47, 14], | |
1044 | + [876, 2022, "동부동", "38 - 38", 67, 46, 21], | |
1045 | + [877, 2022, "동부동", "39 - 39", 63, 44, 19], | |
1046 | + [878, 2022, "동부동", "40 - 40", 64, 55, 9], | |
1047 | + [879, 2022, "동부동", "41 - 41", 84, 58, 26], | |
1048 | + [880, 2022, "동부동", "42 - 42", 74, 54, 20], | |
1049 | + [881, 2022, "동부동", "43 - 43", 83, 57, 26], | |
1050 | + [882, 2022, "동부동", "44 - 44", 58, 42, 16], | |
1051 | + [883, 2022, "동부동", "45 - 45", 56, 32, 24], | |
1052 | + [884, 2022, "동부동", "46 - 46", 68, 44, 24], | |
1053 | + [885, 2022, "동부동", "47 - 47", 87, 61, 26], | |
1054 | + [886, 2022, "동부동", "48 - 48", 94, 53, 41], | |
1055 | + [887, 2022, "동부동", "49 - 49", 76, 40, 36], | |
1056 | + [888, 2022, "동부동", "50 - 50", 74, 45, 29], | |
1057 | + [889, 2022, "동부동", "51 - 51", 101, 51, 50], | |
1058 | + [890, 2022, "동부동", "52 - 52", 98, 56, 42], | |
1059 | + [891, 2022, "동부동", "53 - 53", 106, 56, 50], | |
1060 | + [892, 2022, "동부동", "54 - 54", 116, 60, 56], | |
1061 | + [893, 2022, "동부동", "55 - 55", 126, 68, 58], | |
1062 | + [894, 2022, "동부동", "56 - 56", 115, 66, 49], | |
1063 | + [895, 2022, "동부동", "57 - 57", 108, 47, 61], | |
1064 | + [896, 2022, "동부동", "58 - 58", 121, 61, 60], | |
1065 | + [897, 2022, "동부동", "59 - 59", 111, 48, 63], | |
1066 | + [898, 2022, "동부동", "60 - 60", 108, 45, 63], | |
1067 | + [899, 2022, "동부동", "61 - 61", 155, 77, 78], | |
1068 | + [900, 2022, "동부동", "62 - 62", 144, 62, 82], | |
1069 | + [901, 2022, "동부동", "63 - 63", 151, 55, 96], | |
1070 | + [902, 2022, "동부동", "64 - 64", 121, 45, 76], | |
1071 | + [903, 2022, "동부동", "65 - 65", 114, 56, 58], | |
1072 | + [904, 2022, "동부동", "66 - 66", 115, 40, 75], | |
1073 | + [905, 2022, "동부동", "67 - 67", 104, 37, 67], | |
1074 | + [906, 2022, "동부동", "68 - 68", 107, 37, 70], | |
1075 | + [907, 2022, "동부동", "69 - 69", 88, 32, 56], | |
1076 | + [908, 2022, "동부동", "70 - 70", 92, 26, 66], | |
1077 | + [909, 2022, "동부동", "71 - 71", 78, 23, 55], | |
1078 | + [910, 2022, "동부동", "72 - 72", 81, 28, 53], | |
1079 | + [911, 2022, "동부동", "73 - 73", 66, 17, 49], | |
1080 | + [912, 2022, "동부동", "74 - 74", 91, 24, 67], | |
1081 | + [913, 2022, "동부동", "75 - 75", 74, 16, 58], | |
1082 | + [914, 2022, "동부동", "76 - 76", 50, 18, 32], | |
1083 | + [915, 2022, "동부동", "77 - 77", 56, 17, 39], | |
1084 | + [916, 2022, "동부동", "78 - 78", 51, 11, 40], | |
1085 | + [917, 2022, "동부동", "79 - 79", 70, 11, 59], | |
1086 | + [918, 2022, "동부동", "80 - 80", 74, 12, 62], | |
1087 | + [919, 2022, "동부동", "81 - 81", 54, 14, 40], | |
1088 | + [920, 2022, "동부동", "82 - 82", 68, 9, 59], | |
1089 | + [921, 2022, "동부동", "83 - 83", 56, 10, 46], | |
1090 | + [922, 2022, "동부동", "84 - 84", 66, 11, 55], | |
1091 | + [923, 2022, "동부동", "85 - 85", 53, 7, 46], | |
1092 | + [924, 2022, "동부동", "86 - 86", 52, 9, 43], | |
1093 | + [925, 2022, "동부동", "87 - 87", 37, 5, 32], | |
1094 | + [926, 2022, "동부동", "88 - 88", 38, 4, 34], | |
1095 | + [927, 2022, "동부동", "89 - 89", 18, 1, 17], | |
1096 | + [928, 2022, "동부동", "90 - 90", 24, 4, 20], | |
1097 | + [929, 2022, "동부동", "91 - 91", 13, 1, 12], | |
1098 | + [930, 2022, "동부동", "92 - 92", 9, 2, 7], | |
1099 | + [931, 2022, "동부동", "93 - 93", 8, 1, 7], | |
1100 | + [932, 2022, "동부동", "94 - 94", 7, 1, 6], | |
1101 | + [933, 2022, "동부동", "95 - 95", 3, 0, 3], | |
1102 | + [934, 2022, "동부동", "96 - 96", 1, 0, 1], | |
1103 | + [935, 2022, "동부동", "98 - 98", 1, 0, 1], | |
1104 | + [936, 2022, "동부동", "99 - 99", 1, 0, 1], | |
1105 | + [937, 2022, "중앙동", "08-08", 1, 1, 0], | |
1106 | + [938, 2022, "중앙동", "14 - 14", 1, 0, 1], | |
1107 | + [939, 2022, "중앙동", "15 - 15", 1, 0, 1], | |
1108 | + [940, 2022, "중앙동", "18 - 18", 2, 1, 1], | |
1109 | + [941, 2022, "중앙동", "19 - 19", 3, 1, 2], | |
1110 | + [942, 2022, "중앙동", "20 - 20", 6, 4, 2], | |
1111 | + [943, 2022, "중앙동", "21 - 21", 10, 4, 6], | |
1112 | + [944, 2022, "중앙동", "22 - 22", 12, 7, 5], | |
1113 | + [945, 2022, "중앙동", "23 - 23", 17, 7, 10], | |
1114 | + [946, 2022, "중앙동", "24 - 24", 30, 13, 17], | |
1115 | + [947, 2022, "중앙동", "25 - 25", 26, 13, 13], | |
1116 | + [948, 2022, "중앙동", "26 - 26", 43, 30, 13], | |
1117 | + [949, 2022, "중앙동", "27 - 27", 48, 30, 18], | |
1118 | + [950, 2022, "중앙동", "28 - 28", 47, 33, 14], | |
1119 | + [951, 2022, "중앙동", "29 - 29", 39, 26, 13], | |
1120 | + [952, 2022, "중앙동", "30 - 30", 50, 36, 14], | |
1121 | + [953, 2022, "중앙동", "31 - 31", 41, 25, 16], | |
1122 | + [954, 2022, "중앙동", "32 - 32", 34, 27, 7], | |
1123 | + [955, 2022, "중앙동", "33 - 33", 30, 26, 4], | |
1124 | + [956, 2022, "중앙동", "34 - 34", 28, 23, 5], | |
1125 | + [957, 2022, "중앙동", "35 - 35", 37, 25, 12], | |
1126 | + [958, 2022, "중앙동", "36 - 36", 24, 18, 6], | |
1127 | + [959, 2022, "중앙동", "37 - 37", 16, 10, 6], | |
1128 | + [960, 2022, "중앙동", "38 - 38", 20, 13, 7], | |
1129 | + [961, 2022, "중앙동", "39 - 39", 26, 20, 6], | |
1130 | + [962, 2022, "중앙동", "40 - 40", 21, 13, 8], | |
1131 | + [963, 2022, "중앙동", "41 - 41", 30, 14, 16], | |
1132 | + [964, 2022, "중앙동", "42 - 42", 30, 19, 11], | |
1133 | + [965, 2022, "중앙동", "43 - 43", 19, 16, 3], | |
1134 | + [966, 2022, "중앙동", "44 - 44", 27, 22, 5], | |
1135 | + [967, 2022, "중앙동", "45 - 45", 18, 13, 5], | |
1136 | + [968, 2022, "중앙동", "46 - 46", 30, 20, 10], | |
1137 | + [969, 2022, "중앙동", "47 - 47", 40, 27, 13], | |
1138 | + [970, 2022, "중앙동", "48 - 48", 28, 15, 13], | |
1139 | + [971, 2022, "중앙동", "49 - 49", 38, 22, 16], | |
1140 | + [972, 2022, "중앙동", "50 - 50", 35, 26, 9], | |
1141 | + [973, 2022, "중앙동", "51 - 51", 39, 23, 16], | |
1142 | + [974, 2022, "중앙동", "52 - 52", 36, 21, 15], | |
1143 | + [975, 2022, "중앙동", "53 - 53", 27, 15, 12], | |
1144 | + [976, 2022, "중앙동", "54 - 54", 38, 23, 15], | |
1145 | + [977, 2022, "중앙동", "55 - 55", 59, 39, 20], | |
1146 | + [978, 2022, "중앙동", "56 - 56", 46, 24, 22], | |
1147 | + [979, 2022, "중앙동", "57 - 57", 33, 19, 14], | |
1148 | + [980, 2022, "중앙동", "58 - 58", 34, 16, 18], | |
1149 | + [981, 2022, "중앙동", "59 - 59", 35, 20, 15], | |
1150 | + [982, 2022, "중앙동", "60 - 60", 52, 27, 25], | |
1151 | + [983, 2022, "중앙동", "61 - 61", 43, 23, 20], | |
1152 | + [984, 2022, "중앙동", "62 - 62", 58, 29, 29], | |
1153 | + [985, 2022, "중앙동", "63 - 63", 46, 29, 17], | |
1154 | + [986, 2022, "중앙동", "64 - 64", 29, 16, 13], | |
1155 | + [987, 2022, "중앙동", "65 - 65", 40, 22, 18], | |
1156 | + [988, 2022, "중앙동", "66 - 66", 44, 23, 21], | |
1157 | + [989, 2022, "중앙동", "67 - 67", 27, 12, 15], | |
1158 | + [990, 2022, "중앙동", "68 - 68", 23, 13, 10], | |
1159 | + [991, 2022, "중앙동", "69 - 69", 29, 19, 10], | |
1160 | + [992, 2022, "중앙동", "70 - 70", 27, 13, 14], | |
1161 | + [993, 2022, "중앙동", "71 - 71", 19, 5, 14], | |
1162 | + [994, 2022, "중앙동", "72 - 72", 28, 11, 17], | |
1163 | + [995, 2022, "중앙동", "73 - 73", 27, 11, 16], | |
1164 | + [996, 2022, "중앙동", "74 - 74", 31, 8, 23], | |
1165 | + [997, 2022, "중앙동", "75 - 75", 22, 5, 17], | |
1166 | + [998, 2022, "중앙동", "76 - 76", 32, 6, 26], | |
1167 | + [999, 2022, "중앙동", "77 - 77", 20, 4, 16], | |
1168 | + [1000, 2022, "중앙동", "78 - 78", 13, 4, 9], | |
1169 | + [1001, 2022, "중앙동", "79 - 79", 28, 6, 22], | |
1170 | + [1002, 2022, "중앙동", "80 - 80", 25, 4, 21], | |
1171 | + [1003, 2022, "중앙동", "81 - 81", 20, 3, 17], | |
1172 | + [1004, 2022, "중앙동", "82 - 82", 19, 3, 16], | |
1173 | + [1005, 2022, "중앙동", "83 - 83", 22, 5, 17], | |
1174 | + [1006, 2022, "중앙동", "84 - 84", 20, 4, 16], | |
1175 | + [1007, 2022, "중앙동", "85 - 85", 11, 2, 9], | |
1176 | + [1008, 2022, "중앙동", "86 - 86", 17, 6, 11], | |
1177 | + [1009, 2022, "중앙동", "87 - 87", 9, 3, 6], | |
1178 | + [1010, 2022, "중앙동", "88 - 88", 11, 3, 8], | |
1179 | + [1011, 2022, "중앙동", "89 - 89", 10, 3, 7], | |
1180 | + [1012, 2022, "중앙동", "90 - 90", 9, 3, 6], | |
1181 | + [1013, 2022, "중앙동", "91 - 91", 4, 0, 4], | |
1182 | + [1014, 2022, "중앙동", "92 - 92", 5, 1, 4], | |
1183 | + [1015, 2022, "중앙동", "93 - 93", 6, 0, 6], | |
1184 | + [1016, 2022, "중앙동", "96 - 96", 1, 0, 1], | |
1185 | + [1017, 2022, "중앙동", "97 - 97", 1, 0, 1], | |
1186 | + [1018, 2022, "서부동", "15 - 15", 1, 1, 0], | |
1187 | + [1019, 2022, "서부동", "16 - 16", 6, 0, 6], | |
1188 | + [1020, 2022, "서부동", "17 - 17", 10, 0, 10], | |
1189 | + [1021, 2022, "서부동", "18 - 18", 2, 0, 2], | |
1190 | + [1022, 2022, "서부동", "19 - 19", 2, 2, 0], | |
1191 | + [1023, 2022, "서부동", "20 - 20", 8, 7, 1], | |
1192 | + [1024, 2022, "서부동", "21 - 21", 10, 6, 4], | |
1193 | + [1025, 2022, "서부동", "22 - 22", 6, 4, 2], | |
1194 | + [1026, 2022, "서부동", "23 - 23", 14, 7, 7], | |
1195 | + [1027, 2022, "서부동", "24 - 24", 12, 7, 5], | |
1196 | + [1028, 2022, "서부동", "25 - 25", 14, 11, 3], | |
1197 | + [1029, 2022, "서부동", "26 - 26", 12, 4, 8], | |
1198 | + [1030, 2022, "서부동", "27 - 27", 15, 9, 6], | |
1199 | + [1031, 2022, "서부동", "28 - 28", 19, 12, 7], | |
1200 | + [1032, 2022, "서부동", "29 - 29", 11, 8, 3], | |
1201 | + [1033, 2022, "서부동", "30 - 30", 11, 4, 7], | |
1202 | + [1034, 2022, "서부동", "31 - 31", 15, 8, 7], | |
1203 | + [1035, 2022, "서부동", "32 - 32", 17, 13, 4], | |
1204 | + [1036, 2022, "서부동", "33 - 33", 9, 5, 4], | |
1205 | + [1037, 2022, "서부동", "34 - 34", 10, 6, 4], | |
1206 | + [1038, 2022, "서부동", "35 - 35", 7, 4, 3], | |
1207 | + [1039, 2022, "서부동", "36 - 36", 11, 4, 7], | |
1208 | + [1040, 2022, "서부동", "37 - 37", 14, 11, 3], | |
1209 | + [1041, 2022, "서부동", "38 - 38", 13, 11, 2], | |
1210 | + [1042, 2022, "서부동", "39 - 39", 16, 14, 2], | |
1211 | + [1043, 2022, "서부동", "40 - 40", 15, 12, 3], | |
1212 | + [1044, 2022, "서부동", "41 - 41", 14, 7, 7], | |
1213 | + [1045, 2022, "서부동", "42 - 42", 18, 14, 4], | |
1214 | + [1046, 2022, "서부동", "43 - 43", 11, 10, 1], | |
1215 | + [1047, 2022, "서부동", "44 - 44", 10, 8, 2], | |
1216 | + [1048, 2022, "서부동", "45 - 45", 14, 12, 2], | |
1217 | + [1049, 2022, "서부동", "46 - 46", 16, 13, 3], | |
1218 | + [1050, 2022, "서부동", "47 - 47", 23, 17, 6], | |
1219 | + [1051, 2022, "서부동", "48 - 48", 30, 22, 8], | |
1220 | + [1052, 2022, "서부동", "49 - 49", 16, 12, 4], | |
1221 | + [1053, 2022, "서부동", "50 - 50", 15, 11, 4], | |
1222 | + [1054, 2022, "서부동", "51 - 51", 32, 25, 7], | |
1223 | + [1055, 2022, "서부동", "52 - 52", 19, 14, 5], | |
1224 | + [1056, 2022, "서부동", "53 - 53", 41, 25, 16], | |
1225 | + [1057, 2022, "서부동", "54 - 54", 27, 17, 10], | |
1226 | + [1058, 2022, "서부동", "55 - 55", 38, 25, 13], | |
1227 | + [1059, 2022, "서부동", "56 - 56", 35, 24, 11], | |
1228 | + [1060, 2022, "서부동", "57 - 57", 43, 27, 16], | |
1229 | + [1061, 2022, "서부동", "58 - 58", 41, 26, 15], | |
1230 | + [1062, 2022, "서부동", "59 - 59", 27, 17, 10], | |
1231 | + [1063, 2022, "서부동", "60 - 60", 42, 30, 12], | |
1232 | + [1064, 2022, "서부동", "61 - 61", 37, 20, 17], | |
1233 | + [1065, 2022, "서부동", "62 - 62", 32, 19, 13], | |
1234 | + [1066, 2022, "서부동", "63 - 63", 34, 15, 19], | |
1235 | + [1067, 2022, "서부동", "64 - 64", 34, 14, 20], | |
1236 | + [1068, 2022, "서부동", "65 - 65", 33, 21, 12], | |
1237 | + [1069, 2022, "서부동", "66 - 66", 41, 21, 20], | |
1238 | + [1070, 2022, "서부동", "67 - 67", 35, 24, 11], | |
1239 | + [1071, 2022, "서부동", "68 - 68", 19, 10, 9], | |
1240 | + [1072, 2022, "서부동", "69 - 69", 33, 17, 16], | |
1241 | + [1073, 2022, "서부동", "70 - 70", 23, 10, 13], | |
1242 | + [1074, 2022, "서부동", "71 - 71", 32, 13, 19], | |
1243 | + [1075, 2022, "서부동", "72 - 72", 33, 14, 19], | |
1244 | + [1076, 2022, "서부동", "73 - 73", 28, 12, 16], | |
1245 | + [1077, 2022, "서부동", "74 - 74", 27, 12, 15], | |
1246 | + [1078, 2022, "서부동", "75 - 75", 29, 10, 19], | |
1247 | + [1079, 2022, "서부동", "76 - 76", 9, 2, 7], | |
1248 | + [1080, 2022, "서부동", "77 - 77", 17, 4, 13], | |
1249 | + [1081, 2022, "서부동", "78 - 78", 33, 7, 26], | |
1250 | + [1082, 2022, "서부동", "79 - 79", 24, 4, 20], | |
1251 | + [1083, 2022, "서부동", "80 - 80", 31, 5, 26], | |
1252 | + [1084, 2022, "서부동", "81 - 81", 18, 0, 18], | |
1253 | + [1085, 2022, "서부동", "82 - 82", 14, 3, 11], | |
1254 | + [1086, 2022, "서부동", "83 - 83", 15, 2, 13], | |
1255 | + [1087, 2022, "서부동", "84 - 84", 18, 3, 15], | |
1256 | + [1088, 2022, "서부동", "85 - 85", 22, 1, 21], | |
1257 | + [1089, 2022, "서부동", "86 - 86", 22, 1, 21], | |
1258 | + [1090, 2022, "서부동", "87 - 87", 11, 0, 11], | |
1259 | + [1091, 2022, "서부동", "88 - 88", 9, 2, 7], | |
1260 | + [1092, 2022, "서부동", "89 - 89", 14, 3, 11], | |
1261 | + [1093, 2022, "서부동", "90 - 90", 4, 1, 3], | |
1262 | + [1094, 2022, "서부동", "91 - 91", 6, 1, 5], | |
1263 | + [1095, 2022, "서부동", "92 - 92", 6, 1, 5], | |
1264 | + [1096, 2022, "서부동", "94 - 94", 1, 0, 1], | |
1265 | + [1097, 2022, "서부동", "95 - 95", 1, 0, 1], | |
1266 | + [1098, 2022, "서부동", "97 - 97", 2, 0, 2], | |
1267 | + [1099, 2022, "서부동", "100 - 100", 1, 0, 1], | |
1268 | + [1100, 2022, "서부동", "103 - 103", 1, 0, 1], | |
1269 | + [1101, 2022, "완산동", "12-12", 1, 0, 1], | |
1270 | + [1102, 2022, "완산동", "19 - 19", 1, 1, 0], | |
1271 | + [1103, 2022, "완산동", "20 - 20", 2, 2, 0], | |
1272 | + [1104, 2022, "완산동", "21 - 21", 4, 2, 2], | |
1273 | + [1105, 2022, "완산동", "22 - 22", 2, 1, 1], | |
1274 | + [1106, 2022, "완산동", "23 - 23", 7, 4, 3], | |
1275 | + [1107, 2022, "완산동", "24 - 24", 6, 1, 5], | |
1276 | + [1108, 2022, "완산동", "25 - 25", 5, 3, 2], | |
1277 | + [1109, 2022, "완산동", "26 - 26", 26, 7, 19], | |
1278 | + [1110, 2022, "완산동", "27 - 27", 19, 13, 6], | |
1279 | + [1111, 2022, "완산동", "28 - 28", 31, 22, 9], | |
1280 | + [1112, 2022, "완산동", "29 - 29", 17, 12, 5], | |
1281 | + [1113, 2022, "완산동", "30 - 30", 25, 13, 12], | |
1282 | + [1114, 2022, "완산동", "31 - 31", 19, 14, 5], | |
1283 | + [1115, 2022, "완산동", "32 - 32", 17, 8, 9], | |
1284 | + [1116, 2022, "완산동", "33 - 33", 18, 15, 3], | |
1285 | + [1117, 2022, "완산동", "34 - 34", 22, 17, 5], | |
1286 | + [1118, 2022, "완산동", "35 - 35", 17, 13, 4], | |
1287 | + [1119, 2022, "완산동", "36 - 36", 21, 15, 6], | |
1288 | + [1120, 2022, "완산동", "37 - 37", 16, 11, 5], | |
1289 | + [1121, 2022, "완산동", "38 - 38", 23, 21, 2], | |
1290 | + [1122, 2022, "완산동", "39 - 39", 18, 9, 9], | |
1291 | + [1123, 2022, "완산동", "40 - 40", 32, 21, 11], | |
1292 | + [1124, 2022, "완산동", "41 - 41", 26, 20, 6], | |
1293 | + [1125, 2022, "완산동", "42 - 42", 21, 18, 3], | |
1294 | + [1126, 2022, "완산동", "43 - 43", 17, 12, 5], | |
1295 | + [1127, 2022, "완산동", "44 - 44", 25, 21, 4], | |
1296 | + [1128, 2022, "완산동", "45 - 45", 26, 18, 8], | |
1297 | + [1129, 2022, "완산동", "46 - 46", 27, 16, 11], | |
1298 | + [1130, 2022, "완산동", "47 - 47", 21, 10, 11], | |
1299 | + [1131, 2022, "완산동", "48 - 48", 29, 16, 13], | |
1300 | + [1132, 2022, "완산동", "49 - 49", 27, 20, 7], | |
1301 | + [1133, 2022, "완산동", "50 - 50", 29, 20, 9], | |
1302 | + [1134, 2022, "완산동", "51 - 51", 34, 22, 12], | |
1303 | + [1135, 2022, "완산동", "52 - 52", 28, 12, 16], | |
1304 | + [1136, 2022, "완산동", "53 - 53", 20, 8, 12], | |
1305 | + [1137, 2022, "완산동", "54 - 54", 33, 22, 11], | |
1306 | + [1138, 2022, "완산동", "55 - 55", 31, 15, 16], | |
1307 | + [1139, 2022, "완산동", "56 - 56", 33, 16, 17], | |
1308 | + [1140, 2022, "완산동", "57 - 57", 34, 18, 16], | |
1309 | + [1141, 2022, "완산동", "58 - 58", 30, 15, 15], | |
1310 | + [1142, 2022, "완산동", "59 - 59", 43, 25, 18], | |
1311 | + [1143, 2022, "완산동", "60 - 60", 34, 18, 16], | |
1312 | + [1144, 2022, "완산동", "61 - 61", 36, 19, 17], | |
1313 | + [1145, 2022, "완산동", "62 - 62", 42, 21, 21], | |
1314 | + [1146, 2022, "완산동", "63 - 63", 35, 9, 26], | |
1315 | + [1147, 2022, "완산동", "64 - 64", 37, 14, 23], | |
1316 | + [1148, 2022, "완산동", "65 - 65", 35, 16, 19], | |
1317 | + [1149, 2022, "완산동", "66 - 66", 37, 15, 22], | |
1318 | + [1150, 2022, "완산동", "67 - 67", 38, 16, 22], | |
1319 | + [1151, 2022, "완산동", "68 - 68", 35, 18, 17], | |
1320 | + [1152, 2022, "완산동", "69 - 69", 29, 8, 21], | |
1321 | + [1153, 2022, "완산동", "70 - 70", 42, 13, 29], | |
1322 | + [1154, 2022, "완산동", "71 - 71", 30, 11, 19], | |
1323 | + [1155, 2022, "완산동", "72 - 72", 21, 3, 18], | |
1324 | + [1156, 2022, "완산동", "73 - 73", 32, 7, 25], | |
1325 | + [1157, 2022, "완산동", "74 - 74", 29, 7, 22], | |
1326 | + [1158, 2022, "완산동", "75 - 75", 17, 4, 13], | |
1327 | + [1159, 2022, "완산동", "76 - 76", 13, 0, 13], | |
1328 | + [1160, 2022, "완산동", "77 - 77", 15, 3, 12], | |
1329 | + [1161, 2022, "완산동", "78 - 78", 13, 3, 10], | |
1330 | + [1162, 2022, "완산동", "79 - 79", 20, 4, 16], | |
1331 | + [1163, 2022, "완산동", "80 - 80", 36, 8, 28], | |
1332 | + [1164, 2022, "완산동", "81 - 81", 16, 4, 12], | |
1333 | + [1165, 2022, "완산동", "82 - 82", 19, 1, 18], | |
1334 | + [1166, 2022, "완산동", "83 - 83", 25, 7, 18], | |
1335 | + [1167, 2022, "완산동", "84 - 84", 14, 4, 10], | |
1336 | + [1168, 2022, "완산동", "85 - 85", 11, 2, 9], | |
1337 | + [1169, 2022, "완산동", "86 - 86", 11, 3, 8], | |
1338 | + [1170, 2022, "완산동", "87 - 87", 11, 4, 7], | |
1339 | + [1171, 2022, "완산동", "88 - 88", 10, 3, 7], | |
1340 | + [1172, 2022, "완산동", "89 - 89", 5, 2, 3], | |
1341 | + [1173, 2022, "완산동", "90 - 90", 4, 1, 3], | |
1342 | + [1174, 2022, "완산동", "91 - 91", 2, 0, 2], | |
1343 | + [1175, 2022, "완산동", "92 - 92", 1, 0, 1], | |
1344 | + [1176, 2022, "완산동", "93 - 93", 4, 1, 3], | |
1345 | + [1177, 2022, "완산동", "94 - 94", 1, 0, 1], | |
1346 | + [1178, 2022, "완산동", "97 - 97", 1, 0, 1], | |
1347 | + [1179, 2022, "남부동", "18 - 18", 3, 2, 1], | |
1348 | + [1180, 2022, "남부동", "19 - 19", 1, 0, 1], | |
1349 | + [1181, 2022, "남부동", "20 - 20", 4, 3, 1], | |
1350 | + [1182, 2022, "남부동", "21 - 21", 3, 3, 0], | |
1351 | + [1183, 2022, "남부동", "22 - 22", 6, 6, 0], | |
1352 | + [1184, 2022, "남부동", "23 - 23", 6, 5, 1], | |
1353 | + [1185, 2022, "남부동", "24 - 24", 12, 5, 7], | |
1354 | + [1186, 2022, "남부동", "25 - 25", 13, 7, 6], | |
1355 | + [1187, 2022, "남부동", "26 - 26", 12, 9, 3], | |
1356 | + [1188, 2022, "남부동", "27 - 27", 21, 18, 3], | |
1357 | + [1189, 2022, "남부동", "28 - 28", 10, 8, 2], | |
1358 | + [1190, 2022, "남부동", "29 - 29", 10, 7, 3], | |
1359 | + [1191, 2022, "남부동", "30 - 30", 22, 16, 6], | |
1360 | + [1192, 2022, "남부동", "31 - 31", 15, 11, 4], | |
1361 | + [1193, 2022, "남부동", "32 - 32", 11, 10, 1], | |
1362 | + [1194, 2022, "남부동", "33 - 33", 15, 11, 4], | |
1363 | + [1195, 2022, "남부동", "34 - 34", 15, 9, 6], | |
1364 | + [1196, 2022, "남부동", "35 - 35", 14, 11, 3], | |
1365 | + [1197, 2022, "남부동", "36 - 36", 11, 8, 3], | |
1366 | + [1198, 2022, "남부동", "37 - 37", 7, 5, 2], | |
1367 | + [1199, 2022, "남부동", "38 - 38", 12, 9, 3], | |
1368 | + [1200, 2022, "남부동", "39 - 39", 14, 12, 2], | |
1369 | + [1201, 2022, "남부동", "40 - 40", 16, 13, 3], | |
1370 | + [1202, 2022, "남부동", "41 - 41", 14, 13, 1], | |
1371 | + [1203, 2022, "남부동", "42 - 42", 10, 7, 3], | |
1372 | + [1204, 2022, "남부동", "43 - 43", 10, 9, 1], | |
1373 | + [1205, 2022, "남부동", "44 - 44", 19, 14, 5], | |
1374 | + [1206, 2022, "남부동", "45 - 45", 12, 11, 1], | |
1375 | + [1207, 2022, "남부동", "46 - 46", 24, 17, 7], | |
1376 | + [1208, 2022, "남부동", "47 - 47", 15, 11, 4], | |
1377 | + [1209, 2022, "남부동", "48 - 48", 27, 22, 5], | |
1378 | + [1210, 2022, "남부동", "49 - 49", 22, 16, 6], | |
1379 | + [1211, 2022, "남부동", "50 - 50", 21, 14, 7], | |
1380 | + [1212, 2022, "남부동", "51 - 51", 27, 19, 8], | |
1381 | + [1213, 2022, "남부동", "52 - 52", 23, 17, 6], | |
1382 | + [1214, 2022, "남부동", "53 - 53", 33, 25, 8], | |
1383 | + [1215, 2022, "남부동", "54 - 54", 46, 30, 16], | |
1384 | + [1216, 2022, "남부동", "55 - 55", 28, 23, 5], | |
1385 | + [1217, 2022, "남부동", "56 - 56", 28, 22, 6], | |
1386 | + [1218, 2022, "남부동", "57 - 57", 24, 16, 8], | |
1387 | + [1219, 2022, "남부동", "58 - 58", 27, 20, 7], | |
1388 | + [1220, 2022, "남부동", "59 - 59", 23, 16, 7], | |
1389 | + [1221, 2022, "남부동", "60 - 60", 40, 30, 10], | |
1390 | + [1222, 2022, "남부동", "61 - 61", 32, 25, 7], | |
1391 | + [1223, 2022, "남부동", "62 - 62", 32, 24, 8], | |
1392 | + [1224, 2022, "남부동", "63 - 63", 49, 29, 20], | |
1393 | + [1225, 2022, "남부동", "64 - 64", 38, 24, 14], | |
1394 | + [1226, 2022, "남부동", "65 - 65", 20, 13, 7], | |
1395 | + [1227, 2022, "남부동", "66 - 66", 26, 17, 9], | |
1396 | + [1228, 2022, "남부동", "67 - 67", 29, 20, 9], | |
1397 | + [1229, 2022, "남부동", "68 - 68", 26, 16, 10], | |
1398 | + [1230, 2022, "남부동", "69 - 69", 20, 8, 12], | |
1399 | + [1231, 2022, "남부동", "70 - 70", 24, 14, 10], | |
1400 | + [1232, 2022, "남부동", "71 - 71", 25, 12, 13], | |
1401 | + [1233, 2022, "남부동", "72 - 72", 20, 4, 16], | |
1402 | + [1234, 2022, "남부동", "73 - 73", 19, 7, 12], | |
1403 | + [1235, 2022, "남부동", "74 - 74", 25, 12, 13], | |
1404 | + [1236, 2022, "남부동", "75 - 75", 15, 5, 10], | |
1405 | + [1237, 2022, "남부동", "76 - 76", 7, 3, 4], | |
1406 | + [1238, 2022, "남부동", "77 - 77", 12, 6, 6], | |
1407 | + [1239, 2022, "남부동", "78 - 78", 15, 5, 10], | |
1408 | + [1240, 2022, "남부동", "79 - 79", 15, 2, 13], | |
1409 | + [1241, 2022, "남부동", "80 - 80", 17, 2, 15], | |
1410 | + [1242, 2022, "남부동", "81 - 81", 19, 2, 17], | |
1411 | + [1243, 2022, "남부동", "82 - 82", 19, 3, 16], | |
1412 | + [1244, 2022, "남부동", "83 - 83", 15, 3, 12], | |
1413 | + [1245, 2022, "남부동", "84 - 84", 15, 4, 11], | |
1414 | + [1246, 2022, "남부동", "85 - 85", 17, 0, 17], | |
1415 | + [1247, 2022, "남부동", "86 - 86", 10, 0, 10], | |
1416 | + [1248, 2022, "남부동", "87 - 87", 14, 4, 10], | |
1417 | + [1249, 2022, "남부동", "88 - 88", 12, 1, 11], | |
1418 | + [1250, 2022, "남부동", "89 - 89", 9, 1, 8], | |
1419 | + [1251, 2022, "남부동", "90 - 90", 6, 2, 4], | |
1420 | + [1252, 2022, "남부동", "91 - 91", 3, 0, 3], | |
1421 | + [1253, 2022, "남부동", "92 - 92", 5, 1, 4], | |
1422 | + [1254, 2022, "남부동", "93 - 93", 7, 1, 6], | |
1423 | + [1255, 2022, "남부동", "94 - 94", 3, 0, 3], | |
1424 | + [1256, 2022, "남부동", "95 - 95", 1, 0, 1], | |
1425 | + [1257, 2022, "남부동", "96 - 96", 2, 0, 2], | |
1426 | + [1258, 2022, "남부동", "99 - 99", 1, 0, 1], | |
1427 | + [1259, 2022, "남부동", "100 - 100", 1, 0, 1], | |
1428 | + ], | |
1429 | + dummyTable: [ | |
1430 | + { columnNm: "spm_row", dataTy: "INT" }, | |
1431 | + { columnNm: "YEAR", dataTy: "INT" }, | |
1432 | + { columnNm: "EMD", dataTy: "STRING" }, | |
1433 | + { columnNm: "AGE", dataTy: "STRING" }, | |
1434 | + { columnNm: "SM", dataTy: "INT" }, | |
1435 | + { columnNm: "MALE", dataTy: "INT" }, | |
1436 | + { columnNm: "FEMALE", dataTy: "INT" }, | |
1437 | + ], | |
1438 | + | |
1439 | + jobGroup: {}, | |
1440 | + getDataTable: {}, | |
1441 | + // 차트 데이터로 사용할 리스트 | |
1442 | + xAxisList: [], | |
1443 | + yAxisList: [], | |
1444 | + // db에서 가져온 row데이터 | |
1445 | + rowdataList: [], | |
1446 | + | |
1447 | + // 생성할 차트의 component 이름 | |
1448 | + componentName: null, | |
1449 | + // 생성할 차트이름 | |
1450 | + chartName: null, | |
1451 | + | |
1452 | + selectedX: "", | |
1453 | + selectedY: "", | |
1454 | + selectedGroup: "", | |
1455 | + selectedGroupNm: "", | |
1456 | + | |
1457 | + selectedFieldArr: [], | |
1458 | + groupArr: [], | |
1459 | + | |
1460 | + // 다중 데이터 사용 여부 확인 | |
1461 | + isMultiData: false, | |
1462 | + // 선택된 계산 방법 | |
1463 | + selectedCal: "default", | |
1464 | + | |
1465 | + // 차트 테마 설정 | |
1466 | + themes: [ | |
1467 | + "Frozen", | |
1468 | + "Dark", | |
1469 | + "Dataviz", | |
1470 | + "Kelly", | |
1471 | + "Material", | |
1472 | + "Micro", | |
1473 | + "Moonrise", | |
1474 | + "Spirited", | |
1475 | + ], | |
1476 | + selectedTheme: "", | |
1477 | + }; | |
1478 | + }, | |
1479 | + components: { | |
1480 | + JobContainer, | |
1481 | + // 차트 컴포넌트 | |
1482 | + ChartCmmn, | |
1483 | + }, | |
1484 | + methods: { | |
1485 | + // 테마 선택 함수 | |
1486 | + setThemes: function (e) { | |
1487 | + this.selectedTheme = e.target.value; | |
1488 | + }, | |
1489 | + selectJobGroup: function () { | |
1490 | + let vm = this; | |
1491 | + axios({ | |
1492 | + url: "/job/selectJobGroup.json", | |
1493 | + method: "post", | |
1494 | + headers: { | |
1495 | + "Content-Type": "application/json; charset=UTF-8", | |
1496 | + }, | |
1497 | + data: JSON.stringify({ group_id: "JOBGROUP_2024020517352987547982" }), | |
1498 | + }) | |
1499 | + .then(function (response) { | |
1500 | + vm.jobGroup = response.data.resultData.jobGroup; | |
1501 | + }) | |
1502 | + .catch(function (error) { | |
1503 | + this.$showAlert( | |
1504 | + "에러 발생", | |
1505 | + "에러가 발생했습니다. 관리자에게 문의해 주세요." | |
1506 | + ); | |
1507 | + }); | |
1508 | + }, | |
1509 | + // 데이터 가져오기 | |
1510 | + getResult: function (table) { | |
1511 | + this.rowdataList = table.rowData; | |
1512 | + // x축, y축 select option에 추가를 위함 | |
1513 | + for (let i = 0; i < table.columnDatas.length; i++) { | |
1514 | + let Obj = { | |
1515 | + index: i, | |
1516 | + columnNm: table.columnDatas[i].columnNm, | |
1517 | + }; | |
1518 | + if ( | |
1519 | + table.columnDatas[i].dataTy == "INTEGER" || | |
1520 | + table.columnDatas[i].dataTy == "DOUBLE" || | |
1521 | + table.columnDatas[i].dataTy == "FLOAT" || | |
1522 | + table.columnDatas[i].dataTy == "LONG" || | |
1523 | + table.columnDatas[i].dataTy == "NUMBER" | |
1524 | + ) { | |
1525 | + this.yAxisList.push(Obj); | |
1526 | + this.xAxisList.push(Obj); | |
1527 | + } else { | |
1528 | + this.xAxisList.push(Obj); | |
1529 | + } | |
1530 | + } | |
1531 | + | |
1532 | + this.rowdataList = this.dummyDataList; | |
1533 | + // x축, y축 select option에 추가를 위함 | |
1534 | + for (let i = 0; i < this.dummyTable.length; i++) { | |
1535 | + let Obj = { | |
1536 | + index: i, | |
1537 | + columnNm: this.dummyTable[i].columnNm, | |
1538 | + }; | |
1539 | + if ( | |
1540 | + this.dummyTable[i].dataTy == "INT" || | |
1541 | + this.dummyTable[i].dataTy == "DOUBLE" || | |
1542 | + this.dummyTable[i].dataTy == "FLOAT" || | |
1543 | + this.dummyTable[i].dataTy == "LONG" || | |
1544 | + this.dummyTable[i].dataTy == "NUMBER" | |
1545 | + ) { | |
1546 | + this.yAxisList.push(Obj); | |
1547 | + this.xAxisList.push(Obj); | |
1548 | + } else { | |
1549 | + this.xAxisList.push(Obj); | |
1550 | + } | |
1551 | + } | |
1552 | + }, | |
1553 | + | |
1554 | + // X축 option 선택 | |
1555 | + selectedXAxis: function (e) { | |
1556 | + this.selectedX = e.target.value; | |
1557 | + // 선택된 x축을 제외한 나머지 x축을 groupArr에 저장 | |
1558 | + this.groupArr = this.xAxisList.filter( | |
1559 | + (item) => item.index != this.selectedX | |
1560 | + ); | |
1561 | + this.createChartData(); | |
1562 | + }, | |
1563 | + // Y축 option 선택 | |
1564 | + selectedYAxis: function (e) { | |
1565 | + this.selectedY = e.target.value; | |
1566 | + const exists = this.selectedFieldArr.some( | |
1567 | + (item) => item.valIndex == this.selectedY | |
1568 | + ); | |
1569 | + | |
1570 | + // 선택된 y축이 이미 선택된 필드에 있는 경우 return | |
1571 | + if (exists) { | |
1572 | + return; | |
1573 | + } | |
1574 | + | |
1575 | + // 다중 데이터 사용이 아닌 경우, 선택된 필드 초기화 | |
1576 | + if (!this.isMultiData) { | |
1577 | + this.selectedFieldArr = []; | |
1578 | + } | |
1579 | + | |
1580 | + // COL_AND_LINE은 데이터가 2개가 필요하여, 선택된 데이터가 2개 이상 적용되지 않게 설정 | |
1581 | + if (this.chartName == "mix_chart" && this.selectedFieldArr.length > 1) { | |
1582 | + alert("선택된 필드는 2개까지만 가능합니다."); | |
1583 | + return; | |
1584 | + } | |
1585 | + let data = { | |
1586 | + valIndex: Number(this.selectedY), | |
1587 | + valNm: e.target.options[e.target.selectedIndex].text, | |
1588 | + }; | |
1589 | + this.selectedFieldArr.push(data); | |
1590 | + this.createChartData(); | |
1591 | + }, | |
1592 | + changeSelectGroup: function (e) { | |
1593 | + this.selectedGroup = e.target.value; | |
1594 | + this.selectedGroupNm = e.target.options[e.target.selectedIndex].text; | |
1595 | + this.createChartData(); | |
1596 | + }, | |
1597 | + // 차트 데이터 생성 | |
1598 | + createChartData: function () { | |
1599 | + let vm = this; | |
1600 | + if (vm.selectedX === "" || vm.selectedY === "") { | |
1601 | + return; | |
1602 | + } | |
1603 | + // 초기화 | |
1604 | + vm.datalist = []; | |
1605 | + vm.datalist = chartDataTransform.createData( | |
1606 | + vm.dummyDataList, | |
1607 | + vm.selectedX, | |
1608 | + vm.selectedFieldArr, | |
1609 | + vm.selectedGroup | |
1610 | + ); | |
1611 | + }, | |
1612 | + // chart 변경 시 실행 | |
1613 | + changeChartName: function (event, chartType) { | |
1614 | + this.componentName = event.target.name; | |
1615 | + this.chartName = chartType; | |
1616 | + | |
1617 | + // 초기화 | |
1618 | + this.selectedX = ""; | |
1619 | + this.selectedY = ""; | |
1620 | + this.selectedGroup = ""; | |
1621 | + this.selectedFieldArr = []; | |
1622 | + this.groupArr = []; | |
1623 | + | |
1624 | + // 차트 종류에 따라 다중 데이터 사용 여부 체크 하여 isMultiData에 저장 | |
1625 | + if ( | |
1626 | + this.chartName === "column_chart_v" || | |
1627 | + this.chartName === "column_chart_h" || // || this.chartName === "line_chart" | |
1628 | + this.chartName === "node_line_chart" || | |
1629 | + this.chartName === "liin_chart_back" || | |
1630 | + this.chartName === "pie_chart" || | |
1631 | + this.chartName === "dounet_chart" || | |
1632 | + this.chartName === "semicircle_chart" | |
1633 | + ) { | |
1634 | + this.isMultiData = false; | |
1635 | + } else { | |
1636 | + this.isMultiData = true; | |
1637 | + } | |
1638 | + }, | |
1639 | + // y축 배열 내 선택된 필드 삭제 | |
1640 | + deleteField: function (index) { | |
1641 | + this.selectedFieldArr.splice(index, 1); | |
1642 | + }, | |
1643 | + }, | |
1644 | + watch: { | |
1645 | + selectedFieldArr: function () { | |
1646 | + this.createChartData(); | |
1647 | + }, | |
1648 | + }, | |
1649 | + mounted() { | |
1650 | + this.jobGroup = this.$getDefaultJobGroup().jobGroup; | |
1651 | + }, | |
1652 | +}; | |
1653 | +</script> | |
1654 | + | |
1655 | +<style scoped> | |
1656 | +#chartdiv { | |
1657 | + width: 100%; | |
1658 | + height: 500px; | |
1659 | +} | |
1660 | +button { | |
1661 | + margin: 10px; | |
1662 | + background-color: antiquewhite; | |
1663 | + border-radius: 10px; | |
1664 | +} | |
1665 | +</style>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/chart/ColAndLine.vue
... | ... | @@ -0,0 +1,279 @@ |
1 | +<template> | |
2 | + <div | |
3 | + class="chartdiv" | |
4 | + id="chartdiv" | |
5 | + ref="chartdiv" | |
6 | + style="width: 100%; height: 700px" | |
7 | + ></div> | |
8 | +</template> | |
9 | + | |
10 | +<script> | |
11 | +import * as am5 from "@amcharts/amcharts5"; | |
12 | +import * as am5xy from "@amcharts/amcharts5/xy"; | |
13 | +import * as am5percent from "@amcharts/amcharts5/percent"; | |
14 | +import am5themes_Animated from "@amcharts/amcharts5/themes/Animated"; | |
15 | +import chartDataTransform from "./chartDataTransform"; | |
16 | + | |
17 | +export default { | |
18 | + props: { | |
19 | + datalist: { | |
20 | + type: Array, | |
21 | + default: [], | |
22 | + }, | |
23 | + chartName: { | |
24 | + type: String, | |
25 | + }, | |
26 | + selectedFieldArr: { | |
27 | + type: Array, | |
28 | + default: [], | |
29 | + }, | |
30 | + selectedCal: { | |
31 | + type: String, | |
32 | + default: "default", | |
33 | + }, | |
34 | + }, | |
35 | + data() { | |
36 | + return { | |
37 | + jsonData: [], | |
38 | + chartType: null, | |
39 | + }; | |
40 | + }, | |
41 | + components: {}, | |
42 | + methods: { | |
43 | + // 그룹 선택으로 인한 데이터 필터링 함수 | |
44 | + changeSelectSubGroup: function (event) { | |
45 | + this.selectedSubGroup = event.target.value; | |
46 | + // 필터 적용 함수 호출 | |
47 | + const filterDataList = chartDataTransform.setFilteringData( | |
48 | + this.datalist, | |
49 | + this.subGroupArr, | |
50 | + this.selectedSubGroup | |
51 | + ); | |
52 | + this.transformData(filterDataList); | |
53 | + }, | |
54 | + changeSubGroupData: function (val) { | |
55 | + if (Array.isArray(val)) { | |
56 | + this.subGroupArr = []; | |
57 | + this.subGroupArr = val | |
58 | + .map((data) => data.group) | |
59 | + .filter((value, index, self) => self.indexOf(value) === index) | |
60 | + .map((column, index) => { | |
61 | + return { | |
62 | + index: index, | |
63 | + columnNm: column, | |
64 | + }; | |
65 | + }); | |
66 | + // subGroupArr sort 처리 | |
67 | + this.subGroupArr.sort((a, b) => | |
68 | + a.columnNm < b.columnNm ? -1 : a.columnNm > b.columnNm ? 1 : 0 | |
69 | + ); | |
70 | + } | |
71 | + }, | |
72 | + //y 객체 배열 생성 | |
73 | + createYData: function () { | |
74 | + this.selectedFieldArr = this.createDataObj.valueAxis.map((field) => { | |
75 | + return { | |
76 | + valNm: field.columnNm, | |
77 | + valIndex: field.columnIdx, | |
78 | + }; | |
79 | + }); | |
80 | + }, | |
81 | + // 데이터 생성 함수 | |
82 | + // dataList를 받아와 차트에 표현할 데이터로 가공 | |
83 | + transformData: function (val) { | |
84 | + if (this.selectedCal === "default") { | |
85 | + this.chartJsondData = val; | |
86 | + this.createChart(); | |
87 | + return; | |
88 | + } | |
89 | + // 전체 데이터를 그룹화하여 가공 (All Data) | |
90 | + this.groupsResult = chartDataTransform.dataGrouping( | |
91 | + val, | |
92 | + this.selectedFieldArr | |
93 | + ); | |
94 | + if (!this.isSelectGroup) { | |
95 | + // groupSubArr에 group의 key를 중복을 제거하고 담음 | |
96 | + this.changeSubGroupData(this.datalist); | |
97 | + } | |
98 | + // } | |
99 | + // 데이터 가공 함수 | |
100 | + this.chartJsondData = chartDataTransform.calculateSetting( | |
101 | + this.groupsResult, | |
102 | + this.selectedCal | |
103 | + ); | |
104 | + | |
105 | + this.createChart(); | |
106 | + }, | |
107 | + initializeChartArea: function () { | |
108 | + let chartWarp = this.$refs["chartdiv"]; // 차트 상위 div ref 매칭 | |
109 | + chartWarp.innerHTML = ""; // 차트 상위 div 내용 초기화 (기존 차트 삭제) | |
110 | + | |
111 | + let div = document.createElement("div"); // 차트를 담을 빈 div 생성 (차트 하위 div) | |
112 | + div.style.width = "100%"; // 차트를 담을 div의 넓이 | |
113 | + div.style.height = "100%"; // 차트를 담을 div의 높이 | |
114 | + chartWarp.appendChild(div); // 차트 상위 div 안에 차트 하위 div를 추가 | |
115 | + | |
116 | + let chartArea = am5.Root.new(chartWarp.firstChild); | |
117 | + | |
118 | + chartArea.setThemes([am5themes_Animated.new(root)]); | |
119 | + | |
120 | + return chartArea; | |
121 | + }, | |
122 | + createChart: function () { | |
123 | + let vm = this; | |
124 | + let data = vm.chartJsondData; | |
125 | + | |
126 | + let income = Object.keys(data[0])[1]; | |
127 | + let expenses = Object.keys(data[0])[2]; | |
128 | + | |
129 | + let root = vm.initializeChartArea(); // 차트 초기화 | |
130 | + | |
131 | + let chart = root.container.children.push( | |
132 | + am5xy.XYChart.new(root, { | |
133 | + panX: false, | |
134 | + panY: false, | |
135 | + wheelX: "panX", | |
136 | + wheelY: "zoomX", | |
137 | + paddingLeft: 0, | |
138 | + layout: root.verticalLayout, | |
139 | + }) | |
140 | + ); | |
141 | + | |
142 | + chart.set( | |
143 | + "scrollbarX", | |
144 | + am5.Scrollbar.new(root, { | |
145 | + orientation: "horizontal", | |
146 | + }) | |
147 | + ); | |
148 | + | |
149 | + let xRenderer = am5xy.AxisRendererX.new(root, { | |
150 | + minorGridEnabled: true, | |
151 | + minGridDistance: 60, | |
152 | + }); | |
153 | + let xAxis = chart.xAxes.push( | |
154 | + am5xy.CategoryAxis.new(root, { | |
155 | + categoryField: "categoryData", | |
156 | + renderer: xRenderer, | |
157 | + tooltip: am5.Tooltip.new(root, {}), | |
158 | + }) | |
159 | + ); | |
160 | + xRenderer.grid.template.setAll({ | |
161 | + location: 1, | |
162 | + }); | |
163 | + | |
164 | + xAxis.data.setAll(data); | |
165 | + | |
166 | + let yAxis = chart.yAxes.push( | |
167 | + am5xy.ValueAxis.new(root, { | |
168 | + min: 0, | |
169 | + extraMax: 0.1, | |
170 | + renderer: am5xy.AxisRendererY.new(root, { | |
171 | + strokeOpacity: 0.1, | |
172 | + }), | |
173 | + }) | |
174 | + ); | |
175 | + | |
176 | + let series1 = chart.series.push( | |
177 | + am5xy.ColumnSeries.new(root, { | |
178 | + name: income, | |
179 | + xAxis: xAxis, | |
180 | + yAxis: yAxis, | |
181 | + valueYField: income, | |
182 | + categoryXField: "categoryData", | |
183 | + tooltip: am5.Tooltip.new(root, { | |
184 | + pointerOrientation: "horizontal", | |
185 | + labelText: "{name} in {categoryX}: {valueY} {info}", | |
186 | + }), | |
187 | + }) | |
188 | + ); | |
189 | + | |
190 | + series1.columns.template.setAll({ | |
191 | + tooltipY: am5.percent(10), | |
192 | + templateField: "columnSettings", | |
193 | + }); | |
194 | + | |
195 | + series1.data.setAll(data); | |
196 | + | |
197 | + let series2 = chart.series.push( | |
198 | + am5xy.LineSeries.new(root, { | |
199 | + name: expenses, | |
200 | + xAxis: xAxis, | |
201 | + yAxis: yAxis, | |
202 | + valueYField: expenses, | |
203 | + categoryXField: "categoryData", | |
204 | + tooltip: am5.Tooltip.new(root, { | |
205 | + pointerOrientation: "horizontal", | |
206 | + labelText: "{name} in {categoryX}: {valueY} {info}", | |
207 | + }), | |
208 | + }) | |
209 | + ); | |
210 | + | |
211 | + series2.strokes.template.setAll({ | |
212 | + strokeWidth: 3, | |
213 | + templateField: "strokeSettings", | |
214 | + }); | |
215 | + | |
216 | + series2.data.setAll(data); | |
217 | + | |
218 | + series2.bullets.push(function () { | |
219 | + return am5.Bullet.new(root, { | |
220 | + sprite: am5.Circle.new(root, { | |
221 | + strokeWidth: 3, | |
222 | + stroke: series2.get("stroke"), | |
223 | + radius: 5, | |
224 | + fill: root.interfaceColors.get("background"), | |
225 | + }), | |
226 | + }); | |
227 | + }); | |
228 | + | |
229 | + chart.set("cursor", am5xy.XYCursor.new(root, {})); | |
230 | + | |
231 | + let legend = chart.children.push( | |
232 | + am5.Legend.new(root, { | |
233 | + centerX: am5.p50, | |
234 | + x: am5.p50, | |
235 | + }) | |
236 | + ); | |
237 | + legend.data.setAll(chart.series.values); | |
238 | + | |
239 | + series1.appear(1000); | |
240 | + chart.appear(1000, 300); | |
241 | + | |
242 | + root._logo.dispose(); //amChart 로고 삭제 | |
243 | + }, | |
244 | + }, | |
245 | + watch: { | |
246 | + datalist: { | |
247 | + handler: function (newVal, oldVal) { | |
248 | + this.transformData(newVal); | |
249 | + }, | |
250 | + deep: true, | |
251 | + }, | |
252 | + selectedCal: { | |
253 | + handler: function () { | |
254 | + this.transformData(this.datalist); | |
255 | + }, | |
256 | + }, | |
257 | + }, | |
258 | + mounted() { | |
259 | + this.datalist = this.createDataObj.data_list; | |
260 | + this.chartName = this.createDataObj.chart_knd; | |
261 | + this.isMultiData = this.createDataObj.multidata_use_yn; | |
262 | + this.selectedCal = this.createDataObj.chart_cal; | |
263 | + | |
264 | + this.transformData(this.datalist); | |
265 | + }, | |
266 | +}; | |
267 | +</script> | |
268 | + | |
269 | +<style scoped> | |
270 | +#chartdiv { | |
271 | + width: 100%; | |
272 | + height: 500px; | |
273 | +} | |
274 | +button { | |
275 | + margin: 10px; | |
276 | + background-color: antiquewhite; | |
277 | + border-radius: 10px; | |
278 | +} | |
279 | +</style>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/chart/ColumnLablesChart.vue
... | ... | @@ -0,0 +1,202 @@ |
1 | +<template> | |
2 | + <div | |
3 | + class="chartdiv" | |
4 | + id="chartdiv" | |
5 | + ref="chartdiv" | |
6 | + style="width: 100%; height: 700px" | |
7 | + ></div> | |
8 | +</template> | |
9 | + | |
10 | +<script> | |
11 | +import * as am5 from "@amcharts/amcharts5"; | |
12 | +import * as am5xy from "@amcharts/amcharts5/xy"; | |
13 | +import am5themes_Animated from "@amcharts/amcharts5/themes/Animated"; | |
14 | + | |
15 | +export default { | |
16 | + props: { | |
17 | + datalist: { | |
18 | + type: Array, | |
19 | + default: [], | |
20 | + }, | |
21 | + chartName: { | |
22 | + type: String, | |
23 | + }, | |
24 | + }, | |
25 | + data() { | |
26 | + return { | |
27 | + jsonData: [], | |
28 | + root: null, | |
29 | + }; | |
30 | + }, | |
31 | + components: {}, | |
32 | + methods: { | |
33 | + initializeChartArea: function () { | |
34 | + let chartWarp = this.$refs["chartdiv"]; // 차트 상위 div ref 매칭 | |
35 | + chartWarp.innerHTML = ""; // 차트 상위 div 내용 초기화 (기존 차트 삭제) | |
36 | + | |
37 | + let div = document.createElement("div"); // 차트를 담을 빈 div 생성 (차트 하위 div) | |
38 | + div.style.width = "100%"; // 차트를 담을 div의 넓이 | |
39 | + div.style.height = "100%"; // 차트를 담을 div의 높이 | |
40 | + chartWarp.appendChild(div); // 차트 상위 div 안에 차트 하위 div를 추가 | |
41 | + | |
42 | + let chartArea = am5.Root.new(chartWarp.firstChild); | |
43 | + | |
44 | + chartArea.setThemes([am5themes_Animated.new(root)]); | |
45 | + | |
46 | + return chartArea; | |
47 | + }, | |
48 | + createChart: function (data) { | |
49 | + let vm = this; | |
50 | + | |
51 | + let root = this.initializeChartArea(); // 차트 초기화 | |
52 | + | |
53 | + let series = null; | |
54 | + let chart = null; | |
55 | + | |
56 | + // 동적 데이터 설정 | |
57 | + let categoryAxisRenderer = null; | |
58 | + let valueAxisRenderer = null; | |
59 | + let categoryAxes = null; | |
60 | + let valueAxes = null; | |
61 | + let categoryInversed = null; // 카테고리 셀 순서 (순차: false, 역순: true) | |
62 | + | |
63 | + categoryAxisRenderer = | |
64 | + vm.chartName == "column_chart_v" ? "AxisRendererX" : "AxisRendererY"; | |
65 | + valueAxisRenderer = | |
66 | + vm.chartName == "column_chart_v" ? "AxisRendererY" : "AxisRendererX"; | |
67 | + categoryAxes = vm.chartName == "column_chart_v" ? "xAxes" : "yAxes"; | |
68 | + valueAxes = vm.chartName == "column_chart_v" ? "yAxes" : "xAxes"; | |
69 | + categoryInversed = vm.chartName == "column_chart_v" ? false : true; | |
70 | + | |
71 | + // Create chart | |
72 | + let panX = true; | |
73 | + let panY = true; | |
74 | + let wheelX = "panX"; | |
75 | + let wheelY = vm.chartName == "column_chart_v" ? "panY" : "zoomX"; | |
76 | + | |
77 | + chart = root.container.children.push( | |
78 | + am5xy.XYChart.new(root, { | |
79 | + panX: panX, | |
80 | + panY: panY, | |
81 | + wheelX: wheelX, | |
82 | + wheelY: wheelY, | |
83 | + }) | |
84 | + ); | |
85 | + | |
86 | + let categoryRenderer = am5xy[categoryAxisRenderer].new(root, { | |
87 | + minGridDistance: 30, | |
88 | + minorGridEnabled: true, | |
89 | + inversed: categoryInversed, | |
90 | + }); | |
91 | + | |
92 | + categoryRenderer.labels.template.setAll({ | |
93 | + rotation: -90, | |
94 | + centerY: am5.p50, | |
95 | + centerX: am5.p100, | |
96 | + paddingRight: 15, | |
97 | + }); | |
98 | + | |
99 | + categoryRenderer.grid.template.setAll({ | |
100 | + stroke: am5.color(0xf15f5f), | |
101 | + location: 1, | |
102 | + }); | |
103 | + | |
104 | + let categoryAxis = chart[categoryAxes].push( | |
105 | + am5xy.CategoryAxis.new(root, { | |
106 | + maxDeviation: 0.3, | |
107 | + categoryField: "categoryData", | |
108 | + renderer: categoryRenderer, | |
109 | + tooltip: am5.Tooltip.new(root, {}), | |
110 | + }) | |
111 | + ); | |
112 | + | |
113 | + let valueRenderer = am5xy[valueAxisRenderer].new(root, { | |
114 | + strokeOpacity: 0.1, | |
115 | + }); | |
116 | + | |
117 | + let valueAxis = chart[valueAxes].push( | |
118 | + am5xy.ValueAxis.new(root, { | |
119 | + maxDeviation: 0.3, | |
120 | + renderer: valueRenderer, | |
121 | + }) | |
122 | + ); | |
123 | + | |
124 | + let xAxis = null; | |
125 | + let yAxis = null; | |
126 | + let categoryField = null; | |
127 | + let valueField = null; | |
128 | + let categoryXY = null; | |
129 | + let valueXY = null; | |
130 | + if (vm.chartName == "column_chart_v") { | |
131 | + xAxis = categoryAxis; | |
132 | + yAxis = valueAxis; | |
133 | + categoryField = "categoryXField"; | |
134 | + valueField = "valueYField"; | |
135 | + categoryXY = "categoryX"; | |
136 | + valueXY = "valueY"; | |
137 | + } else { | |
138 | + xAxis = valueAxis; | |
139 | + yAxis = categoryAxis; | |
140 | + categoryField = "categoryYField"; | |
141 | + valueField = "valueXField"; | |
142 | + categoryXY = "categoryY"; | |
143 | + valueXY = "valueX"; | |
144 | + } | |
145 | + | |
146 | + series = chart.series.push( | |
147 | + am5xy.ColumnSeries.new(root, { | |
148 | + name: "Series 1", | |
149 | + xAxis: xAxis, | |
150 | + yAxis: yAxis, | |
151 | + [valueField]: "value", | |
152 | + [categoryField]: "categoryData", | |
153 | + tooltip: am5.Tooltip.new(root, { | |
154 | + labelText: "{valueY}", | |
155 | + }), | |
156 | + }) | |
157 | + ); | |
158 | + | |
159 | + series.columns.template.setAll({ | |
160 | + cornerRadiusTL: 5, | |
161 | + cornerRadiusTR: 5, | |
162 | + strokeOpacity: 0, | |
163 | + }); | |
164 | + series.columns.template.adapters.add("fill", function (fill, target) { | |
165 | + return chart.get("colors").getIndex(series.columns.indexOf(target)); | |
166 | + }); | |
167 | + | |
168 | + series.columns.template.adapters.add("stroke", function (stroke, target) { | |
169 | + return chart.get("colors").getIndex(series.columns.indexOf(target)); | |
170 | + }); | |
171 | + | |
172 | + categoryAxis.data.setAll(data); | |
173 | + series.data.setAll(data); | |
174 | + series.appear(1000); | |
175 | + }, | |
176 | + }, | |
177 | + watch: { | |
178 | + datalist: { | |
179 | + handler: function (newVal, oldVal) { | |
180 | + this.jsonData = newVal; | |
181 | + this.createChart(this.jsonData); | |
182 | + }, | |
183 | + deep: true, | |
184 | + }, | |
185 | + }, | |
186 | + mounted() { | |
187 | + this.jsonData = this.datalist; | |
188 | + }, | |
189 | +}; | |
190 | +</script> | |
191 | + | |
192 | +<style scoped> | |
193 | +#chartdiv { | |
194 | + width: 100%; | |
195 | + height: 500px; | |
196 | +} | |
197 | +button { | |
198 | + margin: 10px; | |
199 | + background-color: antiquewhite; | |
200 | + border-radius: 10px; | |
201 | +} | |
202 | +</style>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/chart/chartDataTransform.js
... | ... | @@ -0,0 +1,136 @@ |
1 | + | |
2 | +const chartDataTransform = { | |
3 | + /** | |
4 | + * rowdata를 사용자 선택에 맞게 반환 | |
5 | + * @param rowdata : 원본 데이터 | |
6 | + * @param x : x축 객체 데이터 | |
7 | + * @param yArr : y축 배열 데이터 | |
8 | + */ | |
9 | + createData: function(rowdata, xAxisList, yAxisList, group){ | |
10 | + let resultArr = []; | |
11 | + let x; | |
12 | + //x축 객체 배열 데이터를 가져와서 columnNm 값을 가져옴 | |
13 | + if(xAxisList.length !== 0){ | |
14 | + x = xAxisList[0].columnIdx; | |
15 | + } | |
16 | + | |
17 | + //y축 객체 배열 데이터를 가져와서 columnNm 값을 배열로 가져옴 | |
18 | + let yArr = yAxisList.map(field => { | |
19 | + return { | |
20 | + valNm: field.columnNm, | |
21 | + valIndex: field.columnIdx | |
22 | + } | |
23 | + }); | |
24 | + | |
25 | + if (yArr.length > 0) { | |
26 | + // 반복문을 통해 y축의 데이터를 가져와서 datalist에 추가 | |
27 | + for(let i = 0; i < rowdata.length; i++) { | |
28 | + let data = { | |
29 | + categoryData: rowdata[i][x] | |
30 | + } | |
31 | + // data에 y축의 다중데이터 추가 | |
32 | + yArr.forEach(field => { | |
33 | + const fieldName = field.valNm; | |
34 | + if(!data[fieldName]) { | |
35 | + data[fieldName] = 0; | |
36 | + } | |
37 | + data[fieldName] += Number(rowdata[i][field.valIndex]); | |
38 | + }); | |
39 | + // group 추가 | |
40 | + if(group !== undefined && group !== ""){ | |
41 | + data.group = rowdata[i][group];} | |
42 | + resultArr.push(data); | |
43 | + } | |
44 | + } | |
45 | + return resultArr; | |
46 | + | |
47 | + }, | |
48 | + /** | |
49 | + * 변환된 데이터 그룹화 | |
50 | + * @param data : 변환된 데이터 | |
51 | + * @param yArr : y축 카테고리 배열 데이터 | |
52 | + */ | |
53 | + dataGrouping: function(data, yArr){ | |
54 | + if (!Array.isArray(data) || !data) { | |
55 | + return {}; | |
56 | + } | |
57 | + return data.reduce((acc, cur) => { | |
58 | + if (!acc[cur.categoryData]) { | |
59 | + acc[cur.categoryData] = {}; | |
60 | + // 각 필드 초기화 | |
61 | + yArr.forEach(field => { | |
62 | + acc[cur.categoryData][field.valNm] = { values: [], sum: 0, min: Infinity, max: -Infinity, count: 0 }; | |
63 | + }); | |
64 | + } | |
65 | + | |
66 | + // 각 필드별 합계 계산 | |
67 | + yArr.forEach(field => { | |
68 | + const fieldName = field.valNm; | |
69 | + // cur[fieldName]이 배열인지 확인하고, 아니라면 배열로 만듦 | |
70 | + let values = Array.isArray(cur[fieldName]) ? cur[fieldName] : [cur[fieldName]]; | |
71 | + values.forEach(value => { | |
72 | + acc[cur.categoryData][fieldName].values.push(value); | |
73 | + acc[cur.categoryData][fieldName].sum += value; | |
74 | + acc[cur.categoryData][fieldName].min = Math.min(acc[cur.categoryData][fieldName].min, value); | |
75 | + acc[cur.categoryData][fieldName].max = Math.max(acc[cur.categoryData][fieldName].max, value); | |
76 | + acc[cur.categoryData][fieldName].count += 1; | |
77 | + }); | |
78 | + }); | |
79 | + | |
80 | + return acc; | |
81 | + }, {}); | |
82 | + }, | |
83 | + | |
84 | + /** | |
85 | + * 데이터 계산 값으로 가공 (min, max, sum, avg) | |
86 | + * @param groupData : 그룹화된 데이터 | |
87 | + * @param selectedCal : 사용자가 선택한 계산 값 | |
88 | + */ | |
89 | + calculateSetting: function(groupData, selectedCal) { | |
90 | + let result = Object.keys(groupData).map(categoryData => { | |
91 | + const result = { categoryData: categoryData }; | |
92 | + Object.keys(groupData[categoryData]).forEach(fieldName => { | |
93 | + const fieldData = groupData[categoryData][fieldName]; | |
94 | + switch (selectedCal) { | |
95 | + case "sum": | |
96 | + result[fieldName] = fieldData.sum; | |
97 | + break; | |
98 | + case "avg": | |
99 | + result[fieldName] = fieldData.sum / fieldData.count; | |
100 | + break; | |
101 | + case "min": | |
102 | + result[fieldName] = fieldData.min; | |
103 | + break; | |
104 | + case "max": | |
105 | + result[fieldName] = fieldData.max; | |
106 | + break; | |
107 | + } | |
108 | + }); | |
109 | + return result; | |
110 | + }); | |
111 | + return result; | |
112 | + }, | |
113 | + | |
114 | + /** | |
115 | + * 데이터 필터링 | |
116 | + * 선택 된 그룹의 데이터로 가공 | |
117 | + */ | |
118 | + setFilteringData : function(dataList ,subGroupArr, selectedSubGroup){ | |
119 | + // groupSubArr에서 selectedSubGroup의 index를 찾아서 그 columnNm을 찾아옴 | |
120 | + const selectedSubGroupNm = subGroupArr.find(column => column.index === Number(selectedSubGroup)).columnNm; | |
121 | + return dataList.filter(data => data.group === selectedSubGroupNm); | |
122 | + }, | |
123 | + /** | |
124 | + * 음수 차트 데이터 처리 | |
125 | + */ | |
126 | + convertValuesToNegative: function(data, fieldNm){ | |
127 | + // 데이터 리스트를 순회하며 각 객체의 값을 확인 | |
128 | + let test = data.forEach(obj => { | |
129 | + obj[fieldNm] = -Math.abs(obj[fieldNm]); | |
130 | + }); | |
131 | + return test; | |
132 | + }, | |
133 | + | |
134 | +} | |
135 | + | |
136 | +export default chartDataTransform;(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/common/AlertModal.vue
... | ... | @@ -0,0 +1,151 @@ |
1 | +<template> | |
2 | + <div v-show="isModalOpen" class="modal-wrapper"> | |
3 | + <div class="modal-container small-modal"> | |
4 | + <div class="modal-title text-ct"> | |
5 | + <h2>{{ title }}</h2> | |
6 | + </div> | |
7 | + <div class="modal-content-monthly"> | |
8 | + <p class="alert-write text-ct" v-html="message"> | |
9 | + </p> | |
10 | + <div v-if="radioAt"> | |
11 | + <p>원본파일 : </p> | |
12 | + <label></label> | |
13 | + <p>동작 : </p> | |
14 | + <label><input type="radio" value="move" v-model="moveOrCopy.type">덮어쓰기</label> | |
15 | + <label><input type="radio" value="cancel" v-model="moveOrCopy.type">건너뛰기</label> | |
16 | + <label><input type="checkbox" v-model="moveOrCopy.checkBox">항상 이 동작 사용</label> | |
17 | + </div> | |
18 | + </div> | |
19 | + <div class="modal-end flex justify-center" style="flex-wrap: nowrap;"> | |
20 | + <button class="blue-btn large-btn" id="confirmOk" @click="closeModal" @keyup.enter="closeModal">확인</button> | |
21 | + <button class="gray-btn large-btn" id="confirmCancel" @click="closeModal" v-show="confirmAt">취소</button> | |
22 | + <button class="gray-btn large-btn" id="confirmRadioCancel" @click="closeModal" v-show="radioAt">취소</button> | |
23 | + </div> | |
24 | + </div> | |
25 | + </div> | |
26 | +</template> | |
27 | +<script> | |
28 | + | |
29 | +export default { | |
30 | + props: { | |
31 | + title: { | |
32 | + type: String, | |
33 | + default: '모달 제목' | |
34 | + }, | |
35 | + message: { | |
36 | + type: String, | |
37 | + default: '경고 메세지를 입력해주세요. <br /> 삭제,수정,추가 등등' | |
38 | + }, | |
39 | + }, | |
40 | + data() { | |
41 | + return { | |
42 | + isModalOpen: false, | |
43 | + activeTab: 'tab1', | |
44 | + modalType: 'tab-modal', | |
45 | + title: this.title, | |
46 | + message: this.message, | |
47 | + confirmAt: false, | |
48 | + radioAt: false, | |
49 | + moveOrCopy: { | |
50 | + type: null, | |
51 | + checkBox: null | |
52 | + }, | |
53 | + } | |
54 | + }, | |
55 | + methods: { | |
56 | + // 탭 변경 | |
57 | + showTab: function (tabName) { | |
58 | + this.activeTab = tabName; | |
59 | + }, | |
60 | + | |
61 | + // 닫기 | |
62 | + closeModal: function () { | |
63 | + this.isModalOpen = false; | |
64 | + this.confirmAt = false; | |
65 | + this.radioAt = false; | |
66 | + }, | |
67 | + | |
68 | + // 모달 호출 | |
69 | + showModal: function () { | |
70 | + | |
71 | + this.isModalOpen = true; | |
72 | + document.getElementById("confirmOk").focus() | |
73 | + }, | |
74 | + | |
75 | + // confirm 호출 | |
76 | + showConfirm: async function () { | |
77 | + this.confirmAt = true; | |
78 | + this.isModalOpen = true; | |
79 | + document.getElementById("confirmOk").focus() | |
80 | + const promise = new Promise((resolve, reject) => { | |
81 | + document.getElementById("confirmCancel").addEventListener("click", async () => { | |
82 | + resolve('cancel') | |
83 | + }); | |
84 | + | |
85 | + document.getElementById("confirmOk").addEventListener("click", async () => { | |
86 | + resolve('ok') | |
87 | + }); | |
88 | + }); | |
89 | + | |
90 | + return promise.then( | |
91 | + (id) => { | |
92 | + if (id == 'cancel') { | |
93 | + return false; | |
94 | + } else if (id == 'ok') { | |
95 | + return true; | |
96 | + } | |
97 | + } | |
98 | + ); | |
99 | + }, | |
100 | + | |
101 | + // radio confirm 호출 | |
102 | + showRadioConfirm: async function () { | |
103 | + this.radioAt = true; | |
104 | + this.isModalOpen = true; | |
105 | + this.moveOrCopy.type = "move"; | |
106 | + this.moveOrCopy.checkBox = false; | |
107 | + | |
108 | + document.getElementById("confirmOk").focus() | |
109 | + const promise = new Promise((resolve, reject) => { | |
110 | + document.getElementById("confirmRadioCancel").addEventListener("click", async () => { | |
111 | + resolve('cancel') | |
112 | + }); | |
113 | + | |
114 | + document.getElementById("confirmOk").addEventListener("click", async () => { | |
115 | + resolve('ok') | |
116 | + }); | |
117 | + }); | |
118 | + | |
119 | + return promise.then( | |
120 | + (id) => { | |
121 | + if (id == 'cancel') { | |
122 | + this.moveOrCopy.type = "cancel" | |
123 | + this.moveOrCopy.checkBox = false | |
124 | + return this.moveOrCopy; | |
125 | + } else if (id == 'ok') { | |
126 | + return this.moveOrCopy; | |
127 | + } | |
128 | + } | |
129 | + ); | |
130 | + }, | |
131 | + | |
132 | + setTitle: function (Title) { | |
133 | + this.title = Title; | |
134 | + }, | |
135 | + | |
136 | + setMessage: function (message) { | |
137 | + this.message = message; | |
138 | + }, | |
139 | + | |
140 | + }, | |
141 | + watch: { | |
142 | + | |
143 | + }, | |
144 | + computed: { | |
145 | + | |
146 | + }, | |
147 | + components: { | |
148 | + | |
149 | + }, | |
150 | +} | |
151 | +</script>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/common/Component_CodeList.vue
... | ... | @@ -0,0 +1,106 @@ |
1 | +<template> | |
2 | + <div class="content-box overflow-y"> | |
3 | + <div class="content-box"> | |
4 | + <ul class="content-list" v-if="codeList.length > 0"> | |
5 | + <li class="all-data-search cursor"> | |
6 | + <a class="file-list" @click="allSelect"> | |
7 | + <div class="flex align-center"> | |
8 | + <p class="mr5"> | |
9 | + <img src="../../../resources/img/icon/iconCategory.png" /> | |
10 | + </p> | |
11 | + <p>전체</p> | |
12 | + </div> | |
13 | + </a> | |
14 | + </li> | |
15 | + <li class="cursor" v-for="(item, idx) in codeList" :key="idx"> | |
16 | + <a :class="{ 'file-list': true, selected: selected === idx }" @click="categorySelect(item, idx)"> | |
17 | + <div class="flex align-center"> | |
18 | + <p class="mr5"> | |
19 | + <svg-icon type="mdi" :path="path" :color="'#fbbe28'"></svg-icon> | |
20 | + </p> | |
21 | + <p>{{ item.codeNm }}</p> | |
22 | + </div> | |
23 | + </a> | |
24 | + </li> | |
25 | + </ul> | |
26 | + <ul class="content-list" v-else></ul> | |
27 | + </div> | |
28 | + </div> | |
29 | +</template> | |
30 | +<script> | |
31 | +import axios from "axios"; | |
32 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
33 | +import { mdiFolder } from "@mdi/js"; | |
34 | + | |
35 | +export default { | |
36 | + props: { | |
37 | + groupCode: { | |
38 | + type: String, | |
39 | + default: "DATA_CTGRY", | |
40 | + }, | |
41 | + categoryIdx: { | |
42 | + type: Number, | |
43 | + }, | |
44 | + }, | |
45 | + data() { | |
46 | + return { | |
47 | + codeList: [], | |
48 | + groupCode: this.groupCode, | |
49 | + path: mdiFolder, | |
50 | + selected: null, | |
51 | + }; | |
52 | + }, | |
53 | + methods: { | |
54 | + categorySelect: function (item, idx) { | |
55 | + this.selected = idx; | |
56 | + this.$emit("selectCode", item.cmmnCode); | |
57 | + }, | |
58 | + allSelect: function () { | |
59 | + this.selected = null; | |
60 | + this.$emit("selectCode", null); | |
61 | + }, | |
62 | + // 초기화 | |
63 | + init: function () { | |
64 | + const vm = this; | |
65 | + axios({ | |
66 | + url: "/common/getCodeList.json", | |
67 | + method: "post", | |
68 | + headers: { | |
69 | + "Content-Type": "application/json; charset=UTF-8", | |
70 | + }, | |
71 | + data: JSON.stringify({ groupCode: vm.groupCode }), | |
72 | + }) | |
73 | + .then(function (response) { | |
74 | + if (response.data.checkMessage.status == 200) { | |
75 | + vm.codeList = response.data.resultData.codeList; | |
76 | + } else { | |
77 | + this.$showAlert("알람", response.data.checkMessage.message); | |
78 | + } | |
79 | + }) | |
80 | + .catch(function (error) { | |
81 | + this.$showAlert( | |
82 | + "에러 발생", | |
83 | + "에러가 발생했습니다. 관리자에게 문의해 주세요." | |
84 | + ); | |
85 | + }); | |
86 | + }, | |
87 | + }, | |
88 | + watch: { | |
89 | + categoryIdx: function (val) { | |
90 | + this.selected = val; | |
91 | + }, | |
92 | + }, | |
93 | + components: { | |
94 | + SvgIcon: SvgIcon, | |
95 | + }, | |
96 | + mounted() { | |
97 | + this.init(); | |
98 | + }, | |
99 | +}; | |
100 | +</script> | |
101 | +<style scoped> | |
102 | +.all-data-search { | |
103 | + font-size: 15px; | |
104 | + font-weight: bold; | |
105 | +} | |
106 | +</style>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/common/Component_Dataset.vue
... | ... | @@ -0,0 +1,61 @@ |
1 | +<template> | |
2 | + <div class="content-box overflow-y"> | |
3 | + <div class="content-box"> | |
4 | + <ul class="content-list" v-if="postList.length > 0"> | |
5 | + <li class="cursor" v-for="(item, idx) in postList" :key="idx"> | |
6 | + <a | |
7 | + @click="categorySelect(item, idx)" | |
8 | + :class="{ 'file-list': true, selected: selected === idx }" | |
9 | + > | |
10 | + <div class="flex align-center"> | |
11 | + <p class="mr5"> | |
12 | + <svg-icon type="mdi" :path="path" :color="'#fbbe28'"></svg-icon> | |
13 | + </p> | |
14 | + <p>{{ item.post_sj }}</p> | |
15 | + </div> | |
16 | + </a> | |
17 | + </li> | |
18 | + </ul> | |
19 | + <ul class="content-list" v-else></ul> | |
20 | + </div> | |
21 | + </div> | |
22 | +</template> | |
23 | + | |
24 | +<script> | |
25 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
26 | +import { mdiFolder } from "@mdi/js"; | |
27 | + | |
28 | +export default { | |
29 | + props: { | |
30 | + dataPostList: { | |
31 | + type: Array, | |
32 | + default: () => [], | |
33 | + }, | |
34 | + }, | |
35 | + data() { | |
36 | + return { | |
37 | + postList: this.dataPostList, | |
38 | + groupCode: this.groupCode, | |
39 | + path: mdiFolder, | |
40 | + selected: 0, | |
41 | + }; | |
42 | + }, | |
43 | + methods: { | |
44 | + categorySelect: function (item, idx) { | |
45 | + this.selected = idx; | |
46 | + this.$emit("selectDatasetPost", item); | |
47 | + }, | |
48 | + }, | |
49 | + watch: { | |
50 | + dataPostList: function (newVal, oldVal) { | |
51 | + this.postList = newVal; | |
52 | + if (this.postList.length > 0) { | |
53 | + this.categorySelect(this.postList[0], 0); | |
54 | + } | |
55 | + }, | |
56 | + }, | |
57 | + components: { | |
58 | + SvgIcon: SvgIcon, | |
59 | + }, | |
60 | +}; | |
61 | +</script>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/connection/EhojoConnection.vue
... | ... | @@ -0,0 +1,382 @@ |
1 | +<template> | |
2 | + <div v-show="isModalOpen" class="modal-wrapper"> | |
3 | + <div class="modal-container large-modal"> | |
4 | + <div class="modal-title"> | |
5 | + <div class="flex justify-between align-center"> | |
6 | + <h2>차세대 API 연결</h2> | |
7 | + <button class="close-btn" @click="$emit('fnCloseModal')"> | |
8 | + <svg-icon type="mdi" :width="20" :height="20" :path="closePath"></svg-icon> | |
9 | + </button> | |
10 | + </div> | |
11 | + </div> | |
12 | + <div class="modal-content-monthly"> | |
13 | + <div v-if="modalView == 'default'"> | |
14 | + <div class="content-titleZone"> | |
15 | + <p class="box-title">기본 정보</p> | |
16 | + </div> | |
17 | + <div> | |
18 | + <table class="form-table"> | |
19 | + <colgroup> | |
20 | + <col style="width: 15%" /> | |
21 | + <col style="width: 35%" /> | |
22 | + <col style="width: 15%" /> | |
23 | + <col style="width: 35%" /> | |
24 | + </colgroup> | |
25 | + <tbody> | |
26 | + <tr> | |
27 | + <th>송신기관 코드</th> | |
28 | + <td><input type="text" v-model="job.lafCode" class="full-input" placeholder="송신기관 코드를 입력해 주세요."></td> | |
29 | + <th>업무구분 코드</th> | |
30 | + <td><input type="text" v-model="job.taskCode" class="full-input" placeholder="업무구분 코드를 입력해 주세요."></td> | |
31 | + </tr> | |
32 | + <tr> | |
33 | + <th>테이블 명</th> | |
34 | + <td><input type="text" v-model="job.tableName" class="full-input" placeholder="테이블 명을 입력해 주세요."></td> | |
35 | + <th>인터페이스 ID</th> | |
36 | + <td><input type="text" v-model="job.ifId" class="full-input" placeholder="인터페이스 ID를 입력해 주세요."></td> | |
37 | + </tr> | |
38 | + </tbody> | |
39 | + </table> | |
40 | + </div> | |
41 | + <div class="flex justify-between"> | |
42 | + <div class="flex50 pl0"> | |
43 | + <div class="content-titleZone flex justify-between align-center mt20"> | |
44 | + <p class="box-title">헤더 정보</p> | |
45 | + <div> | |
46 | + <button class="blue-border-btn small-btn" @click="fnAddRow('header')">헤더 정보 추가</button> | |
47 | + </div> | |
48 | + </div> | |
49 | + <div style="height: 300px; overflow: auto"> | |
50 | + <table class="list-table" style="table-layout: fixed"> | |
51 | + <colgroup> | |
52 | + <col width="10%" /> | |
53 | + <col width="30%" /> | |
54 | + <col width="50%" /> | |
55 | + <col width="10%" /> | |
56 | + </colgroup> | |
57 | + <thead> | |
58 | + <tr> | |
59 | + <th style="text-align: center">No</th> | |
60 | + <th style="text-align: center">키</th> | |
61 | + <th style="text-align: center">값</th> | |
62 | + <th style="text-align: center">비고</th> | |
63 | + </tr> | |
64 | + </thead> | |
65 | + <tbody> | |
66 | + <tr v-for="(item, idx) in job.headers" :key="idx"> | |
67 | + <td style="text-align: center">{{ idx + 1 }}</td> | |
68 | + <td><input type="text" class="full-input" v-model.trim="item.key" /></td> | |
69 | + <td><input type="text" class="full-input" v-model.trim="item.value" /></td> | |
70 | + <td> | |
71 | + <button v-if="!item.required" class="cbtnDelete" @click="fnDelRow('header', item)"> | |
72 | + <img src="../../../resources/img/delete.png" alt="삭제" /> | |
73 | + </button> | |
74 | + </td> | |
75 | + </tr> | |
76 | + </tbody> | |
77 | + </table> | |
78 | + </div> | |
79 | + </div> | |
80 | + <div class="flex50 pr0"> | |
81 | + <div class="content-titleZone flex justify-between align-center mt20"> | |
82 | + <p class="box-title">바디 정보</p> | |
83 | + <div> | |
84 | + <button class="blue-border-btn small-btn" @click="fnAddRow('body')">바디 정보 추가</button> | |
85 | + </div> | |
86 | + </div> | |
87 | + <div style="height: 300px; overflow: auto"> | |
88 | + <table class="list-table" style="table-layout: fixed"> | |
89 | + <colgroup> | |
90 | + <col width="10%" /> | |
91 | + <col width="30%" /> | |
92 | + <col width="50%" /> | |
93 | + <col width="10%" /> | |
94 | + </colgroup> | |
95 | + <thead> | |
96 | + <tr> | |
97 | + <th style="text-align: center">No</th> | |
98 | + <th style="text-align: center">키</th> | |
99 | + <th style="text-align: center">값</th> | |
100 | + <th style="text-align: center">비고</th> | |
101 | + </tr> | |
102 | + </thead> | |
103 | + <tbody> | |
104 | + <tr v-for="(item, idx) in job.bodys" :key="idx"> | |
105 | + <td style="text-align: center">{{ idx + 1 }}</td> | |
106 | + <td><input type="text" class="full-input" v-model.trim="item.key" /></td> | |
107 | + <td><input type="text" class="full-input" v-model.trim="item.value" /></td> | |
108 | + <td> | |
109 | + <button class="cbtnDelete" @click="fnDelRow('body', item)"> | |
110 | + <img src="../../../resources/img/delete.png" alt="삭제" /> | |
111 | + </button> | |
112 | + </td> | |
113 | + </tr> | |
114 | + </tbody> | |
115 | + </table> | |
116 | + </div> | |
117 | + </div> | |
118 | + </div> | |
119 | + </div> | |
120 | + <div v-if="modalView == 'preview'"> | |
121 | + <div class="content-titleZone flex justify-between align-center mt20"> | |
122 | + <p class="box-title">데이터셋 <small>(총 ROW: {{ rowData.length > 1 ? rowData.length : 0 }}개)</small> | |
123 | + </p> | |
124 | + </div> | |
125 | + <div style="height: 400px; overflow: auto"> | |
126 | + <table class="list-table table-flow"> | |
127 | + <template v-if="!$isEmpty(dataTable)"> | |
128 | + <thead> | |
129 | + <tr> | |
130 | + <th>No</th> | |
131 | + <th v-for="(rowKey, index) of rowKeys" :key="index">{{ rowKey }}</th> | |
132 | + </tr> | |
133 | + </thead> | |
134 | + <tbody> | |
135 | + <tr v-for="(items, indexI) of rowData" :key="indexI"> | |
136 | + <td>{{ indexI + 1 }}</td> | |
137 | + <td v-for="(item, indexJ) of items" :key="indexJ">{{ item }}</td> | |
138 | + </tr> | |
139 | + </tbody> | |
140 | + </template> | |
141 | + <tr v-else> | |
142 | + <td>등록된 데이터가 없습니다.</td> | |
143 | + </tr> | |
144 | + </table> | |
145 | + </div> | |
146 | + </div> | |
147 | + </div> | |
148 | + <div class="modal-end" style="text-align: right"> | |
149 | + <button type="button" :class="{ | |
150 | + 'small-btn': true, | |
151 | + 'blue-btn': modalView == 'default', | |
152 | + 'blue-border-btn': modalView == 'preview', | |
153 | + }" @click="fnPrevNextPage"> | |
154 | + <span v-if="modalView == 'preview'">이전</span> | |
155 | + <span v-if="modalView == 'default'">다음</span> | |
156 | + </button> | |
157 | + <button type="button" v-if="modalView == 'preview'" class="blue-btn small-btn" @click="fnSave">저장</button> | |
158 | + <button type="button" class="blue-border-btn small-btn" @click="$emit('fnCloseModal')">취소</button> | |
159 | + </div> | |
160 | + </div> | |
161 | + </div> | |
162 | +</template> | |
163 | +<script> | |
164 | +// IMPORTS | |
165 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
166 | +import { mdiClose } from "@mdi/js"; | |
167 | +import axios from "axios"; | |
168 | + | |
169 | +export default { | |
170 | + components: { | |
171 | + SvgIcon: SvgIcon, | |
172 | + }, | |
173 | + | |
174 | + props: { | |
175 | + openPopup: { | |
176 | + type: Boolean, | |
177 | + default: false, | |
178 | + }, | |
179 | + jobItem: { | |
180 | + type: Boolean, | |
181 | + default: null, | |
182 | + }, | |
183 | + }, | |
184 | + | |
185 | + data() { | |
186 | + return { | |
187 | + // ICON | |
188 | + closePath: mdiClose, | |
189 | + | |
190 | + // DEFAULT | |
191 | + isModalOpen: this.openPopup, | |
192 | + modalView: "default", | |
193 | + | |
194 | + dataTable: {}, | |
195 | + rowData: [], | |
196 | + rowKeys: [], | |
197 | + | |
198 | + job: Object.assign({}, this.$getDefaultJobGroup().connectionEhojo), | |
199 | + | |
200 | + // 기본값 | |
201 | + defaultHeaders: [ | |
202 | + { index: 1, key: "ifId", value: null, required: true }, | |
203 | + { index: 2, key: "tranId", value: null, required: true }, | |
204 | + { index: 3, key: "trnmtInstCd", value: null, required: true }, | |
205 | + { index: 4, key: "rcptnInstCd", value: null, required: true }, | |
206 | + { index: 5, key: "trnmtInstSysCd", value: null, required: true }, | |
207 | + { index: 6, key: "rcptnInstSysCd", value: null, required: true }, | |
208 | + { index: 7, key: "transfGramNo", value: null, required: false }, | |
209 | + { index: 8, key: "userDeptCode", value: null, required: false }, | |
210 | + { index: 9, key: "userName", value: null, required: false }, | |
211 | + ], | |
212 | + defaultBodys: [ | |
213 | + { index: 1, key: "curPage", value: 1, required: false }, | |
214 | + { index: 2, key: "pageSize", value: 3000, required: false }, | |
215 | + ], | |
216 | + }; | |
217 | + }, | |
218 | + | |
219 | + mounted() { | |
220 | + this.init(); | |
221 | + }, | |
222 | + | |
223 | + methods: { | |
224 | + // 초기화 function | |
225 | + init() { | |
226 | + this.modalView = "default"; | |
227 | + | |
228 | + if (this.jobItem.itm_id != null) { | |
229 | + this.job = this.jobItem.itm; | |
230 | + this.dataTable = this.jobItem.dataTable; | |
231 | + this.rowData = this.dataTable.rowData; | |
232 | + for (let columnData of this.dataTable.columnDatas) { | |
233 | + this.rowKeys.push(columnData.columnNm); | |
234 | + // displayColumnNm 초기값 설정 | |
235 | + columnData.displyColumnNm = columnData.orginlColumnNm; | |
236 | + } | |
237 | + } else { | |
238 | + this.dataTable = {}; | |
239 | + this.rowData = []; | |
240 | + this.rowKeys = []; | |
241 | + | |
242 | + // 헤더, 바디 기본값 설정 | |
243 | + this.job.headers = this.defaultHeaders; | |
244 | + this.job.bodys = this.defaultBodys; | |
245 | + } | |
246 | + }, | |
247 | + | |
248 | + // ##파라미터 | |
249 | + // 행 추가 | |
250 | + fnAddRow(type) { | |
251 | + if (type == 'header') { | |
252 | + this.job.headers.push({ | |
253 | + index: this.job.headers.length + 1, | |
254 | + key: null, | |
255 | + value: null, | |
256 | + required: false | |
257 | + }); | |
258 | + } else if (type == 'body') { | |
259 | + this.job.bodys.push({ | |
260 | + index: this.job.bodys.length + 1, | |
261 | + key: null, | |
262 | + value: null, | |
263 | + required: false | |
264 | + }); | |
265 | + } | |
266 | + }, | |
267 | + // 행 삭제 | |
268 | + fnDelRow(type, item) { | |
269 | + if (type == 'header') { | |
270 | + this.job.headers = this.job.headers.filter(parameter => parameter !== item); | |
271 | + } else if (type == 'body') { | |
272 | + this.job.bodys = this.job.bodys.filter(parameter => parameter !== item); | |
273 | + } | |
274 | + }, | |
275 | + | |
276 | + // 유효성 검사 | |
277 | + chkValidations() { | |
278 | + // 기본 정보 | |
279 | + if (this.$isEmpty(this.job.lafCode)) { | |
280 | + this.$showAlert("경고", "송신기관 코드를 입력하지 않았습니다. 송신기관 코드를 입력해 주세요."); | |
281 | + return false; | |
282 | + } | |
283 | + if (this.$isEmpty(this.job.taskCode)) { | |
284 | + this.$showAlert("경고", "업무구분 코드를 입력하지 않았습니다. 업무구분 코드를 입력해 주세요."); | |
285 | + return false; | |
286 | + } | |
287 | + if (this.$isEmpty(this.job.tableName)) { | |
288 | + this.$showAlert("경고", "테이블 명을 입력하지 않았습니다. 테이블 명을 입력해 주세요."); | |
289 | + return false; | |
290 | + } | |
291 | + if (this.$isEmpty(this.job.ifId)) { | |
292 | + this.$showAlert("경고", "인터페이스 ID를 입력하지 않았습니다. 인터페이스 ID를 입력해 주세요."); | |
293 | + return false; | |
294 | + } | |
295 | + | |
296 | + // 헤더 정보 | |
297 | + for (let item of this.defaultHeaders) { | |
298 | + if (item.required) { | |
299 | + if (this.$isEmpty(item.value)) { | |
300 | + this.$showAlert("경고", "헤더 정보 중 필수 입력값을 입력하지 않았습니다. 필수 입력값을 입력해 주세요."); | |
301 | + return false; | |
302 | + } | |
303 | + } | |
304 | + } | |
305 | + | |
306 | + return true; | |
307 | + }, | |
308 | + | |
309 | + // ##버튼 | |
310 | + // 이전/다음 버튼 | |
311 | + fnPrevNextPage() { | |
312 | + if (this.modalView == "default") { | |
313 | + if (!this.chkValidations()) { return; } | |
314 | + this.fnRun(); | |
315 | + } else if (this.modalView == "preview") { | |
316 | + // defaultData 초기화 | |
317 | + this.job.ifId = null; | |
318 | + | |
319 | + this.dataTable = {}; | |
320 | + this.rowKeys = []; | |
321 | + this.rowData = []; | |
322 | + | |
323 | + // 모달창 변경 | |
324 | + this.modalView = "default"; | |
325 | + } | |
326 | + }, | |
327 | + // 조회 | |
328 | + fnRun() { | |
329 | + const vm = this; | |
330 | + // 실행 | |
331 | + axios({ | |
332 | + url: "/openfiscal.json", | |
333 | + method: "post", | |
334 | + headers: { "Content-Type": "application/json; charset=UTF-8" }, | |
335 | + data: vm.job, | |
336 | + }) | |
337 | + .then((response) => { | |
338 | + if (response.data.checkMessage.status == "0000") { | |
339 | + let result = response.data; | |
340 | + this.dataTable = result; | |
341 | + this.rowKeys = []; // 초기화 | |
342 | + for (let columnData of result.columnDatas) { | |
343 | + this.rowKeys.push(columnData.columnNm); | |
344 | + // displayColumnNm 초기값 설정 | |
345 | + columnData.displyColumnNm = columnData.orginlColumnNm; | |
346 | + } | |
347 | + this.rowData = result.rowData; | |
348 | + | |
349 | + // 모달창 변경 | |
350 | + this.modalView = "preview"; | |
351 | + } else { | |
352 | + this.$showAlert("알람", response.data.checkMessage.message); | |
353 | + } | |
354 | + }) | |
355 | + .catch((error) => { | |
356 | + vm.$showAlert("경고", "응답이 없습니다. 입력한 정보를 확인해 주세요."); | |
357 | + }); | |
358 | + }, | |
359 | + // 저장 버튼 | |
360 | + fnSave() { | |
361 | + // 데이터 세팅 | |
362 | + let jobItm = this.jobItem; | |
363 | + jobItm.itm = this.job; | |
364 | + jobItm.dataTable = this.dataTable; | |
365 | + | |
366 | + console.log("@@", jobItm); | |
367 | + // 실행 | |
368 | + this.$emit("fnSaveSetup", jobItm); | |
369 | + }, | |
370 | + } | |
371 | +} | |
372 | +</script> | |
373 | +<style scoped> | |
374 | +.cbtnDelete { | |
375 | + width: 18px; | |
376 | + height: 18px; | |
377 | +} | |
378 | + | |
379 | +.cbtnDelete img { | |
380 | + width: 100%; | |
381 | +} | |
382 | +</style>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/connection/itm/apiConnection.vue
... | ... | @@ -0,0 +1,441 @@ |
1 | +<template> | |
2 | + <div v-show="isModalOpen" class="modal-wrapper"> | |
3 | + <div class="modal-container large-modal"> | |
4 | + <div class="modal-title"> | |
5 | + <div class="flex justify-between align-center"> | |
6 | + <h2>API 연결</h2> | |
7 | + <button class="close-btn" @click="$emit('fnCloseModal')"> | |
8 | + <svg-icon type="mdi" :width="20" :height="20" :path="closePath"></svg-icon> | |
9 | + </button> | |
10 | + </div> | |
11 | + </div> | |
12 | + <div class="modal-content-monthly"> | |
13 | + <div v-if="modalView == 'default'"> | |
14 | + <div class="content-titleZone"> | |
15 | + <p class="box-title">API 기본정보</p> | |
16 | + </div> | |
17 | + <table class="form-table"> | |
18 | + <colgroup> | |
19 | + <col style="width: 15%" /> | |
20 | + <col style="width: 85%" /> | |
21 | + </colgroup> | |
22 | + <tbody> | |
23 | + <tr> | |
24 | + <th>API URL</th> | |
25 | + <td> | |
26 | + <input type="text" class="full-input" v-model="apiUrl" @change="fnApiUrlChange" /> | |
27 | + </td> | |
28 | + </tr> | |
29 | + <tr> | |
30 | + <th>결과타입</th> | |
31 | + <td> | |
32 | + <div class="flex"> | |
33 | + <div class="flex15 pl0"> | |
34 | + <input type="radio" name="json" id="json" value="1" class="chekck-type" v-model="jobItm.itm.type" /> | |
35 | + <label for="json" class="chekcktype-label text-ct"> json </label> | |
36 | + </div> | |
37 | + <div class="flex15"> | |
38 | + <input type="radio" name="xml" id="xml" value="2" class="chekck-type" v-model="jobItm.itm.type" /> | |
39 | + <label for="xml" class="chekcktype-label text-ct"> xml </label> | |
40 | + </div> | |
41 | + <div class="flex15 pr0"> | |
42 | + <input type="radio" name="seol" id="seol" value="3" class="chekck-type" v-model="jobItm.itm.type" /> | |
43 | + <label for="seol" class="chekcktype-label text-ct"> seol </label> | |
44 | + </div> | |
45 | + </div> | |
46 | + </td> | |
47 | + </tr> | |
48 | + </tbody> | |
49 | + </table> | |
50 | + <div class="content-titleZone flex justify-between align-center mt20"> | |
51 | + <p class="box-title">파라미터 목록</p> | |
52 | + <div> | |
53 | + <button class="blue-border-btn small-btn" @click="fnAddRow()"> 파라미터 추가 </button> | |
54 | + <button v-if="apiUrl" class="blue-btn small-btn" @click="fnAutoCreate()"> 자동 생성 </button> | |
55 | + </div> | |
56 | + </div> | |
57 | + <div style="height: 400px; overflow: auto"> | |
58 | + <table class="list-table" style="table-layout: fixed"> | |
59 | + <colgroup> | |
60 | + <col width="10%" /> | |
61 | + <col width="30%" /> | |
62 | + <col width="50%" /> | |
63 | + <col width="10%" /> | |
64 | + </colgroup> | |
65 | + <thead> | |
66 | + <tr> | |
67 | + <th style="text-align: center">No</th> | |
68 | + <th style="text-align: center">Key</th> | |
69 | + <th style="text-align: center">Value</th> | |
70 | + <th style="text-align: center">삭제</th> | |
71 | + </tr> | |
72 | + </thead> | |
73 | + <tbody> | |
74 | + <template v-if="parameterList.length > 0"> | |
75 | + <tr v-for="(item, index) in parameterList" :key="index"> | |
76 | + <td style="text-align: center">{{ index + 1 }}</td> | |
77 | + <td> | |
78 | + <input type="text" class="full-input" v-model.trim="item.key" /> | |
79 | + </td> | |
80 | + <td> | |
81 | + <input type="text" class="full-input" v-model.trim="item.value" /> | |
82 | + </td> | |
83 | + <td> | |
84 | + <button class="cbtnDelete" @click="fnDeleteRow(item)"> | |
85 | + <img src="../../../../resources/img/delete.png" alt="삭제" /> | |
86 | + </button> | |
87 | + </td> | |
88 | + </tr> | |
89 | + </template> | |
90 | + <tr v-else> | |
91 | + <td colspan="4">등록된 데이터가 없습니다.</td> | |
92 | + </tr> | |
93 | + </tbody> | |
94 | + </table> | |
95 | + </div> | |
96 | + </div> | |
97 | + <div v-if="modalView == 'preview'"> | |
98 | + <div class="content-titleZone flex justify-between align-center"> | |
99 | + <p class="box-title">미리보기</p> | |
100 | + <button type="button" class="blue-border-btn small-btn" @click="fnDataKeySelect"> 조회 </button> | |
101 | + </div> | |
102 | + <table class="form-table"> | |
103 | + <colgroup> | |
104 | + <col style="width: 15%" /> | |
105 | + <col style="width: 85%" /> | |
106 | + </colgroup> | |
107 | + <tbody> | |
108 | + <tr> | |
109 | + <th>사용할 데이터</th> | |
110 | + <td> | |
111 | + <select v-model="selectKey"> | |
112 | + <template v-if="apiResponseKeys.length > 0"> | |
113 | + <option value="">사용할 데이터를 선택하세요.</option> | |
114 | + <option v-for="(key, index) of apiResponseKeys" :key="index" :value="key"> {{ key }} </option> | |
115 | + </template> | |
116 | + <option v-else value="">전체 조회</option> | |
117 | + </select> | |
118 | + </td> | |
119 | + </tr> | |
120 | + </tbody> | |
121 | + </table> | |
122 | + <div class="content-titleZone flex justify-between align-center mt20"> | |
123 | + <p class="box-title"> 데이터셋 <small> (총 ROW: {{ rowData.length > 1 ? rowData.length : 0 }}개) </small> | |
124 | + </p> | |
125 | + </div> | |
126 | + <table class="list-table table-flow"> | |
127 | + <template v-if="!$isEmpty(dataTable)"> | |
128 | + <thead> | |
129 | + <tr> | |
130 | + <th>No</th> | |
131 | + <th v-for="(rowKey, index) of rowKeys" :key="index"> {{ rowKey }} </th> | |
132 | + </tr> | |
133 | + </thead> | |
134 | + <tbody> | |
135 | + <tr v-for="(items, indexI) of rowData" :key="indexI"> | |
136 | + <td>{{ indexI + 1 }}</td> | |
137 | + <td v-for="(item, indexJ) of items" :key="indexJ"> {{ item }} </td> | |
138 | + </tr> | |
139 | + </tbody> | |
140 | + </template> | |
141 | + <tr v-else> | |
142 | + <td>등록된 데이터가 없습니다.</td> | |
143 | + </tr> | |
144 | + </table> | |
145 | + </div> | |
146 | + </div> | |
147 | + <div class="modal-end" style="text-align: right"> | |
148 | + <button type="button" :class="{ | |
149 | + 'small-btn': true, | |
150 | + 'blue-btn': modalView == 'default', | |
151 | + 'blue-border-btn': modalView == 'preview', | |
152 | + }" @click="fnPrevNextPage"> | |
153 | + <span v-if="modalView == 'preview'">이전</span> | |
154 | + <span v-if="modalView == 'default'">다음</span> | |
155 | + </button> | |
156 | + <button type="button" v-if="modalView == 'preview'" class="blue-btn small-btn" @click="fnSave"> 저장 </button> | |
157 | + <button type="button" class="blue-border-btn small-btn" @click="$emit('fnCloseModal')"> 취소 </button> | |
158 | + </div> | |
159 | + </div> | |
160 | + </div> | |
161 | +</template> | |
162 | +<script> | |
163 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
164 | +import { mdiClose } from "@mdi/js"; | |
165 | +import ApiRusultCus from "./apiRusultCus.vue"; | |
166 | +import axios from "axios"; | |
167 | + | |
168 | +export default { | |
169 | + components: { | |
170 | + SvgIcon: SvgIcon, | |
171 | + ApiRusultCus: ApiRusultCus, | |
172 | + }, | |
173 | + props: { | |
174 | + openPopup: { | |
175 | + type: Boolean, | |
176 | + default: false, | |
177 | + }, | |
178 | + jobItem: { | |
179 | + type: Boolean, | |
180 | + default: null, | |
181 | + }, | |
182 | + }, | |
183 | + data() { | |
184 | + return { | |
185 | + isModalOpen: this.openPopup, | |
186 | + closePath: mdiClose, | |
187 | + jobItm: this.jobItem, | |
188 | + | |
189 | + modalView: "default", | |
190 | + | |
191 | + apiUrl: null, | |
192 | + parameterList: [], | |
193 | + | |
194 | + apiResponse: null, | |
195 | + apiResponseKeys: [], | |
196 | + selectKey: "", | |
197 | + | |
198 | + dataTable: {}, | |
199 | + | |
200 | + rowData: [], | |
201 | + rowKeys: [], | |
202 | + }; | |
203 | + }, | |
204 | + mounted() { | |
205 | + this.init(); | |
206 | + }, | |
207 | + methods: { | |
208 | + // 초기화 function | |
209 | + async init() { | |
210 | + this.modalView = "default"; | |
211 | + this.apiUrl = null; | |
212 | + this.parameterList = []; | |
213 | + this.apiResponseKeys = []; | |
214 | + this.selectKey = ""; | |
215 | + this.dataTable = {}; | |
216 | + this.rowData = []; | |
217 | + this.rowKeys = []; | |
218 | + | |
219 | + if (!this.$isEmpty(this.jobItm)) { | |
220 | + let item = this.jobItm.itm; | |
221 | + if (item.url != null && item.url != "") { | |
222 | + if (item.url.indexOf("?") != -1) { | |
223 | + item.url.substring(0, item.url.indexOf("?")); | |
224 | + } | |
225 | + let params = ""; | |
226 | + for (let param of item.params) { | |
227 | + if (params != "") { | |
228 | + params += "&"; | |
229 | + } | |
230 | + params += param.key + "=" + param.value; | |
231 | + } | |
232 | + this.apiUrl = item.url + "?" + params; | |
233 | + | |
234 | + for (let param of item.params) { | |
235 | + this.parameterList.push({ | |
236 | + index: param.index, | |
237 | + key: param.key, | |
238 | + value: param.value, | |
239 | + addMonth: 0, | |
240 | + }); | |
241 | + } | |
242 | + } | |
243 | + } else { | |
244 | + this.jobItm = Object.assign({}, this.$getDefaultJobGroup().node); | |
245 | + this.jobItm.itm = Object.assign( | |
246 | + {}, | |
247 | + this.$getDefaultJobGroup().connectionApi | |
248 | + ); | |
249 | + } | |
250 | + }, | |
251 | + | |
252 | + // API URL 변경 | |
253 | + fnApiUrlChange() { | |
254 | + this.parameterList = []; // 파라미터 목록 초기화 | |
255 | + | |
256 | + // 미리보기 페이지 데이터 초기화 | |
257 | + this.selectKey = ""; | |
258 | + this.dataTable = {}; | |
259 | + this.rowData = []; | |
260 | + this.rowKeys = []; | |
261 | + }, | |
262 | + | |
263 | + // 파라미터 행 추가 | |
264 | + fnAddRow() { | |
265 | + this.parameterList.push({ | |
266 | + index: this.parameterList.length + 1, | |
267 | + key: null, | |
268 | + value: null, | |
269 | + addMonth: 0, | |
270 | + }); | |
271 | + }, | |
272 | + // 파라미터 행 삭제 | |
273 | + fnDeleteRow(item) { | |
274 | + this.parameterList = this.parameterList.filter( | |
275 | + (parameter) => parameter !== item | |
276 | + ); | |
277 | + }, | |
278 | + | |
279 | + // 자동 생성 버튼 | |
280 | + async fnAutoCreate() { | |
281 | + // 덮어쓰기 경고 | |
282 | + if (this.parameterList.length > 0) { | |
283 | + let isChecked = await this.$showConfirm( | |
284 | + "경고", | |
285 | + "자동생성 실행 시, 기존에 작성한 파라미터가 삭제됩니다. 그래도 실행하시겠습니까?" | |
286 | + ); | |
287 | + if (!isChecked) { | |
288 | + return; | |
289 | + } else { | |
290 | + this.parameterList = []; // 파라미터 목록 초기화 | |
291 | + } | |
292 | + } | |
293 | + | |
294 | + // 실행 | |
295 | + let apiUrl = this.apiUrl; | |
296 | + if (apiUrl) { | |
297 | + let parameters = apiUrl.substring(apiUrl.indexOf("?") + 1); | |
298 | + if (parameters.length > 1) { | |
299 | + let params = parameters.split("&"); | |
300 | + for (let param of params) { | |
301 | + let paramIndex = param.indexOf("="); | |
302 | + this.parameterList.push({ | |
303 | + index: this.parameterList.length + 1, | |
304 | + key: param.substring(0, paramIndex), | |
305 | + value: param.substring(paramIndex + 1), | |
306 | + addMonth: 0, | |
307 | + }); | |
308 | + } | |
309 | + } | |
310 | + } | |
311 | + }, | |
312 | + // 셀 크기 | |
313 | + fnColWidth(keys) { | |
314 | + return `width: ${100 / (keys + 1)}%`; | |
315 | + }, | |
316 | + | |
317 | + // 이전/다음 버튼 | |
318 | + fnPrevNextPage() { | |
319 | + if (this.modalView == "default") { | |
320 | + // 데이터 조회 | |
321 | + this.apiRequestTest(); | |
322 | + } else if (this.modalView == "preview") { | |
323 | + // 데이터 초기화 | |
324 | + this.apiResponseKeys = []; | |
325 | + this.dataTable = {}; | |
326 | + this.rowKeys = []; | |
327 | + this.rowData = []; | |
328 | + | |
329 | + // 모달창 변경 | |
330 | + this.modalView = "default"; | |
331 | + } | |
332 | + }, | |
333 | + // 다음 버튼 | |
334 | + apiRequestTest() { | |
335 | + const vm = this; | |
336 | + if (vm.parameterList.length < 1) { | |
337 | + vm.$showAlert( | |
338 | + "경고", | |
339 | + "파라미터가 비어있습니다. 파라미터를 입력해 주세요." | |
340 | + ); | |
341 | + return; | |
342 | + } | |
343 | + // 데이터 세팅 | |
344 | + vm.jobItm.itm.url = vm.apiUrl.substring(0, vm.apiUrl.indexOf("?")); | |
345 | + vm.jobItm.itm.params = vm.parameterList; | |
346 | + // 실행 | |
347 | + axios({ | |
348 | + url: "/apiRequestTest.json", | |
349 | + method: "post", | |
350 | + headers: { | |
351 | + "Content-Type": "application/json; charset=UTF-8", | |
352 | + }, | |
353 | + data: vm.jobItm.itm, | |
354 | + }) | |
355 | + .then((response) => { | |
356 | + vm.apiResponseKeys = response.data.key; | |
357 | + vm.modalView = "preview"; // 모달창 변경 | |
358 | + }) | |
359 | + .catch((error) => { | |
360 | + vm.$showAlert( | |
361 | + "결과내용", | |
362 | + "응답이 없습니다. 파라미터를 확인해 주세요." | |
363 | + ); | |
364 | + }); | |
365 | + }, | |
366 | + getType(target) { | |
367 | + return Object.prototype.toString.call(target).slice(8, -1); | |
368 | + }, | |
369 | + // 조회 버튼 | |
370 | + fnDataKeySelect() { | |
371 | + if (this.apiResponseKeys.length > 0 && (this.selectKey == null || this.selectKey == "")) { | |
372 | + this.$showAlert("경고", "사용할 데이터를 선택해 주세요."); | |
373 | + return; | |
374 | + } | |
375 | + this.jobItm.itm.depth = this.selectKey; | |
376 | + | |
377 | + // 데이터 세팅 | |
378 | + this.jobItm.itm.url = this.apiUrl.substring(0, this.apiUrl.indexOf("?")); | |
379 | + | |
380 | + // 실행 | |
381 | + axios({ | |
382 | + url: "/getApiDataTable.json", | |
383 | + method: "post", | |
384 | + headers: { | |
385 | + "Content-Type": "application/json; charset=UTF-8", | |
386 | + }, | |
387 | + data: this.jobItm.itm, | |
388 | + }) | |
389 | + .then((response) => { | |
390 | + let result = response.data; | |
391 | + this.dataTable = result; | |
392 | + this.rowKeys = []; // 초기화 | |
393 | + for (let columnData of result.columnDatas) { | |
394 | + this.rowKeys.push(columnData.columnNm); | |
395 | + // displayColumnNm 초기값 설정 | |
396 | + columnData.displyColumnNm = columnData.orginlColumnNm; | |
397 | + } | |
398 | + this.rowData = result.rowData; | |
399 | + }) | |
400 | + .catch((error) => { | |
401 | + this.$showAlert( | |
402 | + "결과내용", | |
403 | + "응답이 없습니다. 데이터를 확인해 주세요." | |
404 | + ); | |
405 | + }); | |
406 | + }, | |
407 | + // 저장 버튼 | |
408 | + fnSave() { | |
409 | + // 유효성 검사 | |
410 | + if (this.$isEmpty(this.apiUrl)) { | |
411 | + this.$showAlert("경고", "API URL을 입력해 주세요."); | |
412 | + return false; | |
413 | + } | |
414 | + if (this.$isEmpty(this.dataTable)) { | |
415 | + this.$showAlert("경고", "사용할 데이터를 조회해 주세요."); | |
416 | + return false; | |
417 | + } | |
418 | + // 데이터 세팅 | |
419 | + this.jobItm.dataTable = this.dataTable; | |
420 | + // 실행 | |
421 | + let jobItem = this.jobItm; | |
422 | + this.$emit("fnSaveSetup", jobItem); | |
423 | + }, | |
424 | + }, | |
425 | +}; | |
426 | +</script> | |
427 | +<style scoped> | |
428 | +.cbtnDelete { | |
429 | + width: 18px; | |
430 | + height: 18px; | |
431 | +} | |
432 | + | |
433 | +.cbtnDelete img { | |
434 | + width: 100%; | |
435 | +} | |
436 | + | |
437 | +.table-flow { | |
438 | + width: 100%; | |
439 | + word-wrap: break-word; | |
440 | +} | |
441 | +</style>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/connection/itm/apiRusultCus.vue
... | ... | @@ -0,0 +1,19 @@ |
1 | +<template> | |
2 | + <div v-for="(item, index) in apiResponseResult" :key="index"> | |
3 | + <p> | |
4 | + <span @click="$emit('fnJobItmDepth', item.key)">{{ item.key }}</span> | |
5 | + <span>{{ item.value }}</span> | |
6 | + </p> | |
7 | + </div> | |
8 | +</template> | |
9 | + | |
10 | +<script> | |
11 | +export default { | |
12 | + props: { | |
13 | + apiResponseResult: { | |
14 | + type: Object, | |
15 | + default: null, | |
16 | + }, | |
17 | + }, | |
18 | +}; | |
19 | +</script> |
+++ client/views/component/connection/itm/dataCheck.vue
... | ... | @@ -0,0 +1,146 @@ |
1 | +<template> | |
2 | + <div v-show="isModalOpen" class="modal-wrapper"> | |
3 | + <div class="modal-container"> | |
4 | + <div class="modal-title"> | |
5 | + <div class="flex justify-between align-center"> | |
6 | + <h2>센서 알람 설정</h2> | |
7 | + <button class="close-btn" @click="closeModal"><svg-icon type="mdi" :width="20" :height="20" :path="closePath"></svg-icon></button> | |
8 | + </div> | |
9 | + </div> | |
10 | + <div class="modal-content-monthly"> | |
11 | + <div class="flex justify-between align-center"> | |
12 | + <div class="count-zone"> | |
13 | + <p>총 <span>{{this.jobItem.itm.dataCheckItems.length}}</span>건 중 <span>{{selectCount}}</span>건 선택</p> | |
14 | + </div> | |
15 | + <div class="cunt-selectZone"> | |
16 | + <p>중복알람 주기설정</p> | |
17 | + <select v-model="jobItm.itm_option_string"> | |
18 | + <option value="1">1분</option> | |
19 | + <option value="5">5분</option> | |
20 | + </select> | |
21 | + </div> | |
22 | + </div> | |
23 | + <div class="table-zone"> | |
24 | + <table class="list-table"> | |
25 | + <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 --> | |
26 | + <colgroup> | |
27 | + <col style="width: 22.5%;"> | |
28 | + <col style="width: 22.5%;"> | |
29 | + <col style="width: 22.5%;"> | |
30 | + <col style="width: 22.5%;"> | |
31 | + <col style="width: 10%;"> | |
32 | + </colgroup> | |
33 | + <thead> | |
34 | + <tr> | |
35 | + <th>컬럼명</th> | |
36 | + <th>한글명</th> | |
37 | + <th>MIN</th> | |
38 | + <th>MAX</th> | |
39 | + <th>사용여부</th> | |
40 | + </tr> | |
41 | + </thead> | |
42 | + <tbody> | |
43 | + <tr v-for="(item, indx) in jobItem.itm.dataCheckItems" :key="indx"> | |
44 | + <td>{{item.column_display}}</td> | |
45 | + <td><input type="text" class="full-input" v-model="item.column_origin"/></td> | |
46 | + <td><input type="text" class="full-input" v-model="item.min_value"/></td> | |
47 | + <td><input type="text" class="full-input" v-model="item.max_value"/></td> | |
48 | + <td><input type="checkbox" v-model="item.use_at"></td> | |
49 | + </tr> | |
50 | + </tbody> | |
51 | + </table> | |
52 | + </div> | |
53 | + </div> | |
54 | + <div class="modal-end flex justify-end"> | |
55 | + <button class="orange-btn small-btn" @click="closeModal">닫기</button> | |
56 | + <button class="blue-btn small-btn" @click="closeModal">확인</button> | |
57 | + </div> | |
58 | + </div> | |
59 | + </div> | |
60 | +</template> | |
61 | + | |
62 | +<script> | |
63 | +import SvgIcon from '@jamescoyle/vue-icon'; | |
64 | +import { mdiClose } from '@mdi/js'; | |
65 | + | |
66 | +export default { | |
67 | + name: 'data-check', | |
68 | + props: { | |
69 | + openPopup: { | |
70 | + type: Boolean, | |
71 | + default: false | |
72 | + }, | |
73 | + jobItem: { | |
74 | + type: Boolean, | |
75 | + default: null | |
76 | + } | |
77 | + }, | |
78 | + data() { | |
79 | + return { | |
80 | + isModalOpen: this.openPopup | |
81 | + , jobItm: this.jobItem | |
82 | + , closePath:mdiClose | |
83 | + } | |
84 | + }, | |
85 | + methods: { | |
86 | + // 모달 닫기 | |
87 | + closeModal: function () { | |
88 | + this.isModalOpen = false; | |
89 | + this.$emit("closePopup", this.jobItm.indx); | |
90 | + }, | |
91 | + | |
92 | + dataSetting : function(){ | |
93 | + if(this.jobItm.itm.dataCheckItems.length == 0 ){ | |
94 | + for(let i = 0 ; i < this.jobItm.front_dataTable.columnDatas.length ; i++){ | |
95 | + if(this.jobItm.front_dataTable.columnDatas[i].dataTy == 'INT' || | |
96 | + this.jobItm.front_dataTable.columnDatas[i].dataTy == 'LONG' || | |
97 | + this.jobItm.front_dataTable.columnDatas[i].dataTy == 'FLOAT' || | |
98 | + this.jobItm.front_dataTable.columnDatas[i].dataTy == 'DOUBLE'){ | |
99 | + let DataCheckItem = {}; | |
100 | + DataCheckItem.order = i; | |
101 | + DataCheckItem.column_origin = this.jobItm.front_dataTable.columnDatas[i].columnNm; | |
102 | + DataCheckItem.column_display = this.jobItm.front_dataTable.columnDatas[i].displyColumnNm; | |
103 | + DataCheckItem.min_value = 0.0; | |
104 | + DataCheckItem.max_value = 1.0; | |
105 | + DataCheckItem.use_at = true; | |
106 | + this.jobItem.itm.dataCheckItems.push(DataCheckItem) | |
107 | + } | |
108 | + } | |
109 | + } | |
110 | + }, | |
111 | + | |
112 | + resetData : function(){ | |
113 | + this.jobItm.front_dataTable.columnDatas = []; | |
114 | + this.dataSetting(); | |
115 | + } | |
116 | + }, | |
117 | + watch: { | |
118 | + openPopup: function (v) { | |
119 | + this.isModalOpen = v; | |
120 | + }, | |
121 | + | |
122 | + jobItem: function (v) { | |
123 | + this.jobItm = v; | |
124 | + this.dataSetting(); | |
125 | + }, | |
126 | + }, | |
127 | + computed: { | |
128 | + selectCount: function () { | |
129 | + let count = 0; | |
130 | + for(let i = 0 ; i < this.jobItem.itm.dataCheckItems.length ; i++){ | |
131 | + if(this.jobItem.itm.dataCheckItems[i].use_at){ | |
132 | + count++; | |
133 | + } | |
134 | + } | |
135 | + return count; | |
136 | + } | |
137 | + }, | |
138 | + components: { | |
139 | + 'SvgIcon':SvgIcon | |
140 | + }, | |
141 | + mounted() { | |
142 | + this.dataSetting(); | |
143 | + | |
144 | + } | |
145 | +} | |
146 | +</script>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/connection/itm/dataFilter.vue
... | ... | @@ -0,0 +1,263 @@ |
1 | +<template> | |
2 | + <div v-show="isModalOpen" class="modal-wrapper"> | |
3 | + <div class="modal-container large-modal"> | |
4 | + <div class="modal-title"> | |
5 | + <div class="flex justify-between align-center"> | |
6 | + <h2>데이터 필터</h2> | |
7 | + <button type="button" class="close-btn" @click="$emit('fnCloseModal')"> | |
8 | + <svg-icon type="mdi" :width="20" :height="20" :path="closePath"></svg-icon> | |
9 | + </button> | |
10 | + </div> | |
11 | + </div> | |
12 | + <div class="flex modal-content-monthly"> | |
13 | + <div class="flex50 pl0"> | |
14 | + <div class="flex justify-between"> | |
15 | + <div class="input-container flex"> | |
16 | + <label class="radio-label mr5"> | |
17 | + <input type="radio" :name="comId + 'match'" class="custom-radiobox" :value="true" v-model="jobItm.itm.match_type" /> | |
18 | + <span>AND 연산</span> | |
19 | + </label> | |
20 | + <label class="radio-label"> | |
21 | + <input type="radio" :name="comId + 'match'" class="custom-radiobox" :value="false" v-model="jobItm.itm.match_type" /> | |
22 | + <span>OR 연산</span> | |
23 | + </label> | |
24 | + </div> | |
25 | + <button class="blue-btn small-btn" @click="fnAddFiter"> 필터 추가 </button> | |
26 | + </div> | |
27 | + <div class="table-zone"> | |
28 | + <table class="form-table"> | |
29 | + <thead> | |
30 | + <tr> | |
31 | + <th>컬럼</th> | |
32 | + <th>연산</th> | |
33 | + <th>내용</th> | |
34 | + <th>관리</th> | |
35 | + </tr> | |
36 | + </thead> | |
37 | + <tbody> | |
38 | + <tr v-for="(filter, index) in filterList" :key="index" class="text-ct"> | |
39 | + <td> | |
40 | + <select class="full-input" v-model="filter.coulmn_nm" @change="fnChangeColumn(index)"> | |
41 | + <option v-for="(itemCol, j) in itemColList" :key="j" :value="itemCol.orginlColumnNm"> {{ itemCol.displyColumnNm }} </option> | |
42 | + </select> | |
43 | + </td> | |
44 | + <td> | |
45 | + <select class="full-input" v-model="filter.calc_ty"> | |
46 | + <option value="">선택해주세요</option> | |
47 | + <option value="1">=</option> | |
48 | + <option v-if="filter.data_ty != 'STRING'" value="2"> < </option> | |
49 | + <option v-if="filter.data_ty != 'STRING'" value="3"> > </option> | |
50 | + <option v-if="filter.data_ty != 'STRING'" value="4"> <= </option> | |
51 | + <option v-if="filter.data_ty != 'STRING'" value="5"> >= </option> | |
52 | + <option value="6">!=</option> | |
53 | + <option v-if="filter.data_ty == 'STRING'" value="7"> 포함 </option> | |
54 | + </select> | |
55 | + </td> | |
56 | + <td> | |
57 | + <input type="text" class="full-input" v-model="filter.cmpr_value" @input="fnChangeValue($event, index)" /> | |
58 | + </td> | |
59 | + <td> | |
60 | + <button class="blue-border-btn small-btn" @click="fnDeleteFilter(index)"> 삭제 </button> | |
61 | + </td> | |
62 | + </tr> | |
63 | + </tbody> | |
64 | + </table> | |
65 | + </div> | |
66 | + </div> | |
67 | + <div class="flex50 pr0"> | |
68 | + <div class="content-titleZone flex justify-between align-center"> | |
69 | + <p class="box-title">미리보기</p> | |
70 | + <button class="blue-border-btn small-btn" @click="fnRun"> 조회 </button> | |
71 | + </div> | |
72 | + <table class="list-table" v-if="!$isEmpty(resultJobItm.dataTable)"> | |
73 | + <thead> | |
74 | + <tr> | |
75 | + <th style="min-width: 60px">No</th> | |
76 | + <th v-for="(columnData, index) of resultJobItm.dataTable | |
77 | + .columnDatas" :key="index"> {{ columnData.orginlColumnNm }} </th> | |
78 | + </tr> | |
79 | + </thead> | |
80 | + <tbody> | |
81 | + <tr v-for="(row, i) of resultJobItm.dataTable.rowData" :key="i"> | |
82 | + <td style="min-width: 60px">{{ i + 1 }}</td> | |
83 | + <td v-for="(rowKey, j) of row" :key="j"> {{ rowKey }} </td> | |
84 | + </tr> | |
85 | + </tbody> | |
86 | + </table> | |
87 | + <table class="list-table" v-else> | |
88 | + <tr> | |
89 | + <td>등록된 데이터가 없습니다.</td> | |
90 | + </tr> | |
91 | + </table> | |
92 | + </div> | |
93 | + </div> | |
94 | + <div class="modal-end flex justify-end"> | |
95 | + <button type="button" class="blue-btn small-btn" @click="fnSave"> 저장 </button> | |
96 | + <button type="button" class="blue-border-btn small-btn" @click="$emit('fnCloseModal')"> 닫기 </button> | |
97 | + </div> | |
98 | + </div> | |
99 | + </div> | |
100 | +</template> | |
101 | +<script> | |
102 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
103 | +import { mdiClose } from "@mdi/js"; | |
104 | +import axios from "axios"; | |
105 | + | |
106 | +export default { | |
107 | + components: { | |
108 | + SvgIcon: SvgIcon, | |
109 | + }, | |
110 | + props: { | |
111 | + openPopup: { | |
112 | + type: Boolean, | |
113 | + default: false, | |
114 | + }, | |
115 | + jobItem: { | |
116 | + type: Object, | |
117 | + }, | |
118 | + nodes: { | |
119 | + type: Array, | |
120 | + required: true, | |
121 | + }, | |
122 | + edges: { | |
123 | + type: Array, | |
124 | + required: true, | |
125 | + }, | |
126 | + }, | |
127 | + data() { | |
128 | + return { | |
129 | + isModalOpen: this.openPopup, | |
130 | + closePath: mdiClose, | |
131 | + jobItm: this.jobItem, | |
132 | + nodeList: this.nodes, | |
133 | + edgeList: this.edges, | |
134 | + | |
135 | + comId: | |
136 | + "comId_" + | |
137 | + Math.random().toString(36).substring(2, 15) + | |
138 | + Math.random().toString(36).substring(2, 15), | |
139 | + | |
140 | + prevNode: {}, | |
141 | + filterList: [], | |
142 | + | |
143 | + resultJobItm: {}, | |
144 | + }; | |
145 | + }, | |
146 | + mounted() { | |
147 | + // 첫 실행시 | |
148 | + this.fnFindPrevNode(); // 이전 노드 정보 가져오기 | |
149 | + if (this.jobItm.itm.hasOwnProperty("filterItems")) { | |
150 | + if (this.jobItm.itm.filterItems.length > 0) { | |
151 | + this.filterList = this.jobItm.itm.filterItems; | |
152 | + this.fnRun(); | |
153 | + } | |
154 | + } | |
155 | + }, | |
156 | + methods: { | |
157 | + // 이전 노드 정보 찾기 | |
158 | + fnFindPrevNode() { | |
159 | + // 1. 연결된 이전 노드 아이디 구하기 | |
160 | + loop: for (let edge of this.edgeList) { | |
161 | + if (edge.target == this.jobItm.id) { | |
162 | + // 2. 노드 아이디(edge.source)로 노드 구하기 | |
163 | + for (let node of this.nodeList) { | |
164 | + if (node.id == edge.source) { | |
165 | + this.prevNode = node; | |
166 | + // 3-1. 이전 노드의 설정을 마친 경우 | |
167 | + if (!this.$isEmpty(this.prevNode.dataTable)) { | |
168 | + this.itemColList = this.prevNode.dataTable.columnDatas; | |
169 | + } | |
170 | + // 3-2. 예외 | |
171 | + else { | |
172 | + this.$showAlert( | |
173 | + "에러 발생", | |
174 | + "DATA_FILTER에 연결된 노드의 설정을 마쳐 주세요." | |
175 | + ); | |
176 | + this.$emit("fnCloseModal"); // 모달 닫기 | |
177 | + } | |
178 | + break loop; | |
179 | + } | |
180 | + } | |
181 | + } | |
182 | + } | |
183 | + // 3. 예외 | |
184 | + if (this.$isEmpty(this.prevNode)) { | |
185 | + this.$showAlert( | |
186 | + "에러 발생", | |
187 | + "DATA_FILTER를 설정하기 전, 먼저 노드를 연결해 주세요." | |
188 | + ); | |
189 | + this.$emit("fnCloseModal"); // 모달 닫기 | |
190 | + } | |
191 | + }, | |
192 | + | |
193 | + // 필터 추가 버튼 | |
194 | + fnAddFiter() { | |
195 | + let filter = { | |
196 | + coulmn_nm: null, | |
197 | + calc_ty: 1, | |
198 | + data_ty: null, | |
199 | + cmpr_value: null, | |
200 | + }; | |
201 | + this.filterList.push(filter); | |
202 | + }, | |
203 | + | |
204 | + /* 내용 변경 감지 */ | |
205 | + // 컬럼 | |
206 | + fnChangeColumn(index) { | |
207 | + this.filterList[index].calc_ty = ""; // 내용이 변경되면 연산 초기화 | |
208 | + this.filterList[index].cmpr_value = ""; // 내용이 변경되면 내용 초기화 | |
209 | + }, | |
210 | + // 내용 | |
211 | + fnChangeValue(event, index) { | |
212 | + let item = event.target.value; | |
213 | + let type = typeof item; | |
214 | + if (Number(type) != NaN) { | |
215 | + type = "number"; | |
216 | + } | |
217 | + this.filterList[index].data_ty = type.toUpperCase(); | |
218 | + this.filterList[index].calc_ty = ""; // 내용이 변경되면 연산 초기화 | |
219 | + }, | |
220 | + | |
221 | + // row 삭제 버튼 | |
222 | + fnDeleteFilter(index) { | |
223 | + this.filterList.splice(index, 1); | |
224 | + }, | |
225 | + | |
226 | + // 조회 버튼 | |
227 | + fnRun() { | |
228 | + // 데이터 세팅 | |
229 | + let data = this.jobItm; | |
230 | + data.itm.filterItems = this.filterList; | |
231 | + data.dataTable = this.prevNode.dataTable; | |
232 | + // 실행 | |
233 | + axios({ | |
234 | + url: "/diagram/runFilter.json", | |
235 | + method: "post", | |
236 | + headers: { "Content-Type": "application/json; charset=UTF-8" }, | |
237 | + data: data, | |
238 | + }) | |
239 | + .then((response) => { | |
240 | + this.resultJobItm = response.data; | |
241 | + }) | |
242 | + .catch((error) => { | |
243 | + this.$showAlert( | |
244 | + "에러 발생", | |
245 | + "에러가 발생했습니다. 관리자에게 문의해 주세요." | |
246 | + ); | |
247 | + }); | |
248 | + }, | |
249 | + | |
250 | + // 저장 버튼 | |
251 | + fnSave() { | |
252 | + if (this.$isEmpty(this.resultJobItm)) { | |
253 | + this.$showAlert( | |
254 | + "에러 발생", | |
255 | + "조회 결과가 없으면 저장할 수 없습니다. 조회 버튼을 눌러 주세요." | |
256 | + ); | |
257 | + return; | |
258 | + } | |
259 | + this.$emit("fnSaveSetup", this.resultJobItm); | |
260 | + }, | |
261 | + }, | |
262 | +}; | |
263 | +</script>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/connection/itm/dataSort.vue
... | ... | @@ -0,0 +1,150 @@ |
1 | +<template> | |
2 | + <div v-show="isModalOpen" class="modal-wrapper"> | |
3 | + <div class="modal-container" style="height: auto"> | |
4 | + <div class="modal-title"> | |
5 | + <div class="flex justify-between align-center"> | |
6 | + <h2>데이터 정렬</h2> | |
7 | + <button class="close-btn" @click="closeModal"> | |
8 | + <svg-icon | |
9 | + type="mdi" | |
10 | + :width="20" | |
11 | + :height="20" | |
12 | + :path="closePath" | |
13 | + ></svg-icon> | |
14 | + </button> | |
15 | + </div> | |
16 | + </div> | |
17 | + <div class="modal-content-monthly"> | |
18 | + <div class="table-zone"> | |
19 | + <table class="form-table"> | |
20 | + <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 --> | |
21 | + <colgroup> | |
22 | + <col style="width: 20%" /> | |
23 | + <col style="width: 80%" /> | |
24 | + </colgroup> | |
25 | + <tbody> | |
26 | + <tr> | |
27 | + <th>정렬기준</th> | |
28 | + <td> | |
29 | + <select | |
30 | + name="ctgry" | |
31 | + id="ctgry" | |
32 | + class="full-input" | |
33 | + style="width: 100%" | |
34 | + @change="dataInsert($event)" | |
35 | + > | |
36 | + <option | |
37 | + v-for="(temp, j) in item.front_dataTable.columnDatas" | |
38 | + :key="j" | |
39 | + :value="j" | |
40 | + > | |
41 | + {{ temp.orginlColumnNm }} | |
42 | + </option> | |
43 | + </select> | |
44 | + </td> | |
45 | + </tr> | |
46 | + <tr> | |
47 | + <th>정렬방법</th> | |
48 | + <td> | |
49 | + <div class="input-container flex"> | |
50 | + <label class="radio-label"> | |
51 | + <input | |
52 | + type="radio" | |
53 | + name="cmprValue" | |
54 | + class="custom-radiobox" | |
55 | + value="ASC" | |
56 | + v-model="item.itm.filterItems[0].cmpr_value" | |
57 | + /> | |
58 | + <span>오름차순</span> | |
59 | + </label> | |
60 | + <label class="radio-label"> | |
61 | + <input | |
62 | + type="radio" | |
63 | + name="cmprValue" | |
64 | + checked | |
65 | + class="custom-radiobox" | |
66 | + value="DESC" | |
67 | + v-model="item.itm.filterItems[0].cmpr_value" | |
68 | + /> | |
69 | + <span>내림차순</span> | |
70 | + </label> | |
71 | + </div> | |
72 | + </td> | |
73 | + </tr> | |
74 | + <tr> | |
75 | + <th>최대조회수</th> | |
76 | + <td> | |
77 | + <input | |
78 | + type="text" | |
79 | + class="full-input" | |
80 | + v-model="item.itm.filterItems[0].cmpr_value2" | |
81 | + /> | |
82 | + </td> | |
83 | + </tr> | |
84 | + </tbody> | |
85 | + </table> | |
86 | + </div> | |
87 | + </div> | |
88 | + <div class="modal-end flex justify-end"> | |
89 | + <button class="orange-btn small-btn" @click="closeModal">닫기</button> | |
90 | + <button class="blue-btn small-btn" @click="closeModal">확인</button> | |
91 | + </div> | |
92 | + </div> | |
93 | + </div> | |
94 | +</template> | |
95 | + | |
96 | +<script> | |
97 | +export default { | |
98 | + name: "data-check", | |
99 | + props: { | |
100 | + openPopup: { | |
101 | + type: Boolean, | |
102 | + default: false, | |
103 | + }, | |
104 | + item: { | |
105 | + type: Object, | |
106 | + default: null, | |
107 | + }, | |
108 | + }, | |
109 | + data() { | |
110 | + return { | |
111 | + isModalOpen: this.openPopup, | |
112 | + itm: this.item, | |
113 | + }; | |
114 | + }, | |
115 | + methods: { | |
116 | + // 모달 닫기 | |
117 | + closeModal: function () { | |
118 | + this.isModalOpen = false; | |
119 | + this.$emit("closePopup", this.item.indx); | |
120 | + }, | |
121 | + | |
122 | + dataInsert: function (event) { | |
123 | + this.item.itm.filterItems[0].coulmn_nm = | |
124 | + this.item.front_dataTable.columnDatas[ | |
125 | + event.target.value | |
126 | + ].orginlColumnNm; | |
127 | + this.item.itm.filterItems[0].data_ty = | |
128 | + this.item.front_dataTable.columnDatas[event.target.value].dataTy; | |
129 | + }, | |
130 | + }, | |
131 | + watch: { | |
132 | + openPopup: function (v) { | |
133 | + this.isModalOpen = v; | |
134 | + }, | |
135 | + | |
136 | + item: function (v) { | |
137 | + this.itm = v; | |
138 | + }, | |
139 | + }, | |
140 | + mounted() { | |
141 | + // 데이터 초기화 | |
142 | + if (this.item.itm.filterItems[0].coulmn_nm == null) { | |
143 | + this.item.itm.filterItems[0].coulmn_nm = | |
144 | + this.item.front_dataTable.columnDatas[0].orginlColumnNm; | |
145 | + this.item.itm.filterItems[0].data_ty = | |
146 | + this.item.front_dataTable.columnDatas[0].dataTy; | |
147 | + } | |
148 | + }, | |
149 | +}; | |
150 | +</script>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/connection/itm/databaseConnection.vue
... | ... | @@ -0,0 +1,535 @@ |
1 | +<template> | |
2 | + <div v-show="isModalOpen" class="modal-wrapper"> | |
3 | + <div class="modal-container large-modal"> | |
4 | + <div class="modal-title"> | |
5 | + <div class="flex justify-between align-center"> | |
6 | + <h2>데이터베이스 연결</h2> | |
7 | + <button class="close-btn" @click="$emit('closePopup')"> | |
8 | + <svg-icon type="mdi" :width="20" :height="20" :path="closePath"></svg-icon> | |
9 | + </button> | |
10 | + </div> | |
11 | + </div> | |
12 | + <div class="modal-content-monthly" v-show="currentPage == 1"> | |
13 | + <div class="table-zone"> | |
14 | + <table class="form-table"> | |
15 | + <colgroup> | |
16 | + <col style="width: 25%" /> | |
17 | + <col style="width: 75%" /> | |
18 | + </colgroup> | |
19 | + <tbody v-if="jobItm.itm != null"> | |
20 | + <tr> | |
21 | + <th>연계정보 타입</th> | |
22 | + <td> | |
23 | + <div class="input-container flex"> | |
24 | + <label class="radio-label"> | |
25 | + <input type="radio" name="radio" :value="false" class="custom-radiobox" @change="successAt = false" v-model="jobItm.itm_option_bool" /> | |
26 | + <span>직접입력</span> | |
27 | + </label> | |
28 | + <label class="radio-label"> | |
29 | + <input type="radio" name="radio" :value="true" class="custom-radiobox" @change="successAt = false" v-model="jobItm.itm_option_bool" /> | |
30 | + <span>불러오기</span> | |
31 | + </label> | |
32 | + </div> | |
33 | + </td> | |
34 | + </tr> | |
35 | + <tr v-show="jobItm.itm_option_bool"> | |
36 | + <th>연계정보</th> | |
37 | + <td> | |
38 | + <input type="text" class="half-input" disabled :value="linkConnectionDB.conectNm + | |
39 | + '(' + | |
40 | + linkConnectionDB.conectIp + | |
41 | + ')' | |
42 | + " v-if="linkConnectionDB.conectNm != null" /> | |
43 | + <input type="text" class="half-input" disabled v-else /> | |
44 | + <button class="blue-border-btn small-btn" @click="dbConSearchOpen(true)"> 검색 </button> | |
45 | + </td> | |
46 | + </tr> | |
47 | + <tr> | |
48 | + <th>DMBS</th> | |
49 | + <td> | |
50 | + <select id="databaseType" @change="successAt = false" class="square-select half-input" v-model="inputConnectionDB.databaseType" :disabled="jobItm.itm_option_bool"> | |
51 | + <option v-for="(itm, index) in databaseTypeList" :key="index" :value="itm.key"> {{ itm.value }} </option> | |
52 | + </select> | |
53 | + </td> | |
54 | + </tr> | |
55 | + <tr> | |
56 | + <th>IP</th> | |
57 | + <td> | |
58 | + <input id="conectIp" type="text" @input="successAt = false" class="half-input" v-model="inputConnectionDB.conectIp" placeholder="127.0.0.1" :disabled="jobItm.itm_option_bool" /> | |
59 | + </td> | |
60 | + </tr> | |
61 | + <tr> | |
62 | + <th>PORT</th> | |
63 | + <td> | |
64 | + <input id="conectPort" type="text" @input="successAt = false" class="half-input" v-model="inputConnectionDB.conectPort" :disabled="jobItm.itm_option_bool" /> | |
65 | + </td> | |
66 | + </tr> | |
67 | + <tr> | |
68 | + <th>DB 명</th> | |
69 | + <td> | |
70 | + <input id="databaseNm" type="text" @input="successAt = false" class="half-input" v-model="inputConnectionDB.databaseNm" placeholder="데이터베이스명 OR 스키마명" :disabled="jobItm.itm_option_bool" /> | |
71 | + </td> | |
72 | + </tr> | |
73 | + <tr> | |
74 | + <th>접속ID</th> | |
75 | + <td> | |
76 | + <input type="text" class="half-input" @input="successAt = false" v-model="inputConnectionDB.userId" placeholder="접속 ID" :disabled="jobItm.itm_option_bool" /> | |
77 | + </td> | |
78 | + </tr> | |
79 | + <tr> | |
80 | + <th>접속PW</th> | |
81 | + <td> | |
82 | + <input type="password" class="half-input" @input="successAt = false" v-model="inputConnectionDB.userPassword" placeholder="접속 PW" autocomplete="new-password" :disabled="jobItm.itm_option_bool" /> | |
83 | + </td> | |
84 | + </tr> | |
85 | + </tbody> | |
86 | + </table> | |
87 | + <div class="content-titleZone flex justy justify-between align-center mt20"> | |
88 | + <p class="box-title">데이터베이스 연결 결과</p> | |
89 | + <button class="blue-border-btn small-btn" @click="dataBaseConnectionCheck()"> 접속확인 </button> | |
90 | + </div> | |
91 | + <div class="table-zone"> | |
92 | + <table class="list-table"> | |
93 | + <colgroup> | |
94 | + <col width="10%" /> | |
95 | + <col width="10%" /> | |
96 | + <col width="80%" /> | |
97 | + </colgroup> | |
98 | + <thead> | |
99 | + <tr> | |
100 | + <th>No</th> | |
101 | + <th>접속시간</th> | |
102 | + <th>접속결과</th> | |
103 | + </tr> | |
104 | + </thead> | |
105 | + <tbody> | |
106 | + <tr v-for="(itm, indx) in resultMessage" :key="indx"> | |
107 | + <td>{{ indx + 1 }}</td> | |
108 | + <td>{{ itm.time }}</td> | |
109 | + <td>{{ itm.message }}</td> | |
110 | + </tr> | |
111 | + </tbody> | |
112 | + </table> | |
113 | + </div> | |
114 | + </div> | |
115 | + </div> | |
116 | + <div class="modal-content-monthly" v-show="currentPage == 2"> | |
117 | + <Splitter style="height: 100%; border: 0px solid #e5e7eb"> | |
118 | + <SplitterPanel class="flex align-items-center justify-content-center" :size="20" :minSize="10"> | |
119 | + <div class="content-box"> | |
120 | + <div class="file-zone"> | |
121 | + <div class="content-titleZone" style="height: 60px"> | |
122 | + <p class="box-title">table 정보</p> | |
123 | + </div> | |
124 | + <div class="content-zone2"> | |
125 | + <ul class="content-list" v-if="tableList.length > 0"> | |
126 | + <li class="cursor" v-for="(item, indx) in tableList" :key="indx"> | |
127 | + <a @click="getTableData(item)" :class="{ | |
128 | + 'file-list': true, | |
129 | + selected: selectTable === item, | |
130 | + }"> | |
131 | + <div class="flex align-center"> | |
132 | + <!-- <p>{{ item.tableNmKr != null && item.tableNmKr != '' ? item.tableNmKr : '-' }} <span style="opacity: .6;">({{ item.tableNm }})</span></p> --> | |
133 | + <p>{{ item.tableNmKr != null && item.tableNmKr != '' ? item.tableNmKr : item.tableNm }}</p> | |
134 | + </div> | |
135 | + </a> | |
136 | + </li> | |
137 | + </ul> | |
138 | + </div> | |
139 | + </div> | |
140 | + </div> | |
141 | + </SplitterPanel> | |
142 | + <SplitterPanel class="flex align-items-center justify-content-center" :size="80"> | |
143 | + <div class="content-box"> | |
144 | + <div class="content-titleZone" style="height: 60px"> | |
145 | + <div class="flex justify-between aling-center"> | |
146 | + <p class="box-title">쿼리 작업</p> | |
147 | + <div> | |
148 | + <button class="icon-btn" @click="executeQuery" title="실행"> | |
149 | + <svg-icon type="mdi" :path="playPath" :color="'#fbbe28'"></svg-icon> | |
150 | + </button> | |
151 | + </div> | |
152 | + </div> | |
153 | + </div> | |
154 | + <div class="content-zone"> | |
155 | + <Splitter style="height: 100%" layout="vertical"> | |
156 | + <SplitterPanel class="flex align-items-center justify-content-center" :size="50" :minSize="20"> | |
157 | + <textarea style=" | |
158 | + resize: none; | |
159 | + max-width: 100%; | |
160 | + max-height: 100%; | |
161 | + padding: 10px; | |
162 | + " v-model="jobItm.itm.query"></textarea> | |
163 | + </SplitterPanel> | |
164 | + <SplitterPanel class="align-items-center justify-content-center" :size="50"> | |
165 | + <ul class="tab-nav flex justify-start"> | |
166 | + <li @click="showTab('tab1')"> | |
167 | + <a href="#tab01" :class="{ activeTab: activeTab === 'tab1' }">작업결과</a> | |
168 | + </li> | |
169 | + <li @click="showTab('tab2')"> | |
170 | + <a href="#tab02" :class="{ activeTab: activeTab === 'tab2' }">작업log</a> | |
171 | + </li> | |
172 | + </ul> | |
173 | + <div v-show="activeTab === 'tab1'" style=" | |
174 | + height: calc(100% - 60px); | |
175 | + overflow: auto; | |
176 | + padding: 10px; | |
177 | + "> | |
178 | + <div class="count-zone" v-if="jobItm.dataTable.columnDatas.length > 0"> | |
179 | + <p> 총 <span>{{ jobItm.dataTable.totalRows }}</span>건 중 <span>{{ jobItm.dataTable.rowData.length }}</span>건 조회 </p> | |
180 | + </div> | |
181 | + <div class="table-zone"> | |
182 | + <table class="list-table"> | |
183 | + <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 --> | |
184 | + <thead> | |
185 | + <tr v-if="jobItm.dataTable.columnDatas.length > 0"> | |
186 | + <th v-for="(itm, indx) in jobItm.dataTable | |
187 | + .columnDatas" :key="indx" style="min-width: 150px !important"> {{ itm.columnNm }} <label class="check-label"> | |
188 | + <input type="checkbox" class="custom-checkbox" v-model="itm.pkAt" /> | |
189 | + </label> | |
190 | + </th> | |
191 | + </tr> | |
192 | + </thead> | |
193 | + <tbody v-if="jobItm.dataTable.rowData.length > 0"> | |
194 | + <tr v-for="(row, rows) in jobItm.dataTable.rowData" :key="rows"> | |
195 | + <td v-for="(itm, indx) in row" :key="indx" style=" | |
196 | + overflow: hidden; | |
197 | + white-space: nowrap; | |
198 | + text-overflow: ellipsis; | |
199 | + "> {{ itm }} </td> | |
200 | + </tr> | |
201 | + </tbody> | |
202 | + </table> | |
203 | + </div> | |
204 | + </div> | |
205 | + <div v-show="activeTab === 'tab2'" style=" | |
206 | + height: calc(100% - 60px); | |
207 | + overflow: auto; | |
208 | + padding: 10px; | |
209 | + "> | |
210 | + <div class="table-zone"> | |
211 | + <table class="list-table"> | |
212 | + <colgroup> | |
213 | + <col width="10%" /> | |
214 | + <col width="10%" /> | |
215 | + <col width="" /> | |
216 | + <col width="10%" /> | |
217 | + </colgroup> | |
218 | + <thead> | |
219 | + <tr> | |
220 | + <th>No</th> | |
221 | + <th>접속시간</th> | |
222 | + <th>접속결과</th> | |
223 | + <th>접속내용</th> | |
224 | + </tr> | |
225 | + </thead> | |
226 | + <tbody> | |
227 | + <tr v-for="(itm, indx) in executeMessage" :key="indx"> | |
228 | + <td>{{ indx + 1 }}</td> | |
229 | + <td>{{ itm.time }}</td> | |
230 | + <td>{{ itm.message }}</td> | |
231 | + <td>{{ itm.result }}</td> | |
232 | + </tr> | |
233 | + </tbody> | |
234 | + </table> | |
235 | + </div> | |
236 | + </div> | |
237 | + </SplitterPanel> | |
238 | + </Splitter> | |
239 | + </div> | |
240 | + </div> | |
241 | + </SplitterPanel> | |
242 | + </Splitter> | |
243 | + </div> | |
244 | + <div class="modal-end flex justify-end"> | |
245 | + <button class="blue-border-btn small-btn" @click="setPage(1)" v-show="currentPage == 2">이전</button> | |
246 | + <button class="blue-border-btn small-btn" @click="setPage(2)" v-show="currentPage == 1" :disabled="!successAt">다음</button> | |
247 | + <button class="blue-btn small-btn" @click="handleSaveNodeData(jobItm)" v-show="currentPage == 2">확인</button> | |
248 | + <button class="blue-border-btn small-btn" @click="$emit('closePopup')">취소</button> | |
249 | + </div> | |
250 | + </div> | |
251 | + <DBConSearch :openPopup="openSearchModal" @modalclose="dbConSearchOpen" @selectItm="selectDbcon" /> | |
252 | + </div> | |
253 | +</template> | |
254 | +<script> | |
255 | +import axios from "axios"; | |
256 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
257 | +import DBConSearch from "../../dataComponent/DbConnectionSearchModal.vue"; | |
258 | +import { mdiClose, mdiPlay, mdiCancel } from "@mdi/js"; | |
259 | + | |
260 | +export default { | |
261 | + name: "database-connection", | |
262 | + props: { | |
263 | + openPopup: { | |
264 | + type: Boolean, | |
265 | + default: false, | |
266 | + }, | |
267 | + jobItem: { | |
268 | + type: Boolean, | |
269 | + default: null, | |
270 | + }, | |
271 | + }, | |
272 | + data() { | |
273 | + return { | |
274 | + closePath: mdiClose, | |
275 | + // 현재 모달창 관리 | |
276 | + isModalOpen: this.openPopup, | |
277 | + // 연계정보 불러오기 | |
278 | + openSearchModal: false, | |
279 | + // 커넥션 DB 오브젝트 | |
280 | + jobItm: this.jobItem, | |
281 | + connectionDB: {}, | |
282 | + linkConnectionDB: {}, | |
283 | + inputConnectionDB: {}, | |
284 | + resultMessage: [], | |
285 | + executeMessage: [], | |
286 | + // 연계 성공여부 | |
287 | + successAt: false, | |
288 | + databaseTypeList: [], | |
289 | + currentPage: 1, | |
290 | + tableList: [], | |
291 | + selectTable: {}, | |
292 | + activeTab: "tab1", | |
293 | + playPath: mdiPlay, | |
294 | + cancelPath: mdiCancel, | |
295 | + }; | |
296 | + }, | |
297 | + methods: { | |
298 | + // 화면전환 | |
299 | + setPage: function (val) { | |
300 | + this.currentPage = val; | |
301 | + if (val == 2) { | |
302 | + this.getDbConnectionTableList(); | |
303 | + } | |
304 | + }, | |
305 | + handleSaveNodeData(jobItm) { | |
306 | + this.$emit('saveNodeData', jobItm); | |
307 | + this.$emit('closePopup'); | |
308 | + }, | |
309 | + /***************************************************접속정보 설정************************************************************************/ | |
310 | + // 연계가능 여부 테스트 | |
311 | + dataBaseConnectionCheck: function () { | |
312 | + var vm = this; | |
313 | + if (this.jobItm.itm_option_bool == true) { | |
314 | + this.jobItm.itm = this.linkConnectionDB; | |
315 | + } else { | |
316 | + this.jobItm.itm = this.inputConnectionDB; | |
317 | + } | |
318 | + this.jobItm.itm.loadAt = this.jobItm.itm_option_bool; | |
319 | + delete this.jobItm.itm.type; | |
320 | + | |
321 | + axios({ | |
322 | + url: "/data/dataBaseConnectionCheck.json", | |
323 | + method: "post", | |
324 | + headers: { | |
325 | + "Content-Type": "application/json; charset=UTF-8", | |
326 | + }, | |
327 | + data: vm.jobItm.itm, | |
328 | + }) | |
329 | + .then(function (response) { | |
330 | + if (response.data.checkMessage.success == true) { | |
331 | + vm.successAt = true; | |
332 | + } else { | |
333 | + vm.successAt = false; | |
334 | + } | |
335 | + vm.$showAlert("결과내용", response.data.checkMessage.message); | |
336 | + vm.resultMessage.push({ | |
337 | + time: vm.$getFullTime(), | |
338 | + message: response.data.checkMessage.message, | |
339 | + result: response.data.checkMessage.success | |
340 | + ? "접속성공" | |
341 | + : "접속실패", | |
342 | + }); | |
343 | + }) | |
344 | + .catch(function (error) { }); | |
345 | + }, | |
346 | + | |
347 | + // db커넥션 선택창 호출 | |
348 | + dbConSearchOpen: function (val) { | |
349 | + this.openSearchModal = val; | |
350 | + }, | |
351 | + | |
352 | + // 연계정보 받기 | |
353 | + selectDbcon: function (dbcon) { | |
354 | + this.linkConnectionDB = dbcon; | |
355 | + }, | |
356 | + /***************************************************접속정보 설정 끝*******************************************************************/ | |
357 | + /***************************************************데이터 베이스 설정*******************************************************************/ | |
358 | + // 탭 변경 | |
359 | + showTab: function (tabName) { | |
360 | + this.activeTab = tabName; | |
361 | + }, | |
362 | + //DB 접속 - 테이블 목록 조회 | |
363 | + getDbConnectionTableList: function () { | |
364 | + let vm = this; | |
365 | + axios({ | |
366 | + url: "/data/selectTableList.json", | |
367 | + method: "post", | |
368 | + headers: { | |
369 | + "Content-Type": "application/json; charset=UTF-8", | |
370 | + }, | |
371 | + data: vm.jobItm.itm, | |
372 | + }) | |
373 | + .then(function (response) { | |
374 | + if (response.data.checkMessage.success == true) { | |
375 | + vm.tableList = response.data.resultData.tableList; | |
376 | + } | |
377 | + }) | |
378 | + .catch(function (error) { }); | |
379 | + }, | |
380 | + | |
381 | + // 테이블 클릭 | |
382 | + clickTable: function (itm) { | |
383 | + this.selectTable = itm; | |
384 | + }, | |
385 | + | |
386 | + // 선택한 테이블 정보 가져오기 | |
387 | + getTableData: function (itm) { | |
388 | + let vm = this; | |
389 | + itm.perPage = this.jobItm.dataTable.perPage; | |
390 | + | |
391 | + // 통신시 데이터 형문제로 삭제 | |
392 | + this.jobItm.itm.creatDt = null; | |
393 | + let requestData = { | |
394 | + dataset: itm, | |
395 | + connectionDB: this.jobItm.itm, | |
396 | + }; | |
397 | + | |
398 | + axios({ | |
399 | + url: "/data/getTableData.json", | |
400 | + method: "post", | |
401 | + headers: { | |
402 | + "Content-Type": "application/json; charset=UTF-8", | |
403 | + }, | |
404 | + data: requestData, | |
405 | + }) | |
406 | + .then(function (response) { | |
407 | + if (response.data.checkMessage.success) { | |
408 | + vm.jobItm.dataTable = response.data.resultData.dataTable; | |
409 | + vm.jobItm.itm.query = vm.jobItm.dataTable.query; | |
410 | + | |
411 | + vm.columnDatas = response.data.resultData.dataTable.columnDatas; | |
412 | + vm.jobItm.front_dataTable.columnDatas = vm.columnDatas; | |
413 | + } | |
414 | + vm.executeMessage.push({ | |
415 | + time: vm.$getFullTime(), | |
416 | + message: response.data.resultData.dataTable.checkMessage.message, | |
417 | + result: response.data.resultData.dataTable.checkMessage.success | |
418 | + ? "성공" | |
419 | + : "실패", | |
420 | + }); | |
421 | + }) | |
422 | + .catch(function (error) { }); | |
423 | + }, | |
424 | + | |
425 | + // 쿼리 실행 | |
426 | + executeQuery: function () { | |
427 | + let vm = this; | |
428 | + // 통신시 데이터 형문제로 삭제 | |
429 | + this.jobItm.itm.creatDt = null; | |
430 | + this.jobItm.dataTable.query = this.jobItm.itm.query; | |
431 | + let requestData = { | |
432 | + dataTable: this.jobItm.dataTable, | |
433 | + connectionDB: this.jobItm.itm, | |
434 | + }; | |
435 | + | |
436 | + axios({ | |
437 | + url: "/data/getTableDataByQuery.json", | |
438 | + method: "post", | |
439 | + headers: { | |
440 | + "Content-Type": "application/json; charset=UTF-8", | |
441 | + }, | |
442 | + data: requestData, | |
443 | + }) | |
444 | + .then(function (response) { | |
445 | + if (response.data.checkMessage.success) { | |
446 | + vm.jobItm.dataTable = response.data.resultData.dataTable; | |
447 | + vm.jobItm.itm.query = vm.jobItm.dataTable.query; | |
448 | + } | |
449 | + vm.executeMessage.push({ | |
450 | + time: vm.$getFullTime(), | |
451 | + message: response.data.resultData.dataTable.checkMessage.message, | |
452 | + result: response.data.resultData.dataTable.checkMessage.success | |
453 | + ? "성공" | |
454 | + : "실패", | |
455 | + }); | |
456 | + }) | |
457 | + .catch(function (error) { }); | |
458 | + }, | |
459 | + | |
460 | + /***************************************************데이터 베이스 설정끝*******************************************************************/ | |
461 | + | |
462 | + // 초기화 function | |
463 | + init: async function () { | |
464 | + this.databaseTypeList = await this.$getDataBaseTypeList(); | |
465 | + | |
466 | + if (this.jobItm == null) { | |
467 | + this.jobItm = Object.assign({}, this.$getDefaultJobGroup().node); | |
468 | + this.linkConnectionDB = Object.assign( | |
469 | + {}, | |
470 | + this.$getDefaultJobGroup().connectionDb | |
471 | + ); | |
472 | + this.inputConnectionDB = Object.assign( | |
473 | + {}, | |
474 | + this.$getDefaultJobGroup().connectionDb | |
475 | + ); | |
476 | + this.jobItm.itm = Object.assign( | |
477 | + {}, | |
478 | + this.$getDefaultJobGroup().connectionDb | |
479 | + ); | |
480 | + } else { | |
481 | + if (this.jobItm.itm_option_bool) { | |
482 | + this.linkConnectionDB = this.jobItm.itm; | |
483 | + this.connectionDB = this.linkConnectionDB; | |
484 | + } else { | |
485 | + this.inputConnectionDB = this.jobItm.itm; | |
486 | + this.connectionDB = this.inputConnectionDB; | |
487 | + } | |
488 | + } | |
489 | + | |
490 | + if (this.inputConnectionDB.databaseType == null) { | |
491 | + this.inputConnectionDB.databaseType = this.databaseTypeList.MARIADB.key; | |
492 | + } | |
493 | + }, | |
494 | + }, | |
495 | + watch: { | |
496 | + openPopup: function (v) { | |
497 | + this.isModalOpen = v; | |
498 | + }, | |
499 | + | |
500 | + jobItem: function (v) { | |
501 | + this.jobItm = v; | |
502 | + if (v.itm_option_bool) { | |
503 | + this.linkConnectionDB = this.jobItm.itm; | |
504 | + } else { | |
505 | + this.inputConnectionDB = this.jobItm.itm; | |
506 | + } | |
507 | + }, | |
508 | + }, | |
509 | + components: { | |
510 | + DBConSearch: DBConSearch, | |
511 | + SvgIcon: SvgIcon, | |
512 | + }, | |
513 | + mounted() { | |
514 | + this.init(); | |
515 | + }, | |
516 | + create() { | |
517 | + if (this.jobItm == null) { | |
518 | + this.jobItm = Object.assign({}, this.defaultJobItm); | |
519 | + this.jobItm.itm = this.$getDefaultJobGroup().connectionDb; | |
520 | + } | |
521 | + }, | |
522 | +}; | |
523 | +</script> | |
524 | +<style> | |
525 | +.content-zone2 { | |
526 | + height: calc(100% - 57px); | |
527 | + /* 기존 스타일 유지 */ | |
528 | + max-height: calc(100% - 57px); | |
529 | + /* 최대 높이 설정 */ | |
530 | + overflow-y: auto; | |
531 | + /* 수직 스크롤 추가 */ | |
532 | + overflow-x: hidden; | |
533 | + /* 수평 스크롤 숨기기 (원하는 경우) */ | |
534 | +} | |
535 | +</style> |
+++ client/views/component/connection/itm/datasetSelecter.vue
... | ... | @@ -0,0 +1,281 @@ |
1 | +<template> | |
2 | + <div v-show="isModalOpen" class="modal-wrapper"> | |
3 | + <div class="modal-container"> | |
4 | + <div class="modal-title"> | |
5 | + <div class="flex justify-between align-center"> | |
6 | + <h2>데이터 셋 선택</h2> | |
7 | + <button class="close-btn" @click="$emit('closePopup')"> | |
8 | + <svg-icon type="mdi" :width="20" :height="20" :path="closePath"></svg-icon> | |
9 | + </button> | |
10 | + </div> | |
11 | + </div> | |
12 | + <div class="content-wrap"> | |
13 | + <div class="content-box flex justify-between"> | |
14 | + <div class="flex20 content-box"> | |
15 | + <div class="left-content flex100 content-box"> | |
16 | + <CodeList :groupCode="'DATA_CTGRY'" @selectCode="selectCode" /> | |
17 | + </div> | |
18 | + </div> | |
19 | + <div class="flex80 content-box"> | |
20 | + <div class="right-content content-box"> | |
21 | + <div class="flex-column justify-between"> | |
22 | + <div class="flex justify-end align-center flex5 no-gutter"> | |
23 | + <div class="flex justify-end flex100"> | |
24 | + <div class="flex justify-end align-center"> | |
25 | + <input type="date" name="start-date" id="start-date" class="square-date" :class="{ 'date-placeholder': false }" data-placeholder="날짜 선택" v-model="search_date.value" /> | |
26 | + <span class="coupler">~</span> | |
27 | + <input type="date" name="end-date" id="end-date" class="square-date ml5" :class="{ 'date-placeholder': false }" data-placeholder="날짜 선택" v-model="search_date.value2" /> | |
28 | + <select class="square-select ml5" v-model="search_data1.value"> | |
29 | + <option :value="null">공개여부</option> | |
30 | + <option :value="true">공개</option> | |
31 | + <option :value="false">비공개</option> | |
32 | + </select> | |
33 | + <select class="square-select ml5" v-model="search_data2.value"> | |
34 | + <option :value="null">카테고리</option> | |
35 | + <option v-for="(item, indx) in categoryList" :key="indx" :value="item.cmmnCode"> {{ item.codeNm }} </option> | |
36 | + </select> | |
37 | + <select name="" id="searchItm1" class="square-select ml5" v-model="search_data3.key"> | |
38 | + <option :value="null">검색조건</option> | |
39 | + <option value="post_sj">제목</option> | |
40 | + <option value="post_dc">내용</option> | |
41 | + </select> | |
42 | + <div class="search-square ml5"> | |
43 | + <input type="text" class="square-input" placeholder="Search" v-model="search_data3.value" v-on:keyup.enter="searchData()" /> | |
44 | + <button class="square-button blue-btn" @click="searchData()"> | |
45 | + <svg-icon type="mdi" :path="this.$getIconPath()" class="square-icon"></svg-icon> | |
46 | + </button> | |
47 | + </div> | |
48 | + </div> | |
49 | + </div> | |
50 | + </div> | |
51 | + <div class="table-zone flex85"> | |
52 | + <div class="list-info flex justify-between align-center"> | |
53 | + <div class="count-zone"> | |
54 | + <p>총 <span>40</span>건 중 <span>01</span>건 선택</p> | |
55 | + </div> | |
56 | + <div class="cunt-selectZone"> | |
57 | + <select name="" id=""> | |
58 | + <option value="">10개 보기</option> | |
59 | + <option value="">20개 보기</option> | |
60 | + </select> | |
61 | + </div> | |
62 | + </div> | |
63 | + <table class="list-table"> | |
64 | + <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 --> | |
65 | + <colgroup> | |
66 | + <col style="width: 10%" /> | |
67 | + <col style="width: 30%" /> | |
68 | + <col style="width: 10%" /> | |
69 | + <col style="width: 10%" /> | |
70 | + <col style="width: 10%" /> | |
71 | + <col style="width: 30%" /> | |
72 | + </colgroup> | |
73 | + <thead> | |
74 | + <tr> | |
75 | + <th>No</th> | |
76 | + <th>제목</th> | |
77 | + <th>생성자</th> | |
78 | + <th>부서</th> | |
79 | + <th>카테고리</th> | |
80 | + <th>등록일시</th> | |
81 | + </tr> | |
82 | + </thead> | |
83 | + <tbody> | |
84 | + <template v-if="dataPostList.length > 0"> | |
85 | + <tr v-for="(item, indx) in dataPostList" :key="indx" @click="selectPost(item)"> | |
86 | + <td> {{ search.totalRows - indx - (search.currentPage - 1) * search.perPage }} </td> | |
87 | + <td>{{ item.post_sj }}</td> | |
88 | + <td>{{ item.creat_id }}</td> | |
89 | + <td>{{ item.dept_code }}</td> | |
90 | + <td>{{ item.ctgry_id }}</td> | |
91 | + <td>{{ item.creat_dt }}</td> | |
92 | + </tr> | |
93 | + </template> | |
94 | + <template v-else> | |
95 | + <tr> | |
96 | + <td colspan="6">등록된 데이터가 없습니다.</td> | |
97 | + </tr> | |
98 | + </template> | |
99 | + </tbody> | |
100 | + </table> | |
101 | + <PaginationButton v-model:currentPage="search.currentPage" :perPage="search.perPage" :totalCount="search.totalRows" :maxRange="5" :click="searchData" /> | |
102 | + </div> | |
103 | + <div> | |
104 | + <label :for="comId + 'forder'" style="padding-left: 255px"> | |
105 | + <p class="width150 fl mt7" style="margin-bottom: 10px"> 데이터셋(ID) </p> | |
106 | + <input type="text" :name="comId + 'forder'" class="input_M width50p fl" v-model="selectedDatasetId" readonly /> | |
107 | + </label> | |
108 | + </div> | |
109 | + <div class="flex5"> | |
110 | + <div class="flex justify-end"> | |
111 | + <button class="darkg-btn small-btn" @click="complite"> 완료 </button> | |
112 | + </div> | |
113 | + </div> | |
114 | + </div> | |
115 | + </div> | |
116 | + </div> | |
117 | + </div> | |
118 | + </div> | |
119 | + </div> | |
120 | + </div> | |
121 | +</template> | |
122 | +<script> | |
123 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
124 | +import { mdiClose } from "@mdi/js"; | |
125 | +import axios from "axios"; | |
126 | +import CodeList from "../../common/Component_CodeList.vue"; | |
127 | +import PaginationButton from "../../PaginationButton.vue"; | |
128 | + | |
129 | +export default { | |
130 | + components: { | |
131 | + SvgIcon: SvgIcon, | |
132 | + CodeList: CodeList, | |
133 | + PaginationButton: PaginationButton, | |
134 | + }, | |
135 | + props: { | |
136 | + openPopup: { | |
137 | + type: Boolean, | |
138 | + default: false, | |
139 | + }, | |
140 | + jobItem: { | |
141 | + type: Boolean, | |
142 | + default: null, | |
143 | + }, | |
144 | + }, | |
145 | + data() { | |
146 | + return { | |
147 | + isModalOpen: this.openPopup, | |
148 | + closePath: mdiClose, | |
149 | + jobItm: this.jobItem, | |
150 | + | |
151 | + activeTab: "tab1", | |
152 | + modalType: "tab-modal", | |
153 | + //차트 Box ID (랜덤값 필수) | |
154 | + categoryList: [], | |
155 | + pagination: {}, | |
156 | + | |
157 | + selectedDatasetId: null, // 선택한 데이터 셋 아이디 | |
158 | + | |
159 | + // 데이터셋 목록 | |
160 | + datasetPostList: [], | |
161 | + dataPostList: [], | |
162 | + search: this.$getDefaultSerchVO(), | |
163 | + // 공개여부 | |
164 | + search_data1: this.$getDefaultSerchItem("public_at", "bool"), | |
165 | + // 카테고리 | |
166 | + search_data2: this.$getDefaultSerchItem("ctgry_id", "string"), | |
167 | + // 선택 | |
168 | + search_data3: this.$getDefaultSerchItem("post_sj", "string"), | |
169 | + // 날짜 | |
170 | + search_date: this.$getDefaultSerchItem("creat_dt", "dates"), | |
171 | + categoryList: [], | |
172 | + }; | |
173 | + }, | |
174 | + mounted() { | |
175 | + this.init(); | |
176 | + }, | |
177 | + watch: { | |
178 | + openPopup(v) { | |
179 | + this.isModalOpen = v; | |
180 | + }, | |
181 | + jobItem(v) { | |
182 | + this.jobItemInfo = v; | |
183 | + }, | |
184 | + }, | |
185 | + methods: { | |
186 | + async init() { | |
187 | + if (!this.jobItem) { | |
188 | + this.jobItm = Object.assign({}, this.$getDefaultJobGroup().node); | |
189 | + this.jobItm.itm = Object.assign( | |
190 | + {}, | |
191 | + this.$getDefaultJobGroup().DatasetPost | |
192 | + ); | |
193 | + } else { | |
194 | + this.selectedDatasetId = this.jobItm.itm.dataset_post_id; | |
195 | + } | |
196 | + this.search.searchObjectList.push(this.search_date); | |
197 | + this.search.searchObjectList.push(this.search_data1); | |
198 | + this.search.searchObjectList.push(this.search_data2); | |
199 | + this.search.searchObjectList.push(this.search_data3); | |
200 | + this.categoryList = await this.$getCommonCode("DATA_CTGRY"); | |
201 | + }, | |
202 | + // 탭 변경 | |
203 | + showTab(tabName) { | |
204 | + this.activeTab = tabName; | |
205 | + }, | |
206 | + // 카테고리 선택 | |
207 | + categorySelect(item) { | |
208 | + this.pagination.categoryId = item.categoryId; | |
209 | + this.searchDatasetPostList(); | |
210 | + }, | |
211 | + //데이터셋 검색 | |
212 | + searchDatasetPostList() { | |
213 | + this.pagination.currentPage = 1; | |
214 | + this.getDatasetPostList(); | |
215 | + }, | |
216 | + //데이터셋 데이터 리스트 타이틀 가져오기 | |
217 | + getDatasetTitle(info) { | |
218 | + if (info.isStandard == true) { | |
219 | + return info.title; | |
220 | + } else { | |
221 | + return info.title + " (표준화필요)"; | |
222 | + } | |
223 | + }, | |
224 | + selectCode(code) { | |
225 | + this.search_data2.value = code; | |
226 | + this.searchData(); | |
227 | + }, | |
228 | + // 완료버튼 클릭 | |
229 | + complite() { | |
230 | + const vm = this; | |
231 | + axios({ | |
232 | + url: "/dataset/getDataTableInfo.json", | |
233 | + method: "post", | |
234 | + headers: { "Content-Type": "application/json; charset=UTF-8" }, | |
235 | + data: { datapost_id: vm.selectedDatasetId }, | |
236 | + }) | |
237 | + .then(function (response) { | |
238 | + vm.jobItm.dataTable = response.data.resultData.dataTable; | |
239 | + vm.$emit("saveNodeData", vm.jobItm); | |
240 | + }) | |
241 | + .catch(function (error) { | |
242 | + vm.$showAlert( | |
243 | + "에러 발생", | |
244 | + "에러가 발생했습니다. 관리자에게 문의해 주세요." | |
245 | + ); | |
246 | + }); | |
247 | + }, | |
248 | + | |
249 | + selectPost(item) { | |
250 | + this.jobItm.itm = item; | |
251 | + this.selectedDatasetId = item.dataset_post_id; | |
252 | + }, | |
253 | + searchData() { | |
254 | + const vm = this; | |
255 | + axios({ | |
256 | + url: "/dataset/selectDataPostList.json", | |
257 | + method: "post", | |
258 | + headers: { | |
259 | + "Content-Type": "application/json; charset=UTF-8", | |
260 | + }, | |
261 | + data: JSON.stringify(vm.search), | |
262 | + }) | |
263 | + .then(function (response) { | |
264 | + if (response.data.checkMessage.success) { | |
265 | + vm.dataPostList = response.data.resultData.dataPostList; | |
266 | + vm.search.totalRows = response.data.resultData.totalRow; | |
267 | + } | |
268 | + }) | |
269 | + .catch(function (error) { | |
270 | + this.$showAlert( | |
271 | + "에러 발생", | |
272 | + "에러가 발생했습니다. 관리자에게 문의해 주세요." | |
273 | + ); | |
274 | + }); | |
275 | + }, | |
276 | + }, | |
277 | + computed: { | |
278 | + CodeList, | |
279 | + }, | |
280 | +}; | |
281 | +</script>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/connection/itm/datasetUpdate.vue
... | ... | @@ -0,0 +1,439 @@ |
1 | +<template> | |
2 | + <div v-show="isModalOpen" class="modal-wrapper"> | |
3 | + <div class="modal-container large-modal"> | |
4 | + <div class="modal-title"> | |
5 | + <div class="flex justify-between align-center"> | |
6 | + <h2>데이터셋 업데이트</h2> | |
7 | + <button | |
8 | + type="button" | |
9 | + class="close-btn" | |
10 | + @click="$emit('fnCloseModal')" | |
11 | + > | |
12 | + <svg-icon | |
13 | + type="mdi" | |
14 | + :width="20" | |
15 | + :height="20" | |
16 | + :path="closePath" | |
17 | + ></svg-icon> | |
18 | + </button> | |
19 | + </div> | |
20 | + </div> | |
21 | + <div class="modal-content-monthly"> | |
22 | + <div class="tab-contents mt10"> | |
23 | + <div class="content-titleZone flex justify-between align-center"> | |
24 | + <p class="box-title">데이터셋 설정</p> | |
25 | + </div> | |
26 | + <table class="form-table"> | |
27 | + <colgroup> | |
28 | + <col style="width: 15%" /> | |
29 | + <col style="width: 85%" /> | |
30 | + </colgroup> | |
31 | + <tbody> | |
32 | + <tr> | |
33 | + <th>데이터셋 설정</th> | |
34 | + <td> | |
35 | + <button | |
36 | + class="blue-border-btn small-btn" | |
37 | + @click="fnTargetDataRead" | |
38 | + > | |
39 | + 타겟 데이터 조회 | |
40 | + </button> | |
41 | + </td> | |
42 | + </tr> | |
43 | + <tr> | |
44 | + <th>기존 데이터 삭제 여부</th> | |
45 | + <td> | |
46 | + <div class="input-container flex"> | |
47 | + <label class="radio-label mr5"> | |
48 | + <input | |
49 | + type="radio" | |
50 | + name="radio" | |
51 | + :value="false" | |
52 | + class="custom-radiobox" | |
53 | + v-model="jobItm.itm_option_bool" | |
54 | + /> | |
55 | + <span>기존 데이터 삭제</span> | |
56 | + </label> | |
57 | + <label class="radio-label"> | |
58 | + <input | |
59 | + type="radio" | |
60 | + name="radio" | |
61 | + :value="true" | |
62 | + class="custom-radiobox" | |
63 | + v-model="jobItm.itm_option_bool" | |
64 | + /> | |
65 | + <span>기존 데이터 미삭제</span> | |
66 | + </label> | |
67 | + </div> | |
68 | + </td> | |
69 | + </tr> | |
70 | + </tbody> | |
71 | + </table> | |
72 | + <div v-if="targetColumnDatas.length > 0" class="flex mt20"> | |
73 | + <div class="flex50 pl0"> | |
74 | + <div class="content-titleZone flex justify-between align-center"> | |
75 | + <p class="box-title"> | |
76 | + 컬럼 매칭 | |
77 | + <small> | |
78 | + ( 매칭 : {{ matchs.matchCnt }}, 비매칭 : | |
79 | + {{ matchs.unMatchCnt }} | |
80 | + ) | |
81 | + </small> | |
82 | + </p> | |
83 | + </div> | |
84 | + <div | |
85 | + class="columncontBox" | |
86 | + style="height: 620px; overflow-y: auto" | |
87 | + > | |
88 | + <table> | |
89 | + <colgroup> | |
90 | + <col style="width: 50%" /> | |
91 | + </colgroup> | |
92 | + <tbody> | |
93 | + <tr v-for="(column, index) of columnMatchList" :key="index"> | |
94 | + <td> | |
95 | + <p class="col-data"> | |
96 | + {{ column.columnOriginalName }} | |
97 | + </p> | |
98 | + </td> | |
99 | + <td | |
100 | + draggable="true" | |
101 | + @dragstart="fnMoveStart(column)" | |
102 | + @dragover="fnOnDragover" | |
103 | + @drop="fnChangeMatchData('drop', index)" | |
104 | + @dblclick="fnChangeMatchData('doubleClick', index)" | |
105 | + > | |
106 | + <p class="col-data"> | |
107 | + {{ column.columnMatchName }} | |
108 | + </p> | |
109 | + </td> | |
110 | + </tr> | |
111 | + </tbody> | |
112 | + </table> | |
113 | + </div> | |
114 | + </div> | |
115 | + <div class="flex50 pr0"> | |
116 | + <div class="content-titleZone flex justify-between align-center"> | |
117 | + <p class="box-title">데이터 컬럼</p> | |
118 | + </div> | |
119 | + <div | |
120 | + class="columncontBox" | |
121 | + style="height: 620px; overflow-y: auto" | |
122 | + > | |
123 | + <template v-if="sourceColumnDatas.length > 0"> | |
124 | + <ul> | |
125 | + <li | |
126 | + v-for="(source, index) of sourceColumnDatas" | |
127 | + :key="index" | |
128 | + :class="{ 'col-data': true, disabled: !source.view }" | |
129 | + :draggable="source.view" | |
130 | + @dragstart="fnOnDragstart(source.displyColumnNm, index)" | |
131 | + > | |
132 | + {{ source.displyColumnNm }} | |
133 | + </li> | |
134 | + </ul> | |
135 | + </template> | |
136 | + <p v-else class="text-ct">등록된 데이터가 없습니다.</p> | |
137 | + </div> | |
138 | + </div> | |
139 | + </div> | |
140 | + </div> | |
141 | + </div> | |
142 | + <div class="modal-end flex justify-end"> | |
143 | + <button type="button" class="blue-btn small-btn" @click="fnSave"> | |
144 | + 저장 | |
145 | + </button> | |
146 | + <button | |
147 | + type="button" | |
148 | + class="blue-border-btn small-btn" | |
149 | + @click="$emit('fnCloseModal')" | |
150 | + > | |
151 | + 취소 | |
152 | + </button> | |
153 | + </div> | |
154 | + </div> | |
155 | + <datasetSelecter | |
156 | + :openPopup="isSelectModalOpen" | |
157 | + @closePopup="fnCloseDatasetSelectModal" | |
158 | + @saveNodeData="fnUpdateDatasetSelect" | |
159 | + /> | |
160 | + </div> | |
161 | +</template> | |
162 | + | |
163 | +<script> | |
164 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
165 | +import { mdiClose } from "@mdi/js"; | |
166 | +import axios from "axios"; | |
167 | +import datasetSelecter from "./datasetSelecter.vue"; | |
168 | + | |
169 | +export default { | |
170 | + components: { | |
171 | + SvgIcon: SvgIcon, | |
172 | + datasetSelecter: datasetSelecter, | |
173 | + }, | |
174 | + props: { | |
175 | + openPopup: { | |
176 | + type: Boolean, | |
177 | + default: false, | |
178 | + }, | |
179 | + jobItem: { | |
180 | + type: Object, | |
181 | + }, | |
182 | + nodes: { | |
183 | + type: Array, | |
184 | + required: true, | |
185 | + }, | |
186 | + edges: { | |
187 | + type: Array, | |
188 | + required: true, | |
189 | + }, | |
190 | + }, | |
191 | + data() { | |
192 | + return { | |
193 | + isModalOpen: this.openPopup, | |
194 | + closePath: mdiClose, | |
195 | + jobItm: this.jobItem, | |
196 | + nodeList: this.nodes, | |
197 | + edgeList: this.edges, | |
198 | + | |
199 | + // 연결된 이전 노드 | |
200 | + prevNode: {}, | |
201 | + | |
202 | + // 데이터셋 선택 | |
203 | + isSelectModalOpen: false, | |
204 | + | |
205 | + // 컬럼 매칭 | |
206 | + columnMatchList: [], | |
207 | + sourceColumnDatas: [], | |
208 | + targetColumnDatas: [], | |
209 | + changeColList: [], | |
210 | + matchs: { | |
211 | + matchCnt: 0, | |
212 | + unMatchCnt: 0, | |
213 | + }, | |
214 | + dragData: { | |
215 | + source: null, | |
216 | + sourceIdx: null, | |
217 | + }, | |
218 | + datapostId: null, | |
219 | + }; | |
220 | + }, | |
221 | + mounted() { | |
222 | + this.init(); // 초기화 | |
223 | + // 타겟 데이터가 있는 경우 | |
224 | + if (this.jobItm.itm.hasOwnProperty("item")) { | |
225 | + if (this.jobItm.itm.item != null) { | |
226 | + this.datapostId = this.jobItm.itm.item; | |
227 | + this.fnSetupRead(); | |
228 | + } | |
229 | + } | |
230 | + }, | |
231 | + watch: { | |
232 | + columnMatchList: { | |
233 | + deep: true, | |
234 | + handler() { | |
235 | + this.fnMatchList(); // 매치 리스트 | |
236 | + }, | |
237 | + }, | |
238 | + }, | |
239 | + methods: { | |
240 | + // 초기화 | |
241 | + init() { | |
242 | + // 연결된 노드 정보 조회(최초 1회만 실행) | |
243 | + for (let edge of this.edgeList) { | |
244 | + if (edge.target == this.jobItm.id) { | |
245 | + for (let node of this.nodeList) { | |
246 | + if (node.id == edge.source) { | |
247 | + this.prevNode = node; | |
248 | + this.fnPrevNodeDataRead(); // 노드 정보 조회 후, 컬럼 데이터 가져오기 | |
249 | + return; | |
250 | + } | |
251 | + } | |
252 | + } | |
253 | + } | |
254 | + this.$showAlert( | |
255 | + "에러 발생", | |
256 | + "DATASET_UPDATE를 설정하기 전, 먼저 노드를 연결해 주세요." | |
257 | + ); | |
258 | + this.$emit("fnCloseModal"); // 모달 닫기 | |
259 | + }, | |
260 | + // 소스 데이터 조회 | |
261 | + fnPrevNodeDataRead() { | |
262 | + this.sourceColumnDatas = []; // 초기화 | |
263 | + if (!this.$isEmpty(this.prevNode.dataTable)) { | |
264 | + let columnDatas = this.prevNode.dataTable.columnDatas; | |
265 | + for (let data of columnDatas) { | |
266 | + data.view = true; | |
267 | + } | |
268 | + this.sourceColumnDatas = columnDatas; | |
269 | + } else { | |
270 | + this.$showAlert( | |
271 | + "에러 발생", | |
272 | + "DATASET_UPDATE에 연결된 노드의 설정을 마쳐 주세요." | |
273 | + ); | |
274 | + } | |
275 | + }, | |
276 | + // 타겟 데이터 조회 후 처리 | |
277 | + fnSetTargetData(columnDatas) { | |
278 | + // 타겟 컬럼의 ts_row는 조작 불가하므로 목록에서 제거 | |
279 | + columnDatas = columnDatas.filter( | |
280 | + (column) => column.orginlColumnNm !== "ts_row" | |
281 | + ); | |
282 | + this.targetColumnDatas = columnDatas; // 타겟 데이터 저장 | |
283 | + | |
284 | + this.columnMatchList = []; // 초기화 | |
285 | + for (let i = 0; i < columnDatas.length; i++) { | |
286 | + this.columnMatchList.push({ | |
287 | + targetIdx: i, | |
288 | + sourceIdx: null, | |
289 | + columnOriginalName: columnDatas[i].displyColumnNm, | |
290 | + columnMatchName: null, | |
291 | + }); | |
292 | + } | |
293 | + }, | |
294 | + // 타겟 데이터 조회 | |
295 | + fnSetupRead() { | |
296 | + // 데이터 세팅 | |
297 | + let data = { | |
298 | + datapost_id: this.datapostId, | |
299 | + }; | |
300 | + // 실행 | |
301 | + axios({ | |
302 | + url: "/dataset/getDataTableInfo.json", | |
303 | + method: "post", | |
304 | + headers: { "Content-Type": "application/json; charset=UTF-8" }, | |
305 | + data: data, | |
306 | + }) | |
307 | + .then((response) => { | |
308 | + let columnDatas = response.data.resultData.dataTable.columnDatas; | |
309 | + this.fnSetTargetData(columnDatas); // 타겟 데이터 조회 후 처리 | |
310 | + }) | |
311 | + .catch((error) => { | |
312 | + this.$showAlert( | |
313 | + "에러 발생", | |
314 | + "에러가 발생했습니다. 관리자에게 문의해 주세요." | |
315 | + ); | |
316 | + }); | |
317 | + }, | |
318 | + // 타겟 데이터 조회 | |
319 | + fnTargetDataRead() { | |
320 | + this.isSelectModalOpen = true; | |
321 | + }, | |
322 | + // 타겟 데이터 조회 저장 후 모달 닫기 | |
323 | + fnUpdateDatasetSelect(jobItem) { | |
324 | + this.datapostId = jobItem.itm.dataset_post_id; | |
325 | + this.fnSetTargetData(jobItem.dataTable.columnDatas); // 타겟 데이터 조회 후 처리 | |
326 | + this.fnCloseDatasetSelectModal(); // 타겟 데이터 조회 모달 닫기 | |
327 | + }, | |
328 | + // 타겟 데이터 조회 모달 닫기 | |
329 | + fnCloseDatasetSelectModal() { | |
330 | + this.isSelectModalOpen = false; | |
331 | + }, | |
332 | + // 매치 리스트 | |
333 | + fnMatchList() { | |
334 | + // 초기화 | |
335 | + for (let sourceColumn of this.sourceColumnDatas) { | |
336 | + sourceColumn.view = true; | |
337 | + } | |
338 | + // 컬럼 매칭 | |
339 | + let matchCnt = 0; | |
340 | + for (let col of this.columnMatchList) { | |
341 | + if ( | |
342 | + !this.$isEmpty(col.sourceIdx) && | |
343 | + !this.$isEmpty(col.columnMatchName) | |
344 | + ) { | |
345 | + matchCnt++; // 매칭 정보가 있는 경우 +1 | |
346 | + this.sourceColumnDatas[col.sourceIdx].view = false; // 드래그 막기 | |
347 | + } | |
348 | + } | |
349 | + // 컬럼 매칭 결과 조회 | |
350 | + this.matchs.matchCnt = matchCnt; | |
351 | + this.matchs.unMatchCnt = this.targetColumnDatas.length - matchCnt; | |
352 | + }, | |
353 | + | |
354 | + // 드래그 이벤트 | |
355 | + fnMoveStart(match) { | |
356 | + this.dragData.source = match.columnMatchName; | |
357 | + this.dragData.sourceIdx = match.sourceIdx; | |
358 | + }, | |
359 | + fnOnDragstart(displyName, index) { | |
360 | + this.dragData.source = displyName; | |
361 | + this.dragData.sourceIdx = index; | |
362 | + }, | |
363 | + fnOnDragover(event) { | |
364 | + event.preventDefault(); | |
365 | + }, | |
366 | + fnChangeMatchData(type, targetIdx) { | |
367 | + // 기존값 삭제 | |
368 | + for (let match of this.columnMatchList) { | |
369 | + if ( | |
370 | + match.sourceIdx == this.dragData.sourceIdx && | |
371 | + match.columnMatchName == this.dragData.source | |
372 | + ) { | |
373 | + match.sourceIdx = null; | |
374 | + match.columnMatchName = null; | |
375 | + } | |
376 | + } | |
377 | + // 추가 | |
378 | + for (let match of this.columnMatchList) { | |
379 | + if (match.targetIdx == targetIdx) { | |
380 | + if (type == "drop") { | |
381 | + match.sourceIdx = this.dragData.sourceIdx; | |
382 | + match.columnMatchName = this.dragData.source; | |
383 | + } else if (type == "doubleClick") { | |
384 | + match.sourceIdx = null; | |
385 | + match.columnMatchName = null; | |
386 | + } | |
387 | + break; | |
388 | + } | |
389 | + } | |
390 | + }, | |
391 | + | |
392 | + // 저장 버튼 | |
393 | + fnSave() { | |
394 | + var itm = Object.assign({}, this.$getDefaultJobGroup().jobItemGroup); | |
395 | + itm.item = this.datapostId; | |
396 | + itm.itemList = []; // 초기화 | |
397 | + for (let match of this.columnMatchList) { | |
398 | + itm.itemList.push({ | |
399 | + orginColumnNm: | |
400 | + match.targetIdx != null && match.columnOriginalName != null | |
401 | + ? match.targetIdx + "/" + match.columnOriginalName | |
402 | + : null, | |
403 | + targetColumnNm: | |
404 | + match.sourceIdx != null && match.columnMatchName != null | |
405 | + ? match.sourceIdx + "/" + match.columnMatchName | |
406 | + : null, | |
407 | + }); | |
408 | + } | |
409 | + this.jobItm.itm = itm; | |
410 | + this.$emit("fnSaveSetup", this.jobItm); | |
411 | + }, | |
412 | + }, | |
413 | +}; | |
414 | +</script> | |
415 | + | |
416 | +<style scoped> | |
417 | +.columncontBox { | |
418 | + background: #f5f5f5; | |
419 | + border-radius: 10px; | |
420 | +} | |
421 | +.columncontBox tr { | |
422 | + padding: 8px; | |
423 | +} | |
424 | +.columncontBox ul .col-data { | |
425 | + margin: 8px; | |
426 | +} | |
427 | +.columncontBox .col-data { | |
428 | + border: 1px solid #dbdbdb; | |
429 | + padding: 5px 10px; | |
430 | + background-color: var(--color-white); | |
431 | + border-radius: 10px; | |
432 | + cursor: move; | |
433 | + font-size: 1.3rem; | |
434 | + height: 34px; | |
435 | +} | |
436 | +.columncontBox .col-data.disabled { | |
437 | + background-color: #f5f5f5; | |
438 | +} | |
439 | +</style>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/connection/itm/fileDataRead.vue
... | ... | @@ -0,0 +1,382 @@ |
1 | +<template> | |
2 | + <div> | |
3 | + <div | |
4 | + v-show="!preview" | |
5 | + class="data_control flexCenter mt10" | |
6 | + style="display: flex; justify-content: space-evenly" | |
7 | + > | |
8 | + <div class="col-4 fl alignC"> | |
9 | + <label for="datastart"> 데이터 시작 행 </label> | |
10 | + <div class="flexBox" style="align-items: center !important"> | |
11 | + <input | |
12 | + type="text" | |
13 | + name="datastart" | |
14 | + v-model="rowIndex" | |
15 | + @blur="updateRowIndex" | |
16 | + @keyup.enter="updateRowIndex" | |
17 | + /> | |
18 | + <div class="updownbox flexBox"> | |
19 | + <input | |
20 | + type="button" | |
21 | + value="▲" | |
22 | + class="input_up" | |
23 | + @click="dataTableInfo.startRowIndex++" | |
24 | + /> | |
25 | + <input | |
26 | + type="button" | |
27 | + value="▼" | |
28 | + class="input_down" | |
29 | + @click="dataTableInfo.startRowIndex--" | |
30 | + /> | |
31 | + </div> | |
32 | + </div> | |
33 | + </div> | |
34 | + <div class="col-4 fl alignL"> | |
35 | + <label for="dataend"> 데이터 시작 열 </label> | |
36 | + <div class="flexBox" style="align-items: center !important"> | |
37 | + <input | |
38 | + type="text" | |
39 | + name="dataend" | |
40 | + v-model="cellIndex" | |
41 | + @blur="updateCellIndex" | |
42 | + @keyup.enter="updateCellIndex" | |
43 | + /> | |
44 | + <div class="updownbox flexBox"> | |
45 | + <input | |
46 | + type="button" | |
47 | + value="▲" | |
48 | + class="input_up" | |
49 | + @click="dataTableInfo.startCellIndex++" | |
50 | + /> | |
51 | + <input | |
52 | + type="button" | |
53 | + value="▼" | |
54 | + class="input_down" | |
55 | + @click="dataTableInfo.startCellIndex--" | |
56 | + /> | |
57 | + </div> | |
58 | + </div> | |
59 | + </div> | |
60 | + </div> | |
61 | + <div class="datatableInfoBox" style="max-height: 550px; overflow: auto"> | |
62 | + <div class="table-zone"> | |
63 | + <!-- <div class="list-info flex justify-between align-center"> | |
64 | + <div class="count-zone"> | |
65 | + <p>총 <span>40</span>건 중 <span>01</span>건 선택</p> | |
66 | + </div> | |
67 | + </div> --> | |
68 | + <table class="list-table"> | |
69 | + <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 --> | |
70 | + <colgroup> | |
71 | + <col style="width: 10%" /> | |
72 | + <col | |
73 | + v-for="(info, i) in dataTableInfo.changeColumnDatas" | |
74 | + :key="i" | |
75 | + :style="changeWidth()" | |
76 | + /> | |
77 | + </colgroup> | |
78 | + <thead> | |
79 | + <tr> | |
80 | + <th>No</th> | |
81 | + <th | |
82 | + v-for="(info, i) in dataTableInfo.changeColumnDatas" | |
83 | + :key="i" | |
84 | + :style=" | |
85 | + i < dataTableInfo.startCellIndex | |
86 | + ? 'background-color: #dfdfdf;' | |
87 | + : null | |
88 | + " | |
89 | + > | |
90 | + [{{ i + 1 }}] | |
91 | + </th> | |
92 | + </tr> | |
93 | + </thead> | |
94 | + <!-- <tbody> | |
95 | + <tr> | |
96 | + <th :class="{ selected: getRowDataColumnIndex < 0 }">[1]</th> | |
97 | + <td :class="{ | |
98 | + selected: getRowDataColumnIndex < 0 | |
99 | + , disabled: getStartRowIndex >= 0 || dataTableInfo.startCellIndex > j | |
100 | + }" | |
101 | + v-for="(info, j) in dataTableInfo.changeColumnDatas"> | |
102 | + {{ dataTableInfo.columnDatas[j].displyColumnNm }} | |
103 | + </td> | |
104 | + </tr> | |
105 | + <tr v-for="(cells, i) in dataTableInfo.rowData" v-if="i < viewCount"> | |
106 | + <th :class="{ selected: getRowDataColumnIndex == i }">[{{ i + 2 }}]</th> | |
107 | + <td :class="{ | |
108 | + selected: getRowDataColumnIndex == i | |
109 | + , disabled: getStartRowIndex > i || dataTableInfo.startCellIndex > j | |
110 | + }" | |
111 | + v-for="(value, j) in cells" :title="value"> | |
112 | + {{ value }} | |
113 | + </td> | |
114 | + </tr> | |
115 | + <tr v-if="postList === 0"> | |
116 | + <td colspan="5" class="no-list">검색조건에 해당하는 데이터가 없습니다.</td> | |
117 | + </tr> | |
118 | + </tbody> --> | |
119 | + <tbody> | |
120 | + <tr> | |
121 | + <th>[1]</th> | |
122 | + <td | |
123 | + v-for="(info, j) in dataTableInfo.changeColumnDatas" | |
124 | + :key="j" | |
125 | + :style=" | |
126 | + j < dataTableInfo.startCellIndex | |
127 | + ? 'background-color: #dfdfdf;' | |
128 | + : 'background-color: #fdd2d2;' | |
129 | + " | |
130 | + > | |
131 | + {{ dataTableInfo.columnDatas[j].displyColumnNm }} | |
132 | + </td> | |
133 | + </tr> | |
134 | + <tr | |
135 | + v-for="(cells, i) in dataTableInfo.rowData" | |
136 | + :key="i" | |
137 | + :style=" | |
138 | + i + 1 < dataTableInfo.startRowIndex | |
139 | + ? 'background-color: #dfdfdf;' | |
140 | + : null | |
141 | + " | |
142 | + > | |
143 | + <th>[{{ i + 2 }}]</th> | |
144 | + <td | |
145 | + v-for="(value, j) in cells" | |
146 | + :key="j" | |
147 | + :title="value" | |
148 | + :style=" | |
149 | + j < dataTableInfo.startCellIndex | |
150 | + ? 'background-color: #dfdfdf;' | |
151 | + : null | |
152 | + " | |
153 | + > | |
154 | + {{ value }} | |
155 | + </td> | |
156 | + </tr> | |
157 | + <tr v-if="postList === 0"> | |
158 | + <td colspan="5" class="no-list"> | |
159 | + 검색조건에 해당하는 데이터가 없습니다. | |
160 | + </td> | |
161 | + </tr> | |
162 | + </tbody> | |
163 | + </table> | |
164 | + </div> | |
165 | + </div> | |
166 | + </div> | |
167 | +</template> | |
168 | + | |
169 | + | |
170 | +<script> | |
171 | +import _ from "lodash"; | |
172 | + | |
173 | +export default { | |
174 | + name: "file-data-read", | |
175 | + props: { | |
176 | + isLoading: { | |
177 | + type: Boolean, | |
178 | + // default: true, | |
179 | + }, | |
180 | + dataTable: { | |
181 | + type: Object, | |
182 | + default: {}, | |
183 | + }, | |
184 | + // 미리보기, 데이터셋 설정 화면 구분 | |
185 | + preview: { | |
186 | + type: Boolean, | |
187 | + default: false, | |
188 | + }, | |
189 | + }, | |
190 | + data() { | |
191 | + return { | |
192 | + dataTableInfo: this.dataTable, | |
193 | + columnIndex: this.dataTable.rowDataColumnIndex, | |
194 | + rowIndex: this.dataTable.startRowIndex + 1, | |
195 | + cellIndex: this.dataTable.startCellIndex + 1, | |
196 | + }; | |
197 | + }, | |
198 | + methods: { | |
199 | + // 데이터 확정후 창닫기 | |
200 | + displayIndex: function (index) { | |
201 | + if (index) { | |
202 | + return index + 1; | |
203 | + } else { | |
204 | + return 1; | |
205 | + } | |
206 | + }, | |
207 | + | |
208 | + // 데이터 테이블 넓이 조정 | |
209 | + changeWidth: function () { | |
210 | + return `width: ${90 / this.dataTableInfo.changeColumnDatas.length}%`; | |
211 | + }, | |
212 | + | |
213 | + // 데이터 시작 행 변경 | |
214 | + updateRowIndex() { | |
215 | + this.dataTableInfo.startRowIndex = this.rowIndex - 1; // 엔터나 입력창 바깥 클릭 시 값 업데이트 | |
216 | + }, | |
217 | + | |
218 | + // 데이터 시작 열 변경 | |
219 | + updateCellIndex() { | |
220 | + this.dataTableInfo.startCellIndex = this.cellIndex - 1; // 엔터나 입력창 바깥 클릭 시 값 업데이트 | |
221 | + }, | |
222 | + }, | |
223 | + watch: { | |
224 | + dataTable: function (v) { | |
225 | + //데이터 테이블 정보 변경 시(다른 데이터 셋 선택시) | |
226 | + this.dataTableInfo = _.cloneDeep(v); | |
227 | + // 데이터 테이블 변경 이력이 없을 시 초기 세팅 | |
228 | + if (!v.columnNameChange) { | |
229 | + for (let i = 0; i < this.dataTableInfo.columnDatas.length; i++) { | |
230 | + // this.dataTableInfo.changeColumnDatas[i].columnNm = "col"+i;//*columnNm = 저장될 컬럼명 | |
231 | + this.dataTableInfo.columnDatas[i].columnNm = "col" + i; //*columnNm = 저장될 컬럼명 | |
232 | + | |
233 | + //*displyColumnNm | |
234 | + if (this.dataTableInfo.columnDatas[i].orginlColumnNm) { | |
235 | + //원본 cell의 value가 빈값이 아닐 때 | |
236 | + //displyColumnNm = 원본 cell의 value | |
237 | + // this.dataTableInfo.changeColumnDatas[i].displyColumnNm = this.dataTableInfo.columnDatas[i].orginlColumnNm; | |
238 | + this.dataTableInfo.columnDatas[i].displyColumnNm = | |
239 | + this.dataTableInfo.columnDatas[i].orginlColumnNm; | |
240 | + } else { | |
241 | + //원본 cell의 value가 빈값일 때 | |
242 | + //displyColumnNm = columnNm(DB에 생성될 컬럼명) | |
243 | + // this.dataTableInfo.changeColumnDatas[i].displyColumnNm = "col"+i; | |
244 | + this.dataTableInfo.columnDatas[i].displyColumnNm = "col" + i; | |
245 | + } | |
246 | + } | |
247 | + this.dataTableInfo.originalColumnDatas = _.cloneDeep( | |
248 | + this.dataTableInfo.columnDatas | |
249 | + ); // 원본 컬럼행 데이터 저장 | |
250 | + this.dataTableInfo.changeColumnDatas = _.cloneDeep( | |
251 | + this.dataTableInfo.columnDatas | |
252 | + ); // 변경된 컬럼행 데이터 저장 (원본도 변경하는데 왜 필요한거지?) | |
253 | + } | |
254 | + this.$emit("update:isLoading", false); // 데이터테이블 복사 완료 시 로딩 해제 | |
255 | + }, | |
256 | + | |
257 | + //데이터 시작 행의 Index | |
258 | + "dataTableInfo.startRowIndex": function (value) { | |
259 | + this.rowIndex = value + 1; | |
260 | + //value(Index)값이 숫자가 아닐 때 => 0 | |
261 | + try { | |
262 | + if (typeof parseInt(value) !== "number") { | |
263 | + this.dataTableInfo.startRowIndex = 0; | |
264 | + this.rowIndex = this.dataTableInfo.startRowIndex + 1; | |
265 | + return; | |
266 | + } | |
267 | + } catch (e) { | |
268 | + this.dataTableInfo.startRowIndex = 0; | |
269 | + this.rowIndex = this.dataTableInfo.startRowIndex + 1; | |
270 | + return; | |
271 | + } | |
272 | + | |
273 | + let maxIndex = this.dataTableInfo.rowData.length; //컬럼(필드)행의 최대 Index = 데이터의 행 수 - 1 | |
274 | + | |
275 | + if (value < 0) { | |
276 | + //value(Index)가 데이터 시작 행의 Index보다 작을 때 | |
277 | + this.dataTableInfo.startRowIndex = 0; | |
278 | + this.rowIndex = this.dataTableInfo.startRowIndex + 1; | |
279 | + | |
280 | + this.dataTableInfo.rowDataColumnIndex = 0; | |
281 | + } else if (value > maxIndex) { | |
282 | + //value(Index)가 최대 Index 보다 클 때 | |
283 | + this.dataTableInfo.startRowIndex = maxIndex; | |
284 | + this.rowIndex = maxIndex + 1; | |
285 | + | |
286 | + this.dataTableInfo.rowDataColumnIndex = maxIndex; | |
287 | + } else { | |
288 | + //정상적으로 바뀔 때 | |
289 | + this.dataTableInfo.changeColumnDatas = _.cloneDeep( | |
290 | + this.dataTableInfo.columnDatas | |
291 | + ); | |
292 | + | |
293 | + if (value > 0) { | |
294 | + //원본 컬럼으로 바뀌는게 아닐 때 | |
295 | + //rowData에서 row정보를 받아옴 | |
296 | + let row = this.dataTableInfo.rowData[value - 1]; | |
297 | + for ( | |
298 | + let i = 0; | |
299 | + i < this.dataTableInfo.changeColumnDatas.length; | |
300 | + i++ | |
301 | + ) { | |
302 | + this.dataTableInfo.changeColumnDatas[i].orginlColumnNm = row[i]; //*orginlColumnNm = 원본 cell의 value | |
303 | + this.dataTableInfo.changeColumnDatas[i].columnNm = "col" + i; //*columnNm = 저장될 컬럼명 | |
304 | + this.dataTableInfo.columnDatas[i].orginlColumnNm = row[i]; //*orginlColumnNm = 원본 cell의 value | |
305 | + this.dataTableInfo.columnDatas[i].columnNm = "col" + i; //*columnNm = 저장될 컬럼명 | |
306 | + | |
307 | + //*displyColumnNm | |
308 | + if (row[i]) { | |
309 | + //원본 cell의 value가 빈값이 아닐 때 | |
310 | + //displyColumnNm = 원본 cell의 value | |
311 | + this.dataTableInfo.changeColumnDatas[i].displyColumnNm = row[i]; | |
312 | + this.dataTableInfo.columnDatas[i].displyColumnNm = row[i]; | |
313 | + } else { | |
314 | + //원본 cell의 value가 빈값일 때 | |
315 | + //displyColumnNm = columnNm(DB에 생성될 컬럼명) | |
316 | + this.dataTableInfo.changeColumnDatas[i].displyColumnNm = | |
317 | + "col" + i; | |
318 | + this.dataTableInfo.columnDatas[i].displyColumnNm = "col" + i; | |
319 | + } | |
320 | + } | |
321 | + } | |
322 | + //★ 메모상에서 컬럼 데이터 변경 여부 | |
323 | + this.dataTableInfo.columnNameChange = true; | |
324 | + } | |
325 | + this.dataTableInfo.rowDataColumnIndex = this.dataTableInfo.startRowIndex; | |
326 | + | |
327 | + // 데이터 시작 행이 0일 때, 초기 컬럼 데이터로 변경 | |
328 | + if (this.dataTableInfo.startRowIndex == 0) { | |
329 | + this.dataTableInfo.columnDatas = _.cloneDeep( | |
330 | + this.dataTableInfo.originalColumnDatas | |
331 | + ); | |
332 | + } | |
333 | + | |
334 | + this.$emit("index-change", this.dataTableInfo); | |
335 | + }, | |
336 | + | |
337 | + //데이터 시작 열의 Index | |
338 | + "dataTableInfo.startCellIndex": function (value) { | |
339 | + this.cellIndex = value + 1; | |
340 | + //value(Index)값이 숫자가 아닐 때 => 0 | |
341 | + try { | |
342 | + if (typeof parseInt(value) !== "number") { | |
343 | + this.dataTableInfo.startCellIndex = 0; | |
344 | + this.cellIndex = this.dataTableInfo.startCellIndex + 1; | |
345 | + return; | |
346 | + } | |
347 | + } catch (e) { | |
348 | + this.dataTableInfo.startCellIndex = 0; | |
349 | + this.cellIndex = this.dataTableInfo.startCellIndex + 1; | |
350 | + return; | |
351 | + } | |
352 | + | |
353 | + //데이터 시작 열 최대 Index = 데이터의 컬럼 갯수 | |
354 | + let maxIndex = this.dataTableInfo.columnDatas.length - 1; | |
355 | + if (value < 0) { | |
356 | + //value(Index)가 0보다 작을 때 | |
357 | + this.dataTableInfo.startCellIndex = 0; | |
358 | + this.cellIndex = this.dataTableInfo.startCellIndex + 1; | |
359 | + return; | |
360 | + } else if (value > maxIndex) { | |
361 | + //value(Index)가 최대 Index 보다 클 때 | |
362 | + this.dataTableInfo.startCellIndex = maxIndex; | |
363 | + this.cellIndex = maxIndex + 1; | |
364 | + return; | |
365 | + } | |
366 | + this.$emit("index-change", this.dataTableInfo); | |
367 | + }, | |
368 | + }, | |
369 | + computed: { | |
370 | + //column Index에 의한 컬럼 사용 타입 변경 => Index 미만 컬럼은 사용 불가 | |
371 | + columnUseChange: function (columnDatas, index) { | |
372 | + for (let i = 0; i < columnDatas.length; i++) { | |
373 | + if (i > index - 1) { | |
374 | + columnDatas[i].useAt = true; | |
375 | + } else { | |
376 | + columnDatas[i].useAt = false; | |
377 | + } | |
378 | + } | |
379 | + }, | |
380 | + }, | |
381 | +}; | |
382 | +</script> |
+++ client/views/component/connection/itm/fileSelect.vue
... | ... | @@ -0,0 +1,1029 @@ |
1 | +<template> | |
2 | + <div | |
3 | + v-show="isModalOpen" | |
4 | + class="modal-wrapper" | |
5 | + :style="isLoading ? { cursor: 'wait' } : {}" | |
6 | + > | |
7 | + <div v-if="isLoading" class="loading-overlay"> | |
8 | + <div class="loading-div"> | |
9 | + <span>LOADING </span> | |
10 | + <span class="anima">.</span> | |
11 | + <span class="anima">.</span> | |
12 | + <span class="anima">.</span> | |
13 | + </div> | |
14 | + </div> | |
15 | + <div class="modal-container list-modal"> | |
16 | + <div class="modal-title"> | |
17 | + <div class="flex justify-between align-center"> | |
18 | + <h2 v-show="modeInfo == 'fileRead'">파일 읽기</h2> | |
19 | + <h2 v-show="modeInfo == 'fileWrite'">파일 쓰기</h2> | |
20 | + <button class="close-btn" @click="cancelModal()">X</button> | |
21 | + </div> | |
22 | + </div> | |
23 | + <div class="modal-content-monthly" v-show="currentPage == 1"> | |
24 | + <div class="content-wrap"> | |
25 | + <div class="content-box flex justify-between"> | |
26 | + <div class="flex20 content-box"> | |
27 | + <div class="left-content flex100 content-box"> | |
28 | + <div class="content-box"> | |
29 | + <div class="mb10"> | |
30 | + <div class="content-titleZone"> | |
31 | + <p class="box-title">호스트 선택</p> | |
32 | + </div> | |
33 | + <div class="flex align-center justify-between no-gutter"> | |
34 | + <div class="flex100"> | |
35 | + <select | |
36 | + name="" | |
37 | + id="" | |
38 | + class="only full-select" | |
39 | + v-model="selectedHostCode" | |
40 | + > | |
41 | + <option :value="null" disabled>선택</option> | |
42 | + <option | |
43 | + v-for="(host, idx) in hostList" | |
44 | + :key="idx" | |
45 | + :value="host.host_code" | |
46 | + > | |
47 | + {{ host.host_nm + " - (" + host.host_ip + ")" }} | |
48 | + </option> | |
49 | + </select> | |
50 | + </div> | |
51 | + <div class="flex100"> | |
52 | + <button | |
53 | + class="blue-border-btn large-btn" | |
54 | + @click="connectionConfirm()" | |
55 | + > | |
56 | + 연결 | |
57 | + </button> | |
58 | + </div> | |
59 | + </div> | |
60 | + </div> | |
61 | + <div class="file-tree-zone"> | |
62 | + <div | |
63 | + class="content-titleZone flex justify-between align-center" | |
64 | + > | |
65 | + <p class="box-title">폴더 리스트</p> | |
66 | + </div> | |
67 | + <div class="file-zone overflow-y"> | |
68 | + <ul class="tree-wrap"> | |
69 | + <TreeItem | |
70 | + :connection="connection" | |
71 | + :selectedNode="selectedNode" | |
72 | + :selectItem="currentItem" | |
73 | + v-for="(item, idx) in nodes" | |
74 | + :item="item" | |
75 | + :idx="item.id" | |
76 | + :key="idx" | |
77 | + @selectFolder="selectFolder" | |
78 | + @isLoading="handleIsLoading" | |
79 | + @selectItem="handleSelectItem" | |
80 | + /> | |
81 | + </ul> | |
82 | + </div> | |
83 | + </div> | |
84 | + </div> | |
85 | + </div> | |
86 | + </div> | |
87 | + <div class="flex80 content-box"> | |
88 | + <div class="right-content flex100"> | |
89 | + <div class="flex-column justify-between"> | |
90 | + <div class="flex justify-between align-center no-gutter"> | |
91 | + <div class="flex justify-end flex100"> | |
92 | + <div class="search-bar"> | |
93 | + <div class="flex justify-end align-center"> | |
94 | + <select | |
95 | + name="" | |
96 | + id="" | |
97 | + class="square-select" | |
98 | + v-model="searchType" | |
99 | + > | |
100 | + <option value="all">전체</option> | |
101 | + <option value="folder">현재폴더</option> | |
102 | + </select> | |
103 | + <div class="search-square"> | |
104 | + <div | |
105 | + class="flex justify-end align-center no-gutter" | |
106 | + > | |
107 | + <input | |
108 | + type="text" | |
109 | + class="square-input flex90" | |
110 | + placeholder="Search" | |
111 | + v-model="searchText" | |
112 | + @keyup.enter="searchFiles()" | |
113 | + /> | |
114 | + <button class="square-button blue-btn flex10"> | |
115 | + <svg-icon | |
116 | + type="mdi" | |
117 | + :path="searchPath" | |
118 | + class="square-icon" | |
119 | + @click="searchFiles()" | |
120 | + ></svg-icon> | |
121 | + </button> | |
122 | + </div> | |
123 | + </div> | |
124 | + </div> | |
125 | + </div> | |
126 | + </div> | |
127 | + </div> | |
128 | + <div class="content-zone" style="overflow-y: auto"> | |
129 | + <div class="table-zone file-table"> | |
130 | + <div class="list-info flex justify-between align-center"> | |
131 | + <div class="count-zone"> | |
132 | + <p> | |
133 | + 총 | |
134 | + <span v-if="search.totalRows != 0">{{ | |
135 | + search.totalRows | |
136 | + }}</span> | |
137 | + <span v-else>0</span> | |
138 | + 건 | |
139 | + </p> | |
140 | + <p> | |
141 | + 현재경로 : <span>{{ connection.path }}</span> | |
142 | + </p> | |
143 | + </div> | |
144 | + </div> | |
145 | + <table class="list-table"> | |
146 | + <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 --> | |
147 | + <colgroup> | |
148 | + <col style="width: 5%" /> | |
149 | + <col style="width: 45%" /> | |
150 | + <col style="width: 10%" /> | |
151 | + <col style="width: 20%" /> | |
152 | + <col style="width: 10%" /> | |
153 | + <col style="width: 10%" /> | |
154 | + </colgroup> | |
155 | + <thead> | |
156 | + <tr> | |
157 | + <th></th> | |
158 | + <th>이름</th> | |
159 | + <th>타입</th> | |
160 | + <th>마지막 수정</th> | |
161 | + <th>크기</th> | |
162 | + <th>관리</th> | |
163 | + </tr> | |
164 | + </thead> | |
165 | + <tbody> | |
166 | + <tr | |
167 | + v-for="(file, index) in fileList" | |
168 | + :key="index" | |
169 | + @click="selectFileList(file)" | |
170 | + > | |
171 | + <td | |
172 | + v-if="file.text != '상위폴더로 이동'" | |
173 | + @click.stop="" | |
174 | + ></td> | |
175 | + <td v-else @click.stop=""></td> | |
176 | + <td> | |
177 | + <div class="text-lf"> | |
178 | + <span v-if="file.extension === 'txt'" | |
179 | + ><img | |
180 | + src="../../../../resources/img/icon/txt.png" | |
181 | + ref="" | |
182 | + alt="" | |
183 | + /></span> | |
184 | + <span v-else-if="file.extension === 'pdf'" | |
185 | + ><img | |
186 | + src="../../../../resources/img/icon/pdf.png" | |
187 | + alt="" | |
188 | + /></span> | |
189 | + <span | |
190 | + v-else-if=" | |
191 | + file.extension === 'pptx' || | |
192 | + file.extension === 'ppt' | |
193 | + " | |
194 | + ><img | |
195 | + src="../../../../resources/img/icon/ppt.png" | |
196 | + alt="" | |
197 | + /></span> | |
198 | + <span v-else-if="file.extension === 'hwp'" | |
199 | + ><img | |
200 | + src="../../../../resources/img/icon/hwp.png" | |
201 | + alt="" | |
202 | + /></span> | |
203 | + <span | |
204 | + v-else-if=" | |
205 | + file.extension === 'xls' || | |
206 | + file.extension === 'xlsx' | |
207 | + " | |
208 | + ><img | |
209 | + src="../../../../resources/img/icon/xls.png" | |
210 | + alt="" | |
211 | + /></span> | |
212 | + <span v-else-if="file.extension === 'jpg'" | |
213 | + ><img | |
214 | + src="../../../../resources/img/icon/img.png" | |
215 | + alt="" | |
216 | + /></span> | |
217 | + <span v-else-if="file.extension === 'png'" | |
218 | + ><img | |
219 | + src="../../../../resources/img/icon/img.png" | |
220 | + ref="" | |
221 | + alt="" | |
222 | + /></span> | |
223 | + <span v-else>  </span> | |
224 | + <span> {{ file.text }}</span> | |
225 | + </div> | |
226 | + </td> | |
227 | + <td>{{ file.extension }}</td> | |
228 | + <td>{{ file.lastUpdate }}</td> | |
229 | + <td v-if="file.size != 0"> | |
230 | + {{ $filters.bytesToSize(file.size) }} | |
231 | + </td> | |
232 | + <td v-else></td> | |
233 | + <td> | |
234 | + <div | |
235 | + v-if="!file.folder && modeInfo == 'fileWrite'" | |
236 | + > | |
237 | + <button | |
238 | + class="icon-btn orange-btn" | |
239 | + title="미리보기" | |
240 | + @click.stop="filePreview(file)" | |
241 | + > | |
242 | + <svg-icon | |
243 | + type="mdi" | |
244 | + :width="18" | |
245 | + :height="18" | |
246 | + :path="fileFindPath" | |
247 | + ></svg-icon> | |
248 | + </button> | |
249 | + </div> | |
250 | + <div v-else></div> | |
251 | + </td> | |
252 | + </tr> | |
253 | + </tbody> | |
254 | + </table> | |
255 | + </div> | |
256 | + </div> | |
257 | + </div> | |
258 | + </div> | |
259 | + </div> | |
260 | + </div> | |
261 | + </div> | |
262 | + <div v-if="modeInfo == 'fileRead'" class="table-zone"> | |
263 | + <table class="form-table"> | |
264 | + <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 --> | |
265 | + <colgroup> | |
266 | + <col style="width: 20%" /> | |
267 | + <col style="width: 80%" /> | |
268 | + </colgroup> | |
269 | + <tbody> | |
270 | + <tr> | |
271 | + <th>폴더명</th> | |
272 | + <td>{{ selectedFile["parent"] }}</td> | |
273 | + </tr> | |
274 | + <tr> | |
275 | + <th>파일명</th> | |
276 | + <td>{{ selectedFile["text"] }}</td> | |
277 | + </tr> | |
278 | + </tbody> | |
279 | + </table> | |
280 | + <div class="flex flex30 align-center justify-center"> | |
281 | + <div class="input-container flex"> | |
282 | + <label class="check-label"> | |
283 | + <input type="checkbox" name="check" class="custom-checkbox" /> | |
284 | + <span>마지막 파일만</span> | |
285 | + </label> | |
286 | + <label class="check-label"> | |
287 | + <input type="checkbox" name="check" class="custom-checkbox" /> | |
288 | + <span>업데이트일 이후</span> | |
289 | + </label> | |
290 | + </div> | |
291 | + </div> | |
292 | + </div> | |
293 | + <div v-else-if="modeInfo == 'fileWrite'" class="table-zone"> | |
294 | + <table class="form-table"> | |
295 | + <colgroup> | |
296 | + <col style="width: 10%" /> | |
297 | + <col style="width: 40%" /> | |
298 | + <col style="width: 10%" /> | |
299 | + <col style="width: 40%" /> | |
300 | + </colgroup> | |
301 | + <tbody> | |
302 | + <tr> | |
303 | + <th>폴더명</th> | |
304 | + <td>{{ selectedFile["parent"] }}</td> | |
305 | + <th>쓰기유형</th> | |
306 | + <td> | |
307 | + <select name="" id="" class="full-select"> | |
308 | + <option selected>엑셀</option> | |
309 | + </select> | |
310 | + </td> | |
311 | + </tr> | |
312 | + <tr> | |
313 | + <th>파일명</th> | |
314 | + <td> | |
315 | + <input | |
316 | + type="text" | |
317 | + name="" | |
318 | + id="" | |
319 | + class="full-input" | |
320 | + v-model="fileNameBeforeDot" | |
321 | + /> | |
322 | + </td> | |
323 | + <th>쓰기옵션</th> | |
324 | + <td> | |
325 | + <div class="flex flex30 align-center justify-center"> | |
326 | + <div class="input-container flex"> | |
327 | + <label class="radio-label"> | |
328 | + <input | |
329 | + type="radio" | |
330 | + name="radio" | |
331 | + checked="" | |
332 | + class="custom-radiobox" | |
333 | + v-model="selectedFile['streOptn']" | |
334 | + :value="false" | |
335 | + /> | |
336 | + <span>쓰기</span> | |
337 | + </label> | |
338 | + <label class="radio-label"> | |
339 | + <input | |
340 | + type="radio" | |
341 | + name="radio" | |
342 | + class="custom-radiobox" | |
343 | + v-model="selectedFile['streOptn']" | |
344 | + :value="true" | |
345 | + /> | |
346 | + <span>suffix</span> | |
347 | + </label> | |
348 | + </div> | |
349 | + </div> | |
350 | + </td> | |
351 | + </tr> | |
352 | + <tr v-show="selectedFile['streOptn']"> | |
353 | + <th>suffix</th> | |
354 | + <td> | |
355 | + <input | |
356 | + type="text" | |
357 | + name="" | |
358 | + id="" | |
359 | + class="full-input" | |
360 | + v-model="selectedFile['suffix']" | |
361 | + /> | |
362 | + </td> | |
363 | + <th>월변경</th> | |
364 | + <td> | |
365 | + <input | |
366 | + type="number" | |
367 | + name="" | |
368 | + id="" | |
369 | + class="full-input" | |
370 | + v-model="selectedFile['addMonth']" | |
371 | + /> | |
372 | + </td> | |
373 | + </tr> | |
374 | + </tbody> | |
375 | + </table> | |
376 | + </div> | |
377 | + </div> | |
378 | + <div class="modal-content-monthly" v-show="currentPage == 2"> | |
379 | + <div class="content-titleZone"> | |
380 | + <p v-show="modeInfo == 'fileRead'" class="box-title">데이터셋 설정</p> | |
381 | + <p v-show="modeInfo == 'fileWrite'" class="box-title"> | |
382 | + 데이터 미리보기 | |
383 | + </p> | |
384 | + </div> | |
385 | + <FileDataRead | |
386 | + v-if="isModalOpen" | |
387 | + v-model:isLoading="isLoading" | |
388 | + v-model:dataTable="dataTable" | |
389 | + :preview="preview" | |
390 | + @indexChange="indexChange" | |
391 | + ></FileDataRead> | |
392 | + </div> | |
393 | + <div class="modal-end flex justify-end"> | |
394 | + <button | |
395 | + class="blue-btn small-btn" | |
396 | + @click="setPage(1)" | |
397 | + v-show="currentPage == 2" | |
398 | + > | |
399 | + 이전 | |
400 | + </button> | |
401 | + <button | |
402 | + class="blue-btn small-btn" | |
403 | + @click="setPage(2)" | |
404 | + v-show="currentPage == 1 && modeInfo == 'fileRead'" | |
405 | + > | |
406 | + 다음 | |
407 | + </button> | |
408 | + <button | |
409 | + class="blue-btn small-btn" | |
410 | + @click="saveModal()" | |
411 | + v-show=" | |
412 | + (currentPage == 2 && modeInfo == 'fileRead') || | |
413 | + (currentPage == 1 && modeInfo == 'fileWrite') | |
414 | + " | |
415 | + > | |
416 | + 완료 | |
417 | + </button> | |
418 | + </div> | |
419 | + </div> | |
420 | + </div> | |
421 | +</template> | |
422 | + | |
423 | + | |
424 | +<script> | |
425 | +import axios from "axios"; | |
426 | +import TreeItem from "../../FileTree.vue"; | |
427 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
428 | +import FileDataRead from "./fileDataRead.vue"; | |
429 | +import { | |
430 | + mdiMagnify, | |
431 | + mdiClose, | |
432 | + mdiFileCog, | |
433 | + mdiDownload, | |
434 | + mdiFileFind, | |
435 | + mdiPlay, | |
436 | + mdiCancel, | |
437 | +} from "@mdi/js"; | |
438 | +import store from "../../../pages/AppStore"; | |
439 | +import _ from "lodash"; | |
440 | + | |
441 | +export default { | |
442 | + name: "database-connection", | |
443 | + props: { | |
444 | + openPopup: { | |
445 | + type: Boolean, | |
446 | + default: false, | |
447 | + }, | |
448 | + jobItem: { | |
449 | + type: Boolean, | |
450 | + default: null, | |
451 | + }, | |
452 | + mode: { | |
453 | + type: String, | |
454 | + default: null, // 읽기: fileRead, 쓰기: fileWrite | |
455 | + }, | |
456 | + }, | |
457 | + data() { | |
458 | + return { | |
459 | + // 로딩 상태 | |
460 | + isLoading: false, | |
461 | + | |
462 | + // 초기 접속 확인 | |
463 | + isInit: false, | |
464 | + | |
465 | + // 현재 모달창 관리 | |
466 | + isModalOpen: this.openPopup, | |
467 | + jobItm: this.jobItem, | |
468 | + modeInfo: this.mode, | |
469 | + | |
470 | + currentPage: 1, | |
471 | + searchPath: mdiMagnify, // 검색 아이콘 경로 | |
472 | + fileFindPath: mdiFileFind, // 미리보기 아이콘 경로 | |
473 | + | |
474 | + // 검색 객체 | |
475 | + search: this.$getDefaultSerchVO(), | |
476 | + search_data: this.$getDefaultSerchItem(null, "String"), | |
477 | + | |
478 | + selectedHost: {}, | |
479 | + selectedHostCode: null, | |
480 | + // 호스트 목록 | |
481 | + hostList: [], | |
482 | + // 부서-호스트 목록 | |
483 | + deptHostList: [], | |
484 | + connection: { | |
485 | + host_code: null, | |
486 | + path: null, | |
487 | + depth: null, | |
488 | + type: null, | |
489 | + }, | |
490 | + nodes: [], | |
491 | + selectedNode: null, | |
492 | + fileList: [], | |
493 | + | |
494 | + searchType: "all", | |
495 | + searchText: null, | |
496 | + | |
497 | + selectItem: null, | |
498 | + currentItem: { | |
499 | + id: null, | |
500 | + }, | |
501 | + | |
502 | + selectedFile: {}, | |
503 | + | |
504 | + // 초기 빈 데이터 테이블 | |
505 | + defaultDataTable: {}, | |
506 | + | |
507 | + // 임시 저장된 데이터 | |
508 | + tempJobItm: this.jobItem, | |
509 | + tempCurrentPage: 1, | |
510 | + tempSearch: this.$getDefaultSerchVO(), | |
511 | + tempSearch_data: this.$getDefaultSerchItem(null, "String"), | |
512 | + tempSearchText: null, | |
513 | + tempConnection: { | |
514 | + host_code: null, | |
515 | + path: null, | |
516 | + depth: null, | |
517 | + type: null, | |
518 | + }, | |
519 | + tempNodes: [], | |
520 | + tempSelectedNode: null, | |
521 | + tempFileList: [], | |
522 | + tempSelectedFile: {}, | |
523 | + tempSelectedHostCode: null, | |
524 | + tempDataTable: {}, | |
525 | + /** ************************************ 데이터 셋 읽기 ************************************ */ | |
526 | + dataTable: {}, | |
527 | + preview: false, | |
528 | + | |
529 | + fileNameBeforeDot: null, // 파일명(확장자 제외) | |
530 | + }; | |
531 | + }, | |
532 | + methods: { | |
533 | + // 모달 열기 | |
534 | + openModal: function () { | |
535 | + if (!this.isInit) { | |
536 | + // 데이터 초기화 | |
537 | + this.selectHosts(); | |
538 | + this.searchInit(); | |
539 | + /* this.jobItm = _.cloneDeep(this.tempJobItm); | |
540 | + this.currentPage = 1; | |
541 | + this.search = this.$getDefaultSerchVO(), | |
542 | + this.search_data = this.$getDefaultSerchItem(null, 'String'), | |
543 | + this.searchText = null; | |
544 | + this.connection = {}; | |
545 | + this.nodes = []; | |
546 | + this.selectedNode = null; | |
547 | + this.fileList = []; | |
548 | + this.selectedFile = {}; | |
549 | + this.selectedHostCode = null; | |
550 | + this.dataTable = {}; */ | |
551 | + } | |
552 | + // 데이터를 임시 저장 데이터에 복사 | |
553 | + this.tempJobItm = _.cloneDeep(this.jobItm); | |
554 | + this.tempCurrentPage = _.cloneDeep(this.currentPage); | |
555 | + this.tempSearch = _.cloneDeep(this.search); | |
556 | + this.tempSearch_data = _.cloneDeep(this.search_data); | |
557 | + this.tempSearchText = _.cloneDeep(this.searchText); | |
558 | + this.tempConnection = _.cloneDeep(this.connection); | |
559 | + this.tempNodes = _.cloneDeep(this.nodes); | |
560 | + this.tempSelectedNode = _.cloneDeep(this.selectedNode); | |
561 | + this.tempFileList = _.cloneDeep(this.fileList); | |
562 | + this.tempSelectedFil = _.cloneDeep(this.selectedFile); | |
563 | + this.tempSelectedHostCode = _.cloneDeep(this.selectedHostCode); | |
564 | + this.tempDataTable = _.cloneDeep(this.dataTable); | |
565 | + | |
566 | + this.isModalOpen = true; | |
567 | + }, | |
568 | + | |
569 | + // 모달 닫기 | |
570 | + closeModal: function () { | |
571 | + this.isModalOpen = false; | |
572 | + if (this.jobItem) { | |
573 | + this.$emit("closePopup", this.jobItem.id, this.dataTable); | |
574 | + } | |
575 | + | |
576 | + this.$emit("closePopup2", this.jobItm.indx, this.jobItm, this.connection); | |
577 | + }, | |
578 | + | |
579 | + // 화면전환 | |
580 | + async setPage(val) { | |
581 | + if (val == 2) { | |
582 | + if (!this.selectedFile["text"]) { | |
583 | + this.$showAlert("파일 읽기", "파일을 선택해주세요."); | |
584 | + return; | |
585 | + } | |
586 | + await this.fileRead(); | |
587 | + this.currentPage = val; | |
588 | + } else { | |
589 | + this.currentPage = val; | |
590 | + } | |
591 | + }, | |
592 | + | |
593 | + // 모달 취소 | |
594 | + cancelModal: function () { | |
595 | + // 데이터를 임시 저장 데이터로 복구 | |
596 | + this.jobItm = this.tempJobItm; | |
597 | + this.currentPage = this.tempCurrentPage; | |
598 | + this.search = this.tempSearch; | |
599 | + this.search_data = this.tempSearch_data; | |
600 | + this.searchText = this.tempSearchText; | |
601 | + this.connection = this.tempConnection; | |
602 | + this.nodes = this.tempNodes; | |
603 | + this.selectedNode = this.tempSelectedNode; | |
604 | + this.fileList = this.tempFileList; | |
605 | + this.selectedFile = this.tempSelectedFil; | |
606 | + this.selectedHostCode = this.tempSelectedHostCode; | |
607 | + this.dataTable = this.tempDataTable; | |
608 | + | |
609 | + this.closeModal(); | |
610 | + }, | |
611 | + | |
612 | + // 모달 저장 | |
613 | + async saveModal() { | |
614 | + this.isInit = true; // 한 번이라도 저장 시 모달 초기화 안 함 | |
615 | + | |
616 | + // 파일 쓰기용 데이터를 jobItm에 저장 | |
617 | + if (this.modeInfo == "fileWrite") { | |
618 | + if ( | |
619 | + !this.selectedFile["streOptn"] && | |
620 | + this.$filters.removeExtension(this.selectedFile["text"]) == | |
621 | + this.fileNameBeforeDot | |
622 | + ) { | |
623 | + if ( | |
624 | + !(await this.$showConfirm( | |
625 | + "파일 쓰기", | |
626 | + "파일명이 동일할 시 덮어쓰기 됩니다." | |
627 | + )) | |
628 | + ) { | |
629 | + return; | |
630 | + } | |
631 | + } | |
632 | + | |
633 | + this.jobItm.itm["path"] = this.selectedFile["parent"]; | |
634 | + this.jobItm.itm["fileName"] = | |
635 | + this.fileNameBeforeDot + "." + this.selectedFile["fileFom"]; | |
636 | + this.jobItm.itm["fileFom"] = this.selectedFile["fileFom"]; | |
637 | + this.jobItm.itm["streOptn"] = this.selectedFile["streOptn"]; | |
638 | + this.jobItm.itm["suffix"] = this.selectedFile["suffix"]; | |
639 | + this.jobItm.itm["addMonth"] = this.selectedFile["addMonth"]; | |
640 | + | |
641 | + this.jobItm.dataTable = _.cloneDeep(this.defaultDataTable); | |
642 | + } | |
643 | + this.closeModal(); | |
644 | + }, | |
645 | + | |
646 | + // 검색 객체 초기화 | |
647 | + searchInit: function () { | |
648 | + this.search.searchObjectList.push(this.search_data); | |
649 | + }, | |
650 | + | |
651 | + // 파일 검색 | |
652 | + searchFiles() { | |
653 | + const vm = this; | |
654 | + | |
655 | + if (vm.searchText === null || vm.searchText === "") { | |
656 | + vm.$showAlert("파일 검색", "검색어를 입력해 주세요."); | |
657 | + return; | |
658 | + } | |
659 | + | |
660 | + let path = vm.connection.path; | |
661 | + | |
662 | + if (vm.searchType === "all") { | |
663 | + path = "#"; | |
664 | + } | |
665 | + vm.isLoading = true; // 로딩 시작 | |
666 | + | |
667 | + axios({ | |
668 | + url: "/files/search/" + vm.connection.host_code, | |
669 | + method: "post", | |
670 | + headers: {}, | |
671 | + data: { | |
672 | + searchType: vm.searchType, | |
673 | + searchText: vm.searchText, | |
674 | + path: path, | |
675 | + }, | |
676 | + }) | |
677 | + .then(function (response) { | |
678 | + vm.isLoading = false; // 로딩 해제 | |
679 | + vm.fileList = response.data.resultData.fileList; | |
680 | + if (vm.fileList.length == 0) { | |
681 | + vm.$showAlert("파일 검색", "검색 결과가 없습니다."); | |
682 | + } | |
683 | + }) | |
684 | + .catch(function (error) { | |
685 | + vm.isLoading = false; // 로딩 해제 | |
686 | + vm.$showAlert( | |
687 | + "파일 검색", | |
688 | + "파일 검색 오류, 관리자에게 문의바랍니다." | |
689 | + ); | |
690 | + }); | |
691 | + }, | |
692 | + | |
693 | + // 로딩 상태 변경 | |
694 | + handleIsLoading(isLoadingValue) { | |
695 | + this.isLoading = isLoadingValue; // 부모 컴포넌트의 데이터 업데이트 | |
696 | + }, | |
697 | + | |
698 | + // 호스트 목록 조회 | |
699 | + selectHosts() { | |
700 | + const vm = this; | |
701 | + axios({ | |
702 | + url: "/files/hosts", | |
703 | + method: "post", | |
704 | + headers: { | |
705 | + "Content-Type": "application/json; charset=UTF-8", | |
706 | + }, | |
707 | + data: { | |
708 | + userId: store.state.loginUser.user_id, | |
709 | + authList: store.state.loginUser.user_auth, | |
710 | + author: store.state.loginUser.user_auth[0], | |
711 | + dept_code: store.state.loginUser.dept_code, | |
712 | + }, | |
713 | + }) | |
714 | + .then((response) => { | |
715 | + vm.hostList = response.data.resultData.hostList; | |
716 | + vm.deptHostList = response.data.resultData.deptHostList; | |
717 | + if (this.hostList.length > 0) { | |
718 | + vm.selectedHost = vm.hostList[0]; | |
719 | + vm.selectedHostCode = vm.hostList[0].host_code; | |
720 | + for (let i = 0; i < vm.hostList.length; i++) { | |
721 | + for (let j = 0; j < vm.deptHostList.length; j++) { | |
722 | + if (vm.hostList[i].host_code === vm.deptHostList[j].host_code) { | |
723 | + vm.hostList[i].main_folder_path = | |
724 | + vm.deptHostList[j].main_folder_path; | |
725 | + } | |
726 | + } | |
727 | + } | |
728 | + vm.connectionConfirm(); | |
729 | + } else { | |
730 | + vm.selectedHost = {}; | |
731 | + } | |
732 | + }) | |
733 | + .catch((error) => { | |
734 | + vm.$showAlert( | |
735 | + "호스트 조회", | |
736 | + "호스트 조회 오류, 관리자에게 문의하세요." | |
737 | + ); | |
738 | + }); | |
739 | + }, | |
740 | + | |
741 | + // 선택한 호스트 연결 | |
742 | + async connectionConfirm() { | |
743 | + const vm = this; | |
744 | + if (vm.selectedHostCode === null || vm.selectedHostCode === undefined) { | |
745 | + vm.$showAlert("파일시스템 연결", "호스트를 선택해 주세요."); | |
746 | + return; | |
747 | + } | |
748 | + | |
749 | + // 이전에 선택한 호스트 코드가 있으면 저장 | |
750 | + let tempHostCode = null; | |
751 | + if (vm.connection.host_code) { | |
752 | + tempHostCode = _.cloneDeep(vm.connection.host_code); | |
753 | + } | |
754 | + | |
755 | + vm.connection.host_code = vm.selectedHost.host_code; | |
756 | + | |
757 | + vm.isLoading = true; // 로딩 시작 | |
758 | + | |
759 | + /* axios.get('/files/connection', { params: { 'host_code': vm.connection.host_code } }) | |
760 | + .then(response => { | |
761 | + vm.$showAlert('파일시스템 연결', response.data.message); | |
762 | + if (response.data.status === 200) { | |
763 | + vm.fileTreeList(); | |
764 | + } | |
765 | + vm.selectedFile = {}; | |
766 | + }) | |
767 | + .catch(error => { | |
768 | + vm.$showAlert('파일시스템 연결', '파일시스템 연결 오류, 관리자에게 문의하세요.'); | |
769 | + vm.selectedFile = {}; | |
770 | + }); */ | |
771 | + | |
772 | + try { | |
773 | + const response = await axios.get("/files/connection", { | |
774 | + params: { host_code: vm.connection.host_code }, | |
775 | + }); | |
776 | + | |
777 | + vm.isLoading = false; // 로딩 해제 | |
778 | + if (response.data.status === 200) { | |
779 | + await this.fileTreeList(); | |
780 | + } else { | |
781 | + // 이전에 선택한 호스트 코드로 변경 | |
782 | + if (tempHostCode) { | |
783 | + vm.connection.host_code = tempHostCode; | |
784 | + vm.selectedHost.host_code = tempHostCode; | |
785 | + vm.selectedHostCode = tempHostCode; | |
786 | + } | |
787 | + } | |
788 | + vm.selectedFile = {}; | |
789 | + vm.$showAlert("파일시스템 연결", response.data.message); | |
790 | + } catch (error) { | |
791 | + vm.isLoading = false; // 로딩 해제 | |
792 | + vm.$showAlert( | |
793 | + "파일시스템 연결", | |
794 | + "파일시스템 연결 오류, 관리자에게 문의하세요." | |
795 | + ); | |
796 | + vm.selectedFile = {}; | |
797 | + // 이전에 선택한 호스트 코드로 변경 | |
798 | + if (tempHostCode) { | |
799 | + vm.connection.host_code = tempHostCode; | |
800 | + vm.selectedHost.host_code = tempHostCode; | |
801 | + vm.selectedHostCode = tempHostCode; | |
802 | + } | |
803 | + } | |
804 | + }, | |
805 | + | |
806 | + // 폴더 리스트 조회 | |
807 | + async fileTreeList() { | |
808 | + const vm = this; | |
809 | + vm.nodes = []; | |
810 | + vm.connection.path = vm.selectedHost.main_folder_path; | |
811 | + vm.connection.depth = 0; | |
812 | + vm.connection.type = "folder"; | |
813 | + | |
814 | + vm.isLoading = true; // 로딩 시작 | |
815 | + | |
816 | + try { | |
817 | + const response = await axios.get("/files/tree", { | |
818 | + params: vm.connection, | |
819 | + }); | |
820 | + | |
821 | + vm.nodes = response.data.resultData.fileTree; | |
822 | + // vm.connection.path = null; | |
823 | + vm.fileList = []; | |
824 | + vm.isLoading = false; // 로딩 해제 | |
825 | + } catch (error) { | |
826 | + vm.$showAlert( | |
827 | + "파일리스트 조회", | |
828 | + "파일리스트 조회 오류, 관리자에게 문의하세요." | |
829 | + ); | |
830 | + // vm.connection.path = null; | |
831 | + vm.fileList = []; | |
832 | + vm.isLoading = false; // 로딩 해제 | |
833 | + vm.$showAlert( | |
834 | + "파일리스트 조회", | |
835 | + "파일리스트 조회 오류, 관리자에게 문의하세요." | |
836 | + ); | |
837 | + } | |
838 | + }, | |
839 | + | |
840 | + // 폴더 선택 | |
841 | + selectFolder(path) { | |
842 | + if (path === null || path === undefined) { | |
843 | + this.isLoading = false; | |
844 | + } | |
845 | + this.connection.path = path; | |
846 | + this.fileSelectList(); | |
847 | + }, | |
848 | + | |
849 | + handleSelectItem(item, parentItem) { | |
850 | + // 현재 선택된 폴더와 클릭한 폴더가 같을 때 | |
851 | + if (this.currentItem.id == item.id) { | |
852 | + this.selectItem = parentItem; | |
853 | + this.currentItem = parentItem; | |
854 | + } else { | |
855 | + this.selectItem = item; | |
856 | + this.currentItem = item; | |
857 | + } | |
858 | + }, | |
859 | + | |
860 | + // 파일 리스트 조회 | |
861 | + fileSelectList() { | |
862 | + const vm = this; | |
863 | + | |
864 | + if (vm.connection.path === null || vm.connection.path === "") { | |
865 | + vm.fileList = null; | |
866 | + return; | |
867 | + } | |
868 | + | |
869 | + vm.connection.type = "all"; | |
870 | + vm.connection.depth = 1; | |
871 | + | |
872 | + axios | |
873 | + .get("/files/list", { params: vm.connection }) | |
874 | + .then((response) => { | |
875 | + vm.fileList = response.data.resultData.fileList; | |
876 | + vm.search.totalRows = response.data.resultData.totalRow; | |
877 | + vm.isLoading = false; | |
878 | + }) | |
879 | + .catch((error) => { | |
880 | + vm.$showAlert( | |
881 | + "파일리스트 조회", | |
882 | + "파일리스트 조회 오류, 관리자에게 문의하세요." | |
883 | + ); | |
884 | + vm.isLoading = false; | |
885 | + }); | |
886 | + }, | |
887 | + | |
888 | + // 파일 선택 | |
889 | + async selectFile(file) { | |
890 | + if ( | |
891 | + file.extension != "xlsx" && | |
892 | + file.extension != "xls" && | |
893 | + file.extension != "csv" | |
894 | + ) { | |
895 | + this.$showAlert("파일 읽기", "엑셀 파일만 읽을 수 있습니다."); | |
896 | + return; | |
897 | + } | |
898 | + this.selectedFile = _.cloneDeep(file); | |
899 | + // 파일 쓰기용 데이터를 선택 파일에 추가 | |
900 | + if (this.modeInfo == "fileWrite") { | |
901 | + this.selectedFile["fileName"] = file.text; | |
902 | + this.selectedFile["fileFom"] = file.extension; | |
903 | + this.selectedFile["streOptn"] = false; | |
904 | + this.selectedFile["suffix"] = "_YYYYMMDDHHmmss"; | |
905 | + this.selectedFile["addMonth"] = 0; | |
906 | + } | |
907 | + }, | |
908 | + | |
909 | + // 파일/폴더 목록 선택 | |
910 | + selectFileList(item) { | |
911 | + // 폴더가 선택된 경우, 선택된 노드와 경로를 업데이트하고 파일 리스트 조회 | |
912 | + if (item.extension === "폴더" || item.extension === "") { | |
913 | + this.selectedNode = item.id; | |
914 | + this.connection.path = item.id; | |
915 | + this.fileSelectList(); | |
916 | + } else { | |
917 | + this.selectFile(item); // 파일이 선택된 경우 | |
918 | + } | |
919 | + }, | |
920 | + | |
921 | + // 파일 미리보기 | |
922 | + async filePreview(file) { | |
923 | + if (this.selectedFile == null || file["id"] != this.selectedFile["id"]) { | |
924 | + await this.selectFile(file); // 선택 파일 정보 저장 | |
925 | + } | |
926 | + this.preview = true; // 미리보기 활성화 | |
927 | + await this.fileRead(); // 파일 읽기 | |
928 | + this.currentPage = 2; // 데이터셋 설정 페이지로 이동 | |
929 | + }, | |
930 | + | |
931 | + // 초기화 function | |
932 | + init: async function () { | |
933 | + if (this.jobItm == null) { | |
934 | + this.jobItm = JSON.parse( | |
935 | + JSON.stringify(this.$getDefaultJobGroup().jobItem) | |
936 | + ); | |
937 | + this.jobItm.itm = JSON.parse( | |
938 | + JSON.stringify(this.$getDefaultJobGroup().fileRead) | |
939 | + ); | |
940 | + // 아이템이 없을때 | |
941 | + this.jobItm.indx = 0; | |
942 | + } | |
943 | + | |
944 | + this.defaultDataTable = _.cloneDeep(this.jobItm.dataTable); // 초기 빈 데이터테이블 복사 | |
945 | + }, | |
946 | + | |
947 | + /** ************************************ 데이터 셋 읽기 ************************************ */ | |
948 | + // 파일 읽기 | |
949 | + async fileRead() { | |
950 | + const vm = this; | |
951 | + vm.isLoading = true; | |
952 | + | |
953 | + // 선택 파일 정보를 VO 형태로 변환 | |
954 | + axios | |
955 | + .post("/files/read/" + vm.connection.host_code, { | |
956 | + path: vm.selectedFile.path, | |
957 | + fileName: vm.selectedFile.text, | |
958 | + extension: vm.selectedFile.extension, | |
959 | + type: "file", | |
960 | + datasetId: "", | |
961 | + viewMode: true, | |
962 | + }) | |
963 | + .then((response) => { | |
964 | + // 잡아이템에 선택 파일 정보 저장 | |
965 | + if (vm.modeInfo == "fileRead") { | |
966 | + vm.jobItm.itm.fileName = vm.selectedFile.text; | |
967 | + } else if (vm.modeInfo == "fileWrite") { | |
968 | + vm.jobItm.itm.fileName = vm.fileNameBeforeDot; | |
969 | + } | |
970 | + | |
971 | + vm.jobItm.itm.path = vm.selectedFile.path; | |
972 | + vm.jobItm.itm.extension = vm.selectedFile.extension; | |
973 | + vm.jobItm.itm.type = "file"; | |
974 | + vm.jobItm.itm.datasetId = vm.connection.host_code; | |
975 | + vm.jobItm.itm.viewMode = true; | |
976 | + | |
977 | + vm.dataTable = response.data.resultData.dataTableMap; | |
978 | + vm.jobItm.dataTable = vm.dataTable; // 잡아이템에 데이터셋 정보 저장 | |
979 | + | |
980 | + vm.columnDatas = vm.dataTable.columnDatas; | |
981 | + vm.jobItm.front_dataTable.columnDatas = vm.columnDatas; | |
982 | + }) | |
983 | + .catch(() => { | |
984 | + vm.isLoading = false; | |
985 | + }); | |
986 | + }, | |
987 | + | |
988 | + // 데이터셋 시작 행, 열 정보 변경시 | |
989 | + indexChange: function (v) { | |
990 | + this.jobItm.dataTable = v; // 잡아이템 데이터셋에 변경된 데이터셋 정보 저장 | |
991 | + this.jobItm.itm.rowDataColumnIndex = v.rowDataColumnIndex; | |
992 | + this.jobItm.itm.startRowIndex = v.startRowIndex; | |
993 | + this.jobItm.itm.startCellIndex = v.startCellIndex; | |
994 | + }, | |
995 | + }, | |
996 | + watch: { | |
997 | + // 팝업창 열기 요청 시 | |
998 | + openPopup: function (v) { | |
999 | + if (v) { | |
1000 | + this.openModal(); | |
1001 | + } | |
1002 | + }, | |
1003 | + | |
1004 | + // 연결 호스트 변경 시 | |
1005 | + selectedHostCode: function (newValue, oldValue) { | |
1006 | + for (let i = 0; i < this.hostList.length; i++) { | |
1007 | + if (this.hostList[i].host_code === newValue) { | |
1008 | + this.selectedHost = this.hostList[i]; | |
1009 | + break; | |
1010 | + } | |
1011 | + } | |
1012 | + }, | |
1013 | + | |
1014 | + "selectedFile.fileName": function (newValue, oldValue) { | |
1015 | + if (newValue != null) { | |
1016 | + this.fileNameBeforeDot = this.$filters.removeExtension(newValue); | |
1017 | + } | |
1018 | + }, | |
1019 | + }, | |
1020 | + components: { | |
1021 | + SvgIcon: SvgIcon, | |
1022 | + TreeItem: TreeItem, | |
1023 | + FileDataRead: FileDataRead, | |
1024 | + }, | |
1025 | + mounted() { | |
1026 | + this.init(); | |
1027 | + }, | |
1028 | +}; | |
1029 | +</script> |
+++ client/views/component/connection/itm/fileSelectDiagram.vue
... | ... | @@ -0,0 +1,927 @@ |
1 | +<template> | |
2 | + <div | |
3 | + v-show="isModalOpen" | |
4 | + class="modal-wrapper" | |
5 | + :style="isLoading ? { cursor: 'wait' } : {}" | |
6 | + > | |
7 | + <div v-if="isLoading" class="loading-overlay"> | |
8 | + <div class="loading-div"> | |
9 | + <span>LOADING </span> | |
10 | + <span class="anima">.</span> | |
11 | + <span class="anima">.</span> | |
12 | + <span class="anima">.</span> | |
13 | + </div> | |
14 | + </div> | |
15 | + <div class="modal-container list-modal"> | |
16 | + <div class="modal-title"> | |
17 | + <div class="flex justify-between align-center"> | |
18 | + <h2 v-show="modeInfo == 'fileRead'">파일 읽기</h2> | |
19 | + <h2 v-show="modeInfo == 'fileWrite'">파일 쓰기</h2> | |
20 | + <button class="close-btn" @click="cancelModal()">X</button> | |
21 | + </div> | |
22 | + </div> | |
23 | + <div class="modal-content-monthly" v-show="currentPage == 1"> | |
24 | + <div class="content-wrap"> | |
25 | + <div class="content-box flex justify-between"> | |
26 | + <div class="flex20 content-box"> | |
27 | + <div class="left-content flex100 content-box"> | |
28 | + <div class="content-box"> | |
29 | + <div class="mb10"> | |
30 | + <div class="content-titleZone"> | |
31 | + <p class="box-title">호스트 선택</p> | |
32 | + </div> | |
33 | + <div class="flex align-center justify-between no-gutter"> | |
34 | + <div class="flex100"> | |
35 | + <select | |
36 | + name="" | |
37 | + id="" | |
38 | + class="only full-select" | |
39 | + v-model="selectedHostCode" | |
40 | + > | |
41 | + <option :value="null" disabled>선택</option> | |
42 | + <option | |
43 | + v-for="(host, idx) in hostList" | |
44 | + :key="idx" | |
45 | + :value="host.host_code" | |
46 | + > | |
47 | + {{ host.host_nm + " - (" + host.host_ip + ")" }} | |
48 | + </option> | |
49 | + </select> | |
50 | + </div> | |
51 | + <div class="flex100"> | |
52 | + <button | |
53 | + class="blue-border-btn large-btn" | |
54 | + @click="connectionConfirm()" | |
55 | + > | |
56 | + 연결 | |
57 | + </button> | |
58 | + </div> | |
59 | + </div> | |
60 | + </div> | |
61 | + <div class="file-tree-zone"> | |
62 | + <div | |
63 | + class="content-titleZone flex justify-between align-center" | |
64 | + > | |
65 | + <p class="box-title">폴더 리스트</p> | |
66 | + </div> | |
67 | + <div class="file-zone overflow-y"> | |
68 | + <ul class="tree-wrap"> | |
69 | + <TreeItem | |
70 | + :connection="connection" | |
71 | + :selectedNode="selectedNode" | |
72 | + :selectItem="currentItem" | |
73 | + v-for="(item, idx) in nodes" | |
74 | + :item="item" | |
75 | + :idx="item.id" | |
76 | + :key="idx" | |
77 | + @selectFolder="selectFolder" | |
78 | + @isLoading="handleIsLoading" | |
79 | + @selectItem="handleSelectItem" | |
80 | + /> | |
81 | + </ul> | |
82 | + </div> | |
83 | + </div> | |
84 | + </div> | |
85 | + </div> | |
86 | + </div> | |
87 | + <div class="flex80 content-box"> | |
88 | + <div class="right-content flex100"> | |
89 | + <div class="flex-column justify-between"> | |
90 | + <div class="flex justify-between align-center no-gutter"> | |
91 | + <div class="flex justify-end flex100"> | |
92 | + <div class="search-bar"> | |
93 | + <div class="flex justify-end align-center"> | |
94 | + <select | |
95 | + name="" | |
96 | + id="" | |
97 | + class="square-select" | |
98 | + v-model="searchType" | |
99 | + > | |
100 | + <option value="all">전체</option> | |
101 | + <option value="folder">현재폴더</option> | |
102 | + </select> | |
103 | + <div class="search-square"> | |
104 | + <div | |
105 | + class="flex justify-end align-center no-gutter" | |
106 | + > | |
107 | + <input | |
108 | + type="text" | |
109 | + class="square-input flex90" | |
110 | + placeholder="Search" | |
111 | + v-model="searchText" | |
112 | + @keyup.enter="searchFiles()" | |
113 | + /> | |
114 | + <button class="square-button blue-btn flex10"> | |
115 | + <svg-icon | |
116 | + type="mdi" | |
117 | + :path="searchPath" | |
118 | + class="square-icon" | |
119 | + @click="searchFiles()" | |
120 | + ></svg-icon> | |
121 | + </button> | |
122 | + </div> | |
123 | + </div> | |
124 | + </div> | |
125 | + </div> | |
126 | + </div> | |
127 | + </div> | |
128 | + <div class="content-zone" style="overflow-y: auto"> | |
129 | + <div class="table-zone file-table"> | |
130 | + <div class="list-info flex justify-between align-center"> | |
131 | + <div class="count-zone"> | |
132 | + <p> | |
133 | + 총 | |
134 | + <span v-if="search.totalRows != 0">{{ | |
135 | + search.totalRows | |
136 | + }}</span> | |
137 | + <span v-else>0</span> | |
138 | + 건 | |
139 | + </p> | |
140 | + <p> | |
141 | + 현재경로 : <span>{{ connection.path }}</span> | |
142 | + </p> | |
143 | + </div> | |
144 | + </div> | |
145 | + <table class="list-table"> | |
146 | + <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 --> | |
147 | + <colgroup> | |
148 | + <col style="width: 5%" /> | |
149 | + <col style="width: 45%" /> | |
150 | + <col style="width: 10%" /> | |
151 | + <col style="width: 20%" /> | |
152 | + <col style="width: 10%" /> | |
153 | + <col style="width: 10%" /> | |
154 | + </colgroup> | |
155 | + <thead> | |
156 | + <tr> | |
157 | + <th></th> | |
158 | + <th>이름</th> | |
159 | + <th>타입</th> | |
160 | + <th>마지막 수정</th> | |
161 | + <th>크기</th> | |
162 | + <th>관리</th> | |
163 | + </tr> | |
164 | + </thead> | |
165 | + <tbody> | |
166 | + <tr | |
167 | + v-for="(file, index) in fileList" | |
168 | + :key="index" | |
169 | + @click="selectFileList(file)" | |
170 | + > | |
171 | + <td | |
172 | + v-if="file.text != '상위폴더로 이동'" | |
173 | + @click.stop="" | |
174 | + ></td> | |
175 | + <td v-else @click.stop=""></td> | |
176 | + <td> | |
177 | + <div class="text-lf"> | |
178 | + <span v-if="file.extension === 'txt'" | |
179 | + ><img | |
180 | + src="../../../../resources/img/icon/txt.png" | |
181 | + ref="" | |
182 | + alt="" | |
183 | + /></span> | |
184 | + <span v-else-if="file.extension === 'pdf'" | |
185 | + ><img | |
186 | + src="../../../../resources/img/icon/pdf.png" | |
187 | + alt="" | |
188 | + /></span> | |
189 | + <span | |
190 | + v-else-if=" | |
191 | + file.extension === 'pptx' || | |
192 | + file.extension === 'ppt' | |
193 | + " | |
194 | + ><img | |
195 | + src="../../../../resources/img/icon/ppt.png" | |
196 | + alt="" | |
197 | + /></span> | |
198 | + <span v-else-if="file.extension === 'hwp'" | |
199 | + ><img | |
200 | + src="../../../../resources/img/icon/hwp.png" | |
201 | + alt="" | |
202 | + /></span> | |
203 | + <span | |
204 | + v-else-if=" | |
205 | + file.extension === 'xls' || | |
206 | + file.extension === 'xlsx' | |
207 | + " | |
208 | + ><img | |
209 | + src="../../../../resources/img/icon/xls.png" | |
210 | + alt="" | |
211 | + /></span> | |
212 | + <span v-else-if="file.extension === 'jpg'" | |
213 | + ><img | |
214 | + src="../../../../resources/img/icon/img.png" | |
215 | + alt="" | |
216 | + /></span> | |
217 | + <span v-else-if="file.extension === 'png'" | |
218 | + ><img | |
219 | + src="../../../../resources/img/icon/img.png" | |
220 | + ref="" | |
221 | + alt="" | |
222 | + /></span> | |
223 | + <span v-else>  </span> | |
224 | + <span> {{ file.text }}</span> | |
225 | + </div> | |
226 | + </td> | |
227 | + <td>{{ file.extension }}</td> | |
228 | + <td>{{ file.lastUpdate }}</td> | |
229 | + <td v-if="file.size != 0"> | |
230 | + {{ $filters.bytesToSize(file.size) }} | |
231 | + </td> | |
232 | + <td v-else></td> | |
233 | + <td> | |
234 | + <div | |
235 | + v-if="!file.folder && modeInfo == 'fileWrite'" | |
236 | + > | |
237 | + <button | |
238 | + class="icon-btn orange-btn" | |
239 | + title="미리보기" | |
240 | + @click.stop="filePreview(file)" | |
241 | + > | |
242 | + <svg-icon | |
243 | + type="mdi" | |
244 | + :width="18" | |
245 | + :height="18" | |
246 | + :path="fileFindPath" | |
247 | + ></svg-icon> | |
248 | + </button> | |
249 | + </div> | |
250 | + <div v-else></div> | |
251 | + </td> | |
252 | + </tr> | |
253 | + </tbody> | |
254 | + </table> | |
255 | + </div> | |
256 | + </div> | |
257 | + </div> | |
258 | + </div> | |
259 | + </div> | |
260 | + </div> | |
261 | + </div> | |
262 | + <div v-if="modeInfo == 'fileRead'" class="table-zone"> | |
263 | + <table class="form-table"> | |
264 | + <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 --> | |
265 | + <colgroup> | |
266 | + <col style="width: 20%" /> | |
267 | + <col style="width: 80%" /> | |
268 | + </colgroup> | |
269 | + <tbody> | |
270 | + <tr> | |
271 | + <th>폴더명</th> | |
272 | + <td>{{ jobItm.itm["path"] }}</td> | |
273 | + </tr> | |
274 | + <tr> | |
275 | + <th>파일명</th> | |
276 | + <td>{{ jobItm.itm["text"] }}</td> | |
277 | + </tr> | |
278 | + </tbody> | |
279 | + </table> | |
280 | + <div class="flex flex30 align-center justify-center"> | |
281 | + <div class="input-container flex"> | |
282 | + <label class="check-label"> | |
283 | + <input type="checkbox" name="check" class="custom-checkbox" /> | |
284 | + <span>마지막 파일만</span> | |
285 | + </label> | |
286 | + <label class="check-label"> | |
287 | + <input type="checkbox" name="check" class="custom-checkbox" /> | |
288 | + <span>업데이트일 이후</span> | |
289 | + </label> | |
290 | + </div> | |
291 | + </div> | |
292 | + </div> | |
293 | + <div v-else-if="modeInfo == 'fileWrite'" class="table-zone"> | |
294 | + <table class="form-table"> | |
295 | + <colgroup> | |
296 | + <col style="width: 10%" /> | |
297 | + <col style="width: 40%" /> | |
298 | + <col style="width: 10%" /> | |
299 | + <col style="width: 40%" /> | |
300 | + </colgroup> | |
301 | + <tbody> | |
302 | + <tr> | |
303 | + <th>폴더명</th> | |
304 | + <td>{{ jobItm.itm["path"] }}</td> | |
305 | + <th>쓰기유형</th> | |
306 | + <td> | |
307 | + <select name="" id="" class="full-select"> | |
308 | + <option selected>엑셀</option> | |
309 | + </select> | |
310 | + </td> | |
311 | + </tr> | |
312 | + <tr> | |
313 | + <th>파일명</th> | |
314 | + <td> | |
315 | + <input | |
316 | + type="text" | |
317 | + name="" | |
318 | + id="" | |
319 | + class="full-input" | |
320 | + v-model="jobItm.itm.fileName" | |
321 | + /> | |
322 | + </td> | |
323 | + <th>쓰기옵션</th> | |
324 | + <td> | |
325 | + <div class="flex flex30 align-center justify-center"> | |
326 | + <div class="input-container flex"> | |
327 | + <label class="radio-label"> | |
328 | + <input | |
329 | + type="radio" | |
330 | + name="radio" | |
331 | + checked="" | |
332 | + class="custom-radiobox" | |
333 | + v-model="selectedFile['streOptn']" | |
334 | + :value="false" | |
335 | + /> | |
336 | + <span>쓰기</span> | |
337 | + </label> | |
338 | + <label class="radio-label"> | |
339 | + <input | |
340 | + type="radio" | |
341 | + name="radio" | |
342 | + class="custom-radiobox" | |
343 | + v-model="selectedFile['streOptn']" | |
344 | + :value="true" | |
345 | + /> | |
346 | + <span>suffix</span> | |
347 | + </label> | |
348 | + </div> | |
349 | + </div> | |
350 | + </td> | |
351 | + </tr> | |
352 | + <tr v-show="selectedFile['streOptn']"> | |
353 | + <th>suffix</th> | |
354 | + <td> | |
355 | + <input | |
356 | + type="text" | |
357 | + name="" | |
358 | + id="" | |
359 | + class="full-input" | |
360 | + v-model="selectedFile['suffix']" | |
361 | + /> | |
362 | + </td> | |
363 | + <th>월변경</th> | |
364 | + <td> | |
365 | + <input | |
366 | + type="number" | |
367 | + name="" | |
368 | + id="" | |
369 | + class="full-input" | |
370 | + v-model="selectedFile['addMonth']" | |
371 | + /> | |
372 | + </td> | |
373 | + </tr> | |
374 | + </tbody> | |
375 | + </table> | |
376 | + </div> | |
377 | + </div> | |
378 | + <div class="modal-content-monthly" v-show="currentPage == 2"> | |
379 | + <div class="content-titleZone"> | |
380 | + <p v-show="modeInfo == 'fileRead'" class="box-title">데이터셋 설정</p> | |
381 | + <p v-show="modeInfo == 'fileWrite'" class="box-title"> | |
382 | + 데이터 미리보기 | |
383 | + </p> | |
384 | + </div> | |
385 | + <FileDataRead | |
386 | + v-if="isModalOpen" | |
387 | + v-model:isLoading="isLoading" | |
388 | + v-model:dataTable="dataTable" | |
389 | + :preview="preview" | |
390 | + @indexChange="indexChange" | |
391 | + ></FileDataRead> | |
392 | + </div> | |
393 | + <div class="modal-end flex justify-end"> | |
394 | + <button | |
395 | + class="blue-btn small-btn" | |
396 | + @click="setPage(1)" | |
397 | + v-show="currentPage == 2" | |
398 | + > | |
399 | + 이전 | |
400 | + </button> | |
401 | + <button | |
402 | + class="blue-btn small-btn" | |
403 | + @click="setPage(2)" | |
404 | + v-show="currentPage == 1 && modeInfo == 'fileRead'" | |
405 | + > | |
406 | + 다음 | |
407 | + </button> | |
408 | + <button | |
409 | + class="blue-btn small-btn" | |
410 | + @click="saveModal()" | |
411 | + v-show=" | |
412 | + (currentPage == 2 && modeInfo == 'fileRead') || | |
413 | + (currentPage == 1 && modeInfo == 'fileWrite') | |
414 | + " | |
415 | + > | |
416 | + 완료 | |
417 | + </button> | |
418 | + </div> | |
419 | + </div> | |
420 | + </div> | |
421 | +</template> | |
422 | + | |
423 | + | |
424 | + <script> | |
425 | +import axios from "axios"; | |
426 | +import TreeItem from "../../FileTree.vue"; | |
427 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
428 | +import FileDataRead from "./fileDataRead.vue"; | |
429 | +import { mdiMagnify, mdiFileFind } from "@mdi/js"; | |
430 | +import store from "../../../pages/AppStore"; | |
431 | +import _ from "lodash"; | |
432 | + | |
433 | +export default { | |
434 | + name: "database-connection", | |
435 | + props: { | |
436 | + openPopup: { | |
437 | + type: Boolean, | |
438 | + default: false, | |
439 | + }, | |
440 | + jobItem: { | |
441 | + type: Boolean, | |
442 | + default: null, | |
443 | + }, | |
444 | + mode: { | |
445 | + type: String, | |
446 | + default: null, // 읽기: fileRead, 쓰기: fileWrite | |
447 | + }, | |
448 | + }, | |
449 | + data() { | |
450 | + return { | |
451 | + // 로딩 상태 | |
452 | + isLoading: false, | |
453 | + | |
454 | + // 현재 모달창 관리 | |
455 | + isModalOpen: this.openPopup, | |
456 | + jobItm: this.jobItem, | |
457 | + modeInfo: this.mode, | |
458 | + | |
459 | + currentPage: 1, | |
460 | + searchPath: mdiMagnify, // 검색 아이콘 경로 | |
461 | + fileFindPath: mdiFileFind, // 미리보기 아이콘 경로 | |
462 | + | |
463 | + // 검색 객체 | |
464 | + search: this.$getDefaultSerchVO(), | |
465 | + search_data: this.$getDefaultSerchItem(null, "String"), | |
466 | + | |
467 | + selectedHost: {}, | |
468 | + selectedHostCode: null, | |
469 | + // 호스트 목록 | |
470 | + hostList: [], | |
471 | + // 부서-호스트 목록 | |
472 | + deptHostList: [], | |
473 | + connection: { | |
474 | + host_code: null, | |
475 | + path: null, | |
476 | + depth: null, | |
477 | + type: null, | |
478 | + }, | |
479 | + nodes: [], | |
480 | + selectedNode: null, | |
481 | + fileList: [], | |
482 | + | |
483 | + searchType: "all", | |
484 | + searchText: null, | |
485 | + | |
486 | + selectItem: null, | |
487 | + currentItem: { | |
488 | + id: null, | |
489 | + }, | |
490 | + | |
491 | + selectedFile: {}, | |
492 | + | |
493 | + // 초기 빈 데이터 테이블 | |
494 | + defaultDataTable: {}, | |
495 | + | |
496 | + // 임시 저장된 데이터 | |
497 | + tempJobItm: this.jobItem, | |
498 | + tempCurrentPage: 1, | |
499 | + tempSearch: this.$getDefaultSerchVO(), | |
500 | + tempSearch_data: this.$getDefaultSerchItem(null, "String"), | |
501 | + tempSearchText: null, | |
502 | + tempConnection: { | |
503 | + host_code: null, | |
504 | + path: null, | |
505 | + depth: null, | |
506 | + type: null, | |
507 | + }, | |
508 | + tempNodes: [], | |
509 | + tempSelectedNode: null, | |
510 | + tempFileList: [], | |
511 | + tempSelectedFile: {}, | |
512 | + tempSelectedHostCode: null, | |
513 | + tempDataTable: {}, | |
514 | + /** ************************************ 데이터 셋 읽기 ************************************ */ | |
515 | + dataTable: {}, | |
516 | + preview: false, | |
517 | + | |
518 | + fileNameBeforeDot: null, // 파일명(확장자 제외) | |
519 | + }; | |
520 | + }, | |
521 | + methods: { | |
522 | + // 모달 닫기 | |
523 | + closeModal: function () { | |
524 | + this.$emit("closePopup", this.jobItm); | |
525 | + }, | |
526 | + | |
527 | + // 화면전환 | |
528 | + async setPage(val) { | |
529 | + if (val == 2) { | |
530 | + if (!this.jobItm.itm["text"]) { | |
531 | + this.$showAlert("파일 읽기", "파일을 선택해주세요."); | |
532 | + return; | |
533 | + } | |
534 | + await this.fileRead(); | |
535 | + this.currentPage = val; | |
536 | + } else { | |
537 | + this.currentPage = val; | |
538 | + } | |
539 | + }, | |
540 | + | |
541 | + // 모달 취소 | |
542 | + cancelModal: function () { | |
543 | + this.closeModal(); | |
544 | + }, | |
545 | + | |
546 | + // 모달 저장 | |
547 | + async saveModal() { | |
548 | + // 파일 쓰기용 데이터를 jobItm에 저장 | |
549 | + if (this.modeInfo == "fileWrite") { | |
550 | + if ( | |
551 | + !this.selectedFile["streOptn"] && | |
552 | + this.$filters.removeExtension(this.selectedFile["text"]) == | |
553 | + this.fileNameBeforeDot | |
554 | + ) { | |
555 | + if ( | |
556 | + !(await this.$showConfirm( | |
557 | + "파일 쓰기", | |
558 | + "파일명이 동일할 시 덮어쓰기 됩니다." | |
559 | + )) | |
560 | + ) { | |
561 | + return; | |
562 | + } | |
563 | + } | |
564 | + | |
565 | + this.jobItm.itm["path"] = this.selectedFile["parent"]; | |
566 | + this.jobItm.itm["text"] = this.selectedFile["text"]; | |
567 | + this.fileNameBeforeDot + "." + this.selectedFile["fileFom"]; | |
568 | + this.jobItm.itm["fileFom"] = this.selectedFile["fileFom"]; | |
569 | + this.jobItm.itm["streOptn"] = this.selectedFile["streOptn"]; | |
570 | + this.jobItm.itm["suffix"] = this.selectedFile["suffix"]; | |
571 | + this.jobItm.itm["addMonth"] = this.selectedFile["addMonth"]; | |
572 | + | |
573 | + this.jobItm.dataTable = _.cloneDeep(this.defaultDataTable); | |
574 | + } | |
575 | + this.closeModal(); | |
576 | + }, | |
577 | + | |
578 | + // 검색 객체 초기화 | |
579 | + searchInit: function () { | |
580 | + this.search.searchObjectList.push(this.search_data); | |
581 | + }, | |
582 | + | |
583 | + // 파일 검색 | |
584 | + searchFiles() { | |
585 | + const vm = this; | |
586 | + | |
587 | + if (vm.searchText === null || vm.searchText === "") { | |
588 | + vm.$showAlert("파일 검색", "검색어를 입력해 주세요."); | |
589 | + return; | |
590 | + } | |
591 | + | |
592 | + let path = vm.connection.path; | |
593 | + | |
594 | + if (vm.searchType === "all") { | |
595 | + path = "#"; | |
596 | + } | |
597 | + vm.isLoading = true; // 로딩 시작 | |
598 | + | |
599 | + axios({ | |
600 | + url: "/files/search/" + vm.connection.host_code, | |
601 | + method: "post", | |
602 | + headers: {}, | |
603 | + data: { | |
604 | + searchType: vm.searchType, | |
605 | + searchText: vm.searchText, | |
606 | + path: path, | |
607 | + }, | |
608 | + }) | |
609 | + .then(function (response) { | |
610 | + vm.isLoading = false; // 로딩 해제 | |
611 | + vm.fileList = response.data.resultData.fileList; | |
612 | + if (vm.fileList.length == 0) { | |
613 | + vm.$showAlert("파일 검색", "검색 결과가 없습니다."); | |
614 | + } | |
615 | + }) | |
616 | + .catch(function (error) { | |
617 | + vm.isLoading = false; // 로딩 해제 | |
618 | + vm.$showAlert( | |
619 | + "파일 검색", | |
620 | + "파일 검색 오류, 관리자에게 문의바랍니다." | |
621 | + ); | |
622 | + }); | |
623 | + }, | |
624 | + | |
625 | + // 로딩 상태 변경 | |
626 | + handleIsLoading(isLoadingValue) { | |
627 | + this.isLoading = isLoadingValue; // 부모 컴포넌트의 데이터 업데이트 | |
628 | + }, | |
629 | + | |
630 | + // 호스트 목록 조회 | |
631 | + selectHosts() { | |
632 | + const vm = this; | |
633 | + axios({ | |
634 | + url: "/files/hosts", | |
635 | + method: "post", | |
636 | + headers: { | |
637 | + "Content-Type": "application/json; charset=UTF-8", | |
638 | + }, | |
639 | + data: { | |
640 | + userId: store.state.loginUser.user_id, | |
641 | + authList: store.state.loginUser.user_auth, | |
642 | + author: store.state.loginUser.user_auth[0], | |
643 | + dept_code: store.state.loginUser.dept_code, | |
644 | + }, | |
645 | + }) | |
646 | + .then((response) => { | |
647 | + vm.hostList = response.data.resultData.hostList; | |
648 | + vm.deptHostList = response.data.resultData.deptHostList; | |
649 | + if (this.hostList.length > 0) { | |
650 | + vm.selectedHost = vm.hostList[0]; | |
651 | + vm.selectedHostCode = vm.hostList[0].host_code; | |
652 | + for (let i = 0; i < vm.hostList.length; i++) { | |
653 | + for (let j = 0; j < vm.deptHostList.length; j++) { | |
654 | + if (vm.hostList[i].host_code === vm.deptHostList[j].host_code) { | |
655 | + vm.hostList[i].main_folder_path = | |
656 | + vm.deptHostList[j].main_folder_path; | |
657 | + } | |
658 | + } | |
659 | + } | |
660 | + vm.connectionConfirm(); | |
661 | + } else { | |
662 | + vm.selectedHost = {}; | |
663 | + } | |
664 | + }) | |
665 | + .catch((error) => { | |
666 | + vm.$showAlert( | |
667 | + "호스트 조회", | |
668 | + "호스트 조회 오류, 관리자에게 문의하세요." | |
669 | + ); | |
670 | + }); | |
671 | + }, | |
672 | + | |
673 | + // 선택한 호스트 연결 | |
674 | + async connectionConfirm() { | |
675 | + const vm = this; | |
676 | + if (vm.selectedHostCode === null || vm.selectedHostCode === undefined) { | |
677 | + vm.$showAlert("파일시스템 연결", "호스트를 선택해 주세요."); | |
678 | + return; | |
679 | + } | |
680 | + | |
681 | + // 이전에 선택한 호스트 코드가 있으면 저장 | |
682 | + let tempHostCode = null; | |
683 | + if (vm.connection.host_code) { | |
684 | + tempHostCode = _.cloneDeep(vm.connection.host_code); | |
685 | + } | |
686 | + | |
687 | + vm.connection.host_code = vm.selectedHost.host_code; | |
688 | + | |
689 | + vm.isLoading = true; // 로딩 시작 | |
690 | + | |
691 | + try { | |
692 | + const response = await axios.get("/files/connection", { | |
693 | + params: { host_code: vm.connection.host_code }, | |
694 | + }); | |
695 | + | |
696 | + vm.isLoading = false; // 로딩 해제 | |
697 | + if (response.data.status === 200) { | |
698 | + await this.fileTreeList(); | |
699 | + } else { | |
700 | + // 이전에 선택한 호스트 코드로 변경 | |
701 | + if (tempHostCode) { | |
702 | + vm.connection.host_code = tempHostCode; | |
703 | + vm.selectedHost.host_code = tempHostCode; | |
704 | + vm.selectedHostCode = tempHostCode; | |
705 | + } | |
706 | + } | |
707 | + vm.selectedFile = {}; | |
708 | + vm.$showAlert("파일시스템 연결", response.data.message); | |
709 | + } catch (error) { | |
710 | + vm.isLoading = false; // 로딩 해제 | |
711 | + vm.$showAlert( | |
712 | + "파일시스템 연결", | |
713 | + "파일시스템 연결 오류, 관리자에게 문의하세요." | |
714 | + ); | |
715 | + vm.selectedFile = {}; | |
716 | + // 이전에 선택한 호스트 코드로 변경 | |
717 | + if (tempHostCode) { | |
718 | + vm.connection.host_code = tempHostCode; | |
719 | + vm.selectedHost.host_code = tempHostCode; | |
720 | + vm.selectedHostCode = tempHostCode; | |
721 | + } | |
722 | + } | |
723 | + }, | |
724 | + | |
725 | + // 폴더 리스트 조회 | |
726 | + async fileTreeList() { | |
727 | + const vm = this; | |
728 | + vm.nodes = []; | |
729 | + vm.connection.path = vm.selectedHost.main_folder_path; | |
730 | + vm.connection.depth = 0; | |
731 | + vm.connection.type = "folder"; | |
732 | + | |
733 | + vm.isLoading = true; // 로딩 시작 | |
734 | + | |
735 | + try { | |
736 | + const response = await axios.get("/files/tree", { | |
737 | + params: vm.connection, | |
738 | + }); | |
739 | + vm.nodes = response.data.resultData.fileTree; | |
740 | + // vm.connection.path = null; | |
741 | + vm.fileList = []; | |
742 | + vm.isLoading = false; // 로딩 해제 | |
743 | + } catch (error) { | |
744 | + vm.$showAlert( | |
745 | + "파일리스트 조회", | |
746 | + "파일리스트 조회 오류, 관리자에게 문의하세요." | |
747 | + ); | |
748 | + vm.fileList = []; | |
749 | + vm.isLoading = false; // 로딩 해제 | |
750 | + vm.$showAlert( | |
751 | + "파일리스트 조회", | |
752 | + "파일리스트 조회 오류, 관리자에게 문의하세요." | |
753 | + ); | |
754 | + } | |
755 | + }, | |
756 | + | |
757 | + // 폴더 선택 | |
758 | + selectFolder(path) { | |
759 | + if (path === null || path === undefined) { | |
760 | + this.isLoading = false; | |
761 | + } | |
762 | + this.connection.path = path; | |
763 | + this.fileSelectList(); | |
764 | + }, | |
765 | + | |
766 | + handleSelectItem(item, parentItem) { | |
767 | + // 현재 선택된 폴더와 클릭한 폴더가 같을 때 | |
768 | + if (this.currentItem.id == item.id) { | |
769 | + this.selectItem = parentItem; | |
770 | + this.currentItem = parentItem; | |
771 | + } else { | |
772 | + this.selectItem = item; | |
773 | + this.currentItem = item; | |
774 | + } | |
775 | + }, | |
776 | + | |
777 | + // 파일 리스트 조회 | |
778 | + fileSelectList() { | |
779 | + const vm = this; | |
780 | + | |
781 | + if (vm.connection.path === null || vm.connection.path === "") { | |
782 | + vm.fileList = null; | |
783 | + return; | |
784 | + } | |
785 | + | |
786 | + vm.connection.type = "all"; | |
787 | + vm.connection.depth = 1; | |
788 | + | |
789 | + axios | |
790 | + .get("/files/list", { params: vm.connection }) | |
791 | + .then((response) => { | |
792 | + vm.fileList = response.data.resultData.fileList; | |
793 | + vm.search.totalRows = response.data.resultData.totalRow; | |
794 | + vm.isLoading = false; | |
795 | + }) | |
796 | + .catch((error) => { | |
797 | + vm.$showAlert( | |
798 | + "파일리스트 조회", | |
799 | + "파일리스트 조회 오류, 관리자에게 문의하세요." | |
800 | + ); | |
801 | + vm.isLoading = false; | |
802 | + }); | |
803 | + }, | |
804 | + | |
805 | + // 파일 선택 | |
806 | + async selectFile(file) { | |
807 | + if ( | |
808 | + file.extension != "xlsx" && | |
809 | + file.extension != "xls" && | |
810 | + file.extension != "csv" | |
811 | + ) { | |
812 | + this.$showAlert("파일 읽기", "엑셀 파일만 읽을 수 있습니다."); | |
813 | + return; | |
814 | + } | |
815 | + this.selectedFile = _.cloneDeep(file); | |
816 | + this.jobItm.itm = _.cloneDeep(file); | |
817 | + // 파일 쓰기용 데이터를 선택 파일에 추가 | |
818 | + if (this.modeInfo == "fileWrite") { | |
819 | + this.jobItm.itm["path"] = file.path; | |
820 | + this.jobItm.itm["fileName"] = file.text; | |
821 | + this.jobItm.itm["fileFom"] = file.extension; | |
822 | + this.jobItm.itm["streOptn"] = false; | |
823 | + this.jobItm.itm["suffix"] = "_YYYYMMDDHHmmss"; | |
824 | + this.jobItm.itm["addMonth"] = 0; | |
825 | + this.selectedFile["path"] = file.path; | |
826 | + this.selectedFile["fileName"] = file.text; | |
827 | + this.selectedFile["fileFom"] = file.extension; | |
828 | + this.selectedFile["streOptn"] = false; | |
829 | + this.selectedFile["suffix"] = "_YYYYMMDDHHmmss"; | |
830 | + this.selectedFile["addMonth"] = 0; | |
831 | + } | |
832 | + }, | |
833 | + | |
834 | + // 파일/폴더 목록 선택 | |
835 | + selectFileList(item) { | |
836 | + // 폴더가 선택된 경우, 선택된 노드와 경로를 업데이트하고 파일 리스트 조회 | |
837 | + if (item.extension === "폴더" || item.extension === "") { | |
838 | + this.selectedNode = item.id; | |
839 | + this.connection.path = item.id; | |
840 | + this.fileSelectList(); | |
841 | + } else { | |
842 | + this.selectFile(item); // 파일이 선택된 경우 | |
843 | + } | |
844 | + }, | |
845 | + | |
846 | + // 파일 미리보기 | |
847 | + async filePreview(file) { | |
848 | + if (this.selectedFile == null || file["id"] != this.selectedFile["id"]) { | |
849 | + await this.selectFile(file); // 선택 파일 정보 저장 | |
850 | + } | |
851 | + this.preview = true; // 미리보기 활성화 | |
852 | + await this.fileRead(); // 파일 읽기 | |
853 | + this.currentPage = 2; // 데이터셋 설정 페이지로 이동 | |
854 | + }, | |
855 | + | |
856 | + // 초기화 function | |
857 | + init: function () { | |
858 | + this.selectHosts(); | |
859 | + this.searchInit(); | |
860 | + }, | |
861 | + | |
862 | + // 파일 읽기 | |
863 | + async fileRead() { | |
864 | + const vm = this; | |
865 | + vm.isLoading = true; | |
866 | + | |
867 | + // 선택 파일 정보를 VO 형태로 변환 | |
868 | + axios | |
869 | + .post("/files/read/" + vm.connection.host_code, { | |
870 | + path: vm.jobItm.itm.path, | |
871 | + fileName: vm.jobItm.itm.text, | |
872 | + extension: vm.jobItm.itm.extension, | |
873 | + type: "file", | |
874 | + datasetId: "", | |
875 | + viewMode: true, | |
876 | + }) | |
877 | + .then((response) => { | |
878 | + // 잡아이템에 선택 파일 정보 저장 | |
879 | + if (vm.modeInfo == "fileRead") { | |
880 | + vm.jobItm.itm.text = vm.selectedFile.text; | |
881 | + } | |
882 | + vm.jobItm.itm.path = vm.selectedFile.path; | |
883 | + vm.jobItm.itm.extension = vm.selectedFile.extension; | |
884 | + vm.jobItm.itm.type = "file"; | |
885 | + vm.jobItm.itm.datasetId = vm.connection.host_code; | |
886 | + vm.jobItm.itm.viewMode = true; | |
887 | + | |
888 | + vm.dataTable = response.data.resultData.dataTableMap; | |
889 | + vm.jobItm.dataTable = vm.dataTable; // 잡아이템에 데이터셋 정보 저장 | |
890 | + | |
891 | + vm.columnDatas = vm.dataTable.columnDatas; | |
892 | + vm.jobItm.front_dataTable.columnDatas = vm.columnDatas; | |
893 | + }) | |
894 | + .catch(() => { | |
895 | + vm.isLoading = false; | |
896 | + }); | |
897 | + }, | |
898 | + | |
899 | + // 데이터셋 시작 행, 열 정보 변경시 | |
900 | + indexChange: function (v) { | |
901 | + this.jobItm.dataTable = v; // 잡아이템 데이터셋에 변경된 데이터셋 정보 저장 | |
902 | + this.jobItm.itm.rowDataColumnIndex = v.rowDataColumnIndex; | |
903 | + this.jobItm.itm.startRowIndex = v.startRowIndex; | |
904 | + this.jobItm.itm.startCellIndex = v.startCellIndex; | |
905 | + }, | |
906 | + }, | |
907 | + watch: { | |
908 | + // 연결 호스트 변경 시 | |
909 | + selectedHostCode: function (newValue, oldValue) { | |
910 | + for (let i = 0; i < this.hostList.length; i++) { | |
911 | + if (this.hostList[i].host_code === newValue) { | |
912 | + this.selectedHost = this.hostList[i]; | |
913 | + break; | |
914 | + } | |
915 | + } | |
916 | + }, | |
917 | + }, | |
918 | + components: { | |
919 | + SvgIcon: SvgIcon, | |
920 | + TreeItem: TreeItem, | |
921 | + FileDataRead: FileDataRead, | |
922 | + }, | |
923 | + mounted() { | |
924 | + this.init(); | |
925 | + }, | |
926 | +}; | |
927 | +</script> |
+++ client/views/component/connection/jobContainer.vue
... | ... | @@ -0,0 +1,273 @@ |
1 | +<template> | |
2 | + <div class="row"> | |
3 | + <div class="table-zone pd0"> | |
4 | + <table class="form-table"> | |
5 | + <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 --> | |
6 | + <colgroup> | |
7 | + <col style="width: 15%" /> | |
8 | + <col style="width: 85%" /> | |
9 | + </colgroup> | |
10 | + <tbody> | |
11 | + <tr> | |
12 | + <th>데이터 선택</th> | |
13 | + <td> | |
14 | + <button class="blue-border-btn small-btn" @click="itmSelectModal = true">아이템 추가</button> | |
15 | + <button class="blue-btn small-btn" @click="execModel">데이터 적용</button> | |
16 | + </td> | |
17 | + </tr> | |
18 | + <tr> | |
19 | + <td colspan="2"> | |
20 | + <div class="data-list" :style="'height:' + componentHright + 'px;'"> | |
21 | + <ul class="flex"> | |
22 | + <li v-for="(itm, indx) in crrentJobGroup.jobItm" :key="indx" class="flex50"> | |
23 | + <div class="item flex justify-between align-center"> | |
24 | + <p>{{ indx + 1 + ". " + itm.type }}</p> | |
25 | + <div> | |
26 | + <button class="blue-border-btn set-btn" @click="showPopup(indx)">설정</button> | |
27 | + <!-- <button class="red-border-btn set-btn" @click="deleteConnect(indx)">삭제</button> --> | |
28 | + </div> | |
29 | + <FileSelect :openPopup="popupToggls[indx]" :jobItem="itm" :mode="itm.type" @closePopup="closePopup(indx)" v-if="itm.type == 'fileRead' || itm.type == 'fileWrite'" /> | |
30 | + <DatabaseConnection :openPopup="popupToggls[indx]" :jobItem="itm" @closePopup="closePopup(indx)" v-if="itm.type == 'dbConnection'" /> | |
31 | + <DataCheck :openPopup="popupToggls[indx]" :jobItem="itm" @closePopup="closePopup(indx)" v-if="itm.type == 'dataCheck'" /> | |
32 | + <DataSort :openPopup="popupToggls[indx]" :item="itm" @closePopup="closePopup(indx)" v-if="itm.type == 'dataSort'" /> | |
33 | + <DataFilter :openPopup="popupToggls[indx]" :item="itm" @closePopup="closePopup(indx)" v-if="itm.type == 'dataFilter'" /> | |
34 | + </div> | |
35 | + </li> | |
36 | + </ul> | |
37 | + </div> | |
38 | + </td> | |
39 | + </tr> | |
40 | + </tbody> | |
41 | + </table> | |
42 | + </div> | |
43 | + </div> | |
44 | + <div v-show="itmSelectModal" class="modal-wrapper"> | |
45 | + <div class="modal-container small-modal"> | |
46 | + <div class="modal-title"> | |
47 | + <div class="flex justify-between align-center"> | |
48 | + <h2>아이템 선택</h2> | |
49 | + <button class="close-btn" @click="itmSelectModal = false">X</button> | |
50 | + </div> | |
51 | + </div> | |
52 | + <div class="modal-content-monthly"> | |
53 | + <div class="justify-center aling-center" style="text-align: center"> | |
54 | + <select class="square-select half-input" v-model="selectItm"> | |
55 | + <!-- <option v-show="crrentJobGroup.datasetPost_id == null" value=""> 데이터셋 읽기 </option> | |
56 | + <option v-show="crrentJobGroup.datasetPost_id == null" value="fileRead"> 파일 읽기 </option> --> | |
57 | + <option v-show="crrentJobGroup.datasetPost_id == null" value="dbConnection"> DB 수집 </option> | |
58 | + <!-- <option v-show="crrentJobGroup.datasetPost_id == null" value=""> API 수집 </option> | |
59 | + <option v-show="crrentJobGroup.datasetPost_id == null" value="dataSort"> 데이터검증 </option> | |
60 | + <option v-show="crrentJobGroup.datasetPost_id == null" value="fileWrite"> 파일 쓰기 </option> --> | |
61 | + <!-- <option value="dataFilter">필터 모듈</option> | |
62 | + <option value="dataSort">정렬 모듈</option> --> | |
63 | + </select> | |
64 | + </div> | |
65 | + </div> | |
66 | + <div class="modal-end flex justify-end"> | |
67 | + <button class="darkg-border-btn small-btn" @click="addItm">확인</button> | |
68 | + </div> | |
69 | + </div> | |
70 | + </div> | |
71 | +</template> | |
72 | +<script> | |
73 | +import FileSelect from "./itm/fileSelect.vue"; | |
74 | +import DatabaseConnection from "./itm/databaseConnection.vue"; | |
75 | +import DataCheck from "./itm/dataCheck.vue"; | |
76 | +import DataSort from "./itm/dataSort.vue"; | |
77 | +import DataFilter from "./itm/dataFilter.vue"; | |
78 | + | |
79 | +import axios from "axios"; | |
80 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
81 | +import { mdiTrashCan, mdiCog } from "@mdi/js"; | |
82 | +export default { | |
83 | + name: "job-container", | |
84 | + props: { | |
85 | + jobGroup: { | |
86 | + type: Object, | |
87 | + default: null, | |
88 | + }, | |
89 | + height: { | |
90 | + type: Number, | |
91 | + default: 100, | |
92 | + }, | |
93 | + }, | |
94 | + data() { | |
95 | + return { | |
96 | + popupToggls: [], | |
97 | + itmSelectModal: false, | |
98 | + // , jobGroups: this.jobGroup | |
99 | + crrentJobGroup: this.jobGroup, | |
100 | + componentHright: this.height, | |
101 | + defaultJobGroup: Object, //JSON.parse(JSON.stringify(this.$getDefaultJobGroup().jobGroup)) | |
102 | + defaultJobItm: Object, //JSON.parse(JSON.stringify(this.$getDefaultJobGroup().jobItm)) | |
103 | + delPath: mdiTrashCan, | |
104 | + setPath: mdiCog, | |
105 | + selectItm: "dbConnection", | |
106 | + }; | |
107 | + }, | |
108 | + methods: { | |
109 | + // 팝업 호출 | |
110 | + showPopup: function (indx) { | |
111 | + if ( | |
112 | + this.crrentJobGroup.jobItm[indx - 1] != null && | |
113 | + this.crrentJobGroup.datasetPost_id == null | |
114 | + ) { | |
115 | + console.log('show') | |
116 | + this.crrentJobGroup.jobItm[indx].front_dataTable = | |
117 | + this.crrentJobGroup.jobItm[indx - 1].dataTable; | |
118 | + } | |
119 | + | |
120 | + this.popupToggls[indx] = true; | |
121 | + }, | |
122 | + closePopup: function (indx) { | |
123 | + this.popupToggls[indx] = false; | |
124 | + | |
125 | + }, | |
126 | + | |
127 | + // 기본 job 데이터 | |
128 | + getDefaultJobGroup: function () { | |
129 | + var vm = this; | |
130 | + this.defaultJobGroup = JSON.parse( | |
131 | + JSON.stringify(this.$getDefaultJobGroup().jobGroup) | |
132 | + ); | |
133 | + this.defaultJobItm = JSON.parse( | |
134 | + JSON.stringify(this.$getDefaultJobGroup().jobItem) | |
135 | + ); | |
136 | + if (this.crrentJobGroup == null) { | |
137 | + this.crrentJobGroup = JSON.parse(JSON.stringify(this.defaultJobGroup)); | |
138 | + } | |
139 | + }, | |
140 | + | |
141 | + // 아이템 선택 | |
142 | + addItm: function () { | |
143 | + var type = this.selectItm; | |
144 | + this.itmSelectModal = false; | |
145 | + let newItm = JSON.parse( | |
146 | + JSON.stringify(this.$getDefaultJobGroup().jobItem) | |
147 | + ); | |
148 | + newItm.type = type; | |
149 | + newItm.indx = this.crrentJobGroup.jobItm.length + 1; | |
150 | + | |
151 | + // 파일 읽기 모듈 | |
152 | + if (type == "fileRead") { | |
153 | + newItm.itm = JSON.parse( | |
154 | + JSON.stringify(this.$getDefaultJobGroup().fileRead) | |
155 | + ); | |
156 | + } | |
157 | + | |
158 | + // 데이터 베이스 연계 모듈 | |
159 | + else if (type == "dbConnection") { | |
160 | + newItm.itm = JSON.parse( | |
161 | + JSON.stringify(this.$getDefaultJobGroup().connectionDb) | |
162 | + ); | |
163 | + } | |
164 | + | |
165 | + // 데이터 체크모듈 (안씀) | |
166 | + else if (type == "dataCheck") { | |
167 | + newItm.itm = JSON.parse( | |
168 | + JSON.stringify(this.$getDefaultJobGroup().dataCheck) | |
169 | + ); | |
170 | + newItm.itm_option_string = "1"; | |
171 | + } | |
172 | + | |
173 | + // 데이터 정렬 초기화 | |
174 | + else if (type == "dataSort") { | |
175 | + newItm.itm = JSON.parse( | |
176 | + JSON.stringify(this.$getDefaultJobGroup().dataFilter) | |
177 | + ); | |
178 | + newItm.itm.filterItems.push( | |
179 | + JSON.parse(JSON.stringify(this.$getDefaultJobGroup().filterItem)) | |
180 | + ); | |
181 | + newItm.itm.filterItems[0].cmpr_value = "DESC"; | |
182 | + newItm.itm.filterItems[0].cmpr_value2 = "10000"; | |
183 | + // 잡그룹이 데이터셋 형일때 | |
184 | + if (this.crrentJobGroup.datasetPost_id != null) { | |
185 | + newItm.front_dataTable = JSON.parse( | |
186 | + JSON.stringify(this.crrentJobGroup.dataTable) | |
187 | + ); | |
188 | + } | |
189 | + } | |
190 | + | |
191 | + // 데이터 필터 초기화 | |
192 | + else if (type == "dataFilter") { | |
193 | + newItm.itm = JSON.parse( | |
194 | + JSON.stringify(this.$getDefaultJobGroup().dataFilter) | |
195 | + ); | |
196 | + newItm.itm.match_type = true; | |
197 | + // 잡그룹이 데이터셋 형일때 | |
198 | + if (this.crrentJobGroup.datasetPost_id != null) { | |
199 | + newItm.front_dataTable = JSON.parse( | |
200 | + JSON.stringify(this.crrentJobGroup.dataTable) | |
201 | + ); | |
202 | + } | |
203 | + } | |
204 | + | |
205 | + // 파일 쓰기 모듈 | |
206 | + else if (type == "fileWrite") { | |
207 | + newItm.itm = JSON.parse( | |
208 | + JSON.stringify(this.$getDefaultJobGroup().fileRead) | |
209 | + ); | |
210 | + } | |
211 | + | |
212 | + if ( | |
213 | + this.crrentJobGroup.jobItm.length > 0 && | |
214 | + this.crrentJobGroup.datasetPost_id == null | |
215 | + ) { | |
216 | + newItm.front_dataTable = | |
217 | + this.crrentJobGroup.jobItm[ | |
218 | + this.crrentJobGroup.jobItm.length - 1 | |
219 | + ].dataTable; | |
220 | + } | |
221 | + | |
222 | + this.popupToggls.push(false); | |
223 | + | |
224 | + this.crrentJobGroup.jobItm.push(newItm); | |
225 | + }, | |
226 | + | |
227 | + execModel: function () { | |
228 | + let vm = this; | |
229 | + axios({ | |
230 | + url: "/job/executeJob.json", | |
231 | + method: "post", | |
232 | + headers: { | |
233 | + "Content-Type": "application/json; charset=UTF-8", | |
234 | + }, | |
235 | + data: vm.crrentJobGroup, | |
236 | + }) | |
237 | + .then(function (response) { | |
238 | + vm.crrentJobGroup.dataTable = response.data.resultData.dataTable; | |
239 | + vm.$emit('getDataTable', vm.crrentJobGroup.dataTable) | |
240 | + }) | |
241 | + .catch(function (error) { | |
242 | + this.$showAlert( | |
243 | + "에러 발생", | |
244 | + "에러가 발생했습니다. 관리자에게 문의해 주세요." | |
245 | + ); | |
246 | + }); | |
247 | + }, | |
248 | + deleteConnect: function (indx) { | |
249 | + this.crrentJobGroup.jobItm.splice(indx, 1); | |
250 | + }, | |
251 | + }, | |
252 | + watch: { | |
253 | + jobGroup: function (v) { | |
254 | + this.crrentJobGroup = v; | |
255 | + }, | |
256 | + height: function (v) { | |
257 | + this.componentHright = v; | |
258 | + }, | |
259 | + | |
260 | + }, | |
261 | + components: { | |
262 | + FileSelect: FileSelect, | |
263 | + DatabaseConnection: DatabaseConnection, | |
264 | + SvgIcon: SvgIcon, | |
265 | + DataCheck: DataCheck, | |
266 | + DataSort: DataSort, | |
267 | + DataFilter: DataFilter, | |
268 | + }, | |
269 | + mounted() { | |
270 | + this.getDefaultJobGroup(); | |
271 | + }, | |
272 | +}; | |
273 | +</script>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/dataComponent/ColumnEditComponent.vue
... | ... | @@ -0,0 +1,298 @@ |
1 | +<template> | |
2 | + <div class="content content-box"> | |
3 | + <div class="flex-column justify-between"> | |
4 | + <div style="height: 100%"> | |
5 | + <div class="content-titleZone"> | |
6 | + <div class="flex justify-between align-center no-gutter"> | |
7 | + <div class="flex30"> | |
8 | + <p class="box-title">컬럼정보</p> | |
9 | + </div> | |
10 | + <div class="flex justify-end flex70"> | |
11 | + <button class="darkg-border-btn small-btn" @click="executionChangeData('on')"> 자동PK 생성 </button> | |
12 | + <button class="blue-border-btn small-btn" @click="addColumn"> 추가 </button> | |
13 | + <button class="blue-btn small-btn" @click="executionChangeData('off')"> 실행 </button> | |
14 | + </div> | |
15 | + </div> | |
16 | + </div> | |
17 | + <div class="content-zone" style="overflow: auto"> | |
18 | + <div class="table-zone"> | |
19 | + <div class="count-zone"> | |
20 | + <p> 총 ROW : <span> {{ coulmnsInfo.length }} </span> | |
21 | + </p> | |
22 | + </div> | |
23 | + <table class="list-table"> | |
24 | + <colgroup> | |
25 | + <col width="80px" /> | |
26 | + <col width="80px" /> | |
27 | + <col width="" /> | |
28 | + <col width="" /> | |
29 | + <col width="" /> | |
30 | + <col width="100px" /> | |
31 | + <col width="50px" /> | |
32 | + <col width="50px" /> | |
33 | + <col width="400px" /> | |
34 | + </colgroup> | |
35 | + <thead> | |
36 | + <tr> | |
37 | + <th>No</th> | |
38 | + <th>상태</th> | |
39 | + <th>컬럼명(영문)</th> | |
40 | + <th>컬럼한글(한글)</th> | |
41 | + <th>데이터유형</th> | |
42 | + <th>데이터길이</th> | |
43 | + <th>PK</th> | |
44 | + <th>표준</th> | |
45 | + <th>설정</th> | |
46 | + </tr> | |
47 | + </thead> | |
48 | + <tbody v-if="coulmnsInfo != null && coulmnsInfo.length > 0"> | |
49 | + <tr v-for="(cells, i) in coulmnsInfo" :key="i"> | |
50 | + <td>{{ i + 1 }}</td> | |
51 | + <td style="text-align: center"> | |
52 | + <span>{{ cells.status }}</span> | |
53 | + </td> | |
54 | + <td> | |
55 | + <span v-if="!cells.enableEdit">{{ cells.columnNm }}</span> | |
56 | + <input v-else type="text" class="width100p" v-model="cells.columnNm" /> | |
57 | + </td> | |
58 | + <td> | |
59 | + <span v-if="!cells.enableEdit">{{ cells.displyColumnNm }}</span> | |
60 | + <input v-else type="text" autocomplete="off" name="text" class="width100p" v-model="cells.displyColumnNm" /> | |
61 | + <!-- @input="getStandardTermListByContains(cells, $event.target.value, i)" | |
62 | + @keyup="selectingStandardTermByKey(cells, $event, i)"> --> | |
63 | + <div class="search_list_box" id="search_list" name="search_list" v-show="cells.searchListViewAt == true" style=" | |
64 | + top: auto; | |
65 | + left: auto; | |
66 | + border: solid 0.1px #bcbcbc; | |
67 | + max-width: 400px; | |
68 | + "> | |
69 | + <div class="item" v-for="(search, j) in cells.standardTermSearchList" :key="j" :title="'한글명:' + | |
70 | + search.korean + | |
71 | + '/영문명:' + | |
72 | + search.english + | |
73 | + '/영문약어명:' + | |
74 | + search.englishAbbr | |
75 | + " :id="search.termSeqs" :class="{ | |
76 | + item_selected: | |
77 | + cells.selectingStandardTerm != null && | |
78 | + cells.selectingStandardTerm.termSeqs == | |
79 | + search.termSeqs, | |
80 | + }"></div> | |
81 | + </div> | |
82 | + </td> | |
83 | + <td> | |
84 | + <span v-if="!cells.enableEdit">{{ cells.dataTy }}</span> | |
85 | + <select name="public" class="select_R width120 mr5" v-model="cells.dataTy" v-else> | |
86 | + <option v-for="(item, indx) in dataTypeList" :value="item" :key="indx"> {{ item }} </option> | |
87 | + </select> | |
88 | + </td> | |
89 | + <td> | |
90 | + <span v-if="!cells.enableEdit">{{ cells.dataSize }}</span> | |
91 | + <input v-else type="number" class="width100p" v-model="cells.dataSize" /> | |
92 | + </td> | |
93 | + <td style="text-align: center"> | |
94 | + <input type="checkbox" :id="'pr' + i" v-model="cells.pkAt" :disabled="cells.enableEdit == false" /> | |
95 | + <label :for="'pr' + i" style="padding-left: 0px"></label> | |
96 | + </td> | |
97 | + <td style="text-align: center"> | |
98 | + <input type="checkbox" :id="'st' + i" v-model="cells.columnStdizAt" :disabled="cells.enableEdit == false" /> | |
99 | + <label :for="'st' + i" style="padding-left: 0px"></label> | |
100 | + </td> | |
101 | + <td style="text-align: center"> | |
102 | + <button type="button" class="blue-border-btn small-btn" title="수정" v-if="!cells.enableEdit" @click="editStart(cells)"> 수정 </button> | |
103 | + <button type="button" class="blue-border-btn small-btn" title="닫기" v-if="cells.enableEdit" @click="editEnd(cells)"> 완료 </button> | |
104 | + <button type="button" class="red-border-btn small-btn" v-if="cells.status != 3" title="삭제" @click="deleteColumn(cells)"> 삭제 </button> | |
105 | + <button type="button" class="red-border-btn small-btn" v-if="cells.status == 3" title="취소" @click="deleteColumn(cells)"> 취소 </button> | |
106 | + </td> | |
107 | + </tr> | |
108 | + </tbody> | |
109 | + </table> | |
110 | + </div> | |
111 | + </div> | |
112 | + </div> | |
113 | + </div> | |
114 | + </div> | |
115 | +</template> | |
116 | +<script> | |
117 | +import axios from "axios"; | |
118 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
119 | +import _ from "lodash"; | |
120 | + | |
121 | +export default { | |
122 | + props: { | |
123 | + datasetPost_id: { | |
124 | + type: String, | |
125 | + default: "", | |
126 | + }, | |
127 | + }, | |
128 | + data() { | |
129 | + return { | |
130 | + comId: | |
131 | + "comId_" + | |
132 | + Math.random().toString(36).substring(2, 15) + | |
133 | + Math.random().toString(36).substring(2, 15), | |
134 | + dataTable: _.cloneDeep(this.$getDefaultObject().dataTable), | |
135 | + dataTypeList: _.cloneDeep(this.$getDefaultObject().dataTypeList), | |
136 | + coulmn_infoId: | |
137 | + "coulmn_info" + | |
138 | + Math.random().toString(36).substring(2, 15) + | |
139 | + Math.random().toString(36).substring(2, 15), | |
140 | + coulmn_infoIds: | |
141 | + "coulmn_info" + | |
142 | + Math.random().toString(36).substring(2, 15) + | |
143 | + Math.random().toString(36).substring(2, 15), | |
144 | + coulmnsInfo: [], | |
145 | + changeData: [], | |
146 | + orgPkList: [], | |
147 | + currentPkList: [], | |
148 | + }; | |
149 | + }, | |
150 | + methods: { | |
151 | + init: function () { | |
152 | + let vm = this; | |
153 | + axios({ | |
154 | + url: "/dataset/getDataTableInfo.json", | |
155 | + method: "post", | |
156 | + headers: { | |
157 | + "Content-Type": "application/json; charset=UTF-8", | |
158 | + }, | |
159 | + data: { datapost_id: vm.datasetPost_id }, | |
160 | + }) | |
161 | + .then(function (response) { | |
162 | + vm.dataTable = response.data.resultData.dataTable; | |
163 | + vm.coulmnsInfo = response.data.resultData.dataTable.columnDatas; | |
164 | + vm.orgPkList = vm.getcurrentPkList(); | |
165 | + }) | |
166 | + .catch(function (error) { | |
167 | + this.$showAlert( | |
168 | + "에러 발생", | |
169 | + "에러가 발생했습니다. 관리자에게 문의해 주세요." | |
170 | + ); | |
171 | + }); | |
172 | + }, | |
173 | + editStart: function (data) { | |
174 | + data.enableEdit = true; | |
175 | + if (data.status == 1) { | |
176 | + data.orgData = _.cloneDeep(data); | |
177 | + } | |
178 | + }, | |
179 | + editEnd: function (data) { | |
180 | + data.enableEdit = false; | |
181 | + data.searchListViewAt = false; | |
182 | + data.standardTermSearchList = []; | |
183 | + this.checkData(data); | |
184 | + }, | |
185 | + checkData: function (data) { | |
186 | + var checkData = _.cloneDeep(data); | |
187 | + checkData.orgData = null; | |
188 | + checkData.enableEdit = true; | |
189 | + checkData.status = 1; | |
190 | + if (_.isEqual(checkData, data.orgData)) { | |
191 | + data.status = 1; | |
192 | + data.orgData = null; | |
193 | + } else { | |
194 | + data.status = 2; | |
195 | + } | |
196 | + }, | |
197 | + // 컬럼 추가 | |
198 | + addColumn: function () { | |
199 | + var columnData = _.cloneDeep(this.coulmnsInfo[0]); | |
200 | + columnData.dataTy = "STRING"; | |
201 | + columnData.displyColumnNm = "NEW_DT"; | |
202 | + columnData.orginlColumnNm = "NEW_DT"; | |
203 | + columnData.columnNm = "NEW_DT"; | |
204 | + columnData.pkAt = false; | |
205 | + columnData.columnStdizAt = false; | |
206 | + columnData.dataSize = 50; | |
207 | + columnData.ordr = this.coulmnsInfo.length; | |
208 | + columnData.status = 4; | |
209 | + columnData.enableEdit = true; | |
210 | + this.coulmnsInfo.push(columnData); | |
211 | + }, | |
212 | + // 컬럼 삭제 | |
213 | + deleteColumn: function (column) { | |
214 | + if (column.status == 3) { | |
215 | + if (column.orgData == null) { | |
216 | + column.status = 1; | |
217 | + } else { | |
218 | + column.status = 2; | |
219 | + } | |
220 | + } else if (column.status == 4) { | |
221 | + this.coulmnsInfo.splice(column.orders, 1); | |
222 | + } else { | |
223 | + column.status = 3; | |
224 | + } | |
225 | + }, | |
226 | + // 변경된 데이터 리스트 가져오기 | |
227 | + getChangeList: function () { | |
228 | + this.changeData = []; | |
229 | + for (var i = 0; i < this.coulmnsInfo.length; i++) { | |
230 | + if (this.coulmnsInfo[i].status != 1) { | |
231 | + this.changeData.push(this.coulmnsInfo[i]); | |
232 | + } | |
233 | + } | |
234 | + }, | |
235 | + // pk 찾기 | |
236 | + getcurrentPkList: function () { | |
237 | + var pkList = []; | |
238 | + for (var i = 0; i < this.coulmnsInfo.length; i++) { | |
239 | + if (this.coulmnsInfo[i].pkAt) { | |
240 | + pkList.push(this.coulmnsInfo[i].columnNm); | |
241 | + } | |
242 | + } | |
243 | + return pkList; | |
244 | + }, | |
245 | + // 데이터 변경 실행 | |
246 | + executionChangeData: function (autoPk) { | |
247 | + var vm = this; | |
248 | + this.getChangeList(); | |
249 | + | |
250 | + if (this.changeData.length == 0 && autoPk == false) { | |
251 | + alert("변경된 데이터가 없습니다."); | |
252 | + return false; | |
253 | + } | |
254 | + | |
255 | + var request = { | |
256 | + changeData: this.changeData, | |
257 | + connectionId: this.dataTable.dbConectId, | |
258 | + tableName: this.dataTable.tableNm, | |
259 | + databaseName: this.dataTable.databaseNm, | |
260 | + orgPkList: this.orgPkList, | |
261 | + currentPkList: this.getcurrentPkList(), | |
262 | + datasetId: this.dataTable.datasetId, | |
263 | + datasetPostId: this.dataTable.datasetPostId, | |
264 | + pkInsert: autoPk, | |
265 | + }; | |
266 | + axios({ | |
267 | + url: "/dataset/changeColumnInfo.json", | |
268 | + method: "post", | |
269 | + headers: { | |
270 | + "Content-Type": "application/json; charset=UTF-8", | |
271 | + }, | |
272 | + data: request, | |
273 | + }) | |
274 | + .then(function (response) { | |
275 | + if (response.data.checkMessage.success) { | |
276 | + vm.coulmnsInfo = response.data.resultData.columnDatas; | |
277 | + vm.orgPkList = vm.getcurrentPkList(); | |
278 | + vm.$showAlert("message", "변경완료"); | |
279 | + } else { | |
280 | + vm.$showAlert("경고", response.data.checkMessage.message); | |
281 | + } | |
282 | + }) | |
283 | + .catch(function (error) { | |
284 | + this.$showAlert( | |
285 | + "에러 발생", | |
286 | + "에러가 발생했습니다. 관리자에게 문의해 주세요." | |
287 | + ); | |
288 | + }); | |
289 | + }, | |
290 | + }, | |
291 | + components: { | |
292 | + SvgIcon: SvgIcon, | |
293 | + }, | |
294 | + mounted() { | |
295 | + this.init(); | |
296 | + }, | |
297 | +}; | |
298 | +</script>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/dataComponent/DataEditComponent.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/component/dataComponent/DbConnectionSearchModal.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/component/elementComponent/BaseComponent.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/component/elementComponent/ComponentTitle.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/component/elementComponent/EquipmentData.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/component/elementComponent/HorizentalThreeData.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/component/elementComponent/HorizentalTwoData.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/component/elementComponent/TableData.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/component/elementComponent/TestComponent.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/component/elementComponent/ThreeData.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/component/flowComponent/VueFlowZoneGroup.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/component/flowComponent/VueFlowZonePopup.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/component/style/BackgroundOption.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/component/style/CellOption.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/component/style/FontOption.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/component/style/StyleSheetComponent.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/component/style/TitleStyleComponent.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/index.html
This diff is skipped because there are too many other diffs. |
+++ client/views/index.js
This diff is skipped because there are too many other diffs. |
+++ client/views/layout/Header.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/layout/SideMenu.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/layout/TopMenu.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/layout/back241206_TopMenu.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/App.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/AppRouter.js
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/AppStore.js
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/bu_241206_AppRouter.js
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/custom/CustomInsert.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/custom/CustomInsert2.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/custom/CustomInsertDev.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/custom/CustomSelectList.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/custom/CustomSelectOne.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/data/DataEditView.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/data/DataPostDetail.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/data/DataPostManagement.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/data/FileManagement.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/data/HostManagement.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/data/InsertDataPost.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/data/JobTest.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/dbConnection/DBConnectionDetail.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/dbConnection/DBConnectionList.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/dbConnection/InsertDBConnection.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/gisinfo/GisInfoInsert.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/gisinfo/GisInfoList.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/gisinfo/GisInfoSelectListOne.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/integration/DBConnectionList.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/integration/DepartmentManagement.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/integration/InsertDBConnection.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/integration/UserManagement.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/login/Login.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/main/Main.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/meta/DataMetaManagement.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/meta/TermManagement.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/openapi/OpenApiInsert.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/openapi/OpenApiKeyList.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/openapi/OpenApiList.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/openapi/OpenApiSelectListOne.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/push/Push.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/push/service-worker.js
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/schedule/CustomNode.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/schedule/ScheduleLogManagement.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/schedule/ScheduleManagement.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/schedule/VueFlowZone.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/user/myPage.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/pages/user/myPagePwd.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/template/guide/TemplateGuide.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/template/layoutTemplate/Horizontal.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/template/layoutTemplate/Vertical.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/template/templateElement/AlertModal.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/template/templateElement/BtnPosition.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/template/templateElement/BtnTable.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/template/templateElement/DefaultSearchBar.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/template/templateElement/DetailSearchBar.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/template/templateElement/FormModal.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/template/templateElement/FormTable.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/template/templateElement/FormTable2.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/template/templateElement/Icon.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/template/templateElement/ListModal.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/template/templateElement/ListTable.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/template/templateElement/Searchbar.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/template/templateElement/StickyListTable.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/template/templateElement/Table.vue
This diff is skipped because there are too many other diffs. |
+++ client/views/template/templateElement/Test.vue
This diff is skipped because there are too many other diffs. |
+++ package-lock.json
This diff is skipped because there are too many other diffs. |
+++ package.json
This diff is skipped because there are too many other diffs. |
+++ server/modules/db/mysql/MysqlConnection.js
This diff is skipped because there are too many other diffs. |
+++ server/modules/db/oracle/OracleConnection.js
This diff is skipped because there are too many other diffs. |
+++ server/modules/db/oracle/client/client_19.16/BASIC_LICENSE
This diff is skipped because there are too many other diffs. |
+++ server/modules/db/oracle/client/client_19.16/BASIC_README
This diff is skipped because there are too many other diffs. |
+++ server/modules/db/oracle/client/client_19.16/adrci.exe
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/adrci.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/genezi.exe
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/genezi.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/oci.dll
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/oci.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/ocijdbc19.dll
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/ocijdbc19.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/ociw32.dll
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/ociw32.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/ojdbc8.jar
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/oramysql19.dll
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/oramysql19.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/orannzsbb19.dll
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/orannzsbb19.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/oraocci19.dll
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/oraocci19.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/oraocci19d.dll
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/oraocci19d.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/oraociei19.dll
This diff is skipped because there are too many other diffs. |
+++ server/modules/db/oracle/client/client_19.16/oraociei19.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/oraons.dll
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/orasql19.dll
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/orasql19.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/ucp.jar
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/uidrvci.exe
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/uidrvci.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/vc14/oraocci19.dll
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/vc14/oraocci19.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/vc14/oraocci19d.dll
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/vc14/oraocci19d.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_19.16/xstreams.jar
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/BASIC_LICENSE
This diff is skipped because there are too many other diffs. |
+++ server/modules/db/oracle/client/client_21.6/BASIC_README
This diff is skipped because there are too many other diffs. |
+++ server/modules/db/oracle/client/client_21.6/adrci.exe
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/adrci.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/genezi.exe
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/genezi.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/network/admin/README
This diff is skipped because there are too many other diffs. |
+++ server/modules/db/oracle/client/client_21.6/oci.dll
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/oci.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/ocijdbc21.dll
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/ocijdbc21.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/ociw32.dll
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/ociw32.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/ojdbc8.jar
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/oramysql.dll
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/oramysql.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/orannzsbb.dll
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/orannzsbb.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/oraocci21.dll
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/oraocci21.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/oraocci21d.dll
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/oraocci21d.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/oraociei.dll
This diff is skipped because there are too many other diffs. |
+++ server/modules/db/oracle/client/client_21.6/oraociei.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/orasql.dll
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/orasql.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/ucp.jar
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/uidrvci.exe
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/uidrvci.sym
Binary file is not shown |
+++ server/modules/db/oracle/client/client_21.6/xstreams.jar
Binary file is not shown |
+++ server/modules/db/postgresql/PostgresqlConnection.js
This diff is skipped because there are too many other diffs. |
+++ server/modules/log/Logger.js
This diff is skipped because there are too many other diffs. |
+++ server/modules/util/Queue.js
This diff is skipped because there are too many other diffs. |
+++ server/modules/web/2024020798270.crt.pem
This diff is skipped because there are too many other diffs. |
+++ server/modules/web/2024020798270.key.pem
This diff is skipped because there are too many other diffs. |
+++ server/modules/web/server.js
This diff is skipped because there are too many other diffs. |
+++ server/service/test/model/TestDAO.js
This diff is skipped because there are too many other diffs. |
+++ server/service/test/model/TestService.js
This diff is skipped because there are too many other diffs. |
+++ server/service/test/router/TestRouter.js
This diff is skipped because there are too many other diffs. |
+++ vetur.config.js
This diff is skipped because there are too many other diffs. |
+++ webpack.config.js
This diff is skipped because there are too many other diffs. |
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?