SunEditor.vue 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. <template>
  2. <div class="admin--suneditor-wrapper">
  3. <div ref="editorElement" class="admin--suneditor"></div>
  4. </div>
  5. </template>
  6. <script setup>
  7. import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
  8. import suneditor from 'suneditor'
  9. import 'suneditor/dist/css/suneditor.min.css'
  10. import plugins from 'suneditor/src/plugins'
  11. const props = defineProps({
  12. modelValue: {
  13. type: String,
  14. default: ''
  15. },
  16. height: {
  17. type: String,
  18. default: '400px'
  19. },
  20. placeholder: {
  21. type: String,
  22. default: '내용을 입력하세요...'
  23. }
  24. })
  25. const emit = defineEmits(['update:modelValue'])
  26. const editorElement = ref(null)
  27. let editorInstance = null
  28. const { upload } = useApi()
  29. const { getImageUrl } = useImage()
  30. onMounted(() => {
  31. if (editorElement.value) {
  32. editorInstance = suneditor.create(editorElement.value, {
  33. plugins: plugins,
  34. height: props.height,
  35. buttonList: [
  36. ['undo', 'redo'],
  37. ['font', 'fontSize', 'formatBlock'],
  38. ['bold', 'underline', 'italic', 'strike', 'subscript', 'superscript'],
  39. ['fontColor', 'hiliteColor'],
  40. ['removeFormat'],
  41. ['outdent', 'indent'],
  42. ['align', 'horizontalRule', 'list', 'table'],
  43. ['link', 'image', 'video'],
  44. ['fullScreen', 'showBlocks', 'codeView'],
  45. ['preview', 'print']
  46. ],
  47. font: ['Arial', 'Courier New', 'Georgia', 'Tahoma', 'Trebuchet MS', 'Verdana', 'AudiType'],
  48. formats: ['p', 'div', 'blockquote', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
  49. colorList: [
  50. '#ff0000', '#ff5e00', '#ffe400', '#abf200', '#00d8ff', '#0055ff', '#6600ff', '#ff00dd',
  51. '#000000', '#ffffff', '#888888', '#cccccc'
  52. ],
  53. imageResizing: true,
  54. imageHeightShow: true,
  55. imageWidth: '100%',
  56. placeholder: props.placeholder,
  57. callBackSave: function (contents) {
  58. emit('update:modelValue', contents)
  59. }
  60. })
  61. // 커스텀 이미지 업로드 핸들러
  62. editorInstance.onImageUploadBefore = function (files, info, core) {
  63. console.log('[SunEditor] onImageUploadBefore 호출, files:', files, 'info:', info)
  64. // 비동기 파일 업로드 처리
  65. ;(async () => {
  66. for (const file of files) {
  67. const formData = new FormData()
  68. formData.append('file', file)
  69. try {
  70. const { data: uploadData, error } = await upload('/upload/event-file', formData)
  71. console.log('[SunEditor] 업로드 응답:', uploadData)
  72. if (uploadData?.success && uploadData?.data?.url) {
  73. // 전체 URL 생성
  74. const fullUrl = getImageUrl(uploadData.data.url)
  75. console.log('[SunEditor] 원본 URL:', uploadData.data.url)
  76. console.log('[SunEditor] 전체 URL:', fullUrl)
  77. // 에디터에 직접 이미지 태그 삽입
  78. const imgTag = `<img src="${fullUrl}" alt="${file.name}" />`
  79. editorInstance.insertHTML(imgTag)
  80. console.log('[SunEditor] 이미지 삽입 완료:', imgTag)
  81. } else {
  82. console.error('[SunEditor] 이미지 업로드 응답 오류:', uploadData, error)
  83. }
  84. } catch (error) {
  85. console.error('[SunEditor] 이미지 업로드 실패:', error)
  86. }
  87. }
  88. })()
  89. // 기본 동작 막기
  90. return false
  91. }
  92. // 초기값 설정
  93. if (props.modelValue) {
  94. editorInstance.setContents(props.modelValue)
  95. }
  96. // 내용 변경 이벤트
  97. editorInstance.onChange = (contents) => {
  98. emit('update:modelValue', contents)
  99. }
  100. }
  101. })
  102. // modelValue 변경 감지
  103. watch(() => props.modelValue, (newValue) => {
  104. if (editorInstance && editorInstance.getContents() !== newValue) {
  105. editorInstance.setContents(newValue || '')
  106. }
  107. })
  108. onBeforeUnmount(() => {
  109. if (editorInstance) {
  110. editorInstance.destroy()
  111. }
  112. })
  113. </script>
  114. <style scoped>
  115. .admin--suneditor-wrapper {
  116. width: 100%;
  117. }
  118. .admin--suneditor-wrapper :deep(.sun-editor) {
  119. background: var(--admin-bg-tertiary);
  120. border: 1px solid var(--admin-border-color);
  121. border-radius: 6px;
  122. }
  123. .admin--suneditor-wrapper :deep(.se-toolbar) {
  124. background: var(--admin-bg-secondary);
  125. border-bottom: 1px solid var(--admin-border-color);
  126. }
  127. .admin--suneditor-wrapper :deep(.se-btn) {
  128. color: var(--admin-text-secondary);
  129. }
  130. .admin--suneditor-wrapper :deep(.se-btn:hover) {
  131. background: var(--admin-bg-tertiary);
  132. color: var(--admin-text-primary);
  133. }
  134. .admin--suneditor-wrapper :deep(.se-wrapper) {
  135. background: var(--admin-bg-tertiary);
  136. color: var(--admin-text-primary);
  137. }
  138. .admin--suneditor-wrapper :deep(.se-wrapper-inner) {
  139. background: var(--admin-bg-tertiary);
  140. color: #ffffff;
  141. }
  142. .admin--suneditor-wrapper :deep(.se-wrapper-inner.se-wrapper-wysiwyg) {
  143. background: var(--admin-bg-tertiary);
  144. color: #ffffff !important;
  145. }
  146. /* 에디터 내부 모든 텍스트 요소 - 기본 흰색 */
  147. .admin--suneditor-wrapper :deep(.se-wrapper-inner p),
  148. .admin--suneditor-wrapper :deep(.se-wrapper-inner div),
  149. .admin--suneditor-wrapper :deep(.se-wrapper-inner span),
  150. .admin--suneditor-wrapper :deep(.se-wrapper-inner h1),
  151. .admin--suneditor-wrapper :deep(.se-wrapper-inner h2),
  152. .admin--suneditor-wrapper :deep(.se-wrapper-inner h3),
  153. .admin--suneditor-wrapper :deep(.se-wrapper-inner h4),
  154. .admin--suneditor-wrapper :deep(.se-wrapper-inner h5),
  155. .admin--suneditor-wrapper :deep(.se-wrapper-inner h6),
  156. .admin--suneditor-wrapper :deep(.se-wrapper-inner li),
  157. .admin--suneditor-wrapper :deep(.se-wrapper-inner ul),
  158. .admin--suneditor-wrapper :deep(.se-wrapper-inner ol),
  159. .admin--suneditor-wrapper :deep(.se-wrapper-inner blockquote),
  160. .admin--suneditor-wrapper :deep(.se-wrapper-inner pre),
  161. .admin--suneditor-wrapper :deep(.se-wrapper-inner a),
  162. .admin--suneditor-wrapper :deep(.se-wrapper-inner strong),
  163. .admin--suneditor-wrapper :deep(.se-wrapper-inner em),
  164. .admin--suneditor-wrapper :deep(.se-wrapper-inner code) {
  165. color: #ffffff;
  166. }
  167. .admin--suneditor-wrapper :deep(.se-placeholder) {
  168. color: var(--admin-text-muted) !important;
  169. }
  170. </style>