vue-stack-tabs 0.1.19 → 0.1.21

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