import React, { useState, useEffect } from 'react'; import { Input, Button, notification, Spin, message, Modal } from 'antd'; import { CheckOutlined, PlusOutlined } from '@ant-design/icons'; import { connect } from 'react-redux'; import classnames from 'classnames'; import * as _ from 'lodash'; import PinyinMatch from 'pinyin-match'; import PandaEmpty from '@wisdom-components/empty'; import { useHistory } from '@wisdom-utils/runtime'; import { actionCreators } from '@/containers/App/store'; import { appService } from '@/api'; import { savePagePartInfo } from '@/api/service/base'; import styles from './index.less'; import thumbnail from '../../assets/images/commonMenu/常用菜单.png'; import pageLogo from '../../assets/images/commonMenu/page-logo.png'; import starIcon from '../../assets/images/commonMenu/矢量智能对象 拷贝 3@2x.png'; // 是否是灰色的图标(灰色图标在白色背景中看不见,添加滤镜变色) const isNeedFilterIcon = (icon = '') => icon && !icon.includes('一级') && !icon.includes('ios/'); // Modal.config({ // rootPrefixCls: 'panda-console-base' // }); const CommonMenu = props => { const history = useHistory(); const { menus } = props; const [commonMenus, setCommonMenus] = useState([]); // 收藏菜单信息 const [menuList, setMenuList] = useState([]); const [searchInfo, setSearchInfo] = useState(''); const [loading, setLoading] = useState(true); // loading显示隐藏 const [isShowMenuModal, setIsShowMenuModal] = useState(false); /** * 获取收藏的菜单信息 */ const fetchMenus = () => { appService .getPagePartInfo({ UserID: window?.globalConfig?.userInfo?.OID ?? '', }) .then(res => { setLoading(false); if (res.say.statusCode !== '0000') return notification.error({ message: '服务错误', description: res.say.errMsg, }); const newMenus = []; // 过滤出当前client的菜单(widget_client_xxx) const data = res.getMe.filter(item => { const client = item.PartID.split('_')[1] ?? ''; return client === window.globalConfig.client; }); data.forEach(item => { const newMenu = { icon: item.icon, name: item.PartName, path: item.PartUrl, pic: item.BgPicUrl, id: item.PartID, }; // 所属产品 const [product] = item.PartUrl.split('/').filter(d => !!d); newMenu.product = product; // 所属一级分组 const PartIDArr = item.PartID.split('_'); newMenu.topGroup = PartIDArr.splice(2, 1).join('_'); newMenus.push(newMenu); }); setCommonMenus(newMenus); }) .catch(error => { notification.error({ message: '服务错误', description: error, }); }); }; useEffect(() => { fetchMenus(); }, []); useEffect(() => { const isAddedMenu = menu => { const menuTmp = commonMenus.find(item => item.id === menu.extData.PartID); return !!menuTmp; }; const loop = (data, prePartID) => { const newData = []; data.forEach(item => { const { name, level, href, key, path, routes } = item; const newmenu = { ...item }; newmenu.key = href ? key : path; newmenu.title = name; // 菜单进行搜索过滤 const index = name.indexOf(searchInfo); const beforeStr = name.substr(0, index); const afterStr = name.substr(index + searchInfo.length); const title = index > -1 ? ( <span> {beforeStr} <span className={styles.treeSearchInfo}>{searchInfo}</span> {afterStr} </span> ) : ( <span>{name}</span> ); newmenu.title = title; newmenu.extData = { ...item.extData, PartID: `${prePartID}_${name}`, }; newmenu.extData.isAdded = isAddedMenu(newmenu); if (routes) { newmenu.children = loop(routes, `${prePartID}_${name}`); } if (routes && newmenu.children && newmenu.children.length) { newData.push(newmenu); } else if (!routes && index > -1) { newData.push(newmenu); } }); return newData; }; const newmenus = loop(menus, `widget_${window.globalConfig.client}`); setMenuList(newmenus); }, [menus, searchInfo, commonMenus]); /** * 菜单跳转 * @param {*} menu */ const linkToMenu = menu => { const { name, path, topGroup } = menu; const currentIndex = props.menus.findIndex(item => item.name === topGroup); let currentRoutes = props.flatMenu.find(item => item.path === path && item.name === name); // let currentRoutes = props.menus[currentIndex] && props.menus[currentIndex].routes; // currentRoutes = currentRoutes.find(item => item.path === path); if(currentRoutes) { history.push(path); props.updateCurrentIndex(currentIndex); window.share && window.share.event.emit('trigger:updateMenuIndex', currentIndex); window.share && window.share.event && window.share.event.emit('event:updateCurrentChildrenRoutes', { currentPath: path, currentRoute: currentRoutes, selectedIndex: currentIndex }); } }; const mdOk = submitData => { setLoading(true); savePagePartInfo({ query: { UserID: window.globalConfig?.userInfo?.OID ?? '', }, data: submitData, }) .then(res => { setLoading(false); if (res.statusCode === '0000') { message.success('修改常用菜单成功!'); setIsShowMenuModal(false); fetchMenus(); } else { message.error('修改常用菜单失败!'); } }) .catch(err => { setLoading(false); }); }; const mdCancel = () => { setIsShowMenuModal(false); }; const rednerMenuCardData = () => { const menuCardData = []; commonMenus && commonMenus.forEach(item => { const { name } = item; const m = PinyinMatch.match(name, searchInfo); if (!m) return; const [before, after] = m; const beforeStr = name.slice(0, before); const centerStr = name.slice(before, after + 1); const afterStr = name.slice(after + 1); const title = after > -1 ? ( <span> {beforeStr} <em style={{ fontStyle: 'normal', color: 'orange' }}> {centerStr} </em> {afterStr} </span> ) : ( <span>{name}</span> ); menuCardData.push( <MenuCard menu={{ ...item, title }} linkToMenu={linkToMenu} key={item.id} />, ); }); return menuCardData.length > 0 ? menuCardData : <PandaEmpty />; }; return ( <div className={styles.commonMenu}> <Spin spinning={loading} style={{height: '100%'}}> <div className={styles.searchWrapper}> <div className={styles.searchBox}> <div className={styles.searchTitle}> <img style={{ width: '20px', marginRight: '0.5em' }} src={starIcon} alt="" /> <span>我的常用菜单</span> <span>({commonMenus.length})</span> <PlusOutlined className={styles.addIcon} title="添加常用菜单" onClick={() => { setIsShowMenuModal(true); }} /> </div> <div className={styles.searchInput}> <Input.Search maxLength={50} width={400} placeholder="搜索功能菜单" onChange={e => { setSearchInfo(e.target.value); }} /> </div> </div> {/* <Button className={styles.searchBtn} onClick={() => { setIsShowMenuModal(true); }} > 添加菜单 </Button> */} </div> <MenuAddModal isShowMenuModal={isShowMenuModal} setIsShowMenuModal={setIsShowMenuModal} menuList={menuList} mdOk={mdOk} mdCancel={mdCancel} /> <div className={styles.menuCardWrapper}>{rednerMenuCardData()}</div> <div className={styles.pageLogo}> <img src={pageLogo} alt="" /> </div> </Spin> </div> ); }; // 搜藏菜单卡片展示 const MenuCard = ({ menu, linkToMenu }) => { const { icon, name, title, topGroup, menuID, path, pic } = menu; return ( <div className={styles.menuCard} onClick={() => linkToMenu(menu)}> {/* <Link to={menuUrl} title={menuName}> */} <img className={styles.cardThumbnail} src={pic || thumbnail} alt="" /> <div className={styles.cardLabel}> <div className={classnames( styles.cardTitle, isNeedFilterIcon(icon) ? styles.filterIconBox : '', )} > <span className={styles.iconBox}> <img className={styles.cardIcon} src={icon} alt="" /> </span> <span className={styles.cardName}>{title || name}</span> </div> <div className={styles.cardGroup}>{topGroup}</div> </div> {/* </Link> */} </div> ); }; const ModalTitle = () => <p className={styles.modalTitle}>添加常用菜单</p>; const MenuAddModal = props => { const { isShowMenuModal, menuList, mdOk, mdCancel } = props; const [tempMenuList, setTempMenuList] = useState([]); // 模态框菜单列表数据 const [submitData, setSubmitData] = useState([]); const [searchInfo, setSearchInfo] = useState(''); useEffect(() => { const newSubmitData = []; const loop = data => { data && data.forEach(item => { if (item.href && item.extData.isAdded) { newSubmitData.push({ PartID: item.extData.PartID, PartName: item.name, PartUrl: item.path, icon: item.extData.icon, }); } item.children && loop(item.children); }); }; loop(menuList); setSubmitData(newSubmitData); }, [menuList, isShowMenuModal]); useEffect(() => { let newTempMenuList = menuList.map(item => { const childMenus = []; const deep = (data = []) => { data.forEach(d => { if (d.href) { const m = PinyinMatch.match(d.name, searchInfo); const tempIsAdded = !!submitData.find( sd => sd.PartID === d.extData.PartID, ); if (m) childMenus.push({ ...d, extData: { ...d.extData, isAdded: tempIsAdded }, match: m, }); } else { deep(d.children); } }); }; if (item.href) { childMenus.push({ ...item }); } else { deep(item.children); } return { ...item, childMenus }; }); newTempMenuList = newTempMenuList.filter( item => item.childMenus && item.childMenus.length > 0, ); setTempMenuList(newTempMenuList); }, [menuList, isShowMenuModal, searchInfo]); const toggleMenu = menu => { const { key } = menu; const newTempMenuList = _.cloneDeep(tempMenuList); let newSubmitData = []; let flag = false; for (let i = 0; i < newTempMenuList.length; i += 1) { const { childMenus } = newTempMenuList[i]; for (let j = 0; j < childMenus.length; j += 1) { if (childMenus[j].key === key) { flag = true; const { name, href, extData: { PartID, icon, isAdded }, } = childMenus[j]; childMenus[j].extData.isAdded = !isAdded; if (submitData.find(item => item.PartID === PartID)) { newSubmitData = submitData.filter(item => item.PartID !== PartID); } else { newSubmitData = [ ...submitData, { PartID, PartName: name, PartUrl: href, icon, }, ]; } break; } } if (flag) break; } setTempMenuList(newTempMenuList); setSubmitData(newSubmitData); }; return ( <Modal wrapClassName={styles.MenuModal} title={<ModalTitle />} width={900} okText="确定" cancelText="取消" visible={isShowMenuModal} bodyStyle={{ padding: 0 }} onOk={() => { mdOk(submitData); }} onCancel={() => { mdCancel(); }} > <div className={styles.modalBodyHead}> <Input allowClear placeholder="搜索功能菜单" prefix={ <img src={starIcon} alt="" style={{ height: '18px', marginRight: '5px' }} /> // <SearchOutlined style={{ color: '#1d8dff', fontSize: '16px' }} /> } onChange={e => { setSearchInfo(e.target.value); }} /> </div> <div className={classnames(styles.modalBodyMain)}> {tempMenuList.length ? ( tempMenuList.map(item => ( <div className={styles.menuWrapper} key={item.key}> <p className={styles.menuGroupTitle}>{item.name}</p> <div className={styles.menuList}> {Array.isArray(item.childMenus) && item.childMenus.map(child => { const { match, name } = child; const { isAdded } = child.extData; let bname = '', cname = '', aname = name; if (match) { const [before, after] = match; bname = after > -1 ? name.slice(0, before) : ''; cname = after > -1 ? ( <em style={{ color: 'orange', fontStyle: 'normal' }}> {name.slice(before, after + 1)} </em> ) : ( '' ); aname = after > -1 ? name.slice(after + 1, name.length) : name; } return ( <div key={child.key} className={classnames( styles.menuItem, isAdded ? styles.menuItem_added : '', )} onClick={() => { toggleMenu(child); }} > <div> <span> {bname} {cname} {aname} </span> {isAdded ? ( <p className={styles.addedFlag}> <CheckOutlined /> </p> ) : null} </div> </div> ); })} </div> </div> )) ) : ( <PandaEmpty /> )} </div> </Modal> ); }; const mapStateToProps = state => ({ menus: state.getIn(['global', 'menu']), flatMenu: state.getIn(['global', 'flatMenu']), }); const mapDispatchToProps = dispatch => ({ updateCurrentIndex(index) { dispatch(actionCreators.updateCurrentIndex(index)); }, }); export default connect( mapStateToProps, mapDispatchToProps, )(CommonMenu);