feat(测试计划): 测试计划详情卡片样式
This commit is contained in:
parent
7478a1dff0
commit
8508848b48
|
@ -0,0 +1,21 @@
|
|||
import { ReviewStatus } from '@/models/caseManagement/caseReview';
|
||||
import type { TestPlanDetail } from '@/models/testPlan/testPlan';
|
||||
|
||||
// TODO: 对照后端
|
||||
// 测试计划详情
|
||||
export const testPlanDefaultDetail: TestPlanDetail = {
|
||||
id: '',
|
||||
name: '',
|
||||
num: 0,
|
||||
status: 'PREPARED' as ReviewStatus,
|
||||
followFlag: false,
|
||||
passRate: 0,
|
||||
executedCount: 0,
|
||||
caseCount: 0,
|
||||
passCount: 0,
|
||||
unPassCount: 0,
|
||||
reReviewedCount: 0,
|
||||
underReviewedCount: 0,
|
||||
};
|
||||
|
||||
export default {};
|
|
@ -61,6 +61,7 @@ export enum ProjectManagementRouteEnum {
|
|||
export enum TestPlanRouteEnum {
|
||||
TEST_PLAN = 'testPlan',
|
||||
TEST_PLAN_INDEX = 'testPlanIndex',
|
||||
TEST_PLAN_INDEX_DETAIL = 'testPlanIndexDetail',
|
||||
}
|
||||
|
||||
export enum UITestRouteEnum {
|
||||
|
|
|
@ -20,6 +20,7 @@ export default {
|
|||
message: {
|
||||
'menu.workbench': '工作台',
|
||||
'menu.testPlan': '测试计划',
|
||||
'menu.testPlan.testPlanDetail': '测试计划详情',
|
||||
'menu.bugManagement': '缺陷管理',
|
||||
'menu.bugManagement.bugDetail': '缺陷',
|
||||
'menu.bugManagement.bugRecycle': '回收站',
|
||||
|
|
|
@ -82,4 +82,21 @@ export interface UseCountType {
|
|||
testProgress: string; // 测试进度
|
||||
}
|
||||
|
||||
// TODO: 对后端
|
||||
// 测试计划详情
|
||||
export interface TestPlanDetail {
|
||||
id: string;
|
||||
name: string;
|
||||
num: number;
|
||||
status: planStatusType;
|
||||
followFlag: boolean;
|
||||
passRate: number;
|
||||
executedCount: number;
|
||||
caseCount: number;
|
||||
passCount: number;
|
||||
unPassCount: number;
|
||||
reReviewedCount: number;
|
||||
underReviewedCount: number;
|
||||
}
|
||||
|
||||
export default {};
|
||||
|
|
|
@ -27,6 +27,26 @@ const TestPlan: AppRouteRecordRaw = {
|
|||
isTopMenu: true,
|
||||
},
|
||||
},
|
||||
// 测试计划详情
|
||||
{
|
||||
path: 'testPlanIndexDetail',
|
||||
name: TestPlanRouteEnum.TEST_PLAN_INDEX_DETAIL,
|
||||
component: () => import('@/views/test-plan/testPlan/detail/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.testPlan.testPlanDetail',
|
||||
roles: ['PROJECT_TEST_PLAN:READ'],
|
||||
breadcrumbs: [
|
||||
{
|
||||
name: TestPlanRouteEnum.TEST_PLAN_INDEX,
|
||||
locale: 'menu.testPlan',
|
||||
},
|
||||
{
|
||||
name: TestPlanRouteEnum.TEST_PLAN_INDEX_DETAIL,
|
||||
locale: 'menu.testPlan.testPlanDetail',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
|
|
@ -79,7 +79,9 @@
|
|||
</a-tooltip>
|
||||
</div> -->
|
||||
<div class="flex items-center">
|
||||
<div class="one-line-text cursor-pointer text-[rgb(var(--primary-5))]">{{ record.num }}</div>
|
||||
<div class="one-line-text cursor-pointer text-[rgb(var(--primary-5))]" @click="openDetail(record.id)">{{
|
||||
record.num
|
||||
}}</div>
|
||||
<a-tooltip position="right" :disabled="!record.schedule" :mouse-enter-delay="300">
|
||||
<MsTag v-if="record.schedule" size="small" type="link" theme="outline" class="ml-2">{{
|
||||
t('testPlan.testPlanIndex.timing')
|
||||
|
@ -257,6 +259,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
|
@ -283,6 +286,7 @@
|
|||
import { characterLimit } from '@/utils';
|
||||
|
||||
import type { planStatusType, TestPlanItem } from '@/models/testPlan/testPlan';
|
||||
import { TestPlanRouteEnum } from '@/enums/routeEnum';
|
||||
import { ColumnEditTypeEnum, TableKeyEnum } from '@/enums/tableEnum';
|
||||
import { testPlanTypeEnum } from '@/enums/testPlanEnum';
|
||||
|
||||
|
@ -290,6 +294,7 @@
|
|||
|
||||
const tableStore = useTableStore();
|
||||
const appStore = useAppStore();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const { openModal } = useModal();
|
||||
|
||||
|
@ -611,6 +616,16 @@
|
|||
});
|
||||
}
|
||||
|
||||
// 测试计划详情
|
||||
function openDetail(id: string) {
|
||||
router.push({
|
||||
name: TestPlanRouteEnum.TEST_PLAN_INDEX_DETAIL,
|
||||
query: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量执行
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
<template>
|
||||
<MsCard
|
||||
:loading="loading"
|
||||
:header-min-width="1100"
|
||||
:min-width="150"
|
||||
auto-height
|
||||
hide-footer
|
||||
no-content-padding
|
||||
hide-divider
|
||||
>
|
||||
<template #headerLeft>
|
||||
<statusTag :status="(detail.status as ReviewStatus)" />
|
||||
<a-tooltip :content="`[${detail.num}]${detail.name}`">
|
||||
<div class="one-line-text ml-[4px] max-w-[360px] gap-[4px] font-medium text-[var(--color-text-1)]">
|
||||
<span>[{{ detail.num }}]</span>
|
||||
{{ detail.name }}
|
||||
</div>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template #headerRight>
|
||||
<MsButton v-permission="['PROJECT_TEST_PLAN:READ+ASSOCIATION']" type="button" status="default">
|
||||
<MsIcon type="icon-icon_link-record_outlined1" class="mr-[8px]" />
|
||||
{{ t('ms.case.associate.title') }}
|
||||
</MsButton>
|
||||
<MsButton v-permission="['PROJECT_TEST_PLAN:READ+UPDATE']" type="button" status="default">
|
||||
<MsIcon type="icon-icon_edit_outlined" class="mr-[8px]" />
|
||||
{{ t('common.edit') }}
|
||||
</MsButton>
|
||||
<MsButton v-permission="['PROJECT_TEST_PLAN:READ+ADD']" type="button" status="default">
|
||||
<MsIcon type="icon-icon_copy_outlined" class="mr-[8px]" />
|
||||
{{ t('common.copy') }}
|
||||
</MsButton>
|
||||
<MsButton v-permission="['PROJECT_TEST_PLAN:READ+UPDATE']" type="button" status="default">
|
||||
<MsIcon
|
||||
:type="detail.followFlag ? 'icon-icon_collect_filled' : 'icon-icon_collection_outlined'"
|
||||
:class="`mr-[8px] ${detail.followFlag ? 'text-[rgb(var(--warning-6))]' : ''}`"
|
||||
/>
|
||||
{{ t(detail.followFlag ? 'common.forked' : 'common.fork') }}
|
||||
</MsButton>
|
||||
<MsTableMoreAction :list="moreAction" @select="handleMoreSelect">
|
||||
<MsButton v-permission="['PROJECT_TEST_PLAN:READ+DELETE']" type="button" status="default">
|
||||
<MsIcon type="icon-icon_more_outlined" class="mr-[8px]" />
|
||||
{{ t('common.more') }}
|
||||
</MsButton>
|
||||
</MsTableMoreAction>
|
||||
</template>
|
||||
<template #subHeader>
|
||||
<div class="mt-[16px] w-[476px]">
|
||||
<div class="mb-[8px] flex items-center gap-[24px] text-[12px]">
|
||||
<div class="text-[var(--color-text-4)]">
|
||||
<span class="mr-[8px]">{{ t('testPlan.testPlanDetail.executed') }}</span>
|
||||
<span v-if="detail.status === 'PREPARED'" class="text-[var(--color-text-1)]">-</span>
|
||||
<span v-else>
|
||||
<span class="font-medium text-[var(--color-text-1)]"> {{ detail.executedCount }} </span>/{{
|
||||
detail.caseCount
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-[var(--color-text-4)]">
|
||||
<span class="mr-[8px]">{{ t('caseManagement.caseReview.passRate') }}</span>
|
||||
<span v-if="detail.status === 'PREPARED'" class="text-[var(--color-text-1)]">-</span>
|
||||
<span v-else>
|
||||
<span class="font-medium text-[var(--color-text-1)]"> {{ detail.passRate }}% </span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<passRateLine :review-detail="detail" height="8px" radius="var(--border-radius-mini)" />
|
||||
</div>
|
||||
</template>
|
||||
<a-tabs v-model:active-key="activeTab" class="no-content">
|
||||
<a-tab-pane v-for="item of tabList" :key="item.key" :title="item.title" />
|
||||
</a-tabs>
|
||||
</MsCard>
|
||||
<!-- special-height的174: 上面卡片高度158 + mt的16 -->
|
||||
<MsCard class="mt-[16px]" :special-height="174" simple has-breadcrumb no-content-padding></MsCard>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
||||
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||
import passRateLine from '@/views/case-management/caseReview/components/passRateLine.vue';
|
||||
import statusTag from '@/views/case-management/caseReview/components/statusTag.vue';
|
||||
|
||||
import { testPlanDefaultDetail } from '@/config/testPlan';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
import type { ReviewStatus } from '@/models/caseManagement/caseReview';
|
||||
import type { TestPlanDetail } from '@/models/testPlan/testPlan';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const loading = ref(false);
|
||||
const detail = ref<TestPlanDetail>({
|
||||
...testPlanDefaultDetail,
|
||||
});
|
||||
async function initDetail() {
|
||||
try {
|
||||
loading.value = true;
|
||||
// TODO: 调后端
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
initDetail();
|
||||
});
|
||||
|
||||
const fullActions = [
|
||||
{
|
||||
label: t('common.archive'),
|
||||
eventTag: 'archive',
|
||||
permission: ['PROJECT_TEST_PLAN:READ+UPDATE'],
|
||||
},
|
||||
{
|
||||
isDivider: true,
|
||||
},
|
||||
{
|
||||
label: t('common.delete'),
|
||||
eventTag: 'delete',
|
||||
danger: true,
|
||||
permission: ['PROJECT_TEST_PLAN:READ+DELETE'],
|
||||
},
|
||||
];
|
||||
const moreAction = computed(() => {
|
||||
if (detail.value.status === 'COMPLETED') {
|
||||
return [...fullActions];
|
||||
}
|
||||
return fullActions.filter((e) => e.eventTag !== 'archive');
|
||||
});
|
||||
function handleMoreSelect(item: ActionsItem) {
|
||||
switch (item.eventTag) {
|
||||
case 'archive':
|
||||
break;
|
||||
case 'delete':
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const activeTab = ref('featureCase');
|
||||
const tabList = ref([
|
||||
{
|
||||
key: 'featureCase',
|
||||
title: t('menu.caseManagement.featureCase'),
|
||||
},
|
||||
{
|
||||
key: 'defectList',
|
||||
title: t('caseManagement.featureCase.defectList'),
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.arco-tabs-content) {
|
||||
@apply hidden;
|
||||
}
|
||||
</style>
|
|
@ -80,4 +80,5 @@ export default {
|
|||
'testPlan.planForm.repeatCaseTip1': 'Enable: Repeatedly associate the same case',
|
||||
'testPlan.planForm.repeatCaseTip2': 'Close: Cannot be associated with the same case repeatedly',
|
||||
'testPlan.planForm.pickCases': 'Select cases',
|
||||
'testPlan.testPlanDetail.executed': 'Executed',
|
||||
};
|
||||
|
|
|
@ -78,4 +78,5 @@ export default {
|
|||
'testPlan.planForm.repeatCaseTip1': '开启:可重复关联同一个用例',
|
||||
'testPlan.planForm.repeatCaseTip2': '关闭:不可重复关联同一用例',
|
||||
'testPlan.planForm.pickCases': '选择用例',
|
||||
'testPlan.testPlanDetail.executed': '已执行',
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue