diff --git a/src/enums/editPageEnum.ts b/src/enums/editPageEnum.ts index 1bd8fbc6..773963f4 100644 --- a/src/enums/editPageEnum.ts +++ b/src/enums/editPageEnum.ts @@ -1,3 +1,9 @@ +// 鼠标点击左右键 +export enum MouseEventButton { + LEFT = 1, + RIGHT = 2 +} + // 页面拖拽键名 export enum DragKeyEnum { DROG_KEY = 'ChartData' @@ -27,6 +33,9 @@ export enum WinKeyboard { CTRL = 'ctrl', SHIFT = 'shift', ALT = ' alt', + CTRL_SOURCE_KEY = "control", + SHIFT_SOURCE_KEY = "shift", + ALT_SOURCE_KEY = "alt" } // Mac 键盘枚举 @@ -35,4 +44,7 @@ export enum MacKeyboard { CTRL = '⌘', SHIFT = '⇧', ALT = '⌥', -} \ No newline at end of file + CTRL_SOURCE_KEY = "⌘", + SHIFT_SOURCE_KEY = "⇧", + ALT_SOURCE_KEY = "⌥" +} diff --git a/src/plugins/icon.ts b/src/plugins/icon.ts index 7a9b3adf..5c9817f4 100644 --- a/src/plugins/icon.ts +++ b/src/plugins/icon.ts @@ -80,7 +80,9 @@ import { Scale as ScaleIcon, FitToScreen as FitToScreenIcon, FitToHeight as FitToHeightIcon, - FitToWidth as FitToWidthIcon + FitToWidth as FitToWidthIcon, + Carbon3DCursor as Carbon3DCursorIcon, + Carbon3DSoftware as Carbon3DSoftwareIcon } from '@vicons/carbon' const ionicons5 = { @@ -234,7 +236,11 @@ const carbon = { ScaleIcon, FitToScreenIcon, FitToHeightIcon, - FitToWidthIcon + FitToWidthIcon, + // 成组 + Carbon3DCursorIcon, + // 解组 + Carbon3DSoftwareIcon } // https://www.xicons.org/#/ 还有很多 diff --git a/src/store/modules/chartEditStore/chartEditStore.ts b/src/store/modules/chartEditStore/chartEditStore.ts index a0b63abe..568782df 100644 --- a/src/store/modules/chartEditStore/chartEditStore.ts +++ b/src/store/modules/chartEditStore/chartEditStore.ts @@ -172,7 +172,7 @@ export const useChartEditStore = defineStore({ this.targetChart.selectId = [] return } - // 新增 + // 多选 if(push) { // 字符串 if(isString(selectId)) { diff --git a/src/store/modules/chartHistoryStore/chartHistoryStore.d.ts b/src/store/modules/chartHistoryStore/chartHistoryStore.d.ts index 1aee786d..624b954b 100644 --- a/src/store/modules/chartHistoryStore/chartHistoryStore.d.ts +++ b/src/store/modules/chartHistoryStore/chartHistoryStore.d.ts @@ -51,7 +51,8 @@ export enum HistoryStackItemEnum { // 历史记录项类型 export interface HistoryItemType { - [HistoryStackItemEnum.ID]: string + // 会有同时操作多个组件场景 + [HistoryStackItemEnum.ID]: string | string[] [HistoryStackItemEnum.TARGET_TYPE]: HistoryTargetTypeEnum [HistoryStackItemEnum.ACTION_TYPE]: HistoryActionTypeEnum [HistoryStackItemEnum.HISTORY_DATA]: CreateComponentType | EditCanvasType diff --git a/src/views/chart/ContentEdit/components/EditShortcutKey/ShortcutKeyModal.vue b/src/views/chart/ContentEdit/components/EditShortcutKey/ShortcutKeyModal.vue index d3014b04..487d7bad 100644 --- a/src/views/chart/ContentEdit/components/EditShortcutKey/ShortcutKeyModal.vue +++ b/src/views/chart/ContentEdit/components/EditShortcutKey/ShortcutKeyModal.vue @@ -99,6 +99,11 @@ const shortcutKeyOptions = [ win: `${WinKeyboard.CTRL.toUpperCase()} + ${WinKeyboard.SHIFT.toUpperCase()} + Z `, mac: `${MacKeyboard.CTRL.toUpperCase()} + ${MacKeyboard.SHIFT.toUpperCase()} + Z `, }, + { + label: '多选', + win: `${WinKeyboard.CTRL.toUpperCase()} + 🖱️ `, + mac: `${MacKeyboard.CTRL_SOURCE_KEY.toUpperCase()} + 🖱️ `, + }, ] const closeHandle = () => { emit('update:modelShow', false) diff --git a/src/views/chart/ContentEdit/hooks/useDrag.hook.ts b/src/views/chart/ContentEdit/hooks/useDrag.hook.ts index a2292099..ede65ccd 100644 --- a/src/views/chart/ContentEdit/hooks/useDrag.hook.ts +++ b/src/views/chart/ContentEdit/hooks/useDrag.hook.ts @@ -1,10 +1,7 @@ -import { DragKeyEnum } from '@/enums/editPageEnum' +import { DragKeyEnum, MouseEventButton, WinKeyboard, MacKeyboard } from '@/enums/editPageEnum' import { createComponent } from '@/packages' import { ConfigType } from '@/packages/index.d' -import { - CreateComponentType, - PickCreateComponentType, -} from '@/packages/index.d' +import { CreateComponentType, PickCreateComponentType } from '@/packages/index.d' import { useContextMenu } from '@/views/chart/hooks/useContextMenu.hook' import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore' import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d' @@ -35,10 +32,7 @@ export const dragHandle = async (e: DragEvent) => { // 创建新图表组件 let newComponent: CreateComponentType = await createComponent(dropData) - newComponent.setPosition( - e.offsetX - newComponent.attr.w / 2, - e.offsetY - newComponent.attr.h / 2 - ) + newComponent.setPosition(e.offsetX - newComponent.attr.w / 2, e.offsetY - newComponent.attr.h / 2) chartEditStore.addComponentList(newComponent, false, true) chartEditStore.setTargetSelectChart(newComponent.id) loadingFinish() @@ -57,10 +51,7 @@ export const dragoverHandle = (e: DragEvent) => { } // * 不拦截默认行为点击 -export const mousedownHandleUnStop = ( - e: MouseEvent, - item?: CreateComponentType -) => { +export const mousedownHandleUnStop = (e: MouseEvent, item?: CreateComponentType) => { if (item) { chartEditStore.setTargetSelectChart(item.id) return @@ -70,13 +61,42 @@ export const mousedownHandleUnStop = ( // * 移动图表 export const useMouseHandle = () => { - // 点击事件(包含移动事件) + // * Click 事件, 松开鼠标触发 + const mouseClickHandle = (e: MouseEvent, item: CreateComponentType) => { + e.preventDefault() + e.stopPropagation() + // 若此时按下了 CTRL, 表示多选 + if ( + window.$KeyboardActive?.has(WinKeyboard.CTRL_SOURCE_KEY) || + window.$KeyboardActive?.has(MacKeyboard.CTRL_SOURCE_KEY) + ) { + chartEditStore.setTargetSelectChart(item.id, true) + } + } + + // * 按下事件(包含移动事件) const mousedownHandle = (e: MouseEvent, item: CreateComponentType) => { e.preventDefault() e.stopPropagation() - onClickOutSide() + + // 按下左键 + CTRL + if ( + e.buttons === MouseEventButton.LEFT && + (window.$KeyboardActive?.has(WinKeyboard.CTRL_SOURCE_KEY) || + window.$KeyboardActive?.has(MacKeyboard.CTRL_SOURCE_KEY)) + ) + return + + // 按下右键 + 选中多个 + if (e.buttons === MouseEventButton.RIGHT && chartEditStore.getTargetChart.selectId.length > 1) return + + // 选中当前目标组件 chartEditStore.setTargetSelectChart(item.id) + + // 按下右键 + if (e.buttons === MouseEventButton.RIGHT) return + const scale = chartEditStore.getEditCanvas.scale const width = chartEditStore.getEditCanvasConfig.width const height = chartEditStore.getEditCanvasConfig.height @@ -141,15 +161,11 @@ export const useMouseHandle = () => { chartEditStore.setTargetHoverChart(undefined) } - return { mousedownHandle, mouseenterHandle, mouseleaveHandle } + return { mouseClickHandle, mousedownHandle, mouseenterHandle, mouseleaveHandle } } // * 移动锚点 -export const useMousePointHandle = ( - e: MouseEvent, - point: string, - attr: PickCreateComponentType<'attr'> -) => { +export const useMousePointHandle = (e: MouseEvent, point: string, attr: PickCreateComponentType<'attr'>) => { e.stopPropagation() e.preventDefault() diff --git a/src/views/chart/ContentEdit/index.vue b/src/views/chart/ContentEdit/index.vue index 27a017c2..98f1ade2 100644 --- a/src/views/chart/ContentEdit/index.vue +++ b/src/views/chart/ContentEdit/index.vue @@ -27,6 +27,7 @@ :index="index" :style="useComponentStyle(item.attr, index)" :item="item" + @click="mouseClickHandle($event, item)" @mousedown="mousedownHandle($event, item)" @mouseenter="mouseenterHandle($event, item)" @mouseleave="mouseleaveHandle($event, item)" @@ -87,7 +88,7 @@ const { handleContextMenu } = useContextMenu() useLayout() // 点击事件 -const { mouseenterHandle, mouseleaveHandle, mousedownHandle } = useMouseHandle() +const { mouseenterHandle, mouseleaveHandle, mousedownHandle, mouseClickHandle } = useMouseHandle() // 主题色 const themeSetting = computed(() => { diff --git a/src/views/chart/hooks/useContextMenu.hook.ts b/src/views/chart/hooks/useContextMenu.hook.ts index a733df25..c657ee50 100644 --- a/src/views/chart/hooks/useContextMenu.hook.ts +++ b/src/views/chart/hooks/useContextMenu.hook.ts @@ -6,82 +6,85 @@ import { icon } from '@/plugins' import { MenuOptionsItemType } from './useContextMenu.hook.d' import { MenuEnum } from '@/enums/editPageEnum' -const { - CopyIcon, - CutIcon, - ClipboardOutlineIcon, - TrashIcon, - ChevronDownIcon, - ChevronUpIcon, -} = icon.ionicons5 -const { UpToTopIcon, DownToBottomIcon, PaintBrushIcon } = icon.carbon +const { CopyIcon, CutIcon, ClipboardOutlineIcon, TrashIcon, ChevronDownIcon, ChevronUpIcon } = icon.ionicons5 +const { UpToTopIcon, DownToBottomIcon, PaintBrushIcon, Carbon3DSoftwareIcon, Carbon3DCursorIcon } = icon.carbon const chartEditStore = useChartEditStore() -// * 默认选项 +// * 默认单组件选项 const defaultOptions: MenuOptionsItemType[] = [ { label: '复制', key: MenuEnum.COPY, icon: renderIcon(CopyIcon), - fnHandle: chartEditStore.setCopy, + fnHandle: chartEditStore.setCopy }, { label: '剪切', key: MenuEnum.CUT, icon: renderIcon(CutIcon), - fnHandle: chartEditStore.setCut, + fnHandle: chartEditStore.setCut }, { label: '粘贴', key: MenuEnum.PARSE, icon: renderIcon(ClipboardOutlineIcon), - fnHandle: chartEditStore.setParse, + fnHandle: chartEditStore.setParse }, { type: 'divider', - key: 'd1', + key: 'd1' }, { label: '置顶', key: MenuEnum.TOP, icon: renderIcon(UpToTopIcon), - fnHandle: chartEditStore.setTop, + fnHandle: chartEditStore.setTop }, { label: '置底', key: MenuEnum.BOTTOM, icon: renderIcon(DownToBottomIcon), - fnHandle: chartEditStore.setBottom, + fnHandle: chartEditStore.setBottom }, { label: '上移一层', key: MenuEnum.UP, icon: renderIcon(ChevronUpIcon), - fnHandle: chartEditStore.setUp, + fnHandle: chartEditStore.setUp }, { label: '下移一层', key: MenuEnum.DOWN, icon: renderIcon(ChevronDownIcon), - fnHandle: chartEditStore.setDown, + fnHandle: chartEditStore.setDown }, { type: 'divider', - key: 'd2', + key: 'd2' }, { label: '清空剪贴板', key: MenuEnum.CLEAR, icon: renderIcon(PaintBrushIcon), - fnHandle: chartEditStore.setRecordChart, + fnHandle: chartEditStore.setRecordChart }, { label: '删除', key: MenuEnum.DELETE, icon: renderIcon(TrashIcon), - fnHandle: chartEditStore.removeComponentList, - }, + fnHandle: chartEditStore.removeComponentList + } +] + +// * 默认多选组件选项 +const defaultMultiSelectionOptions: MenuOptionsItemType[] = [ + { + label: '成组', + key: MenuEnum.COPY, + icon: renderIcon(Carbon3DSoftwareIcon), + fnHandle: chartEditStore.setCopy + } ] // * 无数据传递拥有的选项 @@ -126,7 +129,7 @@ const handleContextMenu = ( // 隐藏选项列表 hideOptionsList?: MenuEnum[], // 挑选选项列表 - pickOptionsList?: MenuEnum[], + pickOptionsList?: MenuEnum[] ) => { e.stopPropagation() e.preventDefault() @@ -136,8 +139,13 @@ const handleContextMenu = ( } chartEditStore.setRightMenuShow(false) - // * 设置默认选项 - menuOptions.value = defaultOptions + // * 多选默认选项 + if (chartEditStore.getTargetChart.selectId.length > 1) { + menuOptions.value = defaultMultiSelectionOptions + } else { + // * 单选默认选项 + menuOptions.value = defaultOptions + } if (!item) { menuOptions.value = pickOption(menuOptions.value, defaultNoItemKeys) @@ -163,7 +171,6 @@ const handleContextMenu = ( * @returns */ export const useContextMenu = () => { - // 设置默认项 menuOptions.value = defaultOptions @@ -175,9 +182,7 @@ export const useContextMenu = () => { // * 事件处理 const handleMenuSelect = (key: string) => { chartEditStore.setRightMenuShow(false) - const targetItem: MenuOptionsItemType[] = menuOptions.value.filter( - (e: MenuOptionsItemType) => e.key === key - ) + const targetItem: MenuOptionsItemType[] = menuOptions.value.filter((e: MenuOptionsItemType) => e.key === key) menuOptions.value.forEach((e: MenuOptionsItemType) => { if (e.key === key) { @@ -189,12 +194,12 @@ export const useContextMenu = () => { } }) } - + return { menuOptions, handleContextMenu, onClickOutSide, handleMenuSelect, - mousePosition: chartEditStore.getMousePosition, + mousePosition: chartEditStore.getMousePosition } } diff --git a/src/views/chart/hooks/useKeyboard.hook.ts b/src/views/chart/hooks/useKeyboard.hook.ts index 58ab28c4..4429eb0e 100644 --- a/src/views/chart/hooks/useKeyboard.hook.ts +++ b/src/views/chart/hooks/useKeyboard.hook.ts @@ -77,14 +77,14 @@ const macKeyList: Array = [ // 处理键盘记录 const keyRecordHandle = () => { - document.onkeydown = throttle((e: KeyboardEvent) => { + document.onkeydown = (e: KeyboardEvent) => { if(window.$KeyboardActive) window.$KeyboardActive.add(e.key.toLocaleLowerCase()) - else window.$KeyboardActive = new Set([e.key]) - }, 200) + else window.$KeyboardActive = new Set([e.key.toLocaleLowerCase()]) + } - document.onkeyup = throttle((e: KeyboardEvent) => { + document.onkeyup = (e: KeyboardEvent) => { if(window.$KeyboardActive) window.$KeyboardActive.delete(e.key.toLocaleLowerCase()) - }, 200) + } } // 初始化监听事件