fix(测试计划): 修复测试计划bug&修复报告图表自定义tooltip样式

This commit is contained in:
xinxin.wu 2024-06-19 18:02:06 +08:00 committed by Craftsman
parent cf5f9a0b4f
commit ff91f4b77d
26 changed files with 303 additions and 119 deletions

View File

@ -186,8 +186,16 @@
return undefined;
}
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector, setPagination, resetFilterParams } =
useTable(
const {
propsRes,
propsEvent,
loadList,
setLoadListParams,
resetSelector,
setPagination,
resetFilterParams,
setTableSelected,
} = useTable(
getPageList.value,
{
columns,
@ -212,11 +220,13 @@
);
async function getTableParams() {
const { excludeKeys } = propsRes.value;
return {
keyword: props.keyword,
projectId: props.currentProject,
moduleIds: props.activeModule === 'all' || !props.activeModule ? [] : [props.activeModule, ...props.offspringIds],
excludeIds: [...(props.associatedIds || [])], // id
excludeIds: [...excludeKeys],
condition: {
keyword: props.keyword,
},
@ -226,6 +236,11 @@
}
async function getModuleCount() {
if (props.associatedIds && props.associatedIds.length) {
props.associatedIds.forEach((hasNotAssociatedId) => {
setTableSelected(hasNotAssociatedId);
});
}
const tableParams = await getTableParams();
emit('getModuleCount', {
...tableParams,
@ -235,6 +250,11 @@
}
async function loadCaseList() {
if (props.associatedIds && props.associatedIds.length) {
props.associatedIds.forEach((hasNotAssociatedId) => {
setTableSelected(hasNotAssociatedId);
});
}
const tableParams = await getTableParams();
setLoadListParams(tableParams);
loadList();
@ -296,7 +316,7 @@
const tableParams = getTableParams();
return {
...tableParams,
excludeIds: [...excludeKeys].concat(...(props.associatedIds || [])),
excludeIds: [...excludeKeys],
selectIds: selectorStatus === 'all' ? [] : [...selectedKeys],
selectAll: selectorStatus === 'all',
associateApiType: 'API_CASE',

View File

@ -160,8 +160,16 @@
},
];
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector, setPagination, resetFilterParams } =
useTable(getPublicLinkCaseListMap[props.getPageApiType][props.activeSourceType].API, {
const {
propsRes,
propsEvent,
loadList,
setLoadListParams,
resetSelector,
setPagination,
resetFilterParams,
setTableSelected,
} = useTable(getPublicLinkCaseListMap[props.getPageApiType][props.activeSourceType].API, {
columns,
showSetting: false,
selectable: true,
@ -171,12 +179,13 @@
});
async function getTableParams() {
const { excludeKeys } = propsRes.value;
return {
keyword: props.keyword,
projectId: props.currentProject,
protocols: props.protocols,
moduleIds: props.activeModule === 'all' || !props.activeModule ? [] : [props.activeModule, ...props.offspringIds],
excludeIds: [...(props.associatedIds || [])], // id
excludeIds: [...excludeKeys],
condition: {
keyword: props.keyword,
},
@ -194,6 +203,11 @@
}
async function loadApiList() {
if (props.associatedIds && props.associatedIds.length) {
props.associatedIds.forEach((hasNotAssociatedId) => {
setTableSelected(hasNotAssociatedId);
});
}
const tableParams = await getTableParams();
setLoadListParams(tableParams);
loadList();
@ -255,7 +269,7 @@
const tableParams = getTableParams();
return {
...tableParams,
excludeIds: [...excludeKeys].concat(...(props.associatedIds || [])),
excludeIds: [...excludeKeys],
selectIds: selectorStatus === 'all' ? [] : [...selectedKeys],
selectAll: selectorStatus === 'all',
associateApiType: 'API',

View File

@ -82,6 +82,7 @@
(e: 'initModules'): void;
(e: 'update:selectedIds'): void;
}>();
const innerSelectedIds = defineModel<string[]>('selectedIds', { required: true });
const reviewResultOptions = computed(() => {
return Object.keys(statusIconMap).map((key) => {
@ -194,8 +195,16 @@
return undefined;
}
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector, setPagination, resetFilterParams } =
useTable(
const {
propsRes,
propsEvent,
loadList,
setLoadListParams,
resetSelector,
setPagination,
resetFilterParams,
setTableSelected,
} = useTable(
getPageList.value,
{
columns,
@ -220,11 +229,12 @@
);
async function getTableParams() {
const { excludeKeys } = propsRes.value;
return {
keyword: props.keyword,
projectId: props.currentProject,
moduleIds: props.activeModule === 'all' || !props.activeModule ? [] : [props.activeModule, ...props.offspringIds],
excludeIds: [...(props.associatedIds || [])], // id
excludeIds: [...excludeKeys],
condition: {
keyword: props.keyword,
filter: propsRes.value.filter,
@ -243,6 +253,11 @@
}
async function loadCaseList() {
if (props.associatedIds && props.associatedIds.length) {
props.associatedIds.forEach((hasNotAssociatedId) => {
setTableSelected(hasNotAssociatedId);
});
}
const tableParams = await getTableParams();
setLoadListParams(tableParams);
loadList();
@ -256,11 +271,12 @@
const tableRef = ref<InstanceType<typeof MsBaseTable>>();
function getFunctionalSaveParams() {
console.log(111);
const { excludeKeys, selectedKeys, selectorStatus } = propsRes.value;
const tableParams = getTableParams();
return {
...tableParams,
excludeIds: [...excludeKeys].concat(...(props.associatedIds || [])),
excludeIds: [...excludeKeys],
selectIds: selectorStatus === 'all' ? [] : [...selectedKeys],
selectAll: selectorStatus === 'all',
};
@ -278,8 +294,6 @@
}
);
const innerSelectedIds = defineModel<string[]>('selectedIds', { required: true });
const selectIds = computed(() => {
return [...propsRes.value.selectedKeys];
});

View File

@ -445,6 +445,9 @@
const selectPopVisible = ref<boolean>(false);
function loadCaseList() {
if (props.associatedIds && props.associatedIds.length > 0) {
selectedIds.value = props.associatedIds;
}
switch (associationType.value) {
case CaseLinkEnum.FUNCTIONAL:
return functionalTableRef.value?.loadCaseList();

View File

@ -180,8 +180,16 @@
const getPageList = computed(() => {
return getPublicLinkCaseListMap[props.getPageApiType][props.activeSourceType];
});
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector, setPagination, resetFilterParams } =
useTable(getPageList.value, {
const {
propsRes,
propsEvent,
loadList,
setLoadListParams,
resetSelector,
setPagination,
resetFilterParams,
setTableSelected,
} = useTable(getPageList.value, {
columns,
showSetting: false,
selectable: true,
@ -191,11 +199,12 @@
});
async function getTableParams() {
const { excludeKeys } = propsRes.value;
return {
keyword: props.keyword,
projectId: props.currentProject,
moduleIds: props.activeModule === 'all' || !props.activeModule ? [] : [props.activeModule, ...props.offspringIds],
excludeIds: [...(props.associatedIds || [])], // id
excludeIds: [...excludeKeys],
condition: {
keyword: props.keyword,
filter: propsRes.value.filter,
@ -214,6 +223,11 @@
}
async function loadScenarioList() {
if (props.associatedIds && props.associatedIds.length) {
props.associatedIds.forEach((hasNotAssociatedId) => {
setTableSelected(hasNotAssociatedId);
});
}
const tableParams = await getTableParams();
setLoadListParams(tableParams);
loadList();
@ -255,7 +269,7 @@
const tableParams = getTableParams();
return {
...tableParams,
excludeIds: [...excludeKeys].concat(...(props.associatedIds || [])),
excludeIds: [...excludeKeys],
selectIds: selectorStatus === 'all' ? [] : [...selectedKeys],
selectAll: selectorStatus === 'all',
};

View File

@ -55,7 +55,7 @@
async function saveHandler(params: AssociateCaseRequest) {
if (typeof props.saveApi !== 'function') {
emit('success', { ...params, functionalSelectIds: params.selectIds });
emit('success', { ...params });
} else {
try {
confirmLoading.value = true;
@ -63,7 +63,7 @@
functionalSelectIds: params.selectIds,
testPlanId: planId.value,
});
emit('success', { ...params, functionalSelectIds: params.selectIds });
emit('success', { ...params });
Message.success(t('ms.case.associate.associateSuccess'));
} catch (error) {
// eslint-disable-next-line no-console

View File

@ -107,7 +107,7 @@
<div class="flex items-center">
<div class="text-[var(--color-text-2)]">
{{
t('caseManagement.caseReview.selectedCases', {
t('ms.minders.selectedCases', {
count: selectedAssociateCasesParams.selectAll
? selectedAssociateCasesParams.totalCount
: selectedAssociateCasesParams.selectIds.length,
@ -769,7 +769,6 @@
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
selectedAssociateCasesParams.value.selectIds = [];
} finally {
loading.value = false;
tempMinderParams.value = {
@ -777,6 +776,7 @@
editList: [],
deletedIds: [],
};
selectedAssociateCasesParams.value.selectIds = [];
}
}

View File

@ -19,4 +19,5 @@ export default {
'ms.minders.env': 'Environment',
'ms.minders.item': '{count} Strip',
'ms.minders.unsavedTip': 'Please save your changed configuration first!',
'ms.minders.selectedCases': '{count} is selected',
};

View File

@ -19,4 +19,5 @@ export default {
'ms.minders.env': '环境',
'ms.minders.item': '{count}条',
'ms.minders.unsavedTip': '请先保存您更改的配置!',
'ms.minders.selectedCases': '已选 {count} 条',
};

View File

@ -988,6 +988,28 @@ export const pathMap: PathMapItem[] = [
route: RouteEnum.TEST_PLAN_REPORT,
permission: [],
level: MENU_LEVEL[2],
children: [
{
key: 'TEST_PLAN_REPORT_TEST_PLAN', // 测试计划报告
locale: 'menu.apiTest.reportTestPlan',
route: RouteEnum.TEST_PLAN_REPORT,
permission: [],
level: MENU_LEVEL[2],
routeQuery: {
type: 'TEST_PLAN',
},
},
{
key: 'TEST_PLAN_REPORT_TEST_PLAN_GROUP', // 测试计划组报告
locale: 'menu.apiTest.reportTestGroupPlan',
route: RouteEnum.TEST_PLAN_REPORT,
permission: [],
level: MENU_LEVEL[2],
routeQuery: {
type: 'GROUP',
},
},
],
},
],
},

View File

@ -1,5 +1,7 @@
import { cloneDeep } from 'lodash-es';
import { addCommasToNumber } from '@/utils';
import type { PassRateCountDetail, planStatusType, TestPlanDetail } from '@/models/testPlan/testPlan';
import type { countDetail, PlanReportDetail, StatusListType } from '@/models/testPlan/testPlanReport';
import { LastExecuteResults } from '@/enums/caseEnum';
@ -134,6 +136,31 @@ export const statusConfig: StatusListType[] = [
},
];
export const toolTipConfig = {
show: true,
trigger: 'item',
label: {
color: '#959598',
},
backgroundColor: '#fff',
padding: 24,
borderWidth: 0,
formatter(params: any) {
const html = `
<div class="w-[144px] flex items-center justify-between">
<div class=" flex items-center">
<div class="mb-[2px] mr-[8px] h-[6px] w-[6px] rounded-full bg-[${params.color}]" style="background:${
params.color
}"></div>
<div style="color:#959598">${params.name}</div>
</div>
<div class="text-[#323233] font-medium">${addCommasToNumber(params.value)}</div>
</div>
`;
return html;
},
};
export const commonConfig = {
tooltip: {
show: false,

View File

@ -164,6 +164,7 @@ export default {
'common.image': 'Image',
'common.text': 'Text',
'common.resourceDeleted': 'Resource has been deleted',
'common.resourceExpired': 'Link has failed, please get it again',
'common.refresh': 'Refresh',
'common.searchByNameAndId': 'Search by ID, name, or tag',
'common.archive': 'archive',

View File

@ -40,6 +40,8 @@ export default {
'menu.apiTest.apiScenario': 'Scenario',
'menu.apiTest.scenario.recycle': 'Recycle',
'menu.apiTest.report': 'Report',
'menu.apiTest.reportTestPlan': 'Test report',
'menu.apiTest.reportTestGroupPlan': 'Test Group Report',
'menu.apiTest.reportDetail': 'Report Detail',
'menu.uiTest': 'UI Test',
'menu.performanceTest': 'Performance Test',

View File

@ -126,7 +126,7 @@ export default {
'common.move': '移动',
'common.moveSuccess': '移动成功',
'common.batchMove': '批量移动',
'common.batchArchiveSuccess': '归档成功',
'common.batchArchiveSuccess': '归档成功',
'common.batchCopy': '批量复制',
'common.batchCopySuccess': '批量复制成功',
'common.batchMoveSuccess': '批量移动成功',
@ -163,6 +163,7 @@ export default {
'common.image': '图片',
'common.text': '文本',
'common.resourceDeleted': '资源已被删除',
'common.resourceExpired': '链接已失效,请重新获取',
'common.refresh': '刷新',
'common.searchByIdName': '通过ID 或名称搜索',
'common.searchByIDNameTag': '通过ID、名称或标签搜索',

View File

@ -39,6 +39,8 @@ export default {
'menu.apiTest.scenario': '场景',
'menu.apiTest.scenario.recycle': '回收站',
'menu.apiTest.report': '报告',
'menu.apiTest.reportTestPlan': '测试报告',
'menu.apiTest.reportTestGroupPlan': '测试组报告',
'menu.apiTest.reportDetail': '报告详情',
'menu.uiTest': 'UI测试',
'menu.workstation': '工作台',

View File

@ -17,9 +17,10 @@
</template>
</a-popover>
</div>
<a-popover position="bottom" content-class="response-popover-content">
<!-- TODO 汇总图例暂时不要了 -->
<!-- <a-popover position="bottom" content-class="response-popover-content"> -->
<div> <MsChart :width="props.size || '120px'" :height="props.size || '120px'" :options="props.options" /></div>
<template #content>
<!-- <template #content>
<div class="min-w-[176px] max-w-[400px] p-4">
<div v-for="item of legendData" :key="item.value" class="mb-2 flex flex-nowrap justify-between">
<div class="chart-flag flex flex-nowrap items-center">
@ -29,8 +30,8 @@
<div class="count font-medium">{{ item.count || 0 }}</div>
</div>
</div>
</template>
</a-popover>
</template> -->
<!-- </a-popover> -->
</div>
<div class="chart-legend grid flex-1 gap-y-3">

View File

@ -179,6 +179,7 @@
import reportInfoHeader from './step/reportInfoHeaders.vue';
import TiledList from './tiledList.vue';
import { toolTipConfig } from '@/config/testPlan';
import { useI18n } from '@/hooks/useI18n';
import { addCommasToNumber, formatDuration } from '@/utils';
@ -248,8 +249,7 @@
const legendData = ref<LegendData[]>([]);
const charOptions = ref({
tooltip: {
trigger: 'item',
show: false,
...toolTipConfig,
},
legend: {
show: false,

View File

@ -58,6 +58,7 @@
import TiledList from './tiledList.vue';
import ReportMetricsItem from '@/views/test-plan/report/detail/component/ReportMetricsItem.vue';
import { toolTipConfig } from '@/config/testPlan';
import { useI18n } from '@/hooks/useI18n';
import { formatDuration } from '@/utils';
@ -164,8 +165,7 @@
const stepAnalysisLegendData = ref<LegendData[]>([]);
const defaultCharOptions = {
tooltip: {
show: false,
trigger: 'item',
...toolTipConfig,
},
legend: {
show: false,

View File

@ -24,10 +24,22 @@
if (res.deleted) {
router.push({
name: NOT_FOUND_RESOURCE,
query: {
type: 'DELETE',
},
});
} else {
detail.value = await reportCaseDetail(res.reportId, route.query.shareId as string);
}
if (res.expired) {
router.push({
name: NOT_FOUND_RESOURCE,
query: {
type: 'EXPIRED',
},
});
return;
}
detail.value = await reportCaseDetail(res.reportId, route.query.shareId as string);
} catch (error) {
console.log(error);
}

View File

@ -23,10 +23,23 @@
if (res.deleted) {
router.push({
name: NOT_FOUND_RESOURCE,
query: {
type: 'DELETE',
},
});
} else {
detail.value = await reportScenarioDetail(res.reportId, route.query.shareId as string);
}
if (res.expired) {
router.push({
name: NOT_FOUND_RESOURCE,
query: {
type: 'EXPIRED',
},
});
return;
}
detail.value = await reportScenarioDetail(res.reportId, route.query.shareId as string);
} catch (error) {
console.log(error);
}

View File

@ -5,7 +5,7 @@
<div class="content">
<div class="no-resource-svg"></div>
<div class="title">
<span>{{ t('common.resourceDeleted') }}</span>
<span>{{ resourceType === 'DELETE' ? t('common.resourceDeleted') : t('common.resourceExpired') }}</span>
</div>
</div>
</div>
@ -14,11 +14,15 @@
</template>
<script lang="ts" setup>
import { useRouter } from 'vue-router';
import { useRoute } from 'vue-router';
import { useI18n } from '@/hooks/useI18n';
const { t } = useI18n();
const route = useRoute();
const resourceType = ref<string>(route.query.type as string);
</script>
<style lang="less">

View File

@ -48,15 +48,15 @@ export default {
'system.config.email.emailErrTip': 'Email format error, please re-enter',
'system.config.email.updateSuccess': 'Updated successfully',
'system.config.email.testSuccess': 'Email connection is successful',
'system.config.page.style': 'Platform style',
'system.config.page.style': 'Background color',
'system.config.page.styleTip': 'Platform style refers to the page background color style',
'system.config.page.theme': 'Platform theme color',
'system.config.page.theme': 'Theme color',
'system.config.page.themeTip':
'The platform theme color refers to the purple color of MeterSphere except the page background color',
'system.config.page.default': 'Default',
'system.config.page.follow': 'Follow theme',
'system.config.page.custom': 'Customize',
'system.config.page.loginPageConfig': 'Platform login page settings',
'system.config.page.loginPageConfig': 'Login Page Settings',
'system.config.page.pagePreview': 'Page preview',
'system.config.page.reset': 'Reset',
'system.config.page.icon': 'Website icon',

View File

@ -47,14 +47,14 @@ export default {
'system.config.email.emailErrTip': '邮箱格式错误,请重新输入',
'system.config.email.updateSuccess': '更新成功',
'system.config.email.testSuccess': '邮箱连接成功',
'system.config.page.style': '平台风格',
'system.config.page.style': '背景色',
'system.config.page.styleTip': '平台风格是指页面背景色风格',
'system.config.page.theme': '平台主题色',
'system.config.page.theme': '主题色',
'system.config.page.themeTip': '平台主题色是指除了页面背景色之外的 MeterSphere 紫色系颜色',
'system.config.page.default': '默认',
'system.config.page.follow': '跟随主题色',
'system.config.page.custom': '自定义',
'system.config.page.loginPageConfig': '平台登录页设置',
'system.config.page.loginPageConfig': '登录页设置',
'system.config.page.pagePreview': '页面预览',
'system.config.page.reset': '恢复默认',
'system.config.page.icon': '网站 Icon',

View File

@ -13,7 +13,7 @@
import SetReportChart from '@/views/api-test/report/component/case/setReportChart.vue';
import { commonConfig, seriesConfig, statusConfig } from '@/config/testPlan';
import { seriesConfig, statusConfig, toolTipConfig } from '@/config/testPlan';
import { useI18n } from '@/hooks/useI18n';
import type { LegendData } from '@/models/apiTest/report';
@ -31,7 +31,9 @@
//
const executeCharOptions = ref({
...commonConfig,
tooltip: {
...toolTipConfig,
},
series: {
...seriesConfig,
data: [

View File

@ -30,6 +30,19 @@
if (hrefShareDetail.deleted) {
router.push({
name: NOT_FOUND_RESOURCE,
query: {
type: 'DELETE',
},
});
return;
}
if (hrefShareDetail.expired) {
router.push({
name: NOT_FOUND_RESOURCE,
query: {
type: 'EXPIRED',
},
});
return;
}

View File

@ -252,7 +252,6 @@
<MsButton
v-if="isShowExecuteButton(record) && hasAnyPermission(['PROJECT_TEST_PLAN:READ+EXECUTE'])"
class="!mx-0"
:disabled="executeId == record.id"
@click="executePlan(record)"
>{{ t('testPlan.testPlanIndex.execution') }}</MsButton
>
@ -275,13 +274,21 @@
></a-divider>
<MsButton
v-if="!isShowExecuteButton(record) && hasAnyPermission(['PROJECT_TEST_PLAN:READ+ADD'])"
v-if="
!isShowExecuteButton(record) &&
hasAnyPermission(['PROJECT_TEST_PLAN:READ+ADD']) &&
record.status !== 'ARCHIVED'
"
class="!mx-0"
@click="copyTestPlanOrGroup(record.id)"
>{{ t('common.copy') }}</MsButton
>
<a-divider
v-if="!isShowExecuteButton(record) && hasAnyPermission(['PROJECT_TEST_PLAN:READ+ADD'])"
v-if="
!isShowExecuteButton(record) &&
hasAnyPermission(['PROJECT_TEST_PLAN:READ+ADD']) &&
record.status !== 'ARCHIVED'
"
direction="vertical"
:margin="8"
></a-divider>
@ -807,8 +814,16 @@
draggableCondition: true,
});
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector, resetFilterParams, setPagination } =
useTable(
const {
propsRes,
propsEvent,
loadList,
setLoadListParams,
resetSelector,
resetFilterParams,
setPagination,
setLoading,
} = useTable(
getTestPlanList,
tableProps.value,
(item) => {
@ -978,6 +993,7 @@
} finally {
executeId.value = '';
confirmLoading.value = false;
setLoading(false);
}
}
@ -1010,6 +1026,7 @@
},
});
} else {
setLoading(true);
singleExecute(record.id);
}
}