
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
<template>
<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>
<form class="insert-form mb-50">
<dl>
<dd>
<label for="id" class="require">제목</label>
<div class="wfull"><input type="text" id="id" placeholder="제목을 입력하세요."></div>
</dd>
<div class="hr"></div>
<dd>
<label for="year">생산연도</label>
<input type="text" id="year" placeholder="생산연도를 입력하세요">
</dd>
<div class="hr"></div>
<dd>
<label for="address">주소</label>
<div class="wfull"><input type="text" id="address" placeholder="주소를 입력하세요"></div>
</dd>
<div class="hr"></div>
<dd>
<label for="text">내용</label>
<div class="wfull">
<EditorComponent :contents="insertDTO.cn" />
</div>
</dd>
<div class="hr"></div>
<dd>
<label for="category" class="flex align-center">
<p>카테고리</p><button type="button" class="category-add" @click="openModal">추가하기</button>
</label>
<ul class="category">
<li v-for="(category, index) in selectedCtgries" :key="index"> {{ category }} <button type="button" class="cancel" @click="removeCategory(index)"><b>✕</b></button>
</li>
</ul>
</dd>
<div class="hr"></div>
<dd>
<label for="file" class="require">파일</label>
<ul class="wfull">
<li class="flex align-center">
<p>파일첨부</p>
<div class="invalid-feedback"><img :src="erroricon" alt=""><span>첨부파일은 건당 최대 10GB를 초과할 수 없습니다.</span></div>
</li>
<li class="file-insert">
<input type="file" id="fileInput" class="file-input" multiple @change="showFileNames" accept="image/jpeg,image/png,image/gif,image/jpg">
<label for="fileInput" class="file-label mb-20" @dragover.prevent="handleDragOver" @dragleave.prevent="handleDragLeave" @drop.prevent="handleDrop" :class="{ 'drag-over': isDragging }">
<div class="flex-center align-center"><img :src="fileicon" alt="">
<p>파일첨부하기</p>
</div>
<p>파일을 첨부하시려면 이 영역으로 파일을 끌고 오거나 클릭해주세요</p>
</label>
<p class="mb-10">파일목록</p>
<div id="fileNames" class="file-names">
<span v-if="fileNames.length === 0">선택된 파일이 없습니다.</span>
<div v-for="(file, index) in fileNames" :key="index" class="flex-sp-bw mb-5 file-wrap">
<div class="file-name">
<img :src="file.icon" alt="fileicon">
<p>{{ file.name }}</p>
</div>
<button type="button" class="cancel" @click="removeFile(index)"><b>✕</b></button>
</div>
</div>
</li>
</ul>
</dd>
</dl>
</form>
<div class="btn-group flex-center">
<button type="button" class="cancel" @click="fnMoveTo('PicHistorySearch')">취소</button>
<button type="button" class="register" @click="submitForm">등록</button>
</div>
</div>
<CategorySelectModal v-if="isModalOpen" :selectedCtgries="selectedCtgries" @toggleModal="fnToggleModal" />
</template>
<script>
import axios from 'axios';
import apiClient from '../../../resources/api';
import { DoubleLeftOutlined, LeftOutlined, RightOutlined, DoubleRightOutlined } from '@ant-design/icons-vue';
// COMPONENT
import EditorComponent from '../../component/EditorComponent.vue';
import CategorySelectModal from '../../component/modal/CategorySelectModal.vue';
export default {
components: {
DoubleLeftOutlined,
LeftOutlined,
RightOutlined,
DoubleRightOutlined,
EditorComponent,
CategorySelectModal,
},
data() {
return {
// 아이콘 경로
homeicon: 'client/resources/images/icon/home.png',
erroricon: 'client/resources/images/icon/error.png',
righticon: 'client/resources/images/icon/right.png',
fileicon: 'client/resources/images/icon/file.png',
searchicon: 'client/resources/images/icon/search.png',
isModalOpen: false,
isDragging: false,
items: [
{ id: 1, category: '카테고리 1', selected: false },
{ id: 2, category: '카테고리 2', selected: false },
{ id: 3, category: '카테고리 3', selected: false },
],
fileNames: [],
insertDTO: {
sj: null, //제목
cn: null, //내용
adres: null, // 주소
prdctnYear: null, // 생산연도
ty: 'P', // 타입 ( P: 사진, V: 영상 )
ctgryIds: null, // 카테고리 정보
},
selectedFiles: [],
selectedCtgries: [], // 카테고리 목록
};
},
computed: {
filteredItems() {
// This could be modified to support filtering based on searchQuery
return this.items.filter(item =>
item.category.includes(this.searchQuery)
);
}
},
methods: {
// 드래그 앤 드롭 이벤트 핸들러
handleDragOver(event) {
this.isDragging = true;
},
handleDragLeave(event) {
this.isDragging = false;
},
handleDrop(event) {
this.isDragging = false;
const files = event.dataTransfer.files;
if (files.length > 0) {
this.processFiles(files);
}
},
// 파일 업로드 처리 함수
processFiles(files) {
// 파일 타입 검증 (이미지 파일만 허용)
const allowedTypes = ['jpg', 'jpeg', 'png', 'gif'];
const maxSize = 10 * 1024 * 1024 * 1024; // 10GB
for (let i = 0; i < files.length; i++) {
const fileType = files[i].name.split('.').pop().toLowerCase();
if (!allowedTypes.includes(fileType)) {
alert(`${files[i].name} 파일은 허용되지 않는 형식입니다. 이미지 파일(jpg, jpeg, png, gif)만 업로드 가능합니다.`);
return;
}
// 파일 크기 제한 체크 (10GB)
if (files[i].size > maxSize) {
alert(`${files[i].name} 파일이 10GB를 초과합니다.`);
return;
}
}
// 실제 File 객체들을 저장
for (let i = 0; i < files.length; i++) {
this.selectedFiles.push(files[i]);
}
// UI에 표시할 파일 정보 저장
for (let i = 0; i < files.length; i++) {
const file = files[i];
const fileType = file.name.split('.').pop().toLowerCase(); // 파일 확장자 추출
// 파일 타입에 따른 아이콘 선택
let iconPath = this.fileicon; // 기본 아이콘
if (['jpg', 'jpeg', 'png', 'gif'].includes(fileType)) {
iconPath = 'client/resources/images/icon/imgicon.png';
} else if (['pdf'].includes(fileType)) {
iconPath = 'client/resources/images/icon/pdficon.png';
} else if (['xls', 'xlsx'].includes(fileType)) {
iconPath = 'client/resources/images/icon/excelicon.png';
} else if (['hwp'].includes(fileType)) {
iconPath = 'client/resources/images/icon/hwpicon.png';
}
// 파일 이름과 아이콘을 목록에 추가
this.fileNames.push({
name: file.name,
icon: iconPath,
size: this.formatFileSize(file.size)
});
}
},
fnToggleModal(selectedCtgryIds) {
this.isModalOpen = !this.isModalOpen;
if (selectedCtgryIds && selectedCtgryIds.length > 0) {
this.insertDTO.ctgryIds = selectedCtgryIds;
}
},
removeCategory(index) {
// Remove category from the list
this.selectedCtgries.splice(index, 1);
},
openModal() {
this.isModalOpen = true;
},
closeModal() {
this.isModalOpen = false;
},
showFileNames(event) {
const files = event.target.files;
if (files.length > 0) {
this.processFiles(files);
}
},
removeFile(index) {
// UI 목록과 실제 파일 객체 목록에서 모두 제거
this.fileNames.splice(index, 1);
this.selectedFiles.splice(index, 1);
},
formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
},
// 등록
submitForm() {
const vm = this;
// 폼 요소들의 값을 가져오기
const titleInput = document.getElementById('id');
const yearInput = document.getElementById('year');
const addressInput = document.getElementById('address');
// insertDTO 업데이트
vm.insertDTO.sj = titleInput ? titleInput.value : null;
vm.insertDTO.prdctnYear = yearInput ? yearInput.value : null;
vm.insertDTO.adres = addressInput ? addressInput.value : null;
// 유효성 검사
if (!vm.insertDTO.sj) {
alert("제목을 입력해 주세요.");
return;
}
if (vm.selectedFiles.length < 1) {
alert("파일을 1개 이상 첨부해 주세요.");
return;
}
// 데이터 세팅
const formData = new FormData();
// 텍스트 데이터 추가
formData.append('sj', vm.insertDTO.sj);
formData.append('cn', vm.insertDTO.cn || '');
formData.append('adres', vm.insertDTO.adres || '');
formData.append('prdctnYear', vm.insertDTO.prdctnYear || '');
formData.append('ty', vm.insertDTO.ty);
// 카테고리 IDs 추가
if (vm.insertDTO.ctgryIds && vm.insertDTO.ctgryIds.length > 0) {
// 백엔드 요구사항에 따라 조정 필요
vm.insertDTO.ctgryIds.forEach((id, index) => {
formData.append(`ctgryIds[${index}]`, id);
});
}
// 파일 추가
for (let i = 0; i < vm.selectedFiles.length; i++) {
formData.append("multipartFiles", vm.selectedFiles[i]);
}
// API 통신
axios({
url: "/dcry/saveDcry.file",
method: "post",
headers: {
"Content-Type": "multipart/form-data",
},
data: formData,
}).then(response => {
let result = response.data;
let id = result.data.dcryId;
alert("등록 되었습니다.");
// 상세 페이지로 이동
vm.fnMoveTo('PicHistoryDetail', id);
}).catch(error => {
if (error.response) {
alert(error.response.data.message);
} else {
alert("에러가 발생했습니다.");
}
console.error(error.message);
});
},
// 페이지 이동
fnMoveTo(page, id) {
if (id !== null || id !== '') {
this.$router.push({ name: page, query: { id: id } });
} else {
this.$router.push({ name: page });
}
}
}
};
</script>
<style>
.file-label {
border: 2px dashed #ddd;
border-radius: 8px;
padding: 20px;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
}
.file-label:hover {
border-color: #aaa;
background-color: #f9f9f9;
}
.file-label.drag-over {
border-color: #1890ff;
background-color: rgba(24, 144, 255, 0.1);
}
</style>