|
|
@@ -9,7 +9,6 @@ const { Search } = Input
|
|
|
class DashboardMenu extends React.Component {
|
|
|
|
|
|
constructor(props) {
|
|
|
- console.log('create');
|
|
|
super(props);
|
|
|
this.state = {
|
|
|
editingKey: null,
|
|
|
@@ -18,6 +17,14 @@ class DashboardMenu extends React.Component {
|
|
|
};
|
|
|
}
|
|
|
|
|
|
+ static defaultProps = {
|
|
|
+ onlyMenu: true, // 仅展示目录
|
|
|
+ hideEmptyMenu: false, // 隐藏空目录
|
|
|
+ editable: true,
|
|
|
+ searchMenu: true,
|
|
|
+ hideHeader: false,
|
|
|
+ }
|
|
|
+
|
|
|
componentDidMount() {
|
|
|
const { dispatch } = this.props;
|
|
|
dispatch({ type: 'dashboard/remoteMenuTree' });
|
|
|
@@ -35,41 +42,55 @@ class DashboardMenu extends React.Component {
|
|
|
return arr;
|
|
|
}
|
|
|
|
|
|
- reduceTree = (mode, tree, regLabel) => {
|
|
|
+ reduceTree = (tree, regLabel) => {
|
|
|
+ const { onlyMenu, searchMenu, hideEmptyMenu } = this.props;
|
|
|
let arr = tree ? [ ...tree ] : [];
|
|
|
for(let i = arr.length - 1; i >= 0; i--) {
|
|
|
let t = arr[i];
|
|
|
if(t.children && t.children.length > 0) {
|
|
|
- t.children = this.reduceTree(mode, t.children, regLabel);
|
|
|
+ t.children = this.reduceTree(t.children, regLabel);
|
|
|
}
|
|
|
- if(mode === 'view') {
|
|
|
- if((!t.children || t.children.length === 0) && t.type !== 'dashboard') {
|
|
|
- arr.splice(i, 1);
|
|
|
- }
|
|
|
- if(t.type === 'dashboard' && !!regLabel && t.name.search(new RegExp('(' + regLabel + '){1}', 'ig')) === -1) {
|
|
|
- arr.splice(i, 1);
|
|
|
- }
|
|
|
- }else {
|
|
|
+
|
|
|
+ // 纯目录过滤
|
|
|
+ if(onlyMenu) {
|
|
|
if(t.type === 'dashboard') {
|
|
|
arr.splice(i, 1);
|
|
|
+ continue;
|
|
|
}
|
|
|
- if(t.type === 'menu' && !!regLabel && (t.name.search(new RegExp('(' + regLabel + '){1}', 'ig')) === -1) && (t.children.length === 0)) {
|
|
|
+ }
|
|
|
+ // 空目录过滤
|
|
|
+ if(hideEmptyMenu) {
|
|
|
+ // 当搜索包括目录时还是保留目录
|
|
|
+ // if(searchMenu && t.type === 'menu' && t.name.search(new RegExp('(' + regLabel + '){1}', 'ig')) >= -1) {
|
|
|
+
|
|
|
+ // }else if((!t.children || t.children.length === 0) && t.type === 'menu') {
|
|
|
+ // arr.splice(i, 1);
|
|
|
+ // continue;
|
|
|
+ // }
|
|
|
+
|
|
|
+ if(t.type === 'menu' && (!t.children || t.children.length === 0) && !(searchMenu && t.name.search(new RegExp('(' + regLabel + '){1}', 'ig')) >= -1)) {
|
|
|
arr.splice(i, 1);
|
|
|
+ continue;
|
|
|
}
|
|
|
}
|
|
|
+ // 关键字过滤
|
|
|
+ if(!!regLabel && t.name.search(new RegExp('(' + regLabel + '){1}', 'ig')) === -1 && (t.type === 'dashboard' || (t.children.length === 0))) {
|
|
|
+ arr.splice(i, 1);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
}
|
|
|
return arr;
|
|
|
}
|
|
|
|
|
|
generateMenu(tree, regLabel) {
|
|
|
- const { mode, dispatch } = this.props;
|
|
|
+ const { dispatch, onlyMenu, searchMenu, editable } = this.props;
|
|
|
const { editingKey } = this.state;
|
|
|
let ftree = this.cloneTree(tree);
|
|
|
- ftree = this.reduceTree(mode, ftree, regLabel)
|
|
|
- return ftree.filter(t => (mode === 'view' || t.type === 'menu')).sort((a, b) => a.index - b.index).map(t => {
|
|
|
+ ftree = this.reduceTree(ftree, regLabel)
|
|
|
+ return ftree.filter(t => (onlyMenu ? t.type === 'menu' : true)).sort((a, b) => a.index - b.index).map(t => {
|
|
|
let title = <div className='node-title'>
|
|
|
<span>{ (t.code !== editingKey) ?
|
|
|
- ( regLabel ? ( <span style={{ fontWeight: t.type === 'dashboard' ? 'bold' : 'normal' }}>
|
|
|
+ ( !!regLabel && (searchMenu || t.type === 'dashboard') ? (<div className='label'> <span title={t.name} style={{ fontWeight: t.type === 'dashboard' ? 'bold' : 'normal' }}>
|
|
|
{ t.type === 'dashboard' && <Icon style={{ marginRight: '8px' }} type="pushpin" /> }
|
|
|
{ (t.name || '').split(new RegExp(`(${regLabel})`, 'i')).map((fragment, i) => {
|
|
|
return (
|
|
|
@@ -78,13 +99,14 @@ class DashboardMenu extends React.Component {
|
|
|
fragment
|
|
|
)
|
|
|
}) }
|
|
|
- </span> ) : <div className='label'>
|
|
|
- <span style={{ fontWeight: t.type === 'dashboard' ? 'bold' : 'normal' }}>
|
|
|
+ </span> </div> ) : <div className='label'>
|
|
|
+ <span title={t.name} style={{ fontWeight: t.type === 'dashboard' ? 'bold' : 'normal' }}>
|
|
|
{ t.type === 'dashboard' && <Icon style={{ marginRight: '8px' }} type="pushpin" /> }
|
|
|
{ t.name }
|
|
|
</span>
|
|
|
</div> ) : ( <div className='input'>
|
|
|
<Input
|
|
|
+ ref={(input) => { this['inputRef-' + t.code] = input }}
|
|
|
size="small"
|
|
|
defaultValue={t.name}
|
|
|
addonAfter={<Icon style={{ cursor: 'pointer', color: '#52C41A' }} type="check-circle" onClick={() => {
|
|
|
@@ -104,13 +126,18 @@ class DashboardMenu extends React.Component {
|
|
|
/>
|
|
|
</div> )
|
|
|
}</span>
|
|
|
- {mode !== 'view' && <div className='tools'>
|
|
|
+ {editable && <div className='tools'>
|
|
|
+ {t.code !== editingKey && t.type === 'menu' && <Icon type='plus' onClick={() => {
|
|
|
+ this.onAddClick(t)
|
|
|
+ }}/>}
|
|
|
{t.code !== editingKey && <Icon type='edit' onClick={() => {
|
|
|
this.setState({
|
|
|
editingKey: t.code
|
|
|
+ }, () => {
|
|
|
+ this['inputRef-' + t.code].focus();
|
|
|
});
|
|
|
}}/>}
|
|
|
- {!(t.children && t.children.length > 0) && <Icon type='delete' onClick={() => {
|
|
|
+ {t.code !== editingKey && !(t.children && t.children.length > 0) && <Icon type='delete' onClick={() => {
|
|
|
this.setState({
|
|
|
selectedMenu: t,
|
|
|
visibleDeleteBox: true,
|
|
|
@@ -125,58 +152,37 @@ class DashboardMenu extends React.Component {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- onChange = (e) => {
|
|
|
- const { dispatch } = this.props;
|
|
|
- dispatch({ type: 'dashboard/setField', name: 'menuFilterLabel', value: e.target.value })
|
|
|
- }
|
|
|
-
|
|
|
onExpand = (keys) => {
|
|
|
- const { mode, dispatch } = this.props;
|
|
|
- if(mode === 'view') {
|
|
|
- dispatch({ type: 'home/setFields', fields: [
|
|
|
- { name: 'menuExpandedKeys', value: keys },
|
|
|
- { name: 'menuAutoExpandParent', value: false },
|
|
|
- ] });
|
|
|
- }else {
|
|
|
- dispatch({ type: 'dashboard/setFields', fields: [
|
|
|
- { name: 'menuExpandedKeys', value: keys },
|
|
|
- { name: 'menuAutoExpandParent', value: false },
|
|
|
- ] });
|
|
|
+ const { dashboard, onExpand: propsOnExpand } = this.props;
|
|
|
+ const { menuList } = dashboard;
|
|
|
+ let expandedMenus = [];
|
|
|
+ for(let i = 0; i < keys.length; i++) {
|
|
|
+ let menu = [{ code: '-1', name: '全部目录' }].concat(menuList).find(l => l.code === keys[i]);
|
|
|
+ expandedMenus.push(menu);
|
|
|
+ }
|
|
|
+ if(typeof propsOnExpand === 'function') {
|
|
|
+ propsOnExpand(expandedMenus);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
onSelect = (selectedKeys, info) => {
|
|
|
- const { mode, dashboard, dispatch } = this.props;
|
|
|
+ const { dashboard, onSelect: propsOnSelect } = this.props;
|
|
|
const { menuList } = dashboard;
|
|
|
- if(mode === 'view') {
|
|
|
- // dispatch({ type: 'home/setField', name: 'menuSelectedKeys', value: selectedKeys })
|
|
|
- let selectedMenu = menuList.find(l => l.code === selectedKeys[0]);
|
|
|
- if(selectedMenu && selectedMenu.type === 'dashboard') {
|
|
|
- dispatch({ type: 'home/openTab', tab: {
|
|
|
- code: selectedMenu.code,
|
|
|
- name: selectedMenu.name
|
|
|
- } })
|
|
|
- }
|
|
|
- }else {
|
|
|
- dispatch({ type: 'dashboard/setField', name: 'menuSelectedKeys', value: selectedKeys })
|
|
|
- if(selectedKeys.length === 1) {
|
|
|
- if(selectedKeys[0] === '-1') {
|
|
|
- dispatch({ type: 'dashboard/fetchList', mandatory: true });
|
|
|
- }else {
|
|
|
- dispatch({ type: 'dashboard/remoteMenuDashboardList', menuCode: selectedKeys[0] });
|
|
|
- }
|
|
|
- }
|
|
|
+ let selectedMenu = [{ code: '-1', name: '全部目录' }].concat(menuList).find(l => l.code === selectedKeys[0]);
|
|
|
+
|
|
|
+ if(typeof propsOnSelect === 'function') {
|
|
|
+ propsOnSelect(selectedMenu);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
onSearch = (value) => {
|
|
|
- const { dashboard, dispatch } = this.props;
|
|
|
- const expandedKeys = this.findExpandedKeys(dashboard.menuTree, {code: '-1'}, value);
|
|
|
- dispatch({ type: 'dashboard/setFields', fields: [
|
|
|
- { name: 'menuExpandedKeys', value: expandedKeys },
|
|
|
- { name: 'menuFilterLabel', value },
|
|
|
- { name: 'menuAutoExpandParent', value: true },
|
|
|
- ] });
|
|
|
+ const { dashboard, onSearch: propsOnSearch } = this.props;
|
|
|
+ // 获得需要展开的目录(存在包含关键字的报表或目录)
|
|
|
+ const expandedMenus = this.findExpandedKeys(dashboard.menuTree, {code: '-1'}, value);
|
|
|
+
|
|
|
+ if(typeof propsOnSearch === 'function') {
|
|
|
+ propsOnSearch(value, expandedMenus);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
findExpandedKeys = (tree, parent, filterLabel) => {
|
|
|
@@ -184,7 +190,7 @@ class DashboardMenu extends React.Component {
|
|
|
if(tree && tree.length > 0) {
|
|
|
tree.forEach(t => {
|
|
|
if(t.name.toLowerCase().indexOf(filterLabel.toLowerCase()) > -1 && result.indexOf(parent.code) === -1) {
|
|
|
- result.push(parent.code);
|
|
|
+ result.push(parent);
|
|
|
}
|
|
|
result = result.concat(this.findExpandedKeys(t.children, t, filterLabel));
|
|
|
});
|
|
|
@@ -192,21 +198,24 @@ class DashboardMenu extends React.Component {
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
- onAddClick = () => {
|
|
|
- const { dashboard, dispatch } = this.props;
|
|
|
- const { menuList, menuTree, menuSelectedKeys, menuExpandedKeys } = dashboard;
|
|
|
- const pmenu = [{ code: '-1', name: '全部目录', pcode: '-1', childrenCount: menuTree.length }].concat(menuList).find(l => l.code === menuSelectedKeys[0]);
|
|
|
+ onAddClick = (pmenu) => {
|
|
|
+ const { dashboard, dispatch, onExpand } = this.props;
|
|
|
+ const { menuList, menuExpandedKeys } = dashboard;
|
|
|
const menu = {
|
|
|
index: pmenu.childrenCount,
|
|
|
name: '新目录',
|
|
|
pcode: pmenu.code
|
|
|
}
|
|
|
dispatch({ type: 'dashboard/remoteAddMenu', menu }).then(res => {
|
|
|
- if(res) {
|
|
|
- dispatch({ type: 'dashboard/setFields', fields: [
|
|
|
- { name: 'menuExpandedKeys', value: menuExpandedKeys.concat([pmenu.code]) },
|
|
|
- { name: 'autoExpandParent', value: true },
|
|
|
- ] });
|
|
|
+ if(res && typeof onExpand === 'function') {
|
|
|
+ let expandedMenus = [];
|
|
|
+ [{ code: '-1', name: '全部目录' }].concat(menuList).forEach(m => {
|
|
|
+ if(menuExpandedKeys.indexOf(m.code) > -1) {
|
|
|
+ expandedMenus.push(m);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ expandedMenus.push(pmenu);
|
|
|
+ onExpand(expandedMenus);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
@@ -222,24 +231,19 @@ class DashboardMenu extends React.Component {
|
|
|
}
|
|
|
|
|
|
render() {
|
|
|
- const { mode, home, dashboard, dispatch } = this.props;
|
|
|
+ const { dashboard, dispatch, hideHeader, editable, model } = this.props;
|
|
|
const { visibleDeleteBox, selectedMenu } = this.state;
|
|
|
const { menuTree } = dashboard;
|
|
|
- const { menuFilterLabel, menuExpandedKeys, menuSelectedKeys, menuAutoExpandParent } = mode === 'view' ? home : dashboard;
|
|
|
+ const { menuFilterLabel, menuExpandedKeys, menuSelectedKeys, menuAutoExpandParent } = model;
|
|
|
const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
|
|
|
const regLabel = menuFilterLabel.replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1'); // 添加转义符号
|
|
|
|
|
|
- return <div className='menu-container' style={ mode === 'view' ? { paddingTop: 0 } : { paddingTop: '88px' } }>
|
|
|
+ return <div className='menu-container' style={ hideHeader ? { paddingTop: 0 } : { paddingTop: '48px' } }>
|
|
|
|
|
|
- {mode !== 'view' && <div className='menu-buttons'>
|
|
|
- <Button disabled={menuSelectedKeys.length !== 1} onClick={this.onAddClick}>新增目录</Button>
|
|
|
- {/* <Button disabled={menuSelectedKeys.length !== 1} onClick={this.onModifyClick}>修改目录</Button> */}
|
|
|
- </div>}
|
|
|
- {mode !== 'view' && <div className='menu-search'>
|
|
|
+ {!hideHeader && <div className='menu-search'>
|
|
|
<Search
|
|
|
defaultValue={menuFilterLabel}
|
|
|
placeholder='搜索'
|
|
|
- // onChange={this.onChange}
|
|
|
onSearch={this.onSearch}
|
|
|
/>
|
|
|
<Button onClick={() => {
|
|
|
@@ -255,7 +259,16 @@ class DashboardMenu extends React.Component {
|
|
|
onSelect={this.onSelect}
|
|
|
autoExpandParent={menuAutoExpandParent}
|
|
|
>
|
|
|
- <TreeNode title='全部目录' key='-1'>
|
|
|
+ <TreeNode title={
|
|
|
+ <div className='node-title'>
|
|
|
+ <span><div className='label'><span>全部目录</span></div></span>
|
|
|
+ {editable && <div className='tools'>
|
|
|
+ <Icon type='plus' onClick={() => {
|
|
|
+ this.onAddClick({ code: '-1', name: '全部目录', pcode: '-1', childrenCount: menuTree.length })
|
|
|
+ }}/>
|
|
|
+ </div>}
|
|
|
+ </div>
|
|
|
+ } key='-1'>
|
|
|
{ this.generateMenu(menuTree, regLabel) }
|
|
|
</TreeNode>
|
|
|
</Tree>
|
|
|
@@ -276,7 +289,7 @@ class DashboardMenu extends React.Component {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-function mapStateToProps({ present: { dashboard, home }}) {
|
|
|
- return { dashboard, home }
|
|
|
+function mapStateToProps({ present: { dashboard }}) {
|
|
|
+ return { dashboard }
|
|
|
}
|
|
|
export default connect(mapStateToProps)(DashboardMenu)
|