localhost-front/src/components/editor/QEditor.vue
2024-12-21 19:24:35 +09:00

189 lines
6.3 KiB
Vue

<template>
<div>
<!-- 툴바 HTML -->
<div id="toolbar">
<select class="ql-font" v-model="font">
<option value="nanum-gothic">나눔고딕</option>
<option value="d2coding">d2coding</option>
<option value="consolas">consolas</option>
<option value="serif">Serif</option>
<option value="monospace">Monospace</option>
</select>
<select class="ql-size" v-model="fontSize">
<option value="12px">12px</option>
<option value="14px">14px</option>
<option value="16px" selected>16px</option>
<option value="18px">18px</option>
<option value="24px">24px</option>
<option value="32px">32px</option>
<option value="48px">48px</option>
</select>
<button class="ql-bold">B</button>
<button class="ql-italic">I</button>
<button class="ql-underline">U</button>
<button class="ql-header" value="1">H1</button>
<button class="ql-header" value="2">H2</button>
<button class="ql-header" value="3">H3</button>
<button class="ql-header" value="4">H4</button>
<button class="ql-header" value="5">H5</button>
<button class="ql-header" value="6">H6</button>
<button class="ql-list" value="ordered">OL</button>
<button class="ql-list" value="bullet">UL</button>
<button class="ql-align" value="">Left</button>
<button class="ql-align" value="center">Center</button>
<button class="ql-align" value="right">Right</button>
<button class="ql-align" value="justify">Justify</button>
<button class="ql-link">Link</button>
<button class="ql-image">Image</button>
<button class="ql-blockquote">Blockquote</button>
<button class="ql-code-block">Code Block</button>
</div>
<!-- 에디터가 표시될 div -->
<div ref="editor"></div>
</div>
</template>
<script setup>
import Quill from 'quill';
import 'quill/dist/quill.snow.css';
import { onMounted, ref, watch, defineEmits } from 'vue';
import $api from '@api';
const editor = ref(null);
const font = ref('nanum-gothic');
const fontSize = ref('16px');
const emit = defineEmits(['update:data']);
onMounted(() => {
const Font = Quill.import('formats/font');
Font.whitelist = ['nanum-gothic', 'd2coding', 'consolas', 'serif', 'monospace'];
Quill.register(Font, true);
const Size = Quill.import('attributors/style/size');
Size.whitelist = ['12px', '14px', '16px', '18px', '24px', '32px', '48px'];
Quill.register(Size, true);
const quillInstance = new Quill(editor.value, {
theme: 'snow',
placeholder: '내용을 입력해주세요...',
modules: {
toolbar: {
container: '#toolbar',
},
syntax: true,
},
});
quillInstance.format('font', font.value);
quillInstance.format('size', fontSize.value);
quillInstance.on('text-change', () => {
emit('update:data', quillInstance.root.innerHTML);
});
watch([font, fontSize], () => {
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 => {
if (op.insert && typeof op.insert === 'object' && op.insert.image) {
const imageUrl = op.insert.image;
imageUrls.add(imageUrl);
} else if (op.delete) {
checkForDeletedImages();
}
});
});
function selectLocalImage() {
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.click();
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;
try {
const serverImageUrl = await uploadImageToServer(base64Image);
quillInstance.insertEmbed(range.index, 'image', serverImageUrl);
imageUrls.add(serverImageUrl);
} catch (error) {
console.error('이미지 업로드 중 오류 발생:', error);
}
};
reader.readAsDataURL(file);
}
};
}
function checkForDeletedImages() {
const editorImages = document.querySelectorAll('#editor img');
const currentImages = new Set(Array.from(editorImages).map(img => img.src));
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);
}
}
});
</script>
<style>
@import 'quill/dist/quill.snow.css';
.ql-editor {
min-height: 300px;
font-family: 'Nanum Gothic', sans-serif;
}
</style>