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

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

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

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

Co-authored-by: 苗大 <v.caoshm@yoozoo.com>
1个文件已添加
3个文件已修改
144 ■■■■ 已修改文件
src/components/Markdown/src/Markdown.vue 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Markdown/src/MarkdownViewer.vue 65 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Markdown/src/getTheme.ts 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/demo/editor/markdown/index.vue 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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) => {
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();
<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>
src/components/Markdown/src/getTheme.ts
New file
@@ -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';
  }
};
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) {