feat(logging): add loglevel package and implement logger utility
refactor(document): update document list filtering and API request handling
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
"js-base64": "^3.7.8",
|
||||
"jsencrypt": "^3.5.4",
|
||||
"lodash": "^4.17.21",
|
||||
"loglevel": "^1.9.2",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-hook-form": "^7.64.0",
|
||||
|
||||
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@@ -53,6 +53,9 @@ importers:
|
||||
lodash:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
loglevel:
|
||||
specifier: ^1.9.2
|
||||
version: 1.9.2
|
||||
react:
|
||||
specifier: ^18.3.1
|
||||
version: 18.3.1
|
||||
@@ -1434,6 +1437,10 @@ packages:
|
||||
lodash@4.17.21:
|
||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||
|
||||
loglevel@1.9.2:
|
||||
resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
|
||||
loose-envify@1.4.0:
|
||||
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
|
||||
hasBin: true
|
||||
@@ -3164,6 +3171,8 @@ snapshots:
|
||||
|
||||
lodash@4.17.21: {}
|
||||
|
||||
loglevel@1.9.2: {}
|
||||
|
||||
loose-envify@1.4.0:
|
||||
dependencies:
|
||||
js-tokens: 4.0.0
|
||||
|
||||
@@ -17,8 +17,8 @@ export interface UseDocumentListState {
|
||||
|
||||
// 文档列表Hook返回值接口
|
||||
export interface UseDocumentListReturn extends UseDocumentListState {
|
||||
fetchDocuments: (params?: IFetchKnowledgeListRequestParams) => Promise<void>;
|
||||
fetchDocumentsFilter: () => Promise<IDocumentInfoFilter | undefined>;
|
||||
fetchDocuments: (params?: IFetchKnowledgeListRequestParams, body?: IFetchDocumentListRequestBody) => Promise<void>;
|
||||
fetchDocumentsFilter: () => Promise<{ filter?: IDocumentInfoFilter, total?: number } | undefined>;
|
||||
setKeywords: (keywords: string) => void;
|
||||
setCurrentPage: (page: number) => void;
|
||||
setPageSize: (size: number) => void;
|
||||
@@ -44,7 +44,9 @@ export const useDocumentList = (
|
||||
/**
|
||||
* 获取文档列表
|
||||
*/
|
||||
const fetchDocuments = useCallback(async (params?: IFetchKnowledgeListRequestParams) => {
|
||||
const fetchDocuments = useCallback(async (
|
||||
params?: IFetchKnowledgeListRequestParams,
|
||||
body?: IFetchDocumentListRequestBody) => {
|
||||
if (!kbId) return;
|
||||
|
||||
try {
|
||||
@@ -61,6 +63,13 @@ export const useDocumentList = (
|
||||
// 构建请求体
|
||||
const requestBody: IFetchDocumentListRequestBody = {};
|
||||
|
||||
if (body?.suffix) {
|
||||
requestBody.suffix = body.suffix;
|
||||
}
|
||||
if (body?.run_status) {
|
||||
requestBody.run_status = body.run_status;
|
||||
}
|
||||
|
||||
// 构建查询参数
|
||||
const requestParams: IFetchKnowledgeListRequestParams = {
|
||||
kb_id: kbId,
|
||||
@@ -108,7 +117,7 @@ export const useDocumentList = (
|
||||
kb_id: kbId,
|
||||
});
|
||||
if (response.data.code === 0) {
|
||||
const data: IDocumentInfoFilter = response.data.data;
|
||||
const data: { filter?: IDocumentInfoFilter, total?: number } = response.data.data;
|
||||
return data;
|
||||
} else {
|
||||
throw new Error(response.data.message || '获取文档过滤器失败');
|
||||
@@ -189,7 +198,7 @@ export const useDocumentOperations = () => {
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('kb_id', kbId);
|
||||
|
||||
|
||||
files.forEach((file) => {
|
||||
formData.append('file', file);
|
||||
});
|
||||
@@ -221,7 +230,7 @@ export const useDocumentOperations = () => {
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('kb_id', kbId);
|
||||
|
||||
|
||||
files.forEach((file) => {
|
||||
formData.append('file', file);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
Box,
|
||||
@@ -54,6 +54,7 @@ import type { IDocumentInfoFilter } from '@/interfaces/database/document';
|
||||
import { RUNNING_STATUS_KEYS, type RunningStatus } from '@/constants/knowledge';
|
||||
import { LanguageAbbreviation } from '@/locales';
|
||||
import dayjs from 'dayjs';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
|
||||
interface DocumentListComponentProps {
|
||||
@@ -75,7 +76,11 @@ interface DocumentListComponentProps {
|
||||
onPageSizeChange: (pageSize: number) => void;
|
||||
// 筛选器相关props
|
||||
documentFilter?: IDocumentInfoFilter;
|
||||
onFetchFilter?: () => Promise<IDocumentInfoFilter | undefined>;
|
||||
// filter 变化时触发
|
||||
onSelectedFilterChange?: (filter: {
|
||||
run_status: Array<string>,
|
||||
suffix: Array<string>,
|
||||
}) => void;
|
||||
// 新增操作功能props
|
||||
onRename: (fileId: string, newName: string) => void;
|
||||
onChangeStatus: (fileIds: string[], status: string) => void;
|
||||
@@ -183,7 +188,7 @@ const DocumentListComponent: React.FC<DocumentListComponentProps> = ({
|
||||
onPageChange,
|
||||
onPageSizeChange,
|
||||
documentFilter,
|
||||
onFetchFilter,
|
||||
onSelectedFilterChange,
|
||||
onRename,
|
||||
onChangeStatus,
|
||||
onCancelRun,
|
||||
@@ -199,7 +204,6 @@ const DocumentListComponent: React.FC<DocumentListComponentProps> = ({
|
||||
const [selectedFileId, setSelectedFileId] = useState<string>('');
|
||||
const [selectedFile, setSelectedFile] = useState<IKnowledgeFile | undefined>(undefined);
|
||||
|
||||
const [filter, setFilter] = useState<IDocumentInfoFilter | undefined>(documentFilter);
|
||||
const [selectedRunStatus, setSelectedRunStatus] = useState<string[]>([]);
|
||||
const [selectedSuffix, setSelectedSuffix] = useState<string[]>([]);
|
||||
const [renameDialogOpen, setRenameDialogOpen] = useState(false);
|
||||
@@ -213,21 +217,16 @@ const DocumentListComponent: React.FC<DocumentListComponentProps> = ({
|
||||
return currentLanguage === LanguageAbbreviation.Zh ? zhCN : enUS;
|
||||
};
|
||||
|
||||
// 获取筛选器数据
|
||||
useEffect(() => {
|
||||
const fetchFilter = async () => {
|
||||
try {
|
||||
const filterData = await onFetchFilter?.();
|
||||
setFilter(filterData);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch document filter:', error);
|
||||
}
|
||||
const handleFilterSubmit = useCallback(() => {
|
||||
const filter = {
|
||||
run_status: selectedRunStatus,
|
||||
suffix: selectedSuffix,
|
||||
};
|
||||
|
||||
if (!filter) {
|
||||
fetchFilter();
|
||||
if (onSelectedFilterChange) {
|
||||
onSelectedFilterChange(filter);
|
||||
}
|
||||
}, [onFetchFilter, filter]);
|
||||
}, [selectedRunStatus, selectedSuffix])
|
||||
|
||||
const handleMenuClick = (event: React.MouseEvent<HTMLElement>, file: IKnowledgeFile) => {
|
||||
event.stopPropagation();
|
||||
@@ -334,10 +333,10 @@ const DocumentListComponent: React.FC<DocumentListComponentProps> = ({
|
||||
minWidth: 200,
|
||||
cellClassName: 'grid-center-cell',
|
||||
renderCell: (params) => (
|
||||
<Box
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 1,
|
||||
cursor: 'pointer',
|
||||
":hover": { color: 'primary.main', fontWeight: 'bold' }
|
||||
@@ -470,7 +469,7 @@ const DocumentListComponent: React.FC<DocumentListComponentProps> = ({
|
||||
return (
|
||||
<Box>
|
||||
{/* 筛选器 */}
|
||||
{filter && (
|
||||
{documentFilter && (
|
||||
<Paper sx={{ p: 2, mb: 2 }}>
|
||||
<Typography variant="h6" sx={{ mb: 2 }}>筛选器</Typography>
|
||||
<Stack direction="row" spacing={2} alignItems="center">
|
||||
@@ -490,7 +489,7 @@ const DocumentListComponent: React.FC<DocumentListComponentProps> = ({
|
||||
</Box>
|
||||
)}
|
||||
>
|
||||
{Object.entries(filter.run_status).map(([status, count]) => (
|
||||
{Object.entries(documentFilter.run_status).map(([status, count]) => (
|
||||
<MenuItem key={status} value={status}>
|
||||
{getRunStatusLabel(status)} ({count})
|
||||
</MenuItem>
|
||||
@@ -514,20 +513,33 @@ const DocumentListComponent: React.FC<DocumentListComponentProps> = ({
|
||||
</Box>
|
||||
)}
|
||||
>
|
||||
{Object.entries(filter.suffix).map(([suffix, count]) => (
|
||||
{Object.entries(documentFilter.suffix).map(([suffix, count]) => (
|
||||
<MenuItem key={suffix} value={suffix}>
|
||||
{suffix.toUpperCase()} ({count})
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
<Button variant="outlined" onClick={() => {
|
||||
setSelectedRunStatus([]);
|
||||
setSelectedSuffix([]);
|
||||
{/* clear filter */}
|
||||
<Button variant="outlined" onClick={async () => {
|
||||
await new Promise(resolve => {
|
||||
setSelectedRunStatus([]);
|
||||
setSelectedSuffix([]);
|
||||
setTimeout(resolve, 200);
|
||||
});
|
||||
handleFilterSubmit();
|
||||
}}>
|
||||
清除筛选
|
||||
</Button>
|
||||
{/* submit filter */}
|
||||
<Button variant="contained" onClick={async () => {
|
||||
await new Promise(resolve => {
|
||||
setTimeout(resolve, 200);
|
||||
});
|
||||
handleFilterSubmit();
|
||||
}}>
|
||||
确认筛选
|
||||
</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
)}
|
||||
|
||||
@@ -33,11 +33,12 @@ import KnowledgeGraphView from './components/KnowledgeGraphView';
|
||||
import { useDocumentList, useDocumentOperations } from '@/hooks/document-hooks';
|
||||
import { RUNNING_STATUS_KEYS } from '@/constants/knowledge';
|
||||
import { useKnowledgeDetail } from '@/hooks/knowledge-hooks';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
function KnowledgeBaseDetail() {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const navigate = useNavigate();
|
||||
|
||||
|
||||
// 状态管理
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [searchKeyword, setSearchKeyword] = useState('');
|
||||
@@ -47,17 +48,27 @@ function KnowledgeBaseDetail() {
|
||||
});
|
||||
const [uploadDialogOpen, setUploadDialogOpen] = useState(false);
|
||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||
|
||||
|
||||
const [processDetailsDialogOpen, setProcessDetailsDialogOpen] = useState(false);
|
||||
const [selectedFileDetails, setSelectedFileDetails] = useState<IKnowledgeFile | null>(null);
|
||||
|
||||
|
||||
// 标签页状态
|
||||
const [currentTab, setCurrentTab] = useState(0);
|
||||
|
||||
|
||||
// 轮询相关状态
|
||||
const pollingIntervalRef = useRef<any>(null);
|
||||
const [isPolling, setIsPolling] = useState(false);
|
||||
|
||||
// documents filter
|
||||
const [documentsFilter, setDocumentsFilter] = useState<IDocumentInfoFilter | undefined>(undefined);
|
||||
|
||||
const {
|
||||
knowledge: knowledgeBase,
|
||||
refresh: fetchKnowledgeDetail,
|
||||
loading, showKnowledgeGraph,
|
||||
knowledgeGraph
|
||||
} = useKnowledgeDetail(id || '');
|
||||
|
||||
// 使用新的document hooks
|
||||
const {
|
||||
documents: files,
|
||||
@@ -70,6 +81,8 @@ function KnowledgeBaseDetail() {
|
||||
setKeywords,
|
||||
setCurrentPage,
|
||||
setPageSize,
|
||||
fetchDocuments,
|
||||
fetchDocumentsFilter,
|
||||
} = useDocumentList(id || '');
|
||||
|
||||
const {
|
||||
@@ -79,17 +92,19 @@ function KnowledgeBaseDetail() {
|
||||
renameDocument,
|
||||
changeDocumentStatus,
|
||||
cancelRunDocuments,
|
||||
loading: operationLoading,
|
||||
error: operationError,
|
||||
} = useDocumentOperations();
|
||||
|
||||
const { knowledge: knowledgeBase, refresh: fetchKnowledgeDetail, loading, showKnowledgeGraph, knowledgeGraph } = useKnowledgeDetail(id || '');
|
||||
|
||||
console.log('showKnowledgeGraph:', showKnowledgeGraph, knowledgeGraph);
|
||||
|
||||
const refreshDetailData = async () => {
|
||||
await fetchKnowledgeDetail();
|
||||
await refreshFiles();
|
||||
};
|
||||
|
||||
// 删除文件
|
||||
const handleDeleteFiles = async () => {
|
||||
try {
|
||||
try {
|
||||
await deleteDocuments(Array.from(rowSelectionModel.ids) as string[]);
|
||||
setDeleteDialogOpen(false);
|
||||
setRowSelectionModel({
|
||||
@@ -176,7 +191,7 @@ function KnowledgeBaseDetail() {
|
||||
if (pollingIntervalRef.current) {
|
||||
clearInterval(pollingIntervalRef.current);
|
||||
}
|
||||
|
||||
|
||||
setIsPolling(true);
|
||||
pollingIntervalRef.current = setInterval(() => {
|
||||
refreshFiles();
|
||||
@@ -215,7 +230,20 @@ function KnowledgeBaseDetail() {
|
||||
|
||||
// 初始化数据
|
||||
useEffect(() => {
|
||||
fetchKnowledgeDetail();
|
||||
fetchDocumentsFilter().then(filterData => {
|
||||
if (filterData?.filter) {
|
||||
// setKeywords(filter.keywords || '');
|
||||
logger.debug('filter:', filterData.filter);
|
||||
const filter = filterData.filter || {};
|
||||
const showFilter = Object.keys(filter.run_status || {}).length > 0 || Object.keys(filter.suffix || {}).length > 0;
|
||||
if (showFilter) {
|
||||
setDocumentsFilter({
|
||||
run_status: filterData.filter?.run_status || {},
|
||||
suffix: filterData.filter?.suffix || {},
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [id]);
|
||||
|
||||
// 搜索文件
|
||||
@@ -230,7 +258,7 @@ function KnowledgeBaseDetail() {
|
||||
// 合并错误状态
|
||||
const combinedError = error || filesError || operationError;
|
||||
|
||||
if (loading) {
|
||||
if (loading || !knowledgeBase) {
|
||||
return (
|
||||
<Box sx={{ p: 3 }}>
|
||||
<LinearProgress />
|
||||
@@ -247,22 +275,10 @@ function KnowledgeBaseDetail() {
|
||||
);
|
||||
}
|
||||
|
||||
if (!knowledgeBase) {
|
||||
return (
|
||||
<Box sx={{ p: 3 }}>
|
||||
<Alert severity="warning">知识库不存在</Alert>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function fetchDocumentsFilter(): Promise<IDocumentInfoFilter | undefined> {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{ p: 3 }}>
|
||||
{/* 面包屑导航 */}
|
||||
<KnowledgeBreadcrumbs
|
||||
<KnowledgeBreadcrumbs
|
||||
kbItems={[
|
||||
{
|
||||
label: '知识库',
|
||||
@@ -281,15 +297,15 @@ function KnowledgeBaseDetail() {
|
||||
{/* 标签页组件 - 仅在showKnowledgeGraph为true时显示 */}
|
||||
{showKnowledgeGraph ? (
|
||||
<Box sx={{ mt: 3 }}>
|
||||
<Tabs
|
||||
value={currentTab}
|
||||
<Tabs
|
||||
value={currentTab}
|
||||
onChange={(event, newValue) => setCurrentTab(newValue)}
|
||||
sx={{ borderBottom: 1, borderColor: 'divider' }}
|
||||
>
|
||||
<Tab label="Documents" />
|
||||
<Tab label="Graph" />
|
||||
</Tabs>
|
||||
|
||||
|
||||
{/* Document List 标签页内容 */}
|
||||
{currentTab === 0 && (
|
||||
<Box sx={{ mt: 2 }}>
|
||||
@@ -300,7 +316,6 @@ function KnowledgeBaseDetail() {
|
||||
onSearchChange={setSearchKeyword}
|
||||
onReparse={(fileIds) => handleReparse(fileIds)}
|
||||
onDelete={(fileIds) => {
|
||||
console.log('删除文件:', fileIds);
|
||||
setRowSelectionModel({
|
||||
type: 'include',
|
||||
ids: new Set(fileIds)
|
||||
@@ -314,12 +329,15 @@ function KnowledgeBaseDetail() {
|
||||
}}
|
||||
rowSelectionModel={rowSelectionModel}
|
||||
onRowSelectionModelChange={(newModel) => {
|
||||
console.log('新的选择模型:', newModel);
|
||||
setRowSelectionModel(newModel);
|
||||
}}
|
||||
total={total}
|
||||
page={currentPage}
|
||||
pageSize={pageSize}
|
||||
documentFilter={documentsFilter}
|
||||
onSelectedFilterChange={(filterBody) => {
|
||||
fetchDocuments({}, filterBody);
|
||||
}}
|
||||
onPageChange={setCurrentPage}
|
||||
onPageSizeChange={setPageSize}
|
||||
onRename={handleRename}
|
||||
@@ -330,7 +348,7 @@ function KnowledgeBaseDetail() {
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
|
||||
{/* Graph 标签页内容 */}
|
||||
{currentTab === 1 && (
|
||||
<Box sx={{ mt: 2 }}>
|
||||
@@ -367,6 +385,10 @@ function KnowledgeBaseDetail() {
|
||||
total={total}
|
||||
page={currentPage}
|
||||
pageSize={pageSize}
|
||||
documentFilter={documentsFilter}
|
||||
onSelectedFilterChange={(filterBody) => {
|
||||
fetchDocuments({}, filterBody);
|
||||
}}
|
||||
onPageChange={setCurrentPage}
|
||||
onPageSizeChange={setPageSize}
|
||||
onRename={handleRename}
|
||||
@@ -477,14 +499,14 @@ function KnowledgeBaseDetail() {
|
||||
进度
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||
<Chip
|
||||
label={`${100*(selectedFileDetails.progress || 0)}%`}
|
||||
color="primary"
|
||||
size="small"
|
||||
<Chip
|
||||
label={`${100 * (selectedFileDetails.progress || 0)}%`}
|
||||
color="primary"
|
||||
size="small"
|
||||
/>
|
||||
<LinearProgress
|
||||
variant="determinate"
|
||||
value={100*(selectedFileDetails.progress || 0)}
|
||||
<LinearProgress
|
||||
variant="determinate"
|
||||
value={100 * (selectedFileDetails.progress || 0)}
|
||||
sx={{ flexGrow: 1, height: 8, borderRadius: 4 }}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -68,7 +68,7 @@ const knowledgeService = {
|
||||
params?: IFetchKnowledgeListRequestParams,
|
||||
body?: IFetchDocumentListRequestBody
|
||||
) => {
|
||||
return request.post(api.get_document_list, { data: body || {} }, { params });
|
||||
return request.post(api.get_document_list, body || {}, { params });
|
||||
},
|
||||
|
||||
// 创建文档
|
||||
|
||||
17
src/utils/logger.ts
Normal file
17
src/utils/logger.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import log from 'loglevel';
|
||||
|
||||
// 根据环境设置日志级别
|
||||
if (import.meta.env.DEV) {
|
||||
log.setLevel('debug');
|
||||
} else {
|
||||
log.setLevel('warn');
|
||||
}
|
||||
|
||||
const logger = {
|
||||
debug: log.debug.bind(log),
|
||||
info: log.info.bind(log),
|
||||
warn: log.warn.bind(log),
|
||||
error: log.error.bind(log),
|
||||
}
|
||||
|
||||
export default logger;
|
||||
Reference in New Issue
Block a user