F4:新 lib/errors/messages.ts —— friendlyError(code, fallback) 把错误码映射为 中文文案 + 可选「去设置提供商」动作;覆盖后端 ErrorCode + 前端 NETWORK/ STREAM_FAILED;未知码回退原 message。写作页与审稿页错误块改用之,不再暴露 raw error code。+4 vitest。 F3:写作页工具条字数计数器流式时显示「生成中… N 字」+ 朱砂脉冲 (motion-safe:animate-pulse,尊重 prefers-reduced-motion),停后回「字数 N」。 纯 text.length,无后端改动。 前端门禁绿:lint/typecheck/vitest 171/build。无契约变更。
47 lines
1.7 KiB
TypeScript
47 lines
1.7 KiB
TypeScript
// 错误码 → 友好中文文案(+ 可选跳转动作)。
|
||
// 对齐后端 ErrorCode(ARCH §7.1)+ 前端流式/网络码(useDraftStream)。
|
||
// 目的:UI 不直接暴露 error code,给作者可读的下一步。
|
||
|
||
export interface FriendlyError {
|
||
text: string;
|
||
// 可选的「下一步」链接(如未连模型 → 去设置)。
|
||
actionHref?: string;
|
||
actionLabel?: string;
|
||
}
|
||
|
||
const PROVIDER_ACTION = {
|
||
actionHref: "/settings/providers",
|
||
actionLabel: "去设置提供商",
|
||
} as const;
|
||
|
||
const MESSAGES: Record<string, FriendlyError> = {
|
||
// 后端 ErrorCode
|
||
LLM_UNAVAILABLE: {
|
||
text: "未连接可用模型,去设置里连一个 provider。",
|
||
...PROVIDER_ACTION,
|
||
},
|
||
RATE_LIMITED: { text: "请求过于频繁,稍后再试。" },
|
||
CONFLICT_UNRESOLVED: { text: "仍有未裁决的冲突,请先逐条裁决再验收。" },
|
||
NOT_FOUND: { text: "资源不存在或已被删除。" },
|
||
VALIDATION: { text: "提交内容有误,请检查后重试。" },
|
||
INTERNAL: { text: "服务出了点问题,请稍后重试。" },
|
||
// 前端流式/网络码
|
||
NETWORK: { text: "网络中断,请检查连接后重试。" },
|
||
STREAM_FAILED: { text: "写章中断,请重试。" },
|
||
};
|
||
|
||
const DEFAULT_TEXT = "出错了,请稍后重试。";
|
||
|
||
// 把 (code, 原始 message) 映射为友好文案。
|
||
// 已知码 → 预设文案 + 动作;未知码 → 回退到原始 message(非空)或通用兜底。
|
||
export function friendlyError(
|
||
code: string | undefined,
|
||
fallbackMessage?: string,
|
||
): FriendlyError {
|
||
if (code && MESSAGES[code]) {
|
||
return MESSAGES[code];
|
||
}
|
||
const fallback = fallbackMessage?.trim();
|
||
return { text: fallback && fallback.length > 0 ? fallback : DEFAULT_TEXT };
|
||
}
|