1
1
<script setup lang="ts">
2
2
import { onMounted , watch , computed , unref , ref , nextTick } from ' vue'
3
3
import { useRouter } from ' vue-router'
4
- import type { RouteLocationNormalizedLoaded } from ' vue-router'
4
+ import type { RouteLocationNormalizedLoaded , RouterLinkProps } from ' vue-router'
5
5
import { usePermissionStore } from ' @/store/modules/permission'
6
6
import { useTagsViewStore } from ' @/store/modules/tagsView'
7
7
import { useI18n } from ' @/hooks/web/useI18n'
8
8
import { filterAffixTags } from ' ./helper'
9
9
import { ContextMenu , ContextMenuExpose } from ' @/components/ContextMenu'
10
10
import { useDesign } from ' @/hooks/web/useDesign'
11
11
import { useTemplateRefsList } from ' @vueuse/core'
12
+ import { ElScrollbar } from ' element-plus'
13
+ import { useScrollTo } from ' @/hooks/event/useScrollTo'
12
14
13
15
const { getPrefixCls } = useDesign ()
14
16
@@ -111,13 +113,97 @@ const toLastView = () => {
111
113
}
112
114
}
113
115
116
+ // 滚动到选中的tag
117
+ const moveToCurrentTag = async () => {
118
+ await nextTick ()
119
+ for (const v of unref (visitedViews )) {
120
+ if (v .fullPath === unref (currentRoute ).path ) {
121
+ moveToTarget (v )
122
+ if (v .fullPath !== unref (currentRoute ).fullPath ) {
123
+ tagsViewStore .updateVisitedView (unref (currentRoute ))
124
+ }
125
+
126
+ break
127
+ }
128
+ }
129
+ }
130
+
131
+ const tagLinksRefs = useTemplateRefsList <RouterLinkProps >()
132
+
133
+ const moveToTarget = (currentTag : RouteLocationNormalizedLoaded ) => {
134
+ const wrap$ = unref (scrollbarRef )?.wrap$
135
+ let firstTag: Nullable <RouterLinkProps > = null
136
+ let lastTag: Nullable <RouterLinkProps > = null
137
+
138
+ const tagList = unref (tagLinksRefs )
139
+ // find first tag and last tag
140
+ if (tagList .length > 0 ) {
141
+ firstTag = tagList [0 ]
142
+ lastTag = tagList [tagList .length - 1 ]
143
+ }
144
+ if ((firstTag ?.to as RouteLocationNormalizedLoaded ).fullPath === currentTag .fullPath ) {
145
+ // 直接滚动到0的位置
146
+ const { start } = useScrollTo ({
147
+ el: wrap$ ! ,
148
+ position: ' scrollLeft' ,
149
+ to: 0 ,
150
+ duration: 500
151
+ })
152
+ start ()
153
+ } else if ((lastTag ?.to as RouteLocationNormalizedLoaded ).fullPath === currentTag .fullPath ) {
154
+ // 滚动到最后的位置
155
+ const { start } = useScrollTo ({
156
+ el: wrap$ ! ,
157
+ position: ' scrollLeft' ,
158
+ to: wrap$ ! .scrollWidth - wrap$ ! .offsetWidth ,
159
+ duration: 500
160
+ })
161
+ start ()
162
+ } else {
163
+ // find preTag and nextTag
164
+ const currentIndex: number = tagList .findIndex (
165
+ (item ) => (item ?.to as RouteLocationNormalizedLoaded ).fullPath === currentTag .fullPath
166
+ )
167
+ const tgsRefs = document .getElementsByClassName (` ${prefixCls }__item ` )
168
+
169
+ const prevTag = tgsRefs [currentIndex - 1 ] as HTMLElement
170
+ const nextTag = tgsRefs [currentIndex + 1 ] as HTMLElement
171
+
172
+ // the tag's offsetLeft after of nextTag
173
+ const afterNextTagOffsetLeft = nextTag .offsetLeft + nextTag .offsetWidth + 4
174
+
175
+ // the tag's offsetLeft before of prevTag
176
+ const beforePrevTagOffsetLeft = prevTag .offsetLeft - 4
177
+
178
+ if (afterNextTagOffsetLeft > unref (scrollLeftNumber ) + wrap$ ! .offsetWidth ) {
179
+ const { start } = useScrollTo ({
180
+ el: wrap$ ! ,
181
+ position: ' scrollLeft' ,
182
+ to: afterNextTagOffsetLeft - wrap$ ! .offsetWidth ,
183
+ duration: 500
184
+ })
185
+ start ()
186
+ } else if (beforePrevTagOffsetLeft < unref (scrollLeftNumber )) {
187
+ const { start } = useScrollTo ({
188
+ el: wrap$ ! ,
189
+ position: ' scrollLeft' ,
190
+ to: beforePrevTagOffsetLeft ,
191
+ duration: 500
192
+ })
193
+ start ()
194
+ }
195
+ }
196
+ }
197
+
114
198
// 是否是当前tag
115
199
const isActive = (route : RouteLocationNormalizedLoaded ): boolean => {
116
200
return route .path === unref (currentRoute ).path
117
201
}
118
202
203
+ // 所有右键菜单组件的元素
119
204
const itemRefs = useTemplateRefsList <ComponentRef <typeof ContextMenu & ContextMenuExpose >>()
120
205
206
+ // 右键菜单装填改变的时候
121
207
const visibleChange = (
122
208
visible : boolean ,
123
209
ref : ComponentRef <typeof ContextMenu & ContextMenuExpose >
@@ -133,6 +219,28 @@ const visibleChange = (
133
219
}
134
220
}
135
221
222
+ // elscroll 实例
223
+ const scrollbarRef = ref <ComponentRef <typeof ElScrollbar >>()
224
+
225
+ // 保存滚动位置
226
+ const scrollLeftNumber = ref (0 )
227
+
228
+ const scroll = ({ scrollLeft }) => {
229
+ scrollLeftNumber .value = scrollLeft as number
230
+ }
231
+
232
+ // 移动到某个位置
233
+ const move = (to : number ) => {
234
+ const wrap$ = unref (scrollbarRef )?.wrap$
235
+ const { start } = useScrollTo ({
236
+ el: wrap$ ! ,
237
+ position: ' scrollLeft' ,
238
+ to: unref (scrollLeftNumber ) + to ,
239
+ duration: 500
240
+ })
241
+ start ()
242
+ }
243
+
136
244
onMounted (() => {
137
245
initTags ()
138
246
addTags ()
@@ -142,7 +250,7 @@ watch(
142
250
() => currentRoute .value ,
143
251
() => {
144
252
addTags ()
145
- // moveToCurrentTag()
253
+ moveToCurrentTag ()
146
254
}
147
255
)
148
256
</script >
@@ -152,12 +260,14 @@ watch(
152
260
<span
153
261
:class =" `${prefixCls}__tool`"
154
262
class =" w-[var(--tags-view-height)] h-[var(--tags-view-height)] text-center leading-[var(--tags-view-height)] cursor-pointer"
263
+ @click =" move(-200)"
155
264
>
156
265
<Icon icon =" ep:d-arrow-left" color =" #333" />
157
266
</span >
158
267
<div class =" overflow-hidden flex-1" >
159
- <ElScrollbar class =" h-full" >
268
+ <ElScrollbar ref = " scrollbarRef " class =" h-full" @scroll = " scroll " >
160
269
<div class =" flex h-full" >
270
+ <div ></div >
161
271
<ContextMenu
162
272
:ref =" itemRefs.set"
163
273
:schema =" [
@@ -228,10 +338,10 @@ watch(
228
338
]"
229
339
@visible-change =" visibleChange"
230
340
>
231
- <router-link :to =" { ...item }" custom v-slot =" { navigate }" >
341
+ <router-link :ref = " tagLinksRefs.set " : to =" { ...item }" custom v-slot =" { navigate }" >
232
342
<div
233
343
@click =" navigate"
234
- class =" h-full flex justify-center items-center whitespace-nowrap"
344
+ class =" h-full flex justify-center items-center whitespace-nowrap pl-15px "
235
345
>
236
346
{{ t(item?.meta?.title as string) }}
237
347
<Icon
@@ -250,6 +360,7 @@ watch(
250
360
<span
251
361
:class =" `${prefixCls}__tool`"
252
362
class =" w-[var(--tags-view-height)] h-[var(--tags-view-height)] text-center leading-[var(--tags-view-height)] cursor-pointer"
363
+ @click =" move(200)"
253
364
>
254
365
<Icon icon =" ep:d-arrow-right" color =" #333" />
255
366
</span >
@@ -358,9 +469,8 @@ watch(
358
469
position : relative ;
359
470
top : 2px ;
360
471
height : calc (~ ' 100% - 4px' );
361
- padding : 0 15px ;
472
+ // padding: 0 15px;
362
473
font-size : 12px ;
363
- line-height : calc (~ ' var( - -tags-view-height) - 4px' );
364
474
cursor : pointer ;
365
475
border : 1px solid #d9d9d9 ;
366
476
0 commit comments