fix(缺陷管理): 关联用例部分问题
--bug=1036504 --user=宋昌昌 【缺陷管理】缺陷详情-关联用例-列表有数据不展示-无法关联 https://www.tapd.cn/55049933/s/1468978
This commit is contained in:
parent
abcdecb5e2
commit
dbacfc7d7c
|
@ -9,6 +9,9 @@ public class BugRelateCaseDTO{
|
|||
@Schema(description = "关联ID")
|
||||
private String relateId;
|
||||
|
||||
@Schema(description = "关联用例ID")
|
||||
private String relateCaseId;
|
||||
|
||||
@Schema(description = "关联用例名称")
|
||||
private String relateCaseName;
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
from functional_case_module fcm left join functional_case fc on fc.module_id = fcm.id
|
||||
where fc.deleted = #{deleted}
|
||||
and fc.project_id = #{request.projectId}
|
||||
and fc.version_id = #{request.versionId}
|
||||
and fc.id not in
|
||||
(
|
||||
select brc.case_id from bug_relation_case brc where brc.bug_id = #{request.sourceId} and brc.case_type = #{request.sourceType}
|
||||
|
@ -46,7 +45,7 @@
|
|||
</select>
|
||||
|
||||
<select id="list" resultType="io.metersphere.bug.dto.response.BugRelateCaseDTO">
|
||||
select fc.num relateId, fc.name relateCaseName, fc.project_id projectId, fc.version_id versionId, brc.case_type relateCaseType,
|
||||
select brc.id relateId, fc.num relateCaseId, fc.name relateCaseName, fc.project_id projectId, fc.version_id versionId, brc.case_type relateCaseType,
|
||||
brc.test_plan_id is not null relatePlanCase, brc.case_id is not null relateCase
|
||||
from bug_relation_case brc join functional_case fc on (brc.case_id = fc.id or brc.test_plan_case_id = fc.id)
|
||||
where brc.bug_id = #{request.bugId} and fc.deleted = false
|
||||
|
|
|
@ -713,7 +713,6 @@
|
|||
from functional_case fc left join project_version pv ON fc.version_id = pv.id
|
||||
where fc.deleted = #{deleted}
|
||||
and fc.project_id = #{request.projectId}
|
||||
and fc.version_id = #{request.versionId}
|
||||
and fc.id not in
|
||||
(
|
||||
select brc.case_id from bug_relation_case brc where brc.bug_id = #{request.sourceId} and brc.case_type = #{request.sourceType}
|
||||
|
|
|
@ -1,25 +1,45 @@
|
|||
package io.metersphere.functional.provider;
|
||||
|
||||
import io.metersphere.dto.TestCaseProviderDTO;
|
||||
import io.metersphere.functional.dto.FunctionalCaseCustomFieldDTO;
|
||||
import io.metersphere.functional.mapper.ExtFunctionalCaseMapper;
|
||||
import io.metersphere.functional.service.FunctionalCaseService;
|
||||
import io.metersphere.provider.BaseAssociateCaseProvider;
|
||||
import io.metersphere.request.AssociateOtherCaseRequest;
|
||||
import io.metersphere.request.TestCasePageProviderRequest;
|
||||
import io.metersphere.sdk.util.Translator;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class AssociateCaseProvider implements BaseAssociateCaseProvider {
|
||||
|
||||
@Resource
|
||||
private FunctionalCaseService functionalCaseService;
|
||||
@Resource
|
||||
private ExtFunctionalCaseMapper extFunctionalCaseMapper;
|
||||
|
||||
@Override
|
||||
public List<TestCaseProviderDTO> listUnRelatedTestCaseList(TestCasePageProviderRequest testCasePageProviderRequest) {
|
||||
return extFunctionalCaseMapper.listUnRelatedCaseWithBug(testCasePageProviderRequest, false, testCasePageProviderRequest.getSortString());
|
||||
List<TestCaseProviderDTO> functionalCases = extFunctionalCaseMapper.listUnRelatedCaseWithBug(testCasePageProviderRequest, false, testCasePageProviderRequest.getSortString());
|
||||
if (CollectionUtils.isEmpty(functionalCases)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<String> ids = functionalCases.stream().map(TestCaseProviderDTO::getId).toList();
|
||||
Map<String, List<FunctionalCaseCustomFieldDTO>> caseCustomFiledMap = functionalCaseService.getCaseCustomFiledMap(ids);
|
||||
functionalCases.forEach(functionalCase -> {
|
||||
List<FunctionalCaseCustomFieldDTO> customFields = caseCustomFiledMap.get(functionalCase.getId());
|
||||
Optional<FunctionalCaseCustomFieldDTO> priorityField = customFields.stream().filter(field -> StringUtils.equals(Translator.get("custom_field.functional_priority"), field.getFieldName())).findFirst();
|
||||
priorityField.ifPresent(functionalCaseCustomFieldDTO -> functionalCase.setPriority(functionalCaseCustomFieldDTO.getDefaultValue()));
|
||||
});
|
||||
return functionalCases;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -62,7 +62,7 @@ export const getDemandListUrl = '/bug/case/page';
|
|||
// 批量添加关联
|
||||
export const postAddDemandUrl = '/bug/case/relate';
|
||||
// 单个取消关联
|
||||
export const getCancelDemandUrl = '/bug/case/un-relate/';
|
||||
export const getCancelDemandUrl = '/bug/case/un-relate';
|
||||
// 未关联的用例列表
|
||||
export const getUnrelatedDemandListUrl = '/bug/case/un-relate/page';
|
||||
// 未关联的模块树
|
||||
|
|
|
@ -112,7 +112,7 @@
|
|||
</a-tooltip>
|
||||
</template>
|
||||
<template #caseLevel="{ record }">
|
||||
<caseLevel :case-level="getCaseLevel(record)" />
|
||||
<span>{{ t(record.priority) }}</span>
|
||||
</template>
|
||||
</ms-base-table>
|
||||
<div class="footer">
|
||||
|
@ -141,35 +141,33 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import {computed, ref, watch} from 'vue';
|
||||
import {useRouter} from 'vue-router';
|
||||
import {useVModel} from '@vueuse/core';
|
||||
|
||||
import { CustomTypeMaps, MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
|
||||
import { FilterFormItem, FilterType } from '@/components/pure/ms-advance-filter/type';
|
||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import MsProjectSelect from '@/components/business/ms-project-select/index.vue';
|
||||
import MsTree from '@/components/business/ms-tree/index.vue';
|
||||
import type { MsTreeNodeData } from '@/components/business/ms-tree/types';
|
||||
import caseLevel from './caseLevel.vue';
|
||||
import {CustomTypeMaps, MsAdvanceFilter} from '@/components/pure/ms-advance-filter';
|
||||
import {FilterFormItem, FilterType} from '@/components/pure/ms-advance-filter/type';
|
||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import {MsTableColumn} from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import MsProjectSelect from '@/components/business/ms-project-select/index.vue';
|
||||
import MsTree from '@/components/business/ms-tree/index.vue';
|
||||
import type {MsTreeNodeData} from '@/components/business/ms-tree/types';
|
||||
import caseLevel from './caseLevel.vue';
|
||||
|
||||
import { getCustomFieldsTable } from '@/api/modules/case-management/featureCase';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import { mapTree } from '@/utils';
|
||||
import {getCustomFieldsTable} from '@/api/modules/case-management/featureCase';
|
||||
import {useI18n} from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import {mapTree} from '@/utils';
|
||||
|
||||
import type { CaseManagementTable } from '@/models/caseManagement/featureCase';
|
||||
import type { CommonList, ModuleTreeNode, TableQueryParams } from '@/models/common';
|
||||
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
|
||||
import type {CaseManagementTable} from '@/models/caseManagement/featureCase';
|
||||
import type {CommonList, ModuleTreeNode, TableQueryParams} from '@/models/common';
|
||||
import {CaseManagementRouteEnum} from '@/enums/routeEnum';
|
||||
import {initGetModuleCountFunc, type RequestModuleEnum} from './utils';
|
||||
|
||||
import type { CaseLevel } from './types';
|
||||
import { initGetModuleCountFunc, type RequestModuleEnum } from './utils';
|
||||
|
||||
const router = useRouter();
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
|
@ -363,20 +361,7 @@
|
|||
title: 'ms.case.associate.tags',
|
||||
dataIndex: 'tags',
|
||||
isTag: true,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnCreateUser',
|
||||
dataIndex: 'createUserName',
|
||||
showInTable: true,
|
||||
width: 300,
|
||||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnCreateTime',
|
||||
slotName: 'createTime',
|
||||
dataIndex: 'createTime',
|
||||
showInTable: true,
|
||||
width: 300,
|
||||
},
|
||||
}
|
||||
];
|
||||
|
||||
const { propsRes, propsEvent, loadList, setLoadListParams, resetSelector } = useTable(
|
||||
|
@ -568,11 +553,6 @@
|
|||
emit('save', params);
|
||||
}
|
||||
|
||||
// 用例等级
|
||||
function getCaseLevel(record: CaseManagementTable) {
|
||||
return (record.customFields.find((item: any) => item.name === '用例等级')?.value as CaseLevel) || 'P1';
|
||||
}
|
||||
|
||||
function openDetail(id: string) {
|
||||
window.open(
|
||||
`${window.location.origin}#${
|
||||
|
|
|
@ -36,6 +36,7 @@ export default {
|
|||
'common.saveSuccess': 'Save success',
|
||||
'common.saveFailed': 'Save failed',
|
||||
'common.linkSuccess': 'Link success',
|
||||
'common.unLinkSuccess': 'Unlink success',
|
||||
'common.confirmEnable': 'Confirm enable',
|
||||
'common.confirmEnd': 'Confirm end',
|
||||
'common.confirmStart': 'Confirm start',
|
||||
|
@ -122,4 +123,6 @@ export default {
|
|||
'common.apply': 'Apply',
|
||||
'common.stop': 'Stop',
|
||||
'common.module': 'Module',
|
||||
'common.yes': 'Yes',
|
||||
'common.no': 'No'
|
||||
};
|
||||
|
|
|
@ -38,6 +38,7 @@ export default {
|
|||
'common.saveSuccess': '保存成功',
|
||||
'common.saveFailed': '保存失败',
|
||||
'common.linkSuccess': '关联成功',
|
||||
'common.unLinkSuccess': '取消关联成功',
|
||||
'common.confirmEnable': '确认启用',
|
||||
'common.confirmDisable': '确认禁用',
|
||||
'common.confirmClose': '确认关闭',
|
||||
|
@ -125,4 +126,6 @@ export default {
|
|||
'common.apply': '应用',
|
||||
'common.stop': '停止',
|
||||
'common.module': '模块',
|
||||
'common.yes': '是',
|
||||
'common.no': '否'
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
</div>
|
||||
<ms-base-table v-bind="propsRes" v-on="propsEvent">
|
||||
<template #defectName="{ record }">
|
||||
<span class="one-line-text max-w[300px]"> {{ record.name }}</span
|
||||
<span class="one-line-text max-w[300px]"> {{ record.relateCaseName }}</span
|
||||
><span class="ml-1 text-[rgb(var(--primary-5))]">{{ t('caseManagement.featureCase.preview') }}</span>
|
||||
</template>
|
||||
<template #operation="{ record }">
|
||||
|
@ -27,6 +27,29 @@
|
|||
</MsButton>
|
||||
</div>
|
||||
</template>
|
||||
<template #relatePlanTitle>
|
||||
<div class="flex items-center">
|
||||
<div class="font-medium text-[var(--color-text-3)]">
|
||||
{{ t('bugManagement.detail.isPlanRelateCase') }}
|
||||
</div>
|
||||
<a-popover position="rt">
|
||||
<icon-question-circle
|
||||
class="ml-[4px] text-[var(--color-text-3)] hover:text-[rgb(var(--primary-5))]"
|
||||
size="16"
|
||||
/>
|
||||
<template #title>
|
||||
<div class="w-[300px]"> {{ t('bugManagement.detail.isPlanRelateCaseTip1') }} </div>
|
||||
<br>
|
||||
<div class="w-[300px]"> {{ t('bugManagement.detail.isPlanRelateCaseTip2') }} </div>
|
||||
<br>
|
||||
<div class="w-[300px]"> {{ t('bugManagement.detail.isPlanRelateCaseTip3') }} </div>
|
||||
</template>
|
||||
</a-popover>
|
||||
</div>
|
||||
</template>
|
||||
<template #isRelatePlanCase = "{ record }">
|
||||
<span class="text-[var(--color-text-1)]">{{record.isRelatePlanCase ? t('common.yes') : t('common.no') }}</span>
|
||||
</template>
|
||||
</ms-base-table>
|
||||
<MsCaseAssociate
|
||||
v-model:visible="innerVisible"
|
||||
|
@ -98,7 +121,7 @@ const appStore = useAppStore();
|
|||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnID',
|
||||
dataIndex: 'id',
|
||||
dataIndex: 'relateCaseId',
|
||||
width: 200,
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
|
@ -107,8 +130,17 @@ const appStore = useAppStore();
|
|||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnName',
|
||||
slotName: 'name',
|
||||
dataIndex: 'name',
|
||||
dataIndex: 'relateCaseName',
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
showDrag: false,
|
||||
},
|
||||
{
|
||||
title: 'bugManagement.detail.isPlanRelateCase',
|
||||
titleSlotName: 'relatePlanTitle',
|
||||
slotName: 'isRelatePlanCase',
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
|
@ -127,8 +159,7 @@ const appStore = useAppStore();
|
|||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.tableColumnVersion',
|
||||
slotName: 'version',
|
||||
dataIndex: 'version',
|
||||
dataIndex: 'versionName',
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
|
@ -137,8 +168,7 @@ const appStore = useAppStore();
|
|||
},
|
||||
{
|
||||
title: 'caseManagement.featureCase.changeType',
|
||||
slotName: 'type',
|
||||
dataIndex: 'type',
|
||||
dataIndex: 'relateCaseTypeName',
|
||||
showInTable: true,
|
||||
showTooltip: true,
|
||||
width: 300,
|
||||
|
@ -194,8 +224,10 @@ const appStore = useAppStore();
|
|||
|
||||
async function cancelLink(record: any) {
|
||||
try {
|
||||
const { id } = record;
|
||||
await cancelAssociation(id);
|
||||
const { relateId } = record;
|
||||
await cancelAssociation(relateId);
|
||||
await getFetch();
|
||||
Message.success(t('common.unLinkSuccess'));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
@ -211,7 +243,8 @@ const appStore = useAppStore();
|
|||
try {
|
||||
confirmLoading.value = true;
|
||||
await batchAssociation(params);
|
||||
Message.success(t('caseManagement.featureCase.AssociatedSuccess'));
|
||||
await getFetch();
|
||||
Message.success(t('common.linkSuccess'));
|
||||
innerVisible.value = false;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
|
|
|
@ -59,6 +59,10 @@ export default {
|
|||
scenarioCase: 'Scenario Case',
|
||||
uiCase: 'UI Case',
|
||||
performanceCase: 'Performance Case',
|
||||
isPlanRelateCase: 'Relate plan',
|
||||
isPlanRelateCaseTip1: '在测试计划内执行用例时新建或关联的缺陷, 只可在测试计划内取消关联关系;',
|
||||
isPlanRelateCaseTip2: '缺陷在测试计划内执行用例时关联/新建, 显示为“是”;',
|
||||
isPlanRelateCaseTip3: '缺陷在缺陷页/用例详情页添加直接关联关系, 未关联上任何测试计划, 显示为“否”;',
|
||||
searchCase: 'Search by name',
|
||||
creator: 'Creator',
|
||||
createTime: 'Create Time',
|
||||
|
|
|
@ -59,6 +59,10 @@ export default {
|
|||
scenarioCase: '场景用例',
|
||||
uiCase: 'UI用例',
|
||||
performanceCase: '性能用例',
|
||||
isPlanRelateCase: '关联测试计划',
|
||||
isPlanRelateCaseTip1: '在测试计划内执行用例时新建或关联的缺陷, 只可在测试计划内取消关联关系;',
|
||||
isPlanRelateCaseTip2: '缺陷在测试计划内执行用例时关联/新建, 显示为“是”;',
|
||||
isPlanRelateCaseTip3: '缺陷在缺陷页/用例详情页添加直接关联关系, 未关联上任何测试计划, 显示为“否”;',
|
||||
searchCase: '通过名称搜索',
|
||||
creator: '创建人',
|
||||
createTime: '创建时间',
|
||||
|
|
Loading…
Reference in New Issue