diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c702b7441934234e3f71ae41e5212ead3c85bd7..220eb36515a7314dc602e7cf48a9741502780d10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - 新增部件参数batchtoolbarmode、全局参数batchToolbarMode,用于设置批操作工具栏显示模式(default 选数据即显示,multiple 需选 2 条以上),仅限卡片、列表、表格、树表格使用 - 新增列表部件样式-扩展视图3:仅非分组列表和分组样式2列表支持,实现列表的从下往上的绘制,同时滚动加载也支持上滚加载 - 新增数据多项选择视图,数据多项选择视图(左右关系)视图参数checkstrictly(是否严格的遵循穿梭空左右互相关联) +- 新增按钮组支持引用界面行为组 ### Changed @@ -25,6 +26,8 @@ - 修复树选中异常 - 修复导入请求路径不正确异常 +- 修复菜单默认打开菜单项无权限显示问题 +- 修复面板成员点击未传递事件源 ## [0.7.41-alpha.24] - 2025-09-04 diff --git a/src/common/action-toolbar/action-toolbar.scss b/src/common/action-toolbar/action-toolbar.scss index 798d166c168fd56d109361db2ec226408e084648..3d0a62e552c6a8179d27ccefaa376c844b38d119 100644 --- a/src/common/action-toolbar/action-toolbar.scss +++ b/src/common/action-toolbar/action-toolbar.scss @@ -53,6 +53,7 @@ $action-toolbar: ( } @include m(caption) { display: flex; + gap: getCssVar(spacing, extra, tight); align-items: center; justify-content: space-between; width: 100%; diff --git a/src/common/action-toolbar/action-toolbar.tsx b/src/common/action-toolbar/action-toolbar.tsx index a5f11ce361b8d9fd11885dee356027be408ac5f0..0cc40f376b6701e1719072eeeaa127053c535ce7 100644 --- a/src/common/action-toolbar/action-toolbar.tsx +++ b/src/common/action-toolbar/action-toolbar.tsx @@ -39,6 +39,10 @@ export const IBizActionToolbar = defineComponent({ actionCallBack: { type: Function, }, + direction: { + type: String as PropType<'horizontal' | 'vertical'>, + default: 'horizontal', + }, }, setup(props, { emit }) { const ns = useNamespace('action-toolbar'); @@ -113,7 +117,7 @@ export const IBizActionToolbar = defineComponent({ const { actionLevel } = item; return [ ns.e('item'), - ns.is('disabled', false), + item.sysCss?.codeName, ns.em('item', `level-${actionLevel}`), ]; }; @@ -136,11 +140,12 @@ export const IBizActionToolbar = defineComponent({ const details = this.actionDetails || []; const renderDivider = (isExpand: boolean) => { + const ishorizontal = isExpand && this.direction === 'horizontal'; return ( ); }; @@ -153,17 +158,18 @@ export const IBizActionToolbar = defineComponent({ const actionGroup = detail.refUIActionGroup; if (!actionGroup?.uiactionGroupDetails?.length) return null; // 子项所有项都隐藏,父项也应该隐藏 - const pvisible = - actionGroup.uiactionGroupDetails.findIndex(item => { - return this.actionsState[item.id!].visible === true; - }) !== -1; + const pvisible = actionGroup.uiactionGroupDetails.some(item => { + return this.actionsState[item.id!].visible; + }); if (!pvisible) return null; + const ishorizontal = isExpand && this.direction === 'horizontal'; return [ detail.addSeparator && renderDivider(isExpand),
- {actionGroup.id} + {actionGroup.name || actionGroup.id}
@@ -200,7 +210,7 @@ export const IBizActionToolbar = defineComponent({ default: () => { return renderActions( actionGroup.uiactionGroupDetails || [], - isExpand, + false, ); }, }} @@ -214,7 +224,7 @@ export const IBizActionToolbar = defineComponent({ ): any => { return items.map(detail => { if (detail.detailType === 'DEUIACTIONGROUP' && detail.refUIActionGroup) - return renderActionGroup(detail); + return renderActionGroup(detail, isExpand); if (this.actionsState[detail.id!]?.visible) { return [ detail.addSeparator && renderDivider(isExpand), @@ -353,13 +363,10 @@ export const IBizActionToolbar = defineComponent({ if (this.actionsState[detail.id!].visible) { return ( {detail.showIcon && detail.sysImage && ( diff --git a/src/common/button-list/button-list.scss b/src/common/button-list/button-list.scss index 25ccdbbdfe3a664b947df7ff0d84f094438b2e03..c45f2e2348dcd4bca90ccc55dc1ac2403a62a6b6 100644 --- a/src/common/button-list/button-list.scss +++ b/src/common/button-list/button-list.scss @@ -13,7 +13,7 @@ $button-list: ( @include b(button-list) { @include set-component-css-var(button-list, $button-list); - + @include e(content) { display: flex; @@ -28,7 +28,9 @@ $button-list: ( @include e(button-content) { display: flex; + gap: getCssVar(spacing, extra, tight); align-items: center; + width: 100%; @include m(icon) { display: flex; @@ -43,8 +45,15 @@ $button-list: ( @include e(item) { > span { - // 禁用点击事件,保证event.target是button元素 - pointer-events: none; + width: 100%; + + // 禁用点击事件,保证event.target是button元素 + pointer-events: none; + } + @include m(group) { + .#{bem('button-list', 'button-content')} { + justify-content: space-between; + } } } @@ -111,27 +120,9 @@ $button-list: ( @include e(dropdown-popper) { @include set-component-css-var(button-list, $button-list); - &.el-popper.el-dropdown__popper - .el-scrollbar - .el-dropdown__list - .el-dropdown-menu - .el-dropdown-menu__item { - padding: getCssVar(spacing, none); - } - - .el-button { - --el-button-active-bg-color: transparent; - --el-button-hover-bg-color: transparent; - --el-button-bg-color: transparent; - --el-mask-color-extra-light: transparent; - - display: block; - width: 100%; - padding: getCssVar(button-list, popover-button-padding); - margin: getCssVar(button-list, popover-button-margin); - font-size: getCssVar(button-list, popover-button-font-size); - text-align: left; - border-radius: 0; + >.el-scrollbar { + min-width: 150px; + overflow: visible; } @include m(style2) { @@ -156,4 +147,41 @@ $button-list: ( } } } + + @include e(dropdown-popper-content) { + padding: getCssVar(spacing, extra, tight) getCssVar('spacing', 'none'); + } + + @include e(popover) { + padding: getCssVar(spacing, extra, tight) getCssVar('spacing', 'none') !important; + border-radius: 0 !important; + } } + +.#{bem(button-list, dropdown-popper-content)}, +.#{bem(button-list, popover)} { + display: flex; + flex-direction: column; + background-color: getCssVar(color, primary) !important; + + .el-button { + justify-content: flex-start; + width: 100%; + height: getCssVar(height-control, large); + padding: getCssVar(spacing, tight) getCssVar(spacing, base); + margin: getCssVar('spacing', 'none'); + font-size: getCssVar('font-size', 'regular'); + color: getCssVar(color, primary, text); + + ion-icon { + margin-right: getCssVar(spacing, extra, tight); + } + } + + .el-button.is-text:not(.is-disabled) { + &:hover { + background-color: var(--el-button-hover-bg-color); + border-color: var(--el-button-hover-border-color); + } + } +} \ No newline at end of file diff --git a/src/common/button-list/button-list.tsx b/src/common/button-list/button-list.tsx index 25f28b253aafeeb955a8cc2671d9ae52c196c59d..c0c1dcca605a19046586df71c75f4195c4b0ad55 100644 --- a/src/common/button-list/button-list.tsx +++ b/src/common/button-list/button-list.tsx @@ -1,4 +1,5 @@ /* eslint-disable no-unused-expressions */ +/* eslint-disable no-use-before-define */ import { ref, PropType, @@ -9,7 +10,7 @@ import { onDeactivated, defineComponent, } from 'vue'; -import { useNamespace } from '@ibiz-template/vue3-util'; +import { useNamespace, useUIStore } from '@ibiz-template/vue3-util'; import { IPanelButtonList, IDEFormButtonList, @@ -44,6 +45,9 @@ export const IBizButtonList = defineComponent({ }, setup(props, { emit }) { const ns = useNamespace('button-list'); + + const { zIndex } = useUIStore(); + const dropdown = ref(); const componentRef = ref(); const { actionGroupExtractMode, buttonListType, uiactionGroup } = @@ -200,46 +204,124 @@ export const IBizButtonList = defineComponent({ }; /** - * 绘制行为项内容 - * - * @param {IAppDEUIActionGroupDetail} item - * @param {boolean} [disabled=false] - * @return {*} {(JSX.Element | null)} + * @description 绘制界面行为组 + * @param {IAppDEUIActionGroupDetail} detail 引用界面行为 + * @param {boolean} [isFlatten=true] 是否为平铺按钮 + * @returns {*} */ - const renderButton = ( - item: IAppDEUIActionGroupDetail, - type?: string, - ): JSX.Element | null => { - if (props.buttonsState[item.id!]?.visible === false) return null; + const renderActionGroup = ( + detail: IAppDEUIActionGroupDetail, + isFlatten: boolean, + ) => { + const actionGroup = detail.refUIActionGroup; + if (!actionGroup?.uiactionGroupDetails?.length) return null; + // 子项所有项都隐藏,父项也应该隐藏 + const pvisible = + actionGroup.uiactionGroupDetails.findIndex(item => { + return props.buttonsState[item.id!].visible === true; + }) !== -1; + if (!pvisible) return null; return ( - handleClick(event, item)} + -
- {item.showIcon && ( - - )} - {item.showCaption && ( - - {item.caption} - - )} -
-
+ {{ + reference: () => { + return ( + +
+ + {actionGroup.name || actionGroup.id} + + +
+
+ ); + }, + default: () => { + return actionGroup.uiactionGroupDetails?.map(item => + renderButton(item, false), + ); + }, + }} +
); }; + /** + * @description 绘制行为项 + * @param {IAppDEUIActionGroupDetail} item 行为项 + * @param {boolean} [isFlatten=true] 是否为平铺按钮 + * @param {string} [type] 按钮类型 + * @returns {*} + */ + const renderButton = ( + item: IAppDEUIActionGroupDetail, + isFlatten: boolean, + type?: string, + ) => { + if (item.detailType === 'DEUIACTIONGROUP' && item.refUIActionGroup) + return renderActionGroup(item, isFlatten); + if (props.buttonsState[item.id!]?.visible) + return ( + handleClick(event, item)} + disabled={props.buttonsState[item.id!]?.disabled || props.disabled} + > +
+ {item.showIcon && ( + + )} + {item.showCaption && ( + + {item.caption} + + )} +
+
+ ); + }; + /** * 绘制下拉行为项 * @@ -285,7 +367,11 @@ export const IBizButtonList = defineComponent({ {actionGroupExtractMode === 'ITEMX' && item && - renderButton(item, convertBtnType(buttonListStyle.value))} + renderButton( + item, + true, + convertBtnType(buttonListStyle.value), + )} {(actionGroupExtractMode !== 'ITEMX' || item) && ( ( - +
{items.map((item: IAppDEUIActionGroupDetail, index: number) => { if ( !(props.buttonsState[item.id!]?.visible === false) && (actionGroupExtractMode !== 'ITEMX' || index !== firstIndex.value) ) { - return ( - {renderButton(item)} - ); + return renderButton(item, false); } return null; })} - +
), }} @@ -362,7 +446,9 @@ export const IBizButtonList = defineComponent({ sliceIndex.value === -1 ? [] : groupDetails.slice(sliceIndex.value); return (
- {items.map((item: IAppDEUIActionGroupDetail) => renderButton(item))} + {items.map((item: IAppDEUIActionGroupDetail) => + renderButton(item, true), + )} {moreItems.length ? renderDropdown(moreItems, 'ellipsis-horizontal') : null} diff --git a/src/control/app-menu/app-menu.tsx b/src/control/app-menu/app-menu.tsx index bcd455e9c7a94914f41d6e6cb1d1213c7e0a38dc..6051caf54399302c07d34e758efdfee237e90b16 100644 --- a/src/control/app-menu/app-menu.tsx +++ b/src/control/app-menu/app-menu.tsx @@ -460,7 +460,9 @@ export const AppMenuControl = defineComponent({ currentPath: { type: String }, }, setup(props) { - const c = useControlController((...args) => new AppMenuController(...args)); + const c: AppMenuController = useControlController( + (...args) => new AppMenuController(...args), + ); const ns = useNamespace(`control-${c.model.controlType!.toLowerCase()}`); const menus = ref(getMenus(c.model.appMenuItems!)); @@ -539,11 +541,8 @@ export const AppMenuControl = defineComponent({ c.evt.on('onCreated', async () => { saveConfigs.value = c.saveConfigs; - const allItems = c.getAllItems(); // 默认激活的菜单项 - const defaultActiveMenuItem = allItems.find(item => { - return item.openDefault && !item.hidden; - }); + const defaultActiveMenuItem = c.getDefaultOpenMenuItem(); if ( defaultActiveMenuItem && !route?.params.view2 && @@ -556,7 +555,7 @@ export const AppMenuControl = defineComponent({ defaultActive.value = activeMenu ? activeMenu.id! : ''; } // 默认展开的菜单项数组 - const defaultOpensArr = allItems.filter(item => { + const defaultOpensArr = c.getAllItems().filter(item => { return item.expanded && !item.hidden; }); if (defaultOpensArr.length > 0) { diff --git a/src/control/kanban/kanban.scss b/src/control/kanban/kanban.scss index 56db6dde7642a9bd28850f7c4e9ce041b11086b3..1336fcee691f1b93eb12a448ce4c0b49b387fd97 100644 --- a/src/control/kanban/kanban.scss +++ b/src/control/kanban/kanban.scss @@ -282,11 +282,14 @@ $control-kanban: ( position: relative; @include e(popover) { - >.el-scrollbar { + > .el-scrollbar { + min-width: 150px; overflow: visible; } .#{bem(action-toolbar, popover)} { + padding: getCssVar(spacing, extra, tight) getCssVar('spacing', 'none') !important; background-color: getCssVar(color, primary) !important; + border-radius: 0 !important; } } diff --git a/src/control/kanban/kanban.tsx b/src/control/kanban/kanban.tsx index 6d4f296ee8d9e6845779457994b878b0041ec52d..d660941f7bc282d881220f8f0b6089710c23d7d1 100644 --- a/src/control/kanban/kanban.tsx +++ b/src/control/kanban/kanban.tsx @@ -518,6 +518,7 @@ export const KanbanControl = defineComponent({ {c.model.groupUIActionGroup && group.groupActionGroupState && ( { - this.controller.onClick(); - }} + onClick={event => this.controller.onClick(event)} > {this.controller.model.cssStyle ? ( diff --git a/src/panel-component/index-blank-placeholder/index-blank-placeholder.tsx b/src/panel-component/index-blank-placeholder/index-blank-placeholder.tsx index f1e1db766e0a809ab76259a6ecd3e39ea78e3d12..581115562606f9cad82772b637228a95f765ca01 100644 --- a/src/panel-component/index-blank-placeholder/index-blank-placeholder.tsx +++ b/src/panel-component/index-blank-placeholder/index-blank-placeholder.tsx @@ -77,9 +77,7 @@ export const IndexBlankPlaceholder = defineComponent({ return (
{ - this.controller.onClick(); - }} + onClick={event => this.controller.onClick(event)} > {this.controller.model.cssStyle ? ( diff --git a/src/panel-component/panel-app-login-view/panel-app-login-view.tsx b/src/panel-component/panel-app-login-view/panel-app-login-view.tsx index 0c353951189d70bce548d3295bcb693fa64aa230..7f45a02d649b92776908f8f854bb7621fcfb14da 100644 --- a/src/panel-component/panel-app-login-view/panel-app-login-view.tsx +++ b/src/panel-component/panel-app-login-view/panel-app-login-view.tsx @@ -86,9 +86,7 @@ export const PanelAppLoginView = defineComponent({
{ - this.controller.onClick(); - }} + onClick={event => this.controller.onClick(event)} > {this.controller.model.cssStyle ? ( diff --git a/src/panel-component/panel-app-title/panel-app-title.tsx b/src/panel-component/panel-app-title/panel-app-title.tsx index 98c36aa80cdda40f4c53d7b6973446ab7272e7a7..6d9a31f7d54fc449e3fa6a1e5d9320b6816c2615 100644 --- a/src/panel-component/panel-app-title/panel-app-title.tsx +++ b/src/panel-component/panel-app-title/panel-app-title.tsx @@ -52,7 +52,7 @@ export const PanelAppTitle = defineComponent({ return 'LEFT'; }); - const handleClick = async () => { + const handleClick = async (event: MouseEvent) => { // 适配登录页系统标题不提供点击链接能力 if (c.panel.view.model.viewType === 'APPLOGINVIEW') return; // 跳转首页 @@ -66,7 +66,7 @@ export const PanelAppTitle = defineComponent({ }); } - props.controller.onClick(); + props.controller.onClick(event); }; const showImgOnly = computed(() => { diff --git a/src/panel-component/panel-button-list/panel-button-list.controller.ts b/src/panel-component/panel-button-list/panel-button-list.controller.ts index 3ce5dab2f2741deaa370d40007bb207f9917261b..4dee841df08785b36ccfddcc6e068f29684eba84 100644 --- a/src/panel-component/panel-button-list/panel-button-list.controller.ts +++ b/src/panel-component/panel-button-list/panel-button-list.controller.ts @@ -1,11 +1,12 @@ /* eslint-disable object-shorthand */ import { - ButtonContainerState, + UIActionUtil, PanelController, - PanelItemController, PanelNotifyState, + PanelItemController, UIActionButtonState, - UIActionUtil, + getAllUIActionItems, + ButtonContainerState, } from '@ibiz-template/runtime'; import { IPanelButton, @@ -75,17 +76,20 @@ export class PanelButtonListController extends PanelItemController { const { buttonListType, uiactionGroup, panelButtons } = this.model; if (buttonListType === 'UIACTIONGROUP') { - uiactionGroup?.uiactionGroupDetails?.forEach(detail => { - if (detail.uiactionId) { - const buttonState = new UIActionButtonState( - detail.id!, - detail.appId, - detail.uiactionId, - detail, - ); - this.state.buttonsState.addState(detail.id!, buttonState); - } - }); + if (uiactionGroup?.uiactionGroupDetails) { + const actions = getAllUIActionItems(uiactionGroup.uiactionGroupDetails); + actions.forEach(detail => { + if (detail.uiactionId) { + const buttonState = new UIActionButtonState( + detail.id!, + detail.appId, + detail.uiactionId, + detail, + ); + this.state.buttonsState.addState(detail.id!, buttonState); + } + }); + } } else { panelButtons?.forEach(button => { if (button.uiactionId) { @@ -143,10 +147,10 @@ export class PanelButtonListController extends PanelItemController detail.id === id, - ); + if (buttonListType === 'UIACTIONGROUP') { + const actions = getAllUIActionItems(uiactionGroup?.uiactionGroupDetails); + return actions.find(detail => detail.id === id); + } return panelButtons?.find(button => button.id === id); } diff --git a/src/panel-component/panel-button-list/panel-button-list.tsx b/src/panel-component/panel-button-list/panel-button-list.tsx index a279e6e486715b56c80966b533f93bd50e3bb064..d1753c2286bf58ff16586565606f8a82714df722 100644 --- a/src/panel-component/panel-button-list/panel-button-list.tsx +++ b/src/panel-component/panel-button-list/panel-button-list.tsx @@ -47,7 +47,7 @@ export const PanelButtonList = defineComponent({ }, render() { const { state } = this.controller; - if (state.visible) { + if (state.visible) return ( ); - } return null; }, }); diff --git a/src/panel-component/panel-button/panel-button.controller.ts b/src/panel-component/panel-button/panel-button.controller.ts index bbc11c5c069eccca38eb2f28e6df73469903055e..e541dd36c9d64de4758e2157a7898a9aab15e180 100644 --- a/src/panel-component/panel-button/panel-button.controller.ts +++ b/src/panel-component/panel-button/panel-button.controller.ts @@ -128,9 +128,7 @@ export class PanelButtonController extends PanelItemController { */ async onActionClick(event: MouseEvent): Promise { const { uiactionId, actionType } = this.model; - if (actionType === 'NONE') { - return; - } + if (actionType === 'NONE') return; event.stopPropagation(); event.preventDefault(); await UIActionUtil.execAndResolved( diff --git a/src/panel-component/panel-button/panel-button.scss b/src/panel-component/panel-button/panel-button.scss index 7c60a6e6658811d16b76d64149a507d6ec8e88ba..ab471f639985c5ac26ecfd2b1101df4fc1938587 100644 --- a/src/panel-component/panel-button/panel-button.scss +++ b/src/panel-component/panel-button/panel-button.scss @@ -13,7 +13,11 @@ $panel-button: ( .el-button{ width: 100%; - + + > span { + // 禁用点击事件,保证event.target是button元素 + pointer-events: none; + } @include b(panel-button-content){ @include flex(row, flex-start, center);