🎨 Optimized error handling
This commit is contained in:
@@ -4,8 +4,8 @@ import {
|
||||
ConfigService
|
||||
} from '@/../bindings/voidraft/internal/services';
|
||||
import {EditorConfig, TabType, LanguageType} from '@/../bindings/voidraft/internal/models/models';
|
||||
import {useLogStore} from './logStore';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useErrorHandler } from '@/utils/errorHandler';
|
||||
import { ConfigUtils } from '@/utils/configUtils';
|
||||
|
||||
// 国际化相关导入
|
||||
@@ -21,7 +21,7 @@ export const SUPPORTED_LOCALES = [
|
||||
code: 'en-US' as SupportedLocaleType,
|
||||
name: 'English'
|
||||
}
|
||||
];
|
||||
] as const;
|
||||
|
||||
// 配置键映射和限制的类型定义
|
||||
type ConfigKeyMap = {
|
||||
@@ -93,8 +93,8 @@ const DEFAULT_CONFIG: EditorConfig = {
|
||||
};
|
||||
|
||||
export const useConfigStore = defineStore('config', () => {
|
||||
const logStore = useLogStore();
|
||||
const { t, locale } = useI18n();
|
||||
const { locale } = useI18n();
|
||||
const { safeCall } = useErrorHandler();
|
||||
|
||||
// 响应式状态
|
||||
const state = reactive({
|
||||
@@ -105,57 +105,32 @@ export const useConfigStore = defineStore('config', () => {
|
||||
|
||||
// 计算属性 - 使用工厂函数简化
|
||||
const createLimitComputed = (key: NumberConfigKey) => computed(() => CONFIG_LIMITS[key]);
|
||||
const limits = {
|
||||
fontSize: createLimitComputed('fontSize'),
|
||||
tabSize: createLimitComputed('tabSize'),
|
||||
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 limits = Object.fromEntries(
|
||||
(['fontSize', 'tabSize', 'lineHeight'] as const).map(key => [key, createLimitComputed(key)])
|
||||
) as Record<NumberConfigKey, ReturnType<typeof createLimitComputed>>;
|
||||
|
||||
// 通用配置更新方法
|
||||
const updateConfig = withErrorHandling(
|
||||
async <K extends keyof EditorConfig>(key: K, value: EditorConfig[K]): Promise<void> => {
|
||||
// 确保配置已加载
|
||||
if (!state.configLoaded && !state.isLoading) {
|
||||
await initConfig();
|
||||
}
|
||||
|
||||
const backendKey = CONFIG_KEY_MAP[key];
|
||||
if (!backendKey) {
|
||||
throw new Error(`No backend key mapping found for ${key.toString()}`);
|
||||
}
|
||||
|
||||
await ConfigService.Set(backendKey, value);
|
||||
state.config[key] = value;
|
||||
},
|
||||
'config.saveFailed',
|
||||
'config.saveSuccess'
|
||||
);
|
||||
const updateConfig = async <K extends keyof EditorConfig>(key: K, value: EditorConfig[K]): Promise<void> => {
|
||||
// 确保配置已加载
|
||||
if (!state.configLoaded && !state.isLoading) {
|
||||
await initConfig();
|
||||
}
|
||||
|
||||
const backendKey = CONFIG_KEY_MAP[key];
|
||||
if (!backendKey) {
|
||||
throw new Error(`No backend key mapping found for ${key.toString()}`);
|
||||
}
|
||||
|
||||
await ConfigService.Set(backendKey, value);
|
||||
state.config[key] = value;
|
||||
};
|
||||
|
||||
// 加载配置
|
||||
const initConfig = withErrorHandling(
|
||||
async (): Promise<void> => {
|
||||
if (state.isLoading) return;
|
||||
|
||||
state.isLoading = true;
|
||||
const initConfig = async (): Promise<void> => {
|
||||
if (state.isLoading) return;
|
||||
|
||||
state.isLoading = true;
|
||||
try {
|
||||
const appConfig = await ConfigService.GetConfig();
|
||||
|
||||
if (appConfig?.editor) {
|
||||
@@ -163,62 +138,60 @@ export const useConfigStore = defineStore('config', () => {
|
||||
}
|
||||
|
||||
state.configLoaded = true;
|
||||
} finally {
|
||||
state.isLoading = false;
|
||||
},
|
||||
'config.loadFailed',
|
||||
'config.loadSuccess'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// 数值调整工厂函数
|
||||
const createNumberAdjuster = (key: NumberConfigKey) => {
|
||||
// 通用数值调整器工厂
|
||||
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: () => updateConfig(key, ConfigUtils.clamp(state.config[key] + 1, limit.min, limit.max)),
|
||||
decrease: () => updateConfig(key, ConfigUtils.clamp(state.config[key] - 1, limit.min, limit.max)),
|
||||
set: (value: number) => updateConfig(key, ConfigUtils.clamp(value, limit.min, limit.max)),
|
||||
reset: () => updateConfig(key, limit.default)
|
||||
increase: () => safeCall(() => updateConfig(key, clamp(state.config[key] + 1)), 'config.saveFailed', 'config.saveSuccess'),
|
||||
decrease: () => safeCall(() => updateConfig(key, clamp(state.config[key] - 1)), 'config.saveFailed', 'config.saveSuccess'),
|
||||
set: (value: number) => safeCall(() => updateConfig(key, clamp(value)), 'config.saveFailed', 'config.saveSuccess'),
|
||||
reset: () => safeCall(() => updateConfig(key, limit.default), 'config.saveFailed', 'config.saveSuccess')
|
||||
};
|
||||
};
|
||||
|
||||
// 布尔值切换工厂函数
|
||||
const createToggler = (key: BooleanConfigKey) =>
|
||||
() => updateConfig(key, !state.config[key]);
|
||||
// 通用布尔值切换器
|
||||
const createToggler = <T extends BooleanConfigKey>(key: T) =>
|
||||
() => safeCall(() => updateConfig(key, !state.config[key]), 'config.saveFailed', 'config.saveSuccess');
|
||||
|
||||
// 枚举值切换工厂函数
|
||||
// 枚举值切换器
|
||||
const createEnumToggler = <T extends TabType>(key: 'tabType', values: readonly T[]) =>
|
||||
() => {
|
||||
const currentIndex = values.indexOf(state.config[key] as T);
|
||||
const nextIndex = (currentIndex + 1) % values.length;
|
||||
return updateConfig(key, values[nextIndex]);
|
||||
return safeCall(() => updateConfig(key, values[nextIndex]), 'config.saveFailed', 'config.saveSuccess');
|
||||
};
|
||||
|
||||
// 重置配置
|
||||
const resetConfig = withErrorHandling(
|
||||
async (): Promise<void> => {
|
||||
if (state.isLoading) return;
|
||||
|
||||
state.isLoading = true;
|
||||
await ConfigService.ResetConfig();
|
||||
await initConfig();
|
||||
const resetConfig = async (): Promise<void> => {
|
||||
if (state.isLoading) return;
|
||||
|
||||
state.isLoading = true;
|
||||
try {
|
||||
await safeCall(() => ConfigService.ResetConfig(), 'config.resetFailed', 'config.resetSuccess');
|
||||
await safeCall(() => initConfig(), 'config.loadFailed', 'config.loadSuccess');
|
||||
} finally {
|
||||
state.isLoading = false;
|
||||
},
|
||||
'config.resetFailed',
|
||||
'config.resetSuccess'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// 语言设置方法
|
||||
const setLanguage = withErrorHandling(
|
||||
async (language: LanguageType): Promise<void> => {
|
||||
const setLanguage = async (language: LanguageType): Promise<void> => {
|
||||
await safeCall(async () => {
|
||||
await ConfigService.Set(CONFIG_KEY_MAP.language, language);
|
||||
state.config.language = language;
|
||||
|
||||
// 同步更新前端语言
|
||||
const frontendLocale = ConfigUtils.backendLanguageToFrontend(language);
|
||||
locale.value = frontendLocale as any;
|
||||
},
|
||||
'config.languageChangeFailed',
|
||||
'config.languageChanged'
|
||||
);
|
||||
}, 'config.languageChangeFailed', 'config.languageChanged');
|
||||
};
|
||||
|
||||
// 通过前端语言代码设置语言
|
||||
const setLocale = async (localeCode: SupportedLocaleType): Promise<void> => {
|
||||
@@ -243,19 +216,25 @@ export const useConfigStore = defineStore('config', () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 创建数值调整器
|
||||
const fontSize = createNumberAdjuster('fontSize');
|
||||
const tabSize = createNumberAdjuster('tabSize');
|
||||
const lineHeight = createNumberAdjuster('lineHeight');
|
||||
// 创建数值调整器实例
|
||||
const adjusters = {
|
||||
fontSize: createAdjuster('fontSize'),
|
||||
tabSize: createAdjuster('tabSize'),
|
||||
lineHeight: createAdjuster('lineHeight')
|
||||
};
|
||||
|
||||
// 创建切换器
|
||||
const toggleTabIndent = createToggler('enableTabIndent');
|
||||
const toggleAlwaysOnTop = createToggler('alwaysOnTop');
|
||||
const toggleTabType = createEnumToggler('tabType', CONFIG_LIMITS.tabType.values);
|
||||
// 创建切换器实例
|
||||
const togglers = {
|
||||
tabIndent: createToggler('enableTabIndent'),
|
||||
alwaysOnTop: createToggler('alwaysOnTop'),
|
||||
tabType: createEnumToggler('tabType', CONFIG_LIMITS.tabType.values)
|
||||
};
|
||||
|
||||
// 字符串配置设置器
|
||||
const setFontFamily = (value: string) => updateConfig('fontFamily', value);
|
||||
const setFontWeight = (value: string) => updateConfig('fontWeight', value);
|
||||
const setters = {
|
||||
fontFamily: (value: string) => safeCall(() => updateConfig('fontFamily', value), 'config.saveFailed', 'config.saveSuccess'),
|
||||
fontWeight: (value: string) => safeCall(() => updateConfig('fontWeight', value), 'config.saveFailed', 'config.saveSuccess')
|
||||
};
|
||||
|
||||
return {
|
||||
// 状态
|
||||
@@ -267,9 +246,9 @@ export const useConfigStore = defineStore('config', () => {
|
||||
...limits,
|
||||
|
||||
// 核心方法
|
||||
initConfig,
|
||||
initConfig: () => safeCall(() => initConfig(), 'config.loadFailed', 'config.loadSuccess'),
|
||||
resetConfig,
|
||||
updateConfig,
|
||||
updateConfig: (key: keyof EditorConfig, value: any) => safeCall(() => updateConfig(key, value), 'config.saveFailed', 'config.saveSuccess'),
|
||||
|
||||
// 语言相关方法
|
||||
setLanguage,
|
||||
@@ -277,29 +256,29 @@ export const useConfigStore = defineStore('config', () => {
|
||||
initializeLanguage,
|
||||
|
||||
// 字体大小操作
|
||||
...fontSize,
|
||||
increaseFontSize: fontSize.increase,
|
||||
decreaseFontSize: fontSize.decrease,
|
||||
resetFontSize: fontSize.reset,
|
||||
setFontSize: fontSize.set,
|
||||
...adjusters.fontSize,
|
||||
increaseFontSize: adjusters.fontSize.increase,
|
||||
decreaseFontSize: adjusters.fontSize.decrease,
|
||||
resetFontSize: adjusters.fontSize.reset,
|
||||
setFontSize: adjusters.fontSize.set,
|
||||
|
||||
// Tab操作
|
||||
toggleTabIndent,
|
||||
...tabSize,
|
||||
increaseTabSize: tabSize.increase,
|
||||
decreaseTabSize: tabSize.decrease,
|
||||
setTabSize: tabSize.set,
|
||||
toggleTabType,
|
||||
toggleTabIndent: togglers.tabIndent,
|
||||
...adjusters.tabSize,
|
||||
increaseTabSize: adjusters.tabSize.increase,
|
||||
decreaseTabSize: adjusters.tabSize.decrease,
|
||||
setTabSize: adjusters.tabSize.set,
|
||||
toggleTabType: togglers.tabType,
|
||||
|
||||
// 行高操作
|
||||
setLineHeight: lineHeight.set,
|
||||
setLineHeight: adjusters.lineHeight.set,
|
||||
|
||||
// 窗口操作
|
||||
toggleAlwaysOnTop,
|
||||
toggleAlwaysOnTop: togglers.alwaysOnTop,
|
||||
|
||||
// 字体操作
|
||||
setFontFamily,
|
||||
setFontWeight,
|
||||
setFontFamily: setters.fontFamily,
|
||||
setFontWeight: setters.fontWeight,
|
||||
};
|
||||
},{
|
||||
persist: true,
|
||||
|
@@ -2,12 +2,10 @@ import {defineStore} from 'pinia';
|
||||
import {computed, ref} from 'vue';
|
||||
import {DocumentService} from '@/../bindings/voidraft/internal/services';
|
||||
import {Document} from '@/../bindings/voidraft/internal/models/models';
|
||||
import {useLogStore} from './logStore';
|
||||
import {useI18n} from 'vue-i18n';
|
||||
import {useErrorHandler} from '@/utils/errorHandler';
|
||||
|
||||
export const useDocumentStore = defineStore('document', () => {
|
||||
const logStore = useLogStore();
|
||||
const { t } = useI18n();
|
||||
const {safeCall} = useErrorHandler();
|
||||
|
||||
// 状态
|
||||
const activeDocument = ref<Document | null>(null);
|
||||
@@ -16,86 +14,92 @@ export const useDocumentStore = defineStore('document', () => {
|
||||
const lastSaved = ref<Date | null>(null);
|
||||
|
||||
// 计算属性
|
||||
const documentContent = computed(() => activeDocument.value?.content || '');
|
||||
const documentTitle = computed(() => activeDocument.value?.meta?.title || '');
|
||||
const documentContent = computed(() => activeDocument.value?.content ?? '');
|
||||
const documentTitle = computed(() => activeDocument.value?.meta?.title ?? '');
|
||||
const hasActiveDocument = computed(() => !!activeDocument.value);
|
||||
const isSaveInProgress = computed(() => isSaving.value);
|
||||
const lastSavedTime = computed(() => lastSaved.value);
|
||||
|
||||
// 加载文档
|
||||
async function loadDocument() {
|
||||
if (isLoading.value) return;
|
||||
// 状态管理包装器
|
||||
const withStateGuard = async <T>(
|
||||
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 {
|
||||
activeDocument.value = await DocumentService.GetActiveDocument();
|
||||
logStore.info(t('document.loadSuccess'));
|
||||
} catch (err) {
|
||||
console.error('Failed to load document:', err);
|
||||
logStore.error(t('document.loadFailed'));
|
||||
activeDocument.value = null;
|
||||
return await safeCall(operation, errorMessageKey, successMessageKey);
|
||||
} 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> {
|
||||
if (isSaving.value) return false;
|
||||
const saveDocument = async (content: string): Promise<boolean> => {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
return result ?? false;
|
||||
};
|
||||
|
||||
// 强制保存文档到磁盘
|
||||
async function forceSaveDocument(): Promise<boolean> {
|
||||
if (isSaving.value) return false;
|
||||
const forceSaveDocument = async (): Promise<boolean> => {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
return result ?? false;
|
||||
};
|
||||
|
||||
// 初始化
|
||||
async function initialize() {
|
||||
const initialize = async () => {
|
||||
await loadDocument();
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
// 状态
|
||||
|
Reference in New Issue
Block a user