一、使用marked
第一步:下载marked和代码块高亮highlight.js
npm i marked
npm i highlight.js
npm i markdown-loader
npm i github-markdown-css
第二步:注册并使用
main.js
import hljs from "highlight.js";
import "github-markdown-css";
import "highlight.js/styles/atom-one-dark.css";
Vue.directive("highlight", function (el) {
let blocks = el.querySelectorAll("pre code");
blocks.forEach((block) => {
hljs.highlightBlock(block);
});
});
页面内使用
1、其中class的markdown-body是必须要带的,marked的样式,同时如果需要修改样式,也可以通过此修改
2、v-highlight全局注册代码块高亮
3、自定义渲染器我是需要改变标题转换,如果你们有需要可以自行处理
<template>
<div id="Mindopt" class="Mindopt">
<div v-highlight class="replybox markdown-body" v-html="Mindoptinfo"></div>
</div>
</template>
<script>
// 创建自定义渲染器
class CustomRenderer extends marked.Renderer {
heading (text, level) {
// 将一级标题转换为h1标签
if (level === 1) {
return `<h1 class="hClass"># ${text}</h1>`;
} else if (level === 2) {
return `<h2 class="hClass">## ${text}</h2>`;
} else if (level === 3) {
return `<h3 class="hClass">### ${text}</h3>`;
} else if (level === 4) {
return `<h4 class="hClass">#### ${text}</h4>`;
} else if (level === 5) {
return `<h5 class="hClass">##### ${text}</h5>`;
} else if (level === 6) {
return `<h6 class="hClass">###### ${text}</h6>`;
}
}
// text (text) {
// console.log(text);
// }
// code (code, language, isEscaped) {
// console.log(language);
// if (language && language === 'math') {
// return katex.renderToString(code, { throwOnError: false });
// }
// return marked.Renderer.prototype.code.call(renderer, code, language, isEscaped);
// };
}
// 使用自定义渲染器
const renderer = new CustomRenderer();
const markedOptions = {
renderer: renderer,
breaks: true,
};
import { marked } from "marked";
import { getoptInfo, getGroupMessage, updataGroup, deleteGroupMessages } from '@/api/Mindopt'
export default {
name: 'Mindopt',
data () {
return {
Mindoptinfo: '',
}
},
created () {
this.getoptInfo()
},
methods: {
// 获取信息
async getoptInfo () {
const res = await getoptInfo()
this.Mindoptinfo = res.data
this.Mindoptinfo = marked(this.Mindoptinfo, markedOptions);
},
}
}
</script>
<style lang="scss">
// 回答的格式或者数据回显的格式
.markdown-body {
// font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB,
// Microsoft YaHei, Arial, sans-serif !important;
// line-height: 20px;
// & ul {
// list-style: none;
// padding-left: 20px;
// }
color: #000 !important;
p {
margin-top: 10px !important;
margin-bottom: 10px !important;
}
pre {
padding: 5px !important;
margin-bottom: 10px !important;
}
.hljs {
color: #abb2bf;
background: #282c34;
}
.hClass {
//出现#则不转换为h1等标签
font-size: 16px;
color: #8a2328;
font-weight: 600;
margin: 10px 0;
}
/* 只改变普通 code 标签的颜色,不影响 pre 中的 code */
code:not(pre) {
color: red;
font-weight: 600;
background-color: rgba(175, 184, 193, 0.3);
margin: 0 5px;
}
a {
color: #1d71f7 !important;
}
}
</style>
二、代码块和块引用添加复制按钮
页面使用
<template>
<div id="Mindopt" class="Mindopt">
<div v-highlight class="replybox markdown-body" v-html="Mindoptinfo"></div>
</div>
</template>
<script>
import CodeCopy from './CodeCopy.vue'
export default {
name: 'Mindopt',
data () {
return {
Mindoptinfo: '',
}
},
updated () {
this.update()
},
methods: {
//获取对应markdown代码块标签
update () {
setTimeout(() => {
// 代码块添加复制按钮
document.querySelectorAll('pre').forEach(el => {
// console.log(el)
if (el.classList.contains('code-copy-added')) return
// https://cn.vuejs.org/v2/api/index.html#Vue-extend
/* 使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象 */
let ComponentClass = Vue.extend(CodeCopy)
let instance = new ComponentClass()
instance.code = el.innerText
instance.parent = el
/* 手动挂载 */
instance.$mount()
el.classList.add('code-copy-added')
el.appendChild(instance.$el)
})
// 块引用添加复制按钮
document.querySelectorAll('blockquote').forEach(el => {
// console.log(el)
if (el.classList.contains('code-copy-added')) return
// https://cn.vuejs.org/v2/api/index.html#Vue-extend
/* 使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象 */
let ComponentClass = Vue.extend(CodeCopy)
let instance = new ComponentClass()
instance.code = el.innerText
instance.parent = el
/* 手动挂载 */
instance.$mount()
el.classList.add('code-copy-added')
el.appendChild(instance.$el)
})
}, 100)
},
}
}
</script>
<style lang="scss">
// 复制按钮
.codeContent {
max-width: 400px;
margin: 0 auto;
padding-top: 25vh;
}
.code-copy-added {
background-color: #282c34;
color: white;
padding: 25px 20px;
margin: 10px 0;
text-align: left;
border-radius: 3px;
position: relative;
}
.code-copy-added:hover .copy-btn {
opacity: 1;
}
</style>
CodeCopy.vue
<template>
<div class="copy-content">
<!-- 复制按钮 -->
<div
class="copy-btn code-data-copy"
@click="copyMessage"
data-clipboard-action="copy"
:data-clipboard-text="code"
>
复制
</div>
<!-- <div v-if="success" class="copy-success-text">复制成功!</div> -->
</div>
</template>
<script>
import Clipboard from 'clipboard' //复制插件
export default {
data () {
return {
code: null,
// success: false
}
},
methods: {
copyMessage (value) {
let _this = this
// _this.success = false
let clipboard = new Clipboard('.code-data-copy')
clipboard.on('success', function (e) {
_this.$message.success('复制成功')
// _this.success = true
// setTimeout(() => {
// _this.success = false
// }, 300)
clipboard.destroy() // 销毁,避免多次点击重复出现
})
clipboard.on('error', function () {
console.log('复制失败')
})
}
}
}
</script>
<style lang="scss" scoped>
.copy-content {
height: 0;
// position: absolute;
// top: 0;
// right: 0;
}
.icon {
width: 0.8rem;
height: 0.8rem;
fill: white;
}
.copy-btn {
user-select: none;
opacity: 0;
position: absolute;
right: 5px;
top: 5px;
cursor: pointer;
padding: 5px;
border-radius: 3px;
transition: 0.3s;
// background: rgba(255, 255, 255, 0.2);
background: #fff;
&:active {
// background: rgba(253, 253, 253, 0.575);
background: rgba(253, 253, 253, 0.575);
}
}
.copy-success-text {
color: white;
position: absolute;
font-size: 12px;
top: 8px;
right: 2.5rem;
font-weight: 200;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
animation: successCopy 0.5s ease both 1;
}
@keyframes successCopy {
70% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0.5);
}
}
</style>