feat(测试计划): 测试计划详情-修改功能用例步骤执行结果

This commit is contained in:
teukkk 2024-05-16 13:03:35 +08:00 committed by Craftsman
parent f24b54c9e9
commit 7db01bf024
9 changed files with 84 additions and 8 deletions

View File

@ -86,8 +86,12 @@ export interface StepList {
expected: string; // 预期
showStep: boolean; // 编辑步骤模式
showExpected: boolean; // 编辑预期模式
actualResult?: string; // 实际
executeResult?: string; // 步骤执行结果
}
export type StepExecutionResult = Pick<StepList, 'actualResult' | 'executeResult'>;
// 关联文件列表
export interface AssociatedList {
id: string;

View File

@ -28,8 +28,31 @@
@blur="blurHandler(record, 'expected')"
/>
</template>
<template #actualResult="{ record }">
<a-textarea
v-model="record.actualResult"
:max-length="1000"
size="mini"
:auto-size="true"
class="w-max-[267px] param-input"
:placeholder="t('system.orgTemplate.actualResultTip')"
/>
</template>
<template #lastExecResult="{ record }">
<ExecuteResult :execute-result="record.executeResult" />
<a-select
v-if="hasAnyPermission(['PROJECT_TEST_PLAN:READ+EXECUTE'])"
v-model:model-value="record.executeResult"
:placeholder="t('common.pleaseSelect')"
class="param-input w-full"
>
<template #label>
<span class="text-[var(--color-text-2)]"><ExecuteResult :execute-result="record.executeResult" /></span>
</template>
<a-option v-for="item in executionResultList" :key="item.key" :value="item.key">
<ExecuteResult :execute-result="item.key" />
</a-option>
</a-select>
<span v-else class="text-[var(--color-text-2)]"><ExecuteResult :execute-result="record.executeResult" /></span>
</template>
<template #operation="{ record }">
<MsTableMoreAction
@ -60,10 +83,14 @@
import { useI18n } from '@/hooks/useI18n';
import { getGenerateId } from '@/utils';
import { hasAnyPermission } from '@/utils/permission';
import type { StepList } from '@/models/caseManagement/featureCase';
import { LastExecuteResults } from '@/enums/caseEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
import { executionResultMap } from '@/views/case-management/caseManagementFeature/components/utils';
type refItem = Element | ComponentPublicInstance | null;
const { t } = useI18n();
@ -82,6 +109,10 @@
const emit = defineEmits(['update:stepList']);
const executionResultList = computed(() =>
Object.values(executionResultMap).filter((item) => item.key !== LastExecuteResults.UN_EXECUTED)
);
//
const stepData = ref<StepList[]>([
{

View File

@ -745,6 +745,7 @@
defineExpose({
handleOK,
getParams,
stepData,
});
</script>

View File

@ -103,6 +103,7 @@ export default {
'system.orgTemplate.textDescription': 'Text description',
'system.orgTemplate.stepTip': 'Please enter steps',
'system.orgTemplate.expectationTip': 'Please enter expectations',
'system.orgTemplate.actualResultTip': 'Please enter actuality',
'system.orgTemplate.addAttachment': 'Add attachment',
'system.orgTemplate.addAttachmentTip': 'Support any type of file, the file size does not exceed 50MB',
'system.orgTemplate.enabledSuccessfully': 'Enabled successfully',

View File

@ -102,6 +102,7 @@ export default {
'system.orgTemplate.textDescription': '文本描述',
'system.orgTemplate.stepTip': '请输入步骤',
'system.orgTemplate.expectationTip': '请输入预期',
'system.orgTemplate.actualResultTip': '请输入实际',
'system.orgTemplate.addAttachment': '添加附件',
'system.orgTemplate.addAttachmentTip': '支持任意类型文件,文件大小不超过 50MB',
'system.orgTemplate.enabledSuccessfully': '启用成功',

View File

@ -103,6 +103,7 @@
import { ModuleTreeNode } from '@/models/common';
import type { PlanDetailFeatureCaseItem, PlanDetailFeatureCaseListQueryParams } from '@/models/testPlan/testPlan';
import { LastExecuteResults } from '@/enums/caseEnum';
import { TestPlanRouteEnum } from '@/enums/routeEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
@ -217,6 +218,7 @@
{
title: 'testPlan.featureCase.executor',
dataIndex: 'executeUserName',
showTooltip: true,
width: 150,
showDrag: true,
},
@ -243,6 +245,7 @@
(record) => {
return {
...record,
lastExecResult: record.lastExecResult ?? LastExecuteResults.UN_EXECUTED,
caseLevel: getCaseLevels(record.customFields),
moduleId: getModules(record.moduleId, props.moduleTree),
};

View File

@ -33,17 +33,18 @@
import { defaultExecuteForm } from '@/config/testPlan';
import type { ExecuteFeatureCaseFormParams } from '@/models/testPlan/testPlan';
import { LastExecuteResults } from '@/enums/caseEnum';
import { executionResultMap } from '@/views/case-management/caseManagementFeature/components/utils';
const form = defineModel<ExecuteFeatureCaseFormParams>('form', {
required: true,
default: () => ({ ...defaultExecuteForm }),
});
const formRef = ref<FormInstance>();
const executionResultList = computed(() =>
Object.values(executionResultMap).filter((item) => item.key !== 'UN_EXECUTED')
Object.values(executionResultMap).filter((item) => item.key !== LastExecuteResults.UN_EXECUTED)
);
async function handleUploadImage(file: File) {

View File

@ -38,12 +38,15 @@
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
import type { StepExecutionResult } from '@/models/caseManagement/featureCase';
import type { ExecuteFeatureCaseFormParams } from '@/models/testPlan/testPlan';
import { LastExecuteResults } from '@/enums/caseEnum';
const props = defineProps<{
caseId: string;
testPlanId: string;
id: string;
stepExecutionResult?: StepExecutionResult[];
}>();
const emit = defineEmits<{
@ -63,6 +66,21 @@
(form.value.content === '' || form.value.content?.trim() === '<p style=""></p>')
);
watch(
() => props.stepExecutionResult,
() => {
const executionResultList = props.stepExecutionResult?.map((item) => item.executeResult);
if (executionResultList?.includes(LastExecuteResults.FAILED)) {
form.value.lastExecResult = LastExecuteResults.FAILED;
} else if (executionResultList?.includes(LastExecuteResults.BLOCKED)) {
form.value.lastExecResult = LastExecuteResults.BLOCKED;
} else {
form.value.lastExecResult = LastExecuteResults.PASSED;
}
},
{ deep: true }
);
//
async function submit() {
try {
@ -73,6 +91,7 @@
testPlanId: props.testPlanId,
id: props.id,
lastExecResult: form.value.lastExecResult,
stepsExecResult: JSON.stringify(props.stepExecutionResult) ?? '',
content: form.value.content,
notifier: form.value?.commentIds?.join(';'),
};

View File

@ -32,12 +32,12 @@
<div
v-for="item of caseList"
:key="item.id"
:class="['case-item', caseDetail.id === item.caseId ? 'case-item--active' : '']"
:class="['case-item', activeId === item.id ? 'case-item--active' : '']"
@click="changeActiveCase(item)"
>
<div class="mb-[8px] flex items-center justify-between">
<div class="text-[var(--color-text-4)]">{{ item.num }}</div>
<ExecuteResult :execute-result="item.lastExecResult" />
<ExecuteResult :execute-result="item.lastExecResult ?? LastExecuteResults.UN_EXECUTED" />
</div>
<a-tooltip :content="item.name">
<div class="one-line-text">{{ item.name }}</div>
@ -88,7 +88,7 @@
<!-- TODO: 属性的样式 -->
<MsDescription v-if="activeTab === 'baseInfo'" :descriptions="descriptions" :column="2" />
<div v-else-if="activeTab === 'detail'" class="align-content-start flex h-full flex-col">
<CaseTabDetail is-test-plan :form="caseDetail" />
<CaseTabDetail ref="caseTabDetailRef" is-test-plan :form="caseDetail" />
<!-- 开始执行 -->
<div class="px-[16px] py-[8px] shadow-[0_-1px_4px_rgba(2,2,2,0.1)]">
<div class="mb-[12px] flex items-center justify-between">
@ -115,6 +115,7 @@
:id="activeId"
:case-id="activeCaseId"
:test-plan-id="route.query.id as string"
:step-execution-result="stepExecutionResult"
@done="executeDone"
/>
</div>
@ -153,6 +154,7 @@
import useAppStore from '@/store/modules/app';
import type { PlanDetailFeatureCaseItem, TestPlanDetail } from '@/models/testPlan/testPlan';
import { LastExecuteResults } from '@/enums/caseEnum';
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
import { executionResultMap, getCustomField } from '@/views/case-management/caseManagementFeature/components/utils';
@ -308,7 +310,7 @@
}
function changeActiveCase(item: PlanDetailFeatureCaseItem) {
if (activeCaseId.value !== item.caseId) {
if (activeId.value !== item.id) {
activeCaseId.value = item.caseId;
activeId.value = item.id;
}
@ -317,6 +319,7 @@
() => activeCaseId.value,
() => {
loadCaseDetail();
// TODO
}
);
@ -325,11 +328,21 @@
await loadCaseDetail();
}
const caseTabDetailRef = ref<InstanceType<typeof CaseTabDetail>>();
const stepExecutionResult = computed(() => {
const stepData = caseTabDetailRef.value?.stepData;
return stepData?.map((item) => {
return {
actualResult: item.actualResult,
executeResult: item.executeResult,
};
});
});
const autoNext = ref(true);
async function executeDone() {
if (autoNext.value) {
// id
const index = caseList.value.findIndex((e) => e.caseId === activeCaseId.value);
const index = caseList.value.findIndex((e) => e.id === activeId.value);
if (index < caseList.value.length - 1) {
await loadCaseList();
activeCaseId.value = caseList.value[index + 1].caseId;
@ -344,10 +357,12 @@
//
loadCaseDetail();
loadCaseList();
// TODO
}
} else {
//
loadCase();
// TODO
}
}