feat: 知识库文档列表标题增加「清空」按钮——一键清空全部文档(分块与检索索引级联删除),清空前弹窗警告确认

This commit is contained in:
xianrenge
2026-08-18 01:37:29 +08:00
parent 405bdec2c6
commit 01b17177c8
7 changed files with 82 additions and 4 deletions
+10
View File
@@ -1668,6 +1668,16 @@ pub fn remove_kb_document(state: State<'_, App>, document_id: String) -> Result<
kb_db::delete_document(&db, &document_id).map_err(|e| e.to_string())
}
/// 清空知识库内的全部文档(分块与检索索引一并删除)。
#[tauri::command]
pub fn kb_clear_documents(state: State<'_, App>, kb_id: String) -> Result<i64, String> {
let db = state.core.db.lock().unwrap();
kb_db::get_knowledge_base(&db, &kb_id)
.map_err(|e| e.to_string())?
.ok_or_else(|| "知识库不存在".to_string())?;
kb_db::delete_all_documents(&db, &kb_id).map_err(|e| e.to_string())
}
/// 按知识库最新分块设置重新切分指定文档。
#[tauri::command]
pub fn kb_rechunk_document(
+1
View File
@@ -194,6 +194,7 @@ pub fn run() {
commands::get_kb_document,
commands::kb_import_documents,
commands::remove_kb_document,
commands::kb_clear_documents,
commands::kb_rechunk_document,
commands::kb_search,
commands::kb_list_sources,
+35
View File
@@ -250,6 +250,15 @@ pub fn delete_document(db: &Connection, id: &str) -> Result<()> {
Ok(())
}
/// 清空某知识库的全部文档(分块随外键级联删除,FTS 索引由触发器同步)。
pub fn delete_all_documents(db: &Connection, kb_id: &str) -> Result<i64> {
let n = db.execute(
"DELETE FROM kb_documents WHERE kb_id = ?1",
params![kb_id],
)?;
Ok(n as i64)
}
/// 同一知识库下是否已存在来自该文件路径的文档(目录导入去重)。
pub fn document_exists(db: &Connection, kb_id: &str, file_path: &str) -> Result<bool> {
if file_path.is_empty() {
@@ -608,6 +617,32 @@ mod tests {
assert!(hits.is_empty());
}
#[test]
fn clear_all_documents_removes_everything() {
let conn = test_conn();
insert_knowledge_base(
&conn,
&KnowledgeBase {
id: "kb1".to_string(),
name: "测试库".to_string(),
description: String::new(),
chunk_size: 200,
chunk_overlap: 20,
doc_count: 0,
chunk_count: 0,
created_at: String::new(),
updated_at: String::new(),
},
)
.unwrap();
insert_doc(&conn, "d1", "第一份文档内容,用于清空测试。");
insert_doc(&conn, "d2", "第二份文档内容,同样会被清空。");
let n = delete_all_documents(&conn, "kb1").unwrap();
assert_eq!(n, 2);
assert!(list_documents(&conn, "kb1").unwrap().is_empty());
assert!(search(&conn, Some("kb1"), "清空", 10, 0).unwrap().is_empty());
}
#[test]
fn rechunk_changes_chunk_count() {
let conn = test_conn();
+1 -1
View File
@@ -110,7 +110,7 @@ ui/src/
| 工作流 | `list_workflows``add_workflow``update_workflow``remove_workflow``set_workflow_enabled``restore_preset_workflows``run_workflow` |
| 定时计划 | `list_scheduled_tasks``add_scheduled_task``update_scheduled_task``remove_scheduled_task``set_scheduled_task_enabled``run_scheduled_task_now` |
| 邮件 | `mail_test`(用当前 SMTP 配置发送测试邮件) |
| 知识库 | `list_knowledge_bases``add_knowledge_base``update_knowledge_base``remove_knowledge_base``list_kb_documents`(分页)、`get_kb_document``kb_import_documents``remove_kb_document``kb_rechunk_document``kb_search`(分页) |
| 知识库 | `list_knowledge_bases``add_knowledge_base``update_knowledge_base``remove_knowledge_base``list_kb_documents`(分页)、`get_kb_document``kb_import_documents``remove_kb_document``kb_clear_documents``kb_rechunk_document``kb_search`(分页) |
| 知识库·目录来源 | `kb_list_sources``kb_add_source``kb_scan_sources`(后台扫描,`kb://scan-progress` 事件推送进度)、`kb_remove_source` |
| 应用/设置 | `app_info``autostart_status``autostart_set``settings_get``settings_set` |
| 模型 | `list_models``import_model``remove_model``set_model_enabled``scan_models``add_remote_model` |
+2 -1
View File
@@ -140,7 +140,7 @@
- **类型过滤**:只收录**可读文本**(提取内容并分块检索)、**图片 / 视频**、**音频**三类文件(图片视频音频以文件名为内容登记,可按文件名检索),检测到其他类型自动忽略;
- 同一目录重复添加自动更新配置不产生重复来源;按文件路径去重,可一键「重新扫描」增量导入新增文件;删除目录来源会同时删除它导入的文档。
- **扫描进度**:添加目录 / 重新扫描时显示进度条与处理统计(处理 X / Y 个文件、成功、失败、忽略、分块数),失败文件在结尾汇总提示。
- **文档操作**:文档列表每页 10 条,带分页控件;展示类型 / 大小 / 字数 / 分块数;支持全文预览、「重切」(按最新分块设置重新切分,适用于修改设置后)与删除
- **文档操作**:文档列表每页 10 条,带分页控件;展示类型 / 大小 / 字数 / 分块数;支持全文预览、「重切」(按最新分块设置重新切分,适用于修改设置后)、单条删除;标题「文档(共 x 个)」右侧提供「清空」按钮,一键清空该知识库全部文档(含分块与检索索引),清空前弹窗警告确认
- **检索**:全文检索基于 SQLite FTS5(trigram 分词,对中文友好),按 BM25 相关性排序;支持单库检索(也可全库检索),结果每页 10 条带分页控件,展示来源文档与分块序号,可一键复制分块内容用于对话。
- **页面布局**:知识库主区内自上而下依次为「目录来源」→「文档列表」→「检索」。
- 数据表:`knowledge_bases``kb_documents`(含提取后的纯文本)、`kb_chunks`(分块)、`kb_chunks_fts`FTS5 全文索引,contentless-delete 模式 + 触发器同步)。
@@ -232,6 +232,7 @@
### 2026-08-17
- 知识库文档列表标题增加「清空」按钮:一键清空当前知识库全部文档(分块与索引级联删除),清空前弹出警告确认。
- 知识库页调整:文档列表与检索结果增加分页控件;目录来源、文档列表区块移到检索上方;目录扫描增加进度条与处理统计(处理/成功/失败/忽略/分块数,事件驱动实时刷新)。
- 修复:知识库「添加目录 → 浏览…」无响应——为 dialog 插件在 capabilities 中补充权限(`dialog:default`),并让选择器异常在前端可见。
- 知识库「添加目录」弹窗支持两种方式选择目录:手动输入路径,或点击「浏览…」调起系统原生文件夹选择器(接入 tauri-plugin-dialog)。
+2
View File
@@ -526,6 +526,8 @@ export const api = {
) => invoke<KbImportResult[]>("kb_import_documents", { kbId, files }),
removeKbDocument: (documentId: string) =>
invoke<void>("remove_kb_document", { documentId }),
kbClearDocuments: (kbId: string) =>
invoke<number>("kb_clear_documents", { kbId }),
kbRechunkDocument: (documentId: string) =>
invoke<number>("kb_rechunk_document", { documentId }),
kbSearch: (kbId: string | null, query: string, page?: number, pageSize?: number) =>
+31 -2
View File
@@ -253,6 +253,25 @@ export default function KnowledgeBasePage() {
}
}
async function handleClearDocs() {
if (!selected) return;
if (
!confirm(
`确定清空知识库「${selected.name}」的全部 ${docTotal} 个文档吗?\n\n此操作不可恢复,所有文档、分块与检索索引将一并删除。`,
)
) {
return;
}
try {
const n = await api.kbClearDocuments(selected.id);
setMsg(`已清空 ${n} 个文档`);
await loadDocPage(selected.id, 1);
await refreshKbs();
} catch (e) {
setMsg(`清空失败:${String(e)}`);
}
}
async function handleSearch(page = 1) {
if (!query.trim()) return;
setSearching(true);
@@ -553,8 +572,18 @@ export default function KnowledgeBasePage() {
{/* 文档列表 */}
<section className="mb-6">
<div className="mb-2 text-xs uppercase text-slate-500">
{docTotal}
<div className="mb-2 flex items-center justify-between">
<div className="text-xs uppercase text-slate-500">
{docTotal}
</div>
{docTotal > 0 ? (
<button
className="rounded-lg border border-border bg-panel-2 px-2.5 py-1 text-xs text-slate-400 hover:text-red-400"
onClick={handleClearDocs}
>
</button>
) : null}
</div>
{docs.length === 0 ? (
<div className="rounded-xl border border-dashed border-border bg-panel/50 px-4 py-10 text-center text-sm text-slate-500">