feat(用例管理): 新增用例详情执行评论tab

This commit is contained in:
guoyuqi 2024-05-13 10:16:09 +08:00 committed by Craftsman
parent 2c0c4c07fa
commit bcd06b5e27
6 changed files with 103 additions and 21 deletions

View File

@ -12,7 +12,9 @@ import io.metersphere.functional.excel.domain.FunctionalCaseExcelData;
import io.metersphere.functional.mapper.*;
import io.metersphere.functional.request.*;
import io.metersphere.functional.result.CaseManagementResultCode;
import io.metersphere.plan.domain.TestPlanCaseExecuteHistoryExample;
import io.metersphere.plan.domain.TestPlanFunctionalCaseExample;
import io.metersphere.plan.mapper.TestPlanCaseExecuteHistoryMapper;
import io.metersphere.plan.mapper.TestPlanFunctionalCaseMapper;
import io.metersphere.project.domain.*;
import io.metersphere.project.dto.ModuleCountDTO;
@ -166,6 +168,8 @@ public class FunctionalCaseService {
@Resource
private CaseReviewHistoryMapper caseReviewHistoryMapper;
@Resource
private TestPlanCaseExecuteHistoryMapper testPlanCaseExecuteHistoryMapper;
@Resource
private FunctionalCaseCommentMapper functionalCaseCommentMapper;
@Resource
private ProjectService projectService;
@ -420,9 +424,12 @@ public class FunctionalCaseService {
FunctionalCaseCommentExample functionalCaseCommentExample = new FunctionalCaseCommentExample();
functionalCaseCommentExample.createCriteria().andCaseIdEqualTo(functionalCaseDetailDTO.getId());
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);
//获取变更历史数量数量
OperationHistoryExample operationHistoryExample = new OperationHistoryExample();
List<String> types = List.of(OperationLogType.ADD.name(), OperationLogType.IMPORT.name(), OperationLogType.UPDATE.name());

View File

@ -52,6 +52,7 @@ import {
GetDependOnRelationUrl,
GetDetailCaseReviewUrl,
GetFileIsUpdateUrl,
GetPlanExecuteCommentListUrl,
GetRecycleCaseListUrl,
GetRecycleCaseModulesCountUrl,
GetReviewCommentListUrl,
@ -435,4 +436,9 @@ export function getLinkedCaseTestPlanList(data: TableQueryParams) {
return MSR.post<CommonList<AssociateFunctionalCaseItem>>({ url: GetAssociatedTestPlanUrl, data });
}
// 获取执行评论
export function getTestPlanExecuteCommentList(caseId: string) {
return MSR.get<CommentItem[]>({ url: `${GetPlanExecuteCommentListUrl}/${caseId}` });
}
export default {};

View File

@ -154,3 +154,6 @@ export const associatedProjectOptionsUrl = '/project/list/options';
// 获取详情已关联测试计划列表
export const GetAssociatedTestPlanUrl = '/functional/case/test/has/associate/plan/page';
// 评审评论
export const GetPlanExecuteCommentListUrl = '/functional/case/test/plan/comment';

View File

@ -4,7 +4,7 @@
<a-radio-group v-model="activeComment" type="button">
<a-radio value="caseComment">{{ t('caseManagement.featureCase.caseComment') }}</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>
</div>
<div>
@ -21,13 +21,20 @@
</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 v-for="item of reviewCommentList" :key="item.id" class="review-history-list-item">
<div class="flex items-center">
<MSAvatar :avatar="item.userLogo" />
<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>
<div v-if="item.status === 'PASS'" class="flex items-center">
<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))]" />
{{ t('caseManagement.caseReview.reReview') }}
</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 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') }}
<a-tooltip :content="item.reviewName" :mouse-enter-delay="300">
<span v-if="item.deleted" class="one-line-text ml-[16px] max-w-[300px] break-words break-all">
{{ characterLimit(item.reviewName) }}
</span>
<div v-if="activeComment === 'reviewComment'">
<a-tooltip :content="item.reviewName" :mouse-enter-delay="300">
<span v-if="item.deleted" class="one-line-text ml-[16px] max-w-[300px] break-words break-all">
{{ characterLimit(item.reviewName) }}
</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="review(item)"
>
{{ characterLimit(item.reviewName) }}
</span>
</a-tooltip>
<span
v-else
class="one-line-text ml-[16px] max-w-[300px] cursor-pointer break-words break-all text-[rgb(var(--primary-5))]"
@click="review(item)"
>
{{ characterLimit(item.reviewName) }}
</span>
</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>
<MsEmpty v-if="reviewCommentList.length === 0" />
@ -88,15 +124,15 @@
editorUploadFile,
getCommentList,
getReviewCommentList,
getTestPlanExecuteCommentList,
} from '@/api/modules/case-management/featureCase';
import { PreviewEditorImageUrl } from '@/api/requrls/case-management/featureCase';
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
import useFeatureCaseStore from '@/store/modules/case/featureCase';
import { characterLimit } from '@/utils';
import { hasAnyPermission } from '@/utils/permission';
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
import { CaseManagementRouteEnum, TestPlanRouteEnum } from '@/enums/routeEnum';
const featureCaseStore = useFeatureCaseStore();
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() {
switch (activeComment.value) {
case 'caseComment':
@ -144,7 +190,7 @@
featureCaseStore.getCaseCounts(props.caseId);
break;
case 'executiveComment':
await initCommentList();
await initTestPlanExecuteCommentList();
featureCaseStore.getCaseCounts(props.caseId);
break;
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(
() => activeComment.value,
(val) => {

View File

@ -277,4 +277,7 @@ export default {
'caseManagement.featureCase.deleteFileTip':
'After deletion, the file cannot be restored. Please operate with caution!',
'caseManagement.featureCase.nameNotNull': 'The name can not be null!',
'caseManagement.featureCase.execute.success': 'SUCCESS',
'caseManagement.featureCase.execute.failed': 'ERROR',
'caseManagement.featureCase.execute.blocked': 'BLOCKED',
};

View File

@ -272,4 +272,7 @@ export default {
'caseManagement.featureCase.deleteFile': '确认删除文件 {name} 吗',
'caseManagement.featureCase.deleteFileTip': '删除后,文件无法恢复,请谨慎操作!',
'caseManagement.featureCase.nameNotNull': '用例名称不能为空!',
'caseManagement.featureCase.execute.success': '成功',
'caseManagement.featureCase.execute.failed': '失败',
'caseManagement.featureCase.execute.blocked': '阻塞',
};