550 lines
9.4 KiB
Vue
550 lines
9.4 KiB
Vue
<!-- uniapp vue2 markdown解析 -->
|
||
<template>
|
||
<view class="ua__markdown"><rich-text space="nbsp" :nodes="parseNodes(source)"
|
||
@itemclick="handleItemClick"></rich-text></view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import {
|
||
ref,
|
||
computed
|
||
} from 'vue'
|
||
import MarkdownIt from './lib/markdown-it.min.js'
|
||
import hljs from './lib/highlight/uni-highlight.min.js'
|
||
import './lib/highlight/atom-one-dark.css'
|
||
import parseHtml from './lib/html-parser.js'
|
||
|
||
export default {
|
||
|
||
props: {
|
||
source: String,
|
||
showLine: {
|
||
type: [Boolean, String],
|
||
default: true
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
markdown: {},
|
||
}
|
||
},
|
||
|
||
created() {
|
||
let copyCodeData = []
|
||
this.markdown = MarkdownIt({
|
||
html: true,
|
||
highlight: function(str, lang) {
|
||
let preCode = ""
|
||
try {
|
||
preCode = hljs.highlightAuto(str).value
|
||
} catch (err) {
|
||
preCode = this.markdown.utils.escapeHtml(str);
|
||
}
|
||
const lines = preCode.split(/\n/).slice(0, -1)
|
||
// 添加自定义行号
|
||
let html = lines.map((item, index) => {
|
||
if (item == '') {
|
||
return ''
|
||
}
|
||
return '<li><span class="line-num" data-line="' + (index + 1) + '"></span>' +
|
||
item + '</li>'
|
||
}).join('')
|
||
if (props.showLine) {
|
||
html = '<ol style="padding: 0px 30px;">' + html + '</ol>'
|
||
} else {
|
||
html = '<ol style="padding: 0px 7px;list-style:none;">' + html + '</ol>'
|
||
}
|
||
copyCodeData.push(str)
|
||
let htmlCode = `<div class="markdown-wrap">`
|
||
// #ifndef MP-WEIXIN
|
||
htmlCode += `<div style="color: #aaa;text-align: right;font-size: 12px;padding:8px;">`
|
||
htmlCode +=
|
||
`${lang}<a class="copy-btn" code-data-index="${copyCodeData.length - 1}" style="margin-left: 8px;">复制代码</a>`
|
||
htmlCode += `</div>`
|
||
// #endif
|
||
htmlCode +=
|
||
`<pre class="hljs" style="padding:10px 8px 0;margin-bottom:5px;overflow: auto;display: block;border-radius: 5px;"><code>${html}</code></pre>`;
|
||
htmlCode += '</div>'
|
||
return htmlCode
|
||
}
|
||
})
|
||
},
|
||
|
||
methods: {
|
||
|
||
parseNodes(value) {
|
||
if (!value) return
|
||
// 解析<br />到\n
|
||
value = value.replace(/<br>|<br\/>|<br \/>/g, "\n")
|
||
value = value.replace(/ /g, " ")
|
||
let htmlString = ''
|
||
if (value.split("```").length % 2) {
|
||
let mdtext = value
|
||
if (mdtext[mdtext.length - 1] != '\n') {
|
||
mdtext += '\n'
|
||
}
|
||
htmlString = this.markdown.render(mdtext)
|
||
} else {
|
||
htmlString = this.markdown.render(value)
|
||
}
|
||
// 解决小程序表格边框型失效问题
|
||
htmlString = htmlString.replace(/<table/g, `<table class="table"`)
|
||
htmlString = htmlString.replace(/<tr/g, `<tr class="tr"`)
|
||
htmlString = htmlString.replace(/<th>/g, `<th class="th">`)
|
||
htmlString = htmlString.replace(/<td/g, `<td class="td"`)
|
||
htmlString = htmlString.replace(/<hr>|<hr\/>|<hr \/>/g, `<hr class="hr">`)
|
||
|
||
// #ifndef APP-NVUE
|
||
return htmlString
|
||
// #endif
|
||
|
||
// 将htmlString转成htmlArray,反之使用rich-text解析
|
||
// #ifdef APP-NVUE
|
||
return parseHtml(htmlString)
|
||
// #endif
|
||
},
|
||
|
||
// 复制代码
|
||
handleItemClick(e) {
|
||
let {
|
||
attrs
|
||
} = e.detail.node
|
||
let {
|
||
"code-data-index": codeDataIndex,
|
||
"class": className
|
||
} = attrs
|
||
if (className == 'copy-btn') {
|
||
uni.setClipboardData({
|
||
data: copyCodeData[codeDataIndex],
|
||
showToast: false,
|
||
success() {
|
||
uni.showToast({
|
||
title: '复制成功',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
})
|
||
}
|
||
},
|
||
|
||
}
|
||
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.ua__markdown {
|
||
font-size: 14px;
|
||
line-height: 1.5;
|
||
|
||
h1,
|
||
h2,
|
||
h3,
|
||
h4,
|
||
h5,
|
||
h6 {
|
||
font-family: inherit;
|
||
font-weight: 500;
|
||
line-height: 1.1;
|
||
color: inherit;
|
||
}
|
||
|
||
h1,
|
||
h2,
|
||
h3 {
|
||
margin-top: 20px;
|
||
margin-bottom: 10px
|
||
}
|
||
|
||
h4,
|
||
h5,
|
||
h6 {
|
||
margin-top: 10px;
|
||
margin-bottom: 10px
|
||
}
|
||
|
||
.h1,
|
||
h1 {
|
||
font-size: 36px
|
||
}
|
||
|
||
.h2,
|
||
h2 {
|
||
font-size: 30px
|
||
}
|
||
|
||
.h3,
|
||
h3 {
|
||
font-size: 24px
|
||
}
|
||
|
||
.h4,
|
||
h4 {
|
||
font-size: 18px
|
||
}
|
||
|
||
.h5,
|
||
h5 {
|
||
font-size: 14px
|
||
}
|
||
|
||
.h6,
|
||
h6 {
|
||
font-size: 12px
|
||
}
|
||
|
||
a {
|
||
background-color: transparent;
|
||
color: #2196f3;
|
||
text-decoration: none;
|
||
}
|
||
|
||
hr,
|
||
::v-deep .hr {
|
||
margin-top: 20px;
|
||
margin-bottom: 20px;
|
||
border: 0;
|
||
border-top: 1px solid #e5e5e5;
|
||
}
|
||
|
||
img {
|
||
max-width: 35%;
|
||
}
|
||
|
||
p {
|
||
margin: 0 0 10px
|
||
}
|
||
|
||
em {
|
||
font-style: italic;
|
||
font-weight: inherit;
|
||
}
|
||
|
||
ol,
|
||
ul {
|
||
margin-top: 0;
|
||
margin-bottom: 10px;
|
||
padding-left: 40px;
|
||
}
|
||
|
||
ol ol,
|
||
ol ul,
|
||
ul ol,
|
||
ul ul {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
ol ol,
|
||
ul ol {
|
||
list-style-type: lower-roman;
|
||
}
|
||
|
||
ol ol ol,
|
||
ul ul ol {
|
||
list-style-type: lower-alpha;
|
||
}
|
||
|
||
dl {
|
||
margin-top: 0;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
dt {
|
||
font-weight: 600;
|
||
}
|
||
|
||
dt,
|
||
dd {
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.task-list-item {
|
||
list-style-type: none;
|
||
}
|
||
|
||
.task-list-item input {
|
||
margin: 0 .2em .25em -1.6em;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
pre {
|
||
position: relative;
|
||
z-index: 11;
|
||
}
|
||
|
||
code,
|
||
kbd,
|
||
pre,
|
||
samp {
|
||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||
}
|
||
|
||
code:not(.hljs) {
|
||
padding: 2px 4px;
|
||
font-size: 90%;
|
||
color: #c7254e;
|
||
background-color: #ffe7ee;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
code:empty {
|
||
display: none;
|
||
}
|
||
|
||
pre code.hljs {
|
||
color: var(--vg__text-1);
|
||
border-radius: 16px;
|
||
background: var(--vg__bg-1);
|
||
font-size: 12px;
|
||
}
|
||
|
||
.markdown-wrap {
|
||
font-size: 12px;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
pre.code-block-wrapper {
|
||
background: #2b2b2b;
|
||
color: #f8f8f2;
|
||
border-radius: 4px;
|
||
overflow-x: auto;
|
||
padding: 1em;
|
||
position: relative;
|
||
}
|
||
|
||
pre.code-block-wrapper code {
|
||
padding: auto;
|
||
font-size: inherit;
|
||
color: inherit;
|
||
background-color: inherit;
|
||
border-radius: 0;
|
||
}
|
||
|
||
.code-block-header__copy {
|
||
font-size: 16px;
|
||
margin-left: 5px;
|
||
}
|
||
|
||
abbr[data-original-title],
|
||
abbr[title] {
|
||
cursor: help;
|
||
border-bottom: 1px dotted #777;
|
||
}
|
||
|
||
blockquote {
|
||
padding: 10px 20px;
|
||
margin: 0 0 20px;
|
||
font-size: 17.5px;
|
||
border-left: 5px solid #e5e5e5;
|
||
}
|
||
|
||
blockquote ol:last-child,
|
||
blockquote p:last-child,
|
||
blockquote ul:last-child {
|
||
margin-bottom: 0
|
||
}
|
||
|
||
blockquote .small,
|
||
blockquote footer,
|
||
blockquote small {
|
||
display: block;
|
||
font-size: 80%;
|
||
line-height: 1.42857143;
|
||
color: #777
|
||
}
|
||
|
||
blockquote .small:before,
|
||
blockquote footer:before,
|
||
blockquote small:before {
|
||
content: '\2014 \00A0'
|
||
}
|
||
|
||
.blockquote-reverse,
|
||
blockquote.pull-right {
|
||
padding-right: 15px;
|
||
padding-left: 0;
|
||
text-align: right;
|
||
border-right: 5px solid #eee;
|
||
border-left: 0
|
||
}
|
||
|
||
.blockquote-reverse .small:before,
|
||
.blockquote-reverse footer:before,
|
||
.blockquote-reverse small:before,
|
||
blockquote.pull-right .small:before,
|
||
blockquote.pull-right footer:before,
|
||
blockquote.pull-right small:before {
|
||
content: ''
|
||
}
|
||
|
||
.blockquote-reverse .small:after,
|
||
.blockquote-reverse footer:after,
|
||
.blockquote-reverse small:after,
|
||
blockquote.pull-right .small:after,
|
||
blockquote.pull-right footer:after,
|
||
blockquote.pull-right small:after {
|
||
content: '\00A0 \2014'
|
||
}
|
||
|
||
.footnotes {
|
||
-moz-column-count: 2;
|
||
-webkit-column-count: 2;
|
||
column-count: 2
|
||
}
|
||
|
||
.footnotes-list {
|
||
padding-left: 2em
|
||
}
|
||
|
||
table,
|
||
::v-deep .table {
|
||
border-spacing: 0;
|
||
border-collapse: collapse;
|
||
width: 100%;
|
||
max-width: 65em;
|
||
overflow: auto;
|
||
margin-top: 0;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
table tr,
|
||
::v-deep .table .tr {
|
||
border-top: 1px solid #e5e5e5;
|
||
}
|
||
|
||
table th,
|
||
table td,
|
||
::v-deep .table .th,
|
||
::v-deep .table .td {
|
||
padding: 6px 13px;
|
||
border: 1px solid #e5e5e5;
|
||
}
|
||
|
||
table th,
|
||
::v-deep .table .th {
|
||
font-weight: 600;
|
||
background-color: #eee;
|
||
}
|
||
|
||
.hljs[class*=language-]:before {
|
||
position: absolute;
|
||
z-index: 3;
|
||
top: .8em;
|
||
right: 1em;
|
||
font-size: .8em;
|
||
color: #999;
|
||
}
|
||
|
||
.hljs[class~=language-js]:before {
|
||
content: "js"
|
||
}
|
||
|
||
.hljs[class~=language-ts]:before {
|
||
content: "ts"
|
||
}
|
||
|
||
.hljs[class~=language-html]:before {
|
||
content: "html"
|
||
}
|
||
|
||
.hljs[class~=language-md]:before {
|
||
content: "md"
|
||
}
|
||
|
||
.hljs[class~=language-vue]:before {
|
||
content: "vue"
|
||
}
|
||
|
||
.hljs[class~=language-css]:before {
|
||
content: "css"
|
||
}
|
||
|
||
.hljs[class~=language-sass]:before {
|
||
content: "sass"
|
||
}
|
||
|
||
.hljs[class~=language-scss]:before {
|
||
content: "scss"
|
||
}
|
||
|
||
.hljs[class~=language-less]:before {
|
||
content: "less"
|
||
}
|
||
|
||
.hljs[class~=language-stylus]:before {
|
||
content: "stylus"
|
||
}
|
||
|
||
.hljs[class~=language-go]:before {
|
||
content: "go"
|
||
}
|
||
|
||
.hljs[class~=language-java]:before {
|
||
content: "java"
|
||
}
|
||
|
||
.hljs[class~=language-c]:before {
|
||
content: "c"
|
||
}
|
||
|
||
.hljs[class~=language-sh]:before {
|
||
content: "sh"
|
||
}
|
||
|
||
.hljs[class~=language-yaml]:before {
|
||
content: "yaml"
|
||
}
|
||
|
||
.hljs[class~=language-py]:before {
|
||
content: "py"
|
||
}
|
||
|
||
.hljs[class~=language-docker]:before {
|
||
content: "docker"
|
||
}
|
||
|
||
.hljs[class~=language-dockerfile]:before {
|
||
content: "dockerfile"
|
||
}
|
||
|
||
.hljs[class~=language-makefile]:before {
|
||
content: "makefile"
|
||
}
|
||
|
||
.hljs[class~=language-javascript]:before {
|
||
content: "js"
|
||
}
|
||
|
||
.hljs[class~=language-typescript]:before {
|
||
content: "ts"
|
||
}
|
||
|
||
.hljs[class~=language-markup]:before {
|
||
content: "html"
|
||
}
|
||
|
||
.hljs[class~=language-markdown]:before {
|
||
content: "md"
|
||
}
|
||
|
||
.hljs[class~=language-json]:before {
|
||
content: "json"
|
||
}
|
||
|
||
.hljs[class~=language-ruby]:before {
|
||
content: "rb"
|
||
}
|
||
|
||
.hljs[class~=language-python]:before {
|
||
content: "py"
|
||
}
|
||
|
||
.hljs[class~=language-bash]:before {
|
||
content: "sh"
|
||
}
|
||
|
||
.hljs[class~=language-php]:before {
|
||
content: "php"
|
||
}
|
||
}
|
||
</style> |