feat(脑图): 支持节点自定义下拉菜单&测试规划脑图快捷配置
This commit is contained in:
parent
cb72ff6b96
commit
3445189a78
|
@ -206,7 +206,7 @@
|
||||||
</CaseTable>
|
</CaseTable>
|
||||||
<!-- 接口用例 API -->
|
<!-- 接口用例 API -->
|
||||||
<ApiTable
|
<ApiTable
|
||||||
v-if="associationType === CaseLinkEnum.API && showType === 'API'"
|
v-else-if="associationType === CaseLinkEnum.API && showType === 'API'"
|
||||||
ref="apiTableRef"
|
ref="apiTableRef"
|
||||||
v-model:selectedIds="selectedIds"
|
v-model:selectedIds="selectedIds"
|
||||||
v-model:selectedModulesMaps="selectedModulesMaps"
|
v-model:selectedModulesMaps="selectedModulesMaps"
|
||||||
|
@ -229,7 +229,7 @@
|
||||||
</ApiTable>
|
</ApiTable>
|
||||||
<!-- 接口用例 CASE -->
|
<!-- 接口用例 CASE -->
|
||||||
<ApiCaseTable
|
<ApiCaseTable
|
||||||
v-if="associationType === CaseLinkEnum.API && showType === 'CASE'"
|
v-else-if="associationType === CaseLinkEnum.API && showType === 'CASE'"
|
||||||
ref="caseTableRef"
|
ref="caseTableRef"
|
||||||
v-model:selectedIds="selectedIds"
|
v-model:selectedIds="selectedIds"
|
||||||
v-model:selectedModulesMaps="selectedModulesMaps"
|
v-model:selectedModulesMaps="selectedModulesMaps"
|
||||||
|
@ -252,7 +252,7 @@
|
||||||
</ApiCaseTable>
|
</ApiCaseTable>
|
||||||
<!-- 接口场景用例 -->
|
<!-- 接口场景用例 -->
|
||||||
<ScenarioCaseTable
|
<ScenarioCaseTable
|
||||||
v-if="associationType === CaseLinkEnum.SCENARIO"
|
v-else-if="associationType === CaseLinkEnum.SCENARIO"
|
||||||
ref="scenarioTableRef"
|
ref="scenarioTableRef"
|
||||||
v-model:selectedModulesMaps="selectedModulesMaps"
|
v-model:selectedModulesMaps="selectedModulesMaps"
|
||||||
v-model:selectedIds="selectedIds"
|
v-model:selectedIds="selectedIds"
|
||||||
|
@ -637,7 +637,7 @@
|
||||||
selectedProtocols.value = _protocols || [];
|
selectedProtocols.value = _protocols || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeSyncCase(value: string | number | boolean, ev: Event) {
|
function changeSyncCase(value: string | number | boolean) {
|
||||||
if (value) {
|
if (value) {
|
||||||
if (props.nodeApiTestSet && props.nodeScenarioTestSet) {
|
if (props.nodeApiTestSet && props.nodeScenarioTestSet) {
|
||||||
apiCaseCollectionId.value = props.nodeApiTestSet?.[0]?.id ?? '';
|
apiCaseCollectionId.value = props.nodeApiTestSet?.[0]?.id ?? '';
|
||||||
|
|
|
@ -16,6 +16,9 @@
|
||||||
:can-show-delete-menu="canShowDeleteMenu"
|
:can-show-delete-menu="canShowDeleteMenu"
|
||||||
:disabled="!hasEditPermission"
|
:disabled="!hasEditPermission"
|
||||||
:can-show-batch-delete="canShowBatchDelete"
|
:can-show-batch-delete="canShowBatchDelete"
|
||||||
|
:can-show-dropdown="canShowDropdown"
|
||||||
|
:dropdown-list="dropdownList"
|
||||||
|
:checked-val="checkedVal"
|
||||||
custom-priority
|
custom-priority
|
||||||
single-tag
|
single-tag
|
||||||
tag-enable
|
tag-enable
|
||||||
|
@ -239,8 +242,10 @@
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
|
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||||
import MsMinderEditor from '@/components/pure/ms-minder-editor/minderEditor.vue';
|
import MsMinderEditor from '@/components/pure/ms-minder-editor/minderEditor.vue';
|
||||||
import {
|
import {
|
||||||
|
MinderDropdownListItem,
|
||||||
MinderEvent,
|
MinderEvent,
|
||||||
MinderJson,
|
MinderJson,
|
||||||
MinderJsonNode,
|
MinderJsonNode,
|
||||||
|
@ -306,7 +311,6 @@
|
||||||
*/
|
*/
|
||||||
function checkNodeCanShowMenu(node: PlanMinderNode) {
|
function checkNodeCanShowMenu(node: PlanMinderNode) {
|
||||||
const { data } = node;
|
const { data } = node;
|
||||||
|
|
||||||
if (!hasEditPermission.value && (data?.level === 1 || data?.level === 2)) {
|
if (!hasEditPermission.value && (data?.level === 1 || data?.level === 2)) {
|
||||||
// 没有编辑权限,只能查看配置菜单(功能用例只有关联用例,所以配置菜单也不能看)
|
// 没有编辑权限,只能查看配置菜单(功能用例只有关联用例,所以配置菜单也不能看)
|
||||||
if (data?.type === PlanMinderCollectionType.FUNCTIONAL) {
|
if (data?.type === PlanMinderCollectionType.FUNCTIONAL) {
|
||||||
|
@ -608,19 +612,38 @@
|
||||||
selectedAssociateCasesParams.value = { ...param };
|
selectedAssociateCasesParams.value = { ...param };
|
||||||
const node: PlanMinderNode = window.minder.getSelectedNode();
|
const node: PlanMinderNode = window.minder.getSelectedNode();
|
||||||
let associateType: string = '';
|
let associateType: string = '';
|
||||||
if (node && node.data?.type === PlanMinderCollectionType.SCENARIO) {
|
let nodeDataType = node?.data?.type;
|
||||||
|
if (!extraVisible.value) {
|
||||||
|
// 配置抽屉关闭时,选中的节点是测试点下的用例数节点,需要找到测试点节点
|
||||||
|
nodeDataType = node?.parent?.data?.type;
|
||||||
|
}
|
||||||
|
if (nodeDataType === PlanMinderCollectionType.SCENARIO) {
|
||||||
associateType = PlanMinderAssociateType.SCENARIO_CASE;
|
associateType = PlanMinderAssociateType.SCENARIO_CASE;
|
||||||
} else {
|
} else {
|
||||||
associateType = param?.associateType ?? node.data.type;
|
associateType = param?.associateType ?? nodeDataType;
|
||||||
|
}
|
||||||
|
if (extraVisible.value) {
|
||||||
|
// 配置抽屉打开,则选中的节点是测试点节点
|
||||||
|
node.data.associateDTOS = [
|
||||||
|
{
|
||||||
|
...cloneDeep(param),
|
||||||
|
associateType,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else if (node.parent?.data) {
|
||||||
|
// 配置抽屉关闭,则选中的节点是测试点下的用例数节点,需要找到测试点节点赋值
|
||||||
|
node.parent.data.associateDTOS = [
|
||||||
|
{
|
||||||
|
...cloneDeep(param),
|
||||||
|
associateType,
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
node.data.associateDTOS = [
|
if (!extraVisible.value) {
|
||||||
{
|
// 派发SAVE_MINDER事件触发脑图的保存处理
|
||||||
...cloneDeep(param),
|
minderStore.dispatchEvent(MinderEventName.SAVE_MINDER);
|
||||||
associateType,
|
}
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
caseAssociateVisible.value = false;
|
caseAssociateVisible.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,16 +668,14 @@
|
||||||
*/
|
*/
|
||||||
function associateCase() {
|
function associateCase() {
|
||||||
const node: PlanMinderNode = window.minder.getSelectedNode();
|
const node: PlanMinderNode = window.minder.getSelectedNode();
|
||||||
activePlanSet.value = node;
|
if (extraVisible.value) {
|
||||||
switchingConfigFormData.value = true;
|
activePlanSet.value = node;
|
||||||
configForm.value = cloneDeep(activePlanSet.value?.data);
|
} else if (node.parent) {
|
||||||
extraVisible.value = true;
|
activePlanSet.value = node.parent as PlanMinderNode;
|
||||||
|
}
|
||||||
currentSelectCase.value = (activePlanSet.value?.data.type as unknown as CaseLinkEnum) || CaseLinkEnum.FUNCTIONAL;
|
currentSelectCase.value = (activePlanSet.value?.data.type as unknown as CaseLinkEnum) || CaseLinkEnum.FUNCTIONAL;
|
||||||
setCaseSelectedSet();
|
setCaseSelectedSet();
|
||||||
caseAssociateVisible.value = true;
|
caseAssociateVisible.value = true;
|
||||||
nextTick(() => {
|
|
||||||
switchingConfigFormData.value = false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function openCaseAssociateDrawer() {
|
function openCaseAssociateDrawer() {
|
||||||
|
@ -681,6 +702,19 @@
|
||||||
() => {
|
() => {
|
||||||
if ([MinderEventName.EXPAND, MinderEventName.COLLAPSE].includes(minderStore.event.name)) {
|
if ([MinderEventName.EXPAND, MinderEventName.COLLAPSE].includes(minderStore.event.name)) {
|
||||||
setCustomPriorityView(priorityTextMap);
|
setCustomPriorityView(priorityTextMap);
|
||||||
|
} else if (minderStore.event.name === MinderEventName.DROPDOWN_SELECT) {
|
||||||
|
const node: PlanMinderNode = window.minder.getSelectedNode();
|
||||||
|
if (node?.data?.level === 3 && node?.data?.resource?.[0] === resourcePoolTag) {
|
||||||
|
if (node.parent?.data) {
|
||||||
|
node.parent.data.testResourcePoolId = minderStore.event.params;
|
||||||
|
minderStore.dispatchEvent(MinderEventName.SAVE_MINDER);
|
||||||
|
}
|
||||||
|
} else if (node?.data?.level === 3 && node?.data?.resource?.[0] === envTag) {
|
||||||
|
if (node.parent?.data) {
|
||||||
|
node.parent.data.environmentId = minderStore.event.params;
|
||||||
|
minderStore.dispatchEvent(MinderEventName.SAVE_MINDER);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -716,6 +750,13 @@
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否可以显示下拉菜单
|
||||||
|
*/
|
||||||
|
const canShowDropdown = ref(false);
|
||||||
|
const dropdownList = ref<MinderDropdownListItem[]>([]);
|
||||||
|
const checkedVal = ref<string>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理节点选中
|
* 处理节点选中
|
||||||
* @param node 节点
|
* @param node 节点
|
||||||
|
@ -730,8 +771,7 @@
|
||||||
selectNodeExecuteMethod.value = undefined;
|
selectNodeExecuteMethod.value = undefined;
|
||||||
}
|
}
|
||||||
if (node.data?.level === 3 && node.data?.resource?.[0] === caseCountTag) {
|
if (node.data?.level === 3 && node.data?.resource?.[0] === caseCountTag) {
|
||||||
window.minder.toggleSelect(node);
|
canShowFloatMenu.value = false;
|
||||||
window.minder.selectById(node.parent?.data?.id);
|
|
||||||
if (!inInsertingNode.value && hasEditPermission && hasAnyPermission(['PROJECT_TEST_PLAN:READ+ASSOCIATION'])) {
|
if (!inInsertingNode.value && hasEditPermission && hasAnyPermission(['PROJECT_TEST_PLAN:READ+ASSOCIATION'])) {
|
||||||
// 新增测试点时不自动弹出关联用例
|
// 新增测试点时不自动弹出关联用例
|
||||||
associateCase();
|
associateCase();
|
||||||
|
@ -740,8 +780,21 @@
|
||||||
node.data?.level === 3 &&
|
node.data?.level === 3 &&
|
||||||
(node.data?.resource?.[0] === resourcePoolTag || node.data?.resource?.[0] === envTag)
|
(node.data?.resource?.[0] === resourcePoolTag || node.data?.resource?.[0] === envTag)
|
||||||
) {
|
) {
|
||||||
window.minder.toggleSelect(node);
|
canShowFloatMenu.value = false;
|
||||||
window.minder.selectById(node.parent?.data?.id);
|
canShowDropdown.value = !node.parent?.data?.extended; // 继承上级配置的测试点节点不显示下拉菜单
|
||||||
|
if (node.data?.resource?.[0] === resourcePoolTag) {
|
||||||
|
dropdownList.value = resourcePoolOptions.value.map((item) => ({
|
||||||
|
label: item.label || '',
|
||||||
|
value: item.value as string,
|
||||||
|
}));
|
||||||
|
checkedVal.value = node.parent?.data?.testResourcePoolId;
|
||||||
|
} else {
|
||||||
|
dropdownList.value = environmentOptions.value.map((item) => ({
|
||||||
|
label: item.label || '',
|
||||||
|
value: item.value as string,
|
||||||
|
}));
|
||||||
|
checkedVal.value = node.parent?.data?.environmentId;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
checkNodeCanShowMenu(node);
|
checkNodeCanShowMenu(node);
|
||||||
if (extraVisible.value) {
|
if (extraVisible.value) {
|
||||||
|
@ -968,7 +1021,7 @@
|
||||||
}
|
}
|
||||||
if (!configFormValidResult) return;
|
if (!configFormValidResult) return;
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
await editPlanMinder(makeMinderParams(extraVisible.value ? window.minder.exportJson() : fullJson));
|
await editPlanMinder(makeMinderParams(window.minder.exportJson()));
|
||||||
Message.success(t('common.saveSuccess'));
|
Message.success(t('common.saveSuccess'));
|
||||||
emit('save');
|
emit('save');
|
||||||
clearSelectedCases();
|
clearSelectedCases();
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
import useMinderStore from '@/store/modules/components/minder-editor';
|
||||||
|
import type { MinderCustomEvent, MinderNodePosition } from '@/store/modules/components/minder-editor/types';
|
||||||
|
import { sleep } from '@/utils';
|
||||||
|
|
||||||
|
import { MinderEventName } from '@/enums/minderEnum';
|
||||||
|
|
||||||
|
import type { MinderJsonNode } from '../props';
|
||||||
|
import { isNodeInMinderView } from '../script/tool/utils';
|
||||||
|
|
||||||
|
export default function useMinderTrigger(
|
||||||
|
handleSelect?: (event: MinderCustomEvent, selectedNodes: MinderJsonNode[]) => void
|
||||||
|
) {
|
||||||
|
const minderStore = useMinderStore();
|
||||||
|
|
||||||
|
const triggerVisible = ref(false);
|
||||||
|
const triggerOffset = ref([0, 0]);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => minderStore.event.eventId,
|
||||||
|
async () => {
|
||||||
|
if (window.minder) {
|
||||||
|
let nodePosition: MinderNodePosition | undefined;
|
||||||
|
const selectedNodes: MinderJsonNode[] = window.minder.getSelectedNodes();
|
||||||
|
if (minderStore.event.name === MinderEventName.NODE_SELECT) {
|
||||||
|
nodePosition = minderStore.event.nodePosition;
|
||||||
|
if (handleSelect) {
|
||||||
|
handleSelect(minderStore.event, selectedNodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (selectedNodes.length > 1) {
|
||||||
|
// 多选时隐藏悬浮菜单
|
||||||
|
triggerVisible.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ([MinderEventName.VIEW_CHANGE, MinderEventName.DRAG_FINISH].includes(minderStore.event.name)) {
|
||||||
|
// 脑图画布移动时,重新计算节点位置
|
||||||
|
await sleep(300); // 拖拽完毕后会有 300ms 的动画,等待动画结束后再计算
|
||||||
|
nodePosition = window.minder.getSelectedNode()?.getRenderBox();
|
||||||
|
}
|
||||||
|
const state = window.editor.fsm.state();
|
||||||
|
if (
|
||||||
|
nodePosition &&
|
||||||
|
isNodeInMinderView(undefined, nodePosition, Math.min(nodePosition.width / 2, 200)) &&
|
||||||
|
state !== 'input'
|
||||||
|
) {
|
||||||
|
// 判断节点在脑图可视区域内且遮挡的节点不超过节点宽度的一半(超过 200px 则按 200px 算)且当前不是编辑名称状态,则显示菜单
|
||||||
|
const nodeDomHeight = nodePosition.height || 0;
|
||||||
|
triggerOffset.value = [nodePosition.x, nodePosition.y + nodeDomHeight + 4]; // trigger显示在节点下方4px处
|
||||||
|
triggerVisible.value = true;
|
||||||
|
} else {
|
||||||
|
triggerVisible.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
triggerVisible,
|
||||||
|
triggerOffset,
|
||||||
|
};
|
||||||
|
}
|
|
@ -29,6 +29,7 @@
|
||||||
<slot name="extractMenu"></slot>
|
<slot name="extractMenu"></slot>
|
||||||
</template>
|
</template>
|
||||||
</nodeFloatMenu>
|
</nodeFloatMenu>
|
||||||
|
<nodeDropdown v-if="props.canShowDropdown" :dropdown-list="props.dropdownList" :checked-val="props.checkedVal" />
|
||||||
<batchMenu v-bind="props" />
|
<batchMenu v-bind="props" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -37,6 +38,7 @@
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
import batchMenu from '../menu/batchMenu.vue';
|
import batchMenu from '../menu/batchMenu.vue';
|
||||||
|
import nodeDropdown from '../menu/nodeDropdown.vue';
|
||||||
import nodeFloatMenu from '../menu/nodeFloatMenu.vue';
|
import nodeFloatMenu from '../menu/nodeFloatMenu.vue';
|
||||||
import minderHeader from './header.vue';
|
import minderHeader from './header.vue';
|
||||||
import Navigator from './navigator.vue';
|
import Navigator from './navigator.vue';
|
||||||
|
@ -50,6 +52,7 @@
|
||||||
import useEventListener from '../hooks/useMinderEventListener';
|
import useEventListener from '../hooks/useMinderEventListener';
|
||||||
import {
|
import {
|
||||||
batchMenuProps,
|
batchMenuProps,
|
||||||
|
dropdownMenuProps,
|
||||||
editMenuProps,
|
editMenuProps,
|
||||||
floatMenuProps,
|
floatMenuProps,
|
||||||
headerProps,
|
headerProps,
|
||||||
|
@ -72,6 +75,7 @@
|
||||||
...tagProps,
|
...tagProps,
|
||||||
...priorityProps,
|
...priorityProps,
|
||||||
...batchMenuProps,
|
...batchMenuProps,
|
||||||
|
...dropdownMenuProps,
|
||||||
});
|
});
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'save', data: MinderJson, callback: () => void): void;
|
(e: 'save', data: MinderJson, callback: () => void): void;
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
<template>
|
||||||
|
<a-dropdown
|
||||||
|
v-model:popup-visible="triggerVisible"
|
||||||
|
class="ms-minder-node-dropdown"
|
||||||
|
:popup-translate="triggerOffset"
|
||||||
|
position="bl"
|
||||||
|
trigger="click"
|
||||||
|
@select="(val) => handleSelect(val)"
|
||||||
|
>
|
||||||
|
<span></span>
|
||||||
|
<template #content>
|
||||||
|
<a-doption
|
||||||
|
v-for="item in props.dropdownList"
|
||||||
|
:key="item.value"
|
||||||
|
v-permission="item.permission || []"
|
||||||
|
:value="item.value"
|
||||||
|
:class="props.checkedVal === item.value ? 'ms-minder-node-dropdown-item--active' : ''"
|
||||||
|
@click="item.onClick && item.onClick()"
|
||||||
|
>
|
||||||
|
<div>{{ item.label }}</div>
|
||||||
|
</a-doption>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import useMinderStore from '@/store/modules/components/minder-editor';
|
||||||
|
|
||||||
|
import { MinderEventName } from '@/enums/minderEnum';
|
||||||
|
|
||||||
|
import useMinderTrigger from '../hooks/useMinderTrigger';
|
||||||
|
import { dropdownMenuProps } from '../props';
|
||||||
|
|
||||||
|
const props = defineProps(dropdownMenuProps);
|
||||||
|
|
||||||
|
const minderStore = useMinderStore();
|
||||||
|
const { triggerVisible, triggerOffset } = useMinderTrigger();
|
||||||
|
|
||||||
|
function handleSelect(val?: string | number | Record<string, any>) {
|
||||||
|
if (props.checkedVal !== val) {
|
||||||
|
minderStore.dispatchEvent(MinderEventName.DROPDOWN_SELECT, val as string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.ms-minder-node-dropdown {
|
||||||
|
max-height: 350px;
|
||||||
|
.ms-minder-node-dropdown-item--active {
|
||||||
|
color: rgb(var(--primary-5)) !important;
|
||||||
|
background-color: rgb(var(--primary-1)) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -195,15 +195,13 @@
|
||||||
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import useMinderStore from '@/store/modules/components/minder-editor/index';
|
import useMinderStore from '@/store/modules/components/minder-editor/index';
|
||||||
import { MinderNodePosition } from '@/store/modules/components/minder-editor/types';
|
|
||||||
import { sleep } from '@/utils';
|
|
||||||
|
|
||||||
import { MinderEventName } from '@/enums/minderEnum';
|
import { MinderEventName } from '@/enums/minderEnum';
|
||||||
|
|
||||||
import useMinderOperation from '../hooks/useMinderOperation';
|
import useMinderOperation from '../hooks/useMinderOperation';
|
||||||
import usePriority from '../hooks/useMinderPriority';
|
import usePriority from '../hooks/useMinderPriority';
|
||||||
|
import useMinderTrigger from '../hooks/useMinderTrigger';
|
||||||
import { floatMenuProps, mainEditorProps, MinderJsonNode, priorityColorMap, priorityProps, tagProps } from '../props';
|
import { floatMenuProps, mainEditorProps, MinderJsonNode, priorityColorMap, priorityProps, tagProps } from '../props';
|
||||||
import { isNodeInMinderView } from '../script/tool/utils';
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
...mainEditorProps,
|
...mainEditorProps,
|
||||||
|
@ -227,45 +225,29 @@
|
||||||
});
|
});
|
||||||
const menuPopupOffset = ref<TriggerPopupTranslate>([0, 0]);
|
const menuPopupOffset = ref<TriggerPopupTranslate>([0, 0]);
|
||||||
|
|
||||||
|
const { triggerOffset, triggerVisible } = useMinderTrigger((event, selectedNodes) => {
|
||||||
|
currentNodeTags.value = event.nodes?.[0].data?.resource || [];
|
||||||
|
if (props.replaceableTags && !props.disabled) {
|
||||||
|
tags.value = props.replaceableTags(selectedNodes);
|
||||||
|
} else {
|
||||||
|
tags.value = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => minderStore.event.eventId,
|
() => triggerOffset.value,
|
||||||
async () => {
|
(val) => {
|
||||||
if (window.minder) {
|
menuPopupOffset.value = [val[0], val[1]];
|
||||||
let nodePosition: MinderNodePosition | undefined;
|
},
|
||||||
const selectedNodes: MinderJsonNode[] = window.minder.getSelectedNodes();
|
{
|
||||||
if (minderStore.event.name === MinderEventName.NODE_SELECT) {
|
immediate: true,
|
||||||
nodePosition = minderStore.event.nodePosition;
|
}
|
||||||
currentNodeTags.value = minderStore.event.nodes?.[0].data?.resource || [];
|
);
|
||||||
if (props.replaceableTags && !props.disabled) {
|
|
||||||
tags.value = props.replaceableTags(selectedNodes);
|
watch(
|
||||||
} else {
|
() => triggerVisible.value,
|
||||||
tags.value = [];
|
(val) => {
|
||||||
}
|
menuVisible.value = val;
|
||||||
}
|
|
||||||
if (selectedNodes.length > 1) {
|
|
||||||
// 多选时隐藏悬浮菜单
|
|
||||||
menuVisible.value = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ([MinderEventName.VIEW_CHANGE, MinderEventName.DRAG_FINISH].includes(minderStore.event.name)) {
|
|
||||||
// 脑图画布移动时,重新计算节点位置
|
|
||||||
await sleep(300); // 拖拽完毕后会有 300ms 的动画,等待动画结束后再计算
|
|
||||||
nodePosition = window.minder.getSelectedNode()?.getRenderBox();
|
|
||||||
}
|
|
||||||
const state = window.editor.fsm.state();
|
|
||||||
if (
|
|
||||||
nodePosition &&
|
|
||||||
isNodeInMinderView(undefined, nodePosition, Math.min(nodePosition.width / 2, 200)) &&
|
|
||||||
state !== 'input'
|
|
||||||
) {
|
|
||||||
// 判断节点在脑图可视区域内且遮挡的节点不超过节点宽度的一半(超过 200px 则按 200px 算)且当前不是编辑名称状态,则显示菜单
|
|
||||||
const nodeDomHeight = nodePosition.height || 0;
|
|
||||||
menuPopupOffset.value = [nodePosition.x, nodePosition.y + nodeDomHeight + 4]; // 菜单显示在节点下方4px处
|
|
||||||
menuVisible.value = true;
|
|
||||||
} else {
|
|
||||||
menuVisible.value = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
import {
|
import {
|
||||||
batchMenuProps,
|
batchMenuProps,
|
||||||
delProps,
|
delProps,
|
||||||
|
dropdownMenuProps,
|
||||||
editMenuProps,
|
editMenuProps,
|
||||||
floatMenuProps,
|
floatMenuProps,
|
||||||
headerProps,
|
headerProps,
|
||||||
|
@ -77,6 +78,7 @@
|
||||||
...delProps,
|
...delProps,
|
||||||
...viewMenuProps,
|
...viewMenuProps,
|
||||||
...batchMenuProps,
|
...batchMenuProps,
|
||||||
|
...dropdownMenuProps,
|
||||||
});
|
});
|
||||||
|
|
||||||
const minderStore = useMinderStore();
|
const minderStore = useMinderStore();
|
||||||
|
|
|
@ -216,6 +216,31 @@ export const floatMenuProps = {
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface MinderDropdownListItem {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
permission?: string[];
|
||||||
|
onClick?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const dropdownMenuProps = {
|
||||||
|
// 是否显示Dropdown
|
||||||
|
canShowDropdown: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
dropdownList: {
|
||||||
|
type: Array as PropType<MinderDropdownListItem[]>,
|
||||||
|
default() {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
checkedVal: {
|
||||||
|
type: String as PropType<string>,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
export const batchMenuProps = {
|
export const batchMenuProps = {
|
||||||
canShowMoreBatchMenu: {
|
canShowMoreBatchMenu: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|
|
@ -16,6 +16,7 @@ export enum MinderEventName {
|
||||||
'MINDER_CHANGED' = 'MINDER_CHANGED', // 脑图更改事件
|
'MINDER_CHANGED' = 'MINDER_CHANGED', // 脑图更改事件
|
||||||
'SAVE_MINDER' = 'SAVE_MINDER', // 脑图保存事件
|
'SAVE_MINDER' = 'SAVE_MINDER', // 脑图保存事件
|
||||||
'DRAG_FINISH' = 'DRAG_FINISH', // 脑图节点拖拽结束事件
|
'DRAG_FINISH' = 'DRAG_FINISH', // 脑图节点拖拽结束事件
|
||||||
|
'DROPDOWN_SELECT' = 'DROPDOWN_SELECT', // 下拉菜单选中事件
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum MinderKeyEnum {
|
export enum MinderKeyEnum {
|
||||||
|
|
Loading…
Reference in New Issue