vue-stack-tabs 0.1.18 → 0.1.20
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/README.md +2 -2
- package/dist/favicon.svg +9 -9
- package/dist/src/lib/StackTabs.vue.d.ts +4 -4
- package/dist/src/lib/components/TabHeader/TabHeaderItem.vue.d.ts +0 -1
- package/dist/src/lib/hooks/useTabpanel.d.ts +1 -0
- package/dist/src/lib/i18n/index.d.ts +1 -1
- package/dist/src/lib/index.d.ts +4 -1
- package/dist/style.css +1 -1
- package/dist/vue-stack-tabs.es.js +1374 -5060
- package/dist/vue-stack-tabs.umd.js +6 -21
- package/package.json +3 -2
- package/src/lib/StackTabs.vue +124 -124
- package/src/lib/assets/style/overlayer.scss +46 -46
- package/src/lib/banner.ts +26 -26
- package/src/lib/components/ContextMenu/index.vue +2 -2
- package/src/lib/components/TabHeader/TabHeaderItem.vue +4 -4
- package/src/lib/components/TabHeader/index.vue +2 -2
- package/src/lib/hooks/useStackTab.ts +50 -23
- package/src/lib/hooks/useTabRouter.ts +93 -93
- package/src/lib/hooks/useTabpanel.tsx +3 -1
- package/src/lib/i18n/index.ts +17 -7
- package/src/lib/index.ts +37 -33
- package/src/lib/model/TabModel.ts +178 -178
|
@@ -1,93 +1,93 @@
|
|
|
1
|
-
import { defu } from 'defu'
|
|
2
|
-
|
|
3
|
-
import { getCurrentInstance } from 'vue'
|
|
4
|
-
import type { ComponentInternalInstance } from 'vue'
|
|
5
|
-
import { type RouteLocationPathRaw, useRoute, useRouter } from 'vue-router'
|
|
6
|
-
|
|
7
|
-
import useTabpanel from '@/lib/hooks/useTabpanel'
|
|
8
|
-
import { uriDecode } from '@/lib/utils/UriHelper'
|
|
9
|
-
export default () => {
|
|
10
|
-
const { attrs } = getCurrentInstance() as ComponentInternalInstance
|
|
11
|
-
const { pageShown, getTab, markDeletableCache, removeComponent, getComponent } = useTabpanel()
|
|
12
|
-
const route = useRoute()
|
|
13
|
-
const router = useRouter()
|
|
14
|
-
let currentId = ''
|
|
15
|
-
// const currentPageId = ''
|
|
16
|
-
let currentTab = ''
|
|
17
|
-
|
|
18
|
-
currentId = attrs.tId as string
|
|
19
|
-
// currentPageId = attrs.pId as string
|
|
20
|
-
currentTab = route.query.__tab as string
|
|
21
|
-
const forward = (to: RouteLocationPathRaw) => {
|
|
22
|
-
pageShown.value = false
|
|
23
|
-
const query = defu(
|
|
24
|
-
{
|
|
25
|
-
__tab: currentTab
|
|
26
|
-
},
|
|
27
|
-
to.query
|
|
28
|
-
)
|
|
29
|
-
router.push({
|
|
30
|
-
path: to.path,
|
|
31
|
-
query
|
|
32
|
-
})
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* 返回
|
|
36
|
-
* @param to url or step
|
|
37
|
-
* @param query 返回时要加截的数据
|
|
38
|
-
*/
|
|
39
|
-
const backward = (to: number | string, query?: Object) => {
|
|
40
|
-
pageShown.value = false
|
|
41
|
-
|
|
42
|
-
const tab = getTab(currentId)
|
|
43
|
-
if (!tab) {
|
|
44
|
-
return
|
|
45
|
-
}
|
|
46
|
-
const tabStack = tab.pages
|
|
47
|
-
let absTo = 0
|
|
48
|
-
if (typeof to === 'number') {
|
|
49
|
-
absTo = Math.abs(to)
|
|
50
|
-
} else {
|
|
51
|
-
let index = 1
|
|
52
|
-
let hasPath = false
|
|
53
|
-
const pageItems = tabStack.list()
|
|
54
|
-
const url = uriDecode(to)
|
|
55
|
-
query = defu(url.query, query)
|
|
56
|
-
for (let i = tabStack.size() - 2; i >= 0; i--) {
|
|
57
|
-
if (pageItems[i].path === url.path) {
|
|
58
|
-
hasPath = true
|
|
59
|
-
break
|
|
60
|
-
}
|
|
61
|
-
index += 1
|
|
62
|
-
}
|
|
63
|
-
if (hasPath) {
|
|
64
|
-
absTo = index
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (absTo === 0 || tabStack!.size() <= absTo) {
|
|
69
|
-
return
|
|
70
|
-
}
|
|
71
|
-
// const removeCaches = new Set()
|
|
72
|
-
for (let i = 0; i < absTo; i++) {
|
|
73
|
-
const popRoute = tabStack.pop()
|
|
74
|
-
removeComponent(popRoute!.id)
|
|
75
|
-
markDeletableCache(popRoute!.id)
|
|
76
|
-
}
|
|
77
|
-
const pageRoute = tabStack?.peek()
|
|
78
|
-
const component = getComponent(pageRoute!.id)
|
|
79
|
-
if (component !== null && component !== undefined) {
|
|
80
|
-
const sourceComponent = component.components.DynamicComponent
|
|
81
|
-
sourceComponent.props = defu(query, sourceComponent.props)
|
|
82
|
-
}
|
|
83
|
-
router.push({
|
|
84
|
-
path: pageRoute!.path,
|
|
85
|
-
query: pageRoute!.query
|
|
86
|
-
})
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
forward,
|
|
91
|
-
backward
|
|
92
|
-
}
|
|
93
|
-
}
|
|
1
|
+
import { defu } from 'defu'
|
|
2
|
+
|
|
3
|
+
import { getCurrentInstance } from 'vue'
|
|
4
|
+
import type { ComponentInternalInstance } from 'vue'
|
|
5
|
+
import { type RouteLocationPathRaw, useRoute, useRouter } from 'vue-router'
|
|
6
|
+
|
|
7
|
+
import useTabpanel from '@/lib/hooks/useTabpanel'
|
|
8
|
+
import { uriDecode } from '@/lib/utils/UriHelper'
|
|
9
|
+
export default () => {
|
|
10
|
+
const { attrs } = getCurrentInstance() as ComponentInternalInstance
|
|
11
|
+
const { pageShown, getTab, markDeletableCache, removeComponent, getComponent } = useTabpanel()
|
|
12
|
+
const route = useRoute()
|
|
13
|
+
const router = useRouter()
|
|
14
|
+
let currentId = ''
|
|
15
|
+
// const currentPageId = ''
|
|
16
|
+
let currentTab = ''
|
|
17
|
+
|
|
18
|
+
currentId = attrs.tId as string
|
|
19
|
+
// currentPageId = attrs.pId as string
|
|
20
|
+
currentTab = route.query.__tab as string
|
|
21
|
+
const forward = (to: RouteLocationPathRaw) => {
|
|
22
|
+
pageShown.value = false
|
|
23
|
+
const query = defu(
|
|
24
|
+
{
|
|
25
|
+
__tab: currentTab
|
|
26
|
+
},
|
|
27
|
+
to.query
|
|
28
|
+
)
|
|
29
|
+
router.push({
|
|
30
|
+
path: to.path,
|
|
31
|
+
query
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 返回
|
|
36
|
+
* @param to url or step
|
|
37
|
+
* @param query 返回时要加截的数据
|
|
38
|
+
*/
|
|
39
|
+
const backward = (to: number | string, query?: Object) => {
|
|
40
|
+
pageShown.value = false
|
|
41
|
+
|
|
42
|
+
const tab = getTab(currentId)
|
|
43
|
+
if (!tab) {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
const tabStack = tab.pages
|
|
47
|
+
let absTo = 0
|
|
48
|
+
if (typeof to === 'number') {
|
|
49
|
+
absTo = Math.abs(to)
|
|
50
|
+
} else {
|
|
51
|
+
let index = 1
|
|
52
|
+
let hasPath = false
|
|
53
|
+
const pageItems = tabStack.list()
|
|
54
|
+
const url = uriDecode(to)
|
|
55
|
+
query = defu(url.query, query)
|
|
56
|
+
for (let i = tabStack.size() - 2; i >= 0; i--) {
|
|
57
|
+
if (pageItems[i].path === url.path) {
|
|
58
|
+
hasPath = true
|
|
59
|
+
break
|
|
60
|
+
}
|
|
61
|
+
index += 1
|
|
62
|
+
}
|
|
63
|
+
if (hasPath) {
|
|
64
|
+
absTo = index
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (absTo === 0 || tabStack!.size() <= absTo) {
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
// const removeCaches = new Set()
|
|
72
|
+
for (let i = 0; i < absTo; i++) {
|
|
73
|
+
const popRoute = tabStack.pop()
|
|
74
|
+
removeComponent(popRoute!.id)
|
|
75
|
+
markDeletableCache(popRoute!.id)
|
|
76
|
+
}
|
|
77
|
+
const pageRoute = tabStack?.peek()
|
|
78
|
+
const component = getComponent(pageRoute!.id)
|
|
79
|
+
if (component !== null && component !== undefined) {
|
|
80
|
+
const sourceComponent = component.components.DynamicComponent
|
|
81
|
+
sourceComponent.props = defu(query, sourceComponent.props)
|
|
82
|
+
}
|
|
83
|
+
router.push({
|
|
84
|
+
path: pageRoute!.path,
|
|
85
|
+
query: pageRoute!.query
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
forward,
|
|
91
|
+
backward
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -19,7 +19,7 @@ const components = new Map<string, any>()
|
|
|
19
19
|
const deletableCache = new Set<String>()
|
|
20
20
|
const pageShown = ref<boolean>(true)
|
|
21
21
|
const SESSION_TAB_NAME = 'stacktab-active-tab'
|
|
22
|
-
|
|
22
|
+
let initialed = false
|
|
23
23
|
let max = 0
|
|
24
24
|
export default () => {
|
|
25
25
|
const router = useRouter()
|
|
@@ -90,6 +90,7 @@ export default () => {
|
|
|
90
90
|
tabs.value.push(temp)
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
+
initialed = true
|
|
93
94
|
}
|
|
94
95
|
const hasTab = (id: string) => {
|
|
95
96
|
for (const tab of tabs.value) {
|
|
@@ -496,6 +497,7 @@ export default () => {
|
|
|
496
497
|
tabs,
|
|
497
498
|
caches,
|
|
498
499
|
pageShown,
|
|
500
|
+
initialed,
|
|
499
501
|
setMaxSize,
|
|
500
502
|
canAddTab,
|
|
501
503
|
initial,
|
package/src/lib/i18n/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { createI18n } from 'vue-i18n-lite'
|
|
2
|
+
|
|
3
3
|
export default () => {
|
|
4
4
|
// 引入lang目录下文件
|
|
5
5
|
// 此处使用了 VITE 的 import.meta.globEager。非 VITE 的 可以使用 require.context
|
|
@@ -57,14 +57,24 @@ export default () => {
|
|
|
57
57
|
getLangFiles(modules, message)
|
|
58
58
|
return message
|
|
59
59
|
}
|
|
60
|
-
const localeI18n = inject('locales') as { locale: string; messages: object }
|
|
61
|
-
const getI18n = () => {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
// const localeI18n = inject('locales') as { locale: string; messages: object }
|
|
61
|
+
const getI18n = (localeI18n?: any) => {
|
|
62
|
+
let combinateMessage = { ...allLangs() }
|
|
63
|
+
if (!localeI18n) {
|
|
64
|
+
combinateMessage = { ...allLangs(), ...localeI18n.messages }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return createI18n({
|
|
65
68
|
locale: localeI18n.locale,
|
|
69
|
+
fallbackLocale: 'en',
|
|
66
70
|
messages: combinateMessage
|
|
67
71
|
})
|
|
72
|
+
// const combinateMessage = { ...allLangs(), ...localeI18n.messages }
|
|
73
|
+
// return useI18n({
|
|
74
|
+
// useScope: 'local',
|
|
75
|
+
// locale: localeI18n.locale,
|
|
76
|
+
// messages: combinateMessage
|
|
77
|
+
// })
|
|
68
78
|
}
|
|
69
79
|
return {
|
|
70
80
|
getI18n
|
package/src/lib/index.ts
CHANGED
|
@@ -1,33 +1,37 @@
|
|
|
1
|
-
import type { App } from 'vue'
|
|
2
|
-
import { createI18n } from 'vue-i18n'
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import '
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Vue
|
|
32
|
-
|
|
33
|
-
|
|
1
|
+
import type { App } from 'vue'
|
|
2
|
+
// import { createI18n } from 'vue-i18n'
|
|
3
|
+
import i18n from './i18n'
|
|
4
|
+
import StackTab from './StackTabs.vue'
|
|
5
|
+
import tabVersion from './banner'
|
|
6
|
+
import IFrame from './iframe.vue'
|
|
7
|
+
import useTabLoading from './hooks/useTabLoading'
|
|
8
|
+
import useTabRouter from './hooks/useTabRouter'
|
|
9
|
+
import useStackTab from './hooks/useStackTab'
|
|
10
|
+
import TabHeaderButton from '@/lib/components/TabHeader/TabHeaderButton.vue'
|
|
11
|
+
import useTabMitt from '@/lib/hooks/useTabMitt'
|
|
12
|
+
import './assets/style/index.scss'
|
|
13
|
+
//
|
|
14
|
+
// const i18n = createI18n({
|
|
15
|
+
// legacy: false,
|
|
16
|
+
// locale: 'zh-CN',
|
|
17
|
+
// fallbackLocale: 'en-US'
|
|
18
|
+
// })
|
|
19
|
+
|
|
20
|
+
export * from './model/TabModel'
|
|
21
|
+
// Cutsom type
|
|
22
|
+
declare module '@vue/runtime-core' {
|
|
23
|
+
export interface GlobalComponents {
|
|
24
|
+
VueStackTabs: typeof StackTab
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { IFrame, useTabLoading, useTabRouter, useStackTab, TabHeaderButton, StackTab }
|
|
29
|
+
export default {
|
|
30
|
+
install(
|
|
31
|
+
Vue: App,
|
|
32
|
+
options: { locale: string; messages: any } = { locale: 'zh-CN', messages: {} }
|
|
33
|
+
): void {
|
|
34
|
+
tabVersion(import.meta.env.PACKAGE_VERSION)
|
|
35
|
+
Vue.component('VueStackTabs', StackTab).use(useTabMitt).use(i18n().getI18n(options))
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -1,178 +1,178 @@
|
|
|
1
|
-
// Tab的滚动方式
|
|
2
|
-
export enum TabScrollMode {
|
|
3
|
-
// 使用滚轮
|
|
4
|
-
WHEEL = 'wheel',
|
|
5
|
-
// 使用按钮
|
|
6
|
-
BUTTON = 'button',
|
|
7
|
-
// 两者都可以
|
|
8
|
-
BOTH = 'both'
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// export enum EventType {
|
|
12
|
-
// I18N_LOCALES = 'I18N_LOCALES',
|
|
13
|
-
// PANEL_MAXIMUM = 'PANEL_MAXIMUM',
|
|
14
|
-
// TAB_ACTIVE = 'TAB_ACTIVE',
|
|
15
|
-
// TAB_CLOSE = 'TAB_CLOSE',
|
|
16
|
-
// TAB_CLOSE_ALL = 'TAB_CLOSE_ALL',
|
|
17
|
-
// TAB_CLOSE_RIGHT = 'TAB_CLOSE_RIGHT',
|
|
18
|
-
// TAB_CLOSE_LEFT = 'TAB_CLOSE_LEFT',
|
|
19
|
-
// TAB_REFRESH = 'TAB_REFRESH',
|
|
20
|
-
// TAB_REFRESH_ALL = 'TAB_REFRESH_ALL'
|
|
21
|
-
// }
|
|
22
|
-
// 拖拽数据接口
|
|
23
|
-
export interface DragData {
|
|
24
|
-
thumbLeft: number
|
|
25
|
-
startScrollLeft: number
|
|
26
|
-
startThumbLeft: number
|
|
27
|
-
startPageX: number
|
|
28
|
-
}
|
|
29
|
-
// 滑块数据接口
|
|
30
|
-
export interface ScrollData {
|
|
31
|
-
clientWidth: number
|
|
32
|
-
scrollWidth: number
|
|
33
|
-
scrollLeft: number
|
|
34
|
-
}
|
|
35
|
-
// 滚动条事件类型
|
|
36
|
-
export type ScrollEvents = {
|
|
37
|
-
ScrollUpdate: void
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Tab data for open
|
|
42
|
-
*/
|
|
43
|
-
export interface ITabData extends ITabBase {
|
|
44
|
-
// tab name
|
|
45
|
-
title: string
|
|
46
|
-
path: string
|
|
47
|
-
query?: Record<string, string>
|
|
48
|
-
}
|
|
49
|
-
export interface ITabBase {
|
|
50
|
-
id?: string
|
|
51
|
-
title: string
|
|
52
|
-
closable?: boolean
|
|
53
|
-
refreshable?: boolean
|
|
54
|
-
iframe?: boolean
|
|
55
|
-
}
|
|
56
|
-
export interface ITabItem extends ITabBase {
|
|
57
|
-
id: string
|
|
58
|
-
closable: boolean
|
|
59
|
-
refreshable: boolean
|
|
60
|
-
iframe: boolean
|
|
61
|
-
url?: string
|
|
62
|
-
active: boolean
|
|
63
|
-
pages: Stack<ITabPage>
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface ITabPage {
|
|
67
|
-
id: string
|
|
68
|
-
tabId: string
|
|
69
|
-
path: string
|
|
70
|
-
query?: Record<string, string>
|
|
71
|
-
}
|
|
72
|
-
export interface IContextMenu {
|
|
73
|
-
icon?: string
|
|
74
|
-
title: string
|
|
75
|
-
callback(id: string): void
|
|
76
|
-
disabled: (tabData: ITabBase) => boolean
|
|
77
|
-
}
|
|
78
|
-
// export interface TabRouteLocationRaw {
|
|
79
|
-
// path: string
|
|
80
|
-
// query?: any
|
|
81
|
-
// }
|
|
82
|
-
// export interface PageRouteLocationRaw {
|
|
83
|
-
// pId: string
|
|
84
|
-
// path: string
|
|
85
|
-
// query: any
|
|
86
|
-
// }
|
|
87
|
-
export class Stack<T> {
|
|
88
|
-
// 存储的Map
|
|
89
|
-
private items: Map<number, T>
|
|
90
|
-
|
|
91
|
-
//
|
|
92
|
-
constructor(items?:T[]) {
|
|
93
|
-
this.items = new Map()
|
|
94
|
-
if(items) {
|
|
95
|
-
for (const item of items) {
|
|
96
|
-
this.push(item)
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* @description: 入栈
|
|
103
|
-
* @param {T} element 要入栈的元素
|
|
104
|
-
*/
|
|
105
|
-
push(element: T) {
|
|
106
|
-
this.items.set(this.items.size, element)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* @description: 出栈
|
|
111
|
-
* @return {T} 返回出栈的元素
|
|
112
|
-
*/
|
|
113
|
-
pop(): T | undefined {
|
|
114
|
-
if (this.isEmpty()) {
|
|
115
|
-
return undefined
|
|
116
|
-
}
|
|
117
|
-
const result = this.items.get(this.items.size - 1)
|
|
118
|
-
this.items.delete(this.items.size - 1)
|
|
119
|
-
return result
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* @description: 返回栈顶的元素
|
|
124
|
-
* @return {T}
|
|
125
|
-
*/
|
|
126
|
-
peek(): T | undefined {
|
|
127
|
-
if (this.isEmpty()) {
|
|
128
|
-
return undefined
|
|
129
|
-
}
|
|
130
|
-
return this.items.get(this.items.size - 1)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* @description: 返回栈是否为空
|
|
135
|
-
* @return {Boolean}
|
|
136
|
-
*/
|
|
137
|
-
isEmpty(): boolean {
|
|
138
|
-
return this.items.size === 0
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* @description: 返回栈里的元素个数
|
|
143
|
-
* @return {Number}
|
|
144
|
-
*/
|
|
145
|
-
size(): number {
|
|
146
|
-
return this.items.size
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* @description: 清空栈
|
|
151
|
-
*/
|
|
152
|
-
clear() {
|
|
153
|
-
this.items.clear()
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
list() {
|
|
157
|
-
return Array.from(this.items.values())
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* @description: 覆盖Object默认的toString
|
|
162
|
-
* @return {String}
|
|
163
|
-
*/
|
|
164
|
-
toString(): string {
|
|
165
|
-
if (this.isEmpty()) {
|
|
166
|
-
return ''
|
|
167
|
-
}
|
|
168
|
-
let result: string = ''
|
|
169
|
-
this.items.forEach((value, key) => {
|
|
170
|
-
result = `${result}${key === 0 ? '' : ', '}${value}`
|
|
171
|
-
})
|
|
172
|
-
return result
|
|
173
|
-
}
|
|
174
|
-
toJSON() {
|
|
175
|
-
// return Object.fromEntries(this.items)
|
|
176
|
-
return Array.from(this.items.values())
|
|
177
|
-
}
|
|
178
|
-
}
|
|
1
|
+
// Tab的滚动方式
|
|
2
|
+
export enum TabScrollMode {
|
|
3
|
+
// 使用滚轮
|
|
4
|
+
WHEEL = 'wheel',
|
|
5
|
+
// 使用按钮
|
|
6
|
+
BUTTON = 'button',
|
|
7
|
+
// 两者都可以
|
|
8
|
+
BOTH = 'both'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// export enum EventType {
|
|
12
|
+
// I18N_LOCALES = 'I18N_LOCALES',
|
|
13
|
+
// PANEL_MAXIMUM = 'PANEL_MAXIMUM',
|
|
14
|
+
// TAB_ACTIVE = 'TAB_ACTIVE',
|
|
15
|
+
// TAB_CLOSE = 'TAB_CLOSE',
|
|
16
|
+
// TAB_CLOSE_ALL = 'TAB_CLOSE_ALL',
|
|
17
|
+
// TAB_CLOSE_RIGHT = 'TAB_CLOSE_RIGHT',
|
|
18
|
+
// TAB_CLOSE_LEFT = 'TAB_CLOSE_LEFT',
|
|
19
|
+
// TAB_REFRESH = 'TAB_REFRESH',
|
|
20
|
+
// TAB_REFRESH_ALL = 'TAB_REFRESH_ALL'
|
|
21
|
+
// }
|
|
22
|
+
// 拖拽数据接口
|
|
23
|
+
export interface DragData {
|
|
24
|
+
thumbLeft: number
|
|
25
|
+
startScrollLeft: number
|
|
26
|
+
startThumbLeft: number
|
|
27
|
+
startPageX: number
|
|
28
|
+
}
|
|
29
|
+
// 滑块数据接口
|
|
30
|
+
export interface ScrollData {
|
|
31
|
+
clientWidth: number
|
|
32
|
+
scrollWidth: number
|
|
33
|
+
scrollLeft: number
|
|
34
|
+
}
|
|
35
|
+
// 滚动条事件类型
|
|
36
|
+
export type ScrollEvents = {
|
|
37
|
+
ScrollUpdate: void
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Tab data for open
|
|
42
|
+
*/
|
|
43
|
+
export interface ITabData extends ITabBase {
|
|
44
|
+
// tab name
|
|
45
|
+
title: string
|
|
46
|
+
path: string
|
|
47
|
+
query?: Record<string, string>
|
|
48
|
+
}
|
|
49
|
+
export interface ITabBase {
|
|
50
|
+
id?: string
|
|
51
|
+
title: string
|
|
52
|
+
closable?: boolean
|
|
53
|
+
refreshable?: boolean
|
|
54
|
+
iframe?: boolean
|
|
55
|
+
}
|
|
56
|
+
export interface ITabItem extends ITabBase {
|
|
57
|
+
id: string
|
|
58
|
+
closable: boolean
|
|
59
|
+
refreshable: boolean
|
|
60
|
+
iframe: boolean
|
|
61
|
+
url?: string
|
|
62
|
+
active: boolean
|
|
63
|
+
pages: Stack<ITabPage>
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface ITabPage {
|
|
67
|
+
id: string
|
|
68
|
+
tabId: string
|
|
69
|
+
path: string
|
|
70
|
+
query?: Record<string, string>
|
|
71
|
+
}
|
|
72
|
+
export interface IContextMenu {
|
|
73
|
+
icon?: string
|
|
74
|
+
title: string
|
|
75
|
+
callback(id: string): void
|
|
76
|
+
disabled: (tabData: ITabBase) => boolean
|
|
77
|
+
}
|
|
78
|
+
// export interface TabRouteLocationRaw {
|
|
79
|
+
// path: string
|
|
80
|
+
// query?: any
|
|
81
|
+
// }
|
|
82
|
+
// export interface PageRouteLocationRaw {
|
|
83
|
+
// pId: string
|
|
84
|
+
// path: string
|
|
85
|
+
// query: any
|
|
86
|
+
// }
|
|
87
|
+
export class Stack<T> {
|
|
88
|
+
// 存储的Map
|
|
89
|
+
private items: Map<number, T>
|
|
90
|
+
|
|
91
|
+
//
|
|
92
|
+
constructor(items?: T[]) {
|
|
93
|
+
this.items = new Map()
|
|
94
|
+
if (items) {
|
|
95
|
+
for (const item of items) {
|
|
96
|
+
this.push(item)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @description: 入栈
|
|
103
|
+
* @param {T} element 要入栈的元素
|
|
104
|
+
*/
|
|
105
|
+
push(element: T) {
|
|
106
|
+
this.items.set(this.items.size, element)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @description: 出栈
|
|
111
|
+
* @return {T} 返回出栈的元素
|
|
112
|
+
*/
|
|
113
|
+
pop(): T | undefined {
|
|
114
|
+
if (this.isEmpty()) {
|
|
115
|
+
return undefined
|
|
116
|
+
}
|
|
117
|
+
const result = this.items.get(this.items.size - 1)
|
|
118
|
+
this.items.delete(this.items.size - 1)
|
|
119
|
+
return result
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @description: 返回栈顶的元素
|
|
124
|
+
* @return {T}
|
|
125
|
+
*/
|
|
126
|
+
peek(): T | undefined {
|
|
127
|
+
if (this.isEmpty()) {
|
|
128
|
+
return undefined
|
|
129
|
+
}
|
|
130
|
+
return this.items.get(this.items.size - 1)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @description: 返回栈是否为空
|
|
135
|
+
* @return {Boolean}
|
|
136
|
+
*/
|
|
137
|
+
isEmpty(): boolean {
|
|
138
|
+
return this.items.size === 0
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @description: 返回栈里的元素个数
|
|
143
|
+
* @return {Number}
|
|
144
|
+
*/
|
|
145
|
+
size(): number {
|
|
146
|
+
return this.items.size
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @description: 清空栈
|
|
151
|
+
*/
|
|
152
|
+
clear() {
|
|
153
|
+
this.items.clear()
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
list() {
|
|
157
|
+
return Array.from(this.items.values())
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* @description: 覆盖Object默认的toString
|
|
162
|
+
* @return {String}
|
|
163
|
+
*/
|
|
164
|
+
toString(): string {
|
|
165
|
+
if (this.isEmpty()) {
|
|
166
|
+
return ''
|
|
167
|
+
}
|
|
168
|
+
let result: string = ''
|
|
169
|
+
this.items.forEach((value, key) => {
|
|
170
|
+
result = `${result}${key === 0 ? '' : ', '}${value}`
|
|
171
|
+
})
|
|
172
|
+
return result
|
|
173
|
+
}
|
|
174
|
+
toJSON() {
|
|
175
|
+
// return Object.fromEntries(this.items)
|
|
176
|
+
return Array.from(this.items.values())
|
|
177
|
+
}
|
|
178
|
+
}
|