When using tinymce for the first time in the project, I encountered a big push problem. I felt that this component was too large and cumbersome, especially the slow response of the website. I couldn't respond in half a day, and I couldn't find many classes with APIs. Now I feel it's difficult to control it. Here is the sorting of the APIs used in the project:
Project directory:
index.vue slightly modified the original Tinymce component
<textarea :id="tinymceId" class="tinymce-textarea"/> <div class="editor-custom-btn-container"> <editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK"/> </div>
import editorImage from './components/editorImage'
import plugins from './plugins'
import toolbar from './toolbar'
import emitter from 'element-ui/src/mixins/emitter'
export default {
name: 'Tinymce', mixins: [emitter], components: { editorImage }, props: { id: { type: String, default: function () { return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '') } }, value: { type: String, default: '' }, toolbar: { type: Array, required: false, default () { return [] } }, menubar: { type: String, default: 'file edit insert view format table' }, height: { type: Number, required: false, default: 50 }, pasteImages: { type: Boolean, default: false } }, data () { return { hasChange: false, hasInit: false, tinymceId: this.id, fullscreen: false, languageTypeList: { 'en': 'en', 'zh': 'zh_CN' } } }, computed: { language () { return this.languageTypeList[this.$store.getters.language] } }, watch: { value (val) { try { if (this.value !== this.getContent()) { this.$nextTick(() => window.tinymce.get(this.tinymceId).setContent(val || '')) } } catch (e) { } }, language () { this.destroyTinymce() this.$nextTick(() => this.initTinymce()) } }, mounted () { this.initTinymce() }, activated () { this.initTinymce() }, deactivated () { this.destroyTinymce() }, destroyed () { this.destroyTinymce() }, methods: { initTinymce () { const _this = this window.tinymce.init({ language: this.language, selector: `#${this.tinymceId}`, height: this.height, body_class: 'panel-body ', object_resizing: false, toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar, menubar: this.menubar, plugins: plugins, contextmenu: 'link', end_container_on_empty_block: true, powerpaste_word_import: 'clean', code_dialog_height: 450, code_dialog_width: 1000, advlist_bullet_styles: 'square', advlist_number_styles: 'default', imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'], default_link_target: '_blank', link_title: false, paste_data_images: this.pasteImages, nonbreaking_force_tab: true, // inserting nonbreaking space need Nonbreaking Space Plugin init_instance_callback: editor => { if (_this.value) { editor.setContent(_this.value) } _this.hasInit = true editor.on('NodeChange Change KeyUp SetContent', () => { this.hasChange = true this.$emit('input', editor.getContent()) }) editor.on('blur', () => { _this.dispatch('ElFormItem', 'el.form.blur', [editor.getContent()]) }) }, setup (editor) { editor.on('FullscreenStateChanged', (e) => { _this.fullscreen = e.state }) editor.on('init', function () { if (editor.getContent() === '') { editor.setContent('<p id=\'#Imtheplaceholder \ '> please input content (limited to 2000 characters)! </p>') } }) editor.on('focus', function () { editor.setContent(_this.value) }) } // Integrate seven cattle upload // images_dataimg_filter(img) { // setTimeout(() => { // const $image = $(img); // $image.removeAttr('width'); // $image.removeAttr('height'); // if ($image[0].height && $image[0].width) { // $image.attr('data-wscntype', 'image'); // $image.attr('data-wscnh', $image[0].height); // $image.attr('data-wscnw', $image[0].width); // $image.addClass('wscnph'); // } // }, 0); // return img // }, // images_upload_handler(blobInfo, success, failure, progress) { // progress(0); // const token = _this.$store.getters.token; // getToken(token).then(response => { // const url = response.data.qiniu_url; // const formData = new FormData(); // formData.append('token', response.data.qiniu_token); // formData.append('key', response.data.qiniu_key); // formData.append('file', blobInfo.blob(), url); // upload(formData).then(() => { // success(url); // progress(100); // }) // }).catch(err => { // failure('unknown problem, refresh page, or contact programmer ') // console.log(err); // }); // }, }) }, destroyTinymce () { try { if (window.tinymce.get(this.tinymceId) && window.tinymce.get(this.tinymceId) != null) { window.tinymce.get(this.tinymceId).destroy() } } catch (e) {} }, setContent (value) { window.tinymce.get(this.tinymceId).setContent(value) }, getContent () { return window.tinymce.get(this.tinymceId).getContent() }, imageSuccessCBK (arr) { const _this = this arr.forEach(v => { window.tinymce.get(_this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`) }) } }
}
plugins.js and toolbar.js contain plug-ins, which can add and delete unnecessary plug-ins; editorImage.vue is a plug-in for uploading local images, but cannot upload pasted images
editorImage.vue
<el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary" @click=" dialogVisible=true">Upload pictures </el-button> <el-dialog :visible.sync="dialogVisible" append-to-body> <el-upload :multiple="true" :file-list="fileList" :show-file-list="true" :on-remove="handleRemove" :on-success="handleSuccess" :before-upload="beforeUpload" :on-exceed="handleExceed" class="editor-slide-upload" action="https://httpbin.org/post" list-type="picture-card"> <el-button size="mini" type="primary">Click upload</el-button> </el-upload> <el-button @click="dialogVisible = false">Cancellation</el-button> <el-button type="primary" @click="handleSubmit">Determine</el-button> </el-dialog>
// import { getToken } from 'api/qiniu'
export default {
name: 'EditorSlideUpload', props: { color: { type: String, default: '#1890ff' } }, data () { return { dialogVisible: false, listObj: {}, fileList: [] } }, methods: { checkAllSuccess () { return Object.keys(this.listObj).every(item => this.listObj[item].hasSuccess) }, handleSubmit () { const arr = Object.keys(this.listObj).map(v => this.listObj[v]) if (!this.checkAllSuccess()) { this.$message('Please wait patiently for all pictures to be uploaded successfully!') return } this.$emit('successCBK', arr) this.listObj = {} this.fileList = [] this.dialogVisible = false }, handleSuccess (response, file) { const uid = file.uid const objKeyArr = Object.keys(this.listObj) for (let i = 0, len = objKeyArr.length; i < len; i++) { if (this.listObj[objKeyArr[i]].uid === uid) { this.listObj[objKeyArr[i]].url = response.files.file this.listObj[objKeyArr[i]].hasSuccess = true return } } }, handleRemove (file) { const uid = file.uid const objKeyArr = Object.keys(this.listObj) for (let i = 0, len = objKeyArr.length; i < len; i++) { if (this.listObj[objKeyArr[i]].uid === uid) { delete this.listObj[objKeyArr[i]] return } } }, /**Hook before uploading the file (the parameter is the uploaded file)**/ beforeUpload (file) { const _URL = window.URL || window.webkitURL const fileName = file.uid this.listObj[fileName] = {} //Picture type and size judgment let reg = new RegExp('[.jpg|.png]$') if (Math.floor(file.size / 1024 / 1024) < 2) { if (file && reg.test(file.name)) { } else { this.$message.error('The image format is incorrect, please upload JPG or PNG Format picture!') return false } } else { this.$message.error('Only upload less than 2 MB Picture!') return false } return new Promise((resolve, reject) => { const img = new Image() img.src = _URL.createObjectURL(file) if (img) { img.onload = () => { this.listObj[fileName] = { hasSuccess: false, uid: file.uid, width: this.width, height: this.height } } } resolve(true) }) }, handleExceed (files, fileList) {}//Hook when the number of files exceeds the limit }
}
Problems encountered in the project:
1. Unable to paste the pictures from the screenshot
2. Cannot add placeholder inside rich text
First question:
To solve the first problem, we mainly use the paste ﹣ data ﹣ images attribute of tinymce component, Boolean type. If it is true, it is allowed to paste pictures. If it is false, it is not allowed to paste pictures
Second question:
To solve the second problem, we use the setup method, and then use the init event of the editor event to set placeholders for the text box, that is, when the rich text editor is initially rendered, we add placeholders, that is:
editor.on('init', function () {
if (editor.getContent() === '') { editor.setContent('<p id=\'#Imtheplaceholder \ '> please input content (limited to 2000 characters)! </p>') }
})
Reuse the contents of the placeholder when the focus event is cleared
editor.on('focus', function () { editor.setContent(_this.value) })