
--- client/resources/css/user/sub.css
+++ client/resources/css/user/sub.css
... | ... | @@ -194,7 +194,7 @@ |
194 | 194 |
} |
195 | 195 |
} |
196 | 196 |
|
197 |
- |
|
197 |
+ |
|
198 | 198 |
} |
199 | 199 |
|
200 | 200 |
/* 카테고리 */ |
... | ... | @@ -343,7 +343,7 @@ |
343 | 343 |
overflow-y: auto; |
344 | 344 |
overflow-x: hidden; |
345 | 345 |
} |
346 |
- |
|
346 |
+ |
|
347 | 347 |
} |
348 | 348 |
.mark { |
349 | 349 |
width: fit-content; |
... | ... | @@ -653,7 +653,7 @@ |
653 | 653 |
font-size: 14px; |
654 | 654 |
font-family: "Pretendard-EL"; |
655 | 655 |
} |
656 |
- |
|
656 |
+ |
|
657 | 657 |
} |
658 | 658 |
|
659 | 659 |
} |
... | ... | @@ -917,14 +917,14 @@ |
917 | 917 |
height: 120px; |
918 | 918 |
margin: 0 auto; |
919 | 919 |
.swiper-slide{ |
920 |
- background-image: linear-gradient(#fff, #fff), |
|
920 |
+ background-image: linear-gradient(#fff, #fff), |
|
921 | 921 |
linear-gradient(-45deg, #fff, #fff); |
922 | 922 |
background-origin: border-box; |
923 | 923 |
background-clip: content-box, border-box; |
924 | 924 |
border: 3px solid transparent; |
925 | 925 |
} |
926 | 926 |
.swiper-slide:hover{ |
927 |
- background-image: linear-gradient(#fff, #fff), |
|
927 |
+ background-image: linear-gradient(#fff, #fff), |
|
928 | 928 |
linear-gradient(-45deg, #ca3e49, #3d355d); |
929 | 929 |
} |
930 | 930 |
.swiper-wrapper { |
... | ... | @@ -936,7 +936,7 @@ |
936 | 936 |
|
937 | 937 |
} |
938 | 938 |
|
939 |
- input{position: absolute; right: 6px; top: 6px;} |
|
939 |
+ input{position: absolute; right: 6px; top: 6px; z-index: 5;} |
|
940 | 940 |
} |
941 | 941 |
} |
942 | 942 |
|
... | ... | @@ -1142,7 +1142,7 @@ |
1142 | 1142 |
overflow-y: auto; |
1143 | 1143 |
overflow-x: hidden; /* Optional: Prevent horizontal scroll */ |
1144 | 1144 |
} |
1145 |
- tbody tr{ |
|
1145 |
+ tbody tr{ |
|
1146 | 1146 |
table-layout: fixed; display: table;width: 100%; |
1147 | 1147 |
td{cursor: pointer;} |
1148 | 1148 |
} |
--- client/views/pages/bbsDcry/photo/PicHistoryDetail.vue
+++ client/views/pages/bbsDcry/photo/PicHistoryDetail.vue
... | ... | @@ -35,10 +35,7 @@ |
35 | 35 |
<div class="thumbnail"> |
36 | 36 |
<swiper @swiper="setThumbsSwiper" :spaceBetween="20" :slidesPerView="4" :freeMode="true" :watchSlidesProgress="true" :modules="modules" :navigation="true" class="mySwiper"> |
37 | 37 |
<swiper-slide v-for="(item, idx) of findResult.files" :key="idx"> |
38 |
- <div class="checkbox-wrapper" @click.stop> |
|
39 |
- <input type="checkbox" :id="'photo_' + idx" :value="item.fileId" v-model="selectedFiles" /> |
|
40 |
- <label :for="'photo_' + idx"></label> |
|
41 |
- </div> |
|
38 |
+ <input type="checkbox" :id="'photo_' + idx" :value="item.fileId" v-model="selectedFiles" @click.stop /> |
|
42 | 39 |
<img :src="item.filePath" :alt="item.fileNm" /> |
43 | 40 |
</swiper-slide> |
44 | 41 |
</swiper> |
... | ... | @@ -198,15 +195,12 @@ |
198 | 195 |
if (type === 'selected') { |
199 | 196 |
fileList = this.selectedFiles; |
200 | 197 |
} else if (type === 'all') { |
201 |
- if (this.findResult.files.length == 1) { |
|
202 |
- fileList = this.findResult.files[0].fileId; |
|
203 |
- } else { |
|
204 |
- fileList = this.findResult.files.map(file => file.fileId); |
|
205 |
- } |
|
198 |
+ fileList = this.findResult.files.map(file => file.fileId); |
|
206 | 199 |
} |
207 | 200 |
|
208 | 201 |
let isMultiple = fileList.length > 1; |
209 | 202 |
let fileIds = isMultiple ? fileList : fileList[0]; |
203 |
+ |
|
210 | 204 |
const response = isMultiple ? await multiFileDownloadProc(fileIds) : await fileDownloadProc(fileIds); |
211 | 205 |
|
212 | 206 |
// 파일명 추출 부분 수정 |
--- client/views/pages/bbsDcry/photo/PicHistoryInsert.vue
+++ client/views/pages/bbsDcry/photo/PicHistoryInsert.vue
... | ... | @@ -240,22 +240,45 @@ |
240 | 240 |
const allowedTypes = ['jpg', 'jpeg', 'png', 'gif']; // 이미지 파일만 허용 |
241 | 241 |
const maxSize = 10 * 1024 * 1024 * 1024; // 10GB |
242 | 242 |
|
243 |
+ // 중복 파일 확인을 위한 Map (키: 파일명+확장자(소문자), 값: 원본 파일명) |
|
244 |
+ const existingFilesMap = new Map(); |
|
245 |
+ |
|
246 |
+ // 기존 업로드된 파일 정보 추가 |
|
247 |
+ this.requestDTO.files.forEach(file => { |
|
248 |
+ const fullName = file.fileNm.toLowerCase(); |
|
249 |
+ existingFilesMap.set(fullName, file.fileNm); |
|
250 |
+ }); |
|
251 |
+ |
|
252 |
+ // 새로 추가된 파일 정보 추가 |
|
253 |
+ this.multipartFiles.forEach(file => { |
|
254 |
+ existingFilesMap.set(file.name.toLowerCase(), file.name); |
|
255 |
+ }); |
|
256 |
+ |
|
243 | 257 |
for (let file of files) { |
244 |
- const fileType = file.name.split('.').pop().toLowerCase(); |
|
258 |
+ const fileNameLower = file.name.toLowerCase(); |
|
259 |
+ const fileExtension = fileNameLower.split('.').pop(); |
|
260 |
+ |
|
261 |
+ // 파일명+확장자 중복 검증 (대소문자 무시하고 비교) |
|
262 |
+ if (existingFilesMap.has(fileNameLower)) { |
|
263 |
+ alert("파일 목록에 이미 동일한 파일 명을 가진 파일이 있습니다. 동일한 이름을 가진 파일은 업로드할 수 없습니다."); |
|
264 |
+ continue; |
|
265 |
+ } |
|
245 | 266 |
|
246 | 267 |
// 파일 타입 검증 |
247 |
- if (!allowedTypes.includes(fileType)) { |
|
248 |
- alert(`${file.name} 파일은 허용되지 않는 형식입니다. 이미지 파일(jpg, jpeg, png, gif)만 업로드 가능합니다.`); |
|
249 |
- return; |
|
268 |
+ if (!allowedTypes.includes(fileExtension)) { |
|
269 |
+ alert("이미지 파일(jpg, jpeg, png, gif)만 업로드 가능합니다."); |
|
270 |
+ continue; |
|
250 | 271 |
} |
251 | 272 |
|
252 | 273 |
// 파일 크기 제한 검증 |
253 | 274 |
if (file.size > maxSize) { |
254 | 275 |
alert(`${file.name} 파일이 10GB를 초과합니다.`); |
255 |
- return; |
|
276 |
+ continue; |
|
256 | 277 |
} |
257 | 278 |
|
279 |
+ // 파일명+확장자 조합이 중복되지 않으면 추가 |
|
258 | 280 |
this.multipartFiles.push(file); |
281 |
+ existingFilesMap.set(fileNameLower, file.name); |
|
259 | 282 |
} |
260 | 283 |
}, |
261 | 284 |
|
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?