게시글 에디터 이미지 수정, 제거 로직 추가
This commit is contained in:
parent
a72eb1f81a
commit
52d520f5e4
1
.env.dev
1
.env.dev
@ -3,4 +3,5 @@ VITE_DOMAIN = https://192.168.0.251:5173/
|
|||||||
VITE_SERVER = https://192.168.0.251:10300/
|
VITE_SERVER = https://192.168.0.251:10300/
|
||||||
VITE_API_URL = https://192.168.0.251:10300/api/
|
VITE_API_URL = https://192.168.0.251:10300/api/
|
||||||
VITE_TEST_URL = https://192.168.0.251:10300/test/
|
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
|
VITE_KAKAO_MAP_KEY=6f092e8f45ee81186bb6d8408f66a492
|
||||||
@ -3,4 +3,5 @@ VITE_DOMAIN = http://localhost:5173/
|
|||||||
VITE_SERVER = http://localhost:10325/
|
VITE_SERVER = http://localhost:10325/
|
||||||
VITE_API_URL = http://localhost:10325/api/
|
VITE_API_URL = http://localhost:10325/api/
|
||||||
VITE_TEST_URL = http://localhost:10325/test/
|
VITE_TEST_URL = http://localhost:10325/test/
|
||||||
|
VITE_SERVER_IMG_URL = http://localhost:10325/upload/img/
|
||||||
VITE_KAKAO_MAP_KEY=6f092e8f45ee81186bb6d8408f66a492
|
VITE_KAKAO_MAP_KEY=6f092e8f45ee81186bb6d8408f66a492
|
||||||
@ -45,7 +45,7 @@
|
|||||||
<button class="ql-code-block">Code Block</button>
|
<button class="ql-code-block">Code Block</button>
|
||||||
</div>
|
</div>
|
||||||
<!-- 에디터가 표시될 div -->
|
<!-- 에디터가 표시될 div -->
|
||||||
<div ref="editor"></div>
|
<div id="qEditor" ref="editor"></div>
|
||||||
<!-- Alert 메시지 표시 -->
|
<!-- Alert 메시지 표시 -->
|
||||||
<div class="invalid-feedback" :class="isAlert ? 'd-block' : ''">내용을 확인해주세요.</div>
|
<div class="invalid-feedback" :class="isAlert ? 'd-block' : ''">내용을 확인해주세요.</div>
|
||||||
</div>
|
</div>
|
||||||
@ -71,8 +71,10 @@
|
|||||||
const editor = ref(null); // 에디터 DOM 참조
|
const editor = ref(null); // 에디터 DOM 참조
|
||||||
const font = ref('nanum-gothic'); // 기본 폰트
|
const font = ref('nanum-gothic'); // 기본 폰트
|
||||||
const fontSize = ref('16px'); // 기본 폰트 크기
|
const fontSize = ref('16px'); // 기본 폰트 크기
|
||||||
const emit = defineEmits(['update:data', 'update:uploadedImgList']);
|
const emit = defineEmits(['update:data', 'update:uploadedImgList', 'update:deleteImgIndexList']);
|
||||||
const uploadedImgList = ref([]); // 에디터에 이미지 첨부시 업데이트 된 파일 인덱스 번호 리스트
|
const uploadedImgList = ref([]); // 에디터에 이미지 첨부시 업데이트 된 파일 인덱스 번호 리스트
|
||||||
|
const initImageIndex = ref([]); // 에디터 로드 시 이미지 인덱스 정보
|
||||||
|
const deleteImgIndexList = ref([]); // 에디터의 이미지 파일 수정 및 삭제 시 해당 이미지 인덱스 목록
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 툴바에서 선택할 수 있는 폰트 목록 설정
|
// 툴바에서 선택할 수 있는 폰트 목록 설정
|
||||||
@ -113,16 +115,20 @@
|
|||||||
quillInstance.format('size', fontSize.value);
|
quillInstance.format('size', fontSize.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 이미지 첨부 리스트가 변경 되었을때
|
// 이미지 추가 항목 체크
|
||||||
watch(uploadedImgList, () => {
|
watch(uploadedImgList, () => {
|
||||||
console.log(!23);
|
|
||||||
emit('update:uploadedImgList', uploadedImgList.value);
|
emit('update:uploadedImgList', uploadedImgList.value);
|
||||||
console.log('uploadedImgList.value: ', uploadedImgList.value);
|
});
|
||||||
|
|
||||||
|
// 이미지 첨부 리스트가 변경(삭제) 되었을때
|
||||||
|
watch(deleteImgIndexList, () => {
|
||||||
|
emit('update:deleteImgIndexList', deleteImgIndexList.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 초기 데이터가 있을 경우, HTML 형식으로 삽입
|
// 초기 데이터가 있을 경우, HTML 형식으로 삽입
|
||||||
if (props.initialData) {
|
if (props.initialData) {
|
||||||
quillInstance.setContents(JSON.parse(props.initialData));
|
quillInstance.setContents(JSON.parse(props.initialData));
|
||||||
|
initCheckImageIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 이미지 업로드 기능 처리
|
// 이미지 업로드 기능 처리
|
||||||
@ -141,6 +147,8 @@
|
|||||||
checkForDeletedImages(); // 삭제된 이미지 확인
|
checkForDeletedImages(); // 삭제된 이미지 확인
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
checkDeletedImages();
|
||||||
emit('update:data', quillInstance.getContents());
|
emit('update:data', quillInstance.getContents());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -165,10 +173,12 @@
|
|||||||
// 업로드 된 파일 인덱스 (게시글 저장 시 해당 인덱스 번호에 게시글 인덱스를 업데이트)
|
// 업로드 된 파일 인덱스 (게시글 저장 시 해당 인덱스 번호에 게시글 인덱스를 업데이트)
|
||||||
if (uploadImgIdx) {
|
if (uploadImgIdx) {
|
||||||
uploadedImgList.value = [...uploadedImgList.value, uploadImgIdx];
|
uploadedImgList.value = [...uploadedImgList.value, uploadImgIdx];
|
||||||
|
initImageIndex.value = [...initImageIndex.value, uploadImgIdx];
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseUrl = $api.defaults.baseURL.replace(/api\/$/, '');
|
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();
|
const range = quillInstance.getSelection();
|
||||||
quillInstance.insertEmbed(range.index, 'image', fullImageUrl); // 선택된 위치에 이미지 삽입
|
quillInstance.insertEmbed(range.index, 'image', fullImageUrl); // 선택된 위치에 이미지 삽입
|
||||||
@ -221,7 +231,7 @@
|
|||||||
|
|
||||||
// 삭제된 이미지 확인
|
// 삭제된 이미지 확인
|
||||||
function checkForDeletedImages() {
|
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)); // 현재 에디터에 있는 이미지들
|
const currentImages = new Set(Array.from(editorImages).map(img => img.src)); // 현재 에디터에 있는 이미지들
|
||||||
|
|
||||||
imageUrls.forEach(url => {
|
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>
|
</script>
|
||||||
|
|||||||
@ -58,6 +58,7 @@
|
|||||||
@update:data="content = $event"
|
@update:data="content = $event"
|
||||||
@update:imageUrls="imageUrls = $event"
|
@update:imageUrls="imageUrls = $event"
|
||||||
@update:uploadedImgList="handleUpdateEditorImg"
|
@update:uploadedImgList="handleUpdateEditorImg"
|
||||||
|
@update:deleteImgIndexList="handleDeleteEditorImg"
|
||||||
:initialData="content"
|
:initialData="content"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -117,6 +118,7 @@
|
|||||||
const isFileValid = ref(true);
|
const isFileValid = ref(true);
|
||||||
const delFileIdx = ref([]); // 제외할 기존 첨부파일 ID
|
const delFileIdx = ref([]); // 제외할 기존 첨부파일 ID
|
||||||
const editorUploadedImgList = ref([]);
|
const editorUploadedImgList = ref([]);
|
||||||
|
const editorDeleteImgList = ref([]);
|
||||||
|
|
||||||
// 게시물 데이터 로드
|
// 게시물 데이터 로드
|
||||||
const fetchBoardDetails = async () => {
|
const fetchBoardDetails = async () => {
|
||||||
@ -150,6 +152,10 @@
|
|||||||
editorUploadedImgList.value = item;
|
editorUploadedImgList.value = item;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDeleteEditorImg = item => {
|
||||||
|
editorDeleteImgList.value = item;
|
||||||
|
};
|
||||||
|
|
||||||
// 기존 첨부파일명을 노출
|
// 기존 첨부파일명을 노출
|
||||||
const addDisplayFileName = fileInfos =>
|
const addDisplayFileName = fileInfos =>
|
||||||
fileInfos.map(file => ({
|
fileInfos.map(file => ({
|
||||||
@ -231,11 +237,6 @@
|
|||||||
titleAlert.value = title.value.trim().length === 0;
|
titleAlert.value = title.value.trim().length === 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** `content` 변경 감지하여 자동 유효성 검사 실행 */
|
|
||||||
// watch(content, () => {
|
|
||||||
// contentAlert.value = $common.isNotValidContent(content);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// 게시물 수정
|
// 게시물 수정
|
||||||
const updateBoard = async () => {
|
const updateBoard = async () => {
|
||||||
if (checkValidation()) return;
|
if (checkValidation()) return;
|
||||||
@ -257,6 +258,11 @@
|
|||||||
boardData.editorUploadedImgList = [...editorUploadedImgList.value];
|
boardData.editorUploadedImgList = [...editorUploadedImgList.value];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 삭제할 에디터 이미지 인덱스
|
||||||
|
if (editorDeleteImgList.value && editorDeleteImgList.value.length > 0) {
|
||||||
|
boardData.editorDeleteImgList = [...editorDeleteImgList.value];
|
||||||
|
}
|
||||||
|
|
||||||
const fileArray = newFileFilter(attachFiles);
|
const fileArray = newFileFilter(attachFiles);
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
|
||||||
|
|||||||
@ -92,7 +92,11 @@
|
|||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label class="col-md-2 col-form-label"> 내용 <span class="text-danger">*</span> </label>
|
<label class="col-md-2 col-form-label"> 내용 <span class="text-danger">*</span> </label>
|
||||||
<div class="col-md-12">
|
<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>
|
||||||
<div class="invalid-feedback mt-1" :class="contentAlert ? 'd-block' : 'd-none'">내용을 입력해주세요.</div>
|
<div class="invalid-feedback mt-1" :class="contentAlert ? 'd-block' : 'd-none'">내용을 입력해주세요.</div>
|
||||||
</div>
|
</div>
|
||||||
@ -121,7 +125,7 @@
|
|||||||
const toastStore = useToastStore();
|
const toastStore = useToastStore();
|
||||||
const categoryList = ref([]);
|
const categoryList = ref([]);
|
||||||
const title = ref('');
|
const title = ref('');
|
||||||
const nickname = ref("");
|
const nickname = ref('');
|
||||||
const password = ref('');
|
const password = ref('');
|
||||||
const categoryValue = ref(null);
|
const categoryValue = ref(null);
|
||||||
const content = ref({ ops: [] });
|
const content = ref({ ops: [] });
|
||||||
@ -139,6 +143,7 @@
|
|||||||
const maxSize = 10 * 1024 * 1024;
|
const maxSize = 10 * 1024 * 1024;
|
||||||
const fileError = ref('');
|
const fileError = ref('');
|
||||||
const editorUploadedImgList = ref([]);
|
const editorUploadedImgList = ref([]);
|
||||||
|
const editorDeleteImgList = ref([]);
|
||||||
|
|
||||||
const fetchCategories = async () => {
|
const fetchCategories = async () => {
|
||||||
const response = await axios.get('board/categories');
|
const response = await axios.get('board/categories');
|
||||||
@ -159,6 +164,10 @@
|
|||||||
editorUploadedImgList.value = item;
|
editorUploadedImgList.value = item;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDeleteEditorImg = item => {
|
||||||
|
editorDeleteImgList.value = item;
|
||||||
|
};
|
||||||
|
|
||||||
const handleFileUpload = files => {
|
const handleFileUpload = files => {
|
||||||
const validFiles = files.filter(file => file.size <= maxSize);
|
const validFiles = files.filter(file => file.size <= maxSize);
|
||||||
if (files.some(file => file.size > maxSize)) {
|
if (files.some(file => file.size > maxSize)) {
|
||||||
@ -228,7 +237,14 @@
|
|||||||
validateContent();
|
validateContent();
|
||||||
categoryAlert.value = categoryValue.value == null;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,6 +262,11 @@
|
|||||||
boardData.editorUploadedImgList = [...editorUploadedImgList.value];
|
boardData.editorUploadedImgList = [...editorUploadedImgList.value];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 삭제할 에디터 이미지 인덱스
|
||||||
|
if (editorDeleteImgList.value && editorDeleteImgList.value.length > 0) {
|
||||||
|
boardData.editorDeleteImgList = [...editorDeleteImgList.value];
|
||||||
|
}
|
||||||
|
|
||||||
const { data: boardResponse } = await axios.post('board', boardData);
|
const { data: boardResponse } = await axios.post('board', boardData);
|
||||||
const boardId = boardResponse.data;
|
const boardId = boardResponse.data;
|
||||||
// 첨부파일 업로드 (비동기 병렬 처리)
|
// 첨부파일 업로드 (비동기 병렬 처리)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user