Vue寫一個Markdown編輯器

JesseLuo發表於2017-08-29

這是最近用Vue寫的一個Markeddown編輯器, 主要目的是擴充套件Vue-Manager的編輯器功能。核心功能引入了Marked外掛,將Markedown文件解析為html。樣式基本沿用了vm-editor,並增加了多種主題選擇的功能。

專案已經打包上傳到npm,歡迎使用。

預覽地址 luosijie.github.io/vm-markdown…
原始碼地址 github.com/luosijie/vm…

安裝

npm install --save vm-markdwon複製程式碼

使用

import VmMarkdown from 'vm-markdwon'
export default {
  ...
  components: {
      VmMarkdown
  },
  methods: {
      showHtml (data) {
      console.log(data)
    }
  }
  ...
}複製程式碼
<VmMarkdown :theme="theme" 
            width="1000px" 
            height="600px" 
            v-on:getHtml="showHtml"
            :defaultText="intro">
</VmMarkdown>複製程式碼

功能實現

vm-markdown作為一款 以簡潔易用為目標 的編輯器, 核心解析功能由 Marked 來完成, 而其他功能主要為優化部分不熟悉Markdown語法使用者的使用體驗。
主要功能可以分為:

  • 將Markdown文字插入編輯框
  • 將Mardown文字解析為html,並實時預覽
  • 將Makdown解析的html加入自定義樣式
  • 實現表格的的快速輸入功能
  • 實現編輯區域的縮放功能

Markdown文字插入

Markdown文字輸入
Markdown文字輸入

相容Firefox瀏覽器的文字插入函式

function insertText(dom,string) {
  if (document.execCommand('insertText', false, string)) {
    return
  }else{
    let start = dom.selectionStart
    let end = dom.selectionEnd
    dom.value = dom.value.substring(0, start) + string + dom.value.substring(end, dom.value.length)
    dom.selectionStart = start + string.length;
    dom.selectionEnd = start + string.length;
    dom.focus()
  }
}
export default insertText複製程式碼
  import insertText from '../assets/js/insertText.js'
  ...
  methods: {
    insertText(string){
      let content = document.querySelector('.vm-markdown-content')
      insertText(content, string)
      this.$emit('textChange', content.value)
    }
  }複製程式碼

按鈕繫結 insertText(string) 事件

  ...
  <VmMarkdownButton icon="iconfont icon-bold" @click.native="insertText(' **Bold** ')"></VmMarkdownButton>
  <VmMarkdownButton icon="iconfont icon-italic" @click.native="insertText(' *Italic* ')"></VmMarkdownButton>
  ...複製程式碼

Markdown文字解析為html

解析Markdown為html
解析Markdown為html

Dom結構

  ...
  <div class="vm-markdown-edit" :style="{backgroundColor: themeValue.bgLeft}">
    // 輸入部分
    <textarea v-focus class="content-markdown" v-model="markdString"></textarea>
  </div>
  // 實時預覽部分
  <div class="vm-markdown-html" v-html="htmlString" :style="{backgroundColor: themeValue.bgRight}">
  </div>
  ...複製程式碼

引入 Marked 解析, 並實時預覽

  import marked from 'marked'
  watch: {
    markdString(value){
      marked.setOptions({
        renderer: new marked.Renderer(),
        gfm: true,
        tables: true,
        breaks: true,
        pedantic: false,
        sanitize: false,
        smartLists: true,
        smartypants: false
      })
      this.htmlString = marked(value)
      ...
    }
  },複製程式碼

增加自定義樣式

樣式化html
樣式化html

因為 Marked 解析出來的html,是不帶任何樣式的,所以需要自定義樣式,並確保最後輸出帶樣式的html字串

  parseHtml: function () {
      let style = {
        ul: `
              margin: 10px 20px;
              list-style-type: square;
              padding: 0;
            `,
        ol: `
              margin: 10px 20px;
              list-style-type: decimal;
              padding: 0;
            `,
        li: `
              display: list-item;
              padding: 0;
            `,
        hr: `
              margin: 15px 0;
              border-top: 1px solid #eeeff1;
            `,
        pre: `
              display: block;
              margin: 10px 0;
              padding: 8px;
              border-radius: 4px;
              background-color: #f2f2f2;
              color: #656565;
              font-size: 14px;
             `,
        blockquote: `
                      display: block;
                      border-left: 4px solid #ddd;
                      margin: 15px 0;
                      padding: 0 15px;
                    `,
        img: `
               margin: 20px 0;
             `,
        a: `
            color: #41b883;
           `,
        table: `
                 border: 1px solid #eee;
                 border-collapse: collapse;
               `,
        tr: `
              border: 1px solid #eee;
            `,
        th: `
              padding: 8px 30px;
              border-right: 1px solid #eee;
              background-color: #f2f2f2;
            `,
        td: `
              padding: 8px 30px;
              border-right: 1px solid #eee;
            `
      }
      let html = document.getElementsByClassName('vm-markdown-html')[0]
      let tagNames = Object.keys(style)
      for (let i = 0; i < tagNames.length; i++) {
        let _tagNames = html.getElementsByTagName(tagNames[i])
        if (_tagNames.length > 0) {
          for (let j = 0; j < _tagNames.length; j++) {
            _tagNames[j].style = style[tagNames[i]]
          }
        }
      }
    },
    getHtml: function () {
      let html = document.querySelector('.vm-markdown-html')
      this.$emit('getHtml', html.innerHTML)
    }
  },複製程式碼

表格的的快速輸入

表格輸入
表格輸入

Markdown的表格是相對繁瑣的輸入,vm-markown借用圖形化的操作實現快捷輸入

  <ul class="vm-markdown-table" v-insertTable:color="filterColor">
  </ul>複製程式碼
  directives:{
    insertTable: {
      inserted: function(el,binding){
          // 定義總單元格數目 4*6 = 24
          let length = 24
          // 滑鼠所在的單元格的座標
          let x = 0, y = 0
          // 每個單元格賦值:行和列的座標
          for(let i=0; i<length; i++){
            let setx = i%6 + 1
            let sety = parseInt(i/6) + 1
            let li = document.createElement('li')
            li.setAttribute('data-x', setx)
            li.setAttribute('data-y', sety)
            el.appendChild(li)
          }
          // 滑鼠滑過改變顏色
          el.addEventListener('mouseover', function(evt){
            if (evt.target.tagName === 'LI') {
              x= evt.target.getAttribute('data-x')
              y= evt.target.getAttribute('data-y')
              let lis = el.querySelectorAll('li')
              for(let i=0; i<lis.length; i++){
                lis[i].style.backgroundColor = '#e0e0e0'
                if(lis[i].dataset.x <= x && lis[i].dataset.y <= y){
                  lis[i].style.backgroundColor = binding.value
                }
              }
            }
          })
          // 單擊插入表格字串
          el.addEventListener('click', function(evt){
            if(x && y){
              let th = '| Head '
              let td = '| Data '
              let tl = '| ---  '
              let str = ''
              let ths = '', tls = '', tds = ''
              for(let i=0; i<x; i++){
                ths = ths.concat(th)
                tls = tls.concat(tl)
              }
              for(let j=0; j<y; j++){
                for(let k=0; k<x; k++){
                  tds = tds.concat(td)
                }
                tds += ' |\n'
              }
              ths += ' |\n'
              tls += ' |\n'
              str += ths + tls + tds
              document.execCommand('insertText', false, str)
            }         
          })
       }
    }
  }複製程式碼

編輯區的縮放功能

縮放功能
縮放功能

實現縮放的layout函式

  layout: function (event) {
      let VmMarkdown = document.querySelector('.vm-markdown')
      let VmMarkdownEdit = document.querySelector('.vm-markdown-edit')    
      function classHas(str){
        return event.target.classList.contains(str)
      }
      if(classHas('icon-layout-zoom')){

        if (VmMarkdown.style.position === 'fixed') {
          VmMarkdown.style = 'width:' + this.width + ';' +
                             'height:' + this.height + ';'
        }else{
          VmMarkdown.style.position = 'fixed'
          VmMarkdown.style.left = '0'
          VmMarkdown.style.top = '0'
          VmMarkdown.style.margin = '0'
          VmMarkdown.style.width = '100%'
          VmMarkdown.style.height = '100%'
        }   
      }else if (classHas('icon-layout-left')) {
        VmMarkdownEdit.style.width = '0'
      }else if (classHas('icon-layout-right')) {
        VmMarkdownEdit.style.width = '100%'
      }else if (classHas('icon-layout-default')) {
        VmMarkdownEdit.style.width = '50%'
      }
    },複製程式碼

將layout繫結到頂部選單的點選事件中

<VmMarkdownMenu  @click.native="layout"></VmMarkdownMenu>複製程式碼

先這樣了 歡迎star

相關文章