worklog/.qoder/skills/markdown-render.md
zhangjf 90a5c032c2 docs: 添加开发技能文档(skills)
- markdown-render.md: Markdown渲染实现方案
- dashboard-calendar.md: 管理后台首页日历功能实现
2026-02-25 01:28:34 +08:00

258 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Markdown渲染Skill
## 适用场景
当前端页面需要渲染Markdown格式的文本内容时使用适用于
- 日志详情展示
- 文章内容展示
- 备注/说明信息展示
## 实现方案
使用纯JavaScript实现简单的Markdown渲染无需引入第三方库。
### Vue 3 组件实现
```vue
<script setup lang="ts">
import { computed } from 'vue'
const props = defineProps<{
content?: string
}>()
const renderedContent = computed(() => {
if (!props.content) return '<p class="empty-content">暂无内容</p>'
let content = props.content
// 1. 转义HTML特殊字符安全第一
content = content
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
// 2. 标题(按优先级从高到低)
content = content.replace(/^### (.+)$/gm, '<h3>$1</h3>')
content = content.replace(/^## (.+)$/gm, '<h2>$1</h2>')
content = content.replace(/^# (.+)$/gm, '<h1>$1</h1>')
// 3. 粗体和斜体
content = content.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
content = content.replace(/\*(.+?)\*/g, '<em>$1</em>')
// 4. 代码块(多行)和行内代码
content = content.replace(/```(\w*)\n([\s\S]*?)```/g, '<pre><code class="language-$1">$2</code></pre>')
content = content.replace(/`(.+?)`/g, '<code>$1</code>')
// 5. 列表
content = content.replace(/^- (.+)$/gm, '<li>$1</li>')
content = content.replace(/(<li>.*<\/li>\n?)+/g, '<ul>$&</ul>')
content = content.replace(/^\d+\. (.+)$/gm, '<li>$1</li>')
// 6. 段落和换行
content = content.replace(/\n\n/g, '</p><p>')
content = content.replace(/\n/g, '<br>')
return `<p>${content}</p>`
})
</script>
<template>
<div class="markdown-body" v-html="renderedContent"></div>
</template>
```
### 样式规范
#### 管理后台Element Plus
```css
.markdown-body {
font-size: 14px;
line-height: 1.8;
color: #303133;
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3 {
margin: 16px 0 8px;
font-weight: 600;
line-height: 1.4;
}
.markdown-body h1 {
font-size: 20px;
border-bottom: 1px solid #ebeef5;
padding-bottom: 8px;
}
.markdown-body h2 {
font-size: 18px;
}
.markdown-body h3 {
font-size: 16px;
}
.markdown-body ul {
margin: 8px 0;
padding-left: 24px;
}
.markdown-body li {
margin: 4px 0;
}
.markdown-body code {
background-color: #f5f7fa;
padding: 2px 6px;
border-radius: 4px;
font-family: 'Courier New', Courier, monospace;
font-size: 13px;
}
.markdown-body pre {
background-color: #f5f7fa;
padding: 12px;
border-radius: 6px;
overflow-x: auto;
margin: 12px 0;
}
.markdown-body pre code {
background-color: transparent;
padding: 0;
}
.empty-content {
color: #909399;
text-align: center;
}
```
#### 移动端Vant
```css
.markdown-body {
font-size: 14px;
line-height: 1.8;
color: #333;
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3 {
margin: 16px 0 8px;
font-weight: 600;
line-height: 1.4;
}
.markdown-body h1 {
font-size: 18px;
border-bottom: 1px solid #ebedf0;
padding-bottom: 8px;
}
.markdown-body h2 {
font-size: 16px;
}
.markdown-body h3 {
font-size: 15px;
}
.markdown-body ul {
margin: 8px 0;
padding-left: 20px;
}
.markdown-body li {
margin: 4px 0;
}
.markdown-body code {
background-color: #f5f7fa;
padding: 2px 6px;
border-radius: 4px;
font-family: 'Courier New', Courier, monospace;
font-size: 13px;
}
.markdown-body pre {
background-color: #f5f7fa;
padding: 12px;
border-radius: 6px;
overflow-x: auto;
margin: 12px 0;
}
.markdown-body pre code {
background-color: transparent;
padding: 0;
}
.empty-content {
color: #969799;
text-align: center;
}
```
## 支持的Markdown语法
| 语法 | Markdown | 渲染结果 |
|------|----------|----------|
| 一级标题 | `# 标题` | `<h1>标题</h1>` |
| 二级标题 | `## 标题` | `<h2>标题</h2>` |
| 三级标题 | `### 标题` | `<h3>标题</h3>` |
| 粗体 | `**文本**` | `<strong>文本</strong>` |
| 斜体 | `*文本*` | `<em>文本</em>` |
| 行内代码 | `` `代码` `` | `<code>代码</code>` |
| 代码块 | ` ```代码块``` ` | `<pre><code>代码块</code></pre>` |
| 无序列表 | `- 列表项` | `<ul><li>列表项</li></ul>` |
| 段落换行 | 空行分隔 | `<p>` 标签 |
| 普通换行 | 单个换行 | `<br>` |
## 安全说明
**重要**渲染前必须转义HTML特殊字符`&`, `<`, `>`防止XSS攻击。
```javascript
// 必须首先执行
content = content
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
```
## 使用示例
### 日志详情弹窗
```vue
<template>
<el-dialog v-model="visible" width="700px">
<template #header>
<div class="log-detail-header">
<div class="header-left">
<span class="log-date">{{ logDate }}</span>
<span class="log-title">{{ title }}</span>
</div>
<span class="log-user">{{ userName }}</span>
</div>
</template>
<div class="log-detail-content">
<div class="markdown-body" v-html="renderedContent"></div>
</div>
</el-dialog>
</template>
```
## 注意事项
1. **性能考虑**:对于大量内容的渲染,考虑使用`v-memo`或虚拟滚动
2. **代码高亮**如需语法高亮可额外集成highlight.js或prism.js
3. **扩展语法**:当前实现为基础语法,如需表格、引用等可按需扩展