🎨 Refactor code structure

This commit is contained in:
2025-04-25 18:11:34 +08:00
parent b3ecc08468
commit e87d5ec929
10 changed files with 271 additions and 237 deletions

View File

@@ -1,7 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import {useEditorStore} from '@/stores/editor'; import {useEditorStore} from '@/stores/editorStore';
import {useConfigStore} from '@/stores/configStore';
const editorStore = useEditorStore(); const editorStore = useEditorStore();
const configStore = useConfigStore();
</script> </script>
<template> <template>
@@ -18,25 +20,25 @@ const editorStore = useEditorStore();
</span> </span>
</div> </div>
<div class="actions"> <div class="actions">
<span class="font-size" title="字体大小 (Ctrl+滚轮调整)" @click="editorStore.resetFontSize"> <span class="font-size" title="字体大小 (Ctrl+滚轮调整)" @click="configStore.resetFontSize">
{{ editorStore.fontSize }}px {{ configStore.config.fontSize }}px
</span> </span>
<span class="tab-settings"> <span class="tab-settings">
<label title="启用Tab键缩进" class="tab-toggle"> <label title="启用Tab键缩进" class="tab-toggle">
<input type="checkbox" :checked="editorStore.enableTabIndent" @change="editorStore.toggleTabIndent"/> <input type="checkbox" :checked="configStore.config.enableTabIndent" @change="configStore.toggleTabIndent"/>
<span>Tab</span> <span>Tab</span>
</label> </label>
<span class="tab-type" title="Tab类型切换" @click="editorStore.toggleTabType"> <span class="tab-type" title="Tab类型切换" @click="configStore.toggleTabType">
{{ editorStore.tabType === 'spaces' ? '空格' : '制表符' }} {{ configStore.config.tabType === 'spaces' ? '空格' : '制表符' }}
</span> </span>
<span class="tab-size" title="Tab大小" v-if="editorStore.tabType === 'spaces'"> <span class="tab-size" title="Tab大小" v-if="configStore.config.tabType === 'spaces'">
<button class="tab-btn" @click="editorStore.decreaseTabSize" :disabled="editorStore.tabSize <= 2">-</button> <button class="tab-btn" @click="configStore.decreaseTabSize" :disabled="configStore.config.tabSize <= configStore.MIN_TAB_SIZE">-</button>
<span>{{ editorStore.tabSize }}</span> <span>{{ configStore.config.tabSize }}</span>
<button class="tab-btn" @click="editorStore.increaseTabSize" :disabled="editorStore.tabSize >= 8">+</button> <button class="tab-btn" @click="configStore.increaseTabSize" :disabled="configStore.config.tabSize >= configStore.MAX_TAB_SIZE">+</button>
</span> </span>
</span> </span>
<span class="encoding">{{ editorStore.encoding }}</span> <span class="encoding">{{ configStore.config.encoding }}</span>
<button class="settings-btn" @click="editorStore.openSettings"> <button class="settings-btn" @click="configStore.openSettings">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="3"></circle> <circle cx="12" cy="12" r="3"></circle>

View File

@@ -2,7 +2,8 @@
import {onBeforeUnmount, onMounted, ref, watch} from 'vue'; import {onBeforeUnmount, onMounted, ref, watch} from 'vue';
import {EditorState, Extension} from '@codemirror/state'; import {EditorState, Extension} from '@codemirror/state';
import {EditorView} from '@codemirror/view'; import {EditorView} from '@codemirror/view';
import {useEditorStore} from '@/stores/editor'; import {useEditorStore} from '@/stores/editorStore';
import {useConfigStore} from '@/stores/configStore';
import {createBasicSetup} from './extensions/basicSetup'; import {createBasicSetup} from './extensions/basicSetup';
import { import {
createStatsUpdateExtension, createStatsUpdateExtension,
@@ -14,6 +15,7 @@ import {
} from './extensions'; } from './extensions';
const editorStore = useEditorStore(); const editorStore = useEditorStore();
const configStore = useConfigStore();
const props = defineProps({ const props = defineProps({
initialDoc: { initialDoc: {
@@ -33,9 +35,9 @@ const createEditor = () => {
// 获取Tab相关扩展 // 获取Tab相关扩展
const tabExtensions = getTabExtensions( const tabExtensions = getTabExtensions(
editorStore.tabSize, configStore.config.tabSize,
editorStore.enableTabIndent, configStore.config.enableTabIndent,
editorStore.tabType configStore.config.tabType
); );
// 创建统计信息更新扩展 // 创建统计信息更新扩展
@@ -66,7 +68,7 @@ const createEditor = () => {
editorStore.setEditorView(view); editorStore.setEditorView(view);
// 应用初始字体大小 // 应用初始字体大小
applyFontSize(view, editorStore.fontSize); applyFontSize(view, configStore.config.fontSize);
// 立即更新统计信息,不等待用户交互 // 立即更新统计信息,不等待用户交互
updateStats(view, editorStore.updateDocumentStats); updateStats(view, editorStore.updateDocumentStats);
@@ -74,8 +76,8 @@ const createEditor = () => {
// 创建滚轮事件处理器 // 创建滚轮事件处理器
const handleWheel = createWheelZoomHandler( const handleWheel = createWheelZoomHandler(
editorStore.increaseFontSize, configStore.increaseFontSize,
editorStore.decreaseFontSize configStore.decreaseFontSize
); );
// 重新配置编辑器(仅在必要时) // 重新配置编辑器(仅在必要时)
@@ -83,16 +85,23 @@ const reconfigureTabSettings = () => {
if (!editorStore.editorView) return; if (!editorStore.editorView) return;
updateTabConfig( updateTabConfig(
editorStore.editorView as EditorView, editorStore.editorView as EditorView,
editorStore.tabSize, configStore.config.tabSize,
editorStore.enableTabIndent, configStore.config.enableTabIndent,
editorStore.tabType configStore.config.tabType
); );
}; };
// 监听Tab设置变化 // 监听Tab设置变化
watch(() => editorStore.tabSize, reconfigureTabSettings); watch(() => configStore.config.tabSize, reconfigureTabSettings);
watch(() => editorStore.enableTabIndent, reconfigureTabSettings); watch(() => configStore.config.enableTabIndent, reconfigureTabSettings);
watch(() => editorStore.tabType, reconfigureTabSettings); watch(() => configStore.config.tabType, reconfigureTabSettings);
// 监听字体大小变化
watch(() => configStore.config.fontSize, () => {
if (editorStore.editorView) {
applyFontSize(editorStore.editorView as EditorView, configStore.config.fontSize);
}
});
onMounted(() => { onMounted(() => {
// 创建编辑器 // 创建编辑器

View File

@@ -1,79 +1,75 @@
import {Extension} from '@codemirror/state'; import {Extension} from '@codemirror/state';
import { import {
crosshairCursor, crosshairCursor,
drawSelection, drawSelection,
dropCursor, dropCursor,
EditorView, EditorView,
highlightActiveLine, highlightActiveLine,
highlightActiveLineGutter, highlightActiveLineGutter,
highlightSpecialChars, highlightSpecialChars,
keymap, keymap,
lineNumbers, lineNumbers,
rectangularSelection, rectangularSelection,
} from '@codemirror/view'; } from '@codemirror/view';
import { import {
bracketMatching, bracketMatching,
defaultHighlightStyle, defaultHighlightStyle,
foldGutter, foldGutter,
foldKeymap, foldKeymap,
indentOnInput, indentOnInput,
syntaxHighlighting, syntaxHighlighting,
} from '@codemirror/language'; } from '@codemirror/language';
import { import {defaultKeymap, history, historyKeymap,} from '@codemirror/commands';
defaultKeymap,
history,
historyKeymap,
} from '@codemirror/commands';
import {highlightSelectionMatches, searchKeymap} from '@codemirror/search'; import {highlightSelectionMatches, searchKeymap} from '@codemirror/search';
import {autocompletion, closeBrackets, closeBracketsKeymap, completionKeymap} from '@codemirror/autocomplete'; import {autocompletion, closeBrackets, closeBracketsKeymap, completionKeymap} from '@codemirror/autocomplete';
import {lintKeymap} from '@codemirror/lint'; import {lintKeymap} from '@codemirror/lint';
import {baseDark, customHighlightActiveLine} from '@/editor/theme/base-dark'; import {customHighlightActiveLine, defaultDark} from '@/editor/theme/default-dark';
// 基本编辑器设置,包含常用扩展 // 基本编辑器设置,包含常用扩展
export const createBasicSetup = (): Extension[] => { export const createBasicSetup = (): Extension[] => {
return [ return [
// 主题相关 // 主题相关
baseDark, defaultDark,
// 基础UI // 基础UI
lineNumbers(), lineNumbers(),
highlightActiveLineGutter(), highlightActiveLineGutter(),
highlightSpecialChars(), highlightSpecialChars(),
dropCursor(), dropCursor(),
EditorView.lineWrapping, EditorView.lineWrapping,
// 历史记录 // 历史记录
history(), history(),
// 代码折叠 // 代码折叠
foldGutter(), foldGutter(),
// 选择与高亮 // 选择与高亮
drawSelection(), drawSelection(),
customHighlightActiveLine, customHighlightActiveLine,
highlightActiveLine(), highlightActiveLine(),
highlightSelectionMatches(), highlightSelectionMatches(),
rectangularSelection(), rectangularSelection(),
crosshairCursor(), crosshairCursor(),
// 缩进和编辑辅助 // 缩进和编辑辅助
indentOnInput(), indentOnInput(),
syntaxHighlighting(defaultHighlightStyle, {fallback: true}), syntaxHighlighting(defaultHighlightStyle, {fallback: true}),
bracketMatching(), bracketMatching(),
closeBrackets(), closeBrackets(),
// 自动完成 // 自动完成
autocompletion(), autocompletion(),
// 键盘映射 // 键盘映射
keymap.of([ keymap.of([
...closeBracketsKeymap, ...closeBracketsKeymap,
...defaultKeymap, ...defaultKeymap,
...searchKeymap, ...searchKeymap,
...historyKeymap, ...historyKeymap,
...foldKeymap, ...foldKeymap,
...completionKeymap, ...completionKeymap,
...lintKeymap ...lintKeymap
]), ]),
]; ];
}; };

View File

@@ -2,7 +2,7 @@ import {Compartment, Extension} from '@codemirror/state';
import {EditorView, keymap} from '@codemirror/view'; import {EditorView, keymap} from '@codemirror/view';
import {indentSelection} from '@codemirror/commands'; import {indentSelection} from '@codemirror/commands';
import {indentUnit} from '@codemirror/language'; import {indentUnit} from '@codemirror/language';
import {TabType} from '@/stores/editor'; import {TabType} from "@/types/config";
// Tab设置相关的compartment // Tab设置相关的compartment
export const tabSizeCompartment = new Compartment(); export const tabSizeCompartment = new Compartment();

View File

@@ -137,7 +137,7 @@ export const customHighlightActiveLine = EditorView.theme({
} }
}) })
export const baseDark: Extension = [ export const defaultDark: Extension = [
draculaTheme, draculaTheme,
syntaxHighlighting(draculaHighlightStyle), syntaxHighlighting(draculaHighlightStyle),
] ]

View File

@@ -0,0 +1,106 @@
import { defineStore } from 'pinia';
import { ref } from 'vue';
import { EditorConfig, TabType } from '@/types/config';
// 字体大小范围
const MIN_FONT_SIZE = 12;
const MAX_FONT_SIZE = 28;
const DEFAULT_FONT_SIZE = 13;
// Tab设置
const DEFAULT_TAB_SIZE = 4;
const MIN_TAB_SIZE = 2;
const MAX_TAB_SIZE = 8;
export const useConfigStore = defineStore('config', () => {
// 配置状态
const config = ref<EditorConfig>({
fontSize: DEFAULT_FONT_SIZE,
encoding: 'UTF-8',
enableTabIndent: true,
tabSize: DEFAULT_TAB_SIZE,
tabType: 'spaces'
});
// 字体缩放
function increaseFontSize() {
if (config.value.fontSize < MAX_FONT_SIZE) {
config.value.fontSize += 1;
}
}
// 字体缩小
function decreaseFontSize() {
if (config.value.fontSize > MIN_FONT_SIZE) {
config.value.fontSize -= 1;
}
}
// 重置字体大小
function resetFontSize() {
config.value.fontSize = DEFAULT_FONT_SIZE;
}
// 设置编码
function setEncoding(newEncoding: string) {
config.value.encoding = newEncoding;
}
// Tab相关方法
function toggleTabIndent() {
config.value.enableTabIndent = !config.value.enableTabIndent;
}
// 增加Tab大小
function increaseTabSize() {
if (config.value.tabSize < MAX_TAB_SIZE) {
config.value.tabSize += 1;
}
}
// 减少Tab大小
function decreaseTabSize() {
if (config.value.tabSize > MIN_TAB_SIZE) {
config.value.tabSize -= 1;
}
}
// 切换Tab类型空格或制表符
function toggleTabType() {
config.value.tabType = config.value.tabType === 'spaces' ? 'tab' : 'spaces';
}
// 设置按钮操作
function openSettings() {
console.log('打开设置面板');
// 此处可以实现设置面板的逻辑
}
return {
// 状态
config,
// 常量
MIN_FONT_SIZE,
MAX_FONT_SIZE,
DEFAULT_FONT_SIZE,
MIN_TAB_SIZE,
MAX_TAB_SIZE,
// 方法
setEncoding,
openSettings,
increaseFontSize,
decreaseFontSize,
resetFontSize,
toggleTabIndent,
increaseTabSize,
decreaseTabSize,
toggleTabType
};
}, {
persist: {
key: 'editor-config',
storage: localStorage
}
});

View File

@@ -1,143 +0,0 @@
import {defineStore} from 'pinia';
import {ref} from 'vue';
import {DocumentStats} from '@/types/editor';
import {EditorView} from '@codemirror/view';
// 字体大小范围
const MIN_FONT_SIZE = 12;
const MAX_FONT_SIZE = 28;
const DEFAULT_FONT_SIZE = 13;
// Tab设置
const DEFAULT_TAB_SIZE = 4;
const MIN_TAB_SIZE = 2;
const MAX_TAB_SIZE = 8;
// Tab类型
export type TabType = 'spaces' | 'tab';
export const useEditorStore = defineStore('editor', () => {
// 状态
const documentStats = ref<DocumentStats>({
lines: 0,
characters: 0,
selectedCharacters: 0
});
// 编码
const encoding = ref('UTF-8');
// 编辑器视图
const editorView = ref<EditorView | null>(null);
// 字体大小
const fontSize = ref(DEFAULT_FONT_SIZE);
// Tab键设置
const enableTabIndent = ref(true);
// Tab键大小
const tabSize = ref(DEFAULT_TAB_SIZE);
// Tab类型空格或制表符
const tabType = ref<TabType>('spaces');
// 方法
function setEditorView(view: EditorView | null) {
editorView.value = view;
}
// 更新文档统计信息
function updateDocumentStats(stats: DocumentStats) {
documentStats.value = stats;
}
// 设置编码
function setEncoding(newEncoding: string) {
encoding.value = newEncoding;
}
// 字体缩放
function increaseFontSize() {
if (fontSize.value < MAX_FONT_SIZE) {
fontSize.value += 1;
applyFontSize();
}
}
// 字体缩放
function decreaseFontSize() {
if (fontSize.value > MIN_FONT_SIZE) {
fontSize.value -= 1;
applyFontSize();
}
}
// 重置字体大小
function resetFontSize() {
fontSize.value = DEFAULT_FONT_SIZE;
applyFontSize();
}
// 应用字体大小
function applyFontSize() {
if (!editorView.value) return;
// 更新编辑器的字体大小
const editorDOM = editorView.value.dom;
if (editorDOM) {
editorDOM.style.fontSize = `${fontSize.value}px`;
}
}
// Tab相关方法
function toggleTabIndent() {
enableTabIndent.value = !enableTabIndent.value;
}
// 增加Tab大小
function increaseTabSize() {
if (tabSize.value < MAX_TAB_SIZE) {
tabSize.value += 1;
}
}
// 减少Tab大小
function decreaseTabSize() {
if (tabSize.value > MIN_TAB_SIZE) {
tabSize.value -= 1;
}
}
// 切换Tab类型空格或制表符
function toggleTabType() {
tabType.value = tabType.value === 'spaces' ? 'tab' : 'spaces';
}
// 设置按钮操作
function openSettings() {
console.log('打开设置面板');
// 此处可以实现设置面板的逻辑
}
return {
// 状态
documentStats,
encoding,
editorView,
fontSize,
enableTabIndent,
tabSize,
tabType,
// 方法
setEditorView,
updateDocumentStats,
setEncoding,
openSettings,
increaseFontSize,
decreaseFontSize,
resetFontSize,
toggleTabIndent,
increaseTabSize,
decreaseTabSize,
toggleTabType
};
}, {
persist: {
key: 'editor',
storage: localStorage,
pick: ['fontSize', 'encoding', 'enableTabIndent', 'tabSize', 'tabType']
}
});

View File

@@ -0,0 +1,53 @@
import {defineStore} from 'pinia';
import {ref} from 'vue';
import {DocumentStats} from '@/types/editor';
import {EditorView} from '@codemirror/view';
import {useConfigStore} from './configStore';
export const useEditorStore = defineStore('editor', () => {
// 引用配置store
const configStore = useConfigStore();
// 状态
const documentStats = ref<DocumentStats>({
lines: 0,
characters: 0,
selectedCharacters: 0
});
// 编辑器视图
const editorView = ref<EditorView | null>(null);
// 方法
function setEditorView(view: EditorView | null) {
editorView.value = view;
}
// 更新文档统计信息
function updateDocumentStats(stats: DocumentStats) {
documentStats.value = stats;
}
// 应用字体大小
function applyFontSize() {
if (!editorView.value) return;
// 更新编辑器的字体大小
const editorDOM = editorView.value.dom;
if (editorDOM) {
editorDOM.style.fontSize = `${configStore.config.fontSize}px`;
}
}
return {
// 状态
documentStats,
editorView,
// 配置引用
config: configStore.config,
// 方法
setEditorView,
updateDocumentStats,
applyFontSize
};
});

11
frontend/src/types/config.d.ts vendored Normal file
View File

@@ -0,0 +1,11 @@
// Tab类型
export type TabType = 'spaces' | 'tab';
// 编辑器配置接口
export interface EditorConfig {
fontSize: number;
encoding: string;
enableTabIndent: boolean;
tabSize: number;
tabType: TabType;
}