Skip to content

Commit cdbb5aa

Browse files
committed
feat: tagsView添加右键菜单
1 parent 4206b35 commit cdbb5aa

File tree

7 files changed

+183
-44
lines changed

7 files changed

+183
-44
lines changed

src/store/action-types.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ export const APP_TOGGLE_SETTINGPANEL = "APP_TOGGLE_SETTINGPANEL";
1010
export const SETTINGS_CHANGE_SETTINGS = "SETTINGS_CHANGE_SETTINGS";
1111

1212
// tagsView
13-
export const TAGSVIEW_ADD_TAGLIST = "TAGSVIEW_ADD_TAGLIST";
14-
export const TAGSVIEW_DELETE_TAGLIST = "TAGSVIEW_DELETE_TAGLIST";
13+
export const TAGSVIEW_ADD_TAG = "TAGSVIEW_ADD_TAG";
14+
export const TAGSVIEW_DELETE_TAG = "TAGSVIEW_DELETE_TAG";
1515
export const TAGSVIEW_EMPTY_TAGLIST = "TAGSVIEW_EMPTY_TAGLIST";
16+
export const TAGSVIEW_CLOSE_OTHER_TAGS = "TAGSVIEW_CLOSE_OTHER_TAGS";
17+
1618

src/store/actions/index.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { login, logout } from "./auth";
22
import { getUserInfo, setUserToken, setUserInfo, resetUser } from "./user";
33
import { toggleSiderBar, toggleSettingPanel } from "./app";
44
import { changeSetting } from "./settings";
5-
import { addTaglist, emptyTaglist, deleteTaglist } from "./tagsView";
5+
import { addTag, emptyTaglist, deleteTag, closeOtherTags } from "./tagsView";
66
export {
77
login,
88
logout,
@@ -13,7 +13,8 @@ export {
1313
toggleSiderBar,
1414
toggleSettingPanel,
1515
changeSetting,
16-
addTaglist,
16+
addTag,
1717
emptyTaglist,
18-
deleteTaglist,
18+
deleteTag,
19+
closeOtherTags,
1920
};

src/store/actions/tagsView.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as types from "../action-types";
2-
export const addTaglist = (tag) => {
2+
export const addTag = (tag) => {
33
return {
4-
type: types.TAGSVIEW_ADD_TAGLIST,
4+
type: types.TAGSVIEW_ADD_TAG,
55
tag
66
};
77
};
@@ -12,9 +12,16 @@ export const emptyTaglist = () => {
1212
};
1313
};
1414

15-
export const deleteTaglist = (tag) => {
15+
export const deleteTag = (tag) => {
1616
return {
17-
type: types.TAGSVIEW_DELETE_TAGLIST,
17+
type: types.TAGSVIEW_DELETE_TAG,
18+
tag
19+
};
20+
};
21+
22+
export const closeOtherTags = (tag) => {
23+
return {
24+
type: types.TAGSVIEW_CLOSE_OTHER_TAGS,
1825
tag
1926
};
2027
};

src/store/reducers/tagsView.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const initState = {
44
};
55
export default function app(state = initState, action) {
66
switch (action.type) {
7-
case types.TAGSVIEW_ADD_TAGLIST:
7+
case types.TAGSVIEW_ADD_TAG:
88
const tag = action.tag;
99
if (state.taglist.includes(tag)) {
1010
return state;
@@ -14,15 +14,24 @@ export default function app(state = initState, action) {
1414
taglist: [...state.taglist, tag],
1515
};
1616
}
17-
case types.TAGSVIEW_DELETE_TAGLIST:
17+
case types.TAGSVIEW_DELETE_TAG:
1818
return {
1919
...state,
2020
taglist: [...state.taglist.filter((item) => item !== action.tag)],
2121
};
2222
case types.TAGSVIEW_EMPTY_TAGLIST:
2323
return {
2424
...state,
25-
taglist: [],
25+
taglist: [
26+
...state.taglist.filter((item) => item.path === "/dashboard"),
27+
],
28+
};
29+
case types.TAGSVIEW_CLOSE_OTHER_TAGS:
30+
return {
31+
...state,
32+
taglist: [
33+
...state.taglist.filter((item) => item.path === "/dashboard" || item === action.tag),
34+
],
2635
};
2736
default:
2837
return state;

src/views/layout/Sider/Menu/index.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Link, withRouter } from "react-router-dom";
44
import { Scrollbars } from "react-custom-scrollbars";
55
import { connect } from "react-redux";
66
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
7-
import { addTaglist } from "@/store/actions";
7+
import { addTag } from "@/store/actions";
88
import { getMenuItemInMenuListByProperty } from "@/utils";
99
import menuList from "@/config/menuConfig";
1010
import "./index.less";
@@ -99,7 +99,7 @@ class Meun extends Component {
9999

100100
handleMenuSelect = ({ key = "/dashboard" }) => {
101101
let menuItem = getMenuItemInMenuListByProperty(menuList, "path", key);
102-
this.props.addTaglist(menuItem);
102+
this.props.addTag(menuItem);
103103
};
104104

105105
componentWillMount() {
@@ -154,4 +154,4 @@ class Meun extends Component {
154154
}
155155
}
156156

157-
export default connect((state) => state.user, { addTaglist })(withRouter(Meun));
157+
export default connect((state) => state.user, { addTag })(withRouter(Meun));

src/views/layout/TagsView/components/TagList.jsx

Lines changed: 121 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@ import { connect } from "react-redux";
33
import { withRouter } from "react-router-dom";
44
import { Scrollbars } from "react-custom-scrollbars";
55
import { Tag } from "antd";
6-
import { deleteTaglist } from "@/store/actions";
7-
6+
import { deleteTag, emptyTaglist, closeOtherTags } from "@/store/actions";
87
class TagList extends Component {
8+
tagListContainer = React.createRef();
9+
contextMenuContainer = React.createRef();
10+
state = {
11+
left: 0,
12+
top: 0,
13+
menuVisible: false,
14+
};
915
handleClose = (tag) => {
10-
const { history, deleteTaglist, taglist } = this.props;
16+
const { history, deleteTag, taglist } = this.props;
1117
const path = tag.path;
1218
const currentPath = history.location.pathname;
1319
const length = taglist.length;
@@ -29,41 +35,128 @@ class TagList extends Component {
2935
}
3036

3137
// 先跳转路由,再修改state树的taglist
32-
deleteTaglist(tag);
38+
deleteTag(tag);
3339
};
3440
handleClick = (path) => {
3541
this.props.history.push(path);
3642
};
43+
openContextMenu = (tag, event) => {
44+
event.preventDefault();
45+
const menuMinWidth = 105;
46+
const clickX = event.clientX;
47+
const clickY = event.clientY; //事件发生时鼠标的Y坐标
48+
const clientWidth = this.tagListContainer.current.clientWidth; // container width
49+
const maxLeft = clientWidth - menuMinWidth; // left boundary
50+
51+
// 当鼠标点击位置大于左侧边界时,说明鼠标点击的位置偏右,将菜单放在左边
52+
if (clickX > maxLeft) {
53+
this.setState({
54+
left: clickX - menuMinWidth + 15,
55+
top: clickY,
56+
menuVisible: true,
57+
currentTag: tag,
58+
});
59+
} else {
60+
// 反之,当鼠标点击的位置偏左,将菜单放在右边
61+
this.setState({
62+
left: clickX,
63+
top: clickY,
64+
menuVisible: true,
65+
currentTag: tag,
66+
});
67+
}
68+
};
69+
handleClickOutside = (event) => {
70+
const { menuVisible } = this.state;
71+
const isOutside = !(
72+
this.contextMenuContainer.current &&
73+
this.contextMenuContainer.current.contains(event.target)
74+
);
75+
if (isOutside && menuVisible) {
76+
this.closeContextMenu();
77+
}
78+
};
79+
closeContextMenu() {
80+
this.setState({
81+
menuVisible: false,
82+
});
83+
}
84+
componentDidMount() {
85+
document.body.addEventListener("click", this.handleClickOutside);
86+
}
87+
componentWillUnmount() {
88+
document.body.removeEventListener("click", this.handleClickOutside);
89+
}
90+
handleCloseAllTags = () => {
91+
this.props.emptyTaglist();
92+
this.props.history.push("/dashboard");
93+
this.closeContextMenu();
94+
};
95+
handleRefreshTag = () => {
96+
const { path } = this.state.currentTag;
97+
this.props.history.push(path);
98+
this.closeContextMenu();
99+
};
100+
handleCloseOtherTags = () => {
101+
const currentTag = this.state.currentTag;
102+
const { path } = currentTag;
103+
this.props.closeOtherTags(currentTag)
104+
this.props.history.push(path);
105+
this.closeContextMenu();
106+
};
37107
render() {
108+
const { left, top, menuVisible } = this.state;
38109
const { taglist, history } = this.props;
39110
const currentPath = history.location.pathname;
40111
return (
41-
<Scrollbars
42-
autoHide
43-
autoHideTimeout={1000}
44-
autoHideDuration={200}
45-
hideTracksWhenNotNeeded={true}
46-
renderView={props => <div {...props} className="scrollbar-container" />}
47-
renderTrackVertical={props => <div {...props} className="scrollbar-track-vertical"/>}
48-
>
49-
<ul className="tags-wrap">
50-
{taglist.map((tag) => (
51-
<li key={tag.path}>
52-
<Tag
53-
onClose={this.handleClose.bind(null, tag)}
54-
closable={tag.path !== "/dashboard"}
55-
color={currentPath === tag.path ? "geekblue" : "gold"}
56-
onClick={this.handleClick.bind(null, tag.path)}
57-
>
58-
{tag.title}
59-
</Tag>
60-
</li>
61-
))}
62-
</ul>
63-
</Scrollbars>
112+
<>
113+
<Scrollbars
114+
autoHide
115+
autoHideTimeout={1000}
116+
autoHideDuration={200}
117+
hideTracksWhenNotNeeded={true}
118+
renderView={(props) => (
119+
<div {...props} className="scrollbar-container" />
120+
)}
121+
renderTrackVertical={(props) => (
122+
<div {...props} className="scrollbar-track-vertical" />
123+
)}
124+
>
125+
<ul className="tags-wrap" ref={this.tagListContainer}>
126+
{taglist.map((tag) => (
127+
<li key={tag.path}>
128+
<Tag
129+
onClose={this.handleClose.bind(null, tag)}
130+
closable={tag.path !== "/dashboard"}
131+
color={currentPath === tag.path ? "geekblue" : "gold"}
132+
onClick={this.handleClick.bind(null, tag.path)}
133+
onContextMenu={this.openContextMenu.bind(null, tag)}
134+
>
135+
{tag.title}
136+
</Tag>
137+
</li>
138+
))}
139+
</ul>
140+
</Scrollbars>
141+
{menuVisible ? (
142+
<ul
143+
className="contextmenu"
144+
style={{ left: `${left}px`, top: `${top}px` }}
145+
ref={this.contextMenuContainer}
146+
>
147+
<li onClick={this.handleRefreshTag}>刷新</li>
148+
<li onClick={this.handleCloseOtherTags}>关闭其他</li>
149+
<li onClick={this.handleCloseAllTags}>关闭所有</li>
150+
</ul>
151+
) : null}
152+
</>
64153
);
65154
}
66155
}
67156
export default withRouter(
68-
connect((state) => state.tagsView, { deleteTaglist })(TagList)
157+
connect((state) => state.tagsView, {
158+
deleteTag,
159+
emptyTaglist,
160+
closeOtherTags,
161+
})(TagList)
69162
);

src/views/layout/TagsView/index.less

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@
66
border-bottom: 1px solid #d8dce5;
77
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
88
background: #fff;
9-
padding: 0 16px;
109
.tags-wrap {
1110
padding: 0;
1211
& > li {
1312
display: inline-block;
13+
&:first-of-type {
14+
margin-left: 15px;
15+
}
16+
&:last-of-type {
17+
margin-right: 15px;
18+
}
1419
}
1520
}
1621
}
@@ -20,3 +25,25 @@
2025
.scrollbar-track-vertical {
2126
visibility: hidden !important;
2227
}
28+
.contextmenu {
29+
margin: 0;
30+
background: #fff;
31+
z-index: 3000;
32+
position: absolute;
33+
list-style-type: none;
34+
padding: 5px 0;
35+
border-radius: 4px;
36+
font-size: 12px;
37+
font-weight: 400;
38+
color: #333;
39+
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
40+
li {
41+
margin: 0;
42+
padding: 0px 16px;
43+
height: 30px;
44+
cursor: pointer;
45+
&:hover {
46+
background: #eee;
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)