起因
- 在一个产品需求中,协议是后台模版管理的,模版是
markdown
语法编写的,并且协议中存在一些需要填充的信息。协议内容是在后台模版管理的markdown
语法编辑器中编辑转义好后,发送到后端,再由后端进行安全相关的转义处理,最后返回给前端用户界面一个带变量的markdown
协议字符串。
方案
方案一 marked 配合 highlight.js
- 安装
npm install marked highlight.js --save-dev
# OR
yarn add marked highlight.js --save-dev
- 使用
- 将变量替换后,把协议字符串通过
getHtml
方法转换为html,然后使用v-html
进行渲染。
<template>
<div v-html="getHtml"></div>
</template>
<script>
let marked = require('marked');
let hljs = require('highlight.js');
import 'highlight.js/styles/default.css';
marked.setOptions({
renderer: new marked.Renderer(),
gfm: true,
tables: true,
breaks: false,
pedantic: false,
sanitize: false,
smartLists: true,
smartypants: false,
highlight: function (code, lang) {
if (lang && hljs.getLanguage(lang)) {
return hljs.highlight(lang, code, true).value;
} else {
return hljs.highlightAuto(code).value;
}
}
});
export default{
name: 'protocol',
props: {
protocol: {
type: String,
default: ''
}
}
methods: {
getHtml() {
return marked(this.protocol || '', {
sanitize: true
});
}
}
}
- 问题
- 协议中的变量被替换后,会存在一些特殊的符号,例如脱敏字段的
**
,以及-
、1.
、#
等等markdown
相关的语法,会导致最后生成的HTML与后台创建的协议有出入,并且格式错乱等问题。
方案二 mavon-editor 配合 highlight.js
- 安装
npm install mavon-editor --save-dev
# OR
yarn add mavon-editor --save-dev
- 使用
- 将变量替换后,把协议字符串直接赋值给组件的
value
进行渲染。
<template>
<mavon-editor
class="md"
:value="protocol"
:subfield = "false"
:defaultOpen = "'preview'"
:toolbarsFlag = "false"
:editable="false"
:scrollStyle="true"
:ishljs = "true"
></mavon-editor>
</template>
<script>
import mavonEditor from 'mavon-editor'
import 'mavon-editor/dist/css/index.css'
export default{
name: 'protocol',
props: {
protocol: {
type: String,
default: ''
}
}
}
- 问题
- 与方案一问题相同
最终解决方案 vue-element-admin markdown
MarkdownEditor-gitlab markdown-官网
- 安装
npm install tui-editor --save-dev
# OR
yarn add tui-editor --save-dev
- 使用
- 将后端返回的协议字符串不做任何处理,直接通过组件的
setValue
方法传给组件作为编辑器的初始markdown
语法的编辑值,赋值完成后通过组件的getHtml
方法将markdown
转换为HTML
字符串,拿到HTML
字符串后替换字符串中的所有变量。替换完成后调用动态生成iframe
的方法显示协议内容。
<template>
<div class="protocol-popup">
<div id="self-iframe" class="popup-main">
<div class="close-btn" @click="hideDialog('isShowAbsProtocol')"><img src="../../assets/images/loan/btn_popup_close@2x.png"></div>
<div style="display:none;" :id="id" />
</div>
</div>
</template>
<script>
import { protocolVarsReplace } from '../../filter/index'
import 'codemirror/lib/codemirror.css' // codemirror
import 'tui-editor/dist/tui-editor.css' // editor ui
import 'tui-editor/dist/tui-editor-contents.css' // editor content
import Editor from 'tui-editor'
const defaultOptions = {
... // 参照官网
}
export default {
name: 'MarddownEditor',
props: {
...,
protocol_name: {
type: String,
default: '测试'
},
protocol_object: {
type: Object,
default: {}
},
protocol: {
type: String,
default: ''
}
},
data() {
return {
editor: null,
content: ''
}
},
computed: {
editorOptions() {
const options = Object.assign({}, defaultOptions, this.options)
options.initialEditType = this.mode
options.height = this.height
options.language = this.language
return options
}
},
watch: {
...
},
mounted() {
this.initEditor()
this.editor.setValue(this.protocol) // 设置初始值
let content = `<h3 style="text-align: center;">${this.protocol_name}</h3>${protocolVarsReplace(this.getHtml(),this.protocol_object)}` // 生成HTML字符串并所有protocolVarsReplace方法替换变量
this.createFrame(1, content) // 动态生成iframe并显示
},
destroyed() {
this.destroyEditor()
},
methods: {
createFrame( data, child) {
const that = this
const iframe = document.createElement("iframe");
iframe.id = 'my-iframe'
iframe.style.width = '100%';
iframe.style.height = '100%';
iframe.style.border = 'none';
if (data) {
if (navigator.userAgent.indexOf("MSIE") > -1 && !window.opera) {
iframe.onreadystatechange = function () {
if (iframe.readyState === "complete") { iframe.contentWindow.postMessage(JSON.stringify(data), '*')
}
};
} else {
iframe.onload = function () { iframe.contentWindow.postMessage(JSON.stringify(data), '*')
};
}
}
document.getElementById('self-iframe').appendChild(iframe);
const iframeDom = document.getElementById('my-iframe').contentWindow.document
iframeDom.getElementsByTagName('body')[0].innerHTML = child
document.getElementById('my-iframe').onload = function() {
that.loading = false
}
},
hideDialog(name){
this.$parent.hideDialog(name);
},
initEditor() {
...
},
destroyEditor() {
if (!this.editor) return
this.editor.off('change')
this.editor.remove()
},
setValue(value) {
this.editor.setValue(value)
},
getValue() {
return this.editor.getValue()
},
setHtml(value) {
this.editor.setHtml(value)
},
getHtml() {
return this.editor.getHtml()
}
}
}
</script>