使用marked
This commit is contained in:
parent
3710642bc0
commit
15779301fa
7
assets/svg/time.svg
Normal file
7
assets/svg/time.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg t="1745843719544" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2701"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
|
||||
<path d="M509.9 64.4c-246.6 0-446.6 200-446.6 446.6 0 246.6 199.9 446.6 446.6 446.6S956.5 757.7 956.5 511c0-246.6-199.9-446.6-446.6-446.6zM627 556.3H502.4c-25 0-45.3-20.3-45.3-45.3V274.3c0-25 20.3-45.3 45.3-45.3s45.3 20.3 45.3 45.3v191.5H627c25 0 45.3 20.3 45.3 45.3S652 556.3 627 556.3z"
|
||||
fill="" p-id="2702"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 676 B |
@ -1,21 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { Editor } from '@bytemd/vue-next'
|
||||
import gfm from '@bytemd/plugin-gfm'
|
||||
|
||||
const content = ref('')
|
||||
const plugins = [gfm()]
|
||||
const handleChange = (v: string) => {
|
||||
content.value = v
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Editor :value="content" :plugins="plugins" @change="handleChange" class="byteMd"/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.byteMd{
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
124
components/Home/Blog/Marked.vue
Normal file
124
components/Home/Blog/Marked.vue
Normal file
@ -0,0 +1,124 @@
|
||||
<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>
|
137
docs/f01-mrkdown/byteMD-Markdown.vue
Normal file
137
docs/f01-mrkdown/byteMD-Markdown.vue
Normal file
@ -0,0 +1,137 @@
|
||||
<script setup lang="ts">
|
||||
import { Editor } from '@bytemd/vue-next'
|
||||
import type { BytemdPlugin } from "bytemd";
|
||||
import gfm from '@bytemd/plugin-gfm'
|
||||
import gfmZhHans from "@bytemd/plugin-gfm/locales/zh_Hans.json";
|
||||
import math from '@bytemd/plugin-math'
|
||||
import highlight from '@bytemd/plugin-highlight'
|
||||
import 'highlight.js/styles/default.css'
|
||||
import mediumZoom from '@bytemd/plugin-medium-zoom'
|
||||
import mermaid from '@bytemd/plugin-mermaid'
|
||||
import breaks from '@bytemd/plugin-breaks'
|
||||
import gemoji from '@bytemd/plugin-gemoji'
|
||||
import rehypeHighlightCodeLines from "rehype-highlight-code-lines";
|
||||
import {useNiMessage} from "~/composables/useNiMessage";
|
||||
const niMessage = useNiMessage()
|
||||
|
||||
// Markdown内容
|
||||
const content = ref('')
|
||||
// 自定义插件
|
||||
const customPlugins = {
|
||||
actions: [{
|
||||
title: '插入时间',
|
||||
icon: h('span', { class: 'iconfont icon-time' }, ['🕒']),
|
||||
handler: {
|
||||
type: 'action',
|
||||
click: (targer) => {
|
||||
// wrapText 环绕式字符,像“”【】《》<>之类的
|
||||
// replaceLines((line) => "- [ ] " + line); // 单向的
|
||||
// editor.focus() 聚焦
|
||||
targer.replaceLines((line) => new Date() + line);
|
||||
targer.editor.focus();
|
||||
},
|
||||
},
|
||||
}]
|
||||
}
|
||||
// 显示代码行号
|
||||
const highlightCodeLinesPlugin = (): BytemdPlugin => {
|
||||
return {
|
||||
rehype: (processor) =>
|
||||
processor
|
||||
// 添加代码行号
|
||||
.use(rehypeHighlightCodeLines, {
|
||||
showLineNumbers: true,
|
||||
lineContainerTagName: "div",
|
||||
}),
|
||||
};
|
||||
};
|
||||
// 代码复制按钮
|
||||
const codeCopyPlugin = (): BytemdPlugin => {
|
||||
const createCopyDom = (text: any): HTMLElement => {
|
||||
const copyDom = document.createElement("div");
|
||||
copyDom.className = "icon-[ph--copy-bold] absolute right-2 top-2 cursor-pointer";
|
||||
copyDom.addEventListener("click", () => {
|
||||
window.copyToClipboard(text);
|
||||
niMessage.info("复制成功");
|
||||
});
|
||||
return copyDom;
|
||||
};
|
||||
|
||||
return {
|
||||
viewerEffect: ({ markdownBody }) => {
|
||||
// 获取所有code标签
|
||||
const els = markdownBody.querySelectorAll("pre>code");
|
||||
if (els.length === 0) return;
|
||||
|
||||
// 往pre标签中append copy节点
|
||||
els.forEach((itm: HTMLElement) => {
|
||||
itm.parentNode.appendChild(createCopyDom(itm.innerText));
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
// ByteMD的插件
|
||||
const plugins = [
|
||||
// DFM 任务列表、表格、删除线
|
||||
gfm({locale: gfmZhHans}),
|
||||
// 数学公式
|
||||
math(),
|
||||
// 代码高亮
|
||||
highlight(),
|
||||
// 图片放大
|
||||
mediumZoom(),
|
||||
// Mermaid 图表(流程图、时序图、甘特图等)
|
||||
mermaid(),
|
||||
// 自动将换行符转换为 <br>,支持 GitHub 风格的换行行为
|
||||
breaks(),
|
||||
// 支持 GitHub 风格的表情符号短代码(如 :+1: → 👍)
|
||||
gemoji(),
|
||||
// 代码行号
|
||||
highlightCodeLinesPlugin(),
|
||||
// 代码复制
|
||||
codeCopyPlugin(),
|
||||
// 自定义插件
|
||||
customPlugins,]
|
||||
// 当Markdown内容修改时
|
||||
const handleChange = (v: string) => {
|
||||
content.value = v
|
||||
}
|
||||
// 模式 ['auto', 'split', 'tab'] 自动:根据屏幕宽度,自行选择分屏还是标签选择
|
||||
const mode = ref('auto');
|
||||
// 语言
|
||||
const language = await import('~/node_modules/bytemd/locales/zh_Hans.json')
|
||||
// 编辑器配置
|
||||
const editorConfig = {
|
||||
toolbar: [] // 清空默认工具栏
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Editor class="byteMd"
|
||||
:value="content"
|
||||
:plugins="plugins"
|
||||
:editorConfig
|
||||
:locale="language"
|
||||
@change="handleChange" >
|
||||
<!-- 添加自定义工具栏 -->
|
||||
<template #toolbar="{ editor }">
|
||||
<div class="custom-toolbar">
|
||||
<button
|
||||
title="插入时间"
|
||||
@click="editor.insertContent(`当前时间:${new Date().toLocaleString()}`)">
|
||||
⏰ 插入时间
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</Editor>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.byteMd{
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
:global(.bytemd) {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
77
docs/f01-mrkdown/marked/Marked使用方法.md
Normal file
77
docs/f01-mrkdown/marked/Marked使用方法.md
Normal file
@ -0,0 +1,77 @@
|
||||
## 导入
|
||||
|
||||
```js
|
||||
// 只作用于当前空间
|
||||
import { Marked } from 'marked';
|
||||
const marked = new Marked();
|
||||
|
||||
// 全局
|
||||
import { marked } from 'marked';
|
||||
```
|
||||
|
||||
## 核心方法
|
||||
|
||||
| 方法 | 作用 | 示例 |
|
||||
|:--------------------------|:----------------|:-----------------------------------------|
|
||||
| marked.parse(md) | 同步解析 Markdown | await marked.parse('**bold**') |
|
||||
| marked.parse(md, callback)| 异步解析(处理异步高亮等场景) | marked.parse(md, (err, html) => { ... }) |
|
||||
| marked.use(options) | 全局配置 | marked.use({ breaks: true }) |
|
||||
|
||||
|
||||
## 配置
|
||||
|
||||
```js
|
||||
marked.use({
|
||||
async: true,
|
||||
pedantic: false,
|
||||
gfm: true,
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
| 参数 | 类型 | 作用 | 默认值 |
|
||||
|:-----------|:---------|:---------------------------------------------|:----------------|
|
||||
| breaks | boolean | 将换行符 \n 渲染为 <br>(类似 GitHub) | false |
|
||||
| gfm | boolean | 启用 GitHub Flavored Markdown 扩展(表格、删除线等) | true |
|
||||
| headerIds | boolean | 自动为标题添加 id 属性(如 `<h1 id="hello-world"></h1>`) | true |
|
||||
| highlight | function | 代码高亮处理函数,需返回高亮后的 HTML | null |
|
||||
| renderer | object | 自定义渲染器对象(覆盖默认渲染逻辑) | new Renderer() |
|
||||
| sanitize | boolean | 过滤危险 HTML 标签(防止 XSS 攻击) | false |
|
||||
| sanitizer | function | 自定义 HTML 过滤函数 | - |
|
||||
| silent | boolean | 静默模式:忽略解析错误(如未闭合的代码块) | false |
|
||||
|
||||
## 扩展 Markdown 语法
|
||||
|
||||
## 使用worker多线程
|
||||
|
||||
## 开启 sanitize: true 或使用 DOMPurify 二次过滤
|
||||
|
||||
## 性能优化
|
||||
|
||||
? 建议缓存常用操作
|
||||
|
||||
|
||||
## renderer方法
|
||||
|
||||
| 方法名及参数 | 对应 Markdown 元素 | 默认返回值示例 |
|
||||
|:-----------------------------------|:---------------------|:------------------------------------------|
|
||||
| code(code, language, isEscaped) | 代码块(三个反引号包裹) | `<pre><code class="lang">...</code></pre>` |
|
||||
| blockquote(quote) | 引用块 > | `<blockquote>...</blockquote>` |
|
||||
| html(html) | 原生 HTML 片段 | 直接返回原始 HTML |
|
||||
| heading(text, level, raw, slugger) | 标题 # | `<h1>...</h1>` |
|
||||
| hr() | 分割线 --- | `<hr>` |
|
||||
| list(body, ordered, start) | 列表(有序/无序) | `<ul>...</ul> 或 <ol>...</ol>` |
|
||||
| listitem(text, task, checked) | 列表项 - item | `<li>...</li>` |
|
||||
| checkbox(checked) | 任务列表复选框 - [x] | `<input type="checkbox" checked="">` |
|
||||
| paragraph(text) | 段落 | `<p>...</p>` |
|
||||
| table(header, body) | 表格 | `<table>...</table>` |
|
||||
| tablerow(content) | 表格行 | `<tr>...</tr>` |
|
||||
| tablecell(content, flags) | 表格单元格(flags 含对齐信息) | `<td>...</td> 或 <th>...</th>` |
|
||||
| strong(text) | 加粗 **text** | `<strong>...</strong>` |
|
||||
| em(text) | 斜体 *text* | `<em>...</em>` |
|
||||
| codespan(code) | 行内代码 `code` | `<code>...</code>` |
|
||||
| br() | 换行(两个空格结尾或 br 配置) | `<br>` |
|
||||
| del(text) | 删除线 ~~text~~ | `<del>...</del>` |
|
||||
| link(href, title, text) | 链接 [text](url) | `<a href="...">...</a>` |
|
||||
| image(href, title, text) | 图片  | `<img src="..." alt="...">` |
|
||||
| text(text) | 普通文本 | 直接返回文本(会转义 HTML) |
|
43
docs/f01-mrkdown/marked/highlight.js.md
Normal file
43
docs/f01-mrkdown/marked/highlight.js.md
Normal file
@ -0,0 +1,43 @@
|
||||
## 按需导入
|
||||
```js
|
||||
// 导入核心库和所需语言
|
||||
import hljs from 'highlight.js/lib/core';
|
||||
import javascript from 'highlight.js/lib/languages/javascript';
|
||||
import xml from 'highlight.js/lib/languages/xml';
|
||||
|
||||
// 注册语言
|
||||
hljs.registerLanguage('javascript', javascript);
|
||||
hljs.registerLanguage('xml', xml);
|
||||
|
||||
// 使用接口
|
||||
const code = '<div>Test</div>';
|
||||
const result = hljs.highlight(code, { language: 'xml' }).value;
|
||||
console.log(result);
|
||||
```
|
||||
|
||||
## 全部导入
|
||||
|
||||
```js
|
||||
import hljs from 'highlight.js';
|
||||
import 'highlight.js/styles/github.css';
|
||||
```
|
||||
|
||||
关键接口说明
|
||||
- highlightAll()
|
||||
自动检测页面中所有 `<pre><code>` 块的代码语言并高亮。
|
||||
|
||||
- highlight(code, { language })
|
||||
手动指定语言高亮代码(需提前注册对应语言)。
|
||||
|
||||
- highlightAuto(code)
|
||||
自动检测语言并高亮,返回包含语言类型和高亮结果的对象。
|
||||
|
||||
- registerLanguage(langName, languageDefinition)
|
||||
注册自定义或第三方语言模块。
|
||||
|
||||
## 注意事项
|
||||
- 主题切换:替换 CSS 文件路径即可(如 styles/default.min.css → styles/monokai-sublime.min.css)3。
|
||||
|
||||
- TypeScript 类型:安装 @types/highlight.js 或自定义类型声明(参考此方案)6。
|
||||
|
||||
- 性能优化:动态加载语言模块(如通过 import() 动态导入)10。
|
@ -32,7 +32,6 @@ export default defineNuxtConfig({
|
||||
'~/assets/css/iconfont.css',
|
||||
'~/assets/css/transitions.css',
|
||||
'~/assets/css/Ni.css',
|
||||
'bytemd/dist/index.css', // byteMD 编辑器
|
||||
],
|
||||
app: {
|
||||
head: {
|
||||
|
1633
package-lock.json
generated
1633
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,15 +14,15 @@
|
||||
"sqlV": "drizzle-kit studio"
|
||||
},
|
||||
"dependencies": {
|
||||
"@bytemd/plugin-gfm": "^1.22.0",
|
||||
"@bytemd/vue-next": "^1.22.0",
|
||||
"@nuxt/eslint": "^1.3.0",
|
||||
"bytemd": "^1.22.0",
|
||||
"consola": "^3.4.2",
|
||||
"dayjs": "^1.11.13",
|
||||
"dompurify": "^3.2.5",
|
||||
"drizzle-orm": "^0.42.0",
|
||||
"eslint": "^9.25.0",
|
||||
"highlight.js": "^11.11.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"marked": "^15.0.11",
|
||||
"mysql2": "^3.14.0",
|
||||
"nuxt": "^3.16.2",
|
||||
"redis": "^4.7.0",
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
<template>
|
||||
<div class="BlogEntity">
|
||||
<HomeBlogMarkdown></HomeBlogMarkdown>
|
||||
<HomeBlogMarked></HomeBlogMarked>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user