feat(用例管理): 新增用例详情执行评论tab
This commit is contained in:
parent
2c0c4c07fa
commit
bcd06b5e27
|
@ -12,7 +12,9 @@ import io.metersphere.functional.excel.domain.FunctionalCaseExcelData;
|
||||||
import io.metersphere.functional.mapper.*;
|
import io.metersphere.functional.mapper.*;
|
||||||
import io.metersphere.functional.request.*;
|
import io.metersphere.functional.request.*;
|
||||||
import io.metersphere.functional.result.CaseManagementResultCode;
|
import io.metersphere.functional.result.CaseManagementResultCode;
|
||||||
|
import io.metersphere.plan.domain.TestPlanCaseExecuteHistoryExample;
|
||||||
import io.metersphere.plan.domain.TestPlanFunctionalCaseExample;
|
import io.metersphere.plan.domain.TestPlanFunctionalCaseExample;
|
||||||
|
import io.metersphere.plan.mapper.TestPlanCaseExecuteHistoryMapper;
|
||||||
import io.metersphere.plan.mapper.TestPlanFunctionalCaseMapper;
|
import io.metersphere.plan.mapper.TestPlanFunctionalCaseMapper;
|
||||||
import io.metersphere.project.domain.*;
|
import io.metersphere.project.domain.*;
|
||||||
import io.metersphere.project.dto.ModuleCountDTO;
|
import io.metersphere.project.dto.ModuleCountDTO;
|
||||||
|
@ -166,6 +168,8 @@ public class FunctionalCaseService {
|
||||||
@Resource
|
@Resource
|
||||||
private CaseReviewHistoryMapper caseReviewHistoryMapper;
|
private CaseReviewHistoryMapper caseReviewHistoryMapper;
|
||||||
@Resource
|
@Resource
|
||||||
|
private TestPlanCaseExecuteHistoryMapper testPlanCaseExecuteHistoryMapper;
|
||||||
|
@Resource
|
||||||
private FunctionalCaseCommentMapper functionalCaseCommentMapper;
|
private FunctionalCaseCommentMapper functionalCaseCommentMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private ProjectService projectService;
|
private ProjectService projectService;
|
||||||
|
@ -420,9 +424,12 @@ public class FunctionalCaseService {
|
||||||
FunctionalCaseCommentExample functionalCaseCommentExample = new FunctionalCaseCommentExample();
|
FunctionalCaseCommentExample functionalCaseCommentExample = new FunctionalCaseCommentExample();
|
||||||
functionalCaseCommentExample.createCriteria().andCaseIdEqualTo(functionalCaseDetailDTO.getId());
|
functionalCaseCommentExample.createCriteria().andCaseIdEqualTo(functionalCaseDetailDTO.getId());
|
||||||
long caseComment = functionalCaseCommentMapper.countByExample(functionalCaseCommentExample);
|
long caseComment = functionalCaseCommentMapper.countByExample(functionalCaseCommentExample);
|
||||||
long commentCount = caseComment + reviewComment;
|
//获取关联测试计划的执行评论数量
|
||||||
|
TestPlanCaseExecuteHistoryExample testPlanCaseExecuteHistoryExample = new TestPlanCaseExecuteHistoryExample();
|
||||||
|
testPlanCaseExecuteHistoryExample.createCriteria().andCaseIdEqualTo(functionalCaseDetailDTO.getId());
|
||||||
|
long testPlanExecuteComment = testPlanCaseExecuteHistoryMapper.countByExample(testPlanCaseExecuteHistoryExample);
|
||||||
|
long commentCount = caseComment + reviewComment + testPlanExecuteComment;
|
||||||
functionalCaseDetailDTO.setCommentCount((int) commentCount);
|
functionalCaseDetailDTO.setCommentCount((int) commentCount);
|
||||||
|
|
||||||
//获取变更历史数量数量
|
//获取变更历史数量数量
|
||||||
OperationHistoryExample operationHistoryExample = new OperationHistoryExample();
|
OperationHistoryExample operationHistoryExample = new OperationHistoryExample();
|
||||||
List<String> types = List.of(OperationLogType.ADD.name(), OperationLogType.IMPORT.name(), OperationLogType.UPDATE.name());
|
List<String> types = List.of(OperationLogType.ADD.name(), OperationLogType.IMPORT.name(), OperationLogType.UPDATE.name());
|
||||||
|
|
|
@ -52,6 +52,7 @@ import {
|
||||||
GetDependOnRelationUrl,
|
GetDependOnRelationUrl,
|
||||||
GetDetailCaseReviewUrl,
|
GetDetailCaseReviewUrl,
|
||||||
GetFileIsUpdateUrl,
|
GetFileIsUpdateUrl,
|
||||||
|
GetPlanExecuteCommentListUrl,
|
||||||
GetRecycleCaseListUrl,
|
GetRecycleCaseListUrl,
|
||||||
GetRecycleCaseModulesCountUrl,
|
GetRecycleCaseModulesCountUrl,
|
||||||
GetReviewCommentListUrl,
|
GetReviewCommentListUrl,
|
||||||
|
@ -435,4 +436,9 @@ export function getLinkedCaseTestPlanList(data: TableQueryParams) {
|
||||||
return MSR.post<CommonList<AssociateFunctionalCaseItem>>({ url: GetAssociatedTestPlanUrl, data });
|
return MSR.post<CommonList<AssociateFunctionalCaseItem>>({ url: GetAssociatedTestPlanUrl, data });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取执行评论
|
||||||
|
export function getTestPlanExecuteCommentList(caseId: string) {
|
||||||
|
return MSR.get<CommentItem[]>({ url: `${GetPlanExecuteCommentListUrl}/${caseId}` });
|
||||||
|
}
|
||||||
|
|
||||||
export default {};
|
export default {};
|
||||||
|
|
|
@ -154,3 +154,6 @@ export const associatedProjectOptionsUrl = '/project/list/options';
|
||||||
|
|
||||||
// 获取详情已关联测试计划列表
|
// 获取详情已关联测试计划列表
|
||||||
export const GetAssociatedTestPlanUrl = '/functional/case/test/has/associate/plan/page';
|
export const GetAssociatedTestPlanUrl = '/functional/case/test/has/associate/plan/page';
|
||||||
|
|
||||||
|
// 评审评论
|
||||||
|
export const GetPlanExecuteCommentListUrl = '/functional/case/test/plan/comment';
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<a-radio-group v-model="activeComment" type="button">
|
<a-radio-group v-model="activeComment" type="button">
|
||||||
<a-radio value="caseComment">{{ t('caseManagement.featureCase.caseComment') }}</a-radio>
|
<a-radio value="caseComment">{{ t('caseManagement.featureCase.caseComment') }}</a-radio>
|
||||||
<a-radio value="reviewComment">{{ t('caseManagement.featureCase.reviewComment') }}</a-radio>
|
<a-radio value="reviewComment">{{ t('caseManagement.featureCase.reviewComment') }}</a-radio>
|
||||||
<!-- <a-radio value="executiveComment">{{ t('caseManagement.featureCase.executiveReview') }}</a-radio> -->
|
<a-radio value="executiveComment">{{ t('caseManagement.featureCase.executiveReview') }}</a-radio>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -21,13 +21,20 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 评审评论 -->
|
<!-- 评审评论 -->
|
||||||
<div v-show="activeComment === 'reviewComment'" class="flex flex-1 flex-col overflow-hidden">
|
<div
|
||||||
|
v-show="activeComment === 'reviewComment' || activeComment === 'executiveComment'"
|
||||||
|
class="flex flex-1 flex-col overflow-hidden"
|
||||||
|
>
|
||||||
<div class="review-history-list">
|
<div class="review-history-list">
|
||||||
<div v-for="item of reviewCommentList" :key="item.id" class="review-history-list-item">
|
<div v-for="item of reviewCommentList" :key="item.id" class="review-history-list-item">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<MSAvatar :avatar="item.userLogo" />
|
<MSAvatar :avatar="item.userLogo" />
|
||||||
<div class="ml-[8px] flex items-center">
|
<div class="ml-[8px] flex items-center">
|
||||||
<div class="font-medium text-[var(--color-text-1)]">{{ item.userName }}</div>
|
<a-tooltip :content="item.userName" :mouse-enter-delay="300">
|
||||||
|
<div class="one-line-text max-w-[300px] font-medium text-[var(--color-text-1)]">{{
|
||||||
|
item.userName
|
||||||
|
}}</div>
|
||||||
|
</a-tooltip>
|
||||||
<a-divider direction="vertical" margin="8px"></a-divider>
|
<a-divider direction="vertical" margin="8px"></a-divider>
|
||||||
<div v-if="item.status === 'PASS'" class="flex items-center">
|
<div v-if="item.status === 'PASS'" class="flex items-center">
|
||||||
<MsIcon type="icon-icon_succeed_filled" class="mr-[4px] text-[rgb(var(--success-6))]" />
|
<MsIcon type="icon-icon_succeed_filled" class="mr-[4px] text-[rgb(var(--success-6))]" />
|
||||||
|
@ -45,24 +52,53 @@
|
||||||
<MsIcon type="icon-icon_resubmit_filled" class="mr-[4px] text-[rgb(var(--warning-6))]" />
|
<MsIcon type="icon-icon_resubmit_filled" class="mr-[4px] text-[rgb(var(--warning-6))]" />
|
||||||
{{ t('caseManagement.caseReview.reReview') }}
|
{{ t('caseManagement.caseReview.reReview') }}
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="item.status === 'PASSED'" class="flex items-center">
|
||||||
|
<MsIcon type="icon-icon_succeed_filled" class="mr-[4px] text-[rgb(var(--success-6))]" />
|
||||||
|
{{ t('caseManagement.featureCase.execute.success') }}
|
||||||
|
</div>
|
||||||
|
<div v-if="item.status === 'BLOCKED'" class="flex items-center">
|
||||||
|
<MsIcon type="icon-icon_succeed_filled" class="mr-[4px] text-[rgb(var(--success-6))]" />
|
||||||
|
{{ t('caseManagement.featureCase.execute.blocked') }}
|
||||||
|
</div>
|
||||||
|
<div v-if="item.status === 'FAILED'" class="flex items-center">
|
||||||
|
<MsIcon type="icon-icon_succeed_filled" class="mr-[4px] text-[rgb(var(--success-6))]" />
|
||||||
|
{{ t('caseManagement.featureCase.execute.failed') }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="markdown-body" style="margin-left: 48px" v-html="item.contentText"></div>
|
<div class="markdown-body" style="margin-left: 48px" v-html="item.contentText"></div>
|
||||||
<div class="ml-[48px] mt-[8px] text-[var(--color-text-4)]">
|
<div class="ml-[48px] mt-[8px] flex text-[var(--color-text-4)]">
|
||||||
{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}
|
{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}
|
||||||
<a-tooltip :content="item.reviewName" :mouse-enter-delay="300">
|
<div v-if="activeComment === 'reviewComment'">
|
||||||
<span v-if="item.deleted" class="one-line-text ml-[16px] max-w-[300px] break-words break-all">
|
<a-tooltip :content="item.reviewName" :mouse-enter-delay="300">
|
||||||
{{ characterLimit(item.reviewName) }}
|
<span v-if="item.deleted" class="one-line-text ml-[16px] max-w-[300px] break-words break-all">
|
||||||
</span>
|
{{ characterLimit(item.reviewName) }}
|
||||||
|
</span>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
v-else
|
v-else
|
||||||
class="one-line-text ml-[16px] max-w-[300px] cursor-pointer break-words break-all text-[rgb(var(--primary-5))]"
|
class="one-line-text ml-[16px] max-w-[300px] cursor-pointer break-words break-all text-[rgb(var(--primary-5))]"
|
||||||
@click="review(item)"
|
@click="review(item)"
|
||||||
>
|
>
|
||||||
{{ characterLimit(item.reviewName) }}
|
{{ characterLimit(item.reviewName) }}
|
||||||
</span>
|
</span>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
<div v-if="activeComment === 'executiveComment'">
|
||||||
|
<a-tooltip :content="item.testPlanName" :mouse-enter-delay="300">
|
||||||
|
<span v-if="item.deleted" class="one-line-text ml-[16px] max-w-[300px] break-words break-all">
|
||||||
|
{{ characterLimit(item.testPlanName) }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
class="one-line-text ml-[16px] max-w-[300px] cursor-pointer break-words break-all text-[rgb(var(--primary-5))]"
|
||||||
|
@click="toPlan(item)"
|
||||||
|
>
|
||||||
|
{{ characterLimit(item.testPlanName) }}
|
||||||
|
</span>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MsEmpty v-if="reviewCommentList.length === 0" />
|
<MsEmpty v-if="reviewCommentList.length === 0" />
|
||||||
|
@ -88,15 +124,15 @@
|
||||||
editorUploadFile,
|
editorUploadFile,
|
||||||
getCommentList,
|
getCommentList,
|
||||||
getReviewCommentList,
|
getReviewCommentList,
|
||||||
|
getTestPlanExecuteCommentList,
|
||||||
} from '@/api/modules/case-management/featureCase';
|
} from '@/api/modules/case-management/featureCase';
|
||||||
import { PreviewEditorImageUrl } from '@/api/requrls/case-management/featureCase';
|
import { PreviewEditorImageUrl } from '@/api/requrls/case-management/featureCase';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useModal from '@/hooks/useModal';
|
import useModal from '@/hooks/useModal';
|
||||||
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
||||||
import { characterLimit } from '@/utils';
|
import { characterLimit } from '@/utils';
|
||||||
import { hasAnyPermission } from '@/utils/permission';
|
|
||||||
|
|
||||||
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
|
import { CaseManagementRouteEnum, TestPlanRouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
const featureCaseStore = useFeatureCaseStore();
|
const featureCaseStore = useFeatureCaseStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -133,6 +169,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化执行评论
|
||||||
|
async function initTestPlanExecuteCommentList() {
|
||||||
|
try {
|
||||||
|
const result = await getTestPlanExecuteCommentList(props.caseId);
|
||||||
|
reviewCommentList.value = result;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function getAllCommentList() {
|
async function getAllCommentList() {
|
||||||
switch (activeComment.value) {
|
switch (activeComment.value) {
|
||||||
case 'caseComment':
|
case 'caseComment':
|
||||||
|
@ -144,7 +190,7 @@
|
||||||
featureCaseStore.getCaseCounts(props.caseId);
|
featureCaseStore.getCaseCounts(props.caseId);
|
||||||
break;
|
break;
|
||||||
case 'executiveComment':
|
case 'executiveComment':
|
||||||
await initCommentList();
|
await initTestPlanExecuteCommentList();
|
||||||
featureCaseStore.getCaseCounts(props.caseId);
|
featureCaseStore.getCaseCounts(props.caseId);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -204,6 +250,20 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 去测试计划页面
|
||||||
|
function toPlan(record: CommentItem) {
|
||||||
|
router.push({
|
||||||
|
name: TestPlanRouteEnum.TEST_PLAN_INDEX_DETAIL,
|
||||||
|
query: {
|
||||||
|
...route.query,
|
||||||
|
id: record.testPlanId,
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
params: JSON.stringify(record.moduleName),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => activeComment.value,
|
() => activeComment.value,
|
||||||
(val) => {
|
(val) => {
|
||||||
|
|
|
@ -277,4 +277,7 @@ export default {
|
||||||
'caseManagement.featureCase.deleteFileTip':
|
'caseManagement.featureCase.deleteFileTip':
|
||||||
'After deletion, the file cannot be restored. Please operate with caution!',
|
'After deletion, the file cannot be restored. Please operate with caution!',
|
||||||
'caseManagement.featureCase.nameNotNull': 'The name can not be null!',
|
'caseManagement.featureCase.nameNotNull': 'The name can not be null!',
|
||||||
|
'caseManagement.featureCase.execute.success': 'SUCCESS',
|
||||||
|
'caseManagement.featureCase.execute.failed': 'ERROR',
|
||||||
|
'caseManagement.featureCase.execute.blocked': 'BLOCKED',
|
||||||
};
|
};
|
||||||
|
|
|
@ -272,4 +272,7 @@ export default {
|
||||||
'caseManagement.featureCase.deleteFile': '确认删除文件 {name} 吗',
|
'caseManagement.featureCase.deleteFile': '确认删除文件 {name} 吗',
|
||||||
'caseManagement.featureCase.deleteFileTip': '删除后,文件无法恢复,请谨慎操作!',
|
'caseManagement.featureCase.deleteFileTip': '删除后,文件无法恢复,请谨慎操作!',
|
||||||
'caseManagement.featureCase.nameNotNull': '用例名称不能为空!',
|
'caseManagement.featureCase.nameNotNull': '用例名称不能为空!',
|
||||||
|
'caseManagement.featureCase.execute.success': '成功',
|
||||||
|
'caseManagement.featureCase.execute.failed': '失败',
|
||||||
|
'caseManagement.featureCase.execute.blocked': '阻塞',
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue