import { useState, useEffect } from 'react' import { Card, Upload, Table, Button, Space, Select, Popconfirm, message, Tag } from 'antd' import { InboxOutlined, DeleteOutlined, ReloadOutlined } from '@ant-design/icons' import useAuthStore from '../../stores/authStore' import knowledgeService from '../../services/knowledgeService' const { Dragger } = Upload // source_type 枚举值(固定五类,不写死但供 Select 用) const SOURCE_TYPE_OPTIONS = [ { label: '全部类型', value: '' }, { label: '杂志文章', value: 'military_report' }, { label: '节目文稿', value: 'manuscript' }, { label: '报题单', value: 'baoti' }, ] // source_type 中文标签 const SOURCE_TYPE_LABEL = { military_report: '杂志文章', manuscript: '节目文稿', baoti: '报题单', manual: '其他', } export default function KnowledgeBase() { const { user } = useAuthStore() const [items, setItems] = useState([]) const [sources, setSources] = useState([]) const [loading, setLoading] = useState(false) const [uploading, setUploading] = useState(false) const [sourceTypeFilter, setSourceTypeFilter] = useState('') const [sourceDetailFilter, setSourceDetailFilter] = useState('') const fetchItems = async () => { setLoading(true) try { const data = await knowledgeService.listItems(sourceTypeFilter || null) setItems(data) } catch { message.error('加载知识库失败') } finally { setLoading(false) } } const fetchSources = async () => { try { const data = await knowledgeService.listSources() setSources(data.map(s => ({ label: s.source, value: s.source }))) } catch { // ignore } } useEffect(() => { fetchItems() fetchSources() }, [sourceTypeFilter]) // 上传 const handleUpload = async (file) => { if (!file.name.endsWith('.md')) { message.warning('仅支持 .md 文件') return false } setUploading(true) try { const result = await knowledgeService.uploadFiles([file]) const { uploaded, errors } = result if (uploaded.length > 0) { message.success(`成功入库 ${uploaded.length} 篇`) fetchItems() fetchSources() } if (errors.length > 0) { errors.forEach(e => message.error(`${e.file}: ${e.error}`)) } } catch { message.error('上传失败') } finally { setUploading(false) } return false // 阻止默认上传行为 } // 删除 const handleDelete = async (id) => { try { await knowledgeService.deleteItem(id) message.success('删除成功') fetchItems() fetchSources() } catch { message.error('删除失败') } } // 列表筛选后过滤 source_detail const displayedItems = sourceDetailFilter ? items.filter(i => i.source_detail === sourceDetailFilter) : items const columns = [ { title: '标题', dataIndex: 'title', key: 'title', render: (text, record) => ( {text} ), }, { title: '作者', dataIndex: 'author', key: 'author', render: (val) => val || null, // 无则不显示 }, { title: '播出/发表时间', dataIndex: 'publish_date', key: 'publish_date', render: (val) => val || null, // 无则不显示 }, { title: '出处', dataIndex: 'source_detail', key: 'source_detail', render: (val) => val || '-', }, { title: '类型', dataIndex: 'source_type', key: 'source_type', render: (val) => ( {SOURCE_TYPE_LABEL[val] || val} ), }, { title: '入库时间', dataIndex: 'created_at', key: 'created_at', render: (val) => val ? new Date(val).toLocaleString('zh-CN') : '-', }, { title: '操作', key: 'action', render: (_, record) => ( handleDelete(record.id)} okText="确认删除" cancelText="取消" placement="left" > ), }, ] return (

知识库

上传 md 笔记 · 语义检索 · 报题参考

{/* 上传区 */}

{uploading ? '正在上传并生成向量…' : '点击或拖拽 .md 文件上传'}

支持批量上传,系统自动解析 yaml frontmatter 并生成语义向量

{/* 筛选栏 */} 筛选: {/* 列表 */} ) }