diff --git a/mateclaw-server/src/main/java/vip/mate/wiki/controller/WikiController.java b/mateclaw-server/src/main/java/vip/mate/wiki/controller/WikiController.java index 2278f6ea..c33183d2 100644 --- a/mateclaw-server/src/main/java/vip/mate/wiki/controller/WikiController.java +++ b/mateclaw-server/src/main/java/vip/mate/wiki/controller/WikiController.java @@ -361,6 +361,40 @@ public class WikiController { return R.ok(pageService.getBacklinks(kbId, slug)); } + // RFC-051 PR-7 follow-up: archive surfaces. Default-list is filtered, so the UI + // needs a dedicated endpoint to enumerate archived pages and a way to flip the + // flag via REST (the agent tools wiki_archive_page / wiki_unarchive_page already + // exist, but the admin UI shouldn't have to go through agent plumbing). + + @RequireWorkspaceRole("viewer") + @Operation(summary = "列出知识库中所有 archived=1 的页面(不含 content)") + @GetMapping("/knowledge-bases/{kbId}/pages/archived") + public R> listArchivedPages(@PathVariable Long kbId, + @RequestHeader(value = "X-Workspace-Id", required = false) Long workspaceId) { + verifyKBWorkspace(kbId, workspaceId); + return R.ok(pageService.listArchivedByKbId(kbId)); + } + + @RequireWorkspaceRole("admin") + @Operation(summary = "归档单个页面(软归档;可恢复)") + @PostMapping("/knowledge-bases/{kbId}/pages/{slug}/archive") + public R> archivePage(@PathVariable Long kbId, @PathVariable String slug, + @RequestHeader(value = "X-Workspace-Id", required = false) Long workspaceId) { + verifyKBWorkspace(kbId, workspaceId); + boolean changed = pageService.setArchived(kbId, slug, true); + return R.ok(Map.of("slug", slug, "archived", true, "changed", changed)); + } + + @RequireWorkspaceRole("admin") + @Operation(summary = "取消归档") + @PostMapping("/knowledge-bases/{kbId}/pages/{slug}/unarchive") + public R> unarchivePage(@PathVariable Long kbId, @PathVariable String slug, + @RequestHeader(value = "X-Workspace-Id", required = false) Long workspaceId) { + verifyKBWorkspace(kbId, workspaceId); + boolean changed = pageService.setArchived(kbId, slug, false); + return R.ok(Map.of("slug", slug, "archived", false, "changed", changed)); + } + // ==================== Processing ==================== @RequireWorkspaceRole("member") diff --git a/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiPageService.java b/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiPageService.java index ba9d8b82..e4fd9e15 100644 --- a/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiPageService.java +++ b/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiPageService.java @@ -67,6 +67,21 @@ public class WikiPageService { .toList(); } + /** + * RFC-051 PR-7 follow-up: list ONLY archived pages — the inverse of the + * default {@link #listByKbId} filter. Used by the admin UI's "show archived" + * panel so users can see what they archived and recover it. + */ + public List listArchivedByKbId(Long kbId) { + List pages = pageMapper.selectList( + new LambdaQueryWrapper() + .eq(WikiPageEntity::getKbId, kbId) + .eq(WikiPageEntity::getArchived, 1) + .orderByDesc(WikiPageEntity::getUpdateTime)); + pages.forEach(p -> p.setContent(null)); + return pages; + } + /** * 列出知识库的所有页面(不含 content)。 * RFC-051 PR-7: archived 页面默认不返回。 diff --git a/mateclaw-ui/src/api/index.ts b/mateclaw-ui/src/api/index.ts index 25435e40..6d664134 100644 --- a/mateclaw-ui/src/api/index.ts +++ b/mateclaw-ui/src/api/index.ts @@ -448,6 +448,14 @@ export const wikiApi = { getBacklinks: (kbId: number, slug: string) => http.get(`/wiki/knowledge-bases/${kbId}/pages/${encodeURIComponent(slug)}/backlinks`), + // RFC-051 PR-7: archived pages + listArchivedPages: (kbId: number) => + http.get(`/wiki/knowledge-bases/${kbId}/pages/archived`), + archivePage: (kbId: number, slug: string) => + http.post(`/wiki/knowledge-bases/${kbId}/pages/${encodeURIComponent(slug)}/archive`), + unarchivePage: (kbId: number, slug: string) => + http.post(`/wiki/knowledge-bases/${kbId}/pages/${encodeURIComponent(slug)}/unarchive`), + // Processing processKB: (kbId: number) => http.post(`/wiki/knowledge-bases/${kbId}/process`), getProcessingStatus: (kbId: number) => http.get(`/wiki/knowledge-bases/${kbId}/processing-status`), diff --git a/mateclaw-ui/src/i18n/locales/en-US.ts b/mateclaw-ui/src/i18n/locales/en-US.ts index fd70811d..cf4b498b 100644 --- a/mateclaw-ui/src/i18n/locales/en-US.ts +++ b/mateclaw-ui/src/i18n/locales/en-US.ts @@ -1344,6 +1344,9 @@ export default { systemPageHint: 'overview / log are built-in system pages — they can\'t be deleted', lockedPageBadge: 'Locked', lockedPageHint: 'This page is locked — AI tools and batch operations skip it', + archivedSection: 'Archived', + noArchived: 'No archived pages', + unarchive: 'Restore', pageTypes: { concept: 'Concepts', person: 'People', diff --git a/mateclaw-ui/src/i18n/locales/zh-CN.ts b/mateclaw-ui/src/i18n/locales/zh-CN.ts index b140207a..1f199b9f 100644 --- a/mateclaw-ui/src/i18n/locales/zh-CN.ts +++ b/mateclaw-ui/src/i18n/locales/zh-CN.ts @@ -1354,6 +1354,9 @@ export default { systemPageHint: 'overview / log 是知识库自带的系统页,无法删除', lockedPageBadge: '已锁定', lockedPageHint: '该页被手动锁定,AI 工具与批量操作不会修改或删除', + archivedSection: '已归档', + noArchived: '没有已归档的页面', + unarchive: '恢复', pageTypes: { concept: '概念', person: '人物', diff --git a/mateclaw-ui/src/views/Wiki/index.vue b/mateclaw-ui/src/views/Wiki/index.vue index 73f3030f..12590cb5 100644 --- a/mateclaw-ui/src/views/Wiki/index.vue +++ b/mateclaw-ui/src/views/Wiki/index.vue @@ -159,6 +159,47 @@
{{ t('wiki.noPages') }}
+ + +
+ +
+
{{ t('common.loading') }}
+
{{ t('wiki.noArchived') }}
+
+
+
{{ page.title }}
+
v{{ page.version }} · {{ page.slug }}
+
+ +
+
+
@@ -271,7 +312,7 @@