
--- client/resources/api/category.js
+++ client/resources/api/category.js
... | ... | @@ -1,6 +1,6 @@ |
1 | 1 |
import apiClient from "./index"; |
2 | 2 |
|
3 |
-// 카테고리 목록 조회 |
|
4 |
-export const findAllCategoryProc = mber => { |
|
5 |
- return apiClient.post(`/category/findAllCategory.json`, mber); |
|
3 |
+// 카테고리 목록 조회 (검색조건 없음) |
|
4 |
+export const findAllCategoryProc = () => { |
|
5 |
+ return apiClient.get(`/category/findAllByNull.json`); |
|
6 | 6 |
}(파일 끝에 줄바꿈 문자 없음) |
+++ client/resources/api/dcry.js
... | ... | @@ -0,0 +1,14 @@ |
1 | +import apiClient from "./index"; | |
2 | + | |
3 | +// 기록물 목록 조회 | |
4 | +export const findAllDatas = (searchReqDTO) => { | |
5 | + return apiClient.get(`/main/findAllDatas.json`, { params: searchReqDTO }); | |
6 | +} | |
7 | + | |
8 | +// 기록물 상세 조회 | |
9 | + | |
10 | +// 기록물 등록 | |
11 | + | |
12 | +// 기록물 수정 | |
13 | + | |
14 | +// 기록물 삭제(파일 끝에 줄바꿈 문자 없음) |
--- client/resources/api/main.js
+++ client/resources/api/main.js
... | ... | @@ -7,5 +7,5 @@ |
7 | 7 |
|
8 | 8 |
// 통합 검색 |
9 | 9 |
export const findAllDatas = (searchReqDTO) => { |
10 |
- return apiClient.get(`/main/findAllDatas.json`, { params: { searchReqDTO: JSON.stringify(searchReqDTO) } }); |
|
10 |
+ return apiClient.get(`/main/findAllDatas.json`, { params: searchReqDTO }); |
|
11 | 11 |
}(파일 끝에 줄바꿈 문자 없음) |
--- client/views/pages/main/Main.vue
+++ client/views/pages/main/Main.vue
... | ... | @@ -31,11 +31,11 @@ |
31 | 31 |
<option value="bodo">보도자료</option> |
32 | 32 |
</select> |
33 | 33 |
<div class="line"></div> |
34 |
- <input type="text" placeholder="검색어를 입력하세요"> |
|
34 |
+ <input type="text" placeholder="검색어를 입력하세요" v-model="searchText"> |
|
35 | 35 |
<button class="search-btn"><img :src="search" alt=""></button> |
36 | 36 |
</div> |
37 | 37 |
<div class="total-search"> |
38 |
- <button class="total-btn" @click="fnMoveTo('TotalSearch')"><a href="">상세검색</a></button> |
|
38 |
+ <button class="total-btn" @click="fnMoveTo('TotalSearch')">상세검색</button> |
|
39 | 39 |
</div> |
40 | 40 |
</div> |
41 | 41 |
</div> |
... | ... | @@ -170,6 +170,8 @@ |
170 | 170 |
noimg: "client/resources/images/no_img.png", |
171 | 171 |
nomedia: "client/resources/images/no_media.png", |
172 | 172 |
nobodo: "client/resources/images/no_bodo.png", |
173 |
+ direct: 'client/resources/images/direct-btn.png', |
|
174 |
+ search: 'client/resources/images/icon/search.png', |
|
173 | 175 |
selectedTab: "newPhoto", // Set initial tab index to 신규사진기록물 (first tab) |
174 | 176 |
tabs: [ |
175 | 177 |
{ |
... | ... | @@ -188,8 +190,7 @@ |
188 | 190 |
tabContents: [], // 신규 사진, 영상 기록물 |
189 | 191 |
mediaContent: [], // 신규 미디어 영상 |
190 | 192 |
bodoContent: [], // 신규 보도자료 |
191 |
- direct: 'client/resources/images/direct-btn.png', |
|
192 |
- search: 'client/resources/images/icon/search.png', |
|
193 |
+ searchText: null, // 통합검색 Input 내용 |
|
193 | 194 |
icons: [ |
194 | 195 |
{ |
195 | 196 |
id: "TOTAL", |
... | ... | @@ -320,9 +321,14 @@ |
320 | 321 |
|
321 | 322 |
// 페이지 이동 |
322 | 323 |
fnMoveTo(page) { |
324 |
+ let params = null; |
|
325 |
+ if (page === 'TotalSearch') { |
|
326 |
+ params = { searchText: this.searchText }; |
|
327 |
+ } |
|
328 |
+ |
|
323 | 329 |
this.$router.push({ |
324 | 330 |
name: page, |
325 |
- query: {}, |
|
331 |
+ query: params, |
|
326 | 332 |
}); |
327 | 333 |
} |
328 | 334 |
}, |
--- client/views/pages/user/PicHistoryInsert.vue
+++ client/views/pages/user/PicHistoryInsert.vue
... | ... | @@ -1,158 +1,149 @@ |
1 | 1 |
<template> |
2 |
- <div class="content"> |
|
3 |
- <div class="sub-title-area mb-30"> |
|
4 |
- <h2>사진 기록물</h2> |
|
5 |
- <div class="breadcrumb-list"> |
|
6 |
- <ul> |
|
7 |
- <li><img :src="homeicon" alt="Home Icon"> |
|
8 |
- <p>기록물</p> |
|
9 |
- </li> |
|
10 |
- <li><img :src="righticon" alt=""></li> |
|
11 |
- <li>사진 기록물</li> |
|
12 |
- </ul> |
|
13 |
- </div> |
|
14 |
- </div> |
|
15 |
- <form action="" class="insert-form mb-50"> |
|
16 |
- <dl> |
|
17 |
- <dd> |
|
18 |
- <label for="id" class="require">제목</label> |
|
19 |
- <div class="wfull"><input type="text" id="id" value="" placeholder="제목을 입력하세요."></div> |
|
20 |
- </dd> |
|
21 |
- <div class="hr"></div> |
|
22 |
- <dd> |
|
23 |
- <label for="year">생산연도</label> |
|
24 |
- <input type="text" id="year" value="" placeholder="생산연도를 입력하세요"> |
|
25 |
- |
|
26 |
- </dd> |
|
27 |
- <div class="hr"></div> |
|
28 |
- <dd> |
|
29 |
- <label for="address">주소</label> |
|
30 |
- <div class="wfull"><input type="text" id="address" value="" placeholder="주소를 입력하세요"></div> |
|
31 |
- |
|
32 |
- </dd> |
|
33 |
- <div class="hr"></div> |
|
34 |
- <dd> |
|
35 |
- <label for="text">내용</label> |
|
36 |
- <textarea name="" id=""></textarea> |
|
37 |
- |
|
38 |
- </dd> |
|
39 |
- <div class="hr"></div> |
|
40 |
- <dd> |
|
41 |
- <label for="category" class="flex align-center"> |
|
42 |
- <p>카테고리</p><button type="button" class="category-add" @click="openModal">추가하기</button> |
|
43 |
- </label> |
|
44 |
- <ul class="category"> |
|
45 |
- <li class="category1">카테고리1 <button class="cancel"><b>✕</b></button></li> |
|
46 |
- <li class="category2">카테고리2 <button class="cancel"><b>✕</b></button></li> |
|
47 |
- </ul> |
|
48 |
- |
|
49 |
- </dd> |
|
50 |
- <div class="hr"></div> |
|
51 |
- <dd> |
|
52 |
- <label for="file" class="require">파일</label> |
|
53 |
- <ul class="wfull"> |
|
54 |
- <li class="flex align-center"> |
|
55 |
- <p>파일첨부</p> |
|
56 |
- <div class="invalid-feedback"><img :src="erroricon" alt=""><span>첨부파일은 10건까지 등록 가능하며, 건당 최대 |
|
57 |
- 100MB를 초과할 수 없습니다.</span></div> |
|
58 |
- </li> |
|
59 |
- <li class="file-insert"> |
|
60 |
- <input type="file" id="fileInput" class="file-input" multiple @change="showFileNames"> |
|
61 |
- <label for="fileInput" class="file-label mb-20"> |
|
62 |
- <div class="flex-center align-center"><img :src="fileicon" alt=""> |
|
63 |
- <p>파일첨부하기</p> |
|
64 |
- </div> |
|
65 |
- <p>파일을 첨부하시려면 이 영역으로 파일을 끌고 오거나 클릭해주세요</p> |
|
66 |
- </label> |
|
67 |
- <p class="mb-10">파일목록</p> |
|
68 |
- <div id="fileNames" class="file-names"> |
|
69 |
- <span v-if="fileNames.length === 0">선택된 파일이 없습니다.</span> |
|
70 |
- <div v-for="(file, index) in fileNames" :key="index" class="flex-sp-bw mb-5 file-wrap"> |
|
71 |
- <div class="file-name"> |
|
72 |
- <!-- Corrected here: Use file.icon instead of fileicons.img --> |
|
73 |
- <img :src="file.icon" alt="fileicon"> |
|
74 |
- <p>{{ file.name }}</p> |
|
75 |
- </div> |
|
76 |
- <button type="button" class="cancel" @click="removeFile(index)"><b>✕</b></button> |
|
77 |
- </div> |
|
78 |
- </div> |
|
79 |
- </li> |
|
80 |
- |
|
81 |
- </ul> |
|
82 |
- |
|
83 |
- </dd> |
|
84 |
- </dl> |
|
85 |
- </form> |
|
86 |
- |
|
87 |
- <div class="btn-group flex-center"> |
|
88 |
- <button class="cancel">취소</button> |
|
89 |
- <button class="register">등록</button> |
|
90 |
- </div> |
|
2 |
+ <div class="content"> |
|
3 |
+ <div class="sub-title-area mb-30"> |
|
4 |
+ <h2>사진 기록물</h2> |
|
5 |
+ <div class="breadcrumb-list"> |
|
6 |
+ <ul> |
|
7 |
+ <li><img :src="homeicon" alt="Home Icon"> |
|
8 |
+ <p>기록물</p> |
|
9 |
+ </li> |
|
10 |
+ <li><img :src="righticon" alt=""></li> |
|
11 |
+ <li>사진 기록물</li> |
|
12 |
+ </ul> |
|
13 |
+ </div> |
|
91 | 14 |
</div> |
92 |
- <div v-if="isModalOpen" class="modal-overlay" @click="closeModal"> |
|
93 |
- <div class="modal-content" @click.stop> |
|
94 |
- <h2>모달 제목</h2> |
|
95 |
- <p>모달의 내용이 여기에 들어갑니다.</p> |
|
96 |
- <button @click="closeModal">닫기</button> |
|
97 |
- </div> |
|
15 |
+ <form action="" class="insert-form mb-50"> |
|
16 |
+ <dl> |
|
17 |
+ <dd> |
|
18 |
+ <label for="id" class="require">제목</label> |
|
19 |
+ <div class="wfull"><input type="text" id="id" value="" placeholder="제목을 입력하세요."></div> |
|
20 |
+ </dd> |
|
21 |
+ <div class="hr"></div> |
|
22 |
+ <dd> |
|
23 |
+ <label for="year">생산연도</label> |
|
24 |
+ <input type="text" id="year" value="" placeholder="생산연도를 입력하세요"> |
|
25 |
+ </dd> |
|
26 |
+ <div class="hr"></div> |
|
27 |
+ <dd> |
|
28 |
+ <label for="address">주소</label> |
|
29 |
+ <div class="wfull"><input type="text" id="address" value="" placeholder="주소를 입력하세요"></div> |
|
30 |
+ </dd> |
|
31 |
+ <div class="hr"></div> |
|
32 |
+ <dd> |
|
33 |
+ <label for="text">내용</label> |
|
34 |
+ <textarea name="" id=""></textarea> |
|
35 |
+ </dd> |
|
36 |
+ <div class="hr"></div> |
|
37 |
+ <dd> |
|
38 |
+ <label for="category" class="flex align-center"> |
|
39 |
+ <p>카테고리</p><button type="button" class="category-add" @click="openModal">추가하기</button> |
|
40 |
+ </label> |
|
41 |
+ <ul class="category"> |
|
42 |
+ <li class="category1">카테고리1 <button class="cancel"><b>✕</b></button></li> |
|
43 |
+ <li class="category2">카테고리2 <button class="cancel"><b>✕</b></button></li> |
|
44 |
+ </ul> |
|
45 |
+ </dd> |
|
46 |
+ <div class="hr"></div> |
|
47 |
+ <dd> |
|
48 |
+ <label for="file" class="require">파일</label> |
|
49 |
+ <ul class="wfull"> |
|
50 |
+ <li class="flex align-center"> |
|
51 |
+ <p>파일첨부</p> |
|
52 |
+ <div class="invalid-feedback"><img :src="erroricon" alt=""><span>첨부파일은 10건까지 등록 가능하며, 건당 최대 100MB를 초과할 수 없습니다.</span></div> |
|
53 |
+ </li> |
|
54 |
+ <li class="file-insert"> |
|
55 |
+ <input type="file" id="fileInput" class="file-input" multiple @change="showFileNames"> |
|
56 |
+ <label for="fileInput" class="file-label mb-20"> |
|
57 |
+ <div class="flex-center align-center"><img :src="fileicon" alt=""> |
|
58 |
+ <p>파일첨부하기</p> |
|
59 |
+ </div> |
|
60 |
+ <p>파일을 첨부하시려면 이 영역으로 파일을 끌고 오거나 클릭해주세요</p> |
|
61 |
+ </label> |
|
62 |
+ <p class="mb-10">파일목록</p> |
|
63 |
+ <div id="fileNames" class="file-names"> |
|
64 |
+ <span v-if="fileNames.length === 0">선택된 파일이 없습니다.</span> |
|
65 |
+ <div v-for="(file, index) in fileNames" :key="index" class="flex-sp-bw mb-5 file-wrap"> |
|
66 |
+ <div class="file-name"> |
|
67 |
+ <!-- Corrected here: Use file.icon instead of fileicons.img --> |
|
68 |
+ <img :src="file.icon" alt="fileicon"> |
|
69 |
+ <p>{{ file.name }}</p> |
|
70 |
+ </div> |
|
71 |
+ <button type="button" class="cancel" @click="removeFile(index)"><b>✕</b></button> |
|
72 |
+ </div> |
|
73 |
+ </div> |
|
74 |
+ </li> |
|
75 |
+ </ul> |
|
76 |
+ </dd> |
|
77 |
+ </dl> |
|
78 |
+ </form> |
|
79 |
+ <div class="btn-group flex-center"> |
|
80 |
+ <button class="cancel">취소</button> |
|
81 |
+ <button class="register">등록</button> |
|
98 | 82 |
</div> |
83 |
+ </div> |
|
84 |
+ <div v-if="isModalOpen" class="modal-overlay" @click="closeModal"> |
|
85 |
+ <div class="modal-content" @click.stop> |
|
86 |
+ <h2>모달 제목</h2> |
|
87 |
+ <p>모달의 내용이 여기에 들어갑니다.</p> |
|
88 |
+ <button @click="closeModal">닫기</button> |
|
89 |
+ </div> |
|
90 |
+ </div> |
|
99 | 91 |
</template> |
100 |
- |
|
101 | 92 |
<script> |
102 | 93 |
export default { |
103 |
- data() { |
|
104 |
- return { |
|
105 |
- isModalOpen: false, |
|
106 |
- // Define the image sources |
|
107 |
- homeicon: 'client/resources/images/icon/home.png', |
|
108 |
- erroricon: 'client/resources/images/icon/error.png', |
|
109 |
- righticon: 'client/resources/images/icon/right.png', |
|
110 |
- fileicon: 'client/resources/images/icon/file.png', |
|
111 |
- fileNames: [], |
|
112 |
- }; |
|
113 |
- }, |
|
114 |
- methods: { |
|
115 |
- showFileNames(event) { |
|
116 |
- const files = event.target.files; |
|
117 |
- this.fileNames = []; // Clear previous file names |
|
94 |
+ data() { |
|
95 |
+ return { |
|
96 |
+ isModalOpen: false, |
|
97 |
+ // Define the image sources |
|
98 |
+ homeicon: 'client/resources/images/icon/home.png', |
|
99 |
+ erroricon: 'client/resources/images/icon/error.png', |
|
100 |
+ righticon: 'client/resources/images/icon/right.png', |
|
101 |
+ fileicon: 'client/resources/images/icon/file.png', |
|
102 |
+ fileNames: [], |
|
103 |
+ }; |
|
104 |
+ }, |
|
105 |
+ methods: { |
|
106 |
+ showFileNames(event) { |
|
107 |
+ const files = event.target.files; |
|
108 |
+ this.fileNames = []; // Clear previous file names |
|
118 | 109 |
|
119 |
- for (let i = 0; i < files.length; i++) { |
|
120 |
- const file = files[i]; |
|
121 |
- const fileType = file.name.split('.').pop().toLowerCase(); // Get file extension |
|
110 |
+ for (let i = 0; i < files.length; i++) { |
|
111 |
+ const file = files[i]; |
|
112 |
+ const fileType = file.name.split('.').pop().toLowerCase(); // Get file extension |
|
122 | 113 |
|
123 |
- // Set default icon |
|
124 |
- let iconPath = this.fileicons; |
|
114 |
+ // Set default icon |
|
115 |
+ let iconPath = this.fileicons; |
|
125 | 116 |
|
126 |
- // Determine the icon based on file type |
|
127 |
- if (['jpg', 'jpeg', 'png', 'gif'].includes(fileType)) { |
|
128 |
- iconPath = 'client/resources/images/icon/imgicon.png'; // Example for image files |
|
129 |
- } else if (['pdf'].includes(fileType)) { |
|
130 |
- iconPath = 'client/resources/images/icon/pdficon.png'; // Example for PDF files |
|
131 |
- } else if (['xls'].includes(fileType)) { |
|
132 |
- iconPath = 'client/resources/images/icon/excelicon.png'; // Example for audio files |
|
133 |
- } else if (['hwp'].includes(fileType)) { |
|
134 |
- iconPath = 'client/resources/images/icon/hwpicon.png'; // Example for video files |
|
135 |
- } |
|
136 |
- |
|
137 |
- // Push the file name and corresponding icon to the fileNames array |
|
138 |
- this.fileNames.push({ |
|
139 |
- name: file.name, |
|
140 |
- icon: iconPath |
|
141 |
- }); |
|
142 |
- } |
|
143 |
- }, |
|
144 |
- removeFile(index) { |
|
145 |
- // Remove file from the list |
|
146 |
- this.fileNames.splice(index, 1); |
|
147 |
- console.log(removeFile) |
|
148 |
- }, |
|
149 |
- openModal() { |
|
150 |
- this.isModalOpen = true; |
|
151 |
- }, |
|
152 |
- // 모달 닫기 |
|
153 |
- closeModal() { |
|
154 |
- this.isModalOpen = false; |
|
117 |
+ // Determine the icon based on file type |
|
118 |
+ if (['jpg', 'jpeg', 'png', 'gif'].includes(fileType)) { |
|
119 |
+ iconPath = 'client/resources/images/icon/imgicon.png'; // Example for image files |
|
120 |
+ } else if (['pdf'].includes(fileType)) { |
|
121 |
+ iconPath = 'client/resources/images/icon/pdficon.png'; // Example for PDF files |
|
122 |
+ } else if (['xls'].includes(fileType)) { |
|
123 |
+ iconPath = 'client/resources/images/icon/excelicon.png'; // Example for audio files |
|
124 |
+ } else if (['hwp'].includes(fileType)) { |
|
125 |
+ iconPath = 'client/resources/images/icon/hwpicon.png'; // Example for video files |
|
155 | 126 |
} |
127 |
+ |
|
128 |
+ // Push the file name and corresponding icon to the fileNames array |
|
129 |
+ this.fileNames.push({ |
|
130 |
+ name: file.name, |
|
131 |
+ icon: iconPath |
|
132 |
+ }); |
|
133 |
+ } |
|
134 |
+ }, |
|
135 |
+ removeFile(index) { |
|
136 |
+ // Remove file from the list |
|
137 |
+ this.fileNames.splice(index, 1); |
|
138 |
+ console.log(removeFile) |
|
139 |
+ }, |
|
140 |
+ openModal() { |
|
141 |
+ this.isModalOpen = true; |
|
142 |
+ }, |
|
143 |
+ // 모달 닫기 |
|
144 |
+ closeModal() { |
|
145 |
+ this.isModalOpen = false; |
|
156 | 146 |
} |
147 |
+ } |
|
157 | 148 |
}; |
158 | 149 |
</script> |
--- client/views/pages/user/PicHistorySearch.vue
+++ client/views/pages/user/PicHistorySearch.vue
... | ... | @@ -1,233 +1,256 @@ |
1 | 1 |
<template> |
2 |
- <div class="content"> |
|
3 |
- <div class="sub-title-area mb-30"> |
|
4 |
- <h2>사진 기록물</h2> |
|
5 |
- <div class="breadcrumb-list"> |
|
6 |
- <ul> |
|
7 |
- <!-- Bind the image source dynamically for homeicon --> |
|
8 |
- <li><img :src="homeicon" alt="Home Icon"><p>기록물</p></li> |
|
9 |
- <li><img :src="righticon" alt=""></li> |
|
10 |
- <li>통합검색</li> |
|
11 |
- </ul> |
|
12 |
- </div> |
|
13 |
- </div> |
|
14 |
- <form action="search" class="search-form mb-40"> |
|
15 |
- <dl> |
|
16 |
- <dd class="mb-15"> |
|
17 |
- <p>검색범위</p> |
|
18 |
- <ul> |
|
19 |
- <li v-for="(option, index) in checkOptions2" :key="index"> |
|
20 |
- <input type="checkbox" :id="'check_button_' + index" :name="'check_button_' + index" /> |
|
21 |
- <label :for="'check_button_' + index">{{ option }}</label> |
|
22 |
- </li> |
|
23 |
- </ul> |
|
24 |
- </dd> |
|
25 |
- <dd class="mb-15"> |
|
26 |
- <p>검색어</p> |
|
27 |
- <div class="wfull"><input type="text"></div> |
|
28 |
- </dd> |
|
29 |
- <dd class="mb-15"> |
|
30 |
- <p>생산연도</p> |
|
31 |
- <input type="date"> |
|
32 |
- <p class="mark">~</p> |
|
33 |
- <input type="date"> |
|
34 |
- </dd> |
|
35 |
- <dd class="mb-20"> |
|
36 |
- <p>카테고리</p> |
|
37 |
- <ul> |
|
38 |
- <li v-for="(option, index) in checkOptions3" :key="index"> |
|
39 |
- <input type="checkbox" :id="'check_button_' + index" :name="'check_button_' + index" /> |
|
40 |
- <label :for="'check_button_' + index">{{ option }}</label> |
|
41 |
- </li> |
|
42 |
- </ul> |
|
43 |
- </dd> |
|
44 |
- <dd class="mb-15"> |
|
45 |
- <p>정렬</p> |
|
46 |
- <ul> |
|
47 |
- <li v-for="(option, index) in checkOptions4" :key="index"> |
|
48 |
- <input type="radio" :id="'radio_button_' + index" :name="'radio_button_' + index" /> |
|
49 |
- <label :for="'radio_button_' + index">{{ option }}</label> |
|
50 |
- </li> |
|
51 |
- </ul> |
|
52 |
- </dd> |
|
53 |
- <div class="btn-group"> |
|
54 |
- <button class="reset"><img :src="reseticon" alt=""> |
|
55 |
- <p>초기화</p> |
|
56 |
- </button> |
|
57 |
- <button class="search"><img :src="searchicon" alt=""> |
|
58 |
- <p>검색</p> |
|
59 |
- </button> |
|
60 |
- </div> |
|
61 |
- |
|
62 |
- </dl> |
|
63 |
- |
|
64 |
- </form> |
|
65 |
- <div class="search-result"> |
|
66 |
- <ul> |
|
67 |
- <div class="flex-sp-bw mb-20"> |
|
68 |
- <div class="resultext wfull"> |
|
69 |
- <img :src="resulticon" alt=""> |
|
70 |
- <p>총 <b>{{ count }}개</b>의 사진 기록물이 검색되었습니다. </p> |
|
71 |
- </div> |
|
72 |
- <select v-model="itemsPerPage" @change="changeItemsPerPage"> |
|
73 |
- <option :value="5" selected>5개</option> |
|
74 |
- <option :value="10">10개</option> |
|
75 |
- <option :value="15">15개</option> |
|
76 |
- </select> |
|
77 |
- |
|
78 |
- </div> |
|
79 |
- <li v-for="(resultitem, index) in paginatedItems" :key="index" class="mb-30"> |
|
80 |
- |
|
81 |
- <div class="result-box"> |
|
82 |
- <div class="main-img"><img :src="resultitem.img" alt=""></div> |
|
83 |
- <div class="text-box"> |
|
84 |
- <h5>{{ resultitem.title }}</h5> |
|
85 |
- <p class="address">{{ resultitem.address }}</p> |
|
86 |
- <p class="text">{{ resultitem.content }}</p> |
|
87 |
- <div class="mb-20"> |
|
88 |
- <ul class="category"> |
|
89 |
- <li v-if="resultitem.category1" class="category1">카테고리1</li> |
|
90 |
- <li v-if="resultitem.category2" class="category2">카테고리2</li> |
|
91 |
- </ul> |
|
92 |
- </div> |
|
93 |
- <div class="date"> |
|
94 |
- <ul> |
|
95 |
- <li>생산연도 <b>{{ resultitem.year }}</b></li> |
|
96 |
- <li>|</li> |
|
97 |
- <li>등록 <b>{{ resultitem.date }}</b></li> |
|
98 |
- </ul> |
|
99 |
- </div> |
|
100 |
- </div> |
|
101 |
- </div> |
|
102 |
- </li> |
|
103 |
- </ul> |
|
104 |
- <button> <router-link :to="{ path: '/PicHistoryInsert.page' }" class="insert">등록</router-link></button> |
|
105 |
- <div class="pagination"> |
|
106 |
- |
|
107 |
- <!-- Previous and Next Page Buttons --> |
|
108 |
- <button @click="previousPage" :disabled="currentPage === 1">Prev</button> |
|
109 |
- <span>{{ currentPage }} / {{ totalPages }}</span> |
|
110 |
- <button @click="nextPage" :disabled="currentPage === totalPages">Next</button> |
|
111 |
- </div> |
|
112 |
- </div> |
|
2 |
+ <div class="content"> |
|
3 |
+ <div class="sub-title-area mb-30"> |
|
4 |
+ <h2>사진 기록물</h2> |
|
5 |
+ <div class="breadcrumb-list"> |
|
6 |
+ <ul> |
|
7 |
+ <!-- Bind the image source dynamically for homeicon --> |
|
8 |
+ <li><img :src="homeicon" alt="Home Icon"> |
|
9 |
+ <p>기록물</p> |
|
10 |
+ </li> |
|
11 |
+ <li><img :src="righticon" alt=""></li> |
|
12 |
+ <li>통합검색</li> |
|
13 |
+ </ul> |
|
14 |
+ </div> |
|
113 | 15 |
</div> |
16 |
+ <form action="search" class="search-form mb-40"> |
|
17 |
+ <dl> |
|
18 |
+ <dd class="mb-15"> |
|
19 |
+ <p>검색범위</p> |
|
20 |
+ <ul> |
|
21 |
+ <li> |
|
22 |
+ <input type="checkbox" id="allScope" v-model="isChkAllScope" @change="fnChkAllOptions('scope')" /> |
|
23 |
+ <label for="allScope">전체</label> |
|
24 |
+ </li> |
|
25 |
+ <li v-for="(scope, idx) in searchType" :key="idx"> |
|
26 |
+ <input type="checkbox" :id="idx" :name="searchType" :value="scope.key" v-model="searchReqDTO.searchType" @change="fnChkOption('scope')" /> |
|
27 |
+ <label :for="idx">{{ scope.value }}</label> |
|
28 |
+ </li> |
|
29 |
+ </ul> |
|
30 |
+ </dd> |
|
31 |
+ <dd class="mb-15"> |
|
32 |
+ <p>검색어</p> |
|
33 |
+ <div class="wfull"><input type="text" v-model="searchReqDTO.searchText"></div> |
|
34 |
+ </dd> |
|
35 |
+ <dd class="mb-15"> |
|
36 |
+ <p>생산연도</p> |
|
37 |
+ <input type="date" v-model="searchReqDTO.startYear"> |
|
38 |
+ <p class="mark">~</p> |
|
39 |
+ <input type="date" v-model="searchReqDTO.endYear"> |
|
40 |
+ </dd> |
|
41 |
+ <dd class="mb-20"> |
|
42 |
+ <p>카테고리</p> |
|
43 |
+ <ul> |
|
44 |
+ <li v-for="(category, idx) of categorys" :key="idx"> |
|
45 |
+ <input type="checkbox" :id="category.ctgryId" name="categorys" :value="category.ctgryId" v-model="searchReqDTO.searchCtgry" /> |
|
46 |
+ <label :for="category.ctgryId">{{ category.ctgryNm }}</label> |
|
47 |
+ </li> |
|
48 |
+ </ul> |
|
49 |
+ </dd> |
|
50 |
+ <dd class="mb-15"> |
|
51 |
+ <p>정렬</p> |
|
52 |
+ <ul> |
|
53 |
+ <li v-for="(order, idx) of orders" :key="idx"> |
|
54 |
+ <input type="radio" :id="order.key" name="orders" :value="order.key" v-model="searchReqDTO.order" /> |
|
55 |
+ <label :for="order.key">{{ order.value }}</label> |
|
56 |
+ </li> |
|
57 |
+ </ul> |
|
58 |
+ </dd> |
|
59 |
+ <div class="btn-group"> |
|
60 |
+ <button class="reset"><img :src="reseticon" alt=""> |
|
61 |
+ <p>초기화</p> |
|
62 |
+ </button> |
|
63 |
+ <button class="search"><img :src="searchicon" alt=""> |
|
64 |
+ <p>검색</p> |
|
65 |
+ </button> |
|
66 |
+ </div> |
|
67 |
+ </dl> |
|
68 |
+ </form> |
|
69 |
+ <div class="search-result"> |
|
70 |
+ <ul> |
|
71 |
+ <div class="flex-sp-bw mb-20"> |
|
72 |
+ <div class="resultext wfull"> |
|
73 |
+ <img :src="resulticon" alt=""> |
|
74 |
+ <p>총 <b>{{ count }}개</b>의 사진 기록물이 검색되었습니다. </p> |
|
75 |
+ </div> |
|
76 |
+ <select v-model="itemsPerPage" @change="changeItemsPerPage"> |
|
77 |
+ <option :value="5" selected>5개</option> |
|
78 |
+ <option :value="10">10개</option> |
|
79 |
+ <option :value="15">15개</option> |
|
80 |
+ </select> |
|
81 |
+ </div> |
|
82 |
+ <li v-for="(resultitem, index) in paginatedItems" :key="index" class="mb-30"> |
|
83 |
+ <div class="result-box"> |
|
84 |
+ <div class="main-img"><img :src="resultitem.img" alt=""></div> |
|
85 |
+ <div class="text-box"> |
|
86 |
+ <h5>{{ resultitem.title }}</h5> |
|
87 |
+ <p class="address">{{ resultitem.address }}</p> |
|
88 |
+ <p class="text">{{ resultitem.content }}</p> |
|
89 |
+ <div class="mb-20"> |
|
90 |
+ <ul class="category"> |
|
91 |
+ <li v-if="resultitem.category1" class="category1">카테고리1</li> |
|
92 |
+ <li v-if="resultitem.category2" class="category2">카테고리2</li> |
|
93 |
+ </ul> |
|
94 |
+ </div> |
|
95 |
+ <div class="date"> |
|
96 |
+ <ul> |
|
97 |
+ <li>생산연도 <b>{{ resultitem.year }}</b></li> |
|
98 |
+ <li>|</li> |
|
99 |
+ <li>등록 <b>{{ resultitem.date }}</b></li> |
|
100 |
+ </ul> |
|
101 |
+ </div> |
|
102 |
+ </div> |
|
103 |
+ </div> |
|
104 |
+ </li> |
|
105 |
+ </ul> |
|
106 |
+ <button> <router-link :to="{ path: '/PicHistoryInsert.page' }" class="insert">등록</router-link></button> |
|
107 |
+ <div class="pagination"> |
|
108 |
+ <!-- Previous and Next Page Buttons --> |
|
109 |
+ <button @click="previousPage" :disabled="currentPage === 1">Prev</button> |
|
110 |
+ <span>{{ currentPage }} / {{ totalPages }}</span> |
|
111 |
+ <button @click="nextPage" :disabled="currentPage === totalPages">Next</button> |
|
112 |
+ </div> |
|
113 |
+ </div> |
|
114 |
+ </div> |
|
114 | 115 |
</template> |
115 |
- |
|
116 | 116 |
<script> |
117 |
+import { findAllCategoryProc } from "../../../resources/api/category"; // 카테고리 목록 검색 |
|
118 |
+ |
|
117 | 119 |
export default { |
118 |
- data() { |
|
119 |
- return { |
|
120 |
- currentPage: 1, // Current page number |
|
121 |
- itemsPerPage: 5, |
|
122 |
- resulticon: "client/resources/images/icon/r-check.png", |
|
123 |
- homeicon: 'client/resources/images/icon/home.png', |
|
124 |
- searchicon: 'client/resources/images/icon/search.png', |
|
125 |
- reseticon: 'client/resources/images/icon/reset.png', |
|
126 |
- righticon: 'client/resources/images/icon/right.png', |
|
127 |
- count: 23, |
|
128 |
- checkOptions: [ |
|
129 |
- '전체', |
|
130 |
- '사진', |
|
131 |
- '영상', |
|
132 |
- '미디어 영상', |
|
133 |
- '보도자료', |
|
134 |
- ], |
|
135 |
- checkOptions2: [ |
|
136 |
- '전체', |
|
137 |
- '제목', |
|
138 |
- '내용', |
|
139 |
- '주소', |
|
140 |
- ], |
|
141 |
- checkOptions3: [ |
|
142 |
- '카테고리1', |
|
143 |
- '카테고리2', |
|
144 |
- '카테고리3', |
|
145 |
- '카테고리4', |
|
146 |
- '카테고리5', |
|
147 |
- ], |
|
148 |
- checkOptions4: [ |
|
149 |
- '최신', |
|
150 |
- '인기', |
|
151 |
- ], |
|
152 |
- resultitems: [ |
|
153 |
- { |
|
154 |
- img: 'client/resources/images/img6.png', |
|
155 |
- title: '사진 기록물 제목', |
|
156 |
- address: '경상북도 구미시 송정대로 55', |
|
157 |
- content: '대한민국 최대의 내륙 산업단지를 보유하고, 서울로부터 277km, 부산으로부터 167km 거리에 있으며, 면적은 615㎢로 경상북도 전체 면적의 3.2%에 달합니다. 인구는 41만 명이고, 선산읍, 고아읍, 산동읍을 비롯한 3읍, 5면, 17개 동으로 구성되어…', |
|
158 |
- category1: true, |
|
159 |
- category2: true, |
|
160 |
- year: 2020, |
|
161 |
- date: '2021-01-01' |
|
162 |
- }, |
|
163 |
- { |
|
164 |
- img: 'client/resources/images/img6.png', |
|
165 |
- title: '영상 기록물 제목', |
|
166 |
- address: '경상북도 구미시 송정대로 55', |
|
167 |
- content: '대한민국 최대의 내륙 산업단지를 보유하고, 서울로부터 277km, 부산으로부터 167km 거리에 있으며, 면적은 615㎢로 경상북도 전체 면적의 3.2%에 달합니다. 인구는 41만 명이고, 선산읍, 고아읍, 산동읍을 비롯한 3읍, 5면, 17개 동으로 구성되어…', |
|
168 |
- category1: true, |
|
169 |
- category2: false, |
|
170 |
- year: 2021, |
|
171 |
- date: '2022-01-01' |
|
172 |
- }, |
|
173 |
- { |
|
174 |
- img: 'client/resources/images/img6.png', |
|
175 |
- title: '미디어 영상 제목', |
|
176 |
- address: '경상북도 구미시 송정대로 55', |
|
177 |
- content: '대한민국 최대의 내륙 산업단지를 보유하고, 서울로부터 277km, 부산으로부터 167km 거리에 있으며, 면적은 615㎢로 경상북도 전체 면적의 3.2%에 달합니다. 인구는 41만 명이고, 선산읍, 고아읍, 산동읍을 비롯한 3읍, 5면, 17개 동으로 구성되어…', |
|
178 |
- category1: true, |
|
179 |
- category2: false, |
|
180 |
- year: 2021, |
|
181 |
- date: '2022-01-01' |
|
182 |
- }, |
|
183 |
- { |
|
184 |
- img: 'client/resources/images/img6.png', |
|
185 |
- title: '보도자료 제목', |
|
186 |
- address: '경상북도 구미시 송정대로 55', |
|
187 |
- content: '대한민국 최대의 내륙 산업단지를 보유하고, 서울로부터 277km, 부산으로부터 167km 거리에 있으며, 면적은 615㎢로 경상북도 전체 면적의 3.2%에 달합니다. 인구는 41만 명이고, 선산읍, 고아읍, 산동읍을 비롯한 3읍, 5면, 17개 동으로 구성되어…', |
|
188 |
- category1: true, |
|
189 |
- category2: false, |
|
190 |
- year: 2021, |
|
191 |
- date: '2022-01-01' |
|
192 |
- }, |
|
193 |
- ] |
|
194 |
- }; |
|
195 |
- }, |
|
196 |
- computed: { |
|
197 |
- // Total number of pages |
|
198 |
- totalPages() { |
|
199 |
- return Math.ceil(this.resultitems.length / this.itemsPerPage); |
|
200 |
- }, |
|
120 |
+ data() { |
|
121 |
+ return { |
|
122 |
+ currentPage: 1, // Current page number |
|
123 |
+ itemsPerPage: 5, |
|
124 |
+ resulticon: "client/resources/images/icon/r-check.png", |
|
125 |
+ homeicon: 'client/resources/images/icon/home.png', |
|
126 |
+ searchicon: 'client/resources/images/icon/search.png', |
|
127 |
+ reseticon: 'client/resources/images/icon/reset.png', |
|
128 |
+ righticon: 'client/resources/images/icon/right.png', |
|
201 | 129 |
|
202 |
- // Paginated items based on current page and items per page |
|
203 |
- paginatedItems() { |
|
204 |
- const start = (this.currentPage - 1) * this.itemsPerPage; |
|
205 |
- const end = start + this.itemsPerPage; |
|
206 |
- return this.resultitems.slice(start, end); |
|
207 |
- }, |
|
208 |
- }, |
|
209 |
- methods: { |
|
210 |
- // Change the number of items displayed per page |
|
211 |
- changeItemsPerPage() { |
|
212 |
- this.currentPage = 1; // Reset to first page when changing items per page |
|
213 |
- }, |
|
130 |
+ // 검색용 객체 |
|
131 |
+ searchReqDTO: { |
|
132 |
+ searchType: [], |
|
133 |
+ searchText: null, |
|
134 |
+ startYear: null, |
|
135 |
+ endYear: null, |
|
136 |
+ searchTy: null, |
|
137 |
+ searchCtgry: [], |
|
138 |
+ order: "rgsde", |
|
139 |
+ }, |
|
214 | 140 |
|
215 |
- // Go to the previous page |
|
216 |
- previousPage() { |
|
217 |
- if (this.currentPage > 1) { |
|
218 |
- this.currentPage--; |
|
219 |
- } |
|
220 |
- }, |
|
141 |
+ isChkAllScope: false, // 검색범위 전체 체크 여부 |
|
142 |
+ searchType: [ |
|
143 |
+ { key: "sj", value: "제목" }, |
|
144 |
+ { key: "cn", value: "내용" }, |
|
145 |
+ { key: "adres", value: "주소" }, |
|
146 |
+ ], // 검색범위 목록 |
|
147 |
+ categorys: [], // 카테고리 목록 |
|
148 |
+ orders: [ |
|
149 |
+ { key: "rgsde", value: "최신" }, |
|
150 |
+ { key: "rdcnt", value: "인기" }, |
|
151 |
+ ], // 정렬 목록 |
|
221 | 152 |
|
222 |
- // Go to the next page |
|
223 |
- nextPage() { |
|
224 |
- if (this.currentPage < this.totalPages) { |
|
225 |
- this.currentPage++; |
|
226 |
- } |
|
153 |
+ searchResult: [], // 검색결과 |
|
154 |
+ resultitems: [ |
|
155 |
+ { |
|
156 |
+ img: 'client/resources/images/img6.png', |
|
157 |
+ title: '사진 기록물 제목', |
|
158 |
+ address: '경상북도 구미시 송정대로 55', |
|
159 |
+ content: '대한민국 최대의 내륙 산업단지를 보유하고, 서울로부터 277km, 부산으로부터 167km 거리에 있으며, 면적은 615㎢로 경상북도 전체 면적의 3.2%에 달합니다. 인구는 41만 명이고, 선산읍, 고아읍, 산동읍을 비롯한 3읍, 5면, 17개 동으로 구성되어…', |
|
160 |
+ category1: true, |
|
161 |
+ category2: true, |
|
162 |
+ year: 2020, |
|
163 |
+ date: '2021-01-01' |
|
227 | 164 |
}, |
165 |
+ ] |
|
166 |
+ }; |
|
167 |
+ }, |
|
168 |
+ |
|
169 |
+ computed: { |
|
170 |
+ // Total number of pages |
|
171 |
+ totalPages() { |
|
172 |
+ return Math.ceil(this.resultitems.length / this.itemsPerPage); |
|
228 | 173 |
}, |
174 |
+ |
|
175 |
+ // Paginated items based on current page and items per page |
|
176 |
+ paginatedItems() { |
|
177 |
+ const start = (this.currentPage - 1) * this.itemsPerPage; |
|
178 |
+ const end = start + this.itemsPerPage; |
|
179 |
+ return this.resultitems.slice(start, end); |
|
180 |
+ }, |
|
181 |
+ }, |
|
182 |
+ |
|
183 |
+ created() { |
|
184 |
+ // 초기 데이터 세팅 |
|
185 |
+ this.isChkAllScope = true; |
|
186 |
+ this.searchReqDTO.searchType = this.searchType.map(item => item.key); |
|
187 |
+ this.searchReqDTO.order = this.orders[0].key |
|
188 |
+ |
|
189 |
+ this.fnFindCategorys(); // 카테고리 목록 조회 (검색조건 없음) |
|
190 |
+ }, |
|
191 |
+ |
|
192 |
+ methods: { |
|
193 |
+ // Change the number of items displayed per page |
|
194 |
+ changeItemsPerPage() { |
|
195 |
+ this.currentPage = 1; // Reset to first page when changing items per page |
|
196 |
+ }, |
|
197 |
+ |
|
198 |
+ // Go to the previous page |
|
199 |
+ previousPage() { |
|
200 |
+ if (this.currentPage > 1) { |
|
201 |
+ this.currentPage--; |
|
202 |
+ } |
|
203 |
+ }, |
|
204 |
+ |
|
205 |
+ // Go to the next page |
|
206 |
+ nextPage() { |
|
207 |
+ if (this.currentPage < this.totalPages) { |
|
208 |
+ this.currentPage++; |
|
209 |
+ } |
|
210 |
+ }, |
|
211 |
+ |
|
212 |
+ // 카테고리 목록 조회 |
|
213 |
+ async fnFindCategorys() { |
|
214 |
+ try { |
|
215 |
+ const response = await findAllCategoryProc(); |
|
216 |
+ this.categorys = response.data.data.ctgry; |
|
217 |
+ } catch (error) { |
|
218 |
+ if (error.response) { |
|
219 |
+ console.log("에러 응답:", error.response.data); |
|
220 |
+ } |
|
221 |
+ console.error("Error:", error); |
|
222 |
+ } |
|
223 |
+ }, |
|
224 |
+ |
|
225 |
+ // 통합검색 |
|
226 |
+ async fnFindAllDatas() { |
|
227 |
+ try { |
|
228 |
+ let params = {}; |
|
229 |
+ if (this.searchReqDTO.searchRecord.length > 0) { |
|
230 |
+ params.searchRecords = this.searchReqDTO.searchRecord.join(','); |
|
231 |
+ } |
|
232 |
+ if (this.searchReqDTO.searchType.length > 0) { |
|
233 |
+ params.searchTypes = this.searchReqDTO.searchType.join(','); |
|
234 |
+ } |
|
235 |
+ if (this.searchReqDTO.searchCtgry.length > 0) { |
|
236 |
+ params.searchCtgries = this.searchReqDTO.searchCtgry.join(','); |
|
237 |
+ } |
|
238 |
+ params.searchText = this.searchReqDTO.searchText; |
|
239 |
+ params.startYear = this.searchReqDTO.startYear; |
|
240 |
+ params.endYear = this.searchReqDTO.endYear; |
|
241 |
+ params.order = this.searchReqDTO.order; |
|
242 |
+ |
|
243 |
+ // API 호출 |
|
244 |
+ const response = await findAllDatas(params); |
|
245 |
+ this.searchResult = response.data.data.searchResult; |
|
246 |
+ } catch (error) { |
|
247 |
+ if (error.response) { |
|
248 |
+ console.log("에러 응답:", error.response.data); |
|
249 |
+ } |
|
250 |
+ console.error("Error:", error); |
|
251 |
+ } |
|
252 |
+ }, |
|
253 |
+ }, |
|
229 | 254 |
}; |
230 | 255 |
</script> |
231 |
-<style scoped> |
|
232 |
- |
|
233 |
-</style>(파일 끝에 줄바꿈 문자 없음) |
|
256 |
+<style scoped></style>(파일 끝에 줄바꿈 문자 없음) |
--- client/views/pages/user/TotalSearch.vue
+++ client/views/pages/user/TotalSearch.vue
... | ... | @@ -1,194 +1,286 @@ |
1 | 1 |
<template> |
2 |
- <div class="content"> |
|
3 |
- <div class="sub-title-area mb-30"> |
|
4 |
- <h2>통합검색</h2> |
|
5 |
- <div class="breadcrumb-list"> |
|
6 |
- <ul> |
|
7 |
- <!-- Bind the image source dynamically for homeicon --> |
|
8 |
- <li><img :src="homeicon" alt="Home Icon"><p></p></li> |
|
9 |
- <li><img :src="righticon" alt=""></li> |
|
10 |
- <li>통합검색</li> |
|
11 |
- </ul> |
|
12 |
- </div> |
|
13 |
- </div> |
|
14 |
- <form action="search" class="search-form mb-40"> |
|
15 |
- <dl> |
|
16 |
- <dd class="mb-15"> |
|
17 |
- <p>기록유형</p> |
|
18 |
- <ul> |
|
19 |
- <li v-for="(option, index) in checkOptions" :key="index"> |
|
20 |
- <input type="checkbox" :id="'check_button_' + index" :name="'check_button_' + index" /> |
|
21 |
- <label :for="'check_button_' + index">{{ option }}</label> |
|
22 |
- </li> |
|
23 |
- </ul> |
|
24 |
- </dd> |
|
25 |
- <dd class="mb-15"> |
|
26 |
- <p>검색범위</p> |
|
27 |
- <ul> |
|
28 |
- <li v-for="(option, index) in checkOptions2" :key="index"> |
|
29 |
- <input type="checkbox" :id="'check_button_' + index" :name="'check_button_' + index" /> |
|
30 |
- <label :for="'check_button_' + index">{{ option }}</label> |
|
31 |
- </li> |
|
32 |
- </ul> |
|
33 |
- </dd> |
|
34 |
- <dd class="mb-15"> |
|
35 |
- <p>검색어</p> |
|
36 |
- <div class="wfull"><input type="text"></div> |
|
37 |
- </dd> |
|
38 |
- <dd class="mb-15"> |
|
39 |
- <p>생산연도</p> |
|
40 |
- <input type="date"> |
|
41 |
- <p class="mark">~</p> |
|
42 |
- <input type="date"> |
|
43 |
- </dd> |
|
44 |
- <dd class="mb-20"> |
|
45 |
- <p>카테고리</p> |
|
46 |
- <ul> |
|
47 |
- <li v-for="(option, index) in checkOptions3" :key="index"> |
|
48 |
- <input type="checkbox" :id="'check_button_' + index" :name="'check_button_' + index" /> |
|
49 |
- <label :for="'check_button_' + index">{{ option }}</label> |
|
50 |
- </li> |
|
51 |
- </ul> |
|
52 |
- </dd> |
|
53 |
- <dd class="mb-15"> |
|
54 |
- <p>정렬</p> |
|
55 |
- <ul> |
|
56 |
- <li v-for="(option, index) in checkOptions4" :key="index"> |
|
57 |
- <input type="radio" :id="'radio_button_' + index" :name="'radio_button_' + index" /> |
|
58 |
- <label :for="'radio_button_' + index">{{ option }}</label> |
|
59 |
- </li> |
|
60 |
- </ul> |
|
61 |
- </dd> |
|
62 |
- <div class="btn-group"> |
|
63 |
- <button class="reset"><img :src="reseticon" alt=""> |
|
64 |
- <p>초기화</p> |
|
65 |
- </button> |
|
66 |
- <button class="search"><img :src="searchicon" alt=""> |
|
67 |
- <p>검색</p> |
|
68 |
- </button> |
|
69 |
- </div> |
|
70 |
- |
|
71 |
- </dl> |
|
72 |
- |
|
73 |
- </form> |
|
74 |
- <div class="search-result"> |
|
75 |
- <ul> |
|
76 |
- <li v-for="(resultitem, index) in resultitems" :key="index" class="mb-30"> |
|
77 |
- <div class="flex-sp-bw mb-20"> |
|
78 |
- <div class="resultext"> |
|
79 |
- <img :src="resulticon" alt=""> |
|
80 |
- <p>총 <b>{{ resultitem.count }}개</b>의 사진 기록물이 검색되었습니다. </p> |
|
81 |
- </div> |
|
82 |
- <router-link :to="{ path: '/' + resultitem.id }" class="gopage">모두보기</router-link> |
|
83 |
- </div> |
|
84 |
- <div class="result-box"> |
|
85 |
- <div class="main-img"><img :src="resultitem.img" alt=""></div> |
|
86 |
- <div class="text-box"> |
|
87 |
- <h5>{{ resultitem.title }}</h5> |
|
88 |
- <p class="address">{{ resultitem.address }}</p> |
|
89 |
- <p class="text">{{ resultitem.content }}</p> |
|
90 |
- <div class="mb-20"> |
|
91 |
- <ul class="category"> |
|
92 |
- <li v-if="resultitem.category1" class="category1">카테고리1</li> |
|
93 |
- <li v-if="resultitem.category2" class="category2">카테고리2</li> |
|
94 |
- </ul> |
|
95 |
- </div> |
|
96 |
- <div class="date"> |
|
97 |
- <ul> |
|
98 |
- <li>생산연도 <b>{{ resultitem.year }}</b></li> |
|
99 |
- <li>|</li> |
|
100 |
- <li>등록 <b>{{ resultitem.date }}</b></li> |
|
101 |
- </ul> |
|
102 |
- </div> |
|
103 |
- </div> |
|
104 |
- </div> |
|
105 |
- </li> |
|
106 |
- </ul> |
|
107 |
- </div> |
|
2 |
+ <div class="content"> |
|
3 |
+ <div class="sub-title-area mb-30"> |
|
4 |
+ <h2>통합검색</h2> |
|
5 |
+ <div class="breadcrumb-list"> |
|
6 |
+ <ul> |
|
7 |
+ <!-- Bind the image source dynamically for homeicon --> |
|
8 |
+ <li> |
|
9 |
+ <img :src="homeicon" alt="Home Icon"> |
|
10 |
+ <p></p> |
|
11 |
+ </li> |
|
12 |
+ <li><img :src="righticon" alt=""></li> |
|
13 |
+ <li>통합검색</li> |
|
14 |
+ </ul> |
|
15 |
+ </div> |
|
108 | 16 |
</div> |
17 |
+ <form action="search" class="search-form mb-40"> |
|
18 |
+ <dl> |
|
19 |
+ <dd class="mb-15"> |
|
20 |
+ <p>기록유형</p> |
|
21 |
+ <ul> |
|
22 |
+ <li> |
|
23 |
+ <input type="checkbox" id="allRecord" v-model="isChkAllRecord" @change="fnChkAllOptions('record')" /> |
|
24 |
+ <label for="allRecord">전체</label> |
|
25 |
+ </li> |
|
26 |
+ <li v-for="(record, idx) in searchRecord" :key="idx"> |
|
27 |
+ <input type="checkbox" :id="idx" :name="searchRecord" :value="record.key" v-model="searchReqDTO.searchRecord" @change="fnChkOption('record')" /> |
|
28 |
+ <label :for="idx">{{ record.value }}</label> |
|
29 |
+ </li> |
|
30 |
+ </ul> |
|
31 |
+ </dd> |
|
32 |
+ <dd class="mb-15"> |
|
33 |
+ <p>검색범위</p> |
|
34 |
+ <ul> |
|
35 |
+ <li> |
|
36 |
+ <input type="checkbox" id="allScope" v-model="isChkAllScope" @change="fnChkAllOptions('scope')" /> |
|
37 |
+ <label for="allScope">전체</label> |
|
38 |
+ </li> |
|
39 |
+ <li v-for="(scope, idx) in searchType" :key="idx"> |
|
40 |
+ <input type="checkbox" :id="idx" :name="searchType" :value="scope.key" v-model="searchReqDTO.searchType" @change="fnChkOption('scope')" /> |
|
41 |
+ <label :for="idx">{{ scope.value }}</label> |
|
42 |
+ </li> |
|
43 |
+ </ul> |
|
44 |
+ </dd> |
|
45 |
+ <dd class="mb-15"> |
|
46 |
+ <p>검색어</p> |
|
47 |
+ <div class="wfull"><input type="text" v-model="searchReqDTO.searchText"></div> |
|
48 |
+ </dd> |
|
49 |
+ <dd class="mb-15"> |
|
50 |
+ <p>생산연도</p> |
|
51 |
+ <input type="date" v-model="searchReqDTO.startYear"> |
|
52 |
+ <p class="mark">~</p> |
|
53 |
+ <input type="date" v-model="searchReqDTO.endYear"> |
|
54 |
+ </dd> |
|
55 |
+ <dd class="mb-20"> |
|
56 |
+ <p>카테고리</p> |
|
57 |
+ <ul> |
|
58 |
+ <li v-for="(category, idx) of categorys" :key="idx"> |
|
59 |
+ <input type="checkbox" :id="category.ctgryId" name="categorys" :value="category.ctgryId" v-model="searchReqDTO.searchCtgry" /> |
|
60 |
+ <label :for="category.ctgryId">{{ category.ctgryNm }}</label> |
|
61 |
+ </li> |
|
62 |
+ </ul> |
|
63 |
+ </dd> |
|
64 |
+ <dd class="mb-15"> |
|
65 |
+ <p>정렬</p> |
|
66 |
+ <ul> |
|
67 |
+ <li v-for="(order, idx) of orders" :key="idx"> |
|
68 |
+ <input type="radio" :id="order.key" name="orders" :value="order.key" v-model="searchReqDTO.order" /> |
|
69 |
+ <label :for="order.key">{{ order.value }}</label> |
|
70 |
+ </li> |
|
71 |
+ </ul> |
|
72 |
+ </dd> |
|
73 |
+ <div class="btn-group"> |
|
74 |
+ <button type="button" class="reset" @click="init"> |
|
75 |
+ <img :src="reseticon" alt=""> |
|
76 |
+ <p>초기화</p> |
|
77 |
+ </button> |
|
78 |
+ <button type="button" class="search" @click="fnFindAllDatas"> |
|
79 |
+ <img :src="searchicon" alt=""> |
|
80 |
+ <p>검색</p> |
|
81 |
+ </button> |
|
82 |
+ </div> |
|
83 |
+ </dl> |
|
84 |
+ </form> |
|
85 |
+ <div class="search-result"> |
|
86 |
+ <ul> |
|
87 |
+ <li v-for="(resultItem, idx) of searchResult" :key="idx" class="mb-30"> |
|
88 |
+ <div class="flex-sp-bw mb-20"> |
|
89 |
+ <div class="resultext"> |
|
90 |
+ <img :src="resulticon" alt=""> |
|
91 |
+ <p>총 <b>{{ resultItem.count }}개</b>의 {{ searchRecord[idx].value }}{{ resultItem.key === 'N' ? '가' : '이' }} 검색되었습니다.</p> |
|
92 |
+ </div> |
|
93 |
+ <router-link :to="{ path: '/' + resultItem.id }" class="gopage">모두보기</router-link> |
|
94 |
+ </div> |
|
95 |
+ <div v-for="(item, idx2) of resultItem.list" :key="idx2" class="result-box"> |
|
96 |
+ <div class="main-img"> |
|
97 |
+ <img v-if="item.hasOwnProperty('files') && item.files.length > 0" :src="item.files[0].filePath" alt=""> |
|
98 |
+ <img v-else src="client/resources/images/img6.png" alt=""> |
|
99 |
+ </div> |
|
100 |
+ <div class="text-box"> |
|
101 |
+ <h5>{{ item.sj }}</h5> |
|
102 |
+ <p v-if="resultItem.key === 'P' || resultItem.key === 'V'" class="address">{{ item.adres }}</p> |
|
103 |
+ <p class="text">{{ item.cn }}</p> |
|
104 |
+ <div class="mb-20"> |
|
105 |
+ <ul class="category"> |
|
106 |
+ <li v-for="(ctgry, idx3) of item.ctgrys" :key="idx3" class="category1">{{ ctgry.ctgryNm }}</li> |
|
107 |
+ </ul> |
|
108 |
+ </div> |
|
109 |
+ <div class="date"> |
|
110 |
+ <ul> |
|
111 |
+ <li>생산연도 <b>{{ item.prdctnYear }}</b></li> |
|
112 |
+ <li>|</li> |
|
113 |
+ <li>등록 <b>{{ item.rgsde }}</b></li> |
|
114 |
+ </ul> |
|
115 |
+ </div> |
|
116 |
+ </div> |
|
117 |
+ </div> |
|
118 |
+ </li> |
|
119 |
+ </ul> |
|
120 |
+ </div> |
|
121 |
+ </div> |
|
109 | 122 |
</template> |
110 |
- |
|
111 | 123 |
<script> |
124 |
+// 통합 검색 |
|
125 |
+import { findAllDatas } from "../../../resources/api/main"; |
|
126 |
+import { findAllCategoryProc } from "../../../resources/api/category"; // 카테고리 목록 검색 |
|
127 |
+ |
|
112 | 128 |
export default { |
113 |
- data() { |
|
114 |
- return { |
|
115 |
- resulticon: "client/resources/images/icon/r-check.png", |
|
116 |
- homeicon: 'client/resources/images/icon/home.png', |
|
117 |
- searchicon: 'client/resources/images/icon/search.png', |
|
118 |
- reseticon: 'client/resources/images/icon/reset.png', |
|
119 |
- righticon: 'client/resources/images/icon/right.png', |
|
120 |
- checkOptions: [ |
|
121 |
- '전체', |
|
122 |
- '사진', |
|
123 |
- '영상', |
|
124 |
- '미디어 영상', |
|
125 |
- '보도자료', |
|
126 |
- ], |
|
127 |
- checkOptions2: [ |
|
128 |
- '전체', |
|
129 |
- '제목', |
|
130 |
- '내용', |
|
131 |
- '주소', |
|
132 |
- ], |
|
133 |
- checkOptions3: [ |
|
134 |
- '카테고리1', |
|
135 |
- '카테고리2', |
|
136 |
- '카테고리3', |
|
137 |
- '카테고리4', |
|
138 |
- '카테고리5', |
|
139 |
- ], |
|
140 |
- checkOptions4: [ |
|
141 |
- '최신', |
|
142 |
- '인기', |
|
143 |
- ], |
|
144 |
- resultitems: [ |
|
145 |
- { |
|
146 |
- count: 5, |
|
147 |
- img: 'client/resources/images/img6.png', |
|
148 |
- title: '사진 기록물 제목', |
|
149 |
- address: '경상북도 구미시 송정대로 55', |
|
150 |
- content: '대한민국 최대의 내륙 산업단지를 보유하고, 서울로부터 277km, 부산으로부터 167km 거리에 있으며, 면적은 615㎢로 경상북도 전체 면적의 3.2%에 달합니다. 인구는 41만 명이고, 선산읍, 고아읍, 산동읍을 비롯한 3읍, 5면, 17개 동으로 구성되어…', |
|
151 |
- category1: true, |
|
152 |
- category2: true, |
|
153 |
- year: 2020, |
|
154 |
- date: '2021-01-01', |
|
155 |
- id: 'PicHistorySearch.page' |
|
156 |
- }, |
|
157 |
- { |
|
158 |
- count: 3, |
|
159 |
- img: 'client/resources/images/img6.png', |
|
160 |
- title: '영상 기록물 제목', |
|
161 |
- address: '경상북도 구미시 송정대로 55', |
|
162 |
- content: '대한민국 최대의 내륙 산업단지를 보유하고, 서울로부터 277km, 부산으로부터 167km 거리에 있으며, 면적은 615㎢로 경상북도 전체 면적의 3.2%에 달합니다. 인구는 41만 명이고, 선산읍, 고아읍, 산동읍을 비롯한 3읍, 5면, 17개 동으로 구성되어…', |
|
163 |
- category1: true, |
|
164 |
- category2: false, |
|
165 |
- year: 2021, |
|
166 |
- date: '2022-01-01' |
|
167 |
- }, |
|
168 |
- { |
|
169 |
- count: 3, |
|
170 |
- img: 'client/resources/images/img6.png', |
|
171 |
- title: '미디어 영상 제목', |
|
172 |
- address: '경상북도 구미시 송정대로 55', |
|
173 |
- content: '대한민국 최대의 내륙 산업단지를 보유하고, 서울로부터 277km, 부산으로부터 167km 거리에 있으며, 면적은 615㎢로 경상북도 전체 면적의 3.2%에 달합니다. 인구는 41만 명이고, 선산읍, 고아읍, 산동읍을 비롯한 3읍, 5면, 17개 동으로 구성되어…', |
|
174 |
- category1: true, |
|
175 |
- category2: false, |
|
176 |
- year: 2021, |
|
177 |
- date: '2022-01-01' |
|
178 |
- }, |
|
179 |
- { |
|
180 |
- count: 3, |
|
181 |
- img: 'client/resources/images/img6.png', |
|
182 |
- title: '보도자료 제목', |
|
183 |
- address: '경상북도 구미시 송정대로 55', |
|
184 |
- content: '대한민국 최대의 내륙 산업단지를 보유하고, 서울로부터 277km, 부산으로부터 167km 거리에 있으며, 면적은 615㎢로 경상북도 전체 면적의 3.2%에 달합니다. 인구는 41만 명이고, 선산읍, 고아읍, 산동읍을 비롯한 3읍, 5면, 17개 동으로 구성되어…', |
|
185 |
- category1: true, |
|
186 |
- category2: false, |
|
187 |
- year: 2021, |
|
188 |
- date: '2022-01-01' |
|
189 |
- }, |
|
190 |
- ] |
|
191 |
- }; |
|
129 |
+ data() { |
|
130 |
+ return { |
|
131 |
+ // icon |
|
132 |
+ resulticon: "client/resources/images/icon/r-check.png", |
|
133 |
+ homeicon: 'client/resources/images/icon/home.png', |
|
134 |
+ searchicon: 'client/resources/images/icon/search.png', |
|
135 |
+ reseticon: 'client/resources/images/icon/reset.png', |
|
136 |
+ righticon: 'client/resources/images/icon/right.png', |
|
137 |
+ |
|
138 |
+ // 검색용 객체 초기값 |
|
139 |
+ searchDefault: { |
|
140 |
+ searchRecord: ["P", "V", "M", "N"], |
|
141 |
+ searchType: ["sj", "cn", "adres"], |
|
142 |
+ searchText: null, |
|
143 |
+ startYear: null, |
|
144 |
+ endYear: null, |
|
145 |
+ searchCtgry: [], |
|
146 |
+ order: "rgsde", |
|
147 |
+ }, |
|
148 |
+ searchReqDTO: {}, |
|
149 |
+ |
|
150 |
+ isChkAllRecord: true, // 기록유형 전체 체크 여부 |
|
151 |
+ searchRecord: [ |
|
152 |
+ { key: "P", value: "사진" }, |
|
153 |
+ { key: "V", value: "영상" }, |
|
154 |
+ { key: "M", value: "미디어영상" }, |
|
155 |
+ { key: "N", value: "보도자료" }, |
|
156 |
+ ], // 기록유형 목록 |
|
157 |
+ isChkAllScope: true, // 검색범위 전체 체크 여부 |
|
158 |
+ searchType: [ |
|
159 |
+ { key: "sj", value: "제목" }, |
|
160 |
+ { key: "cn", value: "내용" }, |
|
161 |
+ { key: "adres", value: "주소" }, |
|
162 |
+ ], // 검색범위 목록 |
|
163 |
+ categorys: [], // 카테고리 목록 |
|
164 |
+ orders: [ |
|
165 |
+ { key: "rgsde", value: "최신" }, |
|
166 |
+ { key: "rdcnt", value: "인기" }, |
|
167 |
+ ], // 정렬 목록 |
|
168 |
+ |
|
169 |
+ searchResult: [], // 검색결과 |
|
170 |
+ |
|
171 |
+ isInitialLoad: true // 초기 로드 여부 |
|
172 |
+ }; |
|
173 |
+ }, |
|
174 |
+ |
|
175 |
+ created() { |
|
176 |
+ this.init(); // 초기화 |
|
177 |
+ this.fnFindCategorys(); // 카테고리 목록 조회 (검색조건 없음) |
|
178 |
+ }, |
|
179 |
+ |
|
180 |
+ mounted() { |
|
181 |
+ let searchText = this.$route.query.searchText; |
|
182 |
+ if (searchText !== null) { |
|
183 |
+ this.searchReqDTO.searchText = searchText; |
|
192 | 184 |
} |
185 |
+ |
|
186 |
+ this.fnFindAllDatas(); // 통합검색 |
|
187 |
+ }, |
|
188 |
+ |
|
189 |
+ methods: { |
|
190 |
+ // 초기화 |
|
191 |
+ init() { |
|
192 |
+ if (this.isInitialLoad) { |
|
193 |
+ this.isInitialLoad = false; |
|
194 |
+ } else { |
|
195 |
+ if (!confirm('검색 조건을 초기화하시겠습니까?')) { |
|
196 |
+ return; |
|
197 |
+ } |
|
198 |
+ } |
|
199 |
+ this.searchReqDTO = Object.assign({}, this.searchDefault); // 검색객체 초기화 |
|
200 |
+ this.searchResult = []; // 검색결과 초기화 |
|
201 |
+ }, |
|
202 |
+ // 카테고리 목록 조회 |
|
203 |
+ async fnFindCategorys() { |
|
204 |
+ try { |
|
205 |
+ const response = await findAllCategoryProc(); |
|
206 |
+ this.categorys = response.data.data.ctgry; |
|
207 |
+ } catch (error) { |
|
208 |
+ this.categorys = []; // 카테고리 목록 초기화 |
|
209 |
+ |
|
210 |
+ if (error.response) { |
|
211 |
+ console.log("에러 응답:", error.response.data); |
|
212 |
+ } |
|
213 |
+ console.error("Error:", error); |
|
214 |
+ } |
|
215 |
+ }, |
|
216 |
+ |
|
217 |
+ // 통합검색 |
|
218 |
+ async fnFindAllDatas() { |
|
219 |
+ if (this.searchReqDTO.searchRecord == null || this.searchReqDTO.searchRecord.length < 1) { |
|
220 |
+ alert('검색 유형은 최소 한 개 이상 선택해주세요.'); |
|
221 |
+ return; |
|
222 |
+ } |
|
223 |
+ |
|
224 |
+ try { |
|
225 |
+ let params = {}; |
|
226 |
+ if (this.searchReqDTO.searchRecord.length > 0) { |
|
227 |
+ params.searchRecords = this.searchReqDTO.searchRecord.join(','); |
|
228 |
+ } |
|
229 |
+ if (this.searchReqDTO.searchType.length > 0) { |
|
230 |
+ params.searchTypes = this.searchReqDTO.searchType.join(','); |
|
231 |
+ } |
|
232 |
+ if (this.searchReqDTO.searchCtgry.length > 0) { |
|
233 |
+ params.searchCtgries = this.searchReqDTO.searchCtgry.join(','); |
|
234 |
+ } |
|
235 |
+ params.searchText = this.searchReqDTO.searchText; |
|
236 |
+ params.startYear = this.searchReqDTO.startYear; |
|
237 |
+ params.endYear = this.searchReqDTO.endYear; |
|
238 |
+ params.order = this.searchReqDTO.order; |
|
239 |
+ |
|
240 |
+ // API 호출 |
|
241 |
+ const response = await findAllDatas(params); |
|
242 |
+ this.searchResult = response.data.data.searchResult; |
|
243 |
+ } catch (error) { |
|
244 |
+ this.searchResult = []; // 검색결과 초기화 |
|
245 |
+ |
|
246 |
+ if (error.response) { |
|
247 |
+ alert(error.response.data.message); |
|
248 |
+ } |
|
249 |
+ console.error(error.message); |
|
250 |
+ } |
|
251 |
+ }, |
|
252 |
+ |
|
253 |
+ // 기록유형 전체 선택 여부 변경 |
|
254 |
+ fnChkAllOptions(type) { |
|
255 |
+ switch (type) { |
|
256 |
+ case 'record': |
|
257 |
+ if (this.isChkAllRecord) { |
|
258 |
+ this.searchReqDTO.searchRecord = this.searchRecord.map(item => item.key); |
|
259 |
+ } else { |
|
260 |
+ this.searchReqDTO.searchRecord = []; |
|
261 |
+ } |
|
262 |
+ break; |
|
263 |
+ case 'scope': |
|
264 |
+ if (this.isChkAllScope) { |
|
265 |
+ this.searchReqDTO.searchType = this.searchType.map(item => item.key); |
|
266 |
+ } else { |
|
267 |
+ this.searchReqDTO.searchType = []; |
|
268 |
+ } |
|
269 |
+ break; |
|
270 |
+ } |
|
271 |
+ }, |
|
272 |
+ |
|
273 |
+ // 기록유형 선택 여부 변경 |
|
274 |
+ fnChkOption(type) { |
|
275 |
+ switch (type) { |
|
276 |
+ case 'record': |
|
277 |
+ this.isChkAllRecord = this.searchReqDTO.searchRecord.length === this.searchRecord.length; |
|
278 |
+ break; |
|
279 |
+ case 'scope': |
|
280 |
+ this.isChkAllScope = this.searchReqDTO.searchType.length === this.searchType.length; |
|
281 |
+ break; |
|
282 |
+ } |
|
283 |
+ }, |
|
284 |
+ }, |
|
193 | 285 |
}; |
194 | 286 |
</script> |
--- server/modules/web/server.js
+++ server/modules/web/server.js
... | ... | @@ -89,15 +89,20 @@ |
89 | 89 |
}); |
90 | 90 |
|
91 | 91 |
/** |
92 |
- * @author : 하석형 |
|
93 |
- * @since : 2023.08.24 |
|
92 |
+ * @author : 박정하 |
|
93 |
+ * @since : 2025.03.24 |
|
94 | 94 |
* @dscription : REST API 서버에 데이터 요청 보내기(Proxy) |
95 | 95 |
*/ |
96 | 96 |
webServer.use( |
97 | 97 |
"*.json", |
98 | 98 |
expressProxy(API_SERVER_HOST, { |
99 | 99 |
proxyReqPathResolver: function (request) { |
100 |
- return `${request.params["0"]}.json`; |
|
100 |
+ // 쿼리스트링 여부에 따라 다르게 전송 |
|
101 |
+ if (request.url.split('?')[1]) { |
|
102 |
+ return request.originalUrl; |
|
103 |
+ } else { |
|
104 |
+ return `${request.params["0"]}.json`; |
|
105 |
+ } |
|
101 | 106 |
}, |
102 | 107 |
proxyReqOptDecorator: function (proxyReqOpts, srcReq) { |
103 | 108 |
proxyReqOpts.headers['X-Forwarded-For'] = srcReq.headers['x-forwarded-for'] || srcReq.connection.remoteAddress; |
... | ... | @@ -138,6 +143,6 @@ |
138 | 143 |
webServer.use(function (error, request, response, next) { |
139 | 144 |
const errorCode = !error.statusCode ? 500 : error.statusCode; |
140 | 145 |
response.redirect('/notFound.page'); // 에러 페이지로 유도 |
141 |
- let message = `[Error:${errorCode}] ${request.url}/n ${error.stack}/n`; |
|
146 |
+ let message = `[Error: ${errorCode}]${request.url} / n ${error.stack} / n`; |
|
142 | 147 |
Logger.logging(message); |
143 | 148 |
});(파일 끝에 줄바꿈 문자 없음) |
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?