vue-stack-tabs 0.1.0
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.
- package/LICENSE +201 -0
- package/README.md +3 -0
- package/dist/favicon.svg +10 -0
- package/dist/style.css +1 -0
- package/dist/vue-stack-tabs.es.js +6386 -0
- package/dist/vue-stack-tabs.umd.js +46 -0
- package/package.json +92 -0
- package/src/lib/StackTabs.vue +136 -0
- package/src/lib/assets/image/close.svg +6 -0
- package/src/lib/assets/image/close_all.svg +9 -0
- package/src/lib/assets/image/close_fill.svg +1 -0
- package/src/lib/assets/image/close_lefts.svg +1 -0
- package/src/lib/assets/image/close_other.svg +1 -0
- package/src/lib/assets/image/close_rights.svg +1 -0
- package/src/lib/assets/image/disable.svg +11 -0
- package/src/lib/assets/image/fullscreen.svg +1 -0
- package/src/lib/assets/image/left_arrow.svg +1 -0
- package/src/lib/assets/image/loading.svg +15 -0
- package/src/lib/assets/image/mask.svg +7 -0
- package/src/lib/assets/image/move.svg +6 -0
- package/src/lib/assets/image/reload.svg +3 -0
- package/src/lib/assets/image/reload_all.svg +6 -0
- package/src/lib/assets/image/reload_lefts.svg +15 -0
- package/src/lib/assets/image/reload_rights.svg +15 -0
- package/src/lib/assets/image/restore.svg +1 -0
- package/src/lib/assets/image/right_arrow.svg +1 -0
- package/src/lib/assets/image/scroll_pointer.svg +13 -0
- package/src/lib/assets/style/common/mixins.scss +45 -0
- package/src/lib/assets/style/common/var.scss +65 -0
- package/src/lib/assets/style/icon.scss +93 -0
- package/src/lib/assets/style/index.scss +3 -0
- package/src/lib/assets/style/stackTab.scss +395 -0
- package/src/lib/assets/style/transition.scss +40 -0
- package/src/lib/banner.ts +51 -0
- package/src/lib/components/ContextMenu/ContextMenuItem.vue +15 -0
- package/src/lib/components/ContextMenu/index.vue +128 -0
- package/src/lib/components/TabHeader/TabHeaderButton.vue +35 -0
- package/src/lib/components/TabHeader/TabHeaderItem.vue +63 -0
- package/src/lib/components/TabHeader/TabHeaderScroll.vue +240 -0
- package/src/lib/components/TabHeader/index.vue +134 -0
- package/src/lib/hooks/useContextMenu.ts +50 -0
- package/src/lib/hooks/useStackTab.ts +65 -0
- package/src/lib/hooks/useTabLoading.ts +48 -0
- package/src/lib/hooks/useTabRouter.ts +95 -0
- package/src/lib/hooks/useTabpanel.tsx +489 -0
- package/src/lib/i18n/index.ts +72 -0
- package/src/lib/i18n/lang/en.ts +15 -0
- package/src/lib/i18n/lang/zh-CN.ts +15 -0
- package/src/lib/iframe.vue +7 -0
- package/src/lib/index.ts +25 -0
- package/src/lib/model/TabModel.ts +182 -0
- package/src/lib/utils/TabIdHelper.ts +76 -0
- package/src/lib/utils/TabScrollHelper.ts +104 -0
- package/src/lib/utils/UriHelper.ts +22 -0
- package/types/banner.d.ts +2 -0
- package/types/hooks/useStackTab.d.ts +8 -0
- package/types/hooks/useTabLoading.d.ts +5 -0
- package/types/hooks/useTabRouter.d.ts +5 -0
- package/types/iframe.vue.d.ts +2 -0
- package/types/index.d.ts +10 -0
- package/types/model/TabModel.d.ts +114 -0
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
import type { ITabData, ITabItem, ITabPage } from '@/lib/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 '@/lib/utils/TabIdHelper'
|
|
8
|
+
import { defu } from 'defu'
|
|
9
|
+
import { Stack } from '@/lib/model/TabModel'
|
|
10
|
+
import { uriDecode } from '@/lib/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 (!unref(tabs)[i].closable) {
|
|
198
|
+
break
|
|
199
|
+
}
|
|
200
|
+
for (const item of unref(tabs)[i].pages.list()) {
|
|
201
|
+
removeComponent(item.id)
|
|
202
|
+
markDeletableCache(item.id)
|
|
203
|
+
}
|
|
204
|
+
unref(tabs)[i].pages.clear()
|
|
205
|
+
if (tabs.value.length > 1) {
|
|
206
|
+
if (i === 0) {
|
|
207
|
+
activeTabId = unref(tabs)[1].id
|
|
208
|
+
} else {
|
|
209
|
+
activeTabId = unref(tabs)[i - 1].id
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
unref(tabs).splice(i, 1)
|
|
213
|
+
|
|
214
|
+
break
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
active(activeTabId)
|
|
218
|
+
// if remove inactive tab,then we need remove the cache manually.
|
|
219
|
+
if (activeTabId !== id) {
|
|
220
|
+
removeDeletableCache()
|
|
221
|
+
}
|
|
222
|
+
return activeTabId
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const removeAllTabs = () => {
|
|
226
|
+
const uTabs = unref(tabs)
|
|
227
|
+
for (let i = uTabs.length - 1; i >= 0; i--) {
|
|
228
|
+
if (uTabs[i].closable) {
|
|
229
|
+
for (const item of uTabs[i].pages.list()) {
|
|
230
|
+
removeComponent(item.id)
|
|
231
|
+
markDeletableCache(item.id)
|
|
232
|
+
}
|
|
233
|
+
unref(tabs)[i].pages.clear()
|
|
234
|
+
unref(tabs).splice(i, 1)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
for (const stay of uTabs) {
|
|
238
|
+
if (stay.active) {
|
|
239
|
+
removeDeletableCache()
|
|
240
|
+
return
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// 如果都是非Active状态,就把最后一个active
|
|
244
|
+
active(uTabs[uTabs.length - 1].id)
|
|
245
|
+
}
|
|
246
|
+
const removeOtherTabs = (id: string) => {
|
|
247
|
+
const uTabs = unref(tabs)
|
|
248
|
+
let activeTab
|
|
249
|
+
for (let i = uTabs.length - 1; i >= 0; i--) {
|
|
250
|
+
if (uTabs[i].id === id) {
|
|
251
|
+
activeTab = uTabs[i]
|
|
252
|
+
} else if (uTabs[i].closable && uTabs[i].id !== id) {
|
|
253
|
+
for (const item of uTabs[i].pages.list()) {
|
|
254
|
+
removeComponent(item.id)
|
|
255
|
+
markDeletableCache(item.id)
|
|
256
|
+
}
|
|
257
|
+
unref(tabs)[i].pages.clear()
|
|
258
|
+
unref(tabs).splice(i, 1)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (!activeTab!.active) {
|
|
262
|
+
active(id)
|
|
263
|
+
} else {
|
|
264
|
+
removeDeletableCache()
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* 关闭左边
|
|
269
|
+
* @param id
|
|
270
|
+
*/
|
|
271
|
+
const removeLeftTabs = (id: string) => {
|
|
272
|
+
let startIndex = -1
|
|
273
|
+
const uTabs = unref(tabs)
|
|
274
|
+
for (let i = 0; i < uTabs.length; i++) {
|
|
275
|
+
if (uTabs[i].id === id) {
|
|
276
|
+
startIndex = i - 1
|
|
277
|
+
break
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
for (let i = startIndex; i >= 0; i--) {
|
|
281
|
+
if (uTabs[i].closable) {
|
|
282
|
+
for (const item of uTabs[i].pages.list()) {
|
|
283
|
+
removeComponent(item.id)
|
|
284
|
+
markDeletableCache(item.id)
|
|
285
|
+
}
|
|
286
|
+
unref(tabs)[i].pages.clear()
|
|
287
|
+
unref(tabs).splice(i, 1)
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
for (const stay of uTabs) {
|
|
291
|
+
if (stay.active) {
|
|
292
|
+
removeDeletableCache()
|
|
293
|
+
return
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// 如果都是非Active状态,就把最后一个active
|
|
297
|
+
active(uTabs[0].id)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* 关闭右边
|
|
302
|
+
* @param id
|
|
303
|
+
*/
|
|
304
|
+
const removeRightTabs = (id: string) => {
|
|
305
|
+
const uTabs = unref(tabs)
|
|
306
|
+
let startIndex = uTabs.length
|
|
307
|
+
for (let i = 0; i < uTabs.length; i++) {
|
|
308
|
+
if (uTabs[i].id === id) {
|
|
309
|
+
startIndex = i + 1
|
|
310
|
+
break
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
for (let i = uTabs.length - 1; i >= startIndex; i--) {
|
|
314
|
+
if (uTabs[i].closable) {
|
|
315
|
+
for (const item of uTabs[i].pages.list()) {
|
|
316
|
+
removeComponent(item.id)
|
|
317
|
+
markDeletableCache(item.id)
|
|
318
|
+
}
|
|
319
|
+
unref(tabs)[i].pages.clear()
|
|
320
|
+
unref(tabs).splice(i, 1)
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
for (const stay of uTabs) {
|
|
324
|
+
if (stay.active) {
|
|
325
|
+
removeDeletableCache()
|
|
326
|
+
return
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
active(uTabs[uTabs.length - 1].id)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* 刷新
|
|
334
|
+
*/
|
|
335
|
+
const refreshTab = (id: string) => {
|
|
336
|
+
// const uTabs = unref(tabs)
|
|
337
|
+
for (const uTabs of unref(tabs)) {
|
|
338
|
+
if (uTabs.id === id) {
|
|
339
|
+
const currentPageId = uTabs.pages.peek()?.id
|
|
340
|
+
for (let i = caches.value.length - 1; i >= 0; i--) {
|
|
341
|
+
const cacheId = caches.value[i]
|
|
342
|
+
if (cacheId === currentPageId) {
|
|
343
|
+
caches.value.splice(i, 1)
|
|
344
|
+
// 如果要刷新的是显示的TAB
|
|
345
|
+
if (uTabs.active) {
|
|
346
|
+
pageShown.value = false
|
|
347
|
+
}
|
|
348
|
+
break
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
break
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* 全部刷新
|
|
357
|
+
*/
|
|
358
|
+
const refreshAllTabs = () => {
|
|
359
|
+
const refreshPage = new Set<string>()
|
|
360
|
+
tabs.value.forEach((value) => {
|
|
361
|
+
refreshPage.add(value.pages.peek()!.id)
|
|
362
|
+
})
|
|
363
|
+
for (let i = caches.value.length - 1; i >= 0; i--) {
|
|
364
|
+
const cacheId = caches.value[i]
|
|
365
|
+
if (refreshPage.has(cacheId)) {
|
|
366
|
+
caches.value.splice(i, 1)
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
pageShown.value = false
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const addComponent = (id: string, comp: DefineComponent) => {
|
|
373
|
+
components.set(id, comp)
|
|
374
|
+
}
|
|
375
|
+
const getComponent = (id: string) => {
|
|
376
|
+
return components.get(id)
|
|
377
|
+
}
|
|
378
|
+
const removeComponent = (id: string) => {
|
|
379
|
+
components.delete(id)
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Delete the cache after new Page loaded.
|
|
384
|
+
* @param cacheName
|
|
385
|
+
*/
|
|
386
|
+
const markDeletableCache = (cacheName: string) => {
|
|
387
|
+
deletableCache.add(cacheName)
|
|
388
|
+
}
|
|
389
|
+
const removeDeletableCache = () => {
|
|
390
|
+
for (let i = caches.value.length - 1; i >= 0; i--) {
|
|
391
|
+
if (deletableCache.has(caches.value[i])) {
|
|
392
|
+
unref(caches).splice(i, 1)
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
deletableCache.clear()
|
|
396
|
+
}
|
|
397
|
+
const removeCache = (cacheName: string) => {
|
|
398
|
+
for (let i = caches.value.length - 1; i >= 0; i--) {
|
|
399
|
+
if (cacheName === caches.value[i]) {
|
|
400
|
+
unref(caches).splice(i, 1)
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
const addCache = (cacheName: string) => {
|
|
405
|
+
const cacheSet = new Set(caches.value)
|
|
406
|
+
if (!cacheSet.has(cacheName)) {
|
|
407
|
+
caches.value.push(cacheName)
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* active the tab,
|
|
413
|
+
* @param id
|
|
414
|
+
* @param route
|
|
415
|
+
* @return true if already actived
|
|
416
|
+
*/
|
|
417
|
+
const active = (id: string, route = true) => {
|
|
418
|
+
for (let i = tabs.value.length - 1; i >= 0; i--) {
|
|
419
|
+
const tab = tabs.value[i] as ITabItem
|
|
420
|
+
if (tab.id === id) {
|
|
421
|
+
if (tab.active) {
|
|
422
|
+
break
|
|
423
|
+
} else {
|
|
424
|
+
tab.active = true
|
|
425
|
+
pageShown.value = false
|
|
426
|
+
updateSession(tab)
|
|
427
|
+
if (route) {
|
|
428
|
+
const top = tab.pages.peek()
|
|
429
|
+
router.push({
|
|
430
|
+
path: top!.path,
|
|
431
|
+
query: top!.query
|
|
432
|
+
})
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
} else {
|
|
436
|
+
tabs.value[i].active = false
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* save current tab info into Browser's session
|
|
442
|
+
* @param id
|
|
443
|
+
*/
|
|
444
|
+
const updateSession = (tab: ITabItem) => {
|
|
445
|
+
// window.sessionStorage.setItem('tabItems', JSON.stringify(currentItems?.values()))
|
|
446
|
+
window.sessionStorage.setItem(SESSION_TAB_NAME, JSON.stringify(tab))
|
|
447
|
+
}
|
|
448
|
+
const reset = () => {
|
|
449
|
+
destroy()
|
|
450
|
+
unref(tabs).push(...defaultTabs)
|
|
451
|
+
active(defaultTabs[0].id)
|
|
452
|
+
}
|
|
453
|
+
const destroy = () => {
|
|
454
|
+
unref(tabs).splice(0)
|
|
455
|
+
components.clear()
|
|
456
|
+
deletableCache.clear()
|
|
457
|
+
unref(caches).splice(0)
|
|
458
|
+
pageShown.value = true
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return {
|
|
462
|
+
tabs,
|
|
463
|
+
caches,
|
|
464
|
+
pageShown,
|
|
465
|
+
initial,
|
|
466
|
+
size,
|
|
467
|
+
active,
|
|
468
|
+
destroy,
|
|
469
|
+
reset,
|
|
470
|
+
addCache,
|
|
471
|
+
removeCache,
|
|
472
|
+
getTab,
|
|
473
|
+
addTab,
|
|
474
|
+
removeTab,
|
|
475
|
+
removeAllTabs,
|
|
476
|
+
removeLeftTabs,
|
|
477
|
+
removeRightTabs,
|
|
478
|
+
removeOtherTabs,
|
|
479
|
+
hasTab,
|
|
480
|
+
addPage,
|
|
481
|
+
addComponent,
|
|
482
|
+
getComponent,
|
|
483
|
+
removeComponent,
|
|
484
|
+
markDeletableCache,
|
|
485
|
+
removeDeletableCache,
|
|
486
|
+
refreshTab,
|
|
487
|
+
refreshAllTabs
|
|
488
|
+
}
|
|
489
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { useI18n } from 'vue-i18n'
|
|
2
|
+
import { inject } from 'vue'
|
|
3
|
+
export default () => {
|
|
4
|
+
// 引入lang目录下文件
|
|
5
|
+
// 此处使用了 VITE 的 import.meta.globEager。非 VITE 的 可以使用 require.context
|
|
6
|
+
const modules = import.meta.glob('./lang/*', { eager: true })
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 获取所有语言文件
|
|
10
|
+
* @param {Object} mList
|
|
11
|
+
*/
|
|
12
|
+
function getLangFiles(mList: any, msg: any) {
|
|
13
|
+
for (const path in mList) {
|
|
14
|
+
if (mList[path].default) {
|
|
15
|
+
// 获取文件名
|
|
16
|
+
const pathName = path.substring(path.lastIndexOf('/') + 1, path.lastIndexOf('.'))
|
|
17
|
+
|
|
18
|
+
if (msg[pathName]) {
|
|
19
|
+
msg[pathName] = {
|
|
20
|
+
...mList[pathName],
|
|
21
|
+
...mList[path].default
|
|
22
|
+
}
|
|
23
|
+
} else {
|
|
24
|
+
msg[pathName] = mList[path].default
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 注册i18n实例并引入语言文件
|
|
32
|
+
*/
|
|
33
|
+
// const i18n = createI18n({
|
|
34
|
+
// fallbackLocale: 'zh-CN',
|
|
35
|
+
// // 定义默认语言为中文
|
|
36
|
+
// locale: 'zh-CN',
|
|
37
|
+
// legacy: false,
|
|
38
|
+
// // 挂载到全局,不然会报错
|
|
39
|
+
// // globalInjection: true,
|
|
40
|
+
// messages: getAllLang(),
|
|
41
|
+
// })
|
|
42
|
+
/**
|
|
43
|
+
* 局部使用i18n
|
|
44
|
+
*/
|
|
45
|
+
// const i18n = useI18n({
|
|
46
|
+
// useScope: 'local',
|
|
47
|
+
// locale: 'zh-CN',
|
|
48
|
+
// messages: getAllLang(),
|
|
49
|
+
// })
|
|
50
|
+
|
|
51
|
+
// 假设你还有其他目录下的语言文件 它的路径是 src/views/home/locales/en-US.ts
|
|
52
|
+
// 那么你就可以 使用 :lower:(小写) :upper:(大写) 来引入文件
|
|
53
|
+
// const viewModules = import.meta.globEager('../views/**/locales/[[:lower:]][[:lower:]]-[[:upper:]][[:upper:]].ts')
|
|
54
|
+
|
|
55
|
+
const allLangs: any = () => {
|
|
56
|
+
const message: any = {}
|
|
57
|
+
getLangFiles(modules, message)
|
|
58
|
+
return message
|
|
59
|
+
}
|
|
60
|
+
const localeI18n = inject('locales') as { locale: string; messages: object }
|
|
61
|
+
const getI18n = () => {
|
|
62
|
+
const combinateMessage = { ...allLangs(), ...localeI18n.messages }
|
|
63
|
+
return useI18n({
|
|
64
|
+
useScope: 'local',
|
|
65
|
+
locale: localeI18n.locale,
|
|
66
|
+
messages: combinateMessage
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
getI18n
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
VueStackTab: {
|
|
3
|
+
close: 'close',
|
|
4
|
+
closeLefts: 'close lefts',
|
|
5
|
+
closeRights: 'close rights',
|
|
6
|
+
closeOthers: 'close others',
|
|
7
|
+
closeAll: 'close all',
|
|
8
|
+
reload: 'reload',
|
|
9
|
+
reloadAll: 'reload all',
|
|
10
|
+
maximum: 'maximum',
|
|
11
|
+
restore: 'restore',
|
|
12
|
+
undefined: 'undefined',
|
|
13
|
+
loading: 'loading'
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
VueStackTab: {
|
|
3
|
+
close: '关闭',
|
|
4
|
+
closeLefts: '关闭左侧',
|
|
5
|
+
closeRights: '关闭右侧',
|
|
6
|
+
closeOthers: '关闭其他',
|
|
7
|
+
closeAll: '关闭所有',
|
|
8
|
+
reload: '刷新',
|
|
9
|
+
reloadAll: '刷新所有',
|
|
10
|
+
maximum: '最大化',
|
|
11
|
+
restore: '还原',
|
|
12
|
+
undefined: '未命名',
|
|
13
|
+
loading: '加载中'
|
|
14
|
+
}
|
|
15
|
+
}
|
package/src/lib/index.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { App } from 'vue'
|
|
2
|
+
import { createI18n } from 'vue-i18n'
|
|
3
|
+
import StackTab from './StackTabs.vue'
|
|
4
|
+
// import VueEmttier from './hooks/useMitt'
|
|
5
|
+
import tabVersion from './banner'
|
|
6
|
+
// import './assets/style/index.scss'
|
|
7
|
+
// import 'vue-loading-overlay/dist/css/index.css'
|
|
8
|
+
import IFrame from './iframe.vue'
|
|
9
|
+
import useTabLoading from './hooks/useTabLoading'
|
|
10
|
+
import useTabRouter from './hooks/useTabRouter'
|
|
11
|
+
import useStackTab from './hooks/useStackTab'
|
|
12
|
+
|
|
13
|
+
const i18n = createI18n({
|
|
14
|
+
legacy: false,
|
|
15
|
+
locale: 'zh-CN',
|
|
16
|
+
fallbackLocale: 'en-US'
|
|
17
|
+
})
|
|
18
|
+
export { IFrame, useTabLoading, useTabRouter, useStackTab }
|
|
19
|
+
export * from './model/TabModel'
|
|
20
|
+
export default {
|
|
21
|
+
install(app: App): void {
|
|
22
|
+
tabVersion(import.meta.env.PACKAGE_VERSION)
|
|
23
|
+
app.component('VueStackTabs', StackTab).use(i18n)
|
|
24
|
+
}
|
|
25
|
+
}
|