feat(测试计划): 测试计划详情-功能用例详情样式
This commit is contained in:
parent
b254e3a0a3
commit
be16db3689
|
@ -965,6 +965,13 @@ export const pathMap: PathMapItem[] = [
|
||||||
permission: [],
|
permission: [],
|
||||||
level: MENU_LEVEL[2],
|
level: MENU_LEVEL[2],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'TEST_PLAN_INDEX_DETAIL_FEATURE_CASE_DETAIL', // 测试计划-测试计划-测试计划详情-功能用例详情
|
||||||
|
locale: 'menu.caseManagement.caseManagementCaseDetail',
|
||||||
|
route: RouteEnum.TEST_PLAN_INDEX_DETAIL_FEATURE_CASE_DETAIL,
|
||||||
|
permission: [],
|
||||||
|
level: MENU_LEVEL[2],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -62,6 +62,7 @@ export enum TestPlanRouteEnum {
|
||||||
TEST_PLAN = 'testPlan',
|
TEST_PLAN = 'testPlan',
|
||||||
TEST_PLAN_INDEX = 'testPlanIndex',
|
TEST_PLAN_INDEX = 'testPlanIndex',
|
||||||
TEST_PLAN_INDEX_DETAIL = 'testPlanIndexDetail',
|
TEST_PLAN_INDEX_DETAIL = 'testPlanIndexDetail',
|
||||||
|
TEST_PLAN_INDEX_DETAIL_FEATURE_CASE_DETAIL = 'testPlanIndexDetailFeatureCaseDetail',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum UITestRouteEnum {
|
export enum UITestRouteEnum {
|
||||||
|
|
|
@ -171,4 +171,6 @@ export default {
|
||||||
'common.belongModule': '所属模块',
|
'common.belongModule': '所属模块',
|
||||||
'common.moreSetting': '更多设置',
|
'common.moreSetting': '更多设置',
|
||||||
'common.executionResult': '执行结果',
|
'common.executionResult': '执行结果',
|
||||||
|
'common.detail': '详情',
|
||||||
|
'common.baseInfo': '基本信息',
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type { customFieldsItem } from '@/models/caseManagement/featureCase';
|
import type { customFieldsItem } from '@/models/caseManagement/featureCase';
|
||||||
import type { TableQueryParams } from '@/models/common';
|
import type { TableQueryParams } from '@/models/common';
|
||||||
|
import { LastExecuteResults } from '@/enums/caseEnum';
|
||||||
|
|
||||||
import { BatchApiParams } from '../common';
|
import { BatchApiParams } from '../common';
|
||||||
|
|
||||||
|
@ -133,7 +134,7 @@ export interface PlanDetailFeatureCaseItem {
|
||||||
versionName: string;
|
versionName: string;
|
||||||
createUser: string;
|
createUser: string;
|
||||||
createUserName: string;
|
createUserName: string;
|
||||||
lastExecResult: string;
|
lastExecResult: LastExecuteResults;
|
||||||
lastExecTime: number;
|
lastExecTime: number;
|
||||||
executeUser: string;
|
executeUser: string;
|
||||||
executeUserName: string;
|
executeUserName: string;
|
||||||
|
|
|
@ -47,6 +47,32 @@ const TestPlan: AppRouteRecordRaw = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// 测试计划-测试计划详情-功能用例详情
|
||||||
|
{
|
||||||
|
path: 'testPlanIndexDetailFeatureCaseDetail',
|
||||||
|
name: TestPlanRouteEnum.TEST_PLAN_INDEX_DETAIL_FEATURE_CASE_DETAIL,
|
||||||
|
component: () => import('@/views/test-plan/testPlan/detail/featureCase/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',
|
||||||
|
isBack: true,
|
||||||
|
query: ['id'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: TestPlanRouteEnum.TEST_PLAN_INDEX_DETAIL_FEATURE_CASE_DETAIL,
|
||||||
|
locale: 'menu.caseManagement.caseManagementCaseDetail',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
</div>
|
</div>
|
||||||
<MsBaseTable v-bind="propsRes" :action-config="batchActions" v-on="propsEvent" @batch-action="handleTableBatch">
|
<MsBaseTable v-bind="propsRes" :action-config="batchActions" v-on="propsEvent" @batch-action="handleTableBatch">
|
||||||
<template #num="{ record }">
|
<template #num="{ record }">
|
||||||
<MsButton type="text">{{ record.num }}</MsButton>
|
<MsButton type="text" @click="toCaseDetail(record)">{{ record.num }}</MsButton>
|
||||||
</template>
|
</template>
|
||||||
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL]="{ filterContent }">
|
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL]="{ filterContent }">
|
||||||
<CaseLevel :case-level="filterContent.value" />
|
<CaseLevel :case-level="filterContent.value" />
|
||||||
|
@ -70,6 +70,7 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onBeforeMount, ref } from 'vue';
|
import { computed, onBeforeMount, ref } from 'vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||||
|
@ -85,7 +86,8 @@
|
||||||
import { hasAnyPermission } from '@/utils/permission';
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
import { ModuleTreeNode } from '@/models/common';
|
import { ModuleTreeNode } from '@/models/common';
|
||||||
import type { PlanDetailFeatureCaseListQueryParams } from '@/models/testPlan/testPlan';
|
import type { PlanDetailFeatureCaseItem, PlanDetailFeatureCaseListQueryParams } from '@/models/testPlan/testPlan';
|
||||||
|
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';
|
||||||
|
|
||||||
|
@ -107,8 +109,10 @@
|
||||||
(e: 'init', params: PlanDetailFeatureCaseListQueryParams): void;
|
(e: 'init', params: PlanDetailFeatureCaseListQueryParams): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const appStore = useAppStore();
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const appStore = useAppStore();
|
||||||
const tableStore = useTableStore();
|
const tableStore = useTableStore();
|
||||||
|
|
||||||
const keyword = ref('');
|
const keyword = ref('');
|
||||||
|
@ -203,7 +207,7 @@
|
||||||
width: hasOperationPermission.value ? 200 : 50,
|
width: hasOperationPermission.value ? 200 : 50,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector } = useTable(
|
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector, getTableQueryParams } = useTable(
|
||||||
getPlanDetailFeatureCaseList,
|
getPlanDetailFeatureCaseList,
|
||||||
{
|
{
|
||||||
scroll: { x: '100%' },
|
scroll: { x: '100%' },
|
||||||
|
@ -307,6 +311,20 @@
|
||||||
loadCaseList();
|
loadCaseList();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 去用例详情页面
|
||||||
|
function toCaseDetail(record: PlanDetailFeatureCaseItem) {
|
||||||
|
router.push({
|
||||||
|
name: TestPlanRouteEnum.TEST_PLAN_INDEX_DETAIL_FEATURE_CASE_DETAIL,
|
||||||
|
query: {
|
||||||
|
...route.query,
|
||||||
|
caseId: record.id,
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
params: JSON.stringify(getTableQueryParams()),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
resetSelector,
|
resetSelector,
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,218 @@
|
||||||
|
<template>
|
||||||
|
<MsCard :min-width="1100" has-breadcrumb simple no-content-padding>
|
||||||
|
<div class="flex h-full w-full">
|
||||||
|
<!-- 左侧 -->
|
||||||
|
<div class="flex h-full w-[318px] flex-col border-r border-[var(--color-text-n8)] p-[16px]">
|
||||||
|
<a-tooltip :content="`[${planDetail.num}]${planDetail.name}`">
|
||||||
|
<div class="one-line-text w-full gap-[4px] font-medium">
|
||||||
|
<span>[{{ planDetail.num }}]</span>
|
||||||
|
{{ planDetail.name }}
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
<div class="my-[8px] flex">
|
||||||
|
<a-input-search
|
||||||
|
v-model:model-value="keyword"
|
||||||
|
:placeholder="t('caseManagement.caseReview.searchPlaceholder')"
|
||||||
|
allow-clear
|
||||||
|
class="mr-[8px] w-[176px]"
|
||||||
|
@search="loadCaseList"
|
||||||
|
@press-enter="loadCaseList"
|
||||||
|
@clear="loadCaseList"
|
||||||
|
/>
|
||||||
|
<a-select
|
||||||
|
v-model:model-value="lastExecResult"
|
||||||
|
:options="executeResultOptions"
|
||||||
|
class="flex-1"
|
||||||
|
@change="loadCaseList"
|
||||||
|
>
|
||||||
|
</a-select>
|
||||||
|
</div>
|
||||||
|
<a-spin :loading="caseListLoading" class="w-full flex-1 overflow-hidden">
|
||||||
|
<div class="case-list">
|
||||||
|
<div
|
||||||
|
v-for="item of caseList"
|
||||||
|
:key="item.id"
|
||||||
|
:class="['case-item', caseDetail.id === item.id ? 'case-item--active' : '']"
|
||||||
|
>
|
||||||
|
<div class="mb-[8px] flex items-center justify-between">
|
||||||
|
<div class="text-[var(--color-text-4)]">{{ item.num }}</div>
|
||||||
|
<ExecuteResult :execute-result="item.lastExecResult" />
|
||||||
|
</div>
|
||||||
|
<a-tooltip :content="item.name">
|
||||||
|
<div class="one-line-text">{{ item.name }}</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<MsEmpty v-if="caseList.length === 0" />
|
||||||
|
</div>
|
||||||
|
<!-- TODO 样式 -->
|
||||||
|
<MsPagination
|
||||||
|
v-model:page-size="pageNation.pageSize"
|
||||||
|
v-model:current="pageNation.current"
|
||||||
|
:total="pageNation.total"
|
||||||
|
size="mini"
|
||||||
|
simple
|
||||||
|
@change="loadCaseList"
|
||||||
|
@page-size-change="loadCaseList"
|
||||||
|
/>
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
<!-- 右侧 -->
|
||||||
|
<a-spin :loading="caseDetailLoading" class="relative flex flex-1 flex-col p-[16px]">
|
||||||
|
<div class="flex">
|
||||||
|
<div class="mr-[24px] flex flex-1 items-center">
|
||||||
|
<MsStatusTag :status="caseDetail.status" />
|
||||||
|
<div class="ml-[8px] mr-[2px] font-medium text-[rgb(var(--primary-5))]">[{{ caseDetail.num }}]</div>
|
||||||
|
<div class="flex-1 overflow-hidden">
|
||||||
|
<a-tooltip :content="caseDetail.name">
|
||||||
|
<div class="one-line-text max-w-[100%] font-medium">
|
||||||
|
{{ caseDetail.name }}
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a-button type="outline">{{ t('common.edit') }}</a-button>
|
||||||
|
</div>
|
||||||
|
<MsTab
|
||||||
|
v-model:active-key="activeTab"
|
||||||
|
:show-badge="false"
|
||||||
|
:content-tab-list="contentTabList"
|
||||||
|
no-content
|
||||||
|
class="relative border-b"
|
||||||
|
/>
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
</MsCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||||
|
import MsEmpty from '@/components/pure/ms-empty/index.vue';
|
||||||
|
import MsPagination from '@/components/pure/ms-pagination/index';
|
||||||
|
import MsTab from '@/components/pure/ms-tab/index.vue';
|
||||||
|
import ExecuteResult from '@/components/business/ms-case-associate/executeResult.vue';
|
||||||
|
import MsStatusTag from '@/components/business/ms-status-tag/index.vue';
|
||||||
|
|
||||||
|
import { getPlanDetailFeatureCaseList } from '@/api/modules/test-plan/testPlan';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useAppStore from '@/store/modules/app';
|
||||||
|
|
||||||
|
import type { PlanDetailFeatureCaseItem } from '@/models/testPlan/testPlan';
|
||||||
|
|
||||||
|
import { executionResultMap } from '@/views/case-management/caseManagementFeature/components/utils';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const route = useRoute();
|
||||||
|
const appStore = useAppStore();
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
const planDetail = ref({ num: '111', name: '222lalallalallalalalal222lalallalallalalalal222lalallalallalalalal' });
|
||||||
|
|
||||||
|
const keyword = ref('');
|
||||||
|
const lastExecResult = ref('');
|
||||||
|
const executeResultOptions = computed(() => {
|
||||||
|
return [
|
||||||
|
{ label: t('common.all'), value: '' },
|
||||||
|
...Object.keys(executionResultMap).map((key) => {
|
||||||
|
return {
|
||||||
|
value: key,
|
||||||
|
label: executionResultMap[key].statusText,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
const caseList = ref<PlanDetailFeatureCaseItem[]>([]);
|
||||||
|
const pageNation = ref({
|
||||||
|
total: 0,
|
||||||
|
pageSize: 10,
|
||||||
|
current: 1,
|
||||||
|
});
|
||||||
|
const otherListQueryParams = ref<Record<string, any>>({});
|
||||||
|
const caseListLoading = ref(false);
|
||||||
|
// 加载用例列表
|
||||||
|
async function loadCaseList() {
|
||||||
|
try {
|
||||||
|
caseListLoading.value = true;
|
||||||
|
const res = await getPlanDetailFeatureCaseList({
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
testPlanId: route.query.id as string,
|
||||||
|
keyword: keyword.value,
|
||||||
|
current: pageNation.value.current || 1,
|
||||||
|
pageSize: pageNation.value.pageSize,
|
||||||
|
filter: lastExecResult.value
|
||||||
|
? {
|
||||||
|
lastExecResult: [lastExecResult.value],
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
...otherListQueryParams.value,
|
||||||
|
});
|
||||||
|
caseList.value = res.list;
|
||||||
|
pageNation.value.total = res.total;
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
caseListLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const caseDetail = ref<any>({});
|
||||||
|
const caseDetailLoading = ref(false);
|
||||||
|
const activeTab = ref('detail');
|
||||||
|
const contentTabList = ref([
|
||||||
|
{
|
||||||
|
value: 'baseInfo',
|
||||||
|
label: t('common.baseInfo'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'detail',
|
||||||
|
label: t('common.detail'),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
const lastPageParams = window.history.state.params ? JSON.parse(window.history.state.params) : null; // 获取上个页面带过来的表格查询参数
|
||||||
|
if (lastPageParams) {
|
||||||
|
const { total, pageSize, current, keyword: _keyword, sort, moduleIds } = lastPageParams;
|
||||||
|
pageNation.value = {
|
||||||
|
total: total || 0,
|
||||||
|
pageSize,
|
||||||
|
current,
|
||||||
|
};
|
||||||
|
keyword.value = _keyword;
|
||||||
|
otherListQueryParams.value = {
|
||||||
|
sort,
|
||||||
|
moduleIds,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
await loadCaseList();
|
||||||
|
// TODO 获取用例详情 暂时
|
||||||
|
caseDetail.value = caseList.value[0] ?? {};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.case-list {
|
||||||
|
@apply flex flex-col overflow-y-auto;
|
||||||
|
|
||||||
|
margin-bottom: 16px;
|
||||||
|
height: calc(100% - 40px);
|
||||||
|
gap: 8px;
|
||||||
|
.ms-scroll-bar();
|
||||||
|
.case-item {
|
||||||
|
@apply cursor-pointer;
|
||||||
|
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid var(--color-text-n8);
|
||||||
|
border-radius: var(--border-radius-small);
|
||||||
|
&:hover {
|
||||||
|
border: 1px solid rgb(var(--primary-4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.case-item--active {
|
||||||
|
border: 1px solid rgb(var(--primary-5));
|
||||||
|
background-color: var(--color-text-n9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue