定制mavon-editor


目的

之前在網上看到了一個mavon-editor魔改的版本,給代碼塊加上了行號和一個title,另外加了一個復制按鈕。當時由於種種原因並沒有去實踐。但是這樣天突然之間我又看到了那段代碼,於是自己就過去試了試,最后還是成功做好了。不過里面還是有些坑的。樣式是這樣:

這個樣式加上以后,我才發覺實際上mavon-editor會對樣式信息進行緩存,比如我在文章詳情加載過之后,到編輯的預覽界面也是一樣的樣式了。

思路

思路大概就是對mavon-editor使用的markdown-it流程進行定制,具體而言是把調用highlight.js的過程定制以下,首先建這樣一個js文件:

const hljs = require("highlight.js");

export const markdown = (mavonEditor ,content) => {
    const md = mavonEditor.getMarkdownIt();
    return md
        .set({
            highlight: function(str, lang) {
                const codeIndex =
                    parseInt(Date.now()) + Math.floor(Math.random() * 10000000);
                let html = `<button class="copy-btn" type="button" data-clipboard-action="copy" data-clipboard-target="#copy${codeIndex}">copy</button>`;

                const linesLength = str.split(/\n/).length - 1;
                console.log(linesLength)
                let linesNum = '<span aria-hidden="true" class="line-numbers-rows">';
                for (let index = 0; index < linesLength; index++) {
                    linesNum = linesNum + "<span></span>";
                }
                linesNum += "</span>";
                if (lang && hljs.getLanguage(lang)) {
                    try {
                        const preCode = hljs.highlight(str, {language: lang, ignoreIllegals: true}).value;
                        html = html + preCode;
                        if (linesLength) {
                            html += '<b class="name">' + lang + "</b>";
                        }
                        return `<pre class="hljs"><code>${html}</code>${linesNum}</pre><textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy${codeIndex}">${str.replace(
                            /<\/textarea>/g,
                            "</textarea>"
                        )}</textarea>`;
                    } catch (error) {
                        console.log(error);
                        return
                    }
                }

                const preCode = md.utils.escapeHtml(str);
                html = html + preCode;
                return `<pre class="hljs"><code>${html}</code>${linesNum}</pre><textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy${codeIndex}">${str.replace(
                    /<\/textarea>/g,
                    "</textarea>"
                )}</textarea>`;
            },
        }).render(content);
};


                

mavon-editor在工作的時候,有兩種類型。

  • 一種是以v-model的形式,將markdown格式的內容直接雙向綁定給mavon-editor標簽。這種情況下,mavon-editor會調用其集成的markdown-it對markdown語法的內容進行解析,轉換成html的標簽,然后展示在其預覽界面上(用mavon-editor的話,展示文章也必然是用這個編輯器,將編輯模式默認關閉)。

  • 二種是手動將markdown語法的內容轉換成html,然后再用v-html的形式和mavon-editor進行雙向綁定。這里采用的就是這種手段,只不過是手動的將代碼塊部分的處理進行了一定程度的定制,其他部分的代碼是沒有變化的(交給了mavonEditor.getMarkdownIt()去完成)。

注意好綁定方式以后就可以了。另外代碼塊的title是一個base64編碼的圖片。less的形式如下:

pre.hljs {
  padding: 40px 2px 12px 40px !important;
  border-radius: 5px !important;
  position: relative;
  font-size: 14px !important;
  line-height: 22px !important;
  overflow: hidden !important;
  code {
    display: block !important;
    margin: 0 10px !important;
    overflow-x: auto !important;
    &::-webkit-scrollbar {
      z-index: 11;
      width: 6px;
    }
    &::-webkit-scrollbar:horizontal {
      height: 6px;
    }
    &::-webkit-scrollbar-thumb {
      border-radius: 5px;
      width: 6px;
      background: #666;
    }
    &::-webkit-scrollbar-corner,
    &::-webkit-scrollbar-track {
      background: #1e1e1e;
    }
    &::-webkit-scrollbar-track-piece {
      background: #1e1e1e;
      width: 6px;
    }
  }
  // 行號樣式
  .line-numbers-rows {
    position: absolute;
    pointer-events: none;
    top: 40px;
    bottom: 12px;
    left: 0;
    font-size: 100%;
    width: 40px;
    text-align: center;
    letter-spacing: -1px;
    border-right: 1px solid rgba(0, 0, 0, 0.66);
    user-select: none;
    counter-reset: linenumber;
    span {
      pointer-events: none;
      display: block;
      counter-increment: linenumber;
      &:before {
        content: counter(linenumber);
        color: #999;
        display: block;
        text-align: center;
      }
    }
  }
  // 代碼語言
  b.name {
    position: absolute;
    top: 4px;
    right: 60px;
    z-index: 10;
    color: #999;
    pointer-events: none;
  }
  // 復制按鈕樣式
  .copy-btn {
    position: absolute;
    top: 4px;
    right: 4px;
    z-index: 10;
    color: #333;
    cursor: pointer;
    border: 0;
    border-radius: 2px;
    outline: none;
  }
}

.markdown-body pre.hljs {
  background: #23241f;
}

// 漂亮的title
pre.hljs::after {
  height: 15px;
  width: 100px;
  margin-bottom: -7px;
  border-radius: 5px 5px 0 0;
  display: block;
  content: "";
  background: url(data:image/png;base64,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)
  10px center no-repeat;
  background-size: contain;
  position: absolute;
  top: 10px;
  left: 0;
  box-sizing: border-box;
  z-index: 1;
}

復制

復制部分用的是Clipboard,但是這個東西用起來還是有點小坑的。因為需要復制的形式不太好綁定一個單擊事件,所以代碼是這樣的:

clipboard = new Clipboard(".copy-btn");
        this.$nextTick(() => {
          // 復制成功失敗的提示
          clipboard.on("success", () => {
            this.$message.success("復制成功");
          });
          clipboard.on("error", () => {
            this.$message.error("復制失敗");
          });
        });

把復制寫到this.\(nextTick里的原因是,因為一般給頁面數據賦值,都是\)axios拿到數據以后了。但是有個問題,$axios是一個ajax請求,所以他給頁面數據賦值的時候,頁面到底初始化到什么程度是一個未知的事情。所以要用nextTick在下次DOM更新循環結束之后執行延遲回調,在修改數據之后立即使用這個方法,獲取更新后的DOM。

另外就是要及時把Clipboard對象銷毀掉,否則可能會出現點一下跳出來好幾個成功彈窗這種情況。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM