QuillEditor.vue 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. <template>
  2. <div class="quill-editor-wrapper">
  3. <client-only>
  4. <QuillEditor
  5. ref="quillEditor"
  6. v-model:content="content"
  7. contentType="html"
  8. :options="editorOptions"
  9. :style="{ height: `${height + 50}px` }"
  10. @update:content="onContentChange"
  11. />
  12. <template #fallback>
  13. <textarea
  14. :value="modelValue"
  15. @input="$emit('update:modelValue', $event.target.value)"
  16. :placeholder="placeholder"
  17. :style="{ height: `${height}px` }"
  18. class="fallback-textarea"
  19. />
  20. </template>
  21. </client-only>
  22. </div>
  23. </template>
  24. <script setup>
  25. import { ref, watch, computed } from 'vue'
  26. import { QuillEditor } from '@vueup/vue-quill'
  27. import '@vueup/vue-quill/dist/vue-quill.snow.css'
  28. const props = defineProps({
  29. modelValue: {
  30. type: String,
  31. default: ''
  32. },
  33. height: {
  34. type: Number,
  35. default: 400
  36. },
  37. placeholder: {
  38. type: String,
  39. default: '내용을 입력하세요'
  40. }
  41. })
  42. const emit = defineEmits(['update:modelValue'])
  43. const quillEditor = ref(null)
  44. const content = computed({
  45. get: () => props.modelValue,
  46. set: (value) => emit('update:modelValue', value)
  47. })
  48. const editorOptions = {
  49. theme: 'snow',
  50. placeholder: props.placeholder,
  51. modules: {
  52. toolbar: [
  53. [{ 'header': [1, 2, 3, 4, 5, 6, false] }],
  54. ['bold', 'italic', 'underline', 'strike'],
  55. [{ 'color': [] }, { 'background': [] }],
  56. [{ 'font': [] }],
  57. [{ 'align': [] }],
  58. [{ 'list': 'ordered'}, { 'list': 'bullet' }],
  59. [{ 'indent': '-1'}, { 'indent': '+1' }],
  60. ['blockquote', 'code-block'],
  61. ['link', 'image', 'video'],
  62. ['clean']
  63. ]
  64. },
  65. formats: [
  66. 'header', 'bold', 'italic', 'underline', 'strike',
  67. 'color', 'background', 'font', 'align',
  68. 'list', 'bullet', 'indent', 'blockquote', 'code-block',
  69. 'link', 'image', 'video'
  70. ]
  71. }
  72. const onContentChange = (content) => {
  73. emit('update:modelValue', content)
  74. }
  75. watch(() => props.modelValue, (newValue) => {
  76. if (content.value !== newValue) {
  77. content.value = newValue
  78. }
  79. })
  80. </script>
  81. <style scoped>
  82. .quill-editor-wrapper {
  83. width: 100%;
  84. }
  85. .fallback-textarea {
  86. width: 100%;
  87. padding: 15px;
  88. border: 1px solid #ddd;
  89. border-radius: 4px;
  90. font-family: inherit;
  91. font-size: 14px;
  92. line-height: 1.6;
  93. resize: vertical;
  94. }
  95. :deep(.ql-editor) {
  96. font-family: inherit;
  97. font-size: 14px;
  98. line-height: 1.6;
  99. min-height: 300px;
  100. }
  101. :deep(.ql-toolbar) {
  102. border-top: 1px solid #ddd;
  103. border-left: 1px solid #ddd;
  104. border-right: 1px solid #ddd;
  105. background-color: #f8f9fa;
  106. }
  107. :deep(.ql-container) {
  108. border-bottom: 1px solid #ddd;
  109. border-left: 1px solid #ddd;
  110. border-right: 1px solid #ddd;
  111. border-radius: 0 0 4px 4px;
  112. }
  113. :deep(.ql-toolbar:first-child) {
  114. border-radius: 4px 4px 0 0;
  115. }
  116. </style>