feat(功能用例): 脑图读取
This commit is contained in:
parent
aba2e91ef9
commit
d7a675faf4
|
@ -41,6 +41,7 @@ import {
|
|||
GetAssociationPublicCasePageUrl,
|
||||
GetAssociationPublicModuleTreeUrl,
|
||||
GetCaseListUrl,
|
||||
GetCaseMinderUrl,
|
||||
GetCaseModulesCountUrl,
|
||||
GetCaseModuleTreeUrl,
|
||||
getChangeHistoryListUrl,
|
||||
|
@ -67,6 +68,7 @@ import {
|
|||
publicAssociatedCaseUrl,
|
||||
RecoverRecycleCaseListUrl,
|
||||
RestoreCaseListUrl,
|
||||
SaveCaseMinderUrl,
|
||||
TransferFileUrl,
|
||||
UpdateCaseModuleTreeUrl,
|
||||
UpdateCaseUrl,
|
||||
|
@ -78,11 +80,8 @@ import {
|
|||
import type { BugListItem } from '@/models/bug-management';
|
||||
import type {
|
||||
AssociatedList,
|
||||
BatchDeleteType,
|
||||
BatchEditCaseType,
|
||||
BatchMoveOrCopyType,
|
||||
CaseManagementTable,
|
||||
CaseModuleQueryParams,
|
||||
ChangeHistoryItem,
|
||||
CreateOrUpdateDemand,
|
||||
CreateOrUpdateModule,
|
||||
|
@ -90,6 +89,7 @@ import type {
|
|||
DeleteDependencyParams,
|
||||
DemandItem,
|
||||
DragCase,
|
||||
FeatureCaseMinder,
|
||||
ImportExcelType,
|
||||
ModulesTreeType,
|
||||
OperationFile,
|
||||
|
@ -98,7 +98,7 @@ import type {
|
|||
} from '@/models/caseManagement/featureCase';
|
||||
import type { CommonList, ModuleTreeNode, MoveModules, TableQueryParams } from '@/models/common';
|
||||
import { ProjectListItem } from '@/models/setting/project';
|
||||
import { AssociateFunctionalCaseItem, TestPlanItem } from '@/models/testPlan/testPlan';
|
||||
import { AssociateFunctionalCaseItem } from '@/models/testPlan/testPlan';
|
||||
|
||||
// 获取模块树
|
||||
export function getCaseModuleTree(params: TableQueryParams) {
|
||||
|
@ -180,6 +180,16 @@ export function batchCopyToModules(data: BatchMoveOrCopyType) {
|
|||
return MSR.post({ url: `${BatchCopyCaseUrl}`, data });
|
||||
}
|
||||
|
||||
// 保存脑图
|
||||
export function saveCaseMinder(data: FeatureCaseMinder) {
|
||||
return MSR.post({ url: `${SaveCaseMinderUrl}`, data });
|
||||
}
|
||||
|
||||
// 获取脑图
|
||||
export function getCaseMinder(data: { projectId: string; moduleId: string }) {
|
||||
return MSR.post({ url: `${GetCaseMinderUrl}`, data });
|
||||
}
|
||||
|
||||
// 回收站
|
||||
|
||||
// 回收站用例分页表
|
||||
|
@ -362,7 +372,7 @@ export function getDrawerDebugPage(data: TableQueryParams) {
|
|||
return MSR.post<CommonList<CaseManagementTable>>({ url: GetDebugDrawerPageUrl, data });
|
||||
}
|
||||
// 关联缺陷
|
||||
export function associatedDrawerDebug(data: TableQueryParams) {
|
||||
export function associatedDebug(data: TableQueryParams) {
|
||||
return MSR.post<CommonList<CaseManagementTable>>({ url: AssociatedDebuggerUrl, data });
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@ export const GetCaseCustomFieldsUrl = '/functional/case/default/template/field';
|
|||
export const GetSearchCustomFieldsUrl = '/functional/case/custom/field';
|
||||
// 关联文件列表
|
||||
export const GetAssociatedFilePageUrl = '/attachment/page';
|
||||
export const SaveCaseMinderUrl = '/functional/mind/case/edit'; // 保存用例脑图
|
||||
export const GetCaseMinderUrl = '/functional/mind/case/list'; // 获取脑图数据
|
||||
|
||||
// 获取模块树
|
||||
export const GetCaseModuleTreeUrl = '/functional/case/module/tree';
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
<template>
|
||||
<a-form-item v-if="props.mode === 'button'" field="attachment" :label="t('caseManagement.featureCase.addAttachment')">
|
||||
<a-form-item
|
||||
v-if="props.mode === 'button'"
|
||||
field="attachment"
|
||||
:class="props.onlyButton ? 'hidden-item' : ''"
|
||||
:label="t('caseManagement.featureCase.addAttachment')"
|
||||
>
|
||||
<!-- TODO:跟下面统一样式 -->
|
||||
<div class="flex flex-col">
|
||||
<div class="mb-1">
|
||||
<div class="mb-1" :class="props.onlyButton ? 'mb-[12px]' : ''">
|
||||
<a-dropdown
|
||||
v-model:popup-visible="buttonDropDownVisible"
|
||||
:disabled="props.disabled"
|
||||
|
@ -41,7 +46,7 @@
|
|||
</template>
|
||||
</a-dropdown>
|
||||
</div>
|
||||
<div class="!hover:bg-[rgb(var(--primary-1))] !text-[var(--color-text-4)]">
|
||||
<div v-if="!props.onlyButton" class="!hover:bg-[rgb(var(--primary-1))] !text-[var(--color-text-4)]">
|
||||
{{ t('system.orgTemplate.addAttachmentTip') }}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -244,6 +249,7 @@
|
|||
defineProps<{
|
||||
disabled?: boolean;
|
||||
mode?: 'button' | 'input';
|
||||
onlyButton?: boolean;
|
||||
accept?: UploadType;
|
||||
multiple?: boolean;
|
||||
inputClass?: string;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
></a-input>
|
||||
<div v-else class="flex flex-col justify-between">
|
||||
<MsRichText
|
||||
v-if="props.mode === 'rich'"
|
||||
v-model:raw="currentContent"
|
||||
v-model:commentIds="commentIds"
|
||||
:upload-image="props.uploadImage"
|
||||
|
@ -23,6 +24,11 @@
|
|||
class="w-full"
|
||||
placeholder="ms.comment.enterPlaceHolderTip"
|
||||
/>
|
||||
<a-textarea
|
||||
v-else
|
||||
v-model:model-value="currentContent"
|
||||
:placeholder="t('ms.comment.enterPlaceHolderTip')"
|
||||
></a-textarea>
|
||||
<div class="mt-4 flex flex-row justify-end gap-[12px]">
|
||||
<a-button @click="cancelClick">{{ t('common.cancel') }}</a-button>
|
||||
<a-button type="primary" :disabled="!currentContent" @click="publish">{{ t('common.publish') }}</a-button>
|
||||
|
@ -45,12 +51,18 @@
|
|||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
mode?: 'rich' | 'textarea';
|
||||
isShowAvatar: boolean; // 是否显示评论人头像
|
||||
isUseBottom: boolean; // 是否被用于底部
|
||||
uploadImage?: (file: File) => Promise<any>;
|
||||
previewUrl?: string;
|
||||
}>();
|
||||
}>(),
|
||||
{
|
||||
mode: 'rich',
|
||||
}
|
||||
);
|
||||
|
||||
const currentContent = defineModel<string>('defaultValue', { default: '' });
|
||||
const commentIds = defineModel<string[]>('noticeUserIds', { default: [] });
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<MsMinderEditor
|
||||
v-model:activeExtraKey="activeExtraKey"
|
||||
:tags="tags"
|
||||
:import-json="props.importJson"
|
||||
:import-json="importJson"
|
||||
:replaceable-tags="replaceableTags"
|
||||
:insert-node="insertNode"
|
||||
:priority-disable-check="priorityDisableCheck"
|
||||
|
@ -11,31 +11,379 @@
|
|||
single-tag
|
||||
tag-enable
|
||||
sequence-enable
|
||||
@click="handleNodeClick"
|
||||
@node-click="handleNodeClick"
|
||||
@save="handleMinderSave"
|
||||
>
|
||||
<template #extractTabContent>
|
||||
<div>
|
||||
<div v-if="activeExtraKey === 'baseInfo'" class="pl-[16px]">
|
||||
<a-skeleton v-if="baseInfoLoading" :loading="baseInfoLoading" :animation="true">
|
||||
<a-space direction="vertical" class="w-full" size="large">
|
||||
<a-skeleton-line :rows="rowLength" :line-height="30" :line-spacing="30" />
|
||||
</a-space>
|
||||
</a-skeleton>
|
||||
<a-form v-else ref="baseInfoFormRef" :model="baseInfoForm" layout="vertical">
|
||||
<a-form-item
|
||||
field="name"
|
||||
:label="t('ms.minders.caseName')"
|
||||
:rules="[{ required: true, message: t('ms.minders.caseNameNotNull') }]"
|
||||
>
|
||||
<a-input v-model:model-value="baseInfoForm.name" :placeholder="t('common.pleaseInput')"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
field="moduleId"
|
||||
asterisk-position="end"
|
||||
:label="t('caseManagement.featureCase.ModuleOwned')"
|
||||
:rules="[{ required: true, message: t('system.orgTemplate.moduleRuleTip') }]"
|
||||
>
|
||||
<a-tree-select
|
||||
v-model="baseInfoForm.moduleId"
|
||||
:allow-search="true"
|
||||
:data="caseTree"
|
||||
:field-names="{
|
||||
title: 'name',
|
||||
key: 'id',
|
||||
children: 'children',
|
||||
}"
|
||||
:draggable="false"
|
||||
:tree-props="{
|
||||
virtualListProps: {
|
||||
height: 200,
|
||||
},
|
||||
}"
|
||||
>
|
||||
<template #tree-slot-title="node">
|
||||
<a-tooltip :content="`${node.name}`" position="tl">
|
||||
<div class="one-line-text w-[300px] text-[var(--color-text-1)]">{{ node.name }}</div>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-tree-select>
|
||||
</a-form-item>
|
||||
<MsFormCreate
|
||||
v-if="formRules.length"
|
||||
ref="formCreateRef"
|
||||
v-model:api="fApi"
|
||||
v-model:form-item="formItem"
|
||||
:form-rule="formRules"
|
||||
/>
|
||||
<a-form-item field="tags" :label="t('common.tag')">
|
||||
<MsTagsInput v-model:model-value="baseInfoForm.tags" :max-tag-count="6" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<div class="flex items-center gap-[12px]">
|
||||
<a-button type="primary" @click="handleSave">{{ t('common.save') }}</a-button>
|
||||
<a-button type="secondary">{{ t('common.cancel') }}</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="activeExtraKey === 'attachment'" class="pl-[16px]">
|
||||
<MsAddAttachment
|
||||
v-model:file-list="fileList"
|
||||
multiple
|
||||
only-button
|
||||
@change="handleFileChange"
|
||||
@link-file="() => (showLinkFileDrawer = true)"
|
||||
/>
|
||||
<MsFileList
|
||||
v-if="fileList.length > 0"
|
||||
ref="fileListRef"
|
||||
v-model:file-list="fileList"
|
||||
mode="static"
|
||||
:init-file-save-tips="t('ms.upload.waiting_save')"
|
||||
:show-upload-type-desc="true"
|
||||
>
|
||||
<template #actions="{ item }">
|
||||
<!-- 本地文件 -->
|
||||
<div v-if="item.local || item.status === 'init'" class="flex flex-nowrap">
|
||||
<MsButton
|
||||
v-if="item.status !== 'init' && item.file.type.includes('image/')"
|
||||
type="button"
|
||||
status="primary"
|
||||
class="!mr-[4px]"
|
||||
@click="handlePreview(item)"
|
||||
>
|
||||
{{ t('ms.upload.preview') }}
|
||||
</MsButton>
|
||||
<SaveAsFilePopover
|
||||
v-model:visible="transferVisible"
|
||||
:saving-file="activeTransferFileParams"
|
||||
:file-save-as-source-id="activeCase.id"
|
||||
:file-save-as-api="transferFileRequest"
|
||||
:file-module-options-api="getTransferFileTree"
|
||||
source-id-key="caseId"
|
||||
/>
|
||||
<MsButton
|
||||
v-if="item.status !== 'init'"
|
||||
type="button"
|
||||
status="primary"
|
||||
class="!mr-[4px]"
|
||||
@click="transferFile(item)"
|
||||
>
|
||||
{{ t('caseManagement.featureCase.storage') }}
|
||||
</MsButton>
|
||||
<MsButton
|
||||
v-if="item.status !== 'init'"
|
||||
type="button"
|
||||
status="primary"
|
||||
class="!mr-[4px]"
|
||||
@click="downloadFile(item)"
|
||||
>
|
||||
{{ t('caseManagement.featureCase.download') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
<!-- 关联文件 -->
|
||||
<div v-else class="flex flex-nowrap">
|
||||
<MsButton
|
||||
v-if="item.file.type.includes('/image')"
|
||||
type="button"
|
||||
status="primary"
|
||||
class="!mr-[4px]"
|
||||
@click="handlePreview(item)"
|
||||
>
|
||||
{{ t('ms.upload.preview') }}
|
||||
</MsButton>
|
||||
<MsButton
|
||||
v-if="activeCase.id"
|
||||
type="button"
|
||||
status="primary"
|
||||
class="!mr-[4px]"
|
||||
@click="downloadFile(item)"
|
||||
>
|
||||
{{ t('caseManagement.featureCase.download') }}
|
||||
</MsButton>
|
||||
<MsButton
|
||||
v-if="activeCase.id && item.isUpdateFlag"
|
||||
type="button"
|
||||
status="primary"
|
||||
@click="handleUpdateFile(item)"
|
||||
>
|
||||
{{ t('common.update') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
</template>
|
||||
<template #title="{ item }">
|
||||
<span v-if="item.isUpdateFlag" class="ml-4 flex items-center font-normal text-[rgb(var(--warning-6))]">
|
||||
<icon-exclamation-circle-fill />
|
||||
<span>{{ t('caseManagement.featureCase.fileIsUpdated') }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</MsFileList>
|
||||
</div>
|
||||
<div v-else-if="activeExtraKey === 'comments'" class="pl-[16px]">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-[var(--color-text-4)]">
|
||||
{{
|
||||
t('ms.minders.commentTotal', {
|
||||
num: activeComment === 'caseComment' ? commentList.length : reviewCommentList.length,
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
<a-select
|
||||
v-model:model-value="activeComment"
|
||||
:options="commentTypeOptions"
|
||||
class="w-[120px]"
|
||||
@change="getAllCommentList"
|
||||
></a-select>
|
||||
</div>
|
||||
<ReviewCommentList
|
||||
v-if="activeComment === 'reviewComment' || activeComment === 'executiveComment'"
|
||||
:review-comment-list="reviewCommentList"
|
||||
:active-comment="activeComment"
|
||||
/>
|
||||
<template v-else>
|
||||
<MsComment
|
||||
:upload-image="handleUploadImage"
|
||||
:comment-list="commentList"
|
||||
:preview-url="PreviewEditorImageUrl"
|
||||
@delete="handleDelete"
|
||||
@update-or-add="handleUpdateOrAdd"
|
||||
/>
|
||||
<MsEmpty v-if="commentList.length === 0" />
|
||||
</template>
|
||||
<inputComment
|
||||
ref="commentInputRef"
|
||||
v-model:content="content"
|
||||
v-model:notice-user-ids="noticeUserIds"
|
||||
v-permission="['FUNCTIONAL_CASE:READ+COMMENT']"
|
||||
:preview-url="PreviewEditorImageUrl"
|
||||
:is-active="isActive"
|
||||
mode="textarea"
|
||||
is-show-avatar
|
||||
is-use-bottom
|
||||
:upload-image="handleUploadImage"
|
||||
@publish="publishHandler"
|
||||
@cancel="cancelPublish"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="pl-[16px]">
|
||||
<a-button
|
||||
v-if="hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE'])"
|
||||
class="mr-3"
|
||||
type="primary"
|
||||
@click="linkBug"
|
||||
>
|
||||
{{ t('caseManagement.featureCase.linkDefect') }}
|
||||
</a-button>
|
||||
<a-button v-permission="['PROJECT_BUG:READ+ADD']" type="outline" @click="createBug"
|
||||
>{{ t('caseManagement.featureCase.createDefect') }}
|
||||
</a-button>
|
||||
<div class="bug-list">
|
||||
<div v-for="item of bugList" :key="item.id" class="bug-item">
|
||||
<div class="mb-[4px] flex items-center justify-between">
|
||||
<MsButton type="text" @click="goBug(item.id)">{{ item.num }}</MsButton>
|
||||
<MsButton type="text" @click="disassociateBug(item.id)">
|
||||
{{ t('ms.add.attachment.cancelAssociate') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
<a-tooltip :content="item.name">
|
||||
<div class="one-line-text">{{ item.name }}</div>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<MsEmpty v-if="bugList.length === 0" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</MsMinderEditor>
|
||||
<LinkFileDrawer
|
||||
v-model:visible="showLinkFileDrawer"
|
||||
:get-tree-request="getModules"
|
||||
:get-count-request="getModulesCount"
|
||||
:get-list-request="getAssociatedFileListUrl"
|
||||
:get-list-fun-params="getListFunParams"
|
||||
@save="saveSelectAssociatedFile"
|
||||
/>
|
||||
<a-image-preview v-model:visible="previewVisible" :src="imageUrl" />
|
||||
<AddDefectDrawer
|
||||
v-if="activeCase.id"
|
||||
v-model:visible="showCreateBugDrawer"
|
||||
:case-id="activeCase.id"
|
||||
:extra-params="{ caseId: activeCase.id }"
|
||||
@success="initBugList"
|
||||
/>
|
||||
<LinkDefectDrawer
|
||||
v-if="activeCase.id"
|
||||
v-model:visible="showLinkBugDrawer"
|
||||
:case-id="activeCase.id"
|
||||
:drawer-loading="drawerLoading"
|
||||
@save="saveHandler"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRouter } from 'vue-router';
|
||||
import { FormInstance, Message } from '@arco-design/web-vue';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsEmpty from '@/components/pure/ms-empty/index.vue';
|
||||
import MsFormCreate from '@/components/pure/ms-form-create/ms-form-create.vue';
|
||||
import { FormItem, FormRuleItem } from '@/components/pure/ms-form-create/types';
|
||||
import MsMinderEditor from '@/components/pure/ms-minder-editor/minderEditor.vue';
|
||||
import type { MinderJson, MinderJsonNode, MinderJsonNodeData } from '@/components/pure/ms-minder-editor/props';
|
||||
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
|
||||
import MsFileList from '@/components/pure/ms-upload/fileList.vue';
|
||||
import { MsFileItem } from '@/components/pure/ms-upload/types';
|
||||
import MsAddAttachment from '@/components/business/ms-add-attachment/index.vue';
|
||||
import SaveAsFilePopover from '@/components/business/ms-add-attachment/saveAsFilePopover.vue';
|
||||
import MsComment from '@/components/business/ms-comment/comment';
|
||||
import inputComment from '@/components/business/ms-comment/input.vue';
|
||||
import { CommentItem, CommentParams } from '@/components/business/ms-comment/types';
|
||||
import LinkFileDrawer from '@/components/business/ms-link-file/associatedFileDrawer.vue';
|
||||
import ReviewCommentList from '@/views/case-management/caseManagementFeature/components/tabContent/tabComment/reviewCommentList.vue';
|
||||
|
||||
import {
|
||||
addOrUpdateCommentList,
|
||||
associatedDebug,
|
||||
cancelAssociatedDebug,
|
||||
createCommentList,
|
||||
deleteCommentList,
|
||||
downloadFileRequest,
|
||||
editorUploadFile,
|
||||
getAssociatedFileListUrl,
|
||||
getCaseDefaultFields,
|
||||
getCaseMinder,
|
||||
getCaseModuleTree,
|
||||
getCommentList,
|
||||
getReviewCommentList,
|
||||
getTestPlanExecuteCommentList,
|
||||
getTransferFileTree,
|
||||
previewFile,
|
||||
saveCaseMinder,
|
||||
transferFileRequest,
|
||||
updateFile,
|
||||
} from '@/api/modules/case-management/featureCase';
|
||||
import { getModules, getModulesCount } from '@/api/modules/project-management/fileManagement';
|
||||
import { PreviewEditorImageUrl } from '@/api/requrls/case-management/featureCase';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { getGenerateId } from '@/utils';
|
||||
import useModal from '@/hooks/useModal';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import { downloadByteFile, getGenerateId } from '@/utils';
|
||||
import { hasAnyPermission } from '@/utils/permission';
|
||||
|
||||
import { AssociatedList, OptionsFieldId } from '@/models/caseManagement/featureCase';
|
||||
import { ModuleTreeNode, TableQueryParams } from '@/models/common';
|
||||
import { BugManagementRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
import { convertToFile } from '@/views/case-management/caseManagementFeature/components/utils';
|
||||
|
||||
const AddDefectDrawer = defineAsyncComponent(
|
||||
() => import('@/views/case-management/caseManagementFeature/components/tabContent/tabBug/addDefectDrawer.vue')
|
||||
);
|
||||
const LinkDefectDrawer = defineAsyncComponent(
|
||||
() => import('@/views/case-management/caseManagementFeature/components/tabContent/tabBug/linkDefectDrawer.vue')
|
||||
);
|
||||
|
||||
const props = defineProps<{
|
||||
importJson: MinderJson;
|
||||
moduleId: string;
|
||||
moduleName: string;
|
||||
}>();
|
||||
|
||||
const router = useRouter();
|
||||
const { openModal } = useModal();
|
||||
const appStore = useAppStore();
|
||||
const userStore = useUserStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const importJson = ref<MinderJson>({
|
||||
root: {},
|
||||
template: 'default',
|
||||
treePath: [],
|
||||
});
|
||||
|
||||
async function initMinder() {
|
||||
try {
|
||||
const res = await getCaseMinder({
|
||||
projectId: appStore.currentProjectId,
|
||||
moduleId: props.moduleId === 'all' ? '' : props.moduleId,
|
||||
});
|
||||
importJson.value.root.children = res;
|
||||
importJson.value.root.data = {
|
||||
id: props.moduleId === 'all' ? '' : props.moduleId,
|
||||
text: props.moduleName,
|
||||
resource: [t('common.module')],
|
||||
};
|
||||
window.minder.importJson(importJson.value);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
if (props.moduleId) {
|
||||
initMinder();
|
||||
}
|
||||
});
|
||||
|
||||
const caseTag = t('common.case');
|
||||
const moduleTag = t('common.module');
|
||||
const topTags = [moduleTag, caseTag];
|
||||
const descTags = [t('ms.minders.stepDesc'), t('ms.minders.textDesc')];
|
||||
const tags = [...topTags, t('ms.minders.precondition'), ...descTags, t('ms.minders.stepExpect'), t('common.remark')];
|
||||
const visible = ref<boolean>(false);
|
||||
const nodeData = ref<any>({});
|
||||
const extractContentTabList = [
|
||||
const activeCase = ref<any>({});
|
||||
const extractContentTabList = computed(() => {
|
||||
const fullTabList = [
|
||||
{
|
||||
label: t('common.baseInfo'),
|
||||
value: 'baseInfo',
|
||||
|
@ -53,12 +401,32 @@
|
|||
label: t('caseManagement.featureCase.bug'),
|
||||
},
|
||||
];
|
||||
const activeExtraKey = ref('baseInfo');
|
||||
if (activeCase.value.id) {
|
||||
return fullTabList;
|
||||
}
|
||||
return fullTabList.filter((item) => item.value === 'baseInfo');
|
||||
});
|
||||
const activeExtraKey = ref<'baseInfo' | 'attachment' | 'comments' | 'bug'>('baseInfo');
|
||||
|
||||
function handleNodeClick(data: any) {
|
||||
if (data.resource && data.resource.includes(caseTag)) {
|
||||
visible.value = true;
|
||||
nodeData.value = data;
|
||||
activeCase.value = data;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleMinderSave(data: any) {
|
||||
try {
|
||||
await saveCaseMinder({
|
||||
projectId: appStore.currentProjectId,
|
||||
versionId: '',
|
||||
updateCaseList: data,
|
||||
updateModuleList: [],
|
||||
deleteResourceList: [],
|
||||
});
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,6 +443,13 @@
|
|||
}
|
||||
if (node.data?.resource?.some((e) => descTags.includes(e))) {
|
||||
// 选中节点属于描述节点,可替换为除自身外的描述标签
|
||||
if (
|
||||
node.data?.resource?.includes(t('ms.minders.stepDesc')) &&
|
||||
(node.parent?.children?.filter((e) => e.data?.resource?.includes(t('ms.minders.stepDesc'))) || []).length > 1
|
||||
) {
|
||||
// 如果当前节点是步骤描述,则需要判断是否有其他步骤描述节点,如果有,则不可替换为文本描述
|
||||
return [];
|
||||
}
|
||||
return descTags.filter((tag) => !node.data?.resource?.includes(tag));
|
||||
}
|
||||
if (
|
||||
|
@ -328,6 +703,456 @@
|
|||
window.minder.execCommand('priority');
|
||||
}
|
||||
}
|
||||
|
||||
const baseInfoFormRef = ref<FormInstance>();
|
||||
const baseInfoForm = ref({
|
||||
name: '',
|
||||
tags: [],
|
||||
templateId: '',
|
||||
moduleId: 'root',
|
||||
});
|
||||
const baseInfoLoading = ref(false);
|
||||
|
||||
const rowLength = ref<number>(0);
|
||||
const formRules = ref<FormItem[]>([]);
|
||||
const formItem = ref<FormRuleItem[]>([]);
|
||||
const fApi = ref<any>(null);
|
||||
// 初始化模板默认字段
|
||||
async function initDefaultFields() {
|
||||
formRules.value = [];
|
||||
try {
|
||||
baseInfoLoading.value = true;
|
||||
const res = await getCaseDefaultFields(appStore.currentProjectId);
|
||||
const { customFields, id } = res;
|
||||
baseInfoForm.value.templateId = id;
|
||||
const result = customFields.map((item: any) => {
|
||||
const memberType = ['MEMBER', 'MULTIPLE_MEMBER'];
|
||||
let initValue = item.defaultValue;
|
||||
const optionsValue: OptionsFieldId[] = item.options;
|
||||
if (memberType.includes(item.type)) {
|
||||
if (item.defaultValue === 'CREATE_USER' || item.defaultValue.includes('CREATE_USER')) {
|
||||
initValue = item.type === 'MEMBER' ? userStore.id : [userStore.id];
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: item.type,
|
||||
name: item.fieldId,
|
||||
label: item.fieldName,
|
||||
value: initValue,
|
||||
required: item.required,
|
||||
options: optionsValue || [],
|
||||
props: {
|
||||
modelValue: initValue,
|
||||
options: optionsValue || [],
|
||||
},
|
||||
};
|
||||
});
|
||||
formRules.value = result;
|
||||
baseInfoLoading.value = false;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
const caseTree = ref<ModuleTreeNode[]>([]);
|
||||
|
||||
async function initSelectTree() {
|
||||
try {
|
||||
caseTree.value = await getCaseModuleTree({ projectId: appStore.currentProjectId });
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
initDefaultFields();
|
||||
initSelectTree();
|
||||
});
|
||||
|
||||
function handleSave() {
|
||||
if (activeExtraKey.value === 'baseInfo') {
|
||||
baseInfoFormRef.value?.validate((errors) => {
|
||||
if (!errors) {
|
||||
Message.success(t('common.saveSuccess'));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const fileList = ref<MsFileItem[]>([]);
|
||||
|
||||
// 处理关联文件
|
||||
function saveSelectAssociatedFile(fileData: AssociatedList[]) {
|
||||
const fileResultList = fileData.map((fileInfo) => convertToFile(fileInfo));
|
||||
fileList.value.push(...fileResultList);
|
||||
}
|
||||
const getListFunParams = ref<TableQueryParams>({
|
||||
combine: {
|
||||
hiddenIds: [],
|
||||
},
|
||||
});
|
||||
|
||||
// 监视文件列表处理关联和本地文件
|
||||
watch(
|
||||
() => fileList.value,
|
||||
(val) => {
|
||||
if (val) {
|
||||
getListFunParams.value.combine.hiddenIds = fileList.value.filter((item) => !item.local).map((item) => item.uid);
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
const showLinkFileDrawer = ref<boolean>(false);
|
||||
|
||||
function handleFileChange(_fileList: MsFileItem[]) {
|
||||
fileList.value = _fileList.map((e) => {
|
||||
return {
|
||||
...e,
|
||||
enable: true, // 是否启用
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const imageUrl = ref('');
|
||||
const previewVisible = ref<boolean>(false);
|
||||
|
||||
// 预览图片
|
||||
async function handlePreview(item: MsFileItem) {
|
||||
try {
|
||||
previewVisible.value = true;
|
||||
if (item.status !== 'init') {
|
||||
const res = await previewFile({
|
||||
projectId: appStore.currentProjectId,
|
||||
caseId: activeCase.value.id,
|
||||
fileId: item.uid,
|
||||
local: item.local,
|
||||
});
|
||||
const blob = new Blob([res], { type: 'image/jpeg' });
|
||||
imageUrl.value = URL.createObjectURL(blob);
|
||||
} else {
|
||||
imageUrl.value = item.url || '';
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
const transferVisible = ref<boolean>(false);
|
||||
|
||||
const activeTransferFileParams = ref<MsFileItem>();
|
||||
|
||||
// 转存
|
||||
function transferFile(item: MsFileItem) {
|
||||
activeTransferFileParams.value = { ...item };
|
||||
transferVisible.value = true;
|
||||
}
|
||||
|
||||
// 下载
|
||||
async function downloadFile(item: MsFileItem) {
|
||||
try {
|
||||
const res = await downloadFileRequest({
|
||||
projectId: appStore.currentProjectId,
|
||||
caseId: activeCase.value.id,
|
||||
fileId: item.uid,
|
||||
local: item.local,
|
||||
});
|
||||
downloadByteFile(res, `${item.name}`);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新文件
|
||||
async function handleUpdateFile(item: MsFileItem) {
|
||||
try {
|
||||
await updateFile(appStore.currentProjectId, item.associationId);
|
||||
Message.success(t('common.updateSuccess'));
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleUploadImage(file: File) {
|
||||
const { data } = await editorUploadFile({
|
||||
fileList: [file],
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
const activeComment = ref('caseComment');
|
||||
const commentTypeOptions = [
|
||||
{
|
||||
label: t('caseManagement.featureCase.caseComment'),
|
||||
value: 'caseComment',
|
||||
},
|
||||
{
|
||||
label: t('caseManagement.featureCase.reviewComment'),
|
||||
value: 'reviewComment',
|
||||
},
|
||||
{
|
||||
label: t('caseManagement.featureCase.executiveReview'),
|
||||
value: 'executiveComment',
|
||||
},
|
||||
];
|
||||
const commentList = ref<CommentItem[]>([]);
|
||||
const reviewCommentList = ref<CommentItem[]>([]);
|
||||
|
||||
// 初始化评论列表
|
||||
async function initCommentList() {
|
||||
try {
|
||||
const result = await getCommentList(activeCase.value.id);
|
||||
commentList.value = result;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
// 初始化评审评论
|
||||
async function initReviewCommentList() {
|
||||
try {
|
||||
const result = await getReviewCommentList(activeCase.value.id);
|
||||
reviewCommentList.value = result;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化执行评论
|
||||
async function initTestPlanExecuteCommentList() {
|
||||
try {
|
||||
const result = await getTestPlanExecuteCommentList(activeCase.value.id);
|
||||
reviewCommentList.value = result;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function getAllCommentList() {
|
||||
switch (activeComment.value) {
|
||||
case 'caseComment':
|
||||
await initCommentList();
|
||||
break;
|
||||
case 'reviewComment':
|
||||
await initReviewCommentList();
|
||||
break;
|
||||
case 'executiveComment':
|
||||
await initTestPlanExecuteCommentList();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加或者更新评论
|
||||
async function handleUpdateOrAdd(item: CommentParams, cb: (result: boolean) => void) {
|
||||
try {
|
||||
await addOrUpdateCommentList(item);
|
||||
getAllCommentList();
|
||||
cb(true);
|
||||
} catch (error) {
|
||||
cb(false);
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 删除评论
|
||||
async function handleDelete(caseCommentId: string) {
|
||||
openModal({
|
||||
type: 'error',
|
||||
title: t('ms.comment.deleteConfirm'),
|
||||
content: t('ms.comment.deleteContent'),
|
||||
okText: t('common.confirmDelete'),
|
||||
cancelText: t('common.cancel'),
|
||||
okButtonProps: {
|
||||
status: 'danger',
|
||||
},
|
||||
onBeforeOk: async () => {
|
||||
try {
|
||||
await deleteCommentList(caseCommentId);
|
||||
Message.success(t('common.deleteSuccess'));
|
||||
getAllCommentList();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
hideCancel: false,
|
||||
});
|
||||
}
|
||||
|
||||
const commentInputRef = ref<InstanceType<typeof inputComment>>();
|
||||
const content = ref('');
|
||||
const isActive = ref<boolean>(false);
|
||||
|
||||
const noticeUserIds = ref<string[]>([]);
|
||||
async function publishHandler(currentContent: string) {
|
||||
try {
|
||||
const params: CommentParams = {
|
||||
caseId: activeCase.value.id,
|
||||
notifier: noticeUserIds.value.join(';'),
|
||||
replyUser: '',
|
||||
parentId: '',
|
||||
content: currentContent,
|
||||
event: noticeUserIds.value.join(';') ? 'AT' : 'COMMENT', // 任务事件(仅评论: ’COMMENT‘; 评论并@: ’AT‘; 回复评论/回复并@: ’REPLAY‘;)
|
||||
};
|
||||
await createCommentList(params);
|
||||
if (activeExtraKey.value === 'comments') {
|
||||
getAllCommentList();
|
||||
}
|
||||
|
||||
Message.success(t('common.publishSuccessfully'));
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
function cancelPublish() {
|
||||
isActive.value = !isActive.value;
|
||||
}
|
||||
|
||||
const bugList = ref<any[]>([]);
|
||||
|
||||
async function initBugList() {
|
||||
bugList.value = [
|
||||
{
|
||||
name: 'sdasd',
|
||||
id: '3d23d23d',
|
||||
num: 100001,
|
||||
},
|
||||
{
|
||||
name: 'sdasd',
|
||||
id: '3d23d23d',
|
||||
num: 100002,
|
||||
},
|
||||
{
|
||||
name: 'sdasd',
|
||||
id: '3d23d23d',
|
||||
num: 100003,
|
||||
},
|
||||
{
|
||||
name: 'sdasd',
|
||||
id: '3d23d23d',
|
||||
num: 100004,
|
||||
},
|
||||
{
|
||||
name: 'sdasd',
|
||||
id: '3d23d23d',
|
||||
num: 100005,
|
||||
},
|
||||
{
|
||||
name: 'sdasd',
|
||||
id: '3d23d23d',
|
||||
num: 100006,
|
||||
},
|
||||
{
|
||||
name: 'sdasd',
|
||||
id: '3d23d23d',
|
||||
num: 100007,
|
||||
},
|
||||
{
|
||||
name: 'sdasd',
|
||||
id: '3d23d23d',
|
||||
num: 100008,
|
||||
},
|
||||
{
|
||||
name: 'sdasd',
|
||||
id: '3d23d23d',
|
||||
num: 100009,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const cancelLoading = ref<boolean>(false);
|
||||
// 取消关联
|
||||
async function disassociateBug(id: string) {
|
||||
cancelLoading.value = true;
|
||||
try {
|
||||
await cancelAssociatedDebug(id);
|
||||
bugList.value = bugList.value.filter((item) => item.id !== id);
|
||||
Message.success(t('caseManagement.featureCase.cancelLinkSuccess'));
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
cancelLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function goBug(id: string) {
|
||||
router.push({
|
||||
name: BugManagementRouteEnum.BUG_MANAGEMENT_INDEX,
|
||||
query: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const showCreateBugDrawer = ref<boolean>(false);
|
||||
function createBug() {
|
||||
showCreateBugDrawer.value = true;
|
||||
}
|
||||
|
||||
const showLinkBugDrawer = ref<boolean>(false);
|
||||
|
||||
function linkBug() {
|
||||
showLinkBugDrawer.value = true;
|
||||
}
|
||||
|
||||
const drawerLoading = ref<boolean>(false);
|
||||
async function saveHandler(params: TableQueryParams) {
|
||||
try {
|
||||
drawerLoading.value = true;
|
||||
await associatedDebug(params);
|
||||
Message.success(t('caseManagement.featureCase.associatedSuccess'));
|
||||
initBugList();
|
||||
showLinkBugDrawer.value = false;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
drawerLoading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
<style lang="less" scoped>
|
||||
:deep(.commentWrapper) {
|
||||
right: 0;
|
||||
}
|
||||
.bug-list {
|
||||
.ms-scroll-bar();
|
||||
|
||||
overflow-y: auto;
|
||||
margin-bottom: 16px;
|
||||
height: calc(100% - 46px);
|
||||
border-radius: var(--border-radius-small);
|
||||
.bug-item {
|
||||
@apply cursor-pointer;
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
padding: 8px;
|
||||
border: 1px solid var(--color-text-n8);
|
||||
border-radius: var(--border-radius-small);
|
||||
background-color: white;
|
||||
&:hover {
|
||||
@apply relative;
|
||||
|
||||
background: var(--color-text-n9);
|
||||
box-shadow: inset 0 0 0.5px 0.5px rgb(var(--primary-5));
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
<template>
|
||||
<FeatureCaseMinder :import-json="props.importJson" />
|
||||
<FeatureCaseMinder :module-id="props.moduleId" :module-name="props.moduleName" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { MinderJson } from '@/components/pure/ms-minder-editor/props';
|
||||
import FeatureCaseMinder from '@/components/business/ms-minders/featureCaseMinder.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
minderType: 'FeatureCase';
|
||||
importJson: MinderJson;
|
||||
moduleId: string;
|
||||
moduleName: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
|
|
|
@ -3,4 +3,7 @@ export default {
|
|||
'ms.minders.stepDesc': '步骤描述',
|
||||
'ms.minders.stepExpect': '预期结果',
|
||||
'ms.minders.textDesc': '文本描述',
|
||||
'ms.minders.caseName': '用例名称',
|
||||
'ms.minders.caseNameNotNull': '用例名称不能为空',
|
||||
'ms.minders.commentTotal': '共 {num} 评论',
|
||||
};
|
||||
|
|
|
@ -49,15 +49,18 @@
|
|||
@enter-node="handleEnterNode"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="props.extractContentTabList?.length" class="ms-minder-editor-extra-content">
|
||||
<div v-if="props.extractContentTabList?.length" class="ms-minder-editor-extra">
|
||||
<div class="pl-[16px] pt-[16px]">
|
||||
<MsTab v-model:activeKey="activeExtraKey" :content-tab-list="props.extractContentTabList" mode="button" />
|
||||
</div>
|
||||
<div class="ms-minder-editor-extra-content">
|
||||
<slot name="extractTabContent"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="minderEditor" setup>
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
import MsTab from '@/components/pure/ms-tab/index.vue';
|
||||
import minderHeader from './main/header.vue';
|
||||
import mainEditor from './main/mainEditor.vue';
|
||||
|
@ -134,13 +137,18 @@
|
|||
<style lang="less" scoped>
|
||||
.ms-minder-editor-container {
|
||||
@apply relative flex h-full;
|
||||
.ms-minder-editor-extra-content {
|
||||
@apply border-l;
|
||||
.ms-minder-editor-extra {
|
||||
@apply flex flex-col border-l;
|
||||
|
||||
padding: 16px;
|
||||
width: 35%;
|
||||
min-width: 360px;
|
||||
border-color: var(--color-text-n8);
|
||||
.ms-minder-editor-extra-content {
|
||||
@apply relative flex-1 overflow-y-auto;
|
||||
.ms-scroll-bar();
|
||||
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -44,7 +44,7 @@ export const mainEditorProps = {
|
|||
default: 500,
|
||||
},
|
||||
disabled: Boolean,
|
||||
extractContentTabList: [] as PropType<{ label: string; value: string }[]>,
|
||||
extractContentTabList: Array as PropType<{ label: string; value: string }[]>,
|
||||
};
|
||||
|
||||
export const headerProps = {
|
||||
|
|
|
@ -225,6 +225,7 @@ export interface CommitReviewResultParams {
|
|||
status: ReviewResult;
|
||||
content: string;
|
||||
notifier: string;
|
||||
reviewCommentFileIds?: string[];
|
||||
}
|
||||
// 评审详情-获取用例评审历史
|
||||
export interface ReviewHistoryItem {
|
||||
|
|
|
@ -364,3 +364,57 @@ export interface ContentTabsMap {
|
|||
tabList: TabItemType[];
|
||||
backupTabList: TabItemType[];
|
||||
}
|
||||
// 脑图删除的模块/用例的集合
|
||||
export interface FeatureCaseMinderDeleteResourceList {
|
||||
id: string;
|
||||
type: string;
|
||||
}
|
||||
// 脑图新增/修改的模块集合(只记录操作的节点,节点下的子节点不需要记录)
|
||||
export interface FeatureCaseMinderUpdateModuleList {
|
||||
id: string;
|
||||
name: string;
|
||||
parentId: string;
|
||||
type: 'ADD' | 'UPDATE'; // 操作类型(新增(ADD)/更新(UPDATE))
|
||||
moveMode: string;
|
||||
targetId: string;
|
||||
}
|
||||
|
||||
export interface CustomField {
|
||||
fieldId: string;
|
||||
value: string;
|
||||
}
|
||||
// 脑图用例步骤描述项
|
||||
export interface FeatureCaseMinderStepItem {
|
||||
id: string;
|
||||
num: number;
|
||||
desc: string;
|
||||
result?: string;
|
||||
actualResult?: string;
|
||||
executeResult?: string;
|
||||
}
|
||||
// 脑图新增/修改的用例对象集合
|
||||
export interface FeatureCaseMinderUpdateCaseList {
|
||||
id: string;
|
||||
templateId: string; // 模板id
|
||||
type: string;
|
||||
name: string;
|
||||
moduleId: string;
|
||||
moveMode?: string;
|
||||
targetId?: string;
|
||||
prerequisite: string;
|
||||
caseEditType: 'STEP' | 'TEXT'; // 编辑模式
|
||||
steps: string;
|
||||
textDescription: string;
|
||||
expectedResult: string;
|
||||
description: string;
|
||||
tags: string[];
|
||||
customFields: CustomField[];
|
||||
}
|
||||
// 脑图
|
||||
export interface FeatureCaseMinder {
|
||||
projectId: string;
|
||||
versionId?: string;
|
||||
updateCaseList: FeatureCaseMinderUpdateCaseList[];
|
||||
updateModuleList: FeatureCaseMinderUpdateModuleList[];
|
||||
deleteResourceList: FeatureCaseMinderDeleteResourceList[];
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import localforage from 'localforage';
|
||||
|
||||
import {
|
||||
getCaseDefaultFields,
|
||||
|
|
|
@ -253,9 +253,7 @@
|
|||
json: parseJson.value,
|
||||
path: expressionForm.value.expression,
|
||||
})?.map((e: any) =>
|
||||
typeof e === 'string'
|
||||
? e
|
||||
: JSON.stringify(e)
|
||||
JSON.stringify(e)
|
||||
.replace(/Number\(([^)]+)\)/g, '$1')
|
||||
.replace(/^"|"$/g, '')
|
||||
) || [];
|
||||
|
|
|
@ -299,7 +299,7 @@
|
|||
input-size="small"
|
||||
tag-size="small"
|
||||
@change="(files, file) => handleFileChange(files, record, rowIndex, file)"
|
||||
@delete-file="() => emitChange('deleteFile')"
|
||||
@delete-file="() => handleSingleFileDelete(record)"
|
||||
/>
|
||||
</template>
|
||||
<!-- 长度范围 -->
|
||||
|
@ -725,6 +725,7 @@
|
|||
(e: 'projectChange', projectId: string): void;
|
||||
(e: 'treeDelete', record: Record<string, any>): void;
|
||||
(e: 'batchAdd'): void;
|
||||
(e: 'deleteFile', record: Record<string, any>): void;
|
||||
}>();
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
@ -1052,6 +1053,17 @@
|
|||
}
|
||||
}
|
||||
|
||||
function handleSingleFileDelete(record: Record<string, any>) {
|
||||
record.file = {
|
||||
fileId: '',
|
||||
fileName: '',
|
||||
fileAlias: '',
|
||||
local: false,
|
||||
delete: false,
|
||||
};
|
||||
emitChange('deleteFile');
|
||||
}
|
||||
|
||||
const showQuickInputParam = ref(false);
|
||||
const activeQuickInputRecord = ref<any>({});
|
||||
const quickInputParamValue = ref('');
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
>
|
||||
<MsTagsInput
|
||||
v-model:model-value="mockDetail.tags"
|
||||
class="w-[732px]"
|
||||
input-class="w-[732px]"
|
||||
allow-clear
|
||||
unique-value
|
||||
retain-input-value
|
||||
|
@ -269,12 +269,8 @@
|
|||
}
|
||||
return t('mockManagement.createMock');
|
||||
});
|
||||
const activeTab = ref<RequestComposition>(RequestComposition.BODY);
|
||||
const activeTab = ref<RequestComposition>(RequestComposition.HEADER);
|
||||
const mockTabList = [
|
||||
{
|
||||
value: RequestComposition.BODY,
|
||||
label: t('apiTestDebug.body'),
|
||||
},
|
||||
{
|
||||
value: RequestComposition.HEADER,
|
||||
label: t('apiTestDebug.header'),
|
||||
|
@ -287,6 +283,10 @@
|
|||
value: RequestComposition.REST,
|
||||
label: RequestComposition.REST,
|
||||
},
|
||||
{
|
||||
value: RequestComposition.BODY,
|
||||
label: t('apiTestDebug.body'),
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -210,7 +210,7 @@
|
|||
];
|
||||
|
||||
function handleCsvVariablesChange(resultArr: any[], isInit?: boolean) {
|
||||
csvVariables.value = [...resultArr];
|
||||
csvVariables.value = resultArr.map((e) => ({ ...e, enable: e.name && e.fileId }));
|
||||
if (!isInit) {
|
||||
emit('change');
|
||||
}
|
||||
|
|
|
@ -219,7 +219,7 @@
|
|||
</div>
|
||||
<div class="mt-[16px] h-[calc(100%-32px)] border-t border-[var(--color-text-n8)]">
|
||||
<!-- 脑图开始 -->
|
||||
<MsMinder minder-type="FeatureCase" :import-json="importJson" @node-click="handleNodeClick" />
|
||||
<MsMinder minder-type="FeatureCase" :module-id="props.activeFolder" :module-name="props.moduleName" />
|
||||
<MsDrawer v-model:visible="visible" :width="480" :mask="false">
|
||||
{{ nodeData.text }}
|
||||
</MsDrawer>
|
||||
|
@ -383,7 +383,7 @@
|
|||
|
||||
const props = defineProps<{
|
||||
activeFolder: string;
|
||||
activeFolderType: 'folder' | 'module';
|
||||
moduleName: string;
|
||||
offspringIds: string[]; // 当前选中文件夹的所有子孙节点id
|
||||
modulesCount: Record<string, number>; // 模块数量
|
||||
}>();
|
||||
|
@ -414,65 +414,11 @@
|
|||
};
|
||||
});
|
||||
});
|
||||
const moduleId = computed(() => featureCaseStore.moduleId[0]);
|
||||
const currentProjectId = computed(() => appStore.currentProjectId);
|
||||
|
||||
const visible = ref<boolean>(false);
|
||||
const nodeData = ref<any>({});
|
||||
|
||||
const importJson = ref<any>({});
|
||||
|
||||
function handleNodeClick(data: any) {
|
||||
if (data.resource && data.resource.includes('用例')) {
|
||||
visible.value = true;
|
||||
nodeData.value = data;
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
importJson.value = {
|
||||
root: {
|
||||
data: {
|
||||
text: '测试用例',
|
||||
id: 'xxxx',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
data: {
|
||||
id: 'sdasdas',
|
||||
text: '模块 1',
|
||||
resource: ['模块'],
|
||||
},
|
||||
},
|
||||
{
|
||||
data: {
|
||||
id: 'dasdasda',
|
||||
text: '模块 2',
|
||||
expandState: 'collapse',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
data: {
|
||||
id: 'frihofiuho3f',
|
||||
text: '用例 1',
|
||||
resource: ['用例'],
|
||||
},
|
||||
},
|
||||
{
|
||||
data: {
|
||||
id: 'df09348f034f',
|
||||
text: ' 用例 2',
|
||||
resource: ['用例'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
template: 'default',
|
||||
};
|
||||
});
|
||||
|
||||
const hasOperationPermission = computed(() =>
|
||||
hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE', 'FUNCTIONAL_CASE:READ+DELETE'])
|
||||
);
|
||||
|
|
|
@ -239,7 +239,7 @@
|
|||
offspringIds.push(e.id);
|
||||
return e;
|
||||
});
|
||||
emits('caseNodeSelect', selectedKeys, offspringIds);
|
||||
emits('caseNodeSelect', selectedKeys, offspringIds, node);
|
||||
};
|
||||
|
||||
// 用例树节点更多事件
|
||||
|
|
|
@ -149,7 +149,7 @@
|
|||
|
||||
import { getBugList, getCustomOptionHeader } from '@/api/modules/bug-management';
|
||||
import {
|
||||
associatedDrawerDebug,
|
||||
associatedDebug,
|
||||
cancelAssociatedDebug,
|
||||
getLinkedCaseBugList,
|
||||
} from '@/api/modules/case-management/featureCase';
|
||||
|
@ -412,7 +412,7 @@
|
|||
async function saveHandler(params: TableQueryParams) {
|
||||
try {
|
||||
drawerLoading.value = true;
|
||||
await associatedDrawerDebug(params);
|
||||
await associatedDebug(params);
|
||||
Message.success(t('caseManagement.featureCase.associatedSuccess'));
|
||||
getFetch();
|
||||
showLinkDrawer.value = false;
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
<template>
|
||||
<div class="flex flex-1 flex-col overflow-hidden">
|
||||
<div class="review-history-list">
|
||||
<div v-for="item of props.reviewCommentList" :key="item.id" class="review-history-list-item">
|
||||
<div class="flex items-center">
|
||||
<MSAvatar :avatar="item.userLogo" />
|
||||
<div class="ml-[8px] flex items-center">
|
||||
<a-tooltip :content="item.userName" :mouse-enter-delay="300">
|
||||
<div class="one-line-text max-w-[300px] font-medium text-[var(--color-text-1)]">{{ item.userName }}</div>
|
||||
</a-tooltip>
|
||||
<a-divider direction="vertical" margin="8px"></a-divider>
|
||||
<div v-if="item.status === 'PASS'" class="flex items-center">
|
||||
<MsIcon type="icon-icon_succeed_filled" class="mr-[4px] text-[rgb(var(--success-6))]" />
|
||||
{{ t('caseManagement.caseReview.pass') }}
|
||||
</div>
|
||||
<div v-else-if="item.status === 'UN_PASS'" class="flex items-center">
|
||||
<MsIcon type="icon-icon_close_filled" class="mr-[4px] text-[rgb(var(--danger-6))]" />
|
||||
{{ t('caseManagement.caseReview.fail') }}
|
||||
</div>
|
||||
<div v-else-if="item.status === 'UNDER_REVIEWED'" class="flex items-center">
|
||||
<MsIcon type="icon-icon_warning_filled" class="mr-[4px] text-[rgb(var(--link-6))]" />
|
||||
{{ t('caseManagement.caseReview.suggestion') }}
|
||||
</div>
|
||||
<div v-else-if="item.status === 'RE_REVIEWED'" class="flex items-center">
|
||||
<MsIcon type="icon-icon_resubmit_filled" class="mr-[4px] text-[rgb(var(--warning-6))]" />
|
||||
{{ t('caseManagement.caseReview.reReview') }}
|
||||
</div>
|
||||
<div v-if="item.status === 'PASSED'" class="flex items-center">
|
||||
<MsIcon type="icon-icon_succeed_filled" class="mr-[4px] text-[rgb(var(--success-6))]" />
|
||||
{{ t('caseManagement.featureCase.execute.success') }}
|
||||
</div>
|
||||
<div v-if="item.status === 'BLOCKED'" class="flex items-center">
|
||||
<MsIcon type="icon-icon_succeed_filled" class="mr-[4px] text-[rgb(var(--warning-6))]" />
|
||||
{{ t('caseManagement.featureCase.execute.blocked') }}
|
||||
</div>
|
||||
<div v-if="item.status === 'FAILED'" class="flex items-center">
|
||||
<MsIcon type="icon-icon_succeed_filled" class="mr-[4px] text-[rgb(var(--danger-6))]" />
|
||||
{{ t('caseManagement.featureCase.execute.failed') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="markdown-body" style="margin-left: 48px" v-html="item.contentText"></div>
|
||||
<div class="ml-[48px] mt-[8px] flex text-[var(--color-text-4)]">
|
||||
{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}
|
||||
<div v-if="props.activeComment === 'reviewComment'">
|
||||
<a-tooltip :content="item.reviewName" :mouse-enter-delay="300">
|
||||
<span v-if="item.deleted" class="one-line-text ml-[16px] max-w-[300px] break-words break-all">
|
||||
{{ characterLimit(item.reviewName) }}
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-else
|
||||
class="one-line-text ml-[16px] max-w-[300px] cursor-pointer break-words break-all text-[rgb(var(--primary-5))]"
|
||||
@click="review(item)"
|
||||
>
|
||||
{{ characterLimit(item.reviewName) }}
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<div v-if="props.activeComment === 'executiveComment'">
|
||||
<a-tooltip :content="item.testPlanName" :mouse-enter-delay="300">
|
||||
<span v-if="item.deleted" class="one-line-text ml-[16px] max-w-[300px] break-words break-all">
|
||||
{{ characterLimit(item.testPlanName) }}
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-else
|
||||
class="one-line-text ml-[16px] max-w-[300px] cursor-pointer break-words break-all text-[rgb(var(--primary-5))]"
|
||||
@click="toPlan(item)"
|
||||
>
|
||||
{{ characterLimit(item.testPlanName) }}
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MsEmpty v-if="reviewCommentList.length === 0" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import MSAvatar from '@/components/pure/ms-avatar/index.vue';
|
||||
import MsEmpty from '@/components/pure/ms-empty/index.vue';
|
||||
import { CommentItem } from '@/components/business/ms-comment/types';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { characterLimit } from '@/utils';
|
||||
|
||||
import { CaseManagementRouteEnum, TestPlanRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
const props = defineProps<{
|
||||
reviewCommentList: any[];
|
||||
activeComment: string;
|
||||
}>();
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
|
||||
// 去用例评审页面
|
||||
function review(record: CommentItem) {
|
||||
router.push({
|
||||
name: CaseManagementRouteEnum.CASE_MANAGEMENT_REVIEW_DETAIL_CASE_DETAIL,
|
||||
query: {
|
||||
...route.query,
|
||||
caseId: record.caseId,
|
||||
id: record.reviewId,
|
||||
},
|
||||
state: {
|
||||
params: JSON.stringify(record.moduleName),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 去测试计划页面
|
||||
function toPlan(record: CommentItem) {
|
||||
router.push({
|
||||
name: TestPlanRouteEnum.TEST_PLAN_INDEX_DETAIL,
|
||||
query: {
|
||||
...route.query,
|
||||
id: record.testPlanId,
|
||||
},
|
||||
state: {
|
||||
params: JSON.stringify(record.moduleName),
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
|
@ -21,89 +21,11 @@
|
|||
</div>
|
||||
|
||||
<!-- 评审评论 -->
|
||||
<div
|
||||
<ReviewCommentList
|
||||
v-show="activeComment === 'reviewComment' || activeComment === 'executiveComment'"
|
||||
class="flex flex-1 flex-col overflow-hidden"
|
||||
>
|
||||
<div class="review-history-list">
|
||||
<div v-for="item of reviewCommentList" :key="item.id" class="review-history-list-item">
|
||||
<div class="flex items-center">
|
||||
<MSAvatar :avatar="item.userLogo" />
|
||||
<div class="ml-[8px] flex items-center">
|
||||
<a-tooltip :content="item.userName" :mouse-enter-delay="300">
|
||||
<div class="one-line-text max-w-[300px] font-medium text-[var(--color-text-1)]">{{
|
||||
item.userName
|
||||
}}</div>
|
||||
</a-tooltip>
|
||||
<a-divider direction="vertical" margin="8px"></a-divider>
|
||||
<div v-if="item.status === 'PASS'" class="flex items-center">
|
||||
<MsIcon type="icon-icon_succeed_filled" class="mr-[4px] text-[rgb(var(--success-6))]" />
|
||||
{{ t('caseManagement.caseReview.pass') }}
|
||||
</div>
|
||||
<div v-else-if="item.status === 'UN_PASS'" class="flex items-center">
|
||||
<MsIcon type="icon-icon_close_filled" class="mr-[4px] text-[rgb(var(--danger-6))]" />
|
||||
{{ t('caseManagement.caseReview.fail') }}
|
||||
</div>
|
||||
<div v-else-if="item.status === 'UNDER_REVIEWED'" class="flex items-center">
|
||||
<MsIcon type="icon-icon_warning_filled" class="mr-[4px] text-[rgb(var(--link-6))]" />
|
||||
{{ t('caseManagement.caseReview.suggestion') }}
|
||||
</div>
|
||||
<div v-else-if="item.status === 'RE_REVIEWED'" class="flex items-center">
|
||||
<MsIcon type="icon-icon_resubmit_filled" class="mr-[4px] text-[rgb(var(--warning-6))]" />
|
||||
{{ t('caseManagement.caseReview.reReview') }}
|
||||
</div>
|
||||
<div v-if="item.status === 'PASSED'" class="flex items-center">
|
||||
<MsIcon type="icon-icon_succeed_filled" class="mr-[4px] text-[rgb(var(--success-6))]" />
|
||||
{{ t('caseManagement.featureCase.execute.success') }}
|
||||
</div>
|
||||
<div v-if="item.status === 'BLOCKED'" class="flex items-center">
|
||||
<MsIcon type="icon-icon_succeed_filled" class="mr-[4px] text-[rgb(var(--warning-6))]" />
|
||||
{{ t('caseManagement.featureCase.execute.blocked') }}
|
||||
</div>
|
||||
<div v-if="item.status === 'FAILED'" class="flex items-center">
|
||||
<MsIcon type="icon-icon_succeed_filled" class="mr-[4px] text-[rgb(var(--danger-6))]" />
|
||||
{{ t('caseManagement.featureCase.execute.failed') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="markdown-body" style="margin-left: 48px" v-html="item.contentText"></div>
|
||||
<div class="ml-[48px] mt-[8px] flex text-[var(--color-text-4)]">
|
||||
{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}
|
||||
<div v-if="activeComment === 'reviewComment'">
|
||||
<a-tooltip :content="item.reviewName" :mouse-enter-delay="300">
|
||||
<span v-if="item.deleted" class="one-line-text ml-[16px] max-w-[300px] break-words break-all">
|
||||
{{ characterLimit(item.reviewName) }}
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-else
|
||||
class="one-line-text ml-[16px] max-w-[300px] cursor-pointer break-words break-all text-[rgb(var(--primary-5))]"
|
||||
@click="review(item)"
|
||||
>
|
||||
{{ characterLimit(item.reviewName) }}
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<div v-if="activeComment === 'executiveComment'">
|
||||
<a-tooltip :content="item.testPlanName" :mouse-enter-delay="300">
|
||||
<span v-if="item.deleted" class="one-line-text ml-[16px] max-w-[300px] break-words break-all">
|
||||
{{ characterLimit(item.testPlanName) }}
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-else
|
||||
class="one-line-text ml-[16px] max-w-[300px] cursor-pointer break-words break-all text-[rgb(var(--primary-5))]"
|
||||
@click="toPlan(item)"
|
||||
>
|
||||
{{ characterLimit(item.testPlanName) }}
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MsEmpty v-if="reviewCommentList.length === 0" />
|
||||
</div>
|
||||
</div>
|
||||
:review-comment-list="reviewCommentList"
|
||||
:active-comment="activeComment"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -111,12 +33,11 @@
|
|||
import { ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import MSAvatar from '@/components/pure/ms-avatar/index.vue';
|
||||
import MsEmpty from '@/components/pure/ms-empty/index.vue';
|
||||
import MsComment from '@/components/business/ms-comment/comment';
|
||||
import { CommentItem, CommentParams } from '@/components/business/ms-comment/types';
|
||||
import ReviewCommentList from './reviewCommentList.vue';
|
||||
|
||||
import {
|
||||
addOrUpdateCommentList,
|
||||
|
@ -130,7 +51,6 @@
|
|||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useModal from '@/hooks/useModal';
|
||||
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
||||
import { characterLimit } from '@/utils';
|
||||
|
||||
import { CaseManagementRouteEnum, TestPlanRouteEnum } from '@/enums/routeEnum';
|
||||
|
||||
|
|
|
@ -110,8 +110,8 @@
|
|||
ref="caseTableRef"
|
||||
:active-folder="activeFolder"
|
||||
:offspring-ids="offspringIds"
|
||||
:active-folder-type="activeCaseType"
|
||||
:modules-count="modulesCount"
|
||||
:module-name="activeFolderName"
|
||||
@init="initModulesCount"
|
||||
@import="importCase"
|
||||
></CaseTable>
|
||||
|
@ -156,6 +156,7 @@
|
|||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||
import MsPopConfirm from '@/components/pure/ms-popconfirm/index.vue';
|
||||
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
||||
import { MsTreeNodeData } from '@/components/business/ms-tree/types';
|
||||
import CaseTable from './components/caseTable.vue';
|
||||
import FeatureCaseTree from './components/caseTree.vue';
|
||||
import ExportExcelModal from './components/export/exportCaseModal.vue';
|
||||
|
@ -191,6 +192,7 @@
|
|||
};
|
||||
|
||||
const activeFolder = ref<string>('all');
|
||||
const activeFolderName = ref('');
|
||||
|
||||
// 选中节点
|
||||
const selectedKeys = computed({
|
||||
|
@ -220,11 +222,12 @@
|
|||
|
||||
const featureCaseStore = useFeatureCaseStore();
|
||||
// 处理用例树节点选中
|
||||
function caseNodeSelect(keys: string[], _offspringIds: string[]) {
|
||||
function caseNodeSelect(keys: string[], _offspringIds: string[], node: MsTreeNodeData) {
|
||||
[activeFolder.value] = keys;
|
||||
activeCaseType.value = 'module';
|
||||
offspringIds.value = [..._offspringIds];
|
||||
featureCaseStore.setModuleId(keys);
|
||||
activeFolderName.value = node.title || node.name;
|
||||
}
|
||||
|
||||
const confirmLoading = ref(false);
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
<MsRichText
|
||||
v-model:raw="caseResultForm.reason"
|
||||
v-model:commentIds="caseResultForm.commentIds"
|
||||
v-model:filed-ids="caseResultForm.fileList"
|
||||
:upload-image="handleUploadImage"
|
||||
:preview-url="PreviewEditorImageUrl"
|
||||
class="w-full"
|
||||
|
@ -84,7 +85,6 @@
|
|||
|
||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||
import MsRichText from '@/components/pure/ms-rich-text/MsRichText.vue';
|
||||
import { MsFileItem } from '@/components/pure/ms-upload/types';
|
||||
|
||||
import { getCaseReviewerList, saveCaseReviewResult } from '@/api/modules/case-management/caseReview';
|
||||
import { editorUploadFile } from '@/api/modules/case-management/featureCase';
|
||||
|
@ -114,7 +114,7 @@
|
|||
const caseResultForm = ref({
|
||||
result: 'PASS' as ReviewResult,
|
||||
reason: '',
|
||||
fileList: [] as MsFileItem[],
|
||||
fileList: [] as string[],
|
||||
commentIds: [] as string[],
|
||||
});
|
||||
const submitReviewLoading = ref(false);
|
||||
|
@ -152,6 +152,7 @@
|
|||
reviewPassRule: props.reviewPassRule,
|
||||
content: caseResultForm.value.reason,
|
||||
notifier: caseResultForm.value.commentIds.join(';'),
|
||||
reviewCommentFileIds: caseResultForm.value.fileList,
|
||||
};
|
||||
await saveCaseReviewResult(params);
|
||||
modalVisible.value = false;
|
||||
|
@ -169,7 +170,7 @@
|
|||
caseResultForm.value = {
|
||||
result: 'PASS' as ReviewResult,
|
||||
reason: '',
|
||||
fileList: [] as MsFileItem[],
|
||||
fileList: [] as string[],
|
||||
commentIds: [] as string[],
|
||||
};
|
||||
if (typeof done === 'function') {
|
||||
|
|
Loading…
Reference in New Issue