| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- <template>
- <div class="summernote-wrapper">
- <div ref="editorContainer" class="editor-container">
- <textarea
- ref="summernoteElement"
- :id="editorId"
- :placeholder="placeholder"
- ></textarea>
- </div>
- </div>
- </template>
- <script setup>
- import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
- const props = defineProps({
- modelValue: {
- type: String,
- default: ''
- },
- height: {
- type: Number,
- default: 400
- },
- placeholder: {
- type: String,
- default: '내용을 입력하세요'
- }
- })
- const emit = defineEmits(['update:modelValue'])
- const summernoteElement = ref(null)
- const editorContainer = ref(null)
- const editorId = `summernote-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
- let isInitialized = false
- const initializeSummernote = async () => {
- if (process.client && summernoteElement.value && !isInitialized) {
- try {
- // jQuery와 관련 라이브러리 로드
- const { default: $ } = await import('jquery')
- window.jQuery = window.$ = $
- // Bootstrap CSS를 Summernote 컨테이너에만 적용
- await import('~/assets/scss/summernote-bootstrap-full.scss')
- // Summernote CSS 로드
- await import('summernote/dist/summernote-bs4.min.css')
-
-
- // Bootstrap JS 로드
- await import('bootstrap')
-
- // Summernote JS 로드
- await import('summernote/dist/summernote-bs4.min.js')
-
- // 한국어 언어팩 로드
- try {
- await import('summernote/dist/lang/summernote-ko-KR.min.js')
- } catch (err) {
- console.log('Korean language pack not loaded:', err)
- }
- // DOM 요소가 준비될 때까지 대기
- await nextTick()
- if (summernoteElement.value && $) {
- const $editor = $(summernoteElement.value)
-
- // Summernote 초기화
- $editor.summernote({
- height: props.height,
- placeholder: props.placeholder,
- lang: 'ko-KR',
- toolbar: [
- ['style', ['style']],
- ['font', ['bold', 'italic', 'underline', 'strikethrough', 'clear']],
- ['fontname', ['fontname']],
- ['fontsize', ['fontsize']],
- ['color', ['color']],
- ['para', ['ul', 'ol', 'paragraph']],
- ['table', ['table']],
- ['insert', ['link', 'picture', 'video']],
- ['view', ['fullscreen', 'help']]
- ],
- callbacks: {
- onChange: function(contents) {
- emit('update:modelValue', contents)
- },
- onInit: function() {
- console.log('Summernote initialized successfully')
- if (props.modelValue) {
- $editor.summernote('code', props.modelValue)
- }
- isInitialized = true
- },
- onImageUpload: function(files) {
- console.log('Image upload:', files)
- // 이미지 업로드 처리가 필요한 경우 여기에 구현
- }
- }
- })
-
- console.log('Summernote setup completed')
- }
- } catch (error) {
- console.error('Summernote initialization error:', error)
- isInitialized = false
- }
- }
- }
- const destroySummernote = () => {
- if (process.client && window.$ && summernoteElement.value && isInitialized) {
- try {
- const $editor = window.$(summernoteElement.value)
- if ($editor.length && $editor.summernote) {
- $editor.summernote('destroy')
- isInitialized = false
- console.log('Summernote destroyed')
- }
- } catch (error) {
- console.error('Error destroying Summernote:', error)
- }
- }
- }
- // Props 변경 감지
- watch(() => props.modelValue, (newValue) => {
- if (process.client && window.$ && summernoteElement.value && isInitialized) {
- const $editor = window.$(summernoteElement.value)
- if ($editor.length && $editor.summernote) {
- const currentCode = $editor.summernote('code')
- if (currentCode !== newValue) {
- $editor.summernote('code', newValue || '')
- }
- }
- }
- })
- onMounted(async () => {
- // 약간의 지연을 두고 초기화
- setTimeout(initializeSummernote, 100)
- })
- onBeforeUnmount(() => {
- destroySummernote()
- })
- </script>
- <style scoped>
- .summernote-wrapper {
- width: 100%;
- }
- .editor-container {
- width: 100%;
- min-height: 50px;
- }
- /* Summernote 에디터 스타일 커스터마이징 */
- :deep(.note-editor) {
- border: 1px solid #ddd !important;
- border-radius: 4px !important;
- }
- :deep(.note-editor.note-frame) {
- margin-bottom: 0;
- }
- :deep(.note-editor .note-toolbar) {
- background-color: #f8f9fa;
- border-bottom: 1px solid #ddd;
- padding: 8px;
- }
- :deep(.note-editor .note-editing-area .note-editable) {
- font-family: inherit;
- font-size: 14px;
- line-height: 1.6;
- padding: 15px;
- min-height: 300px;
- }
- :deep(.note-editor.note-airframe .note-editing-area .note-editable),
- :deep(.note-editor.note-frame .note-editing-area .note-editable) {
- padding: 15px;
- }
- /* 로딩 중일 때의 fallback 스타일 */
- textarea {
- width: 100%;
- min-height: 400px;
- padding: 15px;
- border: 1px solid #ddd;
- border-radius: 4px;
- font-family: inherit;
- font-size: 14px;
- line-height: 1.6;
- resize: vertical;
- }
- </style>
|