fix(全局): 部分 bug 修复

This commit is contained in:
baiqi 2024-06-19 18:58:51 +08:00 committed by Craftsman
parent 1b26df9c2d
commit 33929d6978
17 changed files with 201 additions and 124 deletions

View File

@ -85,7 +85,6 @@
} from '@/api/modules/case-management/featureCase';
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
import useFeatureCaseStore from '@/store/modules/case/featureCase';
import useMinderStore from '@/store/modules/components/minder-editor/index';
import { MinderCustomEvent } from '@/store/modules/components/minder-editor/types';
import { filterTree, getGenerateId, mapTree, replaceNodeInTree } from '@/utils';
@ -160,60 +159,66 @@
async function initCaseTree(notRemote = false) {
try {
loading.value = true;
let res: MinderJsonNode[];
if (notRemote) {
res = caseTree.value;
} else {
res = await getCaseMinderTree({
if (!notRemote) {
const res = await getCaseMinderTree({
projectId: appStore.currentProjectId,
moduleId: '', //
});
}
caseTree.value = mapTree<MinderJsonNode>(res, (e) => ({
...e,
data: {
id: e.id,
text: e.name,
resource: props.modulesCount[e.id] !== undefined ? [moduleTag] : e.data?.resource,
expandState: e.level === 1 ? 'expand' : 'collapse',
count: props.modulesCount[e.id],
isNew: false,
changed: false,
},
children:
props.modulesCount[e.id] > 0 && !e.children?.length
? [
{
data: {
id: 'fakeNode',
text: 'fakeNode',
resource: ['fakeNode'],
isNew: false,
changed: false,
caseTree.value = mapTree<MinderJsonNode>(res, (e) => ({
...e,
data: {
...e.data,
id: e.id || e.data?.id || '',
text: e.name || e.data?.text || '',
resource: props.modulesCount[e.id] !== undefined ? [moduleTag] : e.data?.resource,
expandState: e.level === 1 ? 'expand' : 'collapse',
count: props.modulesCount[e.id],
isNew: false,
changed: false,
},
children:
props.modulesCount[e.id] > 0 && !e.children?.length
? [
{
data: {
id: 'fakeNode',
text: 'fakeNode',
resource: ['fakeNode'],
isNew: false,
changed: false,
},
},
},
]
: e.children,
}));
importJson.value.root = {
children: caseTree.value,
data: {
id: 'NONE',
text: t('ms.minders.allModule'),
resource: [moduleTag],
disabled: true,
},
};
importJson.value.treePath = [];
window.minder.importJson(importJson.value);
window.minder.execCommand('camera', window.minder.getRoot(), 100);
if (props.moduleId !== 'all') {
// ID
nextTick(() => {
minderStore.dispatchEvent(MinderEventName.ENTER_NODE, undefined, undefined, undefined, [
window.minder.getNodeById(props.moduleId),
]);
});
]
: e.children,
}));
importJson.value.root = {
children: caseTree.value,
data: {
id: 'NONE',
text: t('ms.minders.allModule'),
resource: [moduleTag],
disabled: true,
},
};
importJson.value.treePath = [];
window.minder.importJson(importJson.value);
}
if (notRemote) {
if (props.moduleId !== 'all') {
// ID
nextTick(() => {
minderStore.dispatchEvent(MinderEventName.ENTER_NODE, undefined, undefined, undefined, [
window.minder.getNodeById(props.moduleId),
]);
});
} else {
// ID
nextTick(() => {
minderStore.dispatchEvent(MinderEventName.ENTER_NODE, undefined, undefined, undefined, [
importJson.value.root,
]);
});
}
}
} catch (error) {
// eslint-disable-next-line no-console
@ -391,12 +396,13 @@
if ((!res || res.length === 0) && node.children?.length) {
//
node.expand();
node.renderTree();
window.minder.renderNodeBatch(node.children);
node.layout();
data.isLoaded = true;
return;
}
// TODO:
const waitingRenderNodes: MinderJsonNode[] = [];
let waitingRenderNodes: MinderJsonNode[] = [];
res.forEach((e) => {
//
const child = window.minder.createNode(
@ -439,14 +445,14 @@
});
node.expand();
// node.renderTree();
if (node.children && node.children.length > 0) {
waitingRenderNodes = waitingRenderNodes.concat(node.children);
}
window.minder.renderNodeBatch(waitingRenderNodes);
node.layout();
window.minder.execCommand('camera', node, 100);
if (node.data) {
node.data.isLoaded = true;
}
data.isLoaded = true;
// importJson
replaceNodeInTree([importJson.value.root], node.data?.id || '', node, 'data', 'id');
replaceNodeInTree([importJson.value.root], node.data?.id || '', window.minder.exportNode(node), 'data', 'id');
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
@ -458,11 +464,6 @@
extraVisible.value = false;
showDetailMenu.value = false;
resetExtractInfo();
if (node.children && node.children.length > 0 && node.data?.expandState === 'collapse') {
node.expand();
node.renderTree();
node.layout();
}
}
setPriorityView(true, 'P');
}
@ -524,7 +525,7 @@
if (!caseOffspringTags.some((e) => node.data?.resource?.includes(e))) {
//
tempMinderParams.value.deleteResourceList.push({
id: node.data?.id || getGenerateId(),
id: node.data?.id || '',
type: node.data?.resource?.[0] || moduleTag,
});
}
@ -556,12 +557,12 @@
* 解析用例节点信息
* @param node 用例节点
*/
function getCaseNodeInfo(node: MinderJsonNode) {
function getCaseNodeInfo(node?: MinderJsonNode) {
let textStep: MinderJsonNode | undefined; //
let prerequisiteNode: MinderJsonNode | undefined; //
let remarkNode: MinderJsonNode | undefined; //
const stepNodes: MinderJsonNode[] = []; //
node.children?.forEach((item) => {
node?.children?.forEach((item) => {
if (item.data?.resource?.includes(textDescTag)) {
textStep = item;
} else if (item.data?.resource?.includes(stepTag)) {
@ -577,7 +578,7 @@
id: child.data?.id || getGenerateId(),
num: i,
desc: child.data?.text || '',
result: child.children?.[0].data?.text || '',
result: child.children?.[0]?.data?.text || '',
};
});
return {
@ -587,7 +588,7 @@
textDescription: textStep?.data?.text || '',
expectedResult: textStep?.children?.[0]?.data?.text || '',
description: remarkNode?.data?.text || '',
priority: node.data?.priority,
priority: node?.data?.priority,
};
}
@ -612,6 +613,17 @@
};
}
function resetMinderParams() {
tempMinderParams.value = {
projectId: appStore.currentProjectId,
versionId: '',
updateCaseList: [],
updateModuleList: [],
deleteResourceList: [],
additionalNodeList: [],
};
}
/**
* 生成脑图保存的入参
*/
@ -675,28 +687,20 @@
await saveCaseMinder(makeMinderParams(fullJson));
extraVisible.value = false;
Message.success(t('common.saveSuccess'));
tempMinderParams.value = {
projectId: appStore.currentProjectId,
versionId: '',
updateCaseList: [],
updateModuleList: [],
deleteResourceList: [],
additionalNodeList: [],
};
resetMinderParams();
emit('save');
callback();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
resetMinderParams();
} finally {
loading.value = false;
}
}
const featureCaseStore = useFeatureCaseStore();
watch(
() => featureCaseStore.modulesCount,
() => props.moduleId,
() => {
initCaseTree(true);
},

View File

@ -259,9 +259,10 @@ export default function useMinderBaseApi({ hasEditPermission }: { hasEditPermiss
if (
Object.keys(node.data || {}).length === 0 ||
node.data?.id === 'root' ||
(node.parent?.data?.resource || []).length === 0
(node.parent?.data?.resource || []).length === 0 ||
node.parent?.data?.id === 'NONE'
) {
// 没有数据的节点、默认模块节点、父节点为文本节点的节点不可替换标签
// 没有数据的节点、默认模块节点、父节点为文本节点、父节点为NONE虚拟根节点的节点不可替换标签
return [];
}
if (node.data?.resource?.some((e) => topTags.includes(e))) {

View File

@ -13,7 +13,7 @@
:can-show-priority-menu="false"
:can-show-float-menu="canShowFloatMenu"
:can-show-delete-menu="canShowDeleteMenu"
:disable="!hasEditPermission"
:disabled="!hasEditPermission"
custom-priority
single-tag
tag-enable
@ -86,7 +86,7 @@
</a-tooltip>
</div>
<a-form ref="configFormRef" :model="configForm" :disabled="!hasEditPermission" layout="vertical">
<a-form-item v-if="hasEditPermission">
<a-form-item v-if="hasEditPermission && configForm.level === 2">
<template #label>
<div class="flex items-center">
<div>{{ t('testPlan.planForm.pickCases') }}</div>
@ -127,27 +127,37 @@
</div>
</div>
</a-form-item>
<template
v-if="
configForm.type !== PlanMinderCollectionType.FUNCTIONAL &&
(configForm.level === 1 || !configForm.extended)
"
>
<template v-if="configForm.type !== PlanMinderCollectionType.FUNCTIONAL">
<a-form-item :label="t('system.project.resourcePool')">
<a-select v-model:model-value="configForm.testResourcePoolId" :options="resourcePoolOptions"></a-select>
<a-select
v-model:model-value="configForm.testResourcePoolId"
:options="resourcePoolOptions"
:disabled="configForm.level === 2 && configForm.extended"
></a-select>
</a-form-item>
<a-form-item :label="t('project.environmental.env')">
<a-select v-model:model-value="configForm.environmentId" :options="environmentOptions"></a-select>
<a-select
v-model:model-value="configForm.environmentId"
:options="environmentOptions"
:disabled="configForm.level === 2 && configForm.extended"
></a-select>
</a-form-item>
<a-form-item class="hidden-item">
<a-radio-group v-model:model-value="configForm.executeMethod">
<a-radio-group
v-model:model-value="configForm.executeMethod"
:disabled="configForm.level === 2 && configForm.extended"
>
<a-radio :value="RunMode.SERIAL">{{ t('testPlan.testPlanIndex.serial') }}</a-radio>
<a-radio :value="RunMode.PARALLEL">{{ t('testPlan.testPlanIndex.parallel') }}</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item v-if="configForm.executeMethod === RunMode.SERIAL" class="hidden-item">
<div class="flex items-center gap-[8px]">
<a-switch v-model:model-value="configForm.stopOnFail" size="small"></a-switch>
<a-switch
v-model:model-value="configForm.stopOnFail"
size="small"
:disabled="configForm.level === 2 && configForm.extended"
></a-switch>
<div>{{ t('ms.minders.failStop') }}</div>
</div>
</a-form-item>
@ -206,7 +216,7 @@
class="hidden-item"
>
<div class="flex items-center gap-[8px]">
<a-switch v-model:model-value="configForm.extended" size="small"></a-switch>
<a-switch v-model:model-value="configForm.extended" size="small" @change="handleExtendChange"></a-switch>
<div>{{ t('ms.minders.extend') }}</div>
</div>
</a-form-item>
@ -270,6 +280,7 @@
const props = defineProps<{
planId: string;
status: string;
}>();
const emit = defineEmits<{
(e: 'save'): void;
@ -297,7 +308,9 @@
const showConfigMenu = ref(false);
const canShowDeleteMenu = ref(false);
const extraVisible = ref<boolean>(false);
const hasEditPermission = hasAnyPermission(['PROJECT_TEST_PLAN:READ+UPDATE']);
const hasEditPermission = computed(
() => props.status !== 'ARCHIVED' && hasAnyPermission(['PROJECT_TEST_PLAN:READ+UPDATE'])
);
/**
* 检测节点可展示的菜单项
@ -306,13 +319,18 @@
function checkNodeCanShowMenu(node: PlanMinderNode) {
const { data } = node;
if (!hasEditPermission && (data?.level === 1 || data?.level === 2)) {
if (!hasEditPermission.value && (data?.level === 1 || data?.level === 2)) {
//
if (data?.type === PlanMinderCollectionType.FUNCTIONAL) {
canShowFloatMenu.value = false;
} else {
canShowFloatMenu.value = true;
showConfigMenu.value = true;
showAssociateCaseMenu.value = false;
canShowExecuteMethodMenu.value = false;
canShowDeleteMenu.value = false;
insertSiblingMenus.value = [];
insertSonMenus.value = [];
}
return;
}
@ -694,6 +712,39 @@
}
}
function handleExtendChange(val: string | number | boolean) {
if (val && configForm.value) {
const node: PlanMinderNode = window.minder.getNodeById(configForm.value.id);
if (node.parent?.data) {
const {
priority,
executeMethod,
grouped,
environmentId,
testResourcePoolId,
retryOnFail,
retryType,
retryTimes,
retryInterval,
stopOnFail,
} = node.parent.data;
configForm.value = {
...configForm.value,
priority,
executeMethod,
grouped,
environmentId,
testResourcePoolId,
retryOnFail,
retryType,
retryTimes,
retryInterval,
stopOnFail,
};
}
}
}
/**
* 初始化测试规划脑图
*/
@ -762,6 +813,7 @@
loading.value = true;
await editPlanMinder(makeMinderParams(fullJson));
Message.success(t('common.saveSuccess'));
clearSelectedCases();
handleConfigCancel();
initMinder();
callback();

View File

@ -90,7 +90,7 @@ export default function useEventListener(listener: UseEventListenerProps) {
// 监听脑图自定义事件
watch(
() => minderStore.event.timestamp,
() => minderStore.event.eventId,
() => {
if (listener.handleMinderEvent) {
listener.handleMinderEvent(minderStore.event);

View File

@ -1,6 +1,6 @@
<template>
<div ref="mec" class="ms-minder-container">
<minderHeader :icon-buttons="props.iconButtons" @save="save" />
<minderHeader :icon-buttons="props.iconButtons" :disabled="props.disabled" @save="save" />
<Navigator />
<div
v-if="currentTreePath?.length > 0"
@ -125,7 +125,6 @@
]);
if (selectNodes.length > 0 && !notChangeCommands.has(event.commandName.toLocaleLowerCase())) {
minderStore.setMinderUnsaved(true);
minderStore.dispatchEvent(MinderEventName.MINDER_CHANGED);
selectNodes.forEach((node: MinderJsonNode) => {
markChangeNode(node);
});
@ -155,7 +154,7 @@
* 切换脑图展示的节点层级
* @param node 切换的节点
*/
function switchNode(node: MinderJsonNode | MinderJsonNodeData) {
function switchNode(node?: MinderJsonNode | MinderJsonNodeData) {
if (minderStore.minderUnsaved) {
//
replaceNodeInTree(
@ -166,12 +165,12 @@
'id'
);
}
if (node.id === 'NONE') {
if (node?.id === 'NONE') {
innerImportJson.value = importJson.value;
} else if (node.data) {
} else if (node?.data) {
innerImportJson.value = findNodePathByKey([importJson.value.root], node.data.id, 'data', 'id') as MinderJson;
} else {
innerImportJson.value = findNodePathByKey([importJson.value.root], node.id, 'data', 'id') as MinderJson;
innerImportJson.value = findNodePathByKey([importJson.value.root], node?.id, 'data', 'id') as MinderJson;
}
window.minder.importJson(innerImportJson.value);
const root: MinderJsonNode = window.minder.getRoot();
@ -203,7 +202,7 @@
}
watch(
() => minderStore.event.timestamp,
() => minderStore.event.eventId,
() => {
if (minderStore.event.name === MinderEventName.HOTBOX && minderStore.event.nodePosition) {
const nodeDomWidth = minderStore.event.nodeDom?.getBoundingClientRect().width || 0;

View File

@ -11,7 +11,7 @@
<span></span>
<template #content>
<a-radio-group
v-if="currentNodeTags.length > 0 && tags.length > 0"
v-if="tags.length > 0"
v-model:model-value="currentNodeTags[0]"
type="button"
size="mini"
@ -212,7 +212,7 @@
const menuPopupOffset = ref<TriggerPopupTranslate>([0, 0]);
watch(
() => minderStore.event.timestamp,
() => minderStore.event.eventId,
async () => {
if (window.minder) {
let nodePosition: MinderNodePosition | undefined;

View File

@ -16,7 +16,7 @@ export interface MinderJsonNodeData {
text: string;
resource?: string[];
expandState?: 'collapse' | 'expand';
priority?: number | string;
priority?: number;
// 前端渲染字段
isNew?: boolean; // 是否脑图新增节点,需要在初始化脑图数据时标记已存在节点为 false 以区分是否新增节点
changed?: boolean; // 脑图节点是否发生过变化

View File

@ -13,7 +13,7 @@ const useMinderStore = defineStore('minder', {
state: (): MinderState => ({
event: {
name: '' as MinderEventName,
timestamp: 0,
eventId: '',
params: '',
nodePosition: {
x: 0,
@ -49,7 +49,7 @@ const useMinderStore = defineStore('minder', {
this.event = {
name,
params,
timestamp: Date.now(),
eventId: getGenerateId(),
nodePosition: position,
nodeDom,
nodes,

View File

@ -17,7 +17,7 @@ export interface MinderNodePosition {
export interface MinderCustomEvent {
name: MinderEventName;
timestamp: number;
eventId: string;
params?: any;
nodePosition?: MinderNodePosition;
nodeDom?: HTMLElement;

View File

@ -580,6 +580,7 @@ export function deleteNodes<T>(
treeArr: TreeNode<T>[],
targetKeys: (string | number)[],
deleteCondition?: (node: TreeNode<T>, parent?: TreeNode<T>) => boolean,
deleteCallBack?: (node: TreeNode<T>) => void,
customKey = 'key'
): boolean {
let hasDeleted = false;
@ -589,6 +590,9 @@ export function deleteNodes<T>(
const node = tree[i];
if (targetKeysSet.has(node[customKey])) {
if (deleteCondition && deleteCondition(node, node.parent)) {
if (deleteCallBack) {
deleteCallBack(node);
}
tree.splice(i, 1); // 直接删除当前节点
hasDeleted = true;
targetKeysSet.delete(node[customKey]); // 删除后从集合中移除

View File

@ -927,7 +927,8 @@
return item;
});
if (
(!props.disabledExceptParam || !props.disabledParamValue) &&
!props.disabledExceptParam &&
!props.disabledParamValue &&
hasNoIdItem &&
!filterKeyValParams(arr, defaultLineData.value, !props.selectable).lastDataIsDefault &&
!props.isTreeTable
@ -935,7 +936,7 @@
addTableLine(arr.length - 1, false, true);
}
} else {
if (props.disabledExceptParam) return;
if (props.disabledExceptParam || props.disabledParamValue) return;
const id = getGenerateId();
paramsData.value = [
{

View File

@ -282,6 +282,9 @@
scenario.value.steps,
checkedKeys.value,
(node) => !node.isQuoteScenarioStep,
(node) => {
delete scenario.value.stepDetails[node.id];
},
'uniqueId'
);
if (deleteResult) {

View File

@ -990,6 +990,7 @@
maskClosable: false,
onBeforeOk: async () => {
deleteNode(steps.value, node.uniqueId, 'uniqueId');
delete stepDetails.value[node.id];
scenario.value.unSaved = true;
},
hideCancel: false,

View File

@ -219,7 +219,7 @@
:module-id="props.activeFolder"
:modules-count="props.modulesCount"
:module-name="props.moduleName"
@save="emitTableParams"
@save="handleMinderSave"
/>
<MsDrawer v-model:visible="visible" :width="480" :mask="false">
{{ nodeData.text }}
@ -390,7 +390,7 @@
}>();
const emit = defineEmits<{
(e: 'init', params: CaseModuleQueryParams): void;
(e: 'init', params: CaseModuleQueryParams, refreshModule?: boolean): void;
(e: 'import', type: 'Excel' | 'Xmind'): void;
}>();
@ -894,14 +894,22 @@
};
}
//
async function emitTableParams() {
async function emitTableParams(refreshModule = false) {
const tableParams = await initTableParams();
emit('init', {
...tableParams,
current: propsRes.value.msPagination?.current,
pageSize: propsRes.value.msPagination?.pageSize,
filter: propsRes.value.filter,
});
emit(
'init',
{
...tableParams,
current: propsRes.value.msPagination?.current,
pageSize: propsRes.value.msPagination?.pageSize,
filter: propsRes.value.filter,
},
refreshModule
);
}
function handleMinderSave() {
emitTableParams(true);
}
const tableSelected = ref<(string | number)[]>([]);

View File

@ -285,7 +285,10 @@
/**
* 右侧表格数据刷新后若当前展示的是模块则刷新模块树的统计数量
*/
function initModulesCount(params: TableQueryParams) {
function initModulesCount(params: TableQueryParams, refreshModule = false) {
if (refreshModule) {
caseTreeRef.value.initModules();
}
featureCaseStore.getCaseModulesCount(params);
featureCaseStore.getRecycleModulesCount(params);
tableFilterParams.value = { ...params };

View File

@ -102,7 +102,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" @refresh="initDetail" />
<Plan v-if="activeTab === 'plan'" :plan-id="planId" :status="detail.status || 'PREPARED'" @refresh="initDetail" />
<FeatureCase
v-if="activeTab === 'featureCase'"
ref="featureCaseRef"

View File

@ -2,7 +2,7 @@
<div class="flex h-full flex-col p-[16px]">
<MsNotRemind tip="testPlan.planTip" class="mb-[16px]" type="info" visited-key="testPlanTip" />
<div class="flex-1 overflow-hidden">
<MsTestPlanMinder :plan-id="props.planId" @save="emit('refresh')" />
<MsTestPlanMinder :plan-id="props.planId" :status="props.status" @save="emit('refresh')" />
</div>
</div>
</template>
@ -13,6 +13,7 @@
const props = defineProps<{
planId: string;
status: string;
}>();
const emit = defineEmits<{
(e: 'refresh'): void;