🎨 Optimize code & Upgrade dependencies

This commit is contained in:
2026-01-01 02:27:21 +08:00
parent 9ec22add55
commit 76f6c30b9d
17 changed files with 316 additions and 1247 deletions

View File

@@ -1,18 +1,10 @@
import {defineStore} from 'pinia';
import {computed, reactive} from 'vue';
import {ConfigService, StartupService} from '@/../bindings/voidraft/internal/services';
import {
AppConfig,
AuthMethod,
EditingConfig,
LanguageType,
SystemThemeType,
TabType
} from '@/../bindings/voidraft/internal/models/models';
import {AppConfig, AuthMethod, LanguageType, SystemThemeType, TabType} from '@/../bindings/voidraft/internal/models/models';
import {useI18n} from 'vue-i18n';
import {ConfigUtils} from '@/common/utils/configUtils';
import {FONT_OPTIONS} from '@/common/constant/fonts';
import {SUPPORTED_LOCALES} from '@/common/constant/locales';
import {
CONFIG_KEY_MAP,
CONFIG_LIMITS,
@@ -36,12 +28,6 @@ export const useConfigStore = defineStore('config', () => {
// Font options (no longer localized)
const fontOptions = computed(() => FONT_OPTIONS);
// 计算属性
const createLimitComputed = (key: NumberConfigKey) => computed(() => CONFIG_LIMITS[key]);
const limits = Object.fromEntries(
(['fontSize', 'tabSize', 'lineHeight'] as const).map(key => [key, createLimitComputed(key)])
) as Record<NumberConfigKey, ReturnType<typeof createLimitComputed>>;
// 统一配置更新方法
const updateConfig = async <K extends ConfigKey>(key: K, value: any): Promise<void> => {
if (!state.configLoaded && !state.isLoading) {
@@ -99,39 +85,12 @@ export const useConfigStore = defineStore('config', () => {
}
};
// 通用数值调整器工厂
const createAdjuster = <T extends NumberConfigKey>(key: T) => {
const limit = CONFIG_LIMITS[key];
const clamp = (value: number) => ConfigUtils.clamp(value, limit.min, limit.max);
return {
increase: async () => await updateConfig(key, clamp(state.config.editing[key] + 1)),
decrease: async () => await updateConfig(key, clamp(state.config.editing[key] - 1)),
set: async (value: number) => await updateConfig(key, clamp(value)),
reset: async () => await updateConfig(key, limit.default),
increaseLocal: () => updateConfigLocal(key, clamp(state.config.editing[key] + 1)),
decreaseLocal: () => updateConfigLocal(key, clamp(state.config.editing[key] - 1))
};
};
const createEditingToggler = <T extends keyof EditingConfig>(key: T) =>
async () => await updateConfig(key as ConfigKey, !state.config.editing[key] as EditingConfig[T]);
// 枚举值切换器
const createEnumToggler = <T extends TabType>(key: 'tabType', values: readonly T[]) =>
async () => {
const currentIndex = values.indexOf(state.config.editing[key] as T);
const nextIndex = (currentIndex + 1) % values.length;
return await updateConfig(key, values[nextIndex]);
};
// 重置配置
const resetConfig = async (): Promise<void> => {
if (state.isLoading) return;
state.isLoading = true;
try {
await ConfigService.ResetConfig();
const appConfig = await ConfigService.GetConfig();
if (appConfig) {
@@ -142,57 +101,25 @@ export const useConfigStore = defineStore('config', () => {
}
};
// 语言设置方法
const setLanguage = async (language: LanguageType): Promise<void> => {
await updateConfig('language', language);
const frontendLocale = ConfigUtils.backendLanguageToFrontend(language);
locale.value = frontendLocale as any;
// 辅助函数:限制数值范围
const clampValue = (value: number, key: NumberConfigKey): number => {
const limit = CONFIG_LIMITS[key];
return ConfigUtils.clamp(value, limit.min, limit.max);
};
// 系统主题设置方法
const setSystemTheme = async (systemTheme: SystemThemeType): Promise<void> => {
await updateConfig('systemTheme', systemTheme);
};
// 计算属性
const fontConfig = computed(() => ({
fontSize: state.config.editing.fontSize,
fontFamily: state.config.editing.fontFamily,
lineHeight: state.config.editing.lineHeight,
fontWeight: state.config.editing.fontWeight
}));
// 当前主题设置方法
const setCurrentTheme = async (themeName: string): Promise<void> => {
await updateConfig('currentTheme', themeName);
};
// 初始化语言设置
const initLanguage = async (): Promise<void> => {
try {
// 如果配置未加载,先加载配置
if (!state.configLoaded) {
await initConfig();
}
// 同步前端语言设置
const frontendLocale = ConfigUtils.backendLanguageToFrontend(state.config.appearance.language);
locale.value = frontendLocale as any;
} catch (_error) {
const browserLang = SUPPORTED_LOCALES[0].code;
locale.value = browserLang as any;
}
};
// 创建数值调整器实例
const adjusters = {
fontSize: createAdjuster('fontSize'),
tabSize: createAdjuster('tabSize'),
lineHeight: createAdjuster('lineHeight')
};
// 创建切换器实例
const togglers = {
tabIndent: createEditingToggler('enableTabIndent'),
alwaysOnTop: async () => {
await updateConfig('alwaysOnTop', !state.config.general.alwaysOnTop);
await runtime.Window.SetAlwaysOnTop(state.config.general.alwaysOnTop);
},
tabType: createEnumToggler('tabType', CONFIG_LIMITS.tabType.values)
};
const tabConfig = computed(() => ({
tabSize: state.config.editing.tabSize,
enableTabIndent: state.config.editing.enableTabIndent,
tabType: state.config.editing.tabType
}));
return {
// 状态
@@ -200,53 +127,84 @@ export const useConfigStore = defineStore('config', () => {
configLoaded: computed(() => state.configLoaded),
isLoading: computed(() => state.isLoading),
fontOptions,
// 限制常量
...limits,
fontConfig,
tabConfig,
// 核心方法
initConfig,
resetConfig,
// 语言相关方法
setLanguage,
initLanguage,
setLanguage: (value: LanguageType) => {
updateConfig('language', value);
locale.value = value as any;
},
// 主题相关方法
setSystemTheme,
setCurrentTheme,
setSystemTheme: (value: SystemThemeType) => updateConfig('systemTheme', value),
setCurrentTheme: (value: string) => updateConfig('currentTheme', value),
// 字体大小操作
...adjusters.fontSize,
increaseFontSize: adjusters.fontSize.increase,
decreaseFontSize: adjusters.fontSize.decrease,
resetFontSize: adjusters.fontSize.reset,
setFontSize: adjusters.fontSize.set,
// 字体大小操作
increaseFontSizeLocal: adjusters.fontSize.increaseLocal,
decreaseFontSizeLocal: adjusters.fontSize.decreaseLocal,
saveFontSize: () => saveConfig('fontSize'),
// Tab操作
toggleTabIndent: togglers.tabIndent,
setEnableTabIndent: (value: boolean) => updateConfig('enableTabIndent', value),
...adjusters.tabSize,
increaseTabSize: adjusters.tabSize.increase,
decreaseTabSize: adjusters.tabSize.decrease,
setTabSize: adjusters.tabSize.set,
toggleTabType: togglers.tabType,
// 行高操作
setLineHeight: adjusters.lineHeight.set,
// 窗口操作
toggleAlwaysOnTop: togglers.alwaysOnTop,
setAlwaysOnTop: (value: boolean) => updateConfig('alwaysOnTop', value),
setFontSize: async (value: number) => {
await updateConfig('fontSize', clampValue(value, 'fontSize'));
},
increaseFontSize: async () => {
const newValue = state.config.editing.fontSize + 1;
await updateConfig('fontSize', clampValue(newValue, 'fontSize'));
},
decreaseFontSize: async () => {
const newValue = state.config.editing.fontSize - 1;
await updateConfig('fontSize', clampValue(newValue, 'fontSize'));
},
resetFontSize: async () => {
await updateConfig('fontSize', CONFIG_LIMITS.fontSize.default);
},
increaseFontSizeLocal: () => {
updateConfigLocal('fontSize', clampValue(state.config.editing.fontSize + 1, 'fontSize'));
},
decreaseFontSizeLocal: () => {
updateConfigLocal('fontSize', clampValue(state.config.editing.fontSize - 1, 'fontSize'));
},
saveFontSize: async () => {
await saveConfig('fontSize');
},
// 字体操作
setFontFamily: (value: string) => updateConfig('fontFamily', value),
setFontWeight: (value: string) => updateConfig('fontWeight', value),
// 行高操作
setLineHeight: async (value: number) => {
await updateConfig('lineHeight', clampValue(value, 'lineHeight'));
},
// Tab操作
setEnableTabIndent: (value: boolean) => updateConfig('enableTabIndent', value),
setTabSize: async (value: number) => {
await updateConfig('tabSize', clampValue(value, 'tabSize'));
},
increaseTabSize: async () => {
const newValue = state.config.editing.tabSize + 1;
await updateConfig('tabSize', clampValue(newValue, 'tabSize'));
},
decreaseTabSize: async () => {
const newValue = state.config.editing.tabSize - 1;
await updateConfig('tabSize', clampValue(newValue, 'tabSize'));
},
toggleTabType: async () => {
const values = CONFIG_LIMITS.tabType.values;
const currentIndex = values.indexOf(state.config.editing.tabType as typeof values[number]);
const nextIndex = (currentIndex + 1) % values.length;
await updateConfig('tabType', values[nextIndex]);
},
// 窗口操作
toggleAlwaysOnTop: async () => {
await updateConfig('alwaysOnTop', !state.config.general.alwaysOnTop);
await runtime.Window.SetAlwaysOnTop(state.config.general.alwaysOnTop);
},
setAlwaysOnTop: (value: boolean) => updateConfig('alwaysOnTop', value),
// 路径操作
setDataPath: (value: string) => updateConfigLocal('dataPath', value),

View File

@@ -3,7 +3,6 @@ import {computed, ref} from 'vue';
import {DocumentService} from '@/../bindings/voidraft/internal/services';
import {OpenDocumentWindow} from '@/../bindings/voidraft/internal/services/windowservice';
import {Document} from '@/../bindings/voidraft/internal/models/ent/models';
import {useTabStore} from "@/stores/tabStore";
import type {EditorViewState} from '@/stores/editorStore';
export const useDocumentStore = defineStore('document', () => {
@@ -70,10 +69,6 @@ export const useDocumentStore = defineStore('document', () => {
// 在新窗口中打开文档
const openDocumentInNewWindow = async (docId: number): Promise<boolean> => {
try {
const tabStore = useTabStore();
if (tabStore.isTabsEnabled && tabStore.hasTab(docId)) {
tabStore.closeTab(docId);
}
await OpenDocumentWindow(docId);
return true;
} catch (error) {
@@ -112,7 +107,7 @@ export const useDocumentStore = defineStore('document', () => {
}
};
// 打开文档
// 打开文档 - 只负责文档数据管理
const openDocument = async (docId: number): Promise<boolean> => {
try {
// 获取完整文档数据
@@ -131,7 +126,7 @@ export const useDocumentStore = defineStore('document', () => {
}
};
// 更新文档元数据
// 更新文档元数据 - 只负责文档数据管理
const updateDocumentMetadata = async (docId: number, title: string): Promise<boolean> => {
try {
await DocumentService.UpdateDocumentTitle(docId, title);
@@ -148,10 +143,6 @@ export const useDocumentStore = defineStore('document', () => {
currentDocument.value.updated_at = new Date().toISOString();
}
// 同步更新标签页标题
const tabStore = useTabStore();
tabStore.updateTabTitle(docId, title);
return true;
} catch (error) {
console.error('Failed to update document metadata:', error);
@@ -159,7 +150,7 @@ export const useDocumentStore = defineStore('document', () => {
}
};
// 删除文档
// 删除文档 - 只负责文档数据管理
const deleteDocument = async (docId: number): Promise<boolean> => {
try {
await DocumentService.DeleteDocument(docId);
@@ -167,12 +158,6 @@ export const useDocumentStore = defineStore('document', () => {
// 更新本地状态
delete documents.value[docId];
// 同步清理标签页
const tabStore = useTabStore();
if (tabStore.hasTab(docId)) {
tabStore.closeTab(docId);
}
// 如果删除的是当前文档,切换到第一个可用文档
if (currentDocumentId.value === docId) {
const availableDocs = Object.values(documents.value);
@@ -192,7 +177,7 @@ export const useDocumentStore = defineStore('document', () => {
};
// === 初始化 ===
const initialize = async (urlDocumentId?: number): Promise<void> => {
const initDocument = async (urlDocumentId?: number): Promise<void> => {
try {
await getDocumentMetaList();
@@ -235,7 +220,7 @@ export const useDocumentStore = defineStore('document', () => {
closeDocumentSelector,
setError,
clearError,
initialize,
initDocument,
};
}, {
persist: {

View File

@@ -1,5 +1,5 @@
import {defineStore} from 'pinia';
import {computed, nextTick, ref, watch} from 'vue';
import {computed, nextTick, ref} from 'vue';
import {EditorView} from '@codemirror/view';
import {EditorState, Extension} from '@codemirror/state';
import {useConfigStore} from './configStore';
@@ -24,7 +24,6 @@ import {
import {useExtensionStore} from './extensionStore';
import createCodeBlockExtension from "@/views/editor/extensions/codeblock";
import {LruCache} from '@/common/utils/lruCache';
import {AsyncManager} from '@/common/utils/asyncManager';
import {generateContentHash} from "@/common/utils/hashUtils";
import {createTimerManager, type TimerManager} from '@/common/utils/timerUtils';
import {EDITOR_CONFIG} from '@/common/constant/editor';
@@ -37,7 +36,6 @@ export interface DocumentStats {
selectedCharacters: number;
}
// 修复:只保存光标位置,恢复时自动滚动到光标处
export interface EditorViewState {
cursorPos: number;
}
@@ -54,7 +52,6 @@ interface EditorInstance {
lastContentHash: string;
lastParsed: Date;
} | null;
// 修复:使用统一的类型,可选但不是 undefined | {...}
editorState?: EditorViewState;
}
@@ -74,14 +71,9 @@ export const useEditorStore = defineStore('editor', () => {
characters: 0,
selectedCharacters: 0
});
// 编辑器加载状态
const isLoading = ref(false);
// 修复:使用操作计数器精确管理加载状态
const loadingOperations = ref(0);
// 异步操作管理器
const operationManager = new AsyncManager<number>();
// 自动保存设置 - 从配置动态获取
const getAutoSaveDelay = () => configStore.config.editing.autoSaveDelay;
@@ -91,7 +83,7 @@ export const useEditorStore = defineStore('editor', () => {
if (instance) {
instance.syntaxTreeCache = null;
}
}, { delay: 500 }); // 500ms 内的多次输入只清理一次
}, {delay: 500}); // 500ms 内的多次输入只清理一次
// 缓存化的语法树确保方法
@@ -106,42 +98,33 @@ export const useEditorStore = defineStore('editor', () => {
// 检查是否需要重新构建语法树
const cache = instance.syntaxTreeCache;
const shouldRebuild = !cache ||
cache.lastDocLength !== docLength ||
const shouldRebuild = !cache ||
cache.lastDocLength !== docLength ||
cache.lastContentHash !== contentHash ||
(now.getTime() - cache.lastParsed.getTime()) > EDITOR_CONFIG.SYNTAX_TREE_CACHE_TIMEOUT;
if (shouldRebuild) {
try {
ensureSyntaxTree(view.state, docLength, 5000);
// 更新缓存
instance.syntaxTreeCache = {
lastDocLength: docLength,
lastContentHash: contentHash,
lastParsed: now
};
} catch (error) {
console.warn('Failed to ensure syntax tree:', error);
}
ensureSyntaxTree(view.state, docLength, 5000);
// 更新缓存
instance.syntaxTreeCache = {
lastDocLength: docLength,
lastContentHash: contentHash,
lastParsed: now
};
}
};
// 创建编辑器实例
const createEditorInstance = async (
content: string,
operationId: number,
content: string,
documentId: number
): Promise<EditorView> => {
if (!containerElement.value) {
throw new Error('Editor container not set');
}
// 检查操作是否仍然有效
if (!operationManager.isOperationValid(operationId, documentId)) {
throw new Error('Operation cancelled');
}
// 获取基本扩展
const basicExtensions = createBasicSetup();
@@ -185,27 +168,12 @@ export const useEditorStore = defineStore('editor', () => {
// 光标位置持久化扩展
const cursorPositionExtension = createCursorPositionExtension(documentId);
// 再次检查操作有效性
if (!operationManager.isOperationValid(operationId, documentId)) {
throw new Error('Operation cancelled');
}
// 快捷键扩展
const keymapExtension = await createDynamicKeymapExtension();
// 检查操作有效性
if (!operationManager.isOperationValid(operationId, documentId)) {
throw new Error('Operation cancelled');
}
// 动态扩展传递文档ID以便扩展管理器可以预初始化
const dynamicExtensions = await createDynamicExtensions();
// 最终检查操作有效性
if (!operationManager.isOperationValid(operationId, documentId)) {
throw new Error('Operation cancelled');
}
// 组合所有扩展
const extensions: Extension[] = [
keymapExtension,
@@ -224,8 +192,8 @@ export const useEditorStore = defineStore('editor', () => {
// 获取保存的光标位置
const savedState = documentStore.documentStates[documentId];
const docLength = content.length;
const initialCursorPos = savedState?.cursorPos !== undefined
? Math.min(savedState.cursorPos, docLength)
const initialCursorPos = savedState?.cursorPos !== undefined
? Math.min(savedState.cursorPos, docLength)
: docLength;
@@ -233,7 +201,7 @@ export const useEditorStore = defineStore('editor', () => {
const state = EditorState.create({
doc: content,
extensions,
selection: { anchor: initialCursorPos, head: initialCursorPos }
selection: {anchor: initialCursorPos, head: initialCursorPos}
});
return new EditorView({
@@ -271,9 +239,8 @@ export const useEditorStore = defineStore('editor', () => {
// 获取或创建编辑器
const getOrCreateEditor = async (
documentId: number,
content: string,
operationId: number
documentId: number,
content: string
): Promise<EditorView> => {
// 检查缓存
const cached = editorCache.get(documentId);
@@ -281,29 +248,8 @@ export const useEditorStore = defineStore('editor', () => {
return cached.view;
}
// 检查操作是否仍然有效
if (!operationManager.isOperationValid(operationId, documentId)) {
throw new Error('Operation cancelled');
}
// 创建新的编辑器实例
const view = await createEditorInstance(content, operationId, documentId);
// 完善取消操作时的清理逻辑
if (!operationManager.isOperationValid(operationId, documentId)) {
// 如果操作已取消,彻底清理创建的实例
try {
// 移除 DOM 元素(如果已添加到文档)
if (view.dom && view.dom.parentElement) {
view.dom.remove();
}
// 销毁编辑器视图
view.destroy();
} catch (error) {
console.error('Error cleaning up cancelled editor:', error);
}
throw new Error('Operation cancelled');
}
const view = await createEditorInstance(content, documentId);
addEditorToCache(documentId, view, content);
@@ -333,10 +279,10 @@ export const useEditorStore = defineStore('editor', () => {
requestAnimationFrame(() => {
// 滚动到当前光标位置
scrollToCursor(instance.view);
// 聚焦编辑器
instance.view.focus();
// 使用缓存的语法树确保方法
ensureSyntaxTreeCached(instance.view, documentId);
});
@@ -354,7 +300,7 @@ export const useEditorStore = defineStore('editor', () => {
try {
const content = instance.view.state.doc.toString();
const lastModified = instance.lastModified;
await DocumentService.UpdateDocumentContent(documentId, content);
// 检查在保存期间内容是否又被修改了
@@ -381,7 +327,7 @@ export const useEditorStore = defineStore('editor', () => {
// 立即设置脏标记和修改时间(切换文档时需要判断)
instance.isDirty = true;
instance.lastModified = new Date();
// 优使用防抖清理语法树缓存
debouncedClearSyntaxCache.debouncedFn(instance);
@@ -392,24 +338,18 @@ export const useEditorStore = defineStore('editor', () => {
};
// 检查容器是否已设置
const hasContainer = computed(() => containerElement.value !== null);
// 设置编辑器容器
const setEditorContainer = (container: HTMLElement | null) => {
containerElement.value = container;
// 如果设置容器时已有当前文档,立即加载编辑器
if (container && documentStore.currentDocument && documentStore.currentDocument.id !== undefined) {
loadEditor(documentStore.currentDocument.id, documentStore.currentDocument.content || '');
}
// watch 会自动监听并加载编辑器,无需手动调用
};
// 加载编辑器
const loadEditor = async (documentId: number, content: string) => {
// 修复:使用计数器精确管理加载状态
loadingOperations.value++;
isLoading.value = true;
// 开始新的操作
const { operationId } = operationManager.startOperation(documentId);
try {
// 验证参数
@@ -422,33 +362,25 @@ export const useEditorStore = defineStore('editor', () => {
const currentDocId = documentStore.currentDocumentId;
if (currentDocId && currentDocId !== documentId) {
await saveEditorContent(currentDocId);
// 检查操作是否仍然有效
if (!operationManager.isOperationValid(operationId, documentId)) {
return;
}
}
}
// 获取或创建编辑器
const view = await getOrCreateEditor(documentId, content, operationId);
const view = await getOrCreateEditor(documentId, content);
// 检查操作是否仍然有效
if (!operationManager.isOperationValid(operationId, documentId)) {
return;
}
// 更新内容(如果需要)
// 更新内容
const instance = editorCache.get(documentId);
if (instance && instance.content !== content) {
// 确保编辑器视图有效
if (view && view.state && view.dispatch) {
const contentLength = content.length;
view.dispatch({
changes: {
from: 0,
to: view.state.doc.length,
insert: content
}
},
selection: {anchor: contentLength, head: contentLength}
});
instance.content = content;
instance.isDirty = false;
@@ -460,32 +392,14 @@ export const useEditorStore = defineStore('editor', () => {
}
}
// 最终检查操作有效性
if (!operationManager.isOperationValid(operationId, documentId)) {
return;
}
// 显示编辑器
showEditor(documentId);
} catch (error) {
if (error instanceof Error && error.message === 'Operation cancelled') {
console.log(`Editor loading cancelled for document ${documentId}`);
} else {
console.error('Failed to load editor:', error);
}
console.error('Failed to load editor:', error);
} finally {
// 完成操作
operationManager.completeOperation(operationId);
// 修复:使用计数器精确管理加载状态,避免快速切换时状态不准确
loadingOperations.value--;
// 延迟一段时间后再取消加载状态,但要确保所有操作都完成了
setTimeout(() => {
if (loadingOperations.value <= 0) {
loadingOperations.value = 0;
isLoading.value = false;
}
isLoading.value = false;
}, EDITOR_CONFIG.LOADING_DELAY);
}
};
@@ -495,12 +409,6 @@ export const useEditorStore = defineStore('editor', () => {
const instance = editorCache.get(documentId);
if (instance) {
try {
// 如果正在加载这个文档,取消操作
if (operationManager.getCurrentContext() === documentId) {
operationManager.cancelAllOperations();
}
// 修复:移除前先保存内容(如果有未保存的修改)
if (instance.isDirty) {
await saveEditorContent(documentId);
}
@@ -583,9 +491,6 @@ export const useEditorStore = defineStore('editor', () => {
// 清空所有编辑器
const clearAllEditors = () => {
// 取消所有挂起的操作
operationManager.cancelAllOperations();
editorCache.clear((_documentId, instance) => {
// 清除自动保存定时器
instance.autoSaveTimer.clear();
@@ -598,7 +503,7 @@ export const useEditorStore = defineStore('editor', () => {
// 销毁编辑器
instance.view.destroy();
});
currentEditor.value = null;
};
@@ -606,7 +511,7 @@ export const useEditorStore = defineStore('editor', () => {
const updateExtension = async (id: number, enabled: boolean, config?: any) => {
// 更新启用状态
await ExtensionService.UpdateExtensionEnabled(id, enabled);
// 如果需要更新配置
if (config !== undefined) {
await ExtensionService.UpdateExtensionConfig(id, config);
@@ -614,7 +519,7 @@ export const useEditorStore = defineStore('editor', () => {
// 重新加载扩展配置
await extensionStore.loadExtensions();
// 获取更新后的扩展名称
const extension = extensionStore.extensions.find(ext => ext.id === id);
if (!extension) return;
@@ -630,38 +535,12 @@ export const useEditorStore = defineStore('editor', () => {
await applyKeymapSettings();
};
// 监听文档切换
watch(() => documentStore.currentDocument, async (newDoc, oldDoc) => {
if (newDoc && newDoc.id !== undefined && containerElement.value) {
// 等待 DOM 更新完成,再加载新文档的编辑器
await nextTick();
loadEditor(newDoc.id, newDoc.content || '');
}
});
// 创建字体配置的计算属性
const fontConfig = computed(() => ({
fontSize: configStore.config.editing.fontSize,
fontFamily: configStore.config.editing.fontFamily,
lineHeight: configStore.config.editing.lineHeight,
fontWeight: configStore.config.editing.fontWeight
}));
// 创建Tab配置的计算属性
const tabConfig = computed(() => ({
tabSize: configStore.config.editing.tabSize,
enableTabIndent: configStore.config.editing.enableTabIndent,
tabType: configStore.config.editing.tabType
}));
// 监听字体配置变化
watch(fontConfig, applyFontSettings, { deep: true });
// 监听Tab配置变化
watch(tabConfig, applyTabSettings, { deep: true });
return {
// 状态
currentEditor,
documentStats,
isLoading,
hasContainer,
// 方法
setEditorContainer,
@@ -670,7 +549,6 @@ export const useEditorStore = defineStore('editor', () => {
clearAllEditors,
onContentChange,
// 配置更新方法
applyFontSettings,
applyThemeSettings,
applyTabSettings,

View File

@@ -18,12 +18,9 @@ export const useTabStore = defineStore('tab', () => {
const tabsMap = ref<Record<number, Tab>>({});
const tabOrder = ref<number[]>([]); // 维护标签页顺序
const draggedTabId = ref<number | null>(null);
// === 计算属性 ===
const isTabsEnabled = computed(() => configStore.config.general.enableTabs);
const canCloseTab = computed(() => tabOrder.value.length > 1);
const currentDocumentId = computed(() => documentStore.currentDocumentId);
// 按顺序返回标签页数组用于UI渲染
const tabs = computed(() => {
@@ -75,7 +72,7 @@ export const useTabStore = defineStore('tab', () => {
/**
* 关闭标签页
*/
const closeTab = (documentId: number) => {
const closeTab = async (documentId: number) => {
if (!hasTab(documentId)) return;
const tabIndex = tabOrder.value.indexOf(documentId);
@@ -95,7 +92,7 @@ export const useTabStore = defineStore('tab', () => {
if (nextIndex >= 0 && tabOrder.value[nextIndex]) {
const nextDocumentId = tabOrder.value[nextIndex];
switchToTabAndDocument(nextDocumentId);
await switchToTabAndDocument(nextDocumentId);
}
}
};
@@ -120,15 +117,15 @@ export const useTabStore = defineStore('tab', () => {
/**
* 切换到指定标签页并打开对应文档
*/
const switchToTabAndDocument = (documentId: number) => {
const switchToTabAndDocument = async (documentId: number) => {
if (!hasTab(documentId)) return;
// 如果点击的是当前已激活的文档,不需要重复请求
if (documentStore.currentDocumentId === documentId) {
return;
}
documentStore.openDocument(documentId);
await documentStore.openDocument(documentId);
};
/**
@@ -172,8 +169,8 @@ export const useTabStore = defineStore('tab', () => {
/**
* 初始化标签页(当前文档)
*/
const initializeTab = () => {
// 先验证并清理无效的标签页(处理持久化的脏数据)
const initTab = () => {
// 先验证并清理无效的标签页
validateTabs();
if (isTabsEnabled.value) {
@@ -189,7 +186,7 @@ export const useTabStore = defineStore('tab', () => {
/**
* 关闭其他标签页(除了指定的标签页)
*/
const closeOtherTabs = (keepDocumentId: number) => {
const closeOtherTabs = async (keepDocumentId: number) => {
if (!hasTab(keepDocumentId)) return;
// 获取所有其他标签页的ID
@@ -200,14 +197,14 @@ export const useTabStore = defineStore('tab', () => {
// 如果当前打开的文档在被关闭的标签中,需要切换到保留的文档
if (otherTabIds.includes(documentStore.currentDocumentId!)) {
switchToTabAndDocument(keepDocumentId);
await switchToTabAndDocument(keepDocumentId);
}
};
/**
* 关闭指定标签页右侧的所有标签页
*/
const closeTabsToRight = (documentId: number) => {
const closeTabsToRight = async (documentId: number) => {
const index = getTabIndex(documentId);
if (index === -1) return;
@@ -219,14 +216,14 @@ export const useTabStore = defineStore('tab', () => {
// 如果当前打开的文档在被关闭的右侧标签中,需要切换到指定的文档
if (rightTabIds.includes(documentStore.currentDocumentId!)) {
switchToTabAndDocument(documentId);
await switchToTabAndDocument(documentId);
}
};
/**
* 关闭指定标签页左侧的所有标签页
*/
const closeTabsToLeft = (documentId: number) => {
const closeTabsToLeft = async (documentId: number) => {
const index = getTabIndex(documentId);
if (index <= 0) return;
@@ -238,7 +235,7 @@ export const useTabStore = defineStore('tab', () => {
// 如果当前打开的文档在被关闭的左侧标签中,需要切换到指定的文档
if (leftTabIds.includes(documentStore.currentDocumentId!)) {
switchToTabAndDocument(documentId);
await switchToTabAndDocument(documentId);
}
};
@@ -262,7 +259,6 @@ export const useTabStore = defineStore('tab', () => {
// 计算属性
isTabsEnabled,
canCloseTab,
currentDocumentId,
// 方法
addOrActivateTab,
@@ -273,7 +269,7 @@ export const useTabStore = defineStore('tab', () => {
switchToTabAndDocument,
moveTab,
getTabIndex,
initializeTab,
initTab,
clearAllTabs,
updateTabTitle,
validateTabs,

View File

@@ -6,6 +6,7 @@ import {ThemeService} from '@/../bindings/voidraft/internal/services';
import {useConfigStore} from './configStore';
import type {ThemeColors} from '@/views/editor/theme/types';
import {cloneThemeColors, FALLBACK_THEME_NAME, themePresetList, themePresetMap} from '@/views/editor/theme/presets';
import {useEditorStore} from "@/stores/editorStore";
// 类型定义
type ThemeOption = { name: string; type: ThemeType };
@@ -91,11 +92,12 @@ export const useThemeStore = defineStore('theme', () => {
// 同步应用到 DOM 与编辑器
const applyAllThemes = () => {
applyThemeToDOM(currentTheme.value);
const editorStore = useEditorStore();
editorStore.applyThemeSettings();
};
// 初始化主题
const initTheme = async () => {
applyThemeToDOM(currentTheme.value);
await loadThemeColors();
applyAllThemes();
};