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.
- package/README.md +2 -2
- package/dist/favicon.svg +9 -9
- package/dist/src/lib/StackTabs.vue.d.ts +12 -32
- package/dist/src/lib/hooks/useTabRouter.d.ts +1 -0
- package/dist/src/lib/hooks/useTabpanel.d.ts +3 -0
- package/dist/src/lib/i18n/index.d.ts +4 -1
- package/dist/src/lib/i18n/lang/en.d.ts +2 -2
- package/dist/src/lib/i18n/lang/zh-CN.d.ts +2 -2
- package/dist/src/lib/index.d.ts +1 -1
- package/dist/style.css +1 -1
- package/dist/vue-stack-tabs.es.js +1341 -1265
- package/dist/vue-stack-tabs.umd.js +3 -3
- package/package.json +1 -1
- package/src/lib/StackTabs.vue +129 -124
- package/src/lib/assets/style/transition.scss +40 -40
- package/src/lib/components/ContextMenu/index.vue +2 -2
- package/src/lib/components/PageLoading.vue +20 -20
- package/src/lib/components/TabHeader/TabHeaderItem.vue +69 -69
- package/src/lib/components/TabHeader/index.vue +137 -137
- package/src/lib/hooks/useStackTab.ts +102 -75
- package/src/lib/hooks/useTabLoading.ts +18 -18
- package/src/lib/hooks/useTabMitt.ts +30 -30
- package/src/lib/hooks/useTabRouter.ts +11 -5
- package/src/lib/hooks/useTabpanel.tsx +597 -525
- package/src/lib/i18n/index.ts +8 -5
- package/src/lib/i18n/lang/en.ts +2 -2
- package/src/lib/i18n/lang/zh-CN.ts +2 -2
- package/src/lib/index.ts +1 -1
- package/src/lib/model/TabModel.ts +2 -2
|
@@ -1,137 +1,137 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="stack-tab__header">
|
|
3
|
-
<slot name="leftButton" />
|
|
4
|
-
<tab-header-scroll
|
|
5
|
-
ref="scroll"
|
|
6
|
-
:space="space"
|
|
7
|
-
:is-scroll-wheel="isScrollWheel"
|
|
8
|
-
:is-scroll-button="isScrollButton"
|
|
9
|
-
>
|
|
10
|
-
<template #default>
|
|
11
|
-
<transition-group
|
|
12
|
-
key="tabItemTrans"
|
|
13
|
-
tag="ul"
|
|
14
|
-
class="stack-tab__nav"
|
|
15
|
-
v-bind="tabTrans"
|
|
16
|
-
appear
|
|
17
|
-
>
|
|
18
|
-
<tab-header-item
|
|
19
|
-
v-for="(item, index) in tabs"
|
|
20
|
-
:key="item.id"
|
|
21
|
-
:contextmenu="contextmenu"
|
|
22
|
-
:item="item"
|
|
23
|
-
@contextmenu.prevent="
|
|
24
|
-
(e: MouseEvent) => showContextMenu(e, item as ITabItem, index, tabs.length)
|
|
25
|
-
"
|
|
26
|
-
@click.middle.prevent="closeTab(item as ITabItem)"
|
|
27
|
-
@close="closeTab"
|
|
28
|
-
@active="activeTab"
|
|
29
|
-
/>
|
|
30
|
-
</transition-group>
|
|
31
|
-
</template>
|
|
32
|
-
</tab-header-scroll>
|
|
33
|
-
<slot name="rightButton" />
|
|
34
|
-
<tab-header-button
|
|
35
|
-
:icon-class="maximum ? 'stack-tab__icon-restore' : 'stack-tab__icon-fullscreen'"
|
|
36
|
-
:title="t('VueStackTab.maximum')"
|
|
37
|
-
@click="maximum = !maximum"
|
|
38
|
-
/>
|
|
39
|
-
<transition name="stack-tab-zoom" appear>
|
|
40
|
-
<context-menu
|
|
41
|
-
v-if="contextMenuShown"
|
|
42
|
-
key="tabMenuTrans"
|
|
43
|
-
:left="contextMenuData.left"
|
|
44
|
-
:top="contextMenuData.top"
|
|
45
|
-
:tab-item="contextMenuData.item as ITabItem"
|
|
46
|
-
:max="contextMenuData.max"
|
|
47
|
-
:index="contextMenuData.index"
|
|
48
|
-
/>
|
|
49
|
-
</transition>
|
|
50
|
-
</div>
|
|
51
|
-
</template>
|
|
52
|
-
|
|
53
|
-
<script lang="ts" setup>
|
|
54
|
-
// @ts-nocheck
|
|
55
|
-
import { inject, computed, ref, nextTick } from 'vue'
|
|
56
|
-
import type { TransitionProps, Ref } from 'vue'
|
|
57
|
-
import { TabScrollMode } from '../../model/TabModel'
|
|
58
|
-
import type { ITabItem } from '../../model/TabModel'
|
|
59
|
-
import ContextMenu from '../ContextMenu/index.vue'
|
|
60
|
-
import useContextMenu from '../../hooks/useContextMenu'
|
|
61
|
-
import useTabpanel from '../../hooks/useTabpanel'
|
|
62
|
-
import TabHeaderItem from './TabHeaderItem.vue'
|
|
63
|
-
import TabHeaderScroll from './TabHeaderScroll.vue'
|
|
64
|
-
import TabHeaderButton from './TabHeaderButton.vue'
|
|
65
|
-
import { useI18n } from 'vue-i18n-lite'
|
|
66
|
-
// @ts-ignore
|
|
67
|
-
const { t } = useI18n()
|
|
68
|
-
const maximum = inject<boolean>('maximum')
|
|
69
|
-
const emit = defineEmits(['active'])
|
|
70
|
-
const scroll = ref()
|
|
71
|
-
const { shown: contextMenuShown, contextMenuData, showContextMenu } = useContextMenu()
|
|
72
|
-
|
|
73
|
-
const props = withDefaults(
|
|
74
|
-
defineProps<{
|
|
75
|
-
space: number
|
|
76
|
-
// tab的滚动方式
|
|
77
|
-
tabScrollMode?: TabScrollMode
|
|
78
|
-
// tab动画
|
|
79
|
-
tabTransition?: string | TransitionProps
|
|
80
|
-
contextmenu?: boolean | object
|
|
81
|
-
max?: number
|
|
82
|
-
}>(),
|
|
83
|
-
{
|
|
84
|
-
tabTransition: () => {
|
|
85
|
-
return {
|
|
86
|
-
name: 'stack-tab-zoom'
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
tabScrollMode: TabScrollMode.BOTH,
|
|
90
|
-
contextmenu: true,
|
|
91
|
-
max: 20
|
|
92
|
-
}
|
|
93
|
-
)
|
|
94
|
-
const { removeTab, active, tabs } = useTabpanel()
|
|
95
|
-
|
|
96
|
-
// 转换tab动画
|
|
97
|
-
const tabTrans = computed<TransitionProps>(() => {
|
|
98
|
-
if (typeof props.tabTransition === 'string') {
|
|
99
|
-
return {
|
|
100
|
-
name: props.tabTransition
|
|
101
|
-
} as TransitionProps
|
|
102
|
-
}
|
|
103
|
-
return props.tabTransition as TransitionProps
|
|
104
|
-
})
|
|
105
|
-
// 判断是否有滚动按钮
|
|
106
|
-
const isScrollButton = computed<boolean>(() => {
|
|
107
|
-
return props.tabScrollMode === TabScrollMode.BOTH || props.tabScrollMode === TabScrollMode.BUTTON
|
|
108
|
-
})
|
|
109
|
-
// 判断是否可以使用滚轮
|
|
110
|
-
const isScrollWheel =
|
|
111
|
-
props.tabScrollMode === TabScrollMode.BOTH || props.tabScrollMode === TabScrollMode.WHEEL
|
|
112
|
-
|
|
113
|
-
// 关闭前,先判断是不最先中状态
|
|
114
|
-
const closeTab = (item: ITabItem) => {
|
|
115
|
-
// emit('close', item)
|
|
116
|
-
if (item.closable) {
|
|
117
|
-
const activeId = removeTab(item.id)
|
|
118
|
-
emit('active', activeId)
|
|
119
|
-
}
|
|
120
|
-
contextMenuShown.value = false
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const activeTab = (item: ITabItem, curActiveTab: Ref<HTMLElement>, isRoute: boolean) => {
|
|
124
|
-
if (scroll.value) {
|
|
125
|
-
nextTick(() => {
|
|
126
|
-
if (!scroll.value!.isInView(curActiveTab.value)) {
|
|
127
|
-
scroll.value!.scrollIntoView(curActiveTab.value)
|
|
128
|
-
}
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
active(item.id, isRoute)
|
|
132
|
-
emit('active', item.id)
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
</script>
|
|
136
|
-
|
|
137
|
-
<style scoped></style>
|
|
1
|
+
<template>
|
|
2
|
+
<div class="stack-tab__header">
|
|
3
|
+
<slot name="leftButton" />
|
|
4
|
+
<tab-header-scroll
|
|
5
|
+
ref="scroll"
|
|
6
|
+
:space="space"
|
|
7
|
+
:is-scroll-wheel="isScrollWheel"
|
|
8
|
+
:is-scroll-button="isScrollButton"
|
|
9
|
+
>
|
|
10
|
+
<template #default>
|
|
11
|
+
<transition-group
|
|
12
|
+
key="tabItemTrans"
|
|
13
|
+
tag="ul"
|
|
14
|
+
class="stack-tab__nav"
|
|
15
|
+
v-bind="tabTrans"
|
|
16
|
+
appear
|
|
17
|
+
>
|
|
18
|
+
<tab-header-item
|
|
19
|
+
v-for="(item, index) in tabs"
|
|
20
|
+
:key="item.id"
|
|
21
|
+
:contextmenu="contextmenu"
|
|
22
|
+
:item="item"
|
|
23
|
+
@contextmenu.prevent="
|
|
24
|
+
(e: MouseEvent) => showContextMenu(e, item as ITabItem, index, tabs.length)
|
|
25
|
+
"
|
|
26
|
+
@click.middle.prevent="closeTab(item as ITabItem)"
|
|
27
|
+
@close="closeTab"
|
|
28
|
+
@active="activeTab"
|
|
29
|
+
/>
|
|
30
|
+
</transition-group>
|
|
31
|
+
</template>
|
|
32
|
+
</tab-header-scroll>
|
|
33
|
+
<slot name="rightButton" />
|
|
34
|
+
<tab-header-button
|
|
35
|
+
:icon-class="maximum ? 'stack-tab__icon-restore' : 'stack-tab__icon-fullscreen'"
|
|
36
|
+
:title="t('VueStackTab.maximum')"
|
|
37
|
+
@click="maximum = !maximum"
|
|
38
|
+
/>
|
|
39
|
+
<transition name="stack-tab-zoom" appear>
|
|
40
|
+
<context-menu
|
|
41
|
+
v-if="contextMenuShown"
|
|
42
|
+
key="tabMenuTrans"
|
|
43
|
+
:left="contextMenuData.left"
|
|
44
|
+
:top="contextMenuData.top"
|
|
45
|
+
:tab-item="contextMenuData.item as ITabItem"
|
|
46
|
+
:max="contextMenuData.max"
|
|
47
|
+
:index="contextMenuData.index"
|
|
48
|
+
/>
|
|
49
|
+
</transition>
|
|
50
|
+
</div>
|
|
51
|
+
</template>
|
|
52
|
+
|
|
53
|
+
<script lang="ts" setup>
|
|
54
|
+
// @ts-nocheck
|
|
55
|
+
import { inject, computed, ref, nextTick } from 'vue'
|
|
56
|
+
import type { TransitionProps, Ref } from 'vue'
|
|
57
|
+
import { TabScrollMode } from '../../model/TabModel'
|
|
58
|
+
import type { ITabItem } from '../../model/TabModel'
|
|
59
|
+
import ContextMenu from '../ContextMenu/index.vue'
|
|
60
|
+
import useContextMenu from '../../hooks/useContextMenu'
|
|
61
|
+
import useTabpanel from '../../hooks/useTabpanel'
|
|
62
|
+
import TabHeaderItem from './TabHeaderItem.vue'
|
|
63
|
+
import TabHeaderScroll from './TabHeaderScroll.vue'
|
|
64
|
+
import TabHeaderButton from './TabHeaderButton.vue'
|
|
65
|
+
import { useI18n } from 'vue-i18n-lite'
|
|
66
|
+
// @ts-ignore
|
|
67
|
+
const { t } = useI18n()
|
|
68
|
+
const maximum = inject<boolean>('maximum')
|
|
69
|
+
const emit = defineEmits(['active'])
|
|
70
|
+
const scroll = ref()
|
|
71
|
+
const { shown: contextMenuShown, contextMenuData, showContextMenu } = useContextMenu()
|
|
72
|
+
|
|
73
|
+
const props = withDefaults(
|
|
74
|
+
defineProps<{
|
|
75
|
+
space: number
|
|
76
|
+
// tab的滚动方式
|
|
77
|
+
tabScrollMode?: TabScrollMode
|
|
78
|
+
// tab动画
|
|
79
|
+
tabTransition?: string | TransitionProps
|
|
80
|
+
contextmenu?: boolean | object
|
|
81
|
+
max?: number
|
|
82
|
+
}>(),
|
|
83
|
+
{
|
|
84
|
+
tabTransition: () => {
|
|
85
|
+
return {
|
|
86
|
+
name: 'stack-tab-zoom'
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
tabScrollMode: TabScrollMode.BOTH,
|
|
90
|
+
contextmenu: true,
|
|
91
|
+
max: 20
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
const { removeTab, active, tabs } = useTabpanel()
|
|
95
|
+
|
|
96
|
+
// 转换tab动画
|
|
97
|
+
const tabTrans = computed<TransitionProps>(() => {
|
|
98
|
+
if (typeof props.tabTransition === 'string') {
|
|
99
|
+
return {
|
|
100
|
+
name: props.tabTransition
|
|
101
|
+
} as TransitionProps
|
|
102
|
+
}
|
|
103
|
+
return props.tabTransition as TransitionProps
|
|
104
|
+
})
|
|
105
|
+
// 判断是否有滚动按钮
|
|
106
|
+
const isScrollButton = computed<boolean>(() => {
|
|
107
|
+
return props.tabScrollMode === TabScrollMode.BOTH || props.tabScrollMode === TabScrollMode.BUTTON
|
|
108
|
+
})
|
|
109
|
+
// 判断是否可以使用滚轮
|
|
110
|
+
const isScrollWheel =
|
|
111
|
+
props.tabScrollMode === TabScrollMode.BOTH || props.tabScrollMode === TabScrollMode.WHEEL
|
|
112
|
+
|
|
113
|
+
// 关闭前,先判断是不最先中状态
|
|
114
|
+
const closeTab = (item: ITabItem) => {
|
|
115
|
+
// emit('close', item)
|
|
116
|
+
if (item.closable) {
|
|
117
|
+
const activeId = removeTab(item.id)
|
|
118
|
+
emit('active', activeId)
|
|
119
|
+
}
|
|
120
|
+
contextMenuShown.value = false
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const activeTab = (item: ITabItem, curActiveTab: Ref<HTMLElement>, isRoute: boolean) => {
|
|
124
|
+
if (scroll.value) {
|
|
125
|
+
nextTick(() => {
|
|
126
|
+
if (!scroll.value!.isInView(curActiveTab.value)) {
|
|
127
|
+
scroll.value!.scrollIntoView(curActiveTab.value)
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
active(item.id, isRoute)
|
|
132
|
+
emit('active', item.id)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
</script>
|
|
136
|
+
|
|
137
|
+
<style scoped></style>
|
|
@@ -1,75 +1,102 @@
|
|
|
1
|
-
import { useRouter } from 'vue-router'
|
|
2
|
-
import useTabpanel from '../hooks/useTabpanel'
|
|
3
|
-
import { throttle } from 'lodash-es'
|
|
4
|
-
import type { ITabData } from '../model/TabModel'
|
|
5
|
-
import { defu } from 'defu'
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
*
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
1
|
+
import { useRouter } from 'vue-router'
|
|
2
|
+
import useTabpanel from '../hooks/useTabpanel'
|
|
3
|
+
import { throttle } from 'lodash-es'
|
|
4
|
+
import type { ITabData, ITabItem, ITabPage } from '../model/TabModel'
|
|
5
|
+
import { defu } from 'defu'
|
|
6
|
+
import { unref } from 'vue'
|
|
7
|
+
import { encodeTabInfo } from '../utils/TabIdHelper'
|
|
8
|
+
import { uriDecode } from '../utils/UriHelper'
|
|
9
|
+
import { ulid } from 'ulidx'
|
|
10
|
+
import { MittType, useEmitter } from './useTabMitt'
|
|
11
|
+
import { Stack } from '../model/TabModel'
|
|
12
|
+
let iframePath: string
|
|
13
|
+
export default () => {
|
|
14
|
+
const router = useRouter()
|
|
15
|
+
const { active, hasTab, pageShown, reset, canAddTab, tabs, addTab, initialed } = useTabpanel()
|
|
16
|
+
const emitter = useEmitter()
|
|
17
|
+
/**
|
|
18
|
+
* 打开新的TAB页面
|
|
19
|
+
* @param tab
|
|
20
|
+
* @param to
|
|
21
|
+
*/
|
|
22
|
+
const openNewTab = throttle((tab: ITabData) => {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
if (!pageShown.value) {
|
|
25
|
+
reject()
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
const tabInfo = defu(tab, { refreshable: true, closable: true, iframe: false })
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* In the function of openNewTab, if the tab already 'ACTIVE' then do nothing.
|
|
32
|
+
*/
|
|
33
|
+
if (hasTab(tabInfo.id!)) {
|
|
34
|
+
emitter.emit(MittType.TAB_ACTIVE, { id: tabInfo.id! })
|
|
35
|
+
// active(tabInfo.id!)
|
|
36
|
+
resolve(tabInfo.id)
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
if (!tabInfo.id) {
|
|
40
|
+
tabInfo.id = ulid()
|
|
41
|
+
}
|
|
42
|
+
if (!canAddTab()) {
|
|
43
|
+
reject('Max Size')
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (initialed) {
|
|
48
|
+
// 增加tab
|
|
49
|
+
let activeTab: ITabItem | null = null
|
|
50
|
+
for (const tab of unref(tabs)) {
|
|
51
|
+
tab.active = false
|
|
52
|
+
}
|
|
53
|
+
activeTab = {
|
|
54
|
+
id: tabInfo.id!,
|
|
55
|
+
title: tabInfo.title,
|
|
56
|
+
closable: tabInfo.closable!,
|
|
57
|
+
refreshable: tabInfo.refreshable!,
|
|
58
|
+
iframe: tabInfo.iframe!,
|
|
59
|
+
url: tabInfo.iframe ? tab.path : null,
|
|
60
|
+
active: true,
|
|
61
|
+
pages: new Stack<ITabPage>()
|
|
62
|
+
} as ITabItem
|
|
63
|
+
addTab(activeTab).finally(() => {
|
|
64
|
+
toPage(tab, tabInfo)
|
|
65
|
+
resolve(tabInfo.id)
|
|
66
|
+
})
|
|
67
|
+
} else {
|
|
68
|
+
toPage(tab, tabInfo)
|
|
69
|
+
resolve(tabInfo.id)
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
}, 500)
|
|
73
|
+
const toPage = (tab: ITabData, tabInfo: any) => {
|
|
74
|
+
const __tab = encodeTabInfo(tabInfo)
|
|
75
|
+
let query = defu(
|
|
76
|
+
{
|
|
77
|
+
__tab
|
|
78
|
+
},
|
|
79
|
+
tab.query
|
|
80
|
+
) as Record<string, string>
|
|
81
|
+
let path
|
|
82
|
+
if (!tab.iframe) {
|
|
83
|
+
const url = uriDecode(tab.path)
|
|
84
|
+
path = url.path
|
|
85
|
+
query = defu(url.query, query)
|
|
86
|
+
} else {
|
|
87
|
+
const __src = encodeURIComponent(tab.path as string)
|
|
88
|
+
path = iframePath
|
|
89
|
+
query['__src'] = __src
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
router.push({
|
|
93
|
+
path,
|
|
94
|
+
query
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
const setIFramePath = (path: string) => {
|
|
98
|
+
iframePath = path
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return { openNewTab, setIFramePath, active, reset }
|
|
102
|
+
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { type ComponentInternalInstance, getCurrentInstance, onUnmounted } from 'vue'
|
|
2
|
-
import { MittType, useEmitter } from '../hooks/useTabMitt'
|
|
3
|
-
|
|
4
|
-
export default () => {
|
|
5
|
-
const emitter = useEmitter()
|
|
6
|
-
const { attrs } = getCurrentInstance() as ComponentInternalInstance
|
|
7
|
-
onUnmounted(() => {
|
|
8
|
-
closeTabLoading()
|
|
9
|
-
})
|
|
10
|
-
const openTabLoading = () => {
|
|
11
|
-
emitter.emit(MittType.PAGE_LOADING, { tId: attrs.tId, value: true })
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const closeTabLoading = () => {
|
|
15
|
-
emitter.emit(MittType.PAGE_LOADING, { tId: attrs.tId, value: false })
|
|
16
|
-
}
|
|
17
|
-
return { openTabLoading, closeTabLoading }
|
|
18
|
-
}
|
|
1
|
+
import { type ComponentInternalInstance, getCurrentInstance, onUnmounted } from 'vue'
|
|
2
|
+
import { MittType, useEmitter } from '../hooks/useTabMitt'
|
|
3
|
+
|
|
4
|
+
export default () => {
|
|
5
|
+
const emitter = useEmitter()
|
|
6
|
+
const { attrs } = getCurrentInstance() as ComponentInternalInstance
|
|
7
|
+
onUnmounted(() => {
|
|
8
|
+
closeTabLoading()
|
|
9
|
+
})
|
|
10
|
+
const openTabLoading = () => {
|
|
11
|
+
emitter.emit(MittType.PAGE_LOADING, { tId: attrs.tId, value: true })
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const closeTabLoading = () => {
|
|
15
|
+
emitter.emit(MittType.PAGE_LOADING, { tId: attrs.tId, value: false })
|
|
16
|
+
}
|
|
17
|
+
return { openTabLoading, closeTabLoading }
|
|
18
|
+
}
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import type { Emitter, EventType } from 'mitt'
|
|
2
|
-
import mitt from 'mitt'
|
|
3
|
-
import { type App, inject } from 'vue'
|
|
4
|
-
// const emitter = mitt()
|
|
5
|
-
// Cutsom type
|
|
6
|
-
declare module '@vue/runtime-core' {
|
|
7
|
-
export interface ComponentCustomProperties {
|
|
8
|
-
$layerEmitter: Emitter<Record<EventType, unknown>>
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
const emitter: Emitter<Record<EventType, unknown>> = mitt()
|
|
12
|
-
|
|
13
|
-
const plugin = {
|
|
14
|
-
install(app: App) {
|
|
15
|
-
// const emitter = mitt()
|
|
16
|
-
app.provide('tabEmitter', emitter)
|
|
17
|
-
// app.config.globalProperties.$emitter = emitter
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
// export const setEmitter = (mitter: Emitter<Record<EventType, unknown>>) => {
|
|
21
|
-
// emitter = mitter
|
|
22
|
-
// }
|
|
23
|
-
export const useEmitter = <T extends Record<EventType, unknown>>(): Emitter<T> => {
|
|
24
|
-
return inject('tabEmitter')!
|
|
25
|
-
}
|
|
26
|
-
export enum MittType {
|
|
27
|
-
PAGE_LOADING = 'PAGE_LOADING',
|
|
28
|
-
TAB_ACTIVE = 'TAB_ACTIVE'
|
|
29
|
-
}
|
|
30
|
-
export default plugin
|
|
1
|
+
import type { Emitter, EventType } from 'mitt'
|
|
2
|
+
import mitt from 'mitt'
|
|
3
|
+
import { type App, inject } from 'vue'
|
|
4
|
+
// const emitter = mitt()
|
|
5
|
+
// Cutsom type
|
|
6
|
+
declare module '@vue/runtime-core' {
|
|
7
|
+
export interface ComponentCustomProperties {
|
|
8
|
+
$layerEmitter: Emitter<Record<EventType, unknown>>
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const emitter: Emitter<Record<EventType, unknown>> = mitt()
|
|
12
|
+
|
|
13
|
+
const plugin = {
|
|
14
|
+
install(app: App) {
|
|
15
|
+
// const emitter = mitt()
|
|
16
|
+
app.provide('tabEmitter', emitter)
|
|
17
|
+
// app.config.globalProperties.$emitter = emitter
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// export const setEmitter = (mitter: Emitter<Record<EventType, unknown>>) => {
|
|
21
|
+
// emitter = mitter
|
|
22
|
+
// }
|
|
23
|
+
export const useEmitter = <T extends Record<EventType, unknown>>(): Emitter<T> => {
|
|
24
|
+
return inject('tabEmitter')!
|
|
25
|
+
}
|
|
26
|
+
export enum MittType {
|
|
27
|
+
PAGE_LOADING = 'PAGE_LOADING',
|
|
28
|
+
TAB_ACTIVE = 'TAB_ACTIVE'
|
|
29
|
+
}
|
|
30
|
+
export default plugin
|
|
@@ -7,17 +7,22 @@ import { type RouteLocationPathRaw, useRoute, useRouter } from 'vue-router'
|
|
|
7
7
|
import useTabpanel from '@/lib/hooks/useTabpanel'
|
|
8
8
|
import { uriDecode } from '@/lib/utils/UriHelper'
|
|
9
9
|
export default () => {
|
|
10
|
-
const { attrs } = getCurrentInstance() as ComponentInternalInstance
|
|
11
|
-
const { pageShown, getTab, markDeletableCache, removeComponent, getComponent } =
|
|
10
|
+
const { attrs, props } = getCurrentInstance() as ComponentInternalInstance
|
|
11
|
+
const { pageShown, getTab, markDeletableCache, removeComponent, getComponent, addPageScroller } =
|
|
12
|
+
useTabpanel()
|
|
12
13
|
const route = useRoute()
|
|
13
14
|
const router = useRouter()
|
|
14
15
|
let currentId = ''
|
|
15
16
|
// const currentPageId = ''
|
|
16
17
|
let currentTab = ''
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
currentId = (attrs.tId || props.tId) as string
|
|
19
|
+
const currentPageId = (attrs.pId || props.pId) as string
|
|
19
20
|
// currentPageId = attrs.pId as string
|
|
20
21
|
currentTab = route.query.__tab as string
|
|
22
|
+
|
|
23
|
+
const addScroller = (...scroller: string[]) => {
|
|
24
|
+
addPageScroller(currentPageId, ...scroller)
|
|
25
|
+
}
|
|
21
26
|
const forward = (to: RouteLocationPathRaw) => {
|
|
22
27
|
pageShown.value = false
|
|
23
28
|
const query = defu(
|
|
@@ -88,6 +93,7 @@ export default () => {
|
|
|
88
93
|
|
|
89
94
|
return {
|
|
90
95
|
forward,
|
|
91
|
-
backward
|
|
96
|
+
backward,
|
|
97
|
+
addScroller
|
|
92
98
|
}
|
|
93
99
|
}
|