11<template >
2- <div class =" flex bg-white h-full relative overflow-hidden" >
3- <!-- 行号 -->
4- <div ref =" lineNumbersRef"
5- class =" bg-gray-50 text-gray-400 text-sm font-mono px-3 pb-4 select-none border-r border-gray-200 overflow-hidden flex-shrink-0 z-10"
6- style =" padding-top : 0 ;" >
7- <div v-for =" (num, index) in lineNumbers" :key =" index" class =" h-6 leading-6 text-right" >
8- {{ num }}
9- </div >
10- </div >
11-
12- <!-- 语法高亮容器 -->
13- <div class =" flex-1 relative overflow-hidden" >
14- <!-- 高亮显示层 -->
15- <pre ref =" highlightRef"
16- class =" absolute inset-0 px-4 pb-4 font-mono text-sm leading-6 bg-transparent pointer-events-none overflow-auto whitespace-pre-wrap z-0"
17- style =" margin : 0 ; border : 0 ; padding-top : 0 ; word-break : break-word ; white-space : pre-wrap ;"
18- v-html =" highlightedCode" ></pre >
19-
20- <!-- 代码输入框 -->
21- <textarea ref =" textareaRef"
22- :value =" modelValue"
23- @input =" handleInput"
24- @keydown =" handleKeyDown"
25- @scroll =" handleScroll"
26- class =" absolute inset-0 px-4 pb-4 font-mono text-sm leading-6 resize-none outline-none bg-transparent z-10 overflow-auto"
27- style =" color : transparent ; caret-color : #374151 ; margin : 0 ; border : 0 ; padding-top : 0 ; word-break : break-word ; white-space : pre-wrap ;"
28- placeholder =" 在此输入代码..."
29- spellcheck =" false" >
30- </textarea >
31- </div >
2+ <div class =" flex bg-white h-full relative" >
3+ <Codemirror v-if =" isReady"
4+ style =" width : 100% ; height : 100% "
5+ :model-value =" modelValue"
6+ :extensions =" extensions"
7+ @change =" handleInput" />
328 </div >
339</template >
3410
3511<script setup lang="ts">
36- import { computed , nextTick , ref } from ' vue'
37- import { highlightCode } from ' ../utils/highlighter'
12+ import { nextTick , onMounted , ref , watch } from ' vue'
13+ import { Codemirror } from ' vue-codemirror'
14+ import { python } from ' @codemirror/lang-python'
15+ import { javascript } from ' @codemirror/lang-javascript'
16+ import { go } from ' @codemirror/lang-go'
17+ import { githubLight } from ' @uiw/codemirror-themes-all'
3818
3919const props = defineProps <{
4020 modelValue: string
@@ -45,46 +25,57 @@ const emit = defineEmits<{
4525 ' update:modelValue' : [value : string ]
4626}>()
4727
48- const textareaRef = ref <HTMLTextAreaElement >()
49- const lineNumbersRef = ref <HTMLElement >()
50- const highlightRef = ref <HTMLPreElement >()
51-
52- const lineNumbers = computed (() => {
53- const lines = props .modelValue .split (' \n ' )
54- return lines .map ((_ , index ) => String (index + 1 ))
55- })
56-
57- const highlightedCode = computed (() => {
58- return highlightCode (props .modelValue , props .language || ' python3' )
59- })
60-
61- const handleInput = (e : Event ) => {
62- const target = e .target as HTMLTextAreaElement
63- emit (' update:modelValue' , target .value )
28+ const handleInput = (value : string ) => {
29+ emit (' update:modelValue' , value )
6430}
6531
66- const handleKeyDown = async (e : KeyboardEvent ) => {
67- if (e .key === ' Tab' ) {
68- e .preventDefault ()
69- const target = e .target as HTMLTextAreaElement
70- const start = target .selectionStart
71- const end = target .selectionEnd
72- const newValue = props .modelValue .substring (0 , start ) + ' ' + props .modelValue .substring (end )
73- emit (' update:modelValue' , newValue )
32+ const isReady = ref (false )
33+ const extensions = ref <[]>([])
7434
75- await nextTick ()
76- target .selectionStart = target .selectionEnd = start + 4
35+ // 获取语言扩展
36+ const getLanguageExtension = (language : string ): any | null => {
37+ switch (language ) {
38+ case ' python2' :
39+ case ' python3' :
40+ return python ()
41+ case ' nodejs' :
42+ return javascript ()
43+ case ' go' :
44+ return go ()
45+ default :
46+ return null
7747 }
7848}
7949
80- const handleScroll = (e : Event ) => {
81- const target = e .target as HTMLTextAreaElement
82- if (lineNumbersRef .value ) {
83- lineNumbersRef .value .scrollTop = target .scrollTop
50+ // 更新扩展的函数
51+ const updateExtensions = async () => {
52+ const result = [githubLight ]
53+
54+ if (props .language ) {
55+ const langExtension = getLanguageExtension (props .language )
56+ if (langExtension ) {
57+ result .push (langExtension )
58+ }
8459 }
85- if (highlightRef .value ) {
86- highlightRef .value .scrollTop = target .scrollTop
87- highlightRef .value .scrollLeft = target .scrollLeft
60+
61+ extensions .value = result as any
62+
63+ // 如果组件还没准备好,等待下一个 tick 后设置为准备好
64+ if (! isReady .value ) {
65+ await nextTick ()
66+ isReady .value = true
8867 }
8968}
90- </script >
69+
70+ // 监听语言变化
71+ watch (() => props .language , async () => {
72+ isReady .value = false
73+ await nextTick ()
74+ await updateExtensions ()
75+ }, { immediate: false })
76+
77+ // 组件挂载时初始化
78+ onMounted (async () => {
79+ await updateExtensions ()
80+ })
81+ </script >
0 commit comments