123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- <template>
- <div class="rich-text-editor-container">
- <Toolbar
- class="rich-text-toolbar"
- :editor="editorRef"
- :defaultConfig="toolbarConfig"
- mode="default"
- />
- <Editor
- class="rich-text-editor"
- :style="{ height: '300px', overflowY: 'hidden' }"
- v-model="localValue"
- :defaultConfig="editorConfig"
- mode="default"
- @onCreated="handleCreated"
- @customPaste="customPaste"
- />
- </div>
- </template>
- <script setup>
- import { ref, defineProps, defineEmits, watch } from 'vue';
- import '@wangeditor/editor/dist/css/style.css';
- import { onBeforeUnmount, shallowRef } from 'vue';
- import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
- import axios from 'axios';
- const props = defineProps({
- modelValue: String
- });
- const emits = defineEmits(['update:modelValue']);
- const localValue = ref(props.modelValue);
- const editorRef = shallowRef();
- const toolbarConfig = ref();
- const editorConfig = ref({ placeholder: '请输入内容...', MENU_CONF: {} });
- // 自定义图片上传
- editorConfig.value.MENU_CONF['uploadImage'] = {
- async customUpload(file, insertFn) {
- try {
- const formData = new FormData();
- formData.append('image', file);
- const response = await axios.post('http://127.0.0.1:5000/upload-image', formData, {
- headers: {
- 'Content-Type': 'multipart/form-data'
- }
- });
- const imageUrl = response.data.imageUrl;
- insertFn(imageUrl, 'img');
- } catch (error) {
- console.error('图片上传失败:', error);
- }
- },
- };
- // 自定义视频上传
- editorConfig.value.MENU_CONF['uploadVideo'] = {
- async customUpload(file, insertFn) {
- console.log('上传视频', file);
- },
- };
- const handleCreated = (editor) => {
- editorRef.value = editor;
- console.log(editorConfig.value.MENU_CONF, 'editorConfig.value');
- };
- const customPaste = (editor, event, callback) => {
- const text = event.clipboardData.getData('text/plain');
- if (text) {
- editor.insertText(text);
- event.preventDefault();
- callback(false);
- }
- };
- onBeforeUnmount(() => {
- const editor = editorRef.value;
- if (editor) {
- editor.destroy();
- }
- });
- const updateValue = () => {
- emits('update:modelValue', localValue.value);
- };
- // 监听 localValue 变化,更新父组件的值
- watch(localValue, (newValue, oldValue) => {
- if (newValue!== props.modelValue) {
- updateValue();
- }
- });
- </script>
- <style scoped lang="scss">
- .rich-text-editor-container {
- width: 90%;
- border: 1px solid #e0e0e0;
- border-radius: 8px;
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
- overflow: hidden;
- margin: 0 auto;
- /* 让容器在小屏幕下也能自适应 */
- @media (max-width: 768px) {
- width: 100%;
- }
- }
- .rich-text-toolbar {
- background-color: #f8f9fa;
- border-bottom: 1px solid #e0e0e0;
- padding: 5px 8px;
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- /* 工具栏按钮样式 */
- .w-e-menu {
- margin-right: 5px;
- margin-bottom: 4px;
- button {
- color: #333;
- border: none;
- background: transparent;
- cursor: pointer;
- padding: 4px 6px;
- border-radius: 4px;
- transition: background-color 0.3s ease;
- &:hover {
- background-color: #e9ecef;
- }
- &:active {
- background-color: #dce0e4;
- }
- }
- /* 下拉菜单样式 */
- .w-e-select {
- color: #333;
- border: 1px solid #e0e0e0;
- background-color: white;
- border-radius: 4px;
- padding: 3px 6px;
- cursor: pointer;
- transition: border-color 0.3s ease;
- &:hover {
- border-color: #999;
- }
- }
- }
- }
- .rich-text-editor {
- padding: 12px;
- font-size: 16px;
- line-height: 1.6;
- color: #333;
- /* 编辑区域滚动条样式 */
- &::-webkit-scrollbar {
- width: 8px;
- }
- &::-webkit-scrollbar-thumb {
- background-color: #ccc;
- border-radius: 4px;
- }
- &::-webkit-scrollbar-track {
- background-color: #f1f1f1;
- }
- }
- </style>
|