refactor(web): 前端目录重构 — consoles/pages → layouts/features + shared

- consoles/admin/ → layouts/admin-layout/
- consoles/workbench/ → layouts/workbench-layout/ + features/chat/
- pages/ → features/ (dashboard, models, projects, not-found)
- components/ → shared/components/
- hooks/ → shared/hooks/
- utils/ → shared/utils/
- 更新所有 import 路径 (src/web/ + tests/web/)
- 更新开发文档 (README.md, frontend.md, architecture.md)
This commit is contained in:
2026-06-02 23:17:28 +08:00
parent 1f05f259d0
commit b1dec691e9
76 changed files with 249 additions and 111 deletions

View File

@@ -0,0 +1,93 @@
import { DeleteOutlined, MoreOutlined } from "@ant-design/icons";
import { Conversations } from "@ant-design/x";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { App, Spin } from "antd";
import { useState } from "react";
import type { Conversation } from "../../../shared/api";
import { useCurrentProject } from "../../layouts/workbench-layout/useCurrentProject";
import { createConversation, deleteConversation, fetchConversations } from "../../shared/hooks/use-conversations";
import { useModelList } from "../../shared/hooks/use-models";
import { ChatPanel } from "./ChatPanel";
export function ChatPage() {
const project = useCurrentProject();
const [activeConversationId, setActiveConversationId] = useState<null | string>(null);
const queryClient = useQueryClient();
const { message } = App.useApp();
const CONVERSATIONS_KEY = ["conversations", project.id] as const;
const { data, isLoading } = useQuery({
queryFn: () => fetchConversations(project.id),
queryKey: CONVERSATIONS_KEY,
});
const { data: modelsData } = useModelList({ pageSize: 200 });
const textModels = (modelsData?.items ?? []).filter((m) => m.capabilities.includes("text"));
const defaultModelId = textModels[0]?.id;
const deleteMutation = useMutation({
mutationFn: (id: string) => deleteConversation(project.id, id),
onError: (err: Error) => {
void message.error(`删除会话失败:${err.message}`);
},
onSuccess: (_data: void, id: string) => {
void queryClient.invalidateQueries({ queryKey: CONVERSATIONS_KEY });
if (activeConversationId === id) setActiveConversationId(null);
},
});
const conversations = (data?.items ?? []).map((c: Conversation) => ({
key: c.id,
label: c.title,
}));
return (
<div className="app-chat-page">
<div className="app-chat-conversations">
{isLoading ? (
<Spin />
) : (
<Conversations
activeKey={activeConversationId ?? ""}
creation={{
onClick: () => {
void createConversation(project.id, defaultModelId)
.then((conv) => {
void queryClient.invalidateQueries({ queryKey: CONVERSATIONS_KEY });
setActiveConversationId(conv.id);
})
.catch((err: Error) => {
void message.error(`创建会话失败:${err.message}`);
});
},
}}
items={conversations}
menu={(conv) => ({
items: [
{
danger: true,
icon: <DeleteOutlined />,
key: "delete",
label: "删除",
onClick: () => {
deleteMutation.mutate(conv.key);
},
},
],
trigger: <MoreOutlined />,
})}
onActiveChange={(key) => setActiveConversationId(key)}
/>
)}
</div>
<ChatPanel
conversationId={activeConversationId}
onConversationCreated={setActiveConversationId}
projectId={project.id}
/>
</div>
);
}