- 新增 useToast composable: 消除 AiChat/Settings/Projects 4处 toast 重复 统一默认3000ms(Projects原4000ms为操作类提示保留参数覆盖) - 新增 utils/json.ts parseJsonArray: 消除 parseStack/parseTags/ModuleNode 3处JSON字符串数组解析重复 - 新增 utils/html.ts escapeHtml: 消除 useMarkdown/FilePreview 2处重复 - ProjectDetail score-bar 内联三元改用 scoreTier(消除最后一处阈值硬编码) - ConversationSidebar 删除 formatTime 透传包装(直接用 formatRelative) - 清理死代码: parseTs/stringifyError/ErrorSink/_Unused 改私有或删除 wrapNakedDiff 改私有(无外部 import) - ModuleNode shortPath 改名 truncatedPath(与 useToolCard.shortPath 语义不同)
81 lines
2.3 KiB
TypeScript
81 lines
2.3 KiB
TypeScript
/**
|
||
* Store action 统一错误处理工具(任务 #7 DRY 抽离)。
|
||
*
|
||
* 之前 4 个 store(knowledge/ideas/projects/tasks)共 38 处重复:
|
||
* try { ... } catch (e: any) { state.error = e?.toString() ?? t('xxx') }
|
||
*
|
||
* 本工具:
|
||
* - 用 `catch (e: unknown)` 替代 `any`(类型安全)
|
||
* - 失败时写 state.error,返回 undefined(调用方判断)
|
||
* - 成功时返回 fn 结果
|
||
*
|
||
* 注:带 seq 竞态守卫的场景调用方需在 fn 内部判断,不在本工具职责范围。
|
||
*/
|
||
|
||
// (Ref import 已移除,_Unused 类型已删除)
|
||
|
||
/** 带 error 字段的 store state 形状(仅约束 error,其他字段任意)。仅本模块内部使用。 */
|
||
interface ErrorSink {
|
||
error: string | null
|
||
}
|
||
|
||
/**
|
||
* 执行异步 action,失败时写 state.error + 返回 undefined。
|
||
*
|
||
* @param state 带 error 字段的响应式 state
|
||
* @param i18nKey 失败时的 i18n 文案 key(兜底)
|
||
* @param fn 异步 action
|
||
* @returns 成功返 fn 结果;失败返 undefined
|
||
*/
|
||
export async function runWithCatch<T>(
|
||
state: ErrorSink,
|
||
i18nKey: string,
|
||
fn: () => Promise<T>,
|
||
): Promise<T | undefined> {
|
||
try {
|
||
return await fn()
|
||
} catch (e: unknown) {
|
||
state.error = stringifyError(e) ?? i18nKey
|
||
return undefined
|
||
}
|
||
}
|
||
|
||
// stringifyError 仅本模块内部使用(runWithCatch/runWithCatchGuarded 调用),不导出。
|
||
function stringifyError(e: unknown): string | undefined {
|
||
if (typeof e === 'string') return e
|
||
if (e instanceof Error) return e.message || e.toString()
|
||
if (e && typeof e === 'object' && 'toString' in e) {
|
||
try {
|
||
return String((e as { toString(): string }).toString())
|
||
} catch {
|
||
return undefined
|
||
}
|
||
}
|
||
return undefined
|
||
}
|
||
|
||
/**
|
||
* Variant:带前置守卫的错误捕获。
|
||
*
|
||
* 用于「旧请求的失败不污染当前视图」场景(如 seq 守卫)。
|
||
*
|
||
* @param guard 返回 true 时才写 state.error;false 时静默(旧响应丢弃)
|
||
*/
|
||
export async function runWithCatchGuarded<T>(
|
||
state: ErrorSink,
|
||
i18nKey: string,
|
||
guard: () => boolean,
|
||
fn: () => Promise<T>,
|
||
): Promise<T | undefined> {
|
||
try {
|
||
return await fn()
|
||
} catch (e: unknown) {
|
||
if (guard()) {
|
||
state.error = stringifyError(e) ?? i18nKey
|
||
}
|
||
return undefined
|
||
}
|
||
}
|
||
|
||
// _Unused 类型从未被外部 import,已删除(同时移除上方 Ref import)。
|