박정하 박정하 03-05
250305 박정하 데이터 활용관리 레이아웃 탭 오류 및 레이아웃 삭제 수정
@8557e0c9006e46c65e114506a0c74fb4b38baf4e
client/views/component/style/StyleSheetComponent.vue
--- client/views/component/style/StyleSheetComponent.vue
+++ client/views/component/style/StyleSheetComponent.vue
@@ -1,7 +1,7 @@
 <template>
   <FontOption v-if="fontAt" />
-  <CellOption :borderStyle="styleSheet.borderStyle" v-if="cellAt" />
-  <BackgroundOption :background_style="styleSheet.background_style" v-if="backgroundAt" />
+  <CellOption v-if="cellAt" :borderStyle="styleSheet.borderStyle" />
+  <BackgroundOption v-if="backgroundAt" :background_style="styleSheet.background_style" />
 </template>
 <script>
 import BackgroundOption from "./BackgroundOption.vue";
@@ -22,12 +22,23 @@
       backgroundAt: false,
     };
   },
+  created() {
+    this.init();
+  },
   watch: {
-    styleSheet: function (v) {
-      this.cellAt = v.borderStyle != null;
-      this.backgroundAt = v.background_style != null;
+    styleSheet: {
+      handler(v) {
+        this.init();
+      },
+      deep: true
     },
   },
+  methods: {
+    init() {
+      this.cellAt = this.styleSheet.borderStyle != null;
+      this.backgroundAt = this.styleSheet.background_style != null;
+    }
+  },
   components: {
     BackgroundOption: BackgroundOption,
     CellOption: CellOption,
client/views/component/treeMenu/LayoutTree.vue
--- client/views/component/treeMenu/LayoutTree.vue
+++ client/views/component/treeMenu/LayoutTree.vue
@@ -7,18 +7,22 @@
       </template>
       <template v-else>
         <svg-icon type="mdi" :width="18" :height="18" :path="dotPath" />
-        <svg-icon type="mdi" :width="18" :height="18" :path="layoutPath" :color="'#fbbe28'" />
+        <template v-if="splitInfo.se == 'splitter'">
+          <svg-icon type="mdi" v-if="splitInfo.useSj" :width="18" :height="18" :path="titlePath" :color="'#fbbe28'" />
+          <svg-icon type="mdi" v-else :width="18" :height="18" :path="layoutPath" :color="'#fbbe28'" />
+        </template>
+        <svg-icon type="mdi" v-if="splitInfo.se == 'component'" :width="18" :height="18" :path="filePath" :color="'#fbbe28'" />
       </template>
       <p class="node-text">{{ splitInfo.layout_nm }}</p>
     </div>
-    <ul v-if="splitInfo.children.length > 0" class="children-node" :style="{ height: toggleSelect ? 'auto' : '0' }">
+    <ul v-if="splitInfo.children.length > 0" class="children-node">
       <TreeItem v-for="(node, indx) in splitInfo.children" :splitInfo="node" :selectLayout='selectLayout' :key="indx" @onChange="onChange" />
     </ul>
   </li>
 </template>
 <script>
 import SvgIcon from '@jamescoyle/vue-icon';
-import { mdiLandPlots, mdiChevronRight, mdiRhombusSplit, mdiCircleSmall, mdiCropSquare } from '@mdi/js';
+import { mdiLandPlots, mdiChevronRight, mdiRhombusSplit, mdiCircleSmall, mdiCropSquare, mdiFormatTitle } from '@mdi/js';
 export default {
   components: {
     'SvgIcon': SvgIcon
@@ -33,16 +37,14 @@
   },
   data() {
     return {
-      currentSelectLayout: this.selectLayout,
-      toggleSelect: false,
-      clidrunNode: false,
       folderPath: mdiLandPlots,
       layoutPath: mdiCropSquare,
       arrowPath: mdiChevronRight,
       filePath: mdiRhombusSplit,
-      selectedId: null,
-      parentSelectNode: null,
       dotPath: mdiCircleSmall,
+      titlePath: mdiFormatTitle,
+
+      currentSelectLayout: this.selectLayout,
     }
   },
   watch: {
@@ -55,7 +57,6 @@
   },
   methods: {
     clickLayout(splitInfo) {
-      this.toggleSelect = !this.toggleSelect;
       this.$emit('onChange', splitInfo);
     },
 
@@ -72,8 +73,6 @@
 
 .children-node {
   padding: 0 0 0 10px;
-  overflow: hidden;
-  transition: max-height 0.5s ease-in-out;
 }
 
 .node-text {
client/views/pages/custom/InsertDataAnalytics.vue
--- client/views/pages/custom/InsertDataAnalytics.vue
+++ client/views/pages/custom/InsertDataAnalytics.vue
@@ -103,25 +103,25 @@
                     <div class="tabnav3">
                       <ul class="flex justify-start align-center">
                         <template v-for="(item, idx) of optionList" :key="idx">
-                          <li class="cursor" v-if="item.useAt" @click="fnChangeTab('opt', item.id)">
+                          <li class="cursor" v-show="item.useAt" @click="fnChangeTab('opt', item.id)">
                             <p :class="{ activeOption: activeOption === item.id }">{{ item.name }}</p>
                           </li>
                         </template>
                       </ul>
                     </div>
-                    <div v-if="activeOption == 'LAYOUT'">
+                    <div v-if="activeOption === 'LAYOUT'">
                       <StyleSheet :styleSheet="currentLayout.styleSheet" />
                     </div>
-                    <div v-if="activeOption == 'TITLE'">
+                    <div v-if="activeOption === 'TITLE'">
                       <div class="table-zone">
                         <p class="object-title mb5">타이틀 사용 여부</p>
                         <div class="input-container flex">
                           <label class="radio-label">
-                            <input type="radio" name="titleUseAt" class="custom-radiobox" :value="true" v-model="currentLayout.useSj">
+                            <input type="radio" name="titleUseAt" class="custom-radiobox" :value="true" v-model="isUseSj" @change="fnChangeUseSj">
                             <span>사용</span>
                           </label>
                           <label class="radio-label">
-                            <input type="radio" name="titleUseAt" class="custom-radiobox" :value="false" v-model="currentLayout.useSj">
+                            <input type="radio" name="titleUseAt" class="custom-radiobox" :value="false" v-model="isUseSj" @change="fnChangeUseSj">
                             <span>미사용</span>
                           </label>
                         </div>
@@ -139,7 +139,7 @@
                         </div>
                       </div>
                     </div>
-                    <div v-if="activeOption == 'COMPONENT'">
+                    <div v-if="activeOption === 'COMPONENT'">
                       <StyleSheet :styleSheet="currentLayout.styleSheet" />
                     </div>
                   </div>
@@ -166,8 +166,8 @@
                               <span>{{ item.group_nm }}</span>
                             </p>
                             <div>
-                              <button class="blue-border-btn set-btn" @click.stop="" @click="fnModalOpen(item, idx)"> 설정 </button>
-                              <button class="red-border-btn set-btn" @click.stop="" @click="deleteDiagram(item)"> 삭제 </button>
+                              <button class="blue-border-btn set-btn" @click.stop="" @click="fnModalOpen(item, idx)">설정</button>
+                              <button class="red-border-btn set-btn" @click.stop="" @click="deleteDiagram(item)">삭제</button>
                             </div>
                           </div>
                         </div>
@@ -495,6 +495,7 @@
       isModalOpen: false,
       isTabZoneOpen: false,
       isAttributeOpen: false,
+      isUseSj: false,
 
       activeTab: "pageInfo",
       activeOption: "LAYOUT",
@@ -534,18 +535,24 @@
 
   computed: {},
 
-  watch: {},
+  watch: {
+    // 컴포넌트 사용 여부
+    'currentLayout.component'(newVal, oldVal) {
+      if (!this.$isEmpty(newVal)) {
+        this.currentLayout.se = 'component'
+      }
+    },
+  },
 
   methods: {
     // 초기화
     async init() {
-      // 탭, 옵션 초기화
-      this.$activeTab = "pageInfo";
-      this.activeOption = "LAYOUT";
       this.activeIndex = null;
 
       // 페이지 정보 초기화
-      this.initPageInfo();
+      this.customTitle = null;
+      this.customComment = null;
+      this.public_at = true;
 
       // 레이아웃 초기화
       let splitInfo = _.cloneDeep(this.$getDefaultJobGroup().customSplitter);
@@ -556,62 +563,10 @@
       splitInfo.styleSheet.borderStyle = Object.assign({}, this.$getDefaultJobGroup().borderStyle);
       splitInfo.styleSheet.background_style = Object.assign({}, this.$getDefaultJobGroup().background_style);
       this.splitInfo = splitInfo;
+      this.fnSelectLayout(splitInfo); // 레이아웃 선택
 
       // 데이터 초기화
       this.jobGroupList = [];
-
-      this.initCurrent(this.splitInfo); // 현재 작업 객체 초기화
-    },
-
-    // 페이지 정보 초기화
-    initPageInfo() {
-      this.customTitle = null;
-      this.customComment = null;
-      this.public_at = true;
-    },
-
-    // 현재 작업 객체 초기화
-    initCurrent(layout) {
-      this.currentLayout = layout;
-
-      // 레이어에 컴포넌트를 생성한 경우
-      let component = layout.component;
-      if (component != null) {
-        // 컴포넌트 선택
-        this.activeIndex = null; // 초기화
-        this.chartComponent.forEach((item, idx) => {
-          if (item.id === component.component_itm.chart_knd) {
-            this.activeIndex = idx;
-          }
-        });
-
-        // 컬럼 정보 변경
-        this.dataX = component.component_itm.categoryAxis;
-        this.dataY = component.component_itm.valueAxis;
-        this.currentCalc = component.component_itm.chart_cal;
-
-        // 현재 잡그룹 변경
-        if (component.jobInfo.length > 0) {
-          this.currentJobGroupIdx = null;
-          this.currentJobGroup = component.jobInfo[0];
-
-          // 현재 데이터테이블 변경
-          if (this.currentJobGroup.jobItms.length === 0) {
-            this.currentDataTable = Object.assign({}, this.$getDefaultObject().dataTable);
-          } else {
-            const index = this.currentJobGroup.jobItms.length - 1;
-            this.currentDataTable = this.currentJobGroup.jobItms[index].dataTable;
-          }
-
-          this.onChangeChartData(); // 차트 데이터 수정
-
-          return;
-        }
-      }
-
-      this.currentJobGroupIdx = null;
-      this.currentJobGroup = Object.assign({}, this.$getDefaultObject().jobGroup);
-      this.initColumnInfo(); // 현재 컬럼 정보 초기화
     },
 
     // 현재 컬럼 정보 초기화
@@ -659,7 +614,7 @@
           // 레이아웃
           let resSplitterInfo = response.data.resultData.splitterInfo;
           vm.splitInfo = resSplitterInfo;
-          vm.currentLayout = vm.splitInfo;
+          this.fnSelectLayout(vm.splitInfo); // 레이아웃 선택
 
           // 데이터 목록
           let resjobGroupList = response.data.resultData.jobGroupList;
@@ -672,11 +627,12 @@
     },
 
     // 현재 탭 변경
-    fnChangeTab(type, current) {
+    fnChangeTab(type, value) {
+      console.log("current: ", value)
       if (type == "nav") {
-        this.activeTab = current;
+        this.activeTab = value;
       } else if (type == "opt") {
-        this.activeOption = current;
+        this.activeOption = value;
       }
     },
 
@@ -695,6 +651,23 @@
     // 레이아웃 및 컴포넌트 더보기 버튼 동작
     fnAttributeToggle() {
       this.isAttributeOpen = !this.isAttributeOpen;
+    },
+
+    // 타이틀 사용 여부
+    async fnChangeUseSj() {
+      const isCheck = await this.$showConfirm("경고", "타이틀 사용 여부 변경 시 작업 중인 데이터가 삭제됩니다.<br>타이틀을 사용 여부를 변경 하시겠습니까?");
+      if (isCheck) {
+        this.currentLayout.useSj = this.isUseSj;
+        this.currentLayout.se = 'splitter';
+
+        if (this.currentLayout.useSj) {
+          this.currentLayout.component = null; // 컴포넌트 초기화
+        } else {
+          this.currentLayout.layoutSj = _.cloneDeep(this.$getDefaultObject().layoutSj); // 타이틀 초기화
+        }
+      } else {
+        this.isUseSj = this.currentLayout.useSj;
+      }
     },
 
     // 유효성 검사 - 컴포넌트 추가
@@ -757,15 +730,56 @@
     fnSelectLayout(layout) {
       console.log("layout: ", layout);
 
+      // 현재 레이아웃 변경
       this.currentLayout = layout;
 
+      // 현재 레이아웃에 따른 레이아웃 탭 옵션
       this.optionList = [
         { id: "LAYOUT", name: "레이아웃", useAt: true },
         { id: "TITLE", name: "타이틀", useAt: layout.children.length == 0 },
         { id: "COMPONENT", name: "컴포넌트", useAt: !this.$isEmpty(layout.component) },
       ];
+      this.fnChangeTab("opt", "LAYOUT"); // 탭 변경
 
-      this.initCurrent(layout); // 현재 작업 객체 초기화
+      // 현재 레이아웃에 컴포넌트가 있는 경우
+      this.activeIndex = null; // 초기화
+
+      let component = layout.component;
+      if (component != null) {
+        for (let i = 0; i < this.chartComponent.length; i++) {
+          let item = this.chartComponent;
+          if (item.id === component.component_itm.chart_knd) {
+            this.activeIndex = i;
+            break;
+          }
+        }
+
+        // 컬럼 정보 변경
+        this.dataX = component.component_itm.categoryAxis;
+        this.dataY = component.component_itm.valueAxis;
+        this.currentCalc = component.component_itm.chart_cal;
+
+        // 현재 잡그룹 변경
+        if (component.jobInfo.length > 0) {
+          this.currentJobGroupIdx = null;
+          this.currentJobGroup = component.jobInfo[0];
+
+          // 현재 데이터테이블 변경
+          if (this.currentJobGroup.jobItms.length === 0) {
+            this.currentDataTable = Object.assign({}, this.$getDefaultObject().dataTable);
+          } else {
+            const index = this.currentJobGroup.jobItms.length - 1;
+            this.currentDataTable = this.currentJobGroup.jobItms[index].dataTable;
+          }
+
+          this.onChangeChartData(); // 차트 데이터 수정
+          return;
+        }
+      }
+
+      this.currentJobGroupIdx = null;
+      this.currentJobGroup = Object.assign({}, this.$getDefaultObject().jobGroup);
+      this.initColumnInfo(); // 현재 컬럼 정보 초기화
     },
 
     findLoop(item) {
@@ -941,72 +955,51 @@
         return;
       }
 
-      let parentLayout = this.findParentLayout(
-        this.splitInfo,
-        this.currentLayout.parents_splitter_id
-      );
+      let parentLayout = this.findParentLayout(this.splitInfo, this.currentLayout.parents_splitter_id);
 
-      //선택한 레이아웃의 형제정보 찾기
-      let otherIdx = parentLayout.children.findIndex(
-        (item) => item.layout_nm !== this.currentLayout.layout_nm
-      );
-      let otherSplit = parentLayout.children[otherIdx];
-      //otherSplit의 component, children 정보 가져오기
-      let otherComponent = otherSplit.component;
-      let otherChildren = otherSplit.children;
-      let layoutSize1 = otherSplit.layout_size1;
-      let layoutSize2 = otherSplit.layout_size2;
-      let layoutType = otherSplit.layout_type;
-      let se = otherSplit.se;
-      let size = otherSplit.size;
-      let styleSheet = otherSplit.styleSheet;
-
-      // 부모 레이아웃에 다른 split의 정보 넣어 주기
-      parentLayout.component = otherComponent;
-      parentLayout.children = otherChildren;
-      parentLayout.layout_size1 = layoutSize1;
-      parentLayout.layout_size2 = layoutSize2;
-      parentLayout.layout_type = layoutType;
-      parentLayout.se = se;
-      parentLayout.size = size;
-      parentLayout.styleSheet = styleSheet;
-
-      // 자식에게서 가져온 자식의 자식 정보의 부모 정보 변경
       if (parentLayout.children.length > 0) {
-        parentLayout.children.forEach((item) => {
-          item.parents_splitter_id = parentLayout.layout_nm;
-        });
+        let copy = _.cloneDeep(parentLayout.children.filter((item) => item !== this.currentLayout));
+        console.log("copy: ", copy);
+
+        if (copy.length == 1) {
+          parentLayout.se = copy[0].se;
+          parentLayout.type = copy[0].type;
+          parentLayout.min_width = copy[0].min_width;
+          parentLayout.min_height = copy[0].min_height;
+          parentLayout.layout_size1 = copy[0].layout_size1;
+          parentLayout.layout_size2 = copy[0].layout_size2;
+          parentLayout.layout_type = copy[0].layout_type;
+          parentLayout.styleSheet = copy[0].styleSheet;
+          parentLayout.children = copy[0].children;
+          parentLayout.component = copy[0].component;
+          parentLayout.active = copy[0].active;
+          parentLayout.layoutSj = copy[0].layoutSj;
+          parentLayout.sizes = copy[0].sizes;
+          parentLayout.useSj = copy[0].useSj;
+
+          return;
+        }
       }
+
+      parentLayout.children = parentLayout.children.filter((item) => item !== this.currentLayout);
     },
 
     // 부모 요소 검색
-    findParentLayout(splitInfo, selectParentLayoutId) {
-      // splitInfo가 배열인 경우 각 요소에 대해 재귀적으로 함수를 호출
-      if (Array.isArray(splitInfo)) {
-        for (let i = 0; i < splitInfo.length; i++) {
-          let result = this.findParentLayout(
-            splitInfo[i],
-            selectParentLayoutId
-          );
-          if (result) return result; // 일치하는 부모 레이아웃을 찾으면 반환
-        }
+    findParentLayout(layout, parentId) {
+      let result = null;
+      if (layout.layout_nm === parentId) {
+        result = layout;
       } else {
-        // 현재 splitInfo 객체의 layout_nm이 찾고자 하는 selectParentLayoutId와 일치하는지 확인
-        if (splitInfo.layout_nm === selectParentLayoutId) {
-          return splitInfo; // 일치하는 경우 현재 객체 반환
-        }
-
-        // 현재 객체의 자식들에 대해 재귀적으로 탐색
-        if (splitInfo.children && splitInfo.children.length > 0) {
-          return this.findParentLayout(
-            splitInfo.children,
-            selectParentLayoutId
-          );
+        if (layout.children.length > 0) {
+          for (let child of layout.children) {
+            result = this.findParentLayout(child, parentId);
+            if (result != null) {
+              break;
+            }
+          }
         }
       }
-
-      // 일치하는 요소를 찾지 못한 경우
-      return null;
+      return result;
     },
 
     // 취소 (데이터활용관리 목록으로 이동)
@@ -1039,17 +1032,18 @@
     // 잡그룹 선택
     async fnSelectJobGroup(jobGroup) {
       let component = this.currentLayout.component;
-      if (component != null && jobGroup.jobItms.length > 0) {
-        let index = jobGroup.jobItms.length - 1;
-        if (component.component_itm.dataTable == jobGroup.jobItms[index].dataTable) {
-          return;
+      if (component != null) {
+        if (jobGroup.jobItms.length > 0) {
+          let index = jobGroup.jobItms.length - 1;
+          if (component.component_itm.dataTable == jobGroup.jobItms[index].dataTable) {
+            return;
+          }
         }
-      }
-
-      if (!this.$isEmpty(component.component_itm.data_list)) {
-        let isCheck = await this.$showConfirm("경고", "컴포넌트 컬럼 설정 후, 다른 데이터를 선택할 경우 기존 정보가 삭제됩니다.<br>다른 데이터를 선택하시겠습니까?");
-        if (!isCheck) {
-          return;
+        if (!this.$isEmpty(component.component_itm.data_list)) {
+          let isCheck = await this.$showConfirm("경고", "컴포넌트 컬럼 설정 후, 다른 데이터를 선택할 경우 기존 정보가 삭제됩니다.<br>다른 데이터를 선택하시겠습니까?");
+          if (!isCheck) {
+            return;
+          }
         }
       }
 
@@ -1083,6 +1077,12 @@
 
     // 드랍 이벤트
     async onDrop(type) {
+      // 레이아웃인 경우 경고 후 실행 취소
+      if (this.currentLayout.children.length > 0) {
+        this.$showAlert("경고", "레이아웃은 설정할 수 없습니다.");
+        return;
+      }
+
       // 컴포넌트가 설정되지 않은 경우 경고 후 실행 취소
       if (this.$isEmpty(this.currentLayout.component)) {
         this.$showAlert("경고", "컴포넌트 등록 후 설정할 수 있습니다.");
Add a comment
List