feat(测试计划): 测试计划详情-功能用例详情样式
This commit is contained in:
parent
b254e3a0a3
commit
be16db3689
|
@ -965,6 +965,13 @@ export const pathMap: PathMapItem[] = [
|
|||
permission: [],
|
||||
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_INDEX = 'testPlanIndex',
|
||||
TEST_PLAN_INDEX_DETAIL = 'testPlanIndexDetail',
|
||||
TEST_PLAN_INDEX_DETAIL_FEATURE_CASE_DETAIL = 'testPlanIndexDetailFeatureCaseDetail',
|
||||
}
|
||||
|
||||
export enum UITestRouteEnum {
|
||||
|
|
|
@ -171,4 +171,6 @@ export default {
|
|||
'common.belongModule': '所属模块',
|
||||
'common.moreSetting': '更多设置',
|
||||
'common.executionResult': '执行结果',
|
||||
'common.detail': '详情',
|
||||
'common.baseInfo': '基本信息',
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import type { customFieldsItem } from '@/models/caseManagement/featureCase';
|
||||
import type { TableQueryParams } from '@/models/common';
|
||||
import { LastExecuteResults } from '@/enums/caseEnum';
|
||||
|
||||
import { BatchApiParams } from '../common';
|
||||
|
||||
|
@ -133,7 +134,7 @@ export interface PlanDetailFeatureCaseItem {
|
|||
versionName: string;
|
||||
createUser: string;
|
||||
createUserName: string;
|
||||
lastExecResult: string;
|
||||
lastExecResult: LastExecuteResults;
|
||||
lastExecTime: number;
|
||||
executeUser: 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>
|
||||
<MsBaseTable v-bind="propsRes" :action-config="batchActions" v-on="propsEvent" @batch-action="handleTableBatch">
|
||||
<template #num="{ record }">
|
||||
<MsButton type="text">{{ record.num }}</MsButton>
|
||||
<MsButton type="text" @click="toCaseDetail(record)">{{ record.num }}</MsButton>
|
||||
</template>
|
||||
<template #[FilterSlotNameEnum.CASE_MANAGEMENT_CASE_LEVEL]="{ filterContent }">
|
||||
<CaseLevel :case-level="filterContent.value" />
|
||||
|
@ -70,6 +70,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeMount, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
|
@ -85,7 +86,8 @@
|
|||
import { hasAnyPermission } from '@/utils/permission';
|
||||
|
||||
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 { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
|
||||
|
||||
|
@ -107,8 +109,10 @@
|
|||
(e: 'init', params: PlanDetailFeatureCaseListQueryParams): void;
|
||||
}>();
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
const tableStore = useTableStore();
|
||||
|
||||
const keyword = ref('');
|
||||
|
@ -203,7 +207,7 @@
|
|||
width: hasOperationPermission.value ? 200 : 50,
|
||||
},
|
||||
];
|
||||
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector } = useTable(
|
||||
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector, getTableQueryParams } = useTable(
|
||||
getPlanDetailFeatureCaseList,
|
||||
{
|
||||
scroll: { x: '100%' },
|
||||
|
@ -307,6 +311,20 @@
|
|||
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({
|
||||
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