feat(测试用例): 功能用例详情抽屉编辑变动为抽屉编辑功能

This commit is contained in:
xinxin.wu 2024-08-12 18:07:53 +08:00 committed by Craftsman
parent d99bde8ba9
commit a0a297f9eb
4 changed files with 162 additions and 38 deletions

View File

@ -12,9 +12,11 @@
<div class="flex flex-1 items-center"> <div class="flex flex-1 items-center">
<!-- 如果设置了tooltipText则优先展示--> <!-- 如果设置了tooltipText则优先展示-->
<a-tooltip :content="props.tooltipText ? props.tooltipText : props.title" position="bottom"> <a-tooltip :content="props.tooltipText ? props.tooltipText : props.title" position="bottom">
<slot name="titleName">
<div class="one-line-text max-w-[300px]"> <div class="one-line-text max-w-[300px]">
{{ props.title }} {{ props.title }}
</div> </div>
</slot>
</a-tooltip> </a-tooltip>
<div class="ml-4 flex items-center"> <div class="ml-4 flex items-center">
<slot name="titleLeft" :loading="loading" :detail="detail"></slot> <slot name="titleLeft" :loading="loading" :detail="detail"></slot>

View File

@ -9,22 +9,45 @@
:detail-id="props.detailId" :detail-id="props.detailId"
:detail-index="props.detailIndex" :detail-index="props.detailIndex"
:get-detail-func="getCaseDetail" :get-detail-func="getCaseDetail"
:pagination="props.pagination" :pagination="!isEditTitle ? props.pagination : undefined"
:table-data="props.tableData" :table-data="props.tableData"
:page-change="props.pageChange" :page-change="props.pageChange"
:mask-closable="true" :mask-closable="true"
:edit-name="true"
show-full-screen show-full-screen
:unmount-on-close="true" :unmount-on-close="true"
@loaded="loadedCase" @loaded="loadedCase"
> >
<template #titleName>
<div :class="`case-title flex items-center ${isEditTitle ? 'w-full' : ''}`">
<a-input
v-if="isEditTitle"
v-model="titleName"
:class="`edit-title w-full ${titleName.length ? '' : 'edit-title-error'}`"
:placeholder="t('case.caseNamePlaceholder')"
allow-clear
:max-length="255"
@blur="handleEditName"
@keydown.enter="handleEditName"
/>
<div v-else class="flex items-center">
<div> {{ detailInfo?.num }} </div>
<div
:class="`${
hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE']) ? 'hover-title-name' : ''
} one-line-text max-w-[200px]`"
@click="clickTitleHandler"
>{{ detailInfo.name }}
</div>
</div>
</div>
</template>
<template #titleLeft> <template #titleLeft>
<div class="flex items-center"><caseLevel :case-level="caseLevels" /></div> <div v-if="!isEditTitle" class="flex items-center"><caseLevel :case-level="caseLevels" /></div>
</template> </template>
<template #titleRight="{ loading }"> <template #titleRight="{ loading }">
<div class="rightButtons flex items-center"> <div class="rightButtons flex items-center">
<MsButton <MsButton
v-permission="['FUNCTIONAL_CASE:READ+UPDATE']" v-if="hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE']) && !isEditTitle"
type="icon" type="icon"
status="secondary" status="secondary"
class="mr-4 !rounded-[var(--border-radius-small)]" class="mr-4 !rounded-[var(--border-radius-small)]"
@ -36,7 +59,7 @@
{{ t('common.edit') }} {{ t('common.edit') }}
</MsButton> </MsButton>
<MsButton <MsButton
v-permission="['FUNCTIONAL_CASE:READ+UPDATE']" v-if="hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE']) && !isEditTitle"
type="icon" type="icon"
status="secondary" status="secondary"
class="mr-4 !rounded-[var(--border-radius-small)]" class="mr-4 !rounded-[var(--border-radius-small)]"
@ -48,7 +71,7 @@
{{ t('caseManagement.featureCase.share') }} {{ t('caseManagement.featureCase.share') }}
</MsButton> </MsButton>
<MsButton <MsButton
v-permission="['FUNCTIONAL_CASE:READ+UPDATE']" v-if="hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE']) && !isEditTitle"
type="icon" type="icon"
status="secondary" status="secondary"
class="mr-4 !rounded-[var(--border-radius-small)]" class="mr-4 !rounded-[var(--border-radius-small)]"
@ -63,7 +86,12 @@
/> />
{{ t('caseManagement.featureCase.follow') }} {{ t('caseManagement.featureCase.follow') }}
</MsButton> </MsButton>
<MsButton type="icon" status="secondary" class="mr-2 !rounded-[var(--border-radius-small)]"> <MsButton
v-if="hasAnyPermission(['FUNCTIONAL_CASE:READ+ADD', 'FUNCTIONAL_CASE:READ+DELETE']) && !isEditTitle"
type="icon"
status="secondary"
class="mr-2 !rounded-[var(--border-radius-small)]"
>
<a-dropdown position="br" :hide-on-select="false"> <a-dropdown position="br" :hide-on-select="false">
<div class="flex items-center"> <div class="flex items-center">
<icon-more class="mr-2" /> <icon-more class="mr-2" />
@ -75,10 +103,14 @@
<!-- <a-doption> <!-- <a-doption>
<a-switch class="mr-1" size="small" type="line" />{{ t('caseManagement.featureCase.addToPublic') }} <a-switch class="mr-1" size="small" type="line" />{{ t('caseManagement.featureCase.addToPublic') }}
</a-doption> --> </a-doption> -->
<a-doption @click="updateHandler('copy')"> <a-doption v-if="hasAnyPermission(['FUNCTIONAL_CASE:READ+ADD'])" @click="updateHandler('copy')">
<MsIcon type="icon-icon_copy_filled" class="font-[16px]" />{{ t('common.copy') }}</a-doption <MsIcon type="icon-icon_copy_filled" class="font-[16px]" />{{ t('common.copy') }}
</a-doption>
<a-doption
v-if="hasAnyPermission(['FUNCTIONAL_CASE:READ+DELETE'])"
class="error-6 text-[rgb(var(--danger-6))]"
@click="deleteHandler"
> >
<a-doption class="error-6 text-[rgb(var(--danger-6))]" @click="deleteHandler">
<MsIcon type="icon-icon_delete-trash_outlined1" class="font-[16px] text-[rgb(var(--danger-6))]" /> <MsIcon type="icon-icon_delete-trash_outlined1" class="font-[16px] text-[rgb(var(--danger-6))]" />
{{ t('common.delete') }} {{ t('common.delete') }}
</a-doption> </a-doption>
@ -110,7 +142,13 @@
} content-wrapper w-full p-[16px] pt-4`" } content-wrapper w-full p-[16px] pt-4`"
> >
<template v-if="activeTab === 'detail'"> <template v-if="activeTab === 'detail'">
<TabDetail :form="detailInfo" :allow-edit="true" :form-rules="formItem" @update-success="updateSuccess" /> <TabDetail
:form="detailInfo"
:allow-edit="true"
:is-edit="props.isEdit"
:form-rules="formItem"
@update-success="updateSuccess"
/>
</template> </template>
<template v-if="activeTab === 'basicInfo'"> <template v-if="activeTab === 'basicInfo'">
<BasicInfo :loading="loading" :detail="detail" @update-success="updateSuccess" /> <BasicInfo :loading="loading" :detail="detail" @update-success="updateSuccess" />
@ -187,6 +225,7 @@
followerCaseRequest, followerCaseRequest,
getCaseDetail, getCaseDetail,
getCaseModuleTree, getCaseModuleTree,
updateCaseRequest,
} from '@/api/modules/case-management/featureCase'; } from '@/api/modules/case-management/featureCase';
import { PreviewEditorImageUrl } from '@/api/requrls/case-management/featureCase'; import { PreviewEditorImageUrl } from '@/api/requrls/case-management/featureCase';
import { defaultCaseDetail } from '@/config/caseManagement'; import { defaultCaseDetail } from '@/config/caseManagement';
@ -196,6 +235,7 @@
import useFeatureCaseStore from '@/store/modules/case/featureCase'; import useFeatureCaseStore from '@/store/modules/case/featureCase';
import useUserStore from '@/store/modules/user'; import useUserStore from '@/store/modules/user';
import { characterLimit } from '@/utils'; import { characterLimit } from '@/utils';
import { hasAnyPermission } from '@/utils/permission';
import type { CustomAttributes, DetailCase, TabItemType } from '@/models/caseManagement/featureCase'; import type { CustomAttributes, DetailCase, TabItemType } from '@/models/caseManagement/featureCase';
import { ModuleTreeNode } from '@/models/common'; import { ModuleTreeNode } from '@/models/common';
@ -229,6 +269,7 @@
tableData: any[]; // tableData: any[]; //
pagination: MsPaginationI; // pagination: MsPaginationI; //
pageChange: (page: number) => Promise<void>; // pageChange: (page: number) => Promise<void>; //
isEdit?: boolean; //
}>(); }>();
const emit = defineEmits(['update:visible', 'success']); const emit = defineEmits(['update:visible', 'success']);
@ -249,6 +290,8 @@
const tabSetting = ref<TabItemType[]>([]); const tabSetting = ref<TabItemType[]>([]);
const activeTab = ref<string | number>('detail'); const activeTab = ref<string | number>('detail');
const titleName = ref('');
function clickMenu(key: string | number) { function clickMenu(key: string | number) {
activeTab.value = key; activeTab.value = key;
featureCaseStore.setActiveTab(key); featureCaseStore.setActiveTab(key);
@ -304,6 +347,7 @@
function loadedCase(detail: DetailCase) { function loadedCase(detail: DetailCase) {
getCaseTree(); getCaseTree();
detailInfo.value = { ...detail }; detailInfo.value = { ...detail };
titleName.value = detail.name;
setCount(detail); setCount(detail);
customFields.value = detailInfo.value?.customFields as CustomAttributes[]; customFields.value = detailInfo.value?.customFields as CustomAttributes[];
caseLevels.value = getCaseLevels(customFields.value) as CaseLevel; caseLevels.value = getCaseLevels(customFields.value) as CaseLevel;
@ -396,7 +440,6 @@
const formRules = ref<FormItem[]>([]); const formRules = ref<FormItem[]>([]);
const formItem = ref<FormRuleItem[]>([]); const formItem = ref<FormRuleItem[]>([]);
const fApi = ref(null);
// //
function initForm() { function initForm() {
formRules.value = initFormCreate(customFields.value, ['FUNCTIONAL_CASE:READ+UPDATE']); formRules.value = initFormCreate(customFields.value, ['FUNCTIONAL_CASE:READ+UPDATE']);
@ -413,6 +456,59 @@
return featureCaseStore.countMap[key] > 99 ? '99+' : `${count > 0 ? count : ''}`; return featureCaseStore.countMap[key] > 99 ? '99+' : `${count > 0 ? count : ''}`;
} }
function getParams() {
const customFieldsArr = (formItem.value || []).map((item: any) => {
return {
fieldId: item.field,
value: Array.isArray(item.value) ? JSON.stringify(item.value) : item.value,
};
});
return {
request: {
...detailInfo.value,
name: titleName.value,
deleteFileMetaIds: [],
unLinkFilesIds: [],
newAssociateFileListIds: [],
customFields: customFieldsArr,
caseDetailFileIds: [],
},
fileList: [],
};
}
const isEditTitle = ref<boolean>(false);
const titleLoading = ref<boolean>(false);
async function handleEditName() {
if (!titleName.value.length) {
return;
}
if (titleName.value === detailInfo.value.name) {
isEditTitle.value = false;
return;
}
titleLoading.value = true;
try {
await updateCaseRequest(getParams());
Message.success(t('caseManagement.featureCase.editSuccess'));
detailInfo.value.name = titleName.value;
isEditTitle.value = false;
updateSuccess();
} catch (error) {
console.log(error);
} finally {
titleLoading.value = false;
}
}
function clickTitleHandler() {
if (hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE'])) {
isEditTitle.value = true;
}
}
watch( watch(
() => customFields.value, () => customFields.value,
() => { () => {
@ -432,6 +528,7 @@
featureCaseStore.setActiveTab(activeTab.value); featureCaseStore.setActiveTab(activeTab.value);
} else { } else {
activeTab.value = ''; activeTab.value = '';
isEditTitle.value = false;
} }
} }
); );
@ -622,4 +719,17 @@
color: rgb(var(--danger-6)); color: rgb(var(--danger-6));
} }
} }
.edit-title-error {
border-color: rgb(var(--danger-6));
}
.hover-title-name {
padding: 4px 8px;
font-size: 16px;
border-radius: 4px;
color: var(--color-text-1);
@apply font-medium;
&:hover {
background: var(--color-text-n9);
}
}
</style> </style>

View File

@ -142,7 +142,7 @@
</template> </template>
<!-- 渲染自定义字段结束 --> <!-- 渲染自定义字段结束 -->
<template #operation="{ record }"> <template #operation="{ record }">
<MsButton v-permission="['FUNCTIONAL_CASE:READ+UPDATE']" class="!mr-0" @click="operateCase(record, 'edit')"> <MsButton v-permission="['FUNCTIONAL_CASE:READ+UPDATE']" class="!mr-0" @click="operateCase(record, true)">
{{ t('common.edit') }} {{ t('common.edit') }}
</MsButton> </MsButton>
<a-divider <a-divider
@ -151,7 +151,7 @@
direction="vertical" direction="vertical"
:margin="8" :margin="8"
></a-divider> ></a-divider>
<MsButton v-permission="['FUNCTIONAL_CASE:READ+ADD']" class="!mr-0" @click="operateCase(record, 'copy')"> <MsButton v-permission="['FUNCTIONAL_CASE:READ+ADD']" class="!mr-0" @click="operateCase(record, false)">
{{ t('caseManagement.featureCase.copy') }} {{ t('caseManagement.featureCase.copy') }}
</MsButton> </MsButton>
<a-divider <a-divider
@ -336,6 +336,7 @@
:table-data="propsRes.data" :table-data="propsRes.data"
:page-change="propsEvent.pageChange" :page-change="propsEvent.pageChange"
:pagination="propsRes.msPagination!" :pagination="propsRes.msPagination!"
:is-edit="isEdit"
@success="initData()" @success="initData()"
/> />
<AddDemandModal <AddDemandModal
@ -1014,18 +1015,36 @@
emitTableParams(); emitTableParams();
} }
const showDetailDrawer = ref(false);
const activeDetailId = ref<string>('');
const activeCaseIndex = ref<number>(0);
//
function showCaseDetail(id: string, index: number) {
activeDetailId.value = id;
activeCaseIndex.value = index;
showDetailDrawer.value = true;
}
const isEdit = ref<boolean>(false);
// & // &
function operateCase(record: CaseManagementTable, mode: string) { function operateCase(record: CaseManagementTable, operateType: boolean) {
// TODO
isEdit.value = operateType;
if (operateType) {
const index = propsRes.value.data.findIndex((item) => item.id === record.id);
showCaseDetail(record.id, index);
} else {
router.push({ router.push({
name: CaseManagementRouteEnum.CASE_MANAGEMENT_CASE_DETAIL, name: CaseManagementRouteEnum.CASE_MANAGEMENT_CASE_DETAIL,
query: { query: {
id: record.id, id: record.id,
}, },
params: { params: {
mode, mode: operateType ? 'edit' : 'copy',
}, },
}); });
} }
}
// //
function deleteCase(record: CaseManagementTable) { function deleteCase(record: CaseManagementTable) {
@ -1446,19 +1465,10 @@
resetSelector(); resetSelector();
initData(); initData();
} }
const showDetailDrawer = ref(false);
const activeDetailId = ref<string>('');
const activeCaseIndex = ref<number>(0);
//
function showCaseDetail(id: string, index: number) {
activeDetailId.value = id;
activeCaseIndex.value = index;
showDetailDrawer.value = true;
}
function handleCellClick(record: TableData) { function handleCellClick(record: TableData) {
const index = propsRes.value.data.findIndex((item) => item.id === record.id); const index = propsRes.value.data.findIndex((item) => item.id === record.id);
isEdit.value = false;
showCaseDetail(record.id, index); showCaseDetail(record.id, index);
} }

View File

@ -305,9 +305,11 @@
formRules?: FormRuleItem[]; // formRules?: FormRuleItem[]; //
isTestPlan?: boolean; // isTestPlan?: boolean; //
isDisabledTestPlan?: boolean; // - isDisabledTestPlan?: boolean; // -
isEdit?: boolean; //
}>(), }>(),
{ {
allowEdit: true, // allowEdit: true, //
isEdit: false,
} }
); );
@ -344,7 +346,7 @@
}, },
]); ]);
const isEditPreposition = ref<boolean>(false); // const isEditPreposition = ref<boolean>(props.isEdit); //
// //
const handleSelectType = (value: string | number | Record<string, any> | undefined) => { const handleSelectType = (value: string | number | Record<string, any> | undefined) => {