From 6d641bb309b10b1f136412098a2bd9794cd108be Mon Sep 17 00:00:00 2001 From: susie-laptop Date: Tue, 1 Apr 2025 22:22:53 -0400 Subject: [PATCH] [dev]add docs list api --- .../KnowledgeBase/Detail/SettingsTab.jsx | 26 ++-- .../Detail/components/DocumentList.jsx | 112 +++++++++++------- .../Detail/components/FileUploadModal.jsx | 6 +- src/services/mockApi.js | 72 ++++++++++- .../knowledgeBase/knowledgeBase.slice.js | 51 ++++++++ .../knowledgeBase/knowledgeBase.thunks.js | 59 +++++++++ src/utils/dateUtils.js | 48 ++++++++ 7 files changed, 313 insertions(+), 61 deletions(-) create mode 100644 src/utils/dateUtils.js diff --git a/src/pages/KnowledgeBase/Detail/SettingsTab.jsx b/src/pages/KnowledgeBase/Detail/SettingsTab.jsx index 8dba641..abca74b 100644 --- a/src/pages/KnowledgeBase/Detail/SettingsTab.jsx +++ b/src/pages/KnowledgeBase/Detail/SettingsTab.jsx @@ -6,6 +6,7 @@ import { updateKnowledgeBase, deleteKnowledgeBase, changeKnowledgeBaseType, + getKnowledgeBaseDocuments, } from '../../../store/knowledgeBase/knowledgeBase.thunks'; // 导入拆分的组件 @@ -14,6 +15,7 @@ import KnowledgeBaseForm from './components/KnowledgeBaseForm'; import DeleteConfirmModal from './components/DeleteConfirmModal'; import UserPermissionsManager from './components/UserPermissionsManager'; import FileUploadModal from './components/FileUploadModal'; +import DocumentList from './components/DocumentList'; // 部门和组别的映射关系 const departmentGroups = { @@ -50,6 +52,13 @@ export default function SettingsTab({ knowledgeBase }) { const [availableGroups, setAvailableGroups] = useState([]); const [showUploadModal, setShowUploadModal] = useState(false); + // 获取文档列表 + useEffect(() => { + if (knowledgeBase?.id) { + dispatch(getKnowledgeBaseDocuments({ knowledge_base_id: knowledgeBase.id })); + } + }, [dispatch, knowledgeBase?.id]); + // 当部门变化时,更新可选的组别 useEffect(() => { if (knowledgeBaseForm.department && departmentGroups[knowledgeBaseForm.department]) { @@ -336,16 +345,17 @@ export default function SettingsTab({ knowledgeBase }) { availableGroups={availableGroups} /> - {/* Document Upload Section */} + {/* Document Management Section */}
-
文档管理
-

- 上传文档到知识库,支持PDF、Word、Excel、TXT、Markdown和CSV等格式。 -

- +
+
文档管理
+ +
+ +
diff --git a/src/pages/KnowledgeBase/Detail/components/DocumentList.jsx b/src/pages/KnowledgeBase/Detail/components/DocumentList.jsx index 747832a..8f78038 100644 --- a/src/pages/KnowledgeBase/Detail/components/DocumentList.jsx +++ b/src/pages/KnowledgeBase/Detail/components/DocumentList.jsx @@ -1,12 +1,42 @@ import React from 'react'; -import SvgIcon from '../../../../components/SvgIcon'; +import { useDispatch, useSelector } from 'react-redux'; +import { formatDate } from '../../../../utils/dateUtils'; +import { deleteKnowledgeBaseDocument } from '../../../../store/knowledgeBase/knowledgeBase.thunks'; /** - * 文档列表组件 + * 知识库文档列表组件 */ -const DocumentList = ({ documents, selectedDocuments, onSelectAll, onSelectDocument, onDeleteDocument, selectAll }) => { - if (documents.length === 0) { - return
暂无数据集,请上传数据集
; +const DocumentList = ({ knowledgeBaseId }) => { + const dispatch = useDispatch(); + const { items, loading, pagination } = useSelector((state) => state.knowledgeBase.documents); + + const handleDeleteDocument = (documentId) => { + if (window.confirm('确定要删除此文档吗?')) { + dispatch( + deleteKnowledgeBaseDocument({ + knowledge_base_id: knowledgeBaseId, + document_id: documentId, + }) + ); + } + }; + + if (loading) { + return ( +
+
+ 加载中... +
+
+ ); + } + + if (items.length === 0) { + return ( +
+

暂无文档,请上传文档

+
+ ); } return ( @@ -14,59 +44,51 @@ const DocumentList = ({ documents, selectedDocuments, onSelectAll, onSelectDocum - - - - + + - + - {documents.map((doc) => ( + {items.map((doc) => ( + + + - - - - - ))}
-
- -
-
名称描述大小文档名称创建时间 更新时间 - 操作 - 操作
{doc.document_name}{formatDateTime(doc.create_time)}{formatDateTime(doc.update_time)} -
- onSelectDocument(doc.id)} - /> -
-
{doc.name}{doc.description}{doc.size}{new Date(doc.update_time).toLocaleDateString()} -
- -
+
+ + {pagination.total > 0 && ( +
+

共 {pagination.total} 条记录

+
+ )} ); }; +// Helper function to format date string +const formatDateTime = (dateString) => { + if (!dateString) return '-'; + + // If the utility function exists, use it, otherwise format manually + try { + return formatDate(dateString); + } catch (error) { + const date = new Date(dateString); + return date.toLocaleString(); + } +}; + export default DocumentList; diff --git a/src/pages/KnowledgeBase/Detail/components/FileUploadModal.jsx b/src/pages/KnowledgeBase/Detail/components/FileUploadModal.jsx index 0839ceb..974d21d 100644 --- a/src/pages/KnowledgeBase/Detail/components/FileUploadModal.jsx +++ b/src/pages/KnowledgeBase/Detail/components/FileUploadModal.jsx @@ -1,6 +1,6 @@ import React, { useRef, useState, useEffect } from 'react'; import { useDispatch } from 'react-redux'; -import { uploadDocument, getKnowledgeBaseById } from '../../../../store/knowledgeBase/knowledgeBase.thunks'; +import { uploadDocument, getKnowledgeBaseDocuments } from '../../../../store/knowledgeBase/knowledgeBase.thunks'; /** * 文件上传模态框组件 @@ -68,8 +68,8 @@ const FileUploadModal = ({ show, knowledgeBaseId, onClose }) => { }) ).unwrap(); - // 成功上传后刷新知识库信息以更新文件列表 - dispatch(getKnowledgeBaseById(knowledgeBaseId)); + // 成功上传后刷新文档列表 + dispatch(getKnowledgeBaseDocuments({ knowledge_base_id: knowledgeBaseId })); // Reset the file input resetFileInput(); diff --git a/src/services/mockApi.js b/src/services/mockApi.js index 5c758da..0eae4f9 100644 --- a/src/services/mockApi.js +++ b/src/services/mockApi.js @@ -522,8 +522,8 @@ const mockPermissionApi = { }; // Mock API functions -export const mockGet = async (url, config = {}) => { - console.log(`[MOCK API] GET ${url}`, config); +export const mockGet = async (url, params = {}) => { + console.log(`[MOCK API] GET ${url}`, params); // Simulate network delay await new Promise((resolve) => setTimeout(resolve, 500)); @@ -537,7 +537,6 @@ export const mockGet = async (url, config = {}) => { // Get knowledge bases if (url === '/knowledge-bases/') { - const params = config.params || { page: 1, page_size: 10 }; const result = paginate(knowledgeBases, params.page_size, params.page); return { @@ -576,7 +575,6 @@ export const mockGet = async (url, config = {}) => { // Get chat history if (url === '/chat-history/') { - const params = config.params || { page: 1, page_size: 10 }; const result = mockGetChatHistory(params); return { data: { @@ -620,7 +618,7 @@ export const mockGet = async (url, config = {}) => { // Knowledge base search if (url === '/knowledge-bases/search/') { - const { keyword = '', page = 1, page_size = 10 } = config.params || {}; + const { keyword = '', page = 1, page_size = 10 } = params.params || {}; const filtered = knowledgeBases.filter( (kb) => kb.name.toLowerCase().includes(keyword.toLowerCase()) || @@ -671,6 +669,54 @@ export const mockGet = async (url, config = {}) => { }; } + // 获取知识库文档列表 + if (url.match(/\/knowledge-bases\/([^/]+)\/documents\//)) { + const knowledge_base_id = url.match(/\/knowledge-bases\/([^/]+)\/documents\//)[1]; + const page = params?.params?.page || 1; + const page_size = params?.params?.page_size || 10; + + // 模拟文档列表数据 + const mockDocuments = [ + { + id: 'df6d2c2b-895c-4c56-83c8-1644345e654d', + document_id: '772044ae-0ecf-11f0-8082-0242ac120002', + document_name: '产品说明书.pdf', + external_id: '772044ae-0ecf-11f0-8082-0242ac120002', + create_time: '2023-04-01 08:01:06', + update_time: '2023-04-01 08:01:06', + }, + { + id: 'eba8f519-debf-461c-b4fd-87177d94bece', + document_id: '429a2c08-0ea3-11f0-bdec-0242ac120002', + document_name: '用户手册.docx', + external_id: '429a2c08-0ea3-11f0-bdec-0242ac120002', + create_time: '2023-04-01 02:44:38', + update_time: '2023-04-01 02:44:38', + }, + { + id: '7a9e4c31-5b2d-437e-9a8f-2b5c7e8a9d1e', + document_id: 'c9a8f2b5-7e8a-9d1e-7a9e-4c315b2d437e', + document_name: '技术文档.txt', + external_id: 'c9a8f2b5-7e8a-9d1e-7a9e-4c315b2d437e', + create_time: '2023-03-15 10:23:45', + update_time: '2023-03-15 10:23:45', + }, + ]; + + return { + data: { + code: 200, + message: '获取文档列表成功', + data: { + total: mockDocuments.length, + page: page, + page_size: page_size, + items: mockDocuments, + }, + }, + }; + } + throw { response: { status: 404, data: { message: 'Not found' } } }; }; @@ -986,6 +1032,22 @@ export const mockDelete = async (url) => { return { data: mockDeleteChat(id) }; } + // 删除知识库文档 + if (url.match(/\/knowledge-bases\/([^/]+)\/documents\/([^/]+)/)) { + const matches = url.match(/\/knowledge-bases\/([^/]+)\/documents\/([^/]+)/); + const knowledge_base_id = matches[1]; + const document_id = matches[2]; + + console.log(`[MOCK API] Deleting document ${document_id} from knowledge base ${knowledge_base_id}`); + + return { + data: { + code: 200, + message: '文档删除成功', + }, + }; + } + throw { response: { status: 404, data: { message: 'Not found' } } }; }; diff --git a/src/store/knowledgeBase/knowledgeBase.slice.js b/src/store/knowledgeBase/knowledgeBase.slice.js index 65defd3..95c45dd 100644 --- a/src/store/knowledgeBase/knowledgeBase.slice.js +++ b/src/store/knowledgeBase/knowledgeBase.slice.js @@ -9,6 +9,8 @@ import { requestKnowledgeBaseAccess, getKnowledgeBaseById, uploadDocument, + getKnowledgeBaseDocuments, + deleteKnowledgeBaseDocument, } from './knowledgeBase.thunks'; const initialState = { @@ -29,6 +31,16 @@ const initialState = { editStatus: 'idle', requestAccessStatus: 'idle', uploadStatus: 'idle', + documents: { + items: [], + loading: false, + error: null, + pagination: { + total: 0, + page: 1, + page_size: 10, + }, + }, }; const knowledgeBaseSlice = createSlice({ @@ -194,6 +206,45 @@ const knowledgeBaseSlice = createSlice({ .addCase(uploadDocument.rejected, (state, action) => { state.uploadStatus = 'failed'; state.error = action.payload || 'Failed to upload document'; + }) + + // 获取知识库文档列表 + .addCase(getKnowledgeBaseDocuments.pending, (state) => { + state.documents.loading = true; + state.documents.error = null; + }) + .addCase(getKnowledgeBaseDocuments.fulfilled, (state, action) => { + state.documents.loading = false; + state.documents.items = action.payload.items || []; + state.documents.pagination = { + total: action.payload.total || 0, + page: action.payload.page || 1, + page_size: action.payload.page_size || 10, + }; + }) + .addCase(getKnowledgeBaseDocuments.rejected, (state, action) => { + state.documents.loading = false; + state.documents.error = action.payload || 'Failed to get documents'; + }) + + // 删除知识库文档 + .addCase(deleteKnowledgeBaseDocument.pending, (state) => { + state.documents.loading = true; + state.documents.error = null; + }) + .addCase(deleteKnowledgeBaseDocument.fulfilled, (state, action) => { + state.documents.loading = false; + const deletedDocId = action.payload; + state.documents.items = state.documents.items.filter( + (doc) => doc.document_id !== deletedDocId + ); + if (state.documents.pagination.total > 0) { + state.documents.pagination.total -= 1; + } + }) + .addCase(deleteKnowledgeBaseDocument.rejected, (state, action) => { + state.documents.loading = false; + state.documents.error = action.payload || 'Failed to delete document'; }); }, }); diff --git a/src/store/knowledgeBase/knowledgeBase.thunks.js b/src/store/knowledgeBase/knowledgeBase.thunks.js index 663f68c..a9f95b9 100644 --- a/src/store/knowledgeBase/knowledgeBase.thunks.js +++ b/src/store/knowledgeBase/knowledgeBase.thunks.js @@ -237,3 +237,62 @@ export const uploadDocument = createAsyncThunk( } } ); + +/** + * Get documents list for a knowledge base + * @param {Object} params - Parameters + * @param {string} params.knowledge_base_id - Knowledge base ID + * @param {number} params.page - Page number (default: 1) + * @param {number} params.page_size - Page size (default: 10) + */ +export const getKnowledgeBaseDocuments = createAsyncThunk( + 'knowledgeBase/getDocuments', + async ({ knowledge_base_id, page = 1, page_size = 10 }, { rejectWithValue }) => { + try { + const response = await get(`/knowledge-bases/${knowledge_base_id}/documents/`, { + params: { page, page_size } + }); + + // 处理返回格式 + if (response.data && response.data.code === 200) { + return response.data.data; + } + + return response.data; + } catch (error) { + return rejectWithValue(error.response?.data?.message || '获取文档列表失败'); + } + } +); + +/** + * Delete a document from a knowledge base + * @param {Object} params - Parameters + * @param {string} params.knowledge_base_id - Knowledge base ID + * @param {string} params.document_id - Document ID + */ +export const deleteKnowledgeBaseDocument = createAsyncThunk( + 'knowledgeBase/deleteDocument', + async ({ knowledge_base_id, document_id }, { rejectWithValue, dispatch }) => { + try { + await del(`/knowledge-bases/${knowledge_base_id}/documents/${document_id}/`); + + dispatch( + showNotification({ + type: 'success', + message: '文档删除成功', + }) + ); + + return document_id; + } catch (error) { + dispatch( + showNotification({ + type: 'danger', + message: error.response?.data?.message || '文档删除失败', + }) + ); + return rejectWithValue(error.response?.data?.message || '文档删除失败'); + } + } +); diff --git a/src/utils/dateUtils.js b/src/utils/dateUtils.js new file mode 100644 index 0000000..9fd7a4a --- /dev/null +++ b/src/utils/dateUtils.js @@ -0,0 +1,48 @@ +/** + * 格式化日期时间 + * @param {string} dateString - 日期字符串 + * @returns {string} 格式化后的日期字符串 + */ +export const formatDate = (dateString) => { + if (!dateString) return '-'; + + const date = new Date(dateString); + + // 检查日期是否有效 + if (isNaN(date.getTime())) { + return dateString; + } + + // 格式化为 YYYY-MM-DD HH:MM:SS + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + const seconds = String(date.getSeconds()).padStart(2, '0'); + + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; +}; + +/** + * 格式化日期(仅日期部分) + * @param {string} dateString - 日期字符串 + * @returns {string} 格式化后的日期字符串 + */ +export const formatDateOnly = (dateString) => { + if (!dateString) return '-'; + + const date = new Date(dateString); + + // 检查日期是否有效 + if (isNaN(date.getTime())) { + return dateString; + } + + // 格式化为 YYYY-MM-DD + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + + return `${year}-${month}-${day}`; +};