From 09f443435bbb8c2c3086ea0a5c7dd10228600726 Mon Sep 17 00:00:00 2001 From: lijianxiong <1518062161@qq.com> Date: Fri, 31 Oct 2025 15:17:03 +0800 Subject: [PATCH 1/8] =?UTF-8?q?feat=EF=BC=9A=E6=96=B0=E5=A2=9E=E6=B5=AE?= =?UTF-8?q?=E5=8A=A8=E6=8C=89=E9=92=AE=E9=80=9A=E7=94=A8=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/float-button/float-button.scss | 81 +++++++++++++++++++++++ src/common/float-button/float-button.tsx | 45 +++++++++++++ src/common/index.ts | 2 + 3 files changed, 128 insertions(+) create mode 100644 src/common/float-button/float-button.scss create mode 100644 src/common/float-button/float-button.tsx diff --git a/src/common/float-button/float-button.scss b/src/common/float-button/float-button.scss new file mode 100644 index 00000000000..4d4f0e74b58 --- /dev/null +++ b/src/common/float-button/float-button.scss @@ -0,0 +1,81 @@ +$float-button: ( + 'color-bg': getCssVar('color', 'bg', 1), // 按钮背景色 + 'color-text': getCssVar('color', 'text', 1), // 按钮文字颜色 + 'color-box-shadow': getCssVar(shadow, elevated), // 按钮阴影 + 'width': rem(50px), // 按钮宽度 + 'height': rem(50px), // 按钮高度 + 'radius-circle': 50%, // 按钮圆角 + 'font-text-font-size': getCssVar('font-size', 'header-6'), // 按钮文字大小 + 'position': fixed, // 按钮定位模式 + 'left': unset, // 按钮基于左边移动位置 + 'right': unset, // 按钮基于右边移动位置 + 'top': unset, // 按钮基于上边移动位置 + 'bottom': unset, // 按钮下边移动位置 + 'transform': unset, // 按钮变换样式 + 'base-offset': rem(30px), // 按钮基础偏移 + 'start-top': calc( + #{getCssVar(float-button, base-offset)} + + #{getCssVar(height, control, large)} + ), // 按钮基于顶部偏移:顶部偏移量 + 视图头高度 + 'end-bottom': calc( + #{getCssVar(float-button, base-offset)} + var(--van-back-top-bottom) + ), // 按钮基于底部偏移:底部偏移量 + 移动端安全距离 +); + +@include b(float-button) { + @include set-component-css-var('float-button', $float-button); + + position: getCssVar(float-button, position); + left: getCssVar(float-button, left); + right: getCssVar(float-button, right); + top: getCssVar(float-button, top); + bottom: getCssVar(float-button, bottom); + display: flex; + align-items: center; + justify-content: center; + width: getCssVar(float-button, width); + height: getCssVar(float-button, height); + background-color: getCssVar(float-button, color-bg); + border-radius: getCssVar(float-button, radius-circle); + box-shadow: getCssVar(float-button, color-box-shadow); + transform: getCssVar(float-button, transform); + + &.van-button { + background-color: getCssVar(float-button, color-bg); + border: none; + color: getCssVar(float-button, color-text); + font-size: getCssVar(float-button, font-text-font-size); + .van-button__text { + display: flex; + align-items: center; + justify-content: center; + } + } + + @include when(leftstart) { + #{getCssVar(float-button, left)}: getCssVar(float-button, base-offset); + #{getCssVarName(float-button, top)}: getCssVar(float-button, start-top); + } + @include when(left) { + #{getCssVarName(float-button, left)}: getCssVar(float-button, base-offset); + #{getCssVarName(float-button, top)}: 50%; + #{getCssVarName(float-button, transform)}: translateY(-50%); + } + @include when(leftend) { + #{getCssVarName(float-button, left)}: getCssVar(float-button, base-offset); + #{getCssVarName(float-button, bottom)}: getCssVar(float-button, end-bottom); + } + @include when(rightstart) { + #{getCssVarName(float-button, right)}: getCssVar(float-button, base-offset); + #{getCssVarName(float-button, top)}: getCssVar(float-button, start-top); + } + @include when(right) { + #{getCssVarName(float-button, right)}: getCssVar(float-button, base-offset); + #{getCssVarName(float-button, top)}: 50%; + #{getCssVarName(float-button, transform)}: translateY(-50%); + } + @include when(rightend) { + #{getCssVarName(float-button, right)}: getCssVar(float-button, base-offset); + #{getCssVarName(float-button, bottom)}: getCssVar(float-button, end-bottom); + } +} diff --git a/src/common/float-button/float-button.tsx b/src/common/float-button/float-button.tsx new file mode 100644 index 00000000000..ede1da866bb --- /dev/null +++ b/src/common/float-button/float-button.tsx @@ -0,0 +1,45 @@ +import { defineComponent, PropType } from 'vue'; +import { useNamespace } from '@ibiz-template/vue3-util'; +import './float-button.scss'; + +export const FloatButton = defineComponent({ + name: 'IBizFloatButton', + props: { + enabledAlign: { + type: Boolean, + default: true, + }, + align: { + type: String as PropType< + 'LEFTSTART' | 'LEFT' | 'LEFTEND' | 'RIGHT' | 'RIGHTSTART' | 'RIGHTEND' + >, + default: 'RIGHTEND', + }, + }, + emits: { + click: (_event: MouseEvent) => true, + }, + setup(props, { emit }) { + const ns = useNamespace('float-button'); + const handleClick = (_event: MouseEvent) => { + emit('click', _event); + }; + + return { ns, handleClick }; + }, + render() { + const align = this.align.toLocaleLowerCase(); + return ( + + + + ); + }, +}); diff --git a/src/common/index.ts b/src/common/index.ts index 6a116581efc..3c0f2cf0bd0 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -31,6 +31,7 @@ import { IBizSplit } from './split/split'; import { IBizSplitTrigger } from './split-trigger/split-trigger'; import { IBizMdAdvanedSearchfrom } from './md-advaned-searchform/md-advaned-searchform'; import { IBizAddBtn } from './add-btn/add-btn'; +import { FloatButton } from './float-button/float-button'; export * from './col/col'; export * from './row/row'; @@ -42,6 +43,7 @@ export * from './add-btn/add-btn'; export const IBizCommonComponents = { install: (v: App): void => { v.component(IBizAddBtn.name!, IBizAddBtn); + v.component(FloatButton.name!, FloatButton); v.component(IBizMdAdvanedSearchfrom.name!, IBizMdAdvanedSearchfrom); v.component(IBizSplit.name!, IBizSplit); v.component(IBizSplitTrigger.name!, IBizSplitTrigger); -- Gitee From 0f9ef04b26f7c8a348bb67055d90c459a60132c5 Mon Sep 17 00:00:00 2001 From: lijianxiong <1518062161@qq.com> Date: Fri, 31 Oct 2025 15:23:39 +0800 Subject: [PATCH 2/8] =?UTF-8?q?feat=EF=BC=9A=E6=96=B0=E5=A2=9E=E5=9B=BE?= =?UTF-8?q?=E6=A0=87=E8=8F=9C=E5=8D=95=E3=80=81=E5=88=97=E8=A1=A8=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E8=AF=86=E5=88=AB=E6=A8=A1=E5=9E=8B=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=EF=BC=88=E6=94=AF=E6=8C=81=E8=87=AA=E5=AE=9A=E4=B9=89=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app-menu-icon-view/app-menu-icon-view.tsx | 63 +++++++++--------- .../app-menu-list-view/app-menu-list-view.tsx | 65 ++++++++++++------- 2 files changed, 73 insertions(+), 55 deletions(-) diff --git a/src/control/app-menu-icon-view/app-menu-icon-view.tsx b/src/control/app-menu-icon-view/app-menu-icon-view.tsx index bd14d7824d8..774147b8847 100644 --- a/src/control/app-menu-icon-view/app-menu-icon-view.tsx +++ b/src/control/app-menu-icon-view/app-menu-icon-view.tsx @@ -1,8 +1,10 @@ import { IAppMenu, IAppMenuItem } from '@ibiz/model-core'; -import { computed, defineComponent, PropType, VNode } from 'vue'; +import { defineComponent, PropType, VNode } from 'vue'; import { AppMenuController, IControlProvider } from '@ibiz-template/runtime'; import { useControlController, useNamespace } from '@ibiz-template/vue3-util'; import { isNil } from 'ramda'; +import { isArray } from 'lodash-es'; +import { getDefaultIconVal, useMenuRender } from '../app-menu/menu-render-util'; import './app-menu-icon-view.scss'; export const AppMenuIconViewControl = defineComponent({ @@ -18,11 +20,8 @@ export const AppMenuIconViewControl = defineComponent({ const ns = useNamespace( `control-${c.model.controlType!.toLowerCase()}-${c.model.controlStyle?.toLowerCase()}`, ); - // 默认激活菜单项 - const columnNum = computed(() => { - // todo 根据屏幕宽度计算显示多少列 - return 5; - }); + + const { onCustomizedClick } = useMenuRender(c); // 绘制菜单项 const renderMenuItem = ( @@ -37,24 +36,22 @@ export const AppMenuIconViewControl = defineComponent({ let renderItem: Array | VNode | null = null; switch (menuItem.itemType) { case 'MENUITEM': - if (menuItem.appMenuItems?.length) { + if (isArray(menuItem.appMenuItems)) { renderItem = ( - + {{ default: () => { return ( -
-
+
+
{[ - menuItem.sysImage ? ( - - ) : null, - + , + {menuItem.caption} , !isNil(counterNum) && ( ), @@ -62,9 +59,9 @@ export const AppMenuIconViewControl = defineComponent({
{menuItem.appMenuItems?.map(_item => renderMenuItem(_item), @@ -90,11 +87,7 @@ export const AppMenuIconViewControl = defineComponent({
{ } {!isNil(counterNum) && ( @@ -125,23 +118,29 @@ export const AppMenuIconViewControl = defineComponent({ return { c, ns, - columnNum, renderMenuItem, + onCustomizedClick, }; }, render() { - const { model } = this.c; + const { model, state } = this.c; + if (!state.isCreated) return; + return ( - - {(model as IAppMenu)?.appMenuItems?.map(item => - this.renderMenuItem(item), + + {state.mobMenuItems.map(item => this.renderMenuItem(item))} + + {model.enableCustomized && ( + )} - +
); }, }); diff --git a/src/control/app-menu-list-view/app-menu-list-view.tsx b/src/control/app-menu-list-view/app-menu-list-view.tsx index 969e6505f08..1aff5854f47 100644 --- a/src/control/app-menu-list-view/app-menu-list-view.tsx +++ b/src/control/app-menu-list-view/app-menu-list-view.tsx @@ -3,6 +3,8 @@ import { IAppMenu, IAppMenuItem } from '@ibiz/model-core'; import { AppMenuController, IControlProvider } from '@ibiz-template/runtime'; import { useControlController, useNamespace } from '@ibiz-template/vue3-util'; import { isNil } from 'ramda'; +import { isArray } from 'lodash-es'; +import { useMenuRender } from '../app-menu/menu-render-util'; import './app-menu-list-view.scss'; export const AppMenuListViewControl = defineComponent({ @@ -18,27 +20,22 @@ export const AppMenuListViewControl = defineComponent({ const ns = useNamespace( `control-${c.model.controlType!.toLowerCase()}-${c.model.controlStyle?.toLowerCase()}`, ); - return { - c, - ns, - }; - }, - render() { - const { model } = this.c; + const { onCustomizedClick } = useMenuRender(c); + const renderMenuItem = (item: IAppMenuItem) => { if (item.hidden === true) { return null; } const counterNum = item.counterId - ? this.c.state.counterData[item.counterId] + ? c.state.counterData[item.counterId] : null; let renderItem = null; switch (item.itemType) { case 'MENUITEM': - if (item.appMenuItems?.length) { + if (isArray(item.appMenuItems)) { renderItem = ( - + {{ default: () => { return item.appMenuItems?.map(child => { @@ -47,16 +44,16 @@ export const AppMenuListViewControl = defineComponent({ }, title: () => { return ( -
+
{item.sysImage ? ( ) : null} - + {item.caption} {!isNil(counterNum) && ( )} @@ -69,14 +66,14 @@ export const AppMenuListViewControl = defineComponent({ } else { renderItem = ( { try { - await this.c.onClickMenuItem(item.id!, event, false); + await c.onClickMenuItem(item.id!, event, false); } catch (error) { ibiz.log.error(error); } @@ -86,7 +83,7 @@ export const AppMenuListViewControl = defineComponent({ icon: () => { return ( item.sysImage && ( -
+
) @@ -94,10 +91,10 @@ export const AppMenuListViewControl = defineComponent({ }, value: () => { return ( -
+
{!isNil(counterNum) && ( )} @@ -117,12 +114,34 @@ export const AppMenuListViewControl = defineComponent({ } return renderItem; }; + + return { + c, + ns, + renderMenuItem, + onCustomizedClick, + }; + }, + + render() { + const { model, state } = this.c; + if (!state.isCreated) return; + return ( - - {model?.appMenuItems?.map(item => { - return renderMenuItem(item); - })} - +
+ + {state.mobMenuItems.map(item => this.renderMenuItem(item))} + + {model.enableCustomized && ( + + )} +
); }, }); -- Gitee From 8852c50a96c59dbaa6c3b8a0b07d6f3c0725fb20 Mon Sep 17 00:00:00 2001 From: lijianxiong <1518062161@qq.com> Date: Fri, 31 Oct 2025 15:28:17 +0800 Subject: [PATCH 3/8] =?UTF-8?q?feat=EF=BC=9A=E4=BC=98=E5=8C=96=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E8=87=AA=E5=AE=9A=E4=B9=89=E9=85=8D=E7=BD=AE=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=EF=BC=8C=E6=94=AF=E6=8C=81=E6=A0=91=E5=BD=A2=E7=BB=93?= =?UTF-8?q?=E6=9E=84=E6=95=B0=E6=8D=AE=E7=9A=84=E7=BB=98=E5=88=B6=E4=B8=8E?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../custom-menu-design.scss | 114 ++++---- .../custom-menu-design/custom-menu-design.tsx | 253 ++++++++---------- 2 files changed, 162 insertions(+), 205 deletions(-) diff --git a/src/control/app-menu/custom-menu-design/custom-menu-design.scss b/src/control/app-menu/custom-menu-design/custom-menu-design.scss index 86152387c13..2c0fc0af54c 100644 --- a/src/control/app-menu/custom-menu-design/custom-menu-design.scss +++ b/src/control/app-menu/custom-menu-design/custom-menu-design.scss @@ -1,98 +1,80 @@ $menu-design: ( - 'padding': getCssVar(spacing, base), - 'bg-color': getCssVar(color, bg, 0), - 'border-color': getCssVar(color, border), - 'header-font-size': getCssVar(font-size, header, 5), - 'header-caption-font-weight': getCssVar(font-weight, bold), - 'close-icon-font-size': getCssVar(font-size, header, 3), - 'group-title-text-color': getCssVar(color, text, 2), - 'group-title-padding': getCssVar(spacing, base) 0, - 'draggable-bg-color': getCssVar(color, bg, 2), - 'draggable-padding': getCssVar(spacing, base, tight), - 'draggable-border-radius': getCssVar(border-radius, medium), - 'draggable-icon-font-size': getCssVar(font-size, header, 3), - 'draggable-prefix-icon-margin': 0 getCssVar(spacing, tight) 0 0, - 'draggable-remove-icon-bg-color': getCssVar(color, danger), - 'item-font-size': getCssVar('spacing', 'base'), - 'item-content-padding': getCssVar(spacing, base, tight) 0, - 'item-icon-margin': 0 getCssVar(spacing, tight) 0 0, - 'item-icon-width': rem(22px), - 'primary-btn-text-color': getCssVar(color, primary), + 'color-item-bg': getCssVar(color, bg, 0), // 树节点项背景色 + 'color-primary-btn-text': getCssVar(color, primary), // 主要按钮文字颜色 + 'height-item': getCssVar('height-control', 'large'), // 树节点项高度 + 'spacing-padding': getCssVar(spacing, base), // 当前组件默认内间距 + 'spacing-header-padding': 0 0 getCssVar('spacing', 'tight'), // 当前组件头部内间距 + 'spacing-item-padding': 0 getCssVar('spacing', 'base'), // 树节点项内间距 + 'spacing-item-margin': getCssVar('spacing', 'tight') 0, // 树节点项外间距 + 'spacing-item-caption-margin': 0 0 0 getCssVar('spacing', 'extra-tight'), // 树节点项主文本外间距 + 'spacing-item-checkbox-margin': 0 0 0 getCssVar('spacing', 'extra-tight'), // 树节点项复选框外间距 + 'spacing-node-item-margin-left': rem(20px), // 树节点项左侧间距,用于缩进区分父节点与子节点 + 'radius-item-circle': getCssVar(border, radius, medium), // 树节点项圆角 + 'font-header-font-size': getCssVar(font-size, header, 5), // 当前组件头部标题字体大小 + 'font-header-caption-font-weight': getCssVar(font-weight, bold), // 当前组件头部标题字体粗细 + 'font-close-icon-font-size': getCssVar(font-size, header, 3), // 当前组件头部关闭图标字体大小 + 'font-item-font-size': getCssVar('font-size', 'header-6'), // 树节点项字体大小 + 'icon-item-size': getCssVar('font-size', 'header-6'), // 树节点项图标大小 ); @include b(menu-design) { @include set-component-css-var('menu-design', $menu-design); + display: flex; + flex-direction: column; width: 100%; height: 100%; - padding: getCssVar(menu-design, padding); - background-color: getCssVar(menu-design, bg-color); + padding: getCssVar(menu-design, spacing-padding); @include e(header) { display: flex; align-items: center; justify-content: space-between; - font-size: getCssVar(menu-design, header-font-size); + padding: getCssVar(menu-design, spacing-header-padding); + font-size: getCssVar(menu-design, font-header-font-size); @include m(close-icon) { - font-size: getCssVar(menu-design, close-icon-font-size); + font-size: getCssVar(menu-design, font-close-icon-font-size); } @include m(caption) { - font-weight: getCssVar(menu-design, header-caption-font-weight); + font-weight: getCssVar(menu-design, font-header-caption-font-weight); } @include m(save) { - color: getCssVar(menu-design, primary-btn-text-color); + color: getCssVar(menu-design, color-primary-btn-text); } } - @include e(group) { - @include m(caption) { - color: getCssVar(menu-design, group-title-text-color); - padding: getCssVar(menu-design, group-title-padding); - } + @include e(content) { + flex: 1; + overflow-y: auto; } - @include e(draggable) { - padding: getCssVar(menu-design, draggable-padding); - border-radius: getCssVar(menu-design, draggable-border-radius); - background-color: getCssVar(menu-design, draggable-bg-color); + @include e(item) { + display: flex; + align-items: center; + height: getCssVar(menu-design, height-item); + padding: getCssVar(menu-design, spacing-item-padding); + margin: getCssVar(menu-design, spacing-item-margin); + font-size: getCssVar(menu-design, font-item-font-size); + background-color: getCssVar(menu-design, color-item-bg); + border-radius: getCssVar(menu-design, radius-item-circle); @include m(icon) { - font-size: getCssVar(menu-design, draggable-icon-font-size); - &.#{bem(menu-design, draggable, prefix-icon)} { - margin: getCssVar(menu-design, draggable-prefix-icon-margin); - } - &.#{bem(menu-design, draggable, remove-icon)} { - color: getCssVar(menu-design, draggable-remove-icon-bg-color); - } - &.#{bem(menu-design, draggable, add-icon)} { - color: getCssVar(menu-design, primary-btn-text-color); - } - } - - @include m(item) { - width: 100%; display: flex; align-items: center; - font-size: getCssVar(menu-design, item-font-size); - } - - @include m(item-content) { - flex-grow: 1; - display: flex; - align-items: center; - justify-content: space-between; - padding: getCssVar(menu-design, item-content-padding); - border-bottom: 1px solid getCssVar(menu-design, border-color); + width: getCssVar(menu-design, icon-item-size); + & + .#{bem(menu-design, item, caption)} { + margin: getCssVar(menu-design, spacing-item-caption-margin); + } } - @include m(item-caption) { - display: flex; - align-items: center; + @include m(caption) { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } - @include m(item-icon) { - display: flex; - align-items: center; - width: getCssVar(menu-design, item-icon-width); - margin: getCssVar(menu-design, item-icon-margin); + @include m(checkbox) { + padding: getCssVar(menu-design, spacing-item-checkbox-margin); + margin-left: auto; } } } diff --git a/src/control/app-menu/custom-menu-design/custom-menu-design.tsx b/src/control/app-menu/custom-menu-design/custom-menu-design.tsx index 8332b6d5cd3..5968482ee9a 100644 --- a/src/control/app-menu/custom-menu-design/custom-menu-design.tsx +++ b/src/control/app-menu/custom-menu-design/custom-menu-design.tsx @@ -1,23 +1,16 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -import { AppMenuController } from '@ibiz-template/runtime'; +import { AppMenuController, IModal } from '@ibiz-template/runtime'; import { useNamespace } from '@ibiz-template/vue3-util'; -import { IAppMenuItem } from '@ibiz/model-core'; -import draggable from 'vuedraggable'; -import { defineComponent, PropType, ref, watch } from 'vue'; +import { IAppMenuItem, ISysImage } from '@ibiz/model-core'; +import { defineComponent, PropType, ref, VNode } from 'vue'; +import { recursiveIterate } from '@ibiz-template/core'; +import { getDefaultIconVal } from '../menu-render-util'; import './custom-menu-design.scss'; /** * 处理菜单自定义配置 - * - * @param {AppMenuController} c - * @param {IData[]} items - * @return {*} */ export const MenuDesign = defineComponent({ name: 'IBizMenuDesign', - components: { - draggable, - }, props: { controller: { type: Object as PropType, @@ -27,159 +20,138 @@ export const MenuDesign = defineComponent({ type: Boolean, default: false, }, + modal: { + type: Object as PropType, + }, + }, + emits: { + close: () => true, }, - emits: ['close'], setup(props, { emit }) { const ns = useNamespace('menu-design'); const c = props.controller; - // 底部导航菜单 - const mobMenuItems = ref([]); + const menuMap = ref(new Map()); + const allMenuMap = new Map(); - // 更多导航菜单 - const moreMenuItems = ref([]); + // 初始化数据 + const initData = () => { + [...c.state.mobMenuItems, ...c.saveConfigs].forEach(item => { + if (item.id) { + menuMap.value.set(item.id, item); + } + }); - const calcMenuItems = () => { - const menuItems = - c.model.appMenuItems?.filter( - item => - item.hidden !== true && - item.itemType === 'MENUITEM' && - c.state.menuItemsState[item.id!].visible, - ) || []; - mobMenuItems.value = [...c.state.mobMenuItems]; - moreMenuItems.value = menuItems.filter( - menu => !c.state.mobMenuItems.find(_item => menu.id === _item.id), + recursiveIterate( + { appMenuItems: c.state.mobAllMenuItems }, + (item: IAppMenuItem) => { + if (item.id) { + allMenuMap.set(item.id, item); + } + }, + { + childrenFields: ['appMenuItems'], + }, ); }; - watch( - () => props.show, - () => { - if (props.show) calcMenuItems(); - }, - { - immediate: true, - }, - ); + initData(); + + // 获取树节点项显示图标 + const getItemIcon = ( + item: IAppMenuItem, + isGroup?: boolean, + ): ISysImage | void => { + if (item.sysImage) return item.sysImage; - /** - * 关闭 - * - */ + switch (c.model.appMenuStyle) { + case 'ICONVIEW': + // 图标视图只有项有默认图标 + return !isGroup ? getDefaultIconVal() : undefined; + case 'LISTVIEW': + // 列表视图没有默认图标 + return; + default: + return getDefaultIconVal(); + } + }; + + // 关闭 const onClose = () => { emit('close'); + props.modal?.dismiss(); + menuMap.value.clear(); }; - /** - * 保存 - * - */ + // 保存 const onSave = async () => { - await c.customController!.saveCustomModelData( - mobMenuItems.value.map(menu => { - return { id: menu.id }; - }), - ); - c.state.mobMenuItems = mobMenuItems.value; + const curMenuItems = Array.from(menuMap.value.values()).map(item => { + return allMenuMap.get(item.id); + }); + c.saveCustomMenusModel(curMenuItems as IAppMenuItem[]); onClose(); }; - /** - * 处理添加或删除 - * - * @param {('nav' | 'more')} type - * @param {number} index - */ - const handleRemoveOrAdd = (type: 'nav' | 'more', index: number) => { - if (type === 'nav') { - const item = mobMenuItems.value[index]; - mobMenuItems.value.splice(index, 1); - moreMenuItems.value.push(item); + // 处理节点项点击 + const handleItemClick = (item: IAppMenuItem, checked: boolean) => { + // 处理选中状态 + if (checked) { + menuMap.value.delete(item.id || ''); } else { - const item = moreMenuItems.value[index]; - moreMenuItems.value.splice(index, 1); - mobMenuItems.value.push(item); + menuMap.value.set(item.id || '', item); } }; - /** - * 绘制拖拽区 - * - * @param {IAppMenuItem[]} menuItems - */ - const renderDraggable = ( - type: 'nav' | 'more', - menuItems: IAppMenuItem[], - ) => { + // 绘制树节点项内容 + const renderNodeItem = ( + _item: IAppMenuItem, + _level: number, + isGroup?: boolean, + ): VNode => { + const marginLeft = `calc(${_level} * var(${ns.cssVarBlockName( + 'spacing-node-item-margin-left', + )}))`; + // eslint-disable-next-line prefer-const + let checked = menuMap.value.has(_item.id || ''); return ( - handleItemClick(_item, checked)} > - {{ - item: ({ - element, - index, - }: { - element: IAppMenuItem; - index: number; - }) => { - return ( -
- handleRemoveOrAdd(type, index)} - > -
-
- -
{element.caption}
-
- -
-
- ); - }, - }} -
+ +
{_item.caption}
+ +
); }; - const renderMenuList = (type: 'nav' | 'more') => { + // 绘制树节点 + const renderNode = ( + _item: IAppMenuItem, + _level: number, + ): VNode | Array => { + if (_item.appMenuItems && _item.appMenuItems.length > 0) { + return [ + renderNodeItem(_item, _level, true), + ...(_item.appMenuItems?.map(item => { + return renderNode(item, _level + 1); + }) as Array), + ] as Array; + } + return renderNodeItem(_item, _level); + }; + + // 绘制树 + const renderTree = (items: IAppMenuItem[]) => { return ( -
-
- {type === 'nav' - ? ibiz.i18n.t('control.appmenu.bottomNav') - : ibiz.i18n.t('control.appmenu.more')} -
- {renderDraggable( - type, - type === 'nav' ? mobMenuItems.value : moreMenuItems.value, - )} -
+
{items.map(item => renderNode(item, 0))}
); }; @@ -188,7 +160,7 @@ export const MenuDesign = defineComponent({ ns, onSave, onClose, - renderMenuList, + renderTree, }; }, @@ -209,8 +181,11 @@ export const MenuDesign = defineComponent({
- {this.renderMenuList('nav')} - {this.renderMenuList('more')} + {this.c.state.mobAllMenuItems.length ? ( + this.renderTree(this.c.state.mobAllMenuItems) + ) : ( + + )}
); -- Gitee From 69133d660c9aeb52fc03633bc88595371d75b395 Mon Sep 17 00:00:00 2001 From: lijianxiong <1518062161@qq.com> Date: Fri, 31 Oct 2025 15:29:33 +0800 Subject: [PATCH 4/8] =?UTF-8?q?feat=EF=BC=9A=E5=B0=86=E6=A0=87=E5=87=86?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E5=AE=9A=E5=88=B6=E6=8C=89=E9=92=AE=E7=9A=84?= =?UTF-8?q?=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=E8=B0=83=E6=95=B4=E4=B8=BA?= =?UTF-8?q?=E9=80=9A=E7=94=A8=E7=9A=84=E5=AE=9A=E5=88=B6=E6=8C=89=E9=92=AE?= =?UTF-8?q?=E7=82=B9=E5=87=BB=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/control/app-menu/app-menu.tsx | 43 ++++++++----------- src/control/app-menu/menu-render-util.tsx | 50 +++++++++++++++++++++++ 2 files changed, 67 insertions(+), 26 deletions(-) create mode 100644 src/control/app-menu/menu-render-util.tsx diff --git a/src/control/app-menu/app-menu.tsx b/src/control/app-menu/app-menu.tsx index 61bfbbdbff5..a4055f5e69d 100644 --- a/src/control/app-menu/app-menu.tsx +++ b/src/control/app-menu/app-menu.tsx @@ -4,7 +4,7 @@ import { useControlController, useNamespace } from '@ibiz-template/vue3-util'; import { IAppMenu, IAppMenuItem } from '@ibiz/model-core'; import { AppMenuController, IControlProvider } from '@ibiz-template/runtime'; import { isNil } from 'ramda'; -import { MenuDesign } from './custom-menu-design/custom-menu-design'; +import { useMenuRender } from './menu-render-util'; import './app-menu.scss'; export const AppMenuControl = defineComponent({ @@ -32,7 +32,7 @@ export const AppMenuControl = defineComponent({ const c = useControlController((...args) => new AppMenuController(...args)); const ns = useNamespace(`control-${c.model.controlType!.toLowerCase()}`); const activeName = ref(); - const showPopup = ref(false); + const { onCustomizedClick } = useMenuRender(c); // 路由对象 const route = useRoute(); // 计算当前路由匹配菜单 @@ -48,16 +48,12 @@ export const AppMenuControl = defineComponent({ }); }; - const setPopupState = (state: boolean): void => { - showPopup.value = state; - }; - const onTabChange = async ( active: string, event?: MouseEvent, opts: IData = {}, ) => { - if (active === 'customized') return setPopupState(true); + if (active === 'customized') return onCustomizedClick(); activeName.value = active; await c.onClickMenuItem(active, event, true, opts); }; @@ -66,7 +62,7 @@ export const AppMenuControl = defineComponent({ const allItems = c.getAllItems(); // 默认激活的菜单项 const defaultActiveMenuItem = allItems.find(item => { - return item.openDefault && !item.hidden; + return item.openDefault && c.isMobMenuItemValid(item); }); if ( defaultActiveMenuItem && @@ -94,15 +90,14 @@ export const AppMenuControl = defineComponent({ return { c, ns, - showPopup, activeName, onTabChange, - setPopupState, }; }, render() { const { model, state } = this.c; if (!state.isCreated) return; + return (
{{ icon: () => ( -
+
{!isNil(counterNum) && ( )}
), - default: () => {item.caption}, + default: () => ( + + {item.caption} + + ), }} ); @@ -145,7 +144,11 @@ export const AppMenuControl = defineComponent({ {model.enableCustomized && ( {{ - icon: () => , + icon: () => ( +
+ +
+ ), default: () => ( {ibiz.i18n.t('control.appmenu.more')} ), @@ -153,18 +156,6 @@ export const AppMenuControl = defineComponent({
)} - - this.setPopupState(false)} - /> -
); }, diff --git a/src/control/app-menu/menu-render-util.tsx b/src/control/app-menu/menu-render-util.tsx new file mode 100644 index 00000000000..91c1e1e0afe --- /dev/null +++ b/src/control/app-menu/menu-render-util.tsx @@ -0,0 +1,50 @@ +import { h } from 'vue'; +import { ISysImage } from '@ibiz/model-core'; +import { AppMenuController, IModal } from '@ibiz-template/runtime'; +import { MenuDesign } from './custom-menu-design/custom-menu-design'; + +/** + * 菜单项未配置图标时,默认显示的图标 + * + * @export + * @return {*} {(ISysImage | void)} + */ +export function getDefaultIconVal(): ISysImage | void { + return { cssClass: 'fa fa-th-large' } as ISysImage; +} + +/** + * 菜单绘制工具 + * + * @export + * @param {AppMenuController} c + * @param {Namespace} ns + * @return {*} {{ + * renderCustomized: () => VNode; + * }} + */ +export function useMenuRender(c: AppMenuController): { + onCustomizedClick: () => Promise; +} { + // 定制按钮点击 + const onCustomizedClick = async (): Promise => { + await ibiz.overlay.drawer( + (modal: IModal) => { + return h(MenuDesign, { + modal, + controller: c, + }); + }, + {}, + { + attrs: { + position: 'bottom', + closeable: false, + round: true, + }, + }, + ); + }; + + return { onCustomizedClick }; +} -- Gitee From 4e1008d12ec901ff845df70d5028721feb481a07 Mon Sep 17 00:00:00 2001 From: lijianxiong <1518062161@qq.com> Date: Fri, 31 Oct 2025 15:29:59 +0800 Subject: [PATCH 5/8] =?UTF-8?q?feat=EF=BC=9A=E4=BC=98=E5=8C=96=E6=A0=87?= =?UTF-8?q?=E5=87=86=E8=8F=9C=E5=8D=95=E3=80=81=E5=9B=BE=E6=A0=87=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E3=80=81=E5=88=97=E8=A1=A8=E8=8F=9C=E5=8D=95=E7=AD=89?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E6=A0=B7=E5=BC=8F=EF=BC=8C=E6=A0=B9=E6=8D=AE?= =?UTF-8?q?=E4=B8=BB=E9=A2=98=E8=B0=83=E6=95=B4=E5=8E=9F=E5=88=99=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app-menu-icon-view.scss | 81 +++++++++++-------- .../app-menu-list-view.scss | 45 +++++++---- src/control/app-menu/app-menu.scss | 26 ++++-- 3 files changed, 94 insertions(+), 58 deletions(-) diff --git a/src/control/app-menu-icon-view/app-menu-icon-view.scss b/src/control/app-menu-icon-view/app-menu-icon-view.scss index ada79d74ad9..caf87b29f21 100644 --- a/src/control/app-menu-icon-view/app-menu-icon-view.scss +++ b/src/control/app-menu-icon-view/app-menu-icon-view.scss @@ -1,20 +1,25 @@ $control-appmenu-iconview: ( - 'bg-color': getCssVar('color', 'bg', 2), - 'icon-width': 100%, - 'icon-size': getCssVar('height-control', 'default'), - 'icon-margin': getCssVar('spacing', 'extra-tight'), - 'item-font-size': getCssVar('font-size', 'small'), - 'item-color': getCssVar('color', 'text', 0), - 'item-caption-line-clamp': 2, - 'item-counter-left': calc(100% - getCssVar('spacing', 'tight')), - 'item-padding': getCssVar('spacing', 'base-tight') getCssVar('spacing', 'base'), - 'group-font-size': getCssVar('font-size', 'header-6'), - 'group-bg-color': getCssVar('color', 'bg', 0), - 'group-text-color': getCssVar('color', 'text', 1), - 'group-padding': getCssVar('spacing', 'base'), - 'group-icon-margin-right': getCssVar('spacing', 'extra-tight'), - 'group-counter-left': getCssVar('spacing', 'tight'), - 'group-icon-width': getCssVar('width-icon', 'medium'), + 'color-bg': getCssVar('color', 'bg', 2), // 菜单背景色 + 'color-item-text': getCssVar('color', 'text', 0), // 菜单项文字颜色 + 'color-nodata-ph-item-text': getCssVar('color', 'text', 0), // 分组项无子数据时占位项文字颜色 + 'color-group-item-bg': getCssVar('color', 'bg', 0), // 菜单分组项背景色 + 'color-group-item-text': getCssVar('color', 'text', 1), // 菜单分组项文字颜色 + 'spacing-item-icon-margin': getCssVar('spacing', 'extra-tight'), // 菜单项图标间距 + 'spacing-item-padding': getCssVar('spacing', 'base-tight') + getCssVar('spacing', 'base'), // 菜单项文字间距 + 'spacing-nodata-ph-item-padding': getCssVar('spacing', 'base-tight') + getCssVar('spacing', 'base'), // 菜单分组项无子数据时占位项文字间距 + 'spacing-group-item-padding': getCssVar('spacing', 'base'), // 菜单分组项文字间距 + 'spacing-group-item-icon-margin': 0 getCssVar('spacing', 'extra-tight') 0 0, // 菜单分组项图标间距 + 'spacing-group-item-counter-margin': 0 0 0 getCssVar('spacing', 'tight'), // 菜单分组项计数器间距 + 'font-item-text-font-size': getCssVar('font-size', 'small'), // 菜单项文字字体大小 + 'font-item-icon-font-size': getCssVar('height-control', 'default'), // 菜单项图标字体大小 + 'font-nodata-ph-item-font-size': getCssVar('font-size', 'regular'), // 菜单分组项无子数据时占位项文字字体大小 + 'font-group-item-font-size': getCssVar('font-size', 'header-6'), // 菜单分组项文字字体大小 + 'position-item-counter-left': calc(100% - getCssVar('spacing', 'tight')), // 项计数器左边距 + 'icon-group-item-icon-size': getCssVar('width-icon', 'medium'), // 分组项图标大小 + 'line-clamp-item-caption': 2, // 项文字行数限制 + 'icon-item-icon-size': getCssVar('height-control', 'default'), // 菜单项图标大小 ); @include b(control-appmenu-iconview) { @@ -22,7 +27,8 @@ $control-appmenu-iconview: ( 'control-appmenu-iconview', $control-appmenu-iconview ); - background-color: getCssVar('control-appmenu-iconview', 'bg-color'); + + background-color: getCssVar('control-appmenu-iconview', 'color-bg'); // 项样式 @include e(item) { @@ -33,8 +39,8 @@ $control-appmenu-iconview: ( align-items: center; width: 100%; height: 100%; - padding: getCssVar('control-appmenu-iconview', 'item-padding'); - color: getCssVar('control-appmenu-iconview', 'item-color'); + padding: getCssVar('control-appmenu-iconview', 'spacing-item-padding'); + color: getCssVar('control-appmenu-iconview', 'color-item-text'); } @include m(icon) { @@ -42,8 +48,9 @@ $control-appmenu-iconview: ( display: flex; align-items: center; justify-content: center; - margin-bottom: getCssVar('control-appmenu-iconview', 'icon-margin'); - font-size: getCssVar('control-appmenu-iconview', 'icon-size'); + width: getCssVar('control-appmenu-iconview', 'icon-item-icon-size'); + margin-bottom: getCssVar('control-appmenu-iconview', 'spacing-item-icon-margin'); + font-size: getCssVar('control-appmenu-iconview', 'font-item-icon-font-size'); .#{bem(icon)} { width: 100%; @@ -54,7 +61,7 @@ $control-appmenu-iconview: ( @include m(counter) { position: absolute; top: 0; - left: getCssVar('control-appmenu-iconview', 'item-counter-left'); + left: getCssVar('control-appmenu-iconview', 'position-item-counter-left'); transform: translateY(-50%); } @@ -63,24 +70,24 @@ $control-appmenu-iconview: ( flex: 1; height: fit-content; overflow: hidden; - font-size: getCssVar('control-appmenu-iconview', 'item-font-size'); + font-size: getCssVar('control-appmenu-iconview', 'font-item-text-font-size'); text-overflow: ellipsis; // 图标菜单最大俩行显示 -webkit-line-clamp: getCssVar( 'control-appmenu-iconview', - 'item-caption-line-clamp' + 'line-clamp-item-caption' ); line-clamp: getCssVar( 'control-appmenu-iconview', - 'item-caption-line-clamp' + 'line-clamp-item-caption' ); -webkit-box-orient: vertical; } } // 分组样式 - @include e(group) { + @include e(group-item) { flex-basis: 100% !important; @include m(container) { width: 100%; @@ -88,16 +95,16 @@ $control-appmenu-iconview: ( @include m(header) { display: flex; align-items: center; - padding: getCssVar('control-appmenu-iconview', 'group-padding'); - font-size: getCssVar('control-appmenu-iconview', 'group-font-size'); - color: getCssVar('control-appmenu-iconview', 'group-text-color'); - background-color: getCssVar('control-appmenu-iconview', 'group-bg-color'); + padding: getCssVar('control-appmenu-iconview', 'spacing-group-item-padding'); + font-size: getCssVar('control-appmenu-iconview', 'font-group-item-font-size'); + color: getCssVar('control-appmenu-iconview', 'color-group-item-text'); + background-color: getCssVar('control-appmenu-iconview', 'color-group-item-bg'); .#{bem(icon)} { - min-width: getCssVar('control-appmenu-iconview', 'group-icon-width'); + min-width: getCssVar('control-appmenu-iconview', 'icon-group-item-icon-size'); margin: getCssVar( 'control-appmenu-iconview', - 'group-icon-margin-right' + 'spacing-group-item-icon-margin' ); } } @@ -106,7 +113,7 @@ $control-appmenu-iconview: ( width: 100%; } @include m(counter) { - margin-left: getCssVar('control-appmenu-iconview', 'group-counter-left'); + margin: getCssVar('control-appmenu-iconview', 'spacing-group-item-counter-margin'); } } @@ -114,3 +121,11 @@ $control-appmenu-iconview: ( padding: 0; } } + +@include b(control-appmenu-iconview-no-data) { + width: 100%; + padding: getCssVar(control-appmenu-iconview, spacing-nodata-ph-item-padding); + font-size: getCssVar(control-appmenu-iconview, font-nodata-ph-item-font-size); + color: getCssVar(control-appmenu-iconview, color-nodata-ph-item-text); + text-align: center; +} diff --git a/src/control/app-menu-list-view/app-menu-list-view.scss b/src/control/app-menu-list-view/app-menu-list-view.scss index 11e2e212808..c060705bdc4 100644 --- a/src/control/app-menu-list-view/app-menu-list-view.scss +++ b/src/control/app-menu-list-view/app-menu-list-view.scss @@ -1,12 +1,16 @@ $control-appmenu-listview: ( - item-padding: getCssVar('spacing', 'tight') getCssVar('spacing', 'base'), - item-color: getCssVar(color, text, 0), - font-size: getCssVar(font-size, regular), - icon-width: getCssVar(font-size, regular), - icon-size: getCssVar(font-size, regular), - icon-margin-right: getCssVar('spacing', 'extra-tight'), - icon-counter-left: getCssVar('spacing', 'tight'), - icon-margin: 0 0 0 getCssVar('spacing', 'extra-tight'), + color-item-text: getCssVar(color, text, 0), // 菜单项文字颜色 + color-nodata-ph-item-text: getCssVar('color', 'text', 0), // 菜单分组项无子数据时占位项文字颜色 + spacing-item-padding: getCssVar('spacing', 'tight') getCssVar('spacing', 'base'), // 菜单项间距 + spacing-group-item-icon-margin: 0 getCssVar('spacing', 'extra-tight') 0 0, // 分组项图标间距 + spacing-icon-counter-margin: 0 0 0 getCssVar('spacing', 'tight'), // 菜单项图标计数器间距 + spacing-item-icon-margin: 0 getCssVar('spacing', 'extra-tight') 0 0, // 菜单项图标间距 + spacing-nodata-ph-item-padding: getCssVar('spacing', 'base-tight') + getCssVar('spacing', 'base'), // 菜单分组项无子数据时占位项间距 + font-item-font-size: getCssVar(font-size, regular), // 菜单项字体大小 + font-icon-font-size: getCssVar(font-size, regular), + font-nodata-ph-item-font-size: getCssVar('font-size', 'regular'), // 菜单分组项无子数据时占位项字体大小 + icon-item-icon-size: getCssVar(font-size, regular), // 菜单项图标大小 ); @include b(control-appmenu-listview) { @@ -19,20 +23,20 @@ $control-appmenu-listview: ( display: flex; align-items: center; - .#{bem(icon)} { - margin-right: getCssVar(control-appmenu-listview, icon-margin-right); + .#{bem(icon)} { + margin: getCssVar(control-appmenu-listview, spacing-group-item-icon-margin); } @include m(counter) { - margin-left: getCssVar(control-appmenu-listview, icon-counter-left); + margin: getCssVar(control-appmenu-listview, spacing-icon-counter-margin); } } @include e(item) { &.#{bem(control-appmenu-listview, item)} { - padding: getCssVar(control-appmenu-listview, item-padding); - font-size: getCssVar(control-appmenu-listview, font-size); - color: getCssVar(control-appmenu-listview, item-color); + padding: getCssVar(control-appmenu-listview, spacing-item-padding); + font-size: getCssVar(control-appmenu-listview, font-item-font-size); + color: getCssVar(control-appmenu-listview, color-item-text); &:last-child { border-width: 0; @@ -52,9 +56,9 @@ $control-appmenu-listview: ( display: flex; align-items: center; justify-content: center; - width: getCssVar(control-appmenu-listview, icon-width); - margin-right: getCssVar(control-appmenu-listview, icon-margin-right); - font-size: getCssVar(control-appmenu-listview, icon-size); + width: getCssVar(control-appmenu-listview, icon-item-icon-size); + margin: getCssVar(control-appmenu-listview, spacing-item-icon-margin); + font-size: getCssVar(control-appmenu-listview, font-icon-font-size); @include b(icon) { display: flex; @@ -64,3 +68,10 @@ $control-appmenu-listview: ( } } } + +@include b(control-appmenu-listview-no-data) { + padding: getCssVar(control-appmenu-listview, spacing-nodata-ph-item-padding); + font-size: getCssVar(control-appmenu-listview, font-nodata-ph-item-font-size); + color: getCssVar(control-appmenu-listview, color-nodata-ph-item-text); + text-align: center; +} diff --git a/src/control/app-menu/app-menu.scss b/src/control/app-menu/app-menu.scss index f7dcb477b4c..75fa333c67b 100644 --- a/src/control/app-menu/app-menu.scss +++ b/src/control/app-menu/app-menu.scss @@ -1,8 +1,7 @@ $control-appmenu: ( - 'menu-height': rem(50px), - 'icon-width': rem(22px), - 'icon-font-size': getCssVar(font-size, header-6), - 'item-counter-left': calc(100% - getCssVar('spacing', 'extra-tight')), + 'height-menu': rem(50px), // 菜单高度 + 'font-item-font-size': getCssVar(font-size, header-6), // 项图标字体大小 + 'position-item-counter-left': calc(100% - getCssVar('spacing', 'extra-tight')), // 项计数器偏移距离 ); @include b(control-appmenu) { @@ -11,21 +10,32 @@ $control-appmenu: ( width: 100%; height: 100%; - @include e(icon-container) { + @include e(item-icon-container) { position: relative; + display: flex; + align-items: center; width: auto; - font-size: getCssVar('control-appmenu', 'icon-font-size'); + font-size: getCssVar('control-appmenu', 'font-item-font-size'); text-align: center; @include m(counter) { position: absolute; top: 0; - left: getCssVar('control-appmenu', 'item-counter-left'); + left: getCssVar('control-appmenu', 'position-item-counter-left'); transform: translateY(-50%); } } + @include e(item-caption) { + white-space: nowrap; + } + @include m(default) { - height: getCssVar('control-appmenu', 'menu-height'); + height: getCssVar('control-appmenu', 'height-menu'); } } + +@include b(control-appmenu-customized) { + display: flex; + align-items: center; +} -- Gitee From d2eca35f389ff07778267170e6cb0e20e9558912 Mon Sep 17 00:00:00 2001 From: lijianxiong <1518062161@qq.com> Date: Fri, 31 Oct 2025 15:31:25 +0800 Subject: [PATCH 6/8] =?UTF-8?q?feat=EF=BC=9A=E9=80=82=E9=85=8D=E5=9B=BE?= =?UTF-8?q?=E6=A0=87=E8=8F=9C=E5=8D=95=E3=80=81=E5=88=97=E8=A1=A8=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E5=9C=A8=E5=88=86=E7=BB=84=E9=A1=B9=E6=97=A0=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=97=B6=E5=91=88=E7=8E=B0=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/control/app-menu-icon-view/app-menu-icon-view.tsx | 10 ++++++++-- src/control/app-menu-list-view/app-menu-list-view.tsx | 7 +++++++ src/locale/en/index.ts | 1 + src/locale/zh-CN/index.ts | 1 + 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/control/app-menu-icon-view/app-menu-icon-view.tsx b/src/control/app-menu-icon-view/app-menu-icon-view.tsx index 774147b8847..070c32ec47b 100644 --- a/src/control/app-menu-icon-view/app-menu-icon-view.tsx +++ b/src/control/app-menu-icon-view/app-menu-icon-view.tsx @@ -63,8 +63,14 @@ export const AppMenuIconViewControl = defineComponent({ border={false} class={ns.em('group-item', 'content')} > - {menuItem.appMenuItems?.map(_item => - renderMenuItem(_item), + {menuItem.appMenuItems?.length ? ( + menuItem.appMenuItems?.map(_item => + renderMenuItem(_item), + ) + ) : ( +
+ {ibiz.i18n.t('control.appmenu.noData')} +
)}
diff --git a/src/control/app-menu-list-view/app-menu-list-view.tsx b/src/control/app-menu-list-view/app-menu-list-view.tsx index 1aff5854f47..b51abda8bb5 100644 --- a/src/control/app-menu-list-view/app-menu-list-view.tsx +++ b/src/control/app-menu-list-view/app-menu-list-view.tsx @@ -38,6 +38,13 @@ export const AppMenuListViewControl = defineComponent({ {{ default: () => { + if (!item.appMenuItems?.length) { + return ( +
+ {ibiz.i18n.t('control.appmenu.noData')} +
+ ); + } return item.appMenuItems?.map(child => { return renderMenuItem(child); }); diff --git a/src/locale/en/index.ts b/src/locale/en/index.ts index f1155eaf4a0..f19e3d1c90a 100644 --- a/src/locale/en/index.ts +++ b/src/locale/en/index.ts @@ -87,6 +87,7 @@ export default { bottomNav: 'Bottom Navigation', customNav: 'Customize Navigation', save: 'Save', + noData: 'No data', }, dataView: { end: 'The end~' }, form: { diff --git a/src/locale/zh-CN/index.ts b/src/locale/zh-CN/index.ts index c3f0a3cc236..12b9348f5d9 100644 --- a/src/locale/zh-CN/index.ts +++ b/src/locale/zh-CN/index.ts @@ -72,6 +72,7 @@ export default { bottomNav: '底部导航', customNav: '自定义导航', save: '保存', + noData: '暂无数据', }, form: { noSupportDetailType: -- Gitee From 041ff8fbb3b3fb8ac832efef43f461a26cf7fe9b Mon Sep 17 00:00:00 2001 From: lijianxiong <1518062161@qq.com> Date: Fri, 31 Oct 2025 15:31:42 +0800 Subject: [PATCH 7/8] =?UTF-8?q?feat=EF=BC=9A=E6=9B=B4=E6=96=B0CHANGELOG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4ac9164039..3ad8e2a9c88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,8 @@ - 新增树部件加载更多和节点绘制器 - 标签编辑器支持转化为代码项文本 - 表单视图消息支持直接内容展示,无效配置为对象格式 +- 新增浮动按钮通用组件 +- 新增图标菜单、列表菜单识别模型配置(支持自定义) ### Change @@ -85,6 +87,10 @@ - 优化搜索栏组件样式,不直接使用基础css变量,组件定义专属变量 - 统一处理界面行为按钮按钮类型、按钮行为级别、按钮样式 - 优化多数据选择编辑器的呈现样式 +- 优化菜单自定义配置组件,支持树形结构数据的绘制与处理 +- 将标准菜单定制按钮的处理逻辑调整为通用的定制按钮点击事件 +- 优化标准菜单、图标菜单、列表菜单等组件样式,根据主题调整原则优化 +- 适配图标菜单、列表菜单在分组项无数据时呈现内容 ### Fixed -- Gitee From aaefa942bb474f64fc3ec5de9c00bffb10479773 Mon Sep 17 00:00:00 2001 From: lijianxiong <1518062161@qq.com> Date: Fri, 31 Oct 2025 16:32:45 +0800 Subject: [PATCH 8/8] =?UTF-8?q?feat=EF=BC=9A=E5=B0=86=E8=8F=9C=E5=8D=95?= =?UTF-8?q?=E9=83=A8=E4=BB=B6=E7=8A=B6=E6=80=81=E5=B1=9E=E6=80=A7mobAllMen?= =?UTF-8?q?uItems=E6=9B=B4=E6=94=B9=E4=B8=BAmobAuthedMenuItems?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app-menu/custom-menu-design/custom-menu-design.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/control/app-menu/custom-menu-design/custom-menu-design.tsx b/src/control/app-menu/custom-menu-design/custom-menu-design.tsx index 5968482ee9a..96d55638d2c 100644 --- a/src/control/app-menu/custom-menu-design/custom-menu-design.tsx +++ b/src/control/app-menu/custom-menu-design/custom-menu-design.tsx @@ -43,7 +43,7 @@ export const MenuDesign = defineComponent({ }); recursiveIterate( - { appMenuItems: c.state.mobAllMenuItems }, + { appMenuItems: c.state.mobAuthedMenuItems }, (item: IAppMenuItem) => { if (item.id) { allMenuMap.set(item.id, item); @@ -181,8 +181,8 @@ export const MenuDesign = defineComponent({
- {this.c.state.mobAllMenuItems.length ? ( - this.renderTree(this.c.state.mobAllMenuItems) + {this.c.state.mobAuthedMenuItems.length ? ( + this.renderTree(this.c.state.mobAuthedMenuItems) ) : ( )} -- Gitee