fix(测试用例&测试计划): 用例详情-修复自动下一条顺序不对的缺陷

--bug=1045034 --user=吕梦园
https://www.tapd.cn/55049933/bugtrace/bugs/view/1155049933001045034
This commit is contained in:
teukkk 2024-08-14 16:55:40 +08:00 committed by 刘瑞斌
parent 48897e83ac
commit d82745cd98
3 changed files with 344 additions and 297 deletions

View File

@ -86,192 +86,195 @@
</a-spin> </a-spin>
</div> </div>
<a-spin :loading="caseDetailLoading" class="relative flex flex-1 flex-col overflow-hidden"> <a-spin :loading="caseDetailLoading" class="relative flex flex-1 flex-col overflow-hidden">
<div class="content-center"> <MsEmpty v-if="!caseList.length" />
<div class="rounded-[var(--border-radius-small)] bg-[var(--color-text-n9)] p-[16px]"> <template v-else>
<div class="mb-[12px] flex items-center"> <div class="content-center">
<div class="mr-[16px] flex-1 overflow-hidden"> <div class="rounded-[var(--border-radius-small)] bg-[var(--color-text-n9)] p-[16px]">
<a-tooltip :content="`【${caseDetail.num}】${caseDetail.name}`"> <div class="mb-[12px] flex items-center">
<div <div class="mr-[16px] flex-1 overflow-hidden">
class="one-line-text w-[fit-content] max-w-[100%] cursor-pointer font-medium text-[rgb(var(--primary-5))]" <a-tooltip :content="`【${caseDetail.num}】${caseDetail.name}`">
@click="goCaseDetail" <div
> class="one-line-text w-[fit-content] max-w-[100%] cursor-pointer font-medium text-[rgb(var(--primary-5))]"
{{ caseDetail.num }}{{ caseDetail.name }} @click="goCaseDetail"
>
{{ caseDetail.num }}{{ caseDetail.name }}
</div>
</a-tooltip>
</div>
<a-button
v-permission="['FUNCTIONAL_CASE:READ+UPDATE']"
type="outline"
size="mini"
class="arco-btn-outline--secondary"
@click="editCaseVisible = true"
>
{{ t('common.edit') }}
</a-button>
</div>
<div class="flex items-center">
<MsIcon type="icon-icon_folder_filled1" class="mr-[4px] text-[var(--color-text-4)]" />
<a-tooltip :content="caseDetail.moduleName || t('common.root')">
<div class="one-line-text mr-[8px] max-w-[300px] font-medium text-[var(--color-text-000)]">
{{ caseDetail.moduleName || t('common.root') }}
</div> </div>
</a-tooltip> </a-tooltip>
</div> <div class="case-detail-label">
<a-button {{ t('caseManagement.caseReview.caseLevel') }}
v-permission="['FUNCTIONAL_CASE:READ+UPDATE']"
type="outline"
size="mini"
class="arco-btn-outline--secondary"
@click="editCaseVisible = true"
>
{{ t('common.edit') }}
</a-button>
</div>
<div class="flex items-center">
<MsIcon type="icon-icon_folder_filled1" class="mr-[4px] text-[var(--color-text-4)]" />
<a-tooltip :content="caseDetail.moduleName || t('common.root')">
<div class="one-line-text mr-[8px] max-w-[300px] font-medium text-[var(--color-text-000)]">
{{ caseDetail.moduleName || t('common.root') }}
</div> </div>
</a-tooltip> <div class="case-detail-value">
<div class="case-detail-label"> <caseLevel :case-level="caseDetailLevel" />
{{ t('caseManagement.caseReview.caseLevel') }} </div>
</div> <!-- <div class="case-detail-label">
<div class="case-detail-value">
<caseLevel :case-level="caseDetailLevel" />
</div>
<!-- <div class="case-detail-label">
{{ t('caseManagement.caseReview.caseVersion') }} {{ t('caseManagement.caseReview.caseVersion') }}
</div> </div>
<div class="case-detail-value"> <div class="case-detail-value">
<MsIcon type="icon-icon_version" size="13" class="mr-[4px]" /> <MsIcon type="icon-icon_version" size="13" class="mr-[4px]" />
{{ caseDetail.versionName }} {{ caseDetail.versionName }}
</div> --> </div> -->
<div class="case-detail-label"> <div class="case-detail-label">
{{ t('caseManagement.caseReview.reviewResult') }} {{ t('caseManagement.caseReview.reviewResult') }}
</div> </div>
<div class="case-detail-value"> <div class="case-detail-value">
<ReviewStatusTrigger v-if="reviewDetail.reviewPassRule === 'MULTIPLE'" ref="reviewStatusTriggerRef" /> <ReviewStatusTrigger v-if="reviewDetail.reviewPassRule === 'MULTIPLE'" ref="reviewStatusTriggerRef" />
<div <div
v-if="reviewResultMap[activeCaseReviewStatus as ReviewResult] && reviewDetail.reviewPassRule !== 'MULTIPLE'" v-if="reviewResultMap[activeCaseReviewStatus as ReviewResult] && reviewDetail.reviewPassRule !== 'MULTIPLE'"
class="flex items-center gap-[4px]" class="flex items-center gap-[4px]"
> >
<MsIcon <MsIcon
:type="reviewResultMap[activeCaseReviewStatus as ReviewResult].icon" :type="reviewResultMap[activeCaseReviewStatus as ReviewResult].icon"
:style="{ :style="{
color: reviewResultMap[activeCaseReviewStatus as ReviewResult].color, color: reviewResultMap[activeCaseReviewStatus as ReviewResult].color,
}" }"
/> />
{{ t(reviewResultMap[activeCaseReviewStatus as ReviewResult].label) }} {{ t(reviewResultMap[activeCaseReviewStatus as ReviewResult].label) }}
</div>
</div> </div>
</div> </div>
</div> </div>
</div> <a-tabs v-model:active-key="showTab" class="no-content">
<a-tabs v-model:active-key="showTab" class="no-content"> <a-tab-pane :key="tabList[0].key" :title="tabList[0].title" />
<a-tab-pane :key="tabList[0].key" :title="tabList[0].title" /> <a-tab-pane :key="tabList[1].key" :title="tabList[1].title" />
<a-tab-pane :key="tabList[1].key" :title="tabList[1].title" /> <a-tab-pane :key="tabList[2].key">
<a-tab-pane :key="tabList[2].key"> <template #title>
<template #title> <div class="flex items-center">
<div class="flex items-center"> {{ tabList[2].title }}
{{ tabList[2].title }} <div
<div v-if="caseDetail.demandCount > 0"
v-if="caseDetail.demandCount > 0" style="min-width: 16px; text-align: center; align-content: center"
style="min-width: 16px; text-align: center; align-content: center" :class="`ml-[4px] h-[16px] rounded-full ${
:class="`ml-[4px] h-[16px] rounded-full ${ showTab === tabList[2].key
showTab === tabList[2].key ? 'bg-[rgb(var(--primary-9))] text-[rgb(var(--primary-5))]'
? 'bg-[rgb(var(--primary-9))] text-[rgb(var(--primary-5))]' : 'bg-[var(--color-text-brand)] text-white'
: 'bg-[var(--color-text-brand)] text-white' } px-[4px] text-[12px]`"
} px-[4px] text-[12px]`" >
> {{ caseDetail.demandCount > 99 ? '99+' : caseDetail.demandCount }}
{{ caseDetail.demandCount > 99 ? '99+' : caseDetail.demandCount }} </div>
</div> </div>
</div> </template>
</template> </a-tab-pane>
</a-tab-pane> <a-tab-pane :key="tabList[3].key" :title="tabList[3].title" />
<a-tab-pane :key="tabList[3].key" :title="tabList[3].title" /> </a-tabs>
</a-tabs> <a-divider class="my-0" />
<a-divider class="my-0" /> <MsDescription
<MsDescription v-if="showTab === 'baseInfo'"
v-if="showTab === 'baseInfo'" :descriptions="descriptions"
:descriptions="descriptions" label-width="90px"
label-width="90px" class="mt-[16px]"
class="mt-[16px]" />
/> <div v-else-if="showTab === 'detail'" class="mt-[16px] h-full">
<div v-else-if="showTab === 'detail'" class="mt-[16px] h-full"> <caseTabDetail :form="caseDetail" :allow-edit="false" />
<caseTabDetail :form="caseDetail" :allow-edit="false" /> </div>
</div> <div v-else-if="showTab === 'demand'">
<div v-else-if="showTab === 'demand'"> <div class="mt-[16px] flex items-center justify-between">
<div class="mt-[16px] flex items-center justify-between"> {{ t('caseManagement.caseReview.demandCases') }}
{{ t('caseManagement.caseReview.demandCases') }} <a-input-search
<a-input-search v-model="demandKeyword"
v-model="demandKeyword" :placeholder="t('caseManagement.caseReview.demandSearchPlaceholder')"
:placeholder="t('caseManagement.caseReview.demandSearchPlaceholder')" allow-clear
allow-clear class="w-[300px]"
class="w-[300px]" @press-enter="searchDemand"
@press-enter="searchDemand" @search="searchDemand"
@search="searchDemand" @clear="searchDemand"
@clear="searchDemand" />
</div>
<caseTabDemand
ref="caseDemandRef"
:fun-params="{ projectId: appStore.currentProjectId, caseId: activeCaseId, keyword: demandKeyword }"
:show-empty="false"
/> />
</div> </div>
<caseTabDemand <div v-else class="flex flex-1 flex-col overflow-hidden pl-[16px] pt-[16px]">
ref="caseDemandRef" <div class="ms-comment-list">
:fun-params="{ projectId: appStore.currentProjectId, caseId: activeCaseId, keyword: demandKeyword }" <a-spin :loading="reviewHistoryListLoading" class="h-full w-full">
:show-empty="false" <div v-for="item of reviewHistoryList" :key="item.id" class="ms-comment-list-item">
/> <MSAvatar :avatar="item.userLogo" />
</div> <div class="flex-1 overflow-hidden">
<div v-else class="flex flex-1 flex-col overflow-hidden pl-[16px] pt-[16px]"> <div class="flex items-center gap-[8px]">
<div class="ms-comment-list"> <div class="flex-1 overflow-hidden">
<a-spin :loading="reviewHistoryListLoading" class="h-full w-full"> <a-tooltip :content="item.userName">
<div v-for="item of reviewHistoryList" :key="item.id" class="ms-comment-list-item"> <div
<MSAvatar :avatar="item.userLogo" /> class="one-line-text w-[fit-content] max-w-[100%] font-medium text-[var(--color-text-1)]"
<div class="flex-1 overflow-hidden"> >
<div class="flex items-center gap-[8px]"> {{ item.userName }}
<div class="flex-1 overflow-hidden"> </div>
<a-tooltip :content="item.userName"> </a-tooltip>
<div </div>
class="one-line-text w-[fit-content] max-w-[100%] font-medium text-[var(--color-text-1)]" <div v-if="item.status === 'PASS'" class="flex items-center">
> <MsIcon type="icon-icon_succeed_filled" class="mr-[4px] text-[rgb(var(--success-6))]" />
{{ item.userName }} {{ t('caseManagement.caseReview.pass') }}
</div> </div>
</a-tooltip> <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(--warning-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> </div>
<div v-if="item.status === 'PASS'" class="flex items-center"> <div class="markdown-body ml-[48px]" v-html="item.contentText"></div>
<MsIcon type="icon-icon_succeed_filled" class="mr-[4px] text-[rgb(var(--success-6))]" /> <div class="mt-[8px] text-[12px] leading-[16px] text-[var(--color-text-4)]">
{{ t('caseManagement.caseReview.pass') }} {{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</div> </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(--warning-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>
<div class="markdown-body ml-[48px]" v-html="item.contentText"></div>
<div class="mt-[8px] text-[12px] leading-[16px] text-[var(--color-text-4)]">
{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</div> </div>
</div> </div>
</div> <MsEmpty v-if="reviewHistoryList.length === 0" />
<MsEmpty v-if="reviewHistoryList.length === 0" /> </a-spin>
</a-spin> </div>
</div> </div>
</div> </div>
</div> <div class="content-footer">
<div class="content-footer"> <div class="mb-[12px] flex items-center justify-between">
<div class="mb-[12px] flex items-center justify-between"> <div class="font-medium text-[var(--color-text-1)]">
<div class="font-medium text-[var(--color-text-1)]"> {{ t('caseManagement.caseReview.startReview') }}
{{ t('caseManagement.caseReview.startReview') }} </div>
</div> <div class="flex items-center">
<div class="flex items-center"> <a-switch v-model:model-value="autoNext" class="mx-[8px]" size="small" type="line" />
<a-switch v-model:model-value="autoNext" class="mx-[8px]" size="small" type="line" /> <div class="text-[var(--color-text-4)]">{{ t('caseManagement.caseReview.autoNext') }}</div>
<div class="text-[var(--color-text-4)]">{{ t('caseManagement.caseReview.autoNext') }}</div> <a-tooltip position="right">
<a-tooltip position="right"> <template #content>
<template #content> <div>{{ t('caseManagement.caseReview.autoNextTip1') }}</div>
<div>{{ t('caseManagement.caseReview.autoNextTip1') }}</div> <div>{{ t('caseManagement.caseReview.autoNextTip2') }}</div>
<div>{{ t('caseManagement.caseReview.autoNextTip2') }}</div> </template>
</template> <icon-question-circle
<icon-question-circle class="mb-[2px] ml-[4px] text-[var(--color-text-4)] hover:text-[rgb(var(--primary-5))]"
class="mb-[2px] ml-[4px] text-[var(--color-text-4)] hover:text-[rgb(var(--primary-5))]" size="16"
size="16" />
/> </a-tooltip>
</a-tooltip> </div>
</div> </div>
<reviewForm
:review-id="reviewId"
:case-id="activeCaseId"
:review-pass-rule="reviewDetail.reviewPassRule"
@done="reviewDone"
/>
</div> </div>
<reviewForm </template>
:review-id="reviewId"
:case-id="activeCaseId"
:review-pass-rule="reviewDetail.reviewPassRule"
@done="reviewDone"
/>
</div>
</a-spin> </a-spin>
</div> </div>
</MsCard> </MsCard>
@ -524,6 +527,7 @@
watch( watch(
() => activeCaseId.value, () => activeCaseId.value,
() => { () => {
console.log('🤔️ =>', activeCaseId.value);
loadCaseDetail(); loadCaseDetail();
initReviewerAndStatus(); initReviewerAndStatus();
initReviewHistoryList(); initReviewHistoryList();
@ -546,10 +550,28 @@
); );
} }
async function reviewDone() { async function reviewDone(status: string) {
if (autoNext.value) { if (autoNext.value) {
// id // id
const index = caseList.value.findIndex((e) => e.caseId === activeCaseId.value); const index = caseList.value.findIndex((e) => e.caseId === activeCaseId.value);
//
const oneMissingCase = type.value !== '' && status !== type.value;
if (oneMissingCase) {
if ((pageNation.value.current - 1) * pageNation.value.pageSize + index + 1 < pageNation.value.total) {
//
await loadCaseList();
activeCaseId.value = caseList.value[index].caseId;
} else {
//
await loadCaseList();
if (caseList.value.length > 1) {
activeCaseId.value = caseList.value[0].caseId;
}
}
return;
}
if (index < caseList.value.length - 1) { if (index < caseList.value.length - 1) {
await loadCaseList(); await loadCaseList();
activeCaseId.value = caseList.value[index + 1].caseId; activeCaseId.value = caseList.value[index + 1].caseId;

View File

@ -178,7 +178,7 @@
if (typeof done === 'function') { if (typeof done === 'function') {
done(true); done(true);
} }
emit('done'); emit('done', params.status);
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);

View File

@ -59,151 +59,156 @@
</div> </div>
<!-- 右侧 --> <!-- 右侧 -->
<a-spin :loading="caseDetailLoading" class="relative flex h-full flex-1 flex-col overflow-hidden"> <a-spin :loading="caseDetailLoading" class="relative flex h-full flex-1 flex-col overflow-hidden">
<div class="flex px-[16px] pt-[16px]"> <MsEmpty v-if="!caseList.length" />
<div class="mr-[24px] flex flex-1 items-center overflow-hidden"> <template v-else>
<MsStatusTag :status="caseDetail.lastExecuteResult || 'PREPARED'" /> <div class="flex px-[16px] pt-[16px]">
<div class="ml-[8px] mr-[2px] cursor-pointer font-medium text-[rgb(var(--primary-5))]" @click="goCaseDetail" <div class="mr-[24px] flex flex-1 items-center overflow-hidden">
>[{{ caseDetail.num }}]</div <MsStatusTag :status="caseDetail.lastExecuteResult || 'PREPARED'" />
> <div
<div class="flex-1 overflow-hidden"> class="ml-[8px] mr-[2px] cursor-pointer font-medium text-[rgb(var(--primary-5))]"
<a-tooltip :content="caseDetail.name"> @click="goCaseDetail"
<div class="one-line-text w-[fit-content] max-w-[100%] font-medium"> >[{{ caseDetail.num }}]</div
{{ caseDetail.name }} >
</div> <div class="flex-1 overflow-hidden">
</a-tooltip> <a-tooltip :content="caseDetail.name">
<div class="one-line-text w-[fit-content] max-w-[100%] font-medium">
{{ caseDetail.name }}
</div>
</a-tooltip>
</div>
</div> </div>
</div> <a-button
<a-button v-if="canEdit && hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE'])"
v-if="canEdit && hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE'])" type="outline"
type="outline" @click="editCaseVisible = true"
@click="editCaseVisible = true" >{{ t('common.edit') }}</a-button
>{{ t('common.edit') }}</a-button
>
</div>
<MsTab
v-model:active-key="activeTab"
:content-tab-list="contentTabList"
no-content
:get-text-func="getTotal"
class="relative mx-[16px] border-b"
/>
<div :class="[' flex-1', activeTab !== 'detail' ? 'tab-content' : 'overflow-hidden']">
<MsDescription v-if="activeTab === 'baseInfo'" :descriptions="descriptions" :column="2" one-line-value>
<template #value="{ item }">
<template v-if="item.key === 'reviewStatus'">
<MsIcon
:type="statusIconMap[item.value as keyof typeof statusIconMap]?.icon || ''"
class="mr-1"
:class="[statusIconMap[item.value as keyof typeof statusIconMap].color]"
></MsIcon>
<span>{{ statusIconMap[item.value as keyof typeof statusIconMap]?.statusText || '' }} </span>
</template>
</template>
</MsDescription>
<div v-else-if="activeTab === 'detail'" class="align-content-start flex h-full flex-col">
<CaseTabDetail ref="caseTabDetailRef" is-test-plan :form="caseDetail" :is-disabled-test-plan="!canEdit" />
<!-- 开始执行 -->
<div
v-if="canEdit && hasAnyPermission(['PROJECT_TEST_PLAN:READ+EXECUTE'])"
class="z-[101] px-[16px] py-[8px] shadow-[0_-1px_4px_rgba(2,2,2,0.1)]"
> >
<div class="mb-[12px] flex items-center justify-between"> </div>
<div class="font-medium text-[var(--color-text-1)]"> <MsTab
{{ t('testPlan.featureCase.startExecution') }} v-model:active-key="activeTab"
</div> :content-tab-list="contentTabList"
<div class="flex items-center"> no-content
<a-switch v-model:model-value="autoNext" size="small" /> :get-text-func="getTotal"
<div class="mx-[8px]">{{ t('caseManagement.caseReview.autoNext') }}</div> class="relative mx-[16px] border-b"
<a-tooltip position="top"> />
<template #content> <div :class="[' flex-1', activeTab !== 'detail' ? 'tab-content' : 'overflow-hidden']">
<div>{{ t('testPlan.featureCase.autoNextTip1') }}</div> <MsDescription v-if="activeTab === 'baseInfo'" :descriptions="descriptions" :column="2" one-line-value>
<div>{{ t('testPlan.featureCase.autoNextTip2') }}</div> <template #value="{ item }">
</template> <template v-if="item.key === 'reviewStatus'">
<icon-question-circle <MsIcon
class="text-[var(--color-text-brand)] hover:text-[rgb(var(--primary-4))]" :type="statusIconMap[item.value as keyof typeof statusIconMap]?.icon || ''"
size="16" class="mr-1"
/> :class="[statusIconMap[item.value as keyof typeof statusIconMap].color]"
</a-tooltip> ></MsIcon>
<MsTag type="danger" theme="light" size="medium" class="ml-4"> <span>{{ statusIconMap[item.value as keyof typeof statusIconMap]?.statusText || '' }} </span>
<MsIcon type="icon-icon_defect" class="!text-[14px] text-[rgb(var(--danger-6))]" size="16" /> </template>
<span class="ml-1 text-[rgb(var(--danger-6))]"> {{ t('testPlan.featureCase.bug') }}</span> </template>
<span class="ml-1 text-[rgb(var(--danger-6))]">{{ caseDetail.bugListCount }}</span> </MsDescription>
</MsTag> <div v-else-if="activeTab === 'detail'" class="align-content-start flex h-full flex-col">
<a-dropdown @select="handleSelect"> <CaseTabDetail ref="caseTabDetailRef" is-test-plan :form="caseDetail" :is-disabled-test-plan="!canEdit" />
<a-button <!-- 开始执行 -->
v-if="hasAllPermission(['PROJECT_BUG:READ', 'PROJECT_TEST_PLAN:READ+EXECUTE'])" <div
type="outline" v-if="canEdit && hasAnyPermission(['PROJECT_TEST_PLAN:READ+EXECUTE'])"
size="small" class="z-[101] px-[16px] py-[8px] shadow-[0_-1px_4px_rgba(2,2,2,0.1)]"
class="ml-1" >
> <div class="mb-[12px] flex items-center justify-between">
<template #icon> <icon-plus class="text-[12px]" /> </template> <div class="font-medium text-[var(--color-text-1)]">
</a-button> {{ t('testPlan.featureCase.startExecution') }}
<template #content> </div>
<a-doption <div class="flex items-center">
v-permission="['PROJECT_BUG:READ+ADD']" <a-switch v-model:model-value="autoNext" size="small" />
:disabled="!hasAnyPermission(['PROJECT_BUG:READ+ADD'])" <div class="mx-[8px]">{{ t('caseManagement.caseReview.autoNext') }}</div>
value="new" <a-tooltip position="top">
>{{ t('common.newCreate') }}</a-doption <template #content>
<div>{{ t('testPlan.featureCase.autoNextTip1') }}</div>
<div>{{ t('testPlan.featureCase.autoNextTip2') }}</div>
</template>
<icon-question-circle
class="text-[var(--color-text-brand)] hover:text-[rgb(var(--primary-4))]"
size="16"
/>
</a-tooltip>
<MsTag type="danger" theme="light" size="medium" class="ml-4">
<MsIcon type="icon-icon_defect" class="!text-[14px] text-[rgb(var(--danger-6))]" size="16" />
<span class="ml-1 text-[rgb(var(--danger-6))]"> {{ t('testPlan.featureCase.bug') }}</span>
<span class="ml-1 text-[rgb(var(--danger-6))]">{{ caseDetail.bugListCount }}</span>
</MsTag>
<a-dropdown @select="handleSelect">
<a-button
v-if="hasAllPermission(['PROJECT_BUG:READ', 'PROJECT_TEST_PLAN:READ+EXECUTE'])"
type="outline"
size="small"
class="ml-1"
> >
<a-doption <template #icon> <icon-plus class="text-[12px]" /> </template>
v-if="createdBugCount > 0 && hasAnyPermission(['PROJECT_BUG:READ'])" </a-button>
:disabled="!hasAnyPermission(['PROJECT_BUG:READ'])" <template #content>
value="link"
>{{ t('common.associated') }}</a-doption
>
<a-popover v-else title="" position="left">
<a-doption <a-doption
v-if="createdBugCount < 1 && hasAnyPermission(['PROJECT_BUG:READ'])" v-permission="['PROJECT_BUG:READ+ADD']"
:disabled="!hasAnyPermission(['PROJECT_BUG:READ+ADD'])"
value="new"
>{{ t('common.newCreate') }}</a-doption
>
<a-doption
v-if="createdBugCount > 0 && hasAnyPermission(['PROJECT_BUG:READ'])"
:disabled="!hasAnyPermission(['PROJECT_BUG:READ'])" :disabled="!hasAnyPermission(['PROJECT_BUG:READ'])"
value="link" value="link"
>{{ t('common.associated') }}</a-doption >{{ t('common.associated') }}</a-doption
> >
<template #content> <a-popover v-else title="" position="left">
<div class="flex items-center text-[14px]"> <a-doption
<span class="text-[var(--color-text-4)]">{{ v-if="createdBugCount < 1 && hasAnyPermission(['PROJECT_BUG:READ'])"
t('testPlan.featureCase.noBugDataTooltip') :disabled="!hasAnyPermission(['PROJECT_BUG:READ'])"
}}</span> value="link"
<MsButton >{{ t('common.associated') }}</a-doption
:disabled="!hasAnyPermission(['PROJECT_BUG:READ+ADD'])" >
type="text" <template #content>
@click="handleSelect('new')" <div class="flex items-center text-[14px]">
> <span class="text-[var(--color-text-4)]">{{
{{ t('testPlan.featureCase.noBugDataNewBug') }} t('testPlan.featureCase.noBugDataTooltip')
</MsButton> }}</span>
</div> <MsButton
</template> :disabled="!hasAnyPermission(['PROJECT_BUG:READ+ADD'])"
</a-popover> type="text"
</template> @click="handleSelect('new')"
</a-dropdown> >
{{ t('testPlan.featureCase.noBugDataNewBug') }}
</MsButton>
</div>
</template>
</a-popover>
</template>
</a-dropdown>
</div>
</div> </div>
<ExecuteSubmit
:id="activeId"
:case-id="activeCaseId"
:test-plan-id="route.query.id as string"
:step-execution-result="stepExecutionResult"
@done="executeDone"
/>
</div> </div>
<ExecuteSubmit
:id="activeId"
:case-id="activeCaseId"
:test-plan-id="route.query.id as string"
:step-execution-result="stepExecutionResult"
@done="executeDone"
/>
</div> </div>
<BugList
v-if="activeTab === 'defectList'"
ref="bugRef"
:case-id="activeCaseId"
:test-plan-case-id="activeId"
:can-edit="canEdit && hasAnyPermission(['PROJECT_TEST_PLAN:READ+EXECUTE'])"
@link="linkDefect"
@new="addBug"
@update-count="loadCaseDetail()"
/>
<ExecutionHistory
v-if="activeTab === 'executionHistory'"
:execute-list="executeHistoryList"
:loading="executeLoading"
height="h-[calc(100vh-240px)]"
show-step-detail-trigger
/>
</div> </div>
<BugList </template>
v-if="activeTab === 'defectList'"
ref="bugRef"
:case-id="activeCaseId"
:test-plan-case-id="activeId"
:can-edit="canEdit && hasAnyPermission(['PROJECT_TEST_PLAN:READ+EXECUTE'])"
@link="linkDefect"
@new="addBug"
@update-count="loadCaseDetail()"
/>
<ExecutionHistory
v-if="activeTab === 'executionHistory'"
:execute-list="executeHistoryList"
:loading="executeLoading"
height="h-[calc(100vh-240px)]"
show-step-detail-trigger
/>
</div>
</a-spin> </a-spin>
</div> </div>
</MsCard> </MsCard>
@ -451,10 +456,30 @@
}); });
}); });
const autoNext = ref(true); const autoNext = ref(true);
async function executeDone() { async function executeDone(status: LastExecuteResults) {
if (autoNext.value) { if (autoNext.value) {
// id // id
const index = caseList.value.findIndex((e) => e.id === activeId.value); const index = caseList.value.findIndex((e) => e.id === activeId.value);
//
const oneMissingCase = lastExecResult.value !== '' && status !== lastExecResult.value;
if (oneMissingCase) {
if ((pageNation.value.current - 1) * pageNation.value.pageSize + index + 1 < pageNation.value.total) {
//
await loadCaseList();
activeCaseId.value = caseList.value[index].caseId;
activeId.value = caseList.value[index].id;
} else {
//
await loadCaseList();
if (caseList.value.length > 1) {
activeCaseId.value = caseList.value[0].caseId;
activeId.value = caseList.value[0].id;
}
}
return;
}
if (index < caseList.value.length - 1) { if (index < caseList.value.length - 1) {
await loadCaseList(); await loadCaseList();
activeCaseId.value = caseList.value[index + 1].caseId; activeCaseId.value = caseList.value[index + 1].caseId;