349 lines
10 KiB
JavaScript
349 lines
10 KiB
JavaScript
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
|
|
};
|
|
}
|