Commit a89e497e82829e9f7f26c7df1e42ed0d74e5dd71
Committed by
GitHub
1 parent
0f50e045
fix: markdown深色模式内容区和代码块未适配bug; markdownViewer改为vidtor自带预览模式 (#2023)
* fix(Markdown): 修复深色模式 内容区和代码块 未改变主题bug * perf(Markdown): MarkDown组件示例增加不同功能示例; 切换深色主题按钮 同时改变 内容区和代码块主题 * perf(MarkdownViewer): MarkdownViewer改为vditor自带的预览模式; 同时适配深色模式 Co-authored-by: 苗大 <v.caoshm@yoozoo.com>
Showing
4 changed files
with
125 additions
and
19 deletions
src/components/Markdown/src/Markdown.vue
@@ -19,6 +19,7 @@ | @@ -19,6 +19,7 @@ | ||
19 | import { useModalContext } from '../../Modal'; | 19 | import { useModalContext } from '../../Modal'; |
20 | import { useRootSetting } from '/@/hooks/setting/useRootSetting'; | 20 | import { useRootSetting } from '/@/hooks/setting/useRootSetting'; |
21 | import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated'; | 21 | import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated'; |
22 | + import { getTheme } from './getTheme'; | ||
22 | 23 | ||
23 | type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined; | 24 | type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined; |
24 | 25 | ||
@@ -46,8 +47,9 @@ | @@ -46,8 +47,9 @@ | ||
46 | if (!inited) { | 47 | if (!inited) { |
47 | return; | 48 | return; |
48 | } | 49 | } |
49 | - const theme = val === 'dark' ? 'dark' : 'classic'; | ||
50 | - instance.getVditor()?.setTheme(theme); | 50 | + instance |
51 | + .getVditor() | ||
52 | + ?.setTheme(getTheme(val) as any, getTheme(val, 'content'), getTheme(val, 'code')); | ||
51 | }, | 53 | }, |
52 | { | 54 | { |
53 | immediate: true, | 55 | immediate: true, |
@@ -87,13 +89,22 @@ | @@ -87,13 +89,22 @@ | ||
87 | if (!wrapEl) return; | 89 | if (!wrapEl) return; |
88 | const bindValue = { ...attrs, ...props }; | 90 | const bindValue = { ...attrs, ...props }; |
89 | const insEditor = new Vditor(wrapEl, { | 91 | const insEditor = new Vditor(wrapEl, { |
90 | - theme: getDarkMode.value === 'dark' ? 'dark' : 'classic', | 92 | + // 设置外观主题 |
93 | + theme: getTheme(getDarkMode.value) as any, | ||
91 | lang: unref(getCurrentLang), | 94 | lang: unref(getCurrentLang), |
92 | mode: 'sv', | 95 | mode: 'sv', |
93 | fullscreen: { | 96 | fullscreen: { |
94 | index: 520, | 97 | index: 520, |
95 | }, | 98 | }, |
96 | preview: { | 99 | preview: { |
100 | + theme: { | ||
101 | + // 设置内容主题 | ||
102 | + current: getTheme(getDarkMode.value, 'content'), | ||
103 | + }, | ||
104 | + hljs: { | ||
105 | + // 设置代码块主题 | ||
106 | + style: getTheme(getDarkMode.value, 'code'), | ||
107 | + }, | ||
97 | actions: [], | 108 | actions: [], |
98 | }, | 109 | }, |
99 | input: (v) => { | 110 | input: (v) => { |
src/components/Markdown/src/MarkdownViewer.vue
1 | <template> | 1 | <template> |
2 | - <!-- eslint-disable vue/no-v-html --> | ||
3 | - <div v-html="getHtmlData" :class="$props.class" class="markdown-viewer"></div> | 2 | + <div ref="viewerRef" id="markdownViewer" :class="$props.class"></div> |
4 | </template> | 3 | </template> |
5 | 4 | ||
6 | <script lang="ts" setup> | 5 | <script lang="ts" setup> |
7 | - import { computed, defineProps } from 'vue'; | ||
8 | - import showdown from 'showdown'; | ||
9 | - | ||
10 | - const converter = new showdown.Converter(); | ||
11 | - converter.setOption('tables', true); | 6 | + import { defineProps, onBeforeUnmount, onDeactivated, Ref, ref, unref, watch } from 'vue'; |
7 | + import VditorPreview from 'vditor/dist/method.min'; | ||
8 | + import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated'; | ||
9 | + import { useRootSetting } from '/@/hooks/setting/useRootSetting'; | ||
10 | + import { getTheme } from './getTheme'; | ||
12 | const props = defineProps({ | 11 | const props = defineProps({ |
13 | value: { type: String }, | 12 | value: { type: String }, |
14 | class: { type: String }, | 13 | class: { type: String }, |
15 | }); | 14 | }); |
16 | - const getHtmlData = computed(() => converter.makeHtml(props.value || '')); | ||
17 | -</script> | 15 | + const viewerRef = ref<ElRef>(null); |
16 | + const vditorPreviewRef = ref(null) as Ref<Nullable<VditorPreview>>; | ||
17 | + const { getDarkMode } = useRootSetting(); | ||
18 | + | ||
19 | + function init() { | ||
20 | + const viewerEl = unref(viewerRef) as HTMLElement; | ||
21 | + vditorPreviewRef.value = VditorPreview.preview(viewerEl, props.value, { | ||
22 | + mode: getTheme(getDarkMode.value, 'content'), | ||
23 | + theme: { | ||
24 | + // 设置内容主题 | ||
25 | + current: getTheme(getDarkMode.value, 'content'), | ||
26 | + }, | ||
27 | + hljs: { | ||
28 | + // 设置代码块主题 | ||
29 | + style: getTheme(getDarkMode.value, 'code'), | ||
30 | + }, | ||
31 | + }); | ||
32 | + } | ||
33 | + watch( | ||
34 | + () => getDarkMode.value, | ||
35 | + (val) => { | ||
36 | + VditorPreview.setContentTheme(getTheme(val, 'content')); | ||
37 | + VditorPreview.setCodeTheme(getTheme(val, 'code')); | ||
38 | + init(); | ||
39 | + }, | ||
40 | + ); | ||
18 | 41 | ||
19 | -<style scoped> | ||
20 | - .markdown-viewer { | ||
21 | - width: 100%; | 42 | + watch( |
43 | + () => props.value, | ||
44 | + (v, oldValue) => { | ||
45 | + v !== oldValue && init(); | ||
46 | + }, | ||
47 | + ); | ||
48 | + | ||
49 | + function destroy() { | ||
50 | + const vditorInstance = unref(vditorPreviewRef); | ||
51 | + if (!vditorInstance) return; | ||
52 | + try { | ||
53 | + vditorInstance?.destroy?.(); | ||
54 | + } catch (error) {} | ||
55 | + vditorPreviewRef.value = null; | ||
22 | } | 56 | } |
23 | -</style> | 57 | + |
58 | + onMountedOrActivated(init); | ||
59 | + | ||
60 | + onBeforeUnmount(destroy); | ||
61 | + onDeactivated(destroy); | ||
62 | +</script> |
src/components/Markdown/src/getTheme.ts
0 → 100644
1 | +/** | ||
2 | + * 获取主题类型 深色浅色模式 对应的值 | ||
3 | + * @param darkModeVal 深色模式值 | ||
4 | + * @param themeMode 主题类型——外观(默认), 内容, 代码块 | ||
5 | + */ | ||
6 | +export const getTheme = ( | ||
7 | + darkModeVal: 'light' | 'dark' | string, | ||
8 | + themeMode: 'default' | 'content' | 'code' = 'default', | ||
9 | +) => { | ||
10 | + const isDark = darkModeVal === 'dark'; | ||
11 | + switch (themeMode) { | ||
12 | + case 'default': | ||
13 | + return isDark ? 'dark' : 'classic'; | ||
14 | + case 'content': | ||
15 | + return isDark ? 'dark' : 'light'; | ||
16 | + case 'code': | ||
17 | + return isDark ? 'dracula' : 'github'; | ||
18 | + } | ||
19 | +}; |
src/views/demo/editor/markdown/index.vue
@@ -28,16 +28,53 @@ | @@ -28,16 +28,53 @@ | ||
28 | setup() { | 28 | setup() { |
29 | const markDownRef = ref<Nullable<MarkDownActionType>>(null); | 29 | const markDownRef = ref<Nullable<MarkDownActionType>>(null); |
30 | const valueRef = ref(` | 30 | const valueRef = ref(` |
31 | -# title | 31 | +# 标题h1 |
32 | 32 | ||
33 | -# content | 33 | +##### 标题h5 |
34 | + | ||
35 | +**加粗** | ||
36 | +*斜体* | ||
37 | +~~删除线~~ | ||
38 | +[链接](https://github.com/vbenjs/vue-vben-admin) | ||
39 | +↓分割线↓ | ||
40 | + | ||
41 | +--- | ||
42 | + | ||
43 | + | ||
44 | +* 无序列表1 | ||
45 | + * 无序列表1.1 | ||
46 | + | ||
47 | +1. 有序列表1 | ||
48 | +2. 有序列表2 | ||
49 | + | ||
50 | +* [ ] 任务列表1 | ||
51 | +* [x] 任务列表2 | ||
52 | + | ||
53 | +> 引用示例 | ||
54 | + | ||
55 | +\`\`\`js | ||
56 | +// 代码块: | ||
57 | +(() => { | ||
58 | + var htmlRoot = document.getElementById('htmlRoot'); | ||
59 | + var theme = window.localStorage.getItem('__APP__DARK__MODE__'); | ||
60 | + if (htmlRoot && theme) { | ||
61 | + htmlRoot.setAttribute('data-theme', theme); | ||
62 | + theme = htmlRoot = null; | ||
63 | + } | ||
64 | +})(); | ||
65 | +\`\`\` | ||
66 | + | ||
67 | +| 表格 | 示例 | 🎉️ | | ||
68 | +| --- | --- | --- | | ||
69 | +| 1 | 2 | 3 | | ||
70 | +| 4 | 5 | 6 | | ||
34 | `); | 71 | `); |
35 | 72 | ||
36 | function toggleTheme() { | 73 | function toggleTheme() { |
37 | const markDown = unref(markDownRef); | 74 | const markDown = unref(markDownRef); |
38 | if (!markDown) return; | 75 | if (!markDown) return; |
39 | const vditor = markDown.getVditor(); | 76 | const vditor = markDown.getVditor(); |
40 | - vditor.setTheme('dark'); | 77 | + vditor.setTheme('dark', 'dark', 'dracula'); |
41 | } | 78 | } |
42 | 79 | ||
43 | function handleChange(v: string) { | 80 | function handleChange(v: string) { |