Background: There are scenarios where a rich text editor needs to be used in the project. There are currently many rich text editors, and the tinymce rich text editor is used in this case;
Solution: There are two methods used in vue3, but each has advantages and disadvantages:
① Use the official method to introduce, advantages: simple operation; disadvantages: use api-key to request the js file in the cloud, the loading speed is slow, which affects the user experience;
②Using local files to import, advantages: fast loading, can be repackaged into editor components; disadvantages: the introduction of styles and plug-ins is more troublesome, and some plug-in customization methods are easy to step on;
Directory
1. Tinymce official provision method:
1. Install tinymce-vue:
2. Apply for an api-key:
3. Use in the component:
2. Use local files to import:
1. Install tinymce and tinymce-vue
2. Process the file path:
3. Secondarily package the editor component and create a new TinymceEditor.vue:
4. Use in components:
5. Pit:
one. tinymce official provision method:
In this case, the method of tinymce version 5 (tinymce-vue version 4) is used
Official documentation (version 5): Vue integration | Docs | TinyMCE
1. Install tinymce-vue:
npm install --save "@tinymce/tinymce-vue@^4" //or yarn add "@tinymce/tinymce-vue@^4"
2. Apply for api-key:
3. Use in component:
<template> <div> <Editor :init="myTinyInit" api-key="A P I key you applied for" v-model="content" /> </div> </template> <script setup> import {defineExpose, reactive, ref, defineEmits} from 'vue' import Editor from '@tinymce/tinymce-vue'; const myTinyInit = ref({ width:'100%', //Editor width, in tinymce version 6, toolbar_mode must be set to wrap to take effect height:500, //Editor height branding: false, //Do not display logo menubar: false, resize: false, toolbar_mode: 'wrap', skeletonScreen: true, //The editor is lazy loaded, but it does not seem to take effect after setting placeholder: 'Please enter notification content', plugins: ['lists link image table paste help wordcount'], //Required plugins, you can refer to the official documentation to import the plugins you need toolbar: 'undo redo | formatselect | bold italic forecolor backcolor | alignleft aligncenter alignright alignjustify | bullet numlist outdent indent | fontsize_formats: '12px 14px 16px 18px 20px 22px 24px 26px 36px 48px 56px', content_style: 'body, p{font-size: 14px;color:#606266}', //The text style in the editor can also be imported after customizing the content.css file //Custom image upload method (tinymce version 5) images_upload_handler: async (blobInfo, success, failure) => { var formData = new FormData(); formData.append('file', blobInfo.blob(), blobInfo.filename()); const res = await tinymceImageUpload(formData); //tinymceImageUpload is an encapsulated image upload method, which can be replaced by your own method if (res. code === 1) { success(`/image_manipulation/${res.data.filePath}`); //After success, return the image path, where /image_manipulation/ is the prefix of the image path, the specific situation depends on the result returned by the backend } else { failure('Image upload failed due to a XHR Transport error. Code: ' + res.msg); } }, }) </script>
2. Use local file to import:
In this case, the method of tinymce version 6 (tinymce-vue version 5) is used
Official documentation: Using the TinyMCE package with the Vue.js framework | TinyMCE Documentation
1. Install tinymce and tinymce-vue
npm install tinymce npm install tinymce-vue //or yarn add tinymce yarn add tinymce-vue
2. Processing file path:
① Create a new tinymce folder under the public folder (if it is vue2, it will be in the static folder);
②Language pack: The default interface is English. If you need to change to Chinese interface, you need to download a Chinese language pack, create a new langs folder under the tinymce folder, and place the downloaded file in the langs folder;
③Skins style: Create a new tinymce folder under the public folder (if it is vue2, it will be in the static folder), find skins in node_modules, and copy the entire folder to /public/tinymce/;
3. Secondary packaging of the editor component, creating a new TinymceEditor.vue:
<template> <div> <Editor v-model="content" :init="myTinyInit"></Editor> </div> </template> <script setup> import {computed, defineEmits, defineProps, onMounted, reactive, ref, watch} from 'vue' import tinymce from "tinymce/tinymce"; import Editor from "@tinymce/tinymce-vue"; import "tinymce/icons/default/icons"; import "tinymce/themes/silver"; import "tinymce/models/dom/model"; //Introduce plugins as needed import "tinymce/plugins/image"; import "tinymce/plugins/table"; import "tinymce/plugins/lists"; import "tinymce/plugins/link"; import "tinymce/plugins/help"; import "tinymce/plugins/wordcount"; import axios from "axios"; import {useStore} from "vuex"; import {ElNotification} from "element-plus"; const props = defineProps({ modelValue: { type: String, default: "" }, plugins: { type: [String, Array], default:'lists link image table help wordcount', }, toolbar: { type: [String, Array], default: 'undo redo | formatselect | bold italic forecolor backcolor | alignleft aligncenter alignright alignjustify | bullet numlist outdent indent | table image | help', } }); const emit = defineEmits(['input']); const store = useStore(); const myTinyInit = reactive({ width: '100%', height: 500, branding: false, menubar: false, resize: false, skin_url: "/tinymce/skins/ui/oxide", //manually introduced content_css: '/tinymce/skins/content/default/content.css', //Manually introduced toolbar_mode: "wrap", plugins: props.plugins, toolbar: props.toolbar, //Image upload method (Note!!: Use resolve in the promise object to return the image path, otherwise an error will be reported) images_upload_handler: (blobInfo) => new Promise((resolve, reject) => { let formData = new FormData(); formData.append('file', blobInfo.blob(), blobInfo.filename()); axios.post(`/api/backend/upload`, formData, { headers: ({ 'Content-Type': 'multipart/form-data', 'Authorization': "Bearer " + store.state.user.accessToken }) }).then(res => { if (res. data. code === 1) { resolve(`/image_manipulation${res.data.data.filePath}`) } else { ElNotification.warning(res.data.msg) } }).catch(error => { reject(error); }) }), }); const initContent = computed(() => { return props. modelValue }); onMounted(() => { tinymce.init({}); }) const content = ref(); watch(initContent, (newVal) => { content.value = newVal; }, {deep: true, immediate: true}); watch(content, (newVal) => { emit("input", newVal); }, {deep: true}); </script> <style scoped lang="scss"> </style>
4. Use in components:
<template> <div> <editor v-model="notificationForm. content" @input="(val)=> {notificationForm. content=val}"></editor> //The @input method needs to be defined by itself, otherwise the parent component may not get the value of the editor </div> </template> <script setup> import {reactive} from 'vue' import Editor from '@/components/backend/TinymceEditor.vue'; const notificationForm = reactive({ content: '', }) </script>
5. Pit:
①Introduction of files: In addition to the need to store language packs and skins locally, errors may occur when importing css or plugin files, just import the corresponding files according to the error status;
②Custom method: You cannot use the packaged axios request, you must use the promise object (see the definition of images_upload_handler method for details);