aura-web/src/pages/chat/components/ChatHeader.tsx

114 lines
3.9 KiB
TypeScript

import { ApiOutlined, DeleteOutlined, EditOutlined, SettingOutlined, EllipsisOutlined, HistoryOutlined, LogoutOutlined } from '@ant-design/icons';
import { Button, Dropdown, Modal, Switch } from 'antd';
import type { Agent } from '../../../api';
import './ChatHeader.css';
function formatAgentModel(raw: string | null | undefined) {
const s = String(raw ?? '').trim();
if (!s) return '默认模型';
if (s.startsWith('[') || s.startsWith('{')) {
try {
const parsed = JSON.parse(s);
if (Array.isArray(parsed)) {
const names = parsed
.map((x: any) => String(x?.name || x?.model || x?.id || '').trim())
.filter(Boolean);
if (names.length > 2) {
return names.slice(0, 2).join(', ') + '...';
}
if (names.length) return names.join(', ');
} else if (parsed && typeof parsed === 'object') {
const name = String((parsed as any).name || (parsed as any).model || (parsed as any).id || '').trim();
if (name) return name;
}
} catch {}
}
return s;
}
const isImageUrl = (url: string) => url?.startsWith('http') || url?.startsWith('/');
export default function ChatHeader(props: {
agent: Agent;
useStream: boolean;
setUseStream: (v: boolean) => void;
onOpenHistory: () => void;
onOpenParams: () => void;
onOpenMcp: () => void;
onManageAgent: () => void;
onClear: () => void;
onLogout?: () => void;
}) {
const { agent, useStream, setUseStream, onOpenHistory, onOpenParams, onOpenMcp, onManageAgent, onClear, onLogout } = props;
const modelText = formatAgentModel(agent.model);
return (
<div className="chat-header">
<div className="chat-header-left">
<div className="chat-header-avatar">
{isImageUrl(agent.avatar) ? (
<img src={agent.avatar} alt="avatar" style={{ width: '100%', height: '100%', borderRadius: '50%' }} />
) : (
(agent.name?.charAt(0) || '?').toUpperCase()
)}
</div>
<div className="chat-header-info">
<div className="chat-header-name-row">
<span className="chat-header-name">{agent.name}</span>
</div>
<div className="chat-header-desc">
<span className="chat-header-model">{modelText}</span>
</div>
</div>
</div>
<div className="chat-header-right">
<div className="chat-header-stream-toggle">
<span></span>
<Switch size="small" checked={useStream} onChange={setUseStream} />
</div>
<Button className="chat-header-btn" onClick={onOpenHistory}>
</Button>
<Dropdown
menu={{
items: [
{ key: 'params', label: '模型参数', icon: <SettingOutlined />, onClick: onOpenParams },
{ key: 'mcp', label: 'MCP 资源', icon: <ApiOutlined />, onClick: onOpenMcp },
{ key: 'edit', label: '管理 Agent', icon: <EditOutlined />, onClick: onManageAgent },
{ type: 'divider' as const },
{
key: 'clear',
label: '清空对话',
icon: <DeleteOutlined />,
danger: true,
onClick: () => {
Modal.confirm({ title: '清空当前会话所有消息?', onOk: onClear });
}
},
...(onLogout
? [
{ type: 'divider' as const },
{
key: 'logout',
label: '退出登录',
icon: <LogoutOutlined />,
onClick: onLogout,
}
]
: [])
]
}}
placement="bottomRight"
>
<Button className="chat-header-more-btn">
<EllipsisOutlined />
</Button>
</Dropdown>
</div>
</div>
);
}