
--- client/views/common/commonPlugin.js
+++ client/views/common/commonPlugin.js
... | ... | @@ -93,6 +93,31 @@ |
93 | 93 |
}); |
94 | 94 |
} |
95 | 95 |
|
96 |
+ // 공통코드 호출 (부모 코드) |
|
97 |
+ Vue.config.globalProperties.$getCommonCodeByTree = async function (codeId) { |
|
98 |
+ const promise = new Promise((resolve, reject) => { |
|
99 |
+ axios({ |
|
100 |
+ url: '/common/getCodeListByTree.json', |
|
101 |
+ method: 'post', |
|
102 |
+ headers: { 'Content-Type': 'application/json; charset=UTF-8' }, |
|
103 |
+ data: JSON.stringify({ 'codeId': codeId }) |
|
104 |
+ }).then(function (response) { |
|
105 |
+ resolve(response.data.resultData.codeList) |
|
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 |
+ |
|
96 | 121 |
// 데이터베이스 타입호출 |
97 | 122 |
Vue.config.globalProperties.$getDataBaseTypeList = async function () { |
98 | 123 |
const promise = new Promise((resolve, reject) => { |
--- client/views/component/connection/EhojoConnection.vue
+++ client/views/component/connection/EhojoConnection.vue
... | ... | @@ -119,7 +119,7 @@ |
119 | 119 |
</div> |
120 | 120 |
<div v-if="modalView == 'preview'"> |
121 | 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> |
|
122 |
+ <p class="box-title">데이터셋 <small>(총 ROW: {{ rowData.length > 0 ? rowData.length : 0 }}개)</small> |
|
123 | 123 |
</p> |
124 | 124 |
</div> |
125 | 125 |
<div style="height: 400px; overflow: auto"> |
--- client/views/component/connection/itm/apiConnection.vue
+++ client/views/component/connection/itm/apiConnection.vue
... | ... | @@ -120,7 +120,7 @@ |
120 | 120 |
</tbody> |
121 | 121 |
</table> |
122 | 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> |
|
123 |
+ <p class="box-title"> 데이터셋 <small> (총 ROW: {{ rowData.length > 0 ? rowData.length : 0 }}개) </small> |
|
124 | 124 |
</p> |
125 | 125 |
</div> |
126 | 126 |
<table class="list-table table-flow"> |
... | ... | @@ -162,13 +162,11 @@ |
162 | 162 |
<script> |
163 | 163 |
import SvgIcon from "@jamescoyle/vue-icon"; |
164 | 164 |
import { mdiClose } from "@mdi/js"; |
165 |
-import ApiRusultCus from "./apiRusultCus.vue"; |
|
166 | 165 |
import axios from "axios"; |
167 | 166 |
|
168 | 167 |
export default { |
169 | 168 |
components: { |
170 | 169 |
SvgIcon: SvgIcon, |
171 |
- ApiRusultCus: ApiRusultCus, |
|
172 | 170 |
}, |
173 | 171 |
props: { |
174 | 172 |
openPopup: { |
... | ... | @@ -295,7 +293,7 @@ |
295 | 293 |
let apiUrl = this.apiUrl; |
296 | 294 |
if (apiUrl) { |
297 | 295 |
let parameters = apiUrl.substring(apiUrl.indexOf("?") + 1); |
298 |
- if (parameters.length > 1) { |
|
296 |
+ if (parameters.length > 0) { |
|
299 | 297 |
let params = parameters.split("&"); |
300 | 298 |
for (let param of params) { |
301 | 299 |
let paramIndex = param.indexOf("="); |
--- client/views/component/connection/itm/apiRusultCus.vue
... | ... | @@ -1,19 +0,0 @@ |
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/modalContents/ApiRead.vue
... | ... | @@ -0,0 +1,489 @@ |
1 | +<template> | |
2 | + <div v-if="currentPage == 1"> | |
3 | + <div class="content-titleZone"> | |
4 | + <p class="box-title">API 기본정보</p> | |
5 | + </div> | |
6 | + <table class="form-table"> | |
7 | + <colgroup> | |
8 | + <col style="width: 15%" /> | |
9 | + <col style="width: 85%" /> | |
10 | + </colgroup> | |
11 | + <tbody> | |
12 | + <tr> | |
13 | + <th>API URL</th> | |
14 | + <td> | |
15 | + <input | |
16 | + type="text" | |
17 | + class="full-input" | |
18 | + v-model="apiUrl" | |
19 | + @change="fnApiUrlChange" | |
20 | + /> | |
21 | + </td> | |
22 | + </tr> | |
23 | + <tr> | |
24 | + <th>결과타입</th> | |
25 | + <td> | |
26 | + <div class="flex"> | |
27 | + <div class="flex15 pl0"> | |
28 | + <input | |
29 | + type="radio" | |
30 | + name="json" | |
31 | + id="json" | |
32 | + value="1" | |
33 | + class="chekck-type" | |
34 | + v-model="jobItm.itm.type" | |
35 | + /> | |
36 | + <label for="json" class="chekcktype-label text-ct"> | |
37 | + json | |
38 | + </label> | |
39 | + </div> | |
40 | + <div class="flex15"> | |
41 | + <input | |
42 | + type="radio" | |
43 | + name="xml" | |
44 | + id="xml" | |
45 | + value="2" | |
46 | + class="chekck-type" | |
47 | + v-model="jobItm.itm.type" | |
48 | + /> | |
49 | + <label for="xml" class="chekcktype-label text-ct"> xml </label> | |
50 | + </div> | |
51 | + <div class="flex15 pr0"> | |
52 | + <input | |
53 | + type="radio" | |
54 | + name="seol" | |
55 | + id="seol" | |
56 | + value="3" | |
57 | + class="chekck-type" | |
58 | + v-model="jobItm.itm.type" | |
59 | + /> | |
60 | + <label for="seol" class="chekcktype-label text-ct"> | |
61 | + seol | |
62 | + </label> | |
63 | + </div> | |
64 | + </div> | |
65 | + </td> | |
66 | + </tr> | |
67 | + </tbody> | |
68 | + </table> | |
69 | + <div class="content-titleZone flex justify-between align-center mt20"> | |
70 | + <p class="box-title">파라미터 목록</p> | |
71 | + <div> | |
72 | + <button class="blue-border-btn small-btn" @click="fnAddRow()"> | |
73 | + 파라미터 추가 | |
74 | + </button> | |
75 | + <button | |
76 | + v-if="apiUrl" | |
77 | + class="blue-btn small-btn" | |
78 | + @click="fnAutoCreate()" | |
79 | + > | |
80 | + 자동 생성 | |
81 | + </button> | |
82 | + </div> | |
83 | + </div> | |
84 | + <div style="height: 400px; overflow: auto"> | |
85 | + <table class="list-table" style="table-layout: fixed"> | |
86 | + <colgroup> | |
87 | + <col width="10%" /> | |
88 | + <col width="30%" /> | |
89 | + <col width="50%" /> | |
90 | + <col width="10%" /> | |
91 | + </colgroup> | |
92 | + <thead> | |
93 | + <tr> | |
94 | + <th style="text-align: center">No</th> | |
95 | + <th style="text-align: center">Key</th> | |
96 | + <th style="text-align: center">Value</th> | |
97 | + <th style="text-align: center">삭제</th> | |
98 | + </tr> | |
99 | + </thead> | |
100 | + <tbody> | |
101 | + <template v-if="parameterList.length > 0"> | |
102 | + <tr v-for="(item, index) in parameterList" :key="index"> | |
103 | + <td style="text-align: center">{{ index + 1 }}</td> | |
104 | + <td> | |
105 | + <input type="text" class="full-input" v-model.trim="item.key" /> | |
106 | + </td> | |
107 | + <td> | |
108 | + <input | |
109 | + type="text" | |
110 | + class="full-input" | |
111 | + v-model.trim="item.value" | |
112 | + /> | |
113 | + </td> | |
114 | + <td> | |
115 | + <button class="cbtnDelete" @click="fnDeleteRow(item)"> | |
116 | + <svg-icon | |
117 | + type="mdi" | |
118 | + :width="20" | |
119 | + :height="20" | |
120 | + :path="delPath" | |
121 | + ></svg-icon> | |
122 | + </button> | |
123 | + </td> | |
124 | + </tr> | |
125 | + </template> | |
126 | + <tr v-else> | |
127 | + <td colspan="4">등록된 데이터가 없습니다.</td> | |
128 | + </tr> | |
129 | + </tbody> | |
130 | + </table> | |
131 | + </div> | |
132 | + </div> | |
133 | + <div v-if="currentPage == 2"> | |
134 | + <div class="content-titleZone flex justify-between align-center"> | |
135 | + <p class="box-title">미리보기</p> | |
136 | + <button | |
137 | + type="button" | |
138 | + class="blue-border-btn small-btn" | |
139 | + @click="fnDataKeySelect" | |
140 | + > | |
141 | + 조회 | |
142 | + </button> | |
143 | + </div> | |
144 | + <table class="form-table"> | |
145 | + <colgroup> | |
146 | + <col style="width: 15%" /> | |
147 | + <col style="width: 85%" /> | |
148 | + </colgroup> | |
149 | + <tbody> | |
150 | + <tr> | |
151 | + <th>사용할 데이터</th> | |
152 | + <td> | |
153 | + <select v-model="selectKey"> | |
154 | + <template v-if="apiResponseKeys.length > 0"> | |
155 | + <option value="">사용할 데이터를 선택하세요.</option> | |
156 | + <option | |
157 | + v-for="(key, index) of apiResponseKeys" | |
158 | + :key="index" | |
159 | + :value="key" | |
160 | + > | |
161 | + {{ key }} | |
162 | + </option> | |
163 | + </template> | |
164 | + <option v-else value="">전체 조회</option> | |
165 | + </select> | |
166 | + </td> | |
167 | + </tr> | |
168 | + </tbody> | |
169 | + </table> | |
170 | + <div class="content-titleZone flex justify-between align-center mt20"> | |
171 | + <p class="box-title"> | |
172 | + 데이터셋 | |
173 | + <small> | |
174 | + (총 ROW: {{ rowData.length > 0 ? rowData.length : 0 }}개) | |
175 | + </small> | |
176 | + </p> | |
177 | + </div> | |
178 | + <table class="list-table table-flow"> | |
179 | + <template v-if="!$isEmpty(dataTable)"> | |
180 | + <thead> | |
181 | + <tr> | |
182 | + <th>No</th> | |
183 | + <th v-for="(rowKey, index) of rowKeys" :key="index"> | |
184 | + {{ rowKey }} | |
185 | + </th> | |
186 | + </tr> | |
187 | + </thead> | |
188 | + <tbody> | |
189 | + <tr v-for="(items, indexI) of rowData" :key="indexI"> | |
190 | + <td>{{ indexI + 1 }}</td> | |
191 | + <td v-for="(item, indexJ) of items" :key="indexJ"> | |
192 | + {{ item }} | |
193 | + </td> | |
194 | + </tr> | |
195 | + </tbody> | |
196 | + </template> | |
197 | + <tr v-else> | |
198 | + <td>등록된 데이터가 없습니다.</td> | |
199 | + </tr> | |
200 | + </table> | |
201 | + </div> | |
202 | +</template> | |
203 | +<script> | |
204 | +// icon용 svg import | |
205 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
206 | +import { mdiTrashCanOutline } from "@mdi/js"; | |
207 | + | |
208 | +import axios from "axios"; | |
209 | + | |
210 | +export default { | |
211 | + name: "ApiRead", | |
212 | + | |
213 | + components: { | |
214 | + SvgIcon, | |
215 | + }, | |
216 | + | |
217 | + props: { | |
218 | + jobItem: { | |
219 | + type: Object, | |
220 | + }, | |
221 | + currentPage: { | |
222 | + type: Number, | |
223 | + }, | |
224 | + }, | |
225 | + | |
226 | + data() { | |
227 | + return { | |
228 | + // icon용 svg path | |
229 | + delPath: mdiTrashCanOutline, | |
230 | + | |
231 | + jobItm: this.jobItem, | |
232 | + | |
233 | + apiUrl: null, | |
234 | + parameterList: [], | |
235 | + | |
236 | + apiResponse: null, | |
237 | + apiResponseKeys: [], | |
238 | + selectKey: "", | |
239 | + | |
240 | + dataTable: {}, | |
241 | + | |
242 | + rowData: [], | |
243 | + rowKeys: [], | |
244 | + }; | |
245 | + }, | |
246 | + | |
247 | + created() { | |
248 | + this.init(); | |
249 | + }, | |
250 | + | |
251 | + watch: { | |
252 | + jobItem(value) { | |
253 | + this.jobItm = value; | |
254 | + if (!this.$isEmpty(value.itm)) { | |
255 | + this.defaultValue(value.itm); | |
256 | + } | |
257 | + }, | |
258 | + | |
259 | + apiUrl(value) { | |
260 | + if (!this.$isEmpty(value)) { | |
261 | + this.$emit("onDfltSetChange", true); | |
262 | + } else { | |
263 | + this.$emit("onDfltSetChange", false); | |
264 | + } | |
265 | + }, | |
266 | + | |
267 | + parameterList: { | |
268 | + deep: true, | |
269 | + handler(value) { | |
270 | + if (value.length > 0) { | |
271 | + this.$emit("onDfltSetChange", true); | |
272 | + } else { | |
273 | + this.$emit("onDfltSetChange", false); | |
274 | + } | |
275 | + }, | |
276 | + }, | |
277 | + | |
278 | + currentPage(value) { | |
279 | + if (value == 2) { | |
280 | + this.apiRequestTest(); | |
281 | + } | |
282 | + }, | |
283 | + }, | |
284 | + | |
285 | + methods: { | |
286 | + // 초기화 function | |
287 | + init() { | |
288 | + if (!this.$isEmpty(this.jobItm)) { | |
289 | + this.defaultValue(this.jobItm.itm); | |
290 | + } else { | |
291 | + this.jobItm = Object.assign({}, this.$getDefaultJobGroup().node); | |
292 | + this.jobItm.itm = Object.assign( | |
293 | + {}, | |
294 | + this.$getDefaultJobGroup().connectionApi | |
295 | + ); | |
296 | + } | |
297 | + }, | |
298 | + | |
299 | + // url 분리 | |
300 | + defaultValue(item) { | |
301 | + if (item.url != null && item.url != "") { | |
302 | + if (item.url.indexOf("?") != -1) { | |
303 | + item.url.substring(0, item.url.indexOf("?")); | |
304 | + } | |
305 | + let params = ""; | |
306 | + for (let param of item.params) { | |
307 | + if (params != "") { | |
308 | + params += "&"; | |
309 | + } | |
310 | + params += param.key + "=" + param.value; | |
311 | + } | |
312 | + this.apiUrl = item.url + "?" + params; | |
313 | + | |
314 | + for (let param of item.params) { | |
315 | + this.parameterList.push({ | |
316 | + index: param.index, | |
317 | + key: param.key, | |
318 | + value: param.value, | |
319 | + addMonth: 0, | |
320 | + }); | |
321 | + } | |
322 | + } | |
323 | + }, | |
324 | + | |
325 | + // API URL 변경 | |
326 | + fnApiUrlChange() { | |
327 | + this.parameterList = []; // 파라미터 목록 초기화 | |
328 | + | |
329 | + // 미리보기 페이지 데이터 초기화 | |
330 | + this.selectKey = ""; | |
331 | + this.dataTable = {}; | |
332 | + this.rowData = []; | |
333 | + this.rowKeys = []; | |
334 | + }, | |
335 | + | |
336 | + // 파라미터 행 추가 | |
337 | + fnAddRow() { | |
338 | + this.parameterList.push({ | |
339 | + index: this.parameterList.length + 1, | |
340 | + key: null, | |
341 | + value: null, | |
342 | + addMonth: 0, | |
343 | + }); | |
344 | + }, | |
345 | + | |
346 | + // 파라미터 행 삭제 | |
347 | + fnDeleteRow(item) { | |
348 | + this.parameterList = this.parameterList.filter( | |
349 | + (parameter) => parameter !== item | |
350 | + ); | |
351 | + }, | |
352 | + | |
353 | + // 자동 생성 버튼 | |
354 | + async fnAutoCreate() { | |
355 | + // 덮어쓰기 경고 | |
356 | + if (this.parameterList.length > 0) { | |
357 | + let isChecked = await this.$showConfirm( | |
358 | + "경고", | |
359 | + "자동생성 실행 시, 기존에 작성한 파라미터가 삭제됩니다. 그래도 실행하시겠습니까?" | |
360 | + ); | |
361 | + if (!isChecked) { | |
362 | + return; | |
363 | + } else { | |
364 | + this.parameterList = []; // 파라미터 목록 초기화 | |
365 | + } | |
366 | + } | |
367 | + | |
368 | + // 실행 | |
369 | + let apiUrl = this.apiUrl; | |
370 | + if (apiUrl) { | |
371 | + let parameters = apiUrl.substring(apiUrl.indexOf("?") + 1); | |
372 | + if (parameters.length > 0) { | |
373 | + let params = parameters.split("&"); | |
374 | + for (let param of params) { | |
375 | + let paramIndex = param.indexOf("="); | |
376 | + this.parameterList.push({ | |
377 | + index: this.parameterList.length + 1, | |
378 | + key: param.substring(0, paramIndex), | |
379 | + value: param.substring(paramIndex + 1), | |
380 | + addMonth: 0, | |
381 | + }); | |
382 | + } | |
383 | + } | |
384 | + } | |
385 | + }, | |
386 | + | |
387 | + // 셀 크기 | |
388 | + fnColWidth(keys) { | |
389 | + return `width: ${100 / (keys + 1)}%`; | |
390 | + }, | |
391 | + | |
392 | + // 다음 버튼 | |
393 | + apiRequestTest() { | |
394 | + const vm = this; | |
395 | + // 데이터 세팅 | |
396 | + vm.jobItm.itm.url = vm.apiUrl.substring(0, vm.apiUrl.indexOf("?")); | |
397 | + vm.jobItm.itm.params = vm.parameterList; | |
398 | + // 실행 | |
399 | + axios({ | |
400 | + url: "/apiRequestTest.json", | |
401 | + method: "post", | |
402 | + headers: { | |
403 | + "Content-Type": "application/json; charset=UTF-8", | |
404 | + }, | |
405 | + data: vm.jobItm.itm, | |
406 | + }) | |
407 | + .then((response) => { | |
408 | + vm.apiResponseKeys = response.data.key; | |
409 | + vm.modalView = "preview"; // 모달창 변경 | |
410 | + }) | |
411 | + .catch((error) => { | |
412 | + vm.$showAlert( | |
413 | + "결과내용", | |
414 | + "응답이 없습니다. 파라미터를 확인해 주세요." | |
415 | + ); | |
416 | + }); | |
417 | + }, | |
418 | + | |
419 | + // 조회 버튼 | |
420 | + fnDataKeySelect() { | |
421 | + let vm = this; | |
422 | + if ( | |
423 | + vm.apiResponseKeys.length > 0 && | |
424 | + (vm.selectKey == null || vm.selectKey == "") | |
425 | + ) { | |
426 | + vm.$showAlert("경고", "사용할 데이터를 선택해 주세요."); | |
427 | + return; | |
428 | + } | |
429 | + vm.jobItm.itm.depth = vm.selectKey; | |
430 | + | |
431 | + // 데이터 세팅 | |
432 | + vm.jobItm.itm.url = vm.apiUrl.substring(0, vm.apiUrl.indexOf("?")); | |
433 | + | |
434 | + // 실행 | |
435 | + axios({ | |
436 | + url: "/getApiDataTable.json", | |
437 | + method: "post", | |
438 | + headers: { | |
439 | + "Content-Type": "application/json; charset=UTF-8", | |
440 | + }, | |
441 | + data: vm.jobItm.itm, | |
442 | + }) | |
443 | + .then((response) => { | |
444 | + let result = response.data; | |
445 | + vm.dataTable = result; | |
446 | + vm.rowKeys = []; // 초기화 | |
447 | + for (let columnData of result.columnDatas) { | |
448 | + vm.rowKeys.push(columnData.columnNm); | |
449 | + // displayColumnNm 초기값 설정 | |
450 | + columnData.displyColumnNm = columnData.orginlColumnNm; | |
451 | + } | |
452 | + vm.rowData = result.rowData; | |
453 | + | |
454 | + vm.$emit("onDataSetChange", true); | |
455 | + }) | |
456 | + .catch((error) => { | |
457 | + vm.$emit("onDataSetChange", false); | |
458 | + vm.$showAlert("결과내용", "응답이 없습니다. 데이터를 확인해 주세요."); | |
459 | + }); | |
460 | + }, | |
461 | + | |
462 | + // 저장 버튼 | |
463 | + fnSave() { | |
464 | + // 유효성 검사 | |
465 | + if (this.$isEmpty(this.apiUrl)) { | |
466 | + this.$showAlert("경고", "API URL을 입력해 주세요."); | |
467 | + return false; | |
468 | + } | |
469 | + if (this.$isEmpty(this.dataTable)) { | |
470 | + this.$showAlert("경고", "사용할 데이터를 조회해 주세요."); | |
471 | + return false; | |
472 | + } | |
473 | + | |
474 | + // 데이터 세팅 | |
475 | + this.jobItm.dataTable = this.dataTable; | |
476 | + | |
477 | + // 실행 | |
478 | + this.$emit("fnSaveSetup", this.jobItm); | |
479 | + }, | |
480 | + }, | |
481 | +}; | |
482 | +</script> | |
483 | + | |
484 | +<style scoped> | |
485 | +.table-flow { | |
486 | + width: 100%; | |
487 | + word-wrap: break-word; | |
488 | +} | |
489 | +</style>(No newline at end of file) |
+++ client/views/component/connection/modalContents/DataFilter.vue
... | ... | @@ -0,0 +1,226 @@ |
1 | +<template> | |
2 | + <div class="content-titleZone"> | |
3 | + <p class="box-title">필터 종류</p> | |
4 | + </div> | |
5 | + <table class="form-table"> | |
6 | + <colgroup> | |
7 | + <col style="width: 15%" /> | |
8 | + <col style="width: 85%" /> | |
9 | + </colgroup> | |
10 | + <tbody> | |
11 | + <tr> | |
12 | + <th>필터 종류</th> | |
13 | + <td> | |
14 | + <div class="input-container flex"> | |
15 | + <label class="radio-label"> | |
16 | + <input | |
17 | + type="radio" | |
18 | + class="custom-radiobox" | |
19 | + name="matchType" | |
20 | + :value="true" | |
21 | + v-model="jobItm.itm.match_type" | |
22 | + /> | |
23 | + <span>AND 연산</span> | |
24 | + </label> | |
25 | + <label class="radio-label"> | |
26 | + <input | |
27 | + type="radio" | |
28 | + class="custom-radiobox" | |
29 | + name="matchType" | |
30 | + :value="false" | |
31 | + v-model="jobItm.itm.match_type" | |
32 | + /> | |
33 | + <span>OR 연산</span> | |
34 | + </label> | |
35 | + </div> | |
36 | + </td> | |
37 | + </tr> | |
38 | + </tbody> | |
39 | + </table> | |
40 | + <div class="content-titleZone flex justify-between align-center mt20"> | |
41 | + <p class="box-title">필터 목록</p> | |
42 | + <div> | |
43 | + <button class="blue-border-btn small-btn" @click="addFiter"> | |
44 | + 필터 추가 | |
45 | + </button> | |
46 | + </div> | |
47 | + </div> | |
48 | + <div style="height: 400px; overflow: auto"> | |
49 | + <table class="list-table" style="table-layout: fixed"> | |
50 | + <colgroup> | |
51 | + <col style="width: 30%" /> | |
52 | + <col style="width: 30%" /> | |
53 | + <col style="width: 30%" /> | |
54 | + <col style="width: 10%" /> | |
55 | + </colgroup> | |
56 | + <thead> | |
57 | + <tr> | |
58 | + <th>컬럼</th> | |
59 | + <th>연산</th> | |
60 | + <th>내용</th> | |
61 | + <th>관리</th> | |
62 | + </tr> | |
63 | + </thead> | |
64 | + <tbody> | |
65 | + <tr v-for="(filter, idx) in jobItm.itm.filterItems" :key="idx"> | |
66 | + <td> | |
67 | + <select | |
68 | + class="full-select" | |
69 | + v-model="filter.coulmn_nm" | |
70 | + @change="onChange(filter)" | |
71 | + > | |
72 | + <option :value="null">필터를 적용할 컬럼을 선택하세요.</option> | |
73 | + <option | |
74 | + v-for="(column, index) of frontNode.dataTable.columnDatas" | |
75 | + :key="index" | |
76 | + :value="column.orginlColumnNm" | |
77 | + > | |
78 | + {{ column.displyColumnNm }} | |
79 | + </option> | |
80 | + </select> | |
81 | + </td> | |
82 | + <td> | |
83 | + <select class="full-select" v-model="filter.calc_ty"> | |
84 | + <option :value="null">필터를 적용할 연산을 선택하세요.</option> | |
85 | + <option value="1">=</option> | |
86 | + <template v-if="filter.data_ty != 'STRING'"> | |
87 | + <option value="2"><</option> | |
88 | + <option value="3">></option> | |
89 | + <option value="4"><=</option> | |
90 | + <option value="5">>=</option> | |
91 | + </template> | |
92 | + <option value="6">!=</option> | |
93 | + <option value="7" v-if="filter.data_ty == 'STRING'">포함</option> | |
94 | + </select> | |
95 | + </td> | |
96 | + <td> | |
97 | + <input | |
98 | + type="text" | |
99 | + class="full-input" | |
100 | + v-if="filter.data_ty == 'STRING'" | |
101 | + v-model="filter.cmpr_value" | |
102 | + placeholder="필터를 적용할 내용을 입력하세요." | |
103 | + /> | |
104 | + <input | |
105 | + type="date" | |
106 | + class="full-select" | |
107 | + v-else-if="filter.data_ty == 'DATE'" | |
108 | + v-model="filter.cmpr_value" | |
109 | + /> | |
110 | + <input | |
111 | + type="datetime-local" | |
112 | + class="full-select" | |
113 | + v-else-if="filter.data_ty == 'DATETIME'" | |
114 | + v-model="filter.cmpr_value" | |
115 | + /> | |
116 | + <input | |
117 | + type="number" | |
118 | + class="full-select" | |
119 | + v-else | |
120 | + v-model="filter.cmpr_value" | |
121 | + /> | |
122 | + </td> | |
123 | + <td style="text-align: center"> | |
124 | + <button type="button" @click="deleteFilter(filter)"> | |
125 | + <svg-icon | |
126 | + type="mdi" | |
127 | + :width="20" | |
128 | + :height="20" | |
129 | + :path="delPath" | |
130 | + ></svg-icon> | |
131 | + </button> | |
132 | + </td> | |
133 | + </tr> | |
134 | + </tbody> | |
135 | + </table> | |
136 | + </div> | |
137 | +</template> | |
138 | + | |
139 | +<script> | |
140 | +// icon용 svg import | |
141 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
142 | +import { mdiTrashCanOutline } from "@mdi/js"; | |
143 | + | |
144 | +export default { | |
145 | + name: "DataFilter", | |
146 | + | |
147 | + components: { | |
148 | + SvgIcon, | |
149 | + }, | |
150 | + | |
151 | + props: { | |
152 | + jobItem: Object, | |
153 | + currentPage: Number, | |
154 | + frontNode: Object, | |
155 | + }, | |
156 | + | |
157 | + data() { | |
158 | + return { | |
159 | + // icon용 svg path | |
160 | + delPath: mdiTrashCanOutline, | |
161 | + | |
162 | + jobItm: this.jobItem, | |
163 | + }; | |
164 | + }, | |
165 | + | |
166 | + created() { | |
167 | + if (this.$isEmpty(this.frontNode.dataTable.columnDatas)) { | |
168 | + this.$showAlert( | |
169 | + "경고", | |
170 | + "연결된 데이터 읽기 노드에 데이터가 비어있습니다." | |
171 | + ); | |
172 | + return; | |
173 | + } | |
174 | + }, | |
175 | + | |
176 | + mounted() {}, | |
177 | + | |
178 | + computed: {}, | |
179 | + | |
180 | + watch: { | |
181 | + jobItem(value) { | |
182 | + this.jobItm = value; | |
183 | + }, | |
184 | + | |
185 | + "jobItm.itm.filterItems": { | |
186 | + deep: true, | |
187 | + handler(value) { | |
188 | + if (value.length > 0) { | |
189 | + this.$emit("onDataSetChange", true); | |
190 | + } else { | |
191 | + this.$emit("onDataSetChange", false); | |
192 | + } | |
193 | + }, | |
194 | + }, | |
195 | + }, | |
196 | + | |
197 | + methods: { | |
198 | + // 필터추가 | |
199 | + addFiter() { | |
200 | + let filter = Object.assign({}, this.$getDefaultJobGroup().filterItem); | |
201 | + this.jobItm.itm.filterItems.push(filter); | |
202 | + }, | |
203 | + | |
204 | + // 데이터 삽입 | |
205 | + onChange(item) { | |
206 | + outLoop: for (let column of this.frontNode.dataTable.columnDatas) { | |
207 | + if (column.orginlColumnNm == item.coulmn_nm) { | |
208 | + for (let filter of this.jobItm.itm.filterItems) { | |
209 | + if (filter.indx === item.indx) { | |
210 | + filter.data_ty = column.dataTy; | |
211 | + break outLoop; | |
212 | + } | |
213 | + } | |
214 | + } | |
215 | + } | |
216 | + }, | |
217 | + | |
218 | + // 필터 삭제 | |
219 | + deleteFilter(item) { | |
220 | + this.jobItm.itm.filterItems = this.jobItm.itm.filterItems.filter( | |
221 | + (filter) => filter !== item | |
222 | + ); | |
223 | + }, | |
224 | + }, | |
225 | +}; | |
226 | +</script>(No newline at end of file) |
+++ client/views/component/connection/modalContents/DatasetUpdate.vue
... | ... | @@ -0,0 +1,328 @@ |
1 | +<template> | |
2 | + <div class="tab-contents mt10"> | |
3 | + <div class="content-titleZone flex justify-between align-center"> | |
4 | + <p class="box-title">데이터셋 설정</p> | |
5 | + </div> | |
6 | + <table class="form-table"> | |
7 | + <colgroup> | |
8 | + <col style="width: 15%" /> | |
9 | + <col style="width: 85%" /> | |
10 | + </colgroup> | |
11 | + <tbody> | |
12 | + <tr> | |
13 | + <th>데이터셋 설정</th> | |
14 | + <td> | |
15 | + <button class="blue-border-btn small-btn" @click="fnOpenModal"> | |
16 | + 타겟 데이터 조회 | |
17 | + </button> | |
18 | + </td> | |
19 | + </tr> | |
20 | + <tr> | |
21 | + <th>기존 데이터 삭제 여부</th> | |
22 | + <td> | |
23 | + <div class="input-container flex"> | |
24 | + <label class="radio-label mr5"> | |
25 | + <input | |
26 | + type="radio" | |
27 | + name="radio" | |
28 | + :value="false" | |
29 | + class="custom-radiobox" | |
30 | + v-model="jobItm.itm_option_bool" | |
31 | + /> | |
32 | + <span>기존 데이터 삭제</span> | |
33 | + </label> | |
34 | + <label class="radio-label"> | |
35 | + <input | |
36 | + type="radio" | |
37 | + name="radio" | |
38 | + :value="true" | |
39 | + class="custom-radiobox" | |
40 | + v-model="jobItm.itm_option_bool" | |
41 | + /> | |
42 | + <span>기존 데이터 미삭제</span> | |
43 | + </label> | |
44 | + </div> | |
45 | + </td> | |
46 | + </tr> | |
47 | + </tbody> | |
48 | + </table> | |
49 | + <!-- 매핑 --> | |
50 | + <div class="flex mt20"> | |
51 | + <div class="flex50 pl0"> | |
52 | + <div class="content-titleZone flex justify-between align-center"> | |
53 | + <p class="box-title"> | |
54 | + 원본 데이터 컴럼 | |
55 | + <small> | |
56 | + ( 매칭 : {{ matchs.matchCnt }}, 비매칭 : | |
57 | + {{ matchs.unMatchCnt }} | |
58 | + ) | |
59 | + </small> | |
60 | + </p> | |
61 | + </div> | |
62 | + <div class="columncontBox" style="height: 620px; overflow-y: auto"> | |
63 | + <table> | |
64 | + <colgroup> | |
65 | + <col style="width: 50%" /> | |
66 | + </colgroup> | |
67 | + <tbody> | |
68 | + <tr v-for="(column, index) of matchList" :key="index"> | |
69 | + <td> | |
70 | + <p class="col-data"> | |
71 | + {{ column.origin.displyColumnNm }} | |
72 | + </p> | |
73 | + </td> | |
74 | + <td | |
75 | + draggable="true" | |
76 | + @dragstart="fnMoveStart(column)" | |
77 | + @dragover="fnOnDragover" | |
78 | + @drop="fnChangeMatch('drop', column)" | |
79 | + @dblclick="fnChangeMatch('doubleClick', column)" | |
80 | + > | |
81 | + <p class="col-data"> | |
82 | + <span v-if="!$isEmpty(column.target)"> | |
83 | + {{ column.target.displyColumnNm }} | |
84 | + </span> | |
85 | + </p> | |
86 | + </td> | |
87 | + </tr> | |
88 | + </tbody> | |
89 | + </table> | |
90 | + </div> | |
91 | + </div> | |
92 | + <div class="flex50 pr0"> | |
93 | + <div class="content-titleZone flex justify-between align-center"> | |
94 | + <p class="box-title">추가 데이터 컬럼</p> | |
95 | + </div> | |
96 | + <div class="columncontBox" style="height: 620px; overflow-y: auto"> | |
97 | + <ul> | |
98 | + <li | |
99 | + v-for="(column, index) of frontColumns" | |
100 | + :key="index" | |
101 | + :class="{ 'col-data': true, disabled: column.isConnect }" | |
102 | + :draggable="!column.isConnect" | |
103 | + @dragstart="fnOnDragstart(column)" | |
104 | + > | |
105 | + {{ column.displyColumnNm }} | |
106 | + </li> | |
107 | + </ul> | |
108 | + </div> | |
109 | + </div> | |
110 | + </div> | |
111 | + </div> | |
112 | + <!-- 데이터셋 선택 모달 --> | |
113 | + <DatasetListModal | |
114 | + v-if="isOpenModal" | |
115 | + @onSelect="fnSave" | |
116 | + @onClose="fnCloseModal" | |
117 | + /> | |
118 | +</template> | |
119 | + | |
120 | +<script> | |
121 | +import axios from "axios"; | |
122 | +import datasetSelecter from "../itm/datasetSelecter.vue"; | |
123 | +import DatasetListModal from "../../modal/DatasetListModal.vue"; | |
124 | + | |
125 | +export default { | |
126 | + name: "DatasetUpdate", | |
127 | + | |
128 | + components: { | |
129 | + datasetSelecter, | |
130 | + DatasetListModal, | |
131 | + }, | |
132 | + | |
133 | + props: { | |
134 | + jobItem: Object, | |
135 | + currentPage: Number, | |
136 | + frontNode: Object, | |
137 | + }, | |
138 | + | |
139 | + data() { | |
140 | + return { | |
141 | + isOpenModal: false, | |
142 | + | |
143 | + jobItm: this.jobItem, | |
144 | + frontColumns: [], | |
145 | + | |
146 | + matchs: { | |
147 | + matchCnt: 0, | |
148 | + unMatchCnt: 0, | |
149 | + }, | |
150 | + matchList: [], | |
151 | + currentColumn: {}, | |
152 | + }; | |
153 | + }, | |
154 | + | |
155 | + created() { | |
156 | + if (this.$isEmpty(this.frontNode.dataTable.columnDatas)) { | |
157 | + this.$showAlert( | |
158 | + "경고", | |
159 | + "연결된 데이터 읽기 노드에 데이터가 비어있습니다." | |
160 | + ); | |
161 | + return; | |
162 | + } | |
163 | + | |
164 | + // 추가 데이터 컬럼 생성 | |
165 | + this.frontColumns = this.frontNode.dataTable.columnDatas; | |
166 | + for (let column of this.frontColumns) { | |
167 | + column.isConnect = false; | |
168 | + } | |
169 | + }, | |
170 | + | |
171 | + watch: { | |
172 | + jobItem(value) { | |
173 | + this.jobItm = value; | |
174 | + }, | |
175 | + | |
176 | + "jobItm.itmId"(value) { | |
177 | + if (this.$isEmpty(value)) { | |
178 | + this.$emit("onDataSetChange", false); | |
179 | + } else { | |
180 | + this.$emit("onDataSetChange", true); | |
181 | + } | |
182 | + }, | |
183 | + }, | |
184 | + | |
185 | + methods: { | |
186 | + // 모달 열기 | |
187 | + fnOpenModal() { | |
188 | + this.isOpenModal = true; | |
189 | + }, | |
190 | + | |
191 | + // 모달 닫기 | |
192 | + fnCloseModal() { | |
193 | + this.isOpenModal = false; | |
194 | + }, | |
195 | + | |
196 | + fnSave(datasetPostId) { | |
197 | + const vm = this; | |
198 | + axios({ | |
199 | + url: "/dataset/getDataTableInfo.json", | |
200 | + method: "post", | |
201 | + headers: { "Content-Type": "application/json; charset=UTF-8" }, | |
202 | + data: { datapost_id: datasetPostId }, | |
203 | + }) | |
204 | + .then(function (response) { | |
205 | + vm.jobItm.itmId = datasetPostId; | |
206 | + vm.jobItm.dataTable = response.data.resultData.dataTable; | |
207 | + vm.fnSetTargetData(); | |
208 | + // 모달 닫기 | |
209 | + vm.fnCloseModal(); | |
210 | + }) | |
211 | + .catch(function (error) { | |
212 | + vm.$showAlert( | |
213 | + "에러 발생", | |
214 | + "에러가 발생했습니다. 관리자에게 문의해 주세요." | |
215 | + ); | |
216 | + }); | |
217 | + }, | |
218 | + | |
219 | + // 타겟 데이터 조회 후 처리 | |
220 | + fnSetTargetData() { | |
221 | + this.jobItm.dataTable.columnDatas = | |
222 | + this.jobItm.dataTable.columnDatas.filter( | |
223 | + (column) => column.orginlColumnNm !== "ts_row" | |
224 | + ); | |
225 | + this.matchList = []; | |
226 | + for (let column of this.jobItm.dataTable.columnDatas) { | |
227 | + this.matchList.push({ | |
228 | + origin: column, | |
229 | + target: column, | |
230 | + }); | |
231 | + } | |
232 | + }, | |
233 | + | |
234 | + // 매치 리스트 | |
235 | + fnMatchList() { | |
236 | + // 초기화 | |
237 | + this.matchs = { | |
238 | + matchCnt: 0, | |
239 | + unMatchCnt: 0, | |
240 | + }; | |
241 | + for (let columns of this.frontColumns) { | |
242 | + columns.isConnect = false; | |
243 | + } | |
244 | + | |
245 | + // 매치 결과 | |
246 | + for (let item of this.matchList) { | |
247 | + if (this.$isEmpty(item.target)) { | |
248 | + this.matchs.unMatchCnt++; | |
249 | + } else { | |
250 | + this.matchs.matchCnt++; | |
251 | + for (let columns of this.frontColumns) { | |
252 | + if (item.target.orginlColumnNm == columns.orginlColumnNm) { | |
253 | + columns.isConnect = true; | |
254 | + } | |
255 | + } | |
256 | + } | |
257 | + } | |
258 | + | |
259 | + this.fnUpdateMatchList(); // 데이터 업데이트 | |
260 | + }, | |
261 | + // 데이터 업데이트 | |
262 | + fnUpdateMatchList() { | |
263 | + this.jobItm.itemList = []; // 초기화 | |
264 | + for (let match of this.matchList) { | |
265 | + let originColumnNm = match.origin.orginlColumnNm; | |
266 | + let targetColumnNm = match.target.orginlColumnNm; | |
267 | + | |
268 | + this.jobItm.itemList.push({ | |
269 | + groupId: null, | |
270 | + jobIndx: null, | |
271 | + indx: null, | |
272 | + originColumnNm: this.$isEmpty(originColumnNm) ? null : originColumnNm, | |
273 | + targetColumnNm: this.$isEmpty(targetColumnNm) ? null : targetColumnNm, | |
274 | + }); | |
275 | + } | |
276 | + }, | |
277 | + | |
278 | + // 드래그 이벤트 | |
279 | + fnMoveStart(column) { | |
280 | + this.currentColumn = column; | |
281 | + }, | |
282 | + fnOnDragstart(column) { | |
283 | + this.currentColumn = column; | |
284 | + }, | |
285 | + fnOnDragover(event) { | |
286 | + event.preventDefault(); | |
287 | + }, | |
288 | + fnChangeMatch(type, current) { | |
289 | + let columnToUpdate = this.matchList.find((column) => column === current); | |
290 | + if (columnToUpdate) { | |
291 | + if (type == "drop") { | |
292 | + columnToUpdate.target = this.currentColumn; | |
293 | + } else if (type == "doubleClick") { | |
294 | + columnToUpdate.target = {}; | |
295 | + } | |
296 | + } | |
297 | + | |
298 | + this.fnMatchList(); | |
299 | + this.fnUpdateMatchList(); // 업데이트 | |
300 | + }, | |
301 | + }, | |
302 | +}; | |
303 | +</script> | |
304 | + | |
305 | +<style scoped> | |
306 | +.columncontBox { | |
307 | + background: #f5f5f5; | |
308 | + border-radius: 10px; | |
309 | +} | |
310 | +.columncontBox tr { | |
311 | + padding: 8px; | |
312 | +} | |
313 | +.columncontBox ul .col-data { | |
314 | + margin: 8px; | |
315 | +} | |
316 | +.columncontBox .col-data { | |
317 | + border: 1px solid #dbdbdb; | |
318 | + padding: 5px 10px; | |
319 | + background-color: var(--color-white); | |
320 | + border-radius: 10px; | |
321 | + cursor: move; | |
322 | + font-size: 1.3rem; | |
323 | + height: 34px; | |
324 | +} | |
325 | +.columncontBox .col-data.disabled { | |
326 | + background-color: #f5f5f5; | |
327 | +} | |
328 | +</style>(No newline at end of file) |
+++ client/views/component/connection/modalContents/DbRead.vue
... | ... | @@ -0,0 +1,674 @@ |
1 | +<template> | |
2 | + <div v-if="currentPage == 1"> | |
3 | + <div class="table-zone"> | |
4 | + <table class="form-table"> | |
5 | + <colgroup> | |
6 | + <col style="width: 25%" /> | |
7 | + <col style="width: 75%" /> | |
8 | + </colgroup> | |
9 | + <tbody v-if="jobItm.itm != null"> | |
10 | + <tr> | |
11 | + <th>연계정보 타입</th> | |
12 | + <td> | |
13 | + <div class="input-container flex"> | |
14 | + <label class="radio-label"> | |
15 | + <input | |
16 | + type="radio" | |
17 | + name="radio" | |
18 | + :value="false" | |
19 | + class="custom-radiobox" | |
20 | + @change="successAt = false" | |
21 | + v-model="jobItm.itm_option_bool" | |
22 | + /> | |
23 | + <span>직접입력</span> | |
24 | + </label> | |
25 | + <label class="radio-label"> | |
26 | + <input | |
27 | + type="radio" | |
28 | + name="radio" | |
29 | + :value="true" | |
30 | + class="custom-radiobox" | |
31 | + @change="successAt = false" | |
32 | + v-model="jobItm.itm_option_bool" | |
33 | + /> | |
34 | + <span>불러오기</span> | |
35 | + </label> | |
36 | + </div> | |
37 | + </td> | |
38 | + </tr> | |
39 | + <tr v-show="jobItm.itm_option_bool"> | |
40 | + <th>연계정보</th> | |
41 | + <td> | |
42 | + <input | |
43 | + type="text" | |
44 | + class="half-input" | |
45 | + disabled | |
46 | + :value=" | |
47 | + linkConnectionDB.conectNm + | |
48 | + '(' + | |
49 | + linkConnectionDB.conectIp + | |
50 | + ')' | |
51 | + " | |
52 | + v-if="linkConnectionDB.conectNm != null" | |
53 | + /> | |
54 | + <input type="text" class="half-input" disabled v-else /> | |
55 | + <button | |
56 | + class="blue-border-btn small-btn" | |
57 | + @click="dbConSearchOpen(true)" | |
58 | + > | |
59 | + 검색 | |
60 | + </button> | |
61 | + </td> | |
62 | + </tr> | |
63 | + <tr> | |
64 | + <th>DMBS</th> | |
65 | + <td> | |
66 | + <select | |
67 | + id="databaseType" | |
68 | + @change="successAt = false" | |
69 | + class="square-select half-input" | |
70 | + v-model="inputConnectionDB.databaseType" | |
71 | + :disabled="jobItm.itm_option_bool" | |
72 | + > | |
73 | + <option | |
74 | + v-for="(itm, index) in databaseTypeList" | |
75 | + :key="index" | |
76 | + :value="itm.key" | |
77 | + > | |
78 | + {{ itm.value }} | |
79 | + </option> | |
80 | + </select> | |
81 | + </td> | |
82 | + </tr> | |
83 | + <tr> | |
84 | + <th>IP</th> | |
85 | + <td> | |
86 | + <input | |
87 | + id="conectIp" | |
88 | + type="text" | |
89 | + @input="successAt = false" | |
90 | + class="half-input" | |
91 | + v-model="inputConnectionDB.conectIp" | |
92 | + placeholder="127.0.0.1" | |
93 | + :disabled="jobItm.itm_option_bool" | |
94 | + /> | |
95 | + </td> | |
96 | + </tr> | |
97 | + <tr> | |
98 | + <th>PORT</th> | |
99 | + <td> | |
100 | + <input | |
101 | + id="conectPort" | |
102 | + type="text" | |
103 | + @input="successAt = false" | |
104 | + class="half-input" | |
105 | + v-model="inputConnectionDB.conectPort" | |
106 | + :disabled="jobItm.itm_option_bool" | |
107 | + /> | |
108 | + </td> | |
109 | + </tr> | |
110 | + <tr> | |
111 | + <th>DB 명</th> | |
112 | + <td> | |
113 | + <input | |
114 | + id="databaseNm" | |
115 | + type="text" | |
116 | + @input="successAt = false" | |
117 | + class="half-input" | |
118 | + v-model="inputConnectionDB.databaseNm" | |
119 | + placeholder="데이터베이스명 OR 스키마명" | |
120 | + :disabled="jobItm.itm_option_bool" | |
121 | + /> | |
122 | + </td> | |
123 | + </tr> | |
124 | + <tr> | |
125 | + <th>접속ID</th> | |
126 | + <td> | |
127 | + <input | |
128 | + type="text" | |
129 | + class="half-input" | |
130 | + @input="successAt = false" | |
131 | + v-model="inputConnectionDB.userId" | |
132 | + placeholder="접속 ID" | |
133 | + :disabled="jobItm.itm_option_bool" | |
134 | + /> | |
135 | + </td> | |
136 | + </tr> | |
137 | + <tr> | |
138 | + <th>접속PW</th> | |
139 | + <td> | |
140 | + <input | |
141 | + type="password" | |
142 | + class="half-input" | |
143 | + @input="successAt = false" | |
144 | + v-model="inputConnectionDB.userPassword" | |
145 | + placeholder="접속 PW" | |
146 | + autocomplete="new-password" | |
147 | + :disabled="jobItm.itm_option_bool" | |
148 | + /> | |
149 | + </td> | |
150 | + </tr> | |
151 | + </tbody> | |
152 | + </table> | |
153 | + </div> | |
154 | + <div class="content-titleZone flex justy justify-between align-center mt20"> | |
155 | + <p class="box-title">데이터베이스 연결 결과</p> | |
156 | + <button | |
157 | + class="blue-border-btn small-btn" | |
158 | + @click="dataBaseConnectionCheck" | |
159 | + > | |
160 | + 접속확인 | |
161 | + </button> | |
162 | + </div> | |
163 | + <div class="table-zone"> | |
164 | + <table class="list-table"> | |
165 | + <colgroup> | |
166 | + <col width="10%" /> | |
167 | + <col width="10%" /> | |
168 | + <col width="80%" /> | |
169 | + </colgroup> | |
170 | + <thead> | |
171 | + <tr> | |
172 | + <th>No</th> | |
173 | + <th>접속시간</th> | |
174 | + <th>접속결과</th> | |
175 | + </tr> | |
176 | + </thead> | |
177 | + <tbody> | |
178 | + <tr v-for="(itm, indx) in resultMessage" :key="indx"> | |
179 | + <td>{{ indx + 1 }}</td> | |
180 | + <td>{{ itm.time }}</td> | |
181 | + <td>{{ itm.message }}</td> | |
182 | + </tr> | |
183 | + </tbody> | |
184 | + </table> | |
185 | + </div> | |
186 | + </div> | |
187 | + <div v-if="currentPage == 2"> | |
188 | + <Splitter style="height: 100%; border: 0px solid #e5e7eb"> | |
189 | + <SplitterPanel | |
190 | + class="flex align-items-center justify-content-center" | |
191 | + :size="20" | |
192 | + :minSize="10" | |
193 | + > | |
194 | + <div class="content-box"> | |
195 | + <div class="file-zone"> | |
196 | + <div class="content-titleZone" style="height: 60px"> | |
197 | + <p class="box-title">table 정보</p> | |
198 | + </div> | |
199 | + <div class="content-zone2"> | |
200 | + <ul class="content-list" v-if="tableList.length > 0"> | |
201 | + <li | |
202 | + class="cursor" | |
203 | + v-for="(item, indx) in tableList" | |
204 | + :key="indx" | |
205 | + > | |
206 | + <a | |
207 | + @click="getTableData(item)" | |
208 | + :class="{ | |
209 | + 'file-list': true, | |
210 | + selected: selectTable === item, | |
211 | + }" | |
212 | + > | |
213 | + <div class="flex align-center"> | |
214 | + <p> | |
215 | + {{ | |
216 | + item.tableNmKr != null && item.tableNmKr != "" | |
217 | + ? item.tableNmKr | |
218 | + : item.tableNm | |
219 | + }} | |
220 | + </p> | |
221 | + </div> | |
222 | + </a> | |
223 | + </li> | |
224 | + </ul> | |
225 | + </div> | |
226 | + </div> | |
227 | + </div> | |
228 | + </SplitterPanel> | |
229 | + <SplitterPanel | |
230 | + class="flex align-items-center justify-content-center" | |
231 | + :size="80" | |
232 | + > | |
233 | + <div class="content-box"> | |
234 | + <div class="content-titleZone" style="height: 60px"> | |
235 | + <div class="flex justify-between aling-center"> | |
236 | + <p class="box-title">쿼리 작업</p> | |
237 | + <div> | |
238 | + <button class="icon-btn" @click="executeQuery" title="실행"> | |
239 | + <svg-icon | |
240 | + type="mdi" | |
241 | + :path="playPath" | |
242 | + :color="'#fbbe28'" | |
243 | + ></svg-icon> | |
244 | + </button> | |
245 | + </div> | |
246 | + </div> | |
247 | + </div> | |
248 | + <div class="content-zone"> | |
249 | + <Splitter style="height: 100%" layout="vertical"> | |
250 | + <SplitterPanel | |
251 | + class="flex align-items-center justify-content-center" | |
252 | + :size="50" | |
253 | + :minSize="20" | |
254 | + > | |
255 | + <textarea | |
256 | + style=" | |
257 | + resize: none; | |
258 | + max-width: 100%; | |
259 | + max-height: 100%; | |
260 | + padding: 10px; | |
261 | + " | |
262 | + v-model="jobItm.itm.query" | |
263 | + ></textarea> | |
264 | + </SplitterPanel> | |
265 | + <SplitterPanel | |
266 | + class="align-items-center justify-content-center" | |
267 | + :size="50" | |
268 | + > | |
269 | + <ul class="tab-nav flex justify-start"> | |
270 | + <li @click="showTab('tab1')"> | |
271 | + <a | |
272 | + href="#tab01" | |
273 | + :class="{ activeTab: activeTab === 'tab1' }" | |
274 | + >작업결과</a | |
275 | + > | |
276 | + </li> | |
277 | + <li @click="showTab('tab2')"> | |
278 | + <a | |
279 | + href="#tab02" | |
280 | + :class="{ activeTab: activeTab === 'tab2' }" | |
281 | + >작업log</a | |
282 | + > | |
283 | + </li> | |
284 | + </ul> | |
285 | + <div | |
286 | + v-show="activeTab === 'tab1'" | |
287 | + style=" | |
288 | + height: calc(100% - 60px); | |
289 | + overflow: auto; | |
290 | + padding: 10px; | |
291 | + " | |
292 | + > | |
293 | + <div | |
294 | + class="count-zone" | |
295 | + v-if="jobItm.dataTable.columnDatas.length > 0" | |
296 | + > | |
297 | + <p> | |
298 | + 총 <span>{{ jobItm.dataTable.totalRows }}</span | |
299 | + >건 중 <span>{{ jobItm.dataTable.rowData.length }}</span | |
300 | + >건 조회 | |
301 | + </p> | |
302 | + </div> | |
303 | + <div class="table-zone"> | |
304 | + <table class="list-table"> | |
305 | + <!-- col 꼭 너비 기재해야함! 그래야 100%로 차지함 --> | |
306 | + <thead> | |
307 | + <tr v-if="jobItm.dataTable.columnDatas.length > 0"> | |
308 | + <th | |
309 | + v-for="(itm, indx) in jobItm.dataTable.columnDatas" | |
310 | + :key="indx" | |
311 | + style="min-width: 150px !important" | |
312 | + > | |
313 | + {{ itm.columnNm }} | |
314 | + <label class="check-label"> | |
315 | + <input | |
316 | + type="checkbox" | |
317 | + class="custom-checkbox" | |
318 | + v-model="itm.pkAt" | |
319 | + /> | |
320 | + </label> | |
321 | + </th> | |
322 | + </tr> | |
323 | + </thead> | |
324 | + <tbody v-if="jobItm.dataTable.rowData.length > 0"> | |
325 | + <tr | |
326 | + v-for="(row, rows) in jobItm.dataTable.rowData" | |
327 | + :key="rows" | |
328 | + > | |
329 | + <td | |
330 | + v-for="(itm, indx) in row" | |
331 | + :key="indx" | |
332 | + style=" | |
333 | + overflow: hidden; | |
334 | + white-space: nowrap; | |
335 | + text-overflow: ellipsis; | |
336 | + " | |
337 | + > | |
338 | + {{ itm }} | |
339 | + </td> | |
340 | + </tr> | |
341 | + </tbody> | |
342 | + </table> | |
343 | + </div> | |
344 | + </div> | |
345 | + <div | |
346 | + v-show="activeTab === 'tab2'" | |
347 | + style=" | |
348 | + height: calc(100% - 60px); | |
349 | + overflow: auto; | |
350 | + padding: 10px; | |
351 | + " | |
352 | + > | |
353 | + <div class="table-zone"> | |
354 | + <table class="list-table"> | |
355 | + <colgroup> | |
356 | + <col width="10%" /> | |
357 | + <col width="10%" /> | |
358 | + <col width="" /> | |
359 | + <col width="10%" /> | |
360 | + </colgroup> | |
361 | + <thead> | |
362 | + <tr> | |
363 | + <th>No</th> | |
364 | + <th>접속시간</th> | |
365 | + <th>접속결과</th> | |
366 | + <th>접속내용</th> | |
367 | + </tr> | |
368 | + </thead> | |
369 | + <tbody> | |
370 | + <tr v-for="(itm, indx) in executeMessage" :key="indx"> | |
371 | + <td>{{ indx + 1 }}</td> | |
372 | + <td>{{ itm.time }}</td> | |
373 | + <td>{{ itm.message }}</td> | |
374 | + <td>{{ itm.result }}</td> | |
375 | + </tr> | |
376 | + </tbody> | |
377 | + </table> | |
378 | + </div> | |
379 | + </div> | |
380 | + </SplitterPanel> | |
381 | + </Splitter> | |
382 | + </div> | |
383 | + </div> | |
384 | + </SplitterPanel> | |
385 | + </Splitter> | |
386 | + </div> | |
387 | + | |
388 | + <DBConSearch | |
389 | + :openPopup="openSearchModal" | |
390 | + @modalclose="dbConSearchOpen" | |
391 | + @selectItm="selectDbcon" | |
392 | + /> | |
393 | +</template> | |
394 | + | |
395 | +<script> | |
396 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
397 | +import { mdiPlay } from "@mdi/js"; | |
398 | + | |
399 | +import axios from "axios"; | |
400 | +import DBConSearch from "../../dataComponent/DbConnectionSearchModal.vue"; | |
401 | + | |
402 | +export default { | |
403 | + name: "DbRead", | |
404 | + | |
405 | + props: { | |
406 | + jobItem: { | |
407 | + type: Object, | |
408 | + }, | |
409 | + currentPage: { | |
410 | + type: Number, | |
411 | + }, | |
412 | + }, | |
413 | + | |
414 | + data() { | |
415 | + return { | |
416 | + // 연계정보 불러오기 | |
417 | + openSearchModal: false, | |
418 | + // 커넥션 DB 오브젝트 | |
419 | + jobItm: this.jobItem, | |
420 | + connectionDB: {}, | |
421 | + linkConnectionDB: {}, | |
422 | + inputConnectionDB: {}, | |
423 | + resultMessage: [], | |
424 | + executeMessage: [], | |
425 | + // 연계 성공여부 | |
426 | + successAt: false, | |
427 | + databaseTypeList: [], | |
428 | + tableList: [], | |
429 | + selectTable: {}, | |
430 | + activeTab: "tab1", | |
431 | + playPath: mdiPlay, | |
432 | + }; | |
433 | + }, | |
434 | + | |
435 | + methods: { | |
436 | + // 연계가능 여부 테스트 | |
437 | + dataBaseConnectionCheck: function () { | |
438 | + var vm = this; | |
439 | + if (this.jobItm.itm_option_bool == true) { | |
440 | + this.jobItm.itm = this.linkConnectionDB; | |
441 | + } else { | |
442 | + this.jobItm.itm = this.inputConnectionDB; | |
443 | + } | |
444 | + this.jobItm.itm.loadAt = this.jobItm.itm_option_bool; | |
445 | + delete this.jobItm.itm.type; | |
446 | + | |
447 | + axios({ | |
448 | + url: "/data/dataBaseConnectionCheck.json", | |
449 | + method: "post", | |
450 | + headers: { | |
451 | + "Content-Type": "application/json; charset=UTF-8", | |
452 | + }, | |
453 | + data: vm.jobItm.itm, | |
454 | + }) | |
455 | + .then(function (response) { | |
456 | + if (response.data.checkMessage.success == true) { | |
457 | + vm.successAt = true; | |
458 | + } else { | |
459 | + vm.successAt = false; | |
460 | + } | |
461 | + | |
462 | + vm.$emit("onDfltSetChange", response.data.checkMessage.success); | |
463 | + | |
464 | + vm.$showAlert("결과내용", response.data.checkMessage.message); | |
465 | + vm.resultMessage.push({ | |
466 | + time: vm.$getFullTime(), | |
467 | + message: response.data.checkMessage.message, | |
468 | + result: response.data.checkMessage.success | |
469 | + ? "접속성공" | |
470 | + : "접속실패", | |
471 | + }); | |
472 | + }) | |
473 | + .catch(function (error) {}); | |
474 | + }, | |
475 | + | |
476 | + // db커넥션 선택창 호출 | |
477 | + dbConSearchOpen: function (val) { | |
478 | + this.openSearchModal = val; | |
479 | + }, | |
480 | + | |
481 | + // 연계정보 받기 | |
482 | + selectDbcon: function (dbcon) { | |
483 | + this.linkConnectionDB = dbcon; | |
484 | + }, | |
485 | + | |
486 | + // 탭 변경 | |
487 | + showTab: function (tabName) { | |
488 | + this.activeTab = tabName; | |
489 | + }, | |
490 | + | |
491 | + //DB 접속 - 테이블 목록 조회 | |
492 | + getDbConnectionTableList: function () { | |
493 | + let vm = this; | |
494 | + axios({ | |
495 | + url: "/data/selectTableList.json", | |
496 | + method: "post", | |
497 | + headers: { | |
498 | + "Content-Type": "application/json; charset=UTF-8", | |
499 | + }, | |
500 | + data: vm.jobItm.itm, | |
501 | + }) | |
502 | + .then(function (response) { | |
503 | + if (response.data.checkMessage.success == true) { | |
504 | + vm.tableList = response.data.resultData.tableList; | |
505 | + } | |
506 | + }) | |
507 | + .catch(function (error) {}); | |
508 | + }, | |
509 | + | |
510 | + // 테이블 클릭 | |
511 | + clickTable: function (itm) { | |
512 | + this.selectTable = itm; | |
513 | + }, | |
514 | + | |
515 | + // 선택한 테이블 정보 가져오기 | |
516 | + getTableData: function (itm) { | |
517 | + let vm = this; | |
518 | + itm.perPage = this.jobItm.dataTable.perPage; | |
519 | + | |
520 | + // 통신시 데이터 형문제로 삭제 | |
521 | + this.jobItm.itm.creatDt = null; | |
522 | + let requestData = { | |
523 | + dataset: itm, | |
524 | + connectionDB: this.jobItm.itm, | |
525 | + }; | |
526 | + | |
527 | + axios({ | |
528 | + url: "/data/getTableData.json", | |
529 | + method: "post", | |
530 | + headers: { | |
531 | + "Content-Type": "application/json; charset=UTF-8", | |
532 | + }, | |
533 | + data: requestData, | |
534 | + }) | |
535 | + .then(function (response) { | |
536 | + if (response.data.checkMessage.success) { | |
537 | + vm.jobItm.dataTable = response.data.resultData.dataTable; | |
538 | + vm.jobItm.itm.query = vm.jobItm.dataTable.query; | |
539 | + | |
540 | + vm.columnDatas = response.data.resultData.dataTable.columnDatas; | |
541 | + } | |
542 | + | |
543 | + vm.$emit("onDataSetChange", response.data.checkMessage.success); | |
544 | + | |
545 | + vm.executeMessage.push({ | |
546 | + time: vm.$getFullTime(), | |
547 | + message: response.data.resultData.dataTable.checkMessage.message, | |
548 | + result: response.data.resultData.dataTable.checkMessage.success | |
549 | + ? "성공" | |
550 | + : "실패", | |
551 | + }); | |
552 | + }) | |
553 | + .catch(function (error) { | |
554 | + vm.$emit("onDataSetChange", false); | |
555 | + }); | |
556 | + }, | |
557 | + | |
558 | + // 쿼리 실행 | |
559 | + executeQuery: function () { | |
560 | + let vm = this; | |
561 | + // 통신시 데이터 형문제로 삭제 | |
562 | + this.jobItm.itm.creatDt = null; | |
563 | + this.jobItm.dataTable.query = this.jobItm.itm.query; | |
564 | + let requestData = { | |
565 | + dataTable: this.jobItm.dataTable, | |
566 | + connectionDB: this.jobItm.itm, | |
567 | + }; | |
568 | + | |
569 | + axios({ | |
570 | + url: "/data/getTableDataByQuery.json", | |
571 | + method: "post", | |
572 | + headers: { | |
573 | + "Content-Type": "application/json; charset=UTF-8", | |
574 | + }, | |
575 | + data: requestData, | |
576 | + }) | |
577 | + .then(function (response) { | |
578 | + if (response.data.checkMessage.success) { | |
579 | + vm.jobItm.dataTable = response.data.resultData.dataTable; | |
580 | + vm.jobItm.itm.query = vm.jobItm.dataTable.query; | |
581 | + } | |
582 | + | |
583 | + vm.$emit("onDataSetChange", response.data.checkMessage.success); | |
584 | + | |
585 | + vm.executeMessage.push({ | |
586 | + time: vm.$getFullTime(), | |
587 | + message: response.data.resultData.dataTable.checkMessage.message, | |
588 | + result: response.data.resultData.dataTable.checkMessage.success | |
589 | + ? "성공" | |
590 | + : "실패", | |
591 | + }); | |
592 | + }) | |
593 | + .catch(function (error) { | |
594 | + vm.$emit("onDataSetChange", false); | |
595 | + }); | |
596 | + }, | |
597 | + | |
598 | + // 초기화 function | |
599 | + init: async function () { | |
600 | + this.databaseTypeList = await this.$getDataBaseTypeList(); | |
601 | + | |
602 | + if (this.jobItm == null) { | |
603 | + this.jobItm = Object.assign({}, this.$getDefaultJobGroup().node); | |
604 | + this.linkConnectionDB = Object.assign( | |
605 | + {}, | |
606 | + this.$getDefaultJobGroup().connectionDb | |
607 | + ); | |
608 | + this.inputConnectionDB = Object.assign( | |
609 | + {}, | |
610 | + this.$getDefaultJobGroup().connectionDb | |
611 | + ); | |
612 | + this.jobItm.itm = Object.assign( | |
613 | + {}, | |
614 | + this.$getDefaultJobGroup().connectionDb | |
615 | + ); | |
616 | + } else { | |
617 | + if (this.jobItm.itm_option_bool) { | |
618 | + this.linkConnectionDB = this.jobItm.itm; | |
619 | + this.connectionDB = this.linkConnectionDB; | |
620 | + } else { | |
621 | + this.inputConnectionDB = this.jobItm.itm; | |
622 | + this.connectionDB = this.inputConnectionDB; | |
623 | + } | |
624 | + } | |
625 | + | |
626 | + if (this.inputConnectionDB.databaseType == null) { | |
627 | + this.inputConnectionDB.databaseType = this.databaseTypeList.MARIADB.key; | |
628 | + } | |
629 | + }, | |
630 | + }, | |
631 | + | |
632 | + watch: { | |
633 | + jobItem: function (v) { | |
634 | + this.jobItm = v; | |
635 | + if (v.itm_option_bool) { | |
636 | + this.linkConnectionDB = this.jobItm.itm; | |
637 | + } else { | |
638 | + this.inputConnectionDB = this.jobItm.itm; | |
639 | + } | |
640 | + }, | |
641 | + | |
642 | + currentPage(value) { | |
643 | + if (value == 2) { | |
644 | + this.getDbConnectionTableList(); | |
645 | + } | |
646 | + }, | |
647 | + }, | |
648 | + | |
649 | + components: { | |
650 | + DBConSearch: DBConSearch, | |
651 | + SvgIcon: SvgIcon, | |
652 | + }, | |
653 | + | |
654 | + mounted() { | |
655 | + this.init(); | |
656 | + }, | |
657 | + | |
658 | + create() { | |
659 | + if (this.jobItm == null) { | |
660 | + this.jobItm = Object.assign({}, this.defaultJobItm); | |
661 | + this.jobItm.itm = this.$getDefaultJobGroup().connectionDb; | |
662 | + } | |
663 | + }, | |
664 | +}; | |
665 | +</script> | |
666 | + | |
667 | +<style> | |
668 | +.content-zone2 { | |
669 | + height: calc(100% - 57px); | |
670 | + max-height: calc(100% - 57px); | |
671 | + overflow-y: auto; | |
672 | + overflow-x: hidden; | |
673 | +} | |
674 | +</style>(No newline at end of file) |
+++ client/views/component/modal/DatasetListModal.vue
... | ... | @@ -0,0 +1,87 @@ |
1 | +<template> | |
2 | + <div class="modal-wrapper"> | |
3 | + <div class="modal-container large-modal"> | |
4 | + <div class="modal-title flex justify-between align-center"> | |
5 | + <h2>데이터 셋 선택</h2> | |
6 | + <button class="close-btn" @click="$emit('onClose')"> | |
7 | + <svg-icon | |
8 | + type="mdi" | |
9 | + :width="20" | |
10 | + :height="20" | |
11 | + :path="closePath" | |
12 | + ></svg-icon> | |
13 | + </button> | |
14 | + </div> | |
15 | + <div class="modal-content-monthly"> | |
16 | + <table class="form-table"> | |
17 | + <colgroup> | |
18 | + <col style="width: 20%" /> | |
19 | + <col style="width: 80%" /> | |
20 | + </colgroup> | |
21 | + <tbody> | |
22 | + <tr> | |
23 | + <th>선택한 데이터셋 아이디</th> | |
24 | + <td> | |
25 | + <input | |
26 | + type="text" | |
27 | + class="full-input" | |
28 | + placeholder="데이터셋을 선택해 주세요." | |
29 | + readonly | |
30 | + v-model="itmId" | |
31 | + /> | |
32 | + </td> | |
33 | + </tr> | |
34 | + </tbody> | |
35 | + </table> | |
36 | + <div style="height: clac(100% - 50px)"> | |
37 | + <DataPostManagerMain type="modal" @onSelected="fnChangeTarget" /> | |
38 | + </div> | |
39 | + </div> | |
40 | + <div class="modal-end flex justify-end"> | |
41 | + <button class="blue-btn small-btn" @click="$emit('onSelect', itmId)"> | |
42 | + 선택 | |
43 | + </button> | |
44 | + <button class="blue-border-btn small-btn" @click="$emit('onClose')"> | |
45 | + 취소 | |
46 | + </button> | |
47 | + </div> | |
48 | + </div> | |
49 | + </div> | |
50 | +</template> | |
51 | + | |
52 | +<script> | |
53 | +// icon용 svg import | |
54 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
55 | +import { mdiClose } from "@mdi/js"; | |
56 | + | |
57 | +import DataPostManagerMain from "../../pages/data/datapost/DataPostManagerMain.vue"; | |
58 | + | |
59 | +export default { | |
60 | + name: "DatasetUpdate", | |
61 | + | |
62 | + components: { | |
63 | + SvgIcon, | |
64 | + DataPostManagerMain, | |
65 | + }, | |
66 | + | |
67 | + data() { | |
68 | + return { | |
69 | + // icon용 svg path | |
70 | + closePath: mdiClose, | |
71 | + | |
72 | + itmId: null, | |
73 | + }; | |
74 | + }, | |
75 | + | |
76 | + created() {}, | |
77 | + | |
78 | + mounted() {}, | |
79 | + | |
80 | + methods: { | |
81 | + // 데이터셋 선택 | |
82 | + fnChangeTarget(id) { | |
83 | + this.itmId = id; | |
84 | + }, | |
85 | + }, | |
86 | +}; | |
87 | +</script>(No newline at end of file) |
+++ client/views/component/modal/NodeSetupModal.vue
... | ... | @@ -0,0 +1,199 @@ |
1 | +<template> | |
2 | + <div class="modal-wrapper"> | |
3 | + <div class="modal-container large-modal"> | |
4 | + <!-- 모달 HEAD (제목) --> | |
5 | + <div class="modal-title flex justify-between align-center"> | |
6 | + <h2>{{ currentNode.label }}</h2> | |
7 | + <button class="close-btn" @click="$emit('onClose')"> | |
8 | + <svg-icon | |
9 | + type="mdi" | |
10 | + :width="20" | |
11 | + :height="20" | |
12 | + :path="closePath" | |
13 | + ></svg-icon> | |
14 | + </button> | |
15 | + </div> | |
16 | + <!-- 모달 BODY (본문) --> | |
17 | + <div class="modal-content-monthly"> | |
18 | + <!-- 리드 노드 --> | |
19 | + <DbRead | |
20 | + v-if="jobItm.type == 'DB_READ'" | |
21 | + :jobItem="jobItm" | |
22 | + :currentPage="currentPage" | |
23 | + @onDfltSetChange="fnDfltSetChange" | |
24 | + @onDataSetChange="fnDataSetChange" | |
25 | + /> | |
26 | + <ApiRead | |
27 | + v-if="jobItm.type == 'API_READ'" | |
28 | + :jobItem="jobItm" | |
29 | + :currentPage="currentPage" | |
30 | + @onDfltSetChange="fnDfltSetChange" | |
31 | + @onDataSetChange="fnDataSetChange" | |
32 | + /> | |
33 | + <!-- 필터 노드 --> | |
34 | + <DataFilter | |
35 | + v-if="jobItm.type == 'DATA_FILTER'" | |
36 | + :jobItem="jobItm" | |
37 | + :currentPage="currentPage" | |
38 | + :frontNode="frontNode" | |
39 | + @onDataSetChange="fnDataSetChange" | |
40 | + /> | |
41 | + <!-- 프로세스 노드 --> | |
42 | + <DatasetUpdate | |
43 | + v-if="jobItm.type == 'DATASET_UPDATE'" | |
44 | + :jobItem="jobItm" | |
45 | + :currentPage="currentPage" | |
46 | + :frontNode="frontNode" | |
47 | + @onDataSetChange="fnDataSetChange" | |
48 | + /> | |
49 | + </div> | |
50 | + <!-- 모달 FOOT (버튼) --> | |
51 | + <div class="modal-end flex justify-end"> | |
52 | + <button | |
53 | + class="blue-border-btn small-btn" | |
54 | + v-if="currentPage == 2" | |
55 | + @click="onChange(1)" | |
56 | + > | |
57 | + 이전 | |
58 | + </button> | |
59 | + <button | |
60 | + class="blue-border-btn small-btn" | |
61 | + v-if="currentPage == 1" | |
62 | + @click="onChange(2)" | |
63 | + > | |
64 | + 다음 | |
65 | + </button> | |
66 | + <button class="blue-btn small-btn" v-if="isDataSet" @click="fnSave"> | |
67 | + 등록 | |
68 | + </button> | |
69 | + <button class="blue-border-btn small-btn" @click="$emit('onClose')"> | |
70 | + 취소 | |
71 | + </button> | |
72 | + </div> | |
73 | + </div> | |
74 | + </div> | |
75 | +</template> | |
76 | +<script> | |
77 | +import _ from "lodash"; | |
78 | + | |
79 | +// icon용 svg import | |
80 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
81 | +import { mdiMagnify, mdiClose } from "@mdi/js"; | |
82 | + | |
83 | +// 컴포넌트 | |
84 | +import DbRead from "../connection/modalContents/DbRead.vue"; | |
85 | +import ApiRead from "../connection/modalContents/ApiRead.vue"; | |
86 | +import DataFilter from "../connection/modalContents/DataFilter.vue"; | |
87 | +import DatasetUpdate from "../connection/modalContents/DatasetUpdate.vue"; | |
88 | + | |
89 | +export default { | |
90 | + components: { SvgIcon, DbRead, ApiRead, DataFilter, DatasetUpdate }, | |
91 | + | |
92 | + props: { | |
93 | + frontNodes: Array, | |
94 | + currentNode: Object, | |
95 | + }, | |
96 | + | |
97 | + data() { | |
98 | + return { | |
99 | + // icon용 svg path | |
100 | + searchPath: mdiMagnify, | |
101 | + closePath: mdiClose, | |
102 | + | |
103 | + isDfltSet: false, // 커넥션 정보 설정 여부 | |
104 | + isDataSet: false, // 노드 데이터 설정 여부 | |
105 | + currentPage: 1, | |
106 | + jobItm: Object.assign({}, this.$getDefaultJobGroup().jobItem), | |
107 | + frontNode: Object.assign({}, this.$getDefaultJobGroup().jobItem), | |
108 | + }; | |
109 | + }, | |
110 | + | |
111 | + created() { | |
112 | + if (!this.$isEmpty(this.currentNode)) { | |
113 | + this.jobItm = _.cloneDeep(this.currentNode.jobItm); | |
114 | + } | |
115 | + if (!this.$isEmpty(this.frontNodes)) { | |
116 | + this.frontNode = _.cloneDeep(this.frontNodes[0].jobItm); | |
117 | + } | |
118 | + | |
119 | + if (this.currentNode.type == "read") { | |
120 | + this.currentPage = 1; | |
121 | + } else { | |
122 | + this.currentPage = 0; | |
123 | + } | |
124 | + }, | |
125 | + | |
126 | + mounted() {}, | |
127 | + | |
128 | + methods: { | |
129 | + onChange(value) { | |
130 | + if (this.currentPage == 1 && !this.isDfltSet) { | |
131 | + this.$showAlert( | |
132 | + "경고", | |
133 | + "오류가 발생했습니다. 입력한 정보를 확인해 주세요." | |
134 | + ); | |
135 | + } else { | |
136 | + this.currentPage = value; | |
137 | + } | |
138 | + }, | |
139 | + | |
140 | + // 저장 | |
141 | + fnSave() { | |
142 | + if (this.jobItm.type == "DATA_FILTER") { | |
143 | + let message = this.filterValidation(); | |
144 | + if (!this.$isEmpty(message)) { | |
145 | + this.$showAlert("경고", message); | |
146 | + return; | |
147 | + } | |
148 | + } else if (this.jobItm.type == "DATASET_UPDATE") { | |
149 | + let cnt = 0; | |
150 | + let list = this.jobItm.itemList; | |
151 | + for (let item of list) { | |
152 | + if (!this.$isEmpty(item.targetColumnNm)) { | |
153 | + cnt++; | |
154 | + } | |
155 | + } | |
156 | + if (cnt < 1) { | |
157 | + this.$showAlert( | |
158 | + "경고", | |
159 | + "데이터셋 매핑을 최소 1개 이상 설정해 주세요." | |
160 | + ); | |
161 | + return; | |
162 | + } | |
163 | + } | |
164 | + | |
165 | + this.$emit("onSave", this.jobItm); | |
166 | + }, | |
167 | + | |
168 | + filterValidation() { | |
169 | + if (this.$isEmpty(this.jobItm.itm.match_type)) { | |
170 | + return "필터 종류를 선택해 주세요."; | |
171 | + } | |
172 | + | |
173 | + for (let filter of this.jobItm.itm.filterItems) { | |
174 | + if ( | |
175 | + this.$isEmpty(filter.coulmn_nm) || | |
176 | + this.$isEmpty(filter.calc_ty) || | |
177 | + this.$isEmpty(filter.cmpr_value) | |
178 | + ) { | |
179 | + return "필터 목록에 빈 값이 있습니다. 빈 값을 채워 주세요."; | |
180 | + } | |
181 | + } | |
182 | + | |
183 | + return null; | |
184 | + }, | |
185 | + | |
186 | + // 커넥션 정보 설정 여부 변경 | |
187 | + fnDfltSetChange(isDfltSet) { | |
188 | + console.log("fnDfltSetChange 실행: ", isDfltSet); | |
189 | + this.isDfltSet = isDfltSet; | |
190 | + }, | |
191 | + | |
192 | + // 노드 데이터 설정 여부 변경 | |
193 | + fnDataSetChange(isDataSet) { | |
194 | + console.log("fnDataSetChange 실행: ", isDataSet); | |
195 | + this.isDataSet = isDataSet; | |
196 | + }, | |
197 | + }, | |
198 | +}; | |
199 | +</script>(No newline at end of file) |
+++ client/views/component/modal/SchedulePreviewModal.vue
... | ... | @@ -0,0 +1,102 @@ |
1 | +<template> | |
2 | + <div class="modal-wrapper"> | |
3 | + <div class="modal-container large-modal"> | |
4 | + <div class="modal-title flex justify-between align-center"> | |
5 | + <h2>스케줄 결과 미리보기</h2> | |
6 | + <button class="close-btn" @click="$emit('onClose')"> | |
7 | + <svg-icon | |
8 | + type="mdi" | |
9 | + :width="20" | |
10 | + :height="20" | |
11 | + :path="closePath" | |
12 | + ></svg-icon> | |
13 | + </button> | |
14 | + </div> | |
15 | + <div class="modal-content-monthly"> | |
16 | + <template v-for="(item, idx) of frontDataTables" :key="idx"> | |
17 | + <ul class="tab-nav2 flex align-center"> | |
18 | + <li @click="showTab(item.name)"> | |
19 | + <a :class="{ activeTab: activeTab === item.name }">데이터셋</a> | |
20 | + </li> | |
21 | + </ul> | |
22 | + <ul class="tab-content2"> | |
23 | + <li v-show="activeTab === item.name" class="content-box"> | |
24 | + <div class="content-titleZone"> | |
25 | + <p class="box-title">데이터셋 정보</p> | |
26 | + </div> | |
27 | + <div class="content-zone" style="overflow: auto"> | |
28 | + <table class="list-table table-flow"> | |
29 | + <template v-if="!$isEmpty(dataTable)"> | |
30 | + <thead> | |
31 | + <tr> | |
32 | + <th>No</th> | |
33 | + <th v-for="(rowKey, index) of rowKeys" :key="index"> | |
34 | + {{ rowKey }} | |
35 | + </th> | |
36 | + </tr> | |
37 | + </thead> | |
38 | + <tbody> | |
39 | + <tr v-for="(items, indexI) of rowData" :key="indexI"> | |
40 | + <td>{{ indexI + 1 }}</td> | |
41 | + <td v-for="(item, indexJ) of items" :key="indexJ"> | |
42 | + {{ item }} | |
43 | + </td> | |
44 | + </tr> | |
45 | + </tbody> | |
46 | + </template> | |
47 | + <tr v-else> | |
48 | + <td>등록된 데이터가 없습니다.</td> | |
49 | + </tr> | |
50 | + </table> | |
51 | + </div> | |
52 | + </li> | |
53 | + </ul> | |
54 | + </template> | |
55 | + </div> | |
56 | + <div class="modal-end flex justify-end"> | |
57 | + <button class="blue-btn small-btn" @click="$emit('onClose')"> | |
58 | + 확인 | |
59 | + </button> | |
60 | + </div> | |
61 | + </div> | |
62 | + </div> | |
63 | +</template> | |
64 | +<script> | |
65 | +// icon용 svg import | |
66 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
67 | +import { mdiMagnify, mdiClose } from "@mdi/js"; | |
68 | + | |
69 | +export default { | |
70 | + components: { SvgIcon }, | |
71 | + | |
72 | + props: { | |
73 | + diagram: Object, | |
74 | + }, | |
75 | + | |
76 | + data() { | |
77 | + return { | |
78 | + // icon용 svg path | |
79 | + searchPath: mdiMagnify, | |
80 | + closePath: mdiClose, | |
81 | + | |
82 | + frontDataTables: [], | |
83 | + rowKeys: [], | |
84 | + rowData: [], | |
85 | + | |
86 | + activeTab: null, | |
87 | + }; | |
88 | + }, | |
89 | + | |
90 | + created() { | |
91 | + console.log("diagram: ", this.diagram); | |
92 | + }, | |
93 | + | |
94 | + mounted() {}, | |
95 | + | |
96 | + methods: { | |
97 | + showTab(tabName) { | |
98 | + this.activeTab = tabName; | |
99 | + }, | |
100 | + }, | |
101 | +}; | |
102 | +</script>(No newline at end of file) |
+++ client/views/component/scheduleComponent/CreateNodeModal.vue
... | ... | @@ -0,0 +1,183 @@ |
1 | +<template> | |
2 | + <div class="modal-wrapper"> | |
3 | + <div class="modal-container small-modal"> | |
4 | + <div class="modal-title"> | |
5 | + <div class="flex justify-between align-center"> | |
6 | + <h2>노드 추가</h2> | |
7 | + <button class="close-btn" @click="fnCloseModal">X</button> | |
8 | + </div> | |
9 | + </div> | |
10 | + <div class="modal-content-monthly"> | |
11 | + <div | |
12 | + class="mb10" | |
13 | + v-for="(type, idx) of typeList" | |
14 | + :key="idx" | |
15 | + :value="type" | |
16 | + > | |
17 | + <div class="content-titleZone flex justify-between align-center mt20"> | |
18 | + <p class="box-title">{{ type.nm }}</p> | |
19 | + </div> | |
20 | + <div class="input-container flex"> | |
21 | + <template v-for="(child, index) of type.childList" :key="index"> | |
22 | + <label class="radio-label"> | |
23 | + <input | |
24 | + type="radio" | |
25 | + name="radio" | |
26 | + class="custom-radiobox" | |
27 | + :value="child" | |
28 | + v-model="selectedNode" | |
29 | + /> | |
30 | + <span>{{ child.nm }}</span> | |
31 | + </label> | |
32 | + </template> | |
33 | + </div> | |
34 | + </div> | |
35 | + </div> | |
36 | + <div class="modal-end flex justify-end"> | |
37 | + <button class="blue-btn small-btn" @click="fnAddNode">등록</button> | |
38 | + <button class="blue-border-btn small-btn" @click="fnCloseModal"> | |
39 | + 취소 | |
40 | + </button> | |
41 | + </div> | |
42 | + </div> | |
43 | + </div> | |
44 | +</template> | |
45 | +<script> | |
46 | +export default { | |
47 | + name: "CreateNodeModal", | |
48 | + | |
49 | + props: { | |
50 | + schdulType: { | |
51 | + type: String, | |
52 | + default: null, | |
53 | + }, | |
54 | + }, | |
55 | + | |
56 | + data() { | |
57 | + return { | |
58 | + // 대분류 | |
59 | + typeList: [], | |
60 | + selectedType: {}, | |
61 | + | |
62 | + // 소분류 | |
63 | + nodeList: [], | |
64 | + selectedNode: {}, | |
65 | + }; | |
66 | + }, | |
67 | + | |
68 | + created() { | |
69 | + this.fnSelectTypeList(); | |
70 | + }, | |
71 | + | |
72 | + computed: {}, | |
73 | + | |
74 | + watch: { | |
75 | + selectedNode: { | |
76 | + deep: true, | |
77 | + handler(value) { | |
78 | + this.selectedType = {}; // 초기화 | |
79 | + for (let type of this.typeList) { | |
80 | + if (type.id === value.upId) { | |
81 | + this.selectedType = type; | |
82 | + break; | |
83 | + } | |
84 | + } | |
85 | + }, | |
86 | + }, | |
87 | + }, | |
88 | + | |
89 | + methods: { | |
90 | + // 분류 조회 | |
91 | + async fnSelectTypeList() { | |
92 | + this.typeList = await this.$getCommonCodeByTree(this.schdulType); | |
93 | + this.selectedType = this.typeList[0]; | |
94 | + this.selectedNode = this.typeList[0].childList[0]; | |
95 | + }, | |
96 | + | |
97 | + // 등록 버튼 동작 | |
98 | + fnAddNode() { | |
99 | + let node = Object.assign({}, this.$getDefaultJobGroup().node); | |
100 | + node.id = | |
101 | + "NODE_" + new Date().getTime() + +Math.floor(Math.random() * 1000) + 1; | |
102 | + node.label = this.selectedNode.nm; | |
103 | + node.position = { | |
104 | + x: Math.floor(Math.random() * 500) + 1, | |
105 | + y: Math.floor(Math.random() * 300) + 1, | |
106 | + }; | |
107 | + | |
108 | + // 타입 설정 | |
109 | + switch (this.selectedType.id) { | |
110 | + case "DATA_READ_NODE": // 리드 | |
111 | + node.type = "read"; | |
112 | + break; | |
113 | + case "DATA_FILTER_NODE": // 필터 | |
114 | + node.type = "filter"; | |
115 | + break; | |
116 | + case "DATA_PROCESS_NODE": // 프로세스 | |
117 | + node.type = "process"; | |
118 | + break; | |
119 | + } | |
120 | + | |
121 | + // jobitem 설정 | |
122 | + node.jobItm = Object.assign({}, this.$getDefaultObject().jobItem); | |
123 | + | |
124 | + // itm, itmType 설정j | |
125 | + node.jobItm.type = this.selectedNode.id; | |
126 | + switch (this.selectedNode.id) { | |
127 | + case "DB_READ": | |
128 | + node.jobItm.itm = Object.assign( | |
129 | + {}, | |
130 | + this.$getDefaultJobGroup().connectionDb | |
131 | + ); | |
132 | + break; | |
133 | + case "API_READ": | |
134 | + node.jobItm.itm = Object.assign( | |
135 | + {}, | |
136 | + this.$getDefaultJobGroup().connectionApi | |
137 | + ); | |
138 | + break; | |
139 | + case "FILE_READ": | |
140 | + node.jobItm.itm = Object.assign( | |
141 | + {}, | |
142 | + this.$getDefaultJobGroup().fileRead | |
143 | + ); | |
144 | + break; | |
145 | + case "DATASET_READ": | |
146 | + node.jobItm.itm = Object.assign( | |
147 | + {}, | |
148 | + this.$getDefaultJobGroup().DatasetPost | |
149 | + ); | |
150 | + break; | |
151 | + case "DATA_FILTER": | |
152 | + node.jobItm.itm = Object.assign( | |
153 | + {}, | |
154 | + this.$getDefaultJobGroup().dataFilter | |
155 | + ); | |
156 | + break; | |
157 | + case "EHOJO_READ": | |
158 | + node.jobItm.itm = Object.assign( | |
159 | + {}, | |
160 | + this.$getDefaultJobGroup().connectionEhojo | |
161 | + ); | |
162 | + break; | |
163 | + case "DATASET_UPDATE": | |
164 | + node.jobItm.itm = Object.assign( | |
165 | + {}, | |
166 | + this.$getDefaultJobGroup().jobItemGroup | |
167 | + ); | |
168 | + break; | |
169 | + } | |
170 | + | |
171 | + this.$emit("addNode", node); | |
172 | + | |
173 | + // 모달 닫기 | |
174 | + this.fnCloseModal(); | |
175 | + }, | |
176 | + | |
177 | + // 모달 닫기 | |
178 | + fnCloseModal() { | |
179 | + this.$emit("closeModal"); | |
180 | + }, | |
181 | + }, | |
182 | +}; | |
183 | +</script>(No newline at end of file) |
+++ client/views/component/scheduleComponent/DiagramList.vue
... | ... | @@ -0,0 +1,376 @@ |
1 | +<template> | |
2 | + <div class="node-zone" style="height: 93%"> | |
3 | + <VueFlow | |
4 | + :nodes="nodes" | |
5 | + :edges="edges" | |
6 | + :default-viewport="{ zoom: 1, position: { x: 0, y: 0 } }" | |
7 | + :min-zoom="0.2" | |
8 | + :max-zoom="4" | |
9 | + :default-edge-options="{ type: 'smoothstep' }" | |
10 | + @connect="onConnect" | |
11 | + @node-drag-stop="onMove" | |
12 | + @node-double-click="fnNodeDblClick" | |
13 | + @edge-double-click="fnDeleteEdge" | |
14 | + @edge-update="onEdgeUpdate" | |
15 | + > | |
16 | + <!-- 시작노드 --> | |
17 | + <template #node-start="props"> | |
18 | + <StartNode | |
19 | + :node="props" | |
20 | + :schdulType="diagram.schdulType" | |
21 | + @updateType="fnUpdateSchdulType" | |
22 | + /> | |
23 | + </template> | |
24 | + <!-- 리드 --> | |
25 | + <template #node-read="props"> | |
26 | + <ReadNode | |
27 | + :node="fnFindNode(props.id)" | |
28 | + @onSetup="fnOpenSetup" | |
29 | + @onRemove="fnRemoveNode" | |
30 | + /> | |
31 | + </template> | |
32 | + <!-- 필터 --> | |
33 | + <template #node-filter="props"> | |
34 | + <FilterNode | |
35 | + :node="fnFindNode(props.id)" | |
36 | + @onSetup="fnOpenSetup" | |
37 | + @onRemove="fnRemoveNode" | |
38 | + /> | |
39 | + </template> | |
40 | + <!-- 프로세스 --> | |
41 | + <template #node-process="props"> | |
42 | + <ProcessNodeVue | |
43 | + :node="fnFindNode(props.id)" | |
44 | + @onSetup="fnOpenSetup" | |
45 | + @onRemove="fnRemoveNode" | |
46 | + /> | |
47 | + </template> | |
48 | + <!-- 종료노드 --> | |
49 | + <template #node-end="props"> | |
50 | + <EndNode :node="props" @onSetup="fnOpenSetup" /> | |
51 | + </template> | |
52 | + | |
53 | + <MiniMap /> | |
54 | + <Controls /> | |
55 | + <Background pattern-color="#aaa" :gap="10" /> | |
56 | + </VueFlow> | |
57 | + </div> | |
58 | + <div class="flex justify-end"> | |
59 | + <button | |
60 | + class="blue-border-btn small-btn" | |
61 | + v-if="!$isEmpty(diagram.schdulType)" | |
62 | + @click="fnOpenModal" | |
63 | + > | |
64 | + 노드 추가 | |
65 | + </button> | |
66 | + <button class="blue-border-btn small-btn">초기화</button> | |
67 | + <button class="blue-btn small-btn">로그 보기</button> | |
68 | + </div> | |
69 | + | |
70 | + <!-- 모달 --> | |
71 | + <CreateNodeModal | |
72 | + v-if="isOpenModal" | |
73 | + :schdulType="diagram.schdulType" | |
74 | + @addNode="fnAddNode" | |
75 | + @closeModal="fnCloseModal" | |
76 | + /> | |
77 | + <ConnectionModal | |
78 | + v-if="isOpenSetupModal" | |
79 | + :frontNodes="frontNodes" | |
80 | + :currentNode="currentNode" | |
81 | + @onSave="fnSaveSetup" | |
82 | + @onClose="fnCloseSetup" | |
83 | + /> | |
84 | + <SchedulePreviewModal | |
85 | + v-if="isOpenPreviewModal" | |
86 | + :diagram="diagram" | |
87 | + @onClose="fnCloseSetup" | |
88 | + /> | |
89 | +</template> | |
90 | +<script> | |
91 | +import StartNode from "./nodes/StartNode.vue"; | |
92 | +import ReadNode from "./nodes/ReadNode.vue"; | |
93 | +import FilterNode from "./nodes/FilterNode.vue"; | |
94 | +import ProcessNodeVue from "./nodes/ProcessNode.vue"; | |
95 | +import EndNode from "./nodes/EndNode.vue"; | |
96 | +// vue-flow api (삭제시 동작 안함) | |
97 | +import { Background } from "@vue-flow/background"; | |
98 | +import { Panel, VueFlow } from "@vue-flow/core"; | |
99 | +import { MiniMap } from "@vue-flow/minimap"; | |
100 | +import { Controls } from "@vue-flow/controls"; | |
101 | +// 모달 컴포넌트 | |
102 | +import CreateNodeModal from "./CreateNodeModal.vue"; | |
103 | +import ConnectionModal from "../modal/NodeSetupModal.vue"; | |
104 | +import SchedulePreviewModal from "../modal/SchedulePreviewModal.vue"; | |
105 | + | |
106 | +export default { | |
107 | + name: "DiagramList", | |
108 | + | |
109 | + components: { | |
110 | + StartNode, | |
111 | + ReadNode, | |
112 | + FilterNode, | |
113 | + ProcessNodeVue, | |
114 | + EndNode, | |
115 | + // vue-flow api (삭제시 동작 안함) | |
116 | + Background, | |
117 | + Panel, | |
118 | + VueFlow, | |
119 | + MiniMap, | |
120 | + Controls, | |
121 | + // 모달 컴포넌트 | |
122 | + CreateNodeModal, | |
123 | + ConnectionModal, | |
124 | + SchedulePreviewModal, | |
125 | + }, | |
126 | + | |
127 | + props: { | |
128 | + currentDiagram: { | |
129 | + type: Object, | |
130 | + }, | |
131 | + }, | |
132 | + | |
133 | + data() { | |
134 | + return { | |
135 | + diagram: this.currentDiagram, | |
136 | + nodes: [], | |
137 | + edges: [], | |
138 | + | |
139 | + startNode: {}, // 시작노드 | |
140 | + endNode: {}, // 종료노드 | |
141 | + | |
142 | + isOpenModal: false, | |
143 | + isOpenSetupModal: false, | |
144 | + isOpenPreviewModal: false, | |
145 | + | |
146 | + frontNodes: [], | |
147 | + currentNode: Object.assign({}, this.$getDefaultJobGroup().node), | |
148 | + }; | |
149 | + }, | |
150 | + | |
151 | + mounted() { | |
152 | + this.init(); // 초기화 | |
153 | + }, | |
154 | + | |
155 | + methods: { | |
156 | + // 초기노드(시작, 종료) 생성 | |
157 | + init() { | |
158 | + if (this.$isEmpty(this.currentDiagram.schdulId)) { | |
159 | + // 시작노드 생성 | |
160 | + this.startNode = Object.assign({}, this.$getDefaultJobGroup().node); | |
161 | + this.startNode.id = | |
162 | + "NODE_" + new Date().getTime() + Math.floor(Math.random() * 1000) + 1; | |
163 | + this.startNode.type = "start"; | |
164 | + this.startNode.label = "시작노드"; | |
165 | + this.startNode.position = { | |
166 | + x: 0, | |
167 | + y: 0, | |
168 | + }; | |
169 | + } else { | |
170 | + this.nodes = this.diagram.nodes; | |
171 | + this.edges = this.diagram.edges; | |
172 | + | |
173 | + // 시작노드 생성 | |
174 | + for (let node of this.nodes) { | |
175 | + if (node.type == "start") { | |
176 | + this.startNode = node; | |
177 | + break; | |
178 | + } | |
179 | + } | |
180 | + } | |
181 | + | |
182 | + // 종료노드 생성 | |
183 | + this.endNode = Object.assign({}, this.$getDefaultJobGroup().node); | |
184 | + this.endNode.id = | |
185 | + "NODE_" + new Date().getTime() + Math.floor(Math.random() * 1000) + 1; | |
186 | + this.endNode.type = "end"; | |
187 | + this.endNode.label = "종료노드"; | |
188 | + this.endNode.position = { | |
189 | + x: 749, | |
190 | + y: 326, | |
191 | + }; | |
192 | + | |
193 | + this.initDiagram(); // 다이어그램 초기화 | |
194 | + }, | |
195 | + | |
196 | + // 다이어그램 초기화 | |
197 | + initDiagram() { | |
198 | + // 노드 정보 확인 | |
199 | + if (this.currentDiagram.nodes.length > 0) { | |
200 | + this.nodes = Object.assign({}, this.currentDiagram.nodes); // 노드 목록 복제 | |
201 | + } else { | |
202 | + this.nodes = []; // 노드 목록 초기화 | |
203 | + this.nodes.push(this.startNode); | |
204 | + this.nodes.push(this.endNode); | |
205 | + } | |
206 | + | |
207 | + // 엣지 정보 확인 | |
208 | + if (this.currentDiagram.edges.length > 0) { | |
209 | + this.edges = Object.assign({}, this.currentDiagram.edges); // 엣지 목록 복제 | |
210 | + } else { | |
211 | + this.edges = []; // 엣지 목록 초기화 | |
212 | + } | |
213 | + }, | |
214 | + | |
215 | + // 시작 노드 선택 | |
216 | + fnUpdateSchdulType(schdulType) { | |
217 | + // 시작 노드 설정 | |
218 | + if (this.$isEmpty(schdulType)) { | |
219 | + this.startNode.isSetup = false; | |
220 | + } else { | |
221 | + this.startNode.isSetup = true; | |
222 | + } | |
223 | + | |
224 | + this.diagram.schdulType = schdulType; // 스케줄 타입 변경 | |
225 | + | |
226 | + this.initDiagram(); // 다이어그램 초기화 | |
227 | + }, | |
228 | + | |
229 | + /* 노드추가 모달 */ | |
230 | + // 모달 열기 | |
231 | + fnOpenModal() { | |
232 | + this.isOpenModal = true; | |
233 | + }, | |
234 | + | |
235 | + // 모달 닫기 | |
236 | + fnCloseModal() { | |
237 | + this.isOpenModal = false; | |
238 | + }, | |
239 | + | |
240 | + /* 노드설정 모달 */ | |
241 | + // 노드설정 모달 열기 | |
242 | + fnOpenSetup(nodeId) { | |
243 | + this.currentNode = this.fnFindNode(nodeId); | |
244 | + | |
245 | + if (this.currentNode.type != "read") { | |
246 | + this.frontNodes = this.fnFindLinkedFrontNodes(nodeId); | |
247 | + | |
248 | + if (this.frontNodes.length < 1) { | |
249 | + this.$showAlert("경고", "연결된 노드가 없습니다."); | |
250 | + return; | |
251 | + } | |
252 | + | |
253 | + // 추후 1:다 관계 노드 생기면 타입에 따른 조건문 씌워서 사용 | |
254 | + if (this.frontNodes.length > 1) { | |
255 | + this.$showAlert("경고", "연결된 노드가 많습니다."); | |
256 | + return; | |
257 | + } | |
258 | + } | |
259 | + | |
260 | + if (this.currentNode.type != "end") { | |
261 | + this.isOpenSetupModal = true; | |
262 | + } else { | |
263 | + this.isOpenPreviewModal = true; | |
264 | + } | |
265 | + }, | |
266 | + | |
267 | + // 노드설정 저장 | |
268 | + fnSaveSetup(jobItem) { | |
269 | + this.currentNode.jobItm = jobItem; | |
270 | + | |
271 | + const nodeToUpdate = this.nodes.find( | |
272 | + (node) => node.id === this.currentNode.id | |
273 | + ); | |
274 | + | |
275 | + if (nodeToUpdate) { | |
276 | + Object.assign(nodeToUpdate, this.currentNode); | |
277 | + nodeToUpdate.isSetup = true; | |
278 | + } | |
279 | + | |
280 | + this.fnCloseSetup(); // 노드설정 모달 닫기 | |
281 | + }, | |
282 | + | |
283 | + // 노드설정 모달 닫기 | |
284 | + fnCloseSetup() { | |
285 | + this.currentNode = Object.assign({}, this.$getDefaultJobGroup().node); | |
286 | + | |
287 | + if (this.currentNode.type != "end") { | |
288 | + this.isOpenSetupModal = false; | |
289 | + } else { | |
290 | + this.isOpenPreviewModal = false; | |
291 | + } | |
292 | + }, | |
293 | + | |
294 | + /* 노드 설정 */ | |
295 | + // 노드 추가 | |
296 | + fnAddNode(node) { | |
297 | + this.nodes.push(node); | |
298 | + }, | |
299 | + | |
300 | + // 노드 목록에서 노드 아이디로 노드 찾기 | |
301 | + fnFindNode(nodeId) { | |
302 | + for (let node of this.nodes) { | |
303 | + if (node.id === nodeId) { | |
304 | + return node; | |
305 | + } | |
306 | + } | |
307 | + | |
308 | + return null; | |
309 | + }, | |
310 | + | |
311 | + // 연결된 소스 노드 찾기 | |
312 | + fnFindLinkedFrontNodes(nodeId) { | |
313 | + let frontNodes = []; | |
314 | + | |
315 | + for (let edge of this.edges) { | |
316 | + if (nodeId === edge.target) { | |
317 | + let frontNode = this.fnFindNode(edge.source); | |
318 | + | |
319 | + if (!this.$isEmpty(frontNode)) { | |
320 | + frontNodes.push(frontNode); | |
321 | + } | |
322 | + } | |
323 | + } | |
324 | + | |
325 | + return frontNodes; | |
326 | + }, | |
327 | + | |
328 | + // 노드 더블 클릭 | |
329 | + fnNodeDblClick(v) { | |
330 | + console.log("nodes: ", this.nodes); | |
331 | + console.log("edges: ", this.edges); | |
332 | + }, | |
333 | + | |
334 | + // 노드 이동 | |
335 | + onMove(moveNode) { | |
336 | + for (let node of this.nodes) { | |
337 | + if (node.id == moveNode.node.id) { | |
338 | + node.position.x = moveNode.node.position.x; | |
339 | + node.position.y = moveNode.node.position.y; | |
340 | + } | |
341 | + } | |
342 | + }, | |
343 | + | |
344 | + // 노드 삭제 | |
345 | + fnRemoveNode(nodeId) { | |
346 | + this.nodes = this.nodes.filter((node) => node.id !== nodeId); | |
347 | + this.edges = this.edges.filter( | |
348 | + (edge) => edge.source !== nodeId && edge.target !== nodeId | |
349 | + ); | |
350 | + }, | |
351 | + | |
352 | + /* 엣지 설정 */ | |
353 | + // 엣지 추가 | |
354 | + onConnect(event) { | |
355 | + let id = "EDGE_" + Date.now(); | |
356 | + | |
357 | + let edge = { | |
358 | + id: id, | |
359 | + edge_id: id, | |
360 | + source: event.source, | |
361 | + target: event.target, | |
362 | + animated: true, | |
363 | + style: { stroke: "#213f99", strokeWidth: 2 }, | |
364 | + }; | |
365 | + | |
366 | + this.edges.push(edge); | |
367 | + }, | |
368 | + | |
369 | + // 엣지 삭제 | |
370 | + fnDeleteEdge(event) { | |
371 | + let edgeId = event.edge.id; | |
372 | + this.edges = this.edges.filter((edge) => edge.id !== edgeId); | |
373 | + }, | |
374 | + }, | |
375 | +}; | |
376 | +</script>(No newline at end of file) |
+++ client/views/component/scheduleComponent/DiagramLogDetailList.vue
... | ... | @@ -0,0 +1,229 @@ |
1 | +<template> | |
2 | + <div class="modal-wrapper"> | |
3 | + <div class="modal-container list-modal"> | |
4 | + <div class="modal-title"> | |
5 | + <div class="flex justify-between align-center"> | |
6 | + <h2 class="main-title flex80">로그 상세보기</h2> | |
7 | + <button class="close-btn" @click="closeModal"> | |
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="modalType == 'form-modal'"> | |
13 | + <div class="flex justify-start align-center"> | |
14 | + <div class="cunt-selectZone"> | |
15 | + <p class="box-title"> 로그분류 <select v-model="searchdetail_data.value" @change="searchDetailData"> | |
16 | + <option :value="null">상태 전체</option> | |
17 | + <option value="success">성공</option> | |
18 | + <option value="fail">실패</option> | |
19 | + </select> | |
20 | + </p> | |
21 | + </div> | |
22 | + </div> | |
23 | + <table class="list-table blue"> | |
24 | + <thead> | |
25 | + <tr> | |
26 | + <th>번호</th> | |
27 | + <th>작동일시</th> | |
28 | + <th>작동완료일시</th> | |
29 | + <th>노드명</th> | |
30 | + <th>호스트명</th> | |
31 | + <th>로그분류</th> | |
32 | + <th>로그메세지</th> | |
33 | + </tr> | |
34 | + </thead> | |
35 | + <tbody> | |
36 | + <tr v-for="(rowdata, dataIndex) in detaillogdata" :key="dataIndex"> | |
37 | + <td>{{ dataIndex + 1 }}</td> | |
38 | + <td>{{ $getFullTime(rowdata.operation_date) }}</td> | |
39 | + <td>{{ $getFullTime(rowdata.operation_success_date) }}</td> | |
40 | + <td>{{ rowdata.node_name }}</td> | |
41 | + <td>{{ rowdata.creat_id }}</td> | |
42 | + <td>{{ rowdata.log_category }}</td> | |
43 | + <td class="text-over" title="-">{{ rowdata.log_message }}</td> | |
44 | + </tr> | |
45 | + </tbody> | |
46 | + </table> | |
47 | + <PaginationButton v-model:currentPage="searchdetail.currentPage" :perPage="searchdetail.perPage" :totalCount="searchdetail.totalRows" :maxRange="5" :click="detaillogSelectListAll" /> | |
48 | + </div> | |
49 | + <div class="modal-end flex justify-end"> | |
50 | + <button v-show="modalType === 'form-modal'" class="blue-btn small-btn" @click="closeModal"> 확인 </button> | |
51 | + </div> | |
52 | + </div> | |
53 | + </div> | |
54 | +</template> | |
55 | +<script> | |
56 | +import PaginationButton from "../PaginationButton.vue"; | |
57 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
58 | +import { mdiMagnify } from "@mdi/js"; | |
59 | +import { mdiClose } from "@mdi/js"; | |
60 | +import { mdiCheckCircle, mdiCloseCircle } from "@mdi/js"; | |
61 | +import DefaultSearchBar from "../../template/templateElement/DefaultSearchBar.vue"; | |
62 | +import { useRoute } from "vue-router"; | |
63 | +import axios from "axios"; | |
64 | +export default { | |
65 | + components: { | |
66 | + PaginationButton: PaginationButton, | |
67 | + SvgIcon: SvgIcon, | |
68 | + DefaultSearchBar: DefaultSearchBar, | |
69 | + }, | |
70 | + props: { | |
71 | + diagram_id: { | |
72 | + type: String, // 또는 String, 실제 타입에 맞게 설정 | |
73 | + required: true, | |
74 | + }, | |
75 | + }, | |
76 | + data() { | |
77 | + return { | |
78 | + // 검색 | |
79 | + search: this.$getDefaultSerchVO(), | |
80 | + search_date: this.$getDefaultSerchItem("operation_date", "dates"), | |
81 | + search_data1: this.$getDefaultSerchItem("state_info", "string"), | |
82 | + search_data2: { | |
83 | + key: "diagram_id", | |
84 | + value: this.diagram_id, | |
85 | + }, | |
86 | + | |
87 | + //상세보기 검색 | |
88 | + searchdetail: this.$getDefaultSerchVO(), | |
89 | + searchdetail_data: this.$getDefaultSerchItem("log_category", "string"), | |
90 | + searchdetail_data1: { | |
91 | + key: null, | |
92 | + value: null, | |
93 | + }, | |
94 | + | |
95 | + //검색 데이터 | |
96 | + searchPath: mdiMagnify, | |
97 | + startDate: true, | |
98 | + endDate: true, | |
99 | + | |
100 | + closePath: mdiClose, | |
101 | + isModalOpen: false, | |
102 | + modalType: "form-modal", | |
103 | + checkPath: mdiCheckCircle, | |
104 | + xPath: mdiCloseCircle, | |
105 | + logdata: [], | |
106 | + detaillogdata: [], | |
107 | + logId: null, | |
108 | + diagram_id: this.diagram_id, | |
109 | + | |
110 | + //현재 라우터의 정보 | |
111 | + route: useRoute(), | |
112 | + // 페이지 경로 목록 | |
113 | + pageList: [ | |
114 | + { path: "/", name: "대시보드" }, | |
115 | + { path: "/fileManagement.page", name: "파일 관리" }, | |
116 | + { path: "/hostManagement.page", name: "호스트 관리" }, | |
117 | + { path: "/scheduleManagement.page", name: "작업 스케줄 관리" }, | |
118 | + ], | |
119 | + }; | |
120 | + }, | |
121 | + mounted() { | |
122 | + this.searchData(); | |
123 | + }, | |
124 | + watch: { | |
125 | + diagram_id(v) { | |
126 | + this.search_data2.value = v; | |
127 | + this.searchData(); | |
128 | + }, | |
129 | + }, | |
130 | + methods: { | |
131 | + searchData() { | |
132 | + const vm = this; | |
133 | + let data = vm.search; | |
134 | + data.searchObjectList = []; | |
135 | + data.searchObjectList.push(this.search_date); | |
136 | + data.searchObjectList.push(this.search_data1); | |
137 | + data.searchObjectList.push(this.search_data2); | |
138 | + axios({ | |
139 | + url: "/diagram/logSelectListAll.json", | |
140 | + method: "post", | |
141 | + headers: { "Content-Type": "application/json; charset=UTF-8" }, | |
142 | + data: data, | |
143 | + }) | |
144 | + .then(function (response) { | |
145 | + vm.search.currentPage = response.data.resultData.searchVO.currentPage; | |
146 | + vm.search.order = response.data.resultData.searchVO.order; | |
147 | + vm.search.orderASC = response.data.resultData.searchVO.orderASC; | |
148 | + vm.search.perPage = response.data.resultData.searchVO.perPage; | |
149 | + vm.search.searchObjectList = []; | |
150 | + vm.search.totalRows = response.data.resultData.searchVO.totalRows; | |
151 | + vm.logdata = response.data.resultData.logList; | |
152 | + }) | |
153 | + .catch(function (error) { | |
154 | + vm.$showAlert( | |
155 | + "오류", | |
156 | + "처리 중 오류가 발생했습니다. 관리자에게 문의바랍니다." | |
157 | + ); | |
158 | + }); | |
159 | + }, | |
160 | + searchDetailData() { | |
161 | + const vm = this; | |
162 | + let datadetail = vm.searchdetail; | |
163 | + datadetail.searchObjectList = []; | |
164 | + datadetail.searchObjectList.push(this.searchdetail_data); | |
165 | + | |
166 | + this.detaillogSelectListAll(); | |
167 | + }, | |
168 | + openModal() { | |
169 | + this.isModalOpen = true; | |
170 | + }, | |
171 | + closeModal() { | |
172 | + this.isModalOpen = false; | |
173 | + }, | |
174 | + pathName() { | |
175 | + for (let i = 0; i < this.pageList.length; i++) { | |
176 | + if (this.route.path == this.pageList[i]["path"]) { | |
177 | + return this.pageList[i]["name"]; | |
178 | + } | |
179 | + } | |
180 | + return "대시보드"; | |
181 | + }, | |
182 | + | |
183 | + handleRowClick(logId) { | |
184 | + this.logId = logId; | |
185 | + this.detaillogSelectListAll(); | |
186 | + this.openModal(); | |
187 | + }, | |
188 | + | |
189 | + detaillogSelectListAll() { | |
190 | + const vm = this; | |
191 | + let datadetail = vm.searchdetail; | |
192 | + datadetail.searchObjectList = []; | |
193 | + datadetail.searchObjectList.push(this.searchdetail_data); | |
194 | + vm.searchdetail_data1.key = "log_id"; | |
195 | + vm.searchdetail_data1.value = vm.logId; | |
196 | + datadetail.searchObjectList.push(this.searchdetail_data1); | |
197 | + | |
198 | + axios({ | |
199 | + url: "/diagram/detaillogSelectListAll.json", | |
200 | + method: "post", | |
201 | + headers: { | |
202 | + "Content-Type": "application/json; charset=UTF-8", | |
203 | + }, | |
204 | + data: datadetail, | |
205 | + }) | |
206 | + .then(function (response) { | |
207 | + vm.searchdetail.currentPage = | |
208 | + response.data.resultData.searchVO.currentPage; | |
209 | + vm.searchdetail.order = response.data.resultData.searchVO.order; | |
210 | + vm.searchdetail.orderASC = response.data.resultData.searchVO.orderASC; | |
211 | + vm.searchdetail.perPage = response.data.resultData.searchVO.perPage; | |
212 | + vm.searchdetail.searchObjectList = []; | |
213 | + vm.searchdetail.totalRows = | |
214 | + response.data.resultData.searchVO.totalRows; | |
215 | + vm.detaillogdata = response.data.resultData.logList; | |
216 | + }) | |
217 | + .catch(function (error) { | |
218 | + alert("상품 주문에 오류가 발생했습니다."); | |
219 | + }); | |
220 | + }, | |
221 | + }, | |
222 | +}; | |
223 | +</script> | |
224 | +<style scoped> | |
225 | +.navigate_bar { | |
226 | + padding: 10px 0px; | |
227 | + font-size: 1.3rem; | |
228 | +} | |
229 | +</style>(No newline at end of file) |
+++ client/views/component/scheduleComponent/DiagramLogList.vue
... | ... | @@ -0,0 +1,68 @@ |
1 | +<template> | |
2 | + <div class="content-titleZone flex justify-between align-center"> | |
3 | + <p class="box-title">다이어그램 목록</p> | |
4 | + <div class="search-bar"> | |
5 | + <div class="flex justify-end align-center"> | |
6 | + <input type="date" name="start-date" id="start-date" class="square-date" :class="{ 'date-placeholder': false }" data-placeholder="날짜 선택" v-model="search_date.value" @change="searchData()" /> | |
7 | + <span class="coupler">~</span> | |
8 | + <input type="date" name="end-date" id="end-date" class="square-date ml5" :class="{ 'date-placeholder': false }" data-placeholder="날짜 선택" v-model="search_date.value2" @change="searchData()" /> | |
9 | + <select class="square-select ml5" v-model="search_data1.value" @change="searchData()"> | |
10 | + <option :value="null">상태 전체</option> | |
11 | + <option value="success">성공</option> | |
12 | + <option value="fail">실패</option> | |
13 | + </select> | |
14 | + </div> | |
15 | + </div> | |
16 | + </div> | |
17 | + <div class="table-zone"> | |
18 | + <table class="list-table"> | |
19 | + <thead> | |
20 | + <tr> | |
21 | + <th>번호</th> | |
22 | + <th>총 노드수</th> | |
23 | + <th>실패 노드수</th> | |
24 | + <th>실패 노드명</th> | |
25 | + <th>실행일자</th> | |
26 | + <th>상태</th> | |
27 | + </tr> | |
28 | + </thead> | |
29 | + <tbody> | |
30 | + <tr v-for="(rowdata, dataIndex) in logdata" :key="dataIndex" @click="handleRowClick(rowdata.log_id)"> | |
31 | + <td>{{ dataIndex + 1 }}</td> | |
32 | + <td>{{ rowdata.total_node_count }}</td> | |
33 | + <td>{{ rowdata.fail_node_count }}</td> | |
34 | + <td>{{ rowdata.fail_node_name }}</td> | |
35 | + <td>{{ $getFullTime(rowdata.operation_date) }}</td> | |
36 | + <td> | |
37 | + <p v-if="rowdata.state_info === 'success'" class="flex justify-center align-center state green icon-area"> | |
38 | + <svg-icon type="mdi" :width="21" :height="21" :path="checkPath"></svg-icon> | |
39 | + <span>성공</span> | |
40 | + </p> | |
41 | + <p v-else-if="rowdata.state_info === 'fail'" class="flex justify-center align-center state red"> | |
42 | + <svg-icon type="mdi" :width="21" :height="21" :path="xPath"></svg-icon> | |
43 | + <span>실패</span> | |
44 | + </p> | |
45 | + </td> | |
46 | + </tr> | |
47 | + </tbody> | |
48 | + </table> | |
49 | + <PaginationButton v-model:currentPage="search.currentPage" :perPage="search.perPage" :totalCount="search.totalRows" :maxRange="5" :click="searchData" /> | |
50 | + </div> | |
51 | +</template> | |
52 | +<script> | |
53 | +import DiagramLogDetailList from './DiagramLogDetailList.vue'; | |
54 | + | |
55 | +export default { | |
56 | + name: "", | |
57 | + | |
58 | + components: { DiagramLogDetailList }, | |
59 | + | |
60 | + data() { | |
61 | + return {}; | |
62 | + }, | |
63 | + | |
64 | + mounted() { }, | |
65 | + | |
66 | + methods: {}, | |
67 | +} | |
68 | +</script>(No newline at end of file) |
--- client/views/component/ScheculerCreateComp.vue
+++ client/views/component/scheduleComponent/ScheduleForm.vue
... | ... | @@ -1,4 +1,7 @@ |
1 | 1 |
<template> |
2 |
+ <div class="content-titleZone"> |
|
3 |
+ <p class="box-title">스케줄 등록</p> |
|
4 |
+ </div> |
|
2 | 5 |
<div class="table-zone"> |
3 | 6 |
<table class="form-table"> |
4 | 7 |
<colgroup> |
... | ... | @@ -9,13 +12,7 @@ |
9 | 12 |
<tr> |
10 | 13 |
<th>제목</th> |
11 | 14 |
<td> |
12 |
- <input |
|
13 |
- type="text" |
|
14 |
- name="" |
|
15 |
- id="inputid" |
|
16 |
- class="full-input" |
|
17 |
- v-model="title" |
|
18 |
- /> |
|
15 |
+ <input type="text" name="" id="inputid" class="full-input" v-model="title" /> |
|
19 | 16 |
</td> |
20 | 17 |
</tr> |
21 | 18 |
<tr> |
... | ... | @@ -27,23 +24,11 @@ |
27 | 24 |
<td> |
28 | 25 |
<div class="input-container flex"> |
29 | 26 |
<label class="radio-label"> |
30 |
- <input |
|
31 |
- type="radio" |
|
32 |
- name="radio2" |
|
33 |
- :value="false" |
|
34 |
- class="custom-radiobox" |
|
35 |
- v-model="isUsehighClassOption" |
|
36 |
- /> |
|
27 |
+ <input type="radio" name="radio2" :value="false" class="custom-radiobox" v-model="isUsehighClassOption" /> |
|
37 | 28 |
<span>일반 방식</span> |
38 | 29 |
</label> |
39 | 30 |
<label class="radio-label"> |
40 |
- <input |
|
41 |
- type="radio" |
|
42 |
- name="radio2" |
|
43 |
- :value="true" |
|
44 |
- class="custom-radiobox" |
|
45 |
- v-model="isUsehighClassOption" |
|
46 |
- /> |
|
31 |
+ <input type="radio" name="radio2" :value="true" class="custom-radiobox" v-model="isUsehighClassOption" /> |
|
47 | 32 |
<span>커스텀 방식</span> |
48 | 33 |
</label> |
49 | 34 |
</div> |
... | ... | @@ -54,13 +39,7 @@ |
54 | 39 |
<td> |
55 | 40 |
<div class="input-container flex"> |
56 | 41 |
<label class="radio-label"> |
57 |
- <input |
|
58 |
- type="radio" |
|
59 |
- name="Cyclical" |
|
60 |
- :value="true" |
|
61 |
- class="custom-radiobox" |
|
62 |
- v-model="isCyclical" |
|
63 |
- /> |
|
42 |
+ <input type="radio" name="Cyclical" :value="true" class="custom-radiobox" v-model="isCyclical" /> |
|
64 | 43 |
<span>반복작업(반복주기 설정)</span> |
65 | 44 |
</label> |
66 | 45 |
</div> |
... | ... | @@ -69,243 +48,93 @@ |
69 | 48 |
<tr> |
70 | 49 |
<th>반복주기 설정</th> |
71 | 50 |
<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> |
|
51 |
+ <div class="flex justify-start" v-show="isUsehighClassOption == false"> |
|
52 |
+ <select name="cycle" v-model="cronItem" :disabled="isUsehighClassOption == true" style="margin-left: 5px; margin-bottom: 5px"> |
|
53 |
+ <option v-for="(value, key, i) in cronItems" :key="i" :value="key"> 매 {{ value.title }} </option> |
|
89 | 54 |
<option value="ALL">전체</option> |
90 | 55 |
</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 |
- > |
|
56 |
+ <select name="year" v-model="cronMap.YEAR" v-if="isUsehighClassOption == false" v-show="cronItem == 'YEAR' || cronItem == 'ALL'" style="margin-left: 5px; margin-bottom: 5px"> |
|
98 | 57 |
<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> |
|
58 |
+ <option v-for="(item, idx) in cronItems.YEAR.values" :key="idx" :value="item"> {{ item }}년 </option> |
|
106 | 59 |
</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 |
- > |
|
60 |
+ <select name="months" v-model="cronMap.MONTHS" v-show="cronItem == 'MONTHS' || |
|
61 |
+ cronItem == 'YEAR' || |
|
62 |
+ cronItem == 'ALL' |
|
63 |
+ " style="margin-left: 5px; margin-bottom: 5px"> |
|
117 | 64 |
<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> |
|
65 |
+ <option v-for="(item, idx) in cronItems.MONTHS.values" :key="idx" :value="item"> {{ item }}월 </option> |
|
125 | 66 |
</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> |
|
67 |
+ <select name="weeks" v-model="cronMap.WEEKS" v-show="cronItem == 'WEEKS' || cronItem == 'ALL'" style="margin-left: 5px; margin-bottom: 5px"> |
|
68 |
+ <option v-for="(item, idx) in cronItems.WEEKS.values" :key="idx" :value="idx + ''"> {{ item }}요일 </option> |
|
139 | 69 |
<option value="1,2,3,4,5">평일</option> |
140 | 70 |
<option value="0,6">주말(토,일)</option> |
141 | 71 |
<option value="?" v-show="cronItem != 'WEEKS'">없음</option> |
142 | 72 |
</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 |
- > |
|
73 |
+ <select name="days" v-model="cronMap.DAYS" v-show="cronItem == 'DAYS' || |
|
74 |
+ cronItem == 'MONTHS' || |
|
75 |
+ cronItem == 'YEAR' || |
|
76 |
+ cronItem == 'ALL' |
|
77 |
+ " style="margin-left: 5px; margin-bottom: 5px"> |
|
154 | 78 |
<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> |
|
79 |
+ <option v-for="(item, idx) in cronItems.DAYS.values" :key="idx" :value="item"> {{ item }}일 </option> |
|
162 | 80 |
<option value="L">말일</option> |
163 | 81 |
</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> |
|
82 |
+ <select name="hours" v-model="cronMap.HOURS" v-show="cronItem == 'HOURS' || |
|
83 |
+ cronItem == 'DAYS' || |
|
84 |
+ cronItem == 'WEEKS' || |
|
85 |
+ cronItem == 'MONTHS' || |
|
86 |
+ cronItem == 'YEAR' || |
|
87 |
+ cronItem == 'ALL' |
|
88 |
+ " style="margin-left: 5px; margin-bottom: 5px"> |
|
89 |
+ <option v-for="(item, idx) in cronItems.HOURS.values" :key="idx" :value="item"> {{ item }}시 </option> |
|
184 | 90 |
</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> |
|
91 |
+ <select name="minute" v-model="cronMap.MINUTES" v-show="cronItem == 'MINUTES' || |
|
92 |
+ cronItem == 'HOURS' || |
|
93 |
+ cronItem == 'DAYS' || |
|
94 |
+ cronItem == 'WEEKS' || |
|
95 |
+ cronItem == 'MONTHS' || |
|
96 |
+ cronItem == 'YEAR' || |
|
97 |
+ cronItem == 'ALL' |
|
98 |
+ " style="margin-left: 5px; margin-bottom: 5px"> |
|
99 |
+ <option v-for="(item, idx) in cronItems.MINUTES.values" :key="idx" :value="item"> {{ item }}분 </option> |
|
206 | 100 |
</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> |
|
101 |
+ <select name="seconds" v-model="cronMap.SECONDS" v-show="cronItem == 'SECONDS' || |
|
102 |
+ 'MINUTES' || |
|
103 |
+ cronItem == 'HOURS' || |
|
104 |
+ cronItem == 'DAYS' || |
|
105 |
+ cronItem == 'WEEKS' || |
|
106 |
+ cronItem == 'MONTHS' || |
|
107 |
+ cronItem == 'YEAR' || |
|
108 |
+ cronItem == 'ALL' |
|
109 |
+ " style="margin-left: 5px; margin-bottom: 5px"> |
|
110 |
+ <option v-for="(item, idx) in cronItems.SECONDS.values" :key="idx" :value="item"> {{ item }}초 </option> |
|
229 | 111 |
</select> |
230 | 112 |
</div> |
231 | 113 |
<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> |
|
114 |
+ <select name="cycle" v-model="cronItem" :disabled="isUsehighClassOption == true" style="width: 100px; margin-left: 5px; margin-bottom: 5px"> |
|
115 |
+ <option v-for="(value, key, i) in cronItems" :key="i" :value="key"> 매 {{ value.title }} </option> |
|
245 | 116 |
<option value="ALL">전체 주기</option> |
246 | 117 |
</select> |
247 | 118 |
<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 |
- /> |
|
119 |
+ <input type="text" id="year" name="year" v-model="cronMap.YEAR" style="margin-bottom: 5px; width: 100px" /> |
|
255 | 120 |
</label> |
256 | 121 |
<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 |
- /> |
|
122 |
+ <input type="text" id="months" name="months" v-model="cronMap.MONTHS" style="width: 100px; margin-bottom: 5px" /> |
|
264 | 123 |
</label> |
265 | 124 |
<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 |
- /> |
|
125 |
+ <input type="text" id="weeks" name="weeks" v-model="cronMap.WEEKS" style="width: 100px; margin-bottom: 5px" /> |
|
273 | 126 |
</label> |
274 | 127 |
<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 |
- /> |
|
128 |
+ <input type="text" id="days" name="days" v-model="cronMap.DAYS" style="width: 100px; margin-bottom: 5px" /> |
|
282 | 129 |
</label> |
283 | 130 |
<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 |
- /> |
|
131 |
+ <input type="text" id="hours" name="hours" v-model="cronMap.HOURS" style="width: 100px; margin-bottom: 5px" /> |
|
291 | 132 |
</label> |
292 | 133 |
<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 |
- /> |
|
134 |
+ <input type="text" id="minutes" name="minutes" v-model="cronMap.MINUTES" style="width: 100px; margin-bottom: 5px" /> |
|
300 | 135 |
</label> |
301 | 136 |
<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 |
- /> |
|
137 |
+ <input type="text" id="seconds" name="seconds" v-model="cronMap.SECONDS" style="width: 100px; margin-bottom: 5px" /> |
|
309 | 138 |
</label> |
310 | 139 |
</div> |
311 | 140 |
</td> |
... | ... | @@ -323,8 +152,14 @@ |
323 | 152 |
</tbody> |
324 | 153 |
</table> |
325 | 154 |
</div> |
155 |
+ <div class="flex justify-end" id="buttonZone"> |
|
156 |
+ <button class="blue-btn small-btn" id="registerButton"> |
|
157 |
+ <span>등록</span> |
|
158 |
+ <span>수정</span> |
|
159 |
+ </button> |
|
160 |
+ <button class="blue-border-btn small-btn" id="resetButton">초기화</button> |
|
161 |
+ </div> |
|
326 | 162 |
</template> |
327 |
- |
|
328 | 163 |
<script> |
329 | 164 |
//크론 설정값 초기화 |
330 | 165 |
var cronMapInit = { |
+++ client/views/component/scheduleComponent/ScheduleList.vue
... | ... | @@ -0,0 +1,105 @@ |
1 | +<template> | |
2 | + <table class="list-table"> | |
3 | + <colgroup> | |
4 | + <col style="width: 5%" /> | |
5 | + <col style="width: 5%" /> | |
6 | + <col style="width: 25%" /> | |
7 | + <col style="width: 15%" /> | |
8 | + <col style="width: 20%" /> | |
9 | + <col style="width: 20%" /> | |
10 | + <col style="width: 15%" /> | |
11 | + </colgroup> | |
12 | + <thead> | |
13 | + <tr> | |
14 | + <th> | |
15 | + <input type="checkbox" v-model="isChkAll" @change="fnChkAll()" /> | |
16 | + </th> | |
17 | + <th>번호</th> | |
18 | + <th>제목</th> | |
19 | + <th>작성자</th> | |
20 | + <th>반복주기</th> | |
21 | + <th>등록일</th> | |
22 | + <th>상태</th> | |
23 | + </tr> | |
24 | + </thead> | |
25 | + <tbody> | |
26 | + <template v-if="list.length > 0"> | |
27 | + <tr v-for="(item, idx) in list" :key="item" @click="$emit('fnSelectDiagram', item.diagram_id)"> | |
28 | + <td> | |
29 | + <input type="checkbox" :value="item" v-model="selectList" @click.stop="" @change="fnChangeCheckList" /> | |
30 | + </td> | |
31 | + <td>{{ search.totalRows - idx - (search.currentPage - 1) * search.perPage }}</td> | |
32 | + <td>{{ item.diagram_nm }}</td> | |
33 | + <td>{{ item.creat_id }}</td> | |
34 | + <td>{{ item.cron_chrctr }}</td> | |
35 | + <td>{{ $getFullTime(item.creat_dt) }}</td> | |
36 | + <td> | |
37 | + <div v-if="item.schdul_sttus === 'run'" class="flex justify-center align-center state green icon-area"> | |
38 | + <svg-icon type="mdi" :width="21" :height="21" :path="checkPath" class="mr5"></svg-icon> | |
39 | + <span>진행</span> | |
40 | + </div> | |
41 | + <div v-else-if="item.schdul_sttus === 'stop'" class="flex justify-center align-center state red"> | |
42 | + <svg-icon type="mdi" :width="21" :height="21" :path="xPath" class="mr5"></svg-icon> | |
43 | + <span>중단</span> | |
44 | + </div> | |
45 | + </td> | |
46 | + </tr> | |
47 | + </template> | |
48 | + <tr v-else> | |
49 | + <td colspan="7">등록된 데이터가 없습니다.</td> | |
50 | + </tr> | |
51 | + </tbody> | |
52 | + </table> | |
53 | + <PaginationButton v-model:currentPage="search.currentPage" :perPage="search.perPage" :totalCount="search.totalRows" :maxRange="5" :click="fnDiagramList" /> | |
54 | +</template> | |
55 | +<script> | |
56 | +// 컴포넌트 import | |
57 | +import PaginationButton from "../../component/PaginationButton.vue"; | |
58 | + | |
59 | +export default { | |
60 | + name: "ScheduleList", | |
61 | + | |
62 | + components: { | |
63 | + PaginationButton, | |
64 | + }, | |
65 | + | |
66 | + props: { | |
67 | + list: { | |
68 | + type: Array, | |
69 | + default: [], | |
70 | + }, | |
71 | + search: { | |
72 | + type: Object, | |
73 | + default: {}, | |
74 | + }, | |
75 | + }, | |
76 | + | |
77 | + data() { | |
78 | + return { | |
79 | + isChkAll: false, | |
80 | + selectList: [], | |
81 | + }; | |
82 | + }, | |
83 | + | |
84 | + mounted() { }, | |
85 | + | |
86 | + methods: { | |
87 | + // 전체 선택 | |
88 | + fnChkAll() { | |
89 | + this.selectList = []; // 초기화 | |
90 | + if (this.isChkAll) { | |
91 | + this.selectList = this.diagramList; | |
92 | + } | |
93 | + }, | |
94 | + | |
95 | + // 개별 선택 | |
96 | + fnChangeCheckList() { | |
97 | + if (this.selectList.length == this.diagramList.length) { | |
98 | + this.isChkAll = true; | |
99 | + } else { | |
100 | + this.isChkAll = false; | |
101 | + } | |
102 | + }, | |
103 | + }, | |
104 | +}; | |
105 | +</script>(No newline at end of file) |
+++ client/views/component/scheduleComponent/nodes/EndNode.vue
... | ... | @@ -0,0 +1,79 @@ |
1 | +<template> | |
2 | + <div class="node"> | |
3 | + <div class="node-header flex justify-between align-center"> | |
4 | + <p class="box-title">{{ node.label }}</p> | |
5 | + <div></div> | |
6 | + </div> | |
7 | + <div class="node-body flex justify-center align-center"> | |
8 | + <button | |
9 | + type="button" | |
10 | + class="blue-border-btn small-btn" | |
11 | + @click="$emit('onSetup', node.id)" | |
12 | + > | |
13 | + 미리보기 | |
14 | + </button> | |
15 | + </div> | |
16 | + </div> | |
17 | + <Handle type="target" :position="Position.Left" /> | |
18 | +</template> | |
19 | + | |
20 | +<script> | |
21 | +import { Handle, Position } from "@vue-flow/core"; | |
22 | + | |
23 | +export default { | |
24 | + components: { | |
25 | + Handle, | |
26 | + }, | |
27 | + | |
28 | + props: { | |
29 | + node: Object, | |
30 | + }, | |
31 | + | |
32 | + data() {}, | |
33 | + | |
34 | + setup() { | |
35 | + return { Position }; | |
36 | + }, | |
37 | + | |
38 | + computed: {}, | |
39 | + | |
40 | + mounted() {}, | |
41 | + | |
42 | + methods: {}, | |
43 | +}; | |
44 | +</script> | |
45 | + | |
46 | +<style scoped> | |
47 | +.node { | |
48 | + background: #ffffff; | |
49 | + border: 1px solid #dbe3fb; | |
50 | + border-radius: 10px; | |
51 | + width: 300px; | |
52 | + box-shadow: 0 2px 5px rgba(237, 240, 255, 0.8); | |
53 | +} | |
54 | + | |
55 | +.node-header { | |
56 | + padding: 10px 15px; | |
57 | + background-color: #edf0ff; | |
58 | + border-bottom: 1px solid #dbe3fb; | |
59 | +} | |
60 | + | |
61 | +.node-header .box-title { | |
62 | + color: #213f99; | |
63 | +} | |
64 | + | |
65 | +.node-header button { | |
66 | + width: 20px; | |
67 | + height: 20px; | |
68 | +} | |
69 | + | |
70 | +.node-header button img { | |
71 | + width: 100%; | |
72 | + height: 100%; | |
73 | +} | |
74 | + | |
75 | +.node-body { | |
76 | + padding: 15px; | |
77 | + border-radius: 5px; | |
78 | +} | |
79 | +</style>(No newline at end of file) |
+++ client/views/component/scheduleComponent/nodes/FilterNode.vue
... | ... | @@ -0,0 +1,110 @@ |
1 | +<template> | |
2 | + <div class="node"> | |
3 | + <div class="node-header flex justify-between align-center"> | |
4 | + <p class="box-title">{{ node.label }}</p> | |
5 | + <div> | |
6 | + <button @click="$emit('onSetup', node.id)"> | |
7 | + <svg-icon | |
8 | + type="mdi" | |
9 | + :width="20" | |
10 | + :height="20" | |
11 | + :path="setPath" | |
12 | + ></svg-icon> | |
13 | + </button> | |
14 | + <button @click="$emit('onRemove', node.id)"> | |
15 | + <svg-icon | |
16 | + type="mdi" | |
17 | + :width="20" | |
18 | + :height="20" | |
19 | + :path="delPath" | |
20 | + ></svg-icon> | |
21 | + </button> | |
22 | + </div> | |
23 | + </div> | |
24 | + <div class="node-body"> | |
25 | + <div v-if="node.isSetup"> | |
26 | + <p style="font-size: 1.5rem">등록된 필터 개수 : {{ filterCnt }}</p> | |
27 | + <p style="font-size: 1.5rem"> | |
28 | + 필터 적용 후 로우 개수 : {{ node.dataTable.rowData.length }} | |
29 | + </p> | |
30 | + </div> | |
31 | + <p v-else style="font-size: 1.5rem">노드 미설정</p> | |
32 | + </div> | |
33 | + <!-- 핸들 --> | |
34 | + <Handle type="source" :position="Position.Right" /> | |
35 | + <Handle type="target" :position="Position.Left" /> | |
36 | + </div> | |
37 | +</template> | |
38 | + | |
39 | +<script> | |
40 | +// icon용 svg import | |
41 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
42 | +import { mdiCog, mdiTrashCanOutline } from "@mdi/js"; | |
43 | + | |
44 | +import { Handle, Position } from "@vue-flow/core"; | |
45 | + | |
46 | +export default { | |
47 | + components: { | |
48 | + SvgIcon, | |
49 | + Handle, | |
50 | + }, | |
51 | + | |
52 | + props: { | |
53 | + node: Object, | |
54 | + }, | |
55 | + | |
56 | + data() { | |
57 | + return { | |
58 | + // icon용 svg path | |
59 | + setPath: mdiCog, | |
60 | + delPath: mdiTrashCanOutline, | |
61 | + | |
62 | + filterCnt: 0, | |
63 | + totalRow: 0, | |
64 | + }; | |
65 | + }, | |
66 | + | |
67 | + setup() { | |
68 | + return { | |
69 | + Position, | |
70 | + }; | |
71 | + }, | |
72 | + | |
73 | + mounted() {}, | |
74 | + | |
75 | + watch: {}, | |
76 | + | |
77 | + methods: {}, | |
78 | +}; | |
79 | +</script> | |
80 | + | |
81 | +<style scoped> | |
82 | +.node { | |
83 | + background: #ffffff; | |
84 | + border: 1px solid #dbe3fb; | |
85 | + border-radius: 10px; | |
86 | + width: 300px; | |
87 | + box-shadow: 0 2px 5px rgba(237, 240, 255, 0.8); | |
88 | +} | |
89 | + | |
90 | +.node-header { | |
91 | + padding: 10px 15px; | |
92 | + background-color: #edf0ff; | |
93 | + border-bottom: 1px solid #dbe3fb; | |
94 | +} | |
95 | + | |
96 | +.node-header button { | |
97 | + width: 20px; | |
98 | + height: 20px; | |
99 | +} | |
100 | + | |
101 | +.node-header button img { | |
102 | + width: 100%; | |
103 | + height: 100%; | |
104 | +} | |
105 | + | |
106 | +.node-body { | |
107 | + padding: 15px; | |
108 | + border-radius: 5px; | |
109 | +} | |
110 | +</style>(No newline at end of file) |
+++ client/views/component/scheduleComponent/nodes/ProcessNode.vue
... | ... | @@ -0,0 +1,118 @@ |
1 | +<template> | |
2 | + <div class="node"> | |
3 | + <div class="node-header flex justify-between align-center"> | |
4 | + <p class="box-title">{{ node.label }}</p> | |
5 | + <div> | |
6 | + <button @click="$emit('onSetup', node.id)"> | |
7 | + <svg-icon | |
8 | + type="mdi" | |
9 | + :width="20" | |
10 | + :height="20" | |
11 | + :path="setPath" | |
12 | + ></svg-icon> | |
13 | + </button> | |
14 | + <button @click="$emit('onRemove', node.id)"> | |
15 | + <svg-icon | |
16 | + type="mdi" | |
17 | + :width="20" | |
18 | + :height="20" | |
19 | + :path="delPath" | |
20 | + ></svg-icon> | |
21 | + </button> | |
22 | + </div> | |
23 | + </div> | |
24 | + <div class="node-body"> | |
25 | + <div v-if="node.isSetup"> | |
26 | + <p style="font-size: 1.5rem">매칭 데이터 개수 : {{ matchCnt }}</p> | |
27 | + </div> | |
28 | + <p v-else style="font-size: 1.5rem">노드 미설정</p> | |
29 | + </div> | |
30 | + <!-- 핸들 --> | |
31 | + <Handle type="source" :position="Position.Right" /> | |
32 | + <Handle type="target" :position="Position.Left" /> | |
33 | + </div> | |
34 | +</template> | |
35 | + | |
36 | +<script> | |
37 | +// icon용 svg import | |
38 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
39 | +import { mdiCog, mdiTrashCanOutline } from "@mdi/js"; | |
40 | + | |
41 | +import { Handle, Position } from "@vue-flow/core"; | |
42 | + | |
43 | +export default { | |
44 | + components: { | |
45 | + SvgIcon, | |
46 | + Handle, | |
47 | + }, | |
48 | + | |
49 | + props: { | |
50 | + node: Object, | |
51 | + }, | |
52 | + | |
53 | + data() { | |
54 | + return { | |
55 | + // icon용 svg path | |
56 | + setPath: mdiCog, | |
57 | + delPath: mdiTrashCanOutline, | |
58 | + | |
59 | + matchCnt: 0, | |
60 | + }; | |
61 | + }, | |
62 | + | |
63 | + setup() { | |
64 | + return { | |
65 | + Position, | |
66 | + }; | |
67 | + }, | |
68 | + | |
69 | + mounted() {}, | |
70 | + | |
71 | + watch: { | |
72 | + "node.jobItm.itemList": { | |
73 | + deep: true, | |
74 | + handler(value) { | |
75 | + this.matchCnt = 0; | |
76 | + for (let item of value) { | |
77 | + if (!this.$isEmpty(item.targetColumnNm)) { | |
78 | + this.matchCnt++; | |
79 | + } | |
80 | + } | |
81 | + }, | |
82 | + }, | |
83 | + }, | |
84 | + | |
85 | + methods: {}, | |
86 | +}; | |
87 | +</script> | |
88 | + | |
89 | +<style scoped> | |
90 | +.node { | |
91 | + background: #ffffff; | |
92 | + border: 1px solid #dbe3fb; | |
93 | + border-radius: 10px; | |
94 | + width: 300px; | |
95 | + box-shadow: 0 2px 5px rgba(237, 240, 255, 0.8); | |
96 | +} | |
97 | + | |
98 | +.node-header { | |
99 | + padding: 10px 15px; | |
100 | + background-color: #edf0ff; | |
101 | + border-bottom: 1px solid #dbe3fb; | |
102 | +} | |
103 | + | |
104 | +.node-header button { | |
105 | + width: 20px; | |
106 | + height: 20px; | |
107 | +} | |
108 | + | |
109 | +.node-header button img { | |
110 | + width: 100%; | |
111 | + height: 100%; | |
112 | +} | |
113 | + | |
114 | +.node-body { | |
115 | + padding: 15px; | |
116 | + border-radius: 5px; | |
117 | +} | |
118 | +</style>(No newline at end of file) |
+++ client/views/component/scheduleComponent/nodes/ReadNode.vue
... | ... | @@ -0,0 +1,93 @@ |
1 | +<template> | |
2 | + <div class="node"> | |
3 | + <div class="node-header flex justify-between align-center"> | |
4 | + <p class="box-title">{{ node.label }}</p> | |
5 | + <div> | |
6 | + <button @click="$emit('onSetup', node.id)"> | |
7 | + <svg-icon | |
8 | + type="mdi" | |
9 | + :width="20" | |
10 | + :height="20" | |
11 | + :path="setPath" | |
12 | + ></svg-icon> | |
13 | + </button> | |
14 | + <button @click="$emit('onRemove', node.id)"> | |
15 | + <svg-icon | |
16 | + type="mdi" | |
17 | + :width="20" | |
18 | + :height="20" | |
19 | + :path="delPath" | |
20 | + ></svg-icon> | |
21 | + </button> | |
22 | + </div> | |
23 | + </div> | |
24 | + <div class="node-body flex justify-center align-center"> | |
25 | + <p style="font-size: 1.5rem"> | |
26 | + {{ node.isSetup ? "노드 설정 완료" : "노드 미설정" }} | |
27 | + </p> | |
28 | + </div> | |
29 | + <!-- 핸들 --> | |
30 | + <Handle type="source" :position="Position.Right" /> | |
31 | + <Handle type="target" :position="Position.Left" /> | |
32 | + </div> | |
33 | +</template> | |
34 | + | |
35 | +<script> | |
36 | +// icon용 svg import | |
37 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
38 | +import { mdiCog, mdiTrashCanOutline } from "@mdi/js"; | |
39 | + | |
40 | +import { Handle, Position } from "@vue-flow/core"; | |
41 | + | |
42 | +export default { | |
43 | + components: { | |
44 | + SvgIcon, | |
45 | + Handle, | |
46 | + }, | |
47 | + | |
48 | + props: { | |
49 | + node: Object, | |
50 | + }, | |
51 | + | |
52 | + data() { | |
53 | + return { | |
54 | + // icon용 svg path | |
55 | + setPath: mdiCog, | |
56 | + delPath: mdiTrashCanOutline, | |
57 | + }; | |
58 | + }, | |
59 | + | |
60 | + setup() { | |
61 | + return { Position }; | |
62 | + }, | |
63 | + | |
64 | + mounted() {}, | |
65 | + | |
66 | + methods: {}, | |
67 | +}; | |
68 | +</script> | |
69 | + | |
70 | +<style scoped> | |
71 | +.node { | |
72 | + background: #ffffff; | |
73 | + border: 1px solid #dbe3fb; | |
74 | + border-radius: 10px; | |
75 | + width: 300px; | |
76 | + box-shadow: 0 2px 5px rgba(237, 240, 255, 0.8); | |
77 | +} | |
78 | + | |
79 | +.node-header { | |
80 | + padding: 10px 15px; | |
81 | + background-color: #edf0ff; | |
82 | + border-bottom: 1px solid #dbe3fb; | |
83 | +} | |
84 | + | |
85 | +.node-header .box-title { | |
86 | + color: #213f99; | |
87 | +} | |
88 | + | |
89 | +.node-body { | |
90 | + padding: 15px; | |
91 | + border-radius: 5px; | |
92 | +} | |
93 | +</style>(No newline at end of file) |
+++ client/views/component/scheduleComponent/nodes/StartNode.vue
... | ... | @@ -0,0 +1,118 @@ |
1 | +<template> | |
2 | + <div class="node"> | |
3 | + <div class="node-header flex justify-between align-center"> | |
4 | + <p class="box-title">{{ node.label }}</p> | |
5 | + <div></div> | |
6 | + </div> | |
7 | + <div class="node-body flex justify-center align-center"> | |
8 | + <select class="full-select" v-model="newType" @change="onChange"> | |
9 | + <option value="">스케줄 정보를 선택해주세요.</option> | |
10 | + <option | |
11 | + v-for="(item, idx) of nodeItemList" | |
12 | + :key="idx" | |
13 | + :value="item.cmmnCode" | |
14 | + > | |
15 | + {{ item.codeNm }} | |
16 | + </option> | |
17 | + </select> | |
18 | + </div> | |
19 | + </div> | |
20 | + <Handle type="source" :position="Position.Right" /> | |
21 | +</template> | |
22 | + | |
23 | +<script> | |
24 | +import { Handle, Position } from "@vue-flow/core"; | |
25 | + | |
26 | +export default { | |
27 | + name: "StartNode", | |
28 | + | |
29 | + components: { | |
30 | + Handle, | |
31 | + }, | |
32 | + | |
33 | + props: { | |
34 | + node: Object, | |
35 | + schdulType: String, | |
36 | + }, | |
37 | + | |
38 | + data() { | |
39 | + return { | |
40 | + oldType: "", | |
41 | + newType: "", | |
42 | + nodeItemList: [], | |
43 | + }; | |
44 | + }, | |
45 | + | |
46 | + setup() { | |
47 | + return { Position }; | |
48 | + }, | |
49 | + | |
50 | + mounted() { | |
51 | + if (!this.$isEmpty(this.schdulType)) { | |
52 | + this.oldType = this.schdulType; | |
53 | + this.newType = this.schdulType; | |
54 | + } | |
55 | + | |
56 | + this.fnSelectTypeList(); // 노드 아이템 조회 | |
57 | + }, | |
58 | + | |
59 | + methods: { | |
60 | + // 노드 아이템 조회 | |
61 | + async fnSelectTypeList() { | |
62 | + this.nodeItemList = await this.$getCommonCode("NODE_ITEM"); | |
63 | + }, | |
64 | + | |
65 | + // 변경 감지 | |
66 | + onChange() { | |
67 | + if (!this.$isEmpty(this.oldType) && this.oldType != this.newType) { | |
68 | + let isChecked = confirm( | |
69 | + "값 변경 시, 기존에 작성한 내용이 삭제됩니다. 그래도 실행하시겠습니까?" | |
70 | + ); | |
71 | + | |
72 | + if (!isChecked) { | |
73 | + this.newType = this.oldType; | |
74 | + return; | |
75 | + } | |
76 | + } | |
77 | + | |
78 | + this.oldType = this.newType; | |
79 | + this.$emit("updateType", this.newType); | |
80 | + }, | |
81 | + }, | |
82 | +}; | |
83 | +</script> | |
84 | + | |
85 | +<style scoped> | |
86 | +.node { | |
87 | + background: #ffffff; | |
88 | + border: 1px solid #dbe3fb; | |
89 | + border-radius: 10px; | |
90 | + width: 300px; | |
91 | + box-shadow: 0 2px 5px rgba(237, 240, 255, 0.8); | |
92 | +} | |
93 | + | |
94 | +.node-header { | |
95 | + padding: 10px 15px; | |
96 | + background-color: #edf0ff; | |
97 | + border-bottom: 1px solid #dbe3fb; | |
98 | +} | |
99 | + | |
100 | +.node-header .box-title { | |
101 | + color: #213f99; | |
102 | +} | |
103 | + | |
104 | +.node-header button { | |
105 | + width: 20px; | |
106 | + height: 20px; | |
107 | +} | |
108 | + | |
109 | +.node-header button img { | |
110 | + width: 100%; | |
111 | + height: 100%; | |
112 | +} | |
113 | + | |
114 | +.node-body { | |
115 | + padding: 15px; | |
116 | + border-radius: 5px; | |
117 | +} | |
118 | +</style>(No newline at end of file) |
--- client/views/pages/AppRouter.js
+++ client/views/pages/AppRouter.js
... | ... | @@ -2,31 +2,34 @@ |
2 | 2 |
import store from "./AppStore"; |
3 | 3 |
import { createWebHistory, createRouter } from "vue-router"; |
4 | 4 |
// #페이지 |
5 |
-// 통합관리 |
|
5 |
+// 데이터관리 |
|
6 |
+import FileManagement from "../pages/data/FileManagement.vue"; // 파일 관리 |
|
7 |
+import DataManagement from "../pages/data/DataPostManagement.vue"; // 데이터 관리 |
|
8 |
+// 작업관리 |
|
9 |
+import ScheduleManagement from "../pages/schedule/ScheduleManagement.vue"; // 작업 스케줄 관리 |
|
10 |
+// 회원관리 |
|
6 | 11 |
import UserManagement from "../pages/integrated/UserManagement.vue"; // 사용자 관리 |
7 | 12 |
import AdminManagement from "../pages/integrated/AdminManagement.vue"; // 관리자 관리 |
8 | 13 |
import DepartmentManagement from "../pages/integrated/DepartmentManagement.vue"; // 부서 관리 |
9 | 14 |
import DBConnectionList from "../pages/dbConnection/DBConnectionList.vue"; // 연계정보 관리 |
15 |
+// 정보관리 |
|
16 |
+import myPage from "../pages/user/myPage.vue"; // 내 정보 관리 |
|
17 |
+import myPagePwd from "../pages/user/myPagePwd.vue"; // 비밀번호 변경 |
|
10 | 18 |
|
19 |
+// 미분류 |
|
11 | 20 |
import DBConnectionDetail from "../pages/dbConnection/DBConnectionDetail.vue"; |
12 |
-import FileManagement from "../pages/data/FileManagement.vue"; |
|
13 | 21 |
import HostManagement from "../pages/data/HostManagement.vue"; |
14 |
-import DataManagement from "../pages/data/DataPostManagement.vue"; |
|
15 | 22 |
import InsertDataPost from "../pages/data/InsertDataPost.vue"; |
16 | 23 |
import DataPostDetail from "../pages/data/DataPostDetail.vue"; |
17 | 24 |
import DataEditView from "../pages/data/DataEditView.vue"; |
18 | 25 |
import TermManagement from "../pages/meta/TermManagement.vue"; |
19 | 26 |
import DataMetaManagement from "../pages/meta/DataMetaManagement.vue"; |
20 |
-import ScheduleManagement from "../pages/schedule/ScheduleManagement.vue"; |
|
21 |
-import ScheduleLogManagement from "../pages/schedule/ScheduleLogManagement.vue"; |
|
22 | 27 |
import Push from "../pages/push/Push.vue"; |
23 | 28 |
import InsertDBConnection from "../pages/dbConnection/InsertDBConnection.vue"; |
24 | 29 |
import CustomSelectList from "../pages/custom/CustomSelectList.vue"; |
25 | 30 |
import CustomSelectOne from "../pages/custom/CustomSelectOne.vue"; |
26 | 31 |
import CustomInsert from "../pages/custom/CustomInsert.vue"; |
27 | 32 |
import CustomInsertDev from "../pages/custom/CustomInsertDev.vue"; |
28 |
-import myPage from "../pages/user/myPage.vue"; |
|
29 |
-import myPagePwd from "../pages/user/myPagePwd.vue"; |
|
30 | 33 |
import OpenApiList from "../pages/openapi/OpenApiList.vue"; |
31 | 34 |
import OpenApiInsert from "../pages/openapi/OpenApiInsert.vue"; |
32 | 35 |
import OpenApiSelectListOne from "../pages/openapi/OpenApiSelectListOne.vue"; |
... | ... | @@ -53,7 +56,9 @@ |
53 | 56 |
|
54 | 57 |
|
55 | 58 |
const routes = [ |
56 |
- // 통합관리 |
|
59 |
+ // 작업관리 |
|
60 |
+ { path: "/scheduleManagement.page", name: "ScheduleManagement", component: ScheduleManagement }, // 작업 스케줄 관리 |
|
61 |
+ // 회원관리 |
|
57 | 62 |
{ path: "/user.page", name: "User", component: UserManagement }, // 사용자 관리 |
58 | 63 |
{ path: "/adminManagement.page", name: "AdminManagement", component: AdminManagement }, // 관리자 관리 |
59 | 64 |
{ path: "/departmentManagement.page", name: "Department", component: DepartmentManagement }, // 부서 관리 |
... | ... | @@ -67,8 +72,6 @@ |
67 | 72 |
{ path: "/DataEditView.page", name: "DataEditView", component: DataEditView }, |
68 | 73 |
{ path: "/termManagement.page", name: "TermManagement", component: TermManagement }, |
69 | 74 |
{ path: "/dataMetaManagement.page", name: "DataMetaManagement", component: DataMetaManagement }, |
70 |
- { path: "/scheduleManagement.page", name: "ScheduleManagement", component: ScheduleManagement }, |
|
71 |
- { path: "/scheduleLogManagement.page", name: "ScheduleLogManagement", component: ScheduleLogManagement }, |
|
72 | 75 |
{ path: "/push.page", name: "Push", component: Push }, |
73 | 76 |
{ path: "/insertDBConnection.page", name: "InsertDBConnection", component: InsertDBConnection }, |
74 | 77 |
{ path: "/DBConnectionDetail.page", name: "DBConnectionDetail", component: DBConnectionDetail }, |
--- client/views/pages/data/datapost/DataPostManagerMain.vue
+++ client/views/pages/data/datapost/DataPostManagerMain.vue
... | ... | @@ -18,11 +18,25 @@ |
18 | 18 |
<div class="flex justify-end"> |
19 | 19 |
<div class="search-bar"> |
20 | 20 |
<div class="flex justify-end align-center" style="gap: 5px"> |
21 |
- <input type="date" name="start-date" id="start-date" class="square-date" |
|
22 |
- :class="{ 'date-placeholder': false }" data-placeholder="날짜 선택" v-model="search_date.value" /> |
|
21 |
+ <input |
|
22 |
+ type="date" |
|
23 |
+ name="start-date" |
|
24 |
+ id="start-date" |
|
25 |
+ class="square-date" |
|
26 |
+ :class="{ 'date-placeholder': false }" |
|
27 |
+ data-placeholder="날짜 선택" |
|
28 |
+ v-model="search_date.value" |
|
29 |
+ /> |
|
23 | 30 |
<span>~</span> |
24 |
- <input type="date" name="end-date" id="end-date" class="square-date" |
|
25 |
- :class="{ 'date-placeholder': false }" data-placeholder="날짜 선택" v-model="search_date.value2" /> |
|
31 |
+ <input |
|
32 |
+ type="date" |
|
33 |
+ name="end-date" |
|
34 |
+ id="end-date" |
|
35 |
+ class="square-date" |
|
36 |
+ :class="{ 'date-placeholder': false }" |
|
37 |
+ data-placeholder="날짜 선택" |
|
38 |
+ v-model="search_date.value2" |
|
39 |
+ /> |
|
26 | 40 |
<select class="square-select" v-model="search_data1.value"> |
27 | 41 |
<option :value="null">공개여부</option> |
28 | 42 |
<option :value="true">공개</option> |
... | ... | @@ -30,29 +44,54 @@ |
30 | 44 |
</select> |
31 | 45 |
<select class="square-select" v-model="search_data2.value"> |
32 | 46 |
<option :value="null">카테고리</option> |
33 |
- <option v-for="(item, indx) in categoryList" :key="indx" :value="item.cmmnCode">{{ |
|
34 |
- item.codeNm }}</option> |
|
47 |
+ <option |
|
48 |
+ v-for="(item, indx) in categoryList" |
|
49 |
+ :key="indx" |
|
50 |
+ :value="item.cmmnCode" |
|
51 |
+ > |
|
52 |
+ {{ item.codeNm }} |
|
53 |
+ </option> |
|
35 | 54 |
</select> |
36 |
- <select name="" id="searchItm1" class="square-select" v-model="search_data3.key"> |
|
55 |
+ <select |
|
56 |
+ name="" |
|
57 |
+ id="searchItm1" |
|
58 |
+ class="square-select" |
|
59 |
+ v-model="search_data3.key" |
|
60 |
+ > |
|
37 | 61 |
<option :value="null">검색조건</option> |
38 | 62 |
<option value="post_sj">제목</option> |
39 | 63 |
<option value="post_dc">내용</option> |
40 | 64 |
</select> |
41 | 65 |
<div class="flex justify-end align-center no-gutter"> |
42 |
- <input type="text" class="square-input flex90" placeholder="Search" v-model="search_data3.value" |
|
43 |
- v-on:keyup.enter="searchData()" /> |
|
44 |
- <button class="square-button blue-btn flex10" @click="searchData()"> |
|
45 |
- <svg-icon type="mdi" :path="this.$getIconPath()" class="square-icon"></svg-icon> |
|
66 |
+ <input |
|
67 |
+ type="text" |
|
68 |
+ class="square-input flex90" |
|
69 |
+ placeholder="Search" |
|
70 |
+ v-model="search_data3.value" |
|
71 |
+ v-on:keyup.enter="searchData()" |
|
72 |
+ /> |
|
73 |
+ <button |
|
74 |
+ class="square-button blue-btn flex10" |
|
75 |
+ @click="searchData()" |
|
76 |
+ > |
|
77 |
+ <svg-icon |
|
78 |
+ type="mdi" |
|
79 |
+ :path="this.$getIconPath()" |
|
80 |
+ class="square-icon" |
|
81 |
+ ></svg-icon> |
|
46 | 82 |
</button> |
47 | 83 |
</div> |
48 | 84 |
</div> |
49 | 85 |
</div> |
50 | 86 |
</div> |
51 |
- <div class="content-zone" style="overflow: auto;"> |
|
87 |
+ <div class="content-zone" style="overflow: auto"> |
|
52 | 88 |
<div class="table-zone"> |
53 | 89 |
<div class="list-info flex justify-between align-center"> |
54 | 90 |
<div class="count-zone"> |
55 |
- <p>총<span>{{ search.totalRows }}</span>건</p> |
|
91 |
+ <p> |
|
92 |
+ 총<span>{{ search.totalRows }}</span |
|
93 |
+ >건 |
|
94 |
+ </p> |
|
56 | 95 |
</div> |
57 | 96 |
<div class="cunt-selectZone"> |
58 | 97 |
<select name="" id=""> |
... | ... | @@ -83,9 +122,18 @@ |
83 | 122 |
</thead> |
84 | 123 |
<tbody> |
85 | 124 |
<template v-if="dataPostList.length > 0"> |
86 |
- <tr v-for="(item, indx) in dataPostList" :key="indx" @click="selectPost(item)"> |
|
87 |
- <td>{{ search.totalRows - indx - (search.currentPage - 1) * search.perPage |
|
88 |
- }}</td> |
|
125 |
+ <tr |
|
126 |
+ v-for="(item, indx) in dataPostList" |
|
127 |
+ :key="indx" |
|
128 |
+ @click="selectPost(item)" |
|
129 |
+ > |
|
130 |
+ <td> |
|
131 |
+ {{ |
|
132 |
+ search.totalRows - |
|
133 |
+ indx - |
|
134 |
+ (search.currentPage - 1) * search.perPage |
|
135 |
+ }} |
|
136 |
+ </td> |
|
89 | 137 |
<td>{{ item.post_sj }}</td> |
90 | 138 |
<td>{{ item.user_nm }}</td> |
91 | 139 |
<td>{{ item.dept_nm }}</td> |
... | ... | @@ -98,12 +146,19 @@ |
98 | 146 |
</tr> |
99 | 147 |
</tbody> |
100 | 148 |
</table> |
101 |
- <PaginationButton v-model:currentPage="search.currentPage" :perPage="search.perPage" |
|
102 |
- :totalCount="search.totalRows" :maxRange="5" :click="searchData" /> |
|
149 |
+ <PaginationButton |
|
150 |
+ v-model:currentPage="search.currentPage" |
|
151 |
+ :perPage="search.perPage" |
|
152 |
+ :totalCount="search.totalRows" |
|
153 |
+ :maxRange="5" |
|
154 |
+ :click="searchData" |
|
155 |
+ /> |
|
103 | 156 |
</div> |
104 | 157 |
</div> |
105 | 158 |
<div class="flex justify-end"> |
106 |
- <button class="blue-btn small-btn" @click="createDataPost">데이터 등록</button> |
|
159 |
+ <button class="blue-btn small-btn" @click="createDataPost"> |
|
160 |
+ 데이터 등록 |
|
161 |
+ </button> |
|
107 | 162 |
</div> |
108 | 163 |
</div> |
109 | 164 |
</div> |
... | ... | @@ -116,9 +171,12 @@ |
116 | 171 |
import PageNavigation from "../../../component/PageNavigation.vue"; |
117 | 172 |
import SvgIcon from "@jamescoyle/vue-icon"; |
118 | 173 |
import PaginationButton from "../../../component/PaginationButton.vue"; |
119 |
-import moment from 'moment'; |
|
174 |
+import moment from "moment"; |
|
120 | 175 |
|
121 | 176 |
export default { |
177 |
+ props: { |
|
178 |
+ type: String, |
|
179 |
+ }, |
|
122 | 180 |
data() { |
123 | 181 |
return { |
124 | 182 |
dataPostList: [], |
... | ... | @@ -152,6 +210,10 @@ |
152 | 210 |
}, |
153 | 211 |
|
154 | 212 |
selectPost: function (item) { |
213 |
+ if (this.type == "modal") { |
|
214 |
+ this.$emit("onSelected", item.dataset_post_id); |
|
215 |
+ return; |
|
216 |
+ } |
|
155 | 217 |
this.$router.push({ |
156 | 218 |
name: "DataPostDetail", |
157 | 219 |
query: { datapost: item.dataset_post_id }, |
... | ... | @@ -164,8 +226,8 @@ |
164 | 226 |
let check = false; |
165 | 227 |
let authList = vm.$store.state.loginUser.user_auth; |
166 | 228 |
for (let auth of authList) { |
167 |
- if (auth == 'ROLE_ADMIN') { |
|
168 |
- check = true |
|
229 |
+ if (auth == "ROLE_ADMIN") { |
|
230 |
+ check = true; |
|
169 | 231 |
break; |
170 | 232 |
} |
171 | 233 |
} |
... | ... | @@ -176,7 +238,7 @@ |
176 | 238 |
type: "string", |
177 | 239 |
value: check + "/" + vm.$store.state.loginUser.user_id, |
178 | 240 |
value2: vm.$store.state.loginUser.dept_code, |
179 |
- }) |
|
241 |
+ }); |
|
180 | 242 |
} |
181 | 243 |
|
182 | 244 |
axios({ |
... | ... | @@ -202,11 +264,13 @@ |
202 | 264 |
}); |
203 | 265 |
}, |
204 | 266 |
setDateRange(dataList) { |
205 |
- const dates = dataList.map(item => item.creat_dt).sort(); |
|
206 |
- |
|
267 |
+ const dates = dataList.map((item) => item.creat_dt).sort(); |
|
268 |
+ |
|
207 | 269 |
if (dates.length > 0) { |
208 |
- this.search_date.value = moment(dates[0]).format('YYYY-MM-DD'); |
|
209 |
- this.search_date.value2 = moment(dates[dates.length - 1]).format('YYYY-MM-DD'); |
|
270 |
+ this.search_date.value = moment(dates[0]).format("YYYY-MM-DD"); |
|
271 |
+ this.search_date.value2 = moment(dates[dates.length - 1]).format( |
|
272 |
+ "YYYY-MM-DD" |
|
273 |
+ ); |
|
210 | 274 |
} |
211 | 275 |
}, |
212 | 276 |
// 초기화 |
--- client/views/pages/schedule/CustomNode.vue
... | ... | @@ -1,70 +0,0 @@ |
1 | -<template> | |
2 | - <div class="node"> | |
3 | - <div class="header"> | |
4 | - <button @click="$emit('openSetup', node.id)"> | |
5 | - <img src="../../../resources/img/option.png" alt="옵션" /> | |
6 | - </button> | |
7 | - <button | |
8 | - v-if="node.label !== 'DATASET_UPDATE'" | |
9 | - @click="$emit('removeNode', node.id)" | |
10 | - > | |
11 | - <img src="../../../resources/img/delete.png" alt="삭제" /> | |
12 | - </button> | |
13 | - </div> | |
14 | - <hr /> | |
15 | - <div class="content"> | |
16 | - <h3>{{ node.label }}</h3> | |
17 | - </div> | |
18 | - <Handle | |
19 | - v-if="node.label !== 'DATASET_UPDATE'" | |
20 | - type="source" | |
21 | - position="right" | |
22 | - /> | |
23 | - <Handle | |
24 | - v-if="node.label === 'DATASET_UPDATE' || node.label !== 'DATASET_UPDATE'" | |
25 | - type="target" | |
26 | - position="left" | |
27 | - /> | |
28 | - </div> | |
29 | -</template> | |
30 | - | |
31 | -<script setup> | |
32 | -import { Handle, Position } from "@vue-flow/core"; | |
33 | -</script> | |
34 | - | |
35 | -<script> | |
36 | -export default { | |
37 | - components: { Handle, Position }, | |
38 | - props: { | |
39 | - node: Object, | |
40 | - }, | |
41 | -}; | |
42 | -</script> | |
43 | - | |
44 | -<style scoped> | |
45 | -.node { | |
46 | - border: 1px solid #ddd; | |
47 | - border-radius: 5px; | |
48 | - padding: 20px; | |
49 | - background-color: #f9f9f9; | |
50 | - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15); | |
51 | - width: 300px; | |
52 | - height: 150px; | |
53 | -} | |
54 | -.header { | |
55 | - display: flex; | |
56 | - justify-content: flex-end; | |
57 | -} | |
58 | -.header button { | |
59 | - width: 20px; | |
60 | - height: 20px; | |
61 | - margin: 0 0 5px 10px; | |
62 | -} | |
63 | -.header button img { | |
64 | - width: 100%; | |
65 | - height: 100%; | |
66 | -} | |
67 | -.content { | |
68 | - margin-top: 20px; | |
69 | -} | |
70 | -</style>(No newline at end of file) |
--- client/views/pages/schedule/ScheduleLogManagement.vue
... | ... | @@ -1,354 +0,0 @@ |
1 | -<template> | |
2 | - <div class="container"> | |
3 | - <div class="content"> | |
4 | - <div class="content-titleZone flex justify-between align-center"> | |
5 | - <p class="box-title">다이어그램 목록</p> | |
6 | - <div class="search-bar"> | |
7 | - <div class="flex justify-end align-center"> | |
8 | - <input | |
9 | - type="date" | |
10 | - name="start-date" | |
11 | - id="start-date" | |
12 | - class="square-date" | |
13 | - :class="{ 'date-placeholder': false }" | |
14 | - data-placeholder="날짜 선택" | |
15 | - v-model="search_date.value" | |
16 | - @change="searchData()" | |
17 | - /> | |
18 | - <span class="coupler">~</span> | |
19 | - <input | |
20 | - type="date" | |
21 | - name="end-date" | |
22 | - id="end-date" | |
23 | - class="square-date ml5" | |
24 | - :class="{ 'date-placeholder': false }" | |
25 | - data-placeholder="날짜 선택" | |
26 | - v-model="search_date.value2" | |
27 | - @change="searchData()" | |
28 | - /> | |
29 | - <select | |
30 | - class="square-select ml5" | |
31 | - v-model="search_data1.value" | |
32 | - @change="searchData()" | |
33 | - > | |
34 | - <option :value="null">상태 전체</option> | |
35 | - <option value="success">성공</option> | |
36 | - <option value="fail">실패</option> | |
37 | - </select> | |
38 | - </div> | |
39 | - </div> | |
40 | - </div> | |
41 | - <div class="table-zone"> | |
42 | - <table class="list-table"> | |
43 | - <thead> | |
44 | - <tr> | |
45 | - <th>번호</th> | |
46 | - <th>총 노드수</th> | |
47 | - <th>실패 노드수</th> | |
48 | - <th>실패 노드명</th> | |
49 | - <th>실행일자</th> | |
50 | - <th>상태</th> | |
51 | - </tr> | |
52 | - </thead> | |
53 | - <tbody> | |
54 | - <tr | |
55 | - v-for="(rowdata, dataIndex) in logdata" | |
56 | - :key="dataIndex" | |
57 | - @click="handleRowClick(rowdata.log_id)" | |
58 | - > | |
59 | - <td>{{ dataIndex + 1 }}</td> | |
60 | - <td>{{ rowdata.total_node_count }}</td> | |
61 | - <td>{{ rowdata.fail_node_count }}</td> | |
62 | - <td>{{ rowdata.fail_node_name }}</td> | |
63 | - <td>{{ $getFullTime(rowdata.operation_date) }}</td> | |
64 | - <td> | |
65 | - <p | |
66 | - v-if="rowdata.state_info === 'success'" | |
67 | - class="flex justify-center align-center state green icon-area" | |
68 | - > | |
69 | - <svg-icon | |
70 | - type="mdi" | |
71 | - :width="21" | |
72 | - :height="21" | |
73 | - :path="checkPath" | |
74 | - ></svg-icon> | |
75 | - <span>성공</span> | |
76 | - </p> | |
77 | - <p | |
78 | - v-else-if="rowdata.state_info === 'fail'" | |
79 | - class="flex justify-center align-center state red" | |
80 | - > | |
81 | - <svg-icon | |
82 | - type="mdi" | |
83 | - :width="21" | |
84 | - :height="21" | |
85 | - :path="xPath" | |
86 | - ></svg-icon> | |
87 | - <span>실패</span> | |
88 | - </p> | |
89 | - </td> | |
90 | - </tr> | |
91 | - </tbody> | |
92 | - </table> | |
93 | - </div> | |
94 | - <PaginationButton | |
95 | - v-model:currentPage="search.currentPage" | |
96 | - :perPage="search.perPage" | |
97 | - :totalCount="search.totalRows" | |
98 | - :maxRange="5" | |
99 | - :click="searchData" | |
100 | - /> | |
101 | - </div> | |
102 | - </div> | |
103 | - <!-- 모달 --> | |
104 | - <div v-if="isModalOpen" class="modal-wrapper"> | |
105 | - <div class="modal-container list-modal"> | |
106 | - <div class="modal-title"> | |
107 | - <div class="flex justify-between align-center"> | |
108 | - <h2 class="main-title flex80">로그 상세보기</h2> | |
109 | - <button class="close-btn" @click="closeModal"> | |
110 | - <svg-icon | |
111 | - type="mdi" | |
112 | - :width="20" | |
113 | - :height="20" | |
114 | - :path="closePath" | |
115 | - ></svg-icon> | |
116 | - </button> | |
117 | - </div> | |
118 | - </div> | |
119 | - <div class="modal-content-monthly" v-show="modalType == 'form-modal'"> | |
120 | - <div class="flex justify-start align-center"> | |
121 | - <div class="cunt-selectZone"> | |
122 | - <p class="box-title"> | |
123 | - 로그분류 | |
124 | - <select | |
125 | - v-model="searchdetail_data.value" | |
126 | - @change="searchDetailData" | |
127 | - > | |
128 | - <option :value="null">상태 전체</option> | |
129 | - <option value="success">성공</option> | |
130 | - <option value="fail">실패</option> | |
131 | - </select> | |
132 | - </p> | |
133 | - </div> | |
134 | - </div> | |
135 | - <table class="list-table blue"> | |
136 | - <thead> | |
137 | - <tr> | |
138 | - <th>번호</th> | |
139 | - <th>작동일시</th> | |
140 | - <th>작동완료일시</th> | |
141 | - <th>노드명</th> | |
142 | - <th>호스트명</th> | |
143 | - <th>로그분류</th> | |
144 | - <th>로그메세지</th> | |
145 | - </tr> | |
146 | - </thead> | |
147 | - <tbody> | |
148 | - <tr v-for="(rowdata, dataIndex) in detaillogdata" :key="dataIndex"> | |
149 | - <td>{{ dataIndex + 1 }}</td> | |
150 | - <td>{{ $getFullTime(rowdata.operation_date) }}</td> | |
151 | - <td>{{ $getFullTime(rowdata.operation_success_date) }}</td> | |
152 | - <td>{{ rowdata.node_name }}</td> | |
153 | - <td>{{ rowdata.creat_id }}</td> | |
154 | - <td>{{ rowdata.log_category }}</td> | |
155 | - <td class="text-over" title="-">{{ rowdata.log_message }}</td> | |
156 | - </tr> | |
157 | - </tbody> | |
158 | - </table> | |
159 | - <PaginationButton | |
160 | - v-model:currentPage="searchdetail.currentPage" | |
161 | - :perPage="searchdetail.perPage" | |
162 | - :totalCount="searchdetail.totalRows" | |
163 | - :maxRange="5" | |
164 | - :click="detaillogSelectListAll" | |
165 | - /> | |
166 | - </div> | |
167 | - <div class="modal-end flex justify-end"> | |
168 | - <button | |
169 | - v-show="modalType === 'form-modal'" | |
170 | - class="blue-btn small-btn" | |
171 | - @click="closeModal" | |
172 | - > | |
173 | - 확인 | |
174 | - </button> | |
175 | - </div> | |
176 | - </div> | |
177 | - </div> | |
178 | -</template> | |
179 | - | |
180 | -<script> | |
181 | -import PaginationButton from "../../component/PaginationButton.vue"; | |
182 | -import SvgIcon from "@jamescoyle/vue-icon"; | |
183 | -import { mdiMagnify } from "@mdi/js"; | |
184 | -import { mdiClose } from "@mdi/js"; | |
185 | -import { mdiCheckCircle, mdiCloseCircle } from "@mdi/js"; | |
186 | -import DefaultSearchBar from "../../template/templateElement/DefaultSearchBar.vue"; | |
187 | -import { useRoute } from "vue-router"; | |
188 | -import axios from "axios"; | |
189 | -export default { | |
190 | - components: { | |
191 | - PaginationButton: PaginationButton, | |
192 | - SvgIcon: SvgIcon, | |
193 | - DefaultSearchBar: DefaultSearchBar, | |
194 | - }, | |
195 | - props: { | |
196 | - diagram_id: { | |
197 | - type: String, // 또는 String, 실제 타입에 맞게 설정 | |
198 | - required: true, | |
199 | - }, | |
200 | - }, | |
201 | - data() { | |
202 | - return { | |
203 | - // 검색 | |
204 | - search: this.$getDefaultSerchVO(), | |
205 | - search_date: this.$getDefaultSerchItem("operation_date", "dates"), | |
206 | - search_data1: this.$getDefaultSerchItem("state_info", "string"), | |
207 | - search_data2: { | |
208 | - key: "diagram_id", | |
209 | - value: this.diagram_id, | |
210 | - }, | |
211 | - | |
212 | - //상세보기 검색 | |
213 | - searchdetail: this.$getDefaultSerchVO(), | |
214 | - searchdetail_data: this.$getDefaultSerchItem("log_category", "string"), | |
215 | - searchdetail_data1: { | |
216 | - key: null, | |
217 | - value: null, | |
218 | - }, | |
219 | - | |
220 | - //검색 데이터 | |
221 | - searchPath: mdiMagnify, | |
222 | - startDate: true, | |
223 | - endDate: true, | |
224 | - | |
225 | - closePath: mdiClose, | |
226 | - isModalOpen: false, | |
227 | - modalType: "form-modal", | |
228 | - checkPath: mdiCheckCircle, | |
229 | - xPath: mdiCloseCircle, | |
230 | - logdata: [], | |
231 | - detaillogdata: [], | |
232 | - logId: null, | |
233 | - diagram_id: this.diagram_id, | |
234 | - | |
235 | - //현재 라우터의 정보 | |
236 | - route: useRoute(), | |
237 | - // 페이지 경로 목록 | |
238 | - pageList: [ | |
239 | - { path: "/", name: "대시보드" }, | |
240 | - { path: "/fileManagement.page", name: "파일 관리" }, | |
241 | - { path: "/hostManagement.page", name: "호스트 관리" }, | |
242 | - { path: "/scheduleManagement.page", name: "작업 스케줄 관리" }, | |
243 | - { path: "/scheduleLogManagement.page", name: "로그 관리" }, | |
244 | - ], | |
245 | - }; | |
246 | - }, | |
247 | - mounted() { | |
248 | - this.searchData(); | |
249 | - }, | |
250 | - watch: { | |
251 | - diagram_id(v) { | |
252 | - this.search_data2.value = v; | |
253 | - this.searchData(); | |
254 | - }, | |
255 | - }, | |
256 | - methods: { | |
257 | - searchData() { | |
258 | - const vm = this; | |
259 | - let data = vm.search; | |
260 | - data.searchObjectList = []; | |
261 | - data.searchObjectList.push(this.search_date); | |
262 | - data.searchObjectList.push(this.search_data1); | |
263 | - data.searchObjectList.push(this.search_data2); | |
264 | - axios({ | |
265 | - url: "/diagram/logSelectListAll.json", | |
266 | - method: "post", | |
267 | - headers: { "Content-Type": "application/json; charset=UTF-8" }, | |
268 | - data: data, | |
269 | - }) | |
270 | - .then(function (response) { | |
271 | - vm.search.currentPage = response.data.resultData.searchVO.currentPage; | |
272 | - vm.search.order = response.data.resultData.searchVO.order; | |
273 | - vm.search.orderASC = response.data.resultData.searchVO.orderASC; | |
274 | - vm.search.perPage = response.data.resultData.searchVO.perPage; | |
275 | - vm.search.searchObjectList = []; | |
276 | - vm.search.totalRows = response.data.resultData.searchVO.totalRows; | |
277 | - vm.logdata = response.data.resultData.logList; | |
278 | - }) | |
279 | - .catch(function (error) { | |
280 | - vm.$showAlert( | |
281 | - "오류", | |
282 | - "처리 중 오류가 발생했습니다. 관리자에게 문의바랍니다." | |
283 | - ); | |
284 | - }); | |
285 | - }, | |
286 | - searchDetailData() { | |
287 | - const vm = this; | |
288 | - let datadetail = vm.searchdetail; | |
289 | - datadetail.searchObjectList = []; | |
290 | - datadetail.searchObjectList.push(this.searchdetail_data); | |
291 | - | |
292 | - this.detaillogSelectListAll(); | |
293 | - }, | |
294 | - openModal() { | |
295 | - this.isModalOpen = true; | |
296 | - }, | |
297 | - closeModal() { | |
298 | - this.isModalOpen = false; | |
299 | - }, | |
300 | - pathName() { | |
301 | - for (let i = 0; i < this.pageList.length; i++) { | |
302 | - if (this.route.path == this.pageList[i]["path"]) { | |
303 | - return this.pageList[i]["name"]; | |
304 | - } | |
305 | - } | |
306 | - return "대시보드"; | |
307 | - }, | |
308 | - | |
309 | - handleRowClick(logId) { | |
310 | - this.logId = logId; | |
311 | - this.detaillogSelectListAll(); | |
312 | - this.openModal(); | |
313 | - }, | |
314 | - | |
315 | - detaillogSelectListAll() { | |
316 | - const vm = this; | |
317 | - let datadetail = vm.searchdetail; | |
318 | - datadetail.searchObjectList = []; | |
319 | - datadetail.searchObjectList.push(this.searchdetail_data); | |
320 | - vm.searchdetail_data1.key = "log_id"; | |
321 | - vm.searchdetail_data1.value = vm.logId; | |
322 | - datadetail.searchObjectList.push(this.searchdetail_data1); | |
323 | - | |
324 | - axios({ | |
325 | - url: "/diagram/detaillogSelectListAll.json", | |
326 | - method: "post", | |
327 | - headers: { | |
328 | - "Content-Type": "application/json; charset=UTF-8", | |
329 | - }, | |
330 | - data: datadetail, | |
331 | - }) | |
332 | - .then(function (response) { | |
333 | - vm.searchdetail.currentPage = response.data.resultData.searchVO.currentPage; | |
334 | - vm.searchdetail.order = response.data.resultData.searchVO.order; | |
335 | - vm.searchdetail.orderASC = response.data.resultData.searchVO.orderASC; | |
336 | - vm.searchdetail.perPage = response.data.resultData.searchVO.perPage; | |
337 | - vm.searchdetail.searchObjectList = []; | |
338 | - vm.searchdetail.totalRows = response.data.resultData.searchVO.totalRows; | |
339 | - vm.detaillogdata = response.data.resultData.logList; | |
340 | - }) | |
341 | - .catch(function (error) { | |
342 | - alert("상품 주문에 오류가 발생했습니다."); | |
343 | - }); | |
344 | - }, | |
345 | - }, | |
346 | -}; | |
347 | -</script> | |
348 | - | |
349 | -<style scoped> | |
350 | -.navigate_bar { | |
351 | - padding: 10px 0px; | |
352 | - font-size: 1.3rem; | |
353 | -} | |
354 | -</style>(No newline at end of file) |
--- client/views/pages/schedule/ScheduleManagement.vue
+++ client/views/pages/schedule/ScheduleManagement.vue
... | ... | @@ -2,13 +2,14 @@ |
2 | 2 |
<div class="container"> |
3 | 3 |
<div class="page-titleZone flex justify-between align-center"> |
4 | 4 |
<p class="main-title flex80">스케줄 관리</p> |
5 |
+ <PageNavigation /> |
|
5 | 6 |
</div> |
6 | 7 |
<div class="content-wrap"> |
7 | 8 |
<div class="content content-box flex-column no-gutter"> |
8 | 9 |
<div class="row flex50"> |
9 | 10 |
<div class="flex100"> |
10 | 11 |
<div class="content-titleZone flex justify-between align-center"> |
11 |
- <p class="box-title">다이어그램 목록</p> |
|
12 |
+ <p class="box-title">스케줄 목록</p> |
|
12 | 13 |
<div class="search-bar flex justify-end align-center"> |
13 | 14 |
<input |
14 | 15 |
type="date" |
... | ... | @@ -61,89 +62,11 @@ |
61 | 62 |
</div> |
62 | 63 |
</div> |
63 | 64 |
<div class="table-zone"> |
64 |
- <table class="list-table"> |
|
65 |
- <colgroup> |
|
66 |
- <col style="width: 5%" /> |
|
67 |
- <col style="width: 5%" /> |
|
68 |
- <col style="width: 25%" /> |
|
69 |
- <col style="width: 15%" /> |
|
70 |
- <col style="width: 20%" /> |
|
71 |
- <col style="width: 20%" /> |
|
72 |
- <col style="width: 15%" /> |
|
73 |
- </colgroup> |
|
74 |
- <thead> |
|
75 |
- <tr> |
|
76 |
- <th> |
|
77 |
- <input |
|
78 |
- type="checkbox" |
|
79 |
- v-model="isChkAll" |
|
80 |
- @change="fnChkAll()" |
|
81 |
- /> |
|
82 |
- </th> |
|
83 |
- <th>번호</th> |
|
84 |
- <th>제목</th> |
|
85 |
- <th>작성자</th> |
|
86 |
- <th>반복주기</th> |
|
87 |
- <th>등록일</th> |
|
88 |
- <th>상태</th> |
|
89 |
- </tr> |
|
90 |
- </thead> |
|
91 |
- <tbody> |
|
92 |
- <template v-if="diagramList.length > 0"> |
|
93 |
- <tr |
|
94 |
- v-for="(diagram, dataIndex) in diagramList" |
|
95 |
- :key="diagram" |
|
96 |
- @click="fnDiagramDetail(diagram.diagram_id)" |
|
97 |
- > |
|
98 |
- <td> |
|
99 |
- <input |
|
100 |
- type="checkbox" |
|
101 |
- :value="diagram" |
|
102 |
- v-model="selectedDiagramList" |
|
103 |
- @click.stop="" |
|
104 |
- @change="fnChangeCheckList" |
|
105 |
- /> |
|
106 |
- </td> |
|
107 |
- <td>{{ dataIndex + 1 }}</td> |
|
108 |
- <td>{{ diagram.diagram_nm }}</td> |
|
109 |
- <td>{{ diagram.creat_id }}</td> |
|
110 |
- <td>{{ diagram.cron_chrctr }}</td> |
|
111 |
- <td>{{ $getFullTime(diagram.creat_dt) }}</td> |
|
112 |
- <td> |
|
113 |
- <div |
|
114 |
- v-if="diagram.schdul_sttus === 'run'" |
|
115 |
- class="flex justify-center align-center state green icon-area" |
|
116 |
- > |
|
117 |
- <svg-icon |
|
118 |
- type="mdi" |
|
119 |
- :width="21" |
|
120 |
- :height="21" |
|
121 |
- :path="checkPath" |
|
122 |
- class="mr5" |
|
123 |
- ></svg-icon> |
|
124 |
- <span>진행</span> |
|
125 |
- </div> |
|
126 |
- <div |
|
127 |
- v-else-if="diagram.schdul_sttus === 'stop'" |
|
128 |
- class="flex justify-center align-center state red" |
|
129 |
- > |
|
130 |
- <svg-icon |
|
131 |
- type="mdi" |
|
132 |
- :width="21" |
|
133 |
- :height="21" |
|
134 |
- :path="xPath" |
|
135 |
- class="mr5" |
|
136 |
- ></svg-icon> |
|
137 |
- <span>중단</span> |
|
138 |
- </div> |
|
139 |
- </td> |
|
140 |
- </tr> |
|
141 |
- </template> |
|
142 |
- <tr v-else> |
|
143 |
- <td colspan="7">등록된 데이터가 없습니다.</td> |
|
144 |
- </tr> |
|
145 |
- </tbody> |
|
146 |
- </table> |
|
65 |
+ <ScheduleList |
|
66 |
+ :list="scheduleList" |
|
67 |
+ :search="search" |
|
68 |
+ @fnSelectDiagram="fnDiagramDetail" |
|
69 |
+ /> |
|
147 | 70 |
</div> |
148 | 71 |
<div class="flex justify-end"> |
149 | 72 |
<button |
... | ... | @@ -162,166 +85,58 @@ |
162 | 85 |
선택 삭제 |
163 | 86 |
</button> |
164 | 87 |
</div> |
165 |
- <PaginationButton |
|
166 |
- v-model:currentPage="search.currentPage" |
|
167 |
- :perPage="search.perPage" |
|
168 |
- :totalCount="search.totalRows" |
|
169 |
- :maxRange="5" |
|
170 |
- :click="fnDiagramList" |
|
171 |
- /> |
|
172 | 88 |
</div> |
173 | 89 |
</div> |
174 | 90 |
<div class="row flex50"> |
175 | 91 |
<div class="flex justify-between align-start content-box"> |
176 | 92 |
<div class="flex30 content-box"> |
177 | 93 |
<div class="left-content border flex100"> |
178 |
- <div class="content-titleZone"> |
|
179 |
- <p class="box-title">다이어그램 등록</p> |
|
180 |
- </div> |
|
181 |
- <ScheculerCreateComp |
|
182 |
- :schedule="schedule" |
|
183 |
- @get-edit-scheduling="getEditScheduling" |
|
184 |
- /> |
|
185 |
- <div class="flex justify-end" id="buttonZone"> |
|
186 |
- <button |
|
187 |
- class="blue-btn small-btn" |
|
188 |
- id="registerButton" |
|
189 |
- @click="fnDataInsert" |
|
190 |
- > |
|
191 |
- <span v-if="!isSelectedDiagram">등록</span> |
|
192 |
- <span v-else>수정</span> |
|
193 |
- </button> |
|
194 |
- <button |
|
195 |
- class="blue-border-btn small-btn" |
|
196 |
- id="resetButton" |
|
197 |
- @click="fnFormReset" |
|
198 |
- > |
|
199 |
- 초기화 |
|
200 |
- </button> |
|
201 |
- </div> |
|
94 |
+ <ScheduleForm /> |
|
202 | 95 |
</div> |
203 | 96 |
</div> |
204 | 97 |
<div class="right-content flex70 form-box"> |
205 |
- <div class="node-zone flex70" style="height: 94%"> |
|
206 |
- <ScheduleLogManagement |
|
207 |
- v-if="isLogListView" |
|
208 |
- :diagram_id="diagram.diagram_id" |
|
209 |
- /> |
|
210 |
- <VueFlowZone |
|
211 |
- v-else |
|
212 |
- :nodes="diagram.nodes" |
|
213 |
- :edges="diagram.edges" |
|
214 |
- @changeDiagram="changeDiagram" |
|
215 |
- /> |
|
216 |
- </div> |
|
217 |
- <div class="flex justify-end"> |
|
218 |
- <button |
|
219 |
- v-if="!isLogListView" |
|
220 |
- class="blue-border-btn small-btn" |
|
221 |
- @click="fnOpenAddModal" |
|
222 |
- > |
|
223 |
- 노드 추가 |
|
224 |
- </button> |
|
225 |
- <button |
|
226 |
- v-if="!isLogListView && diagram.nodes.length > 1" |
|
227 |
- class="blue-border-btn small-btn" |
|
228 |
- @click="operatorNow" |
|
229 |
- > |
|
230 |
- 즉시계산 |
|
231 |
- </button> |
|
232 |
- <button |
|
233 |
- v-if="!isLogListView" |
|
234 |
- class="blue-border-btn small-btn" |
|
235 |
- @click="resetNode" |
|
236 |
- > |
|
237 |
- 초기화 |
|
238 |
- </button> |
|
239 |
- <button |
|
240 |
- v-if="isLogListView" |
|
241 |
- class="blue-btn small-btn" |
|
242 |
- @click="fnNodeFlowView" |
|
243 |
- > |
|
244 |
- 다이어그램 보기 |
|
245 |
- </button> |
|
246 |
- <button |
|
247 |
- v-if="!isLogListView && diagram.nodes.length > 1" |
|
248 |
- class="blue-btn small-btn" |
|
249 |
- @click="fnLogListView" |
|
250 |
- > |
|
251 |
- 로그 보기 |
|
252 |
- </button> |
|
253 |
- </div> |
|
98 |
+ <DiagramList |
|
99 |
+ v-if="editMode == 'vueFlow'" |
|
100 |
+ :currentDiagram="currentSchedule.diagram" |
|
101 |
+ /> |
|
102 |
+ <DiagramLogList |
|
103 |
+ v-if="editMode == 'logList'" |
|
104 |
+ :schdulLogs="currentSchedule.schdulLogs" |
|
105 |
+ /> |
|
254 | 106 |
</div> |
255 | 107 |
</div> |
256 | 108 |
</div> |
257 | 109 |
</div> |
258 | 110 |
</div> |
259 | 111 |
</div> |
260 |
- <!-- 노드 추가 모달 --> |
|
261 |
- <div v-show="isAddModalOpen" class="modal-wrapper"> |
|
262 |
- <div class="modal-container small-modal"> |
|
263 |
- <div class="modal-title"> |
|
264 |
- <div class="flex justify-between align-center"> |
|
265 |
- <h2>추가할 노드 선택</h2> |
|
266 |
- <button class="close-btn" @click="fnCloseAddModal">X</button> |
|
267 |
- </div> |
|
268 |
- </div> |
|
269 |
- <div class="modal-content-monthly"> |
|
270 |
- <div class="justify-center aling-center"> |
|
271 |
- <select class="square-select full-select" v-model="selectItem"> |
|
272 |
- <option |
|
273 |
- v-for="(nodeType, indexNTL) of nodeTypeList" |
|
274 |
- :key="indexNTL" |
|
275 |
- :value="nodeType.cmmnCode" |
|
276 |
- > |
|
277 |
- {{ nodeType.codeNm }} |
|
278 |
- </option> |
|
279 |
- </select> |
|
280 |
- </div> |
|
281 |
- </div> |
|
282 |
- <div class="modal-end flex justify-end"> |
|
283 |
- <button class="blue-btn small-btn" @click="fnAddNodeCheckBtn"> |
|
284 |
- 확인 |
|
285 |
- </button> |
|
286 |
- </div> |
|
287 |
- </div> |
|
288 |
- </div> |
|
289 | 112 |
</template> |
290 |
- |
|
291 | 113 |
<script> |
292 | 114 |
import axios from "axios"; |
115 |
+// icon용 svg import |
|
293 | 116 |
import SvgIcon from "@jamescoyle/vue-icon"; |
294 |
-import { mdiMagnify } from "@mdi/js"; |
|
295 |
-import { mdiCheckCircle, mdiCloseCircle } from "@mdi/js"; |
|
296 |
-import DefaultSearchBar from "../../template/templateElement/DefaultSearchBar.vue"; |
|
297 |
-import PaginationButton from "../../component/PaginationButton.vue"; |
|
298 |
-import ScheculerCreateComp from "../../component/ScheculerCreateComp.vue"; |
|
299 |
-// vueFlowZone |
|
300 |
-import VueFlowZone from "./VueFlowZone.vue"; |
|
301 |
-import ScheduleLogManagement from "./ScheduleLogManagement.vue"; |
|
302 |
-// vueFlowZone - btns |
|
303 |
-import DatabaseConnection from "../../component/connection/itm/databaseConnection.vue"; |
|
304 |
-import apiConnection from "../../component/connection/itm/apiConnection.vue"; |
|
305 |
-import datasetUpdate from "../../component/connection/itm/datasetUpdate.vue"; |
|
306 |
-import fileSelectDiagram from "../../component/connection/itm/fileSelectDiagram.vue"; |
|
307 |
-import DataFilter from "../../component/connection/itm/dataFilter.vue"; |
|
117 |
+import { mdiMagnify, mdiCheckCircle, mdiCloseCircle } from "@mdi/js"; |
|
118 |
+// 컴포넌트 import |
|
119 |
+import PageNavigation from "../../component/PageNavigation.vue"; |
|
120 |
+ |
|
121 |
+import ScheduleList from "../../component/scheduleComponent/ScheduleList.vue"; |
|
122 |
+import ScheduleForm from "../../component/scheduleComponent/ScheduleForm.vue"; |
|
123 |
+ |
|
124 |
+import DiagramList from "../../component/scheduleComponent/DiagramList.vue"; |
|
125 |
+import DiagramLogList from "../../component/scheduleComponent/DiagramLogList.vue"; |
|
308 | 126 |
|
309 | 127 |
export default { |
310 | 128 |
components: { |
311 |
- SvgIcon: SvgIcon, |
|
312 |
- PaginationButton, |
|
313 |
- DefaultSearchBar, |
|
314 |
- ScheculerCreateComp, |
|
315 |
- // vueFlowZone |
|
316 |
- VueFlowZone, |
|
317 |
- ScheduleLogManagement, |
|
318 |
- // vueFlowZone - btns |
|
319 |
- DatabaseConnection, |
|
320 |
- apiConnection, |
|
321 |
- datasetUpdate, |
|
322 |
- fileSelectDiagram, |
|
323 |
- DataFilter, |
|
129 |
+ // 공통 |
|
130 |
+ SvgIcon, |
|
131 |
+ PageNavigation, |
|
132 |
+ // 일반 |
|
133 |
+ ScheduleList, |
|
134 |
+ ScheduleForm, |
|
135 |
+ |
|
136 |
+ DiagramList, |
|
137 |
+ DiagramLogList, |
|
324 | 138 |
}, |
139 |
+ |
|
325 | 140 |
data() { |
326 | 141 |
return { |
327 | 142 |
// svg |
... | ... | @@ -335,440 +150,19 @@ |
335 | 150 |
search_data1: this.$getDefaultSerchItem("schdul_sttus", "string"), |
336 | 151 |
search_data2: this.$getDefaultSerchItem(null, "string"), |
337 | 152 |
|
338 |
- // default |
|
339 |
- diagramList: [], |
|
340 |
- diagram: { |
|
341 |
- nodes: [], |
|
342 |
- edges: [], |
|
343 |
- }, |
|
344 |
- schedule: {}, |
|
345 |
- isSelectedDiagram: false, |
|
153 |
+ // 목록 |
|
154 |
+ scheduleList: [], |
|
346 | 155 |
|
347 |
- // 전체 선택 여부 (true:전체선택 / false:전체선택해제) |
|
348 |
- isChkAll: false, |
|
349 |
- selectedDiagramList: [], // 다이어그램 목록에서 선택한 다이어그램의 목록 |
|
156 |
+ // 스케줄 등록 |
|
350 | 157 |
|
351 |
- showRepeatSettings: true, // 반복주기 |
|
352 |
- logId: null, // 로그 페이지에서 넘어온 ID |
|
353 |
- totalNode: null, // 총 노드 수 |
|
354 |
- repetselect: null, // 반복주기 타입 |
|
355 |
- |
|
356 |
- // 노드 타입 |
|
357 |
- originalNodeTypeList: [], |
|
358 |
- nodeTypeList: [], |
|
359 |
- |
|
360 |
- // 다이어그램 로그 화면 여부(true:열림/false:닫힘) |
|
361 |
- isLogListView: false, |
|
362 |
- |
|
363 |
- // 노드 추가 |
|
364 |
- isAddModalOpen: false, // 노드 추가 모달 열림 여부(true:열림/false:닫힘) |
|
365 |
- selectItem: "DB_READ", // 노드 추가값 (기본:DB_READ) |
|
366 |
- |
|
367 |
- // 노드 수정 |
|
368 |
- isSetupModalOpen: false, // 노드 설정 모달 열림 여부(true:열림/false:닫힘) |
|
369 |
- selectNode: {}, // 선택된 노드 |
|
158 |
+ // 다이어그램 뷰어 |
|
159 |
+ editMode: "vueFlow", |
|
160 |
+ currentSchedule: Object.assign({}, this.$getDefaultJobGroup().schedule), |
|
370 | 161 |
}; |
371 | 162 |
}, |
372 |
- mounted() { |
|
373 |
- this.resetNode(); |
|
374 |
- this.fnDiagramList(); |
|
375 |
- this.fnSearchCommonCode(); |
|
376 |
- }, |
|
377 |
- methods: { |
|
378 |
- //초기화 함수 |
|
379 |
- resetNode() { |
|
380 |
- this.diagram.nodes = []; // 노드 목록 비우기 |
|
381 |
- this.diagram.edges = []; // 엣지 목록 비우기 |
|
382 | 163 |
|
383 |
- // 엔드 노드(datasetUpdate) 추가 |
|
384 |
- const node = Object.assign({}, this.$getDefaultJobGroup().node); |
|
385 |
- node.id = Date.now().toString(); |
|
386 |
- node.node_id = node.id; |
|
387 |
- node.label = "DATASET_UPDATE"; |
|
388 |
- node.node_name = "DATASET_UPDATE"; |
|
389 |
- node.node_type = "DATASET_UPDATE"; |
|
390 |
- node.itm = Object.assign({}, this.$getDefaultJobGroup().jobItemGroup); |
|
391 |
- node.center_x = 1050; |
|
392 |
- node.center_y = 25; |
|
393 |
- node.position = { |
|
394 |
- x: node.center_x, |
|
395 |
- y: node.center_y, |
|
396 |
- }; |
|
397 |
- this.diagram.nodes.push(node); |
|
398 |
- }, |
|
164 |
+ mounted() {}, |
|
399 | 165 |
|
400 |
- // 다이어그램 목록 조회 |
|
401 |
- fnDiagramList() { |
|
402 |
- const vm = this; |
|
403 |
- // 데이터 세팅 |
|
404 |
- let data = vm.search; |
|
405 |
- data.searchObjectList = []; // 초기화 |
|
406 |
- data.searchObjectList.push(this.search_date); |
|
407 |
- data.searchObjectList.push(this.search_data1); |
|
408 |
- data.searchObjectList.push(this.search_data2); |
|
409 |
- // 실행 |
|
410 |
- axios({ |
|
411 |
- url: "/diagram/diagramListRead", |
|
412 |
- method: "post", |
|
413 |
- headers: { "Content-Type": "application/json; charset=UTF-8" }, |
|
414 |
- data: data, |
|
415 |
- }) |
|
416 |
- .then((response) => { |
|
417 |
- vm.diagramList = response.data.resultData.diagramList; |
|
418 |
- vm.search = response.data.resultData.searchVO; |
|
419 |
- }) |
|
420 |
- .catch((error) => { |
|
421 |
- this.$showAlert( |
|
422 |
- "에러 발생", |
|
423 |
- "에러가 발생했습니다. 관리자에게 문의해 주세요." |
|
424 |
- ); |
|
425 |
- }); |
|
426 |
- }, |
|
427 |
- |
|
428 |
- // 노드 타입 목록 조회 |
|
429 |
- async fnSearchCommonCode() { |
|
430 |
- this.originalNodeTypeList = await this.$getCommonCode("NODE_ITEM"); |
|
431 |
- this.nodeTypeList = this.originalNodeTypeList; |
|
432 |
- }, |
|
433 |
- |
|
434 |
- // 다이어그램 전체 선택 |
|
435 |
- fnChkAll() { |
|
436 |
- this.selectedDiagramList = []; // 초기화 |
|
437 |
- if (this.isChkAll) { |
|
438 |
- this.selectedDiagramList = this.diagramList; |
|
439 |
- } |
|
440 |
- }, |
|
441 |
- // 다이어그램 선택 여부 변경 시 |
|
442 |
- fnChangeCheckList() { |
|
443 |
- if (this.selectedDiagramList.length == this.diagramList.length) { |
|
444 |
- this.isChkAll = true; |
|
445 |
- } else { |
|
446 |
- this.isChkAll = false; |
|
447 |
- } |
|
448 |
- }, |
|
449 |
- |
|
450 |
- // 선택 실행, 중단 버튼 |
|
451 |
- fnChkSttus(schdul_sttus) { |
|
452 |
- const vm = this; |
|
453 |
- for (let schedule of this.selectedDiagramList) { |
|
454 |
- axios({ |
|
455 |
- url: |
|
456 |
- "/schedule/scheduleSttusChange/" + |
|
457 |
- schedule.schdul_id + |
|
458 |
- "/" + |
|
459 |
- schdul_sttus, |
|
460 |
- method: "get", |
|
461 |
- headers: { "Content-Type": "application/json; charset=UTF-8" }, |
|
462 |
- }) |
|
463 |
- .then((response) => { |
|
464 |
- vm.$showAlert("수정", "수정이 완료 되었습니다."); |
|
465 |
- this.fnDiagramList(); // 전체 재조회 |
|
466 |
- this.isChkAll = false; // 체크 박스 리셋 |
|
467 |
- this.selectedDiagramList = []; |
|
468 |
- }) |
|
469 |
- .catch((error) => { |
|
470 |
- vm.$showAlert( |
|
471 |
- "오류", |
|
472 |
- "처리 중 오류가 발생했습니다. 관리자에게 문의바랍니다." |
|
473 |
- ); |
|
474 |
- }); |
|
475 |
- } |
|
476 |
- }, |
|
477 |
- // 선택 삭제 버튼 |
|
478 |
- fnChkDel() { |
|
479 |
- const vm = this; |
|
480 |
- for (let schedule of this.selectedDiagramList) { |
|
481 |
- axios({ |
|
482 |
- url: "/schedule/deleteSchedule/" + schedule.schdul_id, |
|
483 |
- method: "get", |
|
484 |
- headers: { "Content-Type": "application/json; charset=UTF-8" }, |
|
485 |
- }) |
|
486 |
- .then((response) => { |
|
487 |
- this.fnDiagramList(); // 전체 재조회 |
|
488 |
- this.isChkAll = false; // 체크 박스 리셋 |
|
489 |
- this.selectedDiagramList = []; |
|
490 |
- }) |
|
491 |
- .catch((error) => { |
|
492 |
- vm.$showAlert( |
|
493 |
- "오류", |
|
494 |
- "처리 중 오류가 발생했습니다. 관리자에게 문의바랍니다." |
|
495 |
- ); |
|
496 |
- }); |
|
497 |
- } |
|
498 |
- }, |
|
499 |
- |
|
500 |
- // 다이어그램 상세 조회 |
|
501 |
- fnDiagramDetail(diagramId) { |
|
502 |
- const vm = this; |
|
503 |
- // 실행 |
|
504 |
- axios({ |
|
505 |
- url: "/diagram/diagramDetailRead/" + diagramId, |
|
506 |
- method: "get", |
|
507 |
- headers: { "Content-Type": "application/json; charset=UTF8" }, |
|
508 |
- }) |
|
509 |
- .then((response) => { |
|
510 |
- vm.isSelectedDiagram = true; |
|
511 |
- vm.schedule = response.data.resultData.diagramDetailDTO.schedule; |
|
512 |
- let diagram = response.data.resultData.diagramDetailDTO.diagram; |
|
513 |
- for (let node of diagram.nodes) { |
|
514 |
- node.position = {}; |
|
515 |
- node.position.x = node.center_x; |
|
516 |
- node.position.y = node.center_x; |
|
517 |
- } |
|
518 |
- for (let edge of diagram.edges) { |
|
519 |
- edge.id = edge.edge_id; |
|
520 |
- } |
|
521 |
- vm.diagram = diagram; |
|
522 |
- }) |
|
523 |
- .catch((error) => { |
|
524 |
- vm.$showAlert( |
|
525 |
- "오류", |
|
526 |
- "처리 중 오류가 발생했습니다. 관리자에게 문의바랍니다." |
|
527 |
- ); |
|
528 |
- }); |
|
529 |
- }, |
|
530 |
- |
|
531 |
- // 노드 추가 버튼 (노드 추가 모달 열기) |
|
532 |
- fnOpenAddModal() { |
|
533 |
- this.nodeTypeList = this.originalNodeTypeList; // 초기화 |
|
534 |
- for (let node of this.diagram.nodes) { |
|
535 |
- if (node["node_type"] == "DATASET_UPDATE") { |
|
536 |
- this.nodeTypeList = this.nodeTypeList.filter( |
|
537 |
- (nodeType) => nodeType.codeNm !== "DATASET_UPDATE" |
|
538 |
- ); |
|
539 |
- } |
|
540 |
- } |
|
541 |
- this.isAddModalOpen = true; |
|
542 |
- }, |
|
543 |
- // 노드 추가 모달 닫기 |
|
544 |
- fnCloseAddModal() { |
|
545 |
- this.isAddModalOpen = false; |
|
546 |
- this.selectItem = "DB_READ"; // 초기화 |
|
547 |
- }, |
|
548 |
- // 아이디 생성 |
|
549 |
- createId(number) { |
|
550 |
- let id = Math.floor(Math.random() * number) + 1; |
|
551 |
- return id.toString(); |
|
552 |
- }, |
|
553 |
- // 노드 추가 |
|
554 |
- fnAddNodeCheckBtn() { |
|
555 |
- let node = Object.assign({}, this.$getDefaultJobGroup().node); |
|
556 |
- node.id = Date.now().toString(); |
|
557 |
- node.node_id = node.id; |
|
558 |
- node.label = this.selectItem; |
|
559 |
- node.node_name = this.selectItem; |
|
560 |
- node.node_type = this.selectItem; |
|
561 |
- node.center_x = this.createId(500); |
|
562 |
- node.center_y = this.createId(500); |
|
563 |
- node.position = { |
|
564 |
- x: node.center_x, |
|
565 |
- y: node.center_y, |
|
566 |
- }; |
|
567 |
- let tempData = JSON.parse(JSON.stringify(node)); |
|
568 |
- tempData.data = null; |
|
569 |
- node.data = tempData; |
|
570 |
- switch (this.selectItem) { |
|
571 |
- case "DB_READ": |
|
572 |
- node.itm = Object.assign({}, this.$getDefaultJobGroup().connectionDb); |
|
573 |
- node.itm.type = "dbConnection"; |
|
574 |
- break; |
|
575 |
- case "API_READ": |
|
576 |
- node.itm = Object.assign( |
|
577 |
- {}, |
|
578 |
- this.$getDefaultJobGroup().connectionApi |
|
579 |
- ); |
|
580 |
- break; |
|
581 |
- case "FILE_READ": |
|
582 |
- node.itm = Object.assign({}, this.$getDefaultJobGroup().fileRead); |
|
583 |
- break; |
|
584 |
- case "DATASET_READ": |
|
585 |
- node.itm = Object.assign({}, this.$getDefaultJobGroup().DatasetPost); |
|
586 |
- break; |
|
587 |
- case "DATA_FILTER": |
|
588 |
- node.itm = Object.assign({}, this.$getDefaultJobGroup().dataFilter); |
|
589 |
- break; |
|
590 |
- case "EHOJO_READ": |
|
591 |
- node.itm = Object.assign({}, this.$getDefaultJobGroup().connectionEhojo); |
|
592 |
- break; |
|
593 |
- case "DATASET_UPDATE": |
|
594 |
- node.itm = Object.assign({}, this.$getDefaultJobGroup().jobItemGroup); |
|
595 |
- break; |
|
596 |
- } |
|
597 |
- this.diagram.nodes.push(node); |
|
598 |
- // 모달 닫기 |
|
599 |
- this.fnCloseAddModal(); |
|
600 |
- }, |
|
601 |
- |
|
602 |
- // 다이어그램 유효성 검사 |
|
603 |
- fnValidation() { |
|
604 |
- if (this.$isEmpty(this.schedule.sj)) { |
|
605 |
- this.$showAlert("경고", "제목이 비어있습니다. 제목을 입력해 주세요."); |
|
606 |
- return false; |
|
607 |
- } |
|
608 |
- // 엔드 노드에 연결된 노드가 없는 경우 |
|
609 |
- let endNodeCnt = 0; |
|
610 |
- let endEdgeCnt = 0; |
|
611 |
- for (let node of this.diagram.nodes) { |
|
612 |
- if (node["node_type"] == "DATASET_UPDATE") { |
|
613 |
- endNodeCnt++; |
|
614 |
- for (let edge of this.diagram.edges) { |
|
615 |
- if (edge.target == node.id) { |
|
616 |
- endEdgeCnt++; |
|
617 |
- } |
|
618 |
- } |
|
619 |
- } |
|
620 |
- } |
|
621 |
- if (endNodeCnt < 1) { |
|
622 |
- this.$showAlert("오류 발생", "DATASET_UPDATE를 생성해 주세요."); |
|
623 |
- return false; |
|
624 |
- } |
|
625 |
- if (endEdgeCnt < 1) { |
|
626 |
- this.$showAlert("오류 발생", "DATASET_UPDATE에 노드를 연결해 주세요."); |
|
627 |
- return false; |
|
628 |
- } |
|
629 |
- return true; |
|
630 |
- }, |
|
631 |
- // 다이어그램 등록 |
|
632 |
- async fnDataInsert() { |
|
633 |
- const vm = this; |
|
634 |
- // 유효성 검사 |
|
635 |
- if (!this.fnValidation()) { |
|
636 |
- return; |
|
637 |
- } |
|
638 |
- // 연결되지 않은 노드 삭제 |
|
639 |
- let emptyEdgeCnt = 0; |
|
640 |
- let emptyEdgeNodeList = []; |
|
641 |
- for (let node of vm.diagram.nodes) { |
|
642 |
- let edgeCnt = 0; |
|
643 |
- for (let edge of vm.diagram.edges) { |
|
644 |
- if (node.id == edge.source || node.id == edge.target) { |
|
645 |
- edgeCnt++; |
|
646 |
- } |
|
647 |
- } |
|
648 |
- if (edgeCnt < 1) { |
|
649 |
- emptyEdgeCnt++; |
|
650 |
- emptyEdgeNodeList.push(node.id); |
|
651 |
- } |
|
652 |
- } |
|
653 |
- if (emptyEdgeCnt > 0) { |
|
654 |
- let isChecked = await vm.$showConfirm( |
|
655 |
- "경고", |
|
656 |
- "노드 전달 시, 연결되지 않은 노드는 삭제됩니다. 그래도 실행하시겠습니까?" |
|
657 |
- ); |
|
658 |
- if (!isChecked) { |
|
659 |
- return; |
|
660 |
- } else { |
|
661 |
- for (let emptyEdgeNode of emptyEdgeNodeList) { |
|
662 |
- vm.diagram.nodes = vm.diagram.nodes.filter( |
|
663 |
- (node) => node.id !== emptyEdgeNode |
|
664 |
- ); |
|
665 |
- } |
|
666 |
- } |
|
667 |
- } |
|
668 |
- // 데이터 세팅 |
|
669 |
- let schedule = vm.schedule; |
|
670 |
- schedule.schdul_sttus = "run"; |
|
671 |
- let diagram = vm.diagram; |
|
672 |
- for (let node of diagram.nodes) { |
|
673 |
- node.position = {}; |
|
674 |
- node.position.x = node.center_x; |
|
675 |
- node.position.y = node.center_x; |
|
676 |
- } |
|
677 |
- for (let edge of diagram.edges) { |
|
678 |
- delete edge.id; |
|
679 |
- } |
|
680 |
- let url = null; |
|
681 |
- let message = null; |
|
682 |
- if (!vm.isSelectedDiagram) { |
|
683 |
- url = "/diagram/dataInsert.json"; |
|
684 |
- message = "등록이 완료 되었습니다."; |
|
685 |
- } else { |
|
686 |
- url = "/diagram/dataEdit.json"; |
|
687 |
- message = "수정이 완료 되었습니다."; |
|
688 |
- } |
|
689 |
- // 실행 |
|
690 |
- axios({ |
|
691 |
- url: url, |
|
692 |
- method: "post", |
|
693 |
- headers: { "Content-Type": "application/json; charset=UTF-8" }, |
|
694 |
- data: { |
|
695 |
- schedule: schedule, |
|
696 |
- diagram: diagram, |
|
697 |
- }, |
|
698 |
- }) |
|
699 |
- .then((response) => { |
|
700 |
- vm.$showAlert("실행 결과", message); |
|
701 |
- this.fnDiagramList(); // 전체 재조회 |
|
702 |
- }) |
|
703 |
- .catch((error) => { |
|
704 |
- vm.$showAlert( |
|
705 |
- "오류", |
|
706 |
- "처리 중 오류가 발생했습니다. 관리자에게 문의바랍니다." |
|
707 |
- ); |
|
708 |
- }); |
|
709 |
- }, |
|
710 |
- // 다이어그램 등록 폼 초기화 |
|
711 |
- fnFormReset() { |
|
712 |
- this.schedule = {}; |
|
713 |
- }, |
|
714 |
- |
|
715 |
- // 스케쥴 업데이트 |
|
716 |
- getEditScheduling(title, dc, getCronStr, getCron, isCyclical) { |
|
717 |
- this.schedule.sj = title; |
|
718 |
- this.schedule.dc = dc; |
|
719 |
- this.schedule.cycle_at = isCyclical; |
|
720 |
- this.schedule.cron = getCron; |
|
721 |
- this.schedule.cron_chrctr = getCronStr; |
|
722 |
- }, |
|
723 |
- |
|
724 |
- //로그 페이지에서 ID를 가져온다 |
|
725 |
- dataDiagram() { |
|
726 |
- this.logId = this.$route.query.receiveQuery; |
|
727 |
- return this.logId; |
|
728 |
- }, |
|
729 |
- |
|
730 |
- // 즉시계산 |
|
731 |
- operatorNow() { |
|
732 |
- const vm = this; |
|
733 |
- // 데이터 세팅 |
|
734 |
- let data = vm.diagram; |
|
735 |
- for (let edge of data.edges) { |
|
736 |
- delete edge.id; |
|
737 |
- } |
|
738 |
- // 실행 |
|
739 |
- axios({ |
|
740 |
- url: "/diagram/operatorNow.json", |
|
741 |
- method: "post", |
|
742 |
- headers: { |
|
743 |
- "Content-Type": "application/json; charset=UTF-8", |
|
744 |
- }, |
|
745 |
- data: vm.diagram, |
|
746 |
- }) |
|
747 |
- .then((response) => { |
|
748 |
- vm.$showAlert("결과내용", "데이터셋 업데이트를 완료했습니다."); |
|
749 |
- }) |
|
750 |
- .catch((error) => { |
|
751 |
- vm.$showAlert( |
|
752 |
- "오류", |
|
753 |
- "처리 중 오류가 발생했습니다. 관리자에게 문의바랍니다." |
|
754 |
- ); |
|
755 |
- }); |
|
756 |
- }, |
|
757 |
- |
|
758 |
- // vueFlowZone 보기 변경 (log view 화면) |
|
759 |
- fnLogListView() { |
|
760 |
- this.isLogListView = true; |
|
761 |
- }, |
|
762 |
- // vueFlowZone 보기 변경 (node flow 화면/디폴트) |
|
763 |
- fnNodeFlowView() { |
|
764 |
- this.isLogListView = false; |
|
765 |
- }, |
|
766 |
- |
|
767 |
- // 다이어그램 수정 |
|
768 |
- changeDiagram(nodes, edges) { |
|
769 |
- this.diagram.nodes = nodes; |
|
770 |
- this.diagram.edges = edges; |
|
771 |
- }, |
|
772 |
- }, |
|
166 |
+ methods: {}, |
|
773 | 167 |
}; |
774 | 168 |
</script>(No newline at end of file) |
--- client/views/pages/schedule/VueFlowZone.vue
... | ... | @@ -1,138 +0,0 @@ |
1 | -<template> | |
2 | - <VueFlow :nodes="nodes" :edges="edges" :default-viewport="{ zoom: 0.75, position: { x: 0, y: 0 } }" :min-zoom="0.2" :max-zoom="4" :default-edge-options="{ type: 'smoothstep' }" @connect="nodeConnect" @node-drag-stop="fnMoveNode" @edge-double-click="fnDeleteEdge" @node-double-click="fnViewDiagramData"> | |
3 | - <template #node-custom="node"> | |
4 | - <CustomNode :node="node" @openSetup="fnOpenSetupModal" @removeNode="fnDeleteNode" /> | |
5 | - </template> | |
6 | - <MiniMap /> | |
7 | - <Controls /> | |
8 | - <Background pattern-color="#aaa" :gap="8" /> | |
9 | - </VueFlow> | |
10 | - <!-- 노드 셋업 모달 --> | |
11 | - <DatabaseConnection v-if="selectNode.node_type === 'DB_READ'" :openPopup="isSetupModalOpen" :jobItem="selectNode" @closePopup="fnCloseSetupModal" @saveNodeData="fnUpdateSetup" /> | |
12 | - <apiConnection v-if="selectNode.node_type === 'API_READ'" :openPopup="isSetupModalOpen" :jobItem="selectNode" @fnCloseModal="fnCloseSetupModal" @fnSaveSetup="fnUpdateSetup" /> | |
13 | - <fileSelectDiagram v-if="selectNode.node_type === 'FILE_READ'" :openPopup="isSetupModalOpen" :jobItem="selectNode" :mode="'fileRead'" @closePopup="fnCloseSetupModal" @saveNodeData="fnUpdateSetup" /> | |
14 | - <datasetSelecter v-if="selectNode.node_type === 'DATASET_READ'" :openPopup="isSetupModalOpen" :jobItem="selectNode" @closePopup="fnCloseSetupModal" @saveNodeData="fnUpdateSetup" /> | |
15 | - <DataFilter v-if="selectNode.node_type === 'DATA_FILTER'" :openPopup="isSetupModalOpen" :jobItem="selectNode" :nodes="nodes" :edges="edges" @fnCloseModal="fnCloseSetupModal" @fnSaveSetup="fnUpdateSetup" /> | |
16 | - <datasetUpdate v-if="selectNode.node_type === 'DATASET_UPDATE'" :openPopup="isSetupModalOpen" :jobItem="selectNode" :nodes="nodes" :edges="edges" @fnCloseModal="fnCloseSetupModal" @fnSaveSetup="fnUpdateSetup" /> | |
17 | - <EhojoConnection v-if="selectNode.node_type === 'EHOJO_READ'" :openPopup="isSetupModalOpen" :jobItem="selectNode" :nodes="nodes" :edges="edges" @fnCloseModal="fnCloseSetupModal" @fnSaveSetup="fnUpdateSetup" /> | |
18 | -</template> | |
19 | -<script> | |
20 | -import CustomNode from "./CustomNode.vue"; | |
21 | -// vue-flow api (삭제시 동작 안함) | |
22 | -import { Background } from "@vue-flow/background"; | |
23 | -import { Panel, VueFlow } from "@vue-flow/core"; | |
24 | -import { MiniMap } from "@vue-flow/minimap"; | |
25 | -import { Controls } from "@vue-flow/controls"; | |
26 | -// | |
27 | -import DatabaseConnection from "../../component/connection/itm/databaseConnection.vue"; | |
28 | -import apiConnection from "../../component/connection/itm/apiConnection.vue"; | |
29 | -import datasetSelecter from "../../component/connection/itm/datasetSelecter.vue"; | |
30 | -import datasetUpdate from "../../component/connection/itm/datasetUpdate.vue"; | |
31 | -import fileSelectDiagram from "../../component/connection/itm/fileSelectDiagram.vue"; | |
32 | -import DataFilter from "../../component/connection/itm/dataFilter.vue"; | |
33 | -import EhojoConnection from "../../component/connection/EhojoConnection.vue"; | |
34 | - | |
35 | -export default { | |
36 | - components: { | |
37 | - CustomNode, | |
38 | - // vue-flow api (삭제시 동작 안함) | |
39 | - Background, | |
40 | - Panel, | |
41 | - VueFlow, | |
42 | - MiniMap, | |
43 | - Controls, | |
44 | - // | |
45 | - DatabaseConnection, | |
46 | - apiConnection, | |
47 | - datasetSelecter, | |
48 | - datasetUpdate, | |
49 | - fileSelectDiagram, | |
50 | - DataFilter, | |
51 | - EhojoConnection | |
52 | - }, | |
53 | - props: { | |
54 | - nodes: Array, | |
55 | - edges: Array, | |
56 | - }, | |
57 | - data() { | |
58 | - return { | |
59 | - // 다이어그램 로그 화면 여부(true:열림/false:닫힘) | |
60 | - isLogListView: false, | |
61 | - | |
62 | - // 노드 추가 | |
63 | - isAddModalOpen: false, // 노드 추가 모달 열림 여부(true:열림/false:닫힘) | |
64 | - originalNodeTypeList: [], | |
65 | - nodeTypeList: [], | |
66 | - selectItem: "DB_READ", // 노드 추가값 (기본:DB_READ) | |
67 | - | |
68 | - // 노드 수정 | |
69 | - isSetupModalOpen: false, // 노드 설정 모달 열림 여부(true:열림/false:닫힘) | |
70 | - selectNode: {}, // 선택된 노드 | |
71 | - }; | |
72 | - }, | |
73 | - methods: { | |
74 | - // 노드 위치 변경 | |
75 | - fnMoveNode(moveNode) { | |
76 | - let nodeId = moveNode.node.id; | |
77 | - for (let node of this.nodes) { | |
78 | - if (node.id == nodeId) { | |
79 | - node.position.x = moveNode.node.position.x; | |
80 | - node.center_x = moveNode.node.position.x; | |
81 | - node.position.y = moveNode.node.position.y; | |
82 | - node.center_y = moveNode.node.position.y; | |
83 | - } | |
84 | - } | |
85 | - }, | |
86 | - | |
87 | - // 선을 연결하면 뜨는 이벤트 | |
88 | - nodeConnect(event) { | |
89 | - let edge = {}; | |
90 | - edge.id = Date.now().toString(); | |
91 | - edge.edge_id = edge.id; | |
92 | - edge.source = event.source; // 시작노드 | |
93 | - edge.target = event.target; // 끝 노드 | |
94 | - this.edges.push(edge); | |
95 | - }, | |
96 | - | |
97 | - // 엣지 삭제 | |
98 | - fnDeleteEdge(event) { | |
99 | - let edgeId = event.edge.id; | |
100 | - let edges = this.edges.filter((edge) => edge.id !== edgeId); | |
101 | - this.$emit("changeDiagram", this.nodes, edges); | |
102 | - }, | |
103 | - | |
104 | - // 노드 설정 모달 열기 | |
105 | - fnOpenSetupModal(nodeId) { | |
106 | - for (let node of this.nodes) { | |
107 | - if (node.id == nodeId) { | |
108 | - this.selectNode = node; | |
109 | - } | |
110 | - } | |
111 | - this.isSetupModalOpen = true; | |
112 | - }, | |
113 | - // 노드 설정 저장 후 닫기 | |
114 | - fnUpdateSetup(jobItem) { | |
115 | - for (let node of this.nodes) { | |
116 | - if (node.id == jobItem.id) { | |
117 | - node = jobItem; | |
118 | - } | |
119 | - } | |
120 | - this.fnCloseSetupModal(); | |
121 | - }, | |
122 | - // 노드 설정 모달 닫기 | |
123 | - fnCloseSetupModal() { | |
124 | - this.selectNode = {}; // 초기화 | |
125 | - this.isSetupModalOpen = false; | |
126 | - }, | |
127 | - | |
128 | - // 노드 삭제 | |
129 | - fnDeleteNode(nodeId) { | |
130 | - let nodes = this.nodes.filter((node) => node.id !== nodeId); | |
131 | - let edges = this.edges.filter( | |
132 | - (edge) => edge.source !== nodeId && edge.target !== nodeId | |
133 | - ); | |
134 | - this.$emit("changeDiagram", nodes, edges); | |
135 | - }, | |
136 | - }, | |
137 | -}; | |
138 | -</script>(No newline at end of file) |
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?