Vue + Element + vue-quill-editor 實現源碼編輯、自定義圖片上傳和漢化

集百家之長,看了不少博客再結合自身狀況,寫了這個小組件功能,僅供參考。 原博客地址: https://finget.github.io/2019...

實現源碼編輯

vue-quill-editor的配置文件:javascript

// toolbar工具欄的工具選項(默認展現所有)
const toolOptions = [
    // 加粗 斜體 下劃線 刪除線
    ['bold', 'italic', 'underline', 'strike'],
    // 加粗 斜體 下劃線 刪除線
    ['blockquote', 'code-block'],
    // 一、2 級標題
    [{header: 1}, {header: 2}],
    // 有序、無序列表
    [{list: 'ordered'}, {list: 'bullet'}],
    // 上標/下標
    [{script: 'sub'}, {script: 'super'}],
    // 縮進
    [{indent: '-1'}, {indent: '+1'}],
    // 文本方向
    [{direction: 'rtl'}],
    // 字體大小
    [{size: ['small', false, 'large', 'huge']}],
    // 標題
    [{header: [1, 2, 3, 4, 5, 6, false]}],
    // 字體顏色、字體背景顏色
    [{color: []}, {background: []}],
    // 字體種類
    [{font: []}],
    // 對齊方式
    [{align: []}],
    [{clean: '源碼編輯'}], // 這是本身加的
    // 連接、圖片、視頻
    ['link', 'image'],
    // 新添加的工具
    ['sourceEditor']
];
const handlers = {
    shadeBox: null,
    // 添加工具方法
    sourceEditor: function () {
        // alert('我新添加的工具方法');
        const container = this.container;
        const firstChild = container.nextElementSibling.firstChild;

// 在第一次點擊源碼編輯的時候,會在整個工具條上加一個div,層級比工具條高,再次點擊工具條任意位置,就會退出源碼編輯。能夠在下面cssText裏面加個背景顏色看看效果。

        if (!this.shadeBox) {
            let shadeBox = this.shadeBox = document.createElement('div');
            
            shadeBox.style.cssText = 'position:absolute; top:0; left:0; width:100%; height:100%; cursor:pointer';
            container.style.position = 'relative';
            container.appendChild(shadeBox);
            firstChild.innerText = firstChild.innerHTML;

            shadeBox.addEventListener('click', function () {
                this.style.display = 'none';
                firstChild.innerHTML = firstChild.innerText.trim();
            }, false);
        } else {
            this.shadeBox.style.display = 'block';
            firstChild.innerText = firstChild.innerHTML;
        }
    }
};

export default {
    placeholder: '',
    // 主題
    theme: 'snow',
    modules: {
        toolbar: {
            // 工具欄選項
            container: toolOptions,
            // 事件重寫
            handlers: handlers
        }
    },
    // 在使用的頁面中初始化按鈕樣式
    initButton: function () {
        // 樣式隨便改
        const sourceEditorButton = document.querySelector('.ql-sourceEditor');
        sourceEditorButton.style.cssText = 'font-size:18px';
        
        // 加了elementui的icon
        sourceEditorButton.classList.add('el-icon-edit-outline');          
        // 鼠標放上去顯示的提示文字
        sourceEditorButton.title = '源碼編輯';
    }
};
工具名,工具方法名,類名:
這裏要注意的是:工具名和工具方法名是同樣的,而且生成的 button工具擁有 ql-工具名的類名。
例如上面代碼中,個人工具名是 sourceEditor,個人方法名也是 sourceEditor,而生成的 button工具的類名就是 ql-sourceEditor了。

自定義上傳圖片

vue-quill-editor自帶的上傳,是把圖片變成了base64的格式,不符合通常的項目需求。我猜它是用的FileReader的API。css

有興趣的能夠試試這個,拖拽圖片轉base64預覽:html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
    <style media="screen">
    #div1 {width:400px; height:300px; background:#CCC; border:1px solid black; text-align:center; line-height:300px;}
    </style>
    <script>
    window.onload=function (){
      let oDiv=document.getElementById('div1');
      let oImg=document.getElementById('img1');

      oDiv.addEventListener('dragenter', function (){
        oDiv.innerHTML='請鬆手';
      }, false);
      oDiv.addEventListener('dragleave', function (){
        oDiv.innerHTML='拖到這裏上傳';
      }, false);

      oDiv.addEventListener('dragover', function (ev){
        ev.preventDefault();
      }, false);
      oDiv.addEventListener('drop', function (ev){
        ev.preventDefault();
        //
        let oFile=ev.dataTransfer.files[0];
        //讀取
        let reader=new FileReader();
        reader.readAsDataURL(oFile);
        reader.onload=function (){
          //alert('成功');
          oImg.src=this.result;
        };
        reader.onerror=function (){
          alert('讀取失敗了');
        };
        console.log(reader);
      }, false);
    }
    </script>
  </head>
  <body>
    <div id="div1">拖到這裏上傳</div>
    <img src="" id="img1">
  </body>
</html>
// 自定義vue-quill-editor的主要文件
<template>
    <div
        v-loading="imageLoading"
        element-loading-text="請稍等,圖片上傳中"
    >
        <quill-editor
            ref="myTextEditor"
            v-model="content"
            :options="quillOption"
            @change="onEditorChange($event)"
            @focus="onEditorFocus($event)"
            @ready="onEditorReady($event)"
        >
        </quill-editor>
        // 這個是elementui的上傳,把它display:none
        <el-upload
            style="display:none;"
            :class="name"
            :action="upload"
            :show-file-list="false"
            :on-success="handleAvatarSuccess"
            :before-upload="beforeAvatarUpload"
            :on-progress="onProgress"
        >
        </el-upload>
    </div>

</template>

<script>

import {
    quillEditor
} from 'vue-quill-editor';
import {
    upload
} from '@/api/upload_api.js';
import Quill from 'quill';

import quillConfig from './quill_config.js';
import 'quill/dist/quill.core.css';
import 'quill/dist/quill.snow.css';
import 'quill/dist/quill.bubble.css';
export default {
    props: {
        // 富文本內容
        value: String,
        // 富文本的名字 同一個頁面的多個富文本name不能重複的
        name: String,
        // 圖片類型
        imgType: {
            type: Array,
            default: () => ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/bmp']
        },
        // 圖片限制大小 單位 M
        limitSize: {
            type: Number,
            default: 1
        }
    },
    components: {
        quillEditor
    },
    mounted() {
        // 
        quillConfig.initButton();
        var vm = this;
        // 當點擊圖書上傳是,觸發elementui的upload點擊
        var imgHandler = async function (image) {
            vm.addImgRange = vm.$refs.myTextEditor.quill.getSelection();
            if (image) {
                document.querySelector(`.${vm.name} input`).click();
            }
        };

        vm.$refs.myTextEditor.quill.getModule('toolbar').addHandler('image', imgHandler);
    },
    model: {
        props: 'content',
        // 必須的change事件否則是不會響應的
        event: 'change'
    },
    computed: {
        content: {
            get: function () {
                return this.value;
            },
            set: function () {}
        }
    },
    data() {
        return {
            // 配置文件
            quillOption: quillConfig,
            imageLoading: false,
            upload: upload(),
        };
    },
    methods: {
        onEditorChange(e) {
            this.$emit('change', e.html);
        },
        onEditorFocus(e) {
            this.$emit('focus', e);
        },
        onEditorReady(e) {
            this.$emit('ready', e);
        },
        // 圖片上傳成功
        handleAvatarSuccess(res) {
            // console.log(res, file);
            if (res.code == 200) {
                // this.form.custom_logo_url = res.data.url;
                let url = res.data.url,
                    vm = this;

                if (url !== null && url.length > 0) {
                    var value = url;

                    // ***主要的東西就是這裏:上傳成功回顯
                    vm.addImgRange = vm.$refs.myTextEditor.quill.getSelection();
                    value = value.indexOf('http') != -1 ? value : 'http:' + value;
                    vm.$refs.myTextEditor.quill.insertEmbed(vm.addImgRange !== null ? vm.addImgRange.index : 0, 'image', value, Quill.sources.USER);
                } else {
                    vm.$message.warning('圖片增長失敗');
                }
            } else {
                this.$message.error(res.message);
            }
            this.imageLoading = false;
        },
        // 圖片上傳前
        beforeAvatarUpload(file) {
            let date = new Date().getTime();
            let imgType = this.imgType;

            const fileType = file.type;
            const isLt1M = file.size / 1024 / 1024 < this.limitSize;

            const isAllowType = imgType.indexOf(fileType) != -1;
            // console.log(fileType);
            // const isPng = fileType == 'image/jpeg';

            if (!isAllowType) {
                this.$message.error('請上傳符合文件格式的圖片!');
                return false;
            }
            if (!isLt1M) {
                this.$message.error('上傳logo圖片大小不能超過 1MB!');
                return false;
            }
            return isAllowType && isLt1M;
        },
        onProgress() {
            this.imageLoading = true;
        }
    }
};
</script>
// 使用方式
<quill-editor v-model="form.content" name="policy"/>
// 作了雙向綁定的,也能夠本身監聽change事件
⚠️注意點:
  1. name不能同樣
  2. 上傳邏輯按本身的來,跟我應該不同
  3. 其實最重要的是回顯到富文本中的那段代碼,不管你怎麼上傳,甚至能夠不用elementui的上傳組件,最後拿到上傳成功的url,再放進去就搞定了。

漢化

把這段代碼放到你的頁面中就好了。前端

.ql-snow .ql-tooltip[data-mode=link]::before {
  content: "請輸入連接地址:" !important;
}
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
    border-right: 0px;
    content: '保存' !important;
    padding-right: 0px;
}
.ql-snow .ql-tooltip[data-mode=video]::before {
    content: "請輸入視頻地址:" !important;
}
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
    content: '14px' !important;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=small]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before {
    content: '10px' !important;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=large]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before {
    content: '18px' !important;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=huge]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {
    content: '32px' !important;
}

.ql-snow .ql-picker.ql-header .ql-picker-label::before,
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
    content: '文本' !important;
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
    content: '標題1' !important;
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
    content: '標題2' !important;
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
    content: '標題3' !important;
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
    content: '標題4' !important;
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
    content: '標題5' !important;
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
    content: '標題6' !important;
}

.ql-snow .ql-picker.ql-font .ql-picker-label::before,
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
    content: '標準字體' !important;
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=serif]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {
    content: '襯線字體' !important;
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {
    content: '等寬字體' !important;
}

最後

建立了一個前端學習交流羣,感興趣的朋友,一塊兒來嗨呀!
vue

相關文章
相關標籤/搜索