作者 RuoYi

当tags-view滚动关闭右键菜单

@@ -31,7 +31,7 @@ @@ -31,7 +31,7 @@
31 .fixed-width { 31 .fixed-width {
32 .el-button--mini { 32 .el-button--mini {
33 padding: 7px 10px; 33 padding: 7px 10px;
34 - width: 60px; 34 + min-width: 60px;
35 } 35 }
36 } 36 }
37 37
@@ -19,12 +19,21 @@ export default { @@ -19,12 +19,21 @@ export default {
19 return this.$refs.scrollContainer.$refs.wrap 19 return this.$refs.scrollContainer.$refs.wrap
20 } 20 }
21 }, 21 },
  22 + mounted() {
  23 + this.scrollWrapper.addEventListener('scroll', this.emitScroll, true)
  24 + },
  25 + beforeDestroy() {
  26 + this.scrollWrapper.removeEventListener('scroll', this.emitScroll)
  27 + },
22 methods: { 28 methods: {
23 handleScroll(e) { 29 handleScroll(e) {
24 const eventDelta = e.wheelDelta || -e.deltaY * 40 30 const eventDelta = e.wheelDelta || -e.deltaY * 40
25 const $scrollWrapper = this.scrollWrapper 31 const $scrollWrapper = this.scrollWrapper
26 $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4 32 $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
27 }, 33 },
  34 + emitScroll() {
  35 + this.$emit('scroll')
  36 + },
28 moveToTarget(currentTag) { 37 moveToTarget(currentTag) {
29 const $container = this.$refs.scrollContainer.$el 38 const $container = this.$refs.scrollContainer.$el
30 const $containerWidth = $container.offsetWidth 39 const $containerWidth = $container.offsetWidth
1 <template> 1 <template>
2 <div id="tags-view-container" class="tags-view-container"> 2 <div id="tags-view-container" class="tags-view-container">
3 - <scroll-pane ref="scrollPane" class="tags-view-wrapper"> 3 + <scroll-pane ref="scrollPane" class="tags-view-wrapper" @scroll="handleScroll">
4 <router-link 4 <router-link
5 v-for="tag in visitedViews" 5 v-for="tag in visitedViews"
6 ref="tag" 6 ref="tag"
@@ -14,11 +14,7 @@ @@ -14,11 +14,7 @@
14 @contextmenu.prevent.native="openMenu(tag,$event)" 14 @contextmenu.prevent.native="openMenu(tag,$event)"
15 > 15 >
16 {{ tag.title }} 16 {{ tag.title }}
17 - <span  
18 - v-if="!isAffix(tag)"  
19 - class="el-icon-close"  
20 - @click.prevent.stop="closeSelectedTag(tag)"  
21 - /> 17 + <span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
22 </router-link> 18 </router-link>
23 </scroll-pane> 19 </scroll-pane>
24 <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu"> 20 <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
@@ -31,8 +27,8 @@ @@ -31,8 +27,8 @@
31 </template> 27 </template>
32 28
33 <script> 29 <script>
34 -import ScrollPane from "./ScrollPane";  
35 -import path from "path"; 30 +import ScrollPane from './ScrollPane'
  31 +import path from 'path'
36 32
37 export default { 33 export default {
38 components: { ScrollPane }, 34 components: { ScrollPane },
@@ -43,14 +39,14 @@ export default { @@ -43,14 +39,14 @@ export default {
43 left: 0, 39 left: 0,
44 selectedTag: {}, 40 selectedTag: {},
45 affixTags: [] 41 affixTags: []
46 - }; 42 + }
47 }, 43 },
48 computed: { 44 computed: {
49 visitedViews() { 45 visitedViews() {
50 - return this.$store.state.tagsView.visitedViews; 46 + return this.$store.state.tagsView.visitedViews
51 }, 47 },
52 routes() { 48 routes() {
53 - return this.$store.state.permission.routes; 49 + return this.$store.state.permission.routes
54 }, 50 },
55 theme() { 51 theme() {
56 return this.$store.state.settings.theme; 52 return this.$store.state.settings.theme;
@@ -58,24 +54,24 @@ export default { @@ -58,24 +54,24 @@ export default {
58 }, 54 },
59 watch: { 55 watch: {
60 $route() { 56 $route() {
61 - this.addTags();  
62 - this.moveToCurrentTag(); 57 + this.addTags()
  58 + this.moveToCurrentTag()
63 }, 59 },
64 visible(value) { 60 visible(value) {
65 if (value) { 61 if (value) {
66 - document.body.addEventListener("click", this.closeMenu); 62 + document.body.addEventListener('click', this.closeMenu)
67 } else { 63 } else {
68 - document.body.removeEventListener("click", this.closeMenu); 64 + document.body.removeEventListener('click', this.closeMenu)
69 } 65 }
70 } 66 }
71 }, 67 },
72 mounted() { 68 mounted() {
73 - this.initTags();  
74 - this.addTags(); 69 + this.initTags()
  70 + this.addTags()
75 }, 71 },
76 methods: { 72 methods: {
77 isActive(route) { 73 isActive(route) {
78 - return route.path === this.$route.path; 74 + return route.path === this.$route.path
79 }, 75 },
80 activeStyle(tag) { 76 activeStyle(tag) {
81 if (!this.isActive(tag)) return {}; 77 if (!this.isActive(tag)) return {};
@@ -85,142 +81,131 @@ export default { @@ -85,142 +81,131 @@ export default {
85 }; 81 };
86 }, 82 },
87 isAffix(tag) { 83 isAffix(tag) {
88 - return tag.meta && tag.meta.affix; 84 + return tag.meta && tag.meta.affix
89 }, 85 },
90 - filterAffixTags(routes, basePath = "/") {  
91 - let tags = []; 86 + filterAffixTags(routes, basePath = '/') {
  87 + let tags = []
92 routes.forEach(route => { 88 routes.forEach(route => {
93 if (route.meta && route.meta.affix) { 89 if (route.meta && route.meta.affix) {
94 - const tagPath = path.resolve(basePath, route.path); 90 + const tagPath = path.resolve(basePath, route.path)
95 tags.push({ 91 tags.push({
96 fullPath: tagPath, 92 fullPath: tagPath,
97 path: tagPath, 93 path: tagPath,
98 name: route.name, 94 name: route.name,
99 meta: { ...route.meta } 95 meta: { ...route.meta }
100 - }); 96 + })
101 } 97 }
102 if (route.children) { 98 if (route.children) {
103 - const tempTags = this.filterAffixTags(  
104 - route.children,  
105 - route.path  
106 - ); 99 + const tempTags = this.filterAffixTags(route.children, route.path)
107 if (tempTags.length >= 1) { 100 if (tempTags.length >= 1) {
108 - tags = [...tags, ...tempTags]; 101 + tags = [...tags, ...tempTags]
109 } 102 }
110 } 103 }
111 - });  
112 - return tags; 104 + })
  105 + return tags
113 }, 106 },
114 initTags() { 107 initTags() {
115 - const affixTags = (this.affixTags = this.filterAffixTags(  
116 - this.routes  
117 - )); 108 + const affixTags = this.affixTags = this.filterAffixTags(this.routes)
118 for (const tag of affixTags) { 109 for (const tag of affixTags) {
119 // Must have tag name 110 // Must have tag name
120 if (tag.name) { 111 if (tag.name) {
121 - this.$store.dispatch("tagsView/addVisitedView", tag); 112 + this.$store.dispatch('tagsView/addVisitedView', tag)
122 } 113 }
123 } 114 }
124 }, 115 },
125 addTags() { 116 addTags() {
126 - const { name } = this.$route; 117 + const { name } = this.$route
127 if (name) { 118 if (name) {
128 - this.$store.dispatch("tagsView/addView", this.$route); 119 + this.$store.dispatch('tagsView/addView', this.$route)
129 } 120 }
130 - return false; 121 + return false
131 }, 122 },
132 moveToCurrentTag() { 123 moveToCurrentTag() {
133 - const tags = this.$refs.tag; 124 + const tags = this.$refs.tag
134 this.$nextTick(() => { 125 this.$nextTick(() => {
135 for (const tag of tags) { 126 for (const tag of tags) {
136 if (tag.to.path === this.$route.path) { 127 if (tag.to.path === this.$route.path) {
137 - this.$refs.scrollPane.moveToTarget(tag); 128 + this.$refs.scrollPane.moveToTarget(tag)
138 // when query is different then update 129 // when query is different then update
139 if (tag.to.fullPath !== this.$route.fullPath) { 130 if (tag.to.fullPath !== this.$route.fullPath) {
140 - this.$store.dispatch(  
141 - "tagsView/updateVisitedView",  
142 - this.$route  
143 - ); 131 + this.$store.dispatch('tagsView/updateVisitedView', this.$route)
144 } 132 }
145 - break; 133 + break
146 } 134 }
147 } 135 }
148 - }); 136 + })
149 }, 137 },
150 refreshSelectedTag(view) { 138 refreshSelectedTag(view) {
151 - this.$store.dispatch("tagsView/delCachedView", view).then(() => {  
152 - const { fullPath } = view; 139 + this.$store.dispatch('tagsView/delCachedView', view).then(() => {
  140 + const { fullPath } = view
153 this.$nextTick(() => { 141 this.$nextTick(() => {
154 this.$router.replace({ 142 this.$router.replace({
155 - path: "/redirect" + fullPath  
156 - });  
157 - });  
158 - }); 143 + path: '/redirect' + fullPath
  144 + })
  145 + })
  146 + })
159 }, 147 },
160 closeSelectedTag(view) { 148 closeSelectedTag(view) {
161 - this.$store  
162 - .dispatch("tagsView/delView", view)  
163 - .then(({ visitedViews }) => { 149 + this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => {
164 if (this.isActive(view)) { 150 if (this.isActive(view)) {
165 - this.toLastView(visitedViews, view); 151 + this.toLastView(visitedViews, view)
166 } 152 }
167 - }); 153 + })
168 }, 154 },
169 closeOthersTags() { 155 closeOthersTags() {
170 - this.$router.push(this.selectedTag);  
171 - this.$store  
172 - .dispatch("tagsView/delOthersViews", this.selectedTag)  
173 - .then(() => {  
174 - this.moveToCurrentTag();  
175 - }); 156 + this.$router.push(this.selectedTag)
  157 + this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => {
  158 + this.moveToCurrentTag()
  159 + })
176 }, 160 },
177 closeAllTags(view) { 161 closeAllTags(view) {
178 - this.$store  
179 - .dispatch("tagsView/delAllViews")  
180 - .then(({ visitedViews }) => { 162 + this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => {
181 if (this.affixTags.some(tag => tag.path === view.path)) { 163 if (this.affixTags.some(tag => tag.path === view.path)) {
182 - return; 164 + return
183 } 165 }
184 - this.toLastView(visitedViews, view);  
185 - }); 166 + this.toLastView(visitedViews, view)
  167 + })
186 }, 168 },
187 toLastView(visitedViews, view) { 169 toLastView(visitedViews, view) {
188 - const latestView = visitedViews.slice(-1)[0]; 170 + const latestView = visitedViews.slice(-1)[0]
189 if (latestView) { 171 if (latestView) {
190 - this.$router.push(latestView.fullPath); 172 + this.$router.push(latestView.fullPath)
191 } else { 173 } else {
192 // now the default is to redirect to the home page if there is no tags-view, 174 // now the default is to redirect to the home page if there is no tags-view,
193 // you can adjust it according to your needs. 175 // you can adjust it according to your needs.
194 - if (view.name === "Dashboard") { 176 + if (view.name === 'Dashboard') {
195 // to reload home page 177 // to reload home page
196 - this.$router.replace({ path: "/redirect" + view.fullPath }); 178 + this.$router.replace({ path: '/redirect' + view.fullPath })
197 } else { 179 } else {
198 - this.$router.push("/"); 180 + this.$router.push('/')
199 } 181 }
200 } 182 }
201 }, 183 },
202 openMenu(tag, e) { 184 openMenu(tag, e) {
203 - const menuMinWidth = 105;  
204 - const offsetLeft = this.$el.getBoundingClientRect().left; // container margin left  
205 - const offsetWidth = this.$el.offsetWidth; // container width  
206 - const maxLeft = offsetWidth - menuMinWidth; // left boundary  
207 - const left = e.clientX - offsetLeft + 15; // 15: margin right 185 + const menuMinWidth = 105
  186 + const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
  187 + const offsetWidth = this.$el.offsetWidth // container width
  188 + const maxLeft = offsetWidth - menuMinWidth // left boundary
  189 + const left = e.clientX - offsetLeft + 15 // 15: margin right
208 190
209 if (left > maxLeft) { 191 if (left > maxLeft) {
210 - this.left = maxLeft; 192 + this.left = maxLeft
211 } else { 193 } else {
212 - this.left = left; 194 + this.left = left
213 } 195 }
214 196
215 - this.top = e.clientY;  
216 - this.visible = true;  
217 - this.selectedTag = tag; 197 + this.top = e.clientY
  198 + this.visible = true
  199 + this.selectedTag = tag
218 }, 200 },
219 closeMenu() { 201 closeMenu() {
220 - this.visible = false; 202 + this.visible = false
  203 + },
  204 + handleScroll() {
  205 + this.closeMenu()
221 } 206 }
222 } 207 }
223 -}; 208 +}
224 </script> 209 </script>
225 210
226 <style lang="scss" scoped> 211 <style lang="scss" scoped>
@@ -229,7 +214,7 @@ export default { @@ -229,7 +214,7 @@ export default {
229 width: 100%; 214 width: 100%;
230 background: #fff; 215 background: #fff;
231 border-bottom: 1px solid #d8dce5; 216 border-bottom: 1px solid #d8dce5;
232 - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04); 217 + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
233 .tags-view-wrapper { 218 .tags-view-wrapper {
234 .tags-view-item { 219 .tags-view-item {
235 display: inline-block; 220 display: inline-block;
@@ -255,7 +240,7 @@ export default { @@ -255,7 +240,7 @@ export default {
255 color: #fff; 240 color: #fff;
256 border-color: #42b983; 241 border-color: #42b983;
257 &::before { 242 &::before {
258 - content: ""; 243 + content: '';
259 background: #fff; 244 background: #fff;
260 display: inline-block; 245 display: inline-block;
261 width: 8px; 246 width: 8px;
@@ -278,7 +263,7 @@ export default { @@ -278,7 +263,7 @@ export default {
278 font-size: 12px; 263 font-size: 12px;
279 font-weight: 400; 264 font-weight: 400;
280 color: #333; 265 color: #333;
281 - box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3); 266 + box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
282 li { 267 li {
283 margin: 0; 268 margin: 0;
284 padding: 7px 16px; 269 padding: 7px 16px;
@@ -301,10 +286,10 @@ export default { @@ -301,10 +286,10 @@ export default {
301 vertical-align: 2px; 286 vertical-align: 2px;
302 border-radius: 50%; 287 border-radius: 50%;
303 text-align: center; 288 text-align: center;
304 - transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); 289 + transition: all .3s cubic-bezier(.645, .045, .355, 1);
305 transform-origin: 100% 50%; 290 transform-origin: 100% 50%;
306 &:before { 291 &:before {
307 - transform: scale(0.6); 292 + transform: scale(.6);
308 display: inline-block; 293 display: inline-block;
309 vertical-align: -3px; 294 vertical-align: -3px;
310 } 295 }