diff --git a/src/App.vue b/src/App.vue index 20d70e5..04bd0bd 100644 --- a/src/App.vue +++ b/src/App.vue @@ -2,6 +2,7 @@ + @@ -10,6 +11,7 @@ import { computed } from 'vue'; import { useRoute } from 'vue-router'; import NormalLayout from './layouts/NormalLayout.vue'; import NoLayout from './layouts/NoLayout.vue'; +import ToastModal from '@c/modal/ToastModal.vue'; const route = useRoute(); diff --git a/src/components/editor/QEditor.vue b/src/components/editor/QEditor.vue index c2776cf..8fb4538 100644 --- a/src/components/editor/QEditor.vue +++ b/src/components/editor/QEditor.vue @@ -79,7 +79,6 @@ onMounted(() => { syntax: true, }, }); - quillInstance.format('font', font.value); quillInstance.format('size', fontSize.value); @@ -91,15 +90,12 @@ onMounted(() => { quillInstance.format('font', font.value); quillInstance.format('size', fontSize.value); }); - - // 이미지 업로드 및 삭제 감지 로직 - // 아직 서버에 실험 안해봄 ***********처리부탁*********** + // 이미지 업로드 let imageUrls = new Set(); quillInstance.getModule('toolbar').addHandler('image', () => { selectLocalImage(); }); - quillInstance.on('text-change', (delta, oldDelta, source) => { emit('update:data', quillInstance.root.innerHTML); delta.ops.forEach(op => { @@ -112,7 +108,7 @@ onMounted(() => { }); }); - function selectLocalImage() { + async function selectLocalImage() { const input = document.createElement('input'); input.setAttribute('type', 'file'); input.setAttribute('accept', 'image/*'); @@ -121,24 +117,33 @@ onMounted(() => { input.onchange = () => { const file = input.files[0]; if (file) { - const reader = new FileReader(); - reader.onload = async (e) => { - const range = quillInstance.getSelection(); - const base64Image = e.target.result; + const formData = new FormData(); + formData.append('file', file); - try { - const serverImageUrl = await uploadImageToServer(base64Image); - quillInstance.insertEmbed(range.index, 'image', serverImageUrl); - imageUrls.add(serverImageUrl); - } catch (error) { - console.error('이미지 업로드 중 오류 발생:', error); - } - }; - reader.readAsDataURL(file); + uploadImageToServer(formData).then(serverImageUrl => { + const baseUrl = $api.defaults.baseURL.replace(/api\/$/, ''); + const fullImageUrl = `${baseUrl}${serverImageUrl.replace(/\\/g, '/')}`; + + const range = quillInstance.getSelection(); + quillInstance.insertEmbed(range.index, 'image', fullImageUrl); + + imageUrls.add(fullImageUrl); + }).catch(e => { + toastStore.onToast('잠시후 다시 시도해주세요.', 'e'); + }); } }; } - + async function uploadImageToServer(formData) { + try { + const response = await $api.post('img/upload', formData, { isFormData: true }); + const imageUrl = response.data.data; + return imageUrl; + } catch (error) { + toastStore.onToast('잠시후 다시 시도해주세요.', 'e'); + throw error; + } + } function checkForDeletedImages() { const editorImages = document.querySelectorAll('#editor img'); @@ -147,33 +152,9 @@ onMounted(() => { imageUrls.forEach(url => { if (!currentImages.has(url)) { imageUrls.delete(url); - removeImageFromServer(url); } }); } - - async function uploadImageToServer(base64Image) { - try { - const response = await $api.post('/img/upload', { - image: base64Image, - }); - return response.data.url; // 서버에서 반환한 이미지 URL - } catch (error) { - console.error('서버 업로드 중 오류 발생:', error); - throw error; - } - } - - async function removeImageFromServer(imageUrl) { - try { - await $api.delete('/img/delete', { - data: { url: imageUrl }, - }); - console.log(`서버에서 이미지 삭제: ${imageUrl}`); - } catch (error) { - console.error('서버 이미지 삭제 중 오류 발생:', error); - } - } }); @@ -185,4 +166,4 @@ onMounted(() => { min-height: 300px; font-family: 'Nanum Gothic', sans-serif; } - + \ No newline at end of file diff --git a/src/components/modal/ToastModal.vue b/src/components/modal/ToastModal.vue new file mode 100644 index 0000000..bf7afe2 --- /dev/null +++ b/src/components/modal/ToastModal.vue @@ -0,0 +1,58 @@ + + + + + 알림 + + + + {{ toastStore.toastMsg }} + + + + + + + diff --git a/src/main.js b/src/main.js index 14f4f3b..12ed27d 100644 --- a/src/main.js +++ b/src/main.js @@ -4,6 +4,7 @@ import piniaPersist from 'pinia-plugin-persist' import App from './App.vue' import router from '@/router' import dayjs from '@p/dayjs' +import ToastModal from '@c/modal/ToastModal.vue'; const pinia = createPinia() pinia.use(piniaPersist) @@ -12,9 +13,9 @@ const app = createApp(App) app.use(router) .use(pinia) .use(dayjs) + .component('ToastModal',ToastModal) .mount('#app') - if (import.meta.env.MODE === "prod") { const console = window.console || {}; console.log = function no_console() { }; // console log 막기 diff --git a/src/stores/toastStore.js b/src/stores/toastStore.js new file mode 100644 index 0000000..e182216 --- /dev/null +++ b/src/stores/toastStore.js @@ -0,0 +1,28 @@ +import { defineStore } from 'pinia'; + +export const useToastStore = defineStore('toastStore', { + state: () => ({ + toastModal: false, + toastMsg: '', + time: 2000, + toastType: 's', + }), + actions: { + onToast(msg = '', type = 's', time = 2000) { + this.toastModal = true; + this.toastMsg = msg; + this.toastType = type; + this.time = time; + + // 시간이 지난 후 토스트 숨기기 + setTimeout(() => { + this.offToast(); + }, this.time); + }, + offToast() { + this.toastModal = false; + this.toastMsg = ''; + this.toastType = 's'; + }, + }, +});