Skip to content

Commit

Permalink
fix(console): rewrite sidebar menu
Browse files Browse the repository at this point in the history
  • Loading branch information
jo-hnny committed Apr 2, 2022
1 parent c1d6ae8 commit 2759493
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 237 deletions.
3 changes: 2 additions & 1 deletion web/console/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@typescript-eslint/no-this-alias": 1,
"prefer-spread": 1,
"prefer-rest-params": 1,
"@typescript-eslint/triple-slash-reference": 1
"@typescript-eslint/triple-slash-reference": 1,
"@typescript-eslint/explicit-module-boundary-types": "off"
}
}
14 changes: 7 additions & 7 deletions web/console/src/modules/cluster/actions/namespaceActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@ const fetchOptions: FetchOptions = {
const fetchNamespaceActions = generateFetcherActionCreator({
actionType: ActionType.FetchNamespaceList,
fetcher: async (getState: GetState, fetchOptions, dispatch: Redux.Dispatch) => {
let { clusterVersion } = getState();
const { clusterVersion } = getState();
// 获取当前的资源的配置
let namespaceInfo = resourceConfig(clusterVersion)['ns'];
let response = await WebAPI.fetchNamespaceList(getState().namespaceQuery, namespaceInfo);
const namespaceInfo = resourceConfig(clusterVersion)['ns'];
const response = await WebAPI.fetchNamespaceList(getState().namespaceQuery, namespaceInfo);
return response;
},
finish: (dispatch, getState: GetState) => {
let { namespaceList, route } = getState();
const { namespaceList, route } = getState();

let defauleNamespace =
const defauleNamespace =
route.queries['np'] ||
(namespaceList.data.recordCount && namespaceList.data.records.find(n => n.name === 'default').name) ||
'default';
Expand All @@ -61,7 +61,7 @@ const queryNamespaceActions = generateQueryActionCreator({
const restActions = {
selectNamespace: (namespace: string) => {
return async (dispatch, getState: GetState) => {
let { subRoot, route } = getState(),
const { subRoot, route } = getState(),
urlParams = router.resolve(route),
{ isNeedFetchNamespace, mode } = subRoot;

Expand All @@ -74,7 +74,7 @@ const restActions = {
if (isNeedFetchNamespace) {
router.navigate(urlParams, Object.assign({}, route.queries, { np: namespace }));
} else {
let routeQueries = Object.assign({}, route.queries, { np: undefined });
const routeQueries = Object.assign({}, route.queries, { np: undefined });
router.navigate(urlParams, JSON.parse(JSON.stringify(routeQueries)));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const mapDispatchToProps = dispatch =>
@connect(state => state, mapDispatchToProps)
export class ResourceListPanel extends React.Component<ResourceListPanelProps, {}> {
render() {
const { subRoot, route, subRouterList } = this.props,
const { subRoot, route, subRouterList, actions } = this.props,
urlParams = router.resolve(route),
{ resourceInfo } = subRoot;
let content: JSX.Element;
Expand Down Expand Up @@ -147,7 +147,9 @@ export class ResourceListPanel extends React.Component<ResourceListPanelProps, {
<ContentView.Header>
<ResourceHeaderPanel />
</ContentView.Header>
<ContentView.Body sidebar={<ResourceSidebarPanel subRouterList={subRouterList} />}>
<ContentView.Body
sidebar={<ResourceSidebarPanel route={route} actions={actions} subRouterList={subRouterList} />}
>
<ContentView>
<ContentView.Header>
<Justify
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,246 +15,102 @@
* WARRANTIES OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
import * as classnames from 'classnames';
import * as React from 'react';
import { connect } from 'react-redux';

import { bindActionCreators } from '@tencent/ff-redux';

import { allActions } from '../../actions';
import { BasicRouter } from '../../models';
import React, { useEffect, useState } from 'react';
import { Menu, StatusTip } from 'tea-component';
import { router } from '../../router';
import { RootProps } from '../ClusterApp';
import { ResourceListPanelProps } from './ResourceListPanel';

const { LoadingTip } = StatusTip;

export const TellIsNeedFetchNS = (resourceName: string) => {
return resourceName !== 'np' &&
resourceName !== 'pv' &&
resourceName !== 'sc' &&
resourceName !== 'log' &&
resourceName !== 'event'
? true
: false;
return !['np', 'pv', 'sc', 'log', 'event'].includes(resourceName);
};

export const TellIsNotNeedFetchResource = (resourceName: string) => {
return resourceName === 'info' ? true : false;
};

interface ResourceSidebarPanelState {
/** 当前选中的resouce */
currentPath?: string;

/** 是否触发二级导航栏 */
isOpenSecondBar?: string;
export const ResourceSidebarPanel = ({ route, actions, subRouterList }) => {
const [type, setType] = useState(null);
const [resourceName, setResourceName] = useState(null);

/** 判断触发那个二级导航栏 */
secondBarPath?: string;
}
useEffect(() => {
const { type = null, resourceName = null } = router.resolve(route);

const mapDispatchToProps = (dispatch) =>
Object.assign({}, bindActionCreators({ actions: allActions }, dispatch), { dispatch });
setType(type);
setResourceName(resourceName);
}, [route]);

@connect((state) => state, mapDispatchToProps)
export class ResourceSidebarPanel extends React.Component<ResourceListPanelProps, ResourceSidebarPanelState> {
constructor(props: ResourceListPanelProps) {
super(props);
let urlParams = router.resolve(props.route);
this.state = {
currentPath: urlParams['resourceName'],
isOpenSecondBar: '',
secondBarPath: '',
};
}

componentWillReceiveProps(nextProps: ResourceListPanelProps) {
let { route, subRoot, subRouterList } = nextProps,
urlParams = router.resolve(route),
{ type = '', resourceName = '' } = urlParams;
function handleClick(_type, _resourceName) {
if (resourceName === _resourceName) return;

let oldSubRouterList = JSON.stringify(this.props.subRouterList),
newSubRouterList = JSON.stringify(subRouterList);
const urlParams = router.resolve(route);
const queries = { ...(route?.queries ?? {}) };

/**
* 这里判断二级路由是否加载完毕,并且判断是否为当前的路由是否有变化,无变化则不需要判断
* condition:
* 1. 首次进入resourceContainerPanel的时候,会去触发拉取二级菜单的配置,会触发oldSubRouterList !== newSubRouterList,进行第一次state初始化
* 2. 由于请求subRouter之后,会在finish的时候,进行动态菜单的判断,所有 subRouterList是动态变化的,参考条件1,可以进行菜单栏的展开
* 3. 后续的点击过程中,会触发 前者的判断条件,subRouter有了,url变化,动态对菜单进行更新
*/
if (
(subRouterList.length && (type !== this.state.secondBarPath || resourceName !== this.state.currentPath)) ||
oldSubRouterList !== newSubRouterList
) {
// 处理state的值的变化
this.setState({
isOpenSecondBar: subRouterList.find((item) => item.path === type) ? type : '',
secondBarPath: type,
currentPath: resourceName,
});
if (!['hpa', 'cronhpa'].includes(_resourceName)) {
actions.resource.reset();
}
}

render() {
return (
<div className="secondary-aside">
<div className="secondary-aside-area">
<div className="secondary-aside-area-main">{this._renderBarList()}</div>
</div>
</div>
);
}

/**
* 生成二级导航路由主体部分
*/
_renderBarList() {
let { subRouterList } = this.props;

return (
<ul className="secondary-aside-list">
{subRouterList.map((sidebar, index) => {
if (sidebar.sub) {
return (
<li
key={index}
className={classnames('', {
'secondary-aside-select': this.state.isOpenSecondBar === sidebar.path,
})}
>
<a
href="javascript:;"
className="secondary-aside-level-1"
onClick={(e) => {
this._handleClickForFirstBar(sidebar.path, true);
}}
>
<span>{sidebar.name}</span>
<i className="secondary-aside-up-icon" />
</a>
{this._renderSecondBarList(sidebar.sub, sidebar.path)}
</li>
);
} else {
return (
<li key={index}>
<a
href="javascript:;"
onClick={(e) => {
this._handleClickForFirstBar(sidebar.basicUrl, false, sidebar.path);
e.stopPropagation();
}}
className={classnames('secondary-aside-level-1', {
'secondary-aside-select': this.state.currentPath === sidebar.basicUrl,
})}
>
<span>{sidebar.name}</span>
</a>
</li>
);
}
})}
</ul>
);
}

/**
* 路由公共处理部分,以及数据数据请求
* @param subSidebarPath 二级路由
* @param sidebarPath 一级路由
*/
private _handleDataFetcher(subSidebarPath: string, sidebarPath: string) {
let { actions, route } = this.props,
urlParams = router.resolve(route);

// 避免重复点击,进行重复的操作
if (urlParams['resourceName'] !== subSidebarPath) {
// 初始化resource fetcher的相关配置信息,因为多个resource用的是同一个fetcher
if (subSidebarPath !== 'hpa' && subSidebarPath !== 'cronhpa') {
actions.resource.reset();
}

// 进行resourceName的变更 并且 请求数据,node 详情页需要node的相关信息
if (!TellIsNotNeedFetchResource(subSidebarPath) && subSidebarPath !== 'hpa' && subSidebarPath !== 'cronhpa') {
// 路由的跳转
router.navigate(
Object.assign({}, urlParams, { type: sidebarPath, resourceName: subSidebarPath }),
Object.assign({}, route.queries)
);
// 这里去判断该资源是否需要进行namespace列表的拉取
let isNeedFetchNamespace = TellIsNeedFetchNS(subSidebarPath);
actions.resource.initResourceInfoAndFetchData(isNeedFetchNamespace, subSidebarPath);
// 这里去清空多选的选项
actions.resource.selectMultipleResource([]);
} else {
// 这几个都和namespace没有太大关系
let queries = Object.assign({}, route.queries, { np: undefined });
router.navigate(
Object.assign({}, urlParams, { type: sidebarPath, resourceName: subSidebarPath }),
JSON.parse(JSON.stringify(queries))
);
}
}
}

/**
* 处理一级导航的操作
* @param path 跳转的路由
* @param isClickNested 是否点击了含有二级的路由
*/
private _handleClickForFirstBar(path: string, isClickNested: boolean = false, sidebarPath?: string) {
if (!isClickNested) {
// 因为是非折叠的路由,所有openSecondBar置为空
this.setState({
isOpenSecondBar: '',
});
this._handleDataFetcher(path, sidebarPath);
if (['hpa', 'cronhpa', 'info'].includes(_resourceName)) {
delete queries.np;
} else {
this.setState({
isOpenSecondBar: path,
});
// 这里去判断该资源是否需要进行namespace列表的拉取
const isNeedFetchNamespace = TellIsNeedFetchNS(_resourceName);
actions.resource.initResourceInfoAndFetchData(isNeedFetchNamespace, _resourceName);
// 这里去清空多选的选项
actions.resource.selectMultipleResource([]);
}
}

/**
* 处理二级导航的操作
* @param subSidebarpath 跳转的路由
* @param sidebarPath 一级路由
*/
private _handleClickForSecondBar(subSidebarpath: string, sidebarPath: string) {
this.setState({
currentPath: subSidebarpath,
router.navigate(Object.assign({}, urlParams, { type: _type, resourceName: _resourceName }), {
...queries
});
this._handleDataFetcher(subSidebarpath, sidebarPath);
}

/**
* 生成二级导航栏
*/
private _renderSecondBarList(subMenu: BasicRouter[], sidebarPath: string) {
let subMenuList = subMenu.map((subSidebar, index) => {
return (
<li key={index}>
<a
href="javascript:;"
onClick={(e) => {
this._handleClickForSecondBar(subSidebar.path, sidebarPath);
e.stopPropagation();
}}
className={classnames('secondary-aside-level-2', {
'secondary-aside-select': this.state.currentPath === subSidebar.path,
})}
>
{subSidebar.name}
</a>
</li>
);
});

return (
<ul className="secondary-aside-subitem" style={{ paddingBottom: '0' }}>
{subMenuList}
</ul>
);
}
}
return (
<Menu>
{subRouterList?.length ? (
subRouterList?.map(({ sub, ...item }) =>
sub ? (
<Menu.SubMenu
title={item.name}
key={item.path}
opened={item.path === type}
onOpenedChange={open => open && setType(item.path)}
>
{sub.map(subItem => (
<Menu.Item
title={subItem.name}
key={subItem.path}
selected={subItem.path === resourceName}
onClick={() => handleClick(item.path, subItem.path)}
/>
))}
</Menu.SubMenu>
) : (
<Menu.Item
title={item.name}
key={item.path}
selected={resourceName === item.basicUrl}
onClick={() => handleClick(item.path, item.basicUrl)}
/>
)
)
) : (
<div
style={{
position: 'absolute',
width: '100%',
top: 0,
bottom: 0,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
marginTop: 100
}}
>
<LoadingTip />
</div>
)}
</Menu>
);
};
Loading

0 comments on commit 2759493

Please sign in to comment.