如果菜单数量少,并且没有其他地方需要共用菜单列表,建议参考react-contexify的右键菜单属性disabled不起作用
此文中处理方法相对快捷
关于为何此插件无法根据state动态更新上文中作者已经说明,这里不在赘述
此方法是根据需要展开右键菜单栏目搭配数量无限增加
一般情况大概这样
const menu = { menuId: '2', items: [ { key: 1, name: 'xxxx', handler: ({props}) => { xxxxx(props); }}, { key: 2, name: 'xxxx', handler: ({props}) => { xxxxx(props); }}, { key: 3, name: 'xxxx', handler: ({props}) => { xxxxx(props); }}, { key: 4, name: 'xxxx', handler: ({props}) => { xxxxx(props); }}, { key: 5, name: 'xxxx', handler: ({props}) => { xxxxx(props); }}, { key: 6, name: 'xxxx', handler: ({props}) => { xxxxx(props); }}, ], }; const ContextMenu = () => ( <Menu id={menu.menuId}> {roleInfoMenu.items.map(item => ( // 这里需要加key,不然要报错 <Item key={item.key} onClick={item.handler}>{item.name}</Item> ))} </Menu> ); const { show } = useContextMenu({ id: '2', }); const handleContextMenu = (event, node) => { event.preventDefault(); show(event,{ props: node, }); }; const rightContextMenu = (e, data) => { e.preventDefault(); handleContextMenu(e, data); }; return ( <div> **** { arr.map(item => { **** **** <ContextMenu /> }); **** </div> );
首先修改菜单数组,增加一个字段用来和实际内容状态判断是否需要此菜单
const menu = { menuId: '2', items: [ { key: 1, showStatusArr: [0, 1] name: 'xxxx', handler: ({props}) => { xxxxx(props); }}, { key: 2, showStatusArr: [0, 3], name: 'xxxx', handler: ({props}) => { xxxxx(props); }}, { key: 3, showStatusArr: [2], name: 'xxxx', handler: ({props}) => { xxxxx(props); }}, { key: 4, showStatusArr: [4], name: 'xxxx', handler: ({props}) => { xxxxx(props); }}, { key: 5, showStatusArr: [0, 5], name: 'xxxx', handler: ({props}) => { xxxxx(props); }}, { key: 6, showStatusArr: [5], name: 'xxxx', handler: ({props}) => { xxxxx(props); }}, ], };
然后直接改写ContextMenu的生成逻辑,根据条件判断来生成菜单
****** const ContextMenu = ({Status}) => ( <ContentMenu id={menu.menuId}> { menu.items.map(item => { // 这里需要加key,不然要报错 if(item.showStatusArr instanceof Array && item.showStatusArr.includes(Status)){ return <Item key={item.key} onClick={item.handler} > {item.name} </Item> } }) } </ContentMenu> ); ****** return ( <div> ... { arr.map(item => { **** **** <ContextMenu Status={item.Status} /> }); ... </div> );
此时出来的效果变成
因为Menu所在的DOM id是一样的,N条数据执行完之会把Menu的内容改写为最后一次渲染的,并且还打乱了Menu的布局
于是就想出了为每条记录单独创建一个id的Menu,执行useContextMenu()时动态为其指定id,这样就能保证每条记录对应的菜单栏目是事先根据状态生成好的,达到伪动态的目的
这做法缺点很明显,有几种组合,就要在右键点击时判断几种然后更新state的值,凭空增加了很多无用代码量
// 这两个主要是用来作监听触发展开右键菜单操作的,如果是类组件,可以不用存储鼠标点击事件和点击的栏目内容 const [rightMenuId, setRightMenuId] = useState(); const [rightClickEve, setRightClickEve] = useState(); // 这个用来存储右键点击的栏目内容, const [rightClickItem, setRightClickItem] = useState(); // 这里同时监听右键点击的对应右键菜单id和右键事件,然后去触发展开ContextMenu的方法 useEffect(() => { if(rightMenuId){ handleContextMenu(rightClickEve, rightClickItem); } }, [rightMenuId,rightClickEve]); const handleMenu = { draft: 'draftMenu', submit: 'submitMenu', reject: 'rejectMenu', access: 'accessMenu', public: 'publicMenu', report: 'reportMenu', items: [ { key: 'edit', showStatusArr: [0,2], name: <span><EditOutlined />编辑</span>, handler: ({props}) => { rightContentClick({key: 'edit', item: {props}}) }}, { key: 'submit', showStatusArr: [0,2], name: <span><CheckOutlined />提交</span>, handler: ({props}) => { rightContentClick({key: 'submit', item: {props}}) }}, { key: 'delete', showStatusArr: [0,5], name: <span style={{color: 'red'}}><DeleteOutlined />删除</span>, handler: ({props}) => { rightContentClick({key: 'delete', item: {props}}) }}, { key: 'rollback', showStatusArr: [1], name: <span><RollbackOutlined />撤销</span>, handler: ({props}) => { rightContentClick({key: 'rollback', item: {props}}) }}, { key: 'showReason', showStatusArr: [2, 5], name: <span><EyeOutlined />查看原因</span>, handler: ({props}) => { rightContentClick({key: 'showReason', item: {props}}) }}, { key: 'public', showStatusArr: [3], name: <span><NodeExpandOutlined />上架</span>, handler: ({props}) => { rightContentClick({key: 'public', item: {props}}) }}, { key: 'reEdit', showStatusArr: [3], name: <span style={{color: 'red'}}><EditOutlined />重新修改</span>, handler: ({props}) => { rightContentClick({key: 'reEdit', item: {props}}) }}, { key: 'cancel', showStatusArr: [4], name: <span><NodeCollapseOutlined />下架</span>, handler: ({props}) => { rightContentClick({key: 'cancel', item: {props}}) }}, { key: 'update', showStatusArr: [4], name: <span><ArrowUpOutlined />升级模板</span>, handler: ({props}) => { rightContentClick({key: 'update', item: {props}}) }}, { key: 'appeal', showStatusArr: [5], name: <span><SoundOutlined />申诉</span>, handler: ({props}) => { rightContentClick({key: 'appeal', item: {props}}) }}, { key: 'history', showStatusArr: [0,1,2,3,4,5], name: <span style={{color: '#b99c00'}}><HistoryOutlined />操作历史</span>, handler: ({props}) => { rightContentClick({key: 'history', item: {props}}) }}, ], }; const ContextMenu = ({divId, templateStatus}) => ( <ContentMenu id={divId}> { handleMenu.items.map(item => { if(item.showStatusArr instanceof Array && item.showStatusArr.includes(templateStatus)){ return <Item key={item.key} onClick={item.handler} > {item.name} </Item> } }) } </ContentMenu> ); const { show } = useContextMenu({ id: rightMenuId, }); const handleContextMenu = (event, node) => { event.preventDefault(); show(event,{ props: node, }); }; const onRightClick = (e, item) => { // 由于下面延时触发了handleContexte,不加这行会把浏览器本身右键菜单也触发了 e.preventDefault(); if(item.templateStatus === 0){ setRightMenuId(handleMenu.draft); }else if(item.templateStatus === 1){ setRightMenuId(handleMenu.submit); }else if(item.templateStatus === 2){ setRightMenuId(handleMenu.reject); }else if(item.templateStatus === 3){ setRightMenuId(handleMenu.access); }else if(item.templateStatus === 4){ setRightMenuId(handleMenu.public); }else if(item.templateStatus === 5){ setRightMenuId(handleMenu.report); }else{ setRightMenuId(handleMenu.draft) } // 由于state异步的问题,不能直接在右击时同步去调用,会出现双击右键才能展开的问题 // 类组件可以直接写在state的回调函数里,这里用的函数式组件所以在上面钩子里监听变化来调用 // handleContextMenu(e, item); }; const generatorDivId = (templateStatus) => { if(templateStatus === 0){ return handleMenu.draft; }else if(templateStatus === 1){ return handleMenu.submit; }else if(templateStatus === 2){ return handleMenu.reject; }else if(templateStatus === 3){ return handleMenu.access; }else if(templateStatus === 4){ return handleMenu.public; }else if(templateStatus === 5){ return handleMenu.report; }else{ return handleMenu.draft; } }; return ( <div> ... { arr.map(item => { <div onContextMenu={(e) => onRightClick(e, item)} title='右键点击展开操作菜单'> **** **** <ContextMenu divId={generatorDivId(item.templateStatus)} templateStatus={item.templateStatus} /> </div> }); ... </div> );
效果如下
方式不是很优美,不过好歹满足了要求,还是开头第一句话,如果菜单本身数量少,菜单列表也无需json格式的话采取开头文章中作者的方法更简单快捷。