簡化了代碼邏輯和代碼量,重寫了一遍,執行邏輯和上個版本有所差別;javascript
在上個版本的功能的基礎上梳理,剔除一些BUG,基本都會觸發聯動java
依賴 :antd
/styled-components
/mobx
/mobx-react
/react
react
getDerivedStateFromProps
和getSnapshotBeforeUpdate
這類React 16.3+
的特性實現側邊欄聯動// 路由容器那個組件
// 注入mobx狀態,這樣活動路由每次都能正確響應
// 減小一些沒必要要的渲染,update須要作一些判斷..一樣的路由不做處理
componentDidMount = () => {
this.props.rstat.searchRoute(location.pathname);
};
componentDidUpdate(prevProps, prevState) {
if (
this.props.rstat.activeRoute.pathname !==
this.props.location.pathname
) {
this.props.rstat.searchRoute(this.props.location.pathname);
}
}
複製代碼
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { observer, inject } from 'mobx-react';
// antd
import { Layout, Menu, Icon } from 'antd';
const { Sider } = Layout;
const { SubMenu, Item } = Menu;
import RouterTree, { groupKey } from 'router';
// Logo組件
import Logo from 'pages/Layout/Logo';
@inject('rstat')
@withRouter
@observer
class Sidebar extends Component {
constructor(props) {
super(props);
this.state = {
openKeys: [''],
selectedKeys: [''],
rootSubmenuKeys: groupKey
};
}
static getDerivedStateFromProps(nextProps, prevState) {
const { groupKey, childKey } = nextProps.rstat.activeRoute;
if (
!prevState.openKeys[0] ||
(groupKey !== prevState.openKeys[0] &&
childKey !== prevState.selectedKeys[0])
) {
return {
openKeys: [groupKey],
selectedKeys: [childKey]
};
}
return null;
}
getSnapshotBeforeUpdate(prevProps, prevState) {
const { openKeys, selectedKeys } = prevState;
const { groupKey, childKey } = prevProps.rstat.activeRoute;
if (openKeys[0] !== groupKey || selectedKeys[0] !== childKey) {
return {
openKeys: [groupKey],
selectedKeys: [childKey]
};
}
return null;
}
componentDidUpdate = (prevProps, prevState, snapshot) => {
if (snapshot) {
this.setState(snapshot);
}
};
OpenChange = openKeys => {
const latestOpenKey = openKeys.find(
key => this.state.openKeys.indexOf(key) === -1
);
if (this.state.rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
this.setState({ openKeys });
} else {
this.setState({
openKeys: latestOpenKey ? [latestOpenKey] : [...openKeys]
});
}
};
// 路由跳轉
gotoUrl = itemurl => {
const { history, location } = this.props;
if (location.pathname === itemurl) {
return;
} else {
this.props.rstat.searchRoute(itemurl)
history.push(itemurl);
}
};
render() {
const { openKeys, selectedKeys } = this.state;
const { collapsed, onCollapse, rstat, history } = this.props;
const SiderTree = RouterTree.map(item => (
<SubMenu
key={item.key}
title={
<span>
<Icon type={item.title.icon} />
<span>{item.title.text}</span>
</span>
}>
{item.children &&
item.children.map(menuItem => (
<Item
key={menuItem.key}
onClick={() => this.gotoUrl(menuItem.path)}>
{menuItem.text}
</Item>
))}
</SubMenu>
));
return (
<Sider
collapsible
breakpoint="lg"
collapsed={collapsed}
onCollapse={onCollapse}
trigger={collapsed}>
<Logo collapsed={collapsed} />
<Menu
subMenuOpenDelay={0.3}
theme="dark"
openKeys={openKeys}
selectedKeys={selectedKeys}
mode="inline"
onOpenChange={this.OpenChange}>
{SiderTree}
</Menu>
</Sider>
);
}
}
export default Sidebar;
複製代碼
import { observable, action, computed, toJS } from 'mobx';
import RouterTree from 'router'; // 這個是本身維護的靜態路由表
function findObj(array, obj) {
for (let i = 0, j = array.length; i < j; i++) {
if (array[i].childKey === obj.childKey) {
return true;
}
}
return false;
}
class RouterStateModel {
@observable
currentRouteInfo; // 當前訪問的信息
@observable
routerCollection; // 訪問過的路由信息
constructor() {
this.currentRouteInfo = {};
this.routerCollection = [];
}
// 當前訪問的信息
@action
addRoute = values => {
// 賦值
this.currentRouteInfo = values;
// 如果數組爲0
if (this.routerCollection.length === 0) {
// 則追加到數組中
this.routerCollection.push(this.currentRouteInfo);
} else {
findObj(this.routerCollection, values)
? null
: this.routerCollection.push(this.currentRouteInfo);
}
};
// 設置index爲高亮路由
@action
setIndex = index => {
this.currentRouteInfo = this.routerCollection[index];
};
// 查詢路由匹配
@action
searchRoute(path) {
RouterTree.map(item => {
if (item.pathname) {
// 作一些事情,這裏只有二級菜單
}
// 由於菜單隻有二級,簡單的作個遍歷就能夠了
if (item.children && item.children.length > 0) {
item.children.map(childitem => {
// 爲何要用match是由於 url有可能帶參數等,全等就不能夠了
// 如果match不到會返回null
if (path.match(childitem.path)) {
// 設置title
document.title = childitem.text;
this.addRoute({
groupKey: item.key,
childKey: childitem.key,
childText: childitem.text,
pathname: childitem.path
});
}
});
}
});
}
// 關閉單一路由
@action
closeCurrentTag = index => {
this.routerCollection.splice(index, 1);
this.currentRouteInfo = this.routerCollection[
this.routerCollection.length - 1
];
};
// 關閉除了當前url的其餘全部路由
@action
closeOtherTag = route => {
if (this.routerCollection.length > 1) {
this.routerCollection = [this.currentRouteInfo];
} else {
return false;
}
};
// 獲取當前激活的item,也就是訪問的路由信息
@computed
get activeRoute() {
return toJS(this.currentRouteInfo);
}
// 獲取當前的訪問歷史集合
@computed
get historyCollection() {
return toJS(this.routerCollection);
}
}
const RouterState = new RouterStateModel();
export default RouterState;
複製代碼
import asyncComponent from 'components/asyncComponent/asyncComponent';
// 數據分析
const Monitor = asyncComponent(() => import('pages/DashBoard/Monitor'));
const Analyze = asyncComponent(() => import('pages/DashBoard/Analyze'));
// 音頻管理
const VoiceList = asyncComponent(() => import('pages/AudioManage/VoiceList'));
const CallVoice = asyncComponent(() => import('pages/AudioManage/CallVoice'));
const PrivateChat = asyncComponent(() =>
import('pages/AudioManage/PrivateChat')
);
const Topic = asyncComponent(() => import('pages/AudioManage/Topic'));
// 活動中心
const ActiveList = asyncComponent(() =>
import('pages/ActivityCenter/ActiveList')
);
// APP 管理
const USERLIST = asyncComponent(() => import('pages/AppManage/UserList'));
const ApkSetting = asyncComponent(() => import('pages/AppManage/ApkSetting'));
const LicenseList = asyncComponent(() => import('pages/AppManage/LicenseList'));
const QaList = asyncComponent(() => import('pages/AppManage/QaList'));
// 安全中心
const REPORT = asyncComponent(() => import('pages/Safety/Report'));
const BroadCast = asyncComponent(() => import('pages/Safety/BroadCast'));
// 電影頻道
const MovieList = asyncComponent(() => import('pages/Movie/MovieList'));
// 後臺管理
const UserSetting = asyncComponent(() =>
import('pages/AdminSetting/UserSetting')
);
const RouterTree = [
{
key: 'g0',
title: {
icon: 'dashboard',
text: '數據分析'
},
exact: true,
path: '/dashboard',
children: [
{
key: '1',
text: '數據監控',
path: '/dashboard/monitor',
component: Monitor
},
{
key: '2',
text: '數據分析',
path: '/dashboard/analyze',
component: Analyze
}
]
},
{
key: 'g1',
title: {
icon: 'play-circle',
text: '音頻管理'
},
exact: true,
path: '/voice',
children: [
{
key: '8',
text: '聲兮列表',
path: '/voice/sxlist',
component: VoiceList
},
{
key: '9',
text: '回聲列表',
path: '/voice/calllist',
component: CallVoice
},
{
key: '10',
text: '私聊列表',
path: '/voice/privatechat',
component: PrivateChat
}
// {
// key: '11',
// text: '熱門話題',
// path: '/voice/topcis',
// component: Topic
// }
]
},
{
key: 'g2',
title: {
icon: 'schedule',
text: '活動中心'
},
exact: true,
path: '/active',
children: [
{
key: '17',
text: '活動列表',
path: '/active/list',
component: ActiveList
}
]
},
{
key: 'g3',
title: {
icon: 'scan',
text: '電影專欄'
},
exact: true,
path: '/active',
children: [
{
key: '22',
text: '電影大全',
path: '/movie/list',
component: MovieList
}
]
},
{
key: 'g4',
title: {
icon: 'apple-o',
text: 'APP管理'
},
exact: true,
path: '/appmanage',
children: [
{
key: '29',
text: 'Apk設置',
path: '/appmanage/apksetting',
component: ApkSetting
},
{
key: '30',
text: '用戶列表',
path: '/appmanage/userlist',
component: USERLIST
},
{
key: '31',
text: '用戶協議',
path: '/platform/license',
component: LicenseList
},
{
key: '32',
text: '幫助中心',
path: '/platform/help',
component: QaList
}
]
},
{
key: 'g5',
title: {
icon: 'safety',
text: '安全中心'
},
exact: true,
path: '/safety',
children: [
{
key: '36',
text: '舉報處理',
path: '/safety/report',
component: REPORT
},
{
key: '37',
text: '廣播中心',
path: '/safety/broadcast',
component: BroadCast
}
]
},
{
key: 'g6',
title: {
icon: 'user',
text: '後臺設置'
},
exact: true,
path: '/user',
children: [
{
key: '43',
text: '我的設置',
path: '/admin/setting',
component: UserSetting
}
]
}
];
export const groupKey = RouterTree.map(item => item.key);
export default RouterTree;
複製代碼
import React, { Component } from 'react';
import styled from 'styled-components';
import { withRouter } from 'react-router-dom';
import { observer, inject } from 'mobx-react';
import { Button, Popover } from 'antd';
import TagList from './TagList';
const DynamicTabMenuCSS = styled.div` box-shadow: 0px 1px 1px -1px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgba(0, 0, 0, 0.12); width: 100%; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; background-color: #fff; .tag-menu { flex: 1; } .operator { padding: 0 15px; flex-shrink: 1; } `;
@inject('rstat')
@withRouter
@observer
class DynamicTabMenu extends Component {
constructor(props) {
super(props);
this.state = {
closeTagIcon: false // 控制關閉全部標籤的狀態
};
}
// 關閉其餘標籤
closeOtherTagFunc = () => {
this.props.rstat.closeOtherTag();
};
render() {
return (
<DynamicTabMenuCSS> <div className="tag-menu"> <TagList /> </div> <div className="operator" onClick={this.closeOtherTagFunc} onMouseEnter={() => { this.setState({ closeTagIcon: true }); }} onMouseLeave={() => { this.setState({ closeTagIcon: false }); }}> <Popover placement="bottom" title="關閉標籤" content={'只會保留當前訪問的標籤'} trigger="hover"> <Button type="dashed" shape="circle" icon="close" /> </Popover> </div> </DynamicTabMenuCSS> ); } } export default DynamicTabMenu; 複製代碼
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { observer, inject } from 'mobx-react';
import { Icon, Menu } from 'antd';
@inject('rstat')
@withRouter
@observer
class TagList extends Component {
constructor(props) {
super(props);
this.state = {
showCloseIcon: false, // 控制自身關閉icon
currentIndex: '' // 當前的索引
};
}
render() {
const { rstat, history, location } = this.props;
const { showCloseIcon, currentIndex } = this.state;
return (
<Menu selectedKeys={[rstat.activeRoute.childKey]} mode="horizontal">
{rstat.historyCollection &&
rstat.historyCollection.map((tag, index) => (
<Menu.Item
key={tag.childKey}
onMouseEnter={() => {
this.setState({
showCloseIcon: true,
currentIndex: tag.childKey
});
}}
onMouseLeave={() => {
this.setState({
showCloseIcon: false
});
}}
onClick={() => {
if (tag.pathname === location.pathname) {
return;
} else {
rstat.setIndex(index)
history.push(tag.pathname);
}
}}>
<span>
<Icon
type="tag-o"
style={{ padding: '0 0 0 10px' }}
/>
{tag.childText}
</span>
{showCloseIcon &&
rstat.historyCollection.length > 1 &&
currentIndex === tag.childKey ? (
<Icon
type="close-circle"
style={{
position: 'absolute',
top: 0,
right: -20,
fontSize: 24
}}
onClick={event => {
event.stopPropagation();
rstat.closeCurrentTag(index);
history.push(
rstat.activeRoute.pathname
);
}}
/>
) : null}
</Menu.Item>
))}
</Menu>
);
}
}
export default TagList;
複製代碼
mobx
整體用下來感受還好,還有些不是很人性化的, 狀態內很難結合react-router 4
進行跳轉數組
雖然還有一些什麼mobx-router
這類能夠整合自定義瀏覽歷史對象,可是我不想用。瀏覽器
因此跳轉都是從外部觸發了,有不對之處請留言,謝謝閱讀。緩存