게시글 에디터 이미지 수정, 제거 로직 추가

This commit is contained in:
nevermoregb 2025-03-24 14:01:06 +09:00
parent a72eb1f81a
commit 52d520f5e4
5 changed files with 90 additions and 15 deletions

View File

@ -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

View File

@ -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

View File

@ -45,7 +45,7 @@
<button class="ql-code-block">Code Block</button>
</div>
<!-- 에디터가 표시될 div -->
<div ref="editor"></div>
<div id="qEditor" ref="editor"></div>
<!-- Alert 메시지 표시 -->
<div class="invalid-feedback" :class="isAlert ? 'd-block' : ''">내용을 확인해주세요.</div>
</div>
@ -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;
}
});
</script>

View File

@ -58,6 +58,7 @@
@update:data="content = $event"
@update:imageUrls="imageUrls = $event"
@update:uploadedImgList="handleUpdateEditorImg"
@update:deleteImgIndexList="handleDeleteEditorImg"
:initialData="content"
/>
</div>
@ -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();

View File

@ -92,7 +92,11 @@
<div class="mb-4">
<label class="col-md-2 col-form-label"> 내용 <span class="text-danger">*</span> </label>
<div class="col-md-12">
<QEditor @update:data="content = $event" @update:uploadedImgList="handleUpdateEditorImg" />
<QEditor
@update:data="content = $event"
@update:uploadedImgList="handleUpdateEditorImg"
@update:deleteImgIndexList="handleDeleteEditorImg"
/>
</div>
<div class="invalid-feedback mt-1" :class="contentAlert ? 'd-block' : 'd-none'">내용을 입력해주세요.</div>
</div>
@ -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;
// ( )