refactor(left-menu): 重构左侧菜单组件状态管理

- 添加 useState 和 useEffect 钩子管理菜单状态
- 将菜单展开状态和激活菜单项改为状态变量
- 实现动态子菜单列表管理功能
- 修复菜单项点击事件类型转换问题
- 优化菜单展开状态更新逻辑
- 删除过时的付款编码字段定义
This commit is contained in:
shenyifei 2026-01-08 11:39:38 +08:00
parent f36678fe6c
commit f68b148319
2 changed files with 116 additions and 112 deletions

View File

@ -1,14 +1,14 @@
import {MenuDataItem} from '@ant-design/pro-components'; import { MenuDataItem } from '@ant-design/pro-components';
import {getMatchMenu} from '@umijs/route-utils'; import { getMatchMenu } from '@umijs/route-utils';
import {Layout, Menu} from 'antd'; import { Layout, Menu } from 'antd';
import {MenuItemType} from 'antd/es/menu/interface'; import { MenuItemType } from 'antd/es/menu/interface';
import React from 'react'; import React, { useEffect, useState } from 'react';
import useButtonStyle from './style.style'; import useButtonStyle from './style.style';
interface ILeftMenuProps { interface ILeftMenuProps {
menuData: MenuDataItem[]; menuData: MenuDataItem[];
pathname: string; pathname: string;
onClick: (item: MenuItemType) => void; onClick: (item: MenuItemType) => void;
} }
// 递归 menuData tree 树状结构 增加 label 参数 // 递归 menuData tree 树状结构 增加 label 参数
@ -18,116 +18,122 @@ const notNullArray = (value: any[]) => Array.isArray(value) && value.length > 0;
* hideInMenu item.name * hideInMenu item.name
*/ */
const defaultFilterMenuData = (menuData: MenuDataItem[] = []): MenuItemType[] => const defaultFilterMenuData = (menuData: MenuDataItem[] = []): MenuItemType[] =>
menuData menuData
.filter( .filter(
(item) => (item) =>
item && item &&
(item.name || notNullArray(item.children as MenuItemType[])) && (item.name || notNullArray(item.children as MenuItemType[])) &&
!item.hideInMenu, !item.hideInMenu,
) )
.map((item) => { .map((item) => {
const newItem = { ...item }; const newItem = { ...item };
const routerChildren = newItem.children || []; const routerChildren = newItem.children || [];
delete newItem.children; delete newItem.children;
if ( if (
notNullArray(routerChildren) && notNullArray(routerChildren) &&
!newItem.hideInMenu && !newItem.hideInMenu &&
routerChildren.some((child) => child && !!child.name) routerChildren.some((child) => child && !!child.name)
) { ) {
const newChildren = defaultFilterMenuData(routerChildren); const newChildren = defaultFilterMenuData(routerChildren);
if (newChildren.length) if (newChildren.length)
return { return {
key: item.key, key: item.key,
label: item.name, label: item.name,
name: item.name, name: item.name,
path: item.path, path: item.path,
children: newChildren, children: newChildren,
// type: 'group', // type: 'group',
} as unknown as MenuItemType; } as unknown as MenuItemType;
} }
return { return {
key: newItem.key, key: newItem.key,
label: newItem.name, label: newItem.name,
name: item.name, name: item.name,
path: newItem.path, path: newItem.path,
} as MenuItemType; } as MenuItemType;
}) })
.filter((item) => item); .filter((item) => item);
const LeftMenu: React.FC<ILeftMenuProps> = (props) => { const LeftMenu: React.FC<ILeftMenuProps> = (props) => {
const { styles } = useButtonStyle(); const { styles } = useButtonStyle();
const { pathname, menuData, onClick } = props; const { pathname, menuData, onClick } = props;
if (!menuData) { // 根据提供的 menuData 提供的 tree 结构,获取第一层等级菜单
return null; // moduleMenu
} const moduleMenu = menuData.map((item: MenuDataItem) => {
return {
key: item.key,
label: item.name,
path: item.path,
} as MenuItemType;
});
// 根据提供的 menuData 提供的 tree 结构,获取第一层等级菜单 const [openKeys, setOpenKeys] = useState<string[]>([]);
// moduleMenu const [activeMenuKey, setActiveMenuKey] = useState<string[]>();
const moduleMenu = menuData.map((item: MenuDataItem) => { const [subMenuList, setSubMenuList] = useState<MenuItemType[]>([]);
return {
key: item.key,
label: item.name,
path: item.path,
} as MenuItemType;
});
// 通过循环 moduleMenu 判断 history.location.pathname 是否包含 key useEffect(() => {
const activeMenu = getMatchMenu(pathname, menuData, true); // 通过循环 moduleMenu 判断 history.location.pathname 是否包含 key
// pluck activeMenu key const activeMenu = getMatchMenu(pathname, menuData, true);
const activeMenuKey = activeMenu.map( const activeMenuKey = activeMenu.map(
(item: MenuDataItem) => item.key as string, (item: MenuDataItem) => item.key as string,
); );
const openKeys = activeMenu.flatMap((item: MenuDataItem) => item.children).map(
(item: any) => item?.key as string,
);
let subMenu: MenuItemType[] = []; setSubMenuList(
if (activeMenu[0]?.children) { activeMenu[0]?.children
subMenu = defaultFilterMenuData(activeMenu[0]?.children); ? defaultFilterMenuData(activeMenu[0]?.children)
} : [],
);
setActiveMenuKey(activeMenuKey);
setOpenKeys([...openKeys, ...activeMenuKey]);
}, [pathname]);
return ( if (!menuData) {
<> return null;
<div }
className={styles.box}
style={{ return (
// @ts-ignore <>
'--box-with': subMenu.length > 0 ? '248px' : '100px', <div
}} className={styles.box}
></div> style={{
<Layout.Sider // @ts-ignore
className={styles.sider} '--box-with': subMenuList.length > 0 ? '248px' : '100px',
style={{ position: 'fixed' }} }}
width={'auto'} ></div>
theme={'light'} <Layout.Sider
> className={styles.sider}
<Menu style={{ position: 'fixed' }}
items={moduleMenu} width={'auto'}
className={styles.moduleMenu} theme={'light'}
selectedKeys={activeMenuKey} >
theme={'dark'} <Menu
onClick={(item) => { items={moduleMenu}
onClick(item); className={styles.moduleMenu}
}} selectedKeys={activeMenuKey}
/> theme={'dark'}
{subMenu.length > 0 && ( onClick={(item) => {
<Menu onClick(item as any);
items={subMenu} }}
className={styles.subMenu} />
selectedKeys={activeMenuKey} {subMenuList.length > 0 && (
openKeys={openKeys} <Menu
mode="inline" items={subMenuList}
inlineIndent={12} className={styles.subMenu}
onClick={(item) => { selectedKeys={activeMenuKey}
onClick(item); openKeys={openKeys}
}} onOpenChange={(keys) => setOpenKeys(keys)}
/> mode="inline"
)} inlineIndent={12}
</Layout.Sider> onClick={(item) => {
</> onClick(item as any);
); }}
/>
)}
</Layout.Sider>
</>
);
}; };
export default LeftMenu; export default LeftMenu;

View File

@ -4908,8 +4908,6 @@ declare namespace BusinessAPI {
taskName: string; taskName: string;
/** 任务类型1-瓜农付款任务 */ /** 任务类型1-瓜农付款任务 */
taskType: 'MELON_FARMER'; taskType: 'MELON_FARMER';
/** 付款编码 */
paymentTaskSn: string;
/** 付款任务对象ID */ /** 付款任务对象ID */
targetId: string; targetId: string;
/** 付款总金额 */ /** 付款总金额 */