fix:批量修复了一些问题

This commit is contained in:
shangfangjian
2026-09-19 22:51:35 +08:00
parent 835d7217d0
commit 8e6ad5394e
44 changed files with 4921 additions and 395 deletions
+56 -7
View File
@@ -8,6 +8,8 @@
import { ReactNode } from 'react'
import { ApiError } from './api'
// ── 卡片 ────────────────────────────────────────────────────────
export function Card({
@@ -271,7 +273,7 @@ export function Alert({
message,
onClose,
}: {
kind: 'error' | 'ok' | 'info'
kind: 'error' | 'ok' | 'info' | 'warning'
title: string
message?: string
onClose?: () => void
@@ -279,17 +281,19 @@ export function Alert({
const style =
kind === 'error'
? 'border-press bg-press-wash'
: kind === 'ok'
? 'border-ink-900 bg-paper-100'
: 'border-ink-300 bg-paper-50'
const titleCls = kind === 'error' ? 'text-press' : 'text-ink-900'
: kind === 'warning'
? 'border-press bg-press-wash/60'
: kind === 'ok'
? 'border-ink-900 bg-paper-100'
: 'border-ink-300 bg-paper-50'
const titleCls = kind === 'error' || kind === 'warning' ? 'text-press' : 'text-ink-900'
return (
<div className={`flex items-start justify-between gap-3 border px-4 py-3 ${style}`}>
<div>
<p className={`flex items-center gap-1.5 text-sm font-medium ${titleCls}`}>
<span
className={`inline-block h-1.5 w-1.5 ${kind === 'error' ? 'bg-press' : 'bg-ink-900'}`}
className={`inline-block h-1.5 w-1.5 ${kind === 'error' || kind === 'warning' ? 'bg-press' : 'bg-ink-900'}`}
aria-hidden="true"
/>
{title}
@@ -315,7 +319,52 @@ export function Alert({
)
}
// ── 加载指示:同前台 Spinner ────────────────────────────────────
// ── 错误横幅:标准化错误标题/文案/建议动作 ──────────────────────
/** 根据错误对象生成标准化的错误标题、文案与建议动作。 */
export function describeError(err: unknown): { title: string; detail: string; kind: 'error' | 'warning' } {
if (err instanceof ApiError) {
const status = err.status
const apiDetail = typeof err.data === 'object' && err.data && 'detail' in (err.data as object)
? String((err.data as { detail: unknown }).detail)
: ''
const msg = apiDetail || err.message
switch (status) {
case 401:
return { title: '登录已过期', detail: '请重新登录后继续操作。', kind: 'warning' }
case 403:
return { title: '无权访问', detail: msg || '当前账号没有执行该操作的权限。', kind: 'error' }
case 429:
return { title: '请求过于频繁', detail: msg || '每分钟最多 10 次预测,请稍后再试。', kind: 'warning' }
case 502:
return { title: '上游 LLM 不可用', detail: msg || 'LLM 服务暂时不可用,请稍后重试或切换到更便宜的模型。', kind: 'error' }
case 503:
return { title: '服务未就绪', detail: msg || '服务器鉴权未配置,请联系管理员。', kind: 'error' }
case 0:
return { title: '网络错误或请求超时', detail: '请检查网络连接后重试。', kind: 'warning' }
}
if (status >= 500) {
return { title: '服务器错误', detail: msg || `HTTP ${status},请稍后重试。`, kind: 'error' }
}
return { title: '请求失败', detail: msg || `HTTP ${status}`, kind: 'error' }
}
if (err instanceof Error) {
return { title: '操作失败', detail: err.message, kind: 'error' }
}
return { title: '未知错误', detail: String(err), kind: 'error' }
}
/** 统一错误横幅:用于页面级错误展示。 */
export function ErrorBanner({
err,
onClose,
}: {
err: unknown
onClose?: () => void
}) {
const { title, detail, kind } = describeError(err)
return <Alert kind={kind} title={title} message={detail} onClose={onClose} />
}
export function Spinner({ className = '' }: { className?: string }) {
return (