diff --git a/packages/admin-client/src/features/knowledge/application/index.ts b/packages/admin-client/src/features/knowledge/application/index.ts index 4eef869..58071a6 100644 --- a/packages/admin-client/src/features/knowledge/application/index.ts +++ b/packages/admin-client/src/features/knowledge/application/index.ts @@ -5,5 +5,6 @@ export { useDeleteArticle, usePublishArticle, useUnpublishArticle, + useUploadKnowledgeFile, KNOWLEDGE_QUERY_KEY, } from './useKnowledge'; diff --git a/packages/admin-client/src/features/knowledge/application/useKnowledge.ts b/packages/admin-client/src/features/knowledge/application/useKnowledge.ts index cdbe0bb..280beb2 100644 --- a/packages/admin-client/src/features/knowledge/application/useKnowledge.ts +++ b/packages/admin-client/src/features/knowledge/application/useKnowledge.ts @@ -59,6 +59,15 @@ export function usePublishArticle() { }); } +export function useUploadKnowledgeFile() { + return useMutation({ + mutationFn: (file: File) => knowledgeApi.uploadFile(file), + onError: () => { + message.error('文件上传失败'); + }, + }); +} + export function useUnpublishArticle() { const queryClient = useQueryClient(); diff --git a/packages/admin-client/src/features/knowledge/infrastructure/index.ts b/packages/admin-client/src/features/knowledge/infrastructure/index.ts index 73f1af9..f5938a7 100644 --- a/packages/admin-client/src/features/knowledge/infrastructure/index.ts +++ b/packages/admin-client/src/features/knowledge/infrastructure/index.ts @@ -1,2 +1,2 @@ export { knowledgeApi } from './knowledge.api'; -export type { Article, ArticleListResponse, CreateArticleParams, UpdateArticleParams } from './knowledge.api'; +export type { Article, ArticleListResponse, CreateArticleParams, UpdateArticleParams, ExtractedTextResponse } from './knowledge.api'; diff --git a/packages/admin-client/src/features/knowledge/infrastructure/knowledge.api.ts b/packages/admin-client/src/features/knowledge/infrastructure/knowledge.api.ts index 6e2dc9c..23c85a8 100644 --- a/packages/admin-client/src/features/knowledge/infrastructure/knowledge.api.ts +++ b/packages/admin-client/src/features/knowledge/infrastructure/knowledge.api.ts @@ -26,6 +26,13 @@ export interface CreateArticleParams { tags?: string[]; } +export interface ExtractedTextResponse { + extractedText: string; + suggestedTitle: string; + wordCount: number; + pageCount?: number; +} + export interface UpdateArticleParams extends Partial { id: string; } @@ -59,4 +66,14 @@ export const knowledgeApi = { unpublishArticle: async (id: string): Promise => { await api.post(`/knowledge/articles/${id}/unpublish`); }, + + uploadFile: async (file: File): Promise => { + const formData = new FormData(); + formData.append('file', file); + const response = await api.post('/knowledge/articles/upload', formData, { + headers: { 'Content-Type': 'multipart/form-data' }, + timeout: 120000, + }); + return response.data.data; + }, }; diff --git a/packages/admin-client/src/features/knowledge/presentation/pages/KnowledgePage.tsx b/packages/admin-client/src/features/knowledge/presentation/pages/KnowledgePage.tsx index 0d3c360..1706bfe 100644 --- a/packages/admin-client/src/features/knowledge/presentation/pages/KnowledgePage.tsx +++ b/packages/admin-client/src/features/knowledge/presentation/pages/KnowledgePage.tsx @@ -12,6 +12,9 @@ import { Popconfirm, Typography, Drawer, + Upload, + Segmented, + message, } from 'antd'; import { PlusOutlined, @@ -21,6 +24,8 @@ import { EyeOutlined, CheckOutlined, StopOutlined, + InboxOutlined, + UploadOutlined, } from '@ant-design/icons'; import { useKnowledgeArticles, @@ -29,6 +34,7 @@ import { useDeleteArticle, usePublishArticle, useUnpublishArticle, + useUploadKnowledgeFile, } from '../../application'; import type { Article, CreateArticleParams } from '../../infrastructure'; @@ -51,6 +57,8 @@ export function KnowledgePage() { const [isModalOpen, setIsModalOpen] = useState(false); const [isDrawerOpen, setIsDrawerOpen] = useState(false); const [selectedArticle, setSelectedArticle] = useState
(null); + const [inputMode, setInputMode] = useState<'manual' | 'upload'>('manual'); + const [isExtracting, setIsExtracting] = useState(false); const [form] = Form.useForm(); const { data, isLoading } = useKnowledgeArticles(categoryFilter); @@ -59,6 +67,7 @@ export function KnowledgePage() { const deleteMutation = useDeleteArticle(); const publishMutation = usePublishArticle(); const unpublishMutation = useUnpublishArticle(); + const uploadMutation = useUploadKnowledgeFile(); const handleEdit = (article: Article) => { setSelectedArticle(article); @@ -98,6 +107,25 @@ export function KnowledgePage() { } }; + const handleFileUpload = (file: File) => { + setIsExtracting(true); + uploadMutation.mutate(file, { + onSuccess: (result) => { + form.setFieldsValue({ + title: result.suggestedTitle, + content: result.extractedText, + }); + const info = result.pageCount + ? `已提取 ${result.wordCount} 字(${result.pageCount} 页)` + : `已提取 ${result.wordCount} 字`; + message.success(info); + setInputMode('manual'); + }, + onSettled: () => setIsExtracting(false), + }); + return false; // prevent default upload + }; + const columns = [ { title: '标题', @@ -234,6 +262,7 @@ export function KnowledgePage() { onClick={() => { setSelectedArticle(null); form.resetFields(); + setInputMode('manual'); setIsModalOpen(true); }} > @@ -287,13 +316,47 @@ export function KnowledgePage() {