feat(测试计划): 脑图执行用例-执行
This commit is contained in:
parent
46259da117
commit
b5a3592e46
|
@ -154,7 +154,7 @@
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'operation', type: string, node: MinderJsonNode): void;
|
(e: 'operation', type: string, node: MinderJsonNode): void;
|
||||||
(e: 'handleReviewDone', refreshTree?: boolean): void;
|
(e: 'handleReviewDone'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@ -484,10 +484,10 @@
|
||||||
selectNode.value.data?.resource?.includes(caseTag)
|
selectNode.value.data?.resource?.includes(caseTag)
|
||||||
) {
|
) {
|
||||||
window.minder.execCommand('resource', [statusTagMap[status], caseTag]);
|
window.minder.execCommand('resource', [statusTagMap[status], caseTag]);
|
||||||
emit('handleReviewDone');
|
|
||||||
} else {
|
} else {
|
||||||
emit('handleReviewDone', true);
|
initCaseTree();
|
||||||
}
|
}
|
||||||
|
emit('handleReviewDone');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 递归更新子节点的用例标签
|
// 递归更新子节点的用例标签
|
||||||
|
|
|
@ -13,7 +13,7 @@ import { getGenerateId } from '@/utils';
|
||||||
* 封装用例脑图基础功能,包含菜单显隐判断、节点插入、节点替换、节点拖拽等
|
* 封装用例脑图基础功能,包含菜单显隐判断、节点插入、节点替换、节点拖拽等
|
||||||
* @returns API 集合
|
* @returns API 集合
|
||||||
*/
|
*/
|
||||||
export default function useMinderBaseApi({ hasEditPermission }: { hasEditPermission: boolean }) {
|
export default function useMinderBaseApi({ hasEditPermission }: { hasEditPermission?: boolean }) {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const minderStore = useMinderStore();
|
const minderStore = useMinderStore();
|
||||||
|
|
||||||
|
|
|
@ -42,14 +42,33 @@
|
||||||
</template>
|
</template>
|
||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
<!-- 执行 -->
|
<!-- 执行 -->
|
||||||
|
<a-trigger
|
||||||
|
v-model:popup-visible="executeVisible"
|
||||||
|
trigger="click"
|
||||||
|
position="bl"
|
||||||
|
:click-outside-to-close="false"
|
||||||
|
popup-container=".ms-minder-container"
|
||||||
|
>
|
||||||
<a-tooltip
|
<a-tooltip
|
||||||
v-if="props.canEdit && hasAnyPermission(['PROJECT_TEST_PLAN:READ+EXECUTE'])"
|
v-if="props.canEdit && hasAnyPermission(['PROJECT_TEST_PLAN:READ+EXECUTE'])"
|
||||||
:content="t('common.execute')"
|
:content="t('common.execute')"
|
||||||
>
|
>
|
||||||
<MsButton type="icon" class="ms-minder-node-float-menu-icon-button">
|
<MsButton
|
||||||
|
type="icon"
|
||||||
|
:class="[
|
||||||
|
'ms-minder-node-float-menu-icon-button',
|
||||||
|
`${executeVisible ? 'ms-minder-node-float-menu-icon-button--focus' : ''}`,
|
||||||
|
]"
|
||||||
|
>
|
||||||
<MsIcon type="icon-icon_play-round_filled" class="text-[var(--color-text-4)]" />
|
<MsIcon type="icon-icon_play-round_filled" class="text-[var(--color-text-4)]" />
|
||||||
</MsButton>
|
</MsButton>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
|
<template #content>
|
||||||
|
<div class="w-[440px] rounded bg-white p-[16px] shadow-[0_0_10px_rgba(0,0,0,0.05)]">
|
||||||
|
<ExecuteSubmit :select-node="selectNode" :test-plan-id="props.planId" @done="handleExecuteDone" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-trigger>
|
||||||
<!-- 查看详情 -->
|
<!-- 查看详情 -->
|
||||||
<a-tooltip v-if="canShowDetail" :content="t('common.detail')">
|
<a-tooltip v-if="canShowDetail" :content="t('common.detail')">
|
||||||
<MsButton
|
<MsButton
|
||||||
|
@ -113,6 +132,29 @@
|
||||||
}"
|
}"
|
||||||
@success="handleAddBugDone"
|
@success="handleAddBugDone"
|
||||||
/>
|
/>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="stepExecuteModelVisible"
|
||||||
|
:title="t('common.executionResult')"
|
||||||
|
class="p-[4px]"
|
||||||
|
title-align="start"
|
||||||
|
body-class="p-0"
|
||||||
|
:width="800"
|
||||||
|
:cancel-button-props="{ disabled: submitStepExecuteLoading }"
|
||||||
|
:ok-loading="submitStepExecuteLoading"
|
||||||
|
:ok-text="t('caseManagement.caseReview.commitResult')"
|
||||||
|
@before-ok="submitStepExecute"
|
||||||
|
@cancel="cancelStepExecute"
|
||||||
|
>
|
||||||
|
<AddStep
|
||||||
|
v-model:step-list="stepData"
|
||||||
|
is-scroll-y
|
||||||
|
is-test-plan
|
||||||
|
:scroll-y="190"
|
||||||
|
:is-disabled-test-plan="false"
|
||||||
|
is-disabled
|
||||||
|
/>
|
||||||
|
<ExecuteForm v-model:form="executeForm" class="mt-[24px]" rich-text-max-height="150px" />
|
||||||
|
</a-modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -134,21 +176,29 @@
|
||||||
import { MsFileItem } from '@/components/pure/ms-upload/types';
|
import { MsFileItem } from '@/components/pure/ms-upload/types';
|
||||||
import Attachment from '@/components/business/ms-minders/featureCaseMinder/attachment.vue';
|
import Attachment from '@/components/business/ms-minders/featureCaseMinder/attachment.vue';
|
||||||
import BugList from '@/components/business/ms-minders/featureCaseMinder/bugList.vue';
|
import BugList from '@/components/business/ms-minders/featureCaseMinder/bugList.vue';
|
||||||
|
import useMinderBaseApi from '@/components/business/ms-minders/featureCaseMinder/useMinderBaseApi';
|
||||||
|
import AddStep from '@/views/case-management/caseManagementFeature/components/addStep.vue';
|
||||||
import AddDefectDrawer from '@/views/case-management/caseManagementFeature/components/tabContent/tabBug/addDefectDrawer.vue';
|
import AddDefectDrawer from '@/views/case-management/caseManagementFeature/components/tabContent/tabBug/addDefectDrawer.vue';
|
||||||
import LinkDefectDrawer from '@/views/case-management/caseManagementFeature/components/tabContent/tabBug/linkDefectDrawer.vue';
|
import LinkDefectDrawer from '@/views/case-management/caseManagementFeature/components/tabContent/tabBug/linkDefectDrawer.vue';
|
||||||
import ReviewCommentList from '@/views/case-management/caseManagementFeature/components/tabContent/tabComment/reviewCommentList.vue';
|
import ReviewCommentList from '@/views/case-management/caseManagementFeature/components/tabContent/tabComment/reviewCommentList.vue';
|
||||||
|
import ExecuteForm from '@/views/test-plan/testPlan/detail/featureCase/components/executeForm.vue';
|
||||||
|
import ExecuteSubmit from '@/views/test-plan/testPlan/detail/featureCase/detail/executeSubmit.vue';
|
||||||
|
|
||||||
import { getCasePlanMinder } from '@/api/modules/case-management/caseReview';
|
import { getCasePlanMinder } from '@/api/modules/case-management/caseReview';
|
||||||
import { associateBugToPlan, executeHistory, getCaseDetail } from '@/api/modules/test-plan/testPlan';
|
import { associateBugToPlan, executeHistory, getCaseDetail, runFeatureCase } from '@/api/modules/test-plan/testPlan';
|
||||||
|
import { defaultExecuteForm } from '@/config/testPlan';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import useAppStore from '@/store/modules/app';
|
||||||
import useMinderStore from '@/store/modules/components/minder-editor/index';
|
import useMinderStore from '@/store/modules/components/minder-editor/index';
|
||||||
import useTestPlanFeatureCaseStore from '@/store/modules/testPlan/testPlanFeatureCase';
|
import useTestPlanFeatureCaseStore from '@/store/modules/testPlan/testPlanFeatureCase';
|
||||||
import { findNodeByKey, mapTree, replaceNodeInTree } from '@/utils';
|
import { findNodeByKey, getGenerateId, mapTree, replaceNodeInTree } from '@/utils';
|
||||||
import { hasAllPermission, hasAnyPermission } from '@/utils/permission';
|
import { hasAllPermission, hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
|
import type { StepList } from '@/models/caseManagement/featureCase';
|
||||||
import type { TableQueryParams } from '@/models/common';
|
import type { TableQueryParams } from '@/models/common';
|
||||||
import { ModuleTreeNode } from '@/models/common';
|
import { ModuleTreeNode } from '@/models/common';
|
||||||
import type { ExecuteHistoryItem } from '@/models/testPlan/testPlan';
|
import type { ExecuteFeatureCaseFormParams, ExecuteHistoryItem } from '@/models/testPlan/testPlan';
|
||||||
|
import { LastExecuteResults } from '@/enums/caseEnum';
|
||||||
import { MinderEventName, MinderKeyEnum } from '@/enums/minderEnum';
|
import { MinderEventName, MinderKeyEnum } from '@/enums/minderEnum';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -166,15 +216,16 @@
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'operation', type: string, node: MinderJsonNode): void;
|
(e: 'operation', type: string, node: MinderJsonNode): void;
|
||||||
(e: 'handleAddBugDone'): void;
|
(e: 'refreshPlan'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const appStore = useAppStore();
|
||||||
const minderStore = useMinderStore();
|
const minderStore = useMinderStore();
|
||||||
const testPlanFeatureCaseStore = useTestPlanFeatureCaseStore();
|
const testPlanFeatureCaseStore = useTestPlanFeatureCaseStore();
|
||||||
|
|
||||||
const caseTag = t('common.case');
|
const { caseTag, moduleTag, stepTag, stepExpectTag } = useMinderBaseApi({});
|
||||||
const moduleTag = t('common.module');
|
const actualResultTag = t('system.orgTemplate.actualResult');
|
||||||
const importJson = ref<MinderJson>({
|
const importJson = ref<MinderJson>({
|
||||||
root: {} as MinderJsonNode,
|
root: {} as MinderJsonNode,
|
||||||
treePath: [],
|
treePath: [],
|
||||||
|
@ -455,6 +506,172 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectNode = ref();
|
||||||
|
|
||||||
|
// 添加缺陷
|
||||||
|
const showLinkDefectDrawer = ref(false);
|
||||||
|
const showAddDefectDrawer = ref(false);
|
||||||
|
const linkDrawerLoading = ref(false);
|
||||||
|
const bugListRef = ref<InstanceType<typeof BugList>>();
|
||||||
|
function handleAddBugDone() {
|
||||||
|
if (extraVisible.value && activeExtraKey.value === 'bug') {
|
||||||
|
bugListRef.value?.handleShowTypeChange();
|
||||||
|
}
|
||||||
|
emit('refreshPlan');
|
||||||
|
}
|
||||||
|
async function associateSuccessHandler(params: TableQueryParams) {
|
||||||
|
try {
|
||||||
|
linkDrawerLoading.value = true;
|
||||||
|
await associateBugToPlan({
|
||||||
|
...params,
|
||||||
|
testPlanCaseId: selectNode.value.data?.id,
|
||||||
|
caseId: selectNode.value.data?.caseId,
|
||||||
|
testPlanId: props.planId,
|
||||||
|
});
|
||||||
|
Message.success(t('caseManagement.featureCase.associatedSuccess'));
|
||||||
|
linkDrawerLoading.value = false;
|
||||||
|
handleAddBugDone();
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
linkDrawerLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行
|
||||||
|
const executeVisible = ref(false);
|
||||||
|
// 新增或更新用例的实际结果节点
|
||||||
|
function updateCaseActualResultNode(node: MinderJsonNode, content: string) {
|
||||||
|
let actualResultNode;
|
||||||
|
actualResultNode = node.children?.find((item: MinderJsonNode) => item.data?.id === `actualResult-${node.data?.id}`);
|
||||||
|
if (actualResultNode) {
|
||||||
|
actualResultNode
|
||||||
|
.setData('resource', [actualResultTag])
|
||||||
|
.setData('text', content ?? '')
|
||||||
|
.render();
|
||||||
|
} else {
|
||||||
|
actualResultNode = createNode(
|
||||||
|
{ resource: [actualResultTag], text: content ?? '', id: `actualResult-${node.data?.id}` },
|
||||||
|
node
|
||||||
|
);
|
||||||
|
handleRenderNode(node, [actualResultNode]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 点击模块/用例执行
|
||||||
|
function handleExecuteDone(status: LastExecuteResults, content: string) {
|
||||||
|
executeVisible.value = false;
|
||||||
|
const resource = selectNode.value.data?.resource;
|
||||||
|
if (resource?.includes(caseTag)) {
|
||||||
|
// 用例添加标签
|
||||||
|
window.minder.execCommand('resource', [executionResultMap[status].statusText, caseTag]);
|
||||||
|
// 新增或更新用例的实际结果节点
|
||||||
|
updateCaseActualResultNode(selectNode.value, content);
|
||||||
|
// 更新执行历史
|
||||||
|
if (extraVisible.value && activeExtraKey.value === 'history') {
|
||||||
|
initExecuteHistory(selectNode.value.data);
|
||||||
|
}
|
||||||
|
} else if (resource?.includes(moduleTag)) {
|
||||||
|
initCaseTree();
|
||||||
|
}
|
||||||
|
emit('refreshPlan');
|
||||||
|
}
|
||||||
|
|
||||||
|
const stepExecuteModelVisible = ref(false);
|
||||||
|
const caseNodeAboveSelectStep = ref(); // 选中的步骤节点对应的用例节点信息
|
||||||
|
const submitStepExecuteLoading = ref(false);
|
||||||
|
const executeForm = ref<ExecuteFeatureCaseFormParams>({ ...defaultExecuteForm });
|
||||||
|
const stepData = ref<StepList[]>([
|
||||||
|
{
|
||||||
|
id: getGenerateId(),
|
||||||
|
step: '',
|
||||||
|
expected: '',
|
||||||
|
showStep: false,
|
||||||
|
showExpected: false,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
async function getStepData(id: string) {
|
||||||
|
try {
|
||||||
|
const res = await getCaseDetail(id);
|
||||||
|
if (res.steps) {
|
||||||
|
stepData.value = JSON.parse(res.steps).map((item: any) => {
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
step: item.desc,
|
||||||
|
expected: item.result,
|
||||||
|
actualResult: item.actualResult,
|
||||||
|
executeResult: item.executeResult,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
stepData.value = [];
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
watch(
|
||||||
|
() => stepData.value,
|
||||||
|
() => {
|
||||||
|
const executionResultList = stepData.value?.map((item) => item.executeResult);
|
||||||
|
if (executionResultList?.includes(LastExecuteResults.ERROR)) {
|
||||||
|
executeForm.value.lastExecResult = LastExecuteResults.ERROR;
|
||||||
|
} else if (executionResultList?.includes(LastExecuteResults.BLOCKED)) {
|
||||||
|
executeForm.value.lastExecResult = LastExecuteResults.BLOCKED;
|
||||||
|
} else {
|
||||||
|
executeForm.value.lastExecResult = LastExecuteResults.SUCCESS;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
function cancelStepExecute() {
|
||||||
|
executeForm.value = { ...defaultExecuteForm };
|
||||||
|
}
|
||||||
|
function submitStepExecuteDone(status: string, content: string) {
|
||||||
|
// 用例更新标签
|
||||||
|
caseNodeAboveSelectStep.value.setData('resource', [executionResultMap[status].statusText, caseTag]).render();
|
||||||
|
// 新增或更新用例的实际结果节点
|
||||||
|
updateCaseActualResultNode(caseNodeAboveSelectStep.value, content);
|
||||||
|
// 更新步骤数据:标签和实际结果
|
||||||
|
caseNodeAboveSelectStep.value.children.forEach((child: MinderJsonNode) => {
|
||||||
|
const step = stepData.value.find((item) => item.id === child.data?.id);
|
||||||
|
if (step?.executeResult?.length) {
|
||||||
|
child.setData('resource', [executionResultMap[step?.executeResult].statusText, stepTag]).render();
|
||||||
|
}
|
||||||
|
if (step?.actualResult?.length) {
|
||||||
|
child.children?.[0].children?.[0].setData('text', step?.actualResult).render();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
caseNodeAboveSelectStep.value.layout();
|
||||||
|
}
|
||||||
|
async function submitStepExecute() {
|
||||||
|
try {
|
||||||
|
submitStepExecuteLoading.value = true;
|
||||||
|
const params = {
|
||||||
|
projectId: appStore.currentProjectId,
|
||||||
|
testPlanId: props.planId,
|
||||||
|
caseId: caseNodeAboveSelectStep.value.data.caseId,
|
||||||
|
id: caseNodeAboveSelectStep.value.data.id,
|
||||||
|
...executeForm.value,
|
||||||
|
notifier: executeForm.value?.commentIds?.join(';'),
|
||||||
|
stepsExecResult: JSON.stringify(stepData.value),
|
||||||
|
};
|
||||||
|
await runFeatureCase(params);
|
||||||
|
stepExecuteModelVisible.value = false;
|
||||||
|
Message.success(t('common.updateSuccess'));
|
||||||
|
cancelStepExecute();
|
||||||
|
emit('refreshPlan');
|
||||||
|
submitStepExecuteDone(params.lastExecResult, params.content ?? '');
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
submitStepExecuteLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 菜单显隐
|
||||||
const hasOperationPermission = computed(
|
const hasOperationPermission = computed(
|
||||||
() => hasAnyPermission(['PROJECT_TEST_PLAN:READ+UPDATE', 'PROJECT_TEST_PLAN:READ+ASSOCIATION']) && props.canEdit
|
() => hasAnyPermission(['PROJECT_TEST_PLAN:READ+UPDATE', 'PROJECT_TEST_PLAN:READ+ASSOCIATION']) && props.canEdit
|
||||||
);
|
);
|
||||||
|
@ -485,8 +702,25 @@
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectNode = ref();
|
/**
|
||||||
|
* 获取步骤节点对应的用例节点
|
||||||
|
* @param node 选中节点
|
||||||
|
* @param resource 标签
|
||||||
|
*/
|
||||||
|
function getCaseNodeWithResource(node: MinderJsonNode, resource: string) {
|
||||||
|
while (node.parent) {
|
||||||
|
if (node?.data?.resource?.includes(resource)) {
|
||||||
|
return node.parent;
|
||||||
|
}
|
||||||
|
if (node.data?.resource?.includes(caseTag)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
node = node.parent;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选中节点
|
||||||
async function handleNodeSelect(node: MinderJsonNode) {
|
async function handleNodeSelect(node: MinderJsonNode) {
|
||||||
const { data } = node;
|
const { data } = node;
|
||||||
// 点击更多节点,加载更多用例
|
// 点击更多节点,加载更多用例
|
||||||
|
@ -511,6 +745,16 @@
|
||||||
canShowFloatMenu.value = false;
|
canShowFloatMenu.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 点步骤描述下的【步骤描述/预期结果/实际结果】标签
|
||||||
|
if ([actualResultTag, stepTag, stepExpectTag].some((item) => node.data?.resource?.includes(item))) {
|
||||||
|
caseNodeAboveSelectStep.value = getCaseNodeWithResource(node, stepTag);
|
||||||
|
if (caseNodeAboveSelectStep.value.data.id) {
|
||||||
|
getStepData(caseNodeAboveSelectStep.value.data.id);
|
||||||
|
stepExecuteModelVisible.value = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 不展示更多:没操作权限的用例
|
// 不展示更多:没操作权限的用例
|
||||||
if (node.data?.resource?.includes(caseTag) && !hasOperationPermission.value) {
|
if (node.data?.resource?.includes(caseTag) && !hasOperationPermission.value) {
|
||||||
canShowMoreMenu.value = false;
|
canShowMoreMenu.value = false;
|
||||||
|
@ -525,6 +769,8 @@
|
||||||
canShowEnterNode.value = false;
|
canShowEnterNode.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
executeVisible.value = false;
|
||||||
|
|
||||||
if (data?.resource?.includes(caseTag)) {
|
if (data?.resource?.includes(caseTag)) {
|
||||||
canShowDetail.value = true;
|
canShowDetail.value = true;
|
||||||
showAssociateBugMenu.value = true;
|
showAssociateBugMenu.value = true;
|
||||||
|
@ -555,37 +801,6 @@
|
||||||
extraVisible.value = false;
|
extraVisible.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加缺陷
|
|
||||||
const showLinkDefectDrawer = ref(false);
|
|
||||||
const showAddDefectDrawer = ref(false);
|
|
||||||
const linkDrawerLoading = ref(false);
|
|
||||||
const bugListRef = ref<InstanceType<typeof BugList>>();
|
|
||||||
function handleAddBugDone() {
|
|
||||||
if (extraVisible.value && activeExtraKey.value === 'bug') {
|
|
||||||
bugListRef.value?.handleShowTypeChange();
|
|
||||||
}
|
|
||||||
emit('handleAddBugDone');
|
|
||||||
}
|
|
||||||
async function associateSuccessHandler(params: TableQueryParams) {
|
|
||||||
try {
|
|
||||||
linkDrawerLoading.value = true;
|
|
||||||
await associateBugToPlan({
|
|
||||||
...params,
|
|
||||||
testPlanCaseId: selectNode.value.data?.id,
|
|
||||||
caseId: selectNode.value.data?.caseId,
|
|
||||||
testPlanId: props.planId,
|
|
||||||
});
|
|
||||||
Message.success(t('caseManagement.featureCase.associatedSuccess'));
|
|
||||||
linkDrawerLoading.value = false;
|
|
||||||
handleAddBugDone();
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(error);
|
|
||||||
} finally {
|
|
||||||
linkDrawerLoading.value = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
initCaseTree,
|
initCaseTree,
|
||||||
});
|
});
|
||||||
|
@ -599,4 +814,10 @@
|
||||||
margin: 0;
|
margin: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
:deep(.execute-form) .rich-wrapper .halo-rich-text-editor .editor-content {
|
||||||
|
max-height: 54px !important;
|
||||||
|
.ProseMirror {
|
||||||
|
min-height: 38px;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -134,7 +134,7 @@ export interface BatchReviewCaseParams extends BatchApiParams {
|
||||||
reviewId: string; // 评审id
|
reviewId: string; // 评审id
|
||||||
userId: string; // 用户id, 用来判断是否只看我的
|
userId: string; // 用户id, 用来判断是否只看我的
|
||||||
reviewPassRule: ReviewPassRule; // 评审规则
|
reviewPassRule: ReviewPassRule; // 评审规则
|
||||||
status: ReviewResult; // 评审结果
|
status: StartReviewStatus; // 评审结果
|
||||||
content: string; // 评论内容
|
content: string; // 评论内容
|
||||||
notifier: string; // 评论@的人的Id, 多个以';'隔开
|
notifier: string; // 评论@的人的Id, 多个以';'隔开
|
||||||
reviewCommentFileIds?: string[]; // 富文本ids
|
reviewCommentFileIds?: string[]; // 富文本ids
|
||||||
|
|
|
@ -105,6 +105,7 @@
|
||||||
stepList: any;
|
stepList: any;
|
||||||
isDisabled?: boolean;
|
isDisabled?: boolean;
|
||||||
isScrollY?: boolean;
|
isScrollY?: boolean;
|
||||||
|
scrollY?: number;
|
||||||
isTestPlan?: boolean;
|
isTestPlan?: boolean;
|
||||||
isDisabledTestPlan?: boolean;
|
isDisabledTestPlan?: boolean;
|
||||||
isPreview?: boolean; // 仅预览不展示状态可操作下拉和文本框
|
isPreview?: boolean; // 仅预览不展示状态可操作下拉和文本框
|
||||||
|
@ -212,7 +213,7 @@
|
||||||
|
|
||||||
const tableProps = ref<Partial<MsTableProps<StepList>>>({
|
const tableProps = ref<Partial<MsTableProps<StepList>>>({
|
||||||
columns: templateFieldColumns.value,
|
columns: templateFieldColumns.value,
|
||||||
scroll: { x: '100%', y: props.isScrollY ? 400 : '' },
|
scroll: { x: '100%', y: props.isScrollY ? props.scrollY ?? 400 : '' },
|
||||||
selectable: false,
|
selectable: false,
|
||||||
noDisable: true,
|
noDisable: true,
|
||||||
showSetting: false,
|
showSetting: false,
|
||||||
|
|
|
@ -175,7 +175,7 @@
|
||||||
:review-progress="props.reviewProgress"
|
:review-progress="props.reviewProgress"
|
||||||
:review-pass-rule="props.reviewPassRule"
|
:review-pass-rule="props.reviewPassRule"
|
||||||
@operation="handleMinderOperation"
|
@operation="handleMinderOperation"
|
||||||
@handle-review-done="handleReviewDone"
|
@handle-review-done="emit('refresh')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<a-modal
|
<a-modal
|
||||||
|
@ -372,6 +372,7 @@
|
||||||
|
|
||||||
import { ReviewCaseItem, ReviewItem, ReviewPassRule, ReviewResult } from '@/models/caseManagement/caseReview';
|
import { ReviewCaseItem, ReviewItem, ReviewPassRule, ReviewResult } from '@/models/caseManagement/caseReview';
|
||||||
import { BatchApiParams, TableQueryParams } from '@/models/common';
|
import { BatchApiParams, TableQueryParams } from '@/models/common';
|
||||||
|
import { StartReviewStatus } from '@/enums/caseEnum';
|
||||||
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
|
import { CaseManagementRouteEnum } from '@/enums/routeEnum';
|
||||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
|
import { FilterSlotNameEnum } from '@/enums/tableFilterEnum';
|
||||||
|
@ -907,7 +908,7 @@
|
||||||
reviewId: route.query.id as string,
|
reviewId: route.query.id as string,
|
||||||
userId: props.onlyMine ? userStore.id || '' : '',
|
userId: props.onlyMine ? userStore.id || '' : '',
|
||||||
reviewPassRule: props.reviewPassRule,
|
reviewPassRule: props.reviewPassRule,
|
||||||
status: dialogForm.value.result as ReviewResult,
|
status: dialogForm.value.result as StartReviewStatus,
|
||||||
content: dialogForm.value.reason,
|
content: dialogForm.value.reason,
|
||||||
notifier: dialogForm.value.commentIds.join(';'),
|
notifier: dialogForm.value.commentIds.join(';'),
|
||||||
moduleIds: props.activeFolder === 'all' ? [] : [props.activeFolder, ...props.offspringIds],
|
moduleIds: props.activeFolder === 'all' ? [] : [props.activeFolder, ...props.offspringIds],
|
||||||
|
@ -989,13 +990,6 @@
|
||||||
handleOperation(type);
|
handleOperation(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleReviewDone(refreshTree?: boolean) {
|
|
||||||
if (refreshTree) {
|
|
||||||
refresh(false);
|
|
||||||
}
|
|
||||||
emit('refresh');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理表格选中后批量操作
|
* 处理表格选中后批量操作
|
||||||
* @param event 批量操作事件对象
|
* @param event 批量操作事件对象
|
||||||
|
|
|
@ -73,10 +73,11 @@
|
||||||
|
|
||||||
const modalVisible = ref(false);
|
const modalVisible = ref(false);
|
||||||
const submitLoading = ref(false);
|
const submitLoading = ref(false);
|
||||||
|
const submitForm = computed(() => (modalVisible.value ? dialogForm.value : form.value));
|
||||||
const submitDisabled = computed(
|
const submitDisabled = computed(
|
||||||
() =>
|
() =>
|
||||||
form.value.status !== StartReviewStatus.PASS &&
|
submitForm.value.status !== StartReviewStatus.PASS &&
|
||||||
(form.value.content === '' || form.value.content.trim() === '<p style=""></p>')
|
(submitForm.value.content === '' || submitForm.value.content.trim() === '<p style=""></p>')
|
||||||
);
|
);
|
||||||
|
|
||||||
// 双击富文本内容打开弹窗
|
// 双击富文本内容打开弹窗
|
||||||
|
@ -114,14 +115,14 @@
|
||||||
userId: props.userId,
|
userId: props.userId,
|
||||||
reviewId: props.reviewId,
|
reviewId: props.reviewId,
|
||||||
reviewPassRule: props.reviewPassRule,
|
reviewPassRule: props.reviewPassRule,
|
||||||
...(modalVisible.value ? dialogForm.value : form.value),
|
...submitForm.value,
|
||||||
notifier: form.value.notifiers?.join(';') ?? '',
|
notifier: submitForm.value.notifiers?.join(';') ?? '',
|
||||||
...getMinderOperationParams(props.selectNode),
|
...getMinderOperationParams(props.selectNode),
|
||||||
};
|
};
|
||||||
await batchReview(params);
|
await batchReview(params);
|
||||||
modalVisible.value = false;
|
modalVisible.value = false;
|
||||||
Message.success(t('caseManagement.caseReview.reviewSuccess'));
|
Message.success(t('caseManagement.caseReview.reviewSuccess'));
|
||||||
emit('done', form.value.status);
|
emit('done', params.status);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|
|
@ -117,7 +117,7 @@
|
||||||
:plan-id="props.planId"
|
:plan-id="props.planId"
|
||||||
:can-edit="props.canEdit"
|
:can-edit="props.canEdit"
|
||||||
@operation="handleMinderOperation"
|
@operation="handleMinderOperation"
|
||||||
@handle-add-bug-done="emit('refresh')"
|
@refresh-plan="emit('refresh')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- 批量执行 -->
|
<!-- 批量执行 -->
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
:preview-url="PreviewEditorImageUrl"
|
:preview-url="PreviewEditorImageUrl"
|
||||||
:auto-height="false"
|
:auto-height="false"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
|
:max-height="props.richTextMaxHeight"
|
||||||
:placeholder="
|
:placeholder="
|
||||||
props.isDblclickPlaceholder
|
props.isDblclickPlaceholder
|
||||||
? t('testPlan.featureCase.richTextDblclickPlaceholder')
|
? t('testPlan.featureCase.richTextDblclickPlaceholder')
|
||||||
|
@ -44,6 +45,7 @@
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
isDblclickPlaceholder?: boolean;
|
isDblclickPlaceholder?: boolean;
|
||||||
|
richTextMaxHeight?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const form = defineModel<ExecuteFeatureCaseFormParams>('form', {
|
const form = defineModel<ExecuteFeatureCaseFormParams>('form', {
|
||||||
|
|
|
@ -26,26 +26,29 @@
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { Message } from '@arco-design/web-vue';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
|
import type { MinderJsonNode } from '@/components/pure/ms-minder-editor/props';
|
||||||
|
import { getMinderOperationParams } from '@/components/business/ms-minders/caseReviewMinder/utils';
|
||||||
import ExecuteForm from '@/views/test-plan/testPlan/detail/featureCase/components/executeForm.vue';
|
import ExecuteForm from '@/views/test-plan/testPlan/detail/featureCase/components/executeForm.vue';
|
||||||
|
|
||||||
import { runFeatureCase } from '@/api/modules/test-plan/testPlan';
|
import { batchExecuteCase, runFeatureCase } from '@/api/modules/test-plan/testPlan';
|
||||||
import { defaultExecuteForm } from '@/config/testPlan';
|
import { defaultExecuteForm } from '@/config/testPlan';
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
|
|
||||||
import type { StepExecutionResult } from '@/models/caseManagement/featureCase';
|
import type { StepExecutionResult } from '@/models/caseManagement/featureCase';
|
||||||
import type { ExecuteFeatureCaseFormParams } from '@/models/testPlan/testPlan';
|
import type { BatchExecuteFeatureCaseParams, ExecuteFeatureCaseFormParams } from '@/models/testPlan/testPlan';
|
||||||
import { LastExecuteResults } from '@/enums/caseEnum';
|
import { LastExecuteResults } from '@/enums/caseEnum';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
caseId: string;
|
caseId?: string;
|
||||||
testPlanId: string;
|
testPlanId: string;
|
||||||
id: string;
|
id?: string;
|
||||||
|
selectNode?: MinderJsonNode;
|
||||||
stepExecutionResult?: StepExecutionResult[];
|
stepExecutionResult?: StepExecutionResult[];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'done'): void;
|
(e: 'done', status: LastExecuteResults, content: string): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -104,18 +107,28 @@
|
||||||
submitLoading.value = true;
|
submitLoading.value = true;
|
||||||
const params = {
|
const params = {
|
||||||
projectId: appStore.currentProjectId,
|
projectId: appStore.currentProjectId,
|
||||||
caseId: props.caseId,
|
|
||||||
testPlanId: props.testPlanId,
|
testPlanId: props.testPlanId,
|
||||||
id: props.id,
|
|
||||||
...(modalVisible.value ? dialogForm.value : form.value),
|
...(modalVisible.value ? dialogForm.value : form.value),
|
||||||
stepsExecResult: JSON.stringify(props.stepExecutionResult) ?? '',
|
stepsExecResult: JSON.stringify(props.stepExecutionResult),
|
||||||
notifier: form.value?.commentIds?.join(';'),
|
notifier: (modalVisible.value ? dialogForm.value : form.value)?.commentIds?.join(';'),
|
||||||
};
|
};
|
||||||
await runFeatureCase(params);
|
// 脑图执行是批量执行
|
||||||
|
if (props.selectNode) {
|
||||||
|
await batchExecuteCase({
|
||||||
|
...params,
|
||||||
|
...getMinderOperationParams(props.selectNode),
|
||||||
|
} as BatchExecuteFeatureCaseParams);
|
||||||
|
} else {
|
||||||
|
await runFeatureCase({
|
||||||
|
...params,
|
||||||
|
caseId: props.caseId ?? '',
|
||||||
|
id: props.id ?? '',
|
||||||
|
});
|
||||||
|
}
|
||||||
modalVisible.value = false;
|
modalVisible.value = false;
|
||||||
Message.success(t('common.updateSuccess'));
|
Message.success(t('common.updateSuccess'));
|
||||||
form.value = { ...defaultExecuteForm };
|
form.value = { ...defaultExecuteForm };
|
||||||
emit('done');
|
emit('done', params.lastExecResult, params.content ?? '');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|
Loading…
Reference in New Issue