feat(脑图): 脑图快捷键屏蔽&脑图拖拽拦截&部分样式调整 (#31248)

Co-authored-by: baiqi <qi.bai@fit2cloud.com>
This commit is contained in:
MeterSphere Bot 2024-06-03 21:58:34 +08:00 committed by GitHub
parent fdafb1236a
commit 800fcdb2ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 100 additions and 53 deletions

View File

@ -469,7 +469,6 @@
/** radio **/
.arco-radio-group-button {
padding: 1px;
background-color: var(--color-text-n8);
.arco-radio-button {
@apply bg-transparent;

View File

@ -16,6 +16,7 @@
@content-change="handleContentChange"
@node-select="handleNodeSelect"
@action="handleAction"
@before-exec-command="handleBeforeExecCommand"
@save="handleMinderSave"
>
<template #extractTabContent>
@ -44,7 +45,12 @@
import { FormItem } from '@/components/pure/ms-form-create/types';
import MsMinderEditor from '@/components/pure/ms-minder-editor/minderEditor.vue';
import type { MinderJson, MinderJsonNode, MinderJsonNodeData } from '@/components/pure/ms-minder-editor/props';
import type {
MinderEvent,
MinderJson,
MinderJsonNode,
MinderJsonNodeData,
} from '@/components/pure/ms-minder-editor/props';
import { MsFileItem } from '@/components/pure/ms-upload/types';
import attachment from './attachment.vue';
import baseInfo from './basInfo.vue';
@ -60,7 +66,7 @@
} from '@/api/modules/case-management/featureCase';
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
import { MinderEvent } from '@/store/modules/components/minder-editor/types';
import { MinderCustomEvent } from '@/store/modules/components/minder-editor/types';
import { filterTree, getGenerateId, mapTree } from '@/utils';
import {
@ -805,7 +811,7 @@
* 处理脑图节点操作
* @param event 脑图事件对象
*/
function handleAction(event: MinderEvent) {
function handleAction(event: MinderCustomEvent) {
const { node, name } = event;
if (node) {
switch (name) {
@ -831,6 +837,13 @@
}
}
}
function handleBeforeExecCommand(event: MinderEvent) {
if (event.commandName === 'movetoparent') {
// TODO:
event.stopPropagation();
}
}
</script>
<style lang="less" scoped></style>

View File

@ -83,9 +83,8 @@
class="list-item-action flex flex-row items-center gap-[8px] opacity-0"
:class="{ '!opacity-100': element.id === currentId }"
>
<div class="icon-button">
<div v-if="element.type === systemType" class="icon-button">
<MsIcon
v-if="element.type === systemType"
v-permission="props.updatePermission"
type="icon-icon_add_outlined"
size="16"
@ -213,9 +212,8 @@
class="list-item-action flex flex-row items-center gap-[8px] opacity-0"
:class="{ '!opacity-100': element.id === currentId }"
>
<div class="icon-button">
<div v-if="element.type === systemType" class="icon-button">
<MsIcon
v-if="element.type === systemType"
v-permission="props.updatePermission"
type="icon-icon_add_outlined"
size="16"
@ -317,9 +315,8 @@
class="list-item-action flex flex-row items-center gap-[8px] opacity-0"
:class="{ '!opacity-100': element.id === currentId }"
>
<div class="icon-button">
<div v-if="element.type === systemType" class="icon-button">
<MsIcon
v-if="element.type === systemType"
v-permission="props.updatePermission"
type="icon-icon_add_outlined"
size="16"

View File

@ -1,14 +1,15 @@
import { debounce } from 'lodash-es';
import useMinderStore from '@/store/modules/components/minder-editor/index';
import type { MinderEvent } from '@/store/modules/components/minder-editor/types';
import type { MinderCustomEvent } from '@/store/modules/components/minder-editor/types';
import type { MinderJsonNode } from '../props';
import type { MinderEvent, MinderJsonNode } from '../props';
export interface UseEventListenerProps {
handleContentChange?: (node: MinderJsonNode) => void;
handleSelectionChange?: (node: MinderJsonNode) => void;
handleMinderEvent?: (event: MinderEvent) => void;
handleMinderEvent?: (event: MinderCustomEvent) => void;
handleBeforeExecCommand?: (event: MinderEvent) => void;
}
export default function useEventListener(listener: UseEventListenerProps) {
@ -34,6 +35,21 @@ export default function useEventListener(listener: UseEventListenerProps) {
}, 300)
);
// minder.on('dragStart', () => {
// const node: MinderJsonNode = minder.getSelectedNode();
// console.log('dragStart', node);
// });
// minder.on('dragFinish', () => {
// console.log('dragFinish', minder.history);
// });
minder.on('beforeExecCommand', (e: any) => {
if (listener.handleBeforeExecCommand) {
listener.handleBeforeExecCommand(e);
}
});
// 监听脑图自定义事件
watch(
() => minderStore.event.timestamp,

View File

@ -67,15 +67,16 @@
import minderHeader from './main/header.vue';
import mainEditor from './main/mainEditor.vue';
import { MinderEvent } from '@/store/modules/components/minder-editor/types';
import { MinderCustomEvent } from '@/store/modules/components/minder-editor/types';
import useEventListener from './hooks/useEventListener';
import useMinderEventListener from './hooks/useMinderEventListener';
import {
delProps,
editMenuProps,
headerProps,
insertProps,
mainEditorProps,
MinderEvent,
MinderJsonNode,
moleProps,
priorityProps,
@ -90,7 +91,8 @@
(e: 'enterNode', data: MinderJsonNode): void;
(e: 'nodeSelect', data: MinderJsonNode): void;
(e: 'contentChange', data: MinderJsonNode): void;
(e: 'action', event: MinderEvent): void;
(e: 'action', event: MinderCustomEvent): void;
(e: 'beforeExecCommand', event: MinderEvent): void;
}>();
const props = defineProps({
@ -132,7 +134,7 @@
}
onMounted(() => {
useEventListener({
useMinderEventListener({
handleSelectionChange: () => {
const selectedNode: MinderJsonNode = window.minder.getSelectedNode();
if (Object.keys(window.minder).length > 0 && selectedNode) {
@ -145,6 +147,9 @@
handleMinderEvent: (event) => {
emit('action', event);
},
handleBeforeExecCommand: (event) => {
emit('beforeExecCommand', event);
},
});
});
</script>

View File

@ -6,6 +6,19 @@ import type { MoveMode } from '@/models/common';
import type { PropType } from 'vue';
export interface MinderClass {
stopPropagation: () => void; // 阻止事件冒泡
stopPropagationImmediately: () => void; // 立即阻止事件冒泡
[key: string]: any; // TODO: 其他事件属性
}
// TODO:脑图事件类型补充
export interface MinderEvent extends MinderClass {
command: any;
commandArgs: Record<string, any>[];
commandName: string;
minder: any;
type: string;
}
export interface MinderIconButtonItem {
icon: string;
tooltip: string;

View File

@ -22,11 +22,13 @@ function createDragRuntime(this: DragRuntimeOptions) {
// when jumped to drag mode, enter
fsm.when('* -> drag', () => {
// now is drag mode
minder.fire('dragStart');
});
fsm.when('drag -> *', (exit: any, enter: any, reason: string) => {
if (reason === 'drag-finish') {
// now exit drag mode
minder.fire('dragFinish');
}
});
}

View File

@ -13,14 +13,6 @@ function handlerConditionMatch(condition: any, when: string, exit: string, enter
return true;
}
type Handler = () => void & {
condition: {
when: string;
exit: string;
enter: string;
};
};
class FSM {
private currentState: string;

View File

@ -180,6 +180,7 @@ export default function HistoryRuntime(this: { minder: any; hotbox: any; editTex
hasUndo,
hasRedo,
};
window.minderHistory = this.history;
reset();
minder.on('contentchange', changed);
minder.on('import', reset);

View File

@ -7,7 +7,7 @@ export interface MinderNodePosition {
y: number;
}
export interface MinderEvent {
export interface MinderCustomEvent {
name: MinderEventName;
timestamp: number;
nodePosition?: MinderNodePosition;
@ -16,6 +16,6 @@ export interface MinderEvent {
}
export interface MinderState {
event: MinderEvent;
event: MinderCustomEvent;
mold: number;
}

View File

@ -4,9 +4,9 @@
class="mb-[16px] flex items-center"
:class="{ 'justify-between': hasAddPermission, 'justify-end': !hasAddPermission }"
>
<a-button v-permission="['ORGANIZATION_PROJECT:READ+ADD']" type="primary" @click="showAddProject">{{
t('system.organization.createProject')
}}</a-button>
<a-button v-permission="['ORGANIZATION_PROJECT:READ+ADD']" type="primary" @click="showAddProject">
{{ t('system.organization.createProject') }}
</a-button>
<a-input-search
v-model="keyword"
:placeholder="t('system.organization.searchIndexPlaceholder')"
@ -22,15 +22,16 @@
<a-tooltip class="tooltip-white">
<template #content>
<div class="flex flex-row">
<span class="text-[var(--color-text-1)]">{{
t('system.project.revokeDeleteToolTip', { count: record.remainDayCount })
}}</span>
<span class="text-[var(--color-text-1)]">
{{ t('system.project.revokeDeleteToolTip', { count: record.remainDayCount }) }}
</span>
<MsButton
v-if="hasAnyPermission(['ORGANIZATION_PROJECT:READ+RECOVER'])"
class="ml-[8px]"
@click="handleRevokeDelete(record)"
>{{ t('common.revokeDelete') }}</MsButton
>
{{ t('common.revokeDelete') }}
</MsButton>
</div>
</template>
<MsIcon v-if="record.deleted" type="icon-icon_alarm_clock" class="ml-[4px] text-[rgb(var(--danger-6))]" />
@ -44,34 +45,35 @@
v-if="hasAnyPermission(['ORGANIZATION_PROJECT:READ+ADD_MEMBER', 'ORGANIZATION_PROJECT:READ'])"
class="cursor-pointer text-[rgb(var(--primary-5))]"
@click="showUserDrawer(record)"
>{{ record.memberCount }}</span
>
{{ record.memberCount }}
</span>
<span v-else>{{ record.memberCount }}</span>
</template>
<template #operation="{ record }">
<template v-if="record.deleted">
<MsButton v-permission="['ORGANIZATION_PROJECT:READ+RECOVER']" @click="handleRevokeDelete(record)">{{
t('common.revokeDelete')
}}</MsButton>
<MsButton v-permission="['ORGANIZATION_PROJECT:READ+RECOVER']" @click="handleRevokeDelete(record)">
{{ t('common.revokeDelete') }}
</MsButton>
</template>
<template v-else-if="!record.enable">
<MsButton v-permission="['ORGANIZATION_PROJECT:READ+UPDATE']" @click="handleEnableOrDisableProject(record)">{{
t('common.enable')
}}</MsButton>
<MsButton v-permission="['ORGANIZATION_PROJECT:READ+DELETE']" @click="handleDelete(record)">{{
t('common.delete')
}}</MsButton>
<MsButton v-permission="['ORGANIZATION_PROJECT:READ+UPDATE']" @click="handleEnableOrDisableProject(record)">
{{ t('common.enable') }}
</MsButton>
<MsButton v-permission="['ORGANIZATION_PROJECT:READ+DELETE']" @click="handleDelete(record)">
{{ t('common.delete') }}
</MsButton>
</template>
<template v-else>
<MsButton v-permission="['ORGANIZATION_PROJECT:READ+UPDATE']" @click="showAddProjectModal(record)">{{
t('common.edit')
}}</MsButton>
<MsButton v-permission="['ORGANIZATION_PROJECT:READ+ADD_MEMBER']" @click="showAddUserModal(record)">{{
t('system.organization.addMember')
}}</MsButton>
<MsButton v-permission="['PROJECT_BASE_INFO:READ']" @click="enterProject(record.id)">{{
t('system.project.enterProject')
}}</MsButton>
<MsButton v-permission="['ORGANIZATION_PROJECT:READ+UPDATE']" @click="showAddProjectModal(record)">
{{ t('common.edit') }}
</MsButton>
<MsButton v-permission="['ORGANIZATION_PROJECT:READ+ADD_MEMBER']" @click="showAddUserModal(record)">
{{ t('system.organization.addMember') }}
</MsButton>
<MsButton v-permission="['PROJECT_BASE_INFO:READ']" @click="enterProject(record.id)">
{{ t('system.project.enterProject') }}
</MsButton>
<MsTableMoreAction
v-permission="['ORGANIZATION_PROJECT:READ+DELETE']"
:list="tableActions"

View File

@ -46,6 +46,13 @@ declare interface Window {
minderEditor: Record<string, any>;
km: Record<string, any>;
canvg: (canvas: HTMLCanvasElement, xml: string, option: Record<string, any>) => void;
minderHistory: {
reset: () => void;
undo: () => void;
redo: () => void;
hasUndo: () => boolean;
hasRedo: () => boolean;
};
DTFrameLogin: (
frameParams: IDTLoginFrameParams, // DOM包裹容器相关参数
loginParams: IDTLoginLoginParams, // 统一登录参数