前端vue進(jìn)行markdown渲染,表格,Echarts,文本,圖片等……

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渲染示例

## 文本樣式

這是**粗體文本**,這是*斜體文本*,這是***粗斜體文本***,這是~~刪除線文本~~。

## 圖片

![示例圖片](https://picsum.photos/800/400?random=1)

## 代碼塊

\`\`\`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>    

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容