Files
geo/apps/admin-web/src/components/KnowledgeDetailDrawer.vue
T
root 162abdc97c
Backend CI / Backend (push) Has been cancelled
Frontend CI / Frontend (push) Failing after 1m39s
chore(frontend): introduce prettier + eslint and prune unused code
- Add Prettier 3 with prettier-plugin-organize-imports (sorts/removes unused imports)
- Add ESLint 10 flat config with typescript-eslint + eslint-plugin-vue + eslint-config-prettier
- Add root scripts: format, format:check, lint, lint:fix
- Reformat 257 files across admin-web, ops-web, desktop-client, packages
- Remove unused locals/exports flagged by --noUnusedLocals/--noUnusedParameters
- Fix duplicate localTabLabel key, surrogate-pair regex u-flag, NBSP literal in regex
- Skip server/ (Go) and apps/browser-extension/ (deprecated per ADR)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 20:39:09 +08:00

181 lines
4.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { useQuery, useQueryClient } from '@tanstack/vue-query'
import { computed, watch } from 'vue'
import MarkdownPreview from '@/components/MarkdownPreview.vue'
import { knowledgeApi } from '@/lib/api'
import { formatDateTime } from '@/lib/display'
const props = defineProps<{
open: boolean
itemId: number | null
}>()
const emit = defineEmits<{
close: []
}>()
const queryClient = useQueryClient()
const enabled = computed(() => props.open && Boolean(props.itemId))
const detailQuery = useQuery({
queryKey: computed(() => ['knowledge', 'detail', props.itemId]),
enabled,
queryFn: () => knowledgeApi.detail(props.itemId as number),
})
const detail = computed(() => detailQuery.data.value)
const hasMarkdown = computed(() => Boolean(detail.value?.markdown_content?.trim()))
const isProcessing = computed(
() => detail.value?.status === 'pending' || detail.value?.status === 'processing',
)
const isFailed = computed(() => detail.value?.status === 'failed')
const statusLabel = computed(() => formatKnowledgeStatus(detail.value?.status))
const statusColor = computed(() => {
if (detail.value?.status === 'completed') return 'success'
if (detail.value?.status === 'failed') return 'error'
if (isProcessing.value) return 'processing'
return 'default'
})
watch(
[() => props.open, () => props.itemId, () => detail.value?.status],
([open, itemId, status], _prev, onCleanup) => {
if (!open || !itemId || (status !== 'pending' && status !== 'processing')) {
return
}
const timer = window.setInterval(() => {
void Promise.all([
detailQuery.refetch(),
queryClient.invalidateQueries({ queryKey: ['knowledge', 'items'] }),
])
}, 2500)
onCleanup(() => {
window.clearInterval(timer)
})
},
{ immediate: true },
)
function handleClose(): void {
emit('close')
}
function formatKnowledgeStatus(status?: string): string {
switch (status) {
case 'completed':
return '学习完成'
case 'processing':
return '处理中'
case 'failed':
return '解析失败'
case 'pending':
return '待处理'
case 'deleted':
return '已删除'
default:
return status || '未知状态'
}
}
</script>
<template>
<a-drawer
:open="open"
width="760"
title="知识详情"
class="knowledge-detail-drawer"
@close="handleClose"
>
<div v-if="detailQuery.isPending.value" class="knowledge-detail-drawer__loading">
<a-skeleton active :paragraph="{ rows: 10 }" />
</div>
<template v-else-if="detail">
<section class="knowledge-detail-drawer__hero">
<div>
<h2>{{ detail.name }}</h2>
<div class="knowledge-detail-drawer__meta">
<span>{{ detail.group_name }}</span>
<span>{{ formatDateTime(detail.created_at) }}</span>
<span v-if="detail.source_uri">{{ detail.source_uri }}</span>
</div>
</div>
<a-tag :color="statusColor">{{ statusLabel }}</a-tag>
</section>
<a-alert
v-if="isProcessing"
type="info"
show-icon
message="处理中"
description="内容正在解析并整理为 Markdown,抽屉会自动刷新。"
class="knowledge-detail-drawer__alert"
/>
<a-alert
v-else-if="isFailed"
type="error"
show-icon
message="解析失败"
:description="detail.error_message || '当前内容解析失败,请稍后重试。'"
class="knowledge-detail-drawer__alert"
/>
<div v-if="hasMarkdown" class="knowledge-detail-drawer__content">
<MarkdownPreview :content="detail.markdown_content || ''" />
</div>
<a-empty v-else description="无可预览文本" />
</template>
<a-result
v-else
status="404"
title="未找到知识内容"
sub-title="该内容可能已删除或当前租户无权访问"
/>
</a-drawer>
</template>
<style scoped>
.knowledge-detail-drawer__loading {
padding-top: 8px;
}
.knowledge-detail-drawer__hero {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 16px;
margin-bottom: 16px;
}
.knowledge-detail-drawer__hero h2 {
margin: 0 0 8px;
color: #111827;
font-size: 22px;
font-weight: 700;
}
.knowledge-detail-drawer__meta {
display: flex;
flex-wrap: wrap;
gap: 8px 16px;
color: #6b7280;
font-size: 13px;
}
.knowledge-detail-drawer__alert {
margin-bottom: 16px;
}
.knowledge-detail-drawer__content {
padding: 20px 24px;
border: 1px solid #f0f0f0;
border-radius: 16px;
background: #fff;
}
</style>