vue-stack-tabs 0.1.9 → 0.1.11

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