124 lines
3.9 KiB
Vue
124 lines
3.9 KiB
Vue
<script setup lang="ts">
|
||
import {Marked} from 'marked';
|
||
// 默认导入所有语言
|
||
import hljs from 'highlight.js';
|
||
import 'highlight.js/styles/atom-one-light.css';
|
||
// 注册语言
|
||
// import hljs from 'highlight.js/lib/core';
|
||
// import javascript from 'highlight.js/lib/languages/javascript';
|
||
// hljs.registerLanguage('javascript', javascript);
|
||
|
||
const marked = new Marked();
|
||
|
||
// 自定义行号注入函数
|
||
const injectLineNumbers = (highlightedCode: string) => {
|
||
const lines = highlightedCode.split('\n')
|
||
|
||
// 移除最后一行空行(常见于代码块末尾的换行)
|
||
if (lines[lines.length - 1] === '') lines.pop()
|
||
|
||
// 为每行添加行号容器
|
||
return lines.map((line, index) => `<div class="code-line"><span class="line-number">${index + 1}</span><span class="line-content">${line}</span></div>`).join('')
|
||
}
|
||
|
||
marked.use({
|
||
async: true,
|
||
pedantic: false,
|
||
gfm: true,
|
||
silent: true,
|
||
renderer: {
|
||
code: ({text, lang}) => {
|
||
console.log(text);
|
||
const validLang = hljs.getLanguage(lang) ? lang : 'plaintext'
|
||
const highlighted = hljs.highlight(text, {language: lang}).value
|
||
console.log(highlighted)
|
||
const withLineNumbers = injectLineNumbers(highlighted)
|
||
return `<pre class="hljs ${validLang}"><code>${withLineNumbers}</code></pre>`;
|
||
}
|
||
}
|
||
})
|
||
|
||
const content = ref(`
|
||
# Hello Vue 3!
|
||
|
||
**Markdown 内容示例:**
|
||
|
||
- 列表项
|
||
- 另一项
|
||
|
||
\`\`\`javascript
|
||
// 代码块示例
|
||
function greet() {
|
||
console.log('Hello marked!');
|
||
}
|
||
\`\`\`
|
||
| 参数 | \t类型 | \t作用 | \t默认值 |
|
||
|:-----------|:---------|:---------------------------------------------|:----------------|
|
||
| breaks | \tboolean | \t将换行符 \\n 渲染为 <br>(类似 GitHub) | \tfalse |
|
||
| gfm | \tboolean | \t启用 GitHub Flavored Markdown 扩展(表格、删除线等) | \ttrue |
|
||
| headerIds\t | boolean | \t自动为标题添加 id 属性(如 \`<h1 id="hello-world"></h1>\`) | \ttrue |
|
||
| highlight\t | function | \t代码高亮处理函数,需返回高亮后的 HTML | \tnull |
|
||
| renderer\t | object | \t自定义渲染器对象(覆盖默认渲染逻辑) | \tnew Renderer() |
|
||
| sanitize\t | boolean | \t过滤危险 HTML 标签(防止 XSS 攻击) | \tfalse |
|
||
| sanitizer\t | function | \t自定义 HTML 过滤函数 | \t- |
|
||
| silent\t | boolean | \t静默模式:忽略解析错误(如未闭合的代码块) | \tfalse |
|
||
`);
|
||
|
||
const compiledMarkdown = ref(await marked.parse(content.value));
|
||
</script>
|
||
<template>
|
||
<div
|
||
ref="markdownContainer"
|
||
class="markdown-content"
|
||
v-html="compiledMarkdown"
|
||
/>
|
||
</template>
|
||
|
||
<style scoped lang="scss">
|
||
.markdown-content {
|
||
// 基础样式...
|
||
|
||
/* 代码块样式 */
|
||
:deep(pre) {
|
||
white-space: pre-wrap;
|
||
background: #cdcdcd;
|
||
padding: 15px;
|
||
border-radius: 5px;
|
||
|
||
code {
|
||
font-family: 'Consolas', 'Fira Code', monospace;
|
||
font-size: 14px;
|
||
line-height: 1.5;
|
||
white-space: pre;
|
||
}
|
||
|
||
/* 确保代码块正确换行 */
|
||
code {
|
||
display: block;
|
||
overflow-x: auto;
|
||
padding: 1em;
|
||
}
|
||
|
||
.code-line {
|
||
display: flex;
|
||
min-height: 1em; /* 防止空行高度塌陷 */
|
||
}
|
||
|
||
.line-number {
|
||
width: 40px;
|
||
padding-right: 12px;
|
||
color: #666;
|
||
text-align: right;
|
||
user-select: none;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.line-content {
|
||
flex-grow: 1;
|
||
white-space: pre-wrap; /* 允许代码换行 */
|
||
}
|
||
|
||
|
||
}
|
||
}
|
||
</style> |