Files
frontend/src/composables/document/editor/useDocumentEditorActions.js
T
2026-07-15 22:51:56 +08:00

307 lines
8.5 KiB
JavaScript

import { ref } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import {
createDocument as createDocumentApi,
createTemplate as createTemplateApi,
deleteDocument as deleteDocumentApi,
updateDocumentMeta
} from '@/services/api';
import { useApiAction } from '@/composables/actions/useApiAction';
import { DEFAULT_DOCUMENT_TYPE, normalizeDocumentType } from '@/utils/documentType';
export function useDocumentEditorActions({
t,
router,
kbId,
docId,
currentDocType,
currentDocTitle,
editorContent,
directoryTree,
updateTreeNodeTitle,
fetchDocuments
}) {
const { runWithLoading } = useApiAction({ t });
const isEditingTitle = ref(false);
const pendingTitle = ref('');
const savingTitle = ref(false);
const renamingNode = ref(false);
const showCreateDialog = ref(false);
const creatingLoading = ref(false);
const pendingParentId = ref(null);
const selectedDocumentType = ref(DEFAULT_DOCUMENT_TYPE);
const savingTemplate = ref(false);
const showTemplateDialog = ref(false);
const templateDialogInit = ref({
name: '',
description: '',
scope: 'own',
tags: '',
content: ''
});
const openCreateDocumentDialog = (parentId = null, documentType = DEFAULT_DOCUMENT_TYPE) => {
pendingParentId.value = parentId;
selectedDocumentType.value = normalizeDocumentType(documentType);
showCreateDialog.value = true;
};
const cancelCreateDocument = () => {
showCreateDialog.value = false;
};
const createDocument = async (payload) => {
const title = payload?.title?.trim() || t('document.untitledDefaultTitle');
await runWithLoading(
creatingLoading,
async () => {
const isPersonal = kbId.value === 'personal';
const requestBody = {
title,
type: normalizeDocumentType(payload?.type || selectedDocumentType.value),
container_type: isPersonal ? 'own' : 'knowledge_base',
is_public: false
};
if (!isPersonal) {
requestBody.knowledge_base_external_id = kbId.value;
}
if (payload?.parent_external_id) {
requestBody.parent_external_id = payload.parent_external_id;
} else if (pendingParentId.value) {
requestBody.parent_external_id = pendingParentId.value;
}
if (payload?.template) {
requestBody.template_id = payload.template;
}
return createDocumentApi(requestBody);
},
{
context: 'createDocument',
errorMessage: t('knowledgeBase.createDocumentError'),
onSuccess: async (response) => {
const isPersonal = kbId.value === 'personal';
showCreateDialog.value = false;
pendingParentId.value = null;
await fetchDocuments();
if (response?.external_id) {
if (isPersonal) {
router.push(`/mydocument/${response.external_id}`);
} else {
router.push(`/knowledge-base/${kbId.value}/document/${response.external_id}`);
}
}
}
}
);
};
const handleCreateFromTree = (payload) => {
if (!payload) {
openCreateDocumentDialog(null, DEFAULT_DOCUMENT_TYPE);
return;
}
if (typeof payload === 'object' && ('target' in payload || 'type' in payload)) {
openCreateDocumentDialog(
payload?.target?.id || null,
payload?.type || DEFAULT_DOCUMENT_TYPE
);
return;
}
openCreateDocumentDialog(payload?.id || null, DEFAULT_DOCUMENT_TYPE);
};
const deletingDocument = ref(false);
const handleDeleteDocument = async (target) => {
const targetId = target?.id || docId.value;
if (!targetId) {
return;
}
try {
await ElMessageBox.confirm(t('document.deleteDocumentConfirm'), t('document.deleteDocument'), {
confirmButtonText: t('button.confirm'),
cancelButtonText: t('button.cancel'),
type: 'warning'
});
} catch {
return;
}
await runWithLoading(
deletingDocument,
() => deleteDocumentApi(targetId),
{
context: 'deleteDocument',
successMessage: t('document.deleteSuccess'),
errorMessage: t('common.requestFailed'),
onSuccess: async () => {
await fetchDocuments();
if (String(targetId) === String(docId.value)) {
const isPersonal = kbId.value === 'personal';
if (isPersonal) {
router.push('/mydocuments');
} else {
router.push(`/knowledge-base/${kbId.value}`);
}
}
}
}
);
};
const handleRenameFromTree = async (payload) => {
const targetNode = payload?.data;
const nextTitle = payload?.title?.trim?.() || '';
if (!targetNode?.id || !nextTitle) {
ElMessage.error(t('knowledgeBase.titleRequired'));
return;
}
const targetId = String(targetNode.id);
const currentTitle = String(targetNode.label || '').trim();
if (nextTitle === currentTitle) {
return;
}
await runWithLoading(
renamingNode,
() => updateDocumentMeta(targetId, { title: nextTitle }),
{
context: 'renameDocumentFromTree',
errorMessage: t('common.requestFailed'),
onSuccess: () => {
updateTreeNodeTitle(directoryTree.value, targetId, nextTitle);
if (String(docId.value || '') === targetId) {
currentDocTitle.value = nextTitle;
}
}
}
);
};
const startEditTitle = async () => {
if (!docId.value) {
return;
}
isEditingTitle.value = true;
pendingTitle.value = currentDocTitle.value || '';
};
const cancelEditTitle = () => {
isEditingTitle.value = false;
pendingTitle.value = '';
};
const commitTitle = async () => {
if (!isEditingTitle.value) {
return;
}
const nextTitle = pendingTitle.value.trim();
if (!nextTitle) {
ElMessage.error(t('knowledgeBase.titleRequired'));
return;
}
if (nextTitle === currentDocTitle.value) {
cancelEditTitle();
return;
}
if (!docId.value) {
cancelEditTitle();
return;
}
await runWithLoading(
savingTitle,
() => updateDocumentMeta(docId.value, { title: nextTitle }),
{
context: 'updateDocumentMeta',
errorMessage: t('common.requestFailed'),
onSuccess: () => {
currentDocTitle.value = nextTitle;
updateTreeNodeTitle(directoryTree.value, docId.value, nextTitle);
cancelEditTitle();
}
}
);
};
const openCreateTemplateDialog = () => {
const defaultScope = kbId.value && kbId.value !== 'personal' ? 'knowledge_base' : 'own';
templateDialogInit.value = {
name: currentDocTitle.value || t('templates.untitled'),
description: '',
scope: defaultScope,
tags: '',
content: editorContent.value || ''
};
showTemplateDialog.value = true;
};
const handleHeaderCommand = (command) => {
if (command === 'create_template') {
openCreateTemplateDialog();
return true;
}
return false;
};
const submitCreateTemplate = async (payload) => {
if (!editorContent.value || !editorContent.value.trim()) {
ElMessage.error(t('templates.emptyContent'));
return;
}
await runWithLoading(
savingTemplate,
async () => {
const requestBody = {
target_container: payload.scope,
type: normalizeDocumentType(currentDocType?.value || DEFAULT_DOCUMENT_TYPE),
template_content: JSON.stringify({
name: payload.name,
description: payload.description,
content: editorContent.value,
tags: payload.tags || []
})
};
if (payload.scope === 'knowledge_base' && kbId.value && kbId.value !== 'personal') {
requestBody.knowledge_base_id = kbId.value;
}
await createTemplateApi(requestBody);
},
{
context: 'createTemplate',
successMessage: t('templates.saveSuccess'),
errorMessage: t('templates.saveFailed'),
onSuccess: () => {
showTemplateDialog.value = false;
}
}
);
};
return {
isEditingTitle,
pendingTitle,
showCreateDialog,
creatingLoading,
pendingParentId,
selectedDocumentType,
savingTemplate,
showTemplateDialog,
templateDialogInit,
openCreateDocumentDialog,
cancelCreateDocument,
createDocument,
handleCreateFromTree,
handleDeleteDocument,
handleRenameFromTree,
startEditTitle,
cancelEditTitle,
commitTitle,
handleHeaderCommand,
submitCreateTemplate
};
}