Browse Source

fix: markdown深色模式内容区和代码块未适配bug; markdownViewer改为vidtor自带预览模式 (#2023)

* fix(Markdown): 修复深色模式 内容区和代码块 未改变主题bug

* perf(Markdown): MarkDown组件示例增加不同功能示例; 切换深色主题按钮 同时改变 内容区和代码块主题

* perf(MarkdownViewer): MarkdownViewer改为vditor自带的预览模式; 同时适配深色模式

Co-authored-by: 苗大 <v.caoshm@yoozoo.com>
Wit〆苗大 2 years ago
parent
commit
a89e497e82

+ 14 - 3
src/components/Markdown/src/Markdown.vue

@@ -19,6 +19,7 @@
   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;
 
@@ -46,8 +47,9 @@
           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,
@@ -87,13 +89,22 @@
         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) => {

+ 52 - 13
src/components/Markdown/src/MarkdownViewer.vue

@@ -1,23 +1,62 @@
 <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();
+
+  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'),
+      },
+    });
+  }
+  watch(
+    () => getDarkMode.value,
+    (val) => {
+      VditorPreview.setContentTheme(getTheme(val, 'content'));
+      VditorPreview.setCodeTheme(getTheme(val, 'code'));
+      init();
+    },
+  );
 
-<style scoped>
-  .markdown-viewer {
-    width: 100%;
+  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;
   }
-</style>
+
+  onMountedOrActivated(init);
+
+  onBeforeUnmount(destroy);
+  onDeactivated(destroy);
+</script>

+ 19 - 0
src/components/Markdown/src/getTheme.ts

@@ -0,0 +1,19 @@
+/**
+ * 获取主题类型 深色浅色模式 对应的值
+ * @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';
+  }
+};

+ 40 - 3
src/views/demo/editor/markdown/index.vue

@@ -28,16 +28,53 @@
     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) {