인증 관련 토스트 제거 및 브라우저 비밀번호 저장안뜨게
This commit is contained in:
parent
2af99d9b51
commit
627e29ec7a
@ -42,7 +42,7 @@ $api.interceptors.response.use(
|
||||
switch (error.response.status) {
|
||||
case 401:
|
||||
if (!error.config.headers.isLogin) {
|
||||
toastStore.onToast('인증이 필요합니다.', 'e');
|
||||
// toastStore.onToast('인증이 필요합니다.', 'e');
|
||||
}
|
||||
break;
|
||||
case 403:
|
||||
|
||||
@ -14,101 +14,98 @@
|
||||
:placeholder="title"
|
||||
:disabled="disabled"
|
||||
:min="min"
|
||||
autocomplete="off"
|
||||
@focusout="$emit('focusout', modelValue)"
|
||||
@input="handleInput"
|
||||
/>
|
||||
<div class="invalid-feedback" :class="isAlert ? 'd-block' : ''">
|
||||
{{ title }}을 확인해주세요.
|
||||
</div>
|
||||
<div class="invalid-feedback" :class="isAlert ? 'd-block' : ''">{{ title }}을 확인해주세요.</div>
|
||||
<!-- 카테고리 중복 -->
|
||||
<div class="invalid-feedback" :class="isCateAlert ? 'd-block' : ''">
|
||||
카테고리 중복입니다.
|
||||
</div>
|
||||
<div class="invalid-feedback" :class="isCateAlert ? 'd-block' : ''">카테고리 중복입니다.</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
// Props 정의
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '라벨',
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: 'nameplz',
|
||||
required: true,
|
||||
},
|
||||
isEssential: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text',
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
maxlength: {
|
||||
type: Number,
|
||||
default: 30,
|
||||
},
|
||||
isAlert: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isCateAlert : {
|
||||
type :Boolean,
|
||||
default: false,
|
||||
},
|
||||
isLabel : {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
min: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
}
|
||||
});
|
||||
// Props 정의
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '라벨',
|
||||
required: true,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: 'nameplz',
|
||||
required: true,
|
||||
},
|
||||
isEssential: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text',
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
maxlength: {
|
||||
type: Number,
|
||||
default: 30,
|
||||
},
|
||||
isAlert: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isCateAlert: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isLabel: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
min: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false,
|
||||
},
|
||||
});
|
||||
|
||||
// Emits 정의
|
||||
const emits = defineEmits(['update:modelValue', 'focusout', 'update:alert']);
|
||||
// Emits 정의
|
||||
const emits = defineEmits(['update:modelValue', 'focusout', 'update:alert']);
|
||||
|
||||
// 로컬 상태로 사용하기 위한 `inputValue`
|
||||
const inputValue = ref(props.modelValue);
|
||||
|
||||
// 로컬 상태로 사용하기 위한 `inputValue`
|
||||
const inputValue = ref(props.modelValue);
|
||||
// 부모로 데이터 업데이트
|
||||
watch(inputValue, newValue => {
|
||||
emits('update:modelValue', newValue);
|
||||
});
|
||||
|
||||
// 부모로 데이터 업데이트
|
||||
watch(inputValue, (newValue) => {
|
||||
emits('update:modelValue', newValue);
|
||||
});
|
||||
// 초기값 동기화
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
newValue => {
|
||||
if (inputValue.value !== newValue) {
|
||||
inputValue.value = newValue;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// 초기값 동기화
|
||||
watch(() => props.modelValue, (newValue) => {
|
||||
if (inputValue.value !== newValue) {
|
||||
inputValue.value = newValue;
|
||||
}
|
||||
});
|
||||
|
||||
const handleInput = (event) => {
|
||||
const newValue = event.target.value.slice(0, props.maxlength);
|
||||
|
||||
if (newValue.trim() !== '') {
|
||||
emits('update:alert', false);
|
||||
}
|
||||
};
|
||||
const handleInput = event => {
|
||||
const newValue = event.target.value.slice(0, props.maxlength);
|
||||
|
||||
if (newValue.trim() !== '') {
|
||||
emits('update:alert', false);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@ -26,9 +26,13 @@
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
autocomplete="off"
|
||||
v-model="password"
|
||||
placeholder="비밀번호 입력"
|
||||
@input="password = password.replace(/\s/g, '')"
|
||||
@input="
|
||||
password = password.replace(/\s/g, '');
|
||||
inputCheck();
|
||||
"
|
||||
/>
|
||||
<button class="btn btn-primary" @click="submitPassword">확인</button>
|
||||
</div>
|
||||
@ -131,8 +135,9 @@
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
||||
import { useToastStore } from '@s/toastStore';
|
||||
import axios from '@api';
|
||||
import LoadingSpinner from "@v/LoadingPage.vue";
|
||||
import LoadingSpinner from '@v/LoadingPage.vue';
|
||||
const isLoading = ref(true);
|
||||
// 게시물 데이터 상태
|
||||
const profileName = ref('');
|
||||
@ -151,6 +156,7 @@
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const userStore = useUserInfoStore();
|
||||
const toastStore = useToastStore();
|
||||
const currentBoardId = ref(Number(route.params.id));
|
||||
const unknown = computed(() => profileName.value === '익명');
|
||||
const currentUserId = computed(() => userStore.user.id); // 현재 로그인한 사용자 id
|
||||
@ -224,6 +230,9 @@
|
||||
navigateLastPage: 1,
|
||||
});
|
||||
|
||||
const inputCheck = () => {
|
||||
passwordAlert.value = '';
|
||||
};
|
||||
// 게시물 상세 데이터 불러오기
|
||||
const fetchBoardDetails = async () => {
|
||||
try {
|
||||
@ -568,6 +577,7 @@
|
||||
const submitPassword = async () => {
|
||||
if (!password.value.trim()) {
|
||||
passwordAlert.value = '비밀번호를 입력해주세요.';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -576,7 +586,7 @@
|
||||
LOCBRDPWD: password.value,
|
||||
LOCBRDSEQ: currentBoardId.value,
|
||||
});
|
||||
|
||||
console.log('response: ', response);
|
||||
if (response.data.code === 200 && response.data.data === true) {
|
||||
password.value = '';
|
||||
isPassword.value = false;
|
||||
@ -591,18 +601,7 @@
|
||||
passwordAlert.value = '비밀번호가 일치하지 않습니다.';
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.reponse && error.reponse.status === 401) passwordAlert.value = '비밀번호가 일치하지 않습니다.';
|
||||
// if (error.response) {
|
||||
// if (error.response.status === 401) {
|
||||
// passwordAlert.value = '비밀번호가 일치하지 않습니다.';
|
||||
// } else {
|
||||
// passwordAlert.value = error.response.data?.message || '서버 오류가 발생했습니다.';
|
||||
// }
|
||||
// } else if (error.request) {
|
||||
// passwordAlert.value = '네트워크 오류가 발생했습니다. 다시 시도해주세요.';
|
||||
// } else {
|
||||
// passwordAlert.value = '요청 중 알 수 없는 오류가 발생했습니다.';
|
||||
// }
|
||||
if (error.response && error.response.status === 401) passwordAlert.value = '비밀번호가 일치하지 않습니다.';
|
||||
}
|
||||
};
|
||||
|
||||
@ -664,7 +663,7 @@
|
||||
});
|
||||
|
||||
if (response.data.code === 200) {
|
||||
alert('게시물이 삭제되었습니다.');
|
||||
toastStore.onToast('게시물이 삭제되었습니다.');
|
||||
router.push({ name: 'BoardList' });
|
||||
} else {
|
||||
alert('삭제 실패: ' + response.data.message);
|
||||
|
||||
@ -23,11 +23,7 @@
|
||||
<div class="mb-4 d-flex align-items-center">
|
||||
<label class="col-md-2 col-form-label">카테고리 <span class="text-danger">*</span></label>
|
||||
<div class="d-flex flex-wrap align-items-center mt-3 ms-1">
|
||||
<div
|
||||
v-for="(category, index) in categoryList"
|
||||
:key="index"
|
||||
class="form-check me-3"
|
||||
>
|
||||
<div v-for="(category, index) in categoryList" :key="index" class="form-check me-3">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
@ -41,9 +37,7 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="invalid-feedback" :class="categoryAlert ? 'd-block' : 'd-none'">
|
||||
카테고리를 선택해주세요.
|
||||
</div>
|
||||
<div class="invalid-feedback" :class="categoryAlert ? 'd-block' : 'd-none'">카테고리를 선택해주세요.</div>
|
||||
</div>
|
||||
|
||||
<!-- 비밀번호 필드 (익명게시판 선택 시 활성화) -->
|
||||
@ -52,6 +46,7 @@
|
||||
title="비밀번호"
|
||||
name="pw"
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
:is-essential="true"
|
||||
:is-alert="passwordAlert"
|
||||
v-model="password"
|
||||
@ -74,7 +69,11 @@
|
||||
<p v-if="fileError" class="text-danger">{{ fileError }}</p>
|
||||
|
||||
<ul class="list-group mt-2" v-if="attachFiles.length">
|
||||
<li v-for="(file, index) in attachFiles" :key="index" class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<li
|
||||
v-for="(file, index) in attachFiles"
|
||||
:key="index"
|
||||
class="list-group-item d-flex justify-content-between align-items-center"
|
||||
>
|
||||
{{ file.name }}
|
||||
<button class="close-btn" @click="removeFile(index)">✖</button>
|
||||
</li>
|
||||
@ -82,15 +81,11 @@
|
||||
|
||||
<!-- 내용 입력 (에디터) -->
|
||||
<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">
|
||||
<QEditor @update:data="content = $event" />
|
||||
</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 class="mb-4 d-flex justify-content-end">
|
||||
@ -104,162 +99,162 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, getCurrentInstance, watch, computed } from 'vue';
|
||||
import QEditor from '@c/editor/QEditor.vue';
|
||||
import FormInput from '@c/input/FormInput.vue';
|
||||
import FormFile from '@c/input/FormFile.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 axios from '@api';
|
||||
import { ref, onMounted, getCurrentInstance, watch, computed } from 'vue';
|
||||
import QEditor from '@c/editor/QEditor.vue';
|
||||
import FormInput from '@c/input/FormInput.vue';
|
||||
import FormFile from '@c/input/FormFile.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 axios from '@api';
|
||||
|
||||
const toastStore = useToastStore();
|
||||
const categoryList = ref([]);
|
||||
const title = ref('');
|
||||
const password = ref('');
|
||||
const categoryValue = ref(null);
|
||||
const content = ref({ ops: [] });
|
||||
const isFileValid = ref(true);
|
||||
const toastStore = useToastStore();
|
||||
const categoryList = ref([]);
|
||||
const title = ref('');
|
||||
const password = ref('');
|
||||
const categoryValue = ref(null);
|
||||
const content = ref({ ops: [] });
|
||||
const isFileValid = ref(true);
|
||||
|
||||
const titleAlert = ref(false);
|
||||
const passwordAlert = ref(false);
|
||||
const contentAlert = ref(false);
|
||||
const categoryAlert = ref(false);
|
||||
const attachFilesAlert = ref(false);
|
||||
const titleAlert = ref(false);
|
||||
const passwordAlert = ref(false);
|
||||
const contentAlert = ref(false);
|
||||
const categoryAlert = ref(false);
|
||||
const attachFilesAlert = ref(false);
|
||||
|
||||
const attachFiles = ref([]);
|
||||
const maxFiles = 5;
|
||||
const maxSize = 10 * 1024 * 1024;
|
||||
const fileError = ref('');
|
||||
const attachFiles = ref([]);
|
||||
const maxFiles = 5;
|
||||
const maxSize = 10 * 1024 * 1024;
|
||||
const fileError = ref('');
|
||||
|
||||
const fetchCategories = async () => {
|
||||
try {
|
||||
const response = await axios.get('board/categories');
|
||||
categoryList.value = response.data.data;
|
||||
const freeCategory = categoryList.value.find(category => category.CMNCODNAM === '자유');
|
||||
if (freeCategory) {
|
||||
categoryValue.value = freeCategory.CMNCODVAL;
|
||||
const fetchCategories = async () => {
|
||||
try {
|
||||
const response = await axios.get('board/categories');
|
||||
categoryList.value = response.data.data;
|
||||
const freeCategory = categoryList.value.find(category => category.CMNCODNAM === '자유');
|
||||
if (freeCategory) {
|
||||
categoryValue.value = freeCategory.CMNCODVAL;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('카테고리 불러오기 오류:', error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('카테고리 불러오기 오류:', error);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchCategories();
|
||||
});
|
||||
onMounted(() => {
|
||||
fetchCategories();
|
||||
});
|
||||
|
||||
const fileCount = computed(() => attachFiles.value.length);
|
||||
const fileCount = computed(() => attachFiles.value.length);
|
||||
|
||||
const handleFileUpload = (files) => {
|
||||
const validFiles = files.filter(file => file.size <= maxSize);
|
||||
if (files.some(file => file.size > maxSize)) {
|
||||
fileError.value = '파일 크기가 10MB를 초과할 수 없습니다.';
|
||||
return;
|
||||
}
|
||||
if (attachFiles.value.length + validFiles.length > maxFiles) {
|
||||
fileError.value = `최대 ${maxFiles}개의 파일만 업로드할 수 있습니다.`;
|
||||
return;
|
||||
}
|
||||
fileError.value = '';
|
||||
attachFiles.value = [...attachFiles.value, ...validFiles].slice(0, maxFiles);
|
||||
};
|
||||
|
||||
const removeFile = (index) => {
|
||||
attachFiles.value.splice(index, 1);
|
||||
if (attachFiles.value.length <= maxFiles) {
|
||||
const handleFileUpload = files => {
|
||||
const validFiles = files.filter(file => file.size <= maxSize);
|
||||
if (files.some(file => file.size > maxSize)) {
|
||||
fileError.value = '파일 크기가 10MB를 초과할 수 없습니다.';
|
||||
return;
|
||||
}
|
||||
if (attachFiles.value.length + validFiles.length > maxFiles) {
|
||||
fileError.value = `최대 ${maxFiles}개의 파일만 업로드할 수 있습니다.`;
|
||||
return;
|
||||
}
|
||||
fileError.value = '';
|
||||
}
|
||||
};
|
||||
attachFiles.value = [...attachFiles.value, ...validFiles].slice(0, maxFiles);
|
||||
};
|
||||
|
||||
watch(attachFiles, () => {
|
||||
isFileValid.value = attachFiles.value.length <= maxFiles;
|
||||
});
|
||||
const removeFile = index => {
|
||||
attachFiles.value.splice(index, 1);
|
||||
if (attachFiles.value.length <= maxFiles) {
|
||||
fileError.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
const validateTitle = () => {
|
||||
titleAlert.value = title.value.trim().length === 0;
|
||||
};
|
||||
watch(attachFiles, () => {
|
||||
isFileValid.value = attachFiles.value.length <= maxFiles;
|
||||
});
|
||||
|
||||
const validatePassword = () => {
|
||||
if (categoryValue.value === 300102) {
|
||||
password.value = password.value.replace(/\s/g, ''); // 공백 제거
|
||||
passwordAlert.value = password.value.length === 0;
|
||||
} else {
|
||||
passwordAlert.value = false;
|
||||
}
|
||||
};
|
||||
const validateTitle = () => {
|
||||
titleAlert.value = title.value.trim().length === 0;
|
||||
};
|
||||
|
||||
const validateContent = () => {
|
||||
if (!content.value?.ops?.length) {
|
||||
contentAlert.value = true;
|
||||
return;
|
||||
}
|
||||
const validatePassword = () => {
|
||||
if (categoryValue.value === 300102) {
|
||||
password.value = password.value.replace(/\s/g, ''); // 공백 제거
|
||||
passwordAlert.value = password.value.length === 0;
|
||||
} else {
|
||||
passwordAlert.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 이미지 포함 여부 확인
|
||||
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);
|
||||
|
||||
// 텍스트 또는 이미지가 하나라도 있으면 유효한 내용
|
||||
contentAlert.value = !(hasText || hasImage);
|
||||
};
|
||||
|
||||
/** 글쓰기 */
|
||||
const write = async () => {
|
||||
validateTitle();
|
||||
validatePassword();
|
||||
validateContent();
|
||||
categoryAlert.value = categoryValue.value == null;
|
||||
|
||||
if (titleAlert.value || passwordAlert.value || contentAlert.value || categoryAlert.value || !isFileValid.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const boardData = {
|
||||
LOCBRDTTL: title.value,
|
||||
LOCBRDCON: JSON.stringify(content.value), // Delta 포맷을 JSON으로 변환
|
||||
LOCBRDPWD: categoryValue.value === 300102 ? password.value : null,
|
||||
LOCBRDTYP: categoryValue.value
|
||||
};
|
||||
|
||||
const { data: boardResponse } = await axios.post('board', boardData);
|
||||
const boardId = boardResponse.data;
|
||||
// 첨부파일 업로드 (비동기 병렬 처리)
|
||||
if (attachFiles.value && attachFiles.value.length > 0) {
|
||||
await Promise.all(attachFiles.value.map(async (file) => {
|
||||
console.log(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('file', file); // 📌 실제 파일 추가
|
||||
|
||||
await axios.post(`board/${boardId}/attachments`, formData,
|
||||
{ isFormData : true }
|
||||
);
|
||||
}));
|
||||
const validateContent = () => {
|
||||
if (!content.value?.ops?.length) {
|
||||
contentAlert.value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
toastStore.onToast('게시물이 작성되었습니다.', 's');
|
||||
goList();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toastStore.onToast('게시물 작성 중 오류가 발생했습니다.', 'e');
|
||||
}
|
||||
};
|
||||
// 이미지 포함 여부 확인
|
||||
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);
|
||||
};
|
||||
|
||||
/** `content` 변경 감지하여 자동 유효성 검사 실행 */
|
||||
watch(content, () => {
|
||||
validateContent();
|
||||
});
|
||||
/** 글쓰기 */
|
||||
const write = async () => {
|
||||
validateTitle();
|
||||
validatePassword();
|
||||
validateContent();
|
||||
categoryAlert.value = categoryValue.value == null;
|
||||
|
||||
if (titleAlert.value || passwordAlert.value || contentAlert.value || categoryAlert.value || !isFileValid.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const boardData = {
|
||||
LOCBRDTTL: title.value,
|
||||
LOCBRDCON: JSON.stringify(content.value), // Delta 포맷을 JSON으로 변환
|
||||
LOCBRDPWD: categoryValue.value === 300102 ? password.value : null,
|
||||
LOCBRDTYP: categoryValue.value,
|
||||
};
|
||||
|
||||
const { data: boardResponse } = await axios.post('board', boardData);
|
||||
const boardId = boardResponse.data;
|
||||
// 첨부파일 업로드 (비동기 병렬 처리)
|
||||
if (attachFiles.value && attachFiles.value.length > 0) {
|
||||
await Promise.all(
|
||||
attachFiles.value.map(async file => {
|
||||
console.log(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('file', file); // 📌 실제 파일 추가
|
||||
|
||||
await axios.post(`board/${boardId}/attachments`, formData, { isFormData: true });
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
toastStore.onToast('게시물이 작성되었습니다.', 's');
|
||||
goList();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toastStore.onToast('게시물 작성 중 오류가 발생했습니다.', 'e');
|
||||
}
|
||||
};
|
||||
|
||||
/** 목록으로 이동 */
|
||||
const goList = () => {
|
||||
router.push('/board');
|
||||
};
|
||||
|
||||
/** `content` 변경 감지하여 자동 유효성 검사 실행 */
|
||||
watch(content, () => {
|
||||
validateContent();
|
||||
});
|
||||
</script>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user