| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- <template>
- <div class="admin--suneditor-wrapper">
- <div ref="editorElement" class="admin--suneditor"></div>
- </div>
- </template>
- <script setup>
- import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
- import suneditor from 'suneditor'
- import 'suneditor/dist/css/suneditor.min.css'
- import plugins from 'suneditor/src/plugins'
- const props = defineProps({
- modelValue: {
- type: String,
- default: ''
- },
- height: {
- type: String,
- default: '400px'
- },
- placeholder: {
- type: String,
- default: '내용을 입력하세요...'
- }
- })
- const emit = defineEmits(['update:modelValue'])
- const editorElement = ref(null)
- let editorInstance = null
- const { upload } = useApi()
- const { getImageUrl } = useImage()
- onMounted(() => {
- if (editorElement.value) {
- editorInstance = suneditor.create(editorElement.value, {
- plugins: plugins,
- height: props.height,
- buttonList: [
- ['undo', 'redo'],
- ['font', 'fontSize', 'formatBlock'],
- ['bold', 'underline', 'italic', 'strike', 'subscript', 'superscript'],
- ['fontColor', 'hiliteColor'],
- ['removeFormat'],
- ['outdent', 'indent'],
- ['align', 'horizontalRule', 'list', 'table'],
- ['link', 'image', 'video'],
- ['fullScreen', 'showBlocks', 'codeView'],
- ['preview', 'print']
- ],
- font: ['Arial', 'Courier New', 'Georgia', 'Tahoma', 'Trebuchet MS', 'Verdana', 'AudiType'],
- formats: ['p', 'div', 'blockquote', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
- colorList: [
- '#ff0000', '#ff5e00', '#ffe400', '#abf200', '#00d8ff', '#0055ff', '#6600ff', '#ff00dd',
- '#000000', '#ffffff', '#888888', '#cccccc'
- ],
- imageResizing: true,
- imageHeightShow: true,
- imageWidth: '100%',
- placeholder: props.placeholder,
- callBackSave: function (contents) {
- emit('update:modelValue', contents)
- }
- })
- // 커스텀 이미지 업로드 핸들러
- editorInstance.onImageUploadBefore = function (files, info, core) {
- console.log('[SunEditor] onImageUploadBefore 호출, files:', files, 'info:', info)
- // 비동기 파일 업로드 처리
- ;(async () => {
- for (const file of files) {
- const formData = new FormData()
- formData.append('file', file)
- try {
- const { data: uploadData, error } = await upload('/upload/event-file', formData)
- console.log('[SunEditor] 업로드 응답:', uploadData)
- if (uploadData?.success && uploadData?.data?.url) {
- // 전체 URL 생성
- const fullUrl = getImageUrl(uploadData.data.url)
- console.log('[SunEditor] 원본 URL:', uploadData.data.url)
- console.log('[SunEditor] 전체 URL:', fullUrl)
- // 에디터에 직접 이미지 태그 삽입
- const imgTag = `<img src="${fullUrl}" alt="${file.name}" />`
- editorInstance.insertHTML(imgTag)
- console.log('[SunEditor] 이미지 삽입 완료:', imgTag)
- } else {
- console.error('[SunEditor] 이미지 업로드 응답 오류:', uploadData, error)
- }
- } catch (error) {
- console.error('[SunEditor] 이미지 업로드 실패:', error)
- }
- }
- })()
- // 기본 동작 막기
- return false
- }
- // 초기값 설정
- if (props.modelValue) {
- editorInstance.setContents(props.modelValue)
- }
- // 내용 변경 이벤트
- editorInstance.onChange = (contents) => {
- emit('update:modelValue', contents)
- }
- }
- })
- // modelValue 변경 감지
- watch(() => props.modelValue, (newValue) => {
- if (editorInstance && editorInstance.getContents() !== newValue) {
- editorInstance.setContents(newValue || '')
- }
- })
- onBeforeUnmount(() => {
- if (editorInstance) {
- editorInstance.destroy()
- }
- })
- </script>
- <style scoped>
- .admin--suneditor-wrapper {
- width: 100%;
- }
- .admin--suneditor-wrapper :deep(.sun-editor) {
- background: var(--admin-bg-tertiary);
- border: 1px solid var(--admin-border-color);
- border-radius: 6px;
- }
- .admin--suneditor-wrapper :deep(.se-toolbar) {
- background: var(--admin-bg-secondary);
- border-bottom: 1px solid var(--admin-border-color);
- }
- .admin--suneditor-wrapper :deep(.se-btn) {
- color: var(--admin-text-secondary);
- }
- .admin--suneditor-wrapper :deep(.se-btn:hover) {
- background: var(--admin-bg-tertiary);
- color: var(--admin-text-primary);
- }
- .admin--suneditor-wrapper :deep(.se-wrapper) {
- background: var(--admin-bg-tertiary);
- color: var(--admin-text-primary);
- }
- .admin--suneditor-wrapper :deep(.se-wrapper-inner) {
- background: var(--admin-bg-tertiary);
- color: #ffffff;
- }
- .admin--suneditor-wrapper :deep(.se-wrapper-inner.se-wrapper-wysiwyg) {
- background: var(--admin-bg-tertiary);
- color: #ffffff !important;
- }
- /* 에디터 내부 모든 텍스트 요소 - 기본 흰색 */
- .admin--suneditor-wrapper :deep(.se-wrapper-inner p),
- .admin--suneditor-wrapper :deep(.se-wrapper-inner div),
- .admin--suneditor-wrapper :deep(.se-wrapper-inner span),
- .admin--suneditor-wrapper :deep(.se-wrapper-inner h1),
- .admin--suneditor-wrapper :deep(.se-wrapper-inner h2),
- .admin--suneditor-wrapper :deep(.se-wrapper-inner h3),
- .admin--suneditor-wrapper :deep(.se-wrapper-inner h4),
- .admin--suneditor-wrapper :deep(.se-wrapper-inner h5),
- .admin--suneditor-wrapper :deep(.se-wrapper-inner h6),
- .admin--suneditor-wrapper :deep(.se-wrapper-inner li),
- .admin--suneditor-wrapper :deep(.se-wrapper-inner ul),
- .admin--suneditor-wrapper :deep(.se-wrapper-inner ol),
- .admin--suneditor-wrapper :deep(.se-wrapper-inner blockquote),
- .admin--suneditor-wrapper :deep(.se-wrapper-inner pre),
- .admin--suneditor-wrapper :deep(.se-wrapper-inner a),
- .admin--suneditor-wrapper :deep(.se-wrapper-inner strong),
- .admin--suneditor-wrapper :deep(.se-wrapper-inner em),
- .admin--suneditor-wrapper :deep(.se-wrapper-inner code) {
- color: #ffffff;
- }
- .admin--suneditor-wrapper :deep(.se-placeholder) {
- color: var(--admin-text-muted) !important;
- }
- </style>
|