Froala rich text editor

Recently, due to product requirements, we replaced the old vue UEditor rich text editor with Froala. If it’s Vue

npm install vue-froala-wysiwyg --save
Copy the code
import VueFroala from 'vue-froala-wysiwyg';
Copy the code

Then go to the import file main.js to import the dependency.

//Import Froala Editor 
import 'froala-editor/js/plugins.pkgd.min.js';
//Import third party plugins
import 'froala-editor/js/third_party/embedly.min';
import 'froala-editor/js/third_party/font_awesome.min';
import 'froala-editor/js/third_party/spell_checker.min';
import 'froala-editor/js/third_party/image_tui.min';
// Import Froala Editor css files.
import 'froala-editor/css/froala_editor.pkgd.min.css';
import 'froala-editor/js/languages/zh_cn';
// Import and use Vue Froala lib.
import VueFroala from 'vue-froala-wysiwyg';

Vue.use(VueFroala)
Copy the code

Custom feature list

data() {
    var _this = this;
    return {
      config: {
        editor: null,
        language: 'zh_cn',// language height: 325, quickInsertEnabled:false// Easily insert the tag charCounterCount:false, // Rich text input length toolbarButtons: ['align'.'bold'.'paragraphFormat'.'outdent'.'indent'.'insertLink',], // Customize toolbarButtons, function menu imageEditButtons: ['imageReplace'.'imageAlign'.'imageCaption'.'imageRemove',],// imageUploadParam:'upfile'// Upload the image to imageUploadURL:'http://localhost:3005/excel'// imageUploadMethod:'POST',
        // Allow to upload PNG and JPG.
        imageAllowedTypes: ['jpeg'.'jpg'.'png'] // Accept an empty placeholderText:'Please enter content', // Set Max file size to 20mb. fileMaxSize: 20 * 1024 * 1024, //false,
        // Allow to upload any file.
      },
    };
  },
Copy the code

These are basic configurations, but I don’t recommend theminsertImageThis comes with a picture upload function, so in the aboveToolbarButtons: ['align', 'bold', 'paragraphFormat', 'outdent', 'Indent ', 'insertLink'], which I left out.

{“link”: “path/to/image.jpg”}


Use plupload instead of default upload

Download an icon and absolutely locate the icon in a rich text editor

<i class="el-icon-picture-outline" ref="uploadBtn"></i>
Copy the code

Then go download Plupload

npm install plupload --save
Copy the code

It is then introduced in the component

import plupload from 'plupload';
Copy the code

Then start initializing the plupload method and triggering the DOM.

// Initialize the uploadinitPlupload() {
      const uploader = new plupload.Uploader({
        browse_button: this.$refs.uploadbtn, // Trigger button multi_selection:false,
        file_data_name: 'upfile', // Define the file parameters filters: this.setfilters (),}); uploader.init(); // Trigger uploader.bind() when a file is added to the upload queue'FilesAdded', (uploader, files) => {
        for (let index = 0; index < files.length; index++) {
          const file = files[index];
          if(! this.checkFileName(file)) { this.$message.error(file.name + 'Filename is not valid'); uploader.removeFile(file); }} // start uploader.start(); }); Uploader.bind () is triggered when a file in the queue is about to start uploading.'BeforeUpload', async (uploader, file) => { uploader.setOption(this.setConfig(file)); }); // Trigger uploader.bind() when a file in the queue is uploaded'FileUploaded', async (uploader, file, info) => {
        console.log(info, 'info==');
        const infoParse = info.response && JSON.parse(info.response);

        console.log(infoParse, 'infoParseinfoParse');
        // const doMain = process.env.REACT_APP_OSS_HOST;
        this.insertHtml('<img src=' + infoParse.url + ' />');
      });

      returnuploader; }, // Set the upload configurationsetConfig(file) {
      return {
        url:
          'http://xxxxxxxxxxxxxxxxxxx'// Upload interface address multipart_params: {// Set other parameters},}; },Copy the code

After initializing the plupload upload method, continue to insert rich text images.

InsertHtml (content) {// Set the editor to get focus this.editor.events.focus(); // Get the selected object const selection = getSelection(); // Determine whether the last cursor object existsif (this.lastEditRange) {
        console.log('jinru = = = = = = = = = to enter'); / / there is finally a cursor object, selected objects to remove all the cursor and add the final state of the cursor reduction before selection. RemoveAllRanges (); selection.addRange(this.lastEditRange); } // Insert content this.editor.html. Insert (content); // Record the last cursor position this.lasteditRange = selection. GetRangeAt (0); },Copy the code

Another important step is to operate on the rich text to event triggering, written in the data config

Initialized: (e, Editor) => {console.log(this).$refs.editS);
            this.editor = this.$refs.editS.getEditor();

            // this.editor = this.$refs.editS.getEditor(); }, // Add an event to record the last position each time the key is pressed keyup: (e, editor) => {const sel = window.getSelection && window.getSelection();if(sel && sel.rangeCount > 0) { _this.lastEditRange = sel.getRangeAt(0); }}, // Copy rich text from other places, including images, etc'paste.after': async function() {// This can be replaced with the re, which is copied to the img SRC, and then write methods to replace the image inside the external chain, upload to their own server. And insert it into rich text},}Copy the code

I posted the full code

<template>
  <div>
    <div class="uploadImage">
      <froala
        id="edit"
        :tag="'textarea'"
        :config="config"
        v-model="model"
        ref="editS"
      ></froala>
      <i class="el-icon-picture-outline" ref="uploadBtn"></i>
    </div>
  </div>
</template>

<script>
import plupload from 'plupload';
import VueFroala from 'vue-froala-wysiwyg'; const imgReg = /<img.*? (? :>|\/>)/gi; // Matches the SRC attribute const srcReg = /\ SSRC =[\'\]?" ([^ \ '\"] *) [\ '\]?" /i; const http = /(http|https):\/\/([\w.]+\/?) \S*/; export default { components: {}, props: { content: {}, }, data() { var _this = this; return { config: { editor: null, lastEditRange: null, body: null, language: 'zh_cn', height: 325, quickInsertEnabled: False, charCounterCount: false, // Customize toolbarButtons [ 'align', 'bold', 'paragraphFormat', 'outdent', 'indent', 'insertLink', ], imageEditButtons: [ 'imageReplace', 'imageAlign', 'imageCaption', 'imageRemove', ], imageUploadMethod: 'POST', // Allow to upload PNG and JPG. imageAllowedTypes: ['jpeg', 'jpg', 'png'], placeholderText: 'Please input content ', quickInsertEnabled: false, // Set Max file size to 20Mb. fileMaxSize: 20 * 1024 * 1024, toolbarSticky: false, // Allow to upload any file. fileAllowedTypes: ['*'], events: { initialized: (e, editor) => { console.log(this.$refs.editS);
            this.editor = this.$refs.editS.getEditor();

            // this.editor = this.$refs.editS.getEditor(); }, // Add an event to record the last position each time the key is pressed keyup: (e, editor) => {const sel = window.getSelection && window.getSelection(); if (sel && sel.rangeCount > 0) { _this.lastEditRange = sel.getRangeAt(0); }}, // paste the event 'paste. After ': Async function () {//newContent, insert the newContent into the rich text this.inserthtml && this.inserthtml (newContent); }},}}; }, computed: { model: { get() { return this.content; }, set(val) {// Trigger to update text data this.$emit('update:content', val); }, }, }, watch: {}, created() {}, mounted() { this.initPlupload(); }, methods: {setFilters() {return {prevent_duplicates: false, // Allow duplicates. The default is false. 1024 * 1024 * 8, mime_types: [// Upload type only {title: 'OSSUploadFiles', Extensions: 'JPG, PNG, JPEG, GIF'},],}; }, // set the upload configuration setConfig(file) {// const name = setFileName(file.name); return { url: '/uploadimage', multipart_params: { }, }; }, // initialupload initPlupload() {const uploader = new plupload.uploader ({browse_button: this.$refs.uploadbtn, // Trigger multi_selection: false, file_data_name: 'upfile', filters: this.setfilters (),}); uploader.init(); Uploader. bind('FilesAdded', (uploader, files) => {for (let index = 0; index < files.length; index++) { const file = files[index]; if (! this.checkFileName(file)) { this.$message.error(file.name + 'filename is invalid '); uploader.removeFile(file); }} // start uploader.start(); }); Uploader. bind('BeforeUpload', async (uploader, async)) triggers uploader when a file in the queue is about to start uploading file) => { uploader.setOption(this.setConfig(file)); }); Uploader. bind('FileUploaded', async (uploader, file, info) => { const infoParse = info.response && JSON.parse(info.response); this.insertHtml('<img src=' + infoParse.url + ' />'); }); return uploader; }, // Check whether the filename is valid checkFileName(file) {if (/[%=+#&?\s]/g.test(file.name)) {return false; } return true; }, initChange(val) { console.log(val, 55434343); }, insertHtml(content) {// Set the editor to get focus this.editor.events.focus(); // Get the selected object const selection = getSelection(); If (this.lasteditrange) {console.log('jinru========= enter '); / / there is finally a cursor object, selected objects to remove all the cursor and add the final state of the cursor reduction before selection. RemoveAllRanges (); selection.addRange(this.lastEditRange); } // Insert content this.editor.html. Insert (content); // Record the last cursor position this.lasteditRange = selection. GetRangeAt (0); ,}}}; </script> <style lang="less" scoped> .fr-uploading { display: none; } .uploadImage { position: relative; /deep/ .el-icon-picture-outline { font-size: 23px; position: absolute; top: 13px; left: 268px; cursor: pointer; } /deep/ .el-icon-picture-outline:before { color: black; } } Copy the code

At first use, this built-in image upload is really bad, but also follow his rules to return data. So encountered pit dad plug-in, do not want to write their own time, you can find a way to replace his original lost function.