// ============================================================ // 知识库管理 // ============================================================ import { useState, useMemo, useEffect } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { Button, message, Tag, Modal, Form, Input, Select, Space, Popconfirm, Card, Statistic, Row, Col, Tabs, Tree, Typography, Empty, Spin, InputNumber, Table, Tooltip, } from 'antd' import { PlusOutlined, SearchOutlined, BookOutlined, FolderOutlined, DeleteOutlined, EditOutlined, EyeOutlined, BarChartOutlined, HistoryOutlined, RollbackOutlined, WarningOutlined, } from '@ant-design/icons' import type { ProColumns } from '@ant-design/pro-components' import { ProTable } from '@ant-design/pro-components' import { knowledgeService } from '@/services/knowledge' import type { CategoryResponse, KnowledgeItem, SearchResult } from '@/services/knowledge' import type { StructuredSource } from '@/services/knowledge' import { TableOutlined } from '@ant-design/icons' const { TextArea } = Input const { Text, Title } = Typography // === 分类树 + 条目列表 Tab === function CategoriesPanel() { const queryClient = useQueryClient() const [createOpen, setCreateOpen] = useState(false) const [editItem, setEditItem] = useState(null) const [createForm] = Form.useForm() const [editForm] = Form.useForm() const { data: categories = [], isLoading } = useQuery({ queryKey: ['knowledge-categories'], queryFn: ({ signal }) => knowledgeService.listCategories(signal), }) const createMutation = useMutation({ mutationFn: (data: Parameters[0]) => knowledgeService.createCategory(data), onSuccess: () => { message.success('分类已创建') queryClient.invalidateQueries({ queryKey: ['knowledge-categories'] }) setCreateOpen(false) createForm.resetFields() }, onError: (err: Error) => message.error(err.message || '创建失败'), }) const deleteMutation = useMutation({ mutationFn: (id: string) => knowledgeService.deleteCategory(id), onSuccess: () => { message.success('分类已删除') queryClient.invalidateQueries({ queryKey: ['knowledge-categories'] }) }, onError: (err: Error) => message.error(err.message || '删除失败'), }) const updateMutation = useMutation({ mutationFn: ({ id, ...data }: { id: string } & Record) => knowledgeService.updateCategory(id, data), onSuccess: () => { message.success('分类已更新') queryClient.invalidateQueries({ queryKey: ['knowledge-categories'] }) setEditItem(null) }, onError: (err: Error) => message.error(err.message || '更新失败'), }) // 编辑弹窗打开时同步表单值(Ant Design Form initialValues 仅首次挂载生效) useEffect(() => { if (editItem) { editForm.setFieldsValue({ name: editItem.name, description: editItem.description, parent_id: editItem.parent_id, icon: editItem.icon, }) } }, [editItem, editForm]) // 获取当前编辑分类及其所有后代的 ID(防止循环引用) const getDescendantIds = (id: string, cats: CategoryResponse[]): string[] => { const ids: string[] = [id] for (const c of cats) { if (c.parent_id === id) { ids.push(...getDescendantIds(c.id, cats)) } } return ids } const treeData = useMemo( () => buildTreeData(categories, (id) => { Modal.confirm({ title: '确认删除', content: '删除后无法恢复,请确保分类下没有子分类和条目。', okType: 'danger', onOk: () => deleteMutation.mutate(id), }) }, (id) => { setEditItem(categories.find((c) => c.id === id) || null) }), [categories, deleteMutation], ) return (
分类管理
{isLoading ? (
) : categories.length === 0 ? ( ) : ( )} {/* 新建分类弹窗 */} { setCreateOpen(false); createForm.resetFields() }} onOk={() => createForm.submit()} confirmLoading={createMutation.isPending} >
createMutation.mutate(v)}>