一、介绍

Markdown是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式。mavonEditor是国人开源的一款比较好用的markdown编辑器,GitHub地址:https://github.com/hinesboy/mavonEditor

二、安装

npm install mavon-editor --save

如果下载不了或安装失败可以(当然你得装了cnpm)

cnpm install mavon-editor --save

如果没装cnpm,也可以

npm install mavon-editor --save --registry=https://registry.npm.taobao.org

这是国人开发的,一般npm即可

三、使用

main.js:

// 全局注册
    // import with ES6
    import Vue from 'vue'
    import mavonEditor from 'mavon-editor'
    import 'mavon-editor/dist/css/index.css'
    // use
    Vue.use(mavonEditor)
    new Vue({
        'el': '#main',
        data() {
            return { value: '' }
        }
    })

index.vue:

<template>
  <div class="app-container">
    <el-form :label-position="'left'" :model="markdownForm" :rules="rules"   label-width="60px" >
      <el-form-item label="标 题" prop="title">
        <el-input v-model="markdownForm.title"></el-input>
      </el-form-item>
    </el-form>
    <mavon-editor ref="md" @save="save" @imgAdd="imgAdd" @imgDel="imgDel" v-model="markdownForm.contentMarkdown" :codeStyle="markdown.codeStyle" :toolbars="markdown.toolbars"   />
    <br>
    <el-row>
      <el-col :span="2">
        <el-button @click="goback">返 回</el-button>
      </el-col>
      <el-col :span="2" :offset="20">
        <el-button type="primary" style="float: right;" @click="save">保 存</el-button>
      </el-col>

    </el-row>
  </div>
</template>

<script>
  import {getMarkdownArticle,saveMarkdownArticle} from '@/api/markdownArticle'
  import {uploadImg} from '@/api/markdownArticleImg'

  export default {
  data() {
    return {
      markdown:{
        codeStyle:'atom-one-dark',
        toolbars:{
          bold: true, // 粗体
          italic: true, // 斜体
          header: true, // 标题
          underline: true, // 下划线
          strikethrough: true, // 中划线
          mark: true, // 标记
          superscript: true, // 上角标
          subscript: true, // 下角标
          quote: true, // 引用
          ol: true, // 有序列表
          ul: true, // 无序列表
          link: true, // 链接
          imagelink: true, // 图片链接
          code: true, // code
          table: true, // 表格
          fullscreen: true, // 全屏编辑
          readmodel: true, // 沉浸式阅读
          htmlcode: true, // 展示html源码
          help: true, // 帮助
          /* 1.3.5 */
          undo: true, // 上一步
          redo: true, // 下一步
          trash: true, // 清空
          save: true, // 保存(触发events中的save事件)
          /* 1.4.2 */
          navigation: true, // 导航目录
          /* 2.1.8 */
          alignleft: true, // 左对齐
          aligncenter: true, // 居中
          alignright: true, // 右对齐
          /* 2.2.1 */
          subfield: true, // 单双栏模式
          preview: false, // 预览
        }
      },
      markdownForm : {
        articleId:null,
        title:"",
        contentMarkdown:'',
        contentHtml:null,
        type:0
      },
      rules: {
        title: [
          { required: true, message: '请输入标题', trigger: 'blur' },
          { min: 1, max: 80, message: '长度在1到80个字符', trigger: 'blur' }
        ]
      },
      lastSaveTime:null,
      timer:null
    }
  },
  created(){
    this.getArticle() 
  },
  mounted() {
    this.timer = setInterval(this.intervalSave, 2*60*1000);
  },
  beforeDestroy() {
    clearInterval(this.timer);
  },
  methods: {
   getArticle(){  //获取文章内容
     let id=this.$route.query.a
     if(id==null){
       this.markdownForm.contentMarkdown=''
     }else{
       getMarkdownArticle(id).then(r => {
         this.markdownForm.contentMarkdown=r.data.contentMarkdown==null?'':r.data.contentMarkdown
         this.markdownForm.articleId=r.data.articleId
         this.markdownForm.title=r.data.title
         this.markdownForm.type=r.data.type
       }).catch(e => {
         console.log(e)
       })
     }
   },
   save(){  //保存文章内容
     saveMarkdownArticle(this.markdownForm).then(r => {
       this.$message.success("保存成功")
       this.markdownForm.articleId=r.data.articleId
       this.lastSaveTime=new Date()
     }).catch(e => {
       console.log(e)
     })
   },
   intervalSave(){ //自动保存
      let now = new Date()
      if(now-this.lastSaveTime>=2*60*1000){
        saveMarkdownArticle(this.markdownForm).then(r => {
          this.$message.success("自动保存成功")
          this.lastSaveTime=new Date()
        }).catch(e => {
          console.log(e)
        })
      }
   },
   imgAdd(pos, file){ //添加图片,pos为位置
      let markdownImg = {},$vm=this.$refs.md
      markdownImg.base64Data=file.miniurl  //获取图片base64内容
      markdownImg.type=file.type
      uploadImg(markdownImg).then(r => {
        console.log(r)
        $vm.$img2Url(pos,process.env.VUE_APP_BASE_API+'/img/'+r.data) 
      }).catch(e => {
        console.log(e)
      })
    },
    imgDel(pos,url){ //删除图片,并不是修改就会触发,仅支持工具栏操作
      console.log(pos)
      console.log(url)
    }
  }
}
</script>

<style lang="scss">
.v-note-panel.shadow{
  min-height: 440px;  //设置编辑框最小高度
}
</style>

api/markdownArticle:

import request from '@/utils/request'

export function getMarkdownArticles() {
  return request({
    url: '/edu/markdown/articles',
    method: 'get'
  })
}
export function getMarkdownArticle(id) {
  return request({
    url: '/edu/markdown/article/'+id,
    method: 'get'
  })
}
export function saveMarkdownArticle(data) {
  return request({
    url: '/edu/markdown/article',
    method: 'post',
    data
  })
}

/**
 * 发布/取消发布
 * @param data
 */
export function updateStatus(data) {
  return request({
    url: '/edu/markdown/article/status',
    method: 'post',
    data
  })
}

/**
 * 删除
 * @param data
 */
export function del(data) {
  return request({
    url: '/edu/markdown/article',
    method: 'delete',
    data
  })
}

api/markdownArticleImg

import request from '@/utils/request'

export function uploadImg(data) {
  return request({
    url: '/edu/markdown/img',
    method: 'put',
    data
  })
}

utils/request仅是对axios的简单封装,没有特殊处理,参照vue-element-admin里request的写法

四、详细

props

name 名称

type 类型

default 默认值

describe 描述

value

String

 

初始值

language

String

zh-CN

语言选择,暂支持 zh-CN: 中文简体 , en: 英文 , fr: 法语, pt-BR: 葡萄牙语, ru: 俄语, de: 德语, ja: 日语

fontSize

String

15px

编辑区域文字大小

scrollStyle

Boolean

true

开启滚动条样式(暂时仅支持chrome)

boxShadow

Boolean

true

开启边框阴影

subfield

Boolean

true

true: 双栏(编辑预览同屏), false: 单栏(编辑预览分屏)

defaultOpen

String

 

edit: 默认展示编辑区域 , preview: 默认展示预览区域 , 其他 = edit

placeholder

String

开始编辑...

输入框为空时默认提示文本

editable

Boolean

true

是否允许编辑

codeStyle

String

code-github

markdown样式: 默认github, 可选配色方案

toolbarsFlag

Boolean

true

工具栏是否显示

navigation

Boolean

false

默认展示目录

shortCut

Boolean

true

是否启用快捷键

autofocus

Boolean

true

自动聚焦到文本框

ishljs

Boolean

true

代码高亮

imageFilter

function

null

图片过滤函数,参数为一个File Object,要求返回一个Booleantrue表示文件合法,false表示文件不合法

imageClick

function

null

图片点击事件,默认为预览,可覆盖

tabSize

Number

\t

tab转化为几个空格,默认为\t

toolbars

Object

如下例

工具栏

 

events 事件绑定

name 方法名

params 参数

describe 描述

change

String: value , String: render

编辑区发生变化的回调事件(render: value 经过markdown解析后的结果)

save

String: value , String: render

ctrl + s 的回调事件(保存按键,同样触发该回调)

fullScreen

Boolean: status , String: value

切换全屏编辑的回调事件(boolean: 全屏开启状态)

readModel

Boolean: status , String: value

切换沉浸式阅读的回调事件(boolean: 阅读开启状态)

htmlCode

Boolean: status , String: value

查看html源码的回调事件(boolean: 源码开启状态)

subfieldToggle

Boolean: status , String: value

切换单双栏编辑的回调事件(boolean: 双栏开启状态)

previewToggle

Boolean: status , String: value

切换预览编辑的回调事件(boolean: 预览开启状态)

helpToggle

Boolean: status , String: value

查看帮助的回调事件(boolean: 帮助开启状态)

navigationToggle

Boolean: status , String: value

切换导航目录的回调事件(boolean: 导航开启状态)

imgAdd

String: filename, File: imgfile

图片文件添加回调事件(filename: 写在md中的文件名, File: File Object)

imgDel

String: filename

图片文件删除回调事件(filename: 写在md中的文件名)

代码高亮

如不需要hightlight代码高亮显示,你应该设置ishljs为false,默认开启所以不需要设置,主要是选择代码样式,即codeStyle属性,可以先到方案对应样式里找到你想要的样式,再到可选配色方案里找具体的对应名称

图片上传

<template>
    <mavon-editor ref=md @imgAdd="$imgAdd" @imgDel="$imgDel"></mavon-editor>
</template>
exports default {
    methods: {
        // 绑定@imgAdd event
        $imgAdd(pos, $file){
            // 第一步.将图片上传到服务器.
           var formdata = new FormData();
           formdata.append('image', $file);
           axios({
               url: 'server url',
               method: 'post',
               data: formdata,
               headers: { 'Content-Type': 'multipart/form-data' },
           }).then((url) => {
               // 第二步.将返回的url替换到文本原位置![...](0) -> ![...](url)
               /**
               * $vm 指为mavonEditor实例,可以通过如下两种方式获取
               * 1. 通过引入对象获取: `import {mavonEditor} from ...` 等方式引入后,`$vm`为`mavonEditor`
               * 2. 通过$refs获取: html声明ref : `<mavon-editor ref=md ></mavon-editor>,`$vm`为 `this.$refs.md`
               */
               $vm.$img2Url(pos, url);
           })
        }
    }
}

  • 默认大小样式为 min-height: 300px , min-width: 300px 可自行覆盖
  • 基础z-index: 1500
  • 仅用作展示可以设置props: toolbarsFlag: false , subfield: false, defaultOpen: "preview"

这里也提到了,当我们仅需要展示的时候,可以设置 toolbarsFlag: false , subfield: false, defaultOpen: "preview",即

<mavon-editor :toolbarsFlag="false"  :subfield="false" defaultOpen="preview" v-model="article.contentMarkdown" :codeStyle="markdown.codeStyle"/>