feat(markdown): 只读内容支持 Markdown 渲染
- 新增 MarkdownRenderer.vue 组件,基于 marked 解析 GFM
- 学习残片/报告历史列表:纯文本 {{ }} 替换为 MarkdownRenderer
- ReviewDetail.vue content-body:p 标签逐行渲染替换为 MarkdownRenderer
- 结束会话总结框:增加「编辑/预览」切换,预览模式实时渲染 markdown
- 学习预期展示也改为 MarkdownRenderer
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { marked } from "marked";
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
content: string;
|
||||
}>(), {
|
||||
content: "",
|
||||
});
|
||||
|
||||
const rendered = computed(() => {
|
||||
if (!props.content) return "";
|
||||
return marked.parse(props.content, { breaks: true, gfm: true });
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="markdown-body" v-html="rendered"></div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.markdown-body {
|
||||
line-height: 1.8;
|
||||
color: #333;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.markdown-body :deep(h1),
|
||||
.markdown-body :deep(h2),
|
||||
.markdown-body :deep(h3),
|
||||
.markdown-body :deep(h4) {
|
||||
margin: 0.75em 0 0.5em;
|
||||
color: var(--green-900, #1b5e20);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-body :deep(h1) { font-size: 1.4em; }
|
||||
.markdown-body :deep(h2) { font-size: 1.25em; }
|
||||
.markdown-body :deep(h3) { font-size: 1.1em; }
|
||||
|
||||
.markdown-body :deep(p) {
|
||||
margin: 0.4em 0;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.markdown-body :deep(ul),
|
||||
.markdown-body :deep(ol) {
|
||||
margin: 0.4em 0;
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
|
||||
.markdown-body :deep(li) {
|
||||
margin: 0.2em 0;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.markdown-body :deep(code) {
|
||||
background: #f0f4f0;
|
||||
padding: 0.15em 0.4em;
|
||||
border-radius: 3px;
|
||||
font-size: 0.9em;
|
||||
color: #c62828;
|
||||
}
|
||||
|
||||
.markdown-body :deep(pre) {
|
||||
background: #f5f7f5;
|
||||
padding: 12px 16px;
|
||||
border-radius: 6px;
|
||||
overflow-x: auto;
|
||||
border: 1px solid #e0e8e0;
|
||||
margin: 0.6em 0;
|
||||
}
|
||||
|
||||
.markdown-body :deep(pre code) {
|
||||
background: none;
|
||||
padding: 0;
|
||||
color: #333;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.markdown-body :deep(blockquote) {
|
||||
margin: 0.5em 0;
|
||||
padding: 0.5em 1em;
|
||||
border-left: 3px solid var(--green-600, #2f8f68);
|
||||
background: #f1f8e9;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.markdown-body :deep(a) {
|
||||
color: var(--green-600, #2f8f68);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.markdown-body :deep(a:hover) {
|
||||
color: var(--green-900, #1b5e20);
|
||||
}
|
||||
|
||||
.markdown-body :deep(strong) {
|
||||
font-weight: 600;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.markdown-body :deep(hr) {
|
||||
border: none;
|
||||
border-top: 1px solid #e0e8e0;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown-body :deep(table) {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin: 0.6em 0;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.markdown-body :deep(th),
|
||||
.markdown-body :deep(td) {
|
||||
border: 1px solid #d0d8d0;
|
||||
padding: 6px 10px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown-body :deep(th) {
|
||||
background: #e8f5e9;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-body :deep(img) {
|
||||
max-width: 100%;
|
||||
border-radius: 4px;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user