
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"; 引入方式根據(jù)版本不同有區(qū)別,使用方法也有區(qū)別,請(qǐng)注意版本
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!');
}
\`\`\`
## 表格
| 姓名 | 年齡 | 職業(yè) |
|------|------|------|
| 張三 | 28 | 工程師 |
| 李四 | 32 | 設(shè)計(jì)師 |
| 王五 | 45 | 產(chǎn)品經(jīng)理 |
## 數(shù)學(xué)公式
當(dāng) $a \\ne 0$ 時(shí),方程 $ax^2 + bx + c = 0$ 有兩個(gè)解,它們是:
$$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": ["直接訪問", "郵件營銷", "聯(lián)盟廣告", "視頻廣告", "搜索引擎"],
"series": [{
"name": "訪問來源",
"type": "pie",
"radius": "50%",
"data": [
{"value": 335, "name": "直接訪問"},
{"value": 310, "name": "郵件營銷"},
{"value": 234, "name": "聯(lián)盟廣告"},
{"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) => {
// 基礎(chǔ)圖片標(biāo)簽
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">';
// 處理圖片懶加載和錯(cuò)誤處理
return `
<div class="markdown-image-container">
${imgTag}
<noscript>${imgTag}</noscript>
</div>
`;
};
// 處理代碼塊
renderer.code = (code, language) => {
return `<div class="code-block">
<div class="copy-btn">復(fù)制</div>
<pre><code class="hljs ${language}">${
hljs.highlightAuto(code).value
}</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);
// 設(shè)置渲染內(nèi)容
this.renderedContent = htmlContent;
// 異步渲染數(shù)學(xué)公式
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
// 創(chuàng)建ECharts實(shí)例
const chart = this.$echarts.init(element);
// // 根據(jù)圖表類型設(shè)置配置
let finalOptions = {
tooltip: {
trigger: "axis",
},
legend: {
data: chartOptions?.legend?.data ?? [],
},
xAxis: {
type: "category",
data: chartOptions?.xAxis?.data ?? [],
},
yAxis: {
type: "value",
},
series: chartOptions?.series ?? [],
};
// // 根據(jù)不同圖表類型調(diào)整配置
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 ?? [],
},
],
};
}
// 設(shè)置圖表配置
chart.setOption(finalOptions);
// 監(jiān)聽窗口大小變化,調(diào)整圖表
window.addEventListener("resize", () => {
chart.resize();
});
} catch (error) {
element.innerHTML =
'<div class="text-danger">圖表渲染失敗,請(qǐng)檢查配置格式</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);
}
/* 基礎(chǔ)樣式 */
.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;
}
/* 標(biāo)題樣式 */
::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>