300 lines
5.7 KiB
Vue
300 lines
5.7 KiB
Vue
|
|
<template>
|
||
|
|
<view class="up-markdown" :class="theme">
|
||
|
|
<up-parse :content="parsedContent" :previewImg="previewImg"></up-parse>
|
||
|
|
</view>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
import { marked } from './marked.esm.js';
|
||
|
|
|
||
|
|
export default {
|
||
|
|
name: 'up-markdown',
|
||
|
|
props: {
|
||
|
|
// markdown内容
|
||
|
|
content: {
|
||
|
|
type: String,
|
||
|
|
default: ''
|
||
|
|
},
|
||
|
|
// 是否启用图片预览
|
||
|
|
previewImg: {
|
||
|
|
type: Boolean,
|
||
|
|
default: true
|
||
|
|
},
|
||
|
|
// 是否显示代码块行号
|
||
|
|
showLineNumber: {
|
||
|
|
type: Boolean,
|
||
|
|
default: false
|
||
|
|
},
|
||
|
|
// 主题样式 'light' | 'dark'
|
||
|
|
theme: {
|
||
|
|
type: String,
|
||
|
|
default: 'light'
|
||
|
|
}
|
||
|
|
},
|
||
|
|
data() {
|
||
|
|
return {
|
||
|
|
parsedContent: ''
|
||
|
|
};
|
||
|
|
},
|
||
|
|
watch: {
|
||
|
|
content: {
|
||
|
|
handler(newVal) {
|
||
|
|
this.parseMarkdown(newVal);
|
||
|
|
},
|
||
|
|
immediate: true
|
||
|
|
}
|
||
|
|
},
|
||
|
|
methods: {
|
||
|
|
// 解析markdown内容
|
||
|
|
parseMarkdown(content) {
|
||
|
|
if (!content) {
|
||
|
|
this.parsedContent = '';
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 使用marked解析markdown
|
||
|
|
let parsed = marked(content);
|
||
|
|
|
||
|
|
// 处理代码块
|
||
|
|
parsed = this.handleCodeBlock(parsed);
|
||
|
|
|
||
|
|
// 应用主题样式
|
||
|
|
parsed = this.applyTheme(parsed);
|
||
|
|
|
||
|
|
this.parsedContent = parsed;
|
||
|
|
},
|
||
|
|
|
||
|
|
// 处理代码块
|
||
|
|
handleCodeBlock(html) {
|
||
|
|
// 添加代码块样式和行号
|
||
|
|
return html.replace(/<pre><code([^>]*)>([^<]+)<\/code><\/pre>/g, (match, lang, code) => {
|
||
|
|
const language = lang.match(/class="language-([^"]+)"/);
|
||
|
|
const langClass = language ? `language-${language[1]}` : '';
|
||
|
|
|
||
|
|
let result = `<pre class="up-markdown-code ${langClass}">`;
|
||
|
|
|
||
|
|
if (this.showLineNumber) {
|
||
|
|
// 添加行号
|
||
|
|
const lines = code.split('\n').filter(line => line.trim() !== '');
|
||
|
|
result += '<span class="up-markdown-line-numbers">';
|
||
|
|
lines.push('');
|
||
|
|
lines.forEach((_, index) => {
|
||
|
|
result += `<span class="up-markdown-line-number">${index + 1}</span>`;
|
||
|
|
});
|
||
|
|
result += '</span>';
|
||
|
|
}
|
||
|
|
|
||
|
|
result += `<code class='code-lang ${langClass}'>${code}</code></pre>`;
|
||
|
|
return result;
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
// 应用主题样式
|
||
|
|
applyTheme(html) {
|
||
|
|
// 可以根据theme属性添加不同的样式类
|
||
|
|
return html;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style lang="scss" scoped>
|
||
|
|
.up-markdown {
|
||
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||
|
|
font-size: 16px;
|
||
|
|
line-height: 1.6;
|
||
|
|
color: #333;
|
||
|
|
padding: 16px;
|
||
|
|
word-wrap: break-word;
|
||
|
|
|
||
|
|
/* 标题样式 */
|
||
|
|
::v-deep h1 {
|
||
|
|
font-size: 32px;
|
||
|
|
margin: 8px 0;
|
||
|
|
font-weight: bold;
|
||
|
|
}
|
||
|
|
|
||
|
|
::v-deep h2 {
|
||
|
|
font-size: 24px;
|
||
|
|
margin: 8px 0;
|
||
|
|
font-weight: bold;
|
||
|
|
}
|
||
|
|
|
||
|
|
::v-deep h3 {
|
||
|
|
font-size: 18px;
|
||
|
|
margin: 7px 0;
|
||
|
|
font-weight: bold;
|
||
|
|
}
|
||
|
|
|
||
|
|
::v-deep h4 {
|
||
|
|
font-size: 16px;
|
||
|
|
margin: 7px 0;
|
||
|
|
font-weight: bold;
|
||
|
|
}
|
||
|
|
|
||
|
|
::v-deep h5 {
|
||
|
|
font-size: 13px;
|
||
|
|
margin: 6px 0;
|
||
|
|
font-weight: bold;
|
||
|
|
}
|
||
|
|
|
||
|
|
::v-deep h6 {
|
||
|
|
font-size: 10px;
|
||
|
|
margin: 5px 0;
|
||
|
|
font-weight: bold;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 段落样式 */
|
||
|
|
::v-deep p {
|
||
|
|
margin: 16px 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 链接样式 */
|
||
|
|
::v-deep a {
|
||
|
|
color: #007AFF;
|
||
|
|
text-decoration: none;
|
||
|
|
|
||
|
|
&:hover {
|
||
|
|
text-decoration: underline;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 列表样式 */
|
||
|
|
::v-deep ul,
|
||
|
|
::v-deep ol {
|
||
|
|
margin: 16px 0;
|
||
|
|
padding-left: 32px;
|
||
|
|
|
||
|
|
li {
|
||
|
|
margin: 8px 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
::v-deep ul li {
|
||
|
|
list-style-type: disc;
|
||
|
|
}
|
||
|
|
|
||
|
|
::v-deep ol li {
|
||
|
|
list-style-type: decimal;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 引用样式 */
|
||
|
|
::v-deep blockquote {
|
||
|
|
margin: 8px 0;
|
||
|
|
padding: 0 10px;
|
||
|
|
border-left: 4px solid #ccc;
|
||
|
|
color: #666;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 代码样式 */
|
||
|
|
::v-deep &-code {
|
||
|
|
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
|
||
|
|
font-size: 14px;
|
||
|
|
background-color: #f6f8fa;
|
||
|
|
padding: 3px 6px;
|
||
|
|
border-radius: 3px;
|
||
|
|
display: flex;
|
||
|
|
}
|
||
|
|
::v-deep .code-lang {
|
||
|
|
width: 100%;
|
||
|
|
overflow-x: auto;
|
||
|
|
}
|
||
|
|
|
||
|
|
::v-deep pre {
|
||
|
|
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
|
||
|
|
font-size: 14px;
|
||
|
|
background-color: #f6f8fa;
|
||
|
|
padding: 16px;
|
||
|
|
overflow: auto;
|
||
|
|
border-radius: 6px;
|
||
|
|
margin: 16px 0;
|
||
|
|
|
||
|
|
::v-deep code {
|
||
|
|
background: none;
|
||
|
|
padding: 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 表格样式 */
|
||
|
|
::v-deep table {
|
||
|
|
border-collapse: collapse;
|
||
|
|
margin: 16px 0;
|
||
|
|
width: 100%;
|
||
|
|
|
||
|
|
th,
|
||
|
|
td {
|
||
|
|
padding: 6px 13px;
|
||
|
|
border: 1px solid #dfe2e5;
|
||
|
|
}
|
||
|
|
|
||
|
|
th {
|
||
|
|
font-weight: 600;
|
||
|
|
}
|
||
|
|
|
||
|
|
tr:nth-child(2n) {
|
||
|
|
background-color: #f6f8fa;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 图片样式 */
|
||
|
|
::v-deep img {
|
||
|
|
max-width: 100%;
|
||
|
|
box-sizing: content-box;
|
||
|
|
background-color: #fff;
|
||
|
|
margin: 16px 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 分割线样式 */
|
||
|
|
::v-deep hr {
|
||
|
|
height: 1px;
|
||
|
|
padding: 0;
|
||
|
|
margin: 24px 0;
|
||
|
|
background-color: #e1e4e8;
|
||
|
|
border: 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 深色主题 */
|
||
|
|
&.dark {
|
||
|
|
color: #ccc;
|
||
|
|
background-color: #1e1e1e;
|
||
|
|
|
||
|
|
::v-deep &-code {
|
||
|
|
background-color: #2d2d2d;
|
||
|
|
color: #dcdcdc;
|
||
|
|
}
|
||
|
|
|
||
|
|
::v-deep pre {
|
||
|
|
background-color: #2d2d2d;
|
||
|
|
color: #dcdcdc;
|
||
|
|
}
|
||
|
|
|
||
|
|
::v-deep blockquote {
|
||
|
|
margin: 8px 0;
|
||
|
|
padding: 0 10px;
|
||
|
|
border-left: 4px solid #ccc;
|
||
|
|
color: #bbb;
|
||
|
|
}
|
||
|
|
|
||
|
|
::v-deep a {
|
||
|
|
color: #4da6ff;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 代码块行号样式 */
|
||
|
|
::v-deep .up-markdown-line-numbers {
|
||
|
|
display: flex;
|
||
|
|
flex-direction: column;
|
||
|
|
align-items: flex-start;
|
||
|
|
padding-right: 10px;
|
||
|
|
margin-right: 10px;
|
||
|
|
border-right: 1px solid #ddd;
|
||
|
|
user-select: none;
|
||
|
|
|
||
|
|
.up-markdown-line-number {
|
||
|
|
color: #999;
|
||
|
|
font-size: 14px;
|
||
|
|
line-height: 1.6;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</style>
|