fix: markdown深色模式内容区和代码块未适配bug; markdownViewer改为vidtor自带预览模式 (#2023)
* fix(Markdown): 修复深色模式 内容区和代码块 未改变主题bug
* perf(Markdown): MarkDown组件示例增加不同功能示例; 切换深色主题按钮 同时改变 内容区和代码块主题
* perf(MarkdownViewer): MarkdownViewer改为vditor自带的预览模式; 同时适配深色模式
Co-authored-by: 苗大 <v.caoshm@yoozoo.com>
| | |
| | | import { useModalContext } from '../../Modal'; |
| | | import { useRootSetting } from '/@/hooks/setting/useRootSetting'; |
| | | import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated'; |
| | | import { getTheme } from './getTheme'; |
| | | |
| | | type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined; |
| | | |
| | |
| | | if (!inited) { |
| | | return; |
| | | } |
| | | const theme = val === 'dark' ? 'dark' : 'classic'; |
| | | instance.getVditor()?.setTheme(theme); |
| | | instance |
| | | .getVditor() |
| | | ?.setTheme(getTheme(val) as any, getTheme(val, 'content'), getTheme(val, 'code')); |
| | | }, |
| | | { |
| | | immediate: true, |
| | |
| | | if (!wrapEl) return; |
| | | const bindValue = { ...attrs, ...props }; |
| | | const insEditor = new Vditor(wrapEl, { |
| | | theme: getDarkMode.value === 'dark' ? 'dark' : 'classic', |
| | | // 设置外观主题 |
| | | theme: getTheme(getDarkMode.value) as any, |
| | | lang: unref(getCurrentLang), |
| | | mode: 'sv', |
| | | fullscreen: { |
| | | index: 520, |
| | | }, |
| | | preview: { |
| | | theme: { |
| | | // 设置内容主题 |
| | | current: getTheme(getDarkMode.value, 'content'), |
| | | }, |
| | | hljs: { |
| | | // 设置代码块主题 |
| | | style: getTheme(getDarkMode.value, 'code'), |
| | | }, |
| | | actions: [], |
| | | }, |
| | | input: (v) => { |
| | |
| | | <template> |
| | | <!-- eslint-disable vue/no-v-html --> |
| | | <div v-html="getHtmlData" :class="$props.class" class="markdown-viewer"></div> |
| | | <div ref="viewerRef" id="markdownViewer" :class="$props.class"></div> |
| | | </template> |
| | | |
| | | <script lang="ts" setup> |
| | | import { computed, defineProps } from 'vue'; |
| | | import showdown from 'showdown'; |
| | | |
| | | const converter = new showdown.Converter(); |
| | | converter.setOption('tables', true); |
| | | import { defineProps, onBeforeUnmount, onDeactivated, Ref, ref, unref, watch } from 'vue'; |
| | | import VditorPreview from 'vditor/dist/method.min'; |
| | | import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated'; |
| | | import { useRootSetting } from '/@/hooks/setting/useRootSetting'; |
| | | import { getTheme } from './getTheme'; |
| | | const props = defineProps({ |
| | | value: { type: String }, |
| | | class: { type: String }, |
| | | }); |
| | | const getHtmlData = computed(() => converter.makeHtml(props.value || '')); |
| | | </script> |
| | | const viewerRef = ref<ElRef>(null); |
| | | const vditorPreviewRef = ref(null) as Ref<Nullable<VditorPreview>>; |
| | | const { getDarkMode } = useRootSetting(); |
| | | |
| | | <style scoped> |
| | | .markdown-viewer { |
| | | width: 100%; |
| | | function init() { |
| | | const viewerEl = unref(viewerRef) as HTMLElement; |
| | | vditorPreviewRef.value = VditorPreview.preview(viewerEl, props.value, { |
| | | mode: getTheme(getDarkMode.value, 'content'), |
| | | theme: { |
| | | // 设置内容主题 |
| | | current: getTheme(getDarkMode.value, 'content'), |
| | | }, |
| | | hljs: { |
| | | // 设置代码块主题 |
| | | style: getTheme(getDarkMode.value, 'code'), |
| | | }, |
| | | }); |
| | | } |
| | | </style> |
| | | watch( |
| | | () => getDarkMode.value, |
| | | (val) => { |
| | | VditorPreview.setContentTheme(getTheme(val, 'content')); |
| | | VditorPreview.setCodeTheme(getTheme(val, 'code')); |
| | | init(); |
| | | }, |
| | | ); |
| | | |
| | | watch( |
| | | () => props.value, |
| | | (v, oldValue) => { |
| | | v !== oldValue && init(); |
| | | }, |
| | | ); |
| | | |
| | | function destroy() { |
| | | const vditorInstance = unref(vditorPreviewRef); |
| | | if (!vditorInstance) return; |
| | | try { |
| | | vditorInstance?.destroy?.(); |
| | | } catch (error) {} |
| | | vditorPreviewRef.value = null; |
| | | } |
| | | |
| | | onMountedOrActivated(init); |
| | | |
| | | onBeforeUnmount(destroy); |
| | | onDeactivated(destroy); |
| | | </script> |
New file |
| | |
| | | /** |
| | | * 获取主题类型 深色浅色模式 对应的值 |
| | | * @param darkModeVal 深色模式值 |
| | | * @param themeMode 主题类型——外观(默认), 内容, 代码块 |
| | | */ |
| | | export const getTheme = ( |
| | | darkModeVal: 'light' | 'dark' | string, |
| | | themeMode: 'default' | 'content' | 'code' = 'default', |
| | | ) => { |
| | | const isDark = darkModeVal === 'dark'; |
| | | switch (themeMode) { |
| | | case 'default': |
| | | return isDark ? 'dark' : 'classic'; |
| | | case 'content': |
| | | return isDark ? 'dark' : 'light'; |
| | | case 'code': |
| | | return isDark ? 'dracula' : 'github'; |
| | | } |
| | | }; |
| | |
| | | setup() { |
| | | const markDownRef = ref<Nullable<MarkDownActionType>>(null); |
| | | const valueRef = ref(` |
| | | # title |
| | | # 标题h1 |
| | | |
| | | # content |
| | | ##### 标题h5 |
| | | |
| | | **加粗** |
| | | *斜体* |
| | | ~~删除线~~ |
| | | [链接](https://github.com/vbenjs/vue-vben-admin) |
| | | ↓分割线↓ |
| | | |
| | | --- |
| | | |
| | | |
| | | * 无序列表1 |
| | | * 无序列表1.1 |
| | | |
| | | 1. 有序列表1 |
| | | 2. 有序列表2 |
| | | |
| | | * [ ] 任务列表1 |
| | | * [x] 任务列表2 |
| | | |
| | | > 引用示例 |
| | | |
| | | \`\`\`js |
| | | // 代码块: |
| | | (() => { |
| | | var htmlRoot = document.getElementById('htmlRoot'); |
| | | var theme = window.localStorage.getItem('__APP__DARK__MODE__'); |
| | | if (htmlRoot && theme) { |
| | | htmlRoot.setAttribute('data-theme', theme); |
| | | theme = htmlRoot = null; |
| | | } |
| | | })(); |
| | | \`\`\` |
| | | |
| | | | 表格 | 示例 | 🎉️ | |
| | | | --- | --- | --- | |
| | | | 1 | 2 | 3 | |
| | | | 4 | 5 | 6 | |
| | | `); |
| | | |
| | | function toggleTheme() { |
| | | const markDown = unref(markDownRef); |
| | | if (!markDown) return; |
| | | const vditor = markDown.getVditor(); |
| | | vditor.setTheme('dark'); |
| | | vditor.setTheme('dark', 'dark', 'dracula'); |
| | | } |
| | | |
| | | function handleChange(v: string) { |