게시판 글쓰기 다시수정
This commit is contained in:
parent
6526e80879
commit
e8c35bba82
@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
<div class="col-xl-12">
|
<div class="col-xl-12">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<!-- 제목 입력 -->
|
|
||||||
<FormInput
|
<FormInput
|
||||||
title="제목"
|
title="제목"
|
||||||
name="title"
|
name="title"
|
||||||
@ -75,7 +74,7 @@
|
|||||||
내용 <span class="text-danger">*</span>
|
내용 <span class="text-danger">*</span>
|
||||||
</label>
|
</label>
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<QEditor @update:data="updateContent" />
|
<QEditor @update:data="content = $event" />
|
||||||
</div>
|
</div>
|
||||||
<div class="invalid-feedback mt-1" :class="contentAlert ? 'd-block' : 'd-none'">
|
<div class="invalid-feedback mt-1" :class="contentAlert ? 'd-block' : 'd-none'">
|
||||||
내용을 입력해주세요.
|
내용을 입력해주세요.
|
||||||
@ -93,22 +92,22 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { ref, onMounted, getCurrentInstance, watch } from 'vue';
|
||||||
import QEditor from '@c/editor/QEditor.vue';
|
import QEditor from '@c/editor/QEditor.vue';
|
||||||
import FormInput from '@c/input/FormInput.vue';
|
import FormInput from '@c/input/FormInput.vue';
|
||||||
import FormFile from '@c/input/FormFile.vue';
|
import FormFile from '@c/input/FormFile.vue';
|
||||||
import { getCurrentInstance, ref, onMounted } from 'vue';
|
import SaveButton from '@c/button/SaveBtn.vue';
|
||||||
|
import BackButton from '@c/button/BackBtn.vue';
|
||||||
|
import { useToastStore } from '@s/toastStore';
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
import axios from '@api';
|
import axios from '@api';
|
||||||
import SaveButton from '@c/button/SaveBtn.vue';
|
|
||||||
import BackButton from '@c/button/BackBtn.vue'
|
|
||||||
import { useToastStore } from '@s/toastStore';
|
|
||||||
|
|
||||||
const toastStore = useToastStore();
|
const toastStore = useToastStore();
|
||||||
const categoryList = ref([]);
|
const categoryList = ref([]);
|
||||||
const title = ref('');
|
const title = ref('');
|
||||||
const password = ref('');
|
const password = ref('');
|
||||||
const categoryValue = ref(null);
|
const categoryValue = ref(null);
|
||||||
const content = ref('');
|
const content = ref({ ops: [] }); // Delta 데이터 유지
|
||||||
const attachFiles = ref(null);
|
const attachFiles = ref(null);
|
||||||
const isFileValid = ref(true);
|
const isFileValid = ref(true);
|
||||||
|
|
||||||
@ -138,44 +137,43 @@ onMounted(() => {
|
|||||||
fetchCategories();
|
fetchCategories();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 제목 유효성 검사: 공백만 입력하면 경고 유지, 문자 포함 시 정상
|
/** ✅ 제목 유효성 검사 */
|
||||||
const validateTitle = () => {
|
const validateTitle = () => {
|
||||||
titleAlert.value = title.value.trim().length === 0;
|
titleAlert.value = title.value.trim().length === 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 비밀번호 유효성 검사: 공백 입력 방지
|
/** ✅ 비밀번호 유효성 검사 (공백 제거) */
|
||||||
const validatePassword = () => {
|
const validatePassword = () => {
|
||||||
password.value = password.value.replace(/\s/g, ""); // 공백 제거
|
if (categoryValue.value === 300102) {
|
||||||
passwordAlert.value = password.value.length === 0;
|
password.value = password.value.replace(/\s/g, ''); // 공백 제거
|
||||||
|
passwordAlert.value = password.value.length === 0;
|
||||||
|
} else {
|
||||||
|
passwordAlert.value = false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 에디터에서 업데이트된 데이터를 반영하는 함수
|
/** ✅ 내용 유효성 검사 */
|
||||||
const updateContent = (data) => {
|
const validateContent = () => {
|
||||||
let rawText = '';
|
if (!content.value?.ops?.length) {
|
||||||
|
contentAlert.value = true;
|
||||||
if (typeof data === 'object' && data.ops) {
|
return;
|
||||||
rawText = data.ops.map(op => (typeof op.insert === 'string' ? op.insert : '')).join('').trim();
|
|
||||||
} else if (typeof data === 'string') {
|
|
||||||
rawText = data.replace(/(<([^>]+)>)/gi, "").trim();
|
|
||||||
} else {
|
|
||||||
rawText = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
content.value = rawText.length > 0 ? data : "";
|
// 이미지 포함 여부 확인
|
||||||
contentAlert.value = rawText.length === 0;
|
const hasImage = content.value.ops.some(op => op.insert && typeof op.insert === 'object' && op.insert.image);
|
||||||
};
|
// 텍스트 포함 여부 확인
|
||||||
|
const hasText = content.value.ops.some(op => typeof op.insert === 'string' && op.insert.trim().length > 0);
|
||||||
/** 페이지 이동 (목록으로 이동) */
|
|
||||||
const goList = () => {
|
// 텍스트 또는 이미지가 하나라도 있으면 유효한 내용
|
||||||
router.push('/board');
|
contentAlert.value = !(hasText || hasImage);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** ✅ 글쓰기 */
|
||||||
const write = async () => {
|
const write = async () => {
|
||||||
validateTitle();
|
validateTitle();
|
||||||
validatePassword();
|
validatePassword();
|
||||||
|
validateContent();
|
||||||
categoryAlert.value = !categoryValue.value;
|
categoryAlert.value = categoryValue.value == null;
|
||||||
contentAlert.value = content.value.length === 0;
|
|
||||||
|
|
||||||
if (titleAlert.value || passwordAlert.value || contentAlert.value || categoryAlert.value || !isFileValid.value) {
|
if (titleAlert.value || passwordAlert.value || contentAlert.value || categoryAlert.value || !isFileValid.value) {
|
||||||
return;
|
return;
|
||||||
@ -184,7 +182,7 @@ const write = async () => {
|
|||||||
try {
|
try {
|
||||||
const boardData = {
|
const boardData = {
|
||||||
LOCBRDTTL: title.value,
|
LOCBRDTTL: title.value,
|
||||||
LOCBRDCON: $common.deltaAsJson(content.value),
|
LOCBRDCON: JSON.stringify(content.value), // Delta 포맷을 JSON으로 변환
|
||||||
LOCBRDPWD: categoryValue.value === 300102 ? password.value : null,
|
LOCBRDPWD: categoryValue.value === 300102 ? password.value : null,
|
||||||
LOCBRDTYP: categoryValue.value
|
LOCBRDTYP: categoryValue.value
|
||||||
};
|
};
|
||||||
@ -192,6 +190,25 @@ const write = async () => {
|
|||||||
const { data: boardResponse } = await axios.post('board', boardData);
|
const { data: boardResponse } = await axios.post('board', boardData);
|
||||||
const boardId = boardResponse.data;
|
const boardId = boardResponse.data;
|
||||||
|
|
||||||
|
// ✅ 첨부파일 업로드 (비동기 병렬 처리)
|
||||||
|
if (attachFiles.value && attachFiles.value.length > 0) {
|
||||||
|
await Promise.all(attachFiles.value.map(async (file) => {
|
||||||
|
const formData = new FormData();
|
||||||
|
const fileNameWithoutExt = file.name.replace(/\.[^/.]+$/, '');
|
||||||
|
|
||||||
|
formData.append('CMNBRDSEQ', boardId);
|
||||||
|
formData.append('CMNFLEORG', fileNameWithoutExt);
|
||||||
|
formData.append('CMNFLEEXT', file.name.split('.').pop());
|
||||||
|
formData.append('CMNFLESIZ', file.size);
|
||||||
|
formData.append('CMNFLEPAT', 'boardfile');
|
||||||
|
formData.append('file', file);
|
||||||
|
|
||||||
|
await axios.post(`board/${boardId}/attachments`, formData, {
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
toastStore.onToast('게시물이 작성되었습니다.', 's');
|
toastStore.onToast('게시물이 작성되었습니다.', 's');
|
||||||
goList();
|
goList();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -199,4 +216,14 @@ const write = async () => {
|
|||||||
toastStore.onToast('게시물 작성 중 오류가 발생했습니다.', 'e');
|
toastStore.onToast('게시물 작성 중 오류가 발생했습니다.', 'e');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** ✅ 목록으로 이동 */
|
||||||
|
const goList = () => {
|
||||||
|
router.push('/board');
|
||||||
|
};
|
||||||
|
|
||||||
|
/** ✅ `content` 변경 감지하여 자동 유효성 검사 실행 */
|
||||||
|
watch(content, () => {
|
||||||
|
validateContent();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user