From fe36d51bb94d2ca130ca117660cb624cebe75884 Mon Sep 17 00:00:00 2001 From: lijisanxiong <1518062161@qq.com> Date: Tue, 29 Jul 2025 20:11:54 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat=EF=BC=9A=E6=96=B0=E5=A2=9E=E6=89=A9?= =?UTF-8?q?=E5=B1=95=E8=8F=9C=E5=8D=95=E5=B8=B8=E8=A7=84=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + .../common-extend-menu/common-extend-menu.tsx | 24 +- .../extend-menu-base/extend-menu-base.util.ts | 64 ++- .../extend-standard-menu.scss | 447 ++++++++++++++++++ .../extend-standard-menu.tsx | 321 +++++++++++++ 5 files changed, 845 insertions(+), 12 deletions(-) create mode 100644 src/panel-component/app-extend-menu/extend-menu-base/extend-standard-menu/extend-standard-menu.scss create mode 100644 src/panel-component/app-extend-menu/extend-menu-base/extend-standard-menu/extend-standard-menu.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d5d4caf4..d5a40f68d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - 泳道看板新增分组隐藏功能 - 看板识别enableFullScreen(启用全屏)和 enableGroupHidden(启用分组隐藏)参数 - 新增卡片新建功能 +- 新增扩展菜单常规模式 ### Fixed diff --git a/src/panel-component/app-extend-menu/extend-menu-base/common-extend-menu/common-extend-menu.tsx b/src/panel-component/app-extend-menu/extend-menu-base/common-extend-menu/common-extend-menu.tsx index e9c0e9d1c..9c05c2bfe 100644 --- a/src/panel-component/app-extend-menu/extend-menu-base/common-extend-menu/common-extend-menu.tsx +++ b/src/panel-component/app-extend-menu/extend-menu-base/common-extend-menu/common-extend-menu.tsx @@ -6,6 +6,7 @@ import { IAppMenuItemProvider } from '@ibiz-template/runtime'; import { useNamespace } from '@ibiz-template/vue3-util'; import './common-extend-menu.scss'; import { ExtendButtonMenu } from '../extend-button-menu/extend-button-menu'; +import { ExtendStandardMenu } from '../extend-standard-menu/extend-standard-menu'; /** * 扩展基础菜单 @@ -17,6 +18,7 @@ export const CommonExtendMenu = defineComponent({ name: 'IBizCommonExtendMenu', components: { ExtendButtonMenu, + ExtendStandardMenu, }, props: { /** @@ -94,9 +96,20 @@ export const CommonExtendMenu = defineComponent({ return { ns, handleMenuItemClick }; }, render() { - return ( -
- + ); + if (this.renderMode?.toLocaleUpperCase() === 'MENU') { + content = ( + -
- ); + ); + } + return
{content}
; }, }); diff --git a/src/panel-component/app-extend-menu/extend-menu-base/extend-menu-base.util.ts b/src/panel-component/app-extend-menu/extend-menu-base/extend-menu-base.util.ts index 7c8d02ec6..cf7207ac9 100644 --- a/src/panel-component/app-extend-menu/extend-menu-base/extend-menu-base.util.ts +++ b/src/panel-component/app-extend-menu/extend-menu-base/extend-menu-base.util.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ /* eslint-disable no-use-before-define */ /* eslint-disable no-restricted-syntax */ /* eslint-disable no-plusplus */ @@ -38,10 +39,10 @@ export interface IExtendMenuProps { * @description 菜单项绘制数据 * * @export - * @interface IButtonMenuItem + * @interface IMenuBaseItem * @extends {IAppMenuItem} */ -export interface IButtonMenuItem extends IAppMenuItem { +export interface IMenuBaseItem extends IAppMenuItem { /** 数据层级 */ level: number; /** 主键 */ @@ -51,9 +52,18 @@ export interface IButtonMenuItem extends IAppMenuItem { /** 父主键 */ parentId?: string; /** 子项数据集合 */ - children?: IButtonMenuItem[]; + children?: IMenuBaseItem[]; } +/** + * @description 按钮菜单项绘制数据 + * + * @export + * @interface IButtonMenuItem + * @extends {IAppMenuItem} + */ +export interface IButtonMenuItem extends IMenuBaseItem {} + /** * @description 菜单绘制通用参数 * @@ -78,7 +88,7 @@ export interface IMenuRenderParams { */ export interface IMenuItemParams extends IMenuRenderParams { /** 菜单项 */ - menu: IButtonMenuItem; + menu: IMenuBaseItem; } /** @@ -107,6 +117,46 @@ export interface IMenuContentParams extends IMenuRenderParams { handleMenuItemMouseLeave: (_menu: IButtonMenuItem, event: MouseEvent) => void; } +/** + * @description 绘制常规菜单项参数 + * + * @export + * @interface IStandardMenuItemParams + * @extends {IMenuRenderParams} + */ +export interface IStandardMenuItemParams extends IMenuRenderParams { + /** 菜单项 */ + menu: IMenuBaseItem; + /** 是否为首次绘制 */ + isFirst: boolean; + /** 是否水平折叠收起菜单 */ + collapse: boolean; +} + +/** + * @description 绘制常规菜单的参数 + * + * @export + * @interface IStandardMenuContentParams + * @extends {IMenuContentParams} + */ +export interface IStandardMenuContentParams extends IMenuRenderParams { + /** 是否支持布局 */ + isLayout: boolean; + /** 菜单方向 */ + position: string; + /** 是否水平折叠收起菜单(仅在 menuAlign 为 vertical 时可用) */ + collapse: boolean; + /** 刷新key */ + refreshKey: string; + /** 菜单项绘制数据集合 */ + menus: IMenuBaseItem[]; + /** 菜单布局模型 */ + menuLayout?: ILayout; + /** 菜单选中 */ + handleMenuSelect: (_id: string, event: MouseEvent) => void; +} + /** * @description 用于构建级联菜单的弹出层控制逻辑 * @@ -611,15 +661,15 @@ export function useBorderLayout( * 递归生成菜单数据,递给 element 的 Menu 组件 * * @param {AppMenuItemModel[]} items - * @return {*} {IButtonMenuItem[]} + * @return {*} {IMenuBaseItem[]} */ export function getMenus( items: IAppMenuItem[], _parentItem?: IAppMenuItem, level = 0, -): IButtonMenuItem[] { +): IMenuBaseItem[] { return items.map(item => { - const data: IButtonMenuItem = { + const data: IMenuBaseItem = { ...item, value: item.id, label: item.caption, diff --git a/src/panel-component/app-extend-menu/extend-menu-base/extend-standard-menu/extend-standard-menu.scss b/src/panel-component/app-extend-menu/extend-menu-base/extend-standard-menu/extend-standard-menu.scss new file mode 100644 index 000000000..2afd3f5a8 --- /dev/null +++ b/src/panel-component/app-extend-menu/extend-menu-base/extend-standard-menu/extend-standard-menu.scss @@ -0,0 +1,447 @@ +/* extend-standard-menu 菜单 start */ +$extend-standard-menu: ( + 'collapse-item-hover-color': getCssVar(color, primary, hover, text), + 'collapse-item-padding': getCssVar(spacing, base), + 'icon-width': getCssVar(width-icon, large), + 'icon-height': getCssVar(width-icon, large), + 'icon-margin': 0 getCssVar(spacing, tight) 0 0, + 'text-margin': 0 0 0 getCssVar(spacing, tight), + 'icon-position': calc(getCssVar(spacing, base) - 2px), + 'icon-size': getCssVar(width-icon,medium), + 'collapse-height': getCssVar(spacing, super-loose), + 'scorll-color': getCssVar(color, scroll, menu), + 'horizontal-height': 50px, + 'hover-color': getCssVar(color, primary, hover, text), + 'hover-bg-color': getCssVar(color, primary, hover), + 'active-color': getCssVar(color, primary, active, text), + 'active-bg-color': getCssVar(color, primary, active), + 'color': getCssVar(color, text, menu), +); + +$extend-standard-menu-item: ( + 'selected-border-color': transparent, + 'selected-color': getCssVar(color, primary, active, text), + 'selected-bg-color': getCssVar(color, primary, active), + 'hover-color': getCssVar(color, primary, hover, text), + 'hover-bg-color': getCssVar(color, primary, hover), + 'padding': getCssVar(spacing, tight), + 'border-radius': getCssVar(border-radius, small), + 'border': 2px solid getCssVar(color, text, 1), + 'font-size': getCssVar(font-size, header-6), + 'height': 42px, + 'gap': 2px, + 'color': getCssVar(color, text, menu), + 'separator-color': getCssVar(color, scroll, menu), + 'horizontal-selected-color': getCssVar(color, primary, active, text), + 'horizontal-selected-bg-color': getCssVar(color, primary, active), + 'horizontal-hover-color': getCssVar(color, primary, hover, text), + 'horizontal-hover-bg-color': getCssVar(color, primary, hover), + 'horizontal-color': getCssVar(color, text, menu), + 'horizontal-font-size': getCssVar(font-size, header-5), + 'horizontal-height': 100%, + 'horizontal-border-radius': 0, + 'horizontal-padding': 0 getCssVar(spacing, base-loose), + 'horizontal-padding-right': calc(getCssVar(width-icon, large) + getCssVar(spacing, base-loose)), + 'horizontal-popup-height': 40px, +); + +/* extend-standard-menu 菜单 end */ + +// 垂直菜单项样式 +@mixin menu-item-vertical-style { + @include flex(row, flex-start, center); + + --el-menu-base-level-padding: #{getCssVar('extend-standard-menu-item', 'padding')}; + + width: 100%; + height: getCssVar('extend-standard-menu-item', 'height'); + font-size: getCssVar('extend-standard-menu-item', 'font-size'); + color: getCssVar('extend-standard-menu-item', 'color'); + white-space: nowrap; + border-radius: getCssVar('extend-standard-menu-item', 'border-radius'); + + &.is-active { + color: getCssVar('extend-standard-menu-item', 'selected-color') !important; + background-color: getCssVar('extend-standard-menu-item', 'selected-bg-color'); + } + + &:hover { + color: getCssVar('extend-standard-menu-item', 'hover-color'); + background-color: getCssVar('extend-standard-menu-item', 'hover-bg-color'); + } +} + +// 水平菜单项样式 +@mixin menu-item-horizontal-style { + height: getCssVar('extend-standard-menu-item', 'horizontal-height'); + padding: getCssVar('extend-standard-menu-item', 'horizontal-padding'); + font-size: getCssVar('extend-standard-menu-item', 'horizontal-font-size'); + line-height: getCssVar('extend-standard-menu-item', 'horizontal-height'); + color: getCssVar('extend-standard-menu-item', 'horizontal-color'); + white-space: nowrap; + border-width: 0; + border-radius: getCssVar('extend-standard-menu-item', 'horizontal-border-radius'); + + &:hover { + color: getCssVar('extend-standard-menu-item', 'horizontal-hover-color'); + background-color: getCssVar( + 'extend-standard-menu-item', + 'horizontal-hover-bg-color' + ); + } + + &.is-active { + // element-plus 给了 !important,所以这里也要加上 + color: getCssVar('extend-standard-menu-item', 'selected-color') !important; + background-color: getCssVar('extend-standard-menu-item', 'horizontal-selected-bg-color'); + border-top: getCssVar('extend-standard-menu-item', 'border'); + } +} + +// 收缩菜单项样式 +@mixin menu-collapse-item-style { + @include flex-center; + + padding: getCssVar(spacing, none); + margin-bottom: getCssVar(spacing, tight); + +} + +// 菜单项选中样式 +@mixin menu-item-selected-style { + color: getCssVar('extend-standard-menu-item', 'selected-color'); + background-color: getCssVar('extend-standard-menu-item', 'selected-bg-color'); +} + +// 直接内容菜单项样式 +@mixin raw-item-menu-style { + --el-menu-hover-text-color: #{getCssVar('extend-standard-menu-item', 'color')}; + --el-menu-hover-bg-color: transparent; + @include set-component-css-var('extend-standard-menu-item', $extend-standard-menu-item); + .#{bem('extend-standard-menu', 'rawitem')} { + min-height: getCssVar('extend-standard-menu-item', 'height'); + line-height: getCssVar('extend-standard-menu-item', 'height'); + .#{bem('rawitem')} { + @include flex(row, flex-start, center); + + font-size: getCssVar('extend-standard-menu-item', 'font-size'); + color: getCssVar('extend-standard-menu-item', 'color'); + + &>.#{bem('icon')} { + display: flex; + align-items: center; + width: getCssVar('extend-standard-menu', 'icon-width'); + height: getCssVar('extend-standard-menu', 'icon-height'); + font-size: getCssVar('extend-standard-menu','icon-size'); + } + + * { + @include utils-ellipsis; + } + } + } +} + +@include b(extend-standard-menu) { + width: 100%; + height: 100%; + + @include e('content') { + position: relative; + width: 100%; + height: 100%; + background-color: getCssVar(color, primary); + + @include set-component-css-var('extend-standard-menu', $extend-standard-menu); + @include set-component-css-var('extend-standard-menu-item', $extend-standard-menu-item); + + &>.el-menu { + height: 100%; + padding: getCssVar(spacing, none) getCssVar(spacing, base); + overflow: hidden auto; + border-right: 0; + @include raw-item-menu-style; + } + + .el-sub-menu { + .el-sub-menu__icon-arrow { + right: 10px; + width: getCssVar('extend-standard-menu', 'icon-width'); + height: getCssVar('extend-standard-menu', 'icon-height'); + margin-top: -10px; + font-size: getCssVar('extend-standard-menu', 'icon-width'); + } + + .el-menu-item { + padding: calc(getCssVar('extend-standard-menu', 'item-padding') * 0.875) + calc(getCssVar('extend-standard-menu', 'item-padding') * 2.5); + } + + .el-sub-menu__title >.#{bem('extend-standard-menu', 'counter')} { + right: 30px; + } + } + + // 垂直 + .el-menu--vertical { + width: 100%; + + // 菜单项样式 + .el-menu-item, + .el-sub-menu__title { + @include menu-item-vertical-style; + } + + .el-menu-item + .el-menu-item { + margin-top: getCssVar('extend-standard-menu-item', 'gap'); + } + + .el-sub-menu + .el-menu-item { + margin-top: getCssVar('extend-standard-menu-item', 'gap'); + } + + .el-sub-menu + .el-sub-menu { + margin-top: getCssVar('extend-standard-menu-item', 'gap'); + } + + .el-menu-item + .el-sub-menu { + margin-top: getCssVar('extend-standard-menu-item', 'gap'); + } + + .el-sub-menu__title + .el-menu { + margin-top: getCssVar('extend-standard-menu-item', 'gap'); + } + + &::-webkit-scrollbar-thumb { + background-color: getCssVar('extend-standard-menu', 'scorll-color'); + } + } + + // 水平 + .el-menu--horizontal { + align-items: center; + height: 100%; + padding: getCssVar(spacing, none) getCssVar(spacing, base-tight); + + // 水平菜单默认为ellipsis模式,无需滚动条 + overflow-x: hidden; + border-bottom: none; + + // 菜单项样式 + .el-menu-item, + .el-sub-menu__title { + @include menu-item-horizontal-style; + + .el-sub-menu__icon-arrow { + right: getCssVar('extend-standard-menu', 'icon-position'); + } + } + + .el-sub-menu { + height: getCssVar('extend-standard-menu-item', 'horizontal-height'); + + >.el-sub-menu__title { + padding-right: getCssVar('extend-standard-menu-item', 'horizontal-padding-right'); + } + } + + // 副菜单悬浮 + > .el-sub-menu:hover .el-sub-menu__title { + color: getCssVar('extend-standard-menu-item', 'horizontal-hover-color'); + background-color: getCssVar( + 'extend-standard-menu-item', + 'horizontal-hover-bg-color' + ); + } + + // 副菜单激活 + > .el-sub-menu.is-active .el-sub-menu__title { + color: getCssVar('extend-standard-menu-item', 'horizontal-selected-color'); + background-color: getCssVar('extend-standard-menu-item', 'horizontal-selected-bg-color'); + border-top: getCssVar('extend-standard-menu-item', 'border'); + border-bottom: 0; + } + + &::-webkit-scrollbar-thumb { + background-color: getCssVar('extend-standard-menu', 'scorll-color'); + } + + .el-divider--vertical { + border-color: getCssVar(extend-standard-menu-item, separator-color); + } + } + @include when('horizontal') { + #{getCssVarName(color, text, 0)}: getCssVar(color, primary, text); + #{getCssVarName(color, text, 1)}: getCssVar(color, primary, text); + #{getCssVarName(color, text, 2)}: getCssVar(color, primary, text); + #{getCssVarName(color, text, 3)}: getCssVar(color, primary, text); + width: 100%; + height: getCssVar('extend-standard-menu', 'horizontal-height'); + } + + @include when('bottom') { + .el-menu--horizontal { + .el-menu-item.is-active, + .el-sub-menu.is-active .el-sub-menu__title { + border-top: 0; + border-bottom: getCssVar('extend-standard-menu-item', 'border'); + } + } + } + + @include when('collapse') { + &>.el-menu { + padding: getCssVar(spacing, tight); + } + } + + .el-menu { + #{getCssVarName(color, primary, hover)}: getCssVar(extend-standard-menu, active, bg, color); + #{getCssVarName(color, primary, hover, text)}: getCssVar(extend-standard-menu, active, color); + #{getCssVarName(color, primary, active)}: getCssVar(extend-standard-menu, hover, bg, color); + #{getCssVarName(color, primary, active, text)}: getCssVar(extend-standard-menu, hover, color); + + --el-button-text-color: #{getCssVar(extend-standard-menu, color)}; + } + } + + // 图标样式 + @include e(icon) { + @include flex(row, center, center); + + width: getCssVar('extend-standard-menu', 'icon-width'); + height: getCssVar('extend-standard-menu', 'icon-height'); + font-size: getCssVar('extend-standard-menu','icon-size'); + + &+.#{bem('extend-standard-menu__caption')} { + margin: getCssVar('extend-standard-menu', 'text-margin'); + } + } + + // 文字样式 + @include e('caption') { + @include utils-ellipsis; + } +} + +@include b(extend-standard-menu-popup-container){ + @include raw-item-menu-style; + + // 收缩时菜单项样式 + // 以及水平菜单悬浮出来菜单样式 + &.el-menu--popup-container { + @include set-component-css-var('extend-standard-menu', $extend-standard-menu); + @include set-component-css-var('extend-standard-menu-item', $extend-standard-menu-item); + + border: none; + box-shadow: getCssVar('shadow', 'elevated'); + + .el-menu--popup { + max-height: 100vh; + padding: getCssVar('spacing', 'extra-tight'); + overflow: auto; + background-color: getCssVar('color', 'primary'); + + .el-menu-item, + .el-sub-menu__title { + @include flex(row, flex-start, center); + + width: 100%; + height: getCssVar('extend-standard-menu-item', 'horizontal-popup-height'); + font-size: getCssVar('extend-standard-menu-item', 'font-size'); + color: getCssVar('extend-standard-menu-item', 'horizontal-color'); + white-space: nowrap; + border-radius: getCssVar('extend-standard-menu-item', 'horizontal-border-radius'); + + &.is-active { + color: getCssVar('extend-standard-menu-item', 'horizontal-selected-color'); + background-color: getCssVar('extend-standard-menu-item', 'horizontal-selected-bg-color'); + } + + &:hover { + color: getCssVar('extend-standard-menu-item', 'hover-color'); + background-color: getCssVar('extend-standard-menu-item', 'hover-bg-color'); + } + + .el-icon { + display: none; + } + } + } + } + + // 计数器样式 + .#{bem('extend-standard-menu', 'counter')} { + position: absolute; + top: 50%; + right: 5px; + height: 20px; + padding: getCssVar('spacing', 'none') getCssVar('spacing', 'tight'); + line-height: initial; + color: getCssVar('color', danger, text); + background-color: getCssVar('color', 'danger'); + border-radius: getCssVar('border','radius','medium'); + transform: translateY(-50%); + } + +} + +// 分割线 +.#{bem('extend-standard-menu', 'separator', 'horizontal')} { + margin: getCssVar(spacing, tight) 0; + border-color: getCssVar(extend-standard-menu-item, separator-color); +} + +.el-menu--horizontal.el-menu--popup-container:has(.#{bem(extend-standard-menu, item)},.#{bem(extend-standard-menu-submenu)}) { + + @include raw-item-menu-style; + + @include set-component-css-var('extend-standard-menu', $extend-standard-menu); + @include set-component-css-var('extend-standard-menu-item', $extend-standard-menu-item); + + border: none; + box-shadow: getCssVar('shadow', 'elevated'); + + .el-menu--popup { + padding: getCssVar('spacing', 'extra-tight'); + background-color: getCssVar('color', 'primary'); + + .el-menu-item, + .el-sub-menu__title { + @include flex(row, flex-start, center); + + width: 100%; + height: getCssVar('extend-standard-menu-item', 'horizontal-popup-height'); + font-size: getCssVar('extend-standard-menu-item', 'font-size'); + color: getCssVar('extend-standard-menu-item', 'horizontal-color'); + white-space: nowrap; + border-radius: getCssVar('extend-standard-menu-item', 'horizontal-border-radius'); + + &.is-active { + color: getCssVar('extend-standard-menu-item', 'horizontal-selected-color'); + background-color: getCssVar('extend-standard-menu-item', 'horizontal-selected-bg-color'); + } + + &:hover { + color: getCssVar('extend-standard-menu-item', 'hover-color'); + background-color: getCssVar('extend-standard-menu-item', 'hover-bg-color'); + } + + .el-icon { + display: none; + } + } + } + + .el-menu .el-sub-menu.is-active>.el-sub-menu__title { + color: getCssVar('extend-standard-menu-item', 'horizontal-selected-color'); + } + + // 分割线 + .#{bem('extend-standard-menu', 'separator', 'vertical')} { + width: 100%; + height: 1px; + margin: getCssVar(spacing, tight) 0; + border-top: 1px getCssVar(extend-standard-menu-item, separator-color) solid; + } +} \ No newline at end of file diff --git a/src/panel-component/app-extend-menu/extend-menu-base/extend-standard-menu/extend-standard-menu.tsx b/src/panel-component/app-extend-menu/extend-menu-base/extend-standard-menu/extend-standard-menu.tsx new file mode 100644 index 000000000..4e2dfc078 --- /dev/null +++ b/src/panel-component/app-extend-menu/extend-menu-base/extend-standard-menu/extend-standard-menu.tsx @@ -0,0 +1,321 @@ +import { ref, VNode, PropType, defineComponent, computed } from 'vue'; +import { IAppMenuItem, ILayout } from '@ibiz/model-core'; +import { showTitle } from '@ibiz-template/core'; +import { useNamespace } from '@ibiz-template/vue3-util'; +import { IAppMenuItemProvider } from '@ibiz-template/runtime'; +import { createUUID } from 'qx-util'; +import { + findMenuItem, + getMenus, + IStandardMenuContentParams, + IStandardMenuItemParams, + IMenuBaseItem, +} from '../extend-menu-base.util'; +import './extend-standard-menu.scss'; + +const ellipsisSvg = (): VNode => { + return ; +}; + +/** + * 绘制菜单项 + * @param {IStandardMenuItemParams} _params + * @returns {*} + */ +function renderMenuItem(_params: IStandardMenuItemParams): VNode | undefined { + const { isFirst, menu, collapse, ns, menuAlign, menuItemsState } = _params; + // TODO 是否显示状态值待定 + if (!menu.id || (menuItemsState && !menuItemsState[menu.id]?.visible)) { + return; + } + + if (menu.itemType === 'MENUITEM') { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let content: any; + if (!(isFirst && collapse)) { + content = [ + menu.sysImage ? ( + + ) : null, + {menu.caption}, + ]; + } else { + content = [ + menu.sysImage ? ( + + ) : ( + {menu.caption?.slice(0, 1)} + ), + ]; + } + + return !(isFirst && collapse) ? ( + + {content} + + ) : ( + + + {content} + + + ); + } + if (menu.itemType === 'SEPERATOR') { + const direction = + menuAlign === 'horizontal' && isFirst ? 'vertical' : 'horizontal'; + return ( + + ); + } + if (menu.itemType === 'RAWITEM') { + return ( + + + + ); + } +} + +/** + * 绘制子菜单 + * @param {IStandardMenuItemParams} _params + * @returns {*} + */ +function renderSubmenu(_params: IStandardMenuItemParams): VNode | undefined { + const { isFirst, menu, collapse, ns, menuAlign, menuItemsState } = _params; + if (!menu.id || (menuItemsState && !menuItemsState[menu.id]?.visible)) { + return; + } + return ( + + {{ + default: () => + menu.children && + menu.children.map((item: IMenuBaseItem) => { + if (item.children && item.children) { + return renderSubmenu({ + isFirst: false, + menu: item, + collapse, + ns, + menuAlign, + menuItemsState, + }); + } + return renderMenuItem({ + isFirst: false, + menu: item, + collapse, + ns, + menuAlign, + menuItemsState, + }); + }), + title: () => { + if (collapse) { + if (menu.sysImage) { + return ( + + ); + } + return [ + isFirst ? menu.caption?.slice(0, 1) : menu.caption, + isFirst ? null : , + ]; + } + return [ + , + {menu.caption}, + ]; + }, + }} + + ); +} + +/** + * 绘制菜单内容 + * + * @param {IStandardMenuContentParams} _params + * @return {*} + */ +function renderMenuContent(_params: IStandardMenuContentParams): VNode { + const { + refreshKey, + ns, + collapse, + menuAlign, + position, + menus, + menuItemsState, + handleMenuSelect, + } = _params; + + return ( +
+ ellipsisSvg()} + ellipsis={menuAlign === 'horizontal'} + > + {{ + default: () => { + return menus.map(item => { + if (item.children && item.children.length > 0) { + return renderSubmenu({ + isFirst: true, + menu: item, + collapse, + ns, + menuAlign, + menuItemsState, + }); + } + return renderMenuItem({ + isFirst: true, + menu: item, + collapse, + ns, + menuAlign, + menuItemsState, + }); + }); + }, + }} + +
+ ); +} + +export const ExtendStandardMenu = defineComponent({ + name: 'IBizExtendStandardMenu', + props: { + items: { type: Object as PropType, required: true }, + menuItemsState: { + type: Object as PropType<{ + [p: string]: { visible: boolean; permitted: boolean }; + }>, + required: true, + }, + providers: { + type: Object as PropType<{ [key: string]: IAppMenuItemProvider }>, + required: true, + }, + position: { + type: String as PropType<'LEFT' | 'RIGHT' | 'TOP' | 'BOTTOM'>, + required: true, + }, + layoutMode: { + type: String as PropType< + 'TABLE' | 'TABLE_12COL' | 'TABLE_24COL' | 'FLEX' | 'BORDER' | 'ABSOLUTE' + >, + required: true, + }, + layout: { type: Object as PropType }, + }, + emits: { + /** + * @description 项点击事件 + */ + menuItemClick: (_item: IAppMenuItem, _event: MouseEvent) => true, + }, + setup(props, { emit }) { + const ns = useNamespace('extend-standard-menu'); + const defaultMenuRef = ref(); + const refreshKey = ref(createUUID()); + const collapse = ref(false); + const menuAlign = computed(() => + ['TOP', 'BOTTOM'].includes(props.position) ? 'horizontal' : 'vertical', + ); + const menus = ref(getMenus(props.items)); + const handleMenuSelect = async ( + _id: string, + _event: MouseEvent, + ): Promise => { + const menuItem = findMenuItem(_id, props.items); + if (!menuItem?.appFuncId) { + ibiz.log.warn( + ibiz.i18n.t('runtime.controller.control.menu.noConfigured'), + ); + return; + } + emit('menuItemClick', menuItem!, _event); + }; + + return { + ns, + defaultMenuRef, + refreshKey, + collapse, + menuAlign, + menus, + handleMenuSelect, + }; + }, + render() { + return ( +
+ {renderMenuContent({ + isLayout: false, + position: this.position, + refreshKey: this.refreshKey, + ns: this.ns, + collapse: this.collapse, + menuAlign: this.menuAlign, + menus: this.menus, + menuLayout: this.layout, + menuItemsState: this.menuItemsState, + handleMenuSelect: this.handleMenuSelect, + })} +
+ ); + }, +}); -- Gitee From 0146217d11d1189ee3664d92305a1def86d57583 Mon Sep 17 00:00:00 2001 From: lijisanxiong <1518062161@qq.com> Date: Tue, 29 Jul 2025 20:23:30 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat=EF=BC=9A=E6=96=B0=E5=A2=9E=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E9=A1=B9=E8=A1=8C=E4=B8=BA=E7=BB=98=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + src/control/list/list.scss | 25 +++++++++++++++++++++++++ src/control/list/list.tsx | 24 +++++++++++++++++++++++- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5a40f68d..6040a5cea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - 看板识别enableFullScreen(启用全屏)和 enableGroupHidden(启用分组隐藏)参数 - 新增卡片新建功能 - 新增扩展菜单常规模式 +- 新增列表项行为绘制 ### Fixed diff --git a/src/control/list/list.scss b/src/control/list/list.scss index 3c2940a35..78512708d 100644 --- a/src/control/list/list.scss +++ b/src/control/list/list.scss @@ -20,6 +20,31 @@ $control-list-group-style2: ( color: getCssVar(control-list, text-color); cursor: pointer; background-color: getCssVar(control-list, item-bg-color); + + @include e(right) { + padding: 0 getCssVar(spacing, extra,tight); + @include m(actions) { + display: flex; + justify-content: center; + .#{bem(action-toolbar, item)} { + margin: 0; + } + .#{bem(action-toolbar, item, label)} { + &.is-has-caption { + margin: 0; + } + } + } + } + + &:has(.#{bem(control-list-item__right)}) { + display: flex; + justify-content: space-between; + + .#{bem(control-list-item__caption)} { + flex: 1; + } + } } @include b(control-list) { diff --git a/src/control/list/list.tsx b/src/control/list/list.tsx index 0c5ce7505..571d8c979 100644 --- a/src/control/list/list.tsx +++ b/src/control/list/list.tsx @@ -216,8 +216,24 @@ export const ListControl = defineComponent({ ); }; + // 绘制项行为 + const renderItemAction = (item: IData): VNode => { + return ( + => c.onActionClick(detail, item, event)} + > + ); + }; + // 绘制默认列表项 const renderDefaultItem = (item: IData): VNode => { + const actionModel = c.getOptItemModel(); return (
=> c.onRowClick(item)} onDblclick={(): Promise => c.onDbRowClick(item)} > - {`${isNil(item.srfmajortext) ? '' : item.srfmajortext}`} + {`${ + isNil(item.srfmajortext) ? '' : item.srfmajortext + }`} + + {actionModel.length ? ( +
{renderItemAction(item)}
+ ) : null}
); }; -- Gitee