diff --git a/packages/layout-design/src/design-index-view/components/design-left-menu/design-left-menu.controller.ts b/packages/layout-design/src/design-index-view/components/design-left-menu/design-left-menu.controller.ts index 39564cd246a7c28a814ecc74b10d27294e7af663..e5470b7871788c7dc5f9e25b19500c1ceaf2cfe7 100644 --- a/packages/layout-design/src/design-index-view/components/design-left-menu/design-left-menu.controller.ts +++ b/packages/layout-design/src/design-index-view/components/design-left-menu/design-left-menu.controller.ts @@ -151,10 +151,9 @@ export class DesignLeftMenuController extends PanelItemController { await super.onInit(); - await this.initAppMenuItem(); - this.app = await ibiz.hub.getApp(this.panel.view.context.srfappid); this.state.menuItemsState = {}; - this.state.allMenuItems.forEach(item => this.initMenuItemState(item)); + this.app = await ibiz.hub.getApp(this.panel.view.context.srfappid); + await this.initAppMenuItem(); } /** @@ -222,6 +221,8 @@ export class DesignLeftMenuController extends PanelItemController this.initMenuItemState(item)); + this.calcMenuItemState(configs); const firstMenuItem = this.state.allMenuItems[0]; // 先拿缓存 this.menuCache = this.getMenuCache() ?? this.menuCache; @@ -235,6 +236,30 @@ export class DesignLeftMenuController extends PanelItemController { + if (this.state.menuItemsState[config.key]) + this.state.menuItemsState[config.key].visible = + config.visible !== false; + }); + } + /** * 菜单项排序 * @@ -250,6 +275,7 @@ export class DesignLeftMenuController extends PanelItemController { const res = await this.config!.load(); @@ -328,34 +355,34 @@ export class DesignLeftMenuController extends PanelItemController} * @memberof DesignLeftMenuController */ - async saveCustomModelData( - model: { - key: string; - index: number; - }[], - ): Promise< + async saveCustomModelData(): Promise< { key: string; index: number; + visible: boolean; }[] > { + const configs = this.state.allMenuItems.map((menu, index) => { + return { + index, + key: menu.id!, + visible: this.state.menuItemsState[menu.id!].visible, + }; + }); const data: IData = { - model: JSON.stringify(model), + model: JSON.stringify(configs), }; await this.config!.save(data); - return model; + return configs; } /** diff --git a/packages/layout-design/src/design-index-view/components/design-left-menu/design-left-menu.state.ts b/packages/layout-design/src/design-index-view/components/design-left-menu/design-left-menu.state.ts index 9dac5a8bf2126e4b94ea489028c7112919b21ae6..5a7f94dc1a94333f8927783c56b62159d025aa6f 100644 --- a/packages/layout-design/src/design-index-view/components/design-left-menu/design-left-menu.state.ts +++ b/packages/layout-design/src/design-index-view/components/design-left-menu/design-left-menu.state.ts @@ -41,8 +41,12 @@ export class DesignLeftMenuState extends PanelItemState { /** * 菜单项状态 * - * @type {IData} + * @type {{ + * [p: string]: { visible: boolean; permitted: boolean; }; + * }} { visible:是否显示; permitted:是否有权限 } * @memberof DesignLeftMenuState */ - menuItemsState: IData = {}; + menuItemsState: { + [p: string]: { visible: boolean; permitted: boolean }; + } = {}; } diff --git a/packages/layout-design/src/design-index-view/components/design-left-menu/design-left-menu.tsx b/packages/layout-design/src/design-index-view/components/design-left-menu/design-left-menu.tsx index 019d77f0175c9786addec092ef4daa4fc6628d6d..cd0ed1e350975b595c628eb1fa8d75ef0e9c5dd6 100644 --- a/packages/layout-design/src/design-index-view/components/design-left-menu/design-left-menu.tsx +++ b/packages/layout-design/src/design-index-view/components/design-left-menu/design-left-menu.tsx @@ -4,11 +4,13 @@ import { h, ref, PropType, + computed, onMounted, onUnmounted, defineComponent, resolveComponent, } from 'vue'; +import { MenuItem } from '@imengyu/vue3-context-menu'; import { useNamespace } from '@ibiz-template/vue3-util'; import { AppCounter } from '@ibiz-template/runtime'; import { IAppMenuItem, IPanelRawItem } from '@ibiz/model-core'; @@ -49,12 +51,27 @@ export default defineComponent({ counterData.value = data; }; + const spanModeIndex = computed(() => { + return c.state.allMenuItems.findIndex( + menuItem => menuItem.itemType === 'SEPERATOR' && menuItem.spanMode, + ); + }); + + let ContextMenu: IData; + onMounted(() => { // 计数器相关 counter = c.getCounter(); if (counter) { counter.onChange(fn); } + const importMenu = () => import('@imengyu/vue3-context-menu'); + importMenu().then(value => { + ContextMenu = value.default; + if (ContextMenu.default && !ContextMenu.showContextMenu) { + ContextMenu = ContextMenu.default; + } + }); c.view.call('LeftMenuToggleCollapse', { resetWidth: !c.state.collapse, collapse: c.state.collapse, @@ -68,12 +85,37 @@ export default defineComponent({ }); // 处理点击事件 - const handleClick = (menuItem: IAppMenuItem, event: MouseEvent) => { + const handleClick = (menuItem: IAppMenuItem, event?: MouseEvent) => { if (menuItem.itemType === 'MENUITEM') { c.handleMenuItemClick(menuItem, event); } }; + /** + * 处理上下文菜单点击 + * + * @param {IAppMenuItem} menuItem + */ + const handleContextmenuClick = ( + menuItem: IAppMenuItem, + event?: MouseEvent, + ) => { + c.state.menuItemsState[menuItem.id!].visible = + !c.state.menuItemsState[menuItem.id!].visible; + let activeItem: IAppMenuItem | undefined; + // 如果切换到显示状态则激活 + if (c.state.menuItemsState[menuItem.id!].visible) { + activeItem = menuItem; + } else if (c.state.activeMenuItem?.id === menuItem.id) { + // 如果隐藏了激活项则默认选中第一项 + activeItem = c.state.allMenuItems.find( + item => c.state.menuItemsState[item.id!].visible, + ); + } + if (activeItem) handleClick(activeItem, event); + c.saveCustomModelData(); + }; + // 处理菜单拖拽 const handleMenuDrag = (evt: MouseEvent) => { evt.preventDefault(); @@ -133,14 +175,74 @@ export default defineComponent({ if (evt.moved) { const movetItem = c.state.allMenuItems.splice(evt.moved.oldIndex, 1)[0]; c.state.allMenuItems.splice(evt.moved.newIndex, 0, movetItem); - const configs = c.state.allMenuItems.map((menu, index) => { - return { - key: menu.id!, - index, - }; + c.saveCustomModelData(); + } + }; + + /** + * 计算上下文菜单项 + * + * @param {IAppMenuItem} [menuItem] + * @return {*} {MenuItem[]} + */ + const calcContextMenuItems = (menuItem?: IAppMenuItem): MenuItem[] => { + const menuItems: MenuItem[] = []; + if (menuItem) { + menuItems.push({ + clickClose: true, + label: `隐藏"${menuItem.caption}"`, + onClick: event => + handleContextmenuClick(menuItem, event as MouseEvent), }); - c.saveCustomModelData(configs); + menuItems.push({ divided: 'self' }); } + c.state.allMenuItems.forEach(item => { + if (item.itemType === 'SEPERATOR') { + const lastMenuItem = menuItems[menuItems.length - 1]; + // 不能连续两项出现分割线 + if (!lastMenuItem.divided) menuItems.push({ divided: 'self' }); + } else if (item.itemType === 'MENUITEM' && !item.disableClose) { + menuItems.push({ + clickClose: true, + label: item.caption!, + hidden: c.state.menuItemsState[item.id!].permitted === false, + icon: + c.state.menuItemsState[item.id!].visible && + (() as any), + onClick: event => handleContextmenuClick(item, event as MouseEvent), + }); + } + }); + // 去除最后一项是分割线的情况 + const lastMenuItem = menuItems[menuItems.length - 1]; + if (lastMenuItem.divided) menuItems.pop(); + return menuItems; + }; + + /** + * 处理右键菜单 + * + * @param {IAppMenuItem} menuItem + * @param {MouseEvent} event + * @return {*} {void} + */ + const handleContextmenu = ( + evt: MouseEvent, + menuItem?: IAppMenuItem, + ): void => { + // 阻止原生浏览器右键菜单打开 + evt.preventDefault(); + evt.stopPropagation(); + const menuItems = calcContextMenuItems(menuItem); + if (!menuItems.length) return; + ContextMenu.showContextMenu({ + x: evt.x, + y: evt.y, + minWidth: 160, + items: menuItems, + theme: 'default dark', + customClass: ns.e('context-menu'), + }); }; /** @@ -182,6 +284,7 @@ export default defineComponent({ ]} title={menuItem.tooltip} onClick={(e: MouseEvent) => handleClick(menuItem, e)} + onContextmenu={evt => handleContextmenu(evt, menuItem)} > {provider && provider.renderText ? provider.renderText(menuItem, c as any) @@ -202,9 +305,6 @@ export default defineComponent({ * @return {*} */ const renderMenuItems = () => { - const spanModeIndex = c.state.allMenuItems.findIndex( - menuItem => menuItem.itemType === 'SEPERATOR' && menuItem.spanMode, - ); return (
{ - if (!spanModeIndex || index < spanModeIndex) { + if (!spanModeIndex.value || index < spanModeIndex.value) { return renderMenuItem(element); } }, }} - {spanModeIndex && [ + {spanModeIndex.value && [
handleContextmenu(evt)} >
, { - if (index > spanModeIndex) { + if (index > spanModeIndex.value) { return renderMenuItem(element); } },