Skip to content

Commit b6d97f1

Browse files
leij1angPanJiaChen
authored andcommitted
feat[tags-view]: tags-view add contextmenu (PanJiaChen#343)
* add the menu by right-clicking the tags * bug fixed * refine
1 parent a68413c commit b6d97f1

File tree

2 files changed

+200
-109
lines changed

2 files changed

+200
-109
lines changed

src/store/modules/tagsView.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,25 @@ const tagsView = {
2929
break
3030
}
3131
}
32+
},
33+
DEL_OTHER_VIEWS: (state, view) => {
34+
for (const [i, v] of state.visitedViews.entries()) {
35+
if (v.path === view.path) {
36+
state.visitedViews = [].concat(state.visitedViews.slice(i, i + 1))
37+
break
38+
}
39+
}
40+
for (const i of state.cachedViews) {
41+
if (i === view.name) {
42+
const index = state.cachedViews.indexOf(i)
43+
state.cachedViews = [].concat(state.cachedViews.slice(index, i + 1))
44+
break
45+
}
46+
}
47+
},
48+
DEL_ALL_VIEWS: (state) => {
49+
state.visitedViews = []
50+
state.cachedViews = []
3251
}
3352
},
3453
actions: {
@@ -40,6 +59,18 @@ const tagsView = {
4059
commit('DEL_VISITED_VIEWS', view)
4160
resolve([...state.visitedViews])
4261
})
62+
},
63+
delOtherViews({ commit, state }, view) {
64+
return new Promise((resolve) => {
65+
commit('DEL_OTHER_VIEWS', view)
66+
resolve([...state.visitedViews])
67+
})
68+
},
69+
delAllViews({ commit, state }) {
70+
return new Promise((resolve) => {
71+
commit('DEL_ALL_VIEWS')
72+
resolve([...state.visitedViews])
73+
})
4374
}
4475
}
4576
}
Lines changed: 169 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,139 +1,199 @@
11
<template>
2-
<scroll-pane class='tags-view-container' ref='scrollPane'>
3-
<router-link ref='tag' class="tags-view-item" :class="isActive(tag)?'active':''" v-for="tag in Array.from(visitedViews)" :to="tag.path":key="tag.path">
4-
{{generateTitle(tag.title)}}
5-
<span class='el-icon-close' @click='closeViewTags(tag,$event)'></span>
6-
</router-link>
7-
</scroll-pane>
2+
<div class="tag-container">
3+
<scroll-pane class='tags-view-container' ref='scrollPane'>
4+
<router-link ref='tag' class="tags-view-item" :class="isActive(tag)?'active':''" v-for="tag in Array.from(visitedViews)" :to="tag.path":key="tag.path" @contextmenu.prevent.native="openMenu(tag,$event)">
5+
{{generateTitle(tag.title)}}
6+
<span class='el-icon-close' @click='closeViewTags(tag,$event)'></span>
7+
</router-link>
8+
</scroll-pane>
9+
<ul class='contextmenu' v-show="visible" :style="{left:left+'px',top:top+'px'}">
10+
<li @click="closeViewTags(selectedTag, $event)">关闭</li>
11+
<li @click="closeOtherTags">关闭其他</li>
12+
<li @click="closeAllTags">关闭所有</li>
13+
</ul>
14+
</div>
815
</template>
916

1017
<script>
11-
import ScrollPane from '@/components/ScrollPane'
12-
import { generateTitle } from '@/utils/i18n'
18+
import ScrollPane from '@/components/ScrollPane'
19+
import { generateTitle } from '@/utils/i18n'
1320
14-
export default {
15-
components: { ScrollPane },
16-
computed: {
17-
visitedViews() {
18-
return this.$store.state.tagsView.visitedViews
19-
}
20-
},
21-
mounted() {
22-
this.addViewTags()
23-
},
24-
methods: {
25-
generateTitle,
26-
closeViewTags(view, $event) {
27-
this.$store.dispatch('delVisitedViews', view).then((views) => {
28-
if (this.isActive(view)) {
29-
const latestView = views.slice(-1)[0]
30-
if (latestView) {
31-
this.$router.push(latestView.path)
32-
} else {
33-
this.$router.push('/')
34-
}
35-
}
36-
})
37-
$event.preventDefault()
38-
},
39-
generateRoute() {
40-
if (this.$route.name) {
41-
return this.$route
21+
export default {
22+
components: { ScrollPane },
23+
data() {
24+
return {
25+
visible: false,
26+
top: 0,
27+
left: 0,
28+
selectedTag: {}
4229
}
43-
return false
4430
},
45-
addViewTags() {
46-
const route = this.generateRoute()
47-
if (!route) {
48-
return false
31+
computed: {
32+
visitedViews() {
33+
return this.$store.state.tagsView.visitedViews
4934
}
50-
this.$store.dispatch('addVisitedViews', route)
5135
},
52-
isActive(route) {
53-
return route.path === this.$route.path || route.name === this.$route.name
36+
mounted() {
37+
this.addViewTags()
5438
},
55-
moveToCurrentTag() {
56-
const tags = this.$refs.tag
57-
this.$nextTick(() => {
58-
for (const tag of tags) {
59-
if (tag.to === this.$route.path) {
60-
this.$refs.scrollPane.moveToTarget(tag.$el)
61-
break
39+
methods: {
40+
generateTitle,
41+
closeViewTags(view, $event) {
42+
this.$store.dispatch('delVisitedViews', view).then((views) => {
43+
if (this.isActive(view)) {
44+
const latestView = views.slice(-1)[0]
45+
if (latestView) {
46+
this.$router.push(latestView.path)
47+
} else {
48+
this.$router.push('/')
49+
}
6250
}
51+
})
52+
$event.preventDefault()
53+
},
54+
closeOtherTags() {
55+
this.$router.push(this.selectedTag.path)
56+
this.$store.dispatch('delOtherViews', this.selectedTag)
57+
},
58+
closeAllTags() {
59+
this.$store.dispatch('delAllViews')
60+
this.$router.push('/')
61+
},
62+
generateRoute() {
63+
if (this.$route.name) {
64+
return this.$route
6365
}
64-
})
65-
}
66-
},
67-
watch: {
68-
$route() {
69-
this.addViewTags()
70-
this.moveToCurrentTag()
66+
return false
67+
},
68+
addViewTags() {
69+
const route = this.generateRoute()
70+
if (!route) {
71+
return false
72+
}
73+
this.$store.dispatch('addVisitedViews', route)
74+
},
75+
isActive(route) {
76+
return route.path === this.$route.path || route.name === this.$route.name
77+
},
78+
moveToCurrentTag() {
79+
const tags = this.$refs.tag
80+
this.$nextTick(() => {
81+
for (const tag of tags) {
82+
if (tag.to === this.$route.path) {
83+
this.$refs.scrollPane.moveToTarget(tag.$el)
84+
break
85+
}
86+
}
87+
})
88+
},
89+
openMenu(tag, e) {
90+
this.visible = true
91+
this.selectedTag = tag
92+
this.left = e.clientX
93+
this.top = e.clientY
94+
},
95+
closeMenu() {
96+
this.visible = false
97+
}
98+
},
99+
watch: {
100+
$route() {
101+
this.addViewTags()
102+
this.moveToCurrentTag()
103+
},
104+
visible(v) {
105+
if (v) {
106+
window.addEventListener('click', this.closeMenu, false)
107+
} else {
108+
window.removeEventListener('click', this.closeMenu, false)
109+
}
110+
}
71111
}
72112
}
73-
}
74113
</script>
75114

76115
<style rel="stylesheet/scss" lang="scss" scoped>
77-
.tags-view-container {
78-
background: #fff;
79-
height: 34px;
80-
border-bottom: 1px solid #d8dce5;
81-
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
82-
.tags-view-item {
83-
display: inline-block;
84-
position: relative;
85-
height: 26px;
86-
line-height: 26px;
87-
border: 1px solid #d8dce5;
88-
color: #495060;
89-
background: #fff;
90-
padding: 0 8px;
91-
font-size: 12px;
92-
margin-left: 5px;
93-
margin-top: 4px;
94-
&:first-of-type {
95-
margin-left: 15px;
116+
.tag-container {
117+
.contextmenu {
118+
margin: 0;
119+
background: #fff;
120+
z-index: 99999;
121+
position: absolute;
122+
list-style-type: none;
123+
padding-left: 0;
124+
border: 1px solid rgba(0, 0, 0, 0.4);
125+
font-size: 0.8rem;
126+
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .5);
127+
li {
128+
margin: 0;
129+
padding: 0.2rem 1.5rem 0.3rem 0.8rem;
130+
&:hover {
131+
background: #eee;
132+
cursor: default;
133+
}
134+
}
96135
}
97-
&.active {
98-
background-color: #42b983;
99-
color: #fff;
100-
border-color: #42b983;
101-
&::before {
102-
content: '';
103-
background: #fff;
136+
.tags-view-container {
137+
background: #fff;
138+
height: 34px;
139+
border-bottom: 1px solid #d8dce5;
140+
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
141+
.tags-view-item {
104142
display: inline-block;
105-
width: 8px;
106-
height: 8px;
107-
border-radius: 50%;
108143
position: relative;
109-
margin-right: 2px;
144+
height: 26px;
145+
line-height: 26px;
146+
border: 1px solid #d8dce5;
147+
color: #495060;
148+
background: #fff;
149+
padding: 0 8px;
150+
font-size: 12px;
151+
margin-left: 5px;
152+
margin-top: 4px;
153+
&:first-of-type {
154+
margin-left: 15px;
155+
}
156+
&.active {
157+
background-color: #42b983;
158+
color: #fff;
159+
border-color: #42b983;
160+
&::before {
161+
content: '';
162+
background: #fff;
163+
display: inline-block;
164+
width: 8px;
165+
height: 8px;
166+
border-radius: 50%;
167+
position: relative;
168+
margin-right: 2px;
169+
}
170+
}
110171
}
111172
}
112173
}
113-
}
114174
</style>
115175

116176
<style rel="stylesheet/scss" lang="scss">
117-
.tags-view-container {
118-
.tags-view-item {
119-
.el-icon-close {
120-
width: 16px;
121-
height: 16px;
122-
vertical-align: 2px;
123-
border-radius: 50%;
124-
text-align: center;
125-
transition: all .3s cubic-bezier(.645, .045, .355, 1);
126-
transform-origin: 100% 50%;
127-
&:before {
128-
transform: scale(.6);
129-
display: inline-block;
130-
vertical-align: -3px;
131-
}
132-
&:hover {
133-
background-color: #b4bccc;
134-
color: #fff;
177+
.tags-view-container {
178+
.tags-view-item {
179+
.el-icon-close {
180+
width: 16px;
181+
height: 16px;
182+
vertical-align: 2px;
183+
border-radius: 50%;
184+
text-align: center;
185+
transition: all .3s cubic-bezier(.645, .045, .355, 1);
186+
transform-origin: 100% 50%;
187+
&:before {
188+
transform: scale(.6);
189+
display: inline-block;
190+
vertical-align: -3px;
191+
}
192+
&:hover {
193+
background-color: #b4bccc;
194+
color: #fff;
195+
}
135196
}
136197
}
137198
}
138-
}
139199
</style>

0 commit comments

Comments
 (0)