定制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 = MarkdownIt();
return md
.set({
highlight: function(str, lang) {
const codeIndex =
w()) + 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 && Language(lang)) {
try {
const preCode = hljs.highlight(str, {language: lang, ignoreIllegals: true}).value;editor bar
html = html + preCode;
if (linesLength) {
html += '<b class="name">' + lang + "</b>";
}
return `<pre class="hljs"><code>${html}</code>${linesNum}</pre><textarea id="copy${codeIndex}">${place(
/<\/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 id="copy${codeIndex}">${place(
/<\/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进⾏双向绑定。这⾥采⽤的就是这种⼿段,只不过是⼿动的将代码块部分的处理进⾏了⼀定程度的定制,其他部分的代码是没有变化的(交给了MarkdownIt()去完成)。
注意好绑定⽅式以后就可以了。另外代码块的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,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAdCAYAAABcz8ldAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAhgSURBVGhD7Zp7bBTHHcdn33t7vvO  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(() => {
// 复制成功失败的提⽰
<("success", () => {
this.$message.success("复制成功");
});
<("error", () => {
this.$("复制失败");
});
});
把复制写到Tick⾥的原因是,因为⼀般给页⾯数据赋值,都是axios拿到数据以后了。但是有个问题,$axios是⼀个ajax请求,所以他给页⾯数据
赋值的时候,页⾯到底初始化到什么程度是⼀个未知的事情。所以要⽤nextTick在下次DOM更新循环结束之后执⾏延迟回调,在修改数据之后⽴即使⽤这个⽅法,获取更新后的
DOM。
另外就是要及时把Clipboard对象销毁掉,否则可能会出现点⼀下跳出来好⼏个成功弹窗这种情况。
Processing math: 100%