From ba53185050b146d17c1943e7e51a49c1a0b5c692 Mon Sep 17 00:00:00 2001 From: susie-laptop Date: Tue, 1 Apr 2025 22:05:43 -0400 Subject: [PATCH 1/2] [dev]add upload file api --- .../KnowledgeBase/Detail/SettingsTab.jsx | 24 ++- .../Detail/components/FileUploadModal.jsx | 149 ++++++++++++------ src/services/mockApi.js | 22 ++- .../knowledgeBase/knowledgeBase.slice.js | 14 ++ .../knowledgeBase/knowledgeBase.thunks.js | 45 +++++- 5 files changed, 202 insertions(+), 52 deletions(-) diff --git a/src/pages/KnowledgeBase/Detail/SettingsTab.jsx b/src/pages/KnowledgeBase/Detail/SettingsTab.jsx index 98454f6..8dba641 100644 --- a/src/pages/KnowledgeBase/Detail/SettingsTab.jsx +++ b/src/pages/KnowledgeBase/Detail/SettingsTab.jsx @@ -13,6 +13,7 @@ import Breadcrumb from './components/Breadcrumb'; import KnowledgeBaseForm from './components/KnowledgeBaseForm'; import DeleteConfirmModal from './components/DeleteConfirmModal'; import UserPermissionsManager from './components/UserPermissionsManager'; +import FileUploadModal from './components/FileUploadModal'; // 部门和组别的映射关系 const departmentGroups = { @@ -47,6 +48,7 @@ export default function SettingsTab({ knowledgeBase }) { const [isSubmitting, setIsSubmitting] = useState(false); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const [availableGroups, setAvailableGroups] = useState([]); + const [showUploadModal, setShowUploadModal] = useState(false); // 当部门变化时,更新可选的组别 useEffect(() => { @@ -93,7 +95,7 @@ export default function SettingsTab({ knowledgeBase }) { allowed = ['admin', 'member', 'private'].includes(value); } else { // 普通成员只能选择公共和private - allowed = ['admin', 'private'].includes(value); + allowed = ['admin', 'private'].includes(value); } if (!allowed) { @@ -334,6 +336,26 @@ export default function SettingsTab({ knowledgeBase }) { availableGroups={availableGroups} /> + {/* Document Upload Section */} +
+
+
文档管理
+

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

+ +
+
+ + {/* File Upload Modal */} + setShowUploadModal(false)} + knowledgeBaseId={knowledgeBase.id} + /> + {/* User Permissions Manager */} {/* */} diff --git a/src/pages/KnowledgeBase/Detail/components/FileUploadModal.jsx b/src/pages/KnowledgeBase/Detail/components/FileUploadModal.jsx index 915f85e..0839ceb 100644 --- a/src/pages/KnowledgeBase/Detail/components/FileUploadModal.jsx +++ b/src/pages/KnowledgeBase/Detail/components/FileUploadModal.jsx @@ -1,23 +1,17 @@ -import React, { useRef, useEffect } from 'react'; +import React, { useRef, useState, useEffect } from 'react'; +import { useDispatch } from 'react-redux'; +import { uploadDocument, getKnowledgeBaseById } from '../../../../store/knowledgeBase/knowledgeBase.thunks'; /** * 文件上传模态框组件 */ -const FileUploadModal = ({ - show, - newFile, - fileErrors, - isSubmitting, - onClose, - onDescriptionChange, - onFileChange, - onFileDrop, - onDragOver, - onUploadAreaClick, - onUpload, -}) => { +const FileUploadModal = ({ show, knowledgeBaseId, onClose }) => { + const dispatch = useDispatch(); const fileInputRef = useRef(null); const modalRef = useRef(null); + const [selectedFile, setSelectedFile] = useState(null); + const [isUploading, setIsUploading] = useState(false); + const [fileError, setFileError] = useState(''); // 处理上传区域点击事件 const handleUploadAreaClick = () => { @@ -28,13 +22,76 @@ const FileUploadModal = ({ const handleDragOver = (e) => { e.preventDefault(); e.stopPropagation(); - onDragOver?.(e); }; const handleDrop = (e) => { e.preventDefault(); e.stopPropagation(); - onFileDrop?.(e); + + if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { + handleFileSelected(e.dataTransfer.files[0]); + } + }; + + const handleFileChange = (e) => { + if (e.target.files && e.target.files.length > 0) { + handleFileSelected(e.target.files[0]); + } + }; + + const handleFileSelected = (file) => { + setFileError(''); + setSelectedFile(file); + }; + + const resetFileInput = () => { + setSelectedFile(null); + setFileError(''); + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + }; + + const handleUpload = async () => { + if (!selectedFile) { + setFileError('请选择要上传的文件'); + return; + } + + setIsUploading(true); + + try { + await dispatch( + uploadDocument({ + knowledge_base_id: knowledgeBaseId, + file: selectedFile, + }) + ).unwrap(); + + // 成功上传后刷新知识库信息以更新文件列表 + dispatch(getKnowledgeBaseById(knowledgeBaseId)); + + // Reset the file input + resetFileInput(); + + // 不要在上传成功后关闭模态框,允许用户继续上传或手动关闭 + } catch (error) { + console.error('Upload failed:', error); + setFileError('文件上传失败: ' + (error?.message || '未知错误')); + + // 清空选中的文件 + resetFileInput(); + } finally { + setIsUploading(false); + } + }; + + const handleClose = () => { + // 只有在非上传状态才允许关闭 + if (!isUploading) { + resetFileInput(); + onClose(); + } }; // 清理函数 @@ -78,64 +135,60 @@ const FileUploadModal = ({ }} >
-
上传文件
- +
上传文档
+
- {newFile.file ? ( + {selectedFile ? (

已选择文件:

-

{newFile.file.name}

+

{selectedFile.name}

) : (

点击或拖拽文件到此处上传

-

支持 PDF, DOCX, TXT, CSV 等格式

+

+ 支持 PDF, Word, Excel, TXT, Markdown, CSV 等格式 +

)} - {fileErrors.file &&
{fileErrors.file}
} -
-
- - + {fileError &&
{fileError}
}
-
diff --git a/src/services/mockApi.js b/src/services/mockApi.js index 46c9355..5c758da 100644 --- a/src/services/mockApi.js +++ b/src/services/mockApi.js @@ -674,7 +674,7 @@ export const mockGet = async (url, config = {}) => { throw { response: { status: 404, data: { message: 'Not found' } } }; }; -export const mockPost = async (url, data) => { +export const mockPost = async (url, data, isMultipart = false) => { console.log(`[MOCK API] POST ${url}`, data); // Simulate network delay @@ -857,6 +857,26 @@ export const mockPost = async (url, data) => { }; } + // 上传知识库文档 + if (url.match(/\/knowledge-bases\/([^/]+)\/upload_document\//)) { + const knowledge_base_id = url.match(/\/knowledge-bases\/([^/]+)\/upload_document\//)[1]; + const file = isMultipart ? data.get('file') : null; + + return { + data: { + code: 200, + message: 'Document uploaded successfully', + data: { + id: `doc-${Date.now()}`, + knowledge_base_id: knowledge_base_id, + filename: file ? file.name : 'mock-document.pdf', + status: 'processing', + created_at: new Date().toISOString(), + }, + }, + }; + } + 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 c95515c..65defd3 100644 --- a/src/store/knowledgeBase/knowledgeBase.slice.js +++ b/src/store/knowledgeBase/knowledgeBase.slice.js @@ -8,6 +8,7 @@ import { searchKnowledgeBases, requestKnowledgeBaseAccess, getKnowledgeBaseById, + uploadDocument, } from './knowledgeBase.thunks'; const initialState = { @@ -27,6 +28,7 @@ const initialState = { batchLoading: false, editStatus: 'idle', requestAccessStatus: 'idle', + uploadStatus: 'idle', }; const knowledgeBaseSlice = createSlice({ @@ -180,6 +182,18 @@ const knowledgeBaseSlice = createSlice({ .addCase(getKnowledgeBaseById.rejected, (state, action) => { state.loading = false; state.error = action.payload || 'Failed to get knowledge base details'; + }) + + // 上传文档 + .addCase(uploadDocument.pending, (state) => { + state.uploadStatus = 'loading'; + }) + .addCase(uploadDocument.fulfilled, (state) => { + state.uploadStatus = 'successful'; + }) + .addCase(uploadDocument.rejected, (state, action) => { + state.uploadStatus = 'failed'; + state.error = action.payload || 'Failed to upload document'; }); }, }); diff --git a/src/store/knowledgeBase/knowledgeBase.thunks.js b/src/store/knowledgeBase/knowledgeBase.thunks.js index 187af7f..663f68c 100644 --- a/src/store/knowledgeBase/knowledgeBase.thunks.js +++ b/src/store/knowledgeBase/knowledgeBase.thunks.js @@ -1,5 +1,5 @@ import { createAsyncThunk } from '@reduxjs/toolkit'; -import { get, post, put, del } from '../../services/api'; +import { get, post, put, del, upload } from '../../services/api'; import { showNotification } from '../notification.slice'; /** @@ -20,7 +20,7 @@ export const fetchKnowledgeBases = createAsyncThunk( return response.data; } catch (error) { console.log(error); - + return rejectWithValue(error.response?.data.error.message || 'Failed to fetch knowledge bases'); } } @@ -196,3 +196,44 @@ export const requestKnowledgeBaseAccess = createAsyncThunk( } } ); + +/** + * Upload a document to a knowledge base + * @param {Object} params - Upload parameters + * @param {string} params.knowledge_base_id - Knowledge base ID + * @param {File} params.file - File to upload + */ +export const uploadDocument = createAsyncThunk( + 'knowledgeBase/uploadDocument', + async ({ knowledge_base_id, file }, { rejectWithValue, dispatch }) => { + try { + const formData = new FormData(); + formData.append('file', file); + + const response = await post(`/knowledge-bases/${knowledge_base_id}/upload_document/`, formData, true); + + dispatch( + showNotification({ + type: 'success', + message: `文档 ${file.name} 上传成功`, + }) + ); + + // 处理新的返回格式 + if (response.data && response.data.code === 200) { + return response.data.data; + } + + return response.data; + } catch (error) { + const errorMessage = error.response?.data?.message || error.message || '文档上传失败'; + dispatch( + showNotification({ + type: 'danger', + message: errorMessage, + }) + ); + return rejectWithValue(errorMessage); + } + } +); From 6d641bb309b10b1f136412098a2bd9794cd108be Mon Sep 17 00:00:00 2001 From: susie-laptop Date: Tue, 1 Apr 2025 22:22:53 -0400 Subject: [PATCH 2/2] [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}`; +};