vue-stack-tabs 0.1.16 → 0.1.18

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