perf(Markdown): 题面、教程、AI 提示每次打开都去 unpkg.com 拉四个库,机房网络下又慢又不稳
md-editor-v3 的 MdPreview 不管正文里有没有代码、公式,都会从 unpkg.com 加载 highlight.js、katex、mermaid、echarts(mermaid 还是另一份 11.x)。而我们只用到最基础的 Markdown:题面 95% 是富文本存的 HTML,教程是标题、表格、图片,AI 输出是标题、列表、 表格、少量代码块,库里一处公式、mermaid 都没有。 - 显示:新组件 shared/components/Markdown.vue,直接用 markdown-it,代码高亮走本地的 highlight.js(和练一练共用 shared/highlight.ts,补了 C++ / SQL)。9 处 MdPreview 全换掉。 正文默认 16px,教程也统一成 16px。 - 编辑:后台教程编辑器换成项目已有的 CodeMirror,加一排格式按钮;图片可以按按钮选、 粘贴、拖进来,都是先上传再插入。md-editor-v3 从依赖里删掉。 - AI 生成的内容(AI 提示、AI 分析、班级报告)不再放行原生 HTML:学生能在代码里诱导 AI 吐出 <img onerror>,老师在后台一打开就执行。库里 1196 条 AI 输出没有一个 HTML 标签,关掉不影响显示。题面、教程照旧放行。 - 长英文改成在单词之间折行,原来 break-all 会把 "I live" 断成 "I l / ive"。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c497ccadc8
commit
6f47ad8358
24 files changed
+504
-308
No files matched your search
@@ -0,0 +1,163 @@
|
||||
<script setup lang="ts">
|
||||
import { useThemeVars } from "naive-ui"
|
||||
import { renderMarkdown } from "shared/markdown"
|
||||
import "shared/highlight.css"
|
||||
|
||||
/**
|
||||
* 显示一段 Markdown / HTML 正文:题面、教程、AI 的提示和报告。
|
||||
* `html` 只给老师写的内容(题面、教程)打开,AI 生成的别开 —— 理由见 shared/markdown.ts。
|
||||
* 默认 16px / 1.75,和原来 md-editor 一样;各页可以用自己的 class 盖掉。
|
||||
*/
|
||||
const { content, html = false } = defineProps<{
|
||||
content: string | null | undefined
|
||||
html?: boolean
|
||||
}>()
|
||||
|
||||
const theme = useThemeVars()
|
||||
const rendered = computed(() => renderMarkdown(content ?? "", html))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="oj-markdown" v-html="rendered" />
|
||||
</template>
|
||||
|
||||
<!-- 不加 scoped:v-html 进来的节点不带 scoped 的属性,靠 .oj-markdown 前缀收住范围 -->
|
||||
<style>
|
||||
.oj-markdown {
|
||||
font-size: 16px;
|
||||
line-height: 1.75;
|
||||
/* 长英文、长网址在单词之间折;实在折不开再断词。原来 md-editor 是 break-all,
|
||||
题面里「Hello, I am …」这种一句话会从单词中间断开 */
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.oj-markdown > :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.oj-markdown > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.oj-markdown p {
|
||||
margin: 0.7em 0;
|
||||
}
|
||||
|
||||
.oj-markdown h1,
|
||||
.oj-markdown h2,
|
||||
.oj-markdown h3,
|
||||
.oj-markdown h4,
|
||||
.oj-markdown h5,
|
||||
.oj-markdown h6 {
|
||||
margin: 1.2em 0 0.5em;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.oj-markdown h1 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.oj-markdown h2 {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.oj-markdown h3 {
|
||||
font-size: 1.15em;
|
||||
}
|
||||
|
||||
.oj-markdown h4,
|
||||
.oj-markdown h5,
|
||||
.oj-markdown h6 {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.oj-markdown ul,
|
||||
.oj-markdown ol {
|
||||
margin: 0.5em 0;
|
||||
padding-left: 1.6em;
|
||||
}
|
||||
|
||||
.oj-markdown li + li {
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
|
||||
.oj-markdown li > p {
|
||||
margin: 0.25em 0;
|
||||
}
|
||||
|
||||
.oj-markdown a {
|
||||
color: v-bind("theme.primaryColor");
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.oj-markdown a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.oj-markdown img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.oj-markdown hr {
|
||||
margin: 1.2em 0;
|
||||
border: 0;
|
||||
border-top: 1px solid v-bind("theme.dividerColor");
|
||||
}
|
||||
|
||||
.oj-markdown blockquote {
|
||||
margin: 0.8em 0;
|
||||
padding: 0.1em 1em;
|
||||
border-left: 4px solid v-bind("theme.dividerColor");
|
||||
color: v-bind("theme.textColor3");
|
||||
}
|
||||
|
||||
.oj-markdown code {
|
||||
padding: 0.1em 0.45em;
|
||||
border-radius: 4px;
|
||||
background-color: rgba(128, 128, 128, 0.12);
|
||||
font-family: Consolas, Monaco, monospace;
|
||||
/* 等宽字体比同字号的中文矮,再缩就和旁边的字差一截 */
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
.oj-markdown pre {
|
||||
margin: 0.8em 0;
|
||||
padding: 12px 14px;
|
||||
border-radius: 6px;
|
||||
background-color: rgba(128, 128, 128, 0.08);
|
||||
overflow-x: auto;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.oj-markdown pre code {
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
background: none;
|
||||
font-size: 14px;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
/* 宽表自己横着滚,不把整页撑出横向滚动条 */
|
||||
.oj-markdown table {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
margin: 0.8em 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.oj-markdown th,
|
||||
.oj-markdown td {
|
||||
padding: 6px 12px;
|
||||
border: 1px solid v-bind("theme.dividerColor");
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.oj-markdown th {
|
||||
font-weight: 600;
|
||||
background-color: rgba(128, 128, 128, 0.08);
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user