← 首页·文章列表

前端目录结构与职责划分详解


📁 一、整体目录结构

frontend/
├── @/                          # 公共 UI 组件库(shadcn/ui)
│   └── components/ui/          # 可复用 UI 组件
│       ├── button.tsx
│       ├── input.tsx
│       ├── dialog.tsx
│       ├── table.tsx
│       └── ... (15+ 组件)
│
├── src/                        # 源代码目录
│   ├── main.tsx               # 应用入口
│   ├── App.tsx                # 根组件
│   ├── router.tsx             # 路由配置
│   │
│   ├── components/            # 组件目录
│   │   ├── admin/            # 管理后台组件
│   │   ├── chat/             # 聊天相关组件
│   │   ├── common/           # 通用组件
│   │   ├── layout/           # 布局组件
│   │   ├── session/          # 会话列表组件
│   │   └── ui/               # UI 组件
│   │
│   ├── pages/                # 页面目录
│   │   ├── LoginPage.tsx     # 登录页
│   │   ├── ChatPage.tsx      # 聊天页
│   │   ├── NotFoundPage.tsx   # 404 页面
│   │   │
│   │   └── admin/           # 管理后台页面
│   │       ├── AdminLayout.tsx       # 管理后台布局
│   │       ├── dashboard/            # 仪表盘
│   │       ├── knowledge/            # 知识库管理
│   │       ├── intent-tree/          # 意图树管理
│   │       ├── ingestion/            # 文档摄取
│   │       ├── traces/               # RAG 追踪
│   │       ├── sample-questions/     # 示例问题
│   │       ├── query-term-mapping/   # 查询词映射
│   │       ├── settings/            # 系统设置
│   │       └── users/               # 用户管理
│   │
│   ├── services/             # API 服务层
│   │   ├── api.ts           # Axios 实例配置
│   │   ├── authService.ts   # 认证服务
│   │   ├── chatService.ts   # 聊天服务
│   │   ├── sessionService.ts # 会话服务
│   │   ├── knowledgeService.ts # 知识库服务
│   │   └── ...
│   │
│   ├── stores/              # 状态管理(Zustand)
│   │   ├── authStore.ts    # 认证状态
│   │   ├── chatStore.ts    # 聊天状态
│   │   └── themeStore.ts   # 主题状态
│   │
│   ├── hooks/              # 自定义 Hooks
│   │   ├── useAuth.ts     # 认证 Hook
│   │   ├── useChat.ts     # 聊天 Hook
│   │   └── useStreamResponse.ts # 流式响应 Hook
│   │
│   ├── types/             # TypeScript 类型定义
│   │   └── index.ts       # 统一类型导出
│   │
│   ├── utils/             # 工具函数
│   │   ├── storage.ts     # 本地存储
│   │   ├── helpers.ts     # 辅助函数
│   │   └── error.ts       # 错误处理
│   │
│   └── lib/              # 库配置
│       └── utils.ts      # 工具函数(cn 等)
│
├── public/                # 静态资源
├── package.json          # 依赖配置
├── vite.config.ts        # Vite 配置
├── tailwind.config.cjs   # Tailwind 配置
└── tsconfig.json         # TypeScript 配置

🎯 二、技术栈概览

{
  "框架": "React 18 + TypeScript",
  "构建工具": "Vite",
  "路由": "React Router 6",
  "状态管理": "Zustand",
  "UI 组件": "shadcn/ui (Radix UI)",
  "样式": "Tailwind CSS",
  "表单验证": "React Hook Form + Zod",
  "HTTP 客户端": "Axios",
  "Markdown 渲染": "react-markdown + remark-gfm",
  "图表": "Recharts",
  "图标": "Lucide React",
  "提示": "Sonner (Toast)"
}

📦 三、核心目录详解

3.1 src/components/ - 组件目录

components/
├── admin/                  # 管理后台专用组件
│   ├── CreateKnowledgeBaseDialog.tsx  # 创建知识库对话框
│   └── SimpleLineChart.tsx           # 简单折线图
│
├── chat/                  # 聊天相关组件
│   ├── ChatInput.tsx       # 聊天输入框
│   ├── MessageList.tsx     # 消息列表
│   ├── MessageItem.tsx     # 单条消息
│   ├── MarkdownRenderer.tsx # Markdown 渲染器
│   ├── ThinkingIndicator.tsx # 思考指示器
│   ├── FeedbackButtons.tsx  # 反馈按钮
│   └── WelcomeScreen.tsx    # 欢迎屏幕
│
├── common/                # 通用组件
│   ├── Avatar.tsx         # 头像组件
│   ├── ErrorBoundary.tsx  # 错误边界
│   ├── Loading.tsx        # 加载状态
│   └── Toast.tsx          # 提示组件
│
├── layout/               # 布局组件
│   ├── MainLayout.tsx     # 主布局(聊天页)
│   ├── Sidebar.tsx        # 侧边栏
│   └── Header.tsx         # 顶部导航
│
├── session/              # 会话列表组件
│   ├── SessionList.tsx    # 会话列表
│   └── SessionItem.tsx    # 单个会话项
│
└── ui/                  # shadcn/ui 基础组件
    ├── button.tsx
    ├── input.tsx
    ├── dialog.tsx
    ├── table.tsx
    ├── select.tsx
    └── ... (15+ 组件)

聊天组件详解

// ChatInput.tsx - 聊天输入框
export function ChatInput() {
  // 状态
  const [value, setValue] = React.useState("");
  const [isFocused, setIsFocused] = React.useState(false);
  
  // 引用
  const textareaRef = React.useRef<HTMLTextAreaElement>(null);
  
  // 自动调整高度
  const adjustHeight = React.useCallback(() => {
    const el = textareaRef.current;
    if (!el) return;
    el.style.height = "auto";
    el.style.height = `${Math.min(el.scrollHeight, 160)}px`;
  }, []);
  
  // 发送消息
  const handleSubmit = async () => {
    if (isStreaming) {
      cancelGeneration();  // 停止生成
      return;
    }
    await sendMessage(value);  // 发送消息
  };
  
  return (
    <div className="flex items-end gap-2">
      <Textarea
        ref={textareaRef}
        value={value}
        onChange={(e) => setValue(e.target.value)}
        onKeyDown={handleKeyDown}
        placeholder="输入问题..."
      />
      <Button onClick={handleSubmit}>
        {isStreaming ? <Square /> : <Send />}
      </Button>
    </div>
  );
}

3.2 src/pages/ - 页面目录

pages/
├── LoginPage.tsx           # 登录页
├── ChatPage.tsx            # 聊天页(用户端)
├── NotFoundPage.tsx        # 404 页面
│
└── admin/                  # 管理后台页面
    ├── AdminLayout.tsx     # 管理后台布局(带侧边栏)
    │
    ├── dashboard/
    │   └── DashboardPage.tsx    # 仪表盘首页
    │
    ├── knowledge/          # 知识库管理
    │   ├── KnowledgeListPage.tsx     # 知识库列表
    │   ├── KnowledgeDocumentsPage.tsx # 文档列表
    │   └── KnowledgeChunksPage.tsx   # 分块列表
    │
    ├── intent-tree/         # 意图树管理
    │   ├── IntentTreePage.tsx       # 意图树视图
    │   ├── IntentListPage.tsx        # 意图列表
    │   └── IntentEditPage.tsx        # 意图编辑
    │
    ├── ingestion/
    │   └── IngestionPage.tsx         # 文档摄取
    │
    ├── traces/              # RAG 追踪
    │   ├── RagTracePage.tsx          # 追踪列表
    │   ├── RagTraceDetailPage.tsx   # 追踪详情
    │   └── components/
    │       ├── FilterBar.tsx
    │       ├── RunsTable.tsx
    │       └── StatCard.tsx
    │
    ├── sample-questions/
    │   └── SampleQuestionPage.tsx    # 示例问题管理
    │
    ├── query-term-mapping/
    │   └── QueryTermMappingPage.tsx  # 查询词映射
    │
    ├── settings/
    │   └── SystemSettingsPage.tsx   # 系统设置
    │
    └── users/
        └── UserListPage.tsx          # 用户列表

页面组件示例

// ChatPage.tsx - 聊天页面
export function ChatPage() {
  const {
    messages,
    isLoading,
    isStreaming,
    fetchSessions,
    selectSession,
    createSession
  } = useChatStore();
  
  // 初始化:加载会话列表
  React.useEffect(() => {
    fetchSessions();
  }, [fetchSessions]);
  
  // 渲染
  return (
    <MainLayout>
      {/* 左侧:会话列表 */}
      <aside className="w-64 border-r">
        <SessionList />
      </aside>
      
      {/* 右侧:聊天区域 */}
      <main className="flex-1 flex flex-col">
        {/* 消息列表 */}
        <MessageList messages={messages} isLoading={isLoading} />
        
        {/* 输入框 */}
        <ChatInput />
      </main>
    </MainLayout>
  );
}

3.3 src/services/ - API 服务层

services/
├── api.ts                  # Axios 实例 + 拦截器
├── authService.ts         # 认证相关
├── chatService.ts         # 聊天 API
├── sessionService.ts      # 会话管理 API
├── knowledgeService.ts    # 知识库 API
├── intentTreeService.ts   # 意图树 API
├── ingestionService.ts    # 文档摄取 API
├── ragTraceService.ts     # RAG 追踪 API
├── settingsService.ts    # 设置 API
├── userService.ts         # 用户管理 API
├── dashboardService.ts    # 仪表盘 API
├── sampleQuestionService.ts # 示例问题 API
└── queryTermMappingService.ts # 查询词映射 API

Axios 实例配置

// services/api.ts
import axios from "axios";
import { storage } from "@/utils/storage";

export const api = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL || "",
  timeout: 60000
});

// 请求拦截器:添加 Token
api.interceptors.request.use((config) => {
  const token = storage.getToken();
  if (token) {
    config.headers.Authorization = token;
  }
  return config;
});

// 响应拦截器:统一错误处理
api.interceptors.response.use(
  (response) => {
    const payload = response.data;
    // 业务错误处理
    if (payload.code !== "0") {
      const message = payload.message || "请求失败";
      // Token 过期,跳转登录
      if (message.includes("未登录")) {
        storage.clearAuth();
        window.location.href = "/login";
      }
      return Promise.reject(new Error(message));
    }
    return payload.data;
  },
  (error) => {
    // HTTP 错误处理
    if (error?.response?.status === 401) {
      storage.clearAuth();
      window.location.href = "/login";
    }
    return Promise.reject(error);
  }
);

服务层示例

// services/chatService.ts
import { api } from "./api";
import type { Message, FeedbackValue } from "@/types";

export interface SendMessageParams {
  question: string;
  conversationId?: string;
  thinking?: boolean;
}

export async function sendMessage(params: SendMessageParams) {
  return api.post("/rag/v3/chat", params);
}

export async function stopTask(taskId: string) {
  return api.post(`/rag/v3/stop/${taskId}`);
}

export async function submitFeedback(messageId: string, feedback: FeedbackValue) {
  return api.post("/feedback", { messageId, feedback });
}

3.4 src/stores/ - 状态管理(Zustand)

stores/
├── authStore.ts    # 认证状态
├── chatStore.ts    # 聊天状态
└── themeStore.ts   # 主题状态

ChatStore 详解

// stores/chatStore.ts
interface ChatState {
  // 数据
  sessions: Session[];
  currentSessionId: string | null;
  messages: Message[];
  
  // UI 状态
  isLoading: boolean;
  isStreaming: boolean;
  deepThinkingEnabled: boolean;
  
  // Actions
  fetchSessions: () => Promise<void>;
  createSession: () => Promise<string>;
  selectSession: (sessionId: string) => Promise<void>;
  sendMessage: (content: string) => Promise<void>;
  cancelGeneration: () => void;
}

export const useChatStore = create<ChatState>((set, get) => ({
  // 初始状态
  sessions: [],
  currentSessionId: null,
  messages: [],
  isLoading: false,
  isStreaming: false,
  deepThinkingEnabled: false,
  
  // Actions
  sendMessage: async (content: string) => {
    set({ isLoading: true });
    try {
      // 调用 API
      const response = await chatService.sendMessage({
        question: content,
        conversationId: get().currentSessionId
      });
      // 处理响应
      // ...
    } finally {
      set({ isLoading: false });
    }
  }
}));

3.5 src/hooks/ - 自定义 Hooks

hooks/
├── useAuth.ts           # 认证相关 Hook
├── useChat.ts           # 聊天相关 Hook(useChatStore 的包装)
└── useStreamResponse.ts # SSE 流式响应处理

流式响应 Hook

// hooks/useStreamResponse.ts
export interface StreamHandlers {
  onMeta?: (payload: StreamMetaPayload) => void;
  onMessage?: (payload: MessageDeltaPayload) => void;
  onThinking?: (payload: MessageDeltaPayload) => void;
  onFinish?: (payload: CompletionPayload) => void;
  onError?: (error: Error) => void;
}

export async function readSseStream(
  response: Response,
  handlers: StreamHandlers,
  signal?: AbortSignal
) {
  const reader = response.body.getReader();
  const decoder = new TextDecoder("utf-8");
  let buffer = "";
  
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    
    buffer += decoder.decode(value, { stream: true });
    
    // 按行解析 SSE 事件
    const lines = buffer.split("\n");
    buffer = lines.pop() || "";
    
    for (const line of lines) {
      if (line.startsWith("event:")) {
        // 事件类型
      } else if (line.startsWith("data:")) {
        // 数据内容
        const data = JSON.parse(line.slice(5));
        handlers.onMessage?.(data);
      }
    }
  }
}

3.6 src/router.tsx - 路由配置

// router.tsx
export const router = createBrowserRouter([
  {
    path: "/",
    element: <HomeRedirect />  // 首页重定向
  },
  {
    path: "/login",
    element: <LoginPage />     // 登录页(已登录则重定向)
  },
  {
    path: "/chat",
    element: (
      <RequireAuth>
        <ChatPage />           // 聊天页(需登录)
      </RequireAuth>
    )
  },
  {
    path: "/admin",
    element: (
      <RequireAdmin>
        <AdminLayout />        // 管理后台布局
      </RequireAdmin>
    ),
    children: [
      { path: "dashboard", element: <DashboardPage /> },
      { path: "knowledge", element: <KnowledgeListPage /> },
      { path: "knowledge/:id/documents", element: <KnowledgeDocumentsPage /> },
      { path: "knowledge/:id/chunks", element: <KnowledgeChunksPage /> },
      { path: "intent-tree", element: <IntentTreePage /> },
      { path: "intent-tree/list", element: <IntentListPage /> },
      { path: "intent-tree/edit/:id", element: <IntentEditPage /> },
      { path: "ingestion", element: <IngestionPage /> },
      { path: "traces", element: <RagTracePage /> },
      { path: "traces/:id", element: <RagTraceDetailPage /> },
      { path: "settings", element: <SystemSettingsPage /> },
      { path: "sample-questions", element: <SampleQuestionPage /> },
      { path: "query-term-mapping", element: <QueryTermMappingPage /> },
      { path: "users", element: <UserListPage /> }
    ]
  }
]);

3.7 src/types/index.ts - 类型定义

// types/index.ts

// 会话
export interface Session {
  id: string;
  title: string;
  lastTime?: string;
  messageCount?: number;
}

// 消息
export interface Message {
  id: string;
  role: "user" | "assistant" | "system";
  content: string;
  thinking?: string;
  vote?: 1 | -1 | null;
  createdAt: string;
}

// 流式事件载荷
export interface MessageDeltaPayload {
  id: string;
  type: "think" | "content";
  delta: string;
}

export interface CompletionPayload {
  id: string;
  content: string;
  thinking?: string;
  taskId: string;
}

export interface StreamMetaPayload {
  conversationId: string;
  messageId: string;
  taskId: string;
}

// 反馈
export type FeedbackValue = "like" | "dislike" | null;

🏗️ 四、数据流架构

┌─────────────────────────────────────────────────────────────────────┐
│                           用户交互                                   │
│                         (输入问题/点击)                              │
└─────────────────────────────────────────────────────────────────────┘
                                  ↓
┌─────────────────────────────────────────────────────────────────────┐
│                        UI 组件层                                     │
│    ChatInput ─→ useChatStore ─→ MessageList ─→ ChatPage            │
└─────────────────────────────────────────────────────────────────────┘
                                  ↓
┌─────────────────────────────────────────────────────────────────────┐
│                       状态管理层 (Zustand)                          │
│                                                                      
│    useChatStore:                                                     │
│    ├── sessions: Session[]        # 会话列表                         │
│    ├── messages: Message[]        # 消息列表                         │
│    ├── isStreaming: boolean       # 流式状态                         │
│    └── methods: sendMessage()     # 发送消息                         │
└─────────────────────────────────────────────────────────────────────┘
                                  ↓
┌─────────────────────────────────────────────────────────────────────┐
│                       服务层 (API Services)                         │
│                                                                      
│    chatService.sendMessage()     # HTTP 请求                        │
│    sessionService.listSessions() # 获取会话                         │
│    ragTraceService.query()       # 查询追踪                         │
└─────────────────────────────────────────────────────────────────────┘
                                  ↓
┌─────────────────────────────────────────────────────────────────────┐
│                       HTTP 层 (Axios)                               │
│                                                                      
│    api.ts:                                                           │
│    ├── 请求拦截器(添加 Token)                                        │
│    ├── 响应拦截器(统一错误处理)                                       │
│    └── SSE 流式处理(readSseStream)                                  │
└─────────────────────────────────────────────────────────────────────┘
                                  ↓
┌─────────────────────────────────────────────────────────────────────┐
│                       后端服务 (Java/Spring)                          │
│                                                                      
│    RAGChatController ─→ RAGChatService ─→ MultiChannelRetrieval     │
└─────────────────────────────────────────────────────────────────────┘

📊 五、管理后台功能模块

模块 页面路径 主要功能
仪表盘 /admin/dashboard 数据统计、趋势图表
知识库管理 /admin/knowledge 创建/编辑/删除知识库
文档管理 /admin/knowledge/:id/documents 上传/查看/删除文档
分块管理 /admin/knowledge/:id/chunks 查看文档分块结果
意图树 /admin/intent-tree 可视化意图树
意图列表 /admin/intent-tree/list 列表管理意图
意图编辑 /admin/intent-tree/edit/:id 编辑意图节点
文档摄取 /admin/ingestion 触发文档处理任务
RAG 追踪 /admin/traces 查看 RAG 执行链路
追踪详情 /admin/traces/:id 查看单次执行详情
示例问题 /admin/sample-questions 管理示例问题
查询词映射 /admin/query-term-mapping 查询词优化
系统设置 /admin/settings 配置系统参数
用户管理 /admin/users 用户列表/权限

🔧 六、组件复用示例

6.1 shadcn/ui 组件使用

// 使用 Button 组件
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";

<Button 
  variant="outline" 
  size="sm" 
  onClick={handleClick}
  className="gap-2"
>
  <Plus className="h-4 w-4" />
  新建
</Button>

6.2 自定义 Hook 封装

// hooks/useChat.ts - 封装 chatStore
import { useChatStore } from "@/stores/chatStore";

export function useChat() {
  return useChatStore();
}

// 使用
const { messages, sendMessage } = useChat();

📁 七、相关文件路径汇总

功能 文件路径
应用入口 src/main.tsx
根组件 src/App.tsx
路由配置 src/router.tsx
聊天页面 src/pages/ChatPage.tsx
登录页面 src/pages/LoginPage.tsx
管理后台布局 src/pages/admin/AdminLayout.tsx
聊天输入框 src/components/chat/ChatInput.tsx
消息列表 src/components/chat/MessageList.tsx
会话列表 src/components/session/SessionList.tsx
状态管理 src/stores/chatStore.ts
API 配置 src/services/api.ts
聊天服务 src/services/chatService.ts
流式响应 src/hooks/useStreamResponse.ts
类型定义 src/types/index.ts

💡 八、开发建议

8.1 新增页面步骤

1. 在 src/pages/admin/ 下创建页面组件
2. 在 router.tsx 中添加路由
3. 在 AdminLayout.tsx 中添加菜单项
4. 在对应的 services/ 中添加 API 方法

8.2 新增组件步骤

1. UI 组件:放在 src/components/ui/
2. 业务组件:放在对应的功能目录下
3. 使用 shadcn/ui:运行 npx shadcn@latest add button

8.3 状态管理原则

✅ 页面级状态:使用 React useState
✅ 跨组件状态:使用 Zustand Store
✅ 服务端状态:使用 API Service + Store

这个前端架构设计清晰,模块化程度高,便于维护和扩展!