feat: add knowledge management pages
This commit is contained in:
151
frontend/src/features/knowledge/KnowledgeBaseListPage.tsx
Normal file
151
frontend/src/features/knowledge/KnowledgeBaseListPage.tsx
Normal file
@@ -0,0 +1,151 @@
|
||||
import * as React from 'react'
|
||||
import { Plus, Search } from 'lucide-react'
|
||||
|
||||
import { AppDataTable, type AppDataTableColumn } from '@/components/app/AppDataTable'
|
||||
import { AppEmptyState } from '@/components/app/AppEmptyState'
|
||||
import { AppField } from '@/components/app/AppField'
|
||||
import { AppPage } from '@/components/app/AppPage'
|
||||
import { AppPageHeader } from '@/components/app/AppPageHeader'
|
||||
import { AppSection } from '@/components/app/AppSection'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { formatDateTime } from '@/lib/format'
|
||||
|
||||
import { createKnowledgeBase, listKnowledgeBases } from './api'
|
||||
import type { KnowledgeBase } from './types'
|
||||
|
||||
type KnowledgeBaseForm = {
|
||||
name: string
|
||||
description: string
|
||||
scope: string
|
||||
}
|
||||
|
||||
const emptyForm: KnowledgeBaseForm = {
|
||||
name: '',
|
||||
description: '',
|
||||
scope: ''
|
||||
}
|
||||
|
||||
const columns: AppDataTableColumn<KnowledgeBase>[] = [
|
||||
{
|
||||
key: 'name',
|
||||
header: '名称',
|
||||
cell: (base) => (
|
||||
<div>
|
||||
<p className="font-medium text-foreground">{base.name}</p>
|
||||
<p className="mt-1 line-clamp-1 text-sm text-muted-foreground">
|
||||
{base.description || '暂无描述'}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
key: 'scope',
|
||||
header: '范围',
|
||||
cell: (base) => <span className="text-muted-foreground">{base.scope || '-'}</span>
|
||||
},
|
||||
{
|
||||
key: 'enabled',
|
||||
header: '启用状态',
|
||||
cell: (base) => (
|
||||
<Badge variant={base.enabled ? 'success' : 'secondary'}>
|
||||
{base.enabled ? '已启用' : '已停用'}
|
||||
</Badge>
|
||||
)
|
||||
},
|
||||
{
|
||||
key: 'updated_at',
|
||||
header: '更新时间',
|
||||
cell: (base) => <span className="text-muted-foreground">{formatDateTime(base.updated_at)}</span>
|
||||
}
|
||||
]
|
||||
|
||||
export function KnowledgeBaseListPage() {
|
||||
const [bases, setBases] = React.useState<KnowledgeBase[]>([])
|
||||
const [form, setForm] = React.useState<KnowledgeBaseForm>(emptyForm)
|
||||
const [error, setError] = React.useState('')
|
||||
|
||||
async function load() {
|
||||
try {
|
||||
setBases(await listKnowledgeBases())
|
||||
setError('')
|
||||
} catch (reason) {
|
||||
setError(reason instanceof Error ? reason.message : '加载知识库失败')
|
||||
}
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
void load()
|
||||
}, [])
|
||||
|
||||
async function handleCreate(event: React.FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault()
|
||||
await createKnowledgeBase(form)
|
||||
setForm(emptyForm)
|
||||
await load()
|
||||
}
|
||||
|
||||
return (
|
||||
<AppPage contentClassName="gap-5">
|
||||
<AppPageHeader
|
||||
title="知识库"
|
||||
actions={
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" onClick={() => (window.location.hash = '/knowledge/search')}>
|
||||
<Search data-icon="inline-start" />
|
||||
知识检索
|
||||
</Button>
|
||||
<Button onClick={() => (window.location.hash = '/knowledge/items/new')}>
|
||||
<Plus data-icon="inline-start" />
|
||||
新建知识条目
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
||||
<AppSection title="新建知识库">
|
||||
<form className="grid gap-4 md:grid-cols-[1fr_1fr_1fr_auto] md:items-end" onSubmit={handleCreate}>
|
||||
<AppField label="名称" htmlFor="knowledge-base-name" required>
|
||||
<Input
|
||||
id="knowledge-base-name"
|
||||
required
|
||||
value={form.name}
|
||||
onChange={(event) => setForm((current) => ({ ...current, name: event.target.value }))}
|
||||
placeholder="例如:税务采集知识库"
|
||||
/>
|
||||
</AppField>
|
||||
<AppField label="范围" htmlFor="knowledge-base-scope">
|
||||
<Input
|
||||
id="knowledge-base-scope"
|
||||
value={form.scope}
|
||||
onChange={(event) => setForm((current) => ({ ...current, scope: event.target.value }))}
|
||||
placeholder="例如:数据采集"
|
||||
/>
|
||||
</AppField>
|
||||
<AppField label="描述" htmlFor="knowledge-base-description">
|
||||
<Input
|
||||
id="knowledge-base-description"
|
||||
value={form.description}
|
||||
onChange={(event) => setForm((current) => ({ ...current, description: event.target.value }))}
|
||||
placeholder="知识库用途说明"
|
||||
/>
|
||||
</AppField>
|
||||
<Button type="submit">创建知识库</Button>
|
||||
</form>
|
||||
</AppSection>
|
||||
|
||||
{error ? <AppEmptyState title="无法加载知识库" description={error} /> : null}
|
||||
{!error ? (
|
||||
<AppDataTable
|
||||
columns={columns}
|
||||
rows={bases}
|
||||
getRowKey={(base) => base.id}
|
||||
emptyTitle="暂无知识库"
|
||||
emptyDescription="创建知识库后,可继续维护文档、知识条目和检索内容。"
|
||||
footer={<p className="text-sm text-muted-foreground">共 {bases.length} 个知识库</p>}
|
||||
/>
|
||||
) : null}
|
||||
</AppPage>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user