
--- client/resources/css/style.css
+++ client/resources/css/style.css
... | ... | @@ -63,18 +63,8 @@ |
63 | 63 |
max-width: 25%; |
64 | 64 |
} |
65 | 65 |
|
66 |
-.gall-list li>div { |
|
67 |
- min-height: 274px; |
|
68 |
- padding: 10px; |
|
69 |
- box-shadow: 0 0 5px #aaa; |
|
70 |
-} |
|
71 |
- |
|
72 |
-.gall-list li>div a { |
|
73 |
- display: block; |
|
74 |
- width: 100%; |
|
75 |
-} |
|
76 |
- |
|
77 | 66 |
.gall-list li .gall-img { |
67 |
+ display: block; |
|
78 | 68 |
width: 100%; |
79 | 69 |
height: 200px; |
80 | 70 |
text-align: center; |
... | ... | @@ -152,7 +142,8 @@ |
152 | 142 |
} |
153 | 143 |
|
154 | 144 |
.data-list { |
155 |
- height: calc(100% - 47px); |
|
145 |
+ height: 100%; |
|
146 |
+ max-height: 310px; |
|
156 | 147 |
overflow-y: auto; |
157 | 148 |
background-color: #f8f8f8; |
158 | 149 |
border-radius: 5px; |
--- client/views/component/LayoutTree.vue
... | ... | @@ -1,116 +0,0 @@ |
1 | -<template> | |
2 | - <li class="cursor"> | |
3 | - <div :class="{ 'tree-container flex align-center': true, 'selected': selectLayout.layout_nm === splitInfo.layout_nm }" @click.stop="clickLayout(splitInfo)" > | |
4 | - <p><svg-icon type="mdi" :width="18" :height="18" :path="arrowPath"></svg-icon></p> | |
5 | - <p><svg-icon type="mdi" :width="18" :height="18" :path="folderPath" :color="'#fbbe28'"></svg-icon></p> | |
6 | - | |
7 | - <p class="node-text">{{ splitInfo.layout_nm}}</p> | |
8 | - </div> | |
9 | - <ul v-if="splitInfo.children.length > 0 " class="children-node" style="height: auto;"> | |
10 | - <TreeItem v-for="(node , indx) in splitInfo.children" :splitInfo="node" :selectLayout = 'selectLayout' :key="indx" @changeselectLayout = "changeselectLayout" /> | |
11 | - </ul> | |
12 | - | |
13 | - <!-- <ul v-if="item.children" class="children-node" :style="{ 'height': toggleSelect === idx ? 'auto' : '0' }"> | |
14 | - <TreeItem :connection="connection" :selectedNode="parentSelectNode" v-for="(child, idx) in item.children" :item="child" :idx="child.id" | |
15 | - :key="idx" @selectForder="emit"/> | |
16 | - </ul> --> | |
17 | - </li> | |
18 | -</template> | |
19 | - | |
20 | -<script> | |
21 | -import axios from 'axios'; | |
22 | -import SvgIcon from '@jamescoyle/vue-icon'; | |
23 | -import {mdiLandPlots, mdiChevronRight, mdiChevronDown, mdiRhombusSplit, mdiFolderOpen } from '@mdi/js'; | |
24 | -export default { | |
25 | - props: { | |
26 | - splitInfo:{ | |
27 | - type: Object, | |
28 | - }, | |
29 | - selectLayout : { | |
30 | - type: Object, | |
31 | - } | |
32 | - }, | |
33 | - data() { | |
34 | - return { | |
35 | - currentSelectLayout : this.selectLayout, | |
36 | - toggleSelect: false, | |
37 | - clidrunNode: false, | |
38 | - folderPath: mdiLandPlots, | |
39 | - arrowPath: mdiChevronRight, | |
40 | - filePath: mdiRhombusSplit, | |
41 | - selectedId: null, | |
42 | - // host_code: null, | |
43 | - parentSelectNode: null, | |
44 | - } | |
45 | - }, | |
46 | - methods: { | |
47 | - handleClick: function (idx, event, item) { | |
48 | - | |
49 | - }, | |
50 | - | |
51 | - emit(path){ | |
52 | - this.$emit('selectForder', path) | |
53 | - }, | |
54 | - | |
55 | - getChildren(path, children) { | |
56 | - const vm = this; | |
57 | - vm.connection.path = path | |
58 | - vm.connection.type = 'folder' | |
59 | - vm.connection.depth = 1 | |
60 | - | |
61 | - axios.get('/files/list', {params: vm.connection}) | |
62 | - .then(response => { | |
63 | - let chilerenList = response.data.resultData.fileList | |
64 | - chilerenList.forEach ( item => { | |
65 | - children.push(item) | |
66 | - }) | |
67 | - this.$emit('selectForder', path) | |
68 | - }).catch(error => { | |
69 | - | |
70 | - }) | |
71 | - }, | |
72 | - | |
73 | - clickLayout : function(splitInfo){ | |
74 | - this.toggleSelect = !this.toggleSelect; | |
75 | - this.$emit('changeselectLayout', splitInfo); | |
76 | - }, | |
77 | - | |
78 | - changeselectLayout : function(layout){ | |
79 | - this.$emit('changeselectLayout', layout); | |
80 | - }, | |
81 | - }, | |
82 | - watch: { | |
83 | - selectLayout: function (v) { | |
84 | - this.currentSelectLayout = v; | |
85 | - }, | |
86 | - }, | |
87 | - computed: { | |
88 | - | |
89 | - }, | |
90 | - components: { | |
91 | - 'SvgIcon': SvgIcon | |
92 | - }, | |
93 | - beforeCreate() { | |
94 | - this.$options.components.TreeItem = require('./LayoutTree.vue').default; | |
95 | - }, | |
96 | - mounted() { | |
97 | - } | |
98 | -} | |
99 | -</script> | |
100 | - | |
101 | -<style scoped> | |
102 | -.tree-container { | |
103 | - padding: 5px 10px; | |
104 | -} | |
105 | - | |
106 | -.children-node { | |
107 | - padding: 0 0 0 10px; | |
108 | - overflow: hidden; | |
109 | - transition: max-height 0.5s ease-in-out; | |
110 | -} | |
111 | - | |
112 | -.node-text { | |
113 | - font-size: 1.4rem; | |
114 | - margin-left: 5px; | |
115 | -} | |
116 | -</style>(파일 끝에 줄바꿈 문자 없음) |
--- client/views/component/SplitterLayout.vue
+++ client/views/component/SplitterLayout.vue
... | ... | @@ -1,131 +1,42 @@ |
1 | 1 |
<template> |
2 |
- <Splitter |
|
3 |
- :id="splitInfo['layoutNm']" |
|
4 |
- class="mb-5" |
|
5 |
- :layout="splitInfo['layoutType']" |
|
6 |
- @resize="updateSizes" |
|
7 |
- :resizable="true" |
|
8 |
- > |
|
9 |
- <SplitterPanel |
|
10 |
- :id="splitInfo['children'][0]['layoutNm']" |
|
11 |
- class="flex align-items-center justify-content-center" |
|
12 |
- :class="{ |
|
13 |
- active: |
|
14 |
- clickElement == splitInfo['children'][0]['layoutNm'] && |
|
15 |
- splitInfo['children'][0], |
|
16 |
- 'padding-1': splitInfo['children'][0]['componentNm'] != null, |
|
17 |
- }" |
|
18 |
- :size="splitInfo['sizes'][0]" |
|
19 |
- > |
|
20 |
- <!-- <SplitterLayout :is="SplitterLayout" id="SplitterLayout" :clickElement="clickElement" :layoutVal="layoutVal"></SplitterLayout> --> |
|
21 |
- <SplitterLayout |
|
22 |
- v-if=" |
|
23 |
- splitInfo['children'][0]['children'] !== null && |
|
24 |
- splitInfo['children'][0]['children'].length > 0 |
|
25 |
- " |
|
26 |
- :is="SplitterLayout" |
|
27 |
- :splitInfo="splitInfo['children'][0]['children'][0]" |
|
28 |
- :clickElement="clickElement" |
|
29 |
- :optionChangeClick="optionChangeClick" |
|
30 |
- :componentOptn="componentOptn" |
|
31 |
- @parentInfo="parentInfo" |
|
32 |
- ></SplitterLayout> |
|
33 |
- |
|
34 |
- <!-- <HorizentalThreeData ></HorizentalThreeData> --> |
|
35 |
- <!-- <div style="height:100%; width:100%;"> --> |
|
36 |
- <component |
|
37 |
- v-if="splitInfo['children'][0]['componentNm']" |
|
38 |
- :is="splitInfo['children'][0]['componentNm']" |
|
39 |
- :parent="splitInfo['children'][0]" |
|
40 |
- :optionChangeClick="optionChangeClick" |
|
41 |
- :componentOptn="componentOptn" |
|
42 |
- @parentInfo="parentInfo" |
|
43 |
- > |
|
44 |
- </component> |
|
45 |
- <!-- </div> --> |
|
2 |
+ <Splitter :class="{ 'content-box': true, active: splitInfo.layout_nm == selectLayout.layout_nm }" :layout="splitInfo.layout_type" @resizeend="updateSizes" @Click.stop="clickLayout(splitInfo)"> |
|
3 |
+ <SplitterPanel class="content-box" v-if="splitInfo.children.length < 1"> |
|
4 |
+ <ComponentTitle v-if="splitInfo.useSj" :title="splitInfo.layoutSj" /> |
|
5 |
+ <BaseComponent v-else-if="splitInfo.se == 'component'" :component="splitInfo.component" @onChange="onChange" @Click.stop="clickLayout(splitInfo)" /> |
|
46 | 6 |
</SplitterPanel> |
47 |
- <SplitterPanel |
|
48 |
- v-if="splitInfo['children'][0]['layoutNm'] !== undefined" |
|
49 |
- :id="splitInfo['children'][1]['layoutNm']" |
|
50 |
- class="flex align-items-center justify-content-center" |
|
51 |
- :class="{ |
|
52 |
- active: |
|
53 |
- clickElement == splitInfo['children'][1]['layoutNm'] && |
|
54 |
- splitInfo['children'][1], |
|
55 |
- 'padding-1': splitInfo['children'][1]['componentNm'] != null, |
|
56 |
- }" |
|
57 |
- :size="splitInfo['sizes'][1]" |
|
58 |
- > |
|
59 |
- <!-- <SplitterLayout :is="SplitterLayout" id="SplitterLayout" :clickElement="clickElement" :layoutVal="layoutVal"></SplitterLayout> --> |
|
60 |
- <SplitterLayout |
|
61 |
- v-if=" |
|
62 |
- splitInfo['children'][0]['children'] !== null && |
|
63 |
- splitInfo['children'][0]['children'].length > 0 |
|
64 |
- " |
|
65 |
- :is="SplitterLayout" |
|
66 |
- :splitInfo="splitInfo['children'][1]['children'][0]" |
|
67 |
- :clickElement="clickElement" |
|
68 |
- :optionChangeClick="optionChangeClick" |
|
69 |
- :componentOptn="componentOptn" |
|
70 |
- @parentInfo="parentInfo" |
|
71 |
- ></SplitterLayout> |
|
72 |
- <component |
|
73 |
- v-if="splitInfo['children'][1]['componentNm']" |
|
74 |
- :is="splitInfo['children'][1]['componentNm']" |
|
75 |
- :parent="splitInfo['children'][1]" |
|
76 |
- :optionChangeClick="optionChangeClick" |
|
77 |
- :componentOptn="componentOptn" |
|
78 |
- @parentInfo="parentInfo" |
|
79 |
- ></component> |
|
80 |
- </SplitterPanel> |
|
7 |
+ <template v-else> |
|
8 |
+ <SplitterPanel class="content-box" v-for="(item, idx) of splitInfo.children" :key="idx"> |
|
9 |
+ <SplitterLayout :splitInfo="item" :selectLayout="selectLayout" @onChange="onChange" /> |
|
10 |
+ </SplitterPanel> |
|
11 |
+ </template> |
|
81 | 12 |
</Splitter> |
82 | 13 |
</template> |
83 | 14 |
<script> |
84 |
-import HorizentalThreeData from "./elementComponent/HorizentalThreeData.vue"; |
|
85 |
-import HorizentalTwoData from "./elementComponent/HorizentalTwoData.vue"; |
|
86 |
-import TableData from "./elementComponent/TableData.vue"; |
|
87 |
-import ThreeData from "./elementComponent/ThreeData.vue"; |
|
88 |
-import EquipmentData from "./elementComponent/EquipmentData.vue"; // 2024.02.16 PJH |
|
15 |
+import BaseComponent from "./elementComponent/BaseComponent.vue"; |
|
16 |
+import ComponentTitle from './elementComponent/ComponentTitle.vue'; |
|
89 | 17 |
|
90 | 18 |
export default { |
91 | 19 |
name: "SplitterLayout", |
20 |
+ components: { BaseComponent, ComponentTitle }, |
|
92 | 21 |
props: { |
93 | 22 |
splitInfo: { |
94 | 23 |
type: Object, |
95 | 24 |
}, |
96 |
- clickElement: null, |
|
97 |
- optionChangeClick: { |
|
98 |
- type: Function, |
|
99 |
- }, |
|
100 |
- componentOptn: { |
|
25 |
+ selectLayout: { |
|
101 | 26 |
type: Object, |
102 |
- required: true, |
|
103 | 27 |
}, |
104 |
- inputVal: { |
|
105 |
- type: Object, |
|
106 |
- required: true, |
|
107 |
- }, |
|
108 |
- }, |
|
109 |
- components: { |
|
110 |
- HorizentalThreeData, |
|
111 |
- HorizentalTwoData, |
|
112 |
- TableData, |
|
113 |
- ThreeData, |
|
114 |
- EquipmentData, // 2024.02.16 PJH |
|
115 |
- SplitterLayout: () => import("./SplitterLayout.vue"), |
|
116 | 28 |
}, |
117 | 29 |
methods: { |
118 |
- // newSizes는 사용자가 조정한 후의 panel 크기 비율을 배열로 제공합니다. |
|
119 | 30 |
updateSizes(newSizes) { |
120 |
- this.splitInfo["sizes"] = newSizes["sizes"]; |
|
31 |
+ this.splitInfo["layout_size1"] = newSizes.sizes[0]; |
|
32 |
+ this.splitInfo["layout_size2"] = newSizes.sizes[1]; |
|
33 |
+ this.splitInfo["sizes"] = newSizes.sizes; |
|
121 | 34 |
}, |
122 |
- parentInfo: function (parentInfo) { |
|
123 |
- this.$emit("parentInfo", parentInfo); |
|
35 |
+ clickLayout: function (splitInfo) { |
|
36 |
+ this.$emit("onChange", splitInfo); |
|
124 | 37 |
}, |
125 |
- }, |
|
126 |
- computed: { |
|
127 |
- isReSize() { |
|
128 |
- return this.$route.path.includes("customSelectOne") ? false : true; |
|
38 |
+ onChange: function (layout) { |
|
39 |
+ this.$emit("onChange", layout); |
|
129 | 40 |
}, |
130 | 41 |
}, |
131 | 42 |
}; |
... | ... | @@ -134,11 +45,8 @@ |
134 | 45 |
.active { |
135 | 46 |
border: 3px dotted red; |
136 | 47 |
} |
48 |
+ |
|
137 | 49 |
.padding-1 { |
138 | 50 |
padding: 1rem; |
139 |
-} |
|
140 |
-.p-splitter-gutter-handle, |
|
141 |
-.p-splitter-gutter { |
|
142 |
- pointer-events: none; |
|
143 | 51 |
} |
144 | 52 |
</style>(파일 끝에 줄바꿈 문자 없음) |
--- client/views/component/SplitterLayoutDev.vue
... | ... | @@ -1,173 +0,0 @@ |
1 | -<template> | |
2 | - <Splitter | |
3 | - v-if="splitInfo['children'].length <= 0" | |
4 | - :id="splitInfo['layout_nm']" | |
5 | - class="mb-5" | |
6 | - :layout="splitInfo['layout_type']" | |
7 | - @resizeend="updateSizes" | |
8 | - @Click.stop="clickLayout(splitInfo)" | |
9 | - :style="this.$createStyleSheet(splitInfo.styleSheet)" | |
10 | - > | |
11 | - <SplitterPanel | |
12 | - v-if="splitInfo['children'][0] != null" | |
13 | - class="flex align-items-center justify-content-center" | |
14 | - :size="50" | |
15 | - > | |
16 | - 11 | |
17 | - </SplitterPanel> | |
18 | - <SplitterPanel | |
19 | - v-if="splitInfo['children'][1] != null" | |
20 | - class="flex align-items-center justify-content-center" | |
21 | - :size="50" | |
22 | - > | |
23 | - 22 | |
24 | - </SplitterPanel> | |
25 | - <BaseComponent | |
26 | - v-if="splitInfo.se == 'component'" | |
27 | - :component="splitInfo.component" | |
28 | - @changeselectLayout="changeselectLayout" | |
29 | - @Click.stop="clickLayout(splitInfo)" | |
30 | - ></BaseComponent> | |
31 | - </Splitter> | |
32 | - <Splitter | |
33 | - v-else | |
34 | - :id="splitInfo['layout_nm']" | |
35 | - class="mb-5" | |
36 | - :layout="splitInfo['layout_type']" | |
37 | - @resizeend="updateSizes" | |
38 | - @Click.stop="clickLayout(splitInfo)" | |
39 | - :style="this.$createStyleSheet(splitInfo.styleSheet)" | |
40 | - > | |
41 | - <SplitterPanel | |
42 | - v-if="splitInfo['children'][0] != null" | |
43 | - :id="splitInfo['children'][0]['layout_nm']" | |
44 | - class="flex align-items-center justify-content-center" | |
45 | - :class="{ | |
46 | - active: | |
47 | - selectLayout['layout_nm'] == splitInfo['children'][0]['layout_nm'] && | |
48 | - splitInfo['children'][0], | |
49 | - 'padding-1': splitInfo['children'][0]['component'] != null, | |
50 | - }" | |
51 | - :size="splitInfo['layout_size1']" | |
52 | - > | |
53 | - <template v-if="splitInfo.children[0]"> | |
54 | - <SplitterLayout | |
55 | - v-if="splitInfo.children[0].se == 'splitter'" | |
56 | - :splitInfo="splitInfo.children[0]" | |
57 | - @changeselectLayout="changeselectLayout" | |
58 | - :selectLayout="selectLayout" | |
59 | - ></SplitterLayout> | |
60 | - <BaseComponent | |
61 | - v-if="splitInfo.children[0].se == 'component'" | |
62 | - :component="splitInfo.children[0].component" | |
63 | - @changeselectLayout="changeselectLayout" | |
64 | - @Click.stop="clickLayout(splitInfo.children[0])" | |
65 | - ></BaseComponent> | |
66 | - </template> | |
67 | - </SplitterPanel> | |
68 | - <SplitterPanel | |
69 | - v-if="splitInfo['children'][1] != null" | |
70 | - :id="splitInfo['children'][1]['layout_nm']" | |
71 | - class="flex align-items-center justify-content-center" | |
72 | - :class="{ | |
73 | - active: | |
74 | - selectLayout['layout_nm'] == splitInfo['children'][1]['layout_nm'] && | |
75 | - splitInfo['children'][1], | |
76 | - 'padding-1': splitInfo['children'][1]['component'] != null, | |
77 | - }" | |
78 | - :size="splitInfo['layout_size2']" | |
79 | - > | |
80 | - <template v-if="splitInfo.children[1]"> | |
81 | - <SplitterLayout | |
82 | - v-if="splitInfo.children[1].se == 'splitter'" | |
83 | - :splitInfo="splitInfo.children[1]" | |
84 | - @changeselectLayout="changeselectLayout" | |
85 | - :selectLayout="selectLayout" | |
86 | - ></SplitterLayout> | |
87 | - <BaseComponent | |
88 | - v-if="splitInfo.children[1].se == 'component'" | |
89 | - :component="splitInfo.children[1].component" | |
90 | - @changeselectLayout="changeselectLayout" | |
91 | - @Click.stop="clickLayout(splitInfo.children[1])" | |
92 | - ></BaseComponent> | |
93 | - </template> | |
94 | - </SplitterPanel> | |
95 | - </Splitter> | |
96 | -</template> | |
97 | - | |
98 | - | |
99 | -<script> | |
100 | -import BaseComponent from "./elementComponent/BaseComponent.vue"; | |
101 | - | |
102 | -export default { | |
103 | - name: "SplitterLayout", | |
104 | - props: { | |
105 | - splitInfo: { | |
106 | - type: Object, | |
107 | - }, | |
108 | - selectLayout: { | |
109 | - type: Object, | |
110 | - default: function () { | |
111 | - return { | |
112 | - layout_nm: "", | |
113 | - layout_type: "", | |
114 | - children: [], | |
115 | - }; | |
116 | - }, | |
117 | - }, | |
118 | - createChartData: { | |
119 | - type: Object, | |
120 | - }, | |
121 | - }, | |
122 | - data() { | |
123 | - return { | |
124 | - currentSelectLayout: this.selectLayout, | |
125 | - }; | |
126 | - }, | |
127 | - | |
128 | - components: { | |
129 | - BaseComponent: BaseComponent, | |
130 | - }, | |
131 | - | |
132 | - methods: { | |
133 | - updateSizes(newSizes) { | |
134 | - // newSizes는 사용자가 조정한 후의 panel 크기 비율을 배열로 제공합니다. | |
135 | - this.splitInfo["layout_size1"] = newSizes.sizes[0]; | |
136 | - this.splitInfo["layout_size2"] = newSizes.sizes[1]; | |
137 | - this.splitInfo["sizes"] = newSizes.sizes; | |
138 | - }, | |
139 | - parentInfo: function (parentInfo) { | |
140 | - this.$emit("parentInfo", parentInfo); | |
141 | - }, | |
142 | - | |
143 | - clickLayout: function (splitInfo) { | |
144 | - this.$emit("changeselectLayout", splitInfo); | |
145 | - }, | |
146 | - | |
147 | - changeselectLayout: function (layout) { | |
148 | - this.$emit("changeselectLayout", layout); | |
149 | - }, | |
150 | - }, | |
151 | - | |
152 | - watch: { | |
153 | - splitInfo: { | |
154 | - handler: function (newVal, oldVal) {}, | |
155 | - deep: true, | |
156 | - }, | |
157 | - selectLayout: { | |
158 | - handler: function (v, old) { | |
159 | - this.currentSelectLayout = v; | |
160 | - }, | |
161 | - deep: true, | |
162 | - }, | |
163 | - }, | |
164 | -}; | |
165 | -</script> | |
166 | -<style scoped> | |
167 | -.active { | |
168 | - border: 3px dotted red; | |
169 | -} | |
170 | -.padding-1 { | |
171 | - padding: 1rem; | |
172 | -} | |
173 | -</style>(파일 끝에 줄바꿈 문자 없음) |
--- client/views/component/connection/itm/fileSelect.vue
+++ client/views/component/connection/itm/fileSelect.vue
... | ... | @@ -424,7 +424,7 @@ |
424 | 424 |
|
425 | 425 |
<script> |
426 | 426 |
import axios from "axios"; |
427 |
-import TreeItem from "../../FileTree.vue"; |
|
427 |
+import TreeItem from "../../treeMenu/FileTree.vue"; |
|
428 | 428 |
import SvgIcon from "@jamescoyle/vue-icon"; |
429 | 429 |
import FileDataRead from "./fileDataRead.vue"; |
430 | 430 |
import { |
--- client/views/component/elementComponent/BaseComponent.vue
+++ client/views/component/elementComponent/BaseComponent.vue
... | ... | @@ -1,70 +1,41 @@ |
1 |
- |
|
2 | 1 |
<template> |
3 |
- <div style="width:100%;height:100%"> |
|
4 |
- <div style="height: 100px;" > |
|
5 |
- <!-- --> |
|
6 |
- <ComponentTitle :title = baseComponent.sj v-if="component.sj_at"/> |
|
7 |
- </div> |
|
8 |
- <div style="height: calc(100% - 100px);"> |
|
9 |
- <component :is="baseComponent.component_itm.component_nm" :createDataObj="baseComponent.component_itm" ></component> |
|
10 |
- </div> |
|
11 |
- |
|
12 |
- </div> |
|
2 |
+ <component :is="baseComponent.component_itm.component_nm" :createDataObj="baseComponent.component_itm" /> |
|
13 | 3 |
</template> |
14 |
- |
|
15 |
- |
|
16 | 4 |
<script> |
17 |
- |
|
18 |
-import ComponentTitle from './ComponentTitle.vue'; |
|
19 | 5 |
import ChartCmmn from '../chart/ChartCmmn.vue'; |
20 | 6 |
import ColAndLine from '../chart/ColAndLine.vue'; |
21 | 7 |
import chartDataTransform from '../../component/chart/chartDataTransform.js'; |
22 | 8 |
|
23 | 9 |
export default { |
24 |
- props: { |
|
25 |
- component: { |
|
26 |
- type: Object, |
|
27 |
- default: null |
|
28 |
- }, |
|
10 |
+ props: { |
|
11 |
+ component: { |
|
12 |
+ type: Object, |
|
13 |
+ default: null |
|
29 | 14 |
}, |
30 |
- data() { |
|
31 |
- return { |
|
32 |
- baseComponent : this.component, |
|
33 |
- |
|
34 |
- } |
|
35 |
- }, |
|
36 |
- methods: { |
|
37 |
- |
|
38 |
- }, |
|
39 |
- watch: { |
|
40 |
- component : { |
|
41 |
- handler : function(){ |
|
42 |
- this.baseComponent = this.component; |
|
43 |
- }, |
|
44 |
- deep : true |
|
45 |
- } |
|
46 |
- |
|
47 |
- }, |
|
48 |
- computed: { |
|
49 |
- |
|
50 |
- |
|
51 |
- |
|
52 |
- |
|
53 |
- |
|
54 |
- }, |
|
55 |
- components: { |
|
56 |
- 'ComponentTitle' : ComponentTitle, |
|
57 |
- 'ChartCmmn' : ChartCmmn, |
|
58 |
- 'ColAndLine' : ColAndLine |
|
59 |
- }, |
|
60 |
- mounted() { |
|
61 |
- let changeData = this.component.component_itm; |
|
62 |
- let rowData = changeData.dataTable.rowData; |
|
63 |
- |
|
64 |
- if(this.component.component_itm.data_list == null){ |
|
65 |
- this.component.component_itm.data_list = chartDataTransform.createData(rowData, changeData.categoryAxis, changeData.valueAxis, "null"); |
|
66 |
- } |
|
67 |
- }, |
|
15 |
+ }, |
|
16 |
+ data() { |
|
17 |
+ return { |
|
18 |
+ baseComponent: this.component, |
|
19 |
+ } |
|
20 |
+ }, |
|
21 |
+ watch: { |
|
22 |
+ component: { |
|
23 |
+ handler: function () { |
|
24 |
+ this.baseComponent = this.component; |
|
25 |
+ }, |
|
26 |
+ deep: true |
|
27 |
+ } |
|
28 |
+ }, |
|
29 |
+ components: { |
|
30 |
+ 'ChartCmmn': ChartCmmn, |
|
31 |
+ 'ColAndLine': ColAndLine |
|
32 |
+ }, |
|
33 |
+ mounted() { |
|
34 |
+ let changeData = this.component.component_itm; |
|
35 |
+ let rowData = changeData.dataTable.rowData; |
|
36 |
+ if (this.component.component_itm.data_list == null) { |
|
37 |
+ this.component.component_itm.data_list = chartDataTransform.createData(rowData, changeData.categoryAxis, changeData.valueAxis, "null"); |
|
38 |
+ } |
|
39 |
+ }, |
|
68 | 40 |
} |
69 |
- |
|
70 |
-</script> |
|
41 |
+</script>(파일 끝에 줄바꿈 문자 없음) |
--- client/views/component/elementComponent/ComponentTitle.vue
+++ client/views/component/elementComponent/ComponentTitle.vue
... | ... | @@ -1,48 +1,26 @@ |
1 |
- |
|
2 | 1 |
<template> |
3 |
- <div> |
|
4 |
- <div class="component-title-zone mb10"> |
|
5 |
- <span class="component-maintitle" :style="this.$createStyleSheet(title.styleSheetMain)"> |
|
6 |
- {{title.main_sj}} |
|
7 |
- </span> |
|
8 |
- <span class="component-subtitle" :style="this.$createStyleSheet(title.styleSheetSub)"> |
|
9 |
- {{title.sub_sj}} |
|
10 |
- </span> |
|
11 |
- </div> |
|
12 |
- </div> |
|
2 |
+ <div class="component-title-zone mb10"> |
|
3 |
+ <span class="component-maintitle" :style="this.$createStyleSheet(title.styleSheetMain)">{{ title.main_sj }}</span> |
|
4 |
+ <span class="component-subtitle" :style="this.$createStyleSheet(title.styleSheetSub)">{{ title.sub_sj }}</span> |
|
5 |
+ </div> |
|
13 | 6 |
</template> |
14 |
- |
|
15 | 7 |
<script> |
16 | 8 |
export default { |
17 |
- props: { |
|
18 |
- title: { |
|
19 |
- type: Object, |
|
20 |
- default: null |
|
21 |
- }, |
|
9 |
+ props: { |
|
10 |
+ title: { |
|
11 |
+ type: Object, |
|
12 |
+ default: null |
|
22 | 13 |
}, |
23 |
- data() { |
|
24 |
- return { |
|
25 |
- titleComponent : this.title |
|
26 |
- } |
|
27 |
- }, |
|
28 |
- methods: { |
|
29 |
- |
|
30 |
- |
|
31 |
- }, |
|
32 |
- watch: { |
|
33 |
- component : function(v){ |
|
34 |
- this.baseComponent = v; |
|
35 |
- } |
|
36 |
- }, |
|
37 |
- computed: { |
|
38 |
- |
|
39 |
- }, |
|
40 |
- components: { |
|
41 |
- |
|
42 |
- }, |
|
43 |
- mounted() { |
|
44 |
- |
|
45 |
- }, |
|
14 |
+ }, |
|
15 |
+ data() { |
|
16 |
+ return { |
|
17 |
+ titleComponent: this.title |
|
18 |
+ } |
|
19 |
+ }, |
|
20 |
+ watch: { |
|
21 |
+ component: function (v) { |
|
22 |
+ this.baseComponent = v; |
|
23 |
+ } |
|
24 |
+ }, |
|
46 | 25 |
} |
47 |
- |
|
48 |
-</script> |
|
26 |
+</script>(파일 끝에 줄바꿈 문자 없음) |
+++ client/views/component/flowComponent/DataComponent.vue
... | ... | @@ -0,0 +1,458 @@ |
1 | +<template> | |
2 | + <!-- 데이터 관리 --> | |
3 | + <div class="data-set" style="border-bottom: 1px solid #ddd"> | |
4 | + <div class="content-box flex-column pd10"> | |
5 | + <div class="content-titleZone"> | |
6 | + <div class="flex justify-between align-center"> | |
7 | + <p class="box-title">데이터 목록</p> | |
8 | + <button | |
9 | + type="button" | |
10 | + class="blue-border-btn small-btn" | |
11 | + @click="fnCreateJobGroup" | |
12 | + > | |
13 | + 데이터 추가 | |
14 | + </button> | |
15 | + </div> | |
16 | + </div> | |
17 | + <div class="data-list"> | |
18 | + <div | |
19 | + v-for="(item, idx) of jobGroupList" | |
20 | + :key="idx" | |
21 | + @click="fnSelectJobGroup(item)" | |
22 | + > | |
23 | + <div | |
24 | + :class="{ | |
25 | + 'item flex justify-between align-center': true, | |
26 | + mb5: idx < jobGroupList.length - 1, | |
27 | + selectData: item.group_nm == currentJobGroup.group_nm, | |
28 | + }" | |
29 | + > | |
30 | + <p class="flex align-center"> | |
31 | + <svg-icon | |
32 | + type="mdi" | |
33 | + class="mr5" | |
34 | + :width="20" | |
35 | + :height="20" | |
36 | + :path="dataPath" | |
37 | + ></svg-icon> | |
38 | + <span>{{ item.group_nm }}</span> | |
39 | + </p> | |
40 | + <div> | |
41 | + <button | |
42 | + class="blue-border-btn set-btn" | |
43 | + @click.stop="" | |
44 | + @click="fnModalOpen(item, idx)" | |
45 | + > | |
46 | + 설정 | |
47 | + </button> | |
48 | + <button | |
49 | + class="red-border-btn set-btn" | |
50 | + @click.stop="" | |
51 | + @click="deleteDiagram(item)" | |
52 | + > | |
53 | + 삭제 | |
54 | + </button> | |
55 | + </div> | |
56 | + </div> | |
57 | + </div> | |
58 | + </div> | |
59 | + </div> | |
60 | + </div> | |
61 | + <!-- 컬럼 정보 --> | |
62 | + <div class="column-list flex no-gutter"> | |
63 | + <div class="content-box flex50 pd10" style="border-right: 1px solid #ddd"> | |
64 | + <div class="flex100 content-box"> | |
65 | + <p class="box-title mb10">컬럼정보</p> | |
66 | + <div class="overflow-y" style="height: calc(100% - 22px)"> | |
67 | + <template v-if="columns.length > 0"> | |
68 | + <div | |
69 | + v-for="(column, idx) of columns" | |
70 | + :key="idx" | |
71 | + :class="{ item: true, mb5: idx < columns.length - 1 }" | |
72 | + draggable="true" | |
73 | + @dragstart="startDrag($event, column, idx)" | |
74 | + > | |
75 | + <p> | |
76 | + <b>{{ column.displyColumnNm }}</b> | |
77 | + ({{ column.columnNm }}) : [{{ column.dataTy }}] | |
78 | + </p> | |
79 | + </div> | |
80 | + </template> | |
81 | + <div v-else> | |
82 | + <p style="font-size: 1.3rem">컬럼 데이터가 존재하지 않습니다.</p> | |
83 | + </div> | |
84 | + </div> | |
85 | + </div> | |
86 | + </div> | |
87 | + <div class="content-box flex50"> | |
88 | + <div class="content-box flex-column no-gutter pd10 overflow-y"> | |
89 | + <div class="flex30 mb10"> | |
90 | + <div class="editor-box content-box pd10"> | |
91 | + <p class="object-title mb5">항목(Category)</p> | |
92 | + <div | |
93 | + class="overflow-y" | |
94 | + style="height: calc(100% - 22px)" | |
95 | + @drop.prevent="onDrop($event, 'xdata')" | |
96 | + @dragenter.prevent | |
97 | + @dragover.prevent | |
98 | + > | |
99 | + <template v-if="dataX.length > 0"> | |
100 | + <div | |
101 | + v-for="(column, indx) of dataX" | |
102 | + :key="indx" | |
103 | + :class="{ | |
104 | + 'item flex justify-between align-center': true, | |
105 | + mb5: idx < dataX.length - 1, | |
106 | + }" | |
107 | + > | |
108 | + <p>{{ column.displyColumnNm }} : [{{ column.dataTy }}]</p> | |
109 | + <div> | |
110 | + <button | |
111 | + class="red-border-btn set-btn" | |
112 | + @click="deleteItem(column, 'x')" | |
113 | + > | |
114 | + 삭제 | |
115 | + </button> | |
116 | + </div> | |
117 | + </div> | |
118 | + </template> | |
119 | + <div class="item mb5" v-else> | |
120 | + <p>NO_DATA</p> | |
121 | + </div> | |
122 | + </div> | |
123 | + </div> | |
124 | + </div> | |
125 | + <div class="flex40 mb10"> | |
126 | + <div class="editor-box content-box pd10"> | |
127 | + <div class="flex justify-between align-center"> | |
128 | + <p class="object-title mb5">값(Value)</p> | |
129 | + <select v-model="currentCalc"> | |
130 | + <option | |
131 | + v-for="(item, idx) of calcList" | |
132 | + :key="idx" | |
133 | + :value="item.key" | |
134 | + > | |
135 | + {{ item.value }} | |
136 | + </option> | |
137 | + </select> | |
138 | + </div> | |
139 | + <div | |
140 | + class="overflow-y" | |
141 | + style="height: calc(100% - 22px)" | |
142 | + @drop.prevent="onDrop($event, 'ydata')" | |
143 | + @dragenter.prevent | |
144 | + @dragover.prevent | |
145 | + > | |
146 | + <template v-if="dataY.length > 0"> | |
147 | + <div | |
148 | + v-for="(column, indx) of dataY" | |
149 | + :key="indx" | |
150 | + :class="{ | |
151 | + 'item flex justify-between align-center': true, | |
152 | + mb5: idx < dataY.length - 1, | |
153 | + }" | |
154 | + > | |
155 | + <p>{{ column.displyColumnNm }} : [{{ column.dataTy }}]</p> | |
156 | + <button | |
157 | + class="red-border-btn set-btn" | |
158 | + @click="deleteItem(column, 'y')" | |
159 | + > | |
160 | + 삭제 | |
161 | + </button> | |
162 | + </div> | |
163 | + </template> | |
164 | + <div class="item mb5" v-else> | |
165 | + <p>NO_DATA</p> | |
166 | + </div> | |
167 | + </div> | |
168 | + </div> | |
169 | + </div> | |
170 | + <div class="flex20 mb10"> | |
171 | + <div class="editor-box content-box pd10"> | |
172 | + <p class="object-title mb5">다음으로 색상 지정</p> | |
173 | + <ul class="overflow-y" style="height: calc(100% - 16px)"> | |
174 | + <li class="item mb5"> | |
175 | + <p>column</p> | |
176 | + </li> | |
177 | + </ul> | |
178 | + </div> | |
179 | + </div> | |
180 | + </div> | |
181 | + </div> | |
182 | + </div> | |
183 | + <!-- 잡그룹관리 모달 --> | |
184 | + <div class="modal-wrapper" v-if="isModalOpen"> | |
185 | + <div class="modal-container"> | |
186 | + <div class="modal-title flex justify-between align-center"> | |
187 | + <h2>잡그룹 관리</h2> | |
188 | + <button class="close-btn" @click="fnModalClose"> | |
189 | + <svg-icon | |
190 | + type="mdi" | |
191 | + :width="20" | |
192 | + :height="20" | |
193 | + :path="closePath" | |
194 | + ></svg-icon> | |
195 | + </button> | |
196 | + </div> | |
197 | + <div class="modal-content-monthly mb10"> | |
198 | + <JobGroupManagement :jobGroup="currentJobGroup" /> | |
199 | + </div> | |
200 | + <div class="modal-end flex justify-end"> | |
201 | + <button class="blue-btn small-btn" @click="fnSaveJobGroup">저장</button> | |
202 | + <button class="blue-border-btn small-btn" @click="fnModalClose"> | |
203 | + 취소 | |
204 | + </button> | |
205 | + </div> | |
206 | + </div> | |
207 | + </div> | |
208 | +</template> | |
209 | +<script> | |
210 | +// icon | |
211 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
212 | +import { mdiDatabase, mdiClose } from "@mdi/js"; | |
213 | +import JobGroupManagement from "./JobGroupManagement.vue"; | |
214 | + | |
215 | +export default { | |
216 | + components: { | |
217 | + SvgIcon, | |
218 | + JobGroupManagement, | |
219 | + }, | |
220 | + | |
221 | + props: { | |
222 | + currentJobGroupList: { | |
223 | + type: Array, | |
224 | + default: [], | |
225 | + }, | |
226 | + layoutChartData: { | |
227 | + type: Object, | |
228 | + default: {}, | |
229 | + }, | |
230 | + splitInfo: { | |
231 | + type: Object, | |
232 | + default: {}, | |
233 | + }, | |
234 | + }, | |
235 | + | |
236 | + data() { | |
237 | + return { | |
238 | + // icon | |
239 | + closePath: mdiClose, | |
240 | + dataPath: mdiDatabase, | |
241 | + | |
242 | + dragData: {}, | |
243 | + dataX: [], // x축 데이터 리스트 | |
244 | + dataY: [], // y축 데이터 리스트 | |
245 | + | |
246 | + // 잡그룹목록 | |
247 | + jobGroupList: [], | |
248 | + | |
249 | + // 잡그룹관리 모달 | |
250 | + isModalOpen: false, | |
251 | + currentJobGroup: Object.assign({}, this.$getDefaultObject().jobGroup), | |
252 | + currentJobGroupIdx: null, | |
253 | + | |
254 | + // 컬럼정보 | |
255 | + columns: [], | |
256 | + currentCalc: "default", | |
257 | + calcList: [ | |
258 | + { key: "default", value: "기본" }, | |
259 | + { key: "sum", value: "합계" }, | |
260 | + { key: "avg", value: "평균" }, | |
261 | + { key: "min", value: "최솟값" }, | |
262 | + { key: "max", value: "최댓값" }, | |
263 | + ], | |
264 | + }; | |
265 | + }, | |
266 | + | |
267 | + watch: { | |
268 | + currentJobGroupList: { | |
269 | + handler(value) { | |
270 | + this.jobGroupList = value; | |
271 | + }, | |
272 | + deep: true, | |
273 | + }, | |
274 | + | |
275 | + layoutChartData: { | |
276 | + handler() { | |
277 | + if (this.layoutChartData.component != null) { | |
278 | + this.dataX = | |
279 | + this.layoutChartData.component.component_itm.categoryAxis; | |
280 | + this.dataY = this.layoutChartData.component.component_itm.valueAxis; | |
281 | + this.currentCalc = | |
282 | + this.layoutChartData.component.component_itm.chart_cal; | |
283 | + } else { | |
284 | + this.dataX = []; | |
285 | + this.dataY = []; | |
286 | + this.currentCalc = "default"; | |
287 | + } | |
288 | + }, | |
289 | + deep: true, | |
290 | + }, | |
291 | + | |
292 | + currentCalc: function (v) { | |
293 | + if (this.layoutChartData.component != null) { | |
294 | + this.$emit("currentCalc", v); | |
295 | + } | |
296 | + }, | |
297 | + }, | |
298 | + | |
299 | + methods: { | |
300 | + // 잡그룹 추가 | |
301 | + fnCreateJobGroup() { | |
302 | + let jobGroup = Object.assign({}, this.$getDefaultObject().jobGroup); | |
303 | + jobGroup.group_nm = "데이터" + (this.jobGroupList.length + 1); | |
304 | + | |
305 | + this.jobGroupList.push(jobGroup); | |
306 | + }, | |
307 | + | |
308 | + // 잡그룹 선택 | |
309 | + fnSelectJobGroup(jobGroup) { | |
310 | + this.currentJobGroup = _.cloneDeep(jobGroup); | |
311 | + this.fnUpdateColumns(); | |
312 | + }, | |
313 | + | |
314 | + // 잡그룹관리 모달 열기 | |
315 | + fnModalOpen(jobGroup, idx) { | |
316 | + this.currentJobGroup = _.cloneDeep(jobGroup); | |
317 | + this.currentJobGroupIdx = idx; | |
318 | + | |
319 | + this.isModalOpen = true; | |
320 | + }, | |
321 | + | |
322 | + // 잡그룹관리 모달 닫기 | |
323 | + fnModalClose() { | |
324 | + this.isModalOpen = false; | |
325 | + }, | |
326 | + | |
327 | + // 잡그룹관리 저장 | |
328 | + fnSaveJobGroup() { | |
329 | + let result = this.currentJobGroup; | |
330 | + this.jobGroupList[this.currentJobGroupIdx] = result; | |
331 | + this.fnUpdateColumns(); // 컬럼정보 갱신 | |
332 | + | |
333 | + this.fnModalClose(); // 노드설정 모달 닫기 | |
334 | + }, | |
335 | + | |
336 | + // 컬럼정보 갱신 | |
337 | + fnUpdateColumns() { | |
338 | + const index = this.currentJobGroup.jobItms.length - 1; | |
339 | + const dataTable = this.currentJobGroup.jobItms[index].dataTable; | |
340 | + | |
341 | + this.columns = []; // 초기화 | |
342 | + if (!this.$isEmpty(dataTable)) { | |
343 | + this.columns = dataTable.columnDatas; | |
344 | + } | |
345 | + | |
346 | + this.$emit("onSelect", this.currentJobGroup); | |
347 | + }, | |
348 | + | |
349 | + // 드래그 이벤트 | |
350 | + startDrag(event, data, idx) { | |
351 | + this.dragData = data; | |
352 | + }, | |
353 | + | |
354 | + // 드랍 이벤트 | |
355 | + async onDrop(event, type) { | |
356 | + if (type == "xdata") { | |
357 | + if (this.dataX.length > 0) { | |
358 | + let isCheck = await this.$showConfirm( | |
359 | + "경고", | |
360 | + "데이터를 변경하시겠습니까?" | |
361 | + ); | |
362 | + if (!isCheck) { | |
363 | + return; | |
364 | + } | |
365 | + } | |
366 | + this.dataX = []; // 초기화 | |
367 | + this.dataX.push(this.dragData); | |
368 | + } else if (type == "ydata") { | |
369 | + if (this.dragData.dataTy === "STRING") { | |
370 | + this.$showAlert( | |
371 | + "메세지", | |
372 | + "값(Value)에는 숫자 데이터만 사용 가능합니다." | |
373 | + ); | |
374 | + return; | |
375 | + } | |
376 | + this.dataY.push(this.dragData); | |
377 | + } | |
378 | + | |
379 | + this.$emit("axisData", { dataX: this.dataX, dataY: this.dataY }); | |
380 | + }, | |
381 | + | |
382 | + // 아이템 삭제 | |
383 | + deleteItem(column, category) { | |
384 | + if (category == "x") { | |
385 | + for (let i = 0; i < this.dataX.length; i++) { | |
386 | + if (this.dataX[i].orginlColumnNm == column.orginlColumnNm) { | |
387 | + this.dataX.splice(i, 1); | |
388 | + } | |
389 | + } | |
390 | + } else if (category == "y") { | |
391 | + for (let i = 0; i < this.dataY.length; i++) { | |
392 | + if (this.dataY[i].orginlColumnNm == column.orginlColumnNm) { | |
393 | + this.dataY.splice(i, 1); | |
394 | + } | |
395 | + } | |
396 | + } | |
397 | + }, | |
398 | + | |
399 | + // 잡그룹 삭제 | |
400 | + async deleteDiagram(item) { | |
401 | + let isCheck = await this.$showConfirm( | |
402 | + "삭제", | |
403 | + "선택한 데이터를 삭제하시겠습니까?\n관련된 데이터도 함께 삭제됩니다." | |
404 | + ); | |
405 | + if (!isCheck) { | |
406 | + return; | |
407 | + } | |
408 | + | |
409 | + // splitInfo에 적용했던 데이터가 있는지 확인하여 삭제 (재귀) | |
410 | + if (this.splitInfo.children.length > 0) { | |
411 | + this.RecursiveFunc(this.splitInfo.children, item); | |
412 | + } | |
413 | + | |
414 | + if (this.splitInfo.component != null) { | |
415 | + if (this.splitInfo.component.jobInfo[0].group_nm == item.group_nm) { | |
416 | + this.splitInfo.component = null; | |
417 | + this.splitInfo.se = "splitter"; | |
418 | + } | |
419 | + } | |
420 | + | |
421 | + this.jobGroupList = this.jobGroupList.filter( | |
422 | + (jobItem) => jobItem !== item | |
423 | + ); | |
424 | + | |
425 | + // 초기화 | |
426 | + this.dragData = {}; | |
427 | + this.dataX = []; | |
428 | + this.dataY = []; | |
429 | + this.columns = []; | |
430 | + }, | |
431 | + | |
432 | + RecursiveFunc(children, item) { | |
433 | + // 재귀함수 | |
434 | + for (let i = 0; i < children.length; i++) { | |
435 | + if (children[i].children.length > 0) { | |
436 | + this.RecursiveFunc(children[i].children, item); | |
437 | + } else { | |
438 | + if (children[i].component != null) { | |
439 | + // children[i].jobInfo 와 item이 같은지 확인하여 삭제 | |
440 | + if (children[i].component.jobInfo[0].group_nm == item.group_nm) { | |
441 | + children[i].component = null; | |
442 | + children[i].se = "splitter"; | |
443 | + } | |
444 | + } | |
445 | + } | |
446 | + } | |
447 | + }, | |
448 | + }, | |
449 | +}; | |
450 | +</script> | |
451 | + | |
452 | +<style scoped> | |
453 | +.selectData { | |
454 | + border: solid 2px #5d5c5c; | |
455 | + color: #ffffff; | |
456 | + background-color: #ff9e29; | |
457 | +} | |
458 | +</style> |
+++ client/views/component/flowComponent/JobGroupManagement.vue
... | ... | @@ -0,0 +1,252 @@ |
1 | +<template> | |
2 | + <div class="flex-column"> | |
3 | + <div class="content-titleZone flex justify-between align-center"> | |
4 | + <p class="box-title">잡그룹 목록</p> | |
5 | + <button class="blue-border-btn small-btn" @click="fnOpenModal"> | |
6 | + 아이템 추가 | |
7 | + </button> | |
8 | + </div> | |
9 | + <div class="data-list"> | |
10 | + <template v-for="(item, idx) in currentJobGroup.jobItms" :key="idx"> | |
11 | + <div | |
12 | + :class="{ | |
13 | + node: true, | |
14 | + mb5: idx < currentJobGroup.jobItms.length - 1, | |
15 | + }" | |
16 | + > | |
17 | + <div class="node-body flex justify-between align-center"> | |
18 | + <div class="flex align-center"> | |
19 | + <p class="box-title"> | |
20 | + {{ idx + 1 }}. {{ viewChanageByType(item.type) }} | |
21 | + </p> | |
22 | + <p class="ml10"> | |
23 | + ({{ | |
24 | + item.dataTable.columnDatas.length > 0 | |
25 | + ? "설정 완료" | |
26 | + : "미설정" | |
27 | + }}) | |
28 | + </p> | |
29 | + </div> | |
30 | + <div> | |
31 | + <button | |
32 | + type="button" | |
33 | + class="blue-border-btn set-btn" | |
34 | + @click="fnOpenSetup(item, idx)" | |
35 | + > | |
36 | + 수정 | |
37 | + </button> | |
38 | + <button | |
39 | + type="button" | |
40 | + class="red-border-btn set-btn" | |
41 | + @click="fnDeleteItem(item)" | |
42 | + > | |
43 | + 삭제 | |
44 | + </button> | |
45 | + </div> | |
46 | + </div> | |
47 | + </div> | |
48 | + </template> | |
49 | + </div> | |
50 | + </div> | |
51 | + <!-- 잡아이템 추가 모달 --> | |
52 | + <CreateNodeModal | |
53 | + v-if="isOpenModal" | |
54 | + page="jobGroup" | |
55 | + @addNode="fnAddNode" | |
56 | + @closeModal="fnCloseModal" | |
57 | + /> | |
58 | + <!-- 잡아이템 셋업 모달 --> | |
59 | + <NodeSetupModal | |
60 | + v-if="isOpenSetupModal" | |
61 | + :frontNodes="frontNodes" | |
62 | + :currentJobItm="currentJobItm" | |
63 | + @onSave="fnSaveSetup" | |
64 | + @onClose="fnCloseSetup" | |
65 | + /> | |
66 | +</template> | |
67 | + | |
68 | +<script> | |
69 | +// icon | |
70 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
71 | +import { mdiClose, mdiTrashCan, mdiCog } from "@mdi/js"; | |
72 | +// modal | |
73 | +import CreateNodeModal from "../modal/CreateNodeModal.vue"; | |
74 | +import NodeSetupModal from "../modal/NodeSetupModal.vue"; | |
75 | + | |
76 | +export default { | |
77 | + components: { | |
78 | + // icon | |
79 | + SvgIcon, | |
80 | + // modal | |
81 | + CreateNodeModal, | |
82 | + NodeSetupModal, | |
83 | + }, | |
84 | + | |
85 | + props: { | |
86 | + jobGroup: { | |
87 | + type: Object, | |
88 | + default: {}, | |
89 | + }, | |
90 | + }, | |
91 | + | |
92 | + data() { | |
93 | + return { | |
94 | + // icon | |
95 | + closePath: mdiClose, | |
96 | + | |
97 | + selectItem: "DATASET_READ", // 노드 추가값 | |
98 | + isSetupModalOpen: false, // 노드 설정 모달 열림 여부(true:열림/false:닫힘) | |
99 | + nodeTypes: [ | |
100 | + { id: "DB_READ", name: "DB 조회" }, | |
101 | + { id: "DATASET_READ", name: "데이터셋 조회" }, | |
102 | + { id: "DATA_FILTER", name: "데이터 필터" }, | |
103 | + ], | |
104 | + selectNode: {}, // 선택된 아이템 | |
105 | + selectNodeIndex: null, // 선택된 아이템 인덱스 | |
106 | + | |
107 | + defaultJobGroup: Object, | |
108 | + defaultJobItm: Object, | |
109 | + delPath: mdiTrashCan, | |
110 | + setPath: mdiCog, | |
111 | + | |
112 | + currentJobGroup: this.jobGroup, | |
113 | + | |
114 | + // 아이템 추가 모달 | |
115 | + isOpenModal: false, | |
116 | + frontNodes: [], | |
117 | + currentJobItm: {}, | |
118 | + currentJobItmIdx: null, | |
119 | + | |
120 | + // 잡아이템 세팅 모달 | |
121 | + isOpenSetupModal: false, | |
122 | + }; | |
123 | + }, | |
124 | + | |
125 | + created() {}, | |
126 | + | |
127 | + mounted() {}, | |
128 | + | |
129 | + watch: { | |
130 | + jobGroup: { | |
131 | + handler(value) { | |
132 | + this.currentJobGroup = value; | |
133 | + }, | |
134 | + deep: true, | |
135 | + }, | |
136 | + }, | |
137 | + | |
138 | + methods: { | |
139 | + // 아이템추가 모달 열기 | |
140 | + fnOpenModal() { | |
141 | + this.isOpenModal = true; | |
142 | + }, | |
143 | + | |
144 | + // 아이템추가 모달 닫기 | |
145 | + fnCloseModal() { | |
146 | + this.isOpenModal = false; | |
147 | + }, | |
148 | + | |
149 | + // 잡아이템 추가 | |
150 | + fnAddNode(item) { | |
151 | + let type = this.fnFindJobItemTypeGroup(item); | |
152 | + if (type == "READ") { | |
153 | + for (let jobItem of this.currentJobGroup.jobItms) { | |
154 | + let jobType = this.fnFindJobItemTypeGroup(jobItem); | |
155 | + if (jobType == "READ") { | |
156 | + this.$showAlert( | |
157 | + "경고", | |
158 | + "읽기 아이템은 2개 이상 추가할 수 없습니다." | |
159 | + ); | |
160 | + return; | |
161 | + } | |
162 | + } | |
163 | + } | |
164 | + | |
165 | + this.currentJobGroup.jobItms.push(item.jobItm); | |
166 | + }, | |
167 | + | |
168 | + // 잡아이템 타입 분류 찾기 | |
169 | + fnFindJobItemTypeGroup(jobItem) { | |
170 | + let typeArr = jobItem.type.split("_"); | |
171 | + return typeArr[typeArr.length - 1].toUpperCase(); | |
172 | + }, | |
173 | + | |
174 | + // 타입 변환 | |
175 | + viewChanageByType(type) { | |
176 | + let typeArr = type.split("_"); | |
177 | + | |
178 | + switch (type) { | |
179 | + case "DB_READ": | |
180 | + case "API_READ": | |
181 | + case "FILE_READ": | |
182 | + case "DATASET_READ": | |
183 | + case "EHOJO_READ": | |
184 | + return typeArr[0] + " 읽기"; | |
185 | + case "DATA_FILTER": | |
186 | + return typeArr[0] + " 필터"; | |
187 | + // 기본 | |
188 | + case defalt: | |
189 | + return type; | |
190 | + } | |
191 | + }, | |
192 | + | |
193 | + // 잡아이템설정 모달 열기 | |
194 | + fnOpenSetup(item, idx) { | |
195 | + this.currentJobItm = item; | |
196 | + this.currentJobItmIdx = idx; | |
197 | + | |
198 | + let typeArr = item.type.split("_"); | |
199 | + if (typeArr[typeArr.length - 1] == "FILTER") { | |
200 | + this.frontNodes = []; // 초기화 | |
201 | + this.frontNodes.push(this.currentJobGroup.jobItms[idx - 1]); | |
202 | + | |
203 | + if (this.frontNodes.length < 1) { | |
204 | + this.$showAlert("경고", "선행 노드가 없습니다."); | |
205 | + return; | |
206 | + } | |
207 | + } | |
208 | + | |
209 | + this.isOpenSetupModal = true; | |
210 | + }, | |
211 | + | |
212 | + // 잡아이템설정 모달 닫기 | |
213 | + fnCloseSetup() { | |
214 | + this.isOpenSetupModal = false; | |
215 | + | |
216 | + // 초기화 | |
217 | + this.currentJobItm = {}; | |
218 | + this.currentJobItmIdx = null; | |
219 | + }, | |
220 | + | |
221 | + // 잡아이템설정 저장 | |
222 | + fnSaveSetup(jobItem) { | |
223 | + this.currentJobGroup.jobItms[this.currentJobItmIdx] = jobItem; | |
224 | + this.fnCloseSetup(); // 노드설정 모달 닫기 | |
225 | + }, | |
226 | + | |
227 | + // 잡아이템 삭제 | |
228 | + async fnDeleteItem(jobItem) { | |
229 | + let isCheck = await this.$showConfirm( | |
230 | + "경고", | |
231 | + "해당 잡아이템을 삭제하시겠습니까?" | |
232 | + ); | |
233 | + if (!isCheck) { | |
234 | + return; | |
235 | + } | |
236 | + | |
237 | + this.currentJobGroup.jobItms = this.currentJobGroup.jobItms.filter( | |
238 | + (item) => item !== jobItem | |
239 | + ); | |
240 | + }, | |
241 | + }, | |
242 | +}; | |
243 | +</script> | |
244 | + | |
245 | +<style scoped> | |
246 | +.node { | |
247 | + background: #ffffff; | |
248 | + border: 1px solid #dbe3fb; | |
249 | + padding: 10px; | |
250 | + border-radius: 10px; | |
251 | +} | |
252 | +</style>(파일 끝에 줄바꿈 문자 없음) |
--- client/views/component/flowComponent/VueFlowZoneGroup.vue
... | ... | @@ -1,309 +0,0 @@ |
1 | -<template> | |
2 | - <div class="data-set pt10 pb10" style="border-bottom: 1px solid #ddd"> | |
3 | - <div class="flex100 content-box"> | |
4 | - <div class="content-titleZone"> | |
5 | - <div class="flex justify-between align-center"> | |
6 | - <p class="box-title">데이터 관리</p> | |
7 | - <button class="blue-border-btn small-btn" @click="dataAdd"> 데이터 추가 </button> | |
8 | - </div> | |
9 | - </div> | |
10 | - <div class="data-list"> | |
11 | - <ul class="flex"> | |
12 | - <li v-for="(diagram, idx) in diagramList" :key="idx" class="flex50 mb5" @click="clickDiagram(diagram)"> | |
13 | - <div class="item flex justify-between align-center" :class="{ | |
14 | - selectData: diagram.group_nm == selectDiagram.group_nm, | |
15 | - }"> | |
16 | - <p class="flex align-center"> | |
17 | - <svg-icon type="mdi" :path="dataPath"> </svg-icon> | |
18 | - <span>{{ diagram.group_nm }}</span> | |
19 | - </p> | |
20 | - <div> | |
21 | - <button class="blue-border-btn set-btn" @click="setModal(diagram)">설정</button> | |
22 | - <button class="red-border-btn set-btn" @click="deleteDiagram(diagram)">삭제</button> | |
23 | - </div> | |
24 | - </div> | |
25 | - </li> | |
26 | - </ul> | |
27 | - </div> | |
28 | - </div> | |
29 | - </div> | |
30 | - <div class="column-list flex no-gutter"> | |
31 | - <div class="content-box flex50 pt10 pb10" style="border-right: 1px solid #ddd"> | |
32 | - <div class="flex100 content-box"> | |
33 | - <p class="box-title mb10">컬럼정보</p> | |
34 | - <ul class="overflow-y" style="height: calc(100% - 29px)" v-if="columns != []"> | |
35 | - <li class="item mb5" v-for="(column, indx) in columns" :key="indx" @dragstart="startDrag($event, column, indx)" draggable="true"> | |
36 | - <p>{{ column.displyColumnNm }} : [{{ column.dataTy }}]</p> | |
37 | - </li> | |
38 | - </ul> | |
39 | - <ul class="overflow-y" style="height: calc(100% - 29px)" v-else> | |
40 | - <li class="item mb5"> | |
41 | - <p>NO_DATA</p> | |
42 | - </li> | |
43 | - </ul> | |
44 | - </div> | |
45 | - </div> | |
46 | - <div class="content-box flex50 pt10 pb10"> | |
47 | - <div class="flex100 content-box flex-column no-gutter overflow-y"> | |
48 | - <div> | |
49 | - <div class="editor-box content-box pd10 mb5" style="display: flex" @drop.prevent="onDrop($event, 'xdata')" @dragenter.prevent @dragover.prevent> | |
50 | - <input type="radio" class="selectCal" id="default" value="default" v-model="selectedCal" /> default<br /> | |
51 | - <input type="radio" class="selectCal" id="sum" value="sum" v-model="selectedCal" /> Sum<br /> | |
52 | - <input type="radio" class="selectCal" id="avg" value="avg" v-model="selectedCal" /> Average<br /> | |
53 | - <input type="radio" class="selectCal" id="min" value="min" v-model="selectedCal" /> Min<br /> | |
54 | - <input type="radio" class="selectCal" id="max" value="max" v-model="selectedCal" /> Max<br /> | |
55 | - </div> | |
56 | - </div> | |
57 | - <div class="flex30"> | |
58 | - <div class="editor-box content-box pd10" @drop.prevent="onDrop($event, 'xdata')" @dragenter.prevent @dragover.prevent> | |
59 | - <p class="object-title mb5">수직(Category)</p> | |
60 | - <ul class="overflow-y" style="height: calc(100% - 29px)" v-if="dataX.length > 0"> | |
61 | - <li class="item mb5" v-for="(column, indx) in dataX" :key="indx"> | |
62 | - <p>{{ column.displyColumnNm }} : [{{ column.dataTy }}]</p> | |
63 | - <div> | |
64 | - <button class="red-border-btn set-btn" @click="deleteItem(column, 'x')"> 삭제 </button> | |
65 | - </div> | |
66 | - </li> | |
67 | - </ul> | |
68 | - <ul class="overflow-y" style="height: calc(100% - 29px)" v-else> | |
69 | - <li class="item mb5"> | |
70 | - <p>NO_DATA</p> | |
71 | - </li> | |
72 | - </ul> | |
73 | - </div> | |
74 | - </div> | |
75 | - <div class="flex30 mt10"> | |
76 | - <div class="editor-box content-box pd10" @drop.prevent="onDrop($event, 'ydata')" @dragenter.prevent @dragover.prevent> | |
77 | - <p class="object-title mb5">수평(Value)</p> | |
78 | - <ul class="overflow-y" style="height: calc(100% - 29px)" v-if="dataY.length > 0"> | |
79 | - <li class="item mb5" v-for="(column, indx) in dataY" :key="indx"> | |
80 | - <p>{{ column.displyColumnNm }} : [{{ column.dataTy }}]</p> | |
81 | - <div> | |
82 | - <button class="red-border-btn set-btn" @click="deleteItem(column, 'y')"> 삭제 </button> | |
83 | - </div> | |
84 | - </li> | |
85 | - </ul> | |
86 | - <ul class="overflow-y" style="height: calc(100% - 16px)" v-else> | |
87 | - <li class="item mb5 orange"> | |
88 | - <p>column</p> | |
89 | - </li> | |
90 | - </ul> | |
91 | - </div> | |
92 | - </div> | |
93 | - <div class="flex30 mt10"> | |
94 | - <div class="editor-box content-box pd10"> | |
95 | - <p class="object-title mb5">다음으로 색상 지정</p> | |
96 | - <ul class="overflow-y" style="height: calc(100% - 16px)"> | |
97 | - <li class="item mb5"> | |
98 | - <p>column</p> | |
99 | - </li> | |
100 | - </ul> | |
101 | - </div> | |
102 | - </div> | |
103 | - </div> | |
104 | - </div> | |
105 | - </div> | |
106 | - <VueFlowZonePopup :isModalOpen="isModalOpen" :diagram="currentDiagram" @updateData="dataUpdate" @close="close" /> | |
107 | -</template> | |
108 | -<script> | |
109 | -import { mdiDatabase } from "@mdi/js"; | |
110 | -import VueFlowZonePopup from "./VueFlowZonePopup.vue"; | |
111 | -import TemplateGuide from "../../template/guide/TemplateGuide.vue"; | |
112 | - | |
113 | -export default { | |
114 | - props: { | |
115 | - diagramList: { | |
116 | - type: Array, | |
117 | - default: [], | |
118 | - }, | |
119 | - layoutChartData: { | |
120 | - type: Object, | |
121 | - default: {}, | |
122 | - }, | |
123 | - splitInfo: { | |
124 | - type: Object, | |
125 | - default: {} | |
126 | - } | |
127 | - }, | |
128 | - data() { | |
129 | - return { | |
130 | - comId: | |
131 | - "comId_" + | |
132 | - Math.random().toString(36).substring(2, 15) + | |
133 | - Math.random().toString(36).substring(2, 15), | |
134 | - diagramListModel: [], | |
135 | - selectDiagram: _.cloneDeep(this.$getDefaultObject().jobGroup), | |
136 | - idIndex: 0, | |
137 | - dataPath: mdiDatabase, | |
138 | - dragData: {}, | |
139 | - columns: [], // 데이터 컬럼 정보 리스트 (선택 다이어그램에 따라 변경됨) | |
140 | - dataX: [], // x축 데이터 리스트 | |
141 | - dataY: [], // y축 데이터 리스트 | |
142 | - selectedCal: "", // 선택된 계산 방법 | |
143 | - | |
144 | - isModalOpen: false, | |
145 | - currentDiagram: {}, | |
146 | - }; | |
147 | - }, | |
148 | - methods: { | |
149 | - // 다이어 그램 추가 | |
150 | - dataAdd: function () { | |
151 | - const temp = _.cloneDeep(this.$getDefaultObject().jobGroup); | |
152 | - temp.group_nm = "데이터" + ++this.idIndex; | |
153 | - this.diagramList.push(temp); | |
154 | - }, | |
155 | - | |
156 | - close() { | |
157 | - this.currentDiagram = {}; | |
158 | - this.isModalOpen = false; | |
159 | - }, | |
160 | - // 모달 오픈 | |
161 | - setModal: function (diagram) { | |
162 | - this.currentDiagram = diagram; | |
163 | - this.isModalOpen = true; | |
164 | - }, | |
165 | - | |
166 | - // 다이어그램 선택 | |
167 | - clickDiagram: function (diagram) { | |
168 | - // 다이어그램 선택에 따라 컬럼 리스트가 변경되어야함 | |
169 | - this.selectDiagram = diagram; | |
170 | - if (diagram.dataTable != null) { | |
171 | - this.columns = diagram.dataTable.columnDatas; | |
172 | - } else { | |
173 | - this.columns = []; | |
174 | - } | |
175 | - // 다이어그램 선택에 따른 데이터 테이블 정보를 가져와야함 | |
176 | - this.$emit("clickDiagramDataTable", diagram); | |
177 | - }, | |
178 | - | |
179 | - // 데이터 내용 및 선택 데이터 변경시 이벤트 | |
180 | - dataUpdate: function (diagram) { | |
181 | - // diagram 데이터 적용시 가지고오는 데이터 테이블 정보 | |
182 | - if (diagram.dataTable != null) { | |
183 | - this.columns = diagram.dataTable.columnDatas; | |
184 | - } else { | |
185 | - this.columns = []; | |
186 | - } | |
187 | - this.$emit("clickDiagramDataTable", diagram); | |
188 | - }, | |
189 | - | |
190 | - // 드래그 이벤트 | |
191 | - startDrag: function (event, data, indx) { | |
192 | - this.dragData = _.cloneDeep(data); | |
193 | - this.dragData.columnIdx = indx; | |
194 | - }, | |
195 | - | |
196 | - // 드랍 이벤트 | |
197 | - onDrop: async function (event, idx) { | |
198 | - if (idx == "xdata") { | |
199 | - if (this.dataX.length > 0) { | |
200 | - if ( | |
201 | - await this.$showConfirm( | |
202 | - " Category변경", | |
203 | - "Category 데이터를 변경하시겠습니까?" | |
204 | - ) | |
205 | - ) { | |
206 | - this.dataX = []; | |
207 | - this.dataX.push(_.cloneDeep(this.dragData)); | |
208 | - } | |
209 | - } | |
210 | - this.dataX.push(_.cloneDeep(this.dragData)); | |
211 | - } else if (idx == "ydata") { | |
212 | - if (this.dragData.dataTy === "STRING") { | |
213 | - this.$showAlert("메세지", "Value에는 숫자 데이터만 사용 가능합니다."); | |
214 | - return; | |
215 | - } | |
216 | - this.dataY.push(_.cloneDeep(this.dragData)); | |
217 | - } | |
218 | - | |
219 | - this.$emit("axisData", { dataX: this.dataX, dataY: this.dataY }); | |
220 | - }, | |
221 | - | |
222 | - // 아이템 삭제 | |
223 | - deleteItem: function (column, category) { | |
224 | - if (category == "x") { | |
225 | - for (let i = 0; i < this.dataX.length; i++) { | |
226 | - if (this.dataX[i].orginlColumnNm == column.orginlColumnNm) { | |
227 | - this.dataX.splice(i, 1); | |
228 | - } | |
229 | - } | |
230 | - } else if (category == "y") { | |
231 | - for (let i = 0; i < this.dataY.length; i++) { | |
232 | - if (this.dataY[i].orginlColumnNm == column.orginlColumnNm) { | |
233 | - this.dataY.splice(i, 1); | |
234 | - } | |
235 | - } | |
236 | - } | |
237 | - }, | |
238 | - async deleteDiagram(item) { | |
239 | - if (await this.$showConfirm("삭제", "선택한 데이터를 삭제하시겠습니까?\n관련된 데이터도 함께 삭제됩니다.")) { | |
240 | - const index = this.diagramList.indexOf(item); | |
241 | - // splitInfo에 적용했던 데이터가 있는지 확인하여 삭제 (재귀) | |
242 | - if (this.splitInfo.children.length > 0) { | |
243 | - this.RecursiveFunc(this.splitInfo.children, item); | |
244 | - } | |
245 | - if (this.splitInfo.component != null) { | |
246 | - if (this.splitInfo.component.jobInfo[0].group_nm == item.group_nm) { | |
247 | - this.splitInfo.component = null; | |
248 | - this.splitInfo.se = "splitter"; | |
249 | - } | |
250 | - } | |
251 | - this.diagramList.splice(index, 1); | |
252 | - this.columns = []; | |
253 | - } | |
254 | - }, | |
255 | - RecursiveFunc: function (children, item) { | |
256 | - // 재귀함수 | |
257 | - for (let i = 0; i < children.length; i++) { | |
258 | - if (children[i].children.length > 0) { | |
259 | - this.RecursiveFunc(children[i].children, item); | |
260 | - } | |
261 | - else { | |
262 | - if (children[i].component != null) { | |
263 | - // children[i].jobInfo 와 item이 같은지 확인하여 삭제 | |
264 | - if (children[i].component.jobInfo[0].group_nm == item.group_nm) { | |
265 | - children[i].component = null; | |
266 | - children[i].se = "splitter"; | |
267 | - | |
268 | - } | |
269 | - } | |
270 | - } | |
271 | - } | |
272 | - } | |
273 | - }, | |
274 | - watch: { | |
275 | - diagramList: function (v) { | |
276 | - this.idIndex = v.length; | |
277 | - }, | |
278 | - layoutChartData: { | |
279 | - handler: function () { | |
280 | - if (this.layoutChartData.component != null) { | |
281 | - this.dataX = this.layoutChartData.component.component_itm.categoryAxis; | |
282 | - this.dataY = this.layoutChartData.component.component_itm.valueAxis; | |
283 | - this.selectedCal = this.layoutChartData.component.component_itm.chart_cal; | |
284 | - } else { | |
285 | - this.dataX = []; | |
286 | - this.dataY = []; | |
287 | - this.selectedCal = "default"; | |
288 | - } | |
289 | - }, | |
290 | - deep: true, | |
291 | - }, | |
292 | - selectedCal: function (v) { | |
293 | - if (this.layoutChartData.component != null) { | |
294 | - this.$emit("selectedCal", v); | |
295 | - } | |
296 | - }, | |
297 | - }, | |
298 | - components: { | |
299 | - VueFlowZonePopup: VueFlowZonePopup, | |
300 | - }, | |
301 | -}; | |
302 | -</script> | |
303 | -<style scoped> | |
304 | -.selectData { | |
305 | - border: solid 2px #5d5c5c; | |
306 | - color: #ffffff; | |
307 | - background-color: #ff9e29; | |
308 | -} | |
309 | -</style> |
--- client/views/component/modal/CreateNodeModal.vue
+++ client/views/component/modal/CreateNodeModal.vue
... | ... | @@ -1,9 +1,12 @@ |
1 | 1 |
<template> |
2 | 2 |
<div class="modal-wrapper"> |
3 |
- <div class="modal-container" style="width: 500px"> |
|
3 |
+ <div class="modal-container small-modal"> |
|
4 | 4 |
<div class="modal-title"> |
5 | 5 |
<div class="flex justify-between align-center"> |
6 |
- <h2>노드 추가</h2> |
|
6 |
+ <h2> |
|
7 |
+ <span v-if="page == 'jobGroup'">잡아이템 추가</span> |
|
8 |
+ <span v-else>노드 추가</span> |
|
9 |
+ </h2> |
|
7 | 10 |
<button class="close-btn" @click="fnCloseModal"> |
8 | 11 |
<svg-icon |
9 | 12 |
type="mdi" |
... | ... | @@ -63,7 +66,12 @@ |
63 | 66 |
props: { |
64 | 67 |
schdulType: { |
65 | 68 |
type: String, |
66 |
- default: null, |
|
69 |
+ default: "DATA_NODE", |
|
70 |
+ }, |
|
71 |
+ |
|
72 |
+ page: { |
|
73 |
+ type: String, |
|
74 |
+ default: "schedule", |
|
67 | 75 |
}, |
68 | 76 |
}, |
69 | 77 |
|
... | ... | @@ -105,7 +113,13 @@ |
105 | 113 |
methods: { |
106 | 114 |
// 분류 조회 |
107 | 115 |
async fnSelectTypeList() { |
108 |
- this.typeList = await this.$getCommonCodeByTree(this.schdulType); |
|
116 |
+ let typeList = await this.$getCommonCodeByTree(this.schdulType); |
|
117 |
+ if (this.page == "jobGroup") { |
|
118 |
+ this.typeList = [typeList[0], typeList[1]]; |
|
119 |
+ } else { |
|
120 |
+ this.typeList = typeList; |
|
121 |
+ } |
|
122 |
+ |
|
109 | 123 |
this.selectedType = this.typeList[0]; |
110 | 124 |
this.selectedNode = this.typeList[0].childList[0]; |
111 | 125 |
}, |
--- client/views/component/modal/HostDrctryListModal.vue
+++ client/views/component/modal/HostDrctryListModal.vue
... | ... | @@ -88,7 +88,7 @@ |
88 | 88 |
import { mdiMagnify, mdiClose } from "@mdi/js"; |
89 | 89 |
// 컴포넌트 import |
90 | 90 |
import PaginationButton from "../PaginationButton.vue"; |
91 |
-import FileTreeModal from "../FileTreeModal.vue"; |
|
91 |
+import FileTreeModal from "../treeMenu/FileTreeModal.vue"; |
|
92 | 92 |
|
93 | 93 |
export default { |
94 | 94 |
components: { SvgIcon, PaginationButton, FileTreeModal }, |
--- client/views/component/scheduleComponent/nodes/ProcessNode.vue
+++ client/views/component/scheduleComponent/nodes/ProcessNode.vue
... | ... | @@ -66,23 +66,31 @@ |
66 | 66 |
}; |
67 | 67 |
}, |
68 | 68 |
|
69 |
+ created() { |
|
70 |
+ this.init(); |
|
71 |
+ }, |
|
72 |
+ |
|
69 | 73 |
mounted() {}, |
70 | 74 |
|
71 | 75 |
watch: { |
72 | 76 |
"node.jobItm.itm.itemList": { |
73 |
- deep: true, |
|
74 | 77 |
handler(value) { |
75 |
- this.matchCnt = 0; |
|
76 |
- for (let item of value) { |
|
77 |
- if (!this.$isEmpty(item.targetColumnNm)) { |
|
78 |
- this.matchCnt++; |
|
79 |
- } |
|
80 |
- } |
|
78 |
+ this.init(); |
|
81 | 79 |
}, |
80 |
+ deep: true, |
|
82 | 81 |
}, |
83 | 82 |
}, |
84 | 83 |
|
85 |
- methods: {}, |
|
84 |
+ methods: { |
|
85 |
+ init() { |
|
86 |
+ this.matchCnt = 0; |
|
87 |
+ for (let item of this.node.jobItm.itm.itemList) { |
|
88 |
+ if (!this.$isEmpty(item.targetColumnNm)) { |
|
89 |
+ this.matchCnt++; |
|
90 |
+ } |
|
91 |
+ } |
|
92 |
+ }, |
|
93 |
+ }, |
|
86 | 94 |
}; |
87 | 95 |
</script> |
88 | 96 |
|
--- client/views/component/style/StyleSheetComponent.vue
+++ client/views/component/style/StyleSheetComponent.vue
... | ... | @@ -1,15 +1,8 @@ |
1 |
- |
|
2 | 1 |
<template> |
3 |
- <div> |
|
4 |
- <FontOption v-if="fontAt" /> |
|
5 |
- <CellOption :borderStyle="styleSheet.borderStyle" v-if="cellAt" /> |
|
6 |
- <BackgroundOption |
|
7 |
- :background_style="styleSheet.background_style" |
|
8 |
- v-if="backgroundAt" |
|
9 |
- /> |
|
10 |
- </div> |
|
2 |
+ <FontOption v-if="fontAt" /> |
|
3 |
+ <CellOption :borderStyle="styleSheet.borderStyle" v-if="cellAt" /> |
|
4 |
+ <BackgroundOption :background_style="styleSheet.background_style" v-if="backgroundAt" /> |
|
11 | 5 |
</template> |
12 |
- |
|
13 | 6 |
<script> |
14 | 7 |
import BackgroundOption from "./BackgroundOption.vue"; |
15 | 8 |
import CellOption from "./CellOption.vue"; |
--- client/views/component/style/TitleStyleComponent.vue
... | ... | @@ -1,78 +0,0 @@ |
1 | - | |
2 | -<template> | |
3 | - <div> | |
4 | - <div class="table-zone"> | |
5 | - <p class="object-title mb5">타이틀 사용 여부</p> | |
6 | - <ul> | |
7 | - <li class="mb10"> | |
8 | - <div class="input-container flex"> | |
9 | - <label class="radio-label"> | |
10 | - <input type="radio" name="comId+'use_at'" class="custom-radiobox" :value="true" v-model="component.sj_at"> | |
11 | - <span>사용</span> | |
12 | - </label> | |
13 | - <label class="radio-label"> | |
14 | - <input type="radio" name="comId+'use_at'" class="custom-radiobox" :value="false" v-model="component.sj_at"> | |
15 | - <span>사용안함</span> | |
16 | - </label> | |
17 | - </div> | |
18 | - </li> | |
19 | - | |
20 | - </ul> | |
21 | - <p class="object-title mb5">메인타이틀</p> | |
22 | - <ul> | |
23 | - <li class="mb10"> | |
24 | - <input type="text" class="full-input" v-model="component.sj.main_sj" /> | |
25 | - </li> | |
26 | - </ul> | |
27 | - <FontOption :fontStyle="component.sj.styleSheetMain.fontStyle" :title="'메인 '"/> | |
28 | - <p class="object-title mb5">서브타이틀</p> | |
29 | - <ul> | |
30 | - <li class="mb10"> | |
31 | - <input type="text" class="full-input" v-model="component.sj.sub_sj" /> | |
32 | - </li> | |
33 | - </ul> | |
34 | - <FontOption :fontStyle="component.sj.styleSheetSub.fontStyle" :title="'서브 '" /> | |
35 | - </div> | |
36 | - </div> | |
37 | -</template> | |
38 | - | |
39 | -<script> | |
40 | -import BackgroundOption from './BackgroundOption.vue'; | |
41 | -import CellOption from './CellOption.vue'; | |
42 | -import FontOption from './FontOption.vue'; | |
43 | - | |
44 | -export default { | |
45 | - props: { | |
46 | - component: { | |
47 | - type: Object, | |
48 | - default: null | |
49 | - }, | |
50 | - }, | |
51 | - data() { | |
52 | - return { | |
53 | - comId: "comId_" + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15), | |
54 | - styleSheetMain : this.component.sj.styleSheetMain, | |
55 | - styleSheetSub : this.component.sj.styleSheetSub | |
56 | - } | |
57 | - }, | |
58 | - methods: { | |
59 | - | |
60 | - | |
61 | - }, | |
62 | - watch: { | |
63 | - component : function(v){ | |
64 | - this.styleSheetMain = v.sj.styleSheetMain; | |
65 | - this.styleSheetSub = v.sj.styleSheetSub; | |
66 | - } | |
67 | - }, | |
68 | - computed: { | |
69 | - | |
70 | - }, | |
71 | - components: { | |
72 | - 'FontOption': FontOption | |
73 | - }, | |
74 | - mounted() { | |
75 | - }, | |
76 | -} | |
77 | - | |
78 | -</script> |
--- client/views/component/FileTree.vue
+++ client/views/component/treeMenu/FileTree.vue
No changes |
--- client/views/component/FileTreeModal.vue
+++ client/views/component/treeMenu/FileTreeModal.vue
... | ... | @@ -30,7 +30,7 @@ |
30 | 30 |
<script> |
31 | 31 |
import axios from 'axios'; |
32 | 32 |
import SvgIcon from '@jamescoyle/vue-icon'; |
33 |
-import TreeItem from '../component/FileTree.vue'; |
|
33 |
+import TreeItem from './FileTree.vue'; |
|
34 | 34 |
import { mdiClose } from '@mdi/js'; |
35 | 35 |
export default { |
36 | 36 |
props: { |
+++ client/views/component/treeMenu/LayoutTree.vue
... | ... | @@ -0,0 +1,83 @@ |
1 | +<template> | |
2 | + <li class="cursor"> | |
3 | + <div :class="{ 'tree-container flex align-center': true, 'selected': selectLayout.layout_nm === splitInfo.layout_nm }" @click="clickLayout(splitInfo)"> | |
4 | + <template v-if="splitInfo.children.length > 0"> | |
5 | + <svg-icon type="mdi" :width="18" :height="18" :path="arrowPath" /> | |
6 | + <svg-icon type="mdi" :width="18" :height="18" :path="folderPath" :color="'#fbbe28'" /> | |
7 | + </template> | |
8 | + <template v-else> | |
9 | + <svg-icon type="mdi" :width="18" :height="18" :path="dotPath" /> | |
10 | + <svg-icon type="mdi" :width="18" :height="18" :path="layoutPath" :color="'#fbbe28'" /> | |
11 | + </template> | |
12 | + <p class="node-text">{{ splitInfo.layout_nm }}</p> | |
13 | + </div> | |
14 | + <ul v-if="splitInfo.children.length > 0" class="children-node" :style="{ height: toggleSelect ? 'auto' : '0' }"> | |
15 | + <TreeItem v-for="(node, indx) in splitInfo.children" :splitInfo="node" :selectLayout='selectLayout' :key="indx" @onChange="onChange" /> | |
16 | + </ul> | |
17 | + </li> | |
18 | +</template> | |
19 | +<script> | |
20 | +import SvgIcon from '@jamescoyle/vue-icon'; | |
21 | +import { mdiLandPlots, mdiChevronRight, mdiRhombusSplit, mdiCircleSmall, mdiCropSquare } from '@mdi/js'; | |
22 | +export default { | |
23 | + components: { | |
24 | + 'SvgIcon': SvgIcon | |
25 | + }, | |
26 | + props: { | |
27 | + splitInfo: { | |
28 | + type: Object, | |
29 | + }, | |
30 | + selectLayout: { | |
31 | + type: Object, | |
32 | + } | |
33 | + }, | |
34 | + data() { | |
35 | + return { | |
36 | + currentSelectLayout: this.selectLayout, | |
37 | + toggleSelect: false, | |
38 | + clidrunNode: false, | |
39 | + folderPath: mdiLandPlots, | |
40 | + layoutPath: mdiCropSquare, | |
41 | + arrowPath: mdiChevronRight, | |
42 | + filePath: mdiRhombusSplit, | |
43 | + selectedId: null, | |
44 | + parentSelectNode: null, | |
45 | + dotPath: mdiCircleSmall, | |
46 | + } | |
47 | + }, | |
48 | + watch: { | |
49 | + selectLayout(v) { | |
50 | + this.currentSelectLayout = v; | |
51 | + }, | |
52 | + }, | |
53 | + beforeCreate() { | |
54 | + this.$options.components.TreeItem = require('./LayoutTree.vue').default; | |
55 | + }, | |
56 | + methods: { | |
57 | + clickLayout(splitInfo) { | |
58 | + this.toggleSelect = !this.toggleSelect; | |
59 | + this.$emit('onChange', splitInfo); | |
60 | + }, | |
61 | + | |
62 | + onChange(layout) { | |
63 | + this.$emit('onChange', layout); | |
64 | + }, | |
65 | + }, | |
66 | +} | |
67 | +</script> | |
68 | +<style scoped> | |
69 | +.tree-container { | |
70 | + padding: 5px 10px; | |
71 | +} | |
72 | + | |
73 | +.children-node { | |
74 | + padding: 0 0 0 10px; | |
75 | + overflow: hidden; | |
76 | + transition: max-height 0.5s ease-in-out; | |
77 | +} | |
78 | + | |
79 | +.node-text { | |
80 | + font-size: 1.4rem; | |
81 | + margin-left: 5px; | |
82 | +} | |
83 | +</style>(파일 끝에 줄바꿈 문자 없음) |
--- client/views/pages/AppRouter.js
+++ client/views/pages/AppRouter.js
... | ... | @@ -28,8 +28,7 @@ |
28 | 28 |
import InsertDBConnection from "../pages/dbConnection/InsertDBConnection.vue"; |
29 | 29 |
import CustomSelectList from "../pages/custom/CustomSelectList.vue"; |
30 | 30 |
import CustomSelectOne from "../pages/custom/CustomSelectOne.vue"; |
31 |
-import CustomInsert from "../pages/custom/CustomInsert.vue"; |
|
32 |
-import CustomInsertDev from "../pages/custom/CustomInsertDev.vue"; |
|
31 |
+import InsertDataAnalytics from "./custom/InsertDataAnalytics.vue"; |
|
33 | 32 |
import OpenApiList from "../pages/openapi/OpenApiList.vue"; |
34 | 33 |
import OpenApiInsert from "../pages/openapi/OpenApiInsert.vue"; |
35 | 34 |
import OpenApiSelectListOne from "../pages/openapi/OpenApiSelectListOne.vue"; |
... | ... | @@ -63,7 +62,6 @@ |
63 | 62 |
{ path: "/adminManagement.page", name: "AdminManagement", component: AdminManagement }, // 관리자 관리 |
64 | 63 |
{ path: "/departmentManagement.page", name: "Department", component: DepartmentManagement }, // 부서 관리 |
65 | 64 |
{ path: "/dbConnectionList.page", name: "DBConnectionList", component: DBConnectionList }, // 연계정보 관리 |
66 |
- |
|
67 | 65 |
{ path: "/fileManagement.page", name: "FileManagement", component: FileManagement }, |
68 | 66 |
{ path: "/hostManagement.page", name: "HostManagement", component: HostManagement }, |
69 | 67 |
{ path: "/dataManagement.page", name: "DataManagement", component: DataManagement }, |
... | ... | @@ -75,10 +73,6 @@ |
75 | 73 |
{ path: "/push.page", name: "Push", component: Push }, |
76 | 74 |
{ path: "/insertDBConnection.page", name: "InsertDBConnection", component: InsertDBConnection }, |
77 | 75 |
{ path: "/DBConnectionDetail.page", name: "DBConnectionDetail", component: DBConnectionDetail }, |
78 |
- { path: "/customSelectList.page", name: "CustomSelectList", component: CustomSelectList }, |
|
79 |
- { path: "/customSelectOne.page", name: "CustomSelectOne", component: CustomSelectOne }, |
|
80 |
- { path: "/customInsert.page", name: "CustomInsert", component: CustomInsert }, |
|
81 |
- { path: "/customInsertDev.page", name: "CustomInsertDev", component: CustomInsertDev }, |
|
82 | 76 |
{ path: "/openApiList.page", name: "OpenApiList", component: OpenApiList }, |
83 | 77 |
{ path: "/openApiInsert.page", name: "OpenApiInsert", component: OpenApiInsert }, |
84 | 78 |
{ path: "/openApiListOne.page", name: "OpenApiSelectListOne", component: OpenApiSelectListOne }, |
... | ... | @@ -89,6 +83,10 @@ |
89 | 83 |
{ path: "/myPage.page", name: "myPage", component: myPage }, |
90 | 84 |
{ path: "/myPagePwd.page", name: "myPagePwd", component: myPagePwd }, |
91 | 85 |
{ path: "/login.page", name: "Login", component: Login }, |
86 |
+ // 데이터활용관리 |
|
87 |
+ { path: "/customSelectList.page", name: "CustomSelectList", component: CustomSelectList }, |
|
88 |
+ { path: "/customSelectOne.page", name: "CustomSelectOne", component: CustomSelectOne }, |
|
89 |
+ { path: "/insertDataAnalytics.page", name: "InsertDataAnalytics", component: InsertDataAnalytics }, |
|
92 | 90 |
// 차트 |
93 | 91 |
{ path: "/chart.page", name: "Chart", component: Chart }, |
94 | 92 |
// 템플릿 화면 |
--- client/views/pages/custom/CustomInsert.vue
... | ... | @@ -1,885 +0,0 @@ |
1 | -<template> | |
2 | - <div class="container"> | |
3 | - <div class="page-titleZone flex justify-between align-center align-start"> | |
4 | - <p class="main-title flex80">데이터 현황관리 차트 등록</p> | |
5 | - <PageNavigation /> | |
6 | - </div> | |
7 | - <div class="content-wrap content"> | |
8 | - <div class="row"> | |
9 | - <div class="custom-info"> | |
10 | - <details> | |
11 | - <summary>데이터 설정</summary> | |
12 | - <div class="pd20 detail-content"> | |
13 | - <JobContainer :jobGroup="jobGroup" @getDataTable="getResult" /> | |
14 | - </div> | |
15 | - </details> | |
16 | - </div> | |
17 | - </div> | |
18 | - <div class="row custom-tab"> | |
19 | - <div class="content-box flex justify-between align-start"> | |
20 | - <div | |
21 | - :class="{ | |
22 | - 'tab-zone flex align-start': true, | |
23 | - flex35: accordion === false, | |
24 | - flex5: accordion === true, | |
25 | - }" | |
26 | - > | |
27 | - <div | |
28 | - :class="{ | |
29 | - 'column-nav': true, | |
30 | - flex10: accordion === false, | |
31 | - flex85: accordion === true, | |
32 | - }" | |
33 | - > | |
34 | - <ul> | |
35 | - <li | |
36 | - v-for="(tab, idx) in tabMenu" | |
37 | - :key="idx" | |
38 | - @click="showTab(tab.tabNic)" | |
39 | - > | |
40 | - <a | |
41 | - :class="{ activeTab: activeTab === tab.tabNic }" | |
42 | - :title="tab.tabName" | |
43 | - > | |
44 | - <svg-icon | |
45 | - type="mdi" | |
46 | - :path="tab.iconPath" | |
47 | - class="mb5" | |
48 | - ></svg-icon> | |
49 | - <p>{{ tab.tabName }}</p> | |
50 | - </a> | |
51 | - </li> | |
52 | - </ul> | |
53 | - <button @click="accordionToggle" class="custom-toggle"> | |
54 | - <svg-icon | |
55 | - type="mdi" | |
56 | - :path="arrowPath" | |
57 | - :color="'#333'" | |
58 | - ></svg-icon> | |
59 | - </button> | |
60 | - </div> | |
61 | - <div | |
62 | - class="tab-content flex90" | |
63 | - :style="accordion === true ? { display: 'none' } : {}" | |
64 | - > | |
65 | - <div | |
66 | - class="content-box flex justify-between tab-box" | |
67 | - v-show="activeTab === 'columnList'" | |
68 | - > | |
69 | - <div | |
70 | - class="content-list layout flex-column justify-between flex40" | |
71 | - > | |
72 | - <div class="page-info mb10"> | |
73 | - <div class="content-titleZone"> | |
74 | - <div class="flex justify-between align-center"> | |
75 | - <p class="box-title">페이지 정보</p> | |
76 | - </div> | |
77 | - </div> | |
78 | - <div> | |
79 | - <div class="input-group mb10"> | |
80 | - <label for="" class="mb5">제목</label> | |
81 | - <input | |
82 | - type="text" | |
83 | - name="" | |
84 | - id="" | |
85 | - class="full-input" | |
86 | - @input="customTitle = $event.target.value" | |
87 | - /> | |
88 | - </div> | |
89 | - <div class="input-group"> | |
90 | - <label for="" class="mb5">설명</label> | |
91 | - <textarea | |
92 | - name="" | |
93 | - id="" | |
94 | - cols="30" | |
95 | - rows="5" | |
96 | - @input="customComment = $event.target.value" | |
97 | - ></textarea> | |
98 | - </div> | |
99 | - </div> | |
100 | - </div> | |
101 | - <div class="layout-tree"> | |
102 | - <div class="content-titleZone"> | |
103 | - <div class="flex justify-between align-center"> | |
104 | - <p class="box-title">레이아웃 옵션</p> | |
105 | - <div> | |
106 | - <button | |
107 | - id="horizontal-btn" | |
108 | - @click="addSplitterLayout('horizontal')" | |
109 | - title="수직레이아웃" | |
110 | - > | |
111 | - <img :src="horizontalImg" /> | |
112 | - </button> | |
113 | - <button | |
114 | - id="vertical-btn" | |
115 | - @click="addSplitterLayout('vertical')" | |
116 | - title="수평레이아웃" | |
117 | - > | |
118 | - <img :src="verticalImg" /> | |
119 | - </button> | |
120 | - </div> | |
121 | - </div> | |
122 | - </div> | |
123 | - <ul></ul> | |
124 | - </div> | |
125 | - </div> | |
126 | - <div class="layout-option flex-column justify-between flex60"> | |
127 | - <div class="content-titleZone"> | |
128 | - <div class="flex justify-between align-center"> | |
129 | - <p class="box-title">옵션</p> | |
130 | - </div> | |
131 | - </div> | |
132 | - <div class="modal-content-monthly"> | |
133 | - <CellOption /> | |
134 | - <BackgroundOption /> | |
135 | - </div> | |
136 | - <div class="modal-end flex justify-end"> | |
137 | - <button class="gray-border-btn small-btn">취소</button> | |
138 | - <button class="blue-border-btn small-btn">적용</button> | |
139 | - </div> | |
140 | - </div> | |
141 | - </div> | |
142 | - <div class="content-box" v-show="activeTab === 'viewSetting'"> | |
143 | - <div class="content-list"> | |
144 | - <div class="component-zone"> | |
145 | - <details> | |
146 | - <summary>요소 선택</summary> | |
147 | - <ul class="component-content"> | |
148 | - <li | |
149 | - id="HorizentalTwoData" | |
150 | - class="flex align-center cursor" | |
151 | - @click="addComponent" | |
152 | - > | |
153 | - <img | |
154 | - src="../../../resources/img/componentIcon/icon2.png" | |
155 | - /> | |
156 | - </li> | |
157 | - <li | |
158 | - id="ThreeData" | |
159 | - class="flex align-center cursor" | |
160 | - @click="addComponent" | |
161 | - > | |
162 | - <img | |
163 | - src="../../../resources/img/componentIcon/icon1.png" | |
164 | - /> | |
165 | - </li> | |
166 | - <li | |
167 | - id="HorizentalThreeData" | |
168 | - class="flex align-center cursor" | |
169 | - @click="addComponent" | |
170 | - > | |
171 | - <img | |
172 | - src="../../../resources/img/componentIcon/icon5.png" | |
173 | - /> | |
174 | - </li> | |
175 | - <li | |
176 | - id="TableData" | |
177 | - class="flex align-center cursor" | |
178 | - @click="addComponent" | |
179 | - > | |
180 | - <img | |
181 | - src="../../../resources/img/componentIcon/icon3.png" | |
182 | - /> | |
183 | - </li> | |
184 | - <li class="flex align-center cursor"> | |
185 | - <img | |
186 | - src="../../../resources/img/componentIcon/icon4.png" | |
187 | - /> | |
188 | - </li> | |
189 | - <!-- 2024.02.16 PJH --> | |
190 | - <li | |
191 | - id="equipmentData" | |
192 | - class="flex align-center cursor" | |
193 | - @click="reOption" | |
194 | - > | |
195 | - <img | |
196 | - src="../../../resources/img/componentIcon/equipment.png" | |
197 | - /> | |
198 | - </li> | |
199 | - </ul> | |
200 | - </details> | |
201 | - </div> | |
202 | - </div> | |
203 | - </div> | |
204 | - </div> | |
205 | - </div> | |
206 | - | |
207 | - <!-- 미리보기 --> | |
208 | - <div | |
209 | - :class="{ | |
210 | - 'preview-zone': true, | |
211 | - flex65: accordion === false, | |
212 | - flex95: accordion === true, | |
213 | - }" | |
214 | - > | |
215 | - <div | |
216 | - id="splitter-container" | |
217 | - ref="splitterContainer" | |
218 | - @click="handleSplitterComponentClick" | |
219 | - :class="{ active: clickElement === 'splitter-container' }" | |
220 | - > | |
221 | - <SplitterLayout | |
222 | - v-if="showSplit" | |
223 | - id="SplitterLayout" | |
224 | - :splitInfo="splitInfo" | |
225 | - :clickElement="clickElement" | |
226 | - :optionChangeClick="optionChangeClick" | |
227 | - :componentOptn="componentOptn" | |
228 | - :inputVal="inputVal" | |
229 | - @parentInfo="parentInfo" | |
230 | - > | |
231 | - </SplitterLayout> | |
232 | - </div> | |
233 | - <div class="flex justify-end" id="buttonZone"> | |
234 | - <button | |
235 | - class="blue-border-btn small-btn" | |
236 | - id="registerButton" | |
237 | - @click="registerLayout" | |
238 | - > | |
239 | - 등록 | |
240 | - </button> | |
241 | - <button | |
242 | - class="orange-border-btn small-btn" | |
243 | - id="editButton" | |
244 | - style="display: none" | |
245 | - > | |
246 | - 수정 | |
247 | - </button> | |
248 | - <button | |
249 | - class="darkg-border-btn small-btn" | |
250 | - id="resetButton" | |
251 | - @click="resetLayout" | |
252 | - > | |
253 | - 초기화 | |
254 | - </button> | |
255 | - </div> | |
256 | - </div> | |
257 | - </div> | |
258 | - </div> | |
259 | - </div> | |
260 | - </div> | |
261 | -</template> | |
262 | - | |
263 | -<script> | |
264 | -import SplitterLayout from "../../../views/component/SplitterLayout.vue"; | |
265 | -import JobContainer from "../../component/connection/jobContainer.vue"; | |
266 | -import axios from "axios"; | |
267 | -import store from "../AppStore"; | |
268 | -import SvgIcon from "@jamescoyle/vue-icon"; | |
269 | -import { | |
270 | - mdiChevronLeft, | |
271 | - mdiChevronRightBox, | |
272 | - mdiClose, | |
273 | - mdiLandPlots, | |
274 | - mdiRhombusSplit, | |
275 | -} from "@mdi/js"; | |
276 | -import BackgroundOption from "../../component/style/BackgroundOption.vue"; | |
277 | -import CellOption from "../../component/style/CellOption.vue"; | |
278 | - | |
279 | -export default { | |
280 | - openPopup: { | |
281 | - type: Boolean, | |
282 | - default: false, | |
283 | - }, | |
284 | - data() { | |
285 | - return { | |
286 | - closePath: mdiClose, | |
287 | - // store: store, | |
288 | - tabMenu: [ | |
289 | - { tabName: "레이아웃", tabNic: "columnList", iconPath: mdiLandPlots }, | |
290 | - { | |
291 | - tabName: "컴포넌트", | |
292 | - tabNic: "viewSetting", | |
293 | - iconPath: mdiRhombusSplit, | |
294 | - }, | |
295 | - ], | |
296 | - activeTab: "columnList", | |
297 | - showSplit: false, // splitterLayout 태그 렌더링 여부 | |
298 | - clickElement: null, // 클릭한 요소의 id 값 들어가는 변수 | |
299 | - layoutVal: null, // 레이아웃 클릭에 따른 layoutNm 값을 위한 변수 | |
300 | - option2: false, | |
301 | - option1: true, | |
302 | - // 최상위 레이아웃 정보 담는 객체 | |
303 | - splitInfo: { | |
304 | - splitterId: null, // splitter 고유 id | |
305 | - layoutNm: null, // splitter 고유 id | |
306 | - type: null, // s, p | |
307 | - parentId: null, // 부모 splitterId, 없는 경우 null | |
308 | - children: [], // 자식 splitter, panel 정보 | |
309 | - index: null, // 위치 index | |
310 | - minWidth: 0, | |
311 | - minHeight: 0, | |
312 | - layoutType: null, // horizontal, vertical | |
313 | - sizes: [], // splitter 사이즈 | |
314 | - componentNm: null, // 컴포넌트 이름 | |
315 | - componentOption: {}, // 컴포넌트 옵션 정보 | |
316 | - }, | |
317 | - // 레이아웃 기본 정보 담는 객체 | |
318 | - splitChildInfo: { | |
319 | - layoutNm: null, // splitter 고유 id | |
320 | - type: null, // s, p | |
321 | - parentId: null, // 부모 splitterId, 없는 경우 null | |
322 | - children: [], // 자식 splitter, panel 정보 | |
323 | - index: null, // 위치 index | |
324 | - minWidth: 0, | |
325 | - minHeight: 0, | |
326 | - layoutType: null, // horizontal, vertical | |
327 | - sizes: [], // splitter 사이즈 | |
328 | - componentNm: null, // 컴포넌트 이름 | |
329 | - componentOption: {}, // 컴포넌트 옵션 정보 | |
330 | - }, | |
331 | - splitDepth: 0, // splitter 고유 id 생성을 위한 깊이 | |
332 | - | |
333 | - // 요소 옵션 정보 | |
334 | - componentOptn: { | |
335 | - key: null, | |
336 | - parent: null, | |
337 | - textData: "new title", | |
338 | - columnData: 0, | |
339 | - unitData: "%", | |
340 | - textSize: 16, | |
341 | - textAlign: null, | |
342 | - textStyle: null, | |
343 | - backColor: null, | |
344 | - }, | |
345 | - clickComponentParentInfo: null, | |
346 | - jobGroup: {}, | |
347 | - getDataTable: {}, | |
348 | - columnList: [], | |
349 | - | |
350 | - // 게시판 정보 | |
351 | - customTitle: null, // 제목 | |
352 | - customComment: null, // 설명 | |
353 | - | |
354 | - // 클릭한 요소의 data-type 값 | |
355 | - selectedOptionDataset: null, | |
356 | - selectedLayout: null, | |
357 | - isbackColor: false, | |
358 | - istextCk: true, | |
359 | - | |
360 | - // 2024.02.16 PJH | |
361 | - // Unity project 호출 정보 | |
362 | - projectInfo: { | |
363 | - command: "getProject", | |
364 | - projectId: "project1", | |
365 | - objectList: [ | |
366 | - { | |
367 | - command: "getObj", | |
368 | - projectId: "project1", | |
369 | - objectId: "object1", | |
370 | - presetId: "preset5", | |
371 | - position: { x: 0, y: 0.5, z: 0 }, | |
372 | - rotation: { x: 0, y: 0, z: 0 }, | |
373 | - pointList: [], | |
374 | - }, | |
375 | - ], | |
376 | - }, | |
377 | - projects: [], | |
378 | - objects: [], | |
379 | - points: [], | |
380 | - sensors: [], | |
381 | - message: {}, | |
382 | - changeInfo: null, | |
383 | - selectPointId: null, | |
384 | - nowSensorId: null, | |
385 | - pageId: null, | |
386 | - | |
387 | - selectPointId: null, | |
388 | - nowSensorId: null, | |
389 | - arrowPath: mdiChevronLeft, | |
390 | - accordion: false, | |
391 | - horizontalImg: require("../../../resources/img/icon/hor_b.png"), | |
392 | - verticalImg: require("../../../resources/img/icon/ver_b.png"), | |
393 | - }; | |
394 | - }, | |
395 | - methods: { | |
396 | - showTab: function (tabName) { | |
397 | - this.activeTab = tabName; | |
398 | - }, | |
399 | - accordionToggle: function () { | |
400 | - this.accordion = !this.accordion; | |
401 | - if (!this.accordion) { | |
402 | - this.path = mdiChevronLeftBox; | |
403 | - } else { | |
404 | - this.path = mdiChevronRightBox; | |
405 | - } | |
406 | - }, | |
407 | - // 2024.02.16 PJH | |
408 | - fetchData: function () { | |
409 | - let projectData = [ | |
410 | - { projectId: "project1", projectName: "모델1" }, | |
411 | - { projectId: "project2", projectName: "모델2" }, | |
412 | - ]; | |
413 | - this.projects = projectData; | |
414 | - | |
415 | - let objectData = [ | |
416 | - { | |
417 | - projectId: "project1", | |
418 | - objectId: "object1", | |
419 | - objectName: "오브젝트1", | |
420 | - presetId: "preset5", | |
421 | - position: { x: 0, y: 0.5, z: 0 }, | |
422 | - rotation: { x: 0, y: 0, z: 0 }, | |
423 | - }, | |
424 | - { | |
425 | - projectId: "project2", | |
426 | - objectId: "object2", | |
427 | - objectName: "오브젝트2", | |
428 | - presetId: "preset1", | |
429 | - position: { x: 0, y: 0.5, z: 0 }, | |
430 | - rotation: { x: 0, y: 0, z: 0 }, | |
431 | - }, | |
432 | - ]; | |
433 | - this.objects = objectData; | |
434 | - | |
435 | - let sensorData = [ | |
436 | - { sensorId: "component5", sensorName: "PM2 평량", sensorUse: false }, | |
437 | - { sensorId: "component6", sensorName: "PM2 수분", sensorUse: false }, | |
438 | - { | |
439 | - sensorId: "component7", | |
440 | - sensorName: "PM2 사용스팀", | |
441 | - sensorUse: false, | |
442 | - }, | |
443 | - { sensorId: "component8", sensorName: "PM2 보류제", sensorUse: false }, | |
444 | - { | |
445 | - sensorId: "component9", | |
446 | - sensorName: "PM2 벤토나이트", | |
447 | - sensorUse: false, | |
448 | - }, | |
449 | - { sensorId: "component10", sensorName: "PM2 지력제", sensorUse: false }, | |
450 | - { sensorId: "component13", sensorName: "PM3 평량", sensorUse: false }, | |
451 | - { sensorId: "component14", sensorName: "PM3 수분", sensorUse: false }, | |
452 | - { | |
453 | - sensorId: "component15", | |
454 | - sensorName: "PM3 사용스팀", | |
455 | - sensorUse: false, | |
456 | - }, | |
457 | - { sensorId: "component16", sensorName: "PM3 보류제", sensorUse: false }, | |
458 | - { | |
459 | - sensorId: "component17", | |
460 | - sensorName: "PM3 벤토나이트", | |
461 | - sensorUse: false, | |
462 | - }, | |
463 | - { sensorId: "component18", sensorName: "PM3 지력제", sensorUse: false }, | |
464 | - ]; | |
465 | - this.sensors = sensorData; | |
466 | - }, | |
467 | - reOption(e) { | |
468 | - this.selectedLayout = this.clickElement; | |
469 | - | |
470 | - const clickBtn = e.currentTarget.id; | |
471 | - if (!this.showSplit) { | |
472 | - alert("요소를 삽입할 레이아웃을 선택해주세요"); | |
473 | - return; | |
474 | - } | |
475 | - this.addComponentRecursiveFunc(this.splitInfo, clickBtn); | |
476 | - | |
477 | - this.option1 = !this.option1; | |
478 | - this.option2 = true; | |
479 | - }, | |
480 | - // 클릭한 요소의 부모 정보를 받아오는 이벤트 | |
481 | - parentInfo: function (parentInfoq) { | |
482 | - this.clickComponentParentInfo = parentInfoq; | |
483 | - }, | |
484 | - | |
485 | - // 레이아웃 아이콘 클릭에 따른 이벤트 | |
486 | - addSplitterLayout: function (e, buttonType) { | |
487 | - const vm = this; | |
488 | - | |
489 | - // vertical-btn, Horizon-btn 클릭에 따른 layoutVal 값 변경 | |
490 | - const clickBtn = e.currentTarget.id; | |
491 | - | |
492 | - // 기존 active 제거 | |
493 | - if (this.activeElement) { | |
494 | - this.activeElement.classList.remove("active-layout"); | |
495 | - } | |
496 | - | |
497 | - // 클릭된 요소에 active-layout 클래스 추가 | |
498 | - const targetClass = e.currentTarget.classList; | |
499 | - targetClass.add("active-layout"); | |
500 | - this.activeElement = e.currentTarget; | |
501 | - | |
502 | - if (clickBtn == "horizontal-btn") { | |
503 | - vm.layoutVal = "horizontal"; | |
504 | - } else if (clickBtn == "vertical-btn") { | |
505 | - vm.layoutVal = "vertical"; | |
506 | - } | |
507 | - | |
508 | - // 버튼 클릭에 따른 버튼 이미지 변경 | |
509 | - if (buttonType === "horizontal") { | |
510 | - this.horizontalImg = "../../../resources/img/icon/hor_a.png"; | |
511 | - } else if (buttonType === "vertical") { | |
512 | - this.verticalImg = "../../../resources/img/icon/ver_a.png"; | |
513 | - } | |
514 | - | |
515 | - // 첫 요소가 생기지 않은 상태 | |
516 | - if (!vm.showSplit) { | |
517 | - const createParentId = "split" + vm.splitDepth; | |
518 | - //부모의 정보 담기 | |
519 | - vm.splitInfo["layoutNm"] = createParentId; | |
520 | - vm.splitInfo["type"] = "s"; | |
521 | - vm.splitInfo["parentId"] = null; | |
522 | - vm.splitInfo["index"] = 1; | |
523 | - vm.splitInfo["minWidth"] = 0; | |
524 | - vm.splitInfo["minHeight"] = 0; | |
525 | - vm.splitInfo["layoutType"] = vm.layoutVal; | |
526 | - vm.splitInfo["sizes"] = [50, 50]; | |
527 | - vm.splitInfo["componentNm"] = null; | |
528 | - vm.splitInfo["componentOption"] = []; | |
529 | - for (let i = 0; i < 2; i++) { | |
530 | - const createChileId = createParentId + "-panel-" + (i + 1); | |
531 | - vm.splitChildInfo["layoutNm"] = createChileId; | |
532 | - vm.splitChildInfo["type"] = "s"; | |
533 | - vm.splitChildInfo["parentId"] = createParentId; | |
534 | - vm.splitChildInfo["index"] = i + 1; | |
535 | - vm.splitChildInfo["minWidth"] = 0; | |
536 | - vm.splitChildInfo["minHeight"] = 0; | |
537 | - vm.splitChildInfo["layoutType"] = null; | |
538 | - vm.splitChildInfo["sizes"] = []; | |
539 | - vm.splitChildInfo["componentNm"] = null; | |
540 | - vm.splitChildInfo["componentOption"] = []; | |
541 | - vm.splitInfo["children"].push( | |
542 | - JSON.parse(JSON.stringify(vm.splitChildInfo)) | |
543 | - ); | |
544 | - vm.resetChildrenInfo(); | |
545 | - } | |
546 | - vm.showSplit = true; | |
547 | - vm.splitDepth++; | |
548 | - } else { | |
549 | - // 최상위 레이아웃 변경 적용 | |
550 | - // SplitterLayout 하위가 있는지 확인 | |
551 | - if ( | |
552 | - vm.clickElement === null || | |
553 | - vm.clickElement == "splitter-container" | |
554 | - ) { | |
555 | - vm.splitInfo["layoutType"] = vm.layoutVal; | |
556 | - return; | |
557 | - } | |
558 | - // 재귀함수 호출 | |
559 | - this.recursiveFunc(vm.splitInfo); | |
560 | - } | |
561 | - }, | |
562 | - // 재귀함수 정의 | |
563 | - recursiveFunc: function (info) { | |
564 | - const vm = this; | |
565 | - //클릭된 요소가 '나'고 자식이 존재하면 layoutNm 변경 | |
566 | - if ( | |
567 | - info["layoutNm"] === this.clickElement && | |
568 | - info["children"].length !== 0 | |
569 | - ) { | |
570 | - const childLayout = info["children"][0]; | |
571 | - childLayout["layoutType"] = vm.layoutVal; | |
572 | - return; | |
573 | - } | |
574 | - //클릭된 요소가 '나'고 자식이 없으면 자식을 추가 | |
575 | - if ( | |
576 | - info["layoutNm"] === this.clickElement && | |
577 | - info["children"].length === 0 | |
578 | - ) { | |
579 | - if (info["componentNm"] !== null) { | |
580 | - if ( | |
581 | - confirm( | |
582 | - "삽입된 요소가 있습니다. 레이아웃을 변경하시겠습니까? /n 변경하면 삽인된 요소는 삭제됩니다." | |
583 | - ) === false | |
584 | - ) { | |
585 | - return; | |
586 | - } | |
587 | - } | |
588 | - const myId = "split" + vm.splitDepth; | |
589 | - | |
590 | - // 값을 추가해줘야함 | |
591 | - vm.splitChildInfo["layoutNm"] = myId; | |
592 | - vm.splitChildInfo["type"] = "s"; | |
593 | - vm.splitChildInfo["parentId"] = vm.clickElement; | |
594 | - vm.splitChildInfo["index"] = 1; | |
595 | - vm.splitChildInfo["minWidth"] = 0; | |
596 | - vm.splitChildInfo["minHeight"] = 0; | |
597 | - vm.splitChildInfo["layoutType"] = vm.layoutVal; | |
598 | - vm.splitChildInfo["sizes"] = [50, 50]; | |
599 | - vm.splitChildInfo["componentNm"] = null; | |
600 | - vm.splitChildInfo["componentOption"] = []; | |
601 | - info["children"].push(JSON.parse(JSON.stringify(vm.splitChildInfo))); | |
602 | - | |
603 | - const myInfo = info["children"][0]; | |
604 | - for (let n = 0; n < 2; n++) { | |
605 | - const childId = myId + "-panel-" + (n + 1); | |
606 | - vm.splitChildInfo["layoutNm"] = childId; | |
607 | - vm.splitChildInfo["type"] = "p"; | |
608 | - vm.splitChildInfo["parentId"] = myId; | |
609 | - vm.splitChildInfo["index"] = n + 1; | |
610 | - vm.splitChildInfo["children"] = []; | |
611 | - vm.splitChildInfo["minWidth"] = 0; | |
612 | - vm.splitChildInfo["minHeight"] = 0; | |
613 | - vm.splitChildInfo["layoutType"] = null; | |
614 | - vm.splitChildInfo["sizes"] = []; | |
615 | - vm.splitChildInfo["componentNm"] = null; | |
616 | - vm.splitChildInfo["componentOption"] = []; | |
617 | - myInfo["children"].push( | |
618 | - JSON.parse(JSON.stringify(vm.splitChildInfo)) | |
619 | - ); | |
620 | - vm.resetChildrenInfo(); | |
621 | - } | |
622 | - vm.splitDepth++; | |
623 | - return; | |
624 | - } | |
625 | - for (let i = 0; i < info["children"].length; i++) { | |
626 | - this.recursiveFunc(info["children"][i]); | |
627 | - } | |
628 | - }, | |
629 | - // 요소 삽입을 위한 재귀 함수 | |
630 | - addComponentRecursiveFunc: function (info, clickBtn) { | |
631 | - //클릭된 요소 componentName 변경 | |
632 | - if ( | |
633 | - info["layoutNm"] === this.clickElement && | |
634 | - info["children"].length === 0 | |
635 | - ) { | |
636 | - info["componentNm"] = clickBtn; | |
637 | - } else if ( | |
638 | - info["layoutNm"] === this.clickElement && | |
639 | - info["children"].length !== 0 | |
640 | - ) { | |
641 | - return; | |
642 | - } | |
643 | - | |
644 | - for (let i = 0; i < info["children"].length; i++) { | |
645 | - this.addComponentRecursiveFunc(info["children"][i], clickBtn); | |
646 | - } | |
647 | - }, | |
648 | - // 요소 선택 이벤트 구현 | |
649 | - addComponent: function (e) { | |
650 | - // 요소를 넣을 레이아웃 id값 저장 TODO: 레이아웃 영역만 저장되는가? | |
651 | - this.selectedLayout = this.clickElement; | |
652 | - | |
653 | - const clickBtn = e.currentTarget.id; | |
654 | - if (!this.showSplit) { | |
655 | - alert("요소를 삽입할 레이아웃을 선택해주세요"); | |
656 | - return; | |
657 | - } | |
658 | - | |
659 | - //클릭된 element 에 요소 컴포넌트 추가 | |
660 | - this.addComponentRecursiveFunc(this.splitInfo, clickBtn); | |
661 | - | |
662 | - this.option2 = !this.option2; | |
663 | - this.option1 = true; | |
664 | - }, | |
665 | - // input 값 저장 | |
666 | - saveInput: function (key, value) { | |
667 | - this.parentInfo["componentOption"][this.clickElement] = {}; | |
668 | - this.componentOptn[key] = value; | |
669 | - let isExist = this.parentInfo["componentOption"].hasOwnProperty( | |
670 | - this.clickElement | |
671 | - ); | |
672 | - | |
673 | - if (isExist) { | |
674 | - let currentOption = | |
675 | - this.parentInfo["componentOption"][this.clickElement]; | |
676 | - currentOption.textData = this.componentOptn.textData; | |
677 | - currentOption.columnData = this.componentOptn.columnData; | |
678 | - currentOption.unitData = this.componentOptn.unitData; | |
679 | - currentOption.unitCk = this.componentOptn.unitCk; | |
680 | - currentOption.textSize = this.componentOptn.textSize; | |
681 | - currentOption.textAlign = this.componentOptn.textAlign; | |
682 | - currentOption.textStyle = this.componentOptn.textStyle; | |
683 | - currentOption.backColor = this.componentOptn.backColor; | |
684 | - } else { | |
685 | - this.parentInfo["componentOption"][this.clickElement] = | |
686 | - this.componentOptn; | |
687 | - } | |
688 | - | |
689 | - this.parentInfo["componentOption"][this.clickElement] = | |
690 | - this.componentOptn; | |
691 | - }, | |
692 | - | |
693 | - // 미리보기 내부 splitter / component 클릭 이벤트 | |
694 | - handleSplitterComponentClick: function (e) { | |
695 | - const eventTarget = e.target.id; | |
696 | - this.clickElement = eventTarget; | |
697 | - }, | |
698 | - registerLayout: function () { | |
699 | - const vm = this; | |
700 | - | |
701 | - if (this.customTitle === null || this.customTitle === "") { | |
702 | - alert("제목을 입력해주세요"); | |
703 | - return; | |
704 | - } | |
705 | - if (!this.showSplit) { | |
706 | - alert("레이아웃 추가해 주세요"); | |
707 | - return; | |
708 | - } | |
709 | - | |
710 | - axios({ | |
711 | - url: "/custom/insert", | |
712 | - method: "post", | |
713 | - headers: { | |
714 | - "Content-Type": "application/json; charset=UTF-8", | |
715 | - }, | |
716 | - data: { | |
717 | - ttl: vm.customTitle, | |
718 | - cn: vm.customComment, | |
719 | - wrt_id: store.state.loginUser.user_id, | |
720 | - splitInfo: vm.splitInfo, | |
721 | - }, | |
722 | - }) | |
723 | - .then(function (response) { | |
724 | - alert("등록되었습니다."); | |
725 | - }) | |
726 | - .catch(function (error) { | |
727 | - this.$showAlert( | |
728 | - "에러 발생", | |
729 | - "에러가 발생했습니다. 관리자에게 문의해 주세요." | |
730 | - ); | |
731 | - }); | |
732 | - }, | |
733 | - optionChangeClick: function (e) { | |
734 | - // 요소 내 input 클릭 시 옵션 선택 탭으로 이동 | |
735 | - this.showTab("option"); | |
736 | - this.option2 = !this.option2; | |
737 | - this.option1 = true; | |
738 | - | |
739 | - //배경색상 커스텀 | |
740 | - if (e.target.dataset.isback) { | |
741 | - this.isbackColor = true; | |
742 | - } else { | |
743 | - this.isbackColor = false; | |
744 | - } | |
745 | - //텍스트 여부 체크 | |
746 | - if (e.target.dataset.type === "text") { | |
747 | - this.istextCk = true; | |
748 | - } else { | |
749 | - this.istextCk = false; | |
750 | - } | |
751 | - const targetId = e.target.id; | |
752 | - | |
753 | - // 클릭한 요소의 데이터를 input에 적용 | |
754 | - // key를 통해 있으면 데이터 가져와 적용 / 없으면 새로운 데이터 생성 | |
755 | - let isExist = this.parentInfo["componentOption"].hasOwnProperty(targetId); | |
756 | - // 요소 옵션 초기값으로 설정 | |
757 | - if (!isExist) { | |
758 | - this.componentOptn = { | |
759 | - columnData: "", | |
760 | - textData: "new Title", | |
761 | - textSize: null, | |
762 | - textAlign: "", | |
763 | - textStyle: "", | |
764 | - unitData: "%", | |
765 | - unitCk: true, | |
766 | - }; | |
767 | - } else { | |
768 | - // 있으면 해당 위치 찾아서 데이터 대입 | |
769 | - const currentOption = this.parentInfo["componentOption"][targetId]; | |
770 | - this.componentOptn = { | |
771 | - columnData: currentOption.columnData, | |
772 | - textData: currentOption.textData, | |
773 | - textSize: currentOption.textSize, | |
774 | - textAlign: currentOption.textAlign, | |
775 | - textStyle: currentOption.textStyle, | |
776 | - unitData: currentOption.unitData, | |
777 | - unitCk: currentOption.unitCk, | |
778 | - }; | |
779 | - } | |
780 | - }, | |
781 | - // 초기화 버튼 클릭 시 최상위 splitInfo 초기화 이벤트 | |
782 | - resetLayout: function () { | |
783 | - this.showSplit = false; | |
784 | - this.splitInfo = { | |
785 | - layoutNm: null, | |
786 | - parentId: null, | |
787 | - type: null, // s, p | |
788 | - children: [], | |
789 | - index: null, | |
790 | - componentNm: null, | |
791 | - componentOption: [], | |
792 | - }; | |
793 | - this.splitDepth = 0; | |
794 | - this.clickElement = ""; | |
795 | - }, | |
796 | - resetChildrenInfo: function () { | |
797 | - let vm = this; | |
798 | - vm.splitChildInfo.layoutNm = null; | |
799 | - vm.splitChildInfo.type = null; | |
800 | - vm.splitChildInfo.parentId = null; | |
801 | - vm.splitChildInfo.children = []; | |
802 | - vm.splitChildInfo.index = null; | |
803 | - vm.splitChildInfo.minWidth = 0; | |
804 | - vm.splitChildInfo.minHeight = 0; | |
805 | - vm.splitChildInfo.layoutType = null; | |
806 | - vm.splitChildInfo.sizes = []; | |
807 | - vm.splitChildInfo.componentNm = null; | |
808 | - vm.splitChildInfo.componentOption = {}; | |
809 | - }, | |
810 | - // jobContainer에서 받은 데이터 테이블 정보 | |
811 | - getResult: function (table) { | |
812 | - this.columnList = []; | |
813 | - for (let i = 0; i < table.columnDatas.length; i++) { | |
814 | - // rowData와 columnNm을 포함한 객체 생성 | |
815 | - let obj = { | |
816 | - rowData: table.rowData[0][i], | |
817 | - columnNm: table.columnDatas[i].columnNm, | |
818 | - }; | |
819 | - this.columnList.push(obj); | |
820 | - } | |
821 | - }, | |
822 | - selectJobGroup: function () { | |
823 | - let vm = this; | |
824 | - axios({ | |
825 | - url: "/job/selectJobGroup.json", | |
826 | - method: "post", | |
827 | - headers: { | |
828 | - "Content-Type": "application/json; charset=UTF-8", | |
829 | - }, | |
830 | - data: JSON.stringify({ group_id: "JOBGROUP_2024020517352987547982" }), | |
831 | - }) | |
832 | - .then(function (response) { | |
833 | - vm.jobGroup = response.data.resultData.jobGroup; | |
834 | - }) | |
835 | - .catch(function (error) { | |
836 | - this.$showAlert( | |
837 | - "에러 발생", | |
838 | - "에러가 발생했습니다. 관리자에게 문의해 주세요." | |
839 | - ); | |
840 | - }); | |
841 | - }, | |
842 | - optionModal: function () { | |
843 | - this.isModal = !this.isModal; | |
844 | - }, | |
845 | - }, | |
846 | - components: { | |
847 | - SplitterLayout, | |
848 | - JobContainer: JobContainer, | |
849 | - SvgIcon: SvgIcon, | |
850 | - BackgroundOption: BackgroundOption, | |
851 | - CellOption: CellOption, | |
852 | - }, | |
853 | - mounted() { | |
854 | - this.activeTab; | |
855 | - this.jobGroup = this.$getDefaultJobGroup().jobGroup; | |
856 | - this.pageId = this.$route.query.pageId; | |
857 | - }, | |
858 | -}; | |
859 | -</script> | |
860 | - | |
861 | -<style scoped> | |
862 | -#splitter-container { | |
863 | - width: 100%; | |
864 | - height: calc(100% - 50px); | |
865 | - border: 1px solid #aaa; | |
866 | - margin-bottom: 20px; | |
867 | -} | |
868 | -#SplitterLayout { | |
869 | - width: 100%; | |
870 | - height: 100%; | |
871 | -} | |
872 | -#splitter-container.active { | |
873 | - border: 3px dotted red; | |
874 | -} | |
875 | -#customComment { | |
876 | - height: 99px; | |
877 | -} | |
878 | -.table-padding { | |
879 | - padding: 15px 0; | |
880 | -} | |
881 | -.paletteLi { | |
882 | - display: inline-block; | |
883 | - margin-right: 10px; | |
884 | -} | |
885 | -</style>(파일 끝에 줄바꿈 문자 없음) |
--- client/views/pages/custom/CustomInsert2.vue
... | ... | @@ -1,893 +0,0 @@ |
1 | -<template> | |
2 | - <div class="container"> | |
3 | - <div class="page-titleZone flex justify-between align-start"> | |
4 | - <p class="main-title">데이터 현황관리 차트 등록</p> | |
5 | - <!-- <button @click="test">test</button> --> | |
6 | - <PageNavigation /> | |
7 | - </div> | |
8 | - <div class="content-wrap content"> | |
9 | - <div class="row"> | |
10 | - <JobContainer :jobGroup="jobGroup" @getDataTable="getResult" /> | |
11 | - </div> | |
12 | - <div class="row custom-tab"> | |
13 | - <div class="content-box flex justify-between align-start"> | |
14 | - <div | |
15 | - :class="{ | |
16 | - 'tab-zone': true, | |
17 | - flex40: accordion === false, | |
18 | - flex5: accordion === true, | |
19 | - }" | |
20 | - > | |
21 | - <div class="tab-nav flex justify-between align-center"> | |
22 | - <ul | |
23 | - class="flex justify-start" | |
24 | - :style="accordion === true ? { display: 'none' } : {}" | |
25 | - > | |
26 | - <li | |
27 | - v-for="(tab, idx) in tabMenu" | |
28 | - :key="idx" | |
29 | - @click="showTab(tab.tabNic)" | |
30 | - > | |
31 | - <a :class="{ activeTab: activeTab === tab.tabNic }">{{ | |
32 | - tab.tabName | |
33 | - }}</a> | |
34 | - </li> | |
35 | - </ul> | |
36 | - <button @click="accordionToggle"> | |
37 | - <svg-icon type="mdi" :path="path" :color="'#333'"></svg-icon> | |
38 | - </button> | |
39 | - </div> | |
40 | - <div | |
41 | - class="tab-content" | |
42 | - :style="accordion === true ? { display: 'none' } : {}" | |
43 | - > | |
44 | - <div class="content-box flex" v-show="activeTab === 'columnList'"> | |
45 | - <div | |
46 | - class="content-list layout flex-column justify-between flex40" | |
47 | - > | |
48 | - <div class="layout-tree"> | |
49 | - <span class="section-title mb10">레이아웃 리스트</span> | |
50 | - <ul class="" style="height: 30%"></ul> | |
51 | - </div> | |
52 | - <div class="layout-zone"> | |
53 | - <span class="section-title mb10">레이아웃</span> | |
54 | - <ul class="layout-content flex justify-center align-center"> | |
55 | - <li | |
56 | - id="horizontal-btn" | |
57 | - class="cursor" | |
58 | - @click="addSplitterLayout" | |
59 | - title="수직레이아웃" | |
60 | - > | |
61 | - <!-- 레이아웃 id 부여 --> | |
62 | - <p | |
63 | - class="vertical-icon flex justify-around align-center" | |
64 | - > | |
65 | - <span></span> | |
66 | - <span></span> | |
67 | - </p> | |
68 | - </li> | |
69 | - <li | |
70 | - id="vertical-btn" | |
71 | - class="cursor" | |
72 | - @click="addSplitterLayout" | |
73 | - title="수평레이아웃" | |
74 | - > | |
75 | - <!-- 레이아웃 id 부여 --> | |
76 | - <p | |
77 | - class="flex-column justify-around align-center horizental-icon" | |
78 | - > | |
79 | - <span></span> | |
80 | - <span></span> | |
81 | - </p> | |
82 | - </li> | |
83 | - </ul> | |
84 | - </div> | |
85 | - <div class="btn-wrap"> | |
86 | - <button @click="optionModal" class="blue-btn large-btn"> | |
87 | - 레이아웃 옵션 | |
88 | - </button> | |
89 | - </div> | |
90 | - </div> | |
91 | - <div class="layout-option flex-column justify-between flex60"> | |
92 | - <div class="modal-title"> | |
93 | - <div class="flex justify-between align-center"> | |
94 | - <span class="section-title">레이아웃 옵션</span> | |
95 | - <button class="close-btn" @click="closeModal"> | |
96 | - <svg-icon | |
97 | - type="mdi" | |
98 | - :width="20" | |
99 | - :height="20" | |
100 | - :path="closePath" | |
101 | - ></svg-icon> | |
102 | - </button> | |
103 | - </div> | |
104 | - </div> | |
105 | - <div class="modal-content-monthly"> | |
106 | - <CellOption /> | |
107 | - <BackgroundOption /> | |
108 | - </div> | |
109 | - <div class="modal-end flex justify-end"> | |
110 | - <button class="gray-border-btn small-btn">취소</button> | |
111 | - <button class="blue-border-btn small-btn">적용</button> | |
112 | - </div> | |
113 | - </div> | |
114 | - </div> | |
115 | - <div class="content-box" v-show="activeTab === 'viewSetting'"> | |
116 | - <div class="content-list"> | |
117 | - <div class="component-zone"> | |
118 | - <details> | |
119 | - <summary>요소 선택</summary> | |
120 | - <ul class="component-content"> | |
121 | - <li | |
122 | - id="HorizentalTwoData" | |
123 | - class="flex align-center cursor" | |
124 | - @click="addComponent" | |
125 | - > | |
126 | - <img | |
127 | - src="../../../resources/img/componentIcon/icon2.png" | |
128 | - /> | |
129 | - </li> | |
130 | - <li | |
131 | - id="ThreeData" | |
132 | - class="flex align-center cursor" | |
133 | - @click="addComponent" | |
134 | - > | |
135 | - <img | |
136 | - src="../../../resources/img/componentIcon/icon1.png" | |
137 | - /> | |
138 | - </li> | |
139 | - <li | |
140 | - id="HorizentalThreeData" | |
141 | - class="flex align-center cursor" | |
142 | - @click="addComponent" | |
143 | - > | |
144 | - <img | |
145 | - src="../../../resources/img/componentIcon/icon5.png" | |
146 | - /> | |
147 | - </li> | |
148 | - <li | |
149 | - id="TableData" | |
150 | - class="flex align-center cursor" | |
151 | - @click="addComponent" | |
152 | - > | |
153 | - <img | |
154 | - src="../../../resources/img/componentIcon/icon3.png" | |
155 | - /> | |
156 | - </li> | |
157 | - <li class="flex align-center cursor"> | |
158 | - <img | |
159 | - src="../../../resources/img/componentIcon/icon4.png" | |
160 | - /> | |
161 | - </li> | |
162 | - <!-- 2024.02.16 PJH --> | |
163 | - <li | |
164 | - id="equipmentData" | |
165 | - class="flex align-center cursor" | |
166 | - @click="reOption" | |
167 | - > | |
168 | - <img | |
169 | - src="../../../resources/img/componentIcon/equipment.png" | |
170 | - /> | |
171 | - </li> | |
172 | - </ul> | |
173 | - </details> | |
174 | - </div> | |
175 | - <div class="chart-zone"> | |
176 | - <details> | |
177 | - <summary>chart 선택</summary> | |
178 | - <ul class="component-content flex justify-between"> | |
179 | - <li class="flex30"> | |
180 | - <img | |
181 | - src="../../../resources/img/chartIcon/bar_b.png" | |
182 | - /> | |
183 | - </li> | |
184 | - <li id="HorizentalTwoData" class="flex30"> | |
185 | - <img | |
186 | - src="../../../resources/img/chartIcon/rowbar_b.png" | |
187 | - /> | |
188 | - </li> | |
189 | - <li id="ThreeData" class="flex30"> | |
190 | - <img | |
191 | - src="../../../resources/img/chartIcon/line_b.png" | |
192 | - /> | |
193 | - </li> | |
194 | - <li id="HorizentalThreeData" class="flex30"> | |
195 | - <img | |
196 | - src="../../../resources/img/chartIcon/pie_b.png" | |
197 | - /> | |
198 | - </li> | |
199 | - <li id="TableData" class="flex30"> | |
200 | - <img | |
201 | - src="../../../resources/img/chartIcon/dounet_b.png" | |
202 | - /> | |
203 | - </li> | |
204 | - <li class="flex30"> | |
205 | - <img | |
206 | - src="../../../resources/img/chartIcon/colbarline_b.png" | |
207 | - /> | |
208 | - </li> | |
209 | - <li class="flex30"> | |
210 | - <img | |
211 | - src="../../../resources/img/chartIcon/barline_b.png" | |
212 | - /> | |
213 | - </li> | |
214 | - <li class="flex30"> | |
215 | - <img | |
216 | - src="../../../resources/img/chartIcon/brokenline_b.png" | |
217 | - /> | |
218 | - </li> | |
219 | - <li class="flex30"> | |
220 | - <img | |
221 | - src="../../../resources/img/chartIcon/line2_b.png" | |
222 | - /> | |
223 | - </li> | |
224 | - </ul> | |
225 | - </details> | |
226 | - </div> | |
227 | - </div> | |
228 | - </div> | |
229 | - </div> | |
230 | - </div> | |
231 | - | |
232 | - <!-- 미리보기 --> | |
233 | - <div | |
234 | - :class="{ | |
235 | - 'preview-zone': true, | |
236 | - flex60: accordion === false, | |
237 | - flex95: accordion === true, | |
238 | - }" | |
239 | - > | |
240 | - <div | |
241 | - id="splitter-container" | |
242 | - ref="splitterContainer" | |
243 | - @click="handleSplitterComponentClick" | |
244 | - :class="{ active: clickElement === 'splitter-container' }" | |
245 | - > | |
246 | - <SplitterLayout | |
247 | - v-if="showSplit" | |
248 | - id="SplitterLayout" | |
249 | - :splitInfo="splitInfo" | |
250 | - :clickElement="clickElement" | |
251 | - :optionChangeClick="optionChangeClick" | |
252 | - :componentOptn="componentOptn" | |
253 | - :inputVal="inputVal" | |
254 | - @parentInfo="parentInfo" | |
255 | - > | |
256 | - </SplitterLayout> | |
257 | - </div> | |
258 | - <div class="flex justify-end" id="buttonZone"> | |
259 | - <button | |
260 | - class="blue-border-btn small-btn" | |
261 | - id="registerButton" | |
262 | - @click="registerLayout" | |
263 | - > | |
264 | - 등록 | |
265 | - </button> | |
266 | - <button | |
267 | - class="orange-border-btn small-btn" | |
268 | - id="editButton" | |
269 | - style="display: none" | |
270 | - > | |
271 | - 수정 | |
272 | - </button> | |
273 | - <button | |
274 | - class="darkg-border-btn small-btn" | |
275 | - id="resetButton" | |
276 | - @click="resetLayout" | |
277 | - > | |
278 | - 초기화 | |
279 | - </button> | |
280 | - </div> | |
281 | - </div> | |
282 | - </div> | |
283 | - </div> | |
284 | - </div> | |
285 | - </div> | |
286 | -</template> | |
287 | - | |
288 | -<script> | |
289 | -import SplitterLayout from "../../component/SplitterLayout.vue"; | |
290 | -import JobContainer from "../../component/connection/jobContainer.vue"; | |
291 | -import axios from "axios"; | |
292 | -import store from "../AppStore"; | |
293 | -import SvgIcon from "@jamescoyle/vue-icon"; | |
294 | -import { mdiChevronLeftBox, mdiChevronRightBox, mdiClose } from "@mdi/js"; | |
295 | -import BackgroundOption from "../../component/style/BackgroundOption.vue"; | |
296 | -import CellOption from "../../component/style/CellOption.vue"; | |
297 | - | |
298 | -export default { | |
299 | - openPopup: { | |
300 | - type: Boolean, | |
301 | - default: false, | |
302 | - }, | |
303 | - data() { | |
304 | - return { | |
305 | - closePath: mdiClose, | |
306 | - // store: store, | |
307 | - tabMenu: [ | |
308 | - { tabName: "레이아웃", tabNic: "columnList" }, | |
309 | - { tabName: "컴포넌트", tabNic: "viewSetting" }, | |
310 | - ], | |
311 | - activeTab: "columnList", | |
312 | - showSplit: false, // splitterLayout 태그 렌더링 여부 | |
313 | - clickElement: null, // 클릭한 요소의 id 값 들어가는 변수 | |
314 | - layoutVal: null, // 레이아웃 클릭에 따른 layoutNm 값을 위한 변수 | |
315 | - option2: false, | |
316 | - option1: true, | |
317 | - // 최상위 레이아웃 정보 담는 객체 | |
318 | - splitInfo: { | |
319 | - splitterId: null, // splitter 고유 id | |
320 | - layoutNm: null, // splitter 고유 id | |
321 | - type: null, // s, p | |
322 | - parentId: null, // 부모 splitterId, 없는 경우 null | |
323 | - children: [], // 자식 splitter, panel 정보 | |
324 | - index: null, // 위치 index | |
325 | - minWidth: 0, | |
326 | - minHeight: 0, | |
327 | - layoutType: null, // horizontal, vertical | |
328 | - sizes: [], // splitter 사이즈 | |
329 | - componentNm: null, // 컴포넌트 이름 | |
330 | - componentOption: {}, // 컴포넌트 옵션 정보 | |
331 | - }, | |
332 | - // 레이아웃 기본 정보 담는 객체 | |
333 | - splitChildInfo: { | |
334 | - layoutNm: null, // splitter 고유 id | |
335 | - type: null, // s, p | |
336 | - parentId: null, // 부모 splitterId, 없는 경우 null | |
337 | - children: [], // 자식 splitter, panel 정보 | |
338 | - index: null, // 위치 index | |
339 | - minWidth: 0, | |
340 | - minHeight: 0, | |
341 | - layoutType: null, // horizontal, vertical | |
342 | - sizes: [], // splitter 사이즈 | |
343 | - componentNm: null, // 컴포넌트 이름 | |
344 | - componentOption: {}, // 컴포넌트 옵션 정보 | |
345 | - }, | |
346 | - splitDepth: 0, // splitter 고유 id 생성을 위한 깊이 | |
347 | - | |
348 | - // 요소 옵션 정보 | |
349 | - componentOptn: { | |
350 | - key: null, | |
351 | - parent: null, | |
352 | - textData: "new title", | |
353 | - columnData: 0, | |
354 | - unitData: "%", | |
355 | - textSize: 16, | |
356 | - textAlign: null, | |
357 | - textStyle: null, | |
358 | - backColor: null, | |
359 | - }, | |
360 | - clickComponentParentInfo: null, | |
361 | - jobGroup: {}, | |
362 | - getDataTable: {}, | |
363 | - columnList: [], | |
364 | - | |
365 | - // 게시판 정보 | |
366 | - customTitle: null, // 제목 | |
367 | - customComment: null, // 설명 | |
368 | - | |
369 | - // 클릭한 요소의 data-type 값 | |
370 | - selectedOptionDataset: null, | |
371 | - selectedLayout: null, | |
372 | - isbackColor: false, | |
373 | - istextCk: true, | |
374 | - | |
375 | - // 2024.02.16 PJH | |
376 | - // Unity project 호출 정보 | |
377 | - projectInfo: { | |
378 | - command: "getProject", | |
379 | - projectId: "project1", | |
380 | - objectList: [ | |
381 | - { | |
382 | - command: "getObj", | |
383 | - projectId: "project1", | |
384 | - objectId: "object1", | |
385 | - presetId: "preset5", | |
386 | - position: { x: 0, y: 0.5, z: 0 }, | |
387 | - rotation: { x: 0, y: 0, z: 0 }, | |
388 | - pointList: [], | |
389 | - }, | |
390 | - ], | |
391 | - }, | |
392 | - projects: [], | |
393 | - objects: [], | |
394 | - points: [], | |
395 | - sensors: [], | |
396 | - message: {}, | |
397 | - changeInfo: null, | |
398 | - selectPointId: null, | |
399 | - nowSensorId: null, | |
400 | - pageId: null, | |
401 | - | |
402 | - selectPointId: null, | |
403 | - nowSensorId: null, | |
404 | - path: mdiChevronLeftBox, | |
405 | - accordion: false, | |
406 | - }; | |
407 | - }, | |
408 | - methods: { | |
409 | - showTab: function (tabName) { | |
410 | - this.activeTab = tabName; | |
411 | - }, | |
412 | - accordionToggle: function () { | |
413 | - this.accordion = !this.accordion; | |
414 | - if (!this.accordion) { | |
415 | - this.path = mdiChevronLeftBox; | |
416 | - } else { | |
417 | - this.path = mdiChevronRightBox; | |
418 | - } | |
419 | - }, | |
420 | - // 2024.02.16 PJH | |
421 | - fetchData: function () { | |
422 | - let projectData = [ | |
423 | - { projectId: "project1", projectName: "모델1" }, | |
424 | - { projectId: "project2", projectName: "모델2" }, | |
425 | - ]; | |
426 | - this.projects = projectData; | |
427 | - | |
428 | - let objectData = [ | |
429 | - { | |
430 | - projectId: "project1", | |
431 | - objectId: "object1", | |
432 | - objectName: "오브젝트1", | |
433 | - presetId: "preset5", | |
434 | - position: { x: 0, y: 0.5, z: 0 }, | |
435 | - rotation: { x: 0, y: 0, z: 0 }, | |
436 | - }, | |
437 | - { | |
438 | - projectId: "project2", | |
439 | - objectId: "object2", | |
440 | - objectName: "오브젝트2", | |
441 | - presetId: "preset1", | |
442 | - position: { x: 0, y: 0.5, z: 0 }, | |
443 | - rotation: { x: 0, y: 0, z: 0 }, | |
444 | - }, | |
445 | - ]; | |
446 | - this.objects = objectData; | |
447 | - | |
448 | - let sensorData = [ | |
449 | - { sensorId: "component5", sensorName: "PM2 평량", sensorUse: false }, | |
450 | - { sensorId: "component6", sensorName: "PM2 수분", sensorUse: false }, | |
451 | - { | |
452 | - sensorId: "component7", | |
453 | - sensorName: "PM2 사용스팀", | |
454 | - sensorUse: false, | |
455 | - }, | |
456 | - { sensorId: "component8", sensorName: "PM2 보류제", sensorUse: false }, | |
457 | - { | |
458 | - sensorId: "component9", | |
459 | - sensorName: "PM2 벤토나이트", | |
460 | - sensorUse: false, | |
461 | - }, | |
462 | - { sensorId: "component10", sensorName: "PM2 지력제", sensorUse: false }, | |
463 | - { sensorId: "component13", sensorName: "PM3 평량", sensorUse: false }, | |
464 | - { sensorId: "component14", sensorName: "PM3 수분", sensorUse: false }, | |
465 | - { | |
466 | - sensorId: "component15", | |
467 | - sensorName: "PM3 사용스팀", | |
468 | - sensorUse: false, | |
469 | - }, | |
470 | - { sensorId: "component16", sensorName: "PM3 보류제", sensorUse: false }, | |
471 | - { | |
472 | - sensorId: "component17", | |
473 | - sensorName: "PM3 벤토나이트", | |
474 | - sensorUse: false, | |
475 | - }, | |
476 | - { sensorId: "component18", sensorName: "PM3 지력제", sensorUse: false }, | |
477 | - ]; | |
478 | - this.sensors = sensorData; | |
479 | - }, | |
480 | - reOption(e) { | |
481 | - this.selectedLayout = this.clickElement; | |
482 | - | |
483 | - const clickBtn = e.currentTarget.id; | |
484 | - if (!this.showSplit) { | |
485 | - alert("요소를 삽입할 레이아웃을 선택해주세요"); | |
486 | - return; | |
487 | - } | |
488 | - this.addComponentRecursiveFunc(this.splitInfo, clickBtn); | |
489 | - | |
490 | - this.option1 = !this.option1; | |
491 | - this.option2 = true; | |
492 | - }, | |
493 | - // 클릭한 요소의 부모 정보를 받아오는 이벤트 | |
494 | - parentInfo: function (parentInfoq) { | |
495 | - this.clickComponentParentInfo = parentInfoq; | |
496 | - }, | |
497 | - | |
498 | - // 레이아웃 아이콘 클릭에 따른 이벤트 | |
499 | - addSplitterLayout: function (e) { | |
500 | - const vm = this; | |
501 | - | |
502 | - // vertical-btn, Horizon-btn 클릭에 따른 layoutVal 값 변경 | |
503 | - const clickBtn = e.currentTarget.id; | |
504 | - | |
505 | - // 기존 active 제거 | |
506 | - if (this.activeElement) { | |
507 | - this.activeElement.classList.remove("active-layout"); | |
508 | - } | |
509 | - | |
510 | - // 클릭된 요소에 active-layout 클래스 추가 | |
511 | - const targetClass = e.currentTarget.classList; | |
512 | - targetClass.add("active-layout"); | |
513 | - this.activeElement = e.currentTarget; | |
514 | - | |
515 | - if (clickBtn == "horizontal-btn") { | |
516 | - vm.layoutVal = "horizontal"; | |
517 | - } else if (clickBtn == "vertical-btn") { | |
518 | - vm.layoutVal = "vertical"; | |
519 | - } | |
520 | - | |
521 | - // 첫 요소가 생기지 않은 상태 | |
522 | - if (!vm.showSplit) { | |
523 | - const createParentId = "split" + vm.splitDepth; | |
524 | - //부모의 정보 담기 | |
525 | - vm.splitInfo["layoutNm"] = createParentId; | |
526 | - vm.splitInfo["type"] = "s"; | |
527 | - vm.splitInfo["parentId"] = null; | |
528 | - vm.splitInfo["index"] = 1; | |
529 | - vm.splitInfo["minWidth"] = 0; | |
530 | - vm.splitInfo["minHeight"] = 0; | |
531 | - vm.splitInfo["layoutType"] = vm.layoutVal; | |
532 | - vm.splitInfo["sizes"] = [50, 50]; | |
533 | - vm.splitInfo["componentNm"] = null; | |
534 | - vm.splitInfo["componentOption"] = []; | |
535 | - for (let i = 0; i < 2; i++) { | |
536 | - const createChileId = createParentId + "-panel-" + (i + 1); | |
537 | - vm.splitChildInfo["layoutNm"] = createChileId; | |
538 | - vm.splitChildInfo["type"] = "s"; | |
539 | - vm.splitChildInfo["parentId"] = createParentId; | |
540 | - vm.splitChildInfo["index"] = i + 1; | |
541 | - vm.splitChildInfo["minWidth"] = 0; | |
542 | - vm.splitChildInfo["minHeight"] = 0; | |
543 | - vm.splitChildInfo["layoutType"] = null; | |
544 | - vm.splitChildInfo["sizes"] = []; | |
545 | - vm.splitChildInfo["componentNm"] = null; | |
546 | - vm.splitChildInfo["componentOption"] = []; | |
547 | - vm.splitInfo["children"].push( | |
548 | - JSON.parse(JSON.stringify(vm.splitChildInfo)) | |
549 | - ); | |
550 | - vm.resetChildrenInfo(); | |
551 | - } | |
552 | - vm.showSplit = true; | |
553 | - vm.splitDepth++; | |
554 | - } else { | |
555 | - // 최상위 레이아웃 변경 적용 | |
556 | - // SplitterLayout 하위가 있는지 확인 | |
557 | - if ( | |
558 | - vm.clickElement === null || | |
559 | - vm.clickElement == "splitter-container" | |
560 | - ) { | |
561 | - vm.splitInfo["layoutType"] = vm.layoutVal; | |
562 | - return; | |
563 | - } | |
564 | - // 재귀함수 호출 | |
565 | - this.recursiveFunc(vm.splitInfo); | |
566 | - } | |
567 | - }, | |
568 | - // 재귀함수 정의 | |
569 | - recursiveFunc: function (info) { | |
570 | - const vm = this; | |
571 | - //클릭된 요소가 '나'고 자식이 존재하면 layoutNm 변경 | |
572 | - if ( | |
573 | - info["layoutNm"] === this.clickElement && | |
574 | - info["children"].length !== 0 | |
575 | - ) { | |
576 | - const childLayout = info["children"][0]; | |
577 | - childLayout["layoutType"] = vm.layoutVal; | |
578 | - return; | |
579 | - } | |
580 | - //클릭된 요소가 '나'고 자식이 없으면 자식을 추가 | |
581 | - if ( | |
582 | - info["layoutNm"] === this.clickElement && | |
583 | - info["children"].length === 0 | |
584 | - ) { | |
585 | - if (info["componentNm"] !== null) { | |
586 | - if ( | |
587 | - confirm( | |
588 | - "삽입된 요소가 있습니다. 레이아웃을 변경하시겠습니까? /n 변경하면 삽인된 요소는 삭제됩니다." | |
589 | - ) === false | |
590 | - ) { | |
591 | - return; | |
592 | - } | |
593 | - } | |
594 | - const myId = "split" + vm.splitDepth; | |
595 | - | |
596 | - // 값을 추가해줘야함 | |
597 | - vm.splitChildInfo["layoutNm"] = myId; | |
598 | - vm.splitChildInfo["type"] = "s"; | |
599 | - vm.splitChildInfo["parentId"] = vm.clickElement; | |
600 | - vm.splitChildInfo["index"] = 1; | |
601 | - vm.splitChildInfo["minWidth"] = 0; | |
602 | - vm.splitChildInfo["minHeight"] = 0; | |
603 | - vm.splitChildInfo["layoutType"] = vm.layoutVal; | |
604 | - vm.splitChildInfo["sizes"] = [50, 50]; | |
605 | - vm.splitChildInfo["componentNm"] = null; | |
606 | - vm.splitChildInfo["componentOption"] = []; | |
607 | - info["children"].push(JSON.parse(JSON.stringify(vm.splitChildInfo))); | |
608 | - | |
609 | - const myInfo = info["children"][0]; | |
610 | - for (let n = 0; n < 2; n++) { | |
611 | - const childId = myId + "-panel-" + (n + 1); | |
612 | - vm.splitChildInfo["layoutNm"] = childId; | |
613 | - vm.splitChildInfo["type"] = "p"; | |
614 | - vm.splitChildInfo["parentId"] = myId; | |
615 | - vm.splitChildInfo["index"] = n + 1; | |
616 | - vm.splitChildInfo["children"] = []; | |
617 | - vm.splitChildInfo["minWidth"] = 0; | |
618 | - vm.splitChildInfo["minHeight"] = 0; | |
619 | - vm.splitChildInfo["layoutType"] = null; | |
620 | - vm.splitChildInfo["sizes"] = []; | |
621 | - vm.splitChildInfo["componentNm"] = null; | |
622 | - vm.splitChildInfo["componentOption"] = []; | |
623 | - myInfo["children"].push( | |
624 | - JSON.parse(JSON.stringify(vm.splitChildInfo)) | |
625 | - ); | |
626 | - vm.resetChildrenInfo(); | |
627 | - } | |
628 | - vm.splitDepth++; | |
629 | - return; | |
630 | - } | |
631 | - for (let i = 0; i < info["children"].length; i++) { | |
632 | - this.recursiveFunc(info["children"][i]); | |
633 | - } | |
634 | - }, | |
635 | - // 요소 삽입을 위한 재귀 함수 | |
636 | - addComponentRecursiveFunc: function (info, clickBtn) { | |
637 | - //클릭된 요소 componentName 변경 | |
638 | - if ( | |
639 | - info["layoutNm"] === this.clickElement && | |
640 | - info["children"].length === 0 | |
641 | - ) { | |
642 | - info["componentNm"] = clickBtn; | |
643 | - } else if ( | |
644 | - info["layoutNm"] === this.clickElement && | |
645 | - info["children"].length !== 0 | |
646 | - ) { | |
647 | - return; | |
648 | - } | |
649 | - | |
650 | - for (let i = 0; i < info["children"].length; i++) { | |
651 | - this.addComponentRecursiveFunc(info["children"][i], clickBtn); | |
652 | - } | |
653 | - }, | |
654 | - // 요소 선택 이벤트 구현 | |
655 | - addComponent: function (e) { | |
656 | - // 요소를 넣을 레이아웃 id값 저장 TODO: 레이아웃 영역만 저장되는가? | |
657 | - this.selectedLayout = this.clickElement; | |
658 | - | |
659 | - const clickBtn = e.currentTarget.id; | |
660 | - if (!this.showSplit) { | |
661 | - alert("요소를 삽입할 레이아웃을 선택해주세요"); | |
662 | - return; | |
663 | - } | |
664 | - | |
665 | - //클릭된 element 에 요소 컴포넌트 추가 | |
666 | - this.addComponentRecursiveFunc(this.splitInfo, clickBtn); | |
667 | - | |
668 | - this.option2 = !this.option2; | |
669 | - this.option1 = true; | |
670 | - }, | |
671 | - // input 값 저장 | |
672 | - saveInput: function (key, value) { | |
673 | - this.parentInfo["componentOption"][this.clickElement] = {}; | |
674 | - this.componentOptn[key] = value; | |
675 | - let isExist = this.parentInfo["componentOption"].hasOwnProperty( | |
676 | - this.clickElement | |
677 | - ); | |
678 | - | |
679 | - if (isExist) { | |
680 | - let currentOption = | |
681 | - this.parentInfo["componentOption"][this.clickElement]; | |
682 | - currentOption.textData = this.componentOptn.textData; | |
683 | - currentOption.columnData = this.componentOptn.columnData; | |
684 | - currentOption.unitData = this.componentOptn.unitData; | |
685 | - currentOption.unitCk = this.componentOptn.unitCk; | |
686 | - currentOption.textSize = this.componentOptn.textSize; | |
687 | - currentOption.textAlign = this.componentOptn.textAlign; | |
688 | - currentOption.textStyle = this.componentOptn.textStyle; | |
689 | - currentOption.backColor = this.componentOptn.backColor; | |
690 | - } else { | |
691 | - this.parentInfo["componentOption"][this.clickElement] = | |
692 | - this.componentOptn; | |
693 | - } | |
694 | - | |
695 | - this.parentInfo["componentOption"][this.clickElement] = | |
696 | - this.componentOptn; | |
697 | - }, | |
698 | - | |
699 | - // 미리보기 내부 splitter / component 클릭 이벤트 | |
700 | - handleSplitterComponentClick: function (e) { | |
701 | - const eventTarget = e.target.id; | |
702 | - this.clickElement = eventTarget; | |
703 | - }, | |
704 | - registerLayout: function () { | |
705 | - const vm = this; | |
706 | - | |
707 | - if (this.customTitle === null || this.customTitle === "") { | |
708 | - alert("제목을 입력해주세요"); | |
709 | - return; | |
710 | - } | |
711 | - if (!this.showSplit) { | |
712 | - alert("레이아웃 추가해 주세요"); | |
713 | - return; | |
714 | - } | |
715 | - | |
716 | - axios({ | |
717 | - url: "/custom/insert", | |
718 | - method: "post", | |
719 | - headers: { | |
720 | - "Content-Type": "application/json; charset=UTF-8", | |
721 | - }, | |
722 | - data: { | |
723 | - ttl: vm.customTitle, | |
724 | - cn: vm.customComment, | |
725 | - wrt_id: store.state.loginUser.user_id, | |
726 | - /* jobGroup:this.jobGroup, | |
727 | - getDataTable: this.getDataTable,*/ | |
728 | - splitInfo: vm.splitInfo, | |
729 | - }, | |
730 | - }) | |
731 | - .then(function (response) { | |
732 | - alert("등록되었습니다."); | |
733 | - }) | |
734 | - .catch(function (error) { | |
735 | - this.$showAlert( | |
736 | - "에러 발생", | |
737 | - "에러가 발생했습니다. 관리자에게 문의해 주세요." | |
738 | - ); | |
739 | - }); | |
740 | - }, | |
741 | - optionChangeClick: function (e) { | |
742 | - // 요소 내 input 클릭 시 옵션 선택 탭으로 이동 | |
743 | - this.showTab("option"); | |
744 | - this.option2 = !this.option2; | |
745 | - this.option1 = true; | |
746 | - | |
747 | - //배경색상 커스텀 | |
748 | - if (e.target.dataset.isback) { | |
749 | - this.isbackColor = true; | |
750 | - } else { | |
751 | - this.isbackColor = false; | |
752 | - } | |
753 | - //텍스트 여부 체크 | |
754 | - if (e.target.dataset.type === "text") { | |
755 | - this.istextCk = true; | |
756 | - } else { | |
757 | - this.istextCk = false; | |
758 | - } | |
759 | - const targetId = e.target.id; | |
760 | - | |
761 | - // 클릭한 요소의 데이터를 input에 적용 | |
762 | - // key를 통해 있으면 데이터 가져와 적용 / 없으면 새로운 데이터 생성 | |
763 | - let isExist = this.parentInfo["componentOption"].hasOwnProperty(targetId); | |
764 | - // 요소 옵션 초기값으로 설정 | |
765 | - if (!isExist) { | |
766 | - this.componentOptn = { | |
767 | - columnData: "", | |
768 | - textData: "new Title", | |
769 | - textSize: null, | |
770 | - textAlign: "", | |
771 | - textStyle: "", | |
772 | - unitData: "%", | |
773 | - unitCk: true, | |
774 | - }; | |
775 | - } else { | |
776 | - // 있으면 해당 위치 찾아서 데이터 대입 | |
777 | - const currentOption = this.parentInfo["componentOption"][targetId]; | |
778 | - this.componentOptn = { | |
779 | - columnData: currentOption.columnData, | |
780 | - textData: currentOption.textData, | |
781 | - textSize: currentOption.textSize, | |
782 | - textAlign: currentOption.textAlign, | |
783 | - textStyle: currentOption.textStyle, | |
784 | - unitData: currentOption.unitData, | |
785 | - unitCk: currentOption.unitCk, | |
786 | - }; | |
787 | - } | |
788 | - }, | |
789 | - // 초기화 버튼 클릭 시 최상위 splitInfo 초기화 이벤트 | |
790 | - resetLayout: function () { | |
791 | - this.showSplit = false; | |
792 | - this.splitInfo = { | |
793 | - layoutNm: null, | |
794 | - parentId: null, | |
795 | - type: null, // s, p | |
796 | - children: [], | |
797 | - index: null, | |
798 | - componentNm: null, | |
799 | - componentOption: [], | |
800 | - }; | |
801 | - this.splitDepth = 0; | |
802 | - this.clickElement = ""; | |
803 | - }, | |
804 | - resetChildrenInfo: function () { | |
805 | - let vm = this; | |
806 | - vm.splitChildInfo.layoutNm = null; | |
807 | - vm.splitChildInfo.type = null; | |
808 | - vm.splitChildInfo.parentId = null; | |
809 | - vm.splitChildInfo.children = []; | |
810 | - vm.splitChildInfo.index = null; | |
811 | - vm.splitChildInfo.minWidth = 0; | |
812 | - vm.splitChildInfo.minHeight = 0; | |
813 | - vm.splitChildInfo.layoutType = null; | |
814 | - vm.splitChildInfo.sizes = []; | |
815 | - vm.splitChildInfo.componentNm = null; | |
816 | - vm.splitChildInfo.componentOption = {}; | |
817 | - }, | |
818 | - // jobContainer에서 받은 데이터 테이블 정보 | |
819 | - getResult: function (table) { | |
820 | - this.columnList = []; | |
821 | - for (let i = 0; i < table.columnDatas.length; i++) { | |
822 | - // rowData와 columnNm을 포함한 객체 생성 | |
823 | - let obj = { | |
824 | - rowData: table.rowData[0][i], | |
825 | - columnNm: table.columnDatas[i].columnNm, | |
826 | - }; | |
827 | - this.columnList.push(obj); | |
828 | - } | |
829 | - }, | |
830 | - selectJobGroup: function () { | |
831 | - let vm = this; | |
832 | - axios({ | |
833 | - url: "/job/selectJobGroup.json", | |
834 | - method: "post", | |
835 | - headers: { | |
836 | - "Content-Type": "application/json; charset=UTF-8", | |
837 | - }, | |
838 | - data: JSON.stringify({ group_id: "JOBGROUP_2024020517352987547982" }), | |
839 | - }) | |
840 | - .then(function (response) { | |
841 | - vm.jobGroup = response.data.resultData.jobGroup; | |
842 | - }) | |
843 | - .catch(function (error) { | |
844 | - this.$showAlert( | |
845 | - "에러 발생", | |
846 | - "에러가 발생했습니다. 관리자에게 문의해 주세요." | |
847 | - ); | |
848 | - }); | |
849 | - }, | |
850 | - optionModal: function () { | |
851 | - this.isModal = !this.isModal; | |
852 | - }, | |
853 | - }, | |
854 | - components: { | |
855 | - SplitterLayout, | |
856 | - JobContainer: JobContainer, | |
857 | - SvgIcon: SvgIcon, | |
858 | - BackgroundOption: BackgroundOption, | |
859 | - CellOption: CellOption, | |
860 | - }, | |
861 | - mounted() { | |
862 | - this.activeTab; | |
863 | - this.jobGroup = this.$getDefaultJobGroup().jobGroup; | |
864 | - this.pageId = this.$route.query.pageId; | |
865 | - }, | |
866 | -}; | |
867 | -</script> | |
868 | - | |
869 | -<style scoped> | |
870 | -#splitter-container { | |
871 | - width: 100%; | |
872 | - height: calc(100% - 50px); | |
873 | - border: 1px solid #aaa; | |
874 | - margin-bottom: 20px; | |
875 | -} | |
876 | -#SplitterLayout { | |
877 | - width: 100%; | |
878 | - height: 100%; | |
879 | -} | |
880 | -#splitter-container.active { | |
881 | - border: 3px dotted red; | |
882 | -} | |
883 | -#customComment { | |
884 | - height: 99px; | |
885 | -} | |
886 | -.table-padding { | |
887 | - padding: 15px 0; | |
888 | -} | |
889 | -.paletteLi { | |
890 | - display: inline-block; | |
891 | - margin-right: 10px; | |
892 | -} | |
893 | -</style>(파일 끝에 줄바꿈 문자 없음) |
--- client/views/pages/custom/CustomInsertDev.vue
... | ... | @@ -1,1012 +0,0 @@ |
1 | -<template> | |
2 | - <div class="container rrmse-wrap" :style="isLoading ? { cursor: 'wait' } : {}"> | |
3 | - <!-- 로딩 시작 { --> | |
4 | - <div v-if="isLoading" class="loading-overlay"> | |
5 | - <div class="loading-div"> | |
6 | - <span>LOADING</span> | |
7 | - <span class="anima">.</span> | |
8 | - <span class="anima">.</span> | |
9 | - <span class="anima">.</span> | |
10 | - </div> | |
11 | - </div> | |
12 | - <!-- } 로딩 종료 --> | |
13 | - <div class="page-titleZone flex justify-between align-center align-start"> | |
14 | - <p class="main-title flex80">데이터 활용관리 {{ !isUpdatePage ? '등록' : '수정' }}</p> | |
15 | - </div> | |
16 | - <div class="content-wrap content flex100"> | |
17 | - <div class="custom-tab content-box"> | |
18 | - <div class="flex justify-start align-center no-gutter content-box"> | |
19 | - <!-- 네비게이션 시작 { --> | |
20 | - <div class="column-nav flex5"> | |
21 | - <ul> | |
22 | - <li v-for="(item, idx) in navList" :key="idx" @click="showTab('nav', item.id)" class="cursor"> | |
23 | - <a :class="{ activeTab: activeTab === item.id }" :title="item.name"> | |
24 | - <svg-icon type="mdi" :path="item.iconPath" class="mb5"></svg-icon> | |
25 | - <p>{{ item.name }}</p> | |
26 | - </a> | |
27 | - </li> | |
28 | - </ul> | |
29 | - </div> | |
30 | - <!-- } 네비게이션 종료 --> | |
31 | - <!-- 네비게이션 화면 { --> | |
32 | - <div class="content-box flex justify-between align-start flex95 no-gutter"> | |
33 | - <div :class="{ | |
34 | - 'tab-zone flex align-start': true, | |
35 | - flex30: accordion === false, | |
36 | - flex0: accordion === true, | |
37 | - }"> | |
38 | - <div class="content-box flex100 pl0" :style="accordion === true ? { display: 'none' } : ''"> | |
39 | - <div class="content-box flex justify-between tab-box overflow-y" v-show="activeTab === 'pageInfo'"> | |
40 | - <div class="content-list layout flex100"> | |
41 | - <div class="page-info content-box"> | |
42 | - <div class="content-titleZone"> | |
43 | - <div class="flex justify-between align-center"> | |
44 | - <p class="box-title">페이지 정보</p> | |
45 | - </div> | |
46 | - </div> | |
47 | - <div class="info-area"> | |
48 | - <div class="input-group mb10"> | |
49 | - <label for="" class="mb10">제목</label> | |
50 | - <input type="text" name="" id="" class="full-input" v-model="customTitle" @input="customTitle = $event.target.value" /> | |
51 | - </div> | |
52 | - <div class="input-group mb10"> | |
53 | - <label for="" class="mb10">공개여부</label> | |
54 | - <div class="input-container flex"> | |
55 | - <label class="radio-label"> | |
56 | - <input type="radio" name="public_at" class="custom-radiobox" :value="true" v-model="public_at" /> | |
57 | - <span>공개</span> | |
58 | - </label> | |
59 | - <label class="radio-label"> | |
60 | - <input type="radio" name="public_at" class="custom-radiobox" :value="false" v-model="public_at" /> | |
61 | - <span>비공개</span> | |
62 | - </label> | |
63 | - </div> | |
64 | - </div> | |
65 | - <div class="input-group" style="height: calc(100% - 150px)"> | |
66 | - <label for="" class="mb10">설명</label> | |
67 | - <textarea name="" id="" cols="30" rows="35" v-model="customComment" @input="customComment = $event.target.value" class="content-box"></textarea> | |
68 | - </div> | |
69 | - </div> | |
70 | - </div> | |
71 | - </div> | |
72 | - </div> | |
73 | - <div class="content-box tab-box" v-show="activeTab === 'dataset'"> | |
74 | - <div class="content-list content-box"> | |
75 | - <VueFlowZoneGroup :diagramList="diagramList" :splitInfo="splitInfo" @axisData="handlerAxisData" @clickDiagramDataTable="getDiagramDataInfo" @selectedCal="changeSelectCal" @updateSplitInfo="updateSplitInfo" :layoutChartData="selectLayout" /> | |
76 | - </div> | |
77 | - </div> | |
78 | - <div class="content-box flex justify-between tab-box" v-show="activeTab === 'viewSetting'"> | |
79 | - <div class="content-list layout flex50"> | |
80 | - <div class="layout-tree content-box"> | |
81 | - <div class="content-titleZone"> | |
82 | - <div class="flex justify-between align-center"> | |
83 | - <p class="box-title">레이아웃 및 컴포넌트</p> | |
84 | - <div> | |
85 | - <button id="horizontal-btn" @click="addSplitterLayout($event, 'vertical')" title="수직레이아웃"> | |
86 | - <img :src="verticalImg" /> | |
87 | - </button> | |
88 | - <button id="vertical-btn" @click="addSplitterLayout($event, 'horizontal')" title="수평레이아웃"> | |
89 | - <img :src="horizontalImg" /> | |
90 | - </button> | |
91 | - <button id="split-delete-btn" @click="deleteSplitterLayout" title="삭제"> | |
92 | - <svg-icon type="mdi" :path="trashPath" :color="'#aaa'"></svg-icon> | |
93 | - </button> | |
94 | - <button class="attribute-btn" @click="attributeSelected"> | |
95 | - <svg-icon type="mdi" :path="dotPath" :color="'#aaa'"></svg-icon> | |
96 | - <div class="attribute-modal" v-show="attribute"> | |
97 | - <ul> | |
98 | - <li class="flex align-center"> | |
99 | - <svg-icon type="mdi" :path="tablePath"></svg-icon> | |
100 | - <p class="ml5">테이블 추가</p> | |
101 | - </li> | |
102 | - <li class="flex align-center"> | |
103 | - <svg-icon type="mdi" :path="chartPath"></svg-icon> | |
104 | - <p class="ml5">차트 추가</p> | |
105 | - </li> | |
106 | - </ul> | |
107 | - </div> | |
108 | - </button> | |
109 | - </div> | |
110 | - </div> | |
111 | - </div> | |
112 | - <ul> | |
113 | - <TreeItem :splitInfo="splitInfo" :selectLayout="selectLayout" @changeselectLayout="changeselectLayout" /> | |
114 | - </ul> | |
115 | - </div> | |
116 | - </div> | |
117 | - <div class="layout-option flex-column flex50"> | |
118 | - <div class="tabnav3"> | |
119 | - <ul class="flex justify-start align-center"> | |
120 | - <li v-for="(nav, idx) in optionList" @click="showTab('opt', nav.name)" :key="idx" class="cursor"> | |
121 | - <p :class="{ | |
122 | - activeOption: activeOption === nav.name, | |
123 | - }">{{ nav.name }}</p> | |
124 | - </li> | |
125 | - </ul> | |
126 | - </div> | |
127 | - <div v-show="activeOption === '레이아웃'"> | |
128 | - <StyleSheet :styleSheet="selectLayout.styleSheet" /> | |
129 | - </div> | |
130 | - <div v-show="activeOption === '컴포넌트'"> | |
131 | - <StyleSheet :styleSheet="selectLayout.styleSheet" /> | |
132 | - </div> | |
133 | - <div v-show="activeOption === '타이틀'"> | |
134 | - <TitleStyleSheet :component="selectLayout.component" v-if="selectLayout.component" /> | |
135 | - </div> | |
136 | - </div> | |
137 | - </div> | |
138 | - <div class="content-box flex justify-between tab-box" v-show="activeTab === 'component'"> | |
139 | - <div class="content-box overflow-y"> | |
140 | - <ul class="flex justify-center"> | |
141 | - <li v-for="(cmpnt, idx) in chartComponent" :key="idx" class="flex5 mt10 cursor" @click=" | |
142 | - addComponent(idx, cmpnt.type, cmpnt.id, cmpnt.name) | |
143 | - "> | |
144 | - <div :class="{ 'img-wrap border': true, active: isActiveIndex(idx) }"> | |
145 | - <img :src="cmpnt.src" alt="" /> | |
146 | - <div class="img-hover"> | |
147 | - <span>{{ cmpnt.name }}</span> | |
148 | - </div> | |
149 | - </div> | |
150 | - </li> | |
151 | - </ul> | |
152 | - </div> | |
153 | - </div> | |
154 | - </div> | |
155 | - <button @click="accordionToggle" class="custom-toggle" :style="accordion === true ? { right: '-25px' } : { right: '-14px' }"> | |
156 | - <svg-icon type="mdi" :path="arrowPath" :color="'#333'"></svg-icon> | |
157 | - </button> | |
158 | - </div> | |
159 | - <!-- 미리보기 --> | |
160 | - <div :class="{ | |
161 | - 'preview-zone': true, | |
162 | - flex70: accordion === false, | |
163 | - flex95: accordion === true, | |
164 | - }"> | |
165 | - <div id="splitter-container" ref="splitterContainer" @click="handleSplitterComponentClick" :class="{ active: clickElement === 'splitter-container' }" class="splitter-panel-custom"> | |
166 | - <SplitterLayout id="SplitterLayout" :splitInfo="splitInfo_dumy" @changeselectLayout="changeselectLayout" :selectLayout="selectLayout" @parentInfo="parentInfo" :createChartData="createChartData"> | |
167 | - </SplitterLayout> | |
168 | - </div> | |
169 | - <div class="flex justify-end" id="buttonZone"> | |
170 | - <button v-if="!isUpdatePage" class="blue-btn small-btn" id="registerButton" @click="registerLayout">등록</button> | |
171 | - <button v-else class="blue-btn small-btn" id="editButton" @click="registerLayout">수정</button> | |
172 | - <button class="blue-border-btn small-btn" id="resetButton" @click="initClickEvent">초기화</button> | |
173 | - <button class="darkg-btn small-btn" id="resetButton" @click="moveBack">취소</button> | |
174 | - </div> | |
175 | - </div> | |
176 | - <!-- 미리보기 --> | |
177 | - </div> | |
178 | - <!-- } 네비게이션 화면 종료 --> | |
179 | - </div> | |
180 | - </div> | |
181 | - </div> | |
182 | - </div> | |
183 | - <!-- 데이터 설정 모달 시작 { --> | |
184 | - <div v-show="isModalOpen" class="modal-wrapper"> | |
185 | - <div class="modal-container"> | |
186 | - <div class="modal-title"> | |
187 | - <div class="flex justify-between align-center"> | |
188 | - <h2>데이터설정</h2> | |
189 | - <button class="close-btn" @click="closeModal"> | |
190 | - <svg-icon type="mdi" :width="20" :height="20" :path="closePath"></svg-icon> | |
191 | - </button> | |
192 | - </div> | |
193 | - </div> | |
194 | - <div class="modal-content-monthly"> | |
195 | - <JobContainer :jobGroup="jobGroup" @getDataTable="getResult" /> | |
196 | - </div> | |
197 | - <div class="modal-end flex justify-end"> | |
198 | - <button class="orange-btn small-btn">접속</button> | |
199 | - <button class="blue-btn small-btn">확인</button> | |
200 | - </div> | |
201 | - </div> | |
202 | - </div> | |
203 | - <!-- } 데이터 설정 모달 종료 --> | |
204 | -</template> | |
205 | -<script> | |
206 | -import SplitterLayout from "../../component/SplitterLayoutDev.vue"; | |
207 | -import JobContainer from "../../component/connection/jobContainer.vue"; | |
208 | -import axios from "axios"; | |
209 | -import store from "../AppStore"; | |
210 | -import SvgIcon from "@jamescoyle/vue-icon"; | |
211 | -import TreeItem from "../../component/LayoutTree.vue"; | |
212 | -import { mdiChevronLeft, mdiChevronRight, mdiLandPlots, mdiRhombusSplit, mdiDatabaseCogOutline, mdiInformationSlabCircleOutline, mdiDotsVertical, mdiTableLarge, mdiChartLine, mdiDatabase, mdiTrashCanOutline, } from "@mdi/js"; | |
213 | -import StyleSheet from "../../component/style/StyleSheetComponent.vue"; | |
214 | -import TitleStyleSheet from "../../component/style/TitleStyleComponent.vue"; | |
215 | -import BackgroundOption from "../../component/style/BackgroundOption.vue"; | |
216 | -import CellOption from "../../component/style/CellOption.vue"; | |
217 | -import FontOption from "../../component/style/FontOption.vue"; | |
218 | -import VueFlowZoneGroup from "../../component/flowComponent/VueFlowZoneGroup.vue"; | |
219 | -import _ from "lodash"; | |
220 | -import chartDataTransform from "../../component/chart/chartDataTransform.js"; | |
221 | -import html2canvas from "html2canvas"; | |
222 | -import { split } from "lodash"; | |
223 | - | |
224 | -export default { | |
225 | - openPopup: { | |
226 | - type: Boolean, | |
227 | - default: false, | |
228 | - }, | |
229 | - data() { | |
230 | - return { | |
231 | - navList: [ | |
232 | - { id: "pageInfo", name: "페이지 정보", iconPath: mdiInformationSlabCircleOutline, }, | |
233 | - { id: "viewSetting", name: "레이아웃", iconPath: mdiLandPlots }, | |
234 | - { id: "component", name: "컴포넌트", iconPath: mdiRhombusSplit }, | |
235 | - { id: "dataset", name: "데이터", iconPath: mdiDatabaseCogOutline, }, | |
236 | - ], | |
237 | - optionList: [ | |
238 | - { id: "layoutOption", name: "레이아웃", }, | |
239 | - { id: "titleOption", name: "타이틀", }, | |
240 | - { id: "componentOption", name: "컴포넌트", }, | |
241 | - ], | |
242 | - activeTab: "pageInfo", // default 페이지 정보 | |
243 | - | |
244 | - // 최상위 레이아웃 정보 담는 객체 | |
245 | - splitInfo: _.cloneDeep(this.$getDefaultJobGroup().customSplitter), | |
246 | - splitInfo_dumy: _.cloneDeep(this.$getDefaultJobGroup().customSplitter), | |
247 | - jobGroup: {}, | |
248 | - getDataTable: {}, | |
249 | - columnList: [], | |
250 | - | |
251 | - // 게시판 정보 | |
252 | - customTitle: null, // 제목 | |
253 | - customComment: null, // 설명 | |
254 | - | |
255 | - selectLayout: {}, | |
256 | - arrowPath: mdiChevronLeft, | |
257 | - accordion: false, | |
258 | - horizontalImg: require("../../../resources/img/icon/hor_b.png"), | |
259 | - verticalImg: require("../../../resources/img/icon/ver_b.png"), | |
260 | - dotPath: mdiDotsVertical, | |
261 | - tablePath: mdiTableLarge, | |
262 | - chartPath: mdiChartLine, | |
263 | - dataPath: mdiDatabase, | |
264 | - attribute: false, | |
265 | - dataItm: [], | |
266 | - diagramList: [], | |
267 | - chartComponent: [ | |
268 | - { | |
269 | - src: require("../../../resources/img/chartIcon/column_chart_v.png"), | |
270 | - type: "chart", | |
271 | - id: "column_chart_v", | |
272 | - name: "세로형 막대 차트", | |
273 | - }, | |
274 | - { | |
275 | - src: require("../../../resources/img/chartIcon/column_chart_h.png"), | |
276 | - type: "chart", | |
277 | - id: "column_chart_h", | |
278 | - name: "가로형 막대 차트", | |
279 | - }, | |
280 | - { | |
281 | - src: require("../../../resources/img/chartIcon/clustered_chart_v.png"), | |
282 | - type: "chart", | |
283 | - id: "clustered_chart_v", | |
284 | - name: "세로형 클러스터 차트", | |
285 | - }, | |
286 | - { | |
287 | - src: require("../../../resources/img/chartIcon/clustered_chart_h.png"), | |
288 | - type: "chart", | |
289 | - id: "clustered_chart_h", | |
290 | - name: "가로형 클러스터 차트", | |
291 | - }, | |
292 | - { | |
293 | - src: require("../../../resources/img/chartIcon/stacked_bar_chart.png"), | |
294 | - type: "chart", | |
295 | - id: "stacked_bar_chart", | |
296 | - name: "적층 막대 차트", | |
297 | - }, | |
298 | - { | |
299 | - src: require("../../../resources/img/chartIcon/line_chart.png"), | |
300 | - type: "chart", | |
301 | - id: "line_chart", | |
302 | - name: "선 차트", | |
303 | - }, | |
304 | - { | |
305 | - src: require("../../../resources/img/chartIcon/mix_chart.png"), | |
306 | - type: "chart", | |
307 | - id: "mix_chart", | |
308 | - name: "혼합 차트", | |
309 | - }, | |
310 | - { | |
311 | - src: require("../../../resources/img/chartIcon/pie_chart.png"), | |
312 | - type: "chart", | |
313 | - id: "pie_chart", | |
314 | - name: "파이 차트", | |
315 | - }, | |
316 | - { | |
317 | - src: require("../../../resources/img/chartIcon/dounet_chart.png"), | |
318 | - type: "chart", | |
319 | - id: "dounet_chart", | |
320 | - name: "도넛 차트", | |
321 | - }, | |
322 | - { | |
323 | - src: require("../../../resources/img/chartIcon/semicircle_chart.png"), | |
324 | - type: "chart", | |
325 | - id: "semicircle_chart", | |
326 | - name: "반원 차트", | |
327 | - }, | |
328 | - { | |
329 | - src: require("../../../resources/img/chartIcon/bubble_chart.png"), | |
330 | - type: "chart", | |
331 | - id: "bubble_chart", | |
332 | - name: "버블 차트", | |
333 | - }, | |
334 | - { | |
335 | - src: require("../../../resources/img/chartIcon/word_chart.png"), | |
336 | - type: "chart", | |
337 | - id: "word_chart", | |
338 | - name: "단어 차트", | |
339 | - }, | |
340 | - { | |
341 | - src: require("../../../resources/img/chartIcon/stacked_column_chart.png"), | |
342 | - type: "chart", | |
343 | - id: "stacked_column_chart", | |
344 | - name: "세로형 적층 막대 차트", | |
345 | - }, | |
346 | - { | |
347 | - src: require("../../../resources/img/chartIcon/node_line_chart.png"), | |
348 | - type: "chart", | |
349 | - id: "node_line_chart", | |
350 | - name: "노드 라인 차트", | |
351 | - }, | |
352 | - { | |
353 | - src: require("../../../resources/img/chartIcon/liin_chart_back.png"), | |
354 | - type: "chart", | |
355 | - id: "liin_chart_back", | |
356 | - name: "라인 백그라운드 차트", | |
357 | - }, | |
358 | - ], | |
359 | - activeIndex: null, | |
360 | - dataCount: 1, | |
361 | - isModalOpen: false, | |
362 | - activeOption: "레이아웃", | |
363 | - trashPath: mdiTrashCanOutline, | |
364 | - | |
365 | - // x, y축 (객체 형식으로 데이터 수정 필요) | |
366 | - createChartData: _.cloneDeep(this.$getDefaultJobGroup().componentData), | |
367 | - clickComponentParentInfo: {}, | |
368 | - rowData: [], // 선택한 다이어그램의 데이터 테이블을 담는 객체 | |
369 | - clickDiagramInfo: {}, // 선택한 다이어그램의 정보를 담는 객체 | |
370 | - | |
371 | - pageId: null, | |
372 | - isLoading: false, | |
373 | - isUpdatePage: false, | |
374 | - | |
375 | - public_at: true, | |
376 | - }; | |
377 | - }, | |
378 | - methods: { | |
379 | - // 탭 전환 | |
380 | - showTab(type, current) { | |
381 | - if (type == 'nav') { | |
382 | - this.activeTab = current; | |
383 | - } else if (type == 'opt') { | |
384 | - this.activeOption = current; | |
385 | - } | |
386 | - }, | |
387 | - accordionToggle: function () { | |
388 | - this.accordion = !this.accordion; | |
389 | - if (!this.accordion) { | |
390 | - this.arrowPath = mdiChevronLeft; | |
391 | - } else { | |
392 | - this.arrowPath = mdiChevronRight; | |
393 | - } | |
394 | - }, | |
395 | - setModal: function () { | |
396 | - this.isModalOpen = true; | |
397 | - }, | |
398 | - closeModal: function () { | |
399 | - this.isModalOpen = false; | |
400 | - }, | |
401 | - attributeSelected: function () { | |
402 | - this.attribute = !this.attribute; | |
403 | - }, | |
404 | - | |
405 | - dataAdd: function () { | |
406 | - this.dataItm.push("Data" + this.dataCount); | |
407 | - this.dataCount++; | |
408 | - }, | |
409 | - | |
410 | - // 컴포넌트 추가 | |
411 | - addComponent: async function (index, type, id, name) { | |
412 | - this.activeIndex = index; | |
413 | - if (id == "bubble_chart") { | |
414 | - this.$showAlert("메세지", "해당 차트는 현재 지원하지 않습니다."); | |
415 | - return; | |
416 | - } | |
417 | - if ( | |
418 | - await this.$showConfirm( | |
419 | - "컴포넌트 추가", | |
420 | - id + "컴포넌트를 추가하시겠습니까?" | |
421 | - ) | |
422 | - ) { | |
423 | - if (this.selectLayout.children.length > 0) { | |
424 | - this.$showAlert("메세지", "레이아웃이 존재합니다."); | |
425 | - return; | |
426 | - } | |
427 | - if (this.selectLayout == null) { | |
428 | - this.$showAlert("메세지", "선택된 레이아웃이 없습니다."); | |
429 | - return; | |
430 | - } | |
431 | - this.selectLayout.component = {}; | |
432 | - this.selectLayout.se = "component"; | |
433 | - let component = _.cloneDeep(this.$getDefaultJobGroup().component); | |
434 | - let chartData = _.cloneDeep(this.$getDefaultJobGroup().componentData); | |
435 | - let jobGroup = _.cloneDeep(this.$getDefaultJobGroup().jobGroup); | |
436 | - | |
437 | - // 차트 생성 데이터 세팅 (chartName, componentName) | |
438 | - chartData.chart_knd = id; | |
439 | - chartData.component_nm = "ChartCmmn"; | |
440 | - chartData.component_type = type; | |
441 | - | |
442 | - component.component_itm = chartData; | |
443 | - component.jobInfo = []; | |
444 | - component.jobInfo.push(jobGroup); | |
445 | - | |
446 | - this.selectLayout.component = component; | |
447 | - } else { | |
448 | - this.getSelectLayoutChart(); | |
449 | - } | |
450 | - }, | |
451 | - getSelectLayoutChart: function () { | |
452 | - // 현재 선택된 컴포넌트 차트 정보로 변경 | |
453 | - if (this.selectLayout.se == "component") { | |
454 | - if (!this.selectLayout.component.component_itm.chart_knd) { | |
455 | - this.activeIndex = null; | |
456 | - return; | |
457 | - } | |
458 | - this.chartComponent.forEach((item, index) => { | |
459 | - if (item.id === this.selectLayout.component.component_itm.chart_knd) { | |
460 | - this.activeIndex = index; | |
461 | - } | |
462 | - }); | |
463 | - } | |
464 | - }, | |
465 | - | |
466 | - isActiveIndex: function (index) { | |
467 | - return this.activeIndex === index; | |
468 | - }, | |
469 | - | |
470 | - // 선택된 레이어웃 반환 | |
471 | - changeselectLayout: function (selectLayout) { | |
472 | - this.selectLayout = selectLayout; | |
473 | - // 차트 리스트 중 인덱스 찾아서 activeIndex에 할당 | |
474 | - this.getSelectLayoutChart(); | |
475 | - }, | |
476 | - | |
477 | - // 클릭한 요소의 부모 정보를 받아오는 이벤트 | |
478 | - parentInfo: function (info) { | |
479 | - this.clickComponentParentInfo = info; | |
480 | - }, | |
481 | - | |
482 | - // 레이아웃 아이콘 클릭에 따른 이벤트 | |
483 | - addSplitterLayout: async function (e, buttonType) { | |
484 | - const vm = this; | |
485 | - // vertical-btn, Horizon-btn 클릭에 따른 layoutVal 값 변경 | |
486 | - const clickBtn = e.currentTarget.id; | |
487 | - | |
488 | - let layoutVal = "horizontal"; | |
489 | - | |
490 | - // 레이아웃 확장 | |
491 | - let extend = false; | |
492 | - // 기존 active 제거 | |
493 | - if (this.activeElement) { | |
494 | - this.activeElement.classList.remove("active-layout"); | |
495 | - } | |
496 | - // 클릭된 요소에 active-layout 클래스 추가 | |
497 | - const targetClass = e.currentTarget.classList; | |
498 | - targetClass.add("active-layout"); | |
499 | - | |
500 | - this.activeElement = e.currentTarget; | |
501 | - if (clickBtn == "horizontal-btn") { | |
502 | - layoutVal = "horizontal"; | |
503 | - } else if (clickBtn == "vertical-btn") { | |
504 | - layoutVal = "vertical"; | |
505 | - } | |
506 | - | |
507 | - // 버튼 클릭에 따른 버튼 이미지 변경 | |
508 | - if (buttonType === "horizontal") { | |
509 | - this.verticalImg = require("../../../resources/img/icon/ver_b.png"); | |
510 | - this.horizontalImg = require("../../../resources/img/icon/hor_a.png"); | |
511 | - } else if (buttonType === "vertical") { | |
512 | - this.horizontalImg = require("../../../resources/img/icon/hor_b.png"); | |
513 | - this.verticalImg = require("../../../resources/img/icon/ver_a.png"); | |
514 | - } | |
515 | - | |
516 | - // 유효성 검사 | |
517 | - if (this.selectLayout.layout_nm == null) { | |
518 | - this.$showAlert("메세지", "선택된 레이아웃이 없습니다."); | |
519 | - return; | |
520 | - } else if ( | |
521 | - this.selectLayout.children.length > 0 || | |
522 | - this.selectLayout.se == "component" | |
523 | - ) { | |
524 | - if ( | |
525 | - await this.$showConfirm( | |
526 | - "메세지", | |
527 | - "선택된 레이아웃을 분할하시겠습니까?" | |
528 | - ) | |
529 | - ) { | |
530 | - extend = true; | |
531 | - } else { | |
532 | - return; | |
533 | - } | |
534 | - } | |
535 | - | |
536 | - // 선택된 객체 세팅 | |
537 | - const temp_layout = _.cloneDeep(this.selectLayout); | |
538 | - this.selectLayout.se = "splitter"; | |
539 | - this.selectLayout.layout_type = _.cloneDeep(layoutVal); | |
540 | - this.selectLayout.layout_size1 = 50; | |
541 | - this.selectLayout.layout_size2 = 50; | |
542 | - | |
543 | - let tempChilds = []; | |
544 | - // 자식요소 추가 | |
545 | - if (extend) { | |
546 | - tempChilds = _.cloneDeep(this.selectLayout.children); | |
547 | - this.selectLayout.children = []; | |
548 | - this.selectLayout.layout_nm = Math.random() | |
549 | - .toString(36) | |
550 | - .substring(2, 15); | |
551 | - } | |
552 | - for (let i = 0; i < 2; i++) { | |
553 | - let tempChild = _.cloneDeep(this.$getDefaultJobGroup().customSplitter); | |
554 | - tempChild.depth = this.selectLayout.depth + 1; | |
555 | - tempChild.parents_splitter_id = this.selectLayout.layout_nm; | |
556 | - tempChild.position_idx = i; | |
557 | - tempChild.styleSheet.borderStyle = _.cloneDeep( | |
558 | - this.$getDefaultJobGroup().borderStyle | |
559 | - ); | |
560 | - tempChild.styleSheet.background_style = _.cloneDeep( | |
561 | - this.$getDefaultJobGroup().background_style | |
562 | - ); | |
563 | - if (extend && i == 0) { | |
564 | - tempChild.layout_nm = temp_layout.layout_nm; | |
565 | - tempChild.se = temp_layout.se; | |
566 | - tempChild.component = _.cloneDeep(this.selectLayout.component); | |
567 | - tempChild.layout_type = temp_layout.layout_type; | |
568 | - this.selectLayout.component = null; | |
569 | - tempChild.children = tempChilds; | |
570 | - this.selectLayout.children.push(tempChild); | |
571 | - } else { | |
572 | - tempChild.layout_nm = Math.random().toString(36).substring(2, 15); | |
573 | - tempChild.se = "splitter"; | |
574 | - tempChild.children = []; | |
575 | - this.selectLayout.children.push(tempChild); | |
576 | - } | |
577 | - } | |
578 | - }, | |
579 | - | |
580 | - registerLayout: function () { | |
581 | - let vm = this; | |
582 | - // 유효성 검사 | |
583 | - if (vm.customTitle == null || vm.customTitle == "") { | |
584 | - vm.$showAlert("메세지", "제목은 필수입력입니다."); | |
585 | - return; | |
586 | - } | |
587 | - if (vm.splitInfo.component == null && vm.splitInfo.children.length == 0) { | |
588 | - vm.$showAlert("메세지", "레이아웃 또는 차트를 추가해야합니다."); | |
589 | - return; | |
590 | - } | |
591 | - if (vm.pageId) { | |
592 | - // 저장 로직 구현 | |
593 | - let vm = this; | |
594 | - // 캡처할 요소 | |
595 | - let element = document.getElementById("splitter-container"); | |
596 | - | |
597 | - // html2canvas 사용하여 element 캡처 | |
598 | - html2canvas(element).then((canvas) => { | |
599 | - // 캔버스를 Blob 데이터로 변환 | |
600 | - canvas.toBlob(function (blob) { | |
601 | - let imgUrl = blob; | |
602 | - | |
603 | - let formData = new FormData(); | |
604 | - formData.append("img", imgUrl, "capture.png"); // Blob 데이터 사용 | |
605 | - | |
606 | - formData.append("page_id", vm.pageId); | |
607 | - formData.append("ttl", vm.customTitle); | |
608 | - formData.append("cn", vm.customComment); | |
609 | - formData.append("mdfcn_id", store.state.loginUser.user_id); | |
610 | - formData.append("splitInfo", JSON.stringify(vm.splitInfo)); // JSON 문자열로 변환 | |
611 | - formData.append( | |
612 | - "jobFlow", | |
613 | - JSON.stringify({ jobGroup: vm.diagramList }) | |
614 | - ); // JSON 문자열로 변환 | |
615 | - formData.append("public_at", vm.public_at); | |
616 | - | |
617 | - axios({ | |
618 | - url: "/custom/customUpdate", | |
619 | - method: "post", | |
620 | - headers: { | |
621 | - "Content-Type": "multipart/form-data", | |
622 | - }, | |
623 | - data: formData, | |
624 | - }) | |
625 | - .then(function (response) { | |
626 | - if (response.data.checkMessage.success) { | |
627 | - vm.$showAlert("메세지", "페이지 수정에 성공하였습니다."); | |
628 | - vm.$router.push({ | |
629 | - path: "/customSelectOne.page", | |
630 | - query: { page_id: vm.pageId }, | |
631 | - }); | |
632 | - } else { | |
633 | - vm.$showAlert("메세지", "수정에 실패하였습니다."); | |
634 | - } | |
635 | - }) | |
636 | - .catch(function (error) { | |
637 | - if (error.response.data.code == "COMPONENT_DATA_NOT_FOUND") { | |
638 | - vm.$showAlert( | |
639 | - "메세지", | |
640 | - "데이터를 추가하지 않은 컴포넌트가 존재합니다." | |
641 | - ); | |
642 | - } | |
643 | - }); | |
644 | - }); | |
645 | - }); | |
646 | - } else { | |
647 | - // canvas로 화면 이미지 저장하여 같이 저장 | |
648 | - let vm = this; | |
649 | - // 캡처할 요소 | |
650 | - let element = document.getElementById("splitter-container"); | |
651 | - | |
652 | - // html2canvas 사용하여 element 캡처 | |
653 | - html2canvas(element).then((canvas) => { | |
654 | - // 캔버스를 Blob 데이터로 변환 | |
655 | - canvas.toBlob(function (blob) { | |
656 | - let imgUrl = blob; | |
657 | - | |
658 | - let formData = new FormData(); | |
659 | - formData.append("img", imgUrl, "capture.png"); // Blob 데이터 사용 | |
660 | - | |
661 | - formData.append("ttl", vm.customTitle); | |
662 | - formData.append("cn", vm.customComment); | |
663 | - formData.append("wrt_id", store.state.loginUser.user_id); | |
664 | - formData.append("splitInfo", JSON.stringify(vm.splitInfo)); // JSON 문자열로 변환 | |
665 | - formData.append( | |
666 | - "jobFlow", | |
667 | - JSON.stringify({ jobGroup: vm.diagramList }) | |
668 | - ); // JSON 문자열로 변환 | |
669 | - formData.append("public_at", vm.public_at); | |
670 | - | |
671 | - axios({ | |
672 | - url: "/custom/insert", | |
673 | - method: "post", | |
674 | - headers: { | |
675 | - "Content-Type": "multipart/form-data", | |
676 | - }, | |
677 | - data: formData, | |
678 | - }) | |
679 | - .then(function (response) { | |
680 | - if (response.data.checkMessage.success) { | |
681 | - let createPageId = response.data.resultData.pageId; | |
682 | - vm.$showAlert("메세지", "페이지 등록에 성공하였습니다."); | |
683 | - vm.$router.push({ | |
684 | - path: "/customSelectOne.page", | |
685 | - query: { page_id: createPageId }, | |
686 | - }); | |
687 | - } else { | |
688 | - vm.$showAlert("메세지", "등록에 실패하였습니다."); | |
689 | - } | |
690 | - }) | |
691 | - .catch(function (error) { | |
692 | - if (error.response.data.code == "COMPONENT_DATA_NOT_FOUND") { | |
693 | - vm.$showAlert( | |
694 | - "메세지", | |
695 | - "데이터를 추가하지 않은 컴포넌트가 존재합니다." | |
696 | - ); | |
697 | - } | |
698 | - }); | |
699 | - }); | |
700 | - }); | |
701 | - } | |
702 | - }, | |
703 | - | |
704 | - // 초기화 | |
705 | - init: async function () { | |
706 | - const defaultJobGroupData = await this.$getDefaultJobGroup(); | |
707 | - | |
708 | - this.splitInfo = _.cloneDeep(defaultJobGroupData.customSplitter); | |
709 | - this.splitInfo_dumy = _.cloneDeep(defaultJobGroupData.customSplitter); | |
710 | - | |
711 | - this.splitInfo.layout_nm = "split_0"; | |
712 | - this.splitInfo.se = "splitter"; | |
713 | - this.splitInfo.position_idx = 1; | |
714 | - this.splitInfo.depth = 0; | |
715 | - this.splitInfo.styleSheet.borderStyle = _.cloneDeep( | |
716 | - defaultJobGroupData.borderStyle | |
717 | - ); | |
718 | - this.splitInfo.styleSheet.background_style = _.cloneDeep( | |
719 | - defaultJobGroupData.background_style | |
720 | - ); | |
721 | - | |
722 | - this.splitInfo_dumy.layout_nm = "dumy"; | |
723 | - this.splitInfo_dumy.se = "splitter"; | |
724 | - this.splitInfo_dumy.children.push(this.splitInfo); | |
725 | - | |
726 | - this.selectLayout = this.splitInfo; | |
727 | - // 데이터 관리 초기화 | |
728 | - this.diagramList = []; | |
729 | - }, | |
730 | - initClickEvent: async function () { | |
731 | - if ( | |
732 | - await this.$showConfirm( | |
733 | - "초기화", | |
734 | - "초기화 하시겠습니까? 데이터가 모두 삭제됩니다." | |
735 | - ) | |
736 | - ) { | |
737 | - this.init(); | |
738 | - } | |
739 | - }, | |
740 | - // 축 데이터 가져오기 | |
741 | - handlerAxisData: function (axis) { | |
742 | - let vm = this; | |
743 | - let changeData = _.cloneDeep(vm.selectLayout.component.component_itm); | |
744 | - | |
745 | - changeData.categoryAxis = axis.dataX; | |
746 | - changeData.valueAxis = axis.dataY; | |
747 | - | |
748 | - // 축 데이터를 이용한 차트 데이터 생성 | |
749 | - changeData.data_list = chartDataTransform.createData( | |
750 | - vm.rowData, | |
751 | - changeData.categoryAxis, | |
752 | - changeData.valueAxis, | |
753 | - "null" | |
754 | - ); | |
755 | - | |
756 | - vm.selectLayout.component.component_itm = changeData; | |
757 | - // 선택한 데이터 테이블 정보 컴포넌트에 저장 | |
758 | - //같은 데이터 테이블 정보가 있는지 확인하고 없으면 넣고 있으면 넣지 않음 | |
759 | - let selectedJob = this.clickDiagramInfo; | |
760 | - this.selectLayout.component.jobInfo[0] = selectedJob; | |
761 | - // if(this.createChartData.); | |
762 | - }, | |
763 | - // 다이어그램 데이터 테이블 정보 가져오기 | |
764 | - getDiagramDataInfo: function (data) { | |
765 | - this.clickDiagramInfo = data; | |
766 | - if (data.dataTable != null) { | |
767 | - this.rowData = data.dataTable.rowData; | |
768 | - } | |
769 | - }, | |
770 | - changeSelectCal: function (cal) { | |
771 | - let changeData = _.cloneDeep(this.selectLayout.component.component_itm); | |
772 | - | |
773 | - changeData.chart_cal = cal; | |
774 | - this.selectLayout.component.component_itm = changeData; | |
775 | - }, | |
776 | - getCustomData: function () { | |
777 | - const vm = this; | |
778 | - this.isUpdatePage = true; | |
779 | - | |
780 | - axios({ | |
781 | - url: "/custom/customPageUpdateSelect", | |
782 | - method: "post", | |
783 | - headers: { | |
784 | - "Content-Type": "application/json; charset=UTF-8", | |
785 | - }, | |
786 | - // 객체 형태로 감싸서 데이터 전송 | |
787 | - data: { page_id: vm.pageId }, | |
788 | - }) | |
789 | - .then(function (response) { | |
790 | - let resPageInfo = response.data.resultData.pageInfo; | |
791 | - let resdiagramList = response.data.resultData.diagramList; | |
792 | - let resSplitterInfo = response.data.resultData.splitterInfo; | |
793 | - | |
794 | - vm.customTitle = resPageInfo.ttl; | |
795 | - vm.customComment = resPageInfo.cn; | |
796 | - | |
797 | - vm.splitInfo = resSplitterInfo; | |
798 | - | |
799 | - vm.splitInfo_dumy.layout_nm = "dumy"; | |
800 | - vm.splitInfo_dumy.se = "splitter"; | |
801 | - vm.splitInfo_dumy.children.push(vm.splitInfo); | |
802 | - | |
803 | - vm.diagramList = resdiagramList; | |
804 | - | |
805 | - vm.selectLayout = vm.splitInfo; | |
806 | - vm.isLoading = false; | |
807 | - vm.public_at = resPageInfo.public_at; | |
808 | - vm.getSelectLayoutChart(); | |
809 | - }) | |
810 | - .catch(function (error) { | |
811 | - vm.isLoading = false; | |
812 | - vm.$showAlert( | |
813 | - "데이터 현황관리 차트 연결", | |
814 | - "데이터 현황관리 차트 연결 오류, 관리자에게 문의하세요." | |
815 | - ); | |
816 | - vm.$router.go(-1); // 오류 시 이전 페이지로 이동 | |
817 | - }); | |
818 | - }, | |
819 | - // 레이아웃 삭제 | |
820 | - deleteSplitterLayout: async function () { | |
821 | - if (!this.selectLayout.layout_nm) { | |
822 | - this.$showAlert("메세지", "삭제할 레이아웃이 없습니다."); | |
823 | - return; | |
824 | - } | |
825 | - if (!this.selectLayout.parents_splitter_id) { | |
826 | - this.$showAlert("메세지", "최상위 레이아웃은 삭제할 수 없습니다."); | |
827 | - return; | |
828 | - } | |
829 | - if ( | |
830 | - await this.$showConfirm( | |
831 | - "레이아웃 삭제", | |
832 | - "선택한 레이아웃을 삭제하시겠습니까?" | |
833 | - ) | |
834 | - ) { | |
835 | - let parentLayout = this.findParentLayout( | |
836 | - this.splitInfo, | |
837 | - this.selectLayout.parents_splitter_id | |
838 | - ); | |
839 | - //선택한 레이아웃의 형제정보 찾기 | |
840 | - let otherIdx = parentLayout.children.findIndex( | |
841 | - (item) => item.layout_nm !== this.selectLayout.layout_nm | |
842 | - ); | |
843 | - let otherSplit = parentLayout.children[otherIdx]; | |
844 | - //otherSplit의 component, children 정보 가져오기 | |
845 | - let otherComponent = otherSplit.component; | |
846 | - let otherChildren = otherSplit.children; | |
847 | - let layoutSize1 = otherSplit.layout_size1; | |
848 | - let layoutSize2 = otherSplit.layout_size2; | |
849 | - let layoutType = otherSplit.layout_type; | |
850 | - let se = otherSplit.se; | |
851 | - let size = otherSplit.size; | |
852 | - let styleSheet = otherSplit.styleSheet; | |
853 | - | |
854 | - // 부모 레이아웃에 다른 split의 정보 넣어 주기 | |
855 | - parentLayout.component = otherComponent; | |
856 | - parentLayout.children = otherChildren; | |
857 | - parentLayout.layout_size1 = layoutSize1; | |
858 | - parentLayout.layout_size2 = layoutSize2; | |
859 | - parentLayout.layout_type = layoutType; | |
860 | - parentLayout.se = se; | |
861 | - parentLayout.size = size; | |
862 | - parentLayout.styleSheet = styleSheet; | |
863 | - | |
864 | - // 자식에게서 가져온 자식의 자식 정보의 부모 정보 변경 | |
865 | - if (parentLayout.children.length > 0) { | |
866 | - parentLayout.children.forEach((item) => { | |
867 | - item.parents_splitter_id = parentLayout.layout_nm; | |
868 | - }); | |
869 | - } | |
870 | - } | |
871 | - }, | |
872 | - | |
873 | - // 삭제할 레이아웃의 부모 레이아웃을 찾는 재귀 함수 | |
874 | - findParentLayout: function (splitInfo, selectParentLayoutId) { | |
875 | - const vm = this; | |
876 | - | |
877 | - // splitInfo가 배열인 경우 각 요소에 대해 재귀적으로 함수를 호출 | |
878 | - if (Array.isArray(splitInfo)) { | |
879 | - for (let i = 0; i < splitInfo.length; i++) { | |
880 | - let result = this.findParentLayout( | |
881 | - splitInfo[i], | |
882 | - selectParentLayoutId | |
883 | - ); | |
884 | - if (result) return result; // 일치하는 부모 레이아웃을 찾으면 반환 | |
885 | - } | |
886 | - } else { | |
887 | - // 현재 splitInfo 객체의 layout_nm이 찾고자 하는 selectParentLayoutId와 일치하는지 확인 | |
888 | - if (splitInfo.layout_nm === selectParentLayoutId) { | |
889 | - return splitInfo; // 일치하는 경우 현재 객체 반환 | |
890 | - } | |
891 | - | |
892 | - // 현재 객체의 자식들에 대해 재귀적으로 탐색 | |
893 | - if (splitInfo.children && splitInfo.children.length > 0) { | |
894 | - return this.findParentLayout( | |
895 | - splitInfo.children, | |
896 | - selectParentLayoutId | |
897 | - ); | |
898 | - } | |
899 | - } | |
900 | - | |
901 | - // 일치하는 요소를 찾지 못한 경우 | |
902 | - return null; | |
903 | - }, | |
904 | - moveBack: async function () { | |
905 | - if (await this.$showConfirm("이전으로", "이전으로 돌아가시겠습니까?")) { | |
906 | - this.$router.go(-1); | |
907 | - } | |
908 | - }, | |
909 | - updateSplitInfo: function (splitInfo) { | |
910 | - this.splitInfo = splitInfo; | |
911 | - }, | |
912 | - }, | |
913 | - watch: { | |
914 | - splitInfo: { | |
915 | - handler: function (newVal, oldVal) { | |
916 | - this.splitInfo_dumy = _.cloneDeep( | |
917 | - this.$getDefaultJobGroup().customSplitter | |
918 | - ); | |
919 | - this.splitInfo_dumy.layout_nm = "dumy"; | |
920 | - this.splitInfo_dumy.se = "splitter"; | |
921 | - this.splitInfo_dumy.children.push(newVal); | |
922 | - }, | |
923 | - deep: true, | |
924 | - }, | |
925 | - splitInfo_dumy: { deep: true }, | |
926 | - }, | |
927 | - components: { | |
928 | - SplitterLayout, | |
929 | - JobContainer: JobContainer, | |
930 | - SvgIcon: SvgIcon, | |
931 | - TreeItem: TreeItem, | |
932 | - BackgroundOption: BackgroundOption, | |
933 | - CellOption: CellOption, | |
934 | - FontOption: FontOption, | |
935 | - StyleSheet: StyleSheet, | |
936 | - TitleStyleSheet: TitleStyleSheet, | |
937 | - VueFlowZoneGroup: VueFlowZoneGroup, | |
938 | - }, | |
939 | - created: function () { | |
940 | - this.pageId = this.$route.query.page_id; | |
941 | - this.jobGroup = this.$getDefaultJobGroup().jobGroup; | |
942 | - this.activeTab = "pageInfo"; | |
943 | - | |
944 | - if (this.pageId != null && this.pageId != undefined) { | |
945 | - this.getCustomData(); // 기존 데이터 가져오기 | |
946 | - } else { | |
947 | - this.init(); | |
948 | - } | |
949 | - } | |
950 | -}; | |
951 | -</script> | |
952 | -<style scoped> | |
953 | -#splitter-container { | |
954 | - width: 100%; | |
955 | - height: calc(100% - 50px); | |
956 | - margin-bottom: 20px; | |
957 | -} | |
958 | - | |
959 | -#SplitterLayout { | |
960 | - width: 100%; | |
961 | - height: 100%; | |
962 | -} | |
963 | - | |
964 | -#splitter-container.active { | |
965 | - border: 3px dotted red; | |
966 | -} | |
967 | - | |
968 | -.padding-1 { | |
969 | - padding: 1rem; | |
970 | -} | |
971 | - | |
972 | -#customComment { | |
973 | - height: 99px; | |
974 | -} | |
975 | - | |
976 | -.table-padding { | |
977 | - padding: 15px 0; | |
978 | -} | |
979 | - | |
980 | -.paletteLi { | |
981 | - display: inline-block; | |
982 | - margin-right: 10px; | |
983 | -} | |
984 | - | |
985 | -.img-wrap { | |
986 | - position: relative; | |
987 | -} | |
988 | - | |
989 | -.img-wrap:hover .img-hover { | |
990 | - display: flex; | |
991 | -} | |
992 | - | |
993 | -.img-wrap .img-hover { | |
994 | - position: absolute; | |
995 | - top: 0; | |
996 | - left: 0; | |
997 | - right: 0; | |
998 | - bottom: 0; | |
999 | - background-color: #213f9999; | |
1000 | - display: none; | |
1001 | - align-items: center; | |
1002 | - justify-content: center; | |
1003 | -} | |
1004 | - | |
1005 | -.img-wrap .img-hover span { | |
1006 | - padding: 10px; | |
1007 | - text-align: center; | |
1008 | - font-size: 1.5em; | |
1009 | - color: #FFFFFF; | |
1010 | - word-break: keep-all; | |
1011 | -} | |
1012 | -</style>(파일 끝에 줄바꿈 문자 없음) |
--- client/views/pages/custom/CustomSelectList.vue
+++ client/views/pages/custom/CustomSelectList.vue
... | ... | @@ -5,68 +5,100 @@ |
5 | 5 |
<PageNavigation /> |
6 | 6 |
</div> |
7 | 7 |
<div class="content-wrap"> |
8 |
- <div class="content content-box flex100"> |
|
9 |
- <div class="column-list"> |
|
10 |
- <div class="search-bar"> |
|
11 |
- <div class="flex justify-end align-center"> |
|
12 |
- <select name="searchItem" id="searchItem" class="square-select" v-model="search_data.key"> |
|
8 |
+ <div class="content-box"> |
|
9 |
+ <div class="content pd10"> |
|
10 |
+ <div class="content-titleZone flex justify-between align-center"> |
|
11 |
+ <p class="box-title"></p> |
|
12 |
+ <div class="search-bar flex justify-end align-center"> |
|
13 |
+ <select |
|
14 |
+ name="searchItem" |
|
15 |
+ id="searchItem" |
|
16 |
+ class="square-select" |
|
17 |
+ v-model="search_data.key" |
|
18 |
+ > |
|
13 | 19 |
<option value="ttl">제목</option> |
14 | 20 |
<option value="cn">내용</option> |
15 | 21 |
</select> |
16 | 22 |
<div class="search-square"> |
17 |
- <div class="flex justify-end align-center no-gutter"> |
|
18 |
- <input type="text" class="square-input flex90" placeholder="Search" v-model="search_data.value" v-on:keyup.enter="customListAllSelect()" /> |
|
19 |
- <button class="square-button blue-btn flex10" @click="customListAllSelect()"> |
|
20 |
- <svg-icon type="mdi" :path="searchPath" class="square-icon"></svg-icon> |
|
21 |
- </button> |
|
22 |
- </div> |
|
23 |
+ <input |
|
24 |
+ type="text" |
|
25 |
+ class="square-input" |
|
26 |
+ placeholder="Search" |
|
27 |
+ v-model="search_data.value" |
|
28 |
+ v-on:keyup.enter="customListAllSelect()" |
|
29 |
+ /> |
|
30 |
+ <button |
|
31 |
+ class="square-button blue-btn" |
|
32 |
+ @click="customListAllSelect()" |
|
33 |
+ > |
|
34 |
+ <svg-icon |
|
35 |
+ type="mdi" |
|
36 |
+ :path="searchPath" |
|
37 |
+ class="square-icon" |
|
38 |
+ ></svg-icon> |
|
39 |
+ </button> |
|
23 | 40 |
</div> |
24 | 41 |
</div> |
25 | 42 |
</div> |
26 | 43 |
<div class="table-zone"> |
27 | 44 |
<ul class="gall-list flex justify-start align-center"> |
28 |
- <li v-for="(item, index) in customList" :key="index" class="gall-list flex25" @click="customUpdatePage(item.page_id)"> |
|
29 |
- <a href=""> |
|
30 |
- <div class="gall-img"> |
|
31 |
- <img :src="item.preview_img_path" alt="" /> |
|
32 |
- </div> |
|
33 |
- <div class="gall-info"> |
|
34 |
- <p class="gall-title">{{ item.ttl }}</p> |
|
35 |
- <p class="gall-detail">{{ item.cn }}</p> |
|
36 |
- </div> |
|
37 |
- </a> |
|
45 |
+ <li |
|
46 |
+ v-for="(item, index) in customList" |
|
47 |
+ :key="index" |
|
48 |
+ class="gall-list flex25" |
|
49 |
+ @click="customUpdatePage(item.page_id)" |
|
50 |
+ > |
|
51 |
+ <div class="gall-img"> |
|
52 |
+ <img :src="item.preview_img_path" /> |
|
53 |
+ </div> |
|
54 |
+ <div class="gall-info"> |
|
55 |
+ <p class="gall-title">{{ item.ttl }}</p> |
|
56 |
+ <p class="gall-detail">{{ item.cn }}</p> |
|
57 |
+ </div> |
|
38 | 58 |
</li> |
39 | 59 |
</ul> |
60 |
+ <PaginationButton |
|
61 |
+ v-model:currentPage="search.currentPage" |
|
62 |
+ :perPage="search.perPage" |
|
63 |
+ :totalCount="search.totalRows" |
|
64 |
+ :maxRange="5" |
|
65 |
+ :click="customListAllSelect" |
|
66 |
+ /> |
|
40 | 67 |
</div> |
41 |
- <PaginationButton v-model:currentPage="search.currentPage" :perPage="search.perPage" :totalCount="search.totalRows" :maxRange="5" :click="customListAllSelect" /> |
|
68 |
+ <div class="flex justify-end align-center"> |
|
69 |
+ <button class="blue-btn small-btn" @click="customInsertPage"> |
|
70 |
+ 페이지 등록 |
|
71 |
+ </button> |
|
72 |
+ </div> |
|
42 | 73 |
</div> |
43 |
- </div> |
|
44 |
- <div class="flex justify-end align-center"> |
|
45 |
- <button class="blue-btn small-btn" @click="customInsertPage">페이지 등록</button> |
|
46 | 74 |
</div> |
47 | 75 |
</div> |
48 | 76 |
</div> |
49 | 77 |
</template> |
50 | 78 |
<script> |
51 |
-import PageNavigation from "../../component/PageNavigation.vue"; |
|
52 |
-import PaginationButton from "../../component/PaginationButton.vue"; |
|
79 |
+import axios from "axios"; |
|
80 |
+// icon용 svg import |
|
53 | 81 |
import SvgIcon from "@jamescoyle/vue-icon"; |
54 | 82 |
import { mdiMagnify } from "@mdi/js"; |
55 |
-import axios from "axios"; |
|
83 |
+// 컴포넌트 import |
|
84 |
+import PageNavigation from "../../component/PageNavigation.vue"; |
|
85 |
+import PaginationButton from "../../component/PaginationButton.vue"; |
|
56 | 86 |
|
57 | 87 |
export default { |
58 | 88 |
data() { |
59 | 89 |
return { |
90 |
+ // svg |
|
91 |
+ searchPath: mdiMagnify, |
|
92 |
+ |
|
60 | 93 |
search: _.cloneDeep(this.$getDefaultSerchVO()), |
61 | 94 |
search_data: _.cloneDeep(this.$getDefaultSerchItem("ttl", "String")), |
62 |
- searchPath: mdiMagnify, |
|
63 | 95 |
inputValue: null, |
64 | 96 |
customList: [], |
65 | 97 |
}; |
66 | 98 |
}, |
67 | 99 |
methods: { |
68 | 100 |
customInsertPage: function () { |
69 |
- this.$router.push({ path: "/customInsertDev.page", query: {} }); |
|
101 |
+ this.$router.push({ path: "/insertDataAnalytics.page", query: {} }); |
|
70 | 102 |
}, |
71 | 103 |
customUpdatePage: function (pageId) { |
72 | 104 |
this.$router.push({ |
... | ... | @@ -80,8 +112,8 @@ |
80 | 112 |
let check = false; |
81 | 113 |
let authList = vm.$store.state.loginUser.user_auth; |
82 | 114 |
for (let auth of authList) { |
83 |
- if (auth == 'ROLE_ADMIN') { |
|
84 |
- check = true |
|
115 |
+ if (auth == "ROLE_ADMIN") { |
|
116 |
+ check = true; |
|
85 | 117 |
break; |
86 | 118 |
} |
87 | 119 |
} |
... | ... | @@ -92,7 +124,7 @@ |
92 | 124 |
type: "string", |
93 | 125 |
value: check + "/" + vm.$store.state.loginUser.user_id, |
94 | 126 |
value2: vm.$store.state.loginUser.dept_code, |
95 |
- }) |
|
127 |
+ }); |
|
96 | 128 |
} |
97 | 129 |
|
98 | 130 |
axios({ |
--- client/views/pages/custom/CustomSelectOne.vue
+++ client/views/pages/custom/CustomSelectOne.vue
... | ... | @@ -82,7 +82,7 @@ |
82 | 82 |
<script> |
83 | 83 |
import SvgIcon from "@jamescoyle/vue-icon"; |
84 | 84 |
import axios from "axios"; |
85 |
-import SplitterLayout from "../../component/SplitterLayoutDev.vue"; |
|
85 |
+import SplitterLayout from "../../component/SplitterLayout.vue"; |
|
86 | 86 |
|
87 | 87 |
export default { |
88 | 88 |
data() { |
... | ... | @@ -161,7 +161,7 @@ |
161 | 161 |
}, |
162 | 162 |
pageUpdate() { |
163 | 163 |
this.$router.push({ |
164 |
- path: "/customInsertDev.page", |
|
164 |
+ path: "/insertDataAnalytics.page", |
|
165 | 165 |
query: { page_id: this.page_id }, |
166 | 166 |
}); |
167 | 167 |
}, |
+++ client/views/pages/custom/InsertDataAnalytics.vue
... | ... | @@ -0,0 +1,1197 @@ |
1 | +<template> | |
2 | + <div class="container"> | |
3 | + <div class="page-titleZone flex justify-between align-center align-start"> | |
4 | + <p class="main-title flex80"> 데이터 활용관리 {{ !pageId ? "등록" : "수정" }} </p> | |
5 | + </div> | |
6 | + <div class="content-wrap content flex100"> | |
7 | + <div class="custom-tab content-box"> | |
8 | + <div class="flex justify-start align-center no-gutter content-box"> | |
9 | + <div class="column-nav flex5"> | |
10 | + <ul> | |
11 | + <li class="cursor" v-for="(item, idx) in navList" :key="idx" @click="fnChangeTab('nav', item.id)"> | |
12 | + <a :class="{ activeTab: activeTab === item.id }"> | |
13 | + <svg-icon type="mdi" class="mb5" :path="item.iconPath" /> | |
14 | + <p>{{ item.name }}</p> | |
15 | + </a> | |
16 | + </li> | |
17 | + </ul> | |
18 | + </div> | |
19 | + <div class="content-box flex justify-between align-start flex95 no-gutter"> | |
20 | + <div :class="{ | |
21 | + 'tab-zone flex align-start': true, | |
22 | + flex30: !isTabZoneOpen, | |
23 | + }"> | |
24 | + <div class="content-box" v-show="!isTabZoneOpen"> | |
25 | + <!-- 페이지정보탭 --> | |
26 | + <div class="content-box tab-box pd10 overflow-y" v-show="activeTab === 'pageInfo'"> | |
27 | + <div class="content-titleZone"> | |
28 | + <div class="flex justify-between align-center"> | |
29 | + <p class="box-title">페이지 정보</p> | |
30 | + </div> | |
31 | + </div> | |
32 | + <table class="form-table"> | |
33 | + <tr> | |
34 | + <th>제목</th> | |
35 | + <td> | |
36 | + <input type="text" class="full-input" v-model="customTitle" /> | |
37 | + </td> | |
38 | + </tr> | |
39 | + <tr> | |
40 | + <th>공개여부</th> | |
41 | + <td> | |
42 | + <div class="input-container flex"> | |
43 | + <label class="radio-label"> | |
44 | + <input type="radio" name="public_at" class="custom-radiobox" :value="true" v-model="public_at" /> | |
45 | + <span>공개</span> | |
46 | + </label> | |
47 | + <label class="radio-label"> | |
48 | + <input type="radio" name="public_at" class="custom-radiobox" :value="false" v-model="public_at" /> | |
49 | + <span>비공개</span> | |
50 | + </label> | |
51 | + </div> | |
52 | + </td> | |
53 | + </tr> | |
54 | + <tr> | |
55 | + <th>설명</th> | |
56 | + <td> | |
57 | + <textarea class="content-box" cols="30" rows="35" v-model="customComment" @input="customComment = $event.target.value"></textarea> | |
58 | + </td> | |
59 | + </tr> | |
60 | + </table> | |
61 | + </div> | |
62 | + <!-- 레이아웃 및 컴포넌트 탭 --> | |
63 | + <div class="content-box flex justify-between tab-box" v-show="activeTab === 'viewSetting'"> | |
64 | + <div class="content-list layout flex50"> | |
65 | + <div class="layout-tree content-box"> | |
66 | + <div class="content-titleZone"> | |
67 | + <div class="flex justify-between align-center"> | |
68 | + <p class="box-title">레이아웃 및 컴포넌트</p> | |
69 | + <div> | |
70 | + <button id="horizontal-btn" @click="addSplitterLayout('horizontal')" title="수직레이아웃"> | |
71 | + <img :src="verticalImg" /> | |
72 | + </button> | |
73 | + <button id="vertical-btn" @click="addSplitterLayout('vertical')" title="수평레이아웃"> | |
74 | + <img :src="horizontalImg" /> | |
75 | + </button> | |
76 | + <button id="split-delete-btn" @click="deleteSplitterLayout" title="삭제"> | |
77 | + <svg-icon type="mdi" :path="trashPath" :color="'#aaa'" /> | |
78 | + </button> | |
79 | + <button class="attribute-btn" @click="fnAttributeToggle"> | |
80 | + <svg-icon type="mdi" :path="dotPath" :color="'#aaa'" /> | |
81 | + <div class="attribute-modal" v-show="isAttributeOpen"> | |
82 | + <ul> | |
83 | + <li class="flex align-center"> | |
84 | + <svg-icon type="mdi" :path="tablePath" /> | |
85 | + <p class="ml5">테이블 추가</p> | |
86 | + </li> | |
87 | + <li class="flex align-center"> | |
88 | + <svg-icon type="mdi" :path="chartPath" /> | |
89 | + <p class="ml5">차트 추가</p> | |
90 | + </li> | |
91 | + </ul> | |
92 | + </div> | |
93 | + </button> | |
94 | + </div> | |
95 | + </div> | |
96 | + </div> | |
97 | + <ul> | |
98 | + <TreeItem :splitInfo="splitInfo" :selectLayout="currentLayout" @onChange="fnSelectLayout" /> | |
99 | + </ul> | |
100 | + </div> | |
101 | + </div> | |
102 | + <div class="layout-option flex-column flex50"> | |
103 | + <div class="tabnav3"> | |
104 | + <ul class="flex justify-start align-center"> | |
105 | + <template v-for="(item, idx) of optionList" :key="idx"> | |
106 | + <li class="cursor" v-if="item.useAt" @click="fnChangeTab('opt', item.id)"> | |
107 | + <p :class="{ activeOption: activeOption === item.id }">{{ item.name }}</p> | |
108 | + </li> | |
109 | + </template> | |
110 | + </ul> | |
111 | + </div> | |
112 | + <div v-if="activeOption == 'LAYOUT'"> | |
113 | + <StyleSheet :styleSheet="currentLayout.styleSheet" /> | |
114 | + </div> | |
115 | + <div v-if="activeOption == 'TITLE'"> | |
116 | + <div class="table-zone"> | |
117 | + <p class="object-title mb5">타이틀 사용 여부</p> | |
118 | + <div class="input-container flex"> | |
119 | + <label class="radio-label"> | |
120 | + <input type="radio" name="titleUseAt" class="custom-radiobox" :value="true" v-model="currentLayout.useSj"> | |
121 | + <span>사용</span> | |
122 | + </label> | |
123 | + <label class="radio-label"> | |
124 | + <input type="radio" name="titleUseAt" class="custom-radiobox" :value="false" v-model="currentLayout.useSj"> | |
125 | + <span>미사용</span> | |
126 | + </label> | |
127 | + </div> | |
128 | + <div v-if="currentLayout.useSj"> | |
129 | + <div> | |
130 | + <p class="object-title mb5">메인타이틀</p> | |
131 | + <input type="text" class="full-input" v-model="currentLayout.layoutSj.main_sj" /> | |
132 | + <FontOption :fontStyle="currentLayout.layoutSj.styleSheetMain.fontStyle" :title="'메인'" /> | |
133 | + </div> | |
134 | + <div> | |
135 | + <p class="object-title mb5">서브타이틀</p> | |
136 | + <input type="text" class="full-input" v-model="currentLayout.layoutSj.sub_sj" /> | |
137 | + <FontOption :fontStyle="currentLayout.layoutSj.styleSheetSub.fontStyle" :title="'서브'" /> | |
138 | + </div> | |
139 | + </div> | |
140 | + </div> | |
141 | + </div> | |
142 | + <div v-if="activeOption == 'COMPONENT'"> | |
143 | + <StyleSheet :styleSheet="currentLayout.styleSheet" /> | |
144 | + </div> | |
145 | + </div> | |
146 | + </div> | |
147 | + <!-- 데이터탭 --> | |
148 | + <div class="content-box tab-box" v-show="activeTab === 'dataset'"> | |
149 | + <div class="data-set" style="border-bottom: 1px solid #ddd"> | |
150 | + <div class="content-box flex-column pd10"> | |
151 | + <div class="content-titleZone"> | |
152 | + <div class="flex justify-between align-center"> | |
153 | + <p class="box-title">데이터 목록</p> | |
154 | + <button type="button" class="blue-border-btn small-btn" @click="fnCreateJobGroup">데이터 추가</button> | |
155 | + </div> | |
156 | + </div> | |
157 | + <div class="data-list"> | |
158 | + <div v-for="(item, idx) of jobGroupList" :key="idx" @click="fnSelectJobGroup(item)"> | |
159 | + <div :class="{ | |
160 | + 'item flex justify-between align-center': true, | |
161 | + mb5: idx < jobGroupList.length - 1, | |
162 | + selectData: item.group_nm == currentJobGroup.group_nm, | |
163 | + }"> | |
164 | + <p class="flex align-center"> | |
165 | + <svg-icon type="mdi" class="mr5" :width="20" :height="20" :path="dataPath" /> | |
166 | + <span>{{ item.group_nm }}</span> | |
167 | + </p> | |
168 | + <div> | |
169 | + <button class="blue-border-btn set-btn" @click.stop="" @click="fnModalOpen(item, idx)"> 설정 </button> | |
170 | + <button class="red-border-btn set-btn" @click.stop="" @click="deleteDiagram(item)"> 삭제 </button> | |
171 | + </div> | |
172 | + </div> | |
173 | + </div> | |
174 | + </div> | |
175 | + </div> | |
176 | + </div> | |
177 | + <!-- 컬럼 정보 --> | |
178 | + <div class="column-list flex no-gutter"> | |
179 | + <div class="content-box flex50 pd10" style="border-right: 1px solid #ddd"> | |
180 | + <div class="flex100 content-box"> | |
181 | + <p class="box-title mb10">컬럼정보</p> | |
182 | + <div class="overflow-y" style="height: calc(100% - 22px)"> | |
183 | + <template v-if="currentDataTable.columnDatas.length > 0"> | |
184 | + <div v-for="(column, idx) of this.currentDataTable.columnDatas" :key="idx" :class="{ | |
185 | + item: true, | |
186 | + mb5: idx < currentDataTable.columnDatas.length - 1, | |
187 | + }" draggable="true" @dragstart="startDrag($event, column, idx)"> | |
188 | + <p> | |
189 | + <b>{{ column.displyColumnNm }}</b> ({{ column.columnNm }}) : [{{ column.dataTy }}] | |
190 | + </p> | |
191 | + </div> | |
192 | + </template> | |
193 | + <div v-else> | |
194 | + <p style="font-size: 1.3rem"> 컬럼 데이터가 존재하지 않습니다. </p> | |
195 | + </div> | |
196 | + </div> | |
197 | + </div> | |
198 | + </div> | |
199 | + <div class="content-box flex50"> | |
200 | + <div class="content-box flex-column no-gutter pd10 overflow-y"> | |
201 | + <div class="flex30 mb10"> | |
202 | + <div class="editor-box content-box pd10"> | |
203 | + <p class="object-title mb5">항목(Category)</p> | |
204 | + <div class="overflow-y" style="height: calc(100% - 22px)" @drop.prevent="onDrop($event, 'xdata')" @dragenter.prevent @dragover.prevent> | |
205 | + <template v-if="dataX.length > 0"> | |
206 | + <div v-for="(column, indx) of dataX" :key="indx" :class="{ | |
207 | + 'item flex justify-between align-center': true, | |
208 | + mb5: idx < dataX.length - 1, | |
209 | + }"> | |
210 | + <p> {{ column.displyColumnNm }} : [{{ column.dataTy }}] </p> | |
211 | + <div> | |
212 | + <button class="red-border-btn set-btn" @click="deleteItem(column, 'x')"> 삭제 </button> | |
213 | + </div> | |
214 | + </div> | |
215 | + </template> | |
216 | + <div class="item mb5" v-else> | |
217 | + <p>NO_DATA</p> | |
218 | + </div> | |
219 | + </div> | |
220 | + </div> | |
221 | + </div> | |
222 | + <div class="flex40 mb10"> | |
223 | + <div class="editor-box content-box pd10"> | |
224 | + <div class="flex justify-between align-center"> | |
225 | + <p class="object-title mb5">값(Value)</p> | |
226 | + <select @change="fnChangeCalc"> | |
227 | + <option v-for="(item, idx) of calcList" :key="idx" :value="item.key">{{ item.value }}</option> | |
228 | + </select> | |
229 | + </div> | |
230 | + <div class="overflow-y" style="height: calc(100% - 22px)" @drop.prevent="onDrop($event, 'ydata')" @dragenter.prevent @dragover.prevent> | |
231 | + <template v-if="dataY.length > 0"> | |
232 | + <div v-for="(column, indx) of dataY" :key="indx" :class="{ | |
233 | + 'item flex justify-between align-center': true, | |
234 | + mb5: idx < dataY.length - 1, | |
235 | + }"> | |
236 | + <p> {{ column.displyColumnNm }} : [{{ column.dataTy }}] </p> | |
237 | + <button class="red-border-btn set-btn" @click="deleteItem(column, 'y')"> 삭제 </button> | |
238 | + </div> | |
239 | + </template> | |
240 | + <div class="item mb5" v-else> | |
241 | + <p>NO_DATA</p> | |
242 | + </div> | |
243 | + </div> | |
244 | + </div> | |
245 | + </div> | |
246 | + <div class="flex20 mb10"> | |
247 | + <div class="editor-box content-box pd10"> | |
248 | + <p class="object-title mb5">다음으로 색상 지정</p> | |
249 | + <ul class="overflow-y" style="height: calc(100% - 16px)"> | |
250 | + <li class="item mb5"> | |
251 | + <p>column</p> | |
252 | + </li> | |
253 | + </ul> | |
254 | + </div> | |
255 | + </div> | |
256 | + </div> | |
257 | + </div> | |
258 | + </div> | |
259 | + </div> | |
260 | + <div class="content-box flex justify-between tab-box" v-show="activeTab === 'component'"> | |
261 | + <div class="content-box overflow-y"> | |
262 | + <ul class="flex justify-center"> | |
263 | + <li v-for="(cmpnt, idx) in chartComponent" :key="idx" class="flex5 mt10 cursor" @click=" | |
264 | + fnCreateComponent(idx, cmpnt.type, cmpnt.id, cmpnt.name) | |
265 | + "> | |
266 | + <div :class="{ | |
267 | + 'img-wrap border': true, | |
268 | + active: this.activeIndex === idx, | |
269 | + }"> | |
270 | + <img :src="cmpnt.src" alt="" /> | |
271 | + <div class="img-hover"> | |
272 | + <span>{{ cmpnt.name }}</span> | |
273 | + </div> | |
274 | + </div> | |
275 | + </li> | |
276 | + </ul> | |
277 | + </div> | |
278 | + </div> | |
279 | + </div> | |
280 | + <button class="custom-toggle" :style="isTabZoneOpen ? { right: '-25px' } : { right: '-24px' }" @click="fnTabZoneToggle"> | |
281 | + <svg-icon type="mdi" :path="arrowPath" :color="'#333'" /> | |
282 | + </button> | |
283 | + </div> | |
284 | + <!-- 미리보기 --> | |
285 | + <div :class="{ | |
286 | + 'preview-zone': true, | |
287 | + flex100: isTabZoneOpen, | |
288 | + flex70: !isTabZoneOpen, | |
289 | + }"> | |
290 | + <div id="splitter-container" ref="splitterContainer" :class="{ | |
291 | + 'splitter-panel-custom pl10': true, | |
292 | + active: clickElement === 'splitter-container', | |
293 | + }"> | |
294 | + <SplitterLayout :splitInfo="splitInfo" :selectLayout="currentLayout" @onChange="fnSelectLayout" /> | |
295 | + </div> | |
296 | + <div class="flex justify-end" id="buttonZone"> | |
297 | + <button class="blue-btn small-btn" @click="fnInsert"> | |
298 | + <span v-if="!pageId">등록</span> | |
299 | + <span v-else>수정</span> | |
300 | + </button> | |
301 | + <button class="blue-border-btn small-btn" @click="fnInit"> 초기화 </button> | |
302 | + <button class="darkg-btn small-btn" @click="moveBack"> 취소 </button> | |
303 | + </div> | |
304 | + </div> | |
305 | + </div> | |
306 | + </div> | |
307 | + </div> | |
308 | + </div> | |
309 | + </div> | |
310 | + <!-- 잡그룹관리 모달 --> | |
311 | + <div class="modal-wrapper" v-if="isModalOpen"> | |
312 | + <div class="modal-container"> | |
313 | + <div class="modal-title flex justify-between align-center"> | |
314 | + <h2>잡그룹 관리</h2> | |
315 | + <button class="close-btn" @click="fnModalClose"> | |
316 | + <svg-icon type="mdi" :width="20" :height="20" :path="closePath" /> | |
317 | + </button> | |
318 | + </div> | |
319 | + <div class="modal-content-monthly mb10"> | |
320 | + <JobGroupManagement :jobGroup="currentJobGroup" /> | |
321 | + </div> | |
322 | + <div class="modal-end flex justify-end"> | |
323 | + <button class="blue-btn small-btn" @click="fnSaveJobGroup"> 저장 </button> | |
324 | + <button class="blue-border-btn small-btn" @click="fnModalClose"> 취소 </button> | |
325 | + </div> | |
326 | + </div> | |
327 | + </div> | |
328 | +</template> | |
329 | +<script> | |
330 | +import _ from "lodash"; | |
331 | +import axios from "axios"; | |
332 | +// icon | |
333 | +import SvgIcon from "@jamescoyle/vue-icon"; | |
334 | +import { | |
335 | + mdiChevronLeft, | |
336 | + mdiChevronRight, | |
337 | + mdiLandPlots, | |
338 | + mdiRhombusSplit, | |
339 | + mdiDatabaseCogOutline, | |
340 | + mdiInformationSlabCircleOutline, | |
341 | + mdiDotsVertical, | |
342 | + mdiTableLarge, | |
343 | + mdiChartLine, | |
344 | + mdiDatabase, | |
345 | + mdiTrashCanOutline, | |
346 | + mdiClose, | |
347 | +} from "@mdi/js"; | |
348 | +import SplitterLayout from "../../component/SplitterLayout.vue"; | |
349 | +import TreeItem from "../../component/treeMenu/LayoutTree.vue"; | |
350 | +import StyleSheet from "../../component/style/StyleSheetComponent.vue"; | |
351 | +import BackgroundOption from "../../component/style/BackgroundOption.vue"; | |
352 | +import CellOption from "../../component/style/CellOption.vue"; | |
353 | +import FontOption from "../../component/style/FontOption.vue"; | |
354 | +import chartDataTransform from "../../component/chart/chartDataTransform.js"; | |
355 | +import html2canvas from "html2canvas"; | |
356 | +import DataComponent from "../../component/flowComponent/DataComponent.vue"; | |
357 | +import JobGroupManagement from "../../component/flowComponent/JobGroupManagement.vue"; | |
358 | + | |
359 | +export default { | |
360 | + components: { | |
361 | + SvgIcon, | |
362 | + SplitterLayout, | |
363 | + TreeItem, | |
364 | + BackgroundOption, | |
365 | + CellOption, | |
366 | + FontOption, | |
367 | + StyleSheet, | |
368 | + DataComponent, | |
369 | + JobGroupManagement, | |
370 | + }, | |
371 | + | |
372 | + data() { | |
373 | + return { | |
374 | + // icon | |
375 | + closePath: mdiClose, | |
376 | + arrowPath: mdiChevronLeft, | |
377 | + dotPath: mdiDotsVertical, | |
378 | + tablePath: mdiTableLarge, | |
379 | + chartPath: mdiChartLine, | |
380 | + dataPath: mdiDatabase, | |
381 | + trashPath: mdiTrashCanOutline, | |
382 | + | |
383 | + horizontalImg: require("../../../resources/img/icon/hor_b.png"), | |
384 | + verticalImg: require("../../../resources/img/icon/ver_b.png"), | |
385 | + | |
386 | + navList: [ | |
387 | + { id: "pageInfo", name: "페이지 정보", iconPath: mdiInformationSlabCircleOutline, }, | |
388 | + { id: "viewSetting", name: "레이아웃", iconPath: mdiLandPlots }, | |
389 | + { id: "component", name: "컴포넌트", iconPath: mdiRhombusSplit }, | |
390 | + { id: "dataset", name: "데이터", iconPath: mdiDatabaseCogOutline }, | |
391 | + ], | |
392 | + optionList: [], | |
393 | + chartComponent: [ | |
394 | + { | |
395 | + src: require("../../../resources/img/chartIcon/column_chart_v.png"), | |
396 | + type: "chart", | |
397 | + id: "column_chart_v", | |
398 | + name: "세로형 막대 차트", | |
399 | + }, | |
400 | + { | |
401 | + src: require("../../../resources/img/chartIcon/column_chart_h.png"), | |
402 | + type: "chart", | |
403 | + id: "column_chart_h", | |
404 | + name: "가로형 막대 차트", | |
405 | + }, | |
406 | + { | |
407 | + src: require("../../../resources/img/chartIcon/clustered_chart_v.png"), | |
408 | + type: "chart", | |
409 | + id: "clustered_chart_v", | |
410 | + name: "세로형 클러스터 차트", | |
411 | + }, | |
412 | + { | |
413 | + src: require("../../../resources/img/chartIcon/clustered_chart_h.png"), | |
414 | + type: "chart", | |
415 | + id: "clustered_chart_h", | |
416 | + name: "가로형 클러스터 차트", | |
417 | + }, | |
418 | + { | |
419 | + src: require("../../../resources/img/chartIcon/stacked_bar_chart.png"), | |
420 | + type: "chart", | |
421 | + id: "stacked_bar_chart", | |
422 | + name: "적층 막대 차트", | |
423 | + }, | |
424 | + { | |
425 | + src: require("../../../resources/img/chartIcon/line_chart.png"), | |
426 | + type: "chart", | |
427 | + id: "line_chart", | |
428 | + name: "선 차트", | |
429 | + }, | |
430 | + { | |
431 | + src: require("../../../resources/img/chartIcon/mix_chart.png"), | |
432 | + type: "chart", | |
433 | + id: "mix_chart", | |
434 | + name: "혼합 차트", | |
435 | + }, | |
436 | + { | |
437 | + src: require("../../../resources/img/chartIcon/pie_chart.png"), | |
438 | + type: "chart", | |
439 | + id: "pie_chart", | |
440 | + name: "파이 차트", | |
441 | + }, | |
442 | + { | |
443 | + src: require("../../../resources/img/chartIcon/dounet_chart.png"), | |
444 | + type: "chart", | |
445 | + id: "dounet_chart", | |
446 | + name: "도넛 차트", | |
447 | + }, | |
448 | + { | |
449 | + src: require("../../../resources/img/chartIcon/semicircle_chart.png"), | |
450 | + type: "chart", | |
451 | + id: "semicircle_chart", | |
452 | + name: "반원 차트", | |
453 | + }, | |
454 | + { | |
455 | + src: require("../../../resources/img/chartIcon/bubble_chart.png"), | |
456 | + type: "chart", | |
457 | + id: "bubble_chart", | |
458 | + name: "버블 차트", | |
459 | + }, | |
460 | + { | |
461 | + src: require("../../../resources/img/chartIcon/word_chart.png"), | |
462 | + type: "chart", | |
463 | + id: "word_chart", | |
464 | + name: "단어 차트", | |
465 | + }, | |
466 | + { | |
467 | + src: require("../../../resources/img/chartIcon/stacked_column_chart.png"), | |
468 | + type: "chart", | |
469 | + id: "stacked_column_chart", | |
470 | + name: "세로형 적층 막대 차트", | |
471 | + }, | |
472 | + { | |
473 | + src: require("../../../resources/img/chartIcon/node_line_chart.png"), | |
474 | + type: "chart", | |
475 | + id: "node_line_chart", | |
476 | + name: "노드 라인 차트", | |
477 | + }, | |
478 | + { | |
479 | + src: require("../../../resources/img/chartIcon/liin_chart_back.png"), | |
480 | + type: "chart", | |
481 | + id: "liin_chart_back", | |
482 | + name: "라인 백그라운드 차트", | |
483 | + }, | |
484 | + ], | |
485 | + | |
486 | + pageId: this.$route.query.page_id, | |
487 | + | |
488 | + activeTab: "pageInfo", | |
489 | + activeOption: "LAYOUT", | |
490 | + | |
491 | + isModalOpen: false, | |
492 | + isTabZoneOpen: false, | |
493 | + isAttributeOpen: false, | |
494 | + | |
495 | + // 최상위 레이아웃 정보 담는 객체 | |
496 | + splitInfo: _.cloneDeep(this.$getDefaultJobGroup().customSplitter), | |
497 | + splitInfo_dumy: _.cloneDeep(this.$getDefaultJobGroup().customSplitter), | |
498 | + | |
499 | + dragData: {}, | |
500 | + dataX: [], | |
501 | + dataY: [], | |
502 | + | |
503 | + // 페이지정보 탭 | |
504 | + customTitle: null, // 제목 | |
505 | + public_at: true, // 공개여부 | |
506 | + customComment: null, // 설명 | |
507 | + | |
508 | + activeIndex: null, | |
509 | + createChartData: _.cloneDeep(this.$getDefaultJobGroup().componentData), // x, y축 (객체 형식으로 데이터 수정 필요) | |
510 | + | |
511 | + // 레이아웃 및 컴포넌트 | |
512 | + // 레이아웃 | |
513 | + // 타이틀 | |
514 | + layoutSj: Object.assign({}, this.$getDefaultObject().layoutSj), | |
515 | + | |
516 | + // 컴포넌트 | |
517 | + | |
518 | + | |
519 | + // 잡그룹목록 | |
520 | + jobGroupList: [], | |
521 | + | |
522 | + // 현재 작업 객체 | |
523 | + currentLayout: {}, | |
524 | + currentJobGroup: Object.assign({}, this.$getDefaultObject().jobGroup), | |
525 | + currentJobGroupIdx: null, | |
526 | + currentCalc: "default", | |
527 | + currentDataTable: Object.assign({}, this.$getDefaultObject().dataTable), | |
528 | + | |
529 | + // 컬럼정보 | |
530 | + calcList: [ | |
531 | + { key: "default", value: "기본" }, | |
532 | + { key: "sum", value: "합계" }, | |
533 | + { key: "avg", value: "평균" }, | |
534 | + { key: "min", value: "최솟값" }, | |
535 | + { key: "max", value: "최댓값" }, | |
536 | + ], | |
537 | + }; | |
538 | + }, | |
539 | + | |
540 | + created() { | |
541 | + this.activeTab = "pageInfo"; | |
542 | + | |
543 | + if (this.pageId != null && this.pageId != undefined) { | |
544 | + this.getCustomData(); // 기존 데이터 가져오기 | |
545 | + } else { | |
546 | + this.init(); | |
547 | + } | |
548 | + }, | |
549 | + | |
550 | + computed: {}, | |
551 | + | |
552 | + watch: {}, | |
553 | + | |
554 | + methods: { | |
555 | + // 현재 탭 변경 | |
556 | + fnChangeTab(type, current) { | |
557 | + if (type == "nav") { | |
558 | + this.activeTab = current; | |
559 | + } else if (type == "opt") { | |
560 | + this.activeOption = current; | |
561 | + } | |
562 | + }, | |
563 | + | |
564 | + // 탭설정패널 여닫기 버튼 동작 | |
565 | + fnTabZoneToggle() { | |
566 | + this.isTabZoneOpen = !this.isTabZoneOpen; | |
567 | + | |
568 | + // 아이콘 변경 | |
569 | + if (this.isTabZoneOpen) { | |
570 | + this.arrowPath = mdiChevronRight; | |
571 | + } else { | |
572 | + this.arrowPath = mdiChevronLeft; | |
573 | + } | |
574 | + }, | |
575 | + | |
576 | + // 레이아웃 및 컴포넌트 더보기 버튼 동작 | |
577 | + fnAttributeToggle() { | |
578 | + this.isAttributeOpen = !this.isAttributeOpen; | |
579 | + }, | |
580 | + | |
581 | + // 유효성 검사 - 컴포넌트 추가 | |
582 | + validationByCreateComponent(id) { | |
583 | + if (id == "bubble_chart") { | |
584 | + this.$showAlert("메세지", "현재 지원하지 않는 컴포넌트입니다."); | |
585 | + return true; | |
586 | + } | |
587 | + | |
588 | + if (this.currentLayout == null) { | |
589 | + this.$showAlert("메세지", "컴포넌트를 추가할 레이아웃을 먼저 선택해 주세요."); | |
590 | + return true; | |
591 | + } | |
592 | + | |
593 | + if (this.currentLayout.children.length > 0) { | |
594 | + this.$showAlert("메세지", "선택된 레이아웃이 비어있지 않습니다.<br>선택된 레이아웃 내부를 비우거나 다른 레이아웃을 선택해 주세요."); | |
595 | + return true; | |
596 | + } | |
597 | + | |
598 | + return false; | |
599 | + }, | |
600 | + | |
601 | + // 컴포넌트 추가 | |
602 | + async fnCreateComponent(index, type, id, name) { | |
603 | + this.activeIndex = index; | |
604 | + | |
605 | + // 유효성 검사 | |
606 | + if (this.validationByCreateComponent(id)) { | |
607 | + return | |
608 | + }; | |
609 | + | |
610 | + let isCheck = await this.$showConfirm("컴포넌트 추가", name + " 컴포넌트를 추가하시겠습니까?"); | |
611 | + if (isCheck) { | |
612 | + let jobGroup = _.cloneDeep(this.$getDefaultJobGroup().jobGroup); | |
613 | + let chartData = _.cloneDeep(this.$getDefaultJobGroup().componentData); | |
614 | + chartData.chart_knd = id; | |
615 | + chartData.component_nm = "ChartCmmn"; | |
616 | + chartData.component_type = type; | |
617 | + | |
618 | + let component = _.cloneDeep(this.$getDefaultJobGroup().component); | |
619 | + component.component_itm = chartData; | |
620 | + component.jobInfo = []; | |
621 | + component.jobInfo.push(jobGroup); | |
622 | + | |
623 | + this.currentLayout.se = "component"; | |
624 | + this.currentLayout.component = component; | |
625 | + | |
626 | + this.getSelectLayoutChart(); // 선택한 레이아웃의 차트 정보 조회 | |
627 | + } | |
628 | + }, | |
629 | + | |
630 | + // 레이아웃 선택 | |
631 | + fnSelectLayout(layout) { | |
632 | + console.log("layout: ", layout); | |
633 | + | |
634 | + this.currentLayout = layout; | |
635 | + this.getSelectLayoutChart(); // 선택한 레이아웃의 차트 정보 조회 | |
636 | + | |
637 | + this.optionList = [ | |
638 | + { id: "LAYOUT", name: "레이아웃", useAt: true }, | |
639 | + { id: "TITLE", name: "타이틀", useAt: layout.children.length == 0 }, | |
640 | + { id: "COMPONENT", name: "컴포넌트", useAt: !this.$isEmpty(layout.component) }, | |
641 | + ]; | |
642 | + | |
643 | + if (this.$isEmpty(layout.component)) { | |
644 | + this.dataX = []; | |
645 | + this.dataY = []; | |
646 | + this.currentCalc = null; | |
647 | + } else { | |
648 | + this.dataX = layout.component.component_itm.categoryAxis; | |
649 | + this.dataY = layout.component.component_itm.valueAxis; | |
650 | + this.currentCalc = layout.component.component_itm.chart_cal; | |
651 | + } | |
652 | + }, | |
653 | + | |
654 | + // 선택한 레이아웃의 차트 정보 조회 | |
655 | + getSelectLayoutChart() { | |
656 | + if (this.currentLayout.se == "component") { | |
657 | + let chartId = this.currentLayout.component.component_itm.chart_knd; | |
658 | + if (this.$isEmpty(chartId)) { | |
659 | + this.chartComponent.forEach((item, idx) => { | |
660 | + if (item.id === chartId) { | |
661 | + this.activeIndex = idx; | |
662 | + } | |
663 | + }); | |
664 | + } else { | |
665 | + this.activeIndex = null; | |
666 | + } | |
667 | + } | |
668 | + }, | |
669 | + | |
670 | + findLoop(item) { | |
671 | + if (item.se == this.currentLayout.se) { | |
672 | + item = this.currentLayout; | |
673 | + return; | |
674 | + } else { | |
675 | + for (let next of item.children) { | |
676 | + this.findLoop(next); | |
677 | + } | |
678 | + } | |
679 | + }, | |
680 | + | |
681 | + // 레이아웃 추가 | |
682 | + async addSplitterLayout(type) { | |
683 | + // 유효성 검사 | |
684 | + if (this.currentLayout.layout_nm == null) { | |
685 | + this.$showAlert("메세지", "선택된 레이아웃이 없습니다."); | |
686 | + return; | |
687 | + } | |
688 | + | |
689 | + let isCheck = await this.$showConfirm("메세지", "선택된 레이아웃을 분할하시겠습니까?"); | |
690 | + if (!isCheck) { | |
691 | + return; | |
692 | + } | |
693 | + | |
694 | + let copy = _.cloneDeep(this.currentLayout); | |
695 | + copy.se = "splitter"; | |
696 | + copy.parents_splitter_id = this.currentLayout.layout_nm; | |
697 | + copy.depth = this.currentLayout.depth + 1; | |
698 | + copy.indx = 0; | |
699 | + copy.layout_nm = "LAY_" + this.generateShortUUID(); | |
700 | + | |
701 | + let newItem = _.cloneDeep(this.$getDefaultJobGroup().customSplitter); | |
702 | + newItem.se = "splitter"; | |
703 | + newItem.parents_splitter_id = this.currentLayout.layout_nm; | |
704 | + newItem.depth = this.currentLayout.depth + 1; | |
705 | + newItem.indx = 1; | |
706 | + newItem.layout_nm = "LAY_" + this.generateShortUUID(); | |
707 | + newItem.styleSheet.borderStyle = _.cloneDeep(this.$getDefaultJobGroup().borderStyle); | |
708 | + newItem.styleSheet.background_style = _.cloneDeep(this.$getDefaultJobGroup().background_style); | |
709 | + | |
710 | + this.currentLayout.se = "splitter"; | |
711 | + this.currentLayout.useSj = false; | |
712 | + this.currentLayout.layoutSj = Object.assign({}, this.$getDefaultJobGroup.layoutSj); | |
713 | + this.currentLayout.layout_type = type; | |
714 | + this.currentLayout.layout_size1 = 50; | |
715 | + this.currentLayout.layout_size2 = 50; | |
716 | + | |
717 | + this.currentLayout.component = null; | |
718 | + | |
719 | + this.currentLayout.children = []; | |
720 | + this.currentLayout.children.push(copy); | |
721 | + this.currentLayout.children.push(newItem); | |
722 | + }, | |
723 | + | |
724 | + // 등록-수정 | |
725 | + fnInsert() { | |
726 | + let vm = this; | |
727 | + | |
728 | + // 유효성 검사 | |
729 | + if (vm.customTitle == null || vm.customTitle == "") { | |
730 | + vm.$showAlert("메세지", "제목은 필수입력입니다."); | |
731 | + return; | |
732 | + } | |
733 | + | |
734 | + if (vm.splitInfo.component == null && vm.splitInfo.children.length == 0) { | |
735 | + vm.$showAlert("메세지", "레이아웃 또는 차트를 추가해야합니다."); | |
736 | + return; | |
737 | + } | |
738 | + | |
739 | + // 캡처할 요소 | |
740 | + let element = document.getElementById("splitter-container"); | |
741 | + | |
742 | + // html2canvas 사용하여 element 캡처 | |
743 | + html2canvas(element).then((canvas) => { | |
744 | + // 캔버스를 Blob 데이터로 변환 | |
745 | + canvas.toBlob(function (blob) { | |
746 | + let formData = new FormData(); | |
747 | + | |
748 | + // 공통 데이터 설정 | |
749 | + formData.append("img", blob, "capture.png"); | |
750 | + formData.append("ttl", vm.customTitle); | |
751 | + formData.append("cn", vm.customComment); | |
752 | + formData.append("splitInfo", vm.splitInfo); | |
753 | + formData.append("jobFlow", { jobGroup: vm.jobGroupList }); | |
754 | + formData.append("public_at", vm.public_at); | |
755 | + | |
756 | + // 수정 또는 등록에 따른 데이터 추가 | |
757 | + let url = "/custom/insert"; | |
758 | + if (!vm.pageId) { | |
759 | + formData.append("wrt_id", this.$store.state.loginUser.user_id); | |
760 | + } else { | |
761 | + url = "/custom/customUpdate"; | |
762 | + formData.append("page_id", vm.pageId); | |
763 | + formData.append("mdfcn_id", this.$store.state.loginUser.user_id); | |
764 | + } | |
765 | + | |
766 | + // API 호출 | |
767 | + axios({ | |
768 | + url: url, | |
769 | + method: "post", | |
770 | + headers: { "Content-Type": "multipart/form-data" }, | |
771 | + data: formData, | |
772 | + }) | |
773 | + .then(function (response) { | |
774 | + if (response.data.checkMessage.success) { | |
775 | + const successMsg = vm.pageId ? "페이지 수정에 성공하였습니다." : "페이지 등록에 성공하였습니다."; | |
776 | + vm.$showAlert("메세지", successMsg); | |
777 | + | |
778 | + const pageId = vm.pageId || response.data.resultData.pageId; | |
779 | + vm.$router.push({ | |
780 | + path: "/customSelectOne.page", | |
781 | + query: { page_id: pageId }, | |
782 | + }); | |
783 | + } else { | |
784 | + const failMsg = vm.pageId ? "수정에 실패하였습니다." : "등록에 실패하였습니다."; | |
785 | + vm.$showAlert("메세지", failMsg); | |
786 | + } | |
787 | + }) | |
788 | + .catch(function (error) { | |
789 | + if (error.response && error.response.data && error.response.data.code == "COMPONENT_DATA_NOT_FOUND") { | |
790 | + vm.$showAlert("메세지", "데이터를 추가하지 않은 컴포넌트가 존재합니다."); | |
791 | + } else { | |
792 | + vm.$showAlert("메세지", "요청 처리 중 오류가 발생했습니다."); | |
793 | + } | |
794 | + }); | |
795 | + }); | |
796 | + }); | |
797 | + }, | |
798 | + | |
799 | + // 아이디 만들기 | |
800 | + generateShortUUID() { | |
801 | + const fullUUID = crypto.randomUUID(); | |
802 | + return fullUUID.replace(/-/g, '').substring(0, 8); | |
803 | + }, | |
804 | + | |
805 | + // 초기화 | |
806 | + async init() { | |
807 | + this.splitInfo = _.cloneDeep(this.$getDefaultJobGroup().customSplitter); | |
808 | + this.splitInfo.se = "splitter"; | |
809 | + this.splitInfo.depth = 0; | |
810 | + this.splitInfo.layout_nm = "LAY_" + this.generateShortUUID(); | |
811 | + this.splitInfo.position_idx = 1; | |
812 | + this.splitInfo.styleSheet.borderStyle = Object.assign({}, this.$getDefaultJobGroup().borderStyle); | |
813 | + this.splitInfo.styleSheet.background_style = Object.assign({}, this.$getDefaultJobGroup().background_style); | |
814 | + | |
815 | + this.currentLayout = this.splitInfo; | |
816 | + | |
817 | + // 데이터 관리 초기화 | |
818 | + this.jobGroupList = []; | |
819 | + }, | |
820 | + | |
821 | + // 초기화 버튼 동작 | |
822 | + async fnInit() { | |
823 | + let isCheck = await this.$showConfirm("초기화", "초기화 하시겠습니까? 데이터가 모두 삭제됩니다."); | |
824 | + if (isCheck) { | |
825 | + this.init(); | |
826 | + } | |
827 | + }, | |
828 | + | |
829 | + // 컬럼정보 비우기 | |
830 | + fnInitColInfo() { | |
831 | + this.currentJobGroup = Object.assign({}, this.$getDefaultObject().jobGroup); | |
832 | + }, | |
833 | + | |
834 | + // 값 계산법 변경 | |
835 | + fnChangeCalc(value) { | |
836 | + this.currentLayout.component.component_itm.chart_cal = value; | |
837 | + }, | |
838 | + | |
839 | + // 기존 데이터 가져오기 | |
840 | + getCustomData() { | |
841 | + const vm = this; | |
842 | + axios({ | |
843 | + url: "/custom/customPageUpdateSelect", | |
844 | + method: "post", | |
845 | + headers: { "Content-Type": "application/json; charset=UTF-8" }, | |
846 | + data: { page_id: vm.pageId }, | |
847 | + }) | |
848 | + .then(function (response) { | |
849 | + let resPageInfo = response.data.resultData.pageInfo; | |
850 | + let resjobGroupList = response.data.resultData.jobGroupList; | |
851 | + let resSplitterInfo = response.data.resultData.splitterInfo; | |
852 | + | |
853 | + vm.customTitle = resPageInfo.ttl; | |
854 | + vm.customComment = resPageInfo.cn; | |
855 | + | |
856 | + vm.splitInfo = resSplitterInfo; | |
857 | + | |
858 | + vm.splitInfo_dumy.layout_nm = "dumy"; | |
859 | + vm.splitInfo_dumy.se = "splitter"; | |
860 | + vm.splitInfo_dumy.children.push(vm.splitInfo); | |
861 | + | |
862 | + vm.jobGroupList = resjobGroupList; | |
863 | + | |
864 | + vm.currentLayout = vm.splitInfo; | |
865 | + vm.public_at = resPageInfo.public_at; | |
866 | + vm.getSelectLayoutChart(); | |
867 | + }) | |
868 | + .catch(function (error) { | |
869 | + vm.$showAlert( | |
870 | + "데이터 현황관리 차트 연결", | |
871 | + "데이터 현황관리 차트 연결 오류, 관리자에게 문의하세요." | |
872 | + ); | |
873 | + vm.moveBack(); | |
874 | + }); | |
875 | + }, | |
876 | + | |
877 | + async deleteSplitterLayout() { | |
878 | + if (!this.currentLayout.layout_nm) { | |
879 | + this.$showAlert("메세지", "삭제할 레이아웃이 없습니다."); | |
880 | + return; | |
881 | + } | |
882 | + if (!this.currentLayout.parents_splitter_id) { | |
883 | + this.$showAlert("메세지", "최상위 레이아웃은 삭제할 수 없습니다."); | |
884 | + return; | |
885 | + } | |
886 | + if ( | |
887 | + await this.$showConfirm( | |
888 | + "레이아웃 삭제", | |
889 | + "선택한 레이아웃을 삭제하시겠습니까?" | |
890 | + ) | |
891 | + ) { | |
892 | + let parentLayout = this.findParentLayout( | |
893 | + this.splitInfo, | |
894 | + this.currentLayout.parents_splitter_id | |
895 | + ); | |
896 | + //선택한 레이아웃의 형제정보 찾기 | |
897 | + let otherIdx = parentLayout.children.findIndex( | |
898 | + (item) => item.layout_nm !== this.currentLayout.layout_nm | |
899 | + ); | |
900 | + let otherSplit = parentLayout.children[otherIdx]; | |
901 | + //otherSplit의 component, children 정보 가져오기 | |
902 | + let otherComponent = otherSplit.component; | |
903 | + let otherChildren = otherSplit.children; | |
904 | + let layoutSize1 = otherSplit.layout_size1; | |
905 | + let layoutSize2 = otherSplit.layout_size2; | |
906 | + let layoutType = otherSplit.layout_type; | |
907 | + let se = otherSplit.se; | |
908 | + let size = otherSplit.size; | |
909 | + let styleSheet = otherSplit.styleSheet; | |
910 | + | |
911 | + // 부모 레이아웃에 다른 split의 정보 넣어 주기 | |
912 | + parentLayout.component = otherComponent; | |
913 | + parentLayout.children = otherChildren; | |
914 | + parentLayout.layout_size1 = layoutSize1; | |
915 | + parentLayout.layout_size2 = layoutSize2; | |
916 | + parentLayout.layout_type = layoutType; | |
917 | + parentLayout.se = se; | |
918 | + parentLayout.size = size; | |
919 | + parentLayout.styleSheet = styleSheet; | |
920 | + | |
921 | + // 자식에게서 가져온 자식의 자식 정보의 부모 정보 변경 | |
922 | + if (parentLayout.children.length > 0) { | |
923 | + parentLayout.children.forEach((item) => { | |
924 | + item.parents_splitter_id = parentLayout.layout_nm; | |
925 | + }); | |
926 | + } | |
927 | + } | |
928 | + }, | |
929 | + | |
930 | + findParentLayout(splitInfo, selectParentLayoutId) { | |
931 | + // splitInfo가 배열인 경우 각 요소에 대해 재귀적으로 함수를 호출 | |
932 | + if (Array.isArray(splitInfo)) { | |
933 | + for (let i = 0; i < splitInfo.length; i++) { | |
934 | + let result = this.findParentLayout( | |
935 | + splitInfo[i], | |
936 | + selectParentLayoutId | |
937 | + ); | |
938 | + if (result) return result; // 일치하는 부모 레이아웃을 찾으면 반환 | |
939 | + } | |
940 | + } else { | |
941 | + // 현재 splitInfo 객체의 layout_nm이 찾고자 하는 selectParentLayoutId와 일치하는지 확인 | |
942 | + if (splitInfo.layout_nm === selectParentLayoutId) { | |
943 | + return splitInfo; // 일치하는 경우 현재 객체 반환 | |
944 | + } | |
945 | + | |
946 | + // 현재 객체의 자식들에 대해 재귀적으로 탐색 | |
947 | + if (splitInfo.children && splitInfo.children.length > 0) { | |
948 | + return this.findParentLayout( | |
949 | + splitInfo.children, | |
950 | + selectParentLayoutId | |
951 | + ); | |
952 | + } | |
953 | + } | |
954 | + | |
955 | + // 일치하는 요소를 찾지 못한 경우 | |
956 | + return null; | |
957 | + }, | |
958 | + | |
959 | + // 취소 (데이터활용관리 목록으로 이동) | |
960 | + async moveBack() { | |
961 | + let isCheck = await this.$showConfirm("경고", "취소할 경우 작성된 내용이 삭제됩니다."); | |
962 | + if (isCheck) { | |
963 | + this.$router.push({ path: "/customSelectList.page" }); | |
964 | + } | |
965 | + }, | |
966 | + | |
967 | + // 잡그룹 추가 | |
968 | + fnCreateJobGroup() { | |
969 | + let jobGroup = Object.assign({}, this.$getDefaultObject().jobGroup); | |
970 | + jobGroup.group_nm = "데이터" + (this.jobGroupList.length + 1); | |
971 | + this.jobGroupList.push(jobGroup); | |
972 | + this.fnSelectJobGroup(jobGroup); // 잡그룹 선택 (추가한 잡그룹 선택) | |
973 | + }, | |
974 | + | |
975 | + // 잡그룹 선택 | |
976 | + fnSelectJobGroup(jobGroup) { | |
977 | + this.currentJobGroup = jobGroup; | |
978 | + | |
979 | + // 잡그룹 내 마지막 데이터테이블 조회 | |
980 | + if (!this.currentJobGroup.jobItms || this.currentJobGroup.jobItms.length === 0) { | |
981 | + this.currentDataTable = Object.assign({}, this.$getDefaultObject().dataTable); | |
982 | + } else { | |
983 | + const index = this.currentJobGroup.jobItms.length - 1; | |
984 | + this.currentDataTable = this.currentJobGroup.jobItms[index].dataTable; | |
985 | + } | |
986 | + }, | |
987 | + | |
988 | + // 잡그룹관리 모달 열기 | |
989 | + fnModalOpen(jobGroup, idx) { | |
990 | + this.currentJobGroup = _.cloneDeep(jobGroup); | |
991 | + this.currentJobGroupIdx = idx; | |
992 | + this.isModalOpen = true; | |
993 | + }, | |
994 | + | |
995 | + // 잡그룹관리 모달 닫기 | |
996 | + fnModalClose() { | |
997 | + this.isModalOpen = false; | |
998 | + }, | |
999 | + | |
1000 | + // 잡그룹관리 저장 | |
1001 | + fnSaveJobGroup() { | |
1002 | + this.jobGroupList[this.currentJobGroupIdx] = this.currentJobGroup; | |
1003 | + this.fnSelectJobGroup(this.currentJobGroup); // 잡그룹 선택 (저장한 정보 선택) | |
1004 | + this.fnModalClose(); // 노드설정 모달 닫기 | |
1005 | + }, | |
1006 | + | |
1007 | + // 드래그 이벤트 | |
1008 | + startDrag(event, data, idx) { | |
1009 | + this.dragData = data; | |
1010 | + }, | |
1011 | + | |
1012 | + // 드랍 이벤트 | |
1013 | + async onDrop(event, type) { | |
1014 | + // 컴포넌트가 설정되지 않은 경우 경고 후 실행 취소 | |
1015 | + if (this.$isEmpty(this.currentLayout.component)) { | |
1016 | + this.$showAlert("경고", "컴포넌트 등록 후 설정할 수 있습니다."); | |
1017 | + return; | |
1018 | + } | |
1019 | + | |
1020 | + if (type == "xdata") { | |
1021 | + if (this.dataX.length > 0) { | |
1022 | + let isCheck = await this.$showConfirm( | |
1023 | + "경고", | |
1024 | + "데이터를 변경하시겠습니까?" | |
1025 | + ); | |
1026 | + if (!isCheck) { | |
1027 | + return; | |
1028 | + } | |
1029 | + } | |
1030 | + this.dataX = []; // 초기화 | |
1031 | + this.dataX.push(this.dragData); | |
1032 | + } else if (type == "ydata") { | |
1033 | + if (this.dragData.dataTy === "STRING") { | |
1034 | + this.$showAlert( | |
1035 | + "메세지", | |
1036 | + "값(Value)에는 숫자 데이터만 사용 가능합니다." | |
1037 | + ); | |
1038 | + return; | |
1039 | + } | |
1040 | + this.dataY.push(this.dragData); | |
1041 | + } | |
1042 | + | |
1043 | + if (!this.$isEmpty(this.dataX)) { | |
1044 | + this.currentLayout.component.component_itm.categoryAxis = this.dataX; | |
1045 | + } | |
1046 | + if (!this.$isEmpty(this.dataY)) { | |
1047 | + this.currentLayout.component.component_itm.valueAxis = this.dataY; | |
1048 | + } | |
1049 | + | |
1050 | + // 축 데이터를 이용한 차트 데이터 생성 | |
1051 | + if (!this.$isEmpty(this.dataX) && !this.$isEmpty(this.dataY)) { | |
1052 | + let dataList = chartDataTransform.createData( | |
1053 | + this.currentDataTable.rowData, | |
1054 | + this.currentLayout.component.component_itm.categoryAxis, | |
1055 | + this.currentLayout.component.component_itm.valueAxis, | |
1056 | + "null" | |
1057 | + ); | |
1058 | + this.currentLayout.component.component_itm.data_list = dataList; | |
1059 | + } | |
1060 | + this.currentLayout.component.jobInfo[0] = this.currentJobGroup; | |
1061 | + }, | |
1062 | + | |
1063 | + // 아이템 삭제 | |
1064 | + deleteItem(column, category) { | |
1065 | + if (category == "x") { | |
1066 | + for (let i = 0; i < this.dataX.length; i++) { | |
1067 | + if (this.dataX[i].orginlColumnNm == column.orginlColumnNm) { | |
1068 | + this.dataX.splice(i, 1); | |
1069 | + } | |
1070 | + } | |
1071 | + } else if (category == "y") { | |
1072 | + for (let i = 0; i < this.dataY.length; i++) { | |
1073 | + if (this.dataY[i].orginlColumnNm == column.orginlColumnNm) { | |
1074 | + this.dataY.splice(i, 1); | |
1075 | + } | |
1076 | + } | |
1077 | + } | |
1078 | + }, | |
1079 | + | |
1080 | + // 잡그룹 삭제 | |
1081 | + async deleteDiagram(item) { | |
1082 | + let isCheck = await this.$showConfirm( | |
1083 | + "삭제", | |
1084 | + "선택한 데이터를 삭제하시겠습니까?\n관련된 데이터도 함께 삭제됩니다." | |
1085 | + ); | |
1086 | + if (!isCheck) { | |
1087 | + return; | |
1088 | + } | |
1089 | + | |
1090 | + // splitInfo에 적용했던 데이터가 있는지 확인하여 삭제 (재귀) | |
1091 | + if (this.splitInfo.children.length > 0) { | |
1092 | + this.RecursiveFunc(this.splitInfo.children, item); | |
1093 | + } | |
1094 | + | |
1095 | + if (this.splitInfo.component != null) { | |
1096 | + if (this.splitInfo.component.jobInfo[0].group_nm == item.group_nm) { | |
1097 | + this.splitInfo.component = null; | |
1098 | + this.splitInfo.se = "splitter"; | |
1099 | + } | |
1100 | + } | |
1101 | + | |
1102 | + this.jobGroupList = this.jobGroupList.filter( | |
1103 | + (jobItem) => jobItem !== item | |
1104 | + ); | |
1105 | + | |
1106 | + // 초기화 | |
1107 | + this.dragData = {}; | |
1108 | + this.dataX = []; | |
1109 | + this.dataY = []; | |
1110 | + }, | |
1111 | + | |
1112 | + // 재귀함수 | |
1113 | + RecursiveFunc(children, item) { | |
1114 | + for (let i = 0; i < children.length; i++) { | |
1115 | + if (children[i].children.length > 0) { | |
1116 | + this.RecursiveFunc(children[i].children, item); | |
1117 | + } else { | |
1118 | + if (children[i].component != null) { | |
1119 | + // children[i].jobInfo 와 item이 같은지 확인하여 삭제 | |
1120 | + if (children[i].component.jobInfo[0].group_nm == item.group_nm) { | |
1121 | + children[i].component = null; | |
1122 | + children[i].se = "splitter"; | |
1123 | + } | |
1124 | + } | |
1125 | + } | |
1126 | + } | |
1127 | + }, | |
1128 | + }, | |
1129 | +}; | |
1130 | +</script> | |
1131 | +<style scoped> | |
1132 | +#splitter-container { | |
1133 | + width: 100%; | |
1134 | + height: calc(100% - 50px); | |
1135 | + margin-bottom: 20px; | |
1136 | +} | |
1137 | + | |
1138 | +#SplitterLayout { | |
1139 | + width: 100%; | |
1140 | + height: 100%; | |
1141 | +} | |
1142 | + | |
1143 | +#splitter-container.active { | |
1144 | + border: 3px dotted red; | |
1145 | +} | |
1146 | + | |
1147 | +.padding-1 { | |
1148 | + padding: 1rem; | |
1149 | +} | |
1150 | + | |
1151 | +#customComment { | |
1152 | + height: 99px; | |
1153 | +} | |
1154 | + | |
1155 | +.table-padding { | |
1156 | + padding: 15px 0; | |
1157 | +} | |
1158 | + | |
1159 | +.paletteLi { | |
1160 | + display: inline-block; | |
1161 | + margin-right: 10px; | |
1162 | +} | |
1163 | + | |
1164 | +.img-wrap { | |
1165 | + position: relative; | |
1166 | +} | |
1167 | + | |
1168 | +.img-wrap:hover .img-hover { | |
1169 | + display: flex; | |
1170 | +} | |
1171 | + | |
1172 | +.img-wrap .img-hover { | |
1173 | + position: absolute; | |
1174 | + top: 0; | |
1175 | + left: 0; | |
1176 | + right: 0; | |
1177 | + bottom: 0; | |
1178 | + background-color: #213f9999; | |
1179 | + display: none; | |
1180 | + align-items: center; | |
1181 | + justify-content: center; | |
1182 | +} | |
1183 | + | |
1184 | +.img-wrap .img-hover span { | |
1185 | + padding: 10px; | |
1186 | + text-align: center; | |
1187 | + font-size: 1.5em; | |
1188 | + color: #ffffff; | |
1189 | + word-break: keep-all; | |
1190 | +} | |
1191 | + | |
1192 | +.selectData { | |
1193 | + border: solid 2px #5d5c5c; | |
1194 | + color: #ffffff; | |
1195 | + background-color: #ff9e29; | |
1196 | +} | |
1197 | +</style>(파일 끝에 줄바꿈 문자 없음) |
--- client/views/pages/data/filemanger/FileManagementMain.vue
+++ client/views/pages/data/filemanger/FileManagementMain.vue
... | ... | @@ -457,8 +457,8 @@ |
457 | 457 |
</template> |
458 | 458 |
<script> |
459 | 459 |
import axios from "axios"; |
460 |
-import TreeItem from "../../../component/FileTree.vue"; |
|
461 |
-import TreeModal from "../../../component/FileTreeModal.vue"; |
|
460 |
+import TreeItem from "../../../component/treeMenu/FileTree.vue"; |
|
461 |
+import TreeModal from "../../../component/treeMenu/FileTreeModal.vue"; |
|
462 | 462 |
import SvgIcon from "@jamescoyle/vue-icon"; |
463 | 463 |
import NodeSetupModalDA from "../../../component/modal/NodeSetupModalDA.vue"; |
464 | 464 |
import EhojoConnection from "../../../component/connection/EhojoConnection.vue"; |
--- client/views/pages/integrated/department/DeptHostList.vue
+++ client/views/pages/integrated/department/DeptHostList.vue
... | ... | @@ -139,7 +139,7 @@ |
139 | 139 |
// 컴포넌트 import |
140 | 140 |
import PaginationButton from "../../../component/PaginationButton.vue"; |
141 | 141 |
import HostDrctryListModal from "../../../component/modal/HostDrctryListModal.vue"; |
142 |
-import TreeModal from "../../../component/FileTreeModal.vue"; |
|
142 |
+import TreeModal from "../../../component/treeMenu/FileTreeModal.vue"; |
|
143 | 143 |
|
144 | 144 |
export default { |
145 | 145 |
components: { SvgIcon, PaginationButton, HostDrctryListModal, TreeModal }, |
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?