基于MdEditor魔改的markdown编辑器,拓展了图片上传功能

背景

MdEditor是一款非常棒的编辑器,但是在上传图片方面有点美中不足,缺少了本地上传图片到云服务器的功能,如果要日常使用的话还是需要二次开发的,经过一番魔改后可以支持本地图片上传了,本来打算直接引入到项目中的,考虑到编辑器实在太大了,所以就使用了iframe的方式引入,不多BB直接上图。


1.jpg
2.jpg
3.jpg

4.jpg

基于Markdown修改传送门:http://www.mdeditor.com/
拓展后项目源码传送门:https://github.com/LiveJie/simple-markdown

正常使用的话还是需要在editor项目中的html修改一些配置

  
    $(function() {
        window.editor = editormd("min-editor", {
            width  : "100%",
            height : "600px",
            emoji: true,
            updateBefore: updateBefore, //拓展上传文件方法
            theme        : (localStorage.theme) ? localStorage.theme : "dark",
            previewTheme : (localStorage.previewTheme) ? localStorage.previewTheme : "default", 
            editorTheme  : (localStorage.editorTheme) ? localStorage.editorTheme : "pastel-on-dark", 
            path   : "./lib/"
        });
        // 图片上传方法
        function updateBefore(file, callBack) {
            const formData = new FormData()
            formData.append('file', file)
            formData.append('fileName', file.name)
            ajax('post', location.origin+'/api/updateImg', formData, callBack)
        }

        // 简单ajax封装
        function ajax(type, url, data, callBack) {
            var xhr = null;
            if(window.XMLHttpRequest){
                xhr = new XMLHttpRequest();
            } else {
                xhr = new ActiveXObject('Microsoft.XMLHTTP')
            }
            xhr.open(type, url, true);
            xhr.setRequestHeader('token', window._blog_token);
            data && (xhr.send(data));
            // xhr.responseType = "blob";
            xhr.onload = function (e) {
                if (this.status == 200) {
                    let res = JSON.parse(xhr.response)
                    let params = {
                        url: res.data,
                        alt: data.get("fileName"),
                        link: res.data
                    }
                    callBack && callBack(params)
                }
            }
        }
        // 添加监听回应事件
        window.addEventListener('message', function (e) {
            let data = e.data
            //完成事件后响应
            let str = "window_" + data.type
            if(typeof data === 'object') {
                switch(data.type) {
                    case "setToken":
                        window._blog_token = data.blog_token
                        window.parent.postMessage(str,"*");
                        break;
                    case "setValue":
                        window.editor.setValue(data.value)
                        window.parent.postMessage(str,"*");
                    break;
                }
            }else {
                let params = {}
                switch(data) {
                    case "getHTML":
                        params = {
                            type: 'setHtml',
                            data: window.editor.getHTML()
                        }
                        window.parent.postMessage(params,"*");
                    break;
                    case "getValue":
                        params = {
                            type: 'setValue',
                            data: window.editor.getValue()
                        }
                        window.parent.postMessage(params,"*");
                    break;
                }
            }
        });
    });

上面是本人自己用到的图片上传方法使用的时候需要相应的修改,然后需要注意的是callBack方法三个参数需要存在不然可能会报错,也可以再次魔改源码,
下面说下编辑器内嵌的相应事件内嵌的父项目的首页index.html

// 设置iframe需要的token 个人七牛云需要 不需要的不用传
      window._setIframeMessage = function(json) {
        window["_"+json.type] = false
        document.getElementsByTagName('iframe')[0].contentWindow.postMessage(json,"*");
      }

      // 获取编辑器HTML
      window._getEditorHTML = function() {
        document.getElementsByTagName('iframe')[0].contentWindow.postMessage("getHTML","*");
      }

      // 获取编辑器VALUE
      window._getEditorValue = function() {
        document.getElementsByTagName('iframe')[0].contentWindow.postMessage("getValue","*");
      }
      // 捕获反向传播事件
      window.addEventListener('message', function (e) {
        let data = e.data
        if(typeof data === 'object') {
            switch(data.type) {
                case "setHtml":
                    window._editorHtml = data.data
                break;
                case "setValue":
                    window._editorValue = data.data
                break;
            }
          }else {
              switch(data) {
                  case "window_setValue":
                      window._setValue = true
                  break;
                  case "window_setToken":
                      window._setToken = true
                  break;
              }

        }
      })

接着是父项目的具体内嵌页面, 要根据具体情况来调用方法

    mounted () {
        setTimeout(() => {
            let json = {
                type: "setToken",
                blog_token: localStorage.blog_token
            }
            this.getEditorData(json)
        }, 3000)
    },
methods: {
          // 获取获取通讯数据
        getEditorData(json) {
            if (window["_" + json.type]) {
                return
            }
            window._setIframeMessage(json)
            setTimeout(() => {
                this.getEditorData(json)
            }, 100)
        },
}

魔改编辑器的核心拓展文件update-img.js

/**
 * @description editor图片拓展上传插件
 * @author jie
 * @export
 * @param {*} 
 * @returns 
 */

;
(function () {
    var factory = function () {
        let _this = this
        this.Message = null;
        this.fileList = [];
        let imgWrapperDom = document.getElementById("extend-img-dialog-wrapper")
        // 插件初始化
        this.init = function () {
            this.Message = new Message()
            let updateDom = document.createElement("div")
            updateDom.setAttribute("id", "update-wrapper")
            updateDom.className = "update-wrapper"
            updateDom.innerHTML = `
            <div class="input-box-wrapper">      
                <div class="input-box" onclick="selectFile()">
                    <div class="svg-box">
                        <svg viewBox="0 0 1024 1024" focusable="false" class="" data-icon="inbox" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path d="M885.2 446.3l-.2-.8-112.2-285.1c-5-16.1-19.9-27.2-36.8-27.2H281.2c-17 0-32.1 11.3-36.9 27.6L139.4 443l-.3.7-.2.8c-1.3 4.9-1.7 9.9-1 14.8-.1 1.6-.2 3.2-.2 4.8V830a60.9 60.9 0 0060.8 60.8h627.2c33.5 0 60.8-27.3 60.9-60.8V464.1c0-1.3 0-2.6-.1-3.7.4-4.9 0-9.6-1.3-14.1zm-295.8-43l-.3 15.7c-.8 44.9-31.8 75.1-77.1 75.1-22.1 0-41.1-7.1-54.8-20.6S436 441.2 435.6 419l-.3-15.7H229.5L309 210h399.2l81.7 193.3H589.4zm-375 76.8h157.3c24.3 57.1 76 90.8 140.4 90.8 33.7 0 65-9.4 90.3-27.2 22.2-15.6 39.5-37.4 50.7-63.6h156.5V814H214.4V480.1z"></path></svg>
                    </div>
                    <div class="title">Click or drag file to this area to upload</div>
                    <div class="desc">Support for a single or bulk upload. Strictly prohibit from uploading company data or other band files</div>
                </div>
                <div class="file-list-wrapper">
                </div>
                <input id="input-file" type="file" style="display: none;" accept="image/*" multiple></input>
                <div class="button-wrapper">
                    <div class="ensure btn" onclick="confirm()">确认</div>
                    <div class="cencel btn" onclick="hide()">取消</div>
                </div>
            </div>
            `;
            imgWrapperDom.appendChild(updateDom);
            //监听input上传图片
            let fileDom = document.getElementById('input-file');
            fileDom.onchange = function(e) {
                e.preventDefault();
                let fileList = e.target.files
                if(fileList.length > 5) {
                    _this.Message.error("图片最多只能选择5张!")
                }else {
                    for(let item of fileList) {
                        if(!item.type.match("image")) {
                            _this.Message.error("请上传正确格式的图片!")
                            return false
                        }
                        let sign = new Date().getTime()
                        item.sign = sign
                        _this.fileList.push(item)
                        var read = new FileReader();
                        read.readAsDataURL(item);
                        read.onload = function (e) {
                            addFileList(this.result, item.name, sign)
                        }
                    }
                }
            }
            //图片拖拽上传
            let box = document.getElementById('update-wrapper');
            box.ondragover = function (e){
                e.preventDefault();
            }
            // 拖拽图片
            box.ondrop = function (e){
                e.preventDefault();
                var files = e.dataTransfer.files;//获取到第一个上传的文件对象
                for(let file of files) {
                    if(!file.type.match("image")) {
                        _this.Message.error("请上传正确格式的图片!")
                        return false
                    }
                    _this.fileList.push(file)
                    var fr=new FileReader();//实例FileReader对象
                    fr.readAsDataURL(file);//把上传的文件对象转换成url
                    fr.onload=function (e){
                        addFileList(this.result, file.name)
                    }
                }
            }
        }()
        function Message() {
            this.success = function(msg = "", duration = 3) {
                let className = "message active success"
                this.showThis(msg, duration, className)
            }
            this.wraning = function(msg = "", duration = 3) {
                let className = "message active wraning"
                this.showThis(msg, duration, className)
            }
            this.error = function(msg = "", duration = 3) {
                let className = "message active error"
                this.showThis(msg, duration, className)
            }
            this.showThis = function(msg, duration, className) {
                if(!msg) {
                    return Error("msg is null")
                }
                let divDom = document.createElement("div")
                divDom.innerText = msg
                divDom.className = "message";
                imgWrapperDom.appendChild(divDom)
                setTimeout(function(){
                    divDom.className = className || "message"
                },50)
                setTimeout(function(){
                    imgWrapperDom.removeChild(divDom)
                }, duration * 1000)
            }
        }
        this.getFileList = function () {
            return this.fileList
        }
        this.hide = function () {
            this.reset();
            imgWrapperDom.style.display = 'none';
        }
        this.confirm = function () {
            if(!window._updateBefore) {
                _this.Message.error("updateBefore function is not setting!")
                return
            }
            if(!this.fileList.length) {
                _this.Message.error("上传图片不能为空!")
            }
            for(let item of this.fileList) {
                window._updateBefore(item, function(params) {
                    dialog.setFileUrl(params)
                })
            }
            this.reset()
            imgWrapperDom.style.display = 'none';
        }
        this.reset = function() {
            this.fileList = [];
            let fileListDom = document.getElementsByClassName("file-list-wrapper")[0]
            fileListDom.innerHTML = '';
        }
        this.show = function () {
            this.fileList = [];
            imgWrapperDom.style.display = 'block';
        }
        //加载进度条
        this.speedProgress = function (e) {
            const lastLinearDom = e.children[e.children.length - 1]
            let w = ~~lastLinearDom.style.width.replace("%", "")
            if(w >= 100) {
                lastLinearDom.style.width = '';
            }else {
                let randomNum = Math.floor(Math.random()*10)
                if((w + randomNum) >= 100) {
                    lastLinearDom.style.width = '';
                }else {
                    lastLinearDom.style.width = w + randomNum + '%';
                    setTimeout(function(){
                        speedProgress(e)
                    },100)
                }
            }
        }
        //添加图片文件
        this.addFileList = function(url, name, sign) {
            let fileListDom = document.getElementsByClassName("file-list-wrapper")[0]
            if(fileListDom.children.length >= 5) {
                this.Message.error("图片最多只能选择5张!")
                return
            }
            let html = `
                <div class="left">
                    <img src="${url}" alt="">
                    <div class="name">${name}</div>
                </div>
                <div class="right" data-sign="${sign}" onclick="deleteDom(this)">
                    <svg viewBox="64 64 896 896" focusable="false" class="" data-icon="delete" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path></svg>
                </div>
                <div class="file-list-linear"></div>
            `
            let divDom = document.createElement("div")
            divDom.className = "list"
            divDom.innerHTML = html;
            fileListDom.appendChild(divDom)
            
        }
        //点击删除元素
        this.deleteDom = function(e) {
            e.parentNode.remove(e)
            _this.fileList.forEach(function(item, index) {
                if(item.sign == e.attributes[1].value) {
                    _this.fileList.splice(index)
                    return;
                }
            })
            console.log(_this.fileList)
        }
        // 模拟点击上上传图片
        this.selectFile = function() {
            var fileDom = document.getElementById('input-file');
            fileDom.click()
        }
    };
    if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
        module.exports = factory;
    } else if ((typeof define === "function")) {
        if(define.amd) {
            define(null, function() {
                factory()
            });
        }else {
            define(function() {
                factory();
            });
        }
    } else {
        factory();
    }

})();

到这里就介绍的差不多了,希望可以帮助到有需要的人

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271