{!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)}
- >
-
-
- );
- },
- }}
-
+
+ {_item.caption}
+
+
);
};
- const renderMenuList = (type: 'nav' | 'more') => {
+ // 绘制树节点
+ const renderNode = (
+ _item: IAppMenuItem,
+ _level: number,
+ ): VNode | Array