重构:文件系统模块化架构,优化应用启动流程
This commit is contained in:
287
web/src/components/CodeEditor.vue
Normal file
287
web/src/components/CodeEditor.vue
Normal file
@@ -0,0 +1,287 @@
|
||||
<template>
|
||||
<div ref="editorContainer" class="codemirror-editor"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch, onBeforeUnmount } from 'vue'
|
||||
import { EditorView, lineNumbers, highlightActiveLineGutter } from '@codemirror/view'
|
||||
import { EditorState } from '@codemirror/state'
|
||||
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands'
|
||||
import { javascript } from '@codemirror/lang-javascript'
|
||||
import { java } from '@codemirror/lang-java'
|
||||
import { python } from '@codemirror/lang-python'
|
||||
import { go } from '@codemirror/lang-go'
|
||||
import { cpp } from '@codemirror/lang-cpp'
|
||||
import { rust } from '@codemirror/lang-rust'
|
||||
import { php } from '@codemirror/lang-php'
|
||||
import { json } from '@codemirror/lang-json'
|
||||
import { markdown } from '@codemirror/lang-markdown'
|
||||
import { html } from '@codemirror/lang-html'
|
||||
import { css } from '@codemirror/lang-css'
|
||||
import { oneDark } from '@codemirror/theme-one-dark'
|
||||
import { keymap } from '@codemirror/view'
|
||||
import { bracketMatching } from '@codemirror/language'
|
||||
import { useTheme } from '@/composables/useTheme'
|
||||
|
||||
// 使用主题系统
|
||||
const { isDark } = useTheme()
|
||||
|
||||
/**
|
||||
* 文件扩展名到语言的映射
|
||||
*/
|
||||
const LANGUAGE_MAP = {
|
||||
// JavaScript/TypeScript
|
||||
'js': javascript(),
|
||||
'jsx': javascript({ jsx: true }),
|
||||
'ts': javascript({ typescript: true }),
|
||||
'tsx': javascript({ typescript: true, jsx: true }),
|
||||
'mjs': javascript(),
|
||||
'cjs': javascript(),
|
||||
|
||||
// JSON
|
||||
'json': json(),
|
||||
|
||||
// Java
|
||||
'java': java(),
|
||||
|
||||
// Python
|
||||
'py': python(),
|
||||
|
||||
// Go
|
||||
'go': go(),
|
||||
|
||||
// C/C++
|
||||
'c': cpp(),
|
||||
'cpp': cpp(),
|
||||
'cc': cpp(),
|
||||
'cxx': cpp(),
|
||||
'h': cpp(),
|
||||
'hpp': cpp(),
|
||||
'hxx': cpp(),
|
||||
|
||||
// Rust
|
||||
'rs': rust(),
|
||||
|
||||
// PHP
|
||||
'php': php(),
|
||||
|
||||
// Markdown
|
||||
'md': markdown(),
|
||||
'markdown': markdown(),
|
||||
|
||||
// HTML
|
||||
'html': html(),
|
||||
'htm': html(),
|
||||
|
||||
// CSS
|
||||
'css': css(),
|
||||
'scss': css(),
|
||||
'sass': css(),
|
||||
'less': css(),
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
fileExtension: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const editorContainer = ref(null)
|
||||
let view = null
|
||||
|
||||
// 根据主题动态创建编辑器配置
|
||||
const createExtensions = () => {
|
||||
const extensions = [
|
||||
lineNumbers(),
|
||||
highlightActiveLineGutter(),
|
||||
history(),
|
||||
keymap.of(defaultKeymap),
|
||||
keymap.of(historyKeymap),
|
||||
bracketMatching(),
|
||||
EditorView.updateListener.of((update) => {
|
||||
if (update.docChanged) {
|
||||
emit('update:modelValue', update.state.doc.toString())
|
||||
}
|
||||
}),
|
||||
EditorView.theme({
|
||||
'&': {
|
||||
height: '100%',
|
||||
fontSize: '13px'
|
||||
},
|
||||
'.cm-scroller': {
|
||||
overflow: 'auto',
|
||||
fontFamily: 'Consolas, Monaco, Courier New, monospace'
|
||||
},
|
||||
'.cm-content': {
|
||||
padding: '8px',
|
||||
minHeight: '100%'
|
||||
},
|
||||
'.cm-line': {
|
||||
padding: '0 0'
|
||||
},
|
||||
'&.cm-focused': {
|
||||
outline: 'none'
|
||||
}
|
||||
})
|
||||
]
|
||||
|
||||
// 根据主题添加 One Dark
|
||||
if (isDark.value) {
|
||||
extensions.push(oneDark)
|
||||
}
|
||||
|
||||
// 添加语言支持
|
||||
const langSupport = LANGUAGE_MAP[props.fileExtension]
|
||||
if (langSupport) {
|
||||
extensions.push(langSupport)
|
||||
}
|
||||
|
||||
return extensions
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!editorContainer.value) return
|
||||
|
||||
const state = EditorState.create({
|
||||
doc: props.modelValue,
|
||||
extensions: createExtensions()
|
||||
})
|
||||
|
||||
view = new EditorView({
|
||||
state,
|
||||
parent: editorContainer.value
|
||||
})
|
||||
})
|
||||
|
||||
// 监听外部内容变化
|
||||
watch(() => props.modelValue, (newValue) => {
|
||||
if (view && newValue !== view.state.doc.toString()) {
|
||||
view.dispatch({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: view.state.doc.length,
|
||||
insert: newValue
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 监听主题变化,重新创建编辑器
|
||||
watch(isDark, () => {
|
||||
if (view) {
|
||||
view.destroy()
|
||||
const state = EditorState.create({
|
||||
doc: view.state.doc.toString(),
|
||||
extensions: createExtensions()
|
||||
})
|
||||
view = new EditorView({
|
||||
state,
|
||||
parent: editorContainer.value
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 监听文件扩展名变化,重新创建编辑器
|
||||
watch(() => props.fileExtension, () => {
|
||||
if (view) {
|
||||
view.destroy()
|
||||
const state = EditorState.create({
|
||||
doc: view.state.doc.toString(),
|
||||
extensions: createExtensions()
|
||||
})
|
||||
view = new EditorView({
|
||||
state,
|
||||
parent: editorContainer.value
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (view) {
|
||||
view.destroy()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 全局语法高亮样式(适用于亮色主题) */
|
||||
.cm-editor:not(.cm-theme-dark) .tok-keyword {
|
||||
color: #d73a49;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cm-editor:not(.cm-theme-dark) .tok-string {
|
||||
color: #032f62;
|
||||
}
|
||||
|
||||
.cm-editor:not(.cm-theme-dark) .tok-number {
|
||||
color: #005cc5;
|
||||
}
|
||||
|
||||
.cm-editor:not(.cm-theme-dark) .tok-comment {
|
||||
color: #6a737d;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.cm-editor:not(.cm-theme-dark) .tok-function {
|
||||
color: #6f42c1;
|
||||
}
|
||||
|
||||
.cm-editor:not(.cm-theme-dark) .tok-operator {
|
||||
color: #d73a49;
|
||||
}
|
||||
|
||||
.cm-editor:not(.cm-theme-dark) .tok-class-name {
|
||||
color: #22863a;
|
||||
}
|
||||
|
||||
.cm-editor:not(.cm-theme-dark) .tok-property {
|
||||
color: #e36209;
|
||||
}
|
||||
|
||||
.cm-editor:not(.cm-theme-dark) .tok-tag {
|
||||
color: #22863a;
|
||||
}
|
||||
|
||||
.cm-editor:not(.cm-theme-dark) .tok-attribute {
|
||||
color: #6f42c1;
|
||||
}
|
||||
|
||||
.cm-editor:not(.cm-theme-dark) .tok-variableName {
|
||||
color: #e36209;
|
||||
}
|
||||
|
||||
.cm-editor:not(.cm-theme-dark) .tok-variableName2 {
|
||||
color: #005cc5;
|
||||
}
|
||||
|
||||
.cm-editor:not(.cm-theme-dark) .tok-def {
|
||||
color: #6f42c1;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style scoped>
|
||||
.codemirror-editor {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.cm-editor) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.cm-scroller) {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:deep(.cm-content) {
|
||||
padding: 8px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user