feat(测试计划): 修改测试计划部分细节和详情缺陷管理列表页面
This commit is contained in:
parent
92d441eaa8
commit
2c48d8f947
|
@ -3,7 +3,9 @@ import {
|
||||||
addTestPlanModuleUrl,
|
addTestPlanModuleUrl,
|
||||||
AddTestPlanUrl,
|
AddTestPlanUrl,
|
||||||
archivedPlanUrl,
|
archivedPlanUrl,
|
||||||
|
batchCopyPlanUrl,
|
||||||
batchDeletePlanUrl,
|
batchDeletePlanUrl,
|
||||||
|
batchMovePlanUrl,
|
||||||
deletePlanUrl,
|
deletePlanUrl,
|
||||||
DeleteTestPlanModuleUrl,
|
DeleteTestPlanModuleUrl,
|
||||||
getStatisticalCountUrl,
|
getStatisticalCountUrl,
|
||||||
|
@ -12,6 +14,7 @@ import {
|
||||||
GetTestPlanModuleCountUrl,
|
GetTestPlanModuleCountUrl,
|
||||||
GetTestPlanModuleUrl,
|
GetTestPlanModuleUrl,
|
||||||
MoveTestPlanModuleUrl,
|
MoveTestPlanModuleUrl,
|
||||||
|
planDetailBugPageUrl,
|
||||||
updateTestPlanModuleUrl,
|
updateTestPlanModuleUrl,
|
||||||
UpdateTestPlanUrl,
|
UpdateTestPlanUrl,
|
||||||
} from '@/api/requrls/test-plan/testPlan';
|
} from '@/api/requrls/test-plan/testPlan';
|
||||||
|
@ -19,7 +22,13 @@ import {
|
||||||
import type { CreateOrUpdateModule, UpdateModule } from '@/models/caseManagement/featureCase';
|
import type { CreateOrUpdateModule, UpdateModule } from '@/models/caseManagement/featureCase';
|
||||||
import type { CommonList, MoveModules, TableQueryParams } from '@/models/common';
|
import type { CommonList, MoveModules, TableQueryParams } from '@/models/common';
|
||||||
import { ModuleTreeNode } from '@/models/common';
|
import { ModuleTreeNode } from '@/models/common';
|
||||||
import type { AddTestPlanParams, TestPlanDetail, TestPlanItem, UseCountType } from '@/models/testPlan/testPlan';
|
import type {
|
||||||
|
AddTestPlanParams,
|
||||||
|
PlanDetailBugItem,
|
||||||
|
TestPlanDetail,
|
||||||
|
TestPlanItem,
|
||||||
|
UseCountType,
|
||||||
|
} from '@/models/testPlan/testPlan';
|
||||||
|
|
||||||
// 获取模块树
|
// 获取模块树
|
||||||
export function getTestPlanModule(params: TableQueryParams) {
|
export function getTestPlanModule(params: TableQueryParams) {
|
||||||
|
@ -86,3 +95,15 @@ export function getStatisticalCount(id: string) {
|
||||||
export function archivedPlan(id: string | undefined) {
|
export function archivedPlan(id: string | undefined) {
|
||||||
return MSR.get({ url: `${archivedPlanUrl}/${id}` });
|
return MSR.get({ url: `${archivedPlanUrl}/${id}` });
|
||||||
}
|
}
|
||||||
|
// 批量复制测试计划
|
||||||
|
export function batchCopyPlan(data: TableQueryParams) {
|
||||||
|
return MSR.post({ url: batchCopyPlanUrl, data });
|
||||||
|
}
|
||||||
|
// 批量移动测试计划
|
||||||
|
export function batchMovePlan(data: TableQueryParams) {
|
||||||
|
return MSR.post({ url: batchMovePlanUrl, data });
|
||||||
|
}
|
||||||
|
// 计划详情缺陷管理列表
|
||||||
|
export function planDetailBugPage(data: TableQueryParams) {
|
||||||
|
return MSR.post<CommonList<PlanDetailBugItem>>({ url: planDetailBugPageUrl, data });
|
||||||
|
}
|
||||||
|
|
|
@ -26,3 +26,9 @@ export const deletePlanUrl = '/test-plan/delete';
|
||||||
export const getStatisticalCountUrl = '/test-plan/getCount';
|
export const getStatisticalCountUrl = '/test-plan/getCount';
|
||||||
// 归档
|
// 归档
|
||||||
export const archivedPlanUrl = '/test-plan/archived';
|
export const archivedPlanUrl = '/test-plan/archived';
|
||||||
|
// 批量复制
|
||||||
|
export const batchCopyPlanUrl = '/test-plan/batch/copy';
|
||||||
|
// 批量移动
|
||||||
|
export const batchMovePlanUrl = '/test-plan/batch/move';
|
||||||
|
// 计划详情缺陷管理列表
|
||||||
|
export const planDetailBugPageUrl = '/test-plan/bug/page';
|
||||||
|
|
|
@ -63,6 +63,8 @@ export enum TableKeyEnum {
|
||||||
PROJECT_MANAGEMENT_ENV_ALL_PARAM_HEADER = 'projectManagementEnvAllParamHeader',
|
PROJECT_MANAGEMENT_ENV_ALL_PARAM_HEADER = 'projectManagementEnvAllParamHeader',
|
||||||
PROJECT_MANAGEMENT_ENV_ALL_PARAM_VARIABLE = 'projectManagementEnvAllParamVariable',
|
PROJECT_MANAGEMENT_ENV_ALL_PARAM_VARIABLE = 'projectManagementEnvAllParamVariable',
|
||||||
TEST_PLAN_ALL_TABLE = 'testPlanAllTable',
|
TEST_PLAN_ALL_TABLE = 'testPlanAllTable',
|
||||||
|
TEST_PLAN_DETAIL_BUG_TABLE = 'testPlanDetailBug',
|
||||||
|
TEST_PLAN_DETAIL_BUG_TABLE_CASE_COUNT = 'testPlanDetailBugCaseCount',
|
||||||
TASK_API_CASE_SYSTEM = 'taskCenterApiCaseSystem',
|
TASK_API_CASE_SYSTEM = 'taskCenterApiCaseSystem',
|
||||||
TASK_API_CASE_ORGANIZATION = 'taskCenterApiCaseOrganization',
|
TASK_API_CASE_ORGANIZATION = 'taskCenterApiCaseOrganization',
|
||||||
TASK_API_CASE_PROJECT = 'taskCenterApiCaseProject',
|
TASK_API_CASE_PROJECT = 'taskCenterApiCaseProject',
|
||||||
|
|
|
@ -139,6 +139,7 @@ export default {
|
||||||
'common.yes': 'Yes',
|
'common.yes': 'Yes',
|
||||||
'common.no': 'No',
|
'common.no': 'No',
|
||||||
'common.creator': 'Creator',
|
'common.creator': 'Creator',
|
||||||
|
'common.createTime': 'Created time',
|
||||||
'common.followSuccess': 'Followed',
|
'common.followSuccess': 'Followed',
|
||||||
'common.unFollowSuccess': 'Unfollow successfully',
|
'common.unFollowSuccess': 'Unfollow successfully',
|
||||||
'common.share': 'Share',
|
'common.share': 'Share',
|
||||||
|
|
|
@ -139,6 +139,7 @@ export default {
|
||||||
'common.yes': '是',
|
'common.yes': '是',
|
||||||
'common.no': '否',
|
'common.no': '否',
|
||||||
'common.creator': '创建人',
|
'common.creator': '创建人',
|
||||||
|
'common.createTime': '创建时间',
|
||||||
'common.followSuccess': '关注成功',
|
'common.followSuccess': '关注成功',
|
||||||
'common.unFollowSuccess': '取消关注成功',
|
'common.unFollowSuccess': '取消关注成功',
|
||||||
'common.share': '分享',
|
'common.share': '分享',
|
||||||
|
|
|
@ -98,4 +98,20 @@ export interface UseCountType {
|
||||||
testProgress: string; // 测试进度
|
testProgress: string; // 测试进度
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 计划详情缺陷列表
|
||||||
|
export interface PlanDetailBugItem {
|
||||||
|
id: string;
|
||||||
|
num: string;
|
||||||
|
title: string;
|
||||||
|
relateCase: {
|
||||||
|
id: string;
|
||||||
|
bugId: string;
|
||||||
|
name: string;
|
||||||
|
}[];
|
||||||
|
handleUser: string;
|
||||||
|
status: string;
|
||||||
|
createUser: string;
|
||||||
|
createTime: number;
|
||||||
|
}
|
||||||
|
|
||||||
export default {};
|
export default {};
|
||||||
|
|
|
@ -35,7 +35,7 @@ export default {
|
||||||
'apiTestManagement.closeOther': 'Close other tabs',
|
'apiTestManagement.closeOther': 'Close other tabs',
|
||||||
'apiTestManagement.showSubdirectory': 'Show subdirectory use case',
|
'apiTestManagement.showSubdirectory': 'Show subdirectory use case',
|
||||||
'apiTestManagement.searchPlaceholder': 'Enter ID/name/api path search',
|
'apiTestManagement.searchPlaceholder': 'Enter ID/name/api path search',
|
||||||
'apiTestManagement.searchTaskPlaceholder': 'Enter resource Id/name search',
|
'apiTestManagement.searchTaskPlaceholder': 'Enter resource Id/name/URL search',
|
||||||
'apiTestManagement.apiName': 'Api name',
|
'apiTestManagement.apiName': 'Api name',
|
||||||
'apiTestManagement.apiType': 'Api type',
|
'apiTestManagement.apiType': 'Api type',
|
||||||
'apiTestManagement.apiStatus': 'Status',
|
'apiTestManagement.apiStatus': 'Status',
|
||||||
|
|
|
@ -34,7 +34,7 @@ export default {
|
||||||
'apiTestManagement.closeOther': '关闭其他tab',
|
'apiTestManagement.closeOther': '关闭其他tab',
|
||||||
'apiTestManagement.showSubdirectory': '显示子目录用例',
|
'apiTestManagement.showSubdirectory': '显示子目录用例',
|
||||||
'apiTestManagement.searchPlaceholder': '输入 ID/名称/api路径搜索',
|
'apiTestManagement.searchPlaceholder': '输入 ID/名称/api路径搜索',
|
||||||
'apiTestManagement.searchTaskPlaceholder': '输入资源ID/名称搜索',
|
'apiTestManagement.searchTaskPlaceholder': '输入资源ID/名称/URL搜索',
|
||||||
'apiTestManagement.apiName': '接口名称',
|
'apiTestManagement.apiName': '接口名称',
|
||||||
'apiTestManagement.apiType': '请求类型',
|
'apiTestManagement.apiType': '请求类型',
|
||||||
'apiTestManagement.apiStatus': '状态',
|
'apiTestManagement.apiStatus': '状态',
|
||||||
|
@ -79,7 +79,7 @@ export default {
|
||||||
'apiTestManagement.importSwaggerFileTip1': '支持 Swagger 3.0 版本的 json 文件,',
|
'apiTestManagement.importSwaggerFileTip1': '支持 Swagger 3.0 版本的 json 文件,',
|
||||||
'apiTestManagement.importSwaggerFileTip2': '2.0 文件可以在官网一键转换 3.0',
|
'apiTestManagement.importSwaggerFileTip2': '2.0 文件可以在官网一键转换 3.0',
|
||||||
'apiTestManagement.importSwaggerFileTip3': ',大小不超过 50M',
|
'apiTestManagement.importSwaggerFileTip3': ',大小不超过 50M',
|
||||||
'apiTestManagement.urlImportPlaceholder': '请输入OpenAPI/Swagger URL',
|
'apiTestManagement.urlImportPlaceholder': '请输入OpenAPI/URL',
|
||||||
'apiTestManagement.swaggerURLRequired': 'SwaggerURL 不能为空',
|
'apiTestManagement.swaggerURLRequired': 'SwaggerURL 不能为空',
|
||||||
'apiTestManagement.basicAuth': 'Basic Auth 认证',
|
'apiTestManagement.basicAuth': 'Basic Auth 认证',
|
||||||
'apiTestManagement.account': '账号',
|
'apiTestManagement.account': '账号',
|
||||||
|
|
|
@ -175,7 +175,6 @@
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
import MsChart from '@/components/pure/chart/index.vue';
|
|
||||||
import SetReportChart from './case/setReportChart.vue';
|
import SetReportChart from './case/setReportChart.vue';
|
||||||
import ReportDetailHeader from './reportDetailHeader.vue';
|
import ReportDetailHeader from './reportDetailHeader.vue';
|
||||||
import reportInfoHeader from './step/reportInfoHeaders.vue';
|
import reportInfoHeader from './step/reportInfoHeaders.vue';
|
||||||
|
|
|
@ -174,7 +174,7 @@
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const { propsRes, propsEvent, loadList, setLoadListParams, setKeyword } = useTable(getAssociatedCasePage, {
|
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(getAssociatedCasePage, {
|
||||||
columns,
|
columns,
|
||||||
tableKey: TableKeyEnum.CASE_MANAGEMENT_TAB_DEPENDENCY_PRE_CASE,
|
tableKey: TableKeyEnum.CASE_MANAGEMENT_TAB_DEPENDENCY_PRE_CASE,
|
||||||
scroll: { x: '100%' },
|
scroll: { x: '100%' },
|
||||||
|
@ -184,7 +184,6 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
const innerVisible = ref(false);
|
const innerVisible = ref(false);
|
||||||
const innerProject = ref(currentProjectId.value);
|
|
||||||
|
|
||||||
const associateForm = ref({
|
const associateForm = ref({
|
||||||
reviewers: [],
|
reviewers: [],
|
||||||
|
|
|
@ -9,7 +9,11 @@
|
||||||
<div class="items-right flex gap-[8px]">
|
<div class="items-right flex gap-[8px]">
|
||||||
<a-input-search
|
<a-input-search
|
||||||
v-model:model-value="keyword"
|
v-model:model-value="keyword"
|
||||||
:placeholder="t('system.organization.searchIndexPlaceholder')"
|
:placeholder="
|
||||||
|
props.moduleType === 'API_IMPORT'
|
||||||
|
? t('apiTestManagement.searchTaskPlaceholder')
|
||||||
|
: t('system.organization.searchIndexPlaceholder')
|
||||||
|
"
|
||||||
allow-clear
|
allow-clear
|
||||||
class="mx-[8px] w-[240px]"
|
class="mx-[8px] w-[240px]"
|
||||||
@search="searchList"
|
@search="searchList"
|
||||||
|
|
|
@ -51,7 +51,7 @@ export default {
|
||||||
'system.organization.updateOrganizationSuccess': '更新组织成功',
|
'system.organization.updateOrganizationSuccess': '更新组织成功',
|
||||||
'system.organization.createProject': '创建项目',
|
'system.organization.createProject': '创建项目',
|
||||||
'system.organization.subordinateOrg': '所属组织',
|
'system.organization.subordinateOrg': '所属组织',
|
||||||
'system.organization.searchIndexPlaceholder': '通过ID或名称搜索',
|
'system.organization.searchIndexPlaceholder': '通过ID/名称搜索',
|
||||||
'system.organization.searchUserPlaceholder': '通过名称/邮箱/手机搜索',
|
'system.organization.searchUserPlaceholder': '通过名称/邮箱/手机搜索',
|
||||||
'system.organization.organizationAdminRequired': '组织管理员不能为空',
|
'system.organization.organizationAdminRequired': '组织管理员不能为空',
|
||||||
'system.project.enableTitle': '启用项目',
|
'system.project.enableTitle': '启用项目',
|
||||||
|
|
|
@ -4,10 +4,6 @@
|
||||||
title-align="start"
|
title-align="start"
|
||||||
class="ms-modal-no-padding ms-modal-small"
|
class="ms-modal-no-padding ms-modal-small"
|
||||||
:mask-closable="false"
|
:mask-closable="false"
|
||||||
:ok-text="props.mode === 'move' ? t('common.move') : t('common.copy')"
|
|
||||||
:ok-button-props="{ disabled: innerSelectedModuleKeys.length === 0 }"
|
|
||||||
:cancel-button-props="{ disabled: props.okLoading }"
|
|
||||||
:on-before-ok="handleCaseMoveOrCopy"
|
|
||||||
@close="handleMoveCaseModalCancel"
|
@close="handleMoveCaseModalCancel"
|
||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
|
@ -55,6 +51,18 @@
|
||||||
</template>
|
</template>
|
||||||
</MsTree>
|
</MsTree>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
|
<template #footer>
|
||||||
|
<a-button type="secondary" @click="handleMoveCaseModalCancel">{{ t('common.cancel') }}</a-button>
|
||||||
|
<a-button
|
||||||
|
class="ml-[12px]"
|
||||||
|
type="primary"
|
||||||
|
:loading="props.okLoading"
|
||||||
|
:disabled="innerSelectedModuleKeys.length === 0"
|
||||||
|
@click="handleCaseMoveOrCopy"
|
||||||
|
>
|
||||||
|
{{ props.mode === 'move' ? t('common.move') : t('common.copy') }}
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
</a-radio-group> -->
|
</a-radio-group> -->
|
||||||
<a-popover title="" position="bottom">
|
<a-popover title="" position="bottom">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="one-line-text mr-1 max-h-[32px] max-w-[116px] text-[var(--color-text-1)]">
|
<div class="one-line-text mr-1 max-h-[32px] max-w-[300px] text-[var(--color-text-1)]">
|
||||||
{{ props.activeFolder === 'all' ? t('testPlan.testPlanIndex.allTestPlan') : props.nodeName }}
|
{{ props.activeFolder === 'all' ? t('testPlan.testPlanIndex.allTestPlan') : props.nodeName }}
|
||||||
</div>
|
</div>
|
||||||
<span class="text-[var(--color-text-4)]"> ({{ props.modulesCount[props.activeFolder] || 0 }})</span>
|
<span class="text-[var(--color-text-4)]"> ({{ props.modulesCount[props.activeFolder] || 0 }})</span>
|
||||||
|
@ -279,11 +279,21 @@
|
||||||
import StatusProgress from './statusProgress.vue';
|
import StatusProgress from './statusProgress.vue';
|
||||||
import statusTag from '@/views/case-management/caseReview/components/statusTag.vue';
|
import statusTag from '@/views/case-management/caseReview/components/statusTag.vue';
|
||||||
|
|
||||||
import { archivedPlan, batchDeletePlan, getTestPlanList, getTestPlanModule } from '@/api/modules/test-plan/testPlan';
|
import {
|
||||||
|
archivedPlan,
|
||||||
|
batchCopyPlan,
|
||||||
|
batchDeletePlan,
|
||||||
|
batchMovePlan,
|
||||||
|
getTestPlanDetail,
|
||||||
|
getTestPlanList,
|
||||||
|
getTestPlanModule,
|
||||||
|
updateTestPlan,
|
||||||
|
} from '@/api/modules/test-plan/testPlan';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useModal from '@/hooks/useModal';
|
import useModal from '@/hooks/useModal';
|
||||||
import { useAppStore, useTableStore } from '@/store';
|
import { useAppStore, useTableStore } from '@/store';
|
||||||
import { characterLimit } from '@/utils';
|
import { characterLimit } from '@/utils';
|
||||||
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
import type { planStatusType, TestPlanItem } from '@/models/testPlan/testPlan';
|
import type { planStatusType, TestPlanItem } from '@/models/testPlan/testPlan';
|
||||||
import { TestPlanRouteEnum } from '@/enums/routeEnum';
|
import { TestPlanRouteEnum } from '@/enums/routeEnum';
|
||||||
|
@ -316,7 +326,7 @@
|
||||||
title: 'testPlan.testPlanIndex.ID',
|
title: 'testPlan.testPlanIndex.ID',
|
||||||
slotName: 'num',
|
slotName: 'num',
|
||||||
dataIndex: 'num',
|
dataIndex: 'num',
|
||||||
width: 200,
|
width: 150,
|
||||||
showInTable: true,
|
showInTable: true,
|
||||||
showDrag: false,
|
showDrag: false,
|
||||||
showTooltip: true,
|
showTooltip: true,
|
||||||
|
@ -329,7 +339,7 @@
|
||||||
showInTable: true,
|
showInTable: true,
|
||||||
showTooltip: true,
|
showTooltip: true,
|
||||||
width: 180,
|
width: 180,
|
||||||
editType: ColumnEditTypeEnum.INPUT,
|
editType: hasAnyPermission(['PROJECT_TEST_PLAN:READ+UPDATE']) ? ColumnEditTypeEnum.INPUT : undefined,
|
||||||
sortable: {
|
sortable: {
|
||||||
sortDirections: ['ascend', 'descend'],
|
sortDirections: ['ascend', 'descend'],
|
||||||
sorter: true,
|
sorter: true,
|
||||||
|
@ -446,10 +456,18 @@
|
||||||
/**
|
/**
|
||||||
* 更新测试计划名称
|
* 更新测试计划名称
|
||||||
*/
|
*/
|
||||||
async function updatePlanName() {
|
async function updatePlanName(record: TestPlanItem) {
|
||||||
try {
|
try {
|
||||||
Message.success(t('common.updateSuccess'));
|
if (record.id) {
|
||||||
return Promise.resolve(true);
|
const detail = await getTestPlanDetail(record.id);
|
||||||
|
const params = {
|
||||||
|
...detail,
|
||||||
|
name: record.name,
|
||||||
|
};
|
||||||
|
await updateTestPlan(params);
|
||||||
|
Message.success(t('common.updateSuccess'));
|
||||||
|
return Promise.resolve(true);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
|
@ -553,13 +571,15 @@
|
||||||
showSetting: true,
|
showSetting: true,
|
||||||
heightUsed: 128,
|
heightUsed: 128,
|
||||||
paginationSize: 'mini',
|
paginationSize: 'mini',
|
||||||
|
showSelectorAll: false,
|
||||||
},
|
},
|
||||||
(item) => {
|
(item) => {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
tags: (item.tags || []).map((e: string) => ({ id: e, name: e })),
|
tags: (item.tags || []).map((e: string) => ({ id: e, name: e })),
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
updatePlanName
|
||||||
);
|
);
|
||||||
|
|
||||||
const batchParams = ref<BatchActionQueryParams>({
|
const batchParams = ref<BatchActionQueryParams>({
|
||||||
|
@ -605,9 +625,8 @@
|
||||||
loadList();
|
loadList();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchData() {
|
// 获取父组件模块数量
|
||||||
resetSelector();
|
async function emitTableParams() {
|
||||||
await loadPlanList();
|
|
||||||
const tableParams = await initTableParams();
|
const tableParams = await initTableParams();
|
||||||
emit('init', {
|
emit('init', {
|
||||||
...tableParams,
|
...tableParams,
|
||||||
|
@ -616,6 +635,12 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchData() {
|
||||||
|
resetSelector();
|
||||||
|
await loadPlanList();
|
||||||
|
emitTableParams();
|
||||||
|
}
|
||||||
|
|
||||||
// 测试计划详情
|
// 测试计划详情
|
||||||
function openDetail(id: string) {
|
function openDetail(id: string) {
|
||||||
router.push({
|
router.push({
|
||||||
|
@ -666,23 +691,25 @@
|
||||||
try {
|
try {
|
||||||
const params = {
|
const params = {
|
||||||
selectIds: batchParams.value.selectedIds || [],
|
selectIds: batchParams.value.selectedIds || [],
|
||||||
selectAll: !!batchParams.value?.selectAll,
|
|
||||||
excludeIds: batchParams.value?.excludeIds || [],
|
|
||||||
condition: {
|
condition: {
|
||||||
keyword: keyword.value,
|
keyword: keyword.value,
|
||||||
filter: {
|
filter: {},
|
||||||
reviewStatus: statusFilters.value,
|
|
||||||
},
|
|
||||||
combine: batchParams.value.condition,
|
combine: batchParams.value.condition,
|
||||||
},
|
},
|
||||||
projectId: appStore.currentProjectId,
|
projectId: appStore.currentProjectId,
|
||||||
moduleIds: [...selectNodeKeys.value],
|
moduleIds: [...selectNodeKeys.value],
|
||||||
|
type: showType.value,
|
||||||
|
moduleId: selectNodeKeys.value[0],
|
||||||
};
|
};
|
||||||
if (modeType.value === 'copy') {
|
if (modeType.value === 'copy') {
|
||||||
|
await batchCopyPlan(params);
|
||||||
Message.success(t('common.batchCopySuccess'));
|
Message.success(t('common.batchCopySuccess'));
|
||||||
} else {
|
} else {
|
||||||
|
await batchMovePlan(params);
|
||||||
Message.success(t('common.batchMoveSuccess'));
|
Message.success(t('common.batchMoveSuccess'));
|
||||||
}
|
}
|
||||||
|
showBatchModal.value = false;
|
||||||
|
fetchData();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -812,9 +839,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function deletePlan(record: any) {}
|
function deletePlan(record: TestPlanItem) {}
|
||||||
|
|
||||||
function copyHandler() {}
|
function copyHandler(record: TestPlanItem) {}
|
||||||
|
|
||||||
const showScheduledTaskModal = ref<boolean>(false);
|
const showScheduledTaskModal = ref<boolean>(false);
|
||||||
function handleScheduledTask() {
|
function handleScheduledTask() {
|
||||||
|
@ -854,7 +881,7 @@
|
||||||
function handleMoreActionSelect(item: ActionsItem, record: TestPlanItem) {
|
function handleMoreActionSelect(item: ActionsItem, record: TestPlanItem) {
|
||||||
switch (item.eventTag) {
|
switch (item.eventTag) {
|
||||||
case 'copy':
|
case 'copy':
|
||||||
copyHandler();
|
copyHandler(record);
|
||||||
break;
|
break;
|
||||||
case 'createScheduledTask':
|
case 'createScheduledTask':
|
||||||
handleScheduledTask();
|
handleScheduledTask();
|
||||||
|
@ -928,7 +955,8 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
loadPlanList,
|
fetchData,
|
||||||
|
emitTableParams,
|
||||||
});
|
});
|
||||||
|
|
||||||
await tableStore.initColumn(TableKeyEnum.TEST_PLAN_ALL_TABLE, columns, 'drawer');
|
await tableStore.initColumn(TableKeyEnum.TEST_PLAN_ALL_TABLE, columns, 'drawer');
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<MsTree
|
<MsTree
|
||||||
v-model:focus-node-key="focusNodeKey"
|
v-model:focus-node-key="focusNodeKey"
|
||||||
:selected-keys="props.selectedKeys"
|
:selected-keys="props.selectedKeys"
|
||||||
:data="caseTree"
|
:data="testPlanTree"
|
||||||
:keyword="groupKeyword"
|
:keyword="groupKeyword"
|
||||||
:node-more-actions="caseMoreActions"
|
:node-more-actions="caseMoreActions"
|
||||||
:expand-all="props.isExpandAll"
|
:expand-all="props.isExpandAll"
|
||||||
|
@ -18,8 +18,8 @@
|
||||||
count: 'count',
|
count: 'count',
|
||||||
}"
|
}"
|
||||||
title-tooltip-position="left"
|
title-tooltip-position="left"
|
||||||
@select="caseNodeSelect"
|
@select="planNodeSelect"
|
||||||
@more-action-select="handleCaseMoreSelect"
|
@more-action-select="handlePlanMoreSelect"
|
||||||
@more-actions-close="moreActionsClose"
|
@more-actions-close="moreActionsClose"
|
||||||
@drop="handleDrag"
|
@drop="handleDrag"
|
||||||
>
|
>
|
||||||
|
@ -111,7 +111,7 @@
|
||||||
|
|
||||||
const groupKeyword = ref<string>('');
|
const groupKeyword = ref<string>('');
|
||||||
|
|
||||||
const caseTree = ref<ModuleTreeNode[]>([]);
|
const testPlanTree = ref<ModuleTreeNode[]>([]);
|
||||||
|
|
||||||
const setFocusKey = (node: MsTreeNodeData) => {
|
const setFocusKey = (node: MsTreeNodeData) => {
|
||||||
focusNodeKey.value = node.id || '';
|
focusNodeKey.value = node.id || '';
|
||||||
|
@ -152,19 +152,18 @@
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const res = await getTestPlanModule({ projectId: currentProjectId.value });
|
const res = await getTestPlanModule({ projectId: currentProjectId.value });
|
||||||
caseTree.value = mapTree<ModuleTreeNode>(res, (e) => {
|
testPlanTree.value = mapTree<ModuleTreeNode>(res, (e) => {
|
||||||
return {
|
return {
|
||||||
...e,
|
...e,
|
||||||
hideMoreAction: e.id === 'root',
|
hideMoreAction: e.id === 'root',
|
||||||
draggable: e.id !== 'root',
|
draggable: e.id !== 'root',
|
||||||
disabled: e.id === props.activeFolder,
|
|
||||||
count: props.modulesCount?.[e.id] || 0,
|
count: props.modulesCount?.[e.id] || 0,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
if (isSetDefaultKey) {
|
if (isSetDefaultKey) {
|
||||||
selectedNodeKeys.value = [caseTree.value[0].id];
|
selectedNodeKeys.value = [testPlanTree.value[0].id];
|
||||||
}
|
}
|
||||||
emits('init', caseTree.value);
|
emits('init', testPlanTree.value);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
@ -188,7 +187,7 @@
|
||||||
try {
|
try {
|
||||||
await deletePlanModuleTree(node.id);
|
await deletePlanModuleTree(node.id);
|
||||||
Message.success(t('common.deleteSuccess'));
|
Message.success(t('common.deleteSuccess'));
|
||||||
initModules(true);
|
initModules(selectedNodeKeys.value[0] === node.id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
@ -207,7 +206,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用例树节点选中事件
|
// 用例树节点选中事件
|
||||||
const caseNodeSelect = (selectedKeys: (string | number)[], node: MsTreeNodeData) => {
|
const planNodeSelect = (selectedKeys: (string | number)[], node: MsTreeNodeData) => {
|
||||||
const offspringIds: string[] = [];
|
const offspringIds: string[] = [];
|
||||||
mapTree(node.children || [], (e) => {
|
mapTree(node.children || [], (e) => {
|
||||||
offspringIds.push(e.id);
|
offspringIds.push(e.id);
|
||||||
|
@ -217,7 +216,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
// 用例树节点更多事件
|
// 用例树节点更多事件
|
||||||
const handleCaseMoreSelect = (item: ActionsItem, node: MsTreeNodeData) => {
|
const handlePlanMoreSelect = (item: ActionsItem, node: MsTreeNodeData) => {
|
||||||
switch (item.eventTag) {
|
switch (item.eventTag) {
|
||||||
case 'delete':
|
case 'delete':
|
||||||
deleteHandler(node);
|
deleteHandler(node);
|
||||||
|
@ -266,7 +265,7 @@
|
||||||
if (dropPosition === 0) {
|
if (dropPosition === 0) {
|
||||||
treeNode.value.children.push(dragNode);
|
treeNode.value.children.push(dragNode);
|
||||||
}
|
}
|
||||||
caseNodeSelect(dropNode.id, treeNode.value);
|
planNodeSelect(dropNode.id, treeNode.value);
|
||||||
emits('dragUpdate');
|
emits('dragUpdate');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -347,7 +346,7 @@
|
||||||
watch(
|
watch(
|
||||||
() => props.modulesCount,
|
() => props.modulesCount,
|
||||||
(obj) => {
|
(obj) => {
|
||||||
caseTree.value = mapTree<ModuleTreeNode>(caseTree.value, (node) => {
|
testPlanTree.value = mapTree<ModuleTreeNode>(testPlanTree.value, (node) => {
|
||||||
return {
|
return {
|
||||||
...node,
|
...node,
|
||||||
count: obj?.[node.id] || 0,
|
count: obj?.[node.id] || 0,
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
<template>
|
||||||
|
<a-popover position="bottom" content-class="case-count-popover" @popup-visible-change="popupChange">
|
||||||
|
<div class="one-line-text cursor-pointer px-0 text-[rgb(var(--primary-5))]">{{
|
||||||
|
props.record.relateCase.length
|
||||||
|
}}</div>
|
||||||
|
<template #content>
|
||||||
|
<div class="w-[500px]">
|
||||||
|
<MsBaseTable v-bind="propsRes" v-on="propsEvent"></MsBaseTable>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-popover>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||||
|
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||||
|
import useTable from '@/components/pure/ms-table/useTable';
|
||||||
|
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
|
import type { PlanDetailBugItem } from '@/models/testPlan/testPlan';
|
||||||
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
record: PlanDetailBugItem;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const columns: MsTableColumn = [
|
||||||
|
{
|
||||||
|
title: 'caseManagement.featureCase.tableColumnID',
|
||||||
|
dataIndex: 'num',
|
||||||
|
width: 100,
|
||||||
|
showInTable: true,
|
||||||
|
showTooltip: true,
|
||||||
|
showDrag: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'case.caseName',
|
||||||
|
slotName: 'name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
showInTable: true,
|
||||||
|
showTooltip: true,
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const { propsRes, propsEvent } = useTable(undefined, {
|
||||||
|
columns,
|
||||||
|
tableKey: TableKeyEnum.TEST_PLAN_DETAIL_BUG_TABLE_CASE_COUNT,
|
||||||
|
scroll: { x: '100%' },
|
||||||
|
showSelectorAll: false,
|
||||||
|
heightUsed: 340,
|
||||||
|
enableDrag: false,
|
||||||
|
showPagination: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
function popupChange() {
|
||||||
|
propsRes.value.data = props.record.relateCase;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.case-count-popover {
|
||||||
|
width: 540px;
|
||||||
|
height: 500px;
|
||||||
|
.arco-popover-content {
|
||||||
|
@apply h-full;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,161 @@
|
||||||
|
<template>
|
||||||
|
<div class="p-[16px]">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div
|
||||||
|
>{{ t('testPlan.bugManagement.bug') }}
|
||||||
|
<span class="!text-[var(--color-text-n4)]">({{ addCommasToNumber(count) }})</span>
|
||||||
|
</div>
|
||||||
|
<a-input-search
|
||||||
|
v-model:model-value="keyword"
|
||||||
|
:placeholder="t('caseManagement.featureCase.searchByName')"
|
||||||
|
allow-clear
|
||||||
|
class="mx-[8px] w-[240px]"
|
||||||
|
@search="getFetch"
|
||||||
|
@press-enter="getFetch"
|
||||||
|
@clear="getFetch"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<MsBaseTable ref="tableRef" v-bind="propsRes" v-on="propsEvent">
|
||||||
|
<template #num="{ record }">
|
||||||
|
<a-tooltip :content="`${record.num}`">
|
||||||
|
<a-button type="text" class="px-0 !text-[14px] !leading-[22px]" size="mini">
|
||||||
|
<div class="one-line-text max-w-[168px]">{{ record.num }}</div>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
<template #name="{ record }">
|
||||||
|
<span class="one-line-text max-w-[300px]"> {{ record.name }}</span>
|
||||||
|
<a-popover title="" position="right" style="width: 480px">
|
||||||
|
<span class="ml-1 text-[rgb(var(--primary-5))]">{{ t('caseManagement.featureCase.preview') }}</span>
|
||||||
|
<template #content>
|
||||||
|
<div v-dompurify-html="record.content" class="markdown-body" style="margin-left: 48px"> </div>
|
||||||
|
</template>
|
||||||
|
</a-popover>
|
||||||
|
</template>
|
||||||
|
<template #linkCase="{ record }">
|
||||||
|
<CaseCountPopover :record="record" />
|
||||||
|
</template>
|
||||||
|
</MsBaseTable>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||||
|
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||||
|
import useTable from '@/components/pure/ms-table/useTable';
|
||||||
|
import CaseCountPopover from './caseCountPopover.vue';
|
||||||
|
|
||||||
|
import { planDetailBugPage } from '@/api/modules/test-plan/testPlan';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useAppStore from '@/store/modules/app';
|
||||||
|
import { addCommasToNumber } from '@/utils';
|
||||||
|
|
||||||
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const appStore = useAppStore();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
planId: string | undefined;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const keyword = ref<string>('');
|
||||||
|
|
||||||
|
function getFetch() {}
|
||||||
|
|
||||||
|
const columns: MsTableColumn = [
|
||||||
|
{
|
||||||
|
title: 'ID',
|
||||||
|
dataIndex: 'num',
|
||||||
|
slotName: 'num',
|
||||||
|
sortIndex: 1,
|
||||||
|
sortable: {
|
||||||
|
sortDirections: ['ascend', 'descend'],
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
showTooltip: true,
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'testPlan.bugManagement.bugName',
|
||||||
|
slotName: 'title',
|
||||||
|
dataIndex: 'title',
|
||||||
|
showInTable: true,
|
||||||
|
showTooltip: false,
|
||||||
|
width: 300,
|
||||||
|
ellipsis: true,
|
||||||
|
showDrag: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'testPlan.bugManagement.defectState',
|
||||||
|
slotName: 'statusName',
|
||||||
|
dataIndex: 'statusName',
|
||||||
|
showInTable: true,
|
||||||
|
showTooltip: true,
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true,
|
||||||
|
showDrag: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'caseManagement.featureCase.linkCase',
|
||||||
|
slotName: 'linkCase',
|
||||||
|
dataIndex: 'linkCase',
|
||||||
|
showInTable: true,
|
||||||
|
showTooltip: true,
|
||||||
|
width: 300,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'caseManagement.featureCase.updateUser',
|
||||||
|
slotName: 'handleUser',
|
||||||
|
dataIndex: 'handleUser',
|
||||||
|
titleSlotName: 'handleUserFilter',
|
||||||
|
showInTable: true,
|
||||||
|
showTooltip: true,
|
||||||
|
width: 300,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'common.createTime',
|
||||||
|
slotName: 'createTime',
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
showInTable: true,
|
||||||
|
sortable: {
|
||||||
|
sortDirections: ['ascend', 'descend'],
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
width: 200,
|
||||||
|
showDrag: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const { propsRes, propsEvent, loadList, setLoadListParams } = useTable(planDetailBugPage, {
|
||||||
|
columns,
|
||||||
|
tableKey: TableKeyEnum.TEST_PLAN_DETAIL_BUG_TABLE,
|
||||||
|
scroll: { x: '100%' },
|
||||||
|
showSelectorAll: false,
|
||||||
|
heightUsed: 340,
|
||||||
|
enableDrag: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const count = computed(() => {
|
||||||
|
return propsRes.value.msPagination?.total || 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
function initData() {
|
||||||
|
setLoadListParams({
|
||||||
|
planId: props.planId,
|
||||||
|
keyword: keyword.value,
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
});
|
||||||
|
loadList();
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
initData();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -72,7 +72,9 @@
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
</MsCard>
|
</MsCard>
|
||||||
<!-- special-height的174: 上面卡片高度158 + mt的16 -->
|
<!-- special-height的174: 上面卡片高度158 + mt的16 -->
|
||||||
<MsCard class="mt-[16px]" :special-height="174" simple has-breadcrumb no-content-padding></MsCard>
|
<MsCard class="mt-[16px]" :special-height="174" simple has-breadcrumb no-content-padding>
|
||||||
|
<BugManagement v-if="activeTab === 'defectList'" :plan-id="detail.id" />
|
||||||
|
</MsCard>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -84,6 +86,7 @@
|
||||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
||||||
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||||
|
import BugManagement from './bugManagement/index.vue';
|
||||||
import passRateLine from '@/views/case-management/caseReview/components/passRateLine.vue';
|
import passRateLine from '@/views/case-management/caseReview/components/passRateLine.vue';
|
||||||
import statusTag from '@/views/case-management/caseReview/components/statusTag.vue';
|
import statusTag from '@/views/case-management/caseReview/components/statusTag.vue';
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,7 @@
|
||||||
:modules-count="modulesCount"
|
:modules-count="modulesCount"
|
||||||
@plan-tree-node-select="planNodeSelect"
|
@plan-tree-node-select="planNodeSelect"
|
||||||
@init="setRootModules"
|
@init="setRootModules"
|
||||||
|
@drag-update="dragUpdate"
|
||||||
></TestPlanTree>
|
></TestPlanTree>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -244,7 +245,10 @@
|
||||||
planId.value = '';
|
planId.value = '';
|
||||||
}
|
}
|
||||||
function loadPlanList() {
|
function loadPlanList() {
|
||||||
planTableRef.value?.loadPlanList();
|
planTableRef.value?.fetchData();
|
||||||
|
}
|
||||||
|
function dragUpdate() {
|
||||||
|
planTableRef.value?.emitTableParams();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -81,4 +81,8 @@ export default {
|
||||||
'testPlan.planForm.repeatCaseTip2': 'Close: Cannot be associated with the same case repeatedly',
|
'testPlan.planForm.repeatCaseTip2': 'Close: Cannot be associated with the same case repeatedly',
|
||||||
'testPlan.planForm.pickCases': 'Select cases',
|
'testPlan.planForm.pickCases': 'Select cases',
|
||||||
'testPlan.testPlanDetail.executed': 'Executed',
|
'testPlan.testPlanDetail.executed': 'Executed',
|
||||||
|
'testPlan.bugManagement.bug': 'Defect list',
|
||||||
|
'testPlan.bugManagement.bugName': 'name',
|
||||||
|
'testPlan.bugManagement.defectState': 'Defect state',
|
||||||
|
'testPlan.bugManagement.caseClassification': 'Classification',
|
||||||
};
|
};
|
||||||
|
|
|
@ -79,4 +79,8 @@ export default {
|
||||||
'testPlan.planForm.repeatCaseTip2': '关闭:不可重复关联同一用例',
|
'testPlan.planForm.repeatCaseTip2': '关闭:不可重复关联同一用例',
|
||||||
'testPlan.planForm.pickCases': '选择用例',
|
'testPlan.planForm.pickCases': '选择用例',
|
||||||
'testPlan.testPlanDetail.executed': '已执行',
|
'testPlan.testPlanDetail.executed': '已执行',
|
||||||
|
'testPlan.bugManagement.bug': '缺陷列表',
|
||||||
|
'testPlan.bugManagement.bugName': '名称',
|
||||||
|
'testPlan.bugManagement.defectState': '缺陷状态',
|
||||||
|
'testPlan.bugManagement.caseClassification': '用例分类',
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue