vue-stack-tabs 0.1.27 → 0.1.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,600 +1,602 @@
1
- import type { ITabData, ITabItem, ITabPage } from '../model/TabModel'
2
- import {
3
- defineComponent,
4
- onActivated,
5
- onDeactivated,
6
- onMounted,
7
- onUnmounted,
8
- ref,
9
- unref
10
- } from 'vue'
11
- import type { DefineComponent, VNode } from 'vue'
12
- import { useRouter } from 'vue-router'
13
- import type { RouteLocationNormalizedLoaded } from 'vue-router'
14
- import { ulid } from 'ulidx'
15
- import { encodeTabInfo, createPageId, decodeTabInfo } from '../utils/TabIdHelper'
16
- import { defu } from 'defu'
17
- import { Stack } from '../model/TabModel'
18
- import { uriDecode } from '../utils/UriHelper'
19
- import PageLoading from '../components/PageLoading.vue'
20
- import { MittType, useEmitter } from './useTabMitt'
21
- const tabs = ref<ITabItem[]>([])
22
- const defaultTabs: ITabItem[] = []
23
- // cache
24
- const caches = ref<string[]>([])
25
- // Dynamic components
26
- const components = new Map<string, any>()
27
- const deletableCache = new Set<String>()
28
- const pageShown = ref<boolean>(true)
29
- const SESSION_TAB_NAME = 'stacktab-active-tab'
30
- const pageScroller = new Map<string, Map<string, any>>()
31
- let initialed = false
32
- let max = 0
33
- let scrollbar = false
34
- export default () => {
35
- const router = useRouter()
36
- const emitter = useEmitter()
37
- const size = (): number => {
38
- return tabs.value.length
39
- }
40
- /**
41
- * Init the Tab list
42
- */
43
- const initial = (staticTabs: ITabData[]) => {
44
- // add default tabs
45
- defaultTabs.splice(0)
46
- caches.value.splice(0)
47
- deletableCache.clear()
48
- components.clear()
49
- for (const item of staticTabs) {
50
- // const id = ulid()
51
- const fullItem = defu(item, { id: ulid() })
52
- const __tab = encodeTabInfo(fullItem)
53
- const uri = uriDecode(fullItem.path)
54
- const config = defu(fullItem, {
55
- closable: true,
56
- refreshable: true,
57
- iframe: false
58
- })
59
- const cacheName = createPageId(fullItem.id, uri.path, uri.query)
60
- const page: ITabPage = {
61
- id: cacheName,
62
- tabId: fullItem.id,
63
- path: uri.path,
64
- query: defu(uri.query, { __tab })
65
- }
66
- const pages = new Stack<ITabPage>()
67
- pages.push(page)
68
- const tab: ITabItem = {
69
- id: fullItem.id,
70
- title: config.title,
71
- closable: config.closable,
72
- refreshable: config.refreshable,
73
- iframe: config.iframe,
74
- active: false,
75
- pages
76
- }
77
-
78
- defaultTabs.push(tab)
79
- tabs.value.push(tab)
80
- caches.value.push(cacheName)
81
- }
82
-
83
- // add temp tab from session
84
- const tempTab = window.sessionStorage.getItem(SESSION_TAB_NAME)
85
- if (tempTab !== null && tempTab !== undefined) {
86
- // const tempItems = JSON.parse(window.sessionStorage.getItem('tabItems')!)
87
- const temp = JSON.parse(tempTab, (k, v) => {
88
- if (k === 'pages') {
89
- return new Stack<ITabPage>(v)
90
- } else {
91
- return v
92
- }
93
- })
94
- // const temp = defu({ pages: new Stack<ITabPage>() }, tempItems) as ITabItem
95
- let hasTab = false
96
- for (const tab of tabs.value) {
97
- if (tab.id === temp.id) {
98
- hasTab = true
99
- break
100
- }
101
- }
102
- if (!hasTab) {
103
- tabs.value.push(temp)
104
- }
105
- }
106
- initialed = true
107
- }
108
- const hasTab = (id: string) => {
109
- for (const tab of tabs.value) {
110
- if (tab.id === id) {
111
- return true
112
- }
113
- }
114
- return false
115
- }
116
- const canAddTab = () => {
117
- return max <= 0 || (max > 0 && max > tabs.value.length)
118
- }
119
- const addTab = (tab: ITabItem) => {
120
- return new Promise((resolve) => {
121
- tabs.value.push(tab)
122
- resolve(true)
123
- })
124
- }
125
- const getTab = (id: string) => {
126
- for (const tab of tabs.value) {
127
- if (tab.id === id) {
128
- return tab
129
- }
130
- }
131
- return null
132
- }
133
- const addPage = (route: RouteLocationNormalizedLoaded, component: VNode): DefineComponent => {
134
- let cacheComponent: DefineComponent
135
- const tabInfo = decodeTabInfo(route.query.__tab as string)
136
- const cacheName = createPageId(tabInfo.id!, route.path, route.query.valueOf())
137
- const src = route.query.__src
138
- // 增加tab
139
- let activeTab: ITabItem | null = null
140
- for (const tab of unref(tabs)) {
141
- tab.active = false
142
- if (tab.id === tabInfo.id) {
143
- activeTab = tab as ITabItem
144
- }
145
- }
146
- const page: ITabPage = {
147
- id: cacheName,
148
- tabId: tabInfo.id!,
149
- path: route.path,
150
- query: route.query as Record<string, string>
151
- }
152
-
153
- if (activeTab === null) {
154
- const pages = new Stack<ITabPage>()
155
- pages.push(page)
156
- activeTab = {
157
- id: tabInfo.id!,
158
- title: tabInfo.title,
159
- closable: tabInfo.closable!,
160
- refreshable: tabInfo.refreshable!,
161
- iframe: tabInfo.iframe!,
162
- url: decodeURIComponent(src as string),
163
- active: true,
164
- pages
165
- }
166
- addTab(activeTab).then(() => {
167
- emitter.emit(MittType.TAB_ACTIVE, { id: tabInfo.id!, isRoute: false })
168
- })
169
- } else {
170
- activeTab.active = true
171
- if (activeTab.pages.isEmpty() || activeTab.pages.peek()!.id !== page.id) {
172
- activeTab.pages.push(page)
173
- }
174
- }
175
- updateSession(activeTab)
176
- // const cacheSet = new Set(caches.value)
177
- if (components.has(cacheName)) {
178
- cacheComponent = components.get(cacheName)!
179
- } else {
180
- // 增加控件
181
- cacheComponent = defineComponent({
182
- name: cacheName!,
183
- components: {
184
- DynamicComponent: component
185
- },
186
- emits: {
187
- onLoaded: null
188
- },
189
- setup(props, context) {
190
- onMounted(() => {
191
- context.emit('onLoaded')
192
- addPageScroller(cacheName, '.cache-page-wrapper')
193
- })
194
- onDeactivated(() => {
195
- saveScroller(cacheName)
196
- })
197
- onActivated(() => {
198
- context.emit('onLoaded')
199
- restoreScroller(cacheName)
200
- console.log(pageScroller.get(cacheName))
201
- removeDeletableCache()
202
- component.props = null
203
- })
204
- onUnmounted(() => {
205
- console.log('on unMounted', cacheName)
206
- removeScroller(cacheName)
207
- // setTimeout(() => {
208
- // 刷新时重置路由
209
- // pageShown.value = true
210
- // }, 500)
211
- })
212
- return () => (
213
- <div
214
- class="cache-page-wrapper"
215
- id={'W-' + tabInfo.id}
216
- style={[scrollbar ? 'overflow:auto' : 'overflow:hidden']}
217
- >
218
- <dynamic-component tId={tabInfo.id} pId={cacheName} />
219
- <PageLoading tId={tabInfo.id!} />
220
- </div>
221
- )
222
- }
223
- })
224
- components.set(cacheName, cacheComponent)
225
-
226
- //
227
- }
228
-
229
- // 加入缓存
230
- // const cacheSet = new Set(caches.value)
231
- // if (routerAlive.value) {
232
- // if (!cacheSet.has(cacheName)) {
233
- // caches.value.push(cacheName)
234
- // }
235
- // }
236
- addCache(cacheName)
237
- return cacheComponent
238
- }
239
- const restoreScroller = (pageId: string) => {
240
- const scroller = pageScroller.get(pageId)
241
- if (scroller) {
242
- for (const key of scroller.keys()) {
243
- if (document.querySelector(key)) {
244
- const result = scroller.get(key)!
245
- document.querySelector(key)!.scrollTop = result.top
246
- document.querySelector(key)!.scrollLeft = result.left
247
- }
248
- }
249
- }
250
- }
251
- const saveScroller = (pageId: string) => {
252
- const scroller = pageScroller.get(pageId)
253
- if (scroller) {
254
- for (const key of scroller.keys()) {
255
- const result = {
256
- top: document.querySelector(key)?.scrollTop ?? 0,
257
- left: document.querySelector(key)?.scrollLeft ?? 0
258
- }
259
- scroller.set(key, result)
260
- }
261
- }
262
- }
263
-
264
- const removeScroller = (pageId: string) => {
265
- const scroller = pageScroller.get(pageId)!
266
- if (scroller) {
267
- scroller.clear()
268
- pageScroller.delete(pageId)
269
- }
270
- }
271
- const addPageScroller = (pageId: string, ...scrollerIds: string[]) => {
272
- if (!pageScroller.has(pageId)) {
273
- pageScroller.set(pageId, new Map<string, number>())
274
- }
275
- const scroller = pageScroller.get(pageId)!
276
- for (const sId of scrollerIds) {
277
- scroller.set(sId, { top: 0, left: 0 })
278
- }
279
- }
280
- /**
281
- * if id is null , then remove all tab that deleted is true
282
- * @param id
283
- */
284
- const removeTab = (id: string): string => {
285
- let activeTabId = ''
286
- for (let i = tabs.value.length - 1; i >= 0; i--) {
287
- if (id === unref(tabs)[i].id) {
288
- //if it's not closable then return
289
- if (!unref(tabs)[i].closable) {
290
- return id
291
- }
292
- for (const item of unref(tabs)[i].pages.list()) {
293
- removeComponent(item.id)
294
- markDeletableCache(item.id)
295
- }
296
- unref(tabs)[i].pages.clear()
297
- if (tabs.value.length > 1 && unref(tabs)[i].active) {
298
- if (i === 0) {
299
- activeTabId = unref(tabs)[i + 1].id
300
- } else {
301
- activeTabId = unref(tabs)[i - 1].id
302
- }
303
- }
304
- unref(tabs).splice(i, 1)
305
-
306
- break
307
- }
308
- }
309
- if (activeTabId !== '') {
310
- emitter.emit(MittType.TAB_ACTIVE, { id: activeTabId })
311
- // active(activeTabId)
312
- }
313
- // if remove inactive tab,then we need remove the cache manually.
314
- removeDeletableCache()
315
- return activeTabId
316
- }
317
-
318
- const removeAllTabs = () => {
319
- const uTabs = unref(tabs)
320
- for (let i = uTabs.length - 1; i >= 0; i--) {
321
- if (uTabs[i].closable) {
322
- for (const item of uTabs[i].pages.list()) {
323
- removeComponent(item.id)
324
- markDeletableCache(item.id)
325
- }
326
- unref(tabs)[i].pages.clear()
327
- unref(tabs).splice(i, 1)
328
- }
329
- }
330
- for (const stay of uTabs) {
331
- if (stay.active) {
332
- removeDeletableCache()
333
- return
334
- }
335
- }
336
- // 如果都是非Active状态,就把最后一个active
337
- emitter.emit(MittType.TAB_ACTIVE, { id: uTabs[uTabs.length - 1].id })
338
- // active(uTabs[uTabs.length - 1].id)
339
- }
340
- const removeOtherTabs = (id: string) => {
341
- const uTabs = unref(tabs)
342
- let activeTab
343
- for (let i = uTabs.length - 1; i >= 0; i--) {
344
- if (uTabs[i].id === id) {
345
- activeTab = uTabs[i]
346
- } else if (uTabs[i].closable && uTabs[i].id !== id) {
347
- for (const item of uTabs[i].pages.list()) {
348
- removeComponent(item.id)
349
- markDeletableCache(item.id)
350
- }
351
- unref(tabs)[i].pages.clear()
352
- unref(tabs).splice(i, 1)
353
- }
354
- }
355
- if (!activeTab!.active) {
356
- emitter.emit(MittType.TAB_ACTIVE, { id })
357
- // active(id)
358
- } else {
359
- removeDeletableCache()
360
- }
361
- }
362
- /**
363
- * 关闭左边
364
- * @param id
365
- */
366
- const removeLeftTabs = (id: string) => {
367
- let startIndex = -1
368
- const uTabs = unref(tabs)
369
- for (let i = 0; i < uTabs.length; i++) {
370
- if (uTabs[i].id === id) {
371
- startIndex = i - 1
372
- break
373
- }
374
- }
375
- for (let i = startIndex; i >= 0; i--) {
376
- if (uTabs[i].closable) {
377
- for (const item of uTabs[i].pages.list()) {
378
- removeComponent(item.id)
379
- markDeletableCache(item.id)
380
- }
381
- unref(tabs)[i].pages.clear()
382
- unref(tabs).splice(i, 1)
383
- }
384
- }
385
- for (const stay of uTabs) {
386
- if (stay.active) {
387
- removeDeletableCache()
388
- return
389
- }
390
- }
391
- // 如果都是非Active状态,就把最后一个active
392
- emitter.emit(MittType.TAB_ACTIVE, { id: uTabs[0].id })
393
- // active(uTabs[0].id)
394
- }
395
-
396
- /**
397
- * 关闭右边
398
- * @param id
399
- */
400
- const removeRightTabs = (id: string) => {
401
- const uTabs = unref(tabs)
402
- let startIndex = uTabs.length
403
- for (let i = 0; i < uTabs.length; i++) {
404
- if (uTabs[i].id === id) {
405
- startIndex = i + 1
406
- break
407
- }
408
- }
409
- for (let i = uTabs.length - 1; i >= startIndex; i--) {
410
- if (uTabs[i].closable) {
411
- for (const item of uTabs[i].pages.list()) {
412
- removeComponent(item.id)
413
- markDeletableCache(item.id)
414
- }
415
- unref(tabs)[i].pages.clear()
416
- unref(tabs).splice(i, 1)
417
- }
418
- }
419
- for (const stay of uTabs) {
420
- if (stay.active) {
421
- removeDeletableCache()
422
- return
423
- }
424
- }
425
- emitter.emit(MittType.TAB_ACTIVE, { id: uTabs[uTabs.length - 1].id })
426
- // active(uTabs[uTabs.length - 1].id)
427
- }
428
-
429
- /**
430
- * 刷新
431
- */
432
- const refreshTab = (id: string) => {
433
- // const uTabs = unref(tabs)
434
- for (const uTabs of unref(tabs)) {
435
- if (uTabs.id === id) {
436
- const currentPageId = uTabs.pages.peek()?.id
437
- for (let i = caches.value.length - 1; i >= 0; i--) {
438
- const cacheId = caches.value[i]
439
- if (cacheId === currentPageId) {
440
- caches.value.splice(i, 1)
441
- // 如果要刷新的是显示的TAB
442
- if (uTabs.active) {
443
- pageShown.value = false
444
- }
445
- break
446
- }
447
- }
448
- break
449
- }
450
- }
451
- }
452
- /**
453
- * 全部刷新
454
- */
455
- const refreshAllTabs = () => {
456
- const refreshPage = new Set<string>()
457
- tabs.value.forEach((value) => {
458
- refreshPage.add(value.pages.peek()!.id)
459
- })
460
- for (let i = caches.value.length - 1; i >= 0; i--) {
461
- const cacheId = caches.value[i]
462
- if (refreshPage.has(cacheId)) {
463
- caches.value.splice(i, 1)
464
- }
465
- }
466
- pageShown.value = false
467
- }
468
-
469
- const addComponent = (id: string, comp: DefineComponent) => {
470
- components.set(id, comp)
471
- }
472
- const getComponent = (id: string) => {
473
- return components.get(id)
474
- }
475
- const removeComponent = (id: string) => {
476
- components.delete(id)
477
- }
478
-
479
- /**
480
- * Delete the cache after new Page loaded.
481
- * @param cacheName
482
- */
483
- const markDeletableCache = (cacheName: string) => {
484
- deletableCache.add(cacheName)
485
- }
486
- const removeDeletableCache = () => {
487
- for (let i = caches.value.length - 1; i >= 0; i--) {
488
- if (deletableCache.has(caches.value[i])) {
489
- unref(caches).splice(i, 1)
490
- }
491
- }
492
- deletableCache.clear()
493
- }
494
- const removeCache = (cacheName: string) => {
495
- for (let i = caches.value.length - 1; i >= 0; i--) {
496
- if (cacheName === caches.value[i]) {
497
- unref(caches).splice(i, 1)
498
- }
499
- }
500
- }
501
- const addCache = (cacheName: string) => {
502
- const cacheSet = new Set(caches.value)
503
- if (!cacheSet.has(cacheName)) {
504
- caches.value.push(cacheName)
505
- }
506
- }
507
-
508
- /**
509
- * active the tab,
510
- * @param id
511
- * @param route
512
- * @return true if already actived
513
- */
514
- const active = (id: string, route = true) => {
515
- return new Promise((resolve) => {
516
- for (let i = tabs.value.length - 1; i >= 0; i--) {
517
- const tab = tabs.value[i] as ITabItem
518
- if (tab.id === id) {
519
- if (tab.active) {
520
- break
521
- } else {
522
- tab.active = true
523
- pageShown.value = false
524
- updateSession(tab)
525
- if (route) {
526
- const top = tab.pages.peek()
527
- router.push({
528
- path: top!.path,
529
- query: top!.query
530
- })
531
- }
532
- }
533
- } else {
534
- tabs.value[i].active = false
535
- }
536
- }
537
- resolve(true)
538
- })
539
- }
540
- /**
541
- * save current tab info into Browser's session
542
- * @param id
543
- */
544
- const updateSession = (tab: ITabItem) => {
545
- // window.sessionStorage.setItem('tabItems', JSON.stringify(currentItems?.values()))
546
- window.sessionStorage.setItem(SESSION_TAB_NAME, JSON.stringify(tab))
547
- }
548
- const reset = () => {
549
- destroy()
550
- unref(tabs).push(...defaultTabs)
551
- emitter.emit(MittType.TAB_ACTIVE, { id: defaultTabs[0].id! })
552
- // active(defaultTabs[0].id)
553
- }
554
- const destroy = () => {
555
- unref(tabs).splice(0)
556
- components.clear()
557
- deletableCache.clear()
558
- unref(caches).splice(0)
559
- }
560
- const setMaxSize = (size: number) => {
561
- max = size
562
- }
563
- const setGlobalScroll = (globalScroll: boolean) => {
564
- scrollbar = globalScroll
565
- }
566
-
567
- return {
568
- tabs,
569
- caches,
570
- pageShown,
571
- initialed,
572
- setMaxSize,
573
- canAddTab,
574
- initial,
575
- size,
576
- active,
577
- destroy,
578
- reset,
579
- addCache,
580
- removeCache,
581
- getTab,
582
- addTab,
583
- removeTab,
584
- removeAllTabs,
585
- removeLeftTabs,
586
- removeRightTabs,
587
- removeOtherTabs,
588
- hasTab,
589
- addPage,
590
- addComponent,
591
- getComponent,
592
- removeComponent,
593
- markDeletableCache,
594
- removeDeletableCache,
595
- refreshTab,
596
- refreshAllTabs,
597
- addPageScroller,
598
- setGlobalScroll
599
- }
600
- }
1
+ import type { ITabData, ITabItem, ITabPage } from '../model/TabModel'
2
+ import {
3
+ defineComponent,
4
+ onActivated,
5
+ onDeactivated,
6
+ onMounted,
7
+ onUnmounted,
8
+ ref,
9
+ unref
10
+ } from 'vue'
11
+ import type { DefineComponent, VNode } from 'vue'
12
+ import { useRouter } from 'vue-router'
13
+ import type { RouteLocationNormalizedLoaded } from 'vue-router'
14
+ import { ulid } from 'ulidx'
15
+ import { encodeTabInfo, createPageId, decodeTabInfo } from '../utils/TabIdHelper'
16
+ import { defu } from 'defu'
17
+ import { Stack } from '../model/TabModel'
18
+ import { uriDecode } from '../utils/UriHelper'
19
+ import PageLoading from '../components/PageLoading.vue'
20
+ import { MittType, useEmitter } from './useTabMitt'
21
+ const tabs = ref<ITabItem[]>([])
22
+ const defaultTabs: ITabItem[] = []
23
+ // cache
24
+ const caches = ref<string[]>([])
25
+ // Dynamic components
26
+ const components = new Map<string, any>()
27
+ const deletableCache = new Set<String>()
28
+ const pageShown = ref<boolean>(true)
29
+ const SESSION_TAB_NAME = 'stacktab-active-tab'
30
+ const pageScroller = new Map<string, Map<string, any>>()
31
+ let initialed = false
32
+ let max = 0
33
+ let scrollbar = false
34
+ export default () => {
35
+ const router = useRouter()
36
+ const emitter = useEmitter()
37
+ const size = (): number => {
38
+ return tabs.value.length
39
+ }
40
+ /**
41
+ * Init the Tab list
42
+ */
43
+ const initial = (staticTabs: ITabData[]) => {
44
+ // add default tabs
45
+ defaultTabs.splice(0)
46
+ caches.value.splice(0)
47
+ deletableCache.clear()
48
+ components.clear()
49
+ for (const item of staticTabs) {
50
+ // const id = ulid()
51
+ const fullItem = defu(item, { id: ulid() })
52
+ const __tab = encodeTabInfo(fullItem)
53
+ const uri = uriDecode(fullItem.path)
54
+ const config = defu(fullItem, {
55
+ closable: true,
56
+ refreshable: true,
57
+ iframe: false
58
+ })
59
+ const cacheName = createPageId(fullItem.id, uri.path, uri.query)
60
+ const page: ITabPage = {
61
+ id: cacheName,
62
+ tabId: fullItem.id,
63
+ path: uri.path,
64
+ query: defu(uri.query, { __tab })
65
+ }
66
+ const pages = new Stack<ITabPage>()
67
+ pages.push(page)
68
+ const tab: ITabItem = {
69
+ id: fullItem.id,
70
+ title: config.title,
71
+ closable: config.closable,
72
+ refreshable: config.refreshable,
73
+ iframe: config.iframe,
74
+ active: false,
75
+ pages
76
+ }
77
+
78
+ defaultTabs.push(tab)
79
+ tabs.value.push(tab)
80
+ caches.value.push(cacheName)
81
+ }
82
+
83
+ // add temp tab from session
84
+ const tempTab = window.sessionStorage.getItem(SESSION_TAB_NAME)
85
+ if (tempTab !== null && tempTab !== undefined) {
86
+ // const tempItems = JSON.parse(window.sessionStorage.getItem('tabItems')!)
87
+ const temp = JSON.parse(tempTab, (k, v) => {
88
+ if (k === 'pages') {
89
+ return new Stack<ITabPage>(v)
90
+ } else {
91
+ return v
92
+ }
93
+ })
94
+ // const temp = defu({ pages: new Stack<ITabPage>() }, tempItems) as ITabItem
95
+ let hasTab = false
96
+ for (const tab of tabs.value) {
97
+ if (tab.id === temp.id) {
98
+ hasTab = true
99
+ break
100
+ }
101
+ }
102
+ if (!hasTab) {
103
+ tabs.value.push(temp)
104
+ }
105
+ }
106
+ initialed = true
107
+ }
108
+ const hasTab = (id: string) => {
109
+ for (const tab of tabs.value) {
110
+ if (tab.id === id) {
111
+ return true
112
+ }
113
+ }
114
+ return false
115
+ }
116
+ const canAddTab = () => {
117
+ return max <= 0 || (max > 0 && max > tabs.value.length)
118
+ }
119
+ const addTab = (tab: ITabItem) => {
120
+ return new Promise((resolve) => {
121
+ tabs.value.push(tab)
122
+ resolve(true)
123
+ })
124
+ }
125
+ const getTab = (id: string) => {
126
+ for (const tab of tabs.value) {
127
+ if (tab.id === id) {
128
+ return tab
129
+ }
130
+ }
131
+ return null
132
+ }
133
+ const addPage = (route: RouteLocationNormalizedLoaded, component: VNode): DefineComponent => {
134
+ let cacheComponent: DefineComponent
135
+ const tabInfo = decodeTabInfo(route.query.__tab as string)
136
+ const matchPath = route.matched[route.matched.length-1].path
137
+ const tmpPath = route.path.endsWith("/")?route.path.substring(0, route.path.length - 1):route.path
138
+ const cacheName = createPageId(tabInfo.id!, matchPath===tmpPath?tmpPath:route.path, route.query.valueOf())
139
+ const src = route.query.__src
140
+ // 增加tab
141
+ let activeTab: ITabItem | null = null
142
+ for (const tab of unref(tabs)) {
143
+ tab.active = false
144
+ if (tab.id === tabInfo.id) {
145
+ activeTab = tab as ITabItem
146
+ }
147
+ }
148
+ const page: ITabPage = {
149
+ id: cacheName,
150
+ tabId: tabInfo.id!,
151
+ path: route.path,
152
+ query: route.query as Record<string, string>
153
+ }
154
+
155
+ if (activeTab === null) {
156
+ const pages = new Stack<ITabPage>()
157
+ pages.push(page)
158
+ activeTab = {
159
+ id: tabInfo.id!,
160
+ title: tabInfo.title,
161
+ closable: tabInfo.closable!,
162
+ refreshable: tabInfo.refreshable!,
163
+ iframe: tabInfo.iframe!,
164
+ url: decodeURIComponent(src as string),
165
+ active: true,
166
+ pages
167
+ }
168
+ addTab(activeTab).then(() => {
169
+ emitter.emit(MittType.TAB_ACTIVE, { id: tabInfo.id!, isRoute: false })
170
+ })
171
+ } else {
172
+ activeTab.active = true
173
+ if (activeTab.pages.isEmpty() || activeTab.pages.peek()!.id !== page.id) {
174
+ activeTab.pages.push(page)
175
+ }
176
+ }
177
+ updateSession(activeTab)
178
+ // const cacheSet = new Set(caches.value)
179
+ if (components.has(cacheName)) {
180
+ cacheComponent = components.get(cacheName)!
181
+ } else {
182
+ // 增加控件
183
+ cacheComponent = defineComponent({
184
+ name: cacheName!,
185
+ components: {
186
+ DynamicComponent: component
187
+ },
188
+ emits: {
189
+ onLoaded: null
190
+ },
191
+ setup(props, context) {
192
+ onMounted(() => {
193
+ context.emit('onLoaded')
194
+ addPageScroller(cacheName, '.cache-page-wrapper')
195
+ })
196
+ onDeactivated(() => {
197
+ saveScroller(cacheName)
198
+ })
199
+ onActivated(() => {
200
+ context.emit('onLoaded')
201
+ restoreScroller(cacheName)
202
+ console.log(pageScroller.get(cacheName))
203
+ removeDeletableCache()
204
+ component.props = null
205
+ })
206
+ onUnmounted(() => {
207
+ console.log('on unMounted', cacheName)
208
+ removeScroller(cacheName)
209
+ // setTimeout(() => {
210
+ // 刷新时重置路由
211
+ // pageShown.value = true
212
+ // }, 500)
213
+ })
214
+ return () => (
215
+ <div
216
+ class="cache-page-wrapper"
217
+ id={'W-' + tabInfo.id}
218
+ style={[scrollbar ? 'overflow:auto' : 'overflow:hidden']}
219
+ >
220
+ <dynamic-component tId={tabInfo.id} pId={cacheName} />
221
+ <PageLoading tId={tabInfo.id!} />
222
+ </div>
223
+ )
224
+ }
225
+ })
226
+ components.set(cacheName, cacheComponent)
227
+
228
+ //
229
+ }
230
+
231
+ // 加入缓存
232
+ // const cacheSet = new Set(caches.value)
233
+ // if (routerAlive.value) {
234
+ // if (!cacheSet.has(cacheName)) {
235
+ // caches.value.push(cacheName)
236
+ // }
237
+ // }
238
+ addCache(cacheName)
239
+ return cacheComponent
240
+ }
241
+ const restoreScroller = (pageId: string) => {
242
+ const scroller = pageScroller.get(pageId)
243
+ if (scroller) {
244
+ for (const key of scroller.keys()) {
245
+ if (document.querySelector(key)) {
246
+ const result = scroller.get(key)!
247
+ document.querySelector(key)!.scrollTop = result.top
248
+ document.querySelector(key)!.scrollLeft = result.left
249
+ }
250
+ }
251
+ }
252
+ }
253
+ const saveScroller = (pageId: string) => {
254
+ const scroller = pageScroller.get(pageId)
255
+ if (scroller) {
256
+ for (const key of scroller.keys()) {
257
+ const result = {
258
+ top: document.querySelector(key)?.scrollTop ?? 0,
259
+ left: document.querySelector(key)?.scrollLeft ?? 0
260
+ }
261
+ scroller.set(key, result)
262
+ }
263
+ }
264
+ }
265
+
266
+ const removeScroller = (pageId: string) => {
267
+ const scroller = pageScroller.get(pageId)!
268
+ if (scroller) {
269
+ scroller.clear()
270
+ pageScroller.delete(pageId)
271
+ }
272
+ }
273
+ const addPageScroller = (pageId: string, ...scrollerIds: string[]) => {
274
+ if (!pageScroller.has(pageId)) {
275
+ pageScroller.set(pageId, new Map<string, number>())
276
+ }
277
+ const scroller = pageScroller.get(pageId)!
278
+ for (const sId of scrollerIds) {
279
+ scroller.set(sId, { top: 0, left: 0 })
280
+ }
281
+ }
282
+ /**
283
+ * if id is null , then remove all tab that deleted is true
284
+ * @param id
285
+ */
286
+ const removeTab = (id: string): string => {
287
+ let activeTabId = ''
288
+ for (let i = tabs.value.length - 1; i >= 0; i--) {
289
+ if (id === unref(tabs)[i].id) {
290
+ //if it's not closable then return
291
+ if (!unref(tabs)[i].closable) {
292
+ return id
293
+ }
294
+ for (const item of unref(tabs)[i].pages.list()) {
295
+ removeComponent(item.id)
296
+ markDeletableCache(item.id)
297
+ }
298
+ unref(tabs)[i].pages.clear()
299
+ if (tabs.value.length > 1 && unref(tabs)[i].active) {
300
+ if (i === 0) {
301
+ activeTabId = unref(tabs)[i + 1].id
302
+ } else {
303
+ activeTabId = unref(tabs)[i - 1].id
304
+ }
305
+ }
306
+ unref(tabs).splice(i, 1)
307
+
308
+ break
309
+ }
310
+ }
311
+ if (activeTabId !== '') {
312
+ emitter.emit(MittType.TAB_ACTIVE, { id: activeTabId })
313
+ // active(activeTabId)
314
+ }
315
+ // if remove inactive tab,then we need remove the cache manually.
316
+ removeDeletableCache()
317
+ return activeTabId
318
+ }
319
+
320
+ const removeAllTabs = () => {
321
+ const uTabs = unref(tabs)
322
+ for (let i = uTabs.length - 1; i >= 0; i--) {
323
+ if (uTabs[i].closable) {
324
+ for (const item of uTabs[i].pages.list()) {
325
+ removeComponent(item.id)
326
+ markDeletableCache(item.id)
327
+ }
328
+ unref(tabs)[i].pages.clear()
329
+ unref(tabs).splice(i, 1)
330
+ }
331
+ }
332
+ for (const stay of uTabs) {
333
+ if (stay.active) {
334
+ removeDeletableCache()
335
+ return
336
+ }
337
+ }
338
+ // 如果都是非Active状态,就把最后一个active
339
+ emitter.emit(MittType.TAB_ACTIVE, { id: uTabs[uTabs.length - 1].id })
340
+ // active(uTabs[uTabs.length - 1].id)
341
+ }
342
+ const removeOtherTabs = (id: string) => {
343
+ const uTabs = unref(tabs)
344
+ let activeTab
345
+ for (let i = uTabs.length - 1; i >= 0; i--) {
346
+ if (uTabs[i].id === id) {
347
+ activeTab = uTabs[i]
348
+ } else if (uTabs[i].closable && uTabs[i].id !== id) {
349
+ for (const item of uTabs[i].pages.list()) {
350
+ removeComponent(item.id)
351
+ markDeletableCache(item.id)
352
+ }
353
+ unref(tabs)[i].pages.clear()
354
+ unref(tabs).splice(i, 1)
355
+ }
356
+ }
357
+ if (!activeTab!.active) {
358
+ emitter.emit(MittType.TAB_ACTIVE, { id })
359
+ // active(id)
360
+ } else {
361
+ removeDeletableCache()
362
+ }
363
+ }
364
+ /**
365
+ * 关闭左边
366
+ * @param id
367
+ */
368
+ const removeLeftTabs = (id: string) => {
369
+ let startIndex = -1
370
+ const uTabs = unref(tabs)
371
+ for (let i = 0; i < uTabs.length; i++) {
372
+ if (uTabs[i].id === id) {
373
+ startIndex = i - 1
374
+ break
375
+ }
376
+ }
377
+ for (let i = startIndex; i >= 0; i--) {
378
+ if (uTabs[i].closable) {
379
+ for (const item of uTabs[i].pages.list()) {
380
+ removeComponent(item.id)
381
+ markDeletableCache(item.id)
382
+ }
383
+ unref(tabs)[i].pages.clear()
384
+ unref(tabs).splice(i, 1)
385
+ }
386
+ }
387
+ for (const stay of uTabs) {
388
+ if (stay.active) {
389
+ removeDeletableCache()
390
+ return
391
+ }
392
+ }
393
+ // 如果都是非Active状态,就把最后一个active
394
+ emitter.emit(MittType.TAB_ACTIVE, { id: uTabs[0].id })
395
+ // active(uTabs[0].id)
396
+ }
397
+
398
+ /**
399
+ * 关闭右边
400
+ * @param id
401
+ */
402
+ const removeRightTabs = (id: string) => {
403
+ const uTabs = unref(tabs)
404
+ let startIndex = uTabs.length
405
+ for (let i = 0; i < uTabs.length; i++) {
406
+ if (uTabs[i].id === id) {
407
+ startIndex = i + 1
408
+ break
409
+ }
410
+ }
411
+ for (let i = uTabs.length - 1; i >= startIndex; i--) {
412
+ if (uTabs[i].closable) {
413
+ for (const item of uTabs[i].pages.list()) {
414
+ removeComponent(item.id)
415
+ markDeletableCache(item.id)
416
+ }
417
+ unref(tabs)[i].pages.clear()
418
+ unref(tabs).splice(i, 1)
419
+ }
420
+ }
421
+ for (const stay of uTabs) {
422
+ if (stay.active) {
423
+ removeDeletableCache()
424
+ return
425
+ }
426
+ }
427
+ emitter.emit(MittType.TAB_ACTIVE, { id: uTabs[uTabs.length - 1].id })
428
+ // active(uTabs[uTabs.length - 1].id)
429
+ }
430
+
431
+ /**
432
+ * 刷新
433
+ */
434
+ const refreshTab = (id: string) => {
435
+ // const uTabs = unref(tabs)
436
+ for (const uTabs of unref(tabs)) {
437
+ if (uTabs.id === id) {
438
+ const currentPageId = uTabs.pages.peek()?.id
439
+ for (let i = caches.value.length - 1; i >= 0; i--) {
440
+ const cacheId = caches.value[i]
441
+ if (cacheId === currentPageId) {
442
+ caches.value.splice(i, 1)
443
+ // 如果要刷新的是显示的TAB
444
+ if (uTabs.active) {
445
+ pageShown.value = false
446
+ }
447
+ break
448
+ }
449
+ }
450
+ break
451
+ }
452
+ }
453
+ }
454
+ /**
455
+ * 全部刷新
456
+ */
457
+ const refreshAllTabs = () => {
458
+ const refreshPage = new Set<string>()
459
+ tabs.value.forEach((value) => {
460
+ refreshPage.add(value.pages.peek()!.id)
461
+ })
462
+ for (let i = caches.value.length - 1; i >= 0; i--) {
463
+ const cacheId = caches.value[i]
464
+ if (refreshPage.has(cacheId)) {
465
+ caches.value.splice(i, 1)
466
+ }
467
+ }
468
+ pageShown.value = false
469
+ }
470
+
471
+ const addComponent = (id: string, comp: DefineComponent) => {
472
+ components.set(id, comp)
473
+ }
474
+ const getComponent = (id: string) => {
475
+ return components.get(id)
476
+ }
477
+ const removeComponent = (id: string) => {
478
+ components.delete(id)
479
+ }
480
+
481
+ /**
482
+ * Delete the cache after new Page loaded.
483
+ * @param cacheName
484
+ */
485
+ const markDeletableCache = (cacheName: string) => {
486
+ deletableCache.add(cacheName)
487
+ }
488
+ const removeDeletableCache = () => {
489
+ for (let i = caches.value.length - 1; i >= 0; i--) {
490
+ if (deletableCache.has(caches.value[i])) {
491
+ unref(caches).splice(i, 1)
492
+ }
493
+ }
494
+ deletableCache.clear()
495
+ }
496
+ const removeCache = (cacheName: string) => {
497
+ for (let i = caches.value.length - 1; i >= 0; i--) {
498
+ if (cacheName === caches.value[i]) {
499
+ unref(caches).splice(i, 1)
500
+ }
501
+ }
502
+ }
503
+ const addCache = (cacheName: string) => {
504
+ const cacheSet = new Set(caches.value)
505
+ if (!cacheSet.has(cacheName)) {
506
+ caches.value.push(cacheName)
507
+ }
508
+ }
509
+
510
+ /**
511
+ * active the tab,
512
+ * @param id
513
+ * @param route
514
+ * @return true if already actived
515
+ */
516
+ const active = (id: string, route = true) => {
517
+ return new Promise((resolve) => {
518
+ for (let i = tabs.value.length - 1; i >= 0; i--) {
519
+ const tab = tabs.value[i] as ITabItem
520
+ if (tab.id === id) {
521
+ if (tab.active) {
522
+ break
523
+ } else {
524
+ tab.active = true
525
+ pageShown.value = false
526
+ updateSession(tab)
527
+ if (route) {
528
+ const top = tab.pages.peek()
529
+ router.push({
530
+ path: top!.path,
531
+ query: top!.query
532
+ })
533
+ }
534
+ }
535
+ } else {
536
+ tabs.value[i].active = false
537
+ }
538
+ }
539
+ resolve(true)
540
+ })
541
+ }
542
+ /**
543
+ * save current tab info into Browser's session
544
+ * @param id
545
+ */
546
+ const updateSession = (tab: ITabItem) => {
547
+ // window.sessionStorage.setItem('tabItems', JSON.stringify(currentItems?.values()))
548
+ window.sessionStorage.setItem(SESSION_TAB_NAME, JSON.stringify(tab))
549
+ }
550
+ const reset = () => {
551
+ destroy()
552
+ unref(tabs).push(...defaultTabs)
553
+ emitter.emit(MittType.TAB_ACTIVE, { id: defaultTabs[0].id! })
554
+ // active(defaultTabs[0].id)
555
+ }
556
+ const destroy = () => {
557
+ unref(tabs).splice(0)
558
+ components.clear()
559
+ deletableCache.clear()
560
+ unref(caches).splice(0)
561
+ }
562
+ const setMaxSize = (size: number) => {
563
+ max = size
564
+ }
565
+ const setGlobalScroll = (globalScroll: boolean) => {
566
+ scrollbar = globalScroll
567
+ }
568
+
569
+ return {
570
+ tabs,
571
+ caches,
572
+ pageShown,
573
+ initialed,
574
+ setMaxSize,
575
+ canAddTab,
576
+ initial,
577
+ size,
578
+ active,
579
+ destroy,
580
+ reset,
581
+ addCache,
582
+ removeCache,
583
+ getTab,
584
+ addTab,
585
+ removeTab,
586
+ removeAllTabs,
587
+ removeLeftTabs,
588
+ removeRightTabs,
589
+ removeOtherTabs,
590
+ hasTab,
591
+ addPage,
592
+ addComponent,
593
+ getComponent,
594
+ removeComponent,
595
+ markDeletableCache,
596
+ removeDeletableCache,
597
+ refreshTab,
598
+ refreshAllTabs,
599
+ addPageScroller,
600
+ setGlobalScroll
601
+ }
602
+ }