导航菜单

  • 1.概述
  • 2.功能与能力
  • 3.系统架构
  • 4.部署与配置
  • 5.Docker 部署
  • 6.环境配置
  • 7.外部服务设置
  • 8.AI模型与LLM配置
  • 9.核心系统
  • 10.文档处理流水线
  • 11.RAG引擎与搜索
  • 12.知识库管理系统
  • 13.对话与对话系统
  • 14.翻译与跨语言支持
  • 15.用户界面
  • 16.主应用界面
  • 17.管理仪表盘
  • 18.文档编写界面
  • 19.知识库内容管理
  • 20.国际化与本地化
  • 21.管理功能
  • 22.用户与团队管理
  • 23.文件和存储管理
  • 24.知识库管理
  • 25.系统监控与健康状态
  • 26.API 参考
  • 27.知识库API
  • 28.对话与聊天API
  • 29.文件管理API
  • 30.管理与Admin API
  • 31.开发指南
  • 32.前端开发
  • 33.后端服务架构
  • 34.数据库模式与模型
  • 35.基础设施与文档
  • 36.快速入门指南
  • 1. 目的与范围
  • 2. 前端架构概述
    • 2.1 双前端架构
    • 2.2 技术栈对比
  • 3. React 应用结构
    • 3.1 项目目录结构
    • 3.2 核心组件设计
      • 3.2.1 聊天组件
      • 3.2.2 自定义 Hook
    • 3.3 状态管理模式
      • 3.3.1 Context API
  • 4. Vue.js 管理应用
    • 4.1 项目目录结构
    • 4.2 Composition API 模式
      • 4.2.1 组件示例
      • 4.2.2 Composable 模式
  • 5. 聊天系统实现
    • 5.1 流式响应处理
    • 5.2 消息渲染
  • 6. 状态管理模式
    • 6.1 React 状态管理
    • 6.2 Vue 状态管理
  • 7. 国际化系统
    • 7.1 React 国际化
    • 7.2 Vue 国际化
  • 8. 样式与主题系统
    • 8.1 CSS Modules
    • 8.2 Tailwind CSS
  • 9. API 集成模式
    • 9.1 API 客户端封装
    • 9.2 服务层封装
  • 10. 组件开发模式
    • 10.1 组件设计原则
    • 10.2 组件示例
  • 11. 性能优化
    • 11.1 React 优化
    • 11.2 Vue 优化
  • 12. 测试
    • 12.1 React 测试
  • 13. 总结

# Ragflow-Plus 前端开发教程

1. 目的与范围 #

本文档详细介绍了 Ragflow-Plus 前端开发的技术栈、架构模式、组件设计和最佳实践。系统包含两个独立的前端应用:用户前端(React)和管理前端(Vue.js)。

有关系统架构概述,请参阅 系统架构。有关开发指南,请参阅 开发指南。

2. 前端架构概述 #

Ragflow-Plus 采用双前端架构,分别服务于普通用户和管理员。

2.1 双前端架构 #

┌─────────────────────────────────────────┐
│      用户前端 (User Frontend)            │
│  ┌──────────┐  ┌──────────┐  ┌──────┐  │
│  │ React    │  │ TypeScript│  │ Vite │  │
│  │ 18+      │  │          │  │      │  │
│  └──────────┘  └──────────┘  └──────┘  │
│  位置: web/src/                        │
└─────────────────────────────────────────┘
                 ↕
┌─────────────────────────────────────────┐
│      管理前端 (Management Frontend)      │
│  ┌──────────┐  ┌──────────┐  ┌──────┐  │
│  │ Vue.js   │  │ TypeScript│  │ Vite │  │
│  │ 3        │  │          │  │      │  │
│  └──────────┘  └──────────┘  └──────┘  │
│  位置: management/web/src/             │
└─────────────────────────────────────────┘

2.2 技术栈对比 #

特性 用户前端 管理前端
框架 React 18+ Vue.js 3
语言 TypeScript TypeScript
构建工具 Vite Vite
UI 库 自定义组件 Element Plus
状态管理 Context + Hooks Composables
路由 React Router Vue Router
HTTP 客户端 Axios Axios
国际化 react-i18next vue-i18n

3. React 应用结构 #

用户前端使用 React + TypeScript 构建,提供聊天、文档编写和知识库管理功能。

3.1 项目目录结构 #

web/
├── src/
│   ├── pages/              # 页面组件
│   │   ├── chat/           # 聊天界面
│   │   │   ├── index.tsx
│   │   │   ├── ChatContainer.tsx
│   │   │   └── MessageItem.tsx
│   │   ├── write/          # 文档编写
│   │   │   ├── index.tsx
│   │   │   └── MarkdownEditor.tsx
│   │   └── add-knowledge/  # 知识库管理
│   │       └── index.tsx
│   ├── components/         # 通用组件
│   │   ├── Header/
│   │   ├── Sidebar/
│   │   └── Loading/
│   ├── services/           # API 服务
│   │   ├── api.ts
│   │   ├── chatService.ts
│   │   └── knowledgebaseService.ts
│   ├── hooks/              # 自定义 Hooks
│   │   ├── useChat.ts
│   │   ├── useKnowledgeBase.ts
│   │   └── useAuth.ts
│   ├── utils/              # 工具函数
│   │   ├── format.ts
│   │   └── validation.ts
│   ├── locales/            # 国际化文件
│   │   ├── zh.ts
│   │   ├── en.ts
│   │   └── zh-traditional.ts
│   ├── App.tsx             # 根组件
│   └── main.tsx            # 入口文件
├── public/                 # 静态资源
├── package.json
└── vite.config.ts          # Vite 配置

3.2 核心组件设计 #

3.2.1 聊天组件 #

// pages/chat/ChatContainer.tsx
import React, { useState, useEffect } from 'react';
import { useChat } from '@/hooks/useChat';
import MessageItem from './MessageItem';

interface ChatContainerProps {
  knowledgeBaseId: string;
}

const ChatContainer: React.FC<ChatContainerProps> = ({ knowledgeBaseId }) => {
  const { messages, sendMessage, loading } = useChat(knowledgeBaseId);
  const [input, setInput] = useState('');

  const handleSend = async () => {
    if (!input.trim()) return;

    await sendMessage(input);
    setInput('');
  };

  return (
    <div className="chat-container">
      <div className="messages">
        {messages.map(msg => (
          <MessageItem key={msg.id} message={msg} />
        ))}
      </div>
      <div className="input-area">
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyPress={(e) => e.key === 'Enter' && handleSend()}
          placeholder="输入消息..."
        />
        <button onClick={handleSend} disabled={loading}>
          发送
        </button>
      </div>
    </div>
  );
};

3.2.2 自定义 Hook #

// hooks/useChat.ts
import { useState, useCallback } from 'react';
import { chatService } from '@/services/chatService';
import { Message } from '@/types';

export const useChat = (knowledgeBaseId: string) => {
  const [messages, setMessages] = useState<Message[]>([]);
  const [loading, setLoading] = useState(false);

  const sendMessage = useCallback(async (content: string) => {
    setLoading(true);
    try {
      const userMessage: Message = {
        id: Date.now().toString(),
        role: 'user',
        content,
        timestamp: new Date()
      };

      setMessages(prev => [...prev, userMessage]);

      const response = await chatService.sendMessage({
        knowledgeBaseId,
        content
      });

      setMessages(prev => [...prev, response]);
    } catch (error) {
      console.error('发送消息失败:', error);
    } finally {
      setLoading(false);
    }
  }, [knowledgeBaseId]);

  return { messages, sendMessage, loading };
};

3.3 状态管理模式 #

3.3.1 Context API #

// contexts/AuthContext.tsx
import React, { createContext, useContext, useState } from 'react';

interface AuthContextType {
  user: User | null;
  login: (token: string) => void;
  logout: () => void;
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);

export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const [user, setUser] = useState<User | null>(null);

  const login = (token: string) => {
    // 解析 token 获取用户信息
    const userInfo = parseToken(token);
    setUser(userInfo);
    localStorage.setItem('token', token);
  };

  const logout = () => {
    setUser(null);
    localStorage.removeItem('token');
  };

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within AuthProvider');
  }
  return context;
};

4. Vue.js 管理应用 #

管理前端使用 Vue.js 3 + Composition API 构建,提供用户、团队和知识库管理功能。

4.1 项目目录结构 #

management/web/
├── src/
│   ├── pages/              # 页面组件
│   │   ├── user/           # 用户管理
│   │   │   └── index.vue
│   │   ├── team-management/  # 团队管理
│   │   │   └── index.vue
│   │   └── knowledgebase/  # 知识库管理
│   │       └── index.vue
│   ├── components/         # 通用组件
│   ├── common/             # 公共模块
│   │   ├── apis/           # API 客户端
│   │   │   ├── users/
│   │   │   ├── teams/
│   │   │   └── knowledgebases/
│   │   └── composables/    # Composables
│   │       ├── useUserService.ts
│   │       └── useFileUpload.ts
│   ├── http/               # HTTP 配置
│   │   ├── axios.ts
│   │   └── upload-axios.ts
│   ├── App.vue
│   └── main.ts
├── package.json
└── vite.config.ts

4.2 Composition API 模式 #

4.2.1 组件示例 #

<!-- pages/user/index.vue -->
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useUserService } from '@/common/composables/useUserService';
import { ElTable, ElInput, ElButton } from 'element-plus';

const { users, loading, fetchUsers, createUser, deleteUser } = useUserService();
const searchQuery = ref('');
const dialogVisible = ref(false);

onMounted(() => {
  fetchUsers();
});

const handleSearch = () => {
  fetchUsers({ query: searchQuery.value });
};

const handleCreate = async (userData: any) => {
  await createUser(userData);
  dialogVisible.value = false;
  fetchUsers();
};
</script>

<template>
  <div class="user-management">
    <div class="toolbar">
      <el-input
        v-model="searchQuery"
        placeholder="搜索用户"
        @keyup.enter="handleSearch"
      />
      <el-button type="primary" @click="dialogVisible = true">
        新建用户
      </el-button>
    </div>

    <el-table :data="users" :loading="loading">
      <el-table-column prop="nickname" label="用户名" />
      <el-table-column prop="email" label="邮箱" />
      <el-table-column label="操作">
        <template #default="{ row }">
          <el-button @click="deleteUser(row.id)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

4.2.2 Composable 模式 #

// common/composables/useUserService.ts
import { ref } from 'vue';
import { userApi } from '@/common/apis/users';

export const useUserService = () => {
  const users = ref([]);
  const loading = ref(false);

  const fetchUsers = async (params?: any) => {
    loading.value = true;
    try {
      const response = await userApi.getUsers(params);
      users.value = response.data.list;
    } finally {
      loading.value = false;
    }
  };

  const createUser = async (userData: any) => {
    await userApi.createUser(userData);
    await fetchUsers();
  };

  const deleteUser = async (userId: string) => {
    await userApi.deleteUser(userId);
    await fetchUsers();
  };

  return {
    users,
    loading,
    fetchUsers,
    createUser,
    deleteUser
  };
};

5. 聊天系统实现 #

5.1 流式响应处理 #

// services/chatService.ts
export const chatService = {
  async sendMessage(params: {
    knowledgeBaseId: string;
    content: string;
  }): Promise<Message> {
    const eventSource = new EventSource(
      `/api/v1/completion?kb_id=${params.knowledgeBaseId}&question=${encodeURIComponent(params.content)}`
    );

    return new Promise((resolve, reject) => {
      let fullContent = '';

      eventSource.onmessage = (event) => {
        const data = JSON.parse(event.data);

        if (data.type === 'content') {
          fullContent += data.content;
          // 更新 UI
        } else if (data.type === 'done') {
          eventSource.close();
          resolve({
            id: data.message_id,
            role: 'assistant',
            content: fullContent,
            timestamp: new Date()
          });
        }
      };

      eventSource.onerror = (error) => {
        eventSource.close();
        reject(error);
      };
    });
  }
};

5.2 消息渲染 #

// components/MessageItem.tsx
import React from 'react';
import ReactMarkdown from 'react-markdown';
import { Message } from '@/types';

interface MessageItemProps {
  message: Message;
}

const MessageItem: React.FC<MessageItemProps> = ({ message }) => {
  return (
    <div className={`message ${message.role}`}>
      <div className="message-header">
        <span className="role">{message.role === 'user' ? '用户' : '助手'}</span>
        <span className="timestamp">
          {new Date(message.timestamp).toLocaleString()}
        </span>
      </div>
      <div className="message-content">
        {message.role === 'assistant' ? (
          <ReactMarkdown>{message.content}</ReactMarkdown>
        ) : (
          <p>{message.content}</p>
        )}
      </div>
    </div>
  );
};

6. 状态管理模式 #

6.1 React 状态管理 #

使用 Context API 和自定义 Hooks 进行状态管理:

// contexts/KnowledgeBaseContext.tsx
import React, { createContext, useContext, useState } from 'react';

interface KBContextType {
  knowledgeBases: KnowledgeBase[];
  selectedKB: KnowledgeBase | null;
  setSelectedKB: (kb: KnowledgeBase | null) => void;
  refreshKBList: () => Promise<void>;
}

const KBContext = createContext<KBContextType | undefined>(undefined);

export const KBProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const [knowledgeBases, setKnowledgeBases] = useState<KnowledgeBase[]>([]);
  const [selectedKB, setSelectedKB] = useState<KnowledgeBase | null>(null);

  const refreshKBList = async () => {
    const list = await knowledgebaseService.getList();
    setKnowledgeBases(list);
  };

  return (
    <KBContext.Provider value={{
      knowledgeBases,
      selectedKB,
      setSelectedKB,
      refreshKBList
    }}>
      {children}
    </KBContext.Provider>
  );
};

6.2 Vue 状态管理 #

使用 Composables 进行状态管理:

// composables/useKnowledgeBase.ts
import { ref, computed } from 'vue';
import { knowledgebaseApi } from '@/common/apis/knowledgebases';

export const useKnowledgeBase = () => {
  const knowledgeBases = ref<KnowledgeBase[]>([]);
  const selectedKB = ref<KnowledgeBase | null>(null);
  const loading = ref(false);

  const fetchList = async () => {
    loading.value = true;
    try {
      const response = await knowledgebaseApi.getList();
      knowledgeBases.value = response.data.list;
    } finally {
      loading.value = false;
    }
  };

  const selectKB = (kb: KnowledgeBase) => {
    selectedKB.value = kb;
  };

  return {
    knowledgeBases,
    selectedKB,
    loading,
    fetchList,
    selectKB
  };
};

7. 国际化系统 #

7.1 React 国际化 #

使用 react-i18next:

// i18n.ts
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import zh from './locales/zh';
import en from './locales/en';

i18n
  .use(initReactI18next)
  .init({
    resources: {
      zh: { translation: zh },
      en: { translation: en }
    },
    lng: 'zh',
    fallbackLng: 'zh',
    interpolation: {
      escapeValue: false
    }
  });

// 使用
import { useTranslation } from 'react-i18next';

const MyComponent = () => {
  const { t } = useTranslation();
  return <h1>{t('welcome')}</h1>;
};

7.2 Vue 国际化 #

使用 vue-i18n:

// i18n.ts
import { createI18n } from 'vue-i18n';
import zh from './locales/zh';
import en from './locales/en';

const i18n = createI18n({
  locale: 'zh',
  fallbackLocale: 'zh',
  messages: {
    zh,
    en
  }
});

// 使用
<script setup>
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
</script>

<template>
  <h1>{{ t('welcome') }}</h1>
</template>

8. 样式与主题系统 #

8.1 CSS Modules #

// ChatContainer.module.css
.chatContainer {
  display: flex;
  flex-direction: column;
  height: 100vh;
}

.messages {
  flex: 1;
  overflow-y: auto;
  padding: 20px;
}

.inputArea {
  display: flex;
  padding: 10px;
  border-top: 1px solid #eee;
}

// 使用
import styles from './ChatContainer.module.css';

<div className={styles.chatContainer}>
  {/* ... */}
</div>

8.2 Tailwind CSS #

// 使用 Tailwind 类名
<div className="flex flex-col h-screen">
  <div className="flex-1 overflow-y-auto p-5">
    {/* 消息列表 */}
  </div>
  <div className="flex p-2 border-t">
    {/* 输入区域 */}
  </div>
</div>

9. API 集成模式 #

9.1 API 客户端封装 #

// services/api.ts
import axios from 'axios';

const api = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  timeout: 30000
});

// 请求拦截器
api.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

// 响应拦截器
api.interceptors.response.use(
  (response) => response.data,
  (error) => {
    if (error.response?.status === 401) {
      // 处理未授权
      localStorage.removeItem('token');
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

export default api;

9.2 服务层封装 #

// services/chatService.ts
import api from './api';

export const chatService = {
  sendMessage: (params: {
    knowledgeBaseId: string;
    content: string;
  }) => {
    return api.post('/api/v1/completion', {
      kb_id: params.knowledgeBaseId,
      question: params.content
    });
  },

  getConversation: (conversationId: string) => {
    return api.get(`/api/v1/conversation/${conversationId}`);
  }
};

10. 组件开发模式 #

10.1 组件设计原则 #

  1. 单一职责:每个组件只负责一个功能
  2. 可复用性:通过 props 实现组件复用
  3. 可组合性:小组件组合成复杂组件
  4. 类型安全:使用 TypeScript 定义接口

10.2 组件示例 #

// components/Button.tsx
import React from 'react';

interface ButtonProps {
  variant?: 'primary' | 'secondary' | 'danger';
  size?: 'small' | 'medium' | 'large';
  disabled?: boolean;
  onClick?: () => void;
  children: React.ReactNode;
}

export const Button: React.FC<ButtonProps> = ({
  variant = 'primary',
  size = 'medium',
  disabled = false,
  onClick,
  children
}) => {
  return (
    <button
      className={`btn btn-${variant} btn-${size}`}
      disabled={disabled}
      onClick={onClick}
    >
      {children}
    </button>
  );
};

11. 性能优化 #

11.1 React 优化 #

// 使用 React.memo
const MessageItem = React.memo<MessageItemProps>(({ message }) => {
  return <div>{message.content}</div>;
});

// 使用 useMemo
const filteredMessages = useMemo(() => {
  return messages.filter(msg => msg.role === 'user');
}, [messages]);

// 使用 useCallback
const handleClick = useCallback(() => {
  // 处理逻辑
}, [dependencies]);

11.2 Vue 优化 #

<script setup>
import { computed, watch } from 'vue';

// 计算属性
const filteredUsers = computed(() => {
  return users.value.filter(u => u.active);
});

// 监听器
watch(searchQuery, (newVal) => {
  fetchUsers({ query: newVal });
}, { debounce: 300 });
</script>

12. 测试 #

12.1 React 测试 #

// ChatContainer.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import ChatContainer from './ChatContainer';

test('发送消息', async () => {
  render(<ChatContainer knowledgeBaseId="kb1" />);

  const input = screen.getByPlaceholderText('输入消息...');
  fireEvent.change(input, { target: { value: '测试消息' } });

  const button = screen.getByText('发送');
  fireEvent.click(button);

  await screen.findByText('测试消息');
});

13. 总结 #

本文档介绍了 Ragflow-Plus 前端开发的各个方面:

  • 架构设计:双前端架构(React + Vue.js)
  • 组件开发:函数组件和 Composition API 模式
  • 状态管理:Context API 和 Composables
  • API 集成:统一的 API 客户端封装
  • 国际化:多语言支持
  • 性能优化:React 和 Vue 优化技巧

通过遵循本文档的指南,您可以高效地开发 Ragflow-Plus 前端功能。

访问验证

请输入访问令牌

Token不正确,请重新输入