Commit 5fb069f432799e0d17a7102fae70757e320dc0c5
1 parent
2fbc4504
feat: add markdown component
Showing
8 changed files
with
177 additions
and
0 deletions
package.json
@@ -30,6 +30,7 @@ | @@ -30,6 +30,7 @@ | ||
30 | "nprogress": "^0.2.0", | 30 | "nprogress": "^0.2.0", |
31 | "path-to-regexp": "^6.2.0", | 31 | "path-to-regexp": "^6.2.0", |
32 | "qrcode": "^1.4.4", | 32 | "qrcode": "^1.4.4", |
33 | + "vditor": "^3.5.5", | ||
33 | "vue": "^3.0.1", | 34 | "vue": "^3.0.1", |
34 | "vue-i18n": "^9.0.0-beta.4", | 35 | "vue-i18n": "^9.0.0-beta.4", |
35 | "vue-router": "^4.0.0-beta.13", | 36 | "vue-router": "^4.0.0-beta.13", |
src/components/Markdown/index.ts
0 → 100644
src/components/Markdown/src/index.vue
0 → 100644
1 | +<template> | ||
2 | + <div class="markdown" ref="wrapRef" /> | ||
3 | +</template> | ||
4 | +<script lang="ts"> | ||
5 | + import { | ||
6 | + defineComponent, | ||
7 | + ref, | ||
8 | + onMounted, | ||
9 | + unref, | ||
10 | + PropType, | ||
11 | + onUnmounted, | ||
12 | + nextTick, | ||
13 | + watchEffect, | ||
14 | + } from 'vue'; | ||
15 | + import Vditor from 'vditor'; | ||
16 | + import 'vditor/dist/index.css'; | ||
17 | + export default defineComponent({ | ||
18 | + emits: ['update:value'], | ||
19 | + props: { | ||
20 | + height: { | ||
21 | + type: Number as PropType<number>, | ||
22 | + default: 360, | ||
23 | + }, | ||
24 | + value: { | ||
25 | + type: String, | ||
26 | + default: '', | ||
27 | + }, | ||
28 | + }, | ||
29 | + setup(props, { attrs, emit }) { | ||
30 | + const wrapRef = ref<Nullable<HTMLDivElement>>(null); | ||
31 | + const vditorRef = ref<Nullable<Vditor>>(null); | ||
32 | + | ||
33 | + const initedRef = ref(false); | ||
34 | + | ||
35 | + function init() { | ||
36 | + const wrapEl = unref(wrapRef); | ||
37 | + if (!wrapEl) return; | ||
38 | + const data = { ...attrs, ...props }; | ||
39 | + vditorRef.value = new Vditor(wrapEl, { | ||
40 | + mode: 'sv', | ||
41 | + preview: { | ||
42 | + actions: [], | ||
43 | + }, | ||
44 | + input: (v) => { | ||
45 | + emit('update:value', v); | ||
46 | + }, | ||
47 | + ...data, | ||
48 | + cache: { | ||
49 | + enable: false, | ||
50 | + }, | ||
51 | + }); | ||
52 | + initedRef.value = true; | ||
53 | + } | ||
54 | + | ||
55 | + watchEffect(() => { | ||
56 | + nextTick(() => { | ||
57 | + const vditor = unref(vditorRef); | ||
58 | + if (unref(initedRef) && props.value && vditor) { | ||
59 | + vditor.setValue(props.value); | ||
60 | + } | ||
61 | + }); | ||
62 | + }); | ||
63 | + | ||
64 | + onMounted(() => { | ||
65 | + nextTick(() => { | ||
66 | + init(); | ||
67 | + }); | ||
68 | + }); | ||
69 | + | ||
70 | + onUnmounted(() => { | ||
71 | + const vditorInstance = unref(vditorRef); | ||
72 | + if (!vditorInstance) return; | ||
73 | + vditorInstance.destroy(); | ||
74 | + }); | ||
75 | + | ||
76 | + return { | ||
77 | + wrapRef, | ||
78 | + getVditor: (): Vditor => vditorRef.value!, | ||
79 | + }; | ||
80 | + }, | ||
81 | + }); | ||
82 | +</script> |
src/components/Markdown/src/types.ts
0 → 100644
src/router/menus/modules/demo/editor.ts
0 → 100644
src/router/routes/modules/demo/editor.ts
0 → 100644
1 | +import type { AppRouteModule } from '/@/router/types'; | ||
2 | + | ||
3 | +import { PAGE_LAYOUT_COMPONENT } from '/@/router/constant'; | ||
4 | + | ||
5 | +export default { | ||
6 | + layout: { | ||
7 | + path: '/editor', | ||
8 | + name: 'Editor', | ||
9 | + component: PAGE_LAYOUT_COMPONENT, | ||
10 | + redirect: '/editor/markdown', | ||
11 | + meta: { | ||
12 | + icon: 'ant-design:table-outlined', | ||
13 | + title: '编辑器', | ||
14 | + }, | ||
15 | + }, | ||
16 | + | ||
17 | + routes: [ | ||
18 | + { | ||
19 | + path: '/markdown', | ||
20 | + name: 'MarkdownDemo', | ||
21 | + component: () => import('/@/views/demo/editor/Markdown.vue'), | ||
22 | + meta: { | ||
23 | + title: 'markdown编辑器', | ||
24 | + }, | ||
25 | + }, | ||
26 | + ], | ||
27 | +} as AppRouteModule; |
src/views/demo/editor/Markdown.vue
0 → 100644
1 | +<template> | ||
2 | + <div class="p-4"> | ||
3 | + <a-button @click="toggleTheme" class="mb-2" type="primary">黑暗主题</a-button> | ||
4 | + <MarkDown v-model:value="value" ref="markDownRef" /> | ||
5 | + </div> | ||
6 | +</template> | ||
7 | +<script lang="ts"> | ||
8 | + import { defineComponent, ref, unref } from 'vue'; | ||
9 | + import { MarkDown, MarkDownActionType } from '/@/components/Markdown'; | ||
10 | + export default defineComponent({ | ||
11 | + components: { MarkDown }, | ||
12 | + setup() { | ||
13 | + const markDownRef = ref<Nullable<MarkDownActionType>>(null); | ||
14 | + const valueRef = ref(` | ||
15 | +# title | ||
16 | + | ||
17 | +# content | ||
18 | +`); | ||
19 | + | ||
20 | + function toggleTheme() { | ||
21 | + const markDown = unref(markDownRef); | ||
22 | + if (!markDown) return; | ||
23 | + const vditor = markDown.getVditor(); | ||
24 | + vditor.setTheme('dark'); | ||
25 | + } | ||
26 | + return { | ||
27 | + value: valueRef, | ||
28 | + toggleTheme, | ||
29 | + markDownRef, | ||
30 | + }; | ||
31 | + }, | ||
32 | + }); | ||
33 | +</script> |
yarn.lock
@@ -2346,6 +2346,11 @@ detect-indent@6.0.0: | @@ -2346,6 +2346,11 @@ detect-indent@6.0.0: | ||
2346 | resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-6.0.0.tgz#0abd0f549f69fc6659a254fe96786186b6f528fd" | 2346 | resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-6.0.0.tgz#0abd0f549f69fc6659a254fe96786186b6f528fd" |
2347 | integrity sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA== | 2347 | integrity sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA== |
2348 | 2348 | ||
2349 | +diff-match-patch@^1.0.5: | ||
2350 | + version "1.0.5" | ||
2351 | + resolved "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz#abb584d5f10cd1196dfc55aa03701592ae3f7b37" | ||
2352 | + integrity sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw== | ||
2353 | + | ||
2349 | diff@^4.0.1: | 2354 | diff@^4.0.1: |
2350 | version "4.0.2" | 2355 | version "4.0.2" |
2351 | resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" | 2356 | resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" |
@@ -6980,6 +6985,13 @@ vary@^1.1.2: | @@ -6980,6 +6985,13 @@ vary@^1.1.2: | ||
6980 | resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" | 6985 | resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" |
6981 | integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= | 6986 | integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= |
6982 | 6987 | ||
6988 | +vditor@^3.5.5: | ||
6989 | + version "3.5.5" | ||
6990 | + resolved "https://registry.npmjs.org/vditor/-/vditor-3.5.5.tgz#03b7c965470fd434cd098e3f6ac6c7e65ca5a52a" | ||
6991 | + integrity sha512-NpBa0c1tK3jnH/E+rWkDO2x/bDx33KIWuThjF70gING58zuluxicGczuVb2KvdLxWoozJ6MaH5DQjj66Jct1QA== | ||
6992 | + dependencies: | ||
6993 | + diff-match-patch "^1.0.5" | ||
6994 | + | ||
6983 | vfile-location@^3.0.0: | 6995 | vfile-location@^3.0.0: |
6984 | version "3.1.0" | 6996 | version "3.1.0" |
6985 | resolved "https://registry.npmjs.org/vfile-location/-/vfile-location-3.1.0.tgz#81cd8a04b0ac935185f4fce16f270503fc2f692f" | 6997 | resolved "https://registry.npmjs.org/vfile-location/-/vfile-location-3.1.0.tgz#81cd8a04b0ac935185f4fce16f270503fc2f692f" |