Appearance
标签导航删除选中处理 #
当删除的是激活状态 tag 时,我就让剩下集合中最有一个为激活态并跳转到对应路由
效果图 删除前 删除激活标签 guide 后
2-1 修改 tagviews 组件 #
删除后 调用 toLastView 方法,从剩下集合中得到最后一个路由 tag 触发路由跳转 即可
删除方法 删除后调用 toLastView
src/layout/components/TagsView/index.vue
vue
<template>
<div class="tags-view-container">
<div class="tags-view-wrapper">
<router-link
class="tags-view-item"
:class="{
active: isActive(tag)
}"
v-for="(tag, index) in visitedTags"
:key="index"
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
tag="span"
>
{{ tag.title }}
<span
class="el-icon-close"
@click.prevent.stop="closeSelectedTag(tag)"
></span>
</router-link>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, computed, watch, onMounted } from 'vue'
import { useRoute, RouteRecordRaw, useRouter } from 'vue-router'
import { useStore } from '@/store'
import { RouteLocationWithFullPath } from '@/store/modules/tagsView'
export default defineComponent({
name: 'TagsView',
setup() {
const store = useStore()
const router = useRouter()
const route = useRoute()
// 可显示的tags view
const visitedTags = computed(() => store.state.tagsView.visitedViews)
// 添加tag
const addTags = () => {
const { name } = route
if (name) {
store.dispatch('tagsView/addView', route)
}
}
// 路径发生变化追加tags view
watch(() => route.path, () => {
addTags()
})
// 最近当前router到tags view
onMounted(() => {
addTags()
})
// 当前是否是激活的tag
const isActive = (tag: RouteRecordRaw) => {
return tag.path === route.path
}
// 让删除后tags view集合中最后一个为选中状态
const toLastView = (visitedViews: RouteLocationWithFullPath[], view: RouteLocationWithFullPath) => {
// 得到集合中最后一个项tag view 可能没有
const lastView = visitedViews[visitedViews.length - 1]
if (lastView) {
router.push(lastView.fullPath as string)
} else { // 集合中都没有tag view时
// 如果刚刚删除的正是Dashboard 就重定向回Dashboard(首页)
if (view.name === 'Dashboard') {
router.replace({ path: '/redirect' + view.fullPath as string })
} else {
// tag都没有了 删除的也不是Dashboard 只能跳转首页
router.push('/')
}
}
}
// 关闭当前右键的tag路由
const closeSelectedTag = (view: RouteLocationWithFullPath) => {
// 关掉并移除view
store.dispatch('tagsView/delView', view).then(() => {
// 如果移除的view是当前选中状态view, 就让删除后的集合中最后一个tag view为选中态
if (isActive(view)) {
toLastView(visitedTags.value, view)
}
})
}
return {
visitedTags,
isActive,
closeSelectedTag
}
}
})
</script>
<style lang="scss" scoped>
.tags-view-container {
width: 100%;
height: 34px;
background: #fff;
border-bottom: 1px solid #d8dce5;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
.tags-view-wrapper {
.tags-view-item {
display: inline-block;
height: 26px;
line-height: 26px;
border: 1px solid #d8dce5;
background: #fff;
color: #495060;
padding: 0 8px;
box-sizing: border-box;
font-size: 12px;
margin-left: 5px;
margin-top: 4px;
&:first-of-type {
margin-left: 15px;
}
&:last-of-type {
margin-right: 15px;
}
&.active {
background-color: #42b983;
color: #fff;
border-color: #42b983;
&::before {
position: relative;
display: inline-block;
content: '';
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 5px;
background: #fff;
}
}
}
}
}
</style>
<style lang="scss">
.tags-view-container {
.el-icon-close {
width: 16px;
height: 16px;
position: relative;
left: 2px;
border-radius: 50%;
text-align: center;
transition: all .3s cubic-bezier(.645, .045, .355, 1);
transform-origin: 100% 50%;
&:before {
transform: scale(.6);
display: inline-block;
vertical-align: -1px;
}
&:hover {
background-color: #b4bccc;
color: #fff;
}
}
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
2-2 修改 tagsView/delView action #
修改 delView action 返回 promise
src/store/modules/tagsView.ts
typescript
import { Module, ActionTree, MutationTree } from 'vuex'
import { RouteRecordRaw, RouteRecordNormalized } from 'vue-router'
import { IRootState } from '@/store'
// 携带fullPath
export interface RouteLocationWithFullPath extends RouteRecordNormalized {
fullPath?: string;
}
export interface ITagsViewState {
// 存放当前显示的tags view集合
visitedViews: RouteLocationWithFullPath[];
}
// 定义mutations
const mutations: MutationTree<ITagsViewState> = {
// 添加可显示tags view
ADD_VISITED_VIEW(state, view) {
// 过滤去重
if (state.visitedViews.some(v => v.path === view.path)) return
// 没有titles时处理
state.visitedViews.push(Object.assign({}, view, {
title: view.meta.title || 'tag-name'
}))
},
DEL_VISITED_VIEW(state, view) {
const i = state.visitedViews.indexOf(view)
if (i > -1) {
state.visitedViews.splice(i, 1)
}
}
}
// 定义actions
const actions: ActionTree<ITagsViewState, IRootState> = {
// 添加tags view
addView({ dispatch }, view: RouteRecordRaw) {
dispatch('addVisitedView', view)
},
// 添加可显示的tags view 添加前commit里需要进行去重过滤
addVisitedView({ commit }, view: RouteRecordRaw) {
commit('ADD_VISITED_VIEW', view)
},
// 删除tags view
delView({ dispatch }, view: RouteRecordRaw) {
return new Promise(resolve => {
dispatch('delVisitedView', view)
resolve(null)
})
},
// 从可显示的集合中 删除tags view
delVisitedView({ commit }, view: RouteRecordRaw) {
commit('DEL_VISITED_VIEW', view)
}
}
const tagsView: Module<ITagsViewState, IRootState> = {
namespaced: true,
state: {
visitedViews: []
},
mutations,
actions
}
export default tagsView
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
本节参考源码 #
https://gitee.com/brolly/vue3-element-admin/commit/192704483ace14c92be98dc0a1d76ac6665111a5