1+ <template >
2+ <div class =" space-y-4" >
3+ <!-- 标题栏 -->
4+ <div class =" flex items-center justify-between" >
5+ <label for =" editor" class =" text-lg font-semibold flex items-center gap-3" >
6+ <div class =" p-2 bg-gradient-to-r from-indigo-500 to-purple-600 rounded-lg" >
7+ <svg class =" w-5 h-5 text-white" fill =" none" stroke =" currentColor" viewBox =" 0 0 24 24" >
8+ <path
9+ stroke-linecap =" round"
10+ stroke-linejoin =" round"
11+ stroke-width =" 2"
12+ d =" M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"
13+ />
14+ </svg >
15+ </div >
16+ 代码内容
17+ </label >
18+
19+ <div class =" flex items-center gap-2 text-sm text-gray-400" >
20+ <div class =" flex items-center gap-1" >
21+ <div class =" w-3 h-3 bg-red-500 rounded-full" ></div >
22+ <div class =" w-3 h-3 bg-yellow-500 rounded-full" ></div >
23+ <div class =" w-3 h-3 bg-green-500 rounded-full" ></div >
24+ </div >
25+ </div >
26+ </div >
27+
28+ <!-- 编辑器容器 -->
29+ <div
30+ ref =" editorContainer"
31+ id =" editor"
32+ class =" flex min-h-[300px] w-full rounded-md border border-input bg-muted px-3 py-2 text-sm ring-offset-background font-mono placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
33+ ></div >
34+ </div >
35+ </template >
36+
137<script setup lang="ts">
2- import { ref , onMounted , onBeforeUnmount , defineEmits , watch } from ' vue'
38+ import { ref , onMounted , onBeforeUnmount , defineProps , defineEmits , watch } from ' vue'
339import { EditorView , basicSetup } from ' codemirror'
440import { Compartment } from ' @codemirror/state'
541import { dracula } from ' @uiw/codemirror-theme-dracula'
@@ -17,9 +53,25 @@ import { rust } from '@codemirror/lang-rust'
1753import { yaml } from ' @codemirror/lang-yaml'
1854import { go } from ' @codemirror/lang-go'
1955
56+ import type { Extension } from ' @codemirror/state'
57+
58+ // -------------------- Props & Emits --------------------
59+ const props = defineProps <{
60+ modelValue: string
61+ language: string
62+ }>()
63+
64+ const emits = defineEmits <{
65+ (e : ' update:modelValue' , value : string ): void
66+ }>()
67+
68+ // -------------------- Editor refs --------------------
2069const editorContainer = ref <HTMLDivElement | null >(null )
70+ const editorView = ref <EditorView | null >(null )
2171
22- import type { Extension } from ' @codemirror/state'
72+ // -------------------- 动态配置 --------------------
73+ const languageConf = new Compartment ()
74+ const themeConf = new Compartment ()
2375
2476type LangExtension = (() => Extension ) | null
2577
@@ -29,7 +81,6 @@ const languageExtensions: Record<string, LangExtension> = {
2981 python ,
3082 java ,
3183 cpp ,
32- rust ,
3384 c: cpp ,
3485 php ,
3586 ruby: cpp ,
@@ -41,28 +92,21 @@ const languageExtensions: Record<string, LangExtension> = {
4192 json ,
4293 yaml ,
4394 markdown ,
95+ rust ,
4496 go ,
4597 text: null ,
4698}
4799
48- const props = defineProps <{ language: string }>()
49- const editorView = ref <EditorView | null >(null )
50- const languageConf = new Compartment ()
51- const themeConf = new Compartment ()
52-
53- const emits = defineEmits <{
54- (e : ' update:modelValue' , value : string ): void
55- }>()
56-
100+ // -------------------- 初始化 Editor --------------------
57101onMounted (() => {
58102 if (! editorContainer .value ) return
59103
60104 editorView .value = new EditorView ({
61105 parent: editorContainer .value ,
62- doc: ' ' ,
106+ doc: props . modelValue || ' ' ,
63107 extensions: [
64108 basicSetup ,
65- languageConf .of ([]),
109+ languageConf .of (languageExtensions [ props . language ]?.() ?? []),
66110 themeConf .of (dracula ),
67111 EditorView .editorAttributes .of ({ class: ' w-full h-96' }),
68112 EditorView .updateListener .of ((update ) => {
@@ -75,56 +119,38 @@ onMounted(() => {
75119 })
76120})
77121
122+ // -------------------- 动态语言 --------------------
78123function updateLanguage(lang : string ) {
79124 const ext = languageExtensions [lang ] ?? null
80125 editorView .value ?.dispatch ({
81126 effects: languageConf .reconfigure (ext ? ext () : []),
82127 })
83128}
84129
85- onBeforeUnmount (() => {
86- editorView .value ?.destroy ()
87- })
88-
130+ // -------------------- 监听父组件语言变化 --------------------
89131watch (
90132 () => props .language ,
91133 (lang ) => {
92134 updateLanguage (lang )
93135 },
94136)
95- </script >
96137
97- <template >
98- <div class =" space-y-4" >
99- <div class =" flex items-center justify-between" >
100- <label for =" editor" class =" text-lg font-semibold flex items-center gap-3" >
101- <div class =" p-2 bg-gradient-to-r from-indigo-500 to-purple-600 rounded-lg" >
102- <svg class =" w-5 h-5 text-white" fill =" none" stroke =" currentColor" viewBox =" 0 0 24 24" >
103- <path
104- stroke-linecap =" round"
105- stroke-linejoin =" round"
106- stroke-width =" 2"
107- d =" M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"
108- />
109- </svg >
110- </div >
111- 代码内容
112- </label >
113-
114- <div class =" flex items-center gap-2 text-sm text-gray-400" >
115- <div class =" flex items-center gap-1" >
116- <div class =" w-3 h-3 bg-red-500 rounded-full" ></div >
117- <div class =" w-3 h-3 bg-yellow-500 rounded-full" ></div >
118- <div class =" w-3 h-3 bg-green-500 rounded-full" ></div >
119- </div >
120- </div >
121- </div >
138+ // -------------------- 监听父组件修改内容 --------------------
139+ watch (
140+ () => props .modelValue ,
141+ (newVal ) => {
142+ if (! editorView .value ) return
143+ const currentVal = editorView .value .state .doc .toString ()
144+ if (newVal !== currentVal ) {
145+ editorView .value .dispatch ({
146+ changes: { from: 0 , to: currentVal .length , insert: newVal },
147+ })
148+ }
149+ },
150+ )
122151
123- <!-- 编辑器容器 -->
124- <div
125- ref =" editorContainer"
126- id =" editor"
127- class =" flex min-h-[300px] w-full rounded-md border border-input bg-muted px-3 py-2 text-sm ring-offset-background font-mono placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
128- ></div >
129- </div >
130- </template >
152+ // -------------------- 销毁 --------------------
153+ onBeforeUnmount (() => {
154+ editorView .value ?.destroy ()
155+ })
156+ </script >
0 commit comments