🎨 Optimized error handling

This commit is contained in:
2025-06-02 22:57:11 +08:00
parent a516b8973e
commit 77bd15bed7
9 changed files with 354 additions and 274 deletions

View File

@@ -7,8 +7,8 @@ const configStore = useConfigStore();
// 应用启动时加载配置 // 应用启动时加载配置
onMounted(async () => { onMounted(async () => {
await configStore.initializeLanguage();
await configStore.initConfig(); await configStore.initConfig();
await configStore.initializeLanguage();
}); });
</script> </script>

View File

@@ -13,10 +13,28 @@ let intervalId: ReturnType<typeof setInterval> | null = null;
const historyData = ref<number[]>([]); const historyData = ref<number[]>([]);
const maxDataPoints = 60; const maxDataPoints = 60;
// 静默错误处理包装器
const withSilentErrorHandling = async <T>(
operation: () => Promise<T>,
fallback?: T
): Promise<T | undefined> => {
try {
return await operation();
} catch (error) {
// 静默处理错误,不输出到控制台
return fallback;
}
};
// 获取内存统计信息 // 获取内存统计信息
const fetchMemoryStats = async () => { const fetchMemoryStats = async () => {
try { const stats = await withSilentErrorHandling(() => SystemService.GetMemoryStats());
const stats = await SystemService.GetMemoryStats();
if (!stats) {
isLoading.value = false;
return;
}
memoryStats.value = stats; memoryStats.value = stats;
// 格式化内存显示 - 主要显示堆内存使用量 // 格式化内存显示 - 主要显示堆内存使用量
@@ -42,10 +60,6 @@ const fetchMemoryStats = async () => {
drawChart(); drawChart();
isLoading.value = false; isLoading.value = false;
} catch (error) {
console.error('Failed to fetch memory stats:', error);
isLoading.value = false;
}
}; };
// 绘制实时曲线图 // 绘制实时曲线图
@@ -199,12 +213,11 @@ const drawChart = () => {
// 手动触发GC // 手动触发GC
const triggerGC = async () => { const triggerGC = async () => {
try { const success = await withSilentErrorHandling(() => SystemService.TriggerGC());
await SystemService.TriggerGC();
if (success) {
// 延迟一下再获取新的统计信息 // 延迟一下再获取新的统计信息
setTimeout(fetchMemoryStats, 100); setTimeout(fetchMemoryStats, 100);
} catch (error) {
console.error('Failed to trigger GC:', error);
} }
}; };

View File

@@ -2,15 +2,16 @@
import {useEditorStore} from '@/stores/editorStore'; import {useEditorStore} from '@/stores/editorStore';
import {useConfigStore, SUPPORTED_LOCALES, type SupportedLocaleType} from '@/stores/configStore'; import {useConfigStore, SUPPORTED_LOCALES, type SupportedLocaleType} from '@/stores/configStore';
import {useLogStore} from '@/stores/logStore'; import {useLogStore} from '@/stores/logStore';
import { useErrorHandler } from '@/utils/errorHandler';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { ref, onMounted, watch } from 'vue'; import { ref, onMounted, watch } from 'vue';
import { ConfigUtils } from '@/utils/configUtils';
import * as runtime from '@wailsio/runtime'; import * as runtime from '@wailsio/runtime';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
const editorStore = useEditorStore(); const editorStore = useEditorStore();
const configStore = useConfigStore(); const configStore = useConfigStore();
const logStore = useLogStore(); const logStore = useLogStore();
const { safeCall } = useErrorHandler();
const { t, locale } = useI18n(); const { t, locale } = useI18n();
const router = useRouter(); const router = useRouter();
// 语言下拉菜单 // 语言下拉菜单
@@ -18,13 +19,10 @@ const showLanguageMenu = ref(false);
// 切换语言 // 切换语言
const changeLanguage = async (localeCode: SupportedLocaleType) => { const changeLanguage = async (localeCode: SupportedLocaleType) => {
try { await safeCall(
// 使用 configStore 的语言设置方法 () => configStore.setLocale(localeCode),
await configStore.setLocale(localeCode); 'config.languageChangeFailed'
} catch (error) { );
console.error('Failed to change language:', error);
}
showLanguageMenu.value = false; showLanguageMenu.value = false;
}; };
@@ -35,14 +33,11 @@ const toggleLanguageMenu = () => {
// 窗口置顶控制 // 窗口置顶控制
const toggleAlwaysOnTop = async () => { const toggleAlwaysOnTop = async () => {
try { await safeCall(async () => {
await configStore.toggleAlwaysOnTop(); await configStore.toggleAlwaysOnTop();
// 使用Window.SetAlwaysOnTop方法设置窗口置顶状态 // 使用Window.SetAlwaysOnTop方法设置窗口置顶状态
await runtime.Window.SetAlwaysOnTop(configStore.config.alwaysOnTop); await runtime.Window.SetAlwaysOnTop(configStore.config.alwaysOnTop);
} catch (error) { }, 'config.alwaysOnTopFailed');
console.error('Failed to set window always on top:', error);
logStore.error(t('config.alwaysOnTopFailed'));
}
}; };
// 打开设置页面 // 打开设置页面
@@ -50,31 +45,31 @@ const openSettings = () => {
router.push('/settings'); router.push('/settings');
}; };
// 设置窗口置顶状态的通用函数
const setWindowAlwaysOnTop = async (alwaysOnTop: boolean) => {
await safeCall(
() => runtime.Window.SetAlwaysOnTop(alwaysOnTop),
'config.alwaysOnTopFailed'
);
};
// 初始化配置 // 初始化配置
onMounted(async () => { onMounted(async () => {
// 加载配置 // 加载配置
if (!configStore.configLoaded) { if (!configStore.configLoaded) {
await configStore.loadConfig(); await configStore.initConfig();
} }
// 设置窗口置顶状态 // 设置窗口置顶状态
if (configStore.config.alwaysOnTop) { if (configStore.config.alwaysOnTop) {
try { await setWindowAlwaysOnTop(true);
await runtime.Window.SetAlwaysOnTop(true);
} catch (error) {
console.error('Failed to set window always on top:', error);
}
} }
}); });
// 监听配置加载完成 // 监听配置加载完成
watch(() => configStore.configLoaded, (isLoaded) => { watch(() => configStore.configLoaded, (isLoaded) => {
if (isLoaded && configStore.config.alwaysOnTop) { if (isLoaded && configStore.config.alwaysOnTop) {
try { setWindowAlwaysOnTop(true);
runtime.Window.SetAlwaysOnTop(true);
} catch (error) {
console.error('Failed to set window always on top:', error);
}
} }
}); });
</script> </script>

View File

@@ -4,8 +4,8 @@ import {
ConfigService ConfigService
} from '@/../bindings/voidraft/internal/services'; } from '@/../bindings/voidraft/internal/services';
import {EditorConfig, TabType, LanguageType} from '@/../bindings/voidraft/internal/models/models'; import {EditorConfig, TabType, LanguageType} from '@/../bindings/voidraft/internal/models/models';
import {useLogStore} from './logStore';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useErrorHandler } from '@/utils/errorHandler';
import { ConfigUtils } from '@/utils/configUtils'; import { ConfigUtils } from '@/utils/configUtils';
// 国际化相关导入 // 国际化相关导入
@@ -21,7 +21,7 @@ export const SUPPORTED_LOCALES = [
code: 'en-US' as SupportedLocaleType, code: 'en-US' as SupportedLocaleType,
name: 'English' name: 'English'
} }
]; ] as const;
// 配置键映射和限制的类型定义 // 配置键映射和限制的类型定义
type ConfigKeyMap = { type ConfigKeyMap = {
@@ -93,8 +93,8 @@ const DEFAULT_CONFIG: EditorConfig = {
}; };
export const useConfigStore = defineStore('config', () => { export const useConfigStore = defineStore('config', () => {
const logStore = useLogStore(); const { locale } = useI18n();
const { t, locale } = useI18n(); const { safeCall } = useErrorHandler();
// 响应式状态 // 响应式状态
const state = reactive({ const state = reactive({
@@ -105,57 +105,32 @@ export const useConfigStore = defineStore('config', () => {
// 计算属性 - 使用工厂函数简化 // 计算属性 - 使用工厂函数简化
const createLimitComputed = (key: NumberConfigKey) => computed(() => CONFIG_LIMITS[key]); const createLimitComputed = (key: NumberConfigKey) => computed(() => CONFIG_LIMITS[key]);
const limits = { const limits = Object.fromEntries(
fontSize: createLimitComputed('fontSize'), (['fontSize', 'tabSize', 'lineHeight'] as const).map(key => [key, createLimitComputed(key)])
tabSize: createLimitComputed('tabSize'), ) as Record<NumberConfigKey, ReturnType<typeof createLimitComputed>>;
lineHeight: createLimitComputed('lineHeight')
};
// 错误处理装饰器
const withErrorHandling = <T extends any[], R>(
fn: (...args: T) => Promise<R>,
errorMsg: string,
successMsg?: string
) => async (...args: T): Promise<R | null> => {
const result = await fn(...args).catch(error => {
console.error(errorMsg, error);
logStore.error(t(errorMsg));
return null;
});
if (result !== null && successMsg) {
logStore.info(t(successMsg));
}
return result;
};
// 通用配置更新方法 // 通用配置更新方法
const updateConfig = withErrorHandling( const updateConfig = async <K extends keyof EditorConfig>(key: K, value: EditorConfig[K]): Promise<void> => {
async <K extends keyof EditorConfig>(key: K, value: EditorConfig[K]): Promise<void> => { // 确保配置已加载
// 确保配置已加载 if (!state.configLoaded && !state.isLoading) {
if (!state.configLoaded && !state.isLoading) { await initConfig();
await initConfig(); }
}
const backendKey = CONFIG_KEY_MAP[key];
const backendKey = CONFIG_KEY_MAP[key]; if (!backendKey) {
if (!backendKey) { throw new Error(`No backend key mapping found for ${key.toString()}`);
throw new Error(`No backend key mapping found for ${key.toString()}`); }
}
await ConfigService.Set(backendKey, value);
await ConfigService.Set(backendKey, value); state.config[key] = value;
state.config[key] = value; };
},
'config.saveFailed',
'config.saveSuccess'
);
// 加载配置 // 加载配置
const initConfig = withErrorHandling( const initConfig = async (): Promise<void> => {
async (): Promise<void> => { if (state.isLoading) return;
if (state.isLoading) return;
state.isLoading = true;
state.isLoading = true; try {
const appConfig = await ConfigService.GetConfig(); const appConfig = await ConfigService.GetConfig();
if (appConfig?.editor) { if (appConfig?.editor) {
@@ -163,62 +138,60 @@ export const useConfigStore = defineStore('config', () => {
} }
state.configLoaded = true; state.configLoaded = true;
} finally {
state.isLoading = false; state.isLoading = false;
}, }
'config.loadFailed', };
'config.loadSuccess'
);
// 数值调整工厂函数 // 通用数值调整工厂
const createNumberAdjuster = (key: NumberConfigKey) => { const createAdjuster = <T extends NumberConfigKey>(key: T) => {
const limit = CONFIG_LIMITS[key]; const limit = CONFIG_LIMITS[key];
const clamp = (value: number) => ConfigUtils.clamp(value, limit.min, limit.max);
return { return {
increase: () => updateConfig(key, ConfigUtils.clamp(state.config[key] + 1, limit.min, limit.max)), increase: () => safeCall(() => updateConfig(key, clamp(state.config[key] + 1)), 'config.saveFailed', 'config.saveSuccess'),
decrease: () => updateConfig(key, ConfigUtils.clamp(state.config[key] - 1, limit.min, limit.max)), decrease: () => safeCall(() => updateConfig(key, clamp(state.config[key] - 1)), 'config.saveFailed', 'config.saveSuccess'),
set: (value: number) => updateConfig(key, ConfigUtils.clamp(value, limit.min, limit.max)), set: (value: number) => safeCall(() => updateConfig(key, clamp(value)), 'config.saveFailed', 'config.saveSuccess'),
reset: () => updateConfig(key, limit.default) reset: () => safeCall(() => updateConfig(key, limit.default), 'config.saveFailed', 'config.saveSuccess')
}; };
}; };
// 布尔值切换工厂函数 // 通用布尔值切换
const createToggler = (key: BooleanConfigKey) => const createToggler = <T extends BooleanConfigKey>(key: T) =>
() => updateConfig(key, !state.config[key]); () => safeCall(() => updateConfig(key, !state.config[key]), 'config.saveFailed', 'config.saveSuccess');
// 枚举值切换工厂函数 // 枚举值切换
const createEnumToggler = <T extends TabType>(key: 'tabType', values: readonly T[]) => const createEnumToggler = <T extends TabType>(key: 'tabType', values: readonly T[]) =>
() => { () => {
const currentIndex = values.indexOf(state.config[key] as T); const currentIndex = values.indexOf(state.config[key] as T);
const nextIndex = (currentIndex + 1) % values.length; const nextIndex = (currentIndex + 1) % values.length;
return updateConfig(key, values[nextIndex]); return safeCall(() => updateConfig(key, values[nextIndex]), 'config.saveFailed', 'config.saveSuccess');
}; };
// 重置配置 // 重置配置
const resetConfig = withErrorHandling( const resetConfig = async (): Promise<void> => {
async (): Promise<void> => { if (state.isLoading) return;
if (state.isLoading) return;
state.isLoading = true;
state.isLoading = true; try {
await ConfigService.ResetConfig(); await safeCall(() => ConfigService.ResetConfig(), 'config.resetFailed', 'config.resetSuccess');
await initConfig(); await safeCall(() => initConfig(), 'config.loadFailed', 'config.loadSuccess');
} finally {
state.isLoading = false; state.isLoading = false;
}, }
'config.resetFailed', };
'config.resetSuccess'
);
// 语言设置方法 // 语言设置方法
const setLanguage = withErrorHandling( const setLanguage = async (language: LanguageType): Promise<void> => {
async (language: LanguageType): Promise<void> => { await safeCall(async () => {
await ConfigService.Set(CONFIG_KEY_MAP.language, language); await ConfigService.Set(CONFIG_KEY_MAP.language, language);
state.config.language = language; state.config.language = language;
// 同步更新前端语言 // 同步更新前端语言
const frontendLocale = ConfigUtils.backendLanguageToFrontend(language); const frontendLocale = ConfigUtils.backendLanguageToFrontend(language);
locale.value = frontendLocale as any; locale.value = frontendLocale as any;
}, }, 'config.languageChangeFailed', 'config.languageChanged');
'config.languageChangeFailed', };
'config.languageChanged'
);
// 通过前端语言代码设置语言 // 通过前端语言代码设置语言
const setLocale = async (localeCode: SupportedLocaleType): Promise<void> => { const setLocale = async (localeCode: SupportedLocaleType): Promise<void> => {
@@ -243,19 +216,25 @@ export const useConfigStore = defineStore('config', () => {
} }
}; };
// 创建数值调整器 // 创建数值调整器实例
const fontSize = createNumberAdjuster('fontSize'); const adjusters = {
const tabSize = createNumberAdjuster('tabSize'); fontSize: createAdjuster('fontSize'),
const lineHeight = createNumberAdjuster('lineHeight'); tabSize: createAdjuster('tabSize'),
lineHeight: createAdjuster('lineHeight')
};
// 创建切换器 // 创建切换器实例
const toggleTabIndent = createToggler('enableTabIndent'); const togglers = {
const toggleAlwaysOnTop = createToggler('alwaysOnTop'); tabIndent: createToggler('enableTabIndent'),
const toggleTabType = createEnumToggler('tabType', CONFIG_LIMITS.tabType.values); alwaysOnTop: createToggler('alwaysOnTop'),
tabType: createEnumToggler('tabType', CONFIG_LIMITS.tabType.values)
};
// 字符串配置设置器 // 字符串配置设置器
const setFontFamily = (value: string) => updateConfig('fontFamily', value); const setters = {
const setFontWeight = (value: string) => updateConfig('fontWeight', value); fontFamily: (value: string) => safeCall(() => updateConfig('fontFamily', value), 'config.saveFailed', 'config.saveSuccess'),
fontWeight: (value: string) => safeCall(() => updateConfig('fontWeight', value), 'config.saveFailed', 'config.saveSuccess')
};
return { return {
// 状态 // 状态
@@ -267,9 +246,9 @@ export const useConfigStore = defineStore('config', () => {
...limits, ...limits,
// 核心方法 // 核心方法
initConfig, initConfig: () => safeCall(() => initConfig(), 'config.loadFailed', 'config.loadSuccess'),
resetConfig, resetConfig,
updateConfig, updateConfig: (key: keyof EditorConfig, value: any) => safeCall(() => updateConfig(key, value), 'config.saveFailed', 'config.saveSuccess'),
// 语言相关方法 // 语言相关方法
setLanguage, setLanguage,
@@ -277,29 +256,29 @@ export const useConfigStore = defineStore('config', () => {
initializeLanguage, initializeLanguage,
// 字体大小操作 // 字体大小操作
...fontSize, ...adjusters.fontSize,
increaseFontSize: fontSize.increase, increaseFontSize: adjusters.fontSize.increase,
decreaseFontSize: fontSize.decrease, decreaseFontSize: adjusters.fontSize.decrease,
resetFontSize: fontSize.reset, resetFontSize: adjusters.fontSize.reset,
setFontSize: fontSize.set, setFontSize: adjusters.fontSize.set,
// Tab操作 // Tab操作
toggleTabIndent, toggleTabIndent: togglers.tabIndent,
...tabSize, ...adjusters.tabSize,
increaseTabSize: tabSize.increase, increaseTabSize: adjusters.tabSize.increase,
decreaseTabSize: tabSize.decrease, decreaseTabSize: adjusters.tabSize.decrease,
setTabSize: tabSize.set, setTabSize: adjusters.tabSize.set,
toggleTabType, toggleTabType: togglers.tabType,
// 行高操作 // 行高操作
setLineHeight: lineHeight.set, setLineHeight: adjusters.lineHeight.set,
// 窗口操作 // 窗口操作
toggleAlwaysOnTop, toggleAlwaysOnTop: togglers.alwaysOnTop,
// 字体操作 // 字体操作
setFontFamily, setFontFamily: setters.fontFamily,
setFontWeight, setFontWeight: setters.fontWeight,
}; };
},{ },{
persist: true, persist: true,

View File

@@ -2,12 +2,10 @@ import {defineStore} from 'pinia';
import {computed, ref} from 'vue'; import {computed, ref} from 'vue';
import {DocumentService} from '@/../bindings/voidraft/internal/services'; import {DocumentService} from '@/../bindings/voidraft/internal/services';
import {Document} from '@/../bindings/voidraft/internal/models/models'; import {Document} from '@/../bindings/voidraft/internal/models/models';
import {useLogStore} from './logStore'; import {useErrorHandler} from '@/utils/errorHandler';
import {useI18n} from 'vue-i18n';
export const useDocumentStore = defineStore('document', () => { export const useDocumentStore = defineStore('document', () => {
const logStore = useLogStore(); const {safeCall} = useErrorHandler();
const { t } = useI18n();
// 状态 // 状态
const activeDocument = ref<Document | null>(null); const activeDocument = ref<Document | null>(null);
@@ -16,86 +14,92 @@ export const useDocumentStore = defineStore('document', () => {
const lastSaved = ref<Date | null>(null); const lastSaved = ref<Date | null>(null);
// 计算属性 // 计算属性
const documentContent = computed(() => activeDocument.value?.content || ''); const documentContent = computed(() => activeDocument.value?.content ?? '');
const documentTitle = computed(() => activeDocument.value?.meta?.title || ''); const documentTitle = computed(() => activeDocument.value?.meta?.title ?? '');
const hasActiveDocument = computed(() => !!activeDocument.value); const hasActiveDocument = computed(() => !!activeDocument.value);
const isSaveInProgress = computed(() => isSaving.value); const isSaveInProgress = computed(() => isSaving.value);
const lastSavedTime = computed(() => lastSaved.value); const lastSavedTime = computed(() => lastSaved.value);
// 加载文档 // 状态管理包装器
async function loadDocument() { const withStateGuard = async <T>(
if (isLoading.value) return; operation: () => Promise<T>,
stateRef: typeof isLoading | typeof isSaving,
errorMessageKey: string,
successMessageKey?: string
): Promise<T | null> => {
if (stateRef.value) return null;
isLoading.value = true; stateRef.value = true;
try { try {
activeDocument.value = await DocumentService.GetActiveDocument(); return await safeCall(operation, errorMessageKey, successMessageKey);
logStore.info(t('document.loadSuccess'));
} catch (err) {
console.error('Failed to load document:', err);
logStore.error(t('document.loadFailed'));
activeDocument.value = null;
} finally { } finally {
isLoading.value = false; stateRef.value = false;
} }
} };
// 加载文档
const loadDocument = () => withStateGuard(
async () => {
const doc = await DocumentService.GetActiveDocument();
activeDocument.value = doc;
return doc;
},
isLoading,
'document.loadFailed',
'document.loadSuccess'
);
// 保存文档 // 保存文档
async function saveDocument(content: string): Promise<boolean> { const saveDocument = async (content: string): Promise<boolean> => {
if (isSaving.value) return false; const result = await withStateGuard(
async () => {
await DocumentService.UpdateActiveDocumentContent(content);
lastSaved.value = new Date();
// 使用可选链更新本地副本
if (activeDocument.value) {
activeDocument.value.content = content;
activeDocument.value.meta.lastUpdated = lastSaved.value;
}
return true;
},
isSaving,
'document.saveFailed',
'document.saveSuccess'
);
isSaving.value = true; return result ?? false;
try { };
await DocumentService.UpdateActiveDocumentContent(content);
lastSaved.value = new Date();
// 更新本地副本
if (activeDocument.value) {
activeDocument.value.content = content;
activeDocument.value.meta.lastUpdated = lastSaved.value;
}
logStore.info(t('document.saveSuccess'));
return true;
} catch (err) {
console.error('Failed to save document:', err);
logStore.error(t('document.saveFailed'));
return false;
} finally {
isSaving.value = false;
}
}
// 强制保存文档到磁盘 // 强制保存文档到磁盘
async function forceSaveDocument(): Promise<boolean> { const forceSaveDocument = async (): Promise<boolean> => {
if (isSaving.value) return false; const result = await withStateGuard(
async () => {
// 直接调用强制保存API
await DocumentService.ForceSave();
lastSaved.value = new Date();
// 使用可选链更新时间戳
if (activeDocument.value) {
activeDocument.value.meta.lastUpdated = lastSaved.value;
}
return true;
},
isSaving,
'document.saveFailed',
'document.manualSaveSuccess'
);
isSaving.value = true; return result ?? false;
try { };
// 直接调用强制保存API
await DocumentService.ForceSave();
lastSaved.value = new Date();
// 更新时间戳
if (activeDocument.value) {
activeDocument.value.meta.lastUpdated = lastSaved.value;
}
logStore.info(t('document.manualSaveSuccess'));
return true;
} catch (err) {
console.error('Failed to force save document:', err);
logStore.error(t('document.saveFailed'));
return false;
} finally {
isSaving.value = false;
}
}
// 初始化 // 初始化
async function initialize() { const initialize = async () => {
await loadDocument(); await loadDocument();
} };
return { return {
// 状态 // 状态

View File

@@ -0,0 +1,97 @@
import { useLogStore } from '@/stores/logStore';
import { useI18n } from 'vue-i18n';
/**
* 创建组合式函数,用于在组件中使用错误处理
*/
export function useErrorHandler() {
const logStore = useLogStore();
const { t } = useI18n();
const handleError = (error: unknown, messageKey: string) => {
logStore.error(t(messageKey));
};
const safeCall = async <T>(
operation: () => Promise<T>,
errorMessageKey: string,
successMessageKey?: string
): Promise<T | null> => {
try {
const result = await operation();
if (successMessageKey) {
logStore.info(t(successMessageKey));
}
return result;
} catch (error) {
logStore.error(t(errorMessageKey));
return null;
}
};
/**
* 静默处理错误,不显示用户提示
*/
const silentCall = async <T>(
operation: () => Promise<T>
): Promise<T | null> => {
try {
return await operation();
} catch (error) {
// 静默忽略错误
return null;
}
};
/**
* 创建带错误处理的数值调整器
*/
const createSafeAdjuster = (
adjustFn: () => Promise<void>,
errorMessageKey: string
) => () => safeCall(adjustFn, errorMessageKey);
return {
handleError,
safeCall,
silentCall,
createSafeAdjuster
};
}
/**
* 错误处理工具函数集合不依赖Vue上下文
*/
export const ErrorUtils = {
/**
* 静默处理错误
*/
silent: async <T>(operation: () => Promise<T>): Promise<T | null> => {
try {
return await operation();
} catch (error) {
return null;
}
},
/**
* 带重试的错误处理
*/
withRetry: async <T>(
operation: () => Promise<T>,
maxRetries: number = 3,
delay: number = 1000
): Promise<T | null> => {
for (let i = 0; i < maxRetries; i++) {
try {
return await operation();
} catch (error) {
if (i === maxRetries - 1) {
return null;
}
await new Promise(resolve => setTimeout(resolve, delay));
}
}
return null;
}
};

View File

@@ -49,7 +49,7 @@ export function createAutoSavePlugin(options: AutoSaveOptions = {}) {
await DocumentService.UpdateActiveDocumentContent(content); await DocumentService.UpdateActiveDocumentContent(content);
onSave(true); onSave(true);
} catch (err) { } catch (err) {
console.error('Failed to update document content:', err); // 静默处理错误,不在控制台打印
onSave(false); onSave(false);
} finally { } finally {
this.isSaving = false; this.isSaving = false;
@@ -69,11 +69,11 @@ export function createAutoSavePlugin(options: AutoSaveOptions = {}) {
// 标记插件不再活跃 // 标记插件不再活跃
this.isActive = false; this.isActive = false;
// 直接发送最终内容 // 静默发送最终内容,忽略错误
const content = this.view.state.doc.toString(); const content = this.view.state.doc.toString();
DocumentService.UpdateActiveDocumentContent(content) DocumentService.UpdateActiveDocumentContent(content).catch(() => {
.then(() => console.log('Successfully sent final content on destroy')) // 静默忽略销毁时的错误
.catch(err => console.error('Failed to send content on destroy:', err)); });
} }
} }
); );

View File

@@ -1,5 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { useConfigStore } from '@/stores/configStore'; import { useConfigStore } from '@/stores/configStore';
import { useErrorHandler } from '@/utils/errorHandler';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { ref } from 'vue'; import { ref } from 'vue';
import SettingSection from '../components/SettingSection.vue'; import SettingSection from '../components/SettingSection.vue';
@@ -8,6 +9,7 @@ import { LanguageType } from '../../../../bindings/voidraft/internal/models/mode
const { t } = useI18n(); const { t } = useI18n();
const configStore = useConfigStore(); const configStore = useConfigStore();
const { safeCall } = useErrorHandler();
// 语言选项 // 语言选项
const languageOptions = [ const languageOptions = [
@@ -20,12 +22,10 @@ const updateLanguage = async (event: Event) => {
const select = event.target as HTMLSelectElement; const select = event.target as HTMLSelectElement;
const selectedLanguage = select.value as LanguageType; const selectedLanguage = select.value as LanguageType;
try { await safeCall(
// 使用 configStore 的语言设置方法 () => configStore.setLanguage(selectedLanguage),
await configStore.setLanguage(selectedLanguage); 'config.languageChangeFailed'
} catch (error) { );
console.error('Failed to update language:', error);
}
}; };
// 主题选择(未实际实现,仅界面展示) // 主题选择(未实际实现,仅界面展示)

View File

@@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { useConfigStore } from '@/stores/configStore'; import { useConfigStore } from '@/stores/configStore';
import { FONT_OPTIONS } from '@/stores/configStore'; import { FONT_OPTIONS } from '@/stores/configStore';
import { useErrorHandler } from '@/utils/errorHandler';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { ref, computed, onMounted } from 'vue'; import { ref, computed, onMounted } from 'vue';
import SettingSection from '../components/SettingSection.vue'; import SettingSection from '../components/SettingSection.vue';
@@ -10,11 +11,12 @@ import { TabType } from '../../../../bindings/voidraft/internal/models/models';
const { t } = useI18n(); const { t } = useI18n();
const configStore = useConfigStore(); const configStore = useConfigStore();
const { safeCall } = useErrorHandler();
// 确保配置已加载 // 确保配置已加载
onMounted(async () => { onMounted(async () => {
if (!configStore.configLoaded) { if (!configStore.configLoaded) {
await configStore.loadConfig(); await configStore.initConfig();
} }
}); });
@@ -27,11 +29,10 @@ const handleFontFamilyChange = async (event: Event) => {
const target = event.target as HTMLSelectElement; const target = event.target as HTMLSelectElement;
const fontFamily = target.value; const fontFamily = target.value;
if (fontFamily) { if (fontFamily) {
try { await safeCall(
await configStore.setFontFamily(fontFamily); () => configStore.setFontFamily(fontFamily),
} catch (error) { 'config.fontFamilyUpdateFailed'
console.error('Failed to set font family:', error); );
}
} }
}; };
@@ -51,47 +52,42 @@ const fontWeightOptions = [
// 字体粗细选择 // 字体粗细选择
const handleFontWeightChange = async (event: Event) => { const handleFontWeightChange = async (event: Event) => {
const target = event.target as HTMLSelectElement; const target = event.target as HTMLSelectElement;
try { await safeCall(
await configStore.setFontWeight(target.value); () => configStore.setFontWeight(target.value),
} catch (error) { 'config.fontWeightUpdateFailed'
console.error('Failed to set font weight:', error); );
}
}; };
// 行高控制 // 行高控制
const increaseLineHeight = async () => { const increaseLineHeight = async () => {
const newLineHeight = Math.min(3.0, configStore.config.lineHeight + 0.1); const newLineHeight = Math.min(3.0, configStore.config.lineHeight + 0.1);
try { await safeCall(
await configStore.setLineHeight(Math.round(newLineHeight * 10) / 10); () => configStore.setLineHeight(Math.round(newLineHeight * 10) / 10),
} catch (error) { 'config.lineHeightIncreaseFailed'
console.error('Failed to increase line height:', error); );
}
}; };
const decreaseLineHeight = async () => { const decreaseLineHeight = async () => {
const newLineHeight = Math.max(1.0, configStore.config.lineHeight - 0.1); const newLineHeight = Math.max(1.0, configStore.config.lineHeight - 0.1);
try { await safeCall(
await configStore.setLineHeight(Math.round(newLineHeight * 10) / 10); () => configStore.setLineHeight(Math.round(newLineHeight * 10) / 10),
} catch (error) { 'config.lineHeightDecreaseFailed'
console.error('Failed to decrease line height:', error); );
}
}; };
// 字体大小控制 // 字体大小控制
const increaseFontSize = async () => { const increaseFontSize = async () => {
try { await safeCall(
await configStore.increaseFontSize(); () => configStore.increaseFontSize(),
} catch (error) { 'config.fontSizeIncreaseFailed'
console.error('Failed to increase font size:', error); );
}
}; };
const decreaseFontSize = async () => { const decreaseFontSize = async () => {
try { await safeCall(
await configStore.decreaseFontSize(); () => configStore.decreaseFontSize(),
} catch (error) { 'config.fontSizeDecreaseFailed'
console.error('Failed to decrease font size:', error); );
}
}; };
// Tab类型切换 // Tab类型切换
@@ -103,36 +99,32 @@ const tabTypeText = computed(() => {
// Tab大小增减 // Tab大小增减
const increaseTabSize = async () => { const increaseTabSize = async () => {
try { await safeCall(
await configStore.increaseTabSize(); () => configStore.increaseTabSize(),
} catch (error) { 'config.tabSizeIncreaseFailed'
console.error('Failed to increase tab size:', error); );
}
}; };
const decreaseTabSize = async () => { const decreaseTabSize = async () => {
try { await safeCall(
await configStore.decreaseTabSize(); () => configStore.decreaseTabSize(),
} catch (error) { 'config.tabSizeDecreaseFailed'
console.error('Failed to decrease tab size:', error); );
}
}; };
// Tab相关操作 // Tab相关操作
const handleToggleTabIndent = async () => { const handleToggleTabIndent = async () => {
try { await safeCall(
await configStore.toggleTabIndent(); () => configStore.toggleTabIndent(),
} catch (error) { 'config.tabIndentToggleFailed'
console.error('Failed to toggle tab indent:', error); );
}
}; };
const handleToggleTabType = async () => { const handleToggleTabType = async () => {
try { await safeCall(
await configStore.toggleTabType(); () => configStore.toggleTabType(),
} catch (error) { 'config.tabTypeToggleFailed'
console.error('Failed to toggle tab type:', error); );
}
}; };
</script> </script>