美文网首页
Markdown-富文本转换(2)

Markdown-富文本转换(2)

作者: 渝聆墨 | 来源:发表于2019-07-13 23:33 被阅读0次

上一节编写关于编写使用正则正则转换标题、图片链接,网页链接、代码块的例子,接下继续编写其拓展。
Markdown-富文本转换(1)

粗体&斜体

/**
         * **粗体**
                  *  *斜体*
         * @param {Object} msg
         */
        function transitionFontBoldItalic(msg) {
            var reg = /(\*{1,3})(.*?)(\*{1,3})/g;
            var objExp = new RegExp(reg);
            var now = msg.replace(objExp, function($1, $2, $3, $4) {
                if($2 == '***') {//粗体&斜体
                    $3 = '<b><i>' + $3 + '</i></b>'
                } else if($2 == '**') {//粗体
                    $3 = '<b>' + $3 + '</b>'
                } else if($2 == '*') {//斜体
                    $3 = '<i>' + $3 + '</i>'
                }

                return $3;
            });
            return now;
        }

调用规则:
斜体:
文本文字*
粗体:文本文字
粗体+斜体:文本文字
解析:
1.粗体转换标签采用<b></b>,不使用css来转换,是为了减少代码块。
2.斜体转换标签采用<i></i>,使用原因同理
3.正则/
{1,3}/代表可能会有1~3个字符

引用

js代码块
/**
         * ^引用
         * @param {Object} msg
         */
        function transitionQuote(msg) {
            var reg = /(\^)(.*?)(<div><br><\/div>)/g;
            var objExp = new RegExp(reg);
            var now = msg.replace(objExp, function($1, $2, $3, $4) {
                console.log($2)
                $3 = '<div class="quote-content" ><p class="quote-content-before"></p>' + $3 + '</div>' + $4
                return $3;
            });
            return now;
        }
css代码块
.quote-content {
            display: flex;
            align-items: center;
            background-color: #EDEDED;
            line-height: 3rem;
            width: 100%;
        }
        
        .quote-content-before {
            width: 5px;
            line-height: 3rem;
            min-height: 3rem;
            background-color: #CCCCCC;
            margin: 0;
            margin-right: 1rem;
        }

*调用规则:^文本文字
*解析:

1.引用部分的转换显示主要有引用头部与引用体,他们一部分为深灰色与浅灰色,所以我这里采用div作为引用体并且使用css渲染背景色为浅灰色,引用头部使用p标签渲染为深灰色作为头部。 image.png

字体颜色

/**
         * @字体颜色@rgb(FFFF00)
         * @param {Object} msg
         */
        function transitionFontColor(msg) {
            var reg = /(@)(.*?)(@rgb)\(([0-9a-fA-F]{6}|[0-9a-fA-F]{3})\)/g;
            var objExp = new RegExp(reg);
            var now = msg.replace(objExp, function($1, $2, $3, $4, $5) {
                $3 = '<span style="color:#'+$5+'" >' + $3 + '</span>';
                return $3;
            });
            return now;
        }

*调用规则:@字体颜色@rgb(颜色16进制代码)
eg:@字体颜色@rgb(FF0000)
解析:
1.匹配颜色代码采用[0-9a-fA-F]{6}|[0-9a-fA-F]{3},可以匹配6位16进制数与3位16进制数
2.采用@与@rgb是为了更加形象不会影响其他字数的书写转换

image.png 转换内容:
var data="#@我是红色@rgb(FF0000)<div><br></div>"+
        "#@我是绿色@rgb(00FF00)<div><br></div>"+
        "#@我是蓝色@rgb(0000FF)<div><br></div>";
var content=transitionTitle(data);//转换标题
 content=transitionFontColor(content);
document.getElementById("content").innerHTML =content;  

Markdown 富文本编辑器实例

编辑器组件——WriteTool.vue
<template>
    <div class="write-content-boxs">
        <div class="write-header">
            <div class="write-title">
                <input class="edit-title" v-model.lazy="title" type="text" placeholder="请输入标题" />
            </div>
            <div class="write-tools">
                <span class="item-tool" @click="chooseType">插入图片</span>
                <span class="item-tool" @click="onSaveArticle">保存文章</span>
                <span class="item-tool" @click="onIssueArticle">发布文章</span>
                <span class="item-tool" @click="onPreview">预览模式</span>
            </div>
        </div>
        <div class="write-content-box">
            <div id="mediaedit" class="write-content" contenteditable="true" @mouseup="selectText()">

            </div>
        </div>
        <input type="file" id="upload_file" class="input-file" style="display: none;" name="avatar" ref="avatarInput" @change="fileChange($event)"
         accept="image/gif,image/jpeg,image/jpg,image/png">
    </div>
</template>

<script>
    export default {
        name: 'write-content',
        data() {
            return {
                imgList: [],
                size: 0,
                limit: 6, 
                desc: '',
                text: this.content,
                cursortPosition: null
            }
        },
        watch: {
            //普通的watch监听
            title(val, oldVal) {
                console.log("a: " + val, oldVal);
            },
            content(value, oldVal) {
                if(this.text!=value){
                    this.text = value;
                    this.updataContent(value);
                }
            }
        },
        props: {
            title: {
                type: String,
                default: ''
            },
            content: {
                type: String,
                default: ''
            }
        },
        mounted() {
            var that = this;
            var mediaedit = document.getElementById("mediaedit");
            mediaedit.addEventListener(
                "DOMSubtreeModified",
                function(evt) {
                    that.text = mediaedit.innerHTML;
                    that.cursortPosition= window.getSelection().getRangeAt(0);
                    setTimeout(function() {
                        that.$emit('onchange', that.text);
                    }, 200)
                },
                false
            );
            
            mediaedit.onclick = function() {
                that.cursortPosition= window.getSelection().getRangeAt(0);
            }

            
            mediaedit.onkeyup = function() {
                that.cursortPosition= window.getSelection().getRangeAt(0);
            }
            
        },
        methods: {
            //打开预览视图
            onPreview: function() {
                this.$emit('preview', 0);
            },//保存文章
            onSaveArticle: function() {
                let content = this.write.startTransition(this.text);
                let param = {
                    title: this.title,
                    content: content
                }
                this.$emit('onsave', param);
            },//发布文章
            onIssueArticle: function() {
                let content = this.write.startTransition(this.text);
                let param = {
                    title: this.title,
                    content: content
                }
                this.$emit('onissue', param);
            },//更新内容
            updataContent: function(data) {
                var scope = document.getElementById("mediaedit");
                scope.innerHTML = data;
                // 获取选定对象
                var selection = window.getSelection();
                // 判断是否有最后光标对象存在
                if (this.cursortPosition!=null) {
                    // 存在最后光标对象,选定对象清除所有光标并添加最后光标还原之前的状态
                    selection.removeAllRanges()
                    selection.addRange(this.cursortPosition)
                }
                //this.setCaretPosition(scope, this.cursortPosition);
            }, //获取光标
            getCursortPosition: function(ctrl) {
                var CaretPos = 0; // IE Support
                if (document.selection) {
                    ctrl.focus();
                    var Sel = document.selection.createRange();
                    Sel.moveStart('character', -ctrl.value.length);
                    CaretPos = Sel.text.length;
                }
                // Firefox support
                else if (ctrl.selectionStart || ctrl.selectionStart == '0') {
                    CaretPos = ctrl.selectionStart;
                }

                return CaretPos;
            },//设置光标
            setCaretPosition: function(ctrl, position) {
                if (document.all) {
                    ctrl.range = document.selection.createRange();
                    ctrl.range.select();
                    ctrl.range.moveStart("character", -1);
                } else {
                    try {
                        ctrl.range = window.getSelection().getRangeAt(0);
                        //var words = value.replace(/<[^>]+>/g, "");
                        //words = words.replace(/\s*/g,"");
                        ctrl.range.setStart(ctrl.range.startContainer, position);
                    } catch (e) {
                        //TODO handle the exception
                    }

                }
            },
            insertHtmlAtCursor: function(html) {
                var range, node;
                if (window.getSelection && window.getSelection().getRangeAt) {
                    range = window.getSelection().getRangeAt(0);
                    node = range.createContextualFragment(html);
                    range.insertNode(node);
                } else if (document.selection && document.selection.createRange) {
                    document.selection.createRange().pasteHTML(html);
                }
            },
            chooseType() {
                document.getElementById("upload_file").click();
            },
            fileChange(el) {
                if (!el.target.files[0].size) return;
                this.fileList(el.target);
                el.target.value = "";
            },
            fileList(fileList) {
                let files = fileList.files;
                for (let i = 0; i < files.length; i++) {
                    //判断是否为文件夹
                    if (files[i].type != "") {
                        this.fileAdd(files[i]);
                    } else {
                        //文件夹处理
                        this.folders(fileList.items[i]);
                    }
                }
            },
            //文件夹处理
            folders(files) {
                let _this = this; //判断是否为原生file
                if (files.kind) {
                    files = files.webkitGetAsEntry();
                }
                files.createReader().readEntries(function(file) {
                    for (let i = 0; i < file.length; i++) {
                        if (file[i].isFile) {
                            _this.foldersAdd(file[i]);
                        } else {
                            _this.folders(file[i]);
                        }
                    }
                });
            },
            foldersAdd(entry) {
                let _this = this;
                entry.file(function(file) {
                    if (_this.limit !== undefined) _this.limit--;
                    if (_this.limit !== undefined && _this.limit < 0) return; //总大小
                    _this.size = _this.size + file.size;
                    _this.fileAdd(file);
                });
            },
            fileAdd(file) {
                //判断是否为图片文件
                if (file.type.indexOf("image") == -1) {
                    this.$dialog.toast({
                        mes: "请选择图片文件"
                    });
                } else {
                    let reader = new FileReader();
                    let image = new Image();
                    let _this = this;
                    reader.readAsDataURL(file);
                    reader.onload = function() {
                        file.src = this.result;
                        image.onload = function() {
                            let width = image.width;
                            let height = image.height;
                            file.width = width;
                            file.height = height;
                            _this.imgList.push({
                                file
                            });
                            console.log(_this.imgList);
                        };
                        image.src = file.src;
                        _this.$emit('loadimg', file.src);
                    };
                }
            },
            delImg(index) {
                this.size = this.size - this.imgList[index].file.size;
                //总大小
                this.imgList.splice(index, 1);
                if (this.limit !== undefined) this.limit = 6 - this.imgList.length;
            },
            selectText: function() {
                if (document.Selection) {
                    //ie浏览器
                    this.desc = document.selection.createRange().text;
                } else {
                    //标准浏览器
                    this.desc = window.getSelection().toString();
                }
            }
        }
    }
</script>
<style scoped="scoped">
    @import url("../css/write.css");

    .write-content-boxs {
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0;
        background-color: #FFFFFF;
        overflow-x: hidden;
        overflow-y: hidden;
    }

    .write-content-box {
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0;
        background-color: #FFFFFF;
        overflow-x: hidden;
        overflow-y: hidden;

    }

    .edit-title {
        width: 100%;
        padding: 1.8rem;
        font-size: 1.5rem;
        border: none;
    }

    .write-title {
        border-bottom: solid 1px gainsboro;
    }



    .write-tools {
        background-color: #545c64;
        width: 100%;
        text-align: center;
        line-height: 1rem;
        padding: 1rem;
    }

    .write-tools span {
        color: white;
        font-size: 1rem;
        border-right: solid 1px white;
        padding-left: 0.5rem;
        padding-right: 0.5rem;
    }

    .write-content {
        width: 96%;
        height: 96%;
        text-align: left;
        font-size: 1.2rem;
        -webkit-overflow-scrolling: touch;
        overflow-x: hidden;
        overflow-y: scroll;
        padding: 2%;
    }
</style>
markdown转换组件

js代码块——write.vue

<script type="text/javascript">
    /**
     * 转换标题
     * @param {Object} msg
     */
    function transitionTitle(msg) {
        var reg = /(#{1,5})(.*?)(<div><br><\/div>)/g;
        var objExp = new RegExp(reg);
        var value = msg.match(objExp);
        var now = msg.replace(objExp, function($1, $2, $3, $4) {
            if ($2.length == 1) {
                $2 = "<h1>"
                $4 = "</h1>" + $4;
            } else if ($2.length == 2) {
                $2 = "<h2>";
                $4 = "</h2>" + $4;
            } else if ($2.length == 3) {
                $2 = "<h3>";
                $4 = "</h3>" + $4;
            } else if ($2.length == 4) {
                $2 = "<h4>";
                $4 = "</h4>" + $4;
            } else if ($2.length == 5) {
                $2 = "<h5>"
                $4 = "</h5>" + $4;
            }

            return $2 + $3 + $4;
        });
        return now;
    }
    /**
     * ![转换图片](url)
     * @param {Object} msg
     */
    function transitionImage(msg) {
        var reg = /!\[(.*?)]\((.*?)\)/g;
        var objExp = new RegExp(reg);
        var now = msg.replace(objExp, function($1, $2, $3) {
            $3 = '<img class="image-src" src="' + $3 + '" />'
            $2 = '<h5 class="image-title">' + $2 + '</h5>';
            return $3 + $2;
        });
        return now;
    }
    /**
     * [转换地址]()
     * @param {Object} msg
     */
    function transitionHref(msg) {
        var reg = /\[(.*?)]\((.*?)\)/g;
        var objExp = new RegExp(reg);
        var now = msg.replace(objExp, function($1, $2, $3) {
            $2 = '<a href="' + $3 + '" >' + $2 + '</a>'
            return $2;
        });
        return now;
    }

    /**
     * **粗体**
     * @param {Object} msg
     */
    function transitionFontBoldItalic(msg) {
        var reg = /(\*{1,2})(.*?)(\*{1,2})/g;
        var objExp = new RegExp(reg);
        var now = msg.replace(objExp, function($1, $2, $3, $4) {
            if ($2 == '***') {
                $3 = '<b><i>' + $3 + '</i></b>'
            } else if ($2 == '**') {
                $3 = '<b>' + $3 + '</b>'
            } else if ($2 == '*') {
                $3 = '<i>' + $3 + '</i>'
            }

            return $3;
        });
        return now;
    }
    /**
     * ```
     * 转换代码块
     * ```
     * @param {Object} msg
     */
    function transitionCode(msg) {
        var reg = /(```<div><br><\/div>)(.*?)(```<div><br><\/div>)/g;
        var objExp = new RegExp(reg);
        var now = msg.replace(objExp, function($1, $2, $3, $4) {
            $3 = '<div class="code-content" >' + $3 + '</div>'
            return $3;
        });
        return now;
    }
    /**
     * ^引用
     * @param {Object} msg
     */
    function transitionQuote(msg) {
        var reg = /(\^)(.*?)(<div><br><\/div>)/g;
        var objExp = new RegExp(reg);
        var now = msg.replace(objExp, function($1, $2, $3, $4) {
            console.log($2)
            $3 = '<div class="quote-content" ><p class="quote-content-before"></p>' + $3 + '</div>' + $4
            return $3;
        });
        return now;
    }

/**
         * @字体颜色@rgb(FFFF00)
         * @param {Object} msg
         */
        function transitionFontColor(msg) {
            var reg = /(@)(.*?)(@rgb)\(([0-9a-fA-F]{6}|[0-9a-fA-F]{3})\)/g;
            var objExp = new RegExp(reg);
            var now = msg.replace(objExp, function($1, $2, $3, $4, $5) {
                
                console.log($5)
                console.log($3)
                $3 = '<span style="color:#'+$5+'" >' + $3 + '</span>';
                return $3;
            });
            return now;
        }
    function startTransition(value) {
        var data = transitionCode(value);
        data = transitionTitle(data);
        data = transitionFontBoldItalic(data);
        data = transitionQuote(data);
        data = transitionImage(data);
        data = transitionHref(data);
        data = transitionFontColor(data);
        return data;
    }

    export default {
        startTransition,
        transitionCode,
        transitionTitle,
        transitionFontBoldItalic,
        transitionQuote,
        transitionImage,
        transitionHref,
        transitionFontColor
    }
</script>

css代码块——write.css

#content {
    width: 100%;
    padding: 10rem;
    height: 100%;
    margin: 0;
    padding: 0;
}

.code-content {
    background-color: #666666;
    width: 100%;
    padding: 1rem;
    color: #CCCCCC;
}

.quote-content {
    display: flex;
    align-items: center;
    background-color: #EDEDED;
    line-height: 3rem;
    width: 100%;
}

.quote-content-before {
    width: 5px;
    line-height: 3rem;
    min-height: 3rem;
    background-color: #CCCCCC;
    margin: 0;
    margin-right: 1rem;
}

.image-title {
    text-align: center;
    font-size: 0.75rem;
    color: #CCCCCC;
}

.image-src {
    width: 100%;
    padding: 4px;
}

Markdown预览预览代码——PreView.vue
<template>
    <div id="page" class="page write-page">
        <el-row>
            <el-col :span="12">
                <div class="grid-content  content-left">
                    <writetool :title="articleTitle" :content="content" @onchange="onInput" @loadimg="onRequetImage" @onsave="onSaveArticle"
                     @onissue="onIssueArticle"></writetool>
                </div>
            </el-col>
            <el-col :span="12">
                <h3>{{articleTitle}}</h3>
                <div id="content" class="write-content"></div>
            </el-col>
        </el-row>


    </div>
</template>

<script>
    import writetool from '../element/WriteTool.vue'
    export default {
        components: {
            writetool
        },
        name: "page",
        data() {
            return {
                size: 0,
                articleTitle: "",
                content: "",
                desc: "测试内容测试内容测试内容测试内容。",
                userinfo: {},
                artId: 0,
                uid: "MnF/9XPJEprPvk/EwSLBAg=="
            };
        },
        watch: {
            //普通的watch监听
            articleTitle(val, oldVal) {

            },
            content(val, oldVal) {
                // console.log("a: " + val, this.artId);
            }
        },
        created() {
            this.artId = this.$route.params.id;
            this.initPage();
        },
        mounted: function() {

        },
        methods: {
            initPage: function() {
                let params = {
                    id: this.artId
                };
                console.log(params);
                var that = this;
                this.$axios.post(this.umcons.serviceAddress.ARTICLE, params).then(
                    function(response) {
                        console.log(response);
                        var result = response.data.data;
                        var article = result.article;
                        that.artId = article.id;
                        that.clickItem(article.artTitle, article.artDesc);

                    }.bind(this)
                );
            },
            onInput: function(text) {
                this.content = text;
                var data = this.write.startTransition(text);
                this.setTextContent(data)
            },
            handleSelect(key, keyPath) {

                console.log(key, keyPath);
            },
            handleOpen(key, keyPath) {
                console.log(key, keyPath);
            },
            handleClose(key, keyPath) {
                console.log(key, keyPath);
            },
            handleHrefUrlClose(done) {

            },
            setTextContent: function(text) {
                var mediaedit = document.getElementById("content");
                mediaedit.innerHTML = text;
            },
            clickItem: function(title, content) {
                    this.articleTitle = title;
                    this.content = content;
                    var data = this.write.startTransition(content);
                    this.setTextContent(data);
                }
                /**
                 * 保存文章
                 */
                ,
            onSaveArticle: function(data) {

                let params = {
                    id: this.artId,
                    title: data.title,
                    context: data.content,
                    uid: this.uid
                };
                console.log(params);
                var that = this;
                this.$axios.post(this.umcons.serviceAddress.UPDATA_ARTICLE, params).then(
                    function(response) {
                        var result = response.data.data;
                        console.log(result);
                        if (result == undefined) {
                            that.$message(response.data.msg);
                        } else {
                            //that.articleList[that.artId] = result;
                            that.$message("提交成功");
                        }
                    }.bind(this)
                );
            },

            /**
             * @param {Object} data发布文章
             */
            onIssueArticle: function(data) {

                let params = {
                    id: this.artId,
                    uid: this.uid,
                    is_release: 2,
                    title: data.title,
                    context: data.content
                };

                var that = this;
                this.$axios.post(this.umcons.serviceAddress.CHANGE_RELEASE, params).then(
                    function(response) {
                        var result = response.data.data;
                        console.log(result);
                        if (result == undefined) {
                            that.$message(response.data.msg);
                        } else {
                            that.$message("发布成功");
                            setTimeout(that.toDetails(result.id), 2000);
                        }
                    }.bind(this)
                );
            },
            toDetails: function(id) {
                this.$router.push({
                    name: "ArticleDetails",
                    params: {
                        id: id
                    }
                });
            },

            onRequetImage: function(imageSrc) {
                let params = {
                    file: imageSrc
                };

                var that = this;
                this.$axios.post(this.umcons.serviceAddress.UPLOAD_BASE, params).then(
                    function(response) {
                        var result = response.data.data;

                        if (result == undefined) {
                            that.$message(response.data.msg);
                        } else {
                            let title = result.split("/");
                            that.content += "![" + title[title.length - 1] + "](" + result + ")";
                            //console.log(that.content);
                        }
                    }.bind(this)
                );
            },
            selectText: function() {
                if (document.Selection) {
                    //ie浏览器
                    this.desc = document.selection.createRange().text;
                } else {
                    //标准浏览器
                    this.desc = window.getSelection().toString();
                }
            }

        }
    };
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
    @import url("../css/app.css");

    .el-row {
        padding: 0px;
        margin: 0px;
        width: 100%;
        height: 100%;
    }

    .write-page {
        overflow-y: hidden;

    }

    .el-col {
        border-radius: 4px;
        padding: 0px;
        margin: 0px;
        height: 100%;
        position: relative;
        border-left: solid 1px #DCDCDC;
    }

    .grid-content {
        width: 100%;
        overflow-y: hidden;
    }

    .content-left,
    .write-content {
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0;
        background-color: #FFFFFF;
        overflow-x: hidden;

    }

    .content-left {
        overflow-y: hidden;
        position: relative;
        width: 100%;
    }
    .write-content {
        width: 98%;
        text-align: left;
        font-size: 1.2rem;
        -webkit-overflow-scrolling: touch;
        overflow-x: scroll;
        padding: 1rem;

    }
</style>

备注:
1.案例基于vue+element开发,如果需要体验的朋友可以自行搭建环境
2.图片上传可以基于自己服务器上传,每次上传图片会重新刷新内容从而添加图片地址
3.关于光标定位,插入指定光标位置后续再完善。
4.mardown实际转换过程中,还有还有其余问题后期再继续完善更新。

项目地址:https://gitee.com/sunql-hugh/blog-vue.git

相关文章

网友评论

      本文标题:Markdown-富文本转换(2)

      本文链接:https://www.haomeiwen.com/subject/dfomkctx.html