75 lines
2.8 KiB
TypeScript
75 lines
2.8 KiB
TypeScript
import React, { useState } from 'react';
|
||
import { AlertTriangle, ChevronDown, ChevronUp, XCircle } from 'lucide-react';
|
||
import { useTheme } from '../../contexts/ThemeContext';
|
||
import type { StreamErrorItem } from './types';
|
||
|
||
interface StreamErrorNoticeProps {
|
||
errors: StreamErrorItem[];
|
||
}
|
||
|
||
const StreamErrorNotice: React.FC<StreamErrorNoticeProps> = ({ errors }) => {
|
||
const { theme } = useTheme();
|
||
const isDark = theme === 'dark';
|
||
const [expanded, setExpanded] = useState(false);
|
||
|
||
if (!errors || errors.length === 0) return null;
|
||
|
||
const hasErrors = errors.some((e) => e.level === 'error');
|
||
const errorCount = errors.filter((e) => e.level === 'error').length;
|
||
const warnCount = errors.filter((e) => e.level === 'warning').length;
|
||
|
||
let summaryText: string;
|
||
if (hasErrors && warnCount > 0) {
|
||
summaryText = `${errorCount} 个步骤失败,${warnCount} 个能力调用降级`;
|
||
} else if (hasErrors) {
|
||
summaryText = `${errorCount} 个步骤执行失败`;
|
||
} else {
|
||
summaryText = `${warnCount} 个能力调用失败,系统已继续执行`;
|
||
}
|
||
|
||
const borderClass = hasErrors
|
||
? isDark ? 'border-red-500/40 bg-red-500/10 text-red-200' : 'border-red-200 bg-red-50 text-red-700'
|
||
: isDark ? 'border-amber-500/40 bg-amber-500/10 text-amber-200' : 'border-amber-200 bg-amber-50 text-amber-700';
|
||
|
||
const dividerClass = hasErrors
|
||
? isDark ? 'border-red-500/20' : 'border-red-200'
|
||
: isDark ? 'border-amber-500/20' : 'border-amber-200';
|
||
|
||
return (
|
||
<div className={`rounded-xl border text-xs ${borderClass}`}>
|
||
<button
|
||
className="w-full flex items-center gap-2 px-3 py-2 text-left"
|
||
onClick={() => setExpanded((v) => !v)}
|
||
>
|
||
{hasErrors
|
||
? <XCircle className="w-3.5 h-3.5 shrink-0" />
|
||
: <AlertTriangle className="w-3.5 h-3.5 shrink-0" />}
|
||
<span className="flex-1 font-medium">{summaryText}</span>
|
||
{expanded
|
||
? <ChevronUp className="w-3.5 h-3.5 shrink-0 opacity-50" />
|
||
: <ChevronDown className="w-3.5 h-3.5 shrink-0 opacity-50" />}
|
||
</button>
|
||
|
||
{expanded && (
|
||
<div className={`border-t px-3 py-2 space-y-2.5 ${dividerClass}`}>
|
||
{errors.map((err) => (
|
||
<div key={err.errorId}>
|
||
<div className="flex items-start gap-1.5">
|
||
{err.level === 'error'
|
||
? <XCircle className="w-3 h-3 mt-0.5 shrink-0" />
|
||
: <AlertTriangle className="w-3 h-3 mt-0.5 shrink-0" />}
|
||
<span className="leading-snug">{err.message}</span>
|
||
</div>
|
||
<div className={`mt-0.5 pl-[18px] font-mono text-[10px] opacity-40`}>
|
||
错误 ID:{err.errorId}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default StreamErrorNotice;
|