feat: V1 - DAG编排/告警系统/Agent循环/知识库RAG/Webhook + 审核管理富上下文修复
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import {
|
||||
Card, Table, Button, Modal, Form, Input, Tag, Space, message,
|
||||
Empty, Typography, Input as SearchInput,
|
||||
} from 'antd';
|
||||
import {
|
||||
PlusOutlined, DeleteOutlined, SearchOutlined, BookOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { knowledgeApi } from '@/api';
|
||||
import type { KnowledgeDoc, KnowledgeSearchResult } from '@/types';
|
||||
|
||||
const { TextArea } = Input;
|
||||
const { Text } = Typography;
|
||||
|
||||
export default function KnowledgeBase() {
|
||||
const [docs, setDocs] = useState<KnowledgeDoc[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [searchResults, setSearchResults] = useState<KnowledgeSearchResult[] | null>(null);
|
||||
|
||||
const loadData = useCallback(() => {
|
||||
setLoading(true);
|
||||
knowledgeApi.list().then(setDocs).catch(() => {}).finally(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
useEffect(() => { loadData(); }, [loadData]);
|
||||
|
||||
const handleCreate = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
await knowledgeApi.create(values);
|
||||
message.success('文档创建成功');
|
||||
setModalOpen(false);
|
||||
form.resetFields();
|
||||
loadData();
|
||||
} catch (err: any) {
|
||||
if (err?.errorFields) return;
|
||||
message.error('创建失败');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
await knowledgeApi.delete(id);
|
||||
message.success('已删除');
|
||||
loadData();
|
||||
} catch { message.error('删除失败'); }
|
||||
};
|
||||
|
||||
const handleSearch = async () => {
|
||||
if (!searchQuery.trim()) { setSearchResults(null); return; }
|
||||
try {
|
||||
const results = await knowledgeApi.search(searchQuery);
|
||||
setSearchResults(results);
|
||||
} catch { message.error('搜索失败'); }
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{ title: '标题', dataIndex: 'title', key: 'title', render: (t: string) => <Text strong>{t}</Text> },
|
||||
{ title: '类型', dataIndex: 'doc_type', key: 'doc_type', width: 80, render: (t: string) => <Tag>{t}</Tag> },
|
||||
{
|
||||
title: '标签', dataIndex: 'tags', key: 'tags', width: 200,
|
||||
render: (tags: string) => tags ? tags.split(',').map((t, i) => <Tag key={i} color="blue">{t.trim()}</Tag>) : '-',
|
||||
},
|
||||
{
|
||||
title: '内容预览', dataIndex: 'content', key: 'content',
|
||||
render: (c: string) => <Text type="secondary" ellipsis style={{ maxWidth: 300 }}>{c?.slice(0, 80)}...</Text>,
|
||||
},
|
||||
{
|
||||
title: '操作', key: 'action', width: 80,
|
||||
render: (_: any, r: KnowledgeDoc) => (
|
||||
<Button size="small" danger icon={<DeleteOutlined />} onClick={() => handleDelete(r.id)} />
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card
|
||||
title={<Space><BookOutlined /> 知识库管理</Space>}
|
||||
extra={<Button type="primary" icon={<PlusOutlined />} onClick={() => { form.resetFields(); setModalOpen(true); }}>添加文档</Button>}
|
||||
>
|
||||
<SearchInput.Group compact style={{ marginBottom: 16 }}>
|
||||
<SearchInput
|
||||
style={{ width: 'calc(100% - 100px)' }}
|
||||
placeholder="搜索知识库..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
onPressEnter={handleSearch}
|
||||
prefix={<SearchOutlined />}
|
||||
/>
|
||||
<Button type="primary" onClick={handleSearch} style={{ width: 100 }}>搜索</Button>
|
||||
</SearchInput.Group>
|
||||
|
||||
{searchResults !== null ? (
|
||||
<div>
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<Text type="secondary">搜索结果 ({searchResults.length} 条) </Text>
|
||||
<Button type="link" size="small" onClick={() => { setSearchResults(null); setSearchQuery(''); }}>返回列表</Button>
|
||||
</div>
|
||||
{searchResults.length === 0 ? (
|
||||
<Empty description="未找到匹配文档" />
|
||||
) : (
|
||||
searchResults.map((r) => (
|
||||
<Card key={r.id} size="small" style={{ marginBottom: 8 }}>
|
||||
<Space direction="vertical" size={4} style={{ width: '100%' }}>
|
||||
<Space>
|
||||
<Text strong>{r.title}</Text>
|
||||
<Tag color="gold">匹配度: {r.score}</Tag>
|
||||
</Space>
|
||||
<Text type="secondary">{r.content?.slice(0, 200)}</Text>
|
||||
</Space>
|
||||
</Card>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<Table columns={columns} dataSource={docs} rowKey="id" loading={loading} pagination={{ pageSize: 10 }} />
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Modal title="添加知识文档" open={modalOpen} onOk={handleCreate} onCancel={() => setModalOpen(false)} width={600} okText="保存" cancelText="取消">
|
||||
<Form form={form} layout="vertical">
|
||||
<Form.Item name="title" label="标题" rules={[{ required: true }]}><Input placeholder="文档标题" /></Form.Item>
|
||||
<Form.Item name="content" label="内容" rules={[{ required: true }]}><TextArea rows={6} placeholder="文档内容..." /></Form.Item>
|
||||
<Form.Item name="tags" label="标签"><Input placeholder="用逗号分隔,如: API,文档" /></Form.Item>
|
||||
<Form.Item name="doc_type" label="类型" initialValue="text"><Input /></Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user