RichTextEditor.vue 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. <template>
  2. <div class="rich-text-editor-container">
  3. <Toolbar
  4. class="rich-text-toolbar"
  5. :editor="editorRef"
  6. :defaultConfig="toolbarConfig"
  7. mode="default"
  8. />
  9. <Editor
  10. class="rich-text-editor"
  11. :style="{ height: '300px', overflowY: 'hidden' }"
  12. v-model="localValue"
  13. :defaultConfig="editorConfig"
  14. mode="default"
  15. @onCreated="handleCreated"
  16. @customPaste="customPaste"
  17. />
  18. </div>
  19. </template>
  20. <script setup>
  21. import { ref, defineProps, defineEmits, watch } from 'vue';
  22. import '@wangeditor/editor/dist/css/style.css';
  23. import { onBeforeUnmount, shallowRef } from 'vue';
  24. import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
  25. import axios from 'axios';
  26. const props = defineProps({
  27. modelValue: String
  28. });
  29. const emits = defineEmits(['update:modelValue']);
  30. const localValue = ref(props.modelValue);
  31. const editorRef = shallowRef();
  32. const toolbarConfig = ref();
  33. const editorConfig = ref({ placeholder: '请输入内容...', MENU_CONF: {} });
  34. // 自定义图片上传
  35. editorConfig.value.MENU_CONF['uploadImage'] = {
  36. async customUpload(file, insertFn) {
  37. try {
  38. const formData = new FormData();
  39. formData.append('image', file);
  40. const response = await axios.post('http://127.0.0.1:5000/upload-image', formData, {
  41. headers: {
  42. 'Content-Type': 'multipart/form-data'
  43. }
  44. });
  45. const imageUrl = response.data.imageUrl;
  46. insertFn(imageUrl, 'img');
  47. } catch (error) {
  48. console.error('图片上传失败:', error);
  49. }
  50. },
  51. };
  52. // 自定义视频上传
  53. editorConfig.value.MENU_CONF['uploadVideo'] = {
  54. async customUpload(file, insertFn) {
  55. console.log('上传视频', file);
  56. },
  57. };
  58. const handleCreated = (editor) => {
  59. editorRef.value = editor;
  60. console.log(editorConfig.value.MENU_CONF, 'editorConfig.value');
  61. };
  62. const customPaste = (editor, event, callback) => {
  63. const text = event.clipboardData.getData('text/plain');
  64. if (text) {
  65. editor.insertText(text);
  66. event.preventDefault();
  67. callback(false);
  68. }
  69. };
  70. onBeforeUnmount(() => {
  71. const editor = editorRef.value;
  72. if (editor) {
  73. editor.destroy();
  74. }
  75. });
  76. const updateValue = () => {
  77. emits('update:modelValue', localValue.value);
  78. };
  79. // 监听 localValue 变化,更新父组件的值
  80. watch(localValue, (newValue, oldValue) => {
  81. if (newValue!== props.modelValue) {
  82. updateValue();
  83. }
  84. });
  85. </script>
  86. <style scoped lang="scss">
  87. .rich-text-editor-container {
  88. width: 90%;
  89. border: 1px solid #e0e0e0;
  90. border-radius: 8px;
  91. box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  92. overflow: hidden;
  93. margin: 0 auto;
  94. /* 让容器在小屏幕下也能自适应 */
  95. @media (max-width: 768px) {
  96. width: 100%;
  97. }
  98. }
  99. .rich-text-toolbar {
  100. background-color: #f8f9fa;
  101. border-bottom: 1px solid #e0e0e0;
  102. padding: 5px 8px;
  103. display: flex;
  104. align-items: center;
  105. flex-wrap: wrap;
  106. /* 工具栏按钮样式 */
  107. .w-e-menu {
  108. margin-right: 5px;
  109. margin-bottom: 4px;
  110. button {
  111. color: #333;
  112. border: none;
  113. background: transparent;
  114. cursor: pointer;
  115. padding: 4px 6px;
  116. border-radius: 4px;
  117. transition: background-color 0.3s ease;
  118. &:hover {
  119. background-color: #e9ecef;
  120. }
  121. &:active {
  122. background-color: #dce0e4;
  123. }
  124. }
  125. /* 下拉菜单样式 */
  126. .w-e-select {
  127. color: #333;
  128. border: 1px solid #e0e0e0;
  129. background-color: white;
  130. border-radius: 4px;
  131. padding: 3px 6px;
  132. cursor: pointer;
  133. transition: border-color 0.3s ease;
  134. &:hover {
  135. border-color: #999;
  136. }
  137. }
  138. }
  139. }
  140. .rich-text-editor {
  141. padding: 12px;
  142. font-size: 16px;
  143. line-height: 1.6;
  144. color: #333;
  145. /* 编辑区域滚动条样式 */
  146. &::-webkit-scrollbar {
  147. width: 8px;
  148. }
  149. &::-webkit-scrollbar-thumb {
  150. background-color: #ccc;
  151. border-radius: 4px;
  152. }
  153. &::-webkit-scrollbar-track {
  154. background-color: #f1f1f1;
  155. }
  156. }
  157. </style>