From 52d520f5e4f9e008b051df5d11c931f0a7f6a46f Mon Sep 17 00:00:00 2001 From: nevermoregb Date: Mon, 24 Mar 2025 14:01:06 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B2=8C=EC=8B=9C=EA=B8=80=20=EC=97=90?= =?UTF-8?q?=EB=94=94=ED=84=B0=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20=EC=A0=9C=EA=B1=B0=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.dev | 1 + .env.mine | 1 + src/components/editor/QEditor.vue | 60 +++++++++++++++++++++++++++---- src/views/board/BoardEdit.vue | 16 ++++++--- src/views/board/BoardWrite.vue | 27 ++++++++++++-- 5 files changed, 90 insertions(+), 15 deletions(-) diff --git a/.env.dev b/.env.dev index 4da29f6..3d83355 100644 --- a/.env.dev +++ b/.env.dev @@ -3,4 +3,5 @@ VITE_DOMAIN = https://192.168.0.251:5173/ VITE_SERVER = https://192.168.0.251:10300/ VITE_API_URL = https://192.168.0.251:10300/api/ VITE_TEST_URL = https://192.168.0.251:10300/test/ +VITE_SERVER_IMG_URL = https://192.168.0.251:10300/upload/img/ VITE_KAKAO_MAP_KEY=6f092e8f45ee81186bb6d8408f66a492 \ No newline at end of file diff --git a/.env.mine b/.env.mine index 07aa984..3eb811c 100644 --- a/.env.mine +++ b/.env.mine @@ -3,4 +3,5 @@ VITE_DOMAIN = http://localhost:5173/ VITE_SERVER = http://localhost:10325/ VITE_API_URL = http://localhost:10325/api/ VITE_TEST_URL = http://localhost:10325/test/ +VITE_SERVER_IMG_URL = http://localhost:10325/upload/img/ VITE_KAKAO_MAP_KEY=6f092e8f45ee81186bb6d8408f66a492 \ No newline at end of file diff --git a/src/components/editor/QEditor.vue b/src/components/editor/QEditor.vue index 86deeba..b805c9d 100644 --- a/src/components/editor/QEditor.vue +++ b/src/components/editor/QEditor.vue @@ -45,7 +45,7 @@ -
+
내용을 확인해주세요.
@@ -71,8 +71,10 @@ const editor = ref(null); // 에디터 DOM 참조 const font = ref('nanum-gothic'); // 기본 폰트 const fontSize = ref('16px'); // 기본 폰트 크기 - const emit = defineEmits(['update:data', 'update:uploadedImgList']); + const emit = defineEmits(['update:data', 'update:uploadedImgList', 'update:deleteImgIndexList']); const uploadedImgList = ref([]); // 에디터에 이미지 첨부시 업데이트 된 파일 인덱스 번호 리스트 + const initImageIndex = ref([]); // 에디터 로드 시 이미지 인덱스 정보 + const deleteImgIndexList = ref([]); // 에디터의 이미지 파일 수정 및 삭제 시 해당 이미지 인덱스 목록 onMounted(() => { // 툴바에서 선택할 수 있는 폰트 목록 설정 @@ -113,16 +115,20 @@ quillInstance.format('size', fontSize.value); }); - // 이미지 첨부 리스트가 변경 되었을때 + // 이미지 추가 항목 체크 watch(uploadedImgList, () => { - console.log(!23); emit('update:uploadedImgList', uploadedImgList.value); - console.log('uploadedImgList.value: ', uploadedImgList.value); + }); + + // 이미지 첨부 리스트가 변경(삭제) 되었을때 + watch(deleteImgIndexList, () => { + emit('update:deleteImgIndexList', deleteImgIndexList.value); }); // 초기 데이터가 있을 경우, HTML 형식으로 삽입 if (props.initialData) { quillInstance.setContents(JSON.parse(props.initialData)); + initCheckImageIndex(); } // 이미지 업로드 기능 처리 @@ -141,6 +147,8 @@ checkForDeletedImages(); // 삭제된 이미지 확인 } }); + + checkDeletedImages(); emit('update:data', quillInstance.getContents()); }); @@ -165,10 +173,12 @@ // 업로드 된 파일 인덱스 (게시글 저장 시 해당 인덱스 번호에 게시글 인덱스를 업데이트) if (uploadImgIdx) { uploadedImgList.value = [...uploadedImgList.value, uploadImgIdx]; + initImageIndex.value = [...initImageIndex.value, uploadImgIdx]; } const baseUrl = $api.defaults.baseURL.replace(/api\/$/, ''); - const fullImageUrl = `${baseUrl}${serverImageUrl.replace(/\\/g, '/')}`; + //const fullImageUrl = `${baseUrl}${serverImageUrl.replace(/\\/g, '/')}`; + const fullImageUrl = `${baseUrl}${serverImageUrl.replace(/\\/g, '/')}?imgIndex=${uploadImgIdx}`; // 이미지 경로에 index 정보 추가 const range = quillInstance.getSelection(); quillInstance.insertEmbed(range.index, 'image', fullImageUrl); // 선택된 위치에 이미지 삽입 @@ -221,7 +231,7 @@ // 삭제된 이미지 확인 function checkForDeletedImages() { - const editorImages = document.querySelectorAll('#editor img'); + const editorImages = document.querySelectorAll('#qEditor img'); const currentImages = new Set(Array.from(editorImages).map(img => img.src)); // 현재 에디터에 있는 이미지들 imageUrls.forEach(url => { @@ -230,5 +240,41 @@ } }); } + + // 초기 에디터 로드 시 이미지 인덱스 정보 추출 + function initCheckImageIndex() { + const editorImages = document.querySelectorAll('#qEditor img'); + const currentImages = new Set(Array.from(editorImages).map(img => img.src)); // 현재 에디터에 있는 이미지들 + + currentImages.forEach(url => { + const index = getImgIndex(url); + if (index) { + initImageIndex.value.push(Number(index)); + } + }); + } + + // 이미지에서 index 정보 추출 + function getImgIndex(url) { + const params = new URLSearchParams(url.split('?')[1]); + return params.get('imgIndex'); + } + + // 에디터 이미지 수정 시 삭제 인덱스 확인 + function checkDeletedImages() { + const editorImages = document.querySelectorAll('#qEditor img'); + const currentImages = new Set(Array.from(editorImages).map(img => img.src)); + + // init 이미지 인덱스와 수정 된 이미지 값을 비교 + const tempDeleteImgIndex = [...initImageIndex.value]; + currentImages.forEach(url => { + const imgIndex = getImgIndex(url); + if (imgIndex) { + const index = tempDeleteImgIndex.indexOf(imgIndex); + tempDeleteImgIndex.splice(index, 1); + } + }); + deleteImgIndexList.value = tempDeleteImgIndex; + } }); diff --git a/src/views/board/BoardEdit.vue b/src/views/board/BoardEdit.vue index 0c51296..ca83d6e 100644 --- a/src/views/board/BoardEdit.vue +++ b/src/views/board/BoardEdit.vue @@ -58,6 +58,7 @@ @update:data="content = $event" @update:imageUrls="imageUrls = $event" @update:uploadedImgList="handleUpdateEditorImg" + @update:deleteImgIndexList="handleDeleteEditorImg" :initialData="content" /> @@ -117,6 +118,7 @@ const isFileValid = ref(true); const delFileIdx = ref([]); // 제외할 기존 첨부파일 ID const editorUploadedImgList = ref([]); + const editorDeleteImgList = ref([]); // 게시물 데이터 로드 const fetchBoardDetails = async () => { @@ -150,6 +152,10 @@ editorUploadedImgList.value = item; }; + const handleDeleteEditorImg = item => { + editorDeleteImgList.value = item; + }; + // 기존 첨부파일명을 노출 const addDisplayFileName = fileInfos => fileInfos.map(file => ({ @@ -231,11 +237,6 @@ titleAlert.value = title.value.trim().length === 0; }; - /** `content` 변경 감지하여 자동 유효성 검사 실행 */ - // watch(content, () => { - // contentAlert.value = $common.isNotValidContent(content); - // }); - // 게시물 수정 const updateBoard = async () => { if (checkValidation()) return; @@ -257,6 +258,11 @@ boardData.editorUploadedImgList = [...editorUploadedImgList.value]; } + // 삭제할 에디터 이미지 인덱스 + if (editorDeleteImgList.value && editorDeleteImgList.value.length > 0) { + boardData.editorDeleteImgList = [...editorDeleteImgList.value]; + } + const fileArray = newFileFilter(attachFiles); const formData = new FormData(); diff --git a/src/views/board/BoardWrite.vue b/src/views/board/BoardWrite.vue index a6710b0..0b4c07b 100644 --- a/src/views/board/BoardWrite.vue +++ b/src/views/board/BoardWrite.vue @@ -92,7 +92,11 @@
- +
내용을 입력해주세요.
@@ -121,7 +125,7 @@ const toastStore = useToastStore(); const categoryList = ref([]); const title = ref(''); - const nickname = ref(""); + const nickname = ref(''); const password = ref(''); const categoryValue = ref(null); const content = ref({ ops: [] }); @@ -139,6 +143,7 @@ const maxSize = 10 * 1024 * 1024; const fileError = ref(''); const editorUploadedImgList = ref([]); + const editorDeleteImgList = ref([]); const fetchCategories = async () => { const response = await axios.get('board/categories'); @@ -159,6 +164,10 @@ editorUploadedImgList.value = item; }; + const handleDeleteEditorImg = item => { + editorDeleteImgList.value = item; + }; + const handleFileUpload = files => { const validFiles = files.filter(file => file.size <= maxSize); if (files.some(file => file.size > maxSize)) { @@ -228,7 +237,14 @@ validateContent(); categoryAlert.value = categoryValue.value == null; - if (titleAlert.value || nicknameAlert.value || passwordAlert.value || contentAlert.value || categoryAlert.value || !isFileValid.value) { + if ( + titleAlert.value || + nicknameAlert.value || + passwordAlert.value || + contentAlert.value || + categoryAlert.value || + !isFileValid.value + ) { return; } @@ -246,6 +262,11 @@ boardData.editorUploadedImgList = [...editorUploadedImgList.value]; } + // 삭제할 에디터 이미지 인덱스 + if (editorDeleteImgList.value && editorDeleteImgList.value.length > 0) { + boardData.editorDeleteImgList = [...editorDeleteImgList.value]; + } + const { data: boardResponse } = await axios.post('board', boardData); const boardId = boardResponse.data; // 첨부파일 업로드 (비동기 병렬 처리)