import React, { useCallback, useEffect, useRef, useState, useContext } from 'react'; import { Anchor, Button, Popover, Radio, Result, Spin, Tabs, Divider, Dropdown, Menu, Tooltip, ConfigProvider } from 'antd'; // import { RouteWithSubRoutes, renderRoutes } from '../utils/routes'; // import { renderRoutes } from 'react-router-config'; import { renderRoutes } from '../utils/routes' import { PageContainer } from '@ant-design/pro-layout'; import Icon from '@ant-design/icons'; import { store } from 'microser-data'; import classNames from 'classnames'; import { ReactSVG } from 'react-svg' import Cookies from 'js-cookie'; import { connect } from 'react-redux'; import { Router, Switch } from '@wisdom-utils/runtime'; import RightContent from '@/components/GlobalHeader/ExtendRightContent'; import Panel from '@/components/SliderPanel/MinPanel'; import { actionCreators } from '@/containers/App/store'; import CreateBaseMap from '@/pages/map'; // import Authorized from '@/utils/Authorized'; import { findPathByLeafId, getBaseName, } from '@/utils/utils'; import { // LeftOutlined, LoadingOutlined, // MenuFoldOutlined, DownOutlined } from '@ant-design/icons'; // import { renderRoutes } from 'react-router-config'; import { useHistory } from '@wisdom-utils/runtime'; import SecurityLayout from './SecurityLayout'; import Site from './Site'; import styles from './UserLayout.less'; import layoutStyles from './BasicLayout.less'; import SettingDrawer from '../components/SettingDrawer' const { TabPane } = Tabs; const { layout: defaultSetting } = require('../../config/config'); // import Login from '../pages/user/login/login'; const antIcon = <LoadingOutlined style={{ fontSize: 12 }} spin />; /* eslint-disable */ const homeSvg = () => ( <svg t="1631169883330" className="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2203" width="20" height="20"><path d="M423.9 487H221.7c-34.9 0-63.4-28.4-63.4-63.4V221.5c0-34.9 28.4-63.4 63.4-63.4h202.2c34.9 0 63.4 28.4 63.4 63.4v202.2c-0.1 34.9-28.5 63.3-63.4 63.3z m0-63.3v31.7-31.7zM221.7 221.5v202.2h202.1l0.1-202.2H221.7zM803.1 487H600.9c-34.9 0-63.4-28.4-63.4-63.4V221.5c0-34.9 28.4-63.4 63.4-63.4h202.2c34.9 0 63.4 28.4 63.4 63.4v202.2c0 34.9-28.5 63.3-63.4 63.3z m0-63.3v31.7-31.7zM600.9 221.5v202.2H803l0.1-202.2H600.9zM423.9 865.8H221.7c-34.9 0-63.4-28.4-63.4-63.4V600.3c0-34.9 28.4-63.4 63.4-63.4h202.2c34.9 0 63.4 28.4 63.4 63.4v202.2c-0.1 34.9-28.5 63.3-63.4 63.3z m0-63.3v31.7-31.7zM221.7 600.3v202.2h202.1l0.1-202.2H221.7zM736.9 865.8h-69.7c-71.4 0-129.6-58.1-129.6-129.6v-69.7c0-71.4 58.1-129.6 129.6-129.6h69.7c71.4 0 129.6 58.1 129.6 129.6v69.7c0 71.5-58.2 129.6-129.6 129.6z m-69.8-265.5c-36.5 0-66.2 29.7-66.2 66.2v69.7c0 36.5 29.7 66.2 66.2 66.2h69.7c36.5 0 66.2-29.7 66.2-66.2v-69.7c0-36.5-29.7-66.2-66.2-66.2h-69.7z" fill="#B2B2B2" p-id="2204"></path></svg> ); const arrowSvg = ({fillColor = '#fff'}) => ( <svg t="1543324489942" className="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/1999/xlink" width="16" height="16"> <path d="M511.700683 639.423111 191.917496 319.596945 319.830771 319.596945 511.700683 511.715521 703.570595 319.596945 831.48387 319.596945Z" p-id="8626" fill={fillColor}></path> </svg> ); const HomeIcon = props => <Icon component={homeSvg} {...props} style={{marginRight: '0px'}}/>; const ArrowIcon = props => <Icon component={arrowSvg} {...props} />; // const { TabPane } = Tabs; // const baseURI = isProd // ? window.location.origin // : `http://${window.location.hostname}:3020`; const optionsWith = [ { label: '按站点', value: 'site' }, { label: '按城市', value: 'city' }, ]; const HOT = ['HOT', '县', '市', 'New']; const StationsItem = (item, action) => { const changeGroup = (event, data) => { action.changeGroup && action.changeGroup(event, data); }; return ( <li key={item.groupID} style={{ marginRight: `${item.style.marginRight}px` }} > <a className={classNames(styles.city_select, styles.showTip)} title={item.city} index={item.promoteIndex} style={{ fontSize: '13px' }} onClick={event => changeGroup(event, item)} > {item.groupName} {item.promoteTip && HOT.includes(item.promoteTip) && ( <div title={`${item.promoteTip}`}>{item.promoteTip}</div> )} </a> </li> ); }; const Stations = props => { const data = props.data.stations; const [targetOffset, setTargetOffset] = useState(undefined); const cityPane = useRef(null); const cityContent = useRef(null); const [defaultTab, setDefaultTab] = useState('site'); useEffect(() => { setTargetOffset(cityPane.current.clientHeight / 2); }, []); const handleClick = (event, link) => { event.preventDefault(); }; const handleTabChange = event => { setDefaultTab(event.target.value); }; return ( <> <div className={styles.focusStations}> <ul> {Array.isArray(data) ? data.map(item => StationsItem(item, props.action)) : null} </ul> </div> <Radio.Group options={optionsWith} optionType="button" buttonStyle="solid" size="small" value={defaultTab} onChange={handleTabChange} style={{ marginTop: '6px' }} /> <div style={{ maxWidth: '520px', position: 'relative' }}> {defaultTab === 'site' ? ( <div className={classNames(styles.city_pane, styles.station_container)} ref={cityPane} > <Anchor affix={false} onClick={handleClick} targetOffset={targetOffset} getContainer={() => cityContent.current} > <ul className={styles.py}> {props.data && props.data.siteCityList && props.data.siteCityList.letters} </ul> <div className={styles.cityContent} style={{ height: '335px' }} ref={cityContent} > {props.data && props.data.siteCityList && props.data.siteCityList.content} </div> </Anchor> </div> ) : ( <div className="city_pane city_container"> {props.data.citySelector} </div> )} <Spin spinning={props.loading} tip="加载中" /> </div> </> ); }; const renderSite = ({data, config, loading, setLoading, action}) => { const [visible, setVisible] = useState(false); let loaded = !!((data && !data.stations) || (Array.isArray(data.weathers) && data.weathers.length === 0)); if (config && config.userInfo && config.userInfo.site === '') { loaded = false; }; return ( <> <Spin indicator={antIcon} spinning={loaded} size="small" style={{ marginLeft: '10px' }} tip="" wrapperClassName={styles.spinLoadding}> <> <Popover placement="bottomLeft" trigger="click" content={ <Stations data={data} loading={loading} setLoading={setLoading} action={action} /> } arrowPointAtCenter overlayClassName={classNames(styles.stationsWrapper, styles.stationsTop)} onVisibleChange={visible => setVisible(visible)} > { Array.isArray(data.stations) && data.stations.length > 0 ? ( <div className={layoutStyles.toggleSite}> <img src={require('../assets/basic/site.png')} className={layoutStyles.site}/> <span className={layoutStyles.name}>{data.currentStationName}</span> <ArrowIcon className={layoutStyles.arrow} fillColor="#fff" style={{ transform: !visible ? `rotate(0deg)` : `rotate(180deg)`, }} /> </div> ): null } </Popover> </> </Spin> {data.weathers && Object.keys(data.weathers).length > 0 ? ( <span className={layoutStyles.weatcher} style={{ borderLeft: data.stations.length === 0 ? '0px' : '1px solid rgba(256, 256, 256, 0.3)', }} > <img src={data.weathers && data.weathers.icon} className={layoutStyles.icon}/> <span className={layoutStyles.text}>{data.weathers && data.weathers.text}</span> </span> ) : null} </> ) } const BasicLayout = props => { /* eslint-disable no-unused-vars */ const { getPrefixCls } = useContext(ConfigProvider.ConfigContext); const prefixCls = props.prefixCls || getPrefixCls(''); console.log("prefixCls", prefixCls); // const [currentRoutes, setCurrentRoutes] = useState([]); const [collapse, setCollapse] = useState(false); const [toggleSystem, setToggleSystem] = useState(false); const [cityData, setCityData] = useState({}); const [siteLoading, setSiteLoading] = useState(false); const [selectIndex, setSelectIndex] = useState(-1); const [childrenRoutes, setChildrenRoutes] = useState([]); const [selectedKeys, setSelectedKeys] = useState([]); const [tabActiveKey, setTabActiveKey] = useState("1"); // const [currentRoutes, setCurrentRoutes] = useState([]); const [siteAction, setSiteAction] = useState( () => new Site(props, setSiteLoading), ); const basename = getBaseName(); const history = useHistory(); let currentRoutes = props.route.routes[props.currentMenuIndex] useEffect(() => { const initSelectRoute = findPathByLeafId( `${props.location && props.location.pathname || ''}`, [currentRoutes], '', 'path', true, ); const parentMenuName = initSelectRoute && initSelectRoute.parent && initSelectRoute.parent.label; if(parentMenuName) { let currentChildrenRoute = currentRoutes.routes.find(item => item.name === parentMenuName); if(!currentChildrenRoute) { currentChildrenRoute = initSelectRoute } let childrenName = currentChildrenRoute ? currentChildrenRoute.name: parentMenuName; if(currentChildrenRoute.routes) { const active = currentChildrenRoute.routes.find(item => item.name === initSelectRoute.name); setTabActiveKey(active.path) } else { setTabActiveKey(currentChildrenRoute.path); } const initSelectIndex = currentRoutes.routes.findIndex(item => item.name === childrenName); setSelectIndex(initSelectIndex); currentChildrenRoute && currentChildrenRoute.routes ? setChildrenRoutes(currentChildrenRoute.routes): setChildrenRoutes([currentChildrenRoute]); } else { setChildrenRoutes([ { icon: <HomeIcon/>, path: props.location && props.location.pathname, name: '首页' } ]) } }, []); window.share && window.share.event && window.share.event.on('event:updateCurrentChildrenRoutes', ({currentPath, currentRoute, selectedIndex}) => { currentRoute && currentRoute.parent && currentRoute.parent.routes ? setChildrenRoutes(currentRoute.parent.routes): !currentRoute.parent ? setChildrenRoutes(currentRoute.routes): setChildrenRoutes([currentRoute]); setSelectIndex(selectedIndex); setTabActiveKey(currentPath) }); window.share && window.share.event && window.share.event.on('event:favitor', ({icon, name, path}) => { setChildrenRoutes([ { icon: <HomeIcon/>, path: path, name: name } ]) }) useEffect(() => { siteAction.setGlobalConfig(props.global); if (!Cookies.get('token')) { history.replace(`/user/login?client=${props.global.get('client')}`); props.logout(); return; } if ( props.global && props.global.userInfo && props.global.userInfo.token !== null && props.global.userInfo.loginName && Object.keys(cityData).length === 0 ) { siteAction.getCityStationsForUser().then(res => { setCityData(res); }); } }, [props.global]); // setChildrenRoutes(currentChildrenRoute) window.share.event.on('goHome', url => { setChildrenRoutes([ { icon: <HomeIcon/>, path: url, name: '首页' } ]) }); /** * web4全屏退出,切换三级菜单高亮 */ useEffect(() => { if(selectIndex !== -1) { const routes = currentRoutes.routes[selectIndex]; if(routes) { if( routes && routes.routes) { const route = routes && routes.routes && routes.routes.find(item => item.path === decodeURI(window.location.pathname.replace('/civbase', ''))); if(route && route.path) { setTabActiveKey(route.path) } } } } }, [props.location]); // window.share.event.on('event:history', params => { // setTimeout(() => { // debugger // const routes = currentRoutes.routes[selectIndex]; // if(routes) { // const route = routes && routes.routes.find(item => item.path === decodeURI(window.location.pathname.replace('/civbase', ''))); // setTabActiveKey(route.path) // } // // window.share.event.removeListener('event:history') // }, 0) // }) // window.share && window.share.event.on('event:microError', event => { // // window.history.pushState({message: '应用服务请求异常,请检查应用配置'}, null, '/civbase/404') // history.push('/404'); // }); useEffect(() => { window.share.event.on('updateSite', res => setCityData(res)); return () => { window.share.event.removeAllListeners('updateSite'); }; }, []); const handlerSecond = (item, index) => { let current = void 0; if(item && item.routes) { setChildrenRoutes(item.routes); current = item.routes[0]; } else{ setChildrenRoutes([item]); current = item; } setTabActiveKey(current.path); setSelectIndex(index); if(current.routes) { setSelectedKeys([current.routes[0].href]); window.history.pushState(null, '', `/civbase${current.routes[0].path}`); } else { window.history.pushState(null, '', `/civbase${current.path}`); } } const handleToggleSystem = () => { setToggleSystem(!toggleSystem); } const handleUpdateCurrentIndex = index => { setSelectIndex(-1) props.updateCurrentIndex(index); window.share && window.share.event.emit('trigger:updateMenuIndex', index); store.set('updateMenuIndex', index); } const handleSelectMenuItem = (item) => { setTabActiveKey(item); if(item.indexOf('web_console') === -1) { window.history.pushState(null, '', `/civbase${item}`); } } const handlerSelectMenu = ({ item, key, keyPath, domEvent }) => { setSelectedKeys([key]); window.history.pushState(null, '', `/civbase${key}`); } const updateSettings = config => { props.updageSetting && props.updageSetting(config); }; const renderChildrenMenu = (route) => { const routes = (route.routes || []).map(item => { item.key = item.href; return item; }); const menu = ( <Menu onClick={handlerSelectMenu} selectedKeys={selectedKeys}> { (routes || []).map((item, index) => { return ( <> <Menu.Item key={item.href}> <div> <span></span> <span>{item.name}</span> </div> </Menu.Item> <Divider style={{width: '80%', minWidth: '80%', margin: '0 auto'}}></Divider> </> ) }) } </Menu> ) return ( <> <Dropdown overlay={menu} overlayClassName={`${prefixCls}-dropdown-custom-menu`}> <div > { /.svg/.test(route.extData.icon) ?<ReactSVG src={route.extData.icon} style={{width: '18px', height: '18px', position: 'relative', top: '-3px'}}/>: <img src={route.extData.icon} style={{width: '18px', height: '18px'}}/> } <span style={{marginLeft: '8px'}}>{route.name} </span> <DownOutlined style={{position: 'relative', right: '-10px'}}/> </div> </Dropdown> <Divider type="vertical" /> </> ) } const handlerIndustry = event => { props.global.get('userInfo.site') ? history.push(`/industry`) : void 0; }; const handlerCollapsed = () => { setCollapse(!collapse); } const logo = props.global.get('bannerLogo')? window.globalConfig.transformDevAssetsBaseURL(props.global.get('bannerLogo')): defaultSetting.logo; return ( <SecurityLayout loading> {collapse && props.global.loginTemplate === '新春 - 智联.html' ? null : ( props.global.loginTemplate === '新春 - 智联.html' && ( <img src={logo} style={{ width: '120px', position: 'absolute', bottom: '40px', zIndex: 500, left: '12px', }} />) )} <div className={classNames(layoutStyles.basicLayout)}> <div className={layoutStyles['layout-has-slider']} style={{minHeight: '100%'}}> <div style={{ width: collapse ? '48px': '80px', overflow: 'hidden', flex: collapse ? '0 0 48px': '0 0 80px', maxWidth: collapse ? '48px':'80px', minWidth: collapse ? '48px':'80px', transition: 'background-color 0.3s ease 0s, min-width 0.3s ease 0s, max-width 0.3s cubic-bezier(0.645, 0.045, 0.355, 1) 0s' }}></div> <aside className={classNames(layoutStyles.layoutSlider, layoutStyles.fixed, { [layoutStyles.collapsed]: collapse })}> <div className={layoutStyles['layout-slider-childern']}> <div className={layoutStyles.sliderMenu} style={{flex: '1 1 0%', overflow: 'hidden auto'}}> <ul className={classNames(layoutStyles.menu)} style={{minHeight: '0px', marginBottom: '0px'}}> <li className={classNames(layoutStyles['menu-item'])} onClick={handleToggleSystem}> <Tooltip placement="right" title={collapse ? currentRoutes.name: ''}> <a> { (currentRoutes && currentRoutes.extData && currentRoutes.extData.icon) && <img src={currentRoutes.extData.icon} /> } { collapse ? null: <span className={classNames(layoutStyles.text, layoutStyles.currentText)}>{currentRoutes && currentRoutes.name}</span> } </a> </Tooltip> </li> </ul> <div className={layoutStyles.splitLine}></div> <ul className={classNames(layoutStyles.menu, 'menu-vertical')}> { currentRoutes && (currentRoutes.routes || []).map((item, index) => { return ( <> <li key={index} className={classNames(layoutStyles['menu-item'], { [layoutStyles['active']]: index === selectIndex })} onClick={() => handlerSecond(item, index)}> <Tooltip placement="right" title={collapse ? item.name: ''}> <a> { item.icon ? item.icon: item.extData && /.svg/.test(item.extData.icon) ? <ReactSVG src={item.extData.icon} style={{width: '20px', height: '20px'}} className={layoutStyles.icon}/>: item.extData && <img src={item.extData.icon} /> } { collapse ? item.extData && <span className={classNames(layoutStyles.text)}>{(item.extData.shortName || item.name).substr(0, 2)}</span>: <span className={classNames(layoutStyles.text)}>{item.name}</span> } </a> </Tooltip> </li> <Divider style={{border: '1px solid rgb(49, 53, 62, 0.3)', margin: '0 auto', minWidth: '80%', width: '80%'}}/> </> ) }) } </ul> </div> <div className="ant-pro-sider-links" > <ul className="ant-menu ant-menu-root ant-menu-inline ant-menu-dark ant-pro-sider-link-menu" role="menu" tabIndex="0" > <li className="ant-menu-item ant-menu-item-only-child ant-pro-sider-collapsed-button" role="menuitem" tabIndex="-1" onClick={handlerCollapsed}> <span className="ant-menu-title-content"> <span role="img" aria-label="menu-fold" className="anticon anticon-menu-fold"> <svg viewBox="64 64 896 896" focusable="false" width="1em" height="1em" fill="currentColor" aria-hidden="true"> <path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 000 13.8z"></path> </svg> </span> </span> </li> </ul> <div aria-hidden="true" style={{display: 'none'}}></div> </div> </div> </aside> <div className={layoutStyles.layout} style={{width: 'calc(100% - 80px)'}}> <Panel visible={toggleSystem} keyboard onClose={ () => setToggleSystem(false)} currentMenuIndex={props.currentMenuIndex} onChange={index => handleUpdateCurrentIndex(index)} data={props.route.routes || []} /> <SettingDrawer settings={defaultSetting} onSettingChange={config => updateSettings(config)} publicPath={`${window.location.origin}/${basename}/theme`} /> <div className={classNames(layoutStyles['basicLayout-content'], { [layoutStyles['hook_web4']]: location.pathname.startsWith('/civbase/civweb4') })}> <div className={layoutStyles[`${props.rootPrefix}-page-container`]} style={{paddingTop: '0px'}}> <div className={layoutStyles[`${props.rootPrefix}-page-container-warp`]}> <div className={layoutStyles[`${props.rootPrefix}-page-header`]} style={{position: 'fixed'}}> <img src={require('../assets/basic/图层 998@2x.png')} style={{width: '100%'}}/> <div className={layoutStyles.header}> <a className={layoutStyles.logo} onClick={handlerIndustry}> <img src="https://panda-water.com/web4/assets/images/logo/单独图案-白色.svg" alt="logo"/> </a> <div className={layoutStyles.title}>{props.global.title}</div> { renderSite({ data: cityData, config: props.global, loading: siteLoading, setLoading: setSiteLoading, action: siteAction, }) } <RightContent/> </div> </div> </div> <div className={layoutStyles['menu-item-children']}> <Tabs activeKey={tabActiveKey} defaultActiveKey={tabActiveKey} tabBarGutter={30} tabPosition="top" onTabClick={(event) => handleSelectMenuItem(event)}> {childrenRoutes.map((item, index) => ( <> <TabPane className="only-last-children" tab={ <> { item.routes ? renderChildrenMenu(item) : ( <> { item.icon ? item.icon: item.extData && /.svg/.test(item.extData.icon) ? <ReactSVG src={item.extData.icon} beforeInjection={svg => console.log(svg)} style={{width: '18px', height: '18px'}}/>:item.extData && <img src={item.extData.icon} style={{width: '18px', height: '18px'}}/> } <span className={layoutStyles['menu-item-name']}>{item.name}</span> { childrenRoutes.length === 1 ? null: <Divider type="vertical" /> } </> ) } </> } key={item.path} > </TabPane> </> ))} </Tabs> </div> <PageContainer style={{paddingTop: '0px', height: '100%'}}> { renderRoutes(props.route.routes) } { !window.location.pathname.startsWith('/civbase/civweb4') && <CreateBaseMap options={{type: 'ArcgisMap'}}/> } <div id="micro-container" className="subapp-container"> {/*<CreateBaseMap/>*/} {props.children} </div> </PageContainer> </div> </div> </div> </div> </div> </SecurityLayout>); }; const mapStateToProps = state => ({ global: state.getIn(['global', 'globalConfig']), settings: state.getIn(['global', 'defaultSetting']), collapsed: state.getIn(['global', 'collapsed']), menu: state.getIn(['global', 'menu']), currentMenuIndex: state.getIn(['global', 'currentMenuIndex']), flatMenu: state.getIn(['global', 'flatMenu']), authValidate: state.getIn(['global', 'authValidate']), pathname: state.getIn(['global', 'pathname']), selectedKeys: state.getIn(['global', 'selectedKeys']), openKeys: state.getIn(['global', 'openKeys']), complexConfig: state.getIn(['global', 'complexConfig']), complexPathName: state.getIn(['global', 'complexPathName']), microMounted: state.getIn(['global', 'microMounted']), }); const mapDispatchToProps = dispatch => ({ updageSetting(setting) { dispatch(actionCreators.updageSetting(setting)); }, updateConfig(config) { dispatch(actionCreators.getConfig(config)); }, updateCurrentIndex(index) { dispatch(actionCreators.updateCurrentIndex(index)); }, updateCollapsed(collapsed) { dispatch(actionCreators.updateCollapsed(collapsed)); }, updateAuthValidate(auth) { dispatch(actionCreators.updateAuthValidate(auth)); }, updatePathname(pathname) { dispatch(actionCreators.updatePathname(pathname)); }, updateSelectedKeys(keys) { dispatch(actionCreators.updateSelectedKeys(keys)); }, updateOpenKeys(keys) { dispatch(actionCreators.updateOpenKeys(keys)); }, updateComplexConfig(config) { dispatch(actionCreators.updateComplexConfig(config)); }, updateComplexPathName(pathname) { dispatch(actionCreators.updateComplexPathName(pathname)); }, logout() { dispatch(actionCreators.logout()); } }); export default connect( mapStateToProps, mapDispatchToProps, )(BasicLayout);