feat: 添加对话应用选择和知识库切换功能
- 新增对话应用管理模块(dify-chat-apps),支持获取和切换对话应用 - 优化对话应用切换后自动刷新会话列表功能 - 知识库管理页面新增下拉选择器,支持切换不同知识库 - API 层支持 app_id 参数传递,实现多应用会话隔离 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -91,6 +91,7 @@ export async function action({ request }: ActionFunctionArgs) {
|
||||
files,
|
||||
conversation_id: conversationId,
|
||||
response_mode: responseMode,
|
||||
app_id: appId, // 支持前端传递应用 ID
|
||||
} = body;
|
||||
|
||||
console.log('客戶端調用remix路由_Chat Messages API - 收到请求:', {
|
||||
@@ -98,6 +99,7 @@ export async function action({ request }: ActionFunctionArgs) {
|
||||
queryPreview: query?.substring(0, 100) + (query?.length > 100 ? '...' : ''),
|
||||
conversationId,
|
||||
responseMode,
|
||||
appId, // 记录应用 ID
|
||||
hasInputs: !!inputs,
|
||||
hasFiles: !!files && files.length > 0,
|
||||
filesCount: files?.length || 0,
|
||||
@@ -110,7 +112,8 @@ export async function action({ request }: ActionFunctionArgs) {
|
||||
responseMode,
|
||||
conversationId,
|
||||
files,
|
||||
frontendJWT // 传递 JWT
|
||||
frontendJWT, // 传递 JWT
|
||||
appId // 传递应用 ID
|
||||
);
|
||||
|
||||
// 对于流式响应,直接返回流
|
||||
|
||||
@@ -23,7 +23,13 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
||||
);
|
||||
}
|
||||
|
||||
const data = await difyClient.getConversations(frontendJWT);
|
||||
// 从 URL 参数获取 app_id
|
||||
const url = new URL(request.url);
|
||||
const appId = url.searchParams.get('app_id') || undefined;
|
||||
|
||||
console.log('[API] Conversations - 获取会话列表:', { appId });
|
||||
|
||||
const data = await difyClient.getConversations(frontendJWT, appId);
|
||||
|
||||
return json(data, {
|
||||
headers: {
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import { type LoaderFunctionArgs, json } from '@remix-run/node';
|
||||
import { API_BASE_URL } from '~/config/api-config';
|
||||
import { getUserSession } from '~/api/login/auth.server';
|
||||
import { request as backendRequest } from '~/api/axios-client';
|
||||
|
||||
export async function loader({ request, params }: LoaderFunctionArgs) {
|
||||
return json({ error: 'Method not allowed' }, { status: 405 });
|
||||
@@ -35,11 +34,11 @@ export async function action({ request, params }: LoaderFunctionArgs) {
|
||||
console.log(`[API V3] Update Area Dataset: ${id}`, body);
|
||||
|
||||
const apiUrl = `${API_BASE_URL}/v3/dify/area-datasets/${id}`;
|
||||
const response = await backendRequest(apiUrl, {
|
||||
const response = await fetch(apiUrl, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
Authorization: `Bearer ${frontendJWT}`,
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${frontendJWT}`,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
@@ -52,10 +51,11 @@ export async function action({ request, params }: LoaderFunctionArgs) {
|
||||
console.log(`[API V3] Delete Area Dataset: ${id}`);
|
||||
|
||||
const apiUrl = `${API_BASE_URL}/v3/dify/area-datasets/${id}`;
|
||||
const response = await backendRequest(apiUrl, {
|
||||
const response = await fetch(apiUrl, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
Authorization: `Bearer ${frontendJWT}`,
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${frontendJWT}`,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
import { type LoaderFunctionArgs, json } from '@remix-run/node';
|
||||
import { API_BASE_URL } from '~/config/api-config';
|
||||
import { getUserSession } from '~/api/login/auth.server';
|
||||
import { request as backendRequest } from '~/api/axios-client';
|
||||
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
try {
|
||||
@@ -22,10 +21,11 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
||||
|
||||
// 转发请求到后端
|
||||
const apiUrl = `${API_BASE_URL}/v3/dify/area-datasets/areas`;
|
||||
const response = await backendRequest(apiUrl, {
|
||||
const response = await fetch(apiUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${frontendJWT}`,
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${frontendJWT}`,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
import { type LoaderFunctionArgs, json } from '@remix-run/node';
|
||||
import { API_BASE_URL } from '~/config/api-config';
|
||||
import { getUserSession } from '~/api/login/auth.server';
|
||||
import { request as backendRequest } from '~/api/axios-client';
|
||||
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
try {
|
||||
@@ -22,10 +21,11 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
||||
|
||||
// 转发请求到后端
|
||||
const apiUrl = `${API_BASE_URL}/v3/dify/area-datasets/my`;
|
||||
const response = await backendRequest(apiUrl, {
|
||||
const response = await fetch(apiUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${frontendJWT}`,
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${frontendJWT}`,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import { type LoaderFunctionArgs, json } from '@remix-run/node';
|
||||
import { API_BASE_URL } from '~/config/api-config';
|
||||
import { getUserSession } from '~/api/login/auth.server';
|
||||
import { request as backendRequest } from '~/api/axios-client';
|
||||
|
||||
/**
|
||||
* GET - 获取所有知识库绑定列表
|
||||
@@ -40,10 +39,11 @@ export async function loader({ request }: LoaderFunctionArgs) {
|
||||
|
||||
// 转发请求到后端
|
||||
const apiUrl = `${API_BASE_URL}/v3/dify/area-datasets?${params}`;
|
||||
const response = await backendRequest(apiUrl, {
|
||||
const response = await fetch(apiUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${frontendJWT}`,
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${frontendJWT}`,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -78,11 +78,11 @@ export async function action({ request }: LoaderFunctionArgs) {
|
||||
|
||||
// 转发创建请求到后端
|
||||
const apiUrl = `${API_BASE_URL}/v3/dify/area-datasets`;
|
||||
const response = await backendRequest(apiUrl, {
|
||||
const response = await fetch(apiUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${frontendJWT}`,
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${frontendJWT}`,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* GET /api/v3/dify/chat-apps/default - 获取默认对话应用
|
||||
*
|
||||
* 转发请求到后端 API,后端从配置文件读取默认对话应用
|
||||
* 参考文档:docs/new-dify/dify_api_doc.md - 对话应用多实例支持
|
||||
*/
|
||||
|
||||
import { LoaderFunctionArgs, json } from '@remix-run/node';
|
||||
import { API_BASE_URL } from '~/config/api-config';
|
||||
import { getUserSession } from '~/api/login/auth.server';
|
||||
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
try {
|
||||
const { frontendJWT } = await getUserSession(request);
|
||||
|
||||
if (!frontendJWT) {
|
||||
return json(
|
||||
{ code: 401, message: 'JWT认证失败,请重新登录', data: null },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
console.log('[API] Get Default Chat App - Forwarding to backend');
|
||||
|
||||
// 转发请求到后端
|
||||
const apiUrl = `${API_BASE_URL}/v3/dify/chat-apps/default`;
|
||||
const response = await fetch(apiUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${frontendJWT}`,
|
||||
},
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
console.log('[API] Get Default Chat App - Backend response:', data);
|
||||
|
||||
return json(data, { status: response.status });
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('[API] Get Default Chat App - Error:', error.message);
|
||||
return json(
|
||||
{ code: 500, message: error.message || 'Failed to get default chat app', data: null },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* GET /api/v3/dify/chat-apps/my - 获取当前用户可访问的对话应用列表
|
||||
*
|
||||
* 转发请求到后端 API,后端从配置文件读取对话应用列表
|
||||
* 参考文档:docs/new-dify/dify_api_doc.md - 对话应用多实例支持
|
||||
*/
|
||||
|
||||
import { LoaderFunctionArgs, json } from '@remix-run/node';
|
||||
import { API_BASE_URL } from '~/config/api-config';
|
||||
import { getUserSession } from '~/api/login/auth.server';
|
||||
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
try {
|
||||
const { frontendJWT } = await getUserSession(request);
|
||||
|
||||
if (!frontendJWT) {
|
||||
return json(
|
||||
{ code: 401, message: 'JWT认证失败,请重新登录', data: { data: [], total: 0 } },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
console.log('[API] Get My Chat Apps - Forwarding to backend');
|
||||
|
||||
// 转发请求到后端 - 使用正确的接口路径
|
||||
// 根据文档:GET /api/v3/dify/chat-apps
|
||||
const apiUrl = `${API_BASE_URL}/v3/dify/chat-apps`;
|
||||
const response = await fetch(apiUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${frontendJWT}`,
|
||||
},
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
console.log('[API] Get My Chat Apps - Backend response:', data);
|
||||
|
||||
return json(data, { status: response.status });
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('[API] Get My Chat Apps - Error:', error.message);
|
||||
return json(
|
||||
{ code: 500, message: error.message || 'Failed to get chat apps', data: { data: [], total: 0 } },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,42 @@
|
||||
import { Tabs } from 'antd';
|
||||
import DatasetManager from "~/components/dify-dataset-manager";
|
||||
import AreaDatasetConfig from "~/components/dify-dataset-manager/area-dataset-config";
|
||||
import { Spin } from 'antd';
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
/**
|
||||
* 知识库管理首页 - 带标签页导航
|
||||
* 标签1: 知识库列表 - 进入单个知识库管理
|
||||
* 标签2: 知识库配置管理 - 地区-知识库绑定管理
|
||||
* 知识库管理页面
|
||||
* 动态加载 DatasetManager 组件避免 SSR 问题
|
||||
*/
|
||||
export default function DatasetManagerIndex() {
|
||||
const items = [
|
||||
{
|
||||
key: 'dataset-list',
|
||||
label: '知识库列表',
|
||||
children: <DatasetManager />,
|
||||
},
|
||||
{
|
||||
key: 'area-config',
|
||||
label: '知识库配置管理',
|
||||
children: <AreaDatasetConfig />,
|
||||
},
|
||||
];
|
||||
export default function DatasetManagerPage() {
|
||||
const [DatasetManager, setDatasetManager] = useState<React.ComponentType | null>(null);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
// 使用Tabs作为顶层导航,默认选中第一个
|
||||
const defaultActiveTab = 'dataset-list';
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
// 只在客户端动态导入组件
|
||||
import("~/components/dify-dataset-manager").then((mod) => {
|
||||
setDatasetManager(() => mod.default);
|
||||
}).catch(err => {
|
||||
console.error('加载知识库管理组件失败:', err);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="dataset-manager-container">
|
||||
<Tabs
|
||||
defaultActiveKey={defaultActiveTab}
|
||||
items={items}
|
||||
className="dataset-manager-tabs"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
// 服务端渲染时显示简单加载状态
|
||||
if (!mounted) {
|
||||
return (
|
||||
<div style={{ padding: '40px', textAlign: 'center' }}>
|
||||
<div>加载中...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 客户端加载中
|
||||
if (!DatasetManager) {
|
||||
return (
|
||||
<div style={{ padding: '40px', textAlign: 'center' }}>
|
||||
<Spin size="large" />
|
||||
<p style={{ marginTop: 16 }}>正在加载知识库管理...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <DatasetManager />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user