feat(测试计划): 测试计划详情-修改功能用例步骤执行结果
This commit is contained in:
parent
f24b54c9e9
commit
7db01bf024
|
@ -86,8 +86,12 @@ export interface StepList {
|
||||||
expected: string; // 预期
|
expected: string; // 预期
|
||||||
showStep: boolean; // 编辑步骤模式
|
showStep: boolean; // 编辑步骤模式
|
||||||
showExpected: boolean; // 编辑预期模式
|
showExpected: boolean; // 编辑预期模式
|
||||||
|
actualResult?: string; // 实际
|
||||||
|
executeResult?: string; // 步骤执行结果
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type StepExecutionResult = Pick<StepList, 'actualResult' | 'executeResult'>;
|
||||||
|
|
||||||
// 关联文件列表
|
// 关联文件列表
|
||||||
export interface AssociatedList {
|
export interface AssociatedList {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
|
@ -28,8 +28,31 @@
|
||||||
@blur="blurHandler(record, 'expected')"
|
@blur="blurHandler(record, 'expected')"
|
||||||
/>
|
/>
|
||||||
</template>
|
</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 }">
|
<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>
|
||||||
<template #operation="{ record }">
|
<template #operation="{ record }">
|
||||||
<MsTableMoreAction
|
<MsTableMoreAction
|
||||||
|
@ -60,10 +83,14 @@
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import { getGenerateId } from '@/utils';
|
import { getGenerateId } from '@/utils';
|
||||||
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
import type { StepList } from '@/models/caseManagement/featureCase';
|
import type { StepList } from '@/models/caseManagement/featureCase';
|
||||||
|
import { LastExecuteResults } from '@/enums/caseEnum';
|
||||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
|
|
||||||
|
import { executionResultMap } from '@/views/case-management/caseManagementFeature/components/utils';
|
||||||
|
|
||||||
type refItem = Element | ComponentPublicInstance | null;
|
type refItem = Element | ComponentPublicInstance | null;
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
@ -82,6 +109,10 @@
|
||||||
|
|
||||||
const emit = defineEmits(['update:stepList']);
|
const emit = defineEmits(['update:stepList']);
|
||||||
|
|
||||||
|
const executionResultList = computed(() =>
|
||||||
|
Object.values(executionResultMap).filter((item) => item.key !== LastExecuteResults.UN_EXECUTED)
|
||||||
|
);
|
||||||
|
|
||||||
// 步骤描述
|
// 步骤描述
|
||||||
const stepData = ref<StepList[]>([
|
const stepData = ref<StepList[]>([
|
||||||
{
|
{
|
||||||
|
|
|
@ -745,6 +745,7 @@
|
||||||
defineExpose({
|
defineExpose({
|
||||||
handleOK,
|
handleOK,
|
||||||
getParams,
|
getParams,
|
||||||
|
stepData,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,7 @@ export default {
|
||||||
'system.orgTemplate.textDescription': 'Text description',
|
'system.orgTemplate.textDescription': 'Text description',
|
||||||
'system.orgTemplate.stepTip': 'Please enter steps',
|
'system.orgTemplate.stepTip': 'Please enter steps',
|
||||||
'system.orgTemplate.expectationTip': 'Please enter expectations',
|
'system.orgTemplate.expectationTip': 'Please enter expectations',
|
||||||
|
'system.orgTemplate.actualResultTip': 'Please enter actuality',
|
||||||
'system.orgTemplate.addAttachment': 'Add attachment',
|
'system.orgTemplate.addAttachment': 'Add attachment',
|
||||||
'system.orgTemplate.addAttachmentTip': 'Support any type of file, the file size does not exceed 50MB',
|
'system.orgTemplate.addAttachmentTip': 'Support any type of file, the file size does not exceed 50MB',
|
||||||
'system.orgTemplate.enabledSuccessfully': 'Enabled successfully',
|
'system.orgTemplate.enabledSuccessfully': 'Enabled successfully',
|
||||||
|
|
|
@ -102,6 +102,7 @@ export default {
|
||||||
'system.orgTemplate.textDescription': '文本描述',
|
'system.orgTemplate.textDescription': '文本描述',
|
||||||
'system.orgTemplate.stepTip': '请输入步骤',
|
'system.orgTemplate.stepTip': '请输入步骤',
|
||||||
'system.orgTemplate.expectationTip': '请输入预期',
|
'system.orgTemplate.expectationTip': '请输入预期',
|
||||||
|
'system.orgTemplate.actualResultTip': '请输入实际',
|
||||||
'system.orgTemplate.addAttachment': '添加附件',
|
'system.orgTemplate.addAttachment': '添加附件',
|
||||||
'system.orgTemplate.addAttachmentTip': '支持任意类型文件,文件大小不超过 50MB',
|
'system.orgTemplate.addAttachmentTip': '支持任意类型文件,文件大小不超过 50MB',
|
||||||
'system.orgTemplate.enabledSuccessfully': '启用成功',
|
'system.orgTemplate.enabledSuccessfully': '启用成功',
|
||||||
|
|
|
@ -103,6 +103,7 @@
|
||||||
|
|
||||||
import { ModuleTreeNode } from '@/models/common';
|
import { ModuleTreeNode } from '@/models/common';
|
||||||
import type { PlanDetailFeatureCaseItem, PlanDetailFeatureCaseListQueryParams } from '@/models/testPlan/testPlan';
|
import type { PlanDetailFeatureCaseItem, PlanDetailFeatureCaseListQueryParams } from '@/models/testPlan/testPlan';
|
||||||
|
import { LastExecuteResults } from '@/enums/caseEnum';
|
||||||
import { TestPlanRouteEnum } from '@/enums/routeEnum';
|
import { TestPlanRouteEnum } from '@/enums/routeEnum';
|
||||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
|
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
|
||||||
|
@ -217,6 +218,7 @@
|
||||||
{
|
{
|
||||||
title: 'testPlan.featureCase.executor',
|
title: 'testPlan.featureCase.executor',
|
||||||
dataIndex: 'executeUserName',
|
dataIndex: 'executeUserName',
|
||||||
|
showTooltip: true,
|
||||||
width: 150,
|
width: 150,
|
||||||
showDrag: true,
|
showDrag: true,
|
||||||
},
|
},
|
||||||
|
@ -243,6 +245,7 @@
|
||||||
(record) => {
|
(record) => {
|
||||||
return {
|
return {
|
||||||
...record,
|
...record,
|
||||||
|
lastExecResult: record.lastExecResult ?? LastExecuteResults.UN_EXECUTED,
|
||||||
caseLevel: getCaseLevels(record.customFields),
|
caseLevel: getCaseLevels(record.customFields),
|
||||||
moduleId: getModules(record.moduleId, props.moduleTree),
|
moduleId: getModules(record.moduleId, props.moduleTree),
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,17 +33,18 @@
|
||||||
import { defaultExecuteForm } from '@/config/testPlan';
|
import { defaultExecuteForm } from '@/config/testPlan';
|
||||||
|
|
||||||
import type { ExecuteFeatureCaseFormParams } from '@/models/testPlan/testPlan';
|
import type { ExecuteFeatureCaseFormParams } from '@/models/testPlan/testPlan';
|
||||||
|
import { LastExecuteResults } from '@/enums/caseEnum';
|
||||||
|
|
||||||
import { executionResultMap } from '@/views/case-management/caseManagementFeature/components/utils';
|
import { executionResultMap } from '@/views/case-management/caseManagementFeature/components/utils';
|
||||||
|
|
||||||
const form = defineModel<ExecuteFeatureCaseFormParams>('form', {
|
const form = defineModel<ExecuteFeatureCaseFormParams>('form', {
|
||||||
required: true,
|
default: () => ({ ...defaultExecuteForm }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
const executionResultList = computed(() =>
|
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) {
|
async function handleUploadImage(file: File) {
|
||||||
|
|
|
@ -38,12 +38,15 @@
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
|
|
||||||
|
import type { StepExecutionResult } from '@/models/caseManagement/featureCase';
|
||||||
import type { ExecuteFeatureCaseFormParams } from '@/models/testPlan/testPlan';
|
import type { ExecuteFeatureCaseFormParams } from '@/models/testPlan/testPlan';
|
||||||
|
import { LastExecuteResults } from '@/enums/caseEnum';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
caseId: string;
|
caseId: string;
|
||||||
testPlanId: string;
|
testPlanId: string;
|
||||||
id: string;
|
id: string;
|
||||||
|
stepExecutionResult?: StepExecutionResult[];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -63,6 +66,21 @@
|
||||||
(form.value.content === '' || form.value.content?.trim() === '<p style=""></p>')
|
(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() {
|
async function submit() {
|
||||||
try {
|
try {
|
||||||
|
@ -73,6 +91,7 @@
|
||||||
testPlanId: props.testPlanId,
|
testPlanId: props.testPlanId,
|
||||||
id: props.id,
|
id: props.id,
|
||||||
lastExecResult: form.value.lastExecResult,
|
lastExecResult: form.value.lastExecResult,
|
||||||
|
stepsExecResult: JSON.stringify(props.stepExecutionResult) ?? '',
|
||||||
content: form.value.content,
|
content: form.value.content,
|
||||||
notifier: form.value?.commentIds?.join(';'),
|
notifier: form.value?.commentIds?.join(';'),
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,12 +32,12 @@
|
||||||
<div
|
<div
|
||||||
v-for="item of caseList"
|
v-for="item of caseList"
|
||||||
:key="item.id"
|
: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)"
|
@click="changeActiveCase(item)"
|
||||||
>
|
>
|
||||||
<div class="mb-[8px] flex items-center justify-between">
|
<div class="mb-[8px] flex items-center justify-between">
|
||||||
<div class="text-[var(--color-text-4)]">{{ item.num }}</div>
|
<div class="text-[var(--color-text-4)]">{{ item.num }}</div>
|
||||||
<ExecuteResult :execute-result="item.lastExecResult" />
|
<ExecuteResult :execute-result="item.lastExecResult ?? LastExecuteResults.UN_EXECUTED" />
|
||||||
</div>
|
</div>
|
||||||
<a-tooltip :content="item.name">
|
<a-tooltip :content="item.name">
|
||||||
<div class="one-line-text">{{ item.name }}</div>
|
<div class="one-line-text">{{ item.name }}</div>
|
||||||
|
@ -88,7 +88,7 @@
|
||||||
<!-- TODO: 属性的样式 -->
|
<!-- TODO: 属性的样式 -->
|
||||||
<MsDescription v-if="activeTab === 'baseInfo'" :descriptions="descriptions" :column="2" />
|
<MsDescription v-if="activeTab === 'baseInfo'" :descriptions="descriptions" :column="2" />
|
||||||
<div v-else-if="activeTab === 'detail'" class="align-content-start flex h-full flex-col">
|
<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="px-[16px] py-[8px] shadow-[0_-1px_4px_rgba(2,2,2,0.1)]">
|
||||||
<div class="mb-[12px] flex items-center justify-between">
|
<div class="mb-[12px] flex items-center justify-between">
|
||||||
|
@ -115,6 +115,7 @@
|
||||||
:id="activeId"
|
:id="activeId"
|
||||||
:case-id="activeCaseId"
|
:case-id="activeCaseId"
|
||||||
:test-plan-id="route.query.id as string"
|
:test-plan-id="route.query.id as string"
|
||||||
|
:step-execution-result="stepExecutionResult"
|
||||||
@done="executeDone"
|
@done="executeDone"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -153,6 +154,7 @@
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
|
|
||||||
import type { PlanDetailFeatureCaseItem, TestPlanDetail } from '@/models/testPlan/testPlan';
|
import type { PlanDetailFeatureCaseItem, TestPlanDetail } from '@/models/testPlan/testPlan';
|
||||||
|
import { LastExecuteResults } from '@/enums/caseEnum';
|
||||||
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
|
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
import { executionResultMap, getCustomField } from '@/views/case-management/caseManagementFeature/components/utils';
|
import { executionResultMap, getCustomField } from '@/views/case-management/caseManagementFeature/components/utils';
|
||||||
|
@ -308,7 +310,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeActiveCase(item: PlanDetailFeatureCaseItem) {
|
function changeActiveCase(item: PlanDetailFeatureCaseItem) {
|
||||||
if (activeCaseId.value !== item.caseId) {
|
if (activeId.value !== item.id) {
|
||||||
activeCaseId.value = item.caseId;
|
activeCaseId.value = item.caseId;
|
||||||
activeId.value = item.id;
|
activeId.value = item.id;
|
||||||
}
|
}
|
||||||
|
@ -317,6 +319,7 @@
|
||||||
() => activeCaseId.value,
|
() => activeCaseId.value,
|
||||||
() => {
|
() => {
|
||||||
loadCaseDetail();
|
loadCaseDetail();
|
||||||
|
// TODO 更新历史列表
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -325,11 +328,21 @@
|
||||||
await loadCaseDetail();
|
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);
|
const autoNext = ref(true);
|
||||||
async function executeDone() {
|
async function executeDone() {
|
||||||
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.id === activeId.value);
|
||||||
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;
|
||||||
|
@ -344,10 +357,12 @@
|
||||||
// 当前是最后一个,刷新数据
|
// 当前是最后一个,刷新数据
|
||||||
loadCaseDetail();
|
loadCaseDetail();
|
||||||
loadCaseList();
|
loadCaseList();
|
||||||
|
// TODO 更新历史列表
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 不自动下一个才请求详情
|
// 不自动下一个才请求详情
|
||||||
loadCase();
|
loadCase();
|
||||||
|
// TODO 更新历史列表
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue