178 lines
5.4 KiB
TypeScript
178 lines
5.4 KiB
TypeScript
"use client";
|
|
|
|
import Image from "next/image";
|
|
import React from "react";
|
|
import { motion } from "framer-motion";
|
|
import {
|
|
Avatar,
|
|
Box,
|
|
IconButton,
|
|
ListItemIcon,
|
|
ListItemText,
|
|
Menu,
|
|
MenuItem,
|
|
Stack,
|
|
Typography,
|
|
alpha,
|
|
useTheme,
|
|
} from "@mui/material";
|
|
import AddCommentRounded from "@mui/icons-material/AddCommentRounded";
|
|
import CloseRounded from "@mui/icons-material/CloseRounded";
|
|
|
|
type AgentHeaderProps = {
|
|
isStreaming: boolean;
|
|
menuAnchorEl: HTMLElement | null;
|
|
onMenuOpen: (event: React.MouseEvent<HTMLElement>) => void;
|
|
onMenuClose: () => void;
|
|
onNewConversation: () => void;
|
|
onClose: () => void;
|
|
};
|
|
|
|
export const AgentHeader = ({
|
|
isStreaming,
|
|
menuAnchorEl,
|
|
onMenuOpen,
|
|
onMenuClose,
|
|
onNewConversation,
|
|
onClose,
|
|
}: AgentHeaderProps) => {
|
|
const theme = useTheme();
|
|
const isMenuOpen = Boolean(menuAnchorEl);
|
|
|
|
return (
|
|
<Box
|
|
sx={{
|
|
px: 3,
|
|
py: 2.5,
|
|
zIndex: 10,
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "space-between",
|
|
backdropFilter: "blur(20px)",
|
|
borderBottom: `1px solid ${alpha(theme.palette.divider, 0.1)}`,
|
|
background: `linear-gradient(to bottom, ${alpha("#fff", 0.4)}, ${alpha("#fff", 0.1)})`,
|
|
boxShadow: `0 1px 0 ${alpha("#fff", 0.6)} inset`,
|
|
}}
|
|
>
|
|
<Stack direction="row" alignItems="center" spacing={2}>
|
|
<motion.div whileHover={{ rotate: 10, scale: 1.05 }} whileTap={{ scale: 0.95 }}>
|
|
<IconButton
|
|
onClick={onMenuOpen}
|
|
aria-label="打开 Agent 菜单"
|
|
aria-controls={isMenuOpen ? "global-chatbox-header-menu" : undefined}
|
|
aria-expanded={isMenuOpen ? "true" : undefined}
|
|
aria-haspopup="menu"
|
|
sx={{ p: 0, borderRadius: "50%" }}
|
|
>
|
|
<Box sx={{ position: "relative" }}>
|
|
<Avatar
|
|
sx={{
|
|
background: alpha("#ffffff", 0.9),
|
|
boxShadow: `0 8px 24px ${alpha("#00acc1", 0.4)}`,
|
|
width: 44,
|
|
height: 44,
|
|
border: `2px solid ${alpha("#fff", 0.8)}`,
|
|
p: 0.75,
|
|
}}
|
|
>
|
|
<Image
|
|
src="/ai-agent.svg"
|
|
alt="TJWater Agent"
|
|
width={30}
|
|
height={30}
|
|
style={{ width: "100%", height: "100%", objectFit: "contain" }}
|
|
/>
|
|
</Avatar>
|
|
<Box
|
|
sx={{
|
|
position: "absolute",
|
|
bottom: -2,
|
|
right: -2,
|
|
width: 14,
|
|
height: 14,
|
|
bgcolor: isStreaming ? "#ff9800" : "#00e676",
|
|
borderRadius: "50%",
|
|
border: "2.5px solid #fff",
|
|
boxShadow: `0 0 10px ${isStreaming ? "#ff9800" : "#00e676"}`,
|
|
animation: isStreaming ? "pulse 1.5s infinite" : "none",
|
|
"@keyframes pulse": {
|
|
"0%": { boxShadow: `0 0 0 0 ${alpha("#ff9800", 0.7)}` },
|
|
"70%": { boxShadow: `0 0 0 6px ${alpha("#ff9800", 0)}` },
|
|
"100%": { boxShadow: `0 0 0 0 ${alpha("#ff9800", 0)}` },
|
|
}
|
|
}}
|
|
/>
|
|
</Box>
|
|
</IconButton>
|
|
</motion.div>
|
|
<Box>
|
|
<Typography
|
|
variant="h6"
|
|
fontWeight={800}
|
|
sx={{
|
|
background: `linear-gradient(90deg, #01579b, #00838f)`,
|
|
backgroundClip: "text",
|
|
color: "transparent",
|
|
letterSpacing: -0.3,
|
|
}}
|
|
>
|
|
TJWater Agent
|
|
</Typography>
|
|
<Typography variant="caption" color="text.secondary" fontWeight={500}>
|
|
{isStreaming ? "正在思考分析任务..." : "基于大模型的水力分析引擎"}
|
|
</Typography>
|
|
</Box>
|
|
</Stack>
|
|
|
|
<Menu
|
|
id="global-chatbox-header-menu"
|
|
anchorEl={menuAnchorEl}
|
|
open={isMenuOpen}
|
|
onClose={onMenuClose}
|
|
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
|
|
transformOrigin={{ vertical: "top", horizontal: "left" }}
|
|
slotProps={{
|
|
paper: {
|
|
elevation: 8,
|
|
sx: {
|
|
mt: 1,
|
|
minWidth: 180,
|
|
borderRadius: 3,
|
|
border: `1px solid ${alpha(theme.palette.divider, 0.12)}`,
|
|
backdropFilter: "blur(12px)",
|
|
bgcolor: alpha("#fff", 0.92),
|
|
},
|
|
},
|
|
}}
|
|
>
|
|
<MenuItem onClick={onNewConversation}>
|
|
<ListItemIcon>
|
|
<AddCommentRounded fontSize="small" />
|
|
</ListItemIcon>
|
|
<ListItemText
|
|
primary="新建对话"
|
|
secondary="清空当前会话"
|
|
primaryTypographyProps={{ sx: { fontSize: "0.95rem", fontWeight: 700 } }}
|
|
secondaryTypographyProps={{ sx: { fontSize: "0.8rem" } }}
|
|
/>
|
|
</MenuItem>
|
|
</Menu>
|
|
|
|
<motion.div whileHover={{ scale: 1.08, rotate: 90 }} whileTap={{ scale: 0.92 }}>
|
|
<IconButton
|
|
onClick={onClose}
|
|
size="small"
|
|
aria-label="关闭 Agent"
|
|
sx={{
|
|
color: "text.primary",
|
|
bgcolor: alpha("#fff", 0.54),
|
|
"&:hover": { bgcolor: "#fff" },
|
|
}}
|
|
>
|
|
<CloseRounded />
|
|
</IconButton>
|
|
</motion.div>
|
|
</Box>
|
|
);
|
|
};
|