fix: add mobile navigation and restore chat outline/history access
parent
faef8b6aea
commit
deab555b3a
34
src/App.tsx
34
src/App.tsx
|
|
@ -1,6 +1,7 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { Routes, Route, Navigate, useLocation, useNavigate } from 'react-router-dom';
|
||||
import { Spin } from 'antd';
|
||||
import { Button, Drawer, Spin } from 'antd';
|
||||
import { MenuOutlined, SearchOutlined } from '@ant-design/icons';
|
||||
import Sidebar from './components/Sidebar';
|
||||
import CommandPalette from './components/CommandPalette';
|
||||
import AgentList from './pages/AgentList';
|
||||
|
|
@ -25,6 +26,8 @@ export default function App() {
|
|||
const { user } = useAuth();
|
||||
const location = useLocation();
|
||||
const [paletteOpen, setPaletteOpen] = useState(false);
|
||||
const [mobileNavOpen, setMobileNavOpen] = useState(false);
|
||||
const [isMobile, setIsMobile] = useState(() => (typeof window === 'undefined' ? false : window.innerWidth < 768));
|
||||
|
||||
// 全局快捷键 Ctrl/⌘ + K
|
||||
useEffect(() => {
|
||||
|
|
@ -39,6 +42,12 @@ export default function App() {
|
|||
return () => window.removeEventListener('keydown', handler);
|
||||
}, [user]);
|
||||
|
||||
useEffect(() => {
|
||||
const onResize = () => setIsMobile(window.innerWidth < 768);
|
||||
window.addEventListener('resize', onResize);
|
||||
return () => window.removeEventListener('resize', onResize);
|
||||
}, []);
|
||||
|
||||
const mainContent = (
|
||||
<Routes>
|
||||
<Route path="/" element={<HomeRedirect />} />
|
||||
|
|
@ -73,13 +82,32 @@ export default function App() {
|
|||
) : (
|
||||
<div className="layout-shell">
|
||||
{/* 只有编辑器全屏显示,其他页面均保留侧边栏 */}
|
||||
{!location.pathname.startsWith('/agents/') || location.pathname.includes('/chat') ? (
|
||||
{!isMobile && (!location.pathname.startsWith('/agents/') || location.pathname.includes('/chat')) ? (
|
||||
<Sidebar onOpenPalette={() => setPaletteOpen(true)} />
|
||||
) : null}
|
||||
<main className="main">
|
||||
{mainContent}
|
||||
{isMobile && (
|
||||
<div className="mobile-topbar">
|
||||
<Button type="text" icon={<MenuOutlined />} onClick={() => setMobileNavOpen(true)} />
|
||||
<div className="mobile-topbar-title">Agent Studio</div>
|
||||
<Button type="text" icon={<SearchOutlined />} onClick={() => setPaletteOpen(true)} />
|
||||
</div>
|
||||
)}
|
||||
<div className="main-content">{mainContent}</div>
|
||||
</main>
|
||||
<CommandPalette open={paletteOpen} onClose={() => setPaletteOpen(false)} />
|
||||
{isMobile && (
|
||||
<Drawer
|
||||
title="导航"
|
||||
placement="left"
|
||||
open={mobileNavOpen}
|
||||
onClose={() => setMobileNavOpen(false)}
|
||||
width={280}
|
||||
styles={{ body: { padding: 0 } }}
|
||||
>
|
||||
<Sidebar onOpenPalette={() => setPaletteOpen(true)} onNavigate={() => setMobileNavOpen(false)} />
|
||||
</Drawer>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { useTheme } from '../main';
|
|||
|
||||
interface Props {
|
||||
onOpenPalette?: () => void;
|
||||
onNavigate?: () => void;
|
||||
}
|
||||
|
||||
const NAV_GROUPS: Array<{
|
||||
|
|
@ -54,7 +55,7 @@ const NAV_GROUPS: Array<{
|
|||
}
|
||||
];
|
||||
|
||||
export default function Sidebar({ onOpenPalette }: Props) {
|
||||
export default function Sidebar({ onOpenPalette, onNavigate }: Props) {
|
||||
const { user, logout } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
const { mode, toggle } = useTheme();
|
||||
|
|
@ -77,7 +78,10 @@ export default function Sidebar({ onOpenPalette }: Props) {
|
|||
</div>
|
||||
|
||||
<div
|
||||
onClick={onOpenPalette}
|
||||
onClick={() => {
|
||||
onOpenPalette?.();
|
||||
onNavigate?.();
|
||||
}}
|
||||
className="nav-item"
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
|
|
@ -103,6 +107,7 @@ export default function Sidebar({ onOpenPalette }: Props) {
|
|||
to={it.to}
|
||||
end={it.end}
|
||||
className={({ isActive }) => `nav-item ${isActive ? 'active' : ''}`}
|
||||
onClick={onNavigate}
|
||||
>
|
||||
<span className="nav-icon">{it.icon}</span>
|
||||
<span>{it.label}</span>
|
||||
|
|
@ -135,6 +140,7 @@ export default function Sidebar({ onOpenPalette }: Props) {
|
|||
onClick: async () => {
|
||||
await logout();
|
||||
navigate('/login');
|
||||
onNavigate?.();
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -16,7 +16,14 @@ export default function ChatOutline(props: { messages: ChatMessage[]; onJump: (i
|
|||
const { messages, onJump, activeId } = props;
|
||||
const items = messages.filter((m) => ((m as any)?.speaker?.type ? (m as any).speaker.type === 'agent' : m.role === 'assistant'));
|
||||
|
||||
if (items.length === 0) return null;
|
||||
if (items.length === 0) {
|
||||
return (
|
||||
<aside className="chat-outline">
|
||||
<div className="chat-outline-title">聊天段落</div>
|
||||
<div style={{ padding: 12, color: 'var(--color-text-tertiary)', fontSize: 12 }}>暂无</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<aside className="chat-outline">
|
||||
|
|
|
|||
|
|
@ -100,6 +100,9 @@ export default function ChatPageH5({ logic }: { logic: ChatPageLogicOutput }) {
|
|||
<Button size="small" onClick={() => setOutlineDrawerOpen(true)}>
|
||||
大纲
|
||||
</Button>
|
||||
<Button size="small" onClick={() => setHistoryDrawerOpen(true)}>
|
||||
历史对话
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -620,12 +620,42 @@ body {
|
|||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.sidebar {
|
||||
.layout-shell > .sidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.main {
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mobile-topbar {
|
||||
height: 48px;
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 0 8px;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
background: var(--color-surface);
|
||||
}
|
||||
|
||||
.mobile-topbar-title {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-weight: 700;
|
||||
color: var(--color-text);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.chat-shell {
|
||||
|
|
@ -677,6 +707,10 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
.main-content {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.chat-body .messages-container {
|
||||
max-width: 780px;
|
||||
width: 100%;
|
||||
|
|
|
|||
Loading…
Reference in New Issue