feat(功能用例): 脑图用例缺陷
This commit is contained in:
parent
70fcf72a21
commit
43d6ed5916
|
@ -31,3 +31,14 @@
|
|||
list-style: decimal !important;
|
||||
}
|
||||
}
|
||||
.ms-comment-list {
|
||||
@apply h-full;
|
||||
.ms-comment-list-item {
|
||||
@apply flex;
|
||||
|
||||
gap: 8px;
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
<a-button type="secondary" :disabled="saveLoading">{{ t('common.cancel') }}</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="activeExtraKey === 'attachment'" class="pl-[16px]">
|
||||
<div v-else-if="activeExtraKey === 'attachment'" class="h-full pl-[16px]">
|
||||
<a-spin :loading="attachmentLoading" class="h-full w-full">
|
||||
<MsAddAttachment
|
||||
v-model:file-list="fileList"
|
||||
|
@ -159,7 +159,7 @@
|
|||
</MsFileList>
|
||||
</a-spin>
|
||||
</div>
|
||||
<div v-else-if="activeExtraKey === 'comments'" class="pl-[16px]">
|
||||
<div v-else-if="activeExtraKey === 'comments'" class="h-full pl-[16px]">
|
||||
<div class="mb-[16px] flex items-center justify-between">
|
||||
<div class="text-[var(--color-text-4)]">
|
||||
{{
|
||||
|
@ -175,6 +175,7 @@
|
|||
@change="getAllCommentList"
|
||||
></a-select>
|
||||
</div>
|
||||
<div class="comment-container">
|
||||
<ReviewCommentList
|
||||
v-if="activeComment === 'reviewComment' || activeComment === 'executiveComment'"
|
||||
:review-comment-list="reviewCommentList"
|
||||
|
@ -190,6 +191,7 @@
|
|||
/>
|
||||
<MsEmpty v-if="commentList.length === 0" />
|
||||
</template>
|
||||
</div>
|
||||
<inputComment
|
||||
ref="commentInputRef"
|
||||
v-model:content="content"
|
||||
|
@ -205,27 +207,40 @@
|
|||
@cancel="cancelPublish"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="pl-[16px]">
|
||||
<div v-else class="h-full pl-[16px]">
|
||||
<a-button v-if="hasAnyPermission(['FUNCTIONAL_CASE:READ+UPDATE'])" class="mr-3" type="primary" @click="linkBug">
|
||||
{{ t('caseManagement.featureCase.linkDefect') }}
|
||||
</a-button>
|
||||
<a-button v-permission="['PROJECT_BUG:READ+ADD']" type="outline" @click="createBug"
|
||||
>{{ t('caseManagement.featureCase.createDefect') }}
|
||||
<a-button v-permission="['PROJECT_BUG:READ+ADD']" type="outline" @click="createBug">
|
||||
{{ t('caseManagement.featureCase.createDefect') }}
|
||||
</a-button>
|
||||
<div class="bug-list">
|
||||
<div v-for="item of bugList" :key="item.id" class="bug-item">
|
||||
<MsList
|
||||
v-model:data="bugList"
|
||||
mode="remote"
|
||||
item-key-field="id"
|
||||
:item-border="false"
|
||||
class="my-[16px] h-[calc(100%-64px)] w-full rounded-[var(--border-radius-small)]"
|
||||
:no-more-data="noMoreData"
|
||||
raggable
|
||||
:virtual-list-props="{
|
||||
height: '100%',
|
||||
}"
|
||||
@reach-bottom="handleReachBottom"
|
||||
>
|
||||
<template #item="{ item }">
|
||||
<div class="bug-item">
|
||||
<div class="mb-[4px] flex items-center justify-between">
|
||||
<MsButton type="text" @click="goBug(item.id)">{{ item.num }}</MsButton>
|
||||
<MsButton type="text" @click="disassociateBug(item.id)">
|
||||
{{ t('ms.add.attachment.cancelAssociate') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
<a-tooltip :content="item.name">
|
||||
<a-tooltip :content="item.name" position="tl">
|
||||
<div class="one-line-text">{{ item.name }}</div>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<MsEmpty v-if="bugList.length === 0" />
|
||||
</div>
|
||||
</template>
|
||||
</MsList>
|
||||
</div>
|
||||
</template>
|
||||
</MsMinderEditor>
|
||||
|
@ -243,7 +258,7 @@
|
|||
v-model:visible="showCreateBugDrawer"
|
||||
:case-id="activeCase.id"
|
||||
:extra-params="{ caseId: activeCase.id }"
|
||||
@success="initBugList"
|
||||
@success="loadBugList"
|
||||
/>
|
||||
<LinkDefectDrawer
|
||||
v-if="activeCase.id"
|
||||
|
@ -262,6 +277,7 @@
|
|||
import MsEmpty from '@/components/pure/ms-empty/index.vue';
|
||||
import MsFormCreate from '@/components/pure/ms-form-create/ms-form-create.vue';
|
||||
import { FormItem, FormRuleItem } from '@/components/pure/ms-form-create/types';
|
||||
import MsList from '@/components/pure/ms-list/index.vue';
|
||||
import MsMinderEditor from '@/components/pure/ms-minder-editor/minderEditor.vue';
|
||||
import type { MinderJson, MinderJsonNode, MinderJsonNodeData } from '@/components/pure/ms-minder-editor/props';
|
||||
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
|
||||
|
@ -291,6 +307,7 @@
|
|||
getCaseMinder,
|
||||
getCaseModuleTree,
|
||||
getCommentList,
|
||||
getLinkedCaseBugList,
|
||||
getReviewCommentList,
|
||||
getTestPlanExecuteCommentList,
|
||||
getTransferFileTree,
|
||||
|
@ -1031,70 +1048,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理脑图节点激活/点击
|
||||
* @param node 被激活/点击的节点
|
||||
*/
|
||||
async function handleNodeClick(node: MinderJsonNode) {
|
||||
const { data } = node;
|
||||
if (data?.resource && data.resource.includes(caseTag)) {
|
||||
extraVisible.value = true;
|
||||
activeExtraKey.value = 'baseInfo';
|
||||
initCaseDetail(data);
|
||||
} else if (data?.resource?.includes(moduleTag) && data.count > 0 && data.isLoaded !== true) {
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await getCaseMinder({
|
||||
projectId: appStore.currentProjectId,
|
||||
moduleId: data.id,
|
||||
});
|
||||
const fakeNode = node.children?.find((e) => e.data?.id === undefined); // 移除占位的虚拟节点
|
||||
if (fakeNode) {
|
||||
window.minder.removeNode(fakeNode);
|
||||
}
|
||||
if ((!res || res.length === 0) && node.children?.length) {
|
||||
// 如果模块下没有用例且有别的模块节点,正常展开
|
||||
node.expand();
|
||||
node.renderTree();
|
||||
window.minder.layout();
|
||||
return;
|
||||
}
|
||||
// TODO:递归渲染存在的子节点
|
||||
res.forEach((e) => {
|
||||
// 用例节点
|
||||
const child = window.minder.createNode(e.data, node);
|
||||
child.render();
|
||||
e.children?.forEach((item) => {
|
||||
// 前置/步骤/备注节点
|
||||
const grandChild = window.minder.createNode(item.data, child);
|
||||
grandChild.render();
|
||||
item.children?.forEach((subItem) => {
|
||||
// 预期结果节点
|
||||
const greatGrandChild = window.minder.createNode(subItem.data, grandChild);
|
||||
greatGrandChild.render();
|
||||
});
|
||||
});
|
||||
child.expand();
|
||||
child.renderTree();
|
||||
});
|
||||
node.expand();
|
||||
node.renderTree();
|
||||
window.minder.layout();
|
||||
if (node.data) {
|
||||
node.data.isLoaded = true;
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
} else {
|
||||
extraVisible.value = false;
|
||||
activeCase.value = {};
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
initDefaultFields();
|
||||
});
|
||||
|
@ -1285,55 +1238,37 @@
|
|||
}
|
||||
|
||||
const bugList = ref<any[]>([]);
|
||||
const noMoreData = ref<boolean>(false);
|
||||
const pageNation = ref({
|
||||
total: 0,
|
||||
pageSize: 10,
|
||||
current: 1,
|
||||
});
|
||||
|
||||
async function initBugList() {
|
||||
bugList.value = [
|
||||
{
|
||||
name: 'sdasd',
|
||||
id: '3d23d23d',
|
||||
num: 100001,
|
||||
},
|
||||
{
|
||||
name: 'sdasd',
|
||||
id: '3d23d23d',
|
||||
num: 100002,
|
||||
},
|
||||
{
|
||||
name: 'sdasd',
|
||||
id: '3d23d23d',
|
||||
num: 100003,
|
||||
},
|
||||
{
|
||||
name: 'sdasd',
|
||||
id: '3d23d23d',
|
||||
num: 100004,
|
||||
},
|
||||
{
|
||||
name: 'sdasd',
|
||||
id: '3d23d23d',
|
||||
num: 100005,
|
||||
},
|
||||
{
|
||||
name: 'sdasd',
|
||||
id: '3d23d23d',
|
||||
num: 100006,
|
||||
},
|
||||
{
|
||||
name: 'sdasd',
|
||||
id: '3d23d23d',
|
||||
num: 100007,
|
||||
},
|
||||
{
|
||||
name: 'sdasd',
|
||||
id: '3d23d23d',
|
||||
num: 100008,
|
||||
},
|
||||
{
|
||||
name: 'sdasd',
|
||||
id: '3d23d23d',
|
||||
num: 100009,
|
||||
},
|
||||
];
|
||||
async function loadBugList() {
|
||||
try {
|
||||
const res = await getLinkedCaseBugList({
|
||||
keyword: '',
|
||||
projectId: appStore.currentProjectId,
|
||||
caseId: activeCase.value.id,
|
||||
current: pageNation.value.current || 1,
|
||||
pageSize: pageNation.value.pageSize,
|
||||
});
|
||||
res.list.forEach((item) => bugList.value.push(item));
|
||||
pageNation.value.total = res.total;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 滚动翻页
|
||||
function handleReachBottom() {
|
||||
pageNation.value.current += 1;
|
||||
if (pageNation.value.current > Math.ceil(pageNation.value.total / pageNation.value.pageSize)) {
|
||||
return;
|
||||
}
|
||||
loadBugList();
|
||||
}
|
||||
|
||||
const cancelLoading = ref<boolean>(false);
|
||||
|
@ -1378,7 +1313,7 @@
|
|||
drawerLoading.value = true;
|
||||
await associatedDebug(params);
|
||||
Message.success(t('caseManagement.featureCase.associatedSuccess'));
|
||||
initBugList();
|
||||
loadBugList();
|
||||
showLinkBugDrawer.value = false;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
|
@ -1388,11 +1323,88 @@
|
|||
}
|
||||
}
|
||||
|
||||
function resetExtractInfo() {
|
||||
activeCase.value = {};
|
||||
pageNation.value.current = 1;
|
||||
bugList.value = [];
|
||||
commentList.value = [];
|
||||
reviewCommentList.value = [];
|
||||
fileList.value = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理脑图节点激活/点击
|
||||
* @param node 被激活/点击的节点
|
||||
*/
|
||||
async function handleNodeClick(node: MinderJsonNode) {
|
||||
const { data } = node;
|
||||
if (data?.resource && data.resource.includes(caseTag)) {
|
||||
extraVisible.value = true;
|
||||
activeExtraKey.value = 'baseInfo';
|
||||
resetExtractInfo();
|
||||
initCaseDetail(data);
|
||||
} else if (data?.resource?.includes(moduleTag) && data.count > 0 && data.isLoaded !== true) {
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await getCaseMinder({
|
||||
projectId: appStore.currentProjectId,
|
||||
moduleId: data.id,
|
||||
});
|
||||
const fakeNode = node.children?.find((e) => e.data?.id === undefined); // 移除占位的虚拟节点
|
||||
if (fakeNode) {
|
||||
window.minder.removeNode(fakeNode);
|
||||
}
|
||||
if ((!res || res.length === 0) && node.children?.length) {
|
||||
// 如果模块下没有用例且有别的模块节点,正常展开
|
||||
node.expand();
|
||||
node.renderTree();
|
||||
window.minder.layout();
|
||||
return;
|
||||
}
|
||||
// TODO:递归渲染存在的子节点
|
||||
res.forEach((e) => {
|
||||
// 用例节点
|
||||
const child = window.minder.createNode(e.data, node);
|
||||
child.render();
|
||||
e.children?.forEach((item) => {
|
||||
// 前置/步骤/备注节点
|
||||
const grandChild = window.minder.createNode(item.data, child);
|
||||
grandChild.render();
|
||||
item.children?.forEach((subItem) => {
|
||||
// 预期结果节点
|
||||
const greatGrandChild = window.minder.createNode(subItem.data, grandChild);
|
||||
greatGrandChild.render();
|
||||
});
|
||||
});
|
||||
child.expand();
|
||||
child.renderTree();
|
||||
});
|
||||
node.expand();
|
||||
node.renderTree();
|
||||
window.minder.layout();
|
||||
if (node.data) {
|
||||
node.data.isLoaded = true;
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
} else {
|
||||
extraVisible.value = false;
|
||||
resetExtractInfo();
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => activeExtraKey.value,
|
||||
(val) => {
|
||||
if (val === 'comments') {
|
||||
getAllCommentList();
|
||||
} else if (val === 'bug') {
|
||||
pageNation.value.current = 1;
|
||||
loadBugList();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -1408,13 +1420,6 @@
|
|||
overflow-y: auto;
|
||||
height: calc(100% - 64px);
|
||||
}
|
||||
.bug-list {
|
||||
.ms-scroll-bar();
|
||||
|
||||
overflow-y: auto;
|
||||
margin-bottom: 16px;
|
||||
height: calc(100% - 46px);
|
||||
border-radius: var(--border-radius-small);
|
||||
.bug-item {
|
||||
@apply cursor-pointer;
|
||||
&:not(:last-child) {
|
||||
|
@ -1432,5 +1437,10 @@
|
|||
box-shadow: inset 0 0 0.5px 0.5px rgb(var(--primary-5));
|
||||
}
|
||||
}
|
||||
.comment-container {
|
||||
.ms-scroll-bar();
|
||||
|
||||
overflow-y: auto;
|
||||
height: calc(100% - 130px);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
<template>
|
||||
<a-spin class="z-[100] !block" :loading="props.loading" :size="28">
|
||||
<a-spin
|
||||
class="z-[100] !block"
|
||||
:class="props.autoHeight ? '' : 'h-full min-h-[500px]'"
|
||||
:loading="props.loading"
|
||||
:size="28"
|
||||
>
|
||||
<div
|
||||
ref="fullRef"
|
||||
:class="[
|
||||
'ms-card',
|
||||
'relative',
|
||||
'h-full',
|
||||
props.isFullscreen || isFullScreen ? 'ms-card--fullScreen' : '',
|
||||
props.autoHeight ? '' : 'h-full min-h-[500px]',
|
||||
props.noContentPadding ? 'ms-card--noContentPadding' : 'p-[16px]',
|
||||
props.noBottomRadius ? 'ms-card--noBottomRadius' : '',
|
||||
!props.hideFooter && !props.simple ? 'pb-[24px]' : '',
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
<template>
|
||||
<div class="flex flex-1 flex-col overflow-hidden">
|
||||
<div class="review-history-list">
|
||||
<div v-for="item of props.reviewCommentList" :key="item.id" class="review-history-list-item">
|
||||
<div class="flex items-center">
|
||||
<div class="ms-comment-list">
|
||||
<div v-for="item of props.reviewCommentList" :key="item.id" class="ms-comment-list-item">
|
||||
<MSAvatar :avatar="item.userLogo" />
|
||||
<div class="ml-[8px] flex items-center">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-[8px]">
|
||||
<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))]" />
|
||||
{{ t('caseManagement.caseReview.pass') }}
|
||||
|
@ -38,9 +37,8 @@
|
|||
{{ t('common.fail') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="markdown-body" style="margin-left: 48px" v-html="item.contentText"></div>
|
||||
<div class="ml-[48px] mt-[8px] flex text-[var(--color-text-4)]">
|
||||
<div class="markdown-body" v-html="item.contentText"></div>
|
||||
<div class="mt-[8px] flex text-[12px] leading-[16px] text-[var(--color-text-4)]">
|
||||
{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}
|
||||
<div v-if="props.activeComment === 'reviewComment'">
|
||||
<a-tooltip :content="item.reviewName" :mouse-enter-delay="300">
|
||||
|
@ -74,6 +72,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MsEmpty v-if="reviewCommentList.length === 0" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -194,15 +194,14 @@
|
|||
:show-empty="false"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="flex flex-1 flex-col overflow-hidden">
|
||||
<div class="review-history-list">
|
||||
<div v-else class="flex flex-1 flex-col overflow-hidden pl-[16px] pt-[16px]">
|
||||
<div class="ms-comment-list">
|
||||
<a-spin :loading="reviewHistoryListLoading" class="h-full w-full">
|
||||
<div v-for="item of reviewHistoryList" :key="item.id" class="review-history-list-item">
|
||||
<div class="flex items-center">
|
||||
<div v-for="item of reviewHistoryList" :key="item.id" class="ms-comment-list-item">
|
||||
<MSAvatar :avatar="item.userLogo" />
|
||||
<div class="ml-[8px] flex items-center">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-[8px]">
|
||||
<div class="font-medium text-[var(--color-text-1)]">{{ item.userName }}</div>
|
||||
<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))]" />
|
||||
{{ t('caseManagement.caseReview.pass') }}
|
||||
|
@ -220,12 +219,12 @@
|
|||
{{ t('caseManagement.caseReview.reReview') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="markdown-body ml-[48px]" v-html="item.contentText"></div>
|
||||
<div class="ml-[48px] mt-[8px] text-[var(--color-text-4)]">
|
||||
<div class="mt-[8px] text-[12px] leading-[16px] text-[var(--color-text-4)]">
|
||||
{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MsEmpty v-if="reviewHistoryList.length === 0" />
|
||||
</a-spin>
|
||||
</div>
|
||||
|
@ -640,16 +639,6 @@
|
|||
|
||||
padding: 16px;
|
||||
align-content: start;
|
||||
.review-history-list {
|
||||
@apply h-full;
|
||||
|
||||
padding: 16px 0 0 16px;
|
||||
.review-history-list-item {
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.content-footer {
|
||||
padding: 16px;
|
||||
|
|
Loading…
Reference in New Issue