import { computed, onBeforeUnmount, ref, watch } from 'vue'; import { ElMessage, ElMessageBox } from 'element-plus'; import { Document, Clock, User } from '@element-plus/icons-vue'; import { getKnowledgeBaseDetail, createDocument as createDocumentApi, listDocuments } from '@/services/api'; import { useWorkspaceShell } from '@/composables/shell/useWorkspaceShell'; import { useTemplateCatalog } from '@/composables/template/useTemplateCatalog'; import { usePageBoot } from '@/composables/shell/usePageBoot'; import { useApiAction } from '@/composables/actions/useApiAction'; import { DEFAULT_DOCUMENT_TYPE, normalizeDocumentType } from '@/utils/documentType'; export function useKnowledgeBaseDetailPage({ t, router, route, userStore, locale }) { const { runWithLoading, createApiErrorHandler } = useApiAction({ t }); const { systemName, activeMenu, isDarkMode, currentLanguage, userInfo, userAvatar, initLanguage, handleLanguageChange, toggleTheme, handleLogout, handleMenuSelect, fetchSystemInfo } = useWorkspaceShell({ locale, router, userStore, defaultActiveMenu: 'knowledge-base' }); const { boot } = usePageBoot({ initLanguage, fetchSystemInfo }); const knowledgeBaseName = ref(''); const knowledgeBaseDescription = ref(''); const totalDocuments = ref(0); const lastUpdated = ref(''); const ownerName = ref(''); const knowledgeBaseData = ref(null); const loading = ref(false); const { kbTemplates, loadingKb: kbTemplatesLoading, ensureLoaded: ensureTemplateCatalogLoaded } = useTemplateCatalog({ includeKnowledgeBase: true, knowledgeBaseId: computed(() => route.params.id || ''), onError: createApiErrorHandler({ context: 'loadKnowledgeBaseTemplates', errorMessage: t('common.requestFailed') }) }); const searchKeyword = ref(''); const sortBy = ref('name'); const currentPage = ref(1); const pageSize = ref(50); const totalDocumentsCount = ref(0); let searchTimer = null; const showCreateDialog = ref(false); const creatingLoading = ref(false); const selectedDocumentType = ref(DEFAULT_DOCUMENT_TYPE); const sortOptions = computed(() => [ { value: 'name', label: t('knowledgeBase.sortByName') }, { value: 'date', label: t('knowledgeBase.sortByDate') }, { value: 'type', label: t('knowledgeBase.sortByType') } ]); const formatDate = (dateString) => { if (!dateString) return t('common.unknown'); const date = new Date(dateString); return date.toLocaleDateString(); }; const knowledgeBaseStats = computed(() => [ { key: 'documents', icon: Document, label: t('knowledgeBase.documentsCount'), value: totalDocuments.value }, { key: 'updated', icon: Clock, label: t('knowledgeBase.lastUpdated'), value: formatDate(lastUpdated.value) }, { key: 'owner', icon: User, label: t('knowledgeBase.owner'), value: ownerName.value } ]); const directoryTreeData = computed(() => { if (!knowledgeBaseData.value?.documents) return []; const mapDoc = (doc, parentId = null) => ({ id: doc.external_id, label: doc.title, type: doc.type, isFolder: doc.hasChildren || (doc.children && doc.children.length > 0), isLeaf: !(doc.hasChildren || (doc.children && doc.children.length > 0)), tags: doc.tags || [], parentId, children: doc.children ? doc.children.map((child) => mapDoc(child, doc.external_id)) : [] }); return knowledgeBaseData.value.documents.map((doc) => mapDoc(doc)); }); const loadKnowledgeBaseDetail = async () => { const knowledgeBaseExternalID = route.params.id; if (!knowledgeBaseExternalID) { ElMessage.error(t('message.knowledgeBaseNotFound')); return; } await runWithLoading( loading, () => getKnowledgeBaseDetail(knowledgeBaseExternalID, { record_recent_log: true, page: 1, page_size: 1 }), { context: 'loadKnowledgeBaseDetail', errorMessage: t('message.loadKnowledgeBaseError'), onSuccess: (data) => { knowledgeBaseData.value = data; if (data.knowledge_base) { knowledgeBaseName.value = data.knowledge_base.name; knowledgeBaseDescription.value = data.knowledge_base.description; lastUpdated.value = data.knowledge_base.updated_at; totalDocuments.value = data.knowledge_base.documents_count || 0; ownerName.value = data.knowledge_base.creator_name || t('common.unknown'); } } } ); }; const getSortArgs = () => { if (sortBy.value === 'name') { return { order_by: 'title', order_desc: false }; } if (sortBy.value === 'type') { return { order_by: 'type', order_desc: false }; } return { order_by: 'updated_at', order_desc: true }; }; const loadKnowledgeBaseDocuments = async () => { const knowledgeBaseExternalID = route.params.id; if (!knowledgeBaseExternalID) return; const keyword = `${searchKeyword.value || ''}`.trim(); const sortArgs = getSortArgs(); await runWithLoading( loading, () => listDocuments({ knowledge_base_external_id: knowledgeBaseExternalID, title_keyword: keyword || undefined, page: currentPage.value, page_size: pageSize.value, ...sortArgs, options: { include_children: true, recursive: true } }), { context: 'loadKnowledgeBaseDocuments', errorMessage: t('message.loadKnowledgeBaseError'), onSuccess: (data) => { const previous = knowledgeBaseData.value || {}; knowledgeBaseData.value = { ...previous, documents: data.documents || [] }; totalDocumentsCount.value = data.total_count || 0; } } ); }; const fetchKnowledgeBaseTemplates = async () => { await ensureTemplateCatalogLoaded('knowledge_base'); }; const openCreateDocumentDialog = (documentType = DEFAULT_DOCUMENT_TYPE) => { selectedDocumentType.value = normalizeDocumentType(documentType); showCreateDialog.value = true; }; const createDocument = async (payload) => { const knowledgeBaseExternalID = route.params.id; if (!knowledgeBaseExternalID) { ElMessage.error(t('message.knowledgeBaseNotFound')); return; } const title = payload?.title?.trim() || t('document.untitledDefaultTitle'); await runWithLoading( creatingLoading, () => createDocumentApi({ title, type: normalizeDocumentType(payload?.type || selectedDocumentType.value), container_type: 'knowledge_base', is_public: false, knowledge_base_external_id: knowledgeBaseExternalID, parent_external_id: payload.parent_external_id || undefined, template_id: payload.template || undefined }), { context: 'createDocument', errorMessage: t('knowledgeBase.createDocumentError'), onSuccess: async () => { showCreateDialog.value = false; await loadKnowledgeBaseDetail(); await loadKnowledgeBaseDocuments(); } } ); }; const cancelCreateDocument = () => { showCreateDialog.value = false; }; const openDocument = (data) => { router.push(`/knowledge-base/${route.params.id}/document/${data.id}`); }; const handleTreeNodeClick = (data) => { openDocument(data); }; const handleNodeAction = (command, data) => { switch (command) { case 'rename': ElMessage.info(`${t('message.renameDocument')}: ${data.label}`); break; case 'share': ElMessage.info(`${t('message.shareDocument')}: ${data.label}`); break; case 'delete': ElMessageBox.confirm(t('message.confirmDelete'), t('common.warning'), { confirmButtonText: t('button.confirm'), cancelButtonText: t('button.cancel'), type: 'warning' }).then(() => { ElMessage.success(t('message.deleteSuccess')); }); break; default: break; } }; const handleSizeChange = async (val) => { pageSize.value = val; currentPage.value = 1; await loadKnowledgeBaseDocuments(); }; const handleCurrentChange = async (val) => { currentPage.value = val; await loadKnowledgeBaseDocuments(); }; const goBackToKnowledgeBase = () => { router.push('/knowledge-base'); }; const templateTagMapper = () => ({ type: 'info', label: t('templates.private') }); const templateMetaMapper = (tpl) => [ { label: t('templates.updatedAt'), value: formatDate(tpl.updated_at || tpl.updatedAt) } ]; const openTemplate = (tpl) => { if (!tpl?.id) return; router.push(`/template/${tpl.id}`); }; const init = async () => { await boot(loadKnowledgeBaseDetail, fetchKnowledgeBaseTemplates); await loadKnowledgeBaseDocuments(); }; watch([searchKeyword, sortBy], () => { if (searchTimer) { clearTimeout(searchTimer); } searchTimer = setTimeout(async () => { currentPage.value = 1; await loadKnowledgeBaseDocuments(); }, 300); }); onBeforeUnmount(() => { if (searchTimer) { clearTimeout(searchTimer); searchTimer = null; } }); return { systemName, activeMenu, isDarkMode, currentLanguage, userInfo, userAvatar, knowledgeBaseName, knowledgeBaseDescription, knowledgeBaseData, loading, kbTemplates, kbTemplatesLoading, knowledgeBaseStats, searchKeyword, sortBy, sortOptions, currentPage, pageSize, totalDocumentsCount, showCreateDialog, creatingLoading, selectedDocumentType, createDocument, cancelCreateDocument, openCreateDocumentDialog, handleTreeNodeClick, openDocument, handleNodeAction, handleSizeChange, handleCurrentChange, handleMenuSelect, handleLanguageChange, toggleTheme, handleLogout, goBackToKnowledgeBase, templateTagMapper, templateMetaMapper, openTemplate, directoryTreeData, init }; }