feat:前端新增初版知识库管理页面
This commit is contained in:
@@ -0,0 +1,300 @@
|
||||
/**
|
||||
* Dify Dataset 客户端 API 模块
|
||||
*
|
||||
* 提供浏览器端调用 Dify 知识库 API 的函数
|
||||
* 通过 Remix API Routes 代理请求
|
||||
*
|
||||
* @module api/dify-dataset/client
|
||||
*/
|
||||
|
||||
import axios from 'axios';
|
||||
import type {
|
||||
DatasetsResponse,
|
||||
DocumentsResponse,
|
||||
SegmentsResponse,
|
||||
Document,
|
||||
OperationResult,
|
||||
} from './types';
|
||||
|
||||
// ============================================================================
|
||||
// 基础配置
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* API 基础 URL
|
||||
* 指向 Remix API Routes(/api/dataset/*)
|
||||
*/
|
||||
const API_URL = '/api/dataset';
|
||||
|
||||
// ============================================================================
|
||||
// 知识库 API
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 获取知识库列表
|
||||
*
|
||||
* @param page - 页码,默认 1
|
||||
* @param limit - 每页数量,默认 20
|
||||
* @returns 知识库列表响应
|
||||
*/
|
||||
export async function fetchDatasets(
|
||||
page: number = 1,
|
||||
limit: number = 20
|
||||
): Promise<DatasetsResponse> {
|
||||
const params = new URLSearchParams({
|
||||
page: page.toString(),
|
||||
limit: limit.toString(),
|
||||
});
|
||||
|
||||
const response = await axios.get<DatasetsResponse>(
|
||||
`${API_URL}/datasets?${params}`,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单个知识库详情
|
||||
*
|
||||
* @param datasetId - 知识库 ID
|
||||
* @returns 知识库详情
|
||||
*/
|
||||
export async function fetchDataset(datasetId: string): Promise<any> {
|
||||
const response = await axios.get(
|
||||
`${API_URL}/datasets/${datasetId}`,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 文档 API
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 获取知识库文档列表
|
||||
*
|
||||
* @param datasetId - 知识库 ID
|
||||
* @param page - 页码,默认 1
|
||||
* @param limit - 每页数量,默认 20
|
||||
* @param keyword - 搜索关键词
|
||||
* @returns 文档列表响应
|
||||
*/
|
||||
export async function fetchDocuments(
|
||||
datasetId: string,
|
||||
page: number = 1,
|
||||
limit: number = 20,
|
||||
keyword?: string
|
||||
): Promise<DocumentsResponse> {
|
||||
const params = new URLSearchParams({
|
||||
page: page.toString(),
|
||||
limit: limit.toString(),
|
||||
});
|
||||
|
||||
if (keyword) {
|
||||
params.append('keyword', keyword);
|
||||
}
|
||||
|
||||
console.log('[Dataset Client] 获取文档列表:', { datasetId, page, limit, keyword });
|
||||
|
||||
const response = await axios.get<DocumentsResponse>(
|
||||
`${API_URL}/datasets/${datasetId}/documents?${params}`,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单个文档详情
|
||||
*
|
||||
* @param datasetId - 知识库 ID
|
||||
* @param documentId - 文档 ID
|
||||
* @returns 文档详情
|
||||
*/
|
||||
export async function fetchDocument(
|
||||
datasetId: string,
|
||||
documentId: string
|
||||
): Promise<Document> {
|
||||
const response = await axios.get<Document>(
|
||||
`${API_URL}/datasets/${datasetId}/documents/${documentId}`,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文档
|
||||
*
|
||||
* @param datasetId - 知识库 ID
|
||||
* @param documentId - 文档 ID
|
||||
* @returns 操作结果
|
||||
*/
|
||||
export async function deleteDocument(
|
||||
datasetId: string,
|
||||
documentId: string
|
||||
): Promise<OperationResult> {
|
||||
console.log('[Dataset Client] 删除文档:', { datasetId, documentId });
|
||||
|
||||
const response = await axios.delete<OperationResult>(
|
||||
`${API_URL}/datasets/${datasetId}/documents/${documentId}`,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用/禁用文档
|
||||
*
|
||||
* @param datasetId - 知识库 ID
|
||||
* @param documentId - 文档 ID
|
||||
* @param enabled - 是否启用
|
||||
* @returns 操作结果
|
||||
*/
|
||||
export async function toggleDocumentStatus(
|
||||
datasetId: string,
|
||||
documentId: string,
|
||||
enabled: boolean
|
||||
): Promise<OperationResult> {
|
||||
console.log('[Dataset Client] 切换文档状态:', { datasetId, documentId, enabled });
|
||||
|
||||
const response = await axios.patch<OperationResult>(
|
||||
`${API_URL}/datasets/${datasetId}/documents/${documentId}/status`,
|
||||
{ enabled },
|
||||
{
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
withCredentials: true,
|
||||
}
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 文档分段 API
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 获取文档分段列表
|
||||
*
|
||||
* @param datasetId - 知识库 ID
|
||||
* @param documentId - 文档 ID
|
||||
* @param page - 页码,默认 1
|
||||
* @param limit - 每页数量,默认 20
|
||||
* @param keyword - 搜索关键词
|
||||
* @returns 分段列表响应
|
||||
*/
|
||||
export async function fetchSegments(
|
||||
datasetId: string,
|
||||
documentId: string,
|
||||
page: number = 1,
|
||||
limit: number = 20,
|
||||
keyword?: string
|
||||
): Promise<SegmentsResponse> {
|
||||
const params = new URLSearchParams({
|
||||
page: page.toString(),
|
||||
limit: limit.toString(),
|
||||
});
|
||||
|
||||
if (keyword) {
|
||||
params.append('keyword', keyword);
|
||||
}
|
||||
|
||||
console.log('[Dataset Client] 获取分段列表:', { datasetId, documentId, page, limit });
|
||||
|
||||
const response = await axios.get<SegmentsResponse>(
|
||||
`${API_URL}/datasets/${datasetId}/documents/${documentId}/segments?${params}`,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除分段
|
||||
*
|
||||
* @param datasetId - 知识库 ID
|
||||
* @param documentId - 文档 ID
|
||||
* @param segmentId - 分段 ID
|
||||
* @returns 操作结果
|
||||
*/
|
||||
export async function deleteSegment(
|
||||
datasetId: string,
|
||||
documentId: string,
|
||||
segmentId: string
|
||||
): Promise<OperationResult> {
|
||||
console.log('[Dataset Client] 删除分段:', { datasetId, documentId, segmentId });
|
||||
|
||||
const response = await axios.delete<OperationResult>(
|
||||
`${API_URL}/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}`,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用/禁用分段
|
||||
*
|
||||
* @param datasetId - 知识库 ID
|
||||
* @param documentId - 文档 ID
|
||||
* @param segmentId - 分段 ID
|
||||
* @param enabled - 是否启用
|
||||
* @returns 操作结果
|
||||
*/
|
||||
export async function toggleSegmentStatus(
|
||||
datasetId: string,
|
||||
documentId: string,
|
||||
segmentId: string,
|
||||
enabled: boolean
|
||||
): Promise<OperationResult> {
|
||||
const response = await axios.patch<OperationResult>(
|
||||
`${API_URL}/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}/status`,
|
||||
{ enabled },
|
||||
{
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
withCredentials: true,
|
||||
}
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 文件上传 API
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 上传文件到知识库
|
||||
*
|
||||
* @param datasetId - 知识库 ID
|
||||
* @param file - 文件对象
|
||||
* @param onProgress - 上传进度回调
|
||||
* @returns 创建的文档信息
|
||||
*/
|
||||
export async function uploadDocument(
|
||||
datasetId: string,
|
||||
file: File,
|
||||
onProgress?: (percent: number) => void
|
||||
): Promise<any> {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('data', JSON.stringify({
|
||||
indexing_technique: 'high_quality',
|
||||
process_rule: {
|
||||
mode: 'automatic',
|
||||
},
|
||||
}));
|
||||
|
||||
console.log('[Dataset Client] 上传文档:', { datasetId, fileName: file.name });
|
||||
|
||||
const response = await axios.post(
|
||||
`${API_URL}/datasets/${datasetId}/documents/create-by-file`,
|
||||
formData,
|
||||
{
|
||||
withCredentials: true,
|
||||
onUploadProgress: (progressEvent) => {
|
||||
if (progressEvent.total && onProgress) {
|
||||
const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
|
||||
onProgress(percent);
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Dify Dataset API 模块统一导出
|
||||
*
|
||||
* @module api/dify-dataset
|
||||
*/
|
||||
|
||||
// 类型导出
|
||||
export type {
|
||||
Dataset,
|
||||
DatasetsResponse,
|
||||
Document,
|
||||
DocumentsResponse,
|
||||
Segment,
|
||||
SegmentsResponse,
|
||||
IndexingStatus,
|
||||
OperationResult,
|
||||
CreateDocumentResponse,
|
||||
UploadProgress,
|
||||
} from './types';
|
||||
|
||||
// 客户端 API 导出
|
||||
export {
|
||||
// 知识库
|
||||
fetchDatasets,
|
||||
fetchDataset,
|
||||
// 文档
|
||||
fetchDocuments,
|
||||
fetchDocument,
|
||||
deleteDocument,
|
||||
toggleDocumentStatus,
|
||||
uploadDocument,
|
||||
// 分段
|
||||
fetchSegments,
|
||||
deleteSegment,
|
||||
toggleSegmentStatus,
|
||||
} from './client';
|
||||
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* Dify Dataset API 类型定义
|
||||
*
|
||||
* @module api/dify-dataset/types
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// 知识库类型
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 知识库信息
|
||||
*/
|
||||
export interface Dataset {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
permission: 'only_me' | 'all_team_members';
|
||||
data_source_type: 'upload_file' | 'notion_import' | 'website_crawl';
|
||||
indexing_technique: 'high_quality' | 'economy';
|
||||
app_count: number;
|
||||
document_count: number;
|
||||
word_count: number;
|
||||
created_by: string;
|
||||
created_at: number;
|
||||
updated_by: string;
|
||||
updated_at: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 知识库列表响应
|
||||
*/
|
||||
export interface DatasetsResponse {
|
||||
data: Dataset[];
|
||||
has_more: boolean;
|
||||
limit: number;
|
||||
total: number;
|
||||
page: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 文档类型
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 文档索引状态
|
||||
*/
|
||||
export type IndexingStatus =
|
||||
| 'waiting'
|
||||
| 'parsing'
|
||||
| 'cleaning'
|
||||
| 'splitting'
|
||||
| 'indexing'
|
||||
| 'paused'
|
||||
| 'error'
|
||||
| 'completed';
|
||||
|
||||
/**
|
||||
* 文档信息
|
||||
*/
|
||||
export interface Document {
|
||||
id: string;
|
||||
position: number;
|
||||
data_source_type: 'upload_file' | 'notion_import' | 'website_crawl';
|
||||
data_source_info: {
|
||||
upload_file_id?: string;
|
||||
notion_page_id?: string;
|
||||
website_url?: string;
|
||||
};
|
||||
dataset_process_rule_id: string;
|
||||
name: string;
|
||||
created_from: string;
|
||||
created_by: string;
|
||||
created_at: number;
|
||||
tokens: number;
|
||||
indexing_status: IndexingStatus;
|
||||
error?: string;
|
||||
enabled: boolean;
|
||||
disabled_at?: number;
|
||||
disabled_by?: string;
|
||||
archived: boolean;
|
||||
display_status: string;
|
||||
word_count: number;
|
||||
hit_count: number;
|
||||
doc_form: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文档列表响应
|
||||
*/
|
||||
export interface DocumentsResponse {
|
||||
data: Document[];
|
||||
has_more: boolean;
|
||||
limit: number;
|
||||
total: number;
|
||||
page: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 文档分段类型
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 文档分段
|
||||
*/
|
||||
export interface Segment {
|
||||
id: string;
|
||||
position: number;
|
||||
document_id: string;
|
||||
content: string;
|
||||
answer?: string;
|
||||
word_count: number;
|
||||
tokens: number;
|
||||
keywords: string[];
|
||||
index_node_id: string;
|
||||
index_node_hash: string;
|
||||
hit_count: number;
|
||||
enabled: boolean;
|
||||
disabled_at?: number;
|
||||
disabled_by?: string;
|
||||
status: 'waiting' | 'completed' | 'error' | 'indexing';
|
||||
created_by: string;
|
||||
created_at: number;
|
||||
indexing_at?: number;
|
||||
completed_at?: number;
|
||||
error?: string;
|
||||
stopped_at?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分段列表响应
|
||||
*/
|
||||
export interface SegmentsResponse {
|
||||
data: Segment[];
|
||||
has_more: boolean;
|
||||
limit: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 操作响应类型
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* 通用操作结果
|
||||
*/
|
||||
export interface OperationResult {
|
||||
result: 'success' | 'error';
|
||||
message?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文档响应
|
||||
*/
|
||||
export interface CreateDocumentResponse {
|
||||
document: Document;
|
||||
batch: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文档上传进度
|
||||
*/
|
||||
export interface UploadProgress {
|
||||
loaded: number;
|
||||
total: number;
|
||||
percent: number;
|
||||
}
|
||||
Reference in New Issue
Block a user