diff --git a/mateclaw-ui/src/views/Wiki/components/WikiPageViewer.vue b/mateclaw-ui/src/views/Wiki/components/WikiPageViewer.vue index 4f75f229..4cbeea82 100644 --- a/mateclaw-ui/src/views/Wiki/components/WikiPageViewer.vue +++ b/mateclaw-ui/src/views/Wiki/components/WikiPageViewer.vue @@ -28,7 +28,7 @@ -
+
@@ -52,44 +52,24 @@ import { ref, computed, watch, onMounted } from 'vue' import { useI18n } from 'vue-i18n' import { useWikiStore, type WikiPage } from '@/stores/useWikiStore' import { wikiApi } from '@/api/index' +import { useMarkdownRenderer } from '@/composables/useMarkdownRenderer' const { t } = useI18n() const store = useWikiStore() +const { renderMarkdown } = useMarkdownRenderer() const editing = ref(false) const editContent = ref('') const backlinks = ref([]) -// Simple markdown to HTML renderer with [[link]] support const renderedContent = computed(() => { if (!store.currentPage?.content) return '' - let html = store.currentPage.content - // Escape HTML - .replace(/&/g, '&') - .replace(//g, '>') - // Headers - .replace(/^### (.+)$/gm, '

$1

') - .replace(/^## (.+)$/gm, '

$1

') - .replace(/^# (.+)$/gm, '

$1

') - // Bold and italic - .replace(/\*\*(.+?)\*\*/g, '$1') - .replace(/\*(.+?)\*/g, '$1') - // Wiki links - .replace(/\[\[([^\]]+)\]\]/g, (_match, title) => { - const slug = title.trim().toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff\s-]/g, '').replace(/\s+/g, '-') - return `${title}` - }) - // Regular links - .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1') - // Lists - .replace(/^- (.+)$/gm, '
  • $1
  • ') - // Code - .replace(/`([^`]+)`/g, '$1') - // Paragraphs - .replace(/\n\n/g, '

    ') - .replace(/\n/g, '
    ') - return `

    ${html}

    ` + // Pre-process wiki links [[title]] before markdown rendering + const content = store.currentPage.content.replace(/\[\[([^\]]+)\]\]/g, (_match, title) => { + const slug = title.trim().toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff\s-]/g, '').replace(/\s+/g, '-') + return `${title}` + }) + return renderMarkdown(content) }) watch(() => store.currentPage, async (page) => { @@ -165,6 +145,13 @@ onMounted(() => { .page-content :deep(h3) { font-size: 18px; font-weight: 700; margin: 20px 0 8px; color: var(--mc-text-primary); } .page-content :deep(li) { margin-left: 24px; list-style: disc; } .page-content :deep(code) { background: var(--mc-bg-sunken); padding: 2px 6px; border-radius: 6px; font-size: 0.85em; } +.page-content :deep(pre code) { background: none; padding: 0; } +.page-content :deep(blockquote) { border-left: 3px solid var(--mc-primary); padding: 8px 16px; margin: 12px 0; color: var(--mc-text-secondary); background: var(--mc-bg-muted); border-radius: 0 10px 10px 0; } +.page-content :deep(table) { width: 100%; border-collapse: collapse; margin: 12px 0; font-size: 14px; } +.page-content :deep(th), .page-content :deep(td) { padding: 8px 12px; border: 1px solid var(--mc-border-light); text-align: left; } +.page-content :deep(th) { background: var(--mc-bg-sunken); font-weight: 600; } +.page-content :deep(hr) { border: none; border-top: 1px solid var(--mc-border-light); margin: 20px 0; } +.page-content :deep(img) { max-width: 100%; border-radius: 10px; } .page-content :deep(.wiki-link) { color: var(--mc-primary); text-decoration: none; cursor: pointer; border-bottom: 1px dashed var(--mc-primary); } .page-content :deep(.wiki-link:hover) { text-decoration: underline; }