feat(脑图): 测试规划脑图雏形&测试计划详情去除关联用例
This commit is contained in:
parent
6b6f75ab2f
commit
03ff3517b1
|
@ -33,6 +33,7 @@ import {
|
|||
GetFeatureCaseModuleUrl,
|
||||
GetPlanDetailApiCaseListUrl,
|
||||
GetPlanDetailFeatureCaseListUrl,
|
||||
GetPlanMinderUrl,
|
||||
getStatisticalCountUrl,
|
||||
GetTestPlanCaseListUrl,
|
||||
GetTestPlanDetailUrl,
|
||||
|
@ -86,6 +87,7 @@ import type {
|
|||
PlanDetailExecuteHistoryItem,
|
||||
PlanDetailFeatureCaseItem,
|
||||
PlanDetailFeatureCaseListQueryParams,
|
||||
PlanMinderNode,
|
||||
RunFeatureCaseParams,
|
||||
SortApiCaseParams,
|
||||
SortFeatureCaseParams,
|
||||
|
@ -338,3 +340,7 @@ export function executePlanOrGroup(data: ExecutePlan) {
|
|||
export function deleteScheduleTask(testPlanId: string) {
|
||||
return MSR.get({ url: `${DeleteScheduleTaskUrl}/${testPlanId}` });
|
||||
}
|
||||
// 获取测试规划脑图
|
||||
export function getPlanMinder(testPlanId: string) {
|
||||
return MSR.get<PlanMinderNode[]>({ url: GetPlanMinderUrl, params: testPlanId });
|
||||
}
|
||||
|
|
|
@ -110,3 +110,5 @@ export const DisassociateApiCaseUrl = '/test-plan/api/case/disassociate';
|
|||
export const BatchDisassociateApiCaseUrl = '/test-plan/api/case/batch/disassociate';
|
||||
// 计划详情-接口用例列表-批量更新执行人
|
||||
export const BatchUpdateApiCaseExecutorUrl = '/test-plan/api/case/batch/update/executor';
|
||||
// 测试规划脑图
|
||||
export const GetPlanMinderUrl = '/test-plan/mind/data';
|
||||
|
|
|
@ -215,7 +215,7 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import { FormInstance, Message, SelectOptionData, ValidatedError } from '@arco-design/web-vue';
|
||||
import { FormInstance, SelectOptionData, ValidatedError } from '@arco-design/web-vue';
|
||||
|
||||
import { MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
|
||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||
|
@ -226,7 +226,7 @@
|
|||
import CaseTree from './caseTree.vue';
|
||||
import ScenarioCaseTable from './scenarioCaseTable.vue';
|
||||
|
||||
import { getAssociatedProjectOptions, getCustomFieldsTable } from '@/api/modules/case-management/featureCase';
|
||||
import { getAssociatedProjectOptions } from '@/api/modules/case-management/featureCase';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
|
||||
|
|
|
@ -212,7 +212,6 @@
|
|||
|
||||
import { CustomTypeMaps, MsAdvanceFilter } from '@/components/pure/ms-advance-filter';
|
||||
import { FilterFormItem, FilterType } from '@/components/pure/ms-advance-filter/type';
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
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';
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
<template>
|
||||
<FeatureCaseMinder :module-id="props.moduleId" :module-name="props.moduleName" :modules-count="props.modulesCount" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import FeatureCaseMinder from './featureCaseMinder/index.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
minderType: 'FeatureCase';
|
||||
moduleId: string;
|
||||
moduleName: string;
|
||||
modulesCount: Record<string, number>; // 模块数量
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
|
@ -26,7 +26,6 @@
|
|||
|
||||
import type { AssociateCaseRequest, AssociateCaseRequestType } from '@/models/testPlan/testPlan';
|
||||
import { CaseCountApiTypeEnum, CaseModulesApiTypeEnum, CasePageApiTypeEnum } from '@/enums/associateCaseEnum';
|
||||
import { CaseLinkEnum } from '@/enums/caseEnum';
|
||||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps<{
|
||||
|
@ -49,29 +48,24 @@
|
|||
const planId = ref(route.query.id as string);
|
||||
|
||||
async function saveHandler(params: AssociateCaseRequest) {
|
||||
try {
|
||||
confirmLoading.value = true;
|
||||
if (typeof props.saveApi !== 'function') {
|
||||
if (typeof props.saveApi !== 'function') {
|
||||
emit('success', { ...params, functionalSelectIds: params.selectIds });
|
||||
} else {
|
||||
try {
|
||||
confirmLoading.value = true;
|
||||
await props.saveApi({
|
||||
functionalSelectIds: params.selectIds,
|
||||
testPlanId: planId.value,
|
||||
});
|
||||
emit('success', { ...params, functionalSelectIds: params.selectIds });
|
||||
} else {
|
||||
try {
|
||||
await props.saveApi({
|
||||
functionalSelectIds: params.selectIds,
|
||||
testPlanId: planId.value,
|
||||
});
|
||||
emit('success', { ...params, functionalSelectIds: params.selectIds });
|
||||
Message.success(t('ms.case.associate.associateSuccess'));
|
||||
confirmLoading.value = false;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
Message.success(t('ms.case.associate.associateSuccess'));
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
innerVisible.value = false;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
confirmLoading.value = false;
|
||||
}
|
||||
innerVisible.value = false;
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,493 @@
|
|||
<template>
|
||||
<MsMinderEditor
|
||||
v-model:extra-visible="extraVisible"
|
||||
v-model:loading="loading"
|
||||
v-model:import-json="importJson"
|
||||
:tags="[]"
|
||||
:insert-node="insertNode"
|
||||
:can-show-enter-node="false"
|
||||
:insert-sibling-menus="insertSiblingMenus"
|
||||
:insert-son-menus="insertSonMenus"
|
||||
:can-show-paste-menu="false"
|
||||
:can-show-more-menu="false"
|
||||
:can-show-priority-menu="false"
|
||||
:can-show-float-menu="canShowFloatMenu"
|
||||
custom-priority
|
||||
single-tag
|
||||
tag-enable
|
||||
sequence-enable
|
||||
@content-change="handleContentChange"
|
||||
@node-select="checkNodeCanShowMenu"
|
||||
@before-exec-command="handleBeforeExecCommand"
|
||||
@save="handleMinderSave"
|
||||
>
|
||||
<template #extractMenu>
|
||||
<a-dropdown
|
||||
v-if="canShowExecuteMethodMenu"
|
||||
v-model:popup-visible="executeMethodMenuVisible"
|
||||
class="ms-minder-dropdown"
|
||||
:popup-translate="[0, 4]"
|
||||
position="bl"
|
||||
trigger="click"
|
||||
@select="(val) => handleExecuteMethodMenuSelect(val as RunMode)"
|
||||
>
|
||||
<a-tooltip :content="t('ms.minders.executeMethod')">
|
||||
<MsButton
|
||||
type="icon"
|
||||
class="ms-minder-node-float-menu-icon-button"
|
||||
:class="[executeMethodMenuVisible ? 'ms-minder-node-float-menu-icon-button--focus' : '']"
|
||||
>
|
||||
<MsIcon type="icon-icon_play-round_filled" class="text-[var(--color-text-4)]" />
|
||||
</MsButton>
|
||||
</a-tooltip>
|
||||
<template #content>
|
||||
<div class="mx-[6px] px-[8px] py-[3px] text-[var(--color-text-4)]">
|
||||
{{ t('ms.minders.executeMethod') }}
|
||||
</div>
|
||||
<a-doption :value="RunMode.SERIAL">
|
||||
<div
|
||||
class="flex h-[20px] w-[20px] items-center justify-center rounded-full bg-[rgb(var(--link-1))] text-[12px] font-medium text-[rgb(var(--link-5))]"
|
||||
>
|
||||
{{ t('ms.minders.serial') }}
|
||||
</div>
|
||||
</a-doption>
|
||||
<a-doption :value="RunMode.PARALLEL">
|
||||
<div
|
||||
class="flex h-[20px] w-[20px] items-center justify-center rounded-full bg-[rgb(var(--success-1))] text-[12px] font-medium text-[rgb(var(--success-6))]"
|
||||
>
|
||||
{{ t('ms.minders.parallel') }}
|
||||
</div>
|
||||
</a-doption>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
<a-tooltip v-if="showConfigMenu" :content="t('common.config')">
|
||||
<MsButton
|
||||
type="icon"
|
||||
class="ms-minder-node-float-menu-icon-button"
|
||||
:class="[extraVisible ? 'ms-minder-node-float-menu-icon-button--focus' : '']"
|
||||
@click="toggleDetail"
|
||||
>
|
||||
<MsIcon type="icon-icon_setting_filled" class="text-[var(--color-text-4)]" />
|
||||
</MsButton>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template #extractTabContent>
|
||||
<div class="px-[16px]">
|
||||
<a-form ref="configFormRef" :model="configForm" layout="vertical">
|
||||
<a-form-item>
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<div>{{ t('testPlan.planForm.pickCases') }}</div>
|
||||
<a-divider margin="4px" direction="vertical" />
|
||||
<MsButton
|
||||
type="text"
|
||||
:disabled="
|
||||
(selectedAssociateCasesParams.totalCount || selectedAssociateCasesParams.selectIds.length) === 0
|
||||
"
|
||||
@click="clearSelectedCases"
|
||||
>
|
||||
{{ t('caseManagement.caseReview.clearSelectedCases') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
</template>
|
||||
<div class="bg-[var(--color-text-n9)] p-[12px]">
|
||||
<div class="flex items-center">
|
||||
<div class="text-[var(--color-text-2)]">
|
||||
{{
|
||||
t('caseManagement.caseReview.selectedCases', {
|
||||
count: selectedAssociateCasesParams.selectAll
|
||||
? selectedAssociateCasesParams.totalCount
|
||||
: selectedAssociateCasesParams.selectIds.length,
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
<a-divider margin="8px" direction="vertical" />
|
||||
<MsButton
|
||||
v-permission="['CASE_REVIEW:READ+RELEVANCE']"
|
||||
type="text"
|
||||
class="font-medium"
|
||||
@click="caseAssociateVisible = true"
|
||||
>
|
||||
{{ t('ms.case.associate.title') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('system.project.resourcePool')">
|
||||
<a-select v-model:model-value="configForm.resourcePool" :options="resourcePoolOptions"></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item class="hidden-item">
|
||||
<a-radio-group v-model:model-value="configForm.executeType">
|
||||
<a-radio value="serial">{{ t('testPlan.testPlanIndex.serial') }}</a-radio>
|
||||
<a-radio value="parallel">{{ t('testPlan.testPlanIndex.parallel') }}</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="configForm.executeType === 'serial'" class="hidden-item">
|
||||
<div class="flex items-center gap-[8px]">
|
||||
<a-switch v-model:model-value="configForm.failStop" size="small"></a-switch>
|
||||
<div>{{ t('ms.minders.failStop') }}</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<a-form-item class="hidden-item">
|
||||
<div class="flex items-center gap-[8px]">
|
||||
<a-switch v-model:model-value="configForm.failRetry" size="small"></a-switch>
|
||||
<div>{{ t('ms.minders.failRetry') }}</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<template v-if="configForm.failRetry">
|
||||
<a-form-item class="hidden-item">
|
||||
<a-radio-group v-model:model-value="configForm.failRetryType">
|
||||
<a-radio value="step">{{ t('ms.minders.stepRetry') }}</a-radio>
|
||||
<a-radio value="scenario">{{ t('ms.minders.scenarioRetry') }}</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<div>{{ t('ms.minders.retry') }}</div>
|
||||
<div class="text-[var(--color-text-4)]">{{ t('ms.minders.retryTimes') }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<a-input-number
|
||||
v-model:model-value="configForm.retryTimes"
|
||||
mode="button"
|
||||
:step="1"
|
||||
:min="1"
|
||||
:precision="0"
|
||||
size="small"
|
||||
class="w-[120px]"
|
||||
></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
<div>{{ t('ms.minders.retrySpace') }}</div>
|
||||
<div class="text-[var(--color-text-4)]">{{ t('ms.minders.retrySpaces') }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<a-input-number
|
||||
v-model:model-value="configForm.retrySpace"
|
||||
mode="button"
|
||||
:step="100"
|
||||
:min="0"
|
||||
:precision="0"
|
||||
size="small"
|
||||
class="w-[120px]"
|
||||
></a-input-number>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<a-form-item class="hidden-item">
|
||||
<div class="flex items-center gap-[8px]">
|
||||
<a-switch v-model:model-value="configForm.extend" size="small"></a-switch>
|
||||
<div>{{ t('ms.minders.extend') }}</div>
|
||||
</div>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</template>
|
||||
</MsMinderEditor>
|
||||
<caseAssociate
|
||||
v-model:visible="caseAssociateVisible"
|
||||
v-model:currentSelectCase="currentSelectCase"
|
||||
:has-not-associated-ids="selectedAssociateCasesParams.selectIds"
|
||||
test-plan-id=""
|
||||
@success="writeAssociateCases"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { FormInstance, SelectOptionData } from '@arco-design/web-vue';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsMinderEditor from '@/components/pure/ms-minder-editor/minderEditor.vue';
|
||||
import { InsertMenuItem, MinderEvent, MinderJson, MinderJsonNode } from '@/components/pure/ms-minder-editor/props';
|
||||
import { setCustomPriorityView } from '@/components/pure/ms-minder-editor/script/tool/utils';
|
||||
import caseAssociate from './associateDrawer.vue';
|
||||
|
||||
import { getPlanMinder } from '@/api/modules/test-plan/testPlan';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import useMinderStore from '@/store/modules/components/minder-editor';
|
||||
import { filterTree, mapTree } from '@/utils';
|
||||
|
||||
import { AssociateCaseRequest } from '@/models/testPlan/testPlan';
|
||||
import { CaseLinkEnum } from '@/enums/caseEnum';
|
||||
import { RunMode } from '@/enums/testPlanEnum';
|
||||
|
||||
import Message from '@arco-design/web-vue/es/message';
|
||||
|
||||
const props = defineProps<{
|
||||
planId: string;
|
||||
}>();
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { t } = useI18n();
|
||||
const minderStore = useMinderStore();
|
||||
const loading = ref(false);
|
||||
const extraVisible = ref<boolean>(false);
|
||||
const importJson = ref<MinderJson>({
|
||||
root: {} as MinderJsonNode,
|
||||
template: 'default',
|
||||
treePath: [],
|
||||
});
|
||||
|
||||
/**
|
||||
* 插入节点
|
||||
* @param node 目标节点
|
||||
* @param type 插入类型
|
||||
* @param value 插入值
|
||||
*/
|
||||
function insertNode(node: MinderJsonNode, type: string, value?: string) {
|
||||
switch (type) {
|
||||
case 'AppendChildNode':
|
||||
break;
|
||||
case 'AppendSiblingNode':
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function handleContentChange(node: MinderJsonNode) {}
|
||||
|
||||
const insertSiblingMenus = ref<InsertMenuItem[]>([]);
|
||||
const insertSonMenus = ref<InsertMenuItem[]>([]);
|
||||
const canShowFloatMenu = ref(false);
|
||||
const canShowExecuteMethodMenu = ref(true);
|
||||
const executeMethodMenuVisible = ref(false);
|
||||
const showConfigMenu = ref(false);
|
||||
|
||||
/**
|
||||
* 检测节点可展示的菜单项
|
||||
* @param node 选中节点
|
||||
*/
|
||||
function checkNodeCanShowMenu(node: MinderJsonNode) {
|
||||
const { data } = node;
|
||||
|
||||
if (data?.level === 1 || data?.level === 2) {
|
||||
canShowFloatMenu.value = true;
|
||||
canShowExecuteMethodMenu.value = true;
|
||||
if (data?.level === 1) {
|
||||
insertSiblingMenus.value = [];
|
||||
insertSonMenus.value = [
|
||||
{
|
||||
value: 'testSet',
|
||||
label: t('ms.minders.testSet'),
|
||||
},
|
||||
];
|
||||
showConfigMenu.value = true;
|
||||
} else {
|
||||
insertSiblingMenus.value = [];
|
||||
insertSonMenus.value = [];
|
||||
}
|
||||
} else {
|
||||
canShowFloatMenu.value = false;
|
||||
canShowExecuteMethodMenu.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const currentPriority = ref<RunMode>(RunMode.SERIAL);
|
||||
const priorityTextMap = {
|
||||
2: t('ms.minders.serial'),
|
||||
3: t('ms.minders.parallel'),
|
||||
};
|
||||
const priorityMap = {
|
||||
[RunMode.SERIAL]: 2,
|
||||
[RunMode.PARALLEL]: 3,
|
||||
};
|
||||
function handleExecuteMethodMenuSelect(val: RunMode) {
|
||||
currentPriority.value = val;
|
||||
window.minder.execCommand('priority', priorityMap[val]);
|
||||
setCustomPriorityView(priorityTextMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换用例详情显示
|
||||
*/
|
||||
async function toggleDetail() {
|
||||
extraVisible.value = !extraVisible.value;
|
||||
const node: MinderJsonNode = window.minder.getSelectedNode();
|
||||
const { data } = node;
|
||||
if (extraVisible.value) {
|
||||
if (data?.resource && data.resource.includes('')) {
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否停止拖拽排序动作
|
||||
* @param dragNode 拖动节点
|
||||
* @param dropNode 目标节点
|
||||
*/
|
||||
function stopArrangeDrag(dragNodes: MinderJsonNode | MinderJsonNode[], dropNode: MinderJsonNode) {
|
||||
if (!Array.isArray(dragNodes)) {
|
||||
dragNodes = [dragNodes];
|
||||
}
|
||||
for (let i = 0; i < dragNodes.length; i++) {
|
||||
const dragNode = (dragNodes as MinderJsonNode[])[i];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 脑图命令执行前拦截
|
||||
* @param event 命令执行事件
|
||||
*/
|
||||
function handleBeforeExecCommand(event: MinderEvent) {
|
||||
if (event.commandName === 'movetoparent') {
|
||||
// 不允许跨节点拖拽
|
||||
event.stopPropagation();
|
||||
} else if (event.commandName === 'arrange') {
|
||||
// 拖拽排序拦截
|
||||
const dragNodes: MinderJsonNode[] = window.minder.getSelectedNodes();
|
||||
let dropNode: MinderJsonNode;
|
||||
if (dragNodes[0].parent?.children?.[event.commandArgs[0] as number]) {
|
||||
// 释放到目标节点后
|
||||
dropNode = dragNodes[0].parent?.children?.[event.commandArgs[0] as number];
|
||||
} else if (dragNodes[0].parent?.children?.[(event.commandArgs[0] as number) - 1]) {
|
||||
// 释放到目标节点前
|
||||
dropNode = dragNodes[0].parent?.children?.[(event.commandArgs[0] as number) - 1];
|
||||
} else {
|
||||
// 释放到最后一个节点
|
||||
dropNode = dragNodes[dragNodes.length - 1];
|
||||
}
|
||||
if (stopArrangeDrag(dragNodes, dropNode)) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const tempMinderParams = ref({
|
||||
projectId: appStore.currentProjectId,
|
||||
versionId: '',
|
||||
updateCaseList: [],
|
||||
updateModuleList: [],
|
||||
deleteResourceList: [],
|
||||
additionalNodeList: [],
|
||||
});
|
||||
|
||||
const configFormRef = ref<FormInstance>();
|
||||
const configForm = ref({
|
||||
resourcePool: '',
|
||||
executeType: 'serial',
|
||||
failStop: true,
|
||||
failRetry: true,
|
||||
failRetryType: 'step',
|
||||
retryTimes: 1,
|
||||
retrySpace: 1000,
|
||||
extend: true,
|
||||
});
|
||||
const resourcePoolOptions = ref<SelectOptionData[]>();
|
||||
|
||||
const currentSelectCase = ref<keyof typeof CaseLinkEnum>('FUNCTIONAL');
|
||||
const caseAssociateVisible = ref<boolean>(false);
|
||||
const caseAssociateProject = ref(appStore.currentProjectId);
|
||||
|
||||
// 批量关联用例表格参数
|
||||
const selectedAssociateCasesParams = ref<AssociateCaseRequest>({
|
||||
excludeIds: [],
|
||||
selectIds: [],
|
||||
selectAll: false,
|
||||
condition: {},
|
||||
moduleIds: [],
|
||||
versionId: '',
|
||||
refId: '',
|
||||
projectId: '',
|
||||
});
|
||||
|
||||
function writeAssociateCases(param: AssociateCaseRequest) {
|
||||
selectedAssociateCasesParams.value = { ...param };
|
||||
caseAssociateVisible.value = false;
|
||||
}
|
||||
|
||||
function clearSelectedCases() {
|
||||
selectedAssociateCasesParams.value = {
|
||||
excludeIds: [],
|
||||
selectIds: [],
|
||||
selectAll: false,
|
||||
condition: {},
|
||||
moduleIds: [],
|
||||
versionId: '',
|
||||
refId: '',
|
||||
projectId: '',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化测试规划脑图
|
||||
*/
|
||||
async function initMinder() {
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await getPlanMinder(props.planId);
|
||||
[importJson.value.root] = mapTree(res, (node, path, level) => {
|
||||
node.data = {
|
||||
...node.data,
|
||||
level,
|
||||
isNew: false,
|
||||
changed: false,
|
||||
};
|
||||
return node;
|
||||
});
|
||||
window.minder.importJson(importJson.value);
|
||||
window.minder.execCommand('template', Object.keys(window.kityminder.Minder.getTemplateList())[3]);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成脑图保存的入参
|
||||
*/
|
||||
function makeMinderParams(fullJson: MinderJson) {
|
||||
filterTree(fullJson.root.children, (node, nodeIndex, parent) => {
|
||||
if (node.data.isNew !== false || node.data.changed === true) {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return tempMinderParams.value;
|
||||
}
|
||||
|
||||
async function handleMinderSave(fullJson: MinderJson, callback: () => void) {
|
||||
try {
|
||||
loading.value = true;
|
||||
// await saveCaseMinder(makeMinderParams(fullJson));
|
||||
Message.success(t('common.saveSuccess'));
|
||||
initMinder();
|
||||
callback();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initMinder();
|
||||
nextTick(() => {
|
||||
window.minder.on('contentchange', () => {
|
||||
// 异步执行,否则执行完,还会被重置
|
||||
setTimeout(() => {
|
||||
setCustomPriorityView(priorityTextMap);
|
||||
}, 0);
|
||||
});
|
||||
window.minder.on('selectionchange', () => {
|
||||
setTimeout(() => {
|
||||
setCustomPriorityView(priorityTextMap);
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.arco-form-item) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,15 @@
|
|||
export default {
|
||||
'ms.minders.failStop': '失败停止',
|
||||
'ms.minders.failRetry': '失败重试',
|
||||
'ms.minders.stepRetry': '步骤重试',
|
||||
'ms.minders.scenarioRetry': '场景重试',
|
||||
'ms.minders.retry': '重试',
|
||||
'ms.minders.retryTimes': '(次)',
|
||||
'ms.minders.retrySpace': '每次间隔',
|
||||
'ms.minders.retrySpaces': '(ms)',
|
||||
'ms.minders.extend': '继承上级配置',
|
||||
'ms.minders.testSet': '测试集',
|
||||
'ms.minders.executeMethod': '运行方式',
|
||||
'ms.minders.serial': '串',
|
||||
'ms.minders.parallel': '并',
|
||||
};
|
|
@ -0,0 +1,25 @@
|
|||
<template>
|
||||
<a-alert v-if="!getIsVisited()" :show-icon="false" :type="props.type" closable @close="addVisited">
|
||||
<slot>{{ t(props.tip || '') }}</slot>
|
||||
<template #close-element>
|
||||
<span class="text-[14px]">{{ t('common.notRemind') }}</span>
|
||||
</template>
|
||||
</a-alert>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useVisit from '@/hooks/useVisit';
|
||||
|
||||
const props = defineProps<{
|
||||
tip?: string;
|
||||
type?: 'error' | 'normal' | 'success' | 'warning' | 'info';
|
||||
visitedKey: string;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { addVisited, getIsVisited } = useVisit(props.visitedKey);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
|
@ -12,7 +12,7 @@
|
|||
</a-breadcrumb-item>
|
||||
</a-breadcrumb>
|
||||
</div>
|
||||
<nodeFloatMenu v-bind="props">
|
||||
<nodeFloatMenu v-if="props.canShowFloatMenu" v-bind="props">
|
||||
<template #extractMenu>
|
||||
<slot name="extractMenu"></slot>
|
||||
</template>
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
const commandValue = ref('');
|
||||
const commandDisabled = ref(true);
|
||||
let minder = reactive<any>({});
|
||||
const minder = reactive<any>({});
|
||||
|
||||
const isDisable = (): boolean => {
|
||||
if (Object.keys(minder).length === 0) return true;
|
||||
|
@ -58,23 +58,23 @@
|
|||
return !!minder.queryCommandState && minder.queryCommandState('priority') === -1;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
minder = window.minder;
|
||||
const freshFuc = setPriorityView;
|
||||
if (minder.on) {
|
||||
minder.on('contentchange', () => {
|
||||
// 异步执行,否则执行完,还会被重置
|
||||
setTimeout(() => {
|
||||
freshFuc(props.priorityStartWithZero, props.priorityPrefix);
|
||||
}, 0);
|
||||
});
|
||||
minder.on('selectionchange', () => {
|
||||
commandDisabled.value = isDisable();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
// onMounted(() => {
|
||||
// nextTick(() => {
|
||||
// minder = window.minder;
|
||||
// const freshFuc = setPriorityView;
|
||||
// if (minder.on) {
|
||||
// minder.on('contentchange', () => {
|
||||
// // 异步执行,否则执行完,还会被重置
|
||||
// setTimeout(() => {
|
||||
// freshFuc(props.priorityStartWithZero, props.priorityPrefix);
|
||||
// }, 0);
|
||||
// });
|
||||
// minder.on('selectionchange', () => {
|
||||
// commandDisabled.value = isDisable();
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
function execCommand(index?: number) {
|
||||
if (index && minder.execCommand) {
|
||||
|
|
|
@ -233,6 +233,9 @@
|
|||
} else {
|
||||
menuVisible.value = false;
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -376,7 +379,7 @@
|
|||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
const freshFuc = setPriorityView;
|
||||
if (window.minder) {
|
||||
if (window.minder && !props.customPriority) {
|
||||
window.minder.on('contentchange', () => {
|
||||
// 异步执行,否则执行完,还会被重置
|
||||
setTimeout(() => {
|
||||
|
|
|
@ -7,16 +7,14 @@
|
|||
</template>
|
||||
</mainEditor>
|
||||
</div>
|
||||
<template v-if="props.extractContentTabList?.length">
|
||||
<div class="ms-minder-editor-extra" :class="[extraVisible ? 'ms-minder-editor-extra--visible' : '']">
|
||||
<div class="pl-[16px] pt-[16px]">
|
||||
<MsTab v-model:activeKey="activeExtraKey" :content-tab-list="props.extractContentTabList" mode="button" />
|
||||
</div>
|
||||
<div class="ms-minder-editor-extra-content">
|
||||
<slot name="extractTabContent"></slot>
|
||||
</div>
|
||||
<div class="ms-minder-editor-extra" :class="[extraVisible ? 'ms-minder-editor-extra--visible' : '']">
|
||||
<div v-if="props.extractContentTabList?.length" class="pl-[16px] pt-[16px]">
|
||||
<MsTab v-model:activeKey="activeExtraKey" :content-tab-list="props.extractContentTabList" mode="button" />
|
||||
</div>
|
||||
</template>
|
||||
<div class="ms-minder-editor-extra-content">
|
||||
<slot name="extractTabContent"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ export interface MinderJsonNodeData {
|
|||
text: string;
|
||||
resource?: string[];
|
||||
expandState?: 'collapse' | 'expand';
|
||||
priority?: number;
|
||||
priority?: number | string;
|
||||
// 前端渲染字段
|
||||
isNew?: boolean; // 是否脑图新增节点,需要在初始化脑图数据时标记已存在节点为 false 以区分是否新增节点
|
||||
changed?: boolean; // 脑图节点是否发生过变化
|
||||
|
@ -168,6 +168,16 @@ export const floatMenuProps = {
|
|||
replaceableTags: {
|
||||
type: Function as PropType<(nodes: MinderJsonNode[]) => string[]>,
|
||||
},
|
||||
// 是否显示浮动菜单
|
||||
canShowFloatMenu: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// 是否自定义优先级
|
||||
customPriority: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const insertProps = {
|
||||
|
|
|
@ -99,6 +99,26 @@ export function setPriorityView(priorityStartWithZero: boolean, priorityPrefix:
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置自定义优先级文本
|
||||
* @param valueMap 优先级数字与文本映射
|
||||
*/
|
||||
export function setCustomPriorityView(valueMap: Record<any, string>) {
|
||||
const items = document.getElementsByTagName('text');
|
||||
if (items) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
if (isPriority(item)) {
|
||||
const content = item.innerHTML;
|
||||
if (valueMap[content]) {
|
||||
// 检查当前节点内优先级文本是否在映射中,如果在则替换;否则代表已经被替换过了,不再处理
|
||||
item.innerHTML = valueMap[content];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将节点及其子节点id置为null,changed 标记为true
|
||||
* @param node
|
||||
|
|
|
@ -4,4 +4,18 @@ export enum testPlanTypeEnum {
|
|||
GROUP = 'GROUP',
|
||||
}
|
||||
|
||||
export default {};
|
||||
export enum RunMode {
|
||||
SERIAL = 'SERIAL', // 串行
|
||||
PARALLEL = 'PARALLEL', // 并行
|
||||
}
|
||||
|
||||
export enum TestSetType {
|
||||
FUNCTIONAL_CASE = 'FUNCTIONAL_CASE',
|
||||
API_CASE = 'API_CASE',
|
||||
SCENARIO_CASE = 'SCENARIO_CASE',
|
||||
}
|
||||
|
||||
export enum FailRetry {
|
||||
STEP = 'STEP',
|
||||
SCENARIO = 'SCENARIO',
|
||||
}
|
||||
|
|
|
@ -187,4 +187,5 @@ export default {
|
|||
'common.noMatchData': 'No matching data',
|
||||
'common.name': 'name',
|
||||
'common.stopped': 'Stopped',
|
||||
'common.config': 'Config',
|
||||
};
|
||||
|
|
|
@ -188,4 +188,5 @@ export default {
|
|||
'common.noMatchData': '暂无匹配数据',
|
||||
'common.name': '名称',
|
||||
'common.stopped': '已停止',
|
||||
'common.config': '配置',
|
||||
};
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import type { MinderJsonNodeData } from '@/components/pure/ms-minder-editor/props';
|
||||
import type { BatchActionQueryParams } from '@/components/pure/ms-table/type';
|
||||
|
||||
import type { customFieldsItem } from '@/models/caseManagement/featureCase';
|
||||
import type { TableQueryParams } from '@/models/common';
|
||||
import { BatchApiParams, DragSortParams } from '@/models/common';
|
||||
import { LastExecuteResults } from '@/enums/caseEnum';
|
||||
import { testPlanTypeEnum } from '@/enums/testPlanEnum';
|
||||
import { type FailRetry, type RunMode, testPlanTypeEnum, type TestSetType } from '@/enums/testPlanEnum';
|
||||
|
||||
export type planStatusType = 'PREPARED' | 'UNDERWAY' | 'COMPLETED' | 'ARCHIVED';
|
||||
|
||||
|
@ -352,4 +353,26 @@ export interface ExecutePlan {
|
|||
executeIds: string[];
|
||||
executeMode: RunModeType;
|
||||
}
|
||||
export default {};
|
||||
|
||||
export interface PlanMinderNodeData extends MinderJsonNodeData {
|
||||
id: string;
|
||||
pos: number;
|
||||
text: string;
|
||||
num: number; // 关联用例数量
|
||||
priority: string; // 串行/并行
|
||||
executeMethod: RunMode; // 串行/并行值
|
||||
type: TestSetType; // 测试集类型(功能/接口/场景)
|
||||
extended: boolean;
|
||||
grouped: boolean; // 是否使用环境组
|
||||
environmentId: string;
|
||||
testResourcePoolId: string;
|
||||
retryOnFail: boolean;
|
||||
retryType: FailRetry; // 失败重试类型(步骤/场景)
|
||||
retryTimes: number;
|
||||
retryInterval: number;
|
||||
stopOnFail: boolean;
|
||||
}
|
||||
export interface PlanMinderNode {
|
||||
data: PlanMinderNodeData;
|
||||
children: PlanMinderNode[];
|
||||
}
|
||||
|
|
|
@ -233,7 +233,7 @@ export function traverseTree<T>(
|
|||
*/
|
||||
export function mapTree<T>(
|
||||
tree: TreeNode<T> | TreeNode<T>[] | T | T[],
|
||||
customNodeFn: (node: TreeNode<T>, path: string) => TreeNode<T> | null = (node) => node,
|
||||
customNodeFn: (node: TreeNode<T>, path: string, _level: number) => TreeNode<T> | null = (node) => node,
|
||||
customChildrenKey = 'children',
|
||||
parentPath = '',
|
||||
level = 0,
|
||||
|
@ -258,7 +258,7 @@ export function mapTree<T>(
|
|||
const fullPath = node.path ? `${_parentPath}/${node.path}`.replace(/\/+/g, '/') : '';
|
||||
node.sort = i + 1; // sort 从 1 开始
|
||||
node.parent = _parent || undefined; // 没有父节点说明是树的第一层
|
||||
const newNode = typeof customNodeFn === 'function' ? customNodeFn(node, fullPath) : node;
|
||||
const newNode = typeof customNodeFn === 'function' ? customNodeFn(node, fullPath, _level) : node;
|
||||
if (newNode) {
|
||||
newNode.level = _level;
|
||||
if (newNode[customChildrenKey] && newNode[customChildrenKey].length > 0) {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-alert v-if="!getIsVisited()" :show-icon="false" class="mb-[16px]" type="warning" closable @close="addVisited">
|
||||
{{ t('apiTestManagement.historyListTip') }}
|
||||
<template #close-element>
|
||||
<span class="text-[14px]">{{ t('common.notRemind') }}</span>
|
||||
</template>
|
||||
</a-alert>
|
||||
<MsNotRemind
|
||||
tip="apiTestManagement.historyListTip"
|
||||
class="mb-[16px]"
|
||||
type="warning"
|
||||
visited-key="messageManagementRobotListTip"
|
||||
/>
|
||||
<ms-base-table v-bind="propsRes" no-disable v-on="propsEvent">
|
||||
<!-- <template #action="{ record }">
|
||||
<div class="flex items-center">
|
||||
|
@ -23,11 +23,11 @@
|
|||
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 MsNotRemind from '@/components/business/ms-not-remind/index.vue';
|
||||
|
||||
import { operationHistory } from '@/api/modules/api-test/management';
|
||||
import { operationTypeOptions } from '@/config/common';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useVisit from '@/hooks/useVisit';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
|
||||
const props = defineProps<{
|
||||
|
@ -36,8 +36,6 @@
|
|||
|
||||
const appStore = useAppStore();
|
||||
const { t } = useI18n();
|
||||
const visitedKey = 'messageManagementRobotListTip';
|
||||
const { addVisited, getIsVisited } = useVisit(visitedKey);
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<div class="history-container">
|
||||
<a-alert v-if="!getIsVisited()" :show-icon="false" class="mb-[16px]" type="warning" closable @close="addVisited">
|
||||
{{ t('apiTestManagement.historyListTip') }}
|
||||
<template #close-element>
|
||||
<span class="text-[14px]">{{ t('common.notRemind') }}</span>
|
||||
</template>
|
||||
</a-alert>
|
||||
<MsNotRemind
|
||||
tip="apiTestManagement.historyListTip"
|
||||
class="mb-[16px]"
|
||||
type="warning"
|
||||
visited-key="apiTestCaseChangeHistoryTip"
|
||||
/>
|
||||
<ms-base-table v-bind="propsRes" no-disable v-on="propsEvent" @filter-change="filterChange"> </ms-base-table>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -16,11 +16,11 @@
|
|||
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 MsNotRemind from '@/components/business/ms-not-remind/index.vue';
|
||||
|
||||
import { getApiCaseChangeHistory } from '@/api/modules/api-test/management';
|
||||
import { operationTypeOptions } from '@/config/common';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useVisit from '@/hooks/useVisit';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import { hasAnyPermission } from '@/utils/permission';
|
||||
|
||||
|
@ -33,8 +33,6 @@
|
|||
|
||||
const appStore = useAppStore();
|
||||
const { t } = useI18n();
|
||||
const visitedKey = 'messageManagementRobotListTip';
|
||||
const { addVisited, getIsVisited } = useVisit(visitedKey);
|
||||
|
||||
const typeOptions = [
|
||||
{
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-alert v-if="!getIsVisited()" :show-icon="false" class="mb-[16px]" type="warning" closable @close="addVisited">
|
||||
{{ t('apiScenario.historyListTip') }}
|
||||
<template #close-element>
|
||||
<span class="text-[14px]">{{ t('common.notRemind') }}</span>
|
||||
</template>
|
||||
</a-alert>
|
||||
<MsNotRemind tip="apiScenario.historyListTip" class="mb-[16px]" type="warning" visited-key="scenarioHistoryTip" />
|
||||
<ms-base-table v-bind="propsRes" no-disable v-on="propsEvent"></ms-base-table>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -16,17 +11,15 @@
|
|||
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 MsNotRemind from '@/components/business/ms-not-remind/index.vue';
|
||||
|
||||
import { getScenarioHistory } from '@/api/modules/api-test/scenario';
|
||||
import { operationTypeOptions } from '@/config/common';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useVisit from '@/hooks/useVisit';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { t } = useI18n();
|
||||
const visitedKey = 'scenarioHistoryTip';
|
||||
const { addVisited, getIsVisited } = useVisit(visitedKey);
|
||||
const props = defineProps<{
|
||||
sourceId?: string | number;
|
||||
}>();
|
||||
|
|
|
@ -70,34 +70,6 @@
|
|||
<a-form-item field="tags" :label="t('common.tag')" class="w-[436px]">
|
||||
<MsTagsInput v-model:model-value="form.tags" :max-tag-count="10" />
|
||||
</a-form-item>
|
||||
<a-form-item v-if="!props.planId?.length">
|
||||
<template #label>
|
||||
<div class="flex items-center">
|
||||
{{ t('testPlan.planForm.pickCases') }}
|
||||
<a-divider margin="4px" direction="vertical" />
|
||||
<MsButton
|
||||
type="text"
|
||||
:disabled="form.baseAssociateCaseRequest?.selectIds.length === 0"
|
||||
@click="clearSelectedCases"
|
||||
>
|
||||
{{ t('caseManagement.caseReview.clearSelectedCases') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex w-[436px] items-center rounded bg-[var(--color-text-n9)] p-[12px]">
|
||||
<div class="text-[var(--color-text-2)]">
|
||||
{{
|
||||
t('caseManagement.caseReview.selectedCases', {
|
||||
count: getSelectedCount,
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
<a-divider margin="8px" direction="vertical" />
|
||||
<MsButton type="text" class="font-medium" @click="caseAssociateVisible = true">
|
||||
{{ t('ms.case.associate.title') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
</a-form-item>
|
||||
<MsMoreSettingCollapse>
|
||||
<template #content>
|
||||
<div v-for="item in switchList" :key="item.key" class="mb-[24px] flex items-center gap-[8px]">
|
||||
|
@ -134,11 +106,6 @@
|
|||
</MsMoreSettingCollapse>
|
||||
</a-form>
|
||||
</MsDrawer>
|
||||
<AssociateDrawer
|
||||
v-model:visible="caseAssociateVisible"
|
||||
:has-not-associated-ids="form.baseAssociateCaseRequest?.selectIds"
|
||||
@success="writeAssociateCases"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -147,18 +114,16 @@
|
|||
import { cloneDeep } from 'lodash-es';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
|
||||
import MsMoreSettingCollapse from '@/components/pure/ms-more-setting-collapse/index.vue';
|
||||
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
|
||||
import AssociateDrawer from './components/associateDrawer.vue';
|
||||
|
||||
import { addTestPlan, getTestPlanDetail, updateTestPlan } from '@/api/modules/test-plan/testPlan';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
|
||||
import { ModuleTreeNode } from '@/models/common';
|
||||
import type { AddTestPlanParams, AssociateCaseRequest, SwitchListModel } from '@/models/testPlan/testPlan';
|
||||
import type { AddTestPlanParams, SwitchListModel } from '@/models/testPlan/testPlan';
|
||||
import { testPlanTypeEnum } from '@/enums/testPlanEnum';
|
||||
|
||||
import { DisabledTimeProps } from '@arco-design/web-vue/es/date-picker/interface';
|
||||
|
@ -267,14 +232,6 @@
|
|||
},
|
||||
];
|
||||
|
||||
const caseAssociateVisible = ref(false);
|
||||
function clearSelectedCases() {
|
||||
form.value.baseAssociateCaseRequest = cloneDeep(initForm.baseAssociateCaseRequest);
|
||||
}
|
||||
function writeAssociateCases(param: AssociateCaseRequest) {
|
||||
form.value.baseAssociateCaseRequest = { ...param };
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
innerVisible.value = false;
|
||||
formRef.value?.resetFields();
|
||||
|
@ -351,13 +308,4 @@
|
|||
const okText = computed(() => {
|
||||
return props.planId ? t('common.update') : t('common.create');
|
||||
});
|
||||
|
||||
const getSelectedCount = computed(() => {
|
||||
if (props.planId) {
|
||||
return form.value?.functionalCaseCount || 0;
|
||||
}
|
||||
return form.value.baseAssociateCaseRequest?.selectAll
|
||||
? form.value.baseAssociateCaseRequest?.totalCount
|
||||
: form.value.baseAssociateCaseRequest?.selectIds.length;
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -29,15 +29,6 @@
|
|||
@change="loadActiveTabList"
|
||||
/>
|
||||
<span class="mr-[14px]">{{ t('testPlan.testPlanDetail.moduleView') }}</span>
|
||||
<MsButton
|
||||
v-if="hasAnyPermission(['PROJECT_TEST_PLAN:READ+ASSOCIATION']) && detail.status !== 'ARCHIVED'"
|
||||
type="button"
|
||||
status="default"
|
||||
@click="linkCase"
|
||||
>
|
||||
<MsIcon type="icon-icon_link-record_outlined1" class="mr-[8px]" />
|
||||
{{ t('ms.case.associate.title') }}
|
||||
</MsButton>
|
||||
<MsButton v-if="isEnableEdit" type="button" status="default" @click="editorCopyHandler(false)">
|
||||
<MsIcon type="icon-icon_edit_outlined" class="mr-[8px]" />
|
||||
{{ t('common.edit') }}
|
||||
|
@ -110,6 +101,7 @@
|
|||
</MsCard>
|
||||
<!-- special-height的174: 上面卡片高度158 + mt的16 -->
|
||||
<MsCard class="mt-[16px]" :special-height="174" simple has-breadcrumb no-content-padding>
|
||||
<Plan v-if="activeTab === 'plan'" :plan-id="planId" />
|
||||
<FeatureCase
|
||||
v-if="activeTab === 'featureCase'"
|
||||
ref="featureCaseRef"
|
||||
|
@ -135,15 +127,6 @@
|
|||
/>
|
||||
<ExecuteHistory v-if="activeTab === 'executeHistory'" />
|
||||
</MsCard>
|
||||
<!-- TODO 待联调关联用例 目前可以暂时关联功能用例 -->
|
||||
<AssociateDrawer
|
||||
v-model:visible="caseAssociateVisible"
|
||||
:associated-ids="detail.repeatCase ? hasSelectedIds : []"
|
||||
:save-api="associationCaseToPlan"
|
||||
:test-plan-id="planId"
|
||||
@success="handleSuccess"
|
||||
/>
|
||||
|
||||
<CreateAndEditPlanDrawer
|
||||
v-model:visible="showPlanDrawer"
|
||||
:plan-id="planId"
|
||||
|
@ -167,18 +150,17 @@
|
|||
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||
import MsStatusTag from '@/components/business/ms-status-tag/index.vue';
|
||||
import ActionModal from '../components/actionModal.vue';
|
||||
import AssociateDrawer from '../components/associateDrawer.vue';
|
||||
import StatusProgress from '../components/statusProgress.vue';
|
||||
import ApiCase from './apiCase/index.vue';
|
||||
import ApiScenario from './apiScenario/index.vue';
|
||||
import BugManagement from './bugManagement/index.vue';
|
||||
import ExecuteHistory from './executeHistory/index.vue';
|
||||
import FeatureCase from './featureCase/index.vue';
|
||||
import Plan from './plan/index.vue';
|
||||
import CreateAndEditPlanDrawer from '@/views/test-plan/testPlan/createAndEditPlanDrawer.vue';
|
||||
|
||||
import {
|
||||
archivedPlan,
|
||||
associationCaseToPlan,
|
||||
followPlanRequest,
|
||||
generateReport,
|
||||
getPlanPassRate,
|
||||
|
@ -317,8 +299,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
const activeTab = ref('featureCase');
|
||||
const activeTab = ref('plan');
|
||||
const tabList = ref([
|
||||
{
|
||||
value: 'plan',
|
||||
label: t('testPlan.plan'),
|
||||
},
|
||||
{
|
||||
value: 'featureCase',
|
||||
label: t('menu.caseManagement.featureCase'),
|
||||
|
@ -358,12 +344,7 @@
|
|||
return '';
|
||||
}
|
||||
}
|
||||
const hasSelectedIds = ref<string[]>([]);
|
||||
const caseAssociateVisible = ref(false);
|
||||
// 关联用例
|
||||
function linkCase() {
|
||||
caseAssociateVisible.value = true;
|
||||
}
|
||||
|
||||
const showPlanDrawer = ref(false);
|
||||
|
||||
// 生成报告
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<template>
|
||||
<div class="flex h-full flex-col">
|
||||
<div class="p-[16px]">
|
||||
<MsNotRemind tip="testPlan.planTip" type="info" visited-key="testPlanTip" />
|
||||
</div>
|
||||
<div class="flex-1 overflow-hidden px-[16px]">
|
||||
<MsTestPlanMinder :plan-id="props.planId" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import MsTestPlanMinder from '@/components/business/ms-minders/testPlanMinder/index.vue';
|
||||
import MsNotRemind from '@/components/business/ms-not-remind/index.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
planId: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
|
@ -137,4 +137,7 @@ export default {
|
|||
'testPlan.testPlanGroup.deleteScheduleTaskSuccess': 'Delete the scheduled task successfully',
|
||||
'testPlan.testPlanGroup.enableScheduleTaskSuccess': 'Start the scheduled task successfully',
|
||||
'testPlan.testPlanGroup.closeScheduleTaskSuccess': 'Scheduled mission closed successfully',
|
||||
'testPlan.plan': 'Test plan',
|
||||
'testPlan.planTip':
|
||||
'1. Create a test set for business classification testing; 2. Select the test set associated use case',
|
||||
};
|
||||
|
|
|
@ -126,4 +126,6 @@ export default {
|
|||
'testPlan.testPlanGroup.deleteScheduleTaskSuccess': '删除定时任务成功',
|
||||
'testPlan.testPlanGroup.enableScheduleTaskSuccess': '开启定时任务成功',
|
||||
'testPlan.testPlanGroup.closeScheduleTaskSuccess': '关闭定时任务成功',
|
||||
'testPlan.plan': '测试规划',
|
||||
'testPlan.planTip': '1.创建测试集进行业务分类测试;2.选择测试集关联用例',
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue