From 578fe9a157ee768956674cd21bcb71d8a8b1f2a9 Mon Sep 17 00:00:00 2001 From: baiqi Date: Wed, 31 Jul 2024 18:17:02 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E8=84=91=E5=9B=BE):=20=E8=84=91=E5=9B=BE?= =?UTF-8?q?=E6=89=B9=E9=87=8F=E6=93=8D=E4=BD=9C&=E5=85=AC=E5=85=B1?= =?UTF-8?q?=E6=96=87=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ms-minders/featureCaseMinder/index.vue | 26 ++ .../featureCaseMinder/useMinderBaseApi.ts | 10 +- .../ms-minders/testPlanMinder/index.vue | 9 +- .../hooks/useMinderEventListener.ts | 8 +- .../hooks/useMinderOperation.ts | 114 ++++++-- .../hooks/useMinderPriority.ts | 57 ++++ .../pure/ms-minder-editor/main/mainEditor.vue | 4 + .../pure/ms-minder-editor/menu/batchMenu.vue | 188 +++++++++++++ .../ms-minder-editor/menu/nodeFloatMenu.vue | 69 +---- .../ms-minder-editor/menu/view/arrange.vue | 34 --- .../menu/view/fontOperation.vue | 261 ------------------ .../pure/ms-minder-editor/menu/view/mold.vue | 93 ------- .../menu/view/styleOperation.vue | 79 ------ .../ms-minder-editor/menu/view/viewMenu.vue | 39 --- .../pure/ms-minder-editor/minderEditor.vue | 43 ++- .../components/pure/ms-minder-editor/props.ts | 37 ++- frontend/src/locale/en-US/common.ts | 1 + frontend/src/locale/zh-CN/common.ts | 1 + frontend/src/views/login/locale/en-US.ts | 2 +- frontend/src/views/login/locale/zh-CN.ts | 2 +- 20 files changed, 451 insertions(+), 626 deletions(-) create mode 100644 frontend/src/components/pure/ms-minder-editor/hooks/useMinderPriority.ts create mode 100644 frontend/src/components/pure/ms-minder-editor/menu/batchMenu.vue delete mode 100644 frontend/src/components/pure/ms-minder-editor/menu/view/arrange.vue delete mode 100644 frontend/src/components/pure/ms-minder-editor/menu/view/fontOperation.vue delete mode 100644 frontend/src/components/pure/ms-minder-editor/menu/view/mold.vue delete mode 100644 frontend/src/components/pure/ms-minder-editor/menu/view/styleOperation.vue delete mode 100644 frontend/src/components/pure/ms-minder-editor/menu/view/viewMenu.vue diff --git a/frontend/src/components/business/ms-minders/featureCaseMinder/index.vue b/frontend/src/components/business/ms-minders/featureCaseMinder/index.vue index 20bbebc226..faf597e735 100644 --- a/frontend/src/components/business/ms-minders/featureCaseMinder/index.vue +++ b/frontend/src/components/business/ms-minders/featureCaseMinder/index.vue @@ -19,8 +19,14 @@ :can-show-paste-menu="!stopPaste()" :can-show-more-menu="canShowMoreMenu()" :can-show-priority-menu="canShowPriorityMenu()" + :custom-batch-expand="customBatchExpand" + :can-show-batch-expand="canShowBatchExpand()" + :can-show-batch-cut="true" + :can-show-batch-copy="true" + :can-show-batch-delete="true" :priority-tooltip="t('caseManagement.caseReview.caseLevel')" :disabled="!hasEditPermission" + can-show-more-batch-menu single-tag tag-enable sequence-enable @@ -697,6 +703,26 @@ } } + /** + * 批量展开节点 + */ + function customBatchExpand(node: MinderJsonNode) { + if (node.data?.resource?.includes(caseTag)) { + expendNodeAndChildren(node); + } + } + + /** + * 判断是否显示批量展开按钮 + */ + function canShowBatchExpand() { + if (window.minder) { + const nodes: MinderJsonNode[] = window.minder.getSelectedNodes(); + return nodes.some((node) => !!node.data?.resource?.includes(caseTag)); + } + return false; + } + /** * 解析用例节点信息 * @param node 用例节点 diff --git a/frontend/src/components/business/ms-minders/featureCaseMinder/useMinderBaseApi.ts b/frontend/src/components/business/ms-minders/featureCaseMinder/useMinderBaseApi.ts index e212032923..c0b17ae66f 100644 --- a/frontend/src/components/business/ms-minders/featureCaseMinder/useMinderBaseApi.ts +++ b/frontend/src/components/business/ms-minders/featureCaseMinder/useMinderBaseApi.ts @@ -1,8 +1,8 @@ import type { - InsertMenuItem, MinderEvent, MinderJsonNode, MinderJsonNodeData, + MinderMenuItem, } from '@/components/pure/ms-minder-editor/props'; import { useI18n } from '@/hooks/useI18n'; @@ -50,8 +50,8 @@ export default function useMinderBaseApi({ hasEditPermission }: { hasEditPermiss return true; } - const insertSiblingMenus = ref([]); - const insertSonMenus = ref([]); + const insertSiblingMenus = ref([]); + const insertSonMenus = ref([]); /** * 检测节点可展示的菜单项 @@ -236,9 +236,9 @@ export default function useMinderBaseApi({ hasEditPermission }: { hasEditPermiss return false; } if (window.minder) { - const node: MinderJsonNode = window.minder.getSelectedNode(); + const nodes: MinderJsonNode[] = window.minder.getSelectedNodes(); // 选中节点是用例节点时,可展示优先级菜单 - return !!node?.data?.resource?.includes(caseTag); + return nodes.every((node) => !!node.data?.resource?.includes(caseTag)); } return false; } diff --git a/frontend/src/components/business/ms-minders/testPlanMinder/index.vue b/frontend/src/components/business/ms-minders/testPlanMinder/index.vue index a61cab048d..a26be072e4 100644 --- a/frontend/src/components/business/ms-minders/testPlanMinder/index.vue +++ b/frontend/src/components/business/ms-minders/testPlanMinder/index.vue @@ -15,6 +15,8 @@ :can-show-float-menu="canShowFloatMenu" :can-show-delete-menu="canShowDeleteMenu" :disabled="!hasEditPermission" + :can-show-batch-delete="true" + can-show-more-batch-menu custom-priority single-tag tag-enable @@ -523,8 +525,13 @@ Message.warning(t('ms.minders.unsavedTip')); return; } + const nodes: MinderJsonNode[] = window.minder.getSelectedNodes(); + if (nodes.length > 1) { + extraVisible.value = false; + return; + } extraVisible.value = !extraVisible.value; - const node: MinderJsonNode = window.minder.getSelectedNode(); + const node = nodes[0]; switchingConfigFormData.value = true; if (extraVisible.value) { activePlanSet.value = node as PlanMinderNode; diff --git a/frontend/src/components/pure/ms-minder-editor/hooks/useMinderEventListener.ts b/frontend/src/components/pure/ms-minder-editor/hooks/useMinderEventListener.ts index 53762cda61..97ae90abce 100644 --- a/frontend/src/components/pure/ms-minder-editor/hooks/useMinderEventListener.ts +++ b/frontend/src/components/pure/ms-minder-editor/hooks/useMinderEventListener.ts @@ -7,7 +7,7 @@ import type { MinderEvent, MinderJsonNode } from '../props'; export interface UseEventListenerProps { handleContentChange?: (node?: MinderJsonNode) => void; - handleSelectionChange?: (node?: MinderJsonNode) => void; + handleSelectionChange?: (nodes: MinderJsonNode[]) => void; handleMinderEvent?: (event: MinderCustomEvent) => void; handleBeforeExecCommand?: (event: MinderEvent) => void; handleViewChange?: (event: MinderEvent) => void; @@ -36,16 +36,16 @@ export default function useEventListener(listener: UseEventListenerProps) { minder.on( 'selectionchange', debounce(() => { - const node: MinderJsonNode = minder.getSelectedNode(); + const nodes: MinderJsonNode[] = minder.getSelectedNodes(); // 如果节点选中后即刻进行拖拽,则等待拖拽结束后再触发选中事件 if (isDragging.value) { selectionchangeEvent = () => { if (listener.handleSelectionChange) { - listener.handleSelectionChange(node); + listener.handleSelectionChange(nodes); } }; } else if (listener.handleSelectionChange) { - listener.handleSelectionChange(node); + listener.handleSelectionChange(nodes); } }, 300) ); diff --git a/frontend/src/components/pure/ms-minder-editor/hooks/useMinderOperation.ts b/frontend/src/components/pure/ms-minder-editor/hooks/useMinderOperation.ts index 0f5a7c9d91..c4e9b506ac 100644 --- a/frontend/src/components/pure/ms-minder-editor/hooks/useMinderOperation.ts +++ b/frontend/src/components/pure/ms-minder-editor/hooks/useMinderOperation.ts @@ -1,10 +1,14 @@ +import { useClipboard } from '@vueuse/core'; +import { Message } from '@arco-design/web-vue'; + +import { useI18n } from '@/hooks/useI18n'; import useMinderStore from '@/store/modules/components/minder-editor'; import { getGenerateId } from '@/utils'; import { MinderEventName } from '@/enums/minderEnum'; import type { MinderJsonNode } from '../props'; -import { markDeleteNode, resetNodes } from '../script/tool/utils'; +import { markDeleteNode, resetNodes, setPriorityView } from '../script/tool/utils'; interface IData { getRegisterProtocol(protocol: string): { @@ -15,16 +19,23 @@ interface IData { export interface MinderOperationProps { insertNode?: (node: MinderJsonNode, command: string, value?: string) => void; + customBatchExpand?: (node: MinderJsonNode) => void; + disabled?: boolean; + canShowMoreMenu?: boolean; canShowMoreMenuNodeOperation?: boolean; canShowPasteMenu?: boolean; + canShowDeleteMenu?: boolean; + customPriority?: boolean; + priorityStartWithZero?: boolean; + priorityPrefix?: string; + canShowBatchCut?: boolean; + canShowBatchCopy?: boolean; + canShowBatchDelete?: boolean; } -export default function useMinderOperation({ - insertNode, - canShowMoreMenuNodeOperation, - canShowPasteMenu, -}: MinderOperationProps) { +export default function useMinderOperation(options: MinderOperationProps) { const minderStore = useMinderStore(); + const { t } = useI18n(); function encode(nodes: Array): string { const { editor } = window; @@ -42,8 +53,8 @@ export default function useMinderOperation({ /** * 执行复制 */ - const minderCopy = (e?: ClipboardEvent) => { - if (!canShowMoreMenuNodeOperation) { + const minderCopy = async (e?: ClipboardEvent) => { + if ((!options.canShowMoreMenu || !options.canShowMoreMenuNodeOperation) && options.canShowBatchCopy === false) { e?.preventDefault(); return; } @@ -57,8 +68,15 @@ export default function useMinderOperation({ case 'normal': { const selectedNodes = minder.getSelectedNodes(); minderStore.dispatchEvent(MinderEventName.COPY_NODE, undefined, undefined, undefined, selectedNodes); + if (e?.clipboardData) { + e.clipboardData.setData('text/plain', encode(selectedNodes)); + } else { + const { copy } = useClipboard(); + await copy(encode(selectedNodes)); + } minder.execCommand('Copy'); e?.preventDefault(); + Message.success(t('common.copySuccess')); break; } default: @@ -68,8 +86,11 @@ export default function useMinderOperation({ /** * 执行剪切 */ - const minderCut = (e?: ClipboardEvent) => { - if (!canShowMoreMenuNodeOperation) { + const minderCut = async (e?: ClipboardEvent) => { + if ( + (options.disabled || !options.canShowMoreMenu || !options.canShowMoreMenuNodeOperation) && + options.canShowBatchCut === false + ) { e?.preventDefault(); return; } @@ -79,9 +100,7 @@ export default function useMinderOperation({ e?.preventDefault(); return; } - const state = fsm.state(); - switch (state) { case 'input': { break; @@ -90,11 +109,17 @@ export default function useMinderOperation({ markDeleteNode(minder); const selectedNodes = minder.getSelectedNodes(); if (selectedNodes.length) { - e?.clipboardData?.setData('text/plain', encode(selectedNodes)); + if (e?.clipboardData) { + e.clipboardData.setData('text/plain', encode(selectedNodes)); + } else { + const { copy } = useClipboard(); + await copy(encode(selectedNodes)); + } minder.execCommand('Cut'); } - e?.preventDefault(); minderStore.dispatchEvent(MinderEventName.CUT_NODE, undefined, undefined, undefined, selectedNodes); + e?.preventDefault(); + Message.success(t('common.cutSuccess')); break; } default: @@ -104,8 +129,8 @@ export default function useMinderOperation({ /** * 执行粘贴 */ - const minderPaste = (e?: ClipboardEvent) => { - if (!canShowMoreMenuNodeOperation && !canShowPasteMenu) { + const minderPaste = async (e?: ClipboardEvent) => { + if (options.disabled || !options.canShowPasteMenu) { e?.preventDefault(); return; } @@ -117,10 +142,8 @@ export default function useMinderOperation({ e?.preventDefault(); return; } - const state = fsm.state(); - const textData = e?.clipboardData?.getData('text/plain'); - + const textData = e?.clipboardData ? e.clipboardData.getData('text/plain') : await navigator.clipboard.readText(); switch (state) { case 'input': { // input状态下如果格式为application/km则不进行paste操作 @@ -181,8 +204,8 @@ export default function useMinderOperation({ */ const execInsertCommand = (command: string, value?: string) => { const node: MinderJsonNode = window.minder.getSelectedNode(); - if (insertNode) { - insertNode(node, command, value); + if (options.insertNode) { + options.insertNode(node, command, value); return; } if (window.minder.queryCommandState(command) !== -1) { @@ -220,13 +243,57 @@ export default function useMinderOperation({ minderStore.dispatchEvent(MinderEventName.INSERT_SIBLING, value, undefined, undefined, selectedNodes); }; + /** + * 脑图展开 + * @param selectedNodes 当前选中的节点集合 + */ + const minderExpand = (selectedNodes: MinderJsonNode[]) => { + if (selectedNodes.every((node) => node.isExpanded())) { + // 选中的节点集合全部展开,则全部收起 + selectedNodes.forEach((node) => { + node.collapse(); + node.renderTree(); + }); + if (!options.customPriority) { + // 展开后,需要设置一次优先级展示,避免展开后优先级显示成脑图内置文案;如果设置了自定义优先级,则不在此设置,由外部自行处理 + setPriorityView(!!options.priorityStartWithZero, options.priorityPrefix || ''); + window.minder.refresh(); + } + minderStore.dispatchEvent(MinderEventName.COLLAPSE, undefined, undefined, undefined, selectedNodes); + } else { + // 选中的节点集合中有一个节点未展开,则全部展开 + selectedNodes.forEach((node) => { + if (selectedNodes.length > 1 && options.customBatchExpand) { + // 批量操作节点才执行customBatchExpand + options.customBatchExpand(node); + } else { + node.expand(); + node.renderTree(); + } + }); + if (!options.customPriority) { + // 展开后,需要设置一次优先级展示,避免展开后优先级显示成脑图内置文案;如果设置了自定义优先级,则不在此设置,由外部自行处理 + setPriorityView(!!options.priorityStartWithZero, options.priorityPrefix || ''); + window.minder.refresh(); + } + minderStore.dispatchEvent(MinderEventName.EXPAND, undefined, undefined, undefined, selectedNodes); + } + }; + /** * 删除节点 * @param selectedNodes 当前选中的节点集合 */ const minderDelete = (selectedNodes: MinderJsonNode[]) => { - minderStore.dispatchEvent(MinderEventName.DELETE_NODE, undefined, undefined, undefined, selectedNodes); - window.minder.execCommand('RemoveNode'); + if ( + (options.canShowDeleteMenu || + (options.canShowMoreMenu && options.canShowMoreMenuNodeOperation) || + options.canShowBatchDelete) && + !options.disabled + ) { + minderStore.dispatchEvent(MinderEventName.DELETE_NODE, undefined, undefined, undefined, selectedNodes); + window.minder.execCommand('RemoveNode'); + } }; return { @@ -236,5 +303,6 @@ export default function useMinderOperation({ appendChildNode, appendSiblingNode, minderDelete, + minderExpand, }; } diff --git a/frontend/src/components/pure/ms-minder-editor/hooks/useMinderPriority.ts b/frontend/src/components/pure/ms-minder-editor/hooks/useMinderPriority.ts new file mode 100644 index 0000000000..528bd849f4 --- /dev/null +++ b/frontend/src/components/pure/ms-minder-editor/hooks/useMinderPriority.ts @@ -0,0 +1,57 @@ +import { isDisableNode, setPriorityView } from '../script/tool/utils'; + +export interface PriorityProps { + priorityStartWithZero: boolean; + priorityPrefix: string; + customPriority?: boolean; + priorityDisableCheck?: (node: any) => boolean; +} + +export default function usePriority(options: PriorityProps) { + const priorityDisabled = ref(true); + function isDisable(): boolean { + if (Object.keys(window.minder).length === 0) return true; + nextTick(() => { + setPriorityView(options.priorityStartWithZero, options.priorityPrefix); + }); + const node = window.minder.getSelectedNode(); + if (isDisableNode(window.minder) || !node || node.parent === null) { + return true; + } + if (options.priorityDisableCheck) { + return options.priorityDisableCheck(node); + } + return !!window.minder.queryCommandState && window.minder.queryCommandState('priority') === -1; + } + + function setPriority(value?: string) { + if (value && !priorityDisabled.value) { + window.minder.execCommand('priority', value); + setPriorityView(options.priorityStartWithZero, options.priorityPrefix); + } else if (window.minder.execCommand && !priorityDisabled.value) { + window.minder.execCommand('priority'); + } + } + + onMounted(() => { + nextTick(() => { + const freshFuc = setPriorityView; + if (window.minder && !options.customPriority) { + window.minder.on('contentchange', () => { + // 异步执行,否则执行完,还会被重置 + setTimeout(() => { + freshFuc(options.priorityStartWithZero, options.priorityPrefix); + }, 0); + }); + window.minder.on('selectionchange', () => { + priorityDisabled.value = isDisable(); + }); + } + }); + }); + + return { + priorityDisabled, + setPriority, + }; +} diff --git a/frontend/src/components/pure/ms-minder-editor/main/mainEditor.vue b/frontend/src/components/pure/ms-minder-editor/main/mainEditor.vue index d3712b4164..c88e01b90f 100644 --- a/frontend/src/components/pure/ms-minder-editor/main/mainEditor.vue +++ b/frontend/src/components/pure/ms-minder-editor/main/mainEditor.vue @@ -29,12 +29,14 @@ + + + diff --git a/frontend/src/components/pure/ms-minder-editor/menu/nodeFloatMenu.vue b/frontend/src/components/pure/ms-minder-editor/menu/nodeFloatMenu.vue index 079a2554c6..8b27133048 100644 --- a/frontend/src/components/pure/ms-minder-editor/menu/nodeFloatMenu.vue +++ b/frontend/src/components/pure/ms-minder-editor/menu/nodeFloatMenu.vue @@ -137,32 +137,32 @@
{{ t('minder.hotboxMenu.enterNode') }}
- +
(Ctrl+ Enter)
@@ -200,8 +200,9 @@ import { MinderEventName } from '@/enums/minderEnum'; import useMinderOperation from '../hooks/useMinderOperation'; - import { floatMenuProps, mainEditorProps, MinderJsonNode, priorityProps, tagProps } from '../props'; - import { isDisableNode, isNodeInMinderView, setPriorityView } from '../script/tool/utils'; + import usePriority from '../hooks/useMinderPriority'; + import { floatMenuProps, mainEditorProps, MinderJsonNode, priorityColorMap, priorityProps, tagProps } from '../props'; + import { isNodeInMinderView } from '../script/tool/utils'; const props = defineProps({ ...mainEditorProps, @@ -215,6 +216,7 @@ const { t } = useI18n(); const minderStore = useMinderStore(); + const { setPriority } = usePriority(props); const currentNodeTags = ref([]); const tags = ref([]); @@ -240,7 +242,7 @@ } } if (selectedNodes.length > 1) { - // 多选时隐藏悬浮菜单 TODO:支持批量操作 + // 多选时隐藏悬浮菜单 menuVisible.value = false; return; } @@ -304,31 +306,8 @@ } } - const priorityColorMap: Record = { - 1: 'rgb(var(--danger-6))', - 2: 'rgb(var(--link-6))', - 3: 'rgb(var(--success-6))', - 4: 'rgb(var(--warning-6))', - }; - const priorityDisabled = ref(true); - function isDisable(): boolean { - if (Object.keys(window.minder).length === 0) return true; - nextTick(() => { - setPriorityView(props.priorityStartWithZero, props.priorityPrefix); - }); - const node = window.minder.getSelectedNode(); - if (isDisableNode(window.minder) || !node || node.parent === null) { - return true; - } - if (props.priorityDisableCheck) { - return props.priorityDisableCheck(node); - } - return !!window.minder.queryCommandState && window.minder.queryCommandState('priority') === -1; - } - - const { minderCopy, minderCut, minderPaste, appendChildNode, appendSiblingNode, minderDelete } = useMinderOperation({ - insertNode: props.insertNode, - }); + const { minderCopy, minderCut, minderPaste, appendChildNode, appendSiblingNode, minderDelete } = + useMinderOperation(props); /** * 处理快捷菜单选择 @@ -360,12 +339,7 @@ minderStore.dispatchEvent(MinderEventName.ENTER_NODE, undefined, undefined, undefined, [selectedNodes[0]]); break; case 'priority': - if (value && !priorityDisabled.value) { - window.minder.execCommand('priority', value); - setPriorityView(props.priorityStartWithZero, props.priorityPrefix); - } else if (window.minder.execCommand && !priorityDisabled.value) { - window.minder.execCommand('priority'); - } + setPriority(value); break; default: break; @@ -385,23 +359,6 @@ } } ); - - onMounted(() => { - nextTick(() => { - const freshFuc = setPriorityView; - if (window.minder && !props.customPriority) { - window.minder.on('contentchange', () => { - // 异步执行,否则执行完,还会被重置 - setTimeout(() => { - freshFuc(props.priorityStartWithZero, props.priorityPrefix); - }, 0); - }); - window.minder.on('selectionchange', () => { - priorityDisabled.value = isDisable(); - }); - } - }); - }); diff --git a/frontend/src/components/pure/ms-minder-editor/menu/view/styleOperation.vue b/frontend/src/components/pure/ms-minder-editor/menu/view/styleOperation.vue deleted file mode 100644 index e9adb08ca9..0000000000 --- a/frontend/src/components/pure/ms-minder-editor/menu/view/styleOperation.vue +++ /dev/null @@ -1,79 +0,0 @@ - - - - - diff --git a/frontend/src/components/pure/ms-minder-editor/menu/view/viewMenu.vue b/frontend/src/components/pure/ms-minder-editor/menu/view/viewMenu.vue deleted file mode 100644 index c483e73a3f..0000000000 --- a/frontend/src/components/pure/ms-minder-editor/menu/view/viewMenu.vue +++ /dev/null @@ -1,39 +0,0 @@ - - - - - diff --git a/frontend/src/components/pure/ms-minder-editor/minderEditor.vue b/frontend/src/components/pure/ms-minder-editor/minderEditor.vue index a482eecb68..7ef17f3bad 100644 --- a/frontend/src/components/pure/ms-minder-editor/minderEditor.vue +++ b/frontend/src/components/pure/ms-minder-editor/minderEditor.vue @@ -37,6 +37,7 @@ import useMinderOperation from './hooks/useMinderOperation'; import useShortCut from './hooks/useShortCut'; import { + batchMenuProps, delProps, editMenuProps, floatMenuProps, @@ -50,13 +51,14 @@ tagProps, viewMenuProps, } from './props'; - import { isNodeInMinderView, setPriorityView } from './script/tool/utils'; + import { isNodeInMinderView } from './script/tool/utils'; const emit = defineEmits<{ (e: 'moldChange', data: number): void; (e: 'save', data: MinderJson, callback: () => void): void; (e: 'afterMount'): void; (e: 'nodeSelect', data: MinderJsonNode): void; + (e: 'nodeBatchSelect', data: MinderJsonNode[]): void; (e: 'contentChange', data?: MinderJsonNode): void; (e: 'action', event: MinderCustomEvent): void; (e: 'beforeExecCommand', event: MinderEvent): void; @@ -74,6 +76,7 @@ ...tagProps, ...delProps, ...viewMenuProps, + ...batchMenuProps, }); const minderStore = useMinderStore(); @@ -115,11 +118,7 @@ emit('save', data, callback); } - const { appendChildNode, appendSiblingNode, minderDelete } = useMinderOperation({ - insertNode: props.insertNode, - canShowMoreMenuNodeOperation: props.canShowMoreMenuNodeOperation, - canShowPasteMenu: props.canShowPasteMenu, - }); + const { appendChildNode, appendSiblingNode, minderDelete, minderExpand } = useMinderOperation(props); const { unbindShortcuts } = useShortCut( { undo: () => { @@ -135,26 +134,17 @@ } }, delete: () => { - if (props.canShowMoreMenuNodeOperation && !props.disabled) { + if ( + (props.canShowDeleteMenu || (props.canShowMoreMenu && props.canShowMoreMenuNodeOperation)) && + !props.disabled + ) { const selectedNodes: MinderJsonNode[] = window.minder.getSelectedNodes(); minderDelete(selectedNodes); } }, expand: () => { const selectedNodes: MinderJsonNode[] = window.minder.getSelectedNodes(); - if (selectedNodes.every((node) => node.isExpanded())) { - // 选中的节点集合全部展开,则全部收起 - window.minder.execCommand('Collapse'); - minderStore.dispatchEvent(MinderEventName.COLLAPSE, undefined, undefined, undefined, selectedNodes); - } else { - // 选中的节点集合中有一个节点未展开,则全部展开 - window.minder.execCommand('Expand'); - if (!props.customPriority) { - // 展开后,需要设置一次优先级展示,避免展开后优先级显示成脑图内置文案;如果设置了自定义优先级,则不在此设置,由外部自行处理 - setPriorityView(props.priorityStartWithZero, props.priorityPrefix); - } - minderStore.dispatchEvent(MinderEventName.EXPAND, undefined, undefined, undefined, selectedNodes); - } + minderExpand(selectedNodes); }, appendChildNode: () => { if (props.insertSonMenus.length > 0 || props.insertNode) { @@ -169,17 +159,18 @@ } }, }, - { - insertNode: props.insertNode, - canShowMoreMenuNodeOperation: props.canShowMoreMenuNodeOperation, - canShowPasteMenu: props.canShowPasteMenu, - } + props ); onMounted(() => { window.minderProps = props; useMinderEventListener({ - handleSelectionChange: (node?: MinderJsonNode) => { + handleSelectionChange: (nodes: MinderJsonNode[]) => { + if (nodes && nodes.length > 1) { + emit('nodeBatchSelect', nodes); + return; + } + const node = nodes[0]; if (node) { emit('nodeSelect', node); const box = node.getRenderBox(); diff --git a/frontend/src/components/pure/ms-minder-editor/props.ts b/frontend/src/components/pure/ms-minder-editor/props.ts index db46a92ff7..76f65cc4f9 100644 --- a/frontend/src/components/pure/ms-minder-editor/props.ts +++ b/frontend/src/components/pure/ms-minder-editor/props.ts @@ -102,6 +102,12 @@ export const priorityProps = { default: '', }, }; +export const priorityColorMap: Record = { + 1: 'rgb(var(--danger-6))', + 2: 'rgb(var(--link-6))', + 3: 'rgb(var(--success-6))', + 4: 'rgb(var(--warning-6))', +}; export interface MinderReplaceTag { tags: string[]; @@ -133,7 +139,7 @@ export const tagProps = { afterTagEdit: Function as PropType<(nodes: MinderJsonNode[], tag: string) => void>, }; -export interface InsertMenuItem { +export interface MinderMenuItem { value: string; label: string; } @@ -146,14 +152,14 @@ export interface MoreMenuOtherOperationItem { export const floatMenuProps = { // 插入同级选项 insertSiblingMenus: { - type: Array as PropType, + type: Array as PropType, default() { return []; }, }, // 插入子级选项 insertSonMenus: { - type: Array as PropType, + type: Array as PropType, default() { return []; }, @@ -210,6 +216,31 @@ export const floatMenuProps = { default: false, }, }; +export const batchMenuProps = { + canShowMoreBatchMenu: { + type: Boolean, + default: false, + }, + canShowBatchCopy: { + type: Boolean, + default: false, + }, + canShowBatchCut: { + type: Boolean, + default: false, + }, + canShowBatchDelete: { + type: Boolean, + default: false, + }, + canShowBatchExpand: { + type: Boolean, + default: false, + }, + customBatchExpand: { + type: Function as PropType<(node: MinderJsonNode) => void>, + }, +}; export const editMenuProps = { sequenceEnable: { diff --git a/frontend/src/locale/en-US/common.ts b/frontend/src/locale/en-US/common.ts index e0a98f5afc..54a4ec8d7c 100644 --- a/frontend/src/locale/en-US/common.ts +++ b/frontend/src/locale/en-US/common.ts @@ -201,4 +201,5 @@ export default { 'common.jump': 'Jump', 'common.gotIt': 'Got it', 'common.inputPleaseEnterTags': 'Please enter the update tag enter add, not more than 64 characters', + 'common.cutSuccess': 'Cut successfully', }; diff --git a/frontend/src/locale/zh-CN/common.ts b/frontend/src/locale/zh-CN/common.ts index eb4f8a24ef..d6d1d910f5 100644 --- a/frontend/src/locale/zh-CN/common.ts +++ b/frontend/src/locale/zh-CN/common.ts @@ -200,4 +200,5 @@ export default { 'common.jump': '跳转', 'common.gotIt': '知道了', 'common.inputPleaseEnterTags': '请输入更新标签回车添加,不得超过64字符', + 'common.cutSuccess': '剪切成功', }; diff --git a/frontend/src/views/login/locale/en-US.ts b/frontend/src/views/login/locale/en-US.ts index bd9da405a2..ae4d9d3d8d 100644 --- a/frontend/src/views/login/locale/en-US.ts +++ b/frontend/src/views/login/locale/en-US.ts @@ -1,5 +1,5 @@ export default { - 'login.form.title': 'Modern, open-source test management and interface testing tools', + 'login.form.title': 'Open Source Continuous Testing Tools', 'login.form.userName.errMsg': 'Username cannot be empty', 'login.form.password.errMsg': 'Password cannot be empty', 'login.form.login.errMsg': 'Login error, refresh and try again', diff --git a/frontend/src/views/login/locale/zh-CN.ts b/frontend/src/views/login/locale/zh-CN.ts index fd226e181f..4ae9d01479 100644 --- a/frontend/src/views/login/locale/zh-CN.ts +++ b/frontend/src/views/login/locale/zh-CN.ts @@ -1,5 +1,5 @@ export default { - 'login.form.title': '现代化、开源的测试管理和接口测试工具', + 'login.form.title': '开源持续测试工具', 'login.form.userName.errMsg': '用户名不能为空', 'login.form.password.errMsg': '密码不能为空', 'login.form.login.errMsg': '登录出错,请刷新重试',