feat(admin): add knowledge content preview

This commit is contained in:
2026-07-16 10:39:46 +08:00
parent 56c9f2f359
commit c3f24d0ccd
2 changed files with 201 additions and 10 deletions

View File

@@ -3,6 +3,7 @@ import { ElMessage, ElMessageBox } from "element-plus";
import { onMounted, reactive, ref } from "vue";
import { api } from "../services/api";
import KnowledgeContentPreview from "./KnowledgeContentPreview.vue";
import TableRowActions from "./TableRowActions.vue";
import AdminPagination from "./AdminPagination.vue";
import type {
@@ -10,7 +11,6 @@ import type {
KnowledgeItem,
KnowledgeSyncJob,
KnowledgeVersion,
KnowledgeVersionContent,
MigrationSummary,
} from "../types/api";
@@ -24,7 +24,7 @@ const detailOpen = ref(false);
const detail = ref<KnowledgeDetail | null>(null);
const versions = ref<KnowledgeVersion[]>([]);
const jobs = ref<KnowledgeSyncJob[]>([]);
const versionContent = ref<KnowledgeVersionContent | null>(null);
const previewVersionId = ref<number | null>(null);
const activeTab = ref("overview");
const selectedRows = ref<KnowledgeItem[]>([]);
const editOpen = ref(false);
@@ -194,10 +194,12 @@ async function resolveNode() {
async function openDetail(item: KnowledgeItem) {
detailOpen.value = true;
detail.value = null;
activeTab.value = "overview";
versionContent.value = null;
previewVersionId.value = null;
try {
await refreshDetail(item.id);
const refreshedDetail = await refreshDetail(item.id);
previewVersionId.value = refreshedDetail.currentVersion?.id ?? versions.value[0]?.id ?? null;
} catch (error) {
detailOpen.value = false;
ElMessage.error(errorMessage(error, "知识库详情加载失败"));
@@ -205,9 +207,13 @@ async function openDetail(item: KnowledgeItem) {
}
async function refreshDetail(id: number) {
[detail.value, versions.value, jobs.value] = await Promise.all([
const [nextDetail, nextVersions, nextJobs] = await Promise.all([
api.knowledgeDetail(id), api.knowledgeVersions(id), api.knowledgeSyncJobs(id),
]);
detail.value = nextDetail;
versions.value = nextVersions;
jobs.value = nextJobs;
return nextDetail;
}
async function sync(item: KnowledgeItem, mode: "review" | "replace") {
@@ -358,7 +364,7 @@ async function remove(item: KnowledgeItem) {
}
async function showVersion(version: KnowledgeVersion) {
versionContent.value = await api.knowledgeVersionContent(version.id);
previewVersionId.value = version.id;
activeTab.value = "content";
}
@@ -455,7 +461,7 @@ async function confirmAction(
<template v-if="detail">
<div class="knowledge-detail-head">
<div><h3>{{ detail.knowledge.name }}</h3><p>{{ typeLabel(detail.knowledge.knowledgeType) }} · {{ detail.knowledge.reviewMode === 'manual' ? '必须人工审核' : '允许自动发布' }}</p></div>
<div><el-tag :type="detail.knowledge.status === 1 ? 'success' : 'info'">{{ detail.knowledge.status === 1 ? '已开放' : '已关闭' }}</el-tag><el-tag :type="statusType(detail.knowledge.sourceStatus)">{{ detail.knowledge.sourceStatus === 'normal' ? '源文章正常' : '源文章异常' }}</el-tag></div>
<div class="knowledge-detail-head-actions"><el-button type="primary" plain @click="activeTab = 'content'">预览内容</el-button><el-tag :type="detail.knowledge.status === 1 ? 'success' : 'info'">{{ detail.knowledge.status === 1 ? '已开放' : '已关闭' }}</el-tag><el-tag :type="statusType(detail.knowledge.sourceStatus)">{{ detail.knowledge.sourceStatus === 'normal' ? '源文章正常' : '源文章异常' }}</el-tag></div>
</div>
<el-alert v-if="detail.knowledge.sourceError" :title="detail.knowledge.sourceError" type="error" :closable="false" />
<el-tabs v-model="activeTab">
@@ -467,9 +473,8 @@ async function confirmAction(
<div v-else-if="detail.pendingVersion?.status === 'approved'" class="version-actions"><el-button type="primary" @click="publish(true)">发布并保持开放</el-button><el-button @click="publish(false)">发布后关闭</el-button></div>
<el-alert v-else-if="detail.pendingVersion?.status === 'rejected'" title="该版本已被拒绝,请在飞书修改源文章后重新同步。" type="warning" :closable="false" />
</el-tab-pane>
<el-tab-pane label="内容结构" name="content">
<el-empty v-if="!versionContent" description="请从版本记录中选择一个版本查看" />
<template v-else><el-collapse><el-collapse-item v-for="section in versionContent.sections" :key="section.id" :title="`${section.sectionKey} ${section.title}`"><pre class="source-content">{{ section.content }}</pre></el-collapse-item></el-collapse><h4>知识卡</h4><el-table :data="versionContent.cards"><el-table-column prop="title" label="标题" /><el-table-column prop="summary" label="摘要" min-width="300" /><el-table-column prop="riskLevel" label="风险" width="100" /><el-table-column prop="reviewStatus" label="审核" width="110" /></el-table></template>
<el-tab-pane label="内容预览" name="content" lazy>
<KnowledgeContentPreview v-model:version-id="previewVersionId" :versions="versions" />
</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="mode" label="模式" width="110" /><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>
@@ -522,6 +527,12 @@ async function confirmAction(
gap: 10px;
}
.knowledge-detail-head-actions {
display: flex;
align-items: center;
gap: 8px;
}
.batch-edit-form {
margin-top: 20px;
}