feat: 发布v3.5版本
This commit is contained in:
897
Yi.Ai.Vue3/src/components/MarkedMarkdown/index.vue
Normal file
897
Yi.Ai.Vue3/src/components/MarkedMarkdown/index.vue
Normal file
@@ -0,0 +1,897 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onUnmounted, ref, watch } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { Marked } from 'marked';
|
||||
import hljs from 'highlight.js';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { ElDrawer } from 'element-plus';
|
||||
import { useDesignStore } from '@/stores';
|
||||
|
||||
interface Props {
|
||||
content: string;
|
||||
theme?: 'light' | 'dark' | 'auto';
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
content: '',
|
||||
theme: 'auto',
|
||||
});
|
||||
|
||||
const designStore = useDesignStore();
|
||||
const { darkMode } = storeToRefs(designStore);
|
||||
const containerRef = ref<HTMLElement | null>(null);
|
||||
const renderedHtml = ref('');
|
||||
|
||||
// 抽屉相关状态
|
||||
const drawerVisible = ref(false);
|
||||
const previewHtml = ref('');
|
||||
const iframeRef = ref<HTMLIFrameElement | null>(null);
|
||||
|
||||
// 创建 marked 实例
|
||||
const marked = new Marked();
|
||||
|
||||
// 配置 marked
|
||||
marked.setOptions({
|
||||
gfm: true,
|
||||
breaks: true,
|
||||
});
|
||||
|
||||
// 自定义渲染器
|
||||
const renderer = {
|
||||
// 代码块渲染
|
||||
code(token: { text: string; lang?: string }) {
|
||||
const { text, lang } = token;
|
||||
const language = lang && hljs.getLanguage(lang) ? lang : 'plaintext';
|
||||
const validLanguage = hljs.getLanguage(language) ? language : 'plaintext';
|
||||
|
||||
let highlighted: string;
|
||||
try {
|
||||
highlighted = hljs.highlight(text, { language: validLanguage, ignoreIllegals: true }).value;
|
||||
} catch {
|
||||
highlighted = hljs.highlightAuto(text).value;
|
||||
}
|
||||
|
||||
const langLabel = lang || 'code';
|
||||
|
||||
// 生成行号
|
||||
const lines = text.split('\n');
|
||||
const lineNumbers = lines.map((_, i) => `<span class="line-number">${i + 1}</span>`).join('');
|
||||
|
||||
// 判断是否为HTML代码
|
||||
const isHtml = lang?.toLowerCase() === 'html' || lang?.toLowerCase() === 'htm';
|
||||
|
||||
// 预览按钮(仅HTML显示)
|
||||
const previewBtn = isHtml ? `
|
||||
<button class="preview-btn" data-html="${encodeURIComponent(text)}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
</svg>
|
||||
<span>预览</span>
|
||||
</button>
|
||||
` : '';
|
||||
|
||||
return `<div class="code-block-wrapper${isHtml ? ' is-html' : ''}">
|
||||
<div class="code-block-header">
|
||||
<span class="code-lang">${langLabel}</span>
|
||||
<div class="code-block-actions">
|
||||
${previewBtn}
|
||||
<button class="copy-btn" data-code="${encodeURIComponent(text)}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
||||
</svg>
|
||||
<span>复制</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="code-block-body">
|
||||
<div class="line-numbers">${lineNumbers}</div>
|
||||
<pre class="hljs"><code class="language-${validLanguage}">${highlighted}</code></pre>
|
||||
</div>
|
||||
</div>`;
|
||||
},
|
||||
|
||||
// 行内代码
|
||||
codespan(token: { text: string }) {
|
||||
return `<code class="inline-code">${token.text}</code>`;
|
||||
},
|
||||
|
||||
// 链接
|
||||
link(token: { href: string; title?: string; text: string }) {
|
||||
const { href, title, text } = token;
|
||||
const titleAttr = title ? ` title="${title}"` : '';
|
||||
return `<a href="${href}"${titleAttr} target="_blank" rel="noopener noreferrer">${text}</a>`;
|
||||
},
|
||||
|
||||
// 图片
|
||||
image(token: { href: string; title?: string; text: string }) {
|
||||
const { href, title, text } = token;
|
||||
const titleAttr = title ? ` title="${title}"` : '';
|
||||
return `<img src="${href}" alt="${text}"${titleAttr} loading="lazy" class="markdown-image" />`;
|
||||
},
|
||||
|
||||
// 表格
|
||||
table(token: { header: string; body: string }) {
|
||||
return `<div class="table-wrapper"><table>${token.header}${token.body}</table></div>`;
|
||||
},
|
||||
};
|
||||
|
||||
marked.use({ renderer });
|
||||
|
||||
// 防抖渲染,优化流式性能
|
||||
let renderTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let lastContent = '';
|
||||
const RENDER_DELAY = 16; // 约60fps
|
||||
|
||||
function scheduleRender(content: string) {
|
||||
if (renderTimer) {
|
||||
clearTimeout(renderTimer);
|
||||
}
|
||||
|
||||
renderTimer = setTimeout(() => {
|
||||
renderContent(content);
|
||||
renderTimer = null;
|
||||
}, RENDER_DELAY);
|
||||
}
|
||||
|
||||
// 渲染内容
|
||||
async function renderContent(content: string) {
|
||||
if (!content) {
|
||||
renderedHtml.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果内容没变化,跳过渲染
|
||||
if (content === lastContent) {
|
||||
return;
|
||||
}
|
||||
lastContent = content;
|
||||
|
||||
try {
|
||||
const rawHtml = await marked.parse(content);
|
||||
// 使用 DOMPurify 清理 HTML,防止 XSS
|
||||
renderedHtml.value = DOMPurify.sanitize(rawHtml, {
|
||||
ADD_TAGS: ['iframe'],
|
||||
ADD_ATTR: ['target', 'data-code', 'data-html'],
|
||||
});
|
||||
|
||||
// 渲染后绑定按钮事件
|
||||
nextTick(() => {
|
||||
bindButtons();
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Markdown 渲染错误:', error);
|
||||
renderedHtml.value = `<pre>${content}</pre>`;
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定按钮事件
|
||||
function bindButtons() {
|
||||
if (!containerRef.value) return;
|
||||
|
||||
// 绑定复制按钮
|
||||
const copyButtons = containerRef.value.querySelectorAll('.copy-btn');
|
||||
copyButtons.forEach((btn) => {
|
||||
const newBtn = btn.cloneNode(true) as HTMLElement;
|
||||
btn.parentNode?.replaceChild(newBtn, btn);
|
||||
|
||||
newBtn.addEventListener('click', async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const code = decodeURIComponent(newBtn.getAttribute('data-code') || '');
|
||||
try {
|
||||
await navigator.clipboard.writeText(code);
|
||||
const spanEl = newBtn.querySelector('span');
|
||||
if (spanEl) {
|
||||
const originalText = spanEl.textContent;
|
||||
spanEl.textContent = '已复制';
|
||||
newBtn.classList.add('copied');
|
||||
setTimeout(() => {
|
||||
spanEl.textContent = originalText;
|
||||
newBtn.classList.remove('copied');
|
||||
}, 2000);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('复制失败:', err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 绑定预览按钮
|
||||
const previewButtons = containerRef.value.querySelectorAll('.preview-btn');
|
||||
previewButtons.forEach((btn) => {
|
||||
const newBtn = btn.cloneNode(true) as HTMLElement;
|
||||
btn.parentNode?.replaceChild(newBtn, btn);
|
||||
|
||||
newBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const html = decodeURIComponent(newBtn.getAttribute('data-html') || '');
|
||||
openHtmlPreview(html);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 打开HTML预览抽屉
|
||||
function openHtmlPreview(html: string) {
|
||||
// 检查是否是完整的HTML文档,如果不是则包装一下
|
||||
let fullHtml = html;
|
||||
if (!html.toLowerCase().includes('<!doctype') && !html.toLowerCase().includes('<html')) {
|
||||
fullHtml = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>* { box-sizing: border-box; } body { margin: 0; padding: 16px; font-family: sans-serif; }</style>
|
||||
</head>
|
||||
<body>
|
||||
${html}
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
previewHtml.value = fullHtml;
|
||||
drawerVisible.value = true;
|
||||
}
|
||||
|
||||
// 监听主题变化
|
||||
const resolvedTheme = computed(() => {
|
||||
if (props.theme === 'auto') {
|
||||
// 从全局状态获取主题
|
||||
return darkMode.value === 'light' ? 'light' : 'dark';
|
||||
}
|
||||
return props.theme;
|
||||
});
|
||||
|
||||
// 监听内容变化
|
||||
watch(
|
||||
() => props.content,
|
||||
(newContent) => {
|
||||
scheduleRender(newContent);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 暴露方法供外部调用
|
||||
defineExpose({
|
||||
refresh: () => renderContent(props.content),
|
||||
});
|
||||
|
||||
// 清理定时器
|
||||
onUnmounted(() => {
|
||||
if (renderTimer) {
|
||||
clearTimeout(renderTimer);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
ref="containerRef"
|
||||
class="marked-markdown"
|
||||
:class="[`theme-${resolvedTheme}`]"
|
||||
v-html="renderedHtml"
|
||||
/>
|
||||
|
||||
<!-- HTML预览抽屉 -->
|
||||
<ElDrawer
|
||||
v-model="drawerVisible"
|
||||
title="HTML 预览"
|
||||
direction="rtl"
|
||||
size="50%"
|
||||
:append-to-body="true"
|
||||
class="html-preview-drawer"
|
||||
>
|
||||
<div class="preview-container">
|
||||
<iframe
|
||||
ref="iframeRef"
|
||||
class="preview-iframe"
|
||||
:srcdoc="previewHtml"
|
||||
sandbox="allow-scripts allow-modals allow-forms allow-popups allow-same-origin"
|
||||
/>
|
||||
</div>
|
||||
</ElDrawer>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.marked-markdown {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji';
|
||||
font-size: 15px;
|
||||
line-height: 1.7;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
|
||||
// 深色主题
|
||||
&.theme-dark {
|
||||
color: #e6edf3;
|
||||
|
||||
a {
|
||||
color: #58a6ff;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: #e6edf3;
|
||||
border-bottom-color: #30363d;
|
||||
}
|
||||
|
||||
hr {
|
||||
background-color: #30363d;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
color: #8b949e;
|
||||
border-left-color: #3b434b;
|
||||
}
|
||||
|
||||
code.inline-code {
|
||||
background-color: rgba(110, 118, 129, 0.4);
|
||||
color: #e6edf3;
|
||||
}
|
||||
|
||||
.code-block-wrapper {
|
||||
background-color: #161b22;
|
||||
border: 1px solid #30363d;
|
||||
|
||||
.code-block-header {
|
||||
background-color: #21262d;
|
||||
border-bottom-color: #30363d;
|
||||
|
||||
.code-lang {
|
||||
color: #8b949e;
|
||||
}
|
||||
|
||||
.copy-btn,
|
||||
.preview-btn {
|
||||
color: #8b949e;
|
||||
&:hover {
|
||||
color: #e6edf3;
|
||||
background-color: #30363d;
|
||||
}
|
||||
&.copied {
|
||||
color: #3fb950;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.code-block-body {
|
||||
.line-numbers {
|
||||
background-color: #161b22;
|
||||
border-right: 1px solid #30363d;
|
||||
|
||||
.line-number {
|
||||
color: #6e7681;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pre.hljs {
|
||||
background-color: #161b22;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
th {
|
||||
background-color: #21262d;
|
||||
}
|
||||
td, th {
|
||||
border-color: #30363d;
|
||||
}
|
||||
tr:nth-child(2n) {
|
||||
background-color: #161b22;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-image {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
// 浅色主题
|
||||
&.theme-light {
|
||||
color: #24292f;
|
||||
|
||||
a {
|
||||
color: #0969da;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: #24292f;
|
||||
border-bottom-color: #d0d7de;
|
||||
}
|
||||
|
||||
hr {
|
||||
background-color: #d0d7de;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
color: #57606a;
|
||||
border-left-color: #d0d7de;
|
||||
}
|
||||
|
||||
code.inline-code {
|
||||
background-color: rgba(175, 184, 193, 0.2);
|
||||
color: #24292f;
|
||||
}
|
||||
|
||||
.code-block-wrapper {
|
||||
background-color: #f6f8fa;
|
||||
border: 1px solid #d0d7de;
|
||||
|
||||
.code-block-header {
|
||||
background-color: #f6f8fa;
|
||||
border-bottom-color: #d0d7de;
|
||||
|
||||
.code-lang {
|
||||
color: #57606a;
|
||||
}
|
||||
|
||||
.copy-btn,
|
||||
.preview-btn {
|
||||
color: #57606a;
|
||||
&:hover {
|
||||
color: #24292f;
|
||||
background-color: #d0d7de;
|
||||
}
|
||||
&.copied {
|
||||
color: #1a7f37;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.code-block-body {
|
||||
.line-numbers {
|
||||
background-color: #f6f8fa;
|
||||
border-right: 1px solid #d0d7de;
|
||||
|
||||
.line-number {
|
||||
color: #8c959f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pre.hljs {
|
||||
background-color: #f6f8fa;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
th {
|
||||
background-color: #f6f8fa;
|
||||
}
|
||||
td, th {
|
||||
border-color: #d0d7de;
|
||||
}
|
||||
tr:nth-child(2n) {
|
||||
background-color: #f6f8fa;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 通用样式
|
||||
> *:first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
> *:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
// 标题
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
padding-bottom: 0.3em;
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5em;
|
||||
padding-bottom: 0.3em;
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
// 段落
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
// 列表
|
||||
ul, ol {
|
||||
padding-left: 2em;
|
||||
margin-top: 0;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
|
||||
li + li {
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
|
||||
// 引用
|
||||
blockquote {
|
||||
margin: 0 0 16px 0;
|
||||
padding: 0 1em;
|
||||
border-left: 0.25em solid;
|
||||
|
||||
> :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
> :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 分割线
|
||||
hr {
|
||||
height: 0.25em;
|
||||
padding: 0;
|
||||
margin: 24px 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
// 行内代码
|
||||
code.inline-code {
|
||||
padding: 0.2em 0.4em;
|
||||
margin: 0;
|
||||
font-size: 85%;
|
||||
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
// 代码块
|
||||
.code-block-wrapper {
|
||||
margin: 16px 0;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
|
||||
.code-block-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 16px;
|
||||
border-bottom: 1px solid;
|
||||
|
||||
.code-lang {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.code-block-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.copy-btn,
|
||||
.preview-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 8px;
|
||||
font-size: 12px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.code-block-body {
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
|
||||
// 自定义滚动条样式
|
||||
&::-webkit-scrollbar {
|
||||
height: 6px;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(128, 128, 128, 0.4);
|
||||
border-radius: 3px;
|
||||
&:hover {
|
||||
background-color: rgba(128, 128, 128, 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
.line-numbers {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 16px 0;
|
||||
text-align: right;
|
||||
user-select: none;
|
||||
flex-shrink: 0;
|
||||
|
||||
.line-number {
|
||||
padding: 0 12px;
|
||||
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
||||
font-size: 14px;
|
||||
//line-height: 21px;
|
||||
height: 22.72px;
|
||||
min-width: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pre.hljs {
|
||||
margin: 0;
|
||||
padding: 16px;
|
||||
padding-left: 12px;
|
||||
overflow-x: auto;
|
||||
font-size: 14px;
|
||||
line-height: 21px;
|
||||
flex: 1;
|
||||
white-space: pre;
|
||||
|
||||
code {
|
||||
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
||||
background: transparent;
|
||||
padding: 0;
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 表格
|
||||
.table-wrapper {
|
||||
overflow-x: auto;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
table {
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
|
||||
th, td {
|
||||
padding: 8px 16px;
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
th {
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
// 图片
|
||||
.markdown-image {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
// 强调
|
||||
strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
em {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
// 删除线
|
||||
del {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
// 任务列表
|
||||
input[type="checkbox"] {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
// highlight.js 深色主题样式
|
||||
.marked-markdown.theme-dark {
|
||||
.hljs {
|
||||
color: #c9d1d9;
|
||||
}
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #8b949e;
|
||||
font-style: italic;
|
||||
}
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-addition {
|
||||
color: #ff7b72;
|
||||
}
|
||||
.hljs-number,
|
||||
.hljs-string,
|
||||
.hljs-meta .hljs-meta-string,
|
||||
.hljs-literal,
|
||||
.hljs-doctag,
|
||||
.hljs-regexp {
|
||||
color: #a5d6ff;
|
||||
}
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class {
|
||||
color: #d2a8ff;
|
||||
}
|
||||
.hljs-attribute,
|
||||
.hljs-attr,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-class .hljs-title,
|
||||
.hljs-type {
|
||||
color: #79c0ff;
|
||||
}
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-subst,
|
||||
.hljs-meta,
|
||||
.hljs-meta .hljs-keyword,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-link {
|
||||
color: #ffa657;
|
||||
}
|
||||
.hljs-built_in,
|
||||
.hljs-deletion {
|
||||
color: #ffa198;
|
||||
}
|
||||
.hljs-formula {
|
||||
background-color: #21262d;
|
||||
}
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
// highlight.js 浅色主题样式
|
||||
.marked-markdown.theme-light {
|
||||
.hljs {
|
||||
color: #24292f;
|
||||
}
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #6e7781;
|
||||
font-style: italic;
|
||||
}
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-addition {
|
||||
color: #cf222e;
|
||||
}
|
||||
.hljs-number,
|
||||
.hljs-string,
|
||||
.hljs-meta .hljs-meta-string,
|
||||
.hljs-literal,
|
||||
.hljs-doctag,
|
||||
.hljs-regexp {
|
||||
color: #0a3069;
|
||||
}
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class {
|
||||
color: #8250df;
|
||||
}
|
||||
.hljs-attribute,
|
||||
.hljs-attr,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-class .hljs-title,
|
||||
.hljs-type {
|
||||
color: #0550ae;
|
||||
}
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-subst,
|
||||
.hljs-meta,
|
||||
.hljs-meta .hljs-keyword,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-link {
|
||||
color: #953800;
|
||||
}
|
||||
.hljs-built_in,
|
||||
.hljs-deletion {
|
||||
color: #82071e;
|
||||
}
|
||||
.hljs-formula {
|
||||
background-color: #f6f8fa;
|
||||
}
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
// HTML预览抽屉样式
|
||||
.html-preview-drawer {
|
||||
.el-drawer__header {
|
||||
margin-bottom: 0;
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.el-drawer__body {
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.preview-iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
}
|
||||
.marked-markdown .code-block-wrapper pre.hljs {
|
||||
padding-bottom: 4px;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
height: 6px;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(128, 128, 128, 0.4);
|
||||
border-radius: 3px;
|
||||
&:hover {
|
||||
background-color: rgba(128, 128, 128, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user