박정하 박정하 03-27
250327 박정하 사진기록물 목록 조회, 상세 조회, 등록, 수정, 삭제 수정
@269a57c611f2ad05a71b33e2212079d44e1a1150
client/resources/api/dcry.js
--- client/resources/api/dcry.js
+++ client/resources/api/dcry.js
@@ -1,8 +1,8 @@
 import { apiClient, fileClient } from "./index";
 
 // 기록물 목록 조회
-export const findAllDatas = (searchReqDTO) => {
-  return apiClient.get(`/dcry/findAllDatas.json`, { params: searchReqDTO });
+export const findDcrysProc = (searchReqDTO) => {
+  return apiClient.get(`/dcry/findDcrys.json`, { params: searchReqDTO });
 }
 
 // 기록물 상세 조회
client/resources/js/cmmnPlugin.js
--- client/resources/js/cmmnPlugin.js
+++ client/resources/js/cmmnPlugin.js
@@ -22,5 +22,10 @@
       const dateOnly = dateString.split(' ')[0];
       return dateOnly.replace(/-/g, '.');
     }
+
+    // 정규식을 사용하여 모든 HTML 태그 제거
+    Vue.config.globalProperties.$stripHtml = (html) => {
+      return html.replace(/<[^>]*>/g, '');
+    }
   }
 }
(파일 끝에 줄바꿈 문자 없음)
client/views/component/CardViewList.vue (Renamed from client/views/component/List/CardViewList.vue)
--- client/views/component/List/CardViewList.vue
+++ client/views/component/CardViewList.vue
@@ -10,8 +10,8 @@
     <ul>
       <li v-for="(item, idx2) of list" :key="idx2" class="result-box mb-15">
         <div class="main-img">
-          <img v-if="item.hasOwnProperty('files') && item.files.length > 0" :src="item.files[0].filePath" alt="">
-          <img v-else src="client/resources/images/img6.png" alt="">
+          <img v-if="item.hasOwnProperty('files') && item.files.length > 0" :src="item.files[0].filePath" :alt="item.sj + ' 첫 번째 이미지'">
+          <img v-else src="client/resources/images/img6.png" alt="Not found image">
         </div>
         <div class="text-box">
           <h5>{{ item.sj }}</h5>
 
client/views/component/DefaultPagination.vue (added)
+++ client/views/component/DefaultPagination.vue
@@ -0,0 +1,44 @@
+<template>
+  <div class="pagination flex-center">
+    <button type="button" :disabled="search.currentPage === search.startPage" @click="$emit('onChange', search.startPage)">
+      <DoubleLeftOutlined />
+    </button>
+    <button type="button" :disabled="!search.existPrevPage" @click="$emit('onChange', (search.currentPage - 1))">
+      <LeftOutlined />
+    </button>
+    <template v-for="idx in search.totalPageCount" :key="idx">
+      <button type="button" :class="{ 'page-number': true, clicked: search.currentPage === idx }" :disabled="search.currentPage === idx" @click="$emit('onChange', idx)">{{ idx }}</button>
+    </template>
+    <button type="button" :disabled="!search.existNextPage" @click="$emit('onChange', (search.currentPage + 1))">
+      <RightOutlined />
+    </button>
+    <button type="button" :disabled="search.currentPage === search.endPage" @click="$emit('onChange', search.endPage)">
+      <DoubleRightOutlined />
+    </button>
+  </div>
+</template>
+<script>
+import { DoubleLeftOutlined, LeftOutlined, RightOutlined, DoubleRightOutlined } from '@ant-design/icons-vue';
+export default {
+  components: {
+    DoubleLeftOutlined,
+    LeftOutlined,
+    RightOutlined,
+    DoubleRightOutlined,
+  },
+
+  props: {
+    search: {
+      type: Object,
+      default: {},
+    }
+  },
+
+  methods: {},
+}
+</script>
+<style scoped>
+button:disabled {
+  cursor: not-allowed;
+}
+</style>(파일 끝에 줄바꿈 문자 없음)
client/views/component/editor/EditorComponent.vue (Renamed from client/views/component/EditorComponent.vue)
--- client/views/component/EditorComponent.vue
+++ client/views/component/editor/EditorComponent.vue
No changes
client/views/component/editor/ViewerComponent.vue (Renamed from client/views/component/ViewerComponent.vue)
--- client/views/component/ViewerComponent.vue
+++ client/views/component/editor/ViewerComponent.vue
No changes
 
client/views/component/listLayout/CardStyleComponent.vue (added)
+++ client/views/component/listLayout/CardStyleComponent.vue
@@ -0,0 +1,82 @@
+<template>
+  <ul class="card-wrap">
+    <li v-for="(item, idx) in list" :key="idx" class="mb-30" @click="fnMoveTo(item)">
+      <div class="result-box">
+        <div class="main-img">
+          <img v-if="item.hasOwnProperty('files') && item.files.length > 0" :src="item.files[0].filePath" :alt="item.sj + ' 첫 번째 이미지'">
+          <img v-else src="client/resources/images/img6.png" alt="Not found image">
+        </div>
+        <div class="text-box">
+          <router-link :to="{ path: '/PicHistoryDetail.page' }">
+            <h5>{{ item.sj }}</h5>
+          </router-link>
+          <p v-if="item.hasOwnProperty('adres')" class="address">{{ item.adres }}</p>
+          <p class="text">{{ $stripHtml(item.cn) }}</p>
+          <div class="mb-20">
+            <ul class="category">
+              <li v-for="(ctgry, ctgryIdx) of item.ctgrys" :key="ctgryIdx" class="category1">{{ ctgry.ctgryNm }}</li>
+            </ul>
+          </div>
+          <div class="date">
+            <ul>
+              <li>생산연도 <b>{{ item.prdctnYear ? item.prdctnYear : '-' }}</b></li>
+              <li>|</li>
+              <li>등록 <b>{{ item.rgsde }}</b></li>
+            </ul>
+          </div>
+        </div>
+      </div>
+    </li>
+  </ul>
+</template>
+<script>
+export default {
+  name: "CardStyleComponent",
+
+  props: {
+    name: {
+      type: String,
+      default: 'P',
+    },
+    list: {
+      type: Array,
+      default: () => [],
+    },
+  },
+
+  computed: {
+    page() {
+      switch (this.name) {
+        case 'P':
+          return 'PicHistoryDetail';
+        case 'V':
+          return 'VideoHistoryDetail';
+        case 'M':
+          return 'MediaVideoDetail';
+        case 'N':
+          return 'NewsReleaseDetail';
+      }
+    },
+  },
+
+  methods: {
+    // 페이지 이동
+    fnMoveTo(item) {
+      let key = null;
+      switch (this.name) {
+        case 'P':
+        case 'V':
+          key = 'dcryId';
+          break;
+        case 'M':
+          key = 'mediaVidoId';
+          break;
+        case 'N':
+          key = 'nesDtaId';
+          break;
+      }
+      this.$router.push({ name: this.page, query: { id: item[key] } });
+    }
+  },
+};
+</script>(파일 끝에 줄바꿈 문자 없음)
 
client/views/component/listLayout/ListStyleComponent.vue (added)
+++ client/views/component/listLayout/ListStyleComponent.vue
@@ -0,0 +1,75 @@
+<template>
+  <ul class="list-wrap">
+    <li v-for="(item, idx) in list" :key="idx" class="mb-30" @click="fnMoveTo(item)">
+      <div class="text-box">
+        <h5>{{ item.sj }}</h5>
+        <p v-if="item.hasOwnProperty('adres')" class="address">{{ item.adres }}</p>
+        <div class="flex-sp-bw">
+          <div class="mb-20">
+            <ul class="category">
+              <li v-for="(ctgry, idx3) of item.ctgrys" :key="idx3" class="category1">{{ ctgry.ctgryNm }}</li>
+            </ul>
+          </div>
+          <div class="date ">
+            <ul>
+              <li>생산연도 <b>{{ item.prdctnYear ? item.prdctnYear : '-' }}</b></li>
+              <li>|</li>
+              <li>등록 <b>{{ item.rgsde }}</b></li>
+            </ul>
+          </div>
+        </div>
+      </div>
+    </li>
+  </ul>
+</template>
+<script>
+export default {
+  name: "ListStyleComponent",
+
+  props: {
+    name: {
+      type: String,
+      default: 'P',
+    },
+    list: {
+      type: Array,
+      default: () => [],
+    },
+  },
+
+  computed: {
+    page() {
+      switch (this.name) {
+        case 'P':
+          return 'PicHistoryDetail';
+        case 'V':
+          return 'VideoHistoryDetail';
+        case 'M':
+          return 'MediaVideoDetail';
+        case 'N':
+          return 'NewsReleaseDetail';
+      }
+    },
+  },
+
+  methods: {
+    // 페이지 이동
+    fnMoveTo(item) {
+      let key = null;
+      switch (this.name) {
+        case 'P':
+        case 'V':
+          key = 'dcryId';
+          break;
+        case 'M':
+          key = 'mediaVidoId';
+          break;
+        case 'N':
+          key = 'nesDtaId';
+          break;
+      }
+      this.$router.push({ name: this.page, query: { id: item[key] } });
+    }
+  },
+};
+</script>(파일 끝에 줄바꿈 문자 없음)
client/views/component/modal/CategorySelectModal.vue
--- client/views/component/modal/CategorySelectModal.vue
+++ client/views/component/modal/CategorySelectModal.vue
@@ -1,9 +1,9 @@
 <template>
-  <div class="modal-overlay" @click="closeModal">
+  <div class="modal-overlay">
     <div class="modal-content" @click.stop>
       <div class="flex-sp-bw mb-20">
         <h2>카테고리 조회</h2>
-        <button @click="closeModal" class="closebtn">✕</button>
+        <button class="closebtn" @click="$emit('toggleModal')">✕</button>
       </div>
       <div class="modal-search flex-center mb-20">
         <input type="text" placeholder="카테고리명을 입력하세요." v-model="searchReqDTO.searchText" @keyup.enter="fnFindAllCategory">
@@ -32,27 +32,13 @@
         </tbody>
       </table>
       <div class="flex-end mb-30"><button class="register-b" @click="fnAddCtgries">등록</button></div>
-      <div class="pagination">
-        <!-- Previous and Next Page Buttons -->
-        <button>
-          <DoubleLeftOutlined />
-        </button>
-        <button @click="previousPage" :disabled="searchReqDTO.currentPage === 1">
-          <LeftOutlined />
-        </button>
-        <button class="page-number clicked">1</button>
-        <button @click="nextPage" :disabled="searchReqDTO.currentPage === searchReqDTO.recordSize">
-          <RightOutlined />
-        </button>
-        <button>
-          <DoubleRightOutlined />
-        </button>
-      </div>
+      <DefaultPagination :search="searchReqDTO" @onChange="fnChangeCurrentPage" />
     </div>
   </div>
 </template>
 <script>
-import { DoubleLeftOutlined, LeftOutlined, RightOutlined, DoubleRightOutlined } from '@ant-design/icons-vue';
+// COMPONENT
+import DefaultPagination from '@/views/component/DefaultPagination.vue';
 // API
 import { findAllCategoryProc } from '@/resources/api/category';
 
@@ -60,10 +46,7 @@
   name: 'CategorySelectModal',
 
   components: {
-    DoubleLeftOutlined,
-    LeftOutlined,
-    RightOutlined,
-    DoubleRightOutlined
+    DefaultPagination
   },
 
   props: {
@@ -80,20 +63,13 @@
 
       // 검색 객체
       searchReqDTO: {
-        currentPage: 1,
-        recordSize: 10,
-        pageSize: 10,
-        totalRecordCount: 5,
-        totalPageCount: 1,
-        startPage: 1,
-        endPage: 1,
-        limitStart: 0,
-        existPrevPage: false,
-        existNextPage: false,
         searchType: 'nm',
         searchText: null,
         useAt: 'Y',
         selectedCtgryIds: null,
+        // 페이지네이션
+        currentPage: 1, // 현재 페이지
+        recordSize: 10, // 한 페이지에 표시할 데이터 개수
       },
 
       list: [], // 카테고리 목록
@@ -112,11 +88,8 @@
         if (this.selectedCtgries.length > 0) {
           this.searchReqDTO.selectedCtgryIds = this.selectedCtgries.map(item => item.ctgryId).join(',');
         }
-        console.log('req: ', this.searchReqDTO);
 
         const response = await findAllCategoryProc(this.searchReqDTO);
-        console.log('res: ', response.data.data.ctgry);
-
         let ctgries = response.data.data.ctgry;
         for (let item of ctgries) {
           item.isSelected = false;
@@ -129,6 +102,7 @@
         }
 
         this.list = ctgries;
+        this.searchReqDTO = response.data.data.search;
       } catch (error) {
         alert('조회중 오류가 발생했습니다.');
 
@@ -139,25 +113,19 @@
       }
     },
 
-    closeModal() {
-      this.$emit('toggleModal');
-    },
-
+    // 카테고리 추가
     fnAddCtgries() {
       this.$emit('addCtgries', this.selectedList);
     },
 
-    previousPage() {
-      if (this.searchReqDTO.currentPage > 1) {
-        this.searchReqDTO.currentPage--;
-      }
-    },
+    // 페이지 이동
+    fnChangeCurrentPage(currentPage) {
+      this.searchReqDTO.currentPage = Number(currentPage);
 
-    nextPage() {
-      if (this.searchReqDTO.currentPage < this.searchReqDTO.recordSize) {
-        this.searchReqDTO.currentPage++;
-      }
-    }
+      this.$nextTick(() => {
+        this.fnFindAllCategory();
+      });
+    },
   }
 };
 </script>
(파일 끝에 줄바꿈 문자 없음)
client/views/pages/user/MediaVideoInsert.vue
--- client/views/pages/user/MediaVideoInsert.vue
+++ client/views/pages/user/MediaVideoInsert.vue
@@ -57,7 +57,7 @@
 <script>
 import { DoubleLeftOutlined, LeftOutlined, RightOutlined, DoubleRightOutlined } from '@ant-design/icons-vue';
 // COMPONENT
-import EditorComponent from '../../component/EditorComponent.vue';
+import EditorComponent from '../../component/editor/EditorComponent.vue';
 import CategorySelectModal from '../../component/modal/CategorySelectModal.vue';
 
 export default {
client/views/pages/user/NewsReleaseInsert.vue
--- client/views/pages/user/NewsReleaseInsert.vue
+++ client/views/pages/user/NewsReleaseInsert.vue
@@ -88,7 +88,7 @@
 <script>
 import { DoubleLeftOutlined, LeftOutlined, RightOutlined, DoubleRightOutlined } from '@ant-design/icons-vue';
 // COMPONENT
-import EditorComponent from '../../component/EditorComponent.vue';
+import EditorComponent from '../../component/editor/EditorComponent.vue';
 import CategorySelectModal from '../../component/modal/CategorySelectModal.vue';
 
 export default {
client/views/pages/user/PicHistoryDetail.vue
--- client/views/pages/user/PicHistoryDetail.vue
+++ client/views/pages/user/PicHistoryDetail.vue
@@ -96,7 +96,7 @@
 // import required modules
 import { FreeMode, Navigation, Thumbs } from 'swiper/modules';
 // COMPONENT
-import ViewerComponent from '../../component/ViewerComponent.vue';
+import ViewerComponent from '../../component/editor/ViewerComponent.vue';
 // API
 import { findDcryProc, deleteDcryProc } from '@/resources/api/dcry';
 import { fileDownloadProc, multiFileDownloadProc } from '@/resources/api/file';
@@ -109,6 +109,7 @@
     SwiperSlide,
     ViewerComponent,
   },
+
   setup() {
     const thumbsSwiper = ref(null);
 
@@ -122,6 +123,7 @@
       modules: [FreeMode, Navigation, Thumbs],
     };
   },
+
   data() {
     return {
       // ICON
@@ -138,19 +140,19 @@
       selectedFiles: [],
     };
   },
-  methods: {},
-  watch: {},
-  computed: {},
+
   created() {
     this.pageId = this.$route.query.id;
     if (this.pageId === null) {
       alert("게시물 존재하지 않습니다.");
-      return;
+      this.fnMoveTo('PicHistorySearch');
     }
+  },
 
+  mounted() {
     this.fnFindDcry(); // 상세 조회
   },
-  mounted() { },
+
   methods: {
     // 상세 조회
     async fnFindDcry() {
client/views/pages/user/PicHistoryInsert.vue
--- client/views/pages/user/PicHistoryInsert.vue
+++ client/views/pages/user/PicHistoryInsert.vue
@@ -99,7 +99,7 @@
 <script>
 import { DoubleLeftOutlined, LeftOutlined, RightOutlined, DoubleRightOutlined } from '@ant-design/icons-vue';
 // COMPONENT
-import EditorComponent from '../../component/EditorComponent.vue';
+import EditorComponent from '../../component/editor/EditorComponent.vue';
 import CategorySelectModal from '../../component/modal/CategorySelectModal.vue';
 // API
 import { findDcryProc, saveDcry, updateDcry } from '@/resources/api/dcry';
@@ -148,13 +148,16 @@
     };
   },
 
-  computed: {},
-
   created() {
     this.pageId = this.$route.query.id;
-    if (!this.$isEmpty(this.pageId)) {
-      this.fnFindDcry(); // 상세 조회
+    if (this.pageId === null) {
+      alert("게시물 존재하지 않습니다.");
+      this.fnMoveTo('PicHistorySearch');
     }
+  },
+
+  mounted() {
+    this.fnFindDcry(); // 상세 조회
   },
 
   methods: {
@@ -269,7 +272,7 @@
         alert("제목을 입력해 주세요.");
         return;
       }
-      if (this.$isEmpty(this.pageId) && this.multipartFiles.length < 1) {
+      if (this.$isEmpty(this.pageId) && this.multipartFiles.length == 0) {
         alert("파일을 1개 이상 첨부해 주세요.");
         return;
       }
client/views/pages/user/PicHistorySearch.vue
--- client/views/pages/user/PicHistorySearch.vue
+++ client/views/pages/user/PicHistorySearch.vue
@@ -1,412 +1,296 @@
 <template>
-    <div class="content">
-        <div class="sub-title-area mb-30">
-            <h2>사진 기록물</h2>
-            <div class="breadcrumb-list">
-                <ul>
-                    <!-- Bind the image source dynamically for homeicon -->
-                    <li><img :src="homeicon" alt="Home Icon">
-                        <p>기록물</p>
-                    </li>
-                    <li><img :src="righticon" alt=""></li>
-                    <li>사진 기록물</li>
-                </ul>
-            </div>
-        </div>
-        <div action="search" class="search-form form ">
-            <dl>
-                <dd class="mb-15">
-                    <p>검색범위</p>
-                    <ul>
-                        <li>
-                            <input type="checkbox" id="allScope" v-model="isChkAllScope"
-                                @change="fnChkAllOptions('scope')" />
-                            <label for="allScope">전체</label>
-                        </li>
-                        <li v-for="(scope, idx) in searchType" :key="idx">
-                            <input type="checkbox" :id="idx" :name="searchType" :value="scope.key"
-                                v-model="searchReqDTO.searchType" @change="fnChkOption('scope')" />
-                            <label :for="idx">{{ scope.value }}</label>
-                        </li>
-                    </ul>
-                </dd>
-                <dd class="mb-15">
-                    <p>검색어</p>
-                    <div class="wfull"><input type="text" v-model="searchReqDTO.searchText"></div>
-                </dd>
-                <dd class="mb-15">
-                    <p>생산연도</p>
-                    <input type="date" v-model="searchReqDTO.startYear">
-                    <p class="mark">~</p>
-                    <input type="date" v-model="searchReqDTO.endYear">
-                </dd>
-                <dd class="mb-20">
-                    <p>카테고리</p>
-                    <ul>
-                        <li v-for="(category, idx) of categorys" :key="idx">
-                            <input type="checkbox" :id="category.ctgryId" name="categorys" :value="category.ctgryId"
-                                v-model="searchReqDTO.searchCtgry" />
-                            <label :for="category.ctgryId">{{ category.ctgryNm }}</label>
-                        </li>
-                    </ul>
-                </dd>
-                <dd class="mb-15">
-                    <p>정렬</p>
-                    <ul>
-                        <li v-for="(order, idx) of orders" :key="idx">
-                            <input type="radio" :id="order.key" name="orders" :value="order.key"
-                                v-model="searchReqDTO.order" />
-                            <label :for="order.key">{{ order.value }}</label>
-                        </li>
-                    </ul>
-                </dd>
-                <div class="btn-group">
-                    <button class="reset"><img :src="reseticon" alt="">
-                        <p>초기화</p>
-                    </button>
-                    <button class="search"><img :src="searchicon" alt="">
-                        <p>검색</p>
-                    </button>
-                </div>
-
-            </dl>
-
-        </div>
-        <div class="search-result">
-            <div class="tabs">
-                <div class="flex-sp-bw mb-20 align-center">
-                    <div class="resultext ">
-                        <img :src="resulticon" alt="">
-                        <p>총 <b>{{ count }}개</b>의 사진 기록물이 검색되었습니다. </p>
-                    </div>
-                    <div class="flex ">
-                        <ul class="tab-box mb-20">
-                            <li v-for="(tab, index) in tabs" :key="index" class="tab-title"
-                                :class="{ active: selectedTab === tab.id }" @click="selectTab(tab.id)">
-                                <img :src="selectedTab === tab.id ? tab.activeImage : tab.inactiveImage"
-                                    :alt="tab.title" class="tab-icon" />
-                                <p><b>{{ tab.title }}</b></p>
-                            </li>
-                        </ul>
-                        <div class="select-box">
-                            <select v-model="itemsPerPage" @change="changeItemsPerPage">
-                                <option :value="5" selected>5개</option>
-                                <option :value="10">10개</option>
-                                <option :value="15">15개</option>
-                            </select>
-                        </div>
-                    </div>
-
-                </div>
-
-                <div class="tab-content">
-                    <!-- Loop through tabContents, and only display content that matches selectedTab -->
-                    <div v-for="(tabContent, idx) in tabContents" :key="idx">
-                        <!-- Display content only if the tab's ID matches the selectedTab -->
-                        <div v-show="tabContent.id === selectedTab">
-                            <!-- 카드형 Section (Card Layout) -->
-                            <div v-if="tabContent.viewType === 'card'">
-                                <ul class="card-wrap">
-                                    <li v-for="(resultitem, index) in paginatedItems" :key="index" class="mb-30">
-                                        <div class="result-box">
-                                            <!-- Main Image Section -->
-                                            <div class="main-img">
-                                                <img :src="resultitem.img" alt="" class="tab-image" />
-                                            </div>
-                                            <!-- Text Section -->
-                                            <div class="text-box">
-                                                <router-link :to="{ path: '/PicHistoryDetail.page' }">
-                                                    <h5>{{ resultitem.title }}</h5>
-                                                </router-link>
-
-                                                <p class="address">{{ resultitem.address }}</p>
-                                                <p class="text">{{ resultitem.content }}</p>
-
-                                                <div class="mb-20">
-                                                    <ul class="category">
-                                                        <li v-if="resultitem.category1" class="category1">카테고리1</li>
-                                                        <li v-if="resultitem.category2" class="category2">카테고리2</li>
-                                                    </ul>
-                                                </div>
-
-                                                <div class="date">
-                                                    <ul>
-                                                        <li>생산연도 <b>{{ resultitem.year }}</b></li>
-                                                        <li>|</li>
-                                                        <li>등록 <b>{{ resultitem.date }}</b></li>
-                                                    </ul>
-                                                </div>
-                                            </div>
-                                        </div>
-                                    </li>
-                                </ul>
-
-                                <!-- Empty State if no results in paginatedItems -->
-                                <div v-if="paginatedItems.length === 0" class="no-results">
-                                    <p>등록된 게시물이 없습니다.</p>
-                                </div>
-                            </div>
-
-                            <!-- 리스트형 Section (List Layout) -->
-                            <div v-if="tabContent.viewType === 'list'">
-                                <ul class="list-wrap">
-                                    <li v-for="(resultitem, index) in paginatedItems" :key="index" class="mb-30">
-                                        <div class="text-box">
-                                            <h5>{{ resultitem.title }}</h5>
-                                            <p class="address">{{ resultitem.address }}</p>
-
-                                            <div class="flex-sp-bw">
-                                                <div class="mb-20">
-                                                    <ul class="category">
-                                                        <li v-if="resultitem.category1" class="category1">카테고리1</li>
-                                                        <li v-if="resultitem.category2" class="category2">카테고리2</li>
-                                                    </ul>
-                                                </div>
-
-                                                <div class="date ">
-                                                    <ul>
-                                                        <li>생산연도 <b>{{ resultitem.year }}</b></li>
-                                                        <li>|</li>
-                                                        <li>등록 <b>{{ resultitem.date }}</b></li>
-                                                    </ul>
-                                                </div>
-                                            </div>
-                                        </div>
-                                    </li>
-                                </ul>
-
-                                <!-- Empty State if no results in paginatedItems -->
-                                <div v-if="paginatedItems.length === 0" class="no-results">
-                                    <p>등록된 게시물이 없습니다.</p>
-                                </div>
-                            </div>
-                        </div>
-                    </div>
-                </div>
-            </div>
-
-            <div class="btn-group flex-end mt-40"><button class="register"> <router-link
-                        :to="{ path: '/PicHistoryInsert.page' }">등록</router-link></button></div>
-            <div class="pagination flex-center mt-40">
-
-                <!-- Previous and Next Page Buttons -->
-                <button>
-                    <DoubleLeftOutlined />
-                </button>
-                <button @click="previousPage" :disabled="currentPage === 1">
-                    <LeftOutlined />
-                </button>
-                <button class="page-number clicked">1</button>
-                <button @click="nextPage" :disabled="currentPage === totalPages">
-                    <RightOutlined />
-                </button>
-                <button>
-                    <DoubleRightOutlined />
-                </button>
-            </div>
-        </div>
+  <div class="content">
+    <div class="sub-title-area mb-30">
+      <h2>사진 기록물</h2>
+      <div class="breadcrumb-list">
+        <ul>
+          <li>
+            <img :src="homeicon" alt="Home Icon">
+            <p>기록물</p>
+          </li>
+          <li><img :src="righticon" alt=""></li>
+          <li>사진 기록물</li>
+        </ul>
+      </div>
     </div>
-
+    <div class="search-form form">
+      <dl>
+        <dd class="mb-15">
+          <p>검색범위</p>
+          <ul>
+            <li>
+              <input type="checkbox" id="allScope" v-model="isChkAllScope" @change="fnChkAllOptions" />
+              <label for="allScope">전체</label>
+            </li>
+            <li>
+              <input type="checkbox" id="searchSj" v-model="searchReqDTO.useSj" @change="fnChkOption" />
+              <label for="searchSj">제목</label>
+            </li>
+            <li>
+              <input type="checkbox" id="searchCn" v-model="searchReqDTO.useCn" @change="fnChkOption" />
+              <label for="searchCn">내용</label>
+            </li>
+            <li>
+              <input type="checkbox" id="searchAdres" v-model="searchReqDTO.useAdres" @change="fnChkOption" />
+              <label for="searchAdres">주소</label>
+            </li>
+          </ul>
+        </dd>
+        <dd class="mb-15">
+          <p>검색어</p>
+          <div class="wfull"><input type="text" v-model="searchReqDTO.searchText" v-on:keyup.enter="fnSearch()"></div>
+        </dd>
+        <dd class="mb-15">
+          <p>생산연도</p>
+          <input type="date" v-model="searchReqDTO.startYear">
+          <p class="mark">~</p>
+          <input type="date" v-model="searchReqDTO.endYear">
+        </dd>
+        <dd class="mb-20">
+          <p>카테고리</p>
+          <ul>
+            <li v-for="(category, idx) of categorys" :key="idx">
+              <input type="checkbox" :id="category.ctgryId" name="categorys" :value="category.ctgryId" v-model="searchReqDTO.searchCtgries" />
+              <label :for="category.ctgryId">{{ category.ctgryNm }}</label>
+            </li>
+          </ul>
+        </dd>
+        <dd class="mb-15">
+          <p>정렬</p>
+          <ul>
+            <li v-for="(order, idx) of orders" :key="idx">
+              <input type="radio" :id="order.key" name="orders" :value="order.key" v-model="searchReqDTO.order" />
+              <label :for="order.key">{{ order.value }}</label>
+            </li>
+          </ul>
+        </dd>
+        <div class="btn-group">
+          <button type="button" class="reset" @click="init">
+            <img :src="reseticon" alt="">
+            <p>초기화</p>
+          </button>
+          <button type="button" class="search" @click="fnSearch">
+            <img :src="searchicon" alt="">
+            <p>검색</p>
+          </button>
+        </div>
+      </dl>
+    </div>
+    <div class="search-result">
+      <div class="tabs">
+        <div class="flex-sp-bw mb-20 align-center">
+          <div class="resultext ">
+            <img :src="resulticon" alt="">
+            <p>총 <b>{{ searchReqDTO.totalRecordCount }}개</b>의 사진 기록물이 검색되었습니다. </p>
+          </div>
+          <div class="flex">
+            <ul class="tab-box mb-20">
+              <li v-for="(tab, idx) in tabs" :key="idx" class="tab-title" :class="{ active: selectedTabId === tab.id }" @click="selectTab(tab.id)">
+                <img :src="selectedTabId === tab.id ? tab.activeImage : tab.inactiveImage" :alt="tab.title" class="tab-icon" />
+                <p><b>{{ tab.title }}</b></p>
+              </li>
+            </ul>
+            <div class="select-box">
+              <select v-model="searchReqDTO.recordSize" @change="fnSearch">
+                <option :value="24">24개</option>
+                <option :value="36">36개</option>
+                <option :value="100">100개</option>
+              </select>
+            </div>
+          </div>
+        </div>
+        <div class="tab-content">
+          <div v-if="searchResult.length > 0">
+            <CardStyleComponent v-if="selectedTabId === 'CARD'" :name="'P'" :list="searchResult" />
+            <ListStyleComponent v-if="selectedTabId === 'LIST'" :name="'P'" :list="searchResult" />
+          </div>
+          <div v-else class="no-results">
+            <p>등록된 게시물이 없습니다.</p>
+          </div>
+        </div>
+      </div>
+      <div class="btn-group flex-end mt-40"><button class="register"> <router-link :to="{ path: '/PicHistoryInsert.page' }">등록</router-link></button></div>
+      <DefaultPagination class="mt-40" :search="searchReqDTO" @onChange="fnChangeCurrentPage" />
+    </div>
+  </div>
 </template>
 <script>
-import { DoubleLeftOutlined, LeftOutlined, RightOutlined, DoubleRightOutlined } from '@ant-design/icons-vue';
-import { findAllCategoryProc } from "../../../resources/api/category"; // 카테고리 목록 검색
+// COMPONENT
+import CardStyleComponent from '@/views/component/listLayout/CardStyleComponent.vue';
+import ListStyleComponent from '@/views/component/listLayout/ListStyleComponent.vue';
+import DefaultPagination from '@/views/component/DefaultPagination.vue';
+// API
+import { findAllByNullProc } from "@/resources/api/category";
+import { findDcrysProc } from "@/resources/api/dcry";
 
 export default {
-    components: {
-        DoubleLeftOutlined,
-        LeftOutlined,
-        RightOutlined,
-        DoubleRightOutlined,
-    },
-    data() {
-        return {
-            selectedTab: 1,
-            // 검색용 객체
-            searchReqDTO: {
-                searchType: [],
-                searchText: null,
-                startYear: null,
-                endYear: null,
-                searchTy: null,
-                searchCtgry: [],
-                order: "rgsde",
-            },
-            tabs: [
+  components: {
+    DefaultPagination,
+    CardStyleComponent,
+    ListStyleComponent,
+  },
 
-                {
-                    id: 1,
-                    title: "카드형",
-                    activeImage: "client/resources/images/list_icon01_on.png", // Active tab image
-                    inactiveImage: "client/resources/images/list_icon01_off.png",
-                },
-                {
-                    id: 2,
-                    title: "리스트형",
-                    activeImage: "client/resources/images/list_icon02_on.png", // Active tab image
-                    inactiveImage: "client/resources/images/list_icon02_off.png",
-                },
-            ],
-            tabContents: [
-                { id: 1, viewType: 'card', list: [{ sj: 'Item 1', rgsde: '2025-03-01', files: [{ filePath: 'image1.png' }] }] },
-                { id: 2, viewType: 'list', list: [{ sj: 'Item 2', rgsde: '2025-03-02', files: [{ filePath: 'image2.png' }] }] },
-            ],
-            paginatedItems: [],
-            resultitems: [
-                {
-                    img: 'client/resources/images/img6.png',
-                    title: '사진 기록물 제목',
-                    address: '경상북도 구미시 송정대로 55',
-                    content: '대한민국 최대의 내륙 산업단지를 보유하고, 서울로부터 277km, 부산으로부터 167km 거리에 있으며, 면적은 615㎢로 경상북도 전체 면적의 3.2%에 달합니다. 인구는 41만 명이고, 선산읍, 고아읍, 산동읍을 비롯한 3읍, 5면, 17개 동으로 구성되어…',
-                    category1: true,
-                    category2: true,
-                    year: 2020,
-                    date: '2021-01-01'
-                },
+  data() {
+    return {
+      // ICON
+      resulticon: "client/resources/images/icon/r-check.png",
+      homeicon: 'client/resources/images/icon/home.png',
+      searchicon: 'client/resources/images/icon/search.png',
+      reseticon: 'client/resources/images/icon/reset.png',
+      righticon: 'client/resources/images/icon/right.png',
 
-            ],
-            currentPage: 1, // Current page number
-            itemsPerPage: 5,
-            resulticon: "client/resources/images/icon/r-check.png",
-            homeicon: 'client/resources/images/icon/home.png',
-            searchicon: 'client/resources/images/icon/search.png',
-            reseticon: 'client/resources/images/icon/reset.png',
-            righticon: 'client/resources/images/icon/right.png',
-            count: 23,
-            checkOptions: [
-                '전체',
-                '사진',
-                '영상',
-                '미디어 영상',
-                '보도자료',
-            ],
-            checkOptions2: [
-                '전체',
-                '제목',
-                '내용',
-                '주소',
-            ],
-            checkOptions3: [
-                '카테고리1',
-                '카테고리2',
-                '카테고리3',
-                '카테고리4',
-                '카테고리5',
-            ],
-            checkOptions4: [
-                '최신',
-                '인기',
-            ],
-            isChkAllScope: false, // 검색범위 전체 체크 여부
-            searchType: [
-                { key: "sj", value: "제목" },
-                { key: "cn", value: "내용" },
-                { key: "adres", value: "주소" },
-            ], // 검색범위 목록
-            categorys: [], // 카테고리 목록
-            orders: [
-                { key: "rgsde", value: "최신" },
-                { key: "rdcnt", value: "인기" },
-            ], // 정렬 목록
-        };
-    },
-    computed: {
-        // Total number of pages
-        totalPages() {
-            return Math.ceil(this.resultitems.length / this.itemsPerPage);
-        },
+      // 검색용 객체
+      isChkAllScope: true, // 검색범위 전체 체크 여부
+      searchType: [
+        { key: "sj", value: "제목" },
+        { key: "cn", value: "내용" },
+        { key: "adres", value: "주소" },
+      ], // 검색범위 목록
+      categorys: [], // 카테고리 목록
+      orders: [
+        { key: "rgsde", value: "최신" },
+        { key: "rdcnt", value: "인기" },
+      ], // 정렬 목록
 
-        // Paginated items based on current page and items per page
-        paginatedItems() {
-            const start = (this.currentPage - 1) * this.itemsPerPage;
-            const end = start + this.itemsPerPage;
-            return this.resultitems.slice(start, end);
-        },
-    },
-    created() {
-        // 초기 데이터 세팅
-        this.isChkAllScope = true;
-        this.searchReqDTO.searchType = this.searchType.map(item => item.key);
-        this.searchReqDTO.order = this.orders[0].key
+      // 검색용 객체 초기값
+      searchDefault: {
+        useSj: true,
+        useCn: true,
+        useAdres: true,
+        searchText: null,
+        startYear: null,
+        endYear: null,
+        searchTy: "P",
+        searchCtgries: [],
+        order: "rgsde",
+        // 페이지네이션
+        currentPage: 1, // 현재 페이지
+        recordSize: 24, // 한 페이지에 표시할 데이터 개수
+      },
+      searchReqDTO: {}, // 실제 검색에 사용되는 객체
 
-        this.fnFindCategorys(); // 카테고리 목록 조회 (검색조건 없음)
-    },
-    methods: {
-        selectTab(tabId) {
-            this.selectedTab = tabId; // Update the selected tab index
-        },
-        // Change the number of items displayed per page
-        changeItemsPerPage() {
-            this.currentPage = 1; // Reset to first page when changing items per page
-        },
-        previousPage() {
-            if (this.currentPage > 1) {
-                this.currentPage--;
-            }
-        },
+      searchResult: [], // 검색결과
 
-        // Go to the next page
-        nextPage() {
-            if (this.currentPage < this.totalPages) {
-                this.currentPage++;
-            }
+      // 목록 레이아웃
+      selectedTabId: null,
+      tabs: [
+        {
+          id: "CARD",
+          title: "카드형",
+          activeImage: "client/resources/images/list_icon01_on.png", // Active tab image
+          inactiveImage: "client/resources/images/list_icon01_off.png",
         },
-        isChkAllScope: false, // 검색범위 전체 체크 여부
-        searchType: [
-            { key: "sj", value: "제목" },
-            { key: "cn", value: "내용" },
-            { key: "adres", value: "주소" },
-        ], // 검색범위 목록
-        categorys: [], // 카테고리 목록
-        orders: [
-            { key: "rgsde", value: "최신" },
-            { key: "rdcnt", value: "인기" },
-        ], // 정렬 목록
-
-        async fnFindCategorys() {
-            try {
-                const response = await findAllCategoryProc();
-                this.categorys = response.data.data.ctgry;
-            } catch (error) {
-                if (error.response) {
-                    console.log("에러 응답:", error.response.data);
-                }
-                console.error("Error:", error);
-            }
+        {
+          id: "LIST",
+          title: "리스트형",
+          activeImage: "client/resources/images/list_icon02_on.png", // Active tab image
+          inactiveImage: "client/resources/images/list_icon02_off.png",
         },
+      ],
 
-        // 통합검색
-        async fnFindAllDatas() {
-            try {
-                let params = {};
-                if (this.searchReqDTO.searchRecord.length > 0) {
-                    params.searchRecords = this.searchReqDTO.searchRecord.join(',');
-                }
-                if (this.searchReqDTO.searchType.length > 0) {
-                    params.searchTypes = this.searchReqDTO.searchType.join(',');
-                }
-                if (this.searchReqDTO.searchCtgry.length > 0) {
-                    params.searchCtgries = this.searchReqDTO.searchCtgry.join(',');
-                }
-                params.searchText = this.searchReqDTO.searchText;
-                params.startYear = this.searchReqDTO.startYear;
-                params.endYear = this.searchReqDTO.endYear;
-                params.order = this.searchReqDTO.order;
+      isInitialLoad: true // 초기 로드 여부
+    };
+  },
 
-                // API 호출
-                const response = await findAllDatas(params);
-                this.searchResult = response.data.data.searchResult;
-            } catch (error) {
-                if (error.response) {
-                    console.log("에러 응답:", error.response.data);
-                }
-                console.error("Error:", error);
-            }
-        },
+  created() {
+    this.init(); // 초기화
+    this.fnFindCategorys(); // 카테고리 목록 조회 (검색조건 없음)
+  },
+
+  mounted() {
+    let searchText = this.$route.query.searchText;
+    if (searchText !== null) {
+      this.searchReqDTO.searchText = searchText;
+    }
+
+    this.fnSearch(); // 통합검색
+  },
+
+  methods: {
+    // 초기화
+    init() {
+      if (this.isInitialLoad) {
+        this.isInitialLoad = false;
+      } else {
+        if (!confirm('검색 조건을 초기화하시겠습니까?')) {
+          return;
+        }
+      }
+
+      this.searchReqDTO = JSON.parse(JSON.stringify(this.searchDefault));
+      this.searchResult = []; // 검색결과 초기화
+
+      this.selectedTabId = this.tabs[0].id;
     },
 
+    // 카테고리 목록 조회
+    async fnFindCategorys() {
+      try {
+        const response = await findAllByNullProc();
+        this.categorys = response.data.data.ctgry;
+      } catch (error) {
+        this.categorys = []; // 카테고리 목록 초기화
 
+        if (error.response) {
+          alert(error.response.data.message);
+        }
+        console.error(error.message);
+      }
+    },
 
+    // 페이지 이동
+    fnChangeCurrentPage(currentPage) {
+      this.searchReqDTO.currentPage = Number(currentPage);
+      this.$nextTick(() => {
+        this.fnFindCategorys();
+      });
+    },
+
+    // 통합검색
+    async fnSearch() {
+      try {
+        // 깊은 복사로 파라미터 생성
+        const params = JSON.parse(JSON.stringify(this.searchReqDTO));
+
+        // 디버깅용 로그 - 실제 전송되는 값 확인
+        console.log("검색 파라미터:", params);
+
+        // 카테고리 목록 처리
+        if (this.searchReqDTO.searchCtgries && this.searchReqDTO.searchCtgries.length > 0) {
+          params.searchCtgries = this.searchReqDTO.searchCtgries.join(',');
+        } else {
+          delete params.searchCtgries;
+        }
+
+        // API 호출
+        const response = await findDcrysProc(params);
+        this.searchResult = response.data.data.dcrys;
+        this.searchReqDTO = response.data.data.search;
+      } catch (error) {
+        this.searchResult = []; // 검색결과 초기화
+
+        if (error.response) {
+          alert(error.response.data.message);
+        }
+        console.error(error.message);
+      }
+    },
+
+    // 기록유형 전체 선택 여부 변경
+    fnChkAllOptions() {
+      this.searchReqDTO.useSj = this.isChkAllScope;
+      this.searchReqDTO.useCn = this.isChkAllScope;
+      this.searchReqDTO.useAdres = this.isChkAllScope;
+    },
+
+    // 기록유형 선택 여부 변경
+    fnChkOption() {
+      this.isChkAllScope = this.searchReqDTO.useSj && this.searchReqDTO.useCn && this.searchReqDTO.useAdres;
+    },
+
+    selectTab(tabId) {
+      this.selectedTabId = tabId;
+    },
+  },
 };
-</script>
-<style scoped></style>
(파일 끝에 줄바꿈 문자 없음)
+</script>
(파일 끝에 줄바꿈 문자 없음)
client/views/pages/user/TotalSearch.vue
--- client/views/pages/user/TotalSearch.vue
+++ client/views/pages/user/TotalSearch.vue
@@ -107,10 +107,11 @@
   </div>
 </template>
 <script>
-// 통합 검색
-import { findAllDatas } from "../../../resources/api/main";
+// COMPONENT
+import CardViewList from "../../component/CardViewList.vue";
+// API
+import { findAllDatas } from "../../../resources/api/main"; // 통합 검색
 import { findAllByNullProc } from "../../../resources/api/category"; // 카테고리 목록 검색
-import CardViewList from "../../component/List/CardViewList.vue";
 
 export default {
   components: {
@@ -140,7 +141,6 @@
         searchCtgries: [],
         order: "rgsde",
       },
-
       searchReqDTO: {},
 
       isChkAllRecord: true, // 기록유형 전체 체크 여부
client/views/pages/user/VideoHistoryInsert.vue
--- client/views/pages/user/VideoHistoryInsert.vue
+++ client/views/pages/user/VideoHistoryInsert.vue
@@ -88,7 +88,7 @@
 <script>
 import { DoubleLeftOutlined, LeftOutlined, RightOutlined, DoubleRightOutlined } from '@ant-design/icons-vue';
 // COMPONENT
-import EditorComponent from '../../component/EditorComponent.vue';
+import EditorComponent from '../../component/editor/EditorComponent.vue';
 import CategorySelectModal from '../../component/modal/CategorySelectModal.vue';
 
 export default {
Add a comment
List