diff --git a/frontend/src/components/business/ms-minders/featureCaseMinder.vue b/frontend/src/components/business/ms-minders/featureCaseMinder.vue index c828c97a3b..06ca380d3f 100644 --- a/frontend/src/components/business/ms-minders/featureCaseMinder.vue +++ b/frontend/src/components/business/ms-minders/featureCaseMinder.vue @@ -21,7 +21,7 @@
- + @@ -29,8 +29,13 @@ field="name" :label="t('ms.minders.caseName')" :rules="[{ required: true, message: t('ms.minders.caseNameNotNull') }]" + asterisk-position="end" > - +
- {{ t('common.save') }} - {{ t('common.cancel') }} + + {{ t('common.save') }} + + {{ t('common.cancel') }}
- - - - - + + + + + + +
@@ -262,8 +279,10 @@ addOrUpdateCommentList, associatedDebug, cancelAssociatedDebug, + checkFileIsUpdateRequest, createCommentList, deleteCommentList, + deleteFileOrCancelAssociation, downloadFileRequest, editorUploadFile, getAssociatedFileListUrl, @@ -278,7 +297,9 @@ previewFile, saveCaseMinder, transferFileRequest, + updateCaseRequest, updateFile, + uploadOrAssociationFile, } from '@/api/modules/case-management/featureCase'; import { getModules, getModulesCount } from '@/api/modules/project-management/fileManagement'; import { PreviewEditorImageUrl } from '@/api/requrls/case-management/featureCase'; @@ -286,14 +307,14 @@ import useModal from '@/hooks/useModal'; import useAppStore from '@/store/modules/app'; import useUserStore from '@/store/modules/user'; - import { downloadByteFile, getGenerateId, mapTree, traverseTree } from '@/utils'; + import { downloadByteFile, getGenerateId, mapTree } from '@/utils'; import { hasAnyPermission } from '@/utils/permission'; - import { AssociatedList, OptionsFieldId } from '@/models/caseManagement/featureCase'; + import type { AssociatedList, AttachFileInfo, OptionsFieldId } from '@/models/caseManagement/featureCase'; import { TableQueryParams } from '@/models/common'; import { BugManagementRouteEnum } from '@/enums/routeEnum'; - import { convertToFile } from '@/views/case-management/caseManagementFeature/components/utils'; + import { convertToFile, initFormCreate } from '@/views/case-management/caseManagementFeature/components/utils'; import { Api } from '@form-create/arco-design'; const AddDefectDrawer = defineAsyncComponent( @@ -710,7 +731,6 @@ }); const baseInfoLoading = ref(false); - const rowLength = ref(0); const formRules = ref([]); const formItem = ref([]); const fApi = ref(); @@ -784,100 +804,10 @@ }); const activeExtraKey = ref<'baseInfo' | 'attachment' | 'comments' | 'bug'>('baseInfo'); - async function handleNodeClick(node: MinderJsonNode) { - const { data } = node; - if (data?.resource && data.resource.includes(caseTag)) { - extraVisible.value = true; - try { - baseInfoLoading.value = true; - const res = await getCaseDetail(data.id); - activeCase.value = res; - baseInfoForm.value.name = res.name; - } catch (error) { - // eslint-disable-next-line no-console - console.log(error); - } finally { - baseInfoLoading.value = false; - } - } else if (data?.resource?.includes(moduleTag) && data.count > 0 && data.isLoaded !== true) { - try { - loading.value = true; - const res = await getCaseMinder({ - projectId: appStore.currentProjectId, - moduleId: data.id, - }); - const fakeNode = node.children?.find((e) => e.data?.id === undefined); // 移除占位的虚拟节点 - window.minder.removeNode(fakeNode); - res.forEach((e) => { - // 用例节点 - const child = window.minder.createNode(e.data, node); - child.render(); - e.children?.forEach((item) => { - // 前置/步骤/备注节点 - const grandChild = window.minder.createNode(item.data, child); - grandChild.render(); - item.children?.forEach((subItem) => { - // 预期结果节点 - const greatGrandChild = window.minder.createNode(subItem.data, grandChild); - greatGrandChild.render(); - }); - child.renderTree(); - }); - child.expand(); - child.renderTree(); - }); - node.expand(); - node.renderTree(); - window.minder.layout(); - if (node.data) { - node.data.isLoaded = true; - } - } catch (error) { - // eslint-disable-next-line no-console - console.log(error); - } finally { - loading.value = false; - } - } else { - extraVisible.value = false; - activeCase.value = {}; - } - } - - onBeforeMount(() => { - initDefaultFields(); - }); - - function handleSave() { - if (activeExtraKey.value === 'baseInfo') { - baseInfoFormRef.value?.validate((errors) => { - if (!errors) { - fApi.value?.validate((valid) => { - if (valid) { - const data = { - ...baseInfoForm.value, - customFields: formItem.value.map((item: any) => { - return { - fieldId: item.field, - value: Array.isArray(item.value) ? JSON.stringify(item.value) : item.value, - }; - }), - }; - console.log(data); - } - }); - } - }); - } - } - const fileList = ref([]); + const attachmentsList = ref([]); + const checkUpdateFileIds = ref([]); - // 处理关联文件 - function saveSelectAssociatedFile(fileData: AssociatedList[]) { - const fileResultList = fileData.map((fileInfo) => convertToFile(fileInfo)); - fileList.value.push(...fileResultList); - } const getListFunParams = ref({ combine: { hiddenIds: [], @@ -895,15 +825,93 @@ { deep: true } ); - const showLinkFileDrawer = ref(false); + const showLinkFileDrawer = ref(false); + const attachmentLoading = ref(false); - function handleFileChange(_fileList: MsFileItem[]) { - fileList.value = _fileList.map((e) => { - return { - ...e, - enable: true, // 是否启用 + /** + * 初始化用例详情 + * @param data 节点数据/用例数据 + */ + async function initCaseDetail(data: MinderJsonNodeData | Record) { + try { + baseInfoLoading.value = true; + const res = await getCaseDetail(data.id); + activeCase.value = res; + baseInfoForm.value.name = res.name; + const fileIds = (res.attachments || []).map((item: any) => item.id) || []; + if (fileIds.length) { + checkUpdateFileIds.value = await checkFileIsUpdateRequest(fileIds); + } + formRules.value = initFormCreate(res.customFields, ['FUNCTIONAL_CASE:READ+UPDATE']); + if (res.attachments) { + attachmentsList.value = res.attachments; + // 处理文件列表 + fileList.value = res.attachments + .map((fileInfo: any) => { + return { + ...fileInfo, + name: fileInfo.fileName, + isUpdateFlag: checkUpdateFileIds.value.includes(fileInfo.id), + }; + }) + .map((fileInfo: any) => { + return convertToFile(fileInfo); + }); + } + } catch (error) { + // eslint-disable-next-line no-console + console.log(error); + } finally { + baseInfoLoading.value = false; + } + } + + /** + * 处理文件更改 + * @param _fileList 文件列表 + * @param isAssociated 是否是关联文件 + */ + async function handleFileChange(_fileList: MsFileItem[], isAssociated = false) { + try { + attachmentLoading.value = true; + const params = { + request: { + caseId: activeCase.value.id, + projectId: appStore.currentProjectId, + fileIds: isAssociated ? _fileList.map((item) => item.uid) : [], + enable: true, + }, + file: isAssociated ? _fileList.map((item) => item.file) : _fileList[0].file, }; - }); + await uploadOrAssociationFile(params); + if (isAssociated) { + fileList.value.unshift(..._fileList); + } else { + fileList.value = fileList.value.map((item) => { + if (item.status === 'init') { + return { ...item, status: 'done', local: true }; + } + return item; + }); + } + Message.success(t('ms.upload.uploadSuccess')); + await initCaseDetail(activeCase.value); + } catch (error) { + // eslint-disable-next-line no-console + console.log(error); + fileList.value = fileList.value.map((item) => ({ ...item, status: 'error' })); + } finally { + attachmentLoading.value = false; + } + } + + /** + * 处理关联文件 + * @param fileData 文件信息集合 + */ + function saveSelectAssociatedFile(fileData: AssociatedList[]) { + const fileResultList = fileData.map((fileInfo) => convertToFile(fileInfo)); + handleFileChange(fileResultList, true); } const imageUrl = ref(''); @@ -912,8 +920,9 @@ // 预览图片 async function handlePreview(item: MsFileItem) { try { + imageUrl.value = ''; previewVisible.value = true; - if (item.status !== 'init') { + if (!item.local) { const res = await previewFile({ projectId: appStore.currentProjectId, caseId: activeCase.value.id, @@ -941,6 +950,60 @@ transferVisible.value = true; } + // 删除本地文件 + async function deleteFileHandler(item: MsFileItem) { + if (!item.local) { + try { + const params = { + id: item.uid, + local: item.local, + caseId: activeCase.value.id, + projectId: appStore.currentProjectId, + }; + await deleteFileOrCancelAssociation(params); + Message.success( + item.local ? t('caseManagement.featureCase.deleteSuccess') : t('caseManagement.featureCase.cancelLinkSuccess') + ); + fileList.value = fileList.value.filter((e) => e.uid !== item.uid); + } catch (error) { + // eslint-disable-next-line no-console + console.log(error); + } + } else { + openModal({ + type: 'error', + title: t('caseManagement.featureCase.deleteFile', { name: item?.name }), + content: t('caseManagement.featureCase.deleteFileTip'), + okText: t('common.confirmDelete'), + cancelText: t('common.cancel'), + okButtonProps: { + status: 'danger', + }, + onBeforeOk: async () => { + try { + const params = { + id: item.uid, + local: item.local, + caseId: activeCase.value.id, + projectId: appStore.currentProjectId, + }; + await deleteFileOrCancelAssociation(params); + Message.success( + item.local + ? t('caseManagement.featureCase.deleteSuccess') + : t('caseManagement.featureCase.cancelLinkSuccess') + ); + fileList.value = fileList.value.filter((e) => e.uid !== item.uid); + } catch (error) { + // eslint-disable-next-line no-console + console.log(error); + } + }, + hideCancel: false, + }); + } + } + // 下载 async function downloadFile(item: MsFileItem) { try { @@ -968,6 +1031,117 @@ } } + /** + * 处理脑图节点激活/点击 + * @param node 被激活/点击的节点 + */ + async function handleNodeClick(node: MinderJsonNode) { + const { data } = node; + if (data?.resource && data.resource.includes(caseTag)) { + extraVisible.value = true; + activeExtraKey.value = 'baseInfo'; + initCaseDetail(data); + } else if (data?.resource?.includes(moduleTag) && data.count > 0 && data.isLoaded !== true) { + try { + loading.value = true; + const res = await getCaseMinder({ + projectId: appStore.currentProjectId, + moduleId: data.id, + }); + const fakeNode = node.children?.find((e) => e.data?.id === undefined); // 移除占位的虚拟节点 + if (fakeNode) { + window.minder.removeNode(fakeNode); + } + if ((!res || res.length === 0) && node.children?.length) { + // 如果模块下没有用例且有别的模块节点,正常展开 + node.expand(); + node.renderTree(); + window.minder.layout(); + return; + } + // TODO:递归渲染存在的子节点 + res.forEach((e) => { + // 用例节点 + const child = window.minder.createNode(e.data, node); + child.render(); + e.children?.forEach((item) => { + // 前置/步骤/备注节点 + const grandChild = window.minder.createNode(item.data, child); + grandChild.render(); + item.children?.forEach((subItem) => { + // 预期结果节点 + const greatGrandChild = window.minder.createNode(subItem.data, grandChild); + greatGrandChild.render(); + }); + }); + child.expand(); + child.renderTree(); + }); + node.expand(); + node.renderTree(); + window.minder.layout(); + if (node.data) { + node.data.isLoaded = true; + } + } catch (error) { + // eslint-disable-next-line no-console + console.log(error); + } finally { + loading.value = false; + } + } else { + extraVisible.value = false; + activeCase.value = {}; + } + } + + onBeforeMount(() => { + initDefaultFields(); + }); + + const saveLoading = ref(false); + function handleSave() { + if (activeExtraKey.value === 'baseInfo') { + baseInfoFormRef.value?.validate((errors) => { + if (!errors) { + fApi.value?.validate(async (valid) => { + if (valid === true) { + try { + saveLoading.value = true; + const data = { + ...baseInfoForm.value, + id: activeCase.value.id, + projectId: appStore.currentProjectId, + caseEditType: activeCase.value.caseEditType, + customFields: formItem.value.map((item: any) => { + return { + fieldId: item.field, + value: Array.isArray(item.value) ? JSON.stringify(item.value) : item.value, + }; + }), + }; + await updateCaseRequest({ + request: data, + fileList: [], + }); + const selectedNode: MinderJsonNode = window.minder.getSelectedNode(); + if (selectedNode.data) { + selectedNode.data.text = baseInfoForm.value.name; + } + Message.success(t('common.saveSuccess')); + } catch (error) { + // eslint-disable-next-line no-console + console.log(error); + } finally { + saveLoading.value = false; + } + } + }); + } + }); + } + } + async function handleUploadImage(file: File) { const { data } = await editorUploadFile({ fileList: [file], @@ -1213,6 +1387,15 @@ drawerLoading.value = false; } } + + watch( + () => activeExtraKey.value, + (val) => { + if (val === 'comments') { + getAllCommentList(); + } + } + ); \ No newline at end of file + diff --git a/frontend/src/components/pure/ms-list/index.vue b/frontend/src/components/pure/ms-list/index.vue index c802f35697..cb8dc9a3fd 100644 --- a/frontend/src/components/pure/ms-list/index.vue +++ b/frontend/src/components/pure/ms-list/index.vue @@ -245,7 +245,7 @@ .arco-list-item-meta { @apply p-0; .arco-list-item-meta-content { - @apply flex-1; + @apply flex-1 overflow-hidden; } .arco-list-item-meta-title:not(:last-child) { @apply mb-0; @@ -266,6 +266,9 @@ :deep(.arco-list-content) { .ms-scroll-bar(); } + :deep(.arco-list-item-main) { + @apply overflow-hidden; + } } .ms-list-ghost { opacity: 0.5; diff --git a/frontend/src/components/pure/ms-minder-editor/minderEditor.vue b/frontend/src/components/pure/ms-minder-editor/minderEditor.vue index 0318d09915..0ccfc5afec 100644 --- a/frontend/src/components/pure/ms-minder-editor/minderEditor.vue +++ b/frontend/src/components/pure/ms-minder-editor/minderEditor.vue @@ -164,7 +164,7 @@ } } .ms-minder-editor-extra--visible { - width: 35%; + width: 40%; transition: all 300ms ease-in-out; } } diff --git a/frontend/src/components/pure/ms-upload/fileList.vue b/frontend/src/components/pure/ms-upload/fileList.vue index f17b2bc95f..bcc78a71fd 100644 --- a/frontend/src/components/pure/ms-upload/fileList.vue +++ b/frontend/src/components/pure/ms-upload/fileList.vue @@ -46,12 +46,45 @@
{{ item.file.name.slice(0, item.file.name.indexOf('.')) }}
- {{ - item.file.name.slice(item.file.name.indexOf('.')) - }} + + {{ item.file.name.slice(item.file.name.indexOf('.')) }} +
+
+ + + {{ t('ms.upload.preview') }} + + + + {{ t('ms.upload.reUpload') }} + + + + + {{ t(item.deleteContent) || t('ms.upload.delete') }} + +
-