refactor(knowledge): keep latest version only

This commit is contained in:
2026-07-16 18:24:49 +08:00
parent 9ba8fb6e0c
commit e3634eb26c
8 changed files with 109 additions and 197 deletions

View File

@@ -10,7 +10,6 @@ import type {
KnowledgeDetail,
KnowledgeItem,
KnowledgeSyncJob,
KnowledgeVersion,
MigrationSummary,
} from "../types/api";
@@ -22,7 +21,6 @@ const loading = ref(false);
const operatingId = ref<number | null>(null);
const detailOpen = ref(false);
const detail = ref<KnowledgeDetail | null>(null);
const versions = ref<KnowledgeVersion[]>([]);
const jobs = ref<KnowledgeSyncJob[]>([]);
const previewVersionId = ref<number | null>(null);
const activeTab = ref("overview");
@@ -136,11 +134,6 @@ async function handleMore(command: string, item: KnowledgeItem) {
try {
if (command === "detail") return await openDetail(item);
if (command === "preview") return emit("preview", item.id);
if (command === "versions") {
await openDetail(item);
if (detailOpen.value) activeTab.value = "versions";
return;
}
if (command === "archive") return await archive(item);
if (command === "restore") return await restore(item);
if (command === "delete") return await remove(item);
@@ -196,7 +189,7 @@ async function openDetail(item: KnowledgeItem) {
previewVersionId.value = null;
try {
const refreshedDetail = await refreshDetail(item.id);
previewVersionId.value = refreshedDetail.currentVersion?.id ?? versions.value[0]?.id ?? null;
previewVersionId.value = refreshedDetail.currentVersion?.id ?? null;
} catch (error) {
detailOpen.value = false;
ElMessage.error(errorMessage(error, "知识库详情加载失败"));
@@ -204,11 +197,10 @@ async function openDetail(item: KnowledgeItem) {
}
async function refreshDetail(id: number) {
const [nextDetail, nextVersions, nextJobs] = await Promise.all([
api.knowledgeDetail(id), api.knowledgeVersions(id), api.knowledgeSyncJobs(id),
const [nextDetail, nextJobs] = await Promise.all([
api.knowledgeDetail(id), api.knowledgeSyncJobs(id),
]);
detail.value = nextDetail;
versions.value = nextVersions;
jobs.value = nextJobs;
return nextDetail;
}
@@ -234,7 +226,7 @@ async function sync(item: KnowledgeItem) {
async function runMigration() {
if (!await confirmAction(
"将依次重新读取全部飞书文章。每个知识库同步成功后会立即替换为最新版本,原版本仍可回滚。",
"将依次重新读取全部飞书文章。同步成功后仅保留最新版,旧版本和旧内容会被清理。",
"开始知识迁移",
{ confirmButtonText: "开始迁移", cancelButtonText: "取消", type: "warning" },
)) return;
@@ -250,23 +242,6 @@ async function runMigration() {
}
}
async function rollback(version: KnowledgeVersion) {
if (!detail.value) return;
if (!await confirmAction(
`将从当前版本回滚到 V${version.versionNo},从用户下一条消息开始生效。`,
"回滚知识版本",
{ confirmButtonText: "确认回滚", cancelButtonText: "取消", type: "warning" },
)) return;
try {
await api.rollbackKnowledgeVersion(detail.value.knowledge.id, version.id, detail.value.knowledge.status === 1);
ElMessage.success("知识版本已回滚");
await load();
await refreshDetail(detail.value.knowledge.id);
} catch (error) {
ElMessage.error(errorMessage(error, "版本回滚失败"));
}
}
async function toggleOpen(item: KnowledgeItem): Promise<boolean> {
const next = item.status !== 1;
if (!await confirmAction(
@@ -289,7 +264,7 @@ async function toggleOpen(item: KnowledgeItem): Promise<boolean> {
}
async function archive(item: KnowledgeItem) {
if (!await confirmAction("归档后知识库会立即关闭,但历史正式版本和检索记录保留。", "归档知识库", {
if (!await confirmAction("归档后知识库会立即关闭,当前最新版和检索记录仍会保留。", "归档知识库", {
confirmButtonText: "确认归档", cancelButtonText: "取消", type: "warning",
})) return;
await api.setKnowledgeLifecycle(item.id, "archived");
@@ -308,11 +283,6 @@ async function remove(item: KnowledgeItem) {
await load();
}
async function showVersion(version: KnowledgeVersion) {
previewVersionId.value = version.id;
activeTab.value = "content";
}
function statusType(status: string) {
if (["published", "passed", "normal", "completed"].includes(status)) return "success";
if (["failed", "blocked", "error", "rejected"].includes(status)) return "danger";
@@ -344,7 +314,7 @@ async function confirmAction(
<template>
<div class="page-head inline">
<div><h2>知识库管理</h2><p>一篇飞书文章对应一个知识库同步成功后最新版本自动生效历史版本可随时回滚</p></div>
<div><h2>知识库管理</h2><p>一篇飞书文章对应一个知识库同步成功后自动生效并且只保留最新版</p></div>
<el-button type="primary" :loading="loading" @click="runMigration">迁移现有知识库</el-button>
</div>
@@ -393,7 +363,7 @@ async function confirmAction(
<el-table-column label="当前版本" width="130"><template #default="{ row }"><span>{{ row.currentVersionId ? `#${row.currentVersionId}` : '尚未同步' }}</span></template></el-table-column>
<el-table-column label="状态" width="210"><template #default="{ row }"><div class="knowledge-status-cell"><el-switch v-model="row.status" :active-value="1" :inactive-value="0" active-text="开放" inactive-text="关闭" inline-prompt :loading="operatingId === row.id" :disabled="row.lifecycleStatus === 'archived'" :before-change="() => toggleOpen(row)" /><el-tag :type="statusType(row.sourceStatus)">{{ row.sourceStatus === 'normal' ? '源正常' : '源异常' }}</el-tag></div></template></el-table-column>
<el-table-column prop="lastSyncedAt" label="最近同步" width="180" />
<el-table-column label="操作" width="220" fixed="right"><template #default="{ row }"><TableRowActions><el-button link type="primary" @click="openDetail(row)">详情</el-button><el-button link type="primary" :disabled="row.lifecycleStatus === 'archived'" :loading="operatingId === row.id" @click="sync(row)">同步最新版</el-button><el-dropdown trigger="click" :teleported="true" @command="handleMore($event, row)"><el-button link type="primary">更多</el-button><template #dropdown><el-dropdown-menu><el-dropdown-item command="detail">查看详情</el-dropdown-item><el-dropdown-item command="preview">进入 Agent 预览</el-dropdown-item><el-dropdown-item command="versions">查看版本</el-dropdown-item><el-dropdown-item v-if="row.lifecycleStatus === 'active'" command="archive" divided>归档</el-dropdown-item><el-dropdown-item v-else command="restore" divided>恢复</el-dropdown-item><el-dropdown-item command="delete" :disabled="row.lifecycleStatus !== 'archived'" divided>删除</el-dropdown-item></el-dropdown-menu></template></el-dropdown></TableRowActions></template></el-table-column>
<el-table-column label="操作" width="220" fixed="right"><template #default="{ row }"><TableRowActions><el-button link type="primary" @click="openDetail(row)">详情</el-button><el-button link type="primary" :disabled="row.lifecycleStatus === 'archived'" :loading="operatingId === row.id" @click="sync(row)">同步最新版</el-button><el-dropdown trigger="click" :teleported="true" @command="handleMore($event, row)"><el-button link type="primary">更多</el-button><template #dropdown><el-dropdown-menu><el-dropdown-item command="detail">查看详情</el-dropdown-item><el-dropdown-item command="preview">进入 Agent 预览</el-dropdown-item><el-dropdown-item v-if="row.lifecycleStatus === 'active'" command="archive" divided>归档</el-dropdown-item><el-dropdown-item v-else command="restore" divided>恢复</el-dropdown-item><el-dropdown-item command="delete" :disabled="row.lifecycleStatus !== 'archived'" divided>删除</el-dropdown-item></el-dropdown-menu></template></el-dropdown></TableRowActions></template></el-table-column>
</el-table>
<AdminPagination :page="pager.page" :page-size="pager.pageSize" :total="pager.total" @change="load" />
@@ -411,9 +381,8 @@ async function confirmAction(
<section v-if="detail.manifest" class="manifest-panel"><h4>Knowledge Manifest</h4><p><strong>用途</strong>{{ detail.manifest.purpose }}</p><p><strong>适用</strong>{{ detail.manifest.applicableQuestions }}</p><p><strong>不适用</strong>{{ detail.manifest.inapplicableQuestions }}</p><p><strong>核心主题</strong>{{ detail.manifest.coreTopics }}</p><p><strong>边界</strong>{{ detail.manifest.boundaries }}</p></section>
</el-tab-pane>
<el-tab-pane label="内容预览" name="content" lazy>
<KnowledgeContentPreview v-model:version-id="previewVersionId" :versions="versions" />
<KnowledgeContentPreview :version-id="previewVersionId" />
</el-tab-pane>
<el-tab-pane label="版本记录" name="versions"><el-table :data="versions"><el-table-column label="版本" width="90"><template #default="{ row }">V{{ row.versionNo }}</template></el-table-column><el-table-column prop="status" label="状态" width="130" /><el-table-column prop="qualityStatus" label="质检" width="120" /><el-table-column prop="createdAt" label="生成时间" /><el-table-column prop="publishedAt" label="发布时间" /><el-table-column label="操作" width="180"><template #default="{ row }"><el-button link type="primary" @click="showVersion(row)">查看内容</el-button><el-button v-if="['published','superseded'].includes(row.status) && row.id !== detail?.knowledge.currentVersionId" link @click="rollback(row)">回滚</el-button></template></el-table-column></el-table></el-tab-pane>
<el-tab-pane label="同步记录" name="jobs"><el-table :data="jobs"><el-table-column prop="id" label="任务" width="80" /><el-table-column prop="status" label="状态" width="130" /><el-table-column prop="stage" label="阶段" width="130" /><el-table-column label="进度" width="180"><template #default="{ row }"><el-progress :percentage="row.progress" /></template></el-table-column><el-table-column prop="sourceError" label="失败原因" min-width="260" /></el-table></el-tab-pane>
</el-tabs>
</template>