image.png
image.png
image.png
image.png
我下载的版本
- npm install katex@0.16.8
- npm install marked@4.2.12 --save
- npm install highlight.js@10.7.3 --save
<template>
<div style="margin-top: 100px;width:100%;overflow-x: hidden;">
<div v-html="renderedContent" ref="markdownContent"></div>
</div>
</template>
<script>
import { marked } from "marked";
import hljs from "highlight.js";
// import 'highlight.js/styles/github.css';
import "highlight.js/styles/dracula.css"; // 引入Dracula深色主题
// import { renderMathInElement } from "katex"; 引入方式根据版本不同有区别,使用方法也有区别,请注意版本
import { default as autoRender } from "katex/dist/contrib/auto-render.mjs"; // 注意路径
import "katex/dist/katex.min.css";
export default {
data() {
return {
renderedContent: `# Markdown渲染示例
## 文本样式
这是**粗体文本**,这是*斜体文本*,这是***粗斜体文本***,这是~~删除线文本~~。
## 图片

## 代码块
\`\`\`javascript
function helloWorld() {
console.log('Hello, world!');
}
\`\`\`
## 表格
| 姓名 | 年龄 | 职业 |
|------|------|------|
| 张三 | 28 | 工程师 |
| 李四 | 32 | 设计师 |
| 王五 | 45 | 产品经理 |
## 数学公式
当 $a \\ne 0$ 时,方程 $ax^2 + bx + c = 0$ 有两个解,它们是:
$$x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}$$
## ECharts图表
[chart:bar]
{
"xAxis": ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"],
"yAxis": {},
"series": [{
"name": "销量",
"type": "bar",
"data": [5, 20, 36, 10, 10, 20]
}]
}
[/chart]
[chart:line]
{
"xAxis": {
"type": "category",
"data": ["1月", "2月", "3月", "4月", "5月", "6月"]
},
"yAxis": {
"type": "value"
},
"series": [{
"data": [820, 932, 901, 934, 1290, 1330],
"type": "line"
}]
}
[/chart]
[chart:pie]
{
"legend": ["直接访问", "邮件营销", "联盟广告", "视频广告", "搜索引擎"],
"series": [{
"name": "访问来源",
"type": "pie",
"radius": "50%",
"data": [
{"value": 335, "name": "直接访问"},
{"value": 310, "name": "邮件营销"},
{"value": 234, "name": "联盟广告"},
{"value": 135, "name": "视频广告"},
{"value": 1548, "name": "搜索引擎"}
]
}]
}
[/chart]`,
};
},
watch: {},
mounted() {
},
created() {
this.renderMarkdown(this.renderedContent);
},
methods: {
renderMarkdown(content) {
if (!content) return;
// 配置marked解析器
marked.setOptions({
highlight: (code, lang) => {
if (lang && hljs.getLanguage(lang)) {
return hljs.highlight(code, { language: lang }).value;
}
return hljs.highlightAuto(code).value;
},
breaks: true,
gfm: true,
});
// 自定义解析器处理特殊语法
const renderer = new marked.Renderer();
// 处理图片
renderer.image = (href, title, text) => {
// 基础图片标签
let imgTag = `<img src="${href}" alt="${text}"`;
if (title) imgTag += ` title="${title}"`;
// 检查是否有自定义属性 (例如: width=300)
const widthMatch = href.match(/width=(\d+)/);
const heightMatch = href.match(/height=(\d+)/);
if (widthMatch) imgTag += ` width="${widthMatch[1]}"`;
if (heightMatch) imgTag += ` height="${heightMatch[1]}"`;
imgTag += ' class="img-fluid rounded" loading="lazy">';
// 处理图片懒加载和错误处理
return `
<div class="markdown-image-container">
${imgTag}
<noscript>${imgTag}</noscript>
</div>
`;
};
// 处理代码块
renderer.code = (code, language) => {
return `<div><div>复制</div><pre><code class="hljs ${language}">${marked.parse(
code
)}</code></pre></div>`;
};
//处理表格
renderer.table = function (header, body) {
// 为表格添加样式类
return `<table class="markdown-table">
<thead>${header}</thead>
<tbody>${body}</tbody>
</table>`;
};
// 处理ECharts图表 (使用自定义语法 [chart:type] {...} [/chart])
const chartRegex = /\[chart:(\w+)\]([\s\S]*?)\[\/chart\]/g;
content = content.replace(chartRegex, (match, type, options) => {
const chartId = `chart-${Date.now()}-${Math.floor(
Math.random() * 1000
)}`;
return `<div class="markdown-chart" id="${chartId}" data-chart-type="${type}" data-chart-options="${encodeURIComponent(
options
)}"></div>`;
});
// 渲染Markdown为HTML
let htmlContent = marked(content, { renderer });
// 净化HTML防止XSS攻击
// htmlContent = DOMPurify.sanitize(htmlContent);
// 设置渲染内容
this.renderedContent = htmlContent;
// 异步渲染数学公式
this.$nextTick(() => {
const markdownElement = this.$refs.markdownContent;
if (markdownElement) {
autoRender(markdownElement, {
delimiters: [
{ left: "$$", right: "$$", display: true },
{ left: "$", right: "$", display: false },
{ left: "\\(", right: "\\)", display: false },
{ left: "\\[", right: "\\]", display: true },
],
});
// // 渲染ECharts图表
this.renderCharts();
}
});
},
renderCharts() {
const chartElements =
this.$refs.markdownContent.querySelectorAll(".markdown-chart");
chartElements.forEach((element) => {
const chartType = element.getAttribute("data-chart-type");
let optionsStr = element.getAttribute("data-chart-options");
try {
// 解码并解析图表配置
optionsStr = decodeURIComponent(optionsStr);
const chartOptions = JSON.parse(optionsStr);
// const chartOptions = optionsStr
// 创建ECharts实例
const chart = this.$echarts.init(element);
// // 根据图表类型设置配置
let finalOptions = {
tooltip: {
trigger: "axis",
},
legend: {
data: chartOptions?.legend?.data ?? [],
},
xAxis: {
type: "category",
data: chartOptions?.xAxis?.data ?? [],
},
yAxis: {
type: "value",
},
series: chartOptions?.series ?? [],
};
// // 根据不同图表类型调整配置
if (chartType === "pie") {
finalOptions = {
tooltip: {
trigger: "item",
},
legend: {
orient: "vertical",
left: "left",
data: chartOptions?.legend?.data ?? [],
},
series: [
{
type: "pie",
radius: "50%",
data: chartOptions?.series?.[0]?.data ?? [],
},
],
};
}
// 设置图表配置
chart.setOption(finalOptions);
// 监听窗口大小变化,调整图表
window.addEventListener("resize", () => {
chart.resize();
});
} catch (error) {
element.innerHTML =
'<div class="text-danger">图表渲染失败,请检查配置格式</div>';
}
});
}
},
};
</script>
<style scoped>
::v-deep .markdown-chart {
height: 400px;
width:600px;
margin: 16px auto 16px auto;
border: 1px solid #eee;
border-radius: 4px;
padding: 10px;
background-color: #fff;
box-shadow: 0 1px 3px rgba(0,0,0,0.12);
}
/* 基础样式 */
.markdown-container {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
Ubuntu, sans-serif;
line-height: 1.6;
color: #333;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
/* 代码块样式 */
::v-deep .markdown-body pre {
background-color: #282c34;
padding: 16px;
border-radius: 6px;
overflow-x: auto;
margin: 1em 0;
}
::v-deep .markdown-body code {
font-family: Consolas, Monaco, "Andale Mono", monospace;
font-size: 0.95em;
}
/* 表格样式 */
::v-deep .markdown-body table {
width: 100%;
border-collapse: collapse;
margin: 1em 0;
overflow: hidden;
border-radius: 4px;
}
::v-deep .markdown-table {
border-collapse: collapse;
border: 1px solid black;
}
::v-deep .markdown-table tr,
::v-deep .markdown-table td,
::v-deep .markdown-table th {
border: 1px solid black;
}
::v-deep .markdown-body th,
::v-deep .markdown-body td {
border: 1px solid #3e4452;
padding: 10px 16px;
text-align: left;
}
::v-deep .markdown-body th {
background-color: #21252b;
font-weight: 600;
color: #e06c75;
}
::v-deep .markdown-body tr:nth-child(even) {
background-color: rgba(255, 255, 255, 0.05);
}
::v-deep .markdown-body tr:hover {
background-color: rgba(255, 255, 255, 0.1);
}
/* 链接样式 */
::v-deep .markdown-body a {
color: #61afef;
text-decoration: none;
}
::v-deep .markdown-body a:hover {
text-decoration: underline;
}
/* 标题样式 */
::v-deep .markdown-body h1,
::v-deep .markdown-body h2,
::v-deep .markdown-body h3 {
color: #e06c75;
margin-top: 1.5em;
margin-bottom: 0.5em;
}
/* 列表样式 */
::v-deep .markdown-body ul,
::v-deep .markdown-body ol {
padding-left: 1.5em;
margin: 1em 0;
}
::v-deep .markdown-body li {
margin-bottom: 0.5em;
}
.katex,
.katex * {
font-family: "KaTeX_Main", "Times New Roman", serif;
}
</style>