mirror of
https://github.com/fugary/simple-element-plus-template.git
synced 2025-12-31 03:17:49 +00:00
增加tab模式以及tab缓存
This commit is contained in:
61
src/components/common-tabs-view/index.vue
Normal file
61
src/components/common-tabs-view/index.vue
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<script setup>
|
||||||
|
import { useTabsViewStore } from '@/stores/TabsViewStore'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { onMounted, ref, watch } from 'vue'
|
||||||
|
import isString from 'lodash/isString'
|
||||||
|
import TabsViewItem from '@/components/common-tabs-view/tabs-view-item.vue'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
const tabsViewStore = useTabsViewStore()
|
||||||
|
const currentTabValue = ref('')
|
||||||
|
watch(route, () => {
|
||||||
|
if (route.path) {
|
||||||
|
tabsViewStore.addHistoryTab(route)
|
||||||
|
currentTabValue.value = route.path
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!tabsViewStore.historyTabs.length) {
|
||||||
|
tabsViewStore.addHistoryTab(route)
|
||||||
|
}
|
||||||
|
currentTabValue.value = route.path
|
||||||
|
})
|
||||||
|
|
||||||
|
const selectHistoryTab = path => {
|
||||||
|
const tab = isString(path) ? tabsViewStore.findHistoryTab(path) : path
|
||||||
|
if (tab) {
|
||||||
|
router.push(tab)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeHistoryTab = path => {
|
||||||
|
const lastTab = tabsViewStore.removeHistoryTab(path)
|
||||||
|
if (lastTab) {
|
||||||
|
selectHistoryTab(lastTab)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-tabs
|
||||||
|
v-bind="$attrs"
|
||||||
|
v-model="currentTabValue"
|
||||||
|
type="card"
|
||||||
|
:closable="tabsViewStore.historyTabs.length>1"
|
||||||
|
@tab-change="selectHistoryTab"
|
||||||
|
@tab-remove="removeHistoryTab"
|
||||||
|
>
|
||||||
|
<tabs-view-item
|
||||||
|
v-for="item in tabsViewStore.historyTabs"
|
||||||
|
:key="item.path"
|
||||||
|
:tab-item="item"
|
||||||
|
/>
|
||||||
|
</el-tabs>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
28
src/components/common-tabs-view/tabs-view-item.vue
Normal file
28
src/components/common-tabs-view/tabs-view-item.vue
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<script setup>
|
||||||
|
import { useMenuName } from '@/components/utils'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
tabItem: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const menuName = computed(() => {
|
||||||
|
if (props.tabItem) {
|
||||||
|
return useMenuName(props.tabItem)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-tab-pane
|
||||||
|
:label="menuName"
|
||||||
|
:name="tabItem.path"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -2,6 +2,7 @@ import CommonIcon from '@/components/common-icon/index.vue'
|
|||||||
import CommonInput from '@/components/common-form-input/index.vue'
|
import CommonInput from '@/components/common-form-input/index.vue'
|
||||||
import CommonMenu from '@/components/common-menu/index.vue'
|
import CommonMenu from '@/components/common-menu/index.vue'
|
||||||
import CommonMenuItem from '@/components/common-menu-item/index.vue'
|
import CommonMenuItem from '@/components/common-menu-item/index.vue'
|
||||||
|
import CommonTabsView from '@/components/common-tabs-view/index.vue'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义通用组件自动注册
|
* 自定义通用组件自动注册
|
||||||
@@ -15,5 +16,6 @@ export default {
|
|||||||
Vue.component('CommonInput', CommonInput)
|
Vue.component('CommonInput', CommonInput)
|
||||||
Vue.component('CommonMenu', CommonMenu)
|
Vue.component('CommonMenu', CommonMenu)
|
||||||
Vue.component('CommonMenuItem', CommonMenuItem)
|
Vue.component('CommonMenuItem', CommonMenuItem)
|
||||||
|
Vue.component('CommonTabsView', CommonTabsView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
|
import { $i18nBundle } from '@/messages'
|
||||||
|
|
||||||
const calcWithIf = menuItem => {
|
const calcWithIf = menuItem => {
|
||||||
['icon', 'labelKey', 'label', 'html'].forEach(key => {
|
['icon', 'labelKey', 'label', 'html'].forEach(key => {
|
||||||
const keyIf = menuItem[`${key}If`]
|
const keyIf = menuItem[`${key}If`]
|
||||||
@@ -6,9 +9,37 @@ const calcWithIf = menuItem => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const MENU_INFO_LIST = ref({})
|
||||||
|
|
||||||
|
export const useMenuInfo = path => {
|
||||||
|
const menuInfo = MENU_INFO_LIST.value[path]
|
||||||
|
console.info('================', MENU_INFO_LIST.value)
|
||||||
|
return menuInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useMenuName = item => {
|
||||||
|
const menuInfo = useMenuInfo(item.path)
|
||||||
|
if (menuInfo) {
|
||||||
|
if (menuInfo.label) {
|
||||||
|
return menuInfo.label
|
||||||
|
}
|
||||||
|
if (menuInfo.labelKey) {
|
||||||
|
return $i18nBundle(menuInfo.labelKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item.meta && item.meta.labelKey) {
|
||||||
|
return $i18nBundle(item.meta.labelKey)
|
||||||
|
}
|
||||||
|
return item.name || 'No Name'
|
||||||
|
}
|
||||||
|
|
||||||
export const filterMenus = menus => menus.filter(menu => !menu.disabled)
|
export const filterMenus = menus => menus.filter(menu => !menu.disabled)
|
||||||
.map(menu => {
|
.map(menu => {
|
||||||
calcWithIf(menu)
|
calcWithIf(menu)
|
||||||
|
if (menu.index) { // 把菜单存储下来,后面需要使用名字
|
||||||
|
MENU_INFO_LIST.value[menu.index] = menu
|
||||||
|
}
|
||||||
if (menu.children && menu.children.length) {
|
if (menu.children && menu.children.length) {
|
||||||
menu.children = filterMenus(menu.children)
|
menu.children = filterMenus(menu.children)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
export default [{
|
export default [{
|
||||||
path: 'icons',
|
path: '/icons',
|
||||||
name: 'icons',
|
name: 'icons',
|
||||||
component: () => import('@/views/tools/Icons.vue')
|
component: () => import('@/views/tools/Icons.vue')
|
||||||
}, {
|
}, {
|
||||||
path: 'forms',
|
path: '/forms',
|
||||||
name: 'forms',
|
name: 'forms',
|
||||||
component: () => import('@/views/tools/Forms.vue')
|
component: () => import('@/views/tools/Forms.vue')
|
||||||
}, {
|
}, {
|
||||||
path: 'tables',
|
path: '/tables',
|
||||||
name: 'tables',
|
name: 'tables',
|
||||||
component: () => import('@/views/tools/Tables.vue')
|
component: () => import('@/views/tools/Tables.vue')
|
||||||
}]
|
}]
|
||||||
|
|||||||
85
src/stores/TabsViewStore.js
Normal file
85
src/stores/TabsViewStore.js
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
export const useTabsViewStore = defineStore('tabsView', () => {
|
||||||
|
const isTabMode = ref(true)
|
||||||
|
const isCachedTabMode = ref(true)
|
||||||
|
const historyTabs = ref([])
|
||||||
|
const cachedTabs = ref([])
|
||||||
|
|
||||||
|
const clearHistoryTabs = () => {
|
||||||
|
if (historyTabs.value.length) {
|
||||||
|
const tab = historyTabs.value[0]
|
||||||
|
historyTabs.value = [tab]
|
||||||
|
cachedTabs.value = []
|
||||||
|
if (isCachedTabMode.value && tab.name) {
|
||||||
|
cachedTabs.value = [tab.name]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const findHistoryTab = (path) => {
|
||||||
|
const idx = historyTabs.value.findIndex(v => v.path === path)
|
||||||
|
if (idx > -1) {
|
||||||
|
return historyTabs.value[idx]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const addHistoryTab = tab => {
|
||||||
|
// 添加tab
|
||||||
|
if (isTabMode.value) {
|
||||||
|
const idx = historyTabs.value.findIndex(v => v.path === tab.path)
|
||||||
|
if (idx < 0) {
|
||||||
|
historyTabs.value.push(Object.assign({}, tab)) // 可能是Proxy,需要解析出来
|
||||||
|
if (isCachedTabMode.value && tab.name) {
|
||||||
|
cachedTabs.value.push(tab.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const removeHistoryTab = path => {
|
||||||
|
if (historyTabs.value.length > 1) {
|
||||||
|
const idx = historyTabs.value.findIndex(v => v.path === path)
|
||||||
|
if (idx > -1) {
|
||||||
|
removeCachedTab(historyTabs.value[idx])
|
||||||
|
// 删除tab
|
||||||
|
historyTabs.value.splice(idx, 1)
|
||||||
|
}
|
||||||
|
return historyTabs.value[historyTabs.value.length - 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeCachedTab = tab => {
|
||||||
|
if (tab) {
|
||||||
|
const idx = cachedTabs.value.findIndex(v => v === tab.name)
|
||||||
|
if (idx > -1) {
|
||||||
|
cachedTabs.value.splice(idx, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isTabMode,
|
||||||
|
isCachedTabMode,
|
||||||
|
historyTabs,
|
||||||
|
cachedTabs,
|
||||||
|
changeTabMode () {
|
||||||
|
isTabMode.value = !isTabMode.value
|
||||||
|
if (!isTabMode.value) {
|
||||||
|
clearHistoryTabs()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
changeCachedTabMode () {
|
||||||
|
isCachedTabMode.value = !isCachedTabMode.value
|
||||||
|
if (!isCachedTabMode.value) {
|
||||||
|
cachedTabs.value = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeHistoryTab,
|
||||||
|
clearHistoryTabs,
|
||||||
|
findHistoryTab,
|
||||||
|
addHistoryTab
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
persist: true
|
||||||
|
})
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
import { defineStore, createPinia } from 'pinia'
|
import { defineStore, createPinia } from 'pinia'
|
||||||
import piniaPluginPersistedState from 'pinia-plugin-persistedstate'
|
import piniaPluginPersistedState from 'pinia-plugin-persistedstate'
|
||||||
import { useGlobalConfigStore } from '@/stores/GlobalConfigStore'
|
import { useGlobalConfigStore } from '@/stores/GlobalConfigStore'
|
||||||
|
import { useTabsViewStore } from '@/stores/TabsViewStore'
|
||||||
|
|
||||||
export const useStore = defineStore('store', () => {
|
export const useStore = defineStore('store', () => {
|
||||||
return {
|
return {
|
||||||
globalConfig: useGlobalConfigStore()
|
globalConfig: useGlobalConfigStore(),
|
||||||
|
tabsView: useTabsViewStore()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
import LeftMenu from '@/layout/LeftMenu.vue'
|
import LeftMenu from '@/layout/LeftMenu.vue'
|
||||||
import TopNav from '@/layout/TopNav.vue'
|
import TopNav from '@/layout/TopNav.vue'
|
||||||
import { useGlobalConfigStore } from '@/stores/GlobalConfigStore'
|
import { useGlobalConfigStore } from '@/stores/GlobalConfigStore'
|
||||||
|
import { useTabsViewStore } from '@/stores/TabsViewStore'
|
||||||
import { GlobalLayoutMode } from '@/consts/GlobalConstants'
|
import { GlobalLayoutMode } from '@/consts/GlobalConstants'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
const globalConfigStore = useGlobalConfigStore()
|
const globalConfigStore = useGlobalConfigStore()
|
||||||
|
const tabsViewStore = useTabsViewStore()
|
||||||
const showLeftMenu = computed(() => {
|
const showLeftMenu = computed(() => {
|
||||||
return globalConfigStore.layoutMode === GlobalLayoutMode.LEFT
|
return globalConfigStore.layoutMode === GlobalLayoutMode.LEFT
|
||||||
})
|
})
|
||||||
@@ -23,16 +25,21 @@ const showLeftMenu = computed(() => {
|
|||||||
<el-header>
|
<el-header>
|
||||||
<top-nav />
|
<top-nav />
|
||||||
</el-header>
|
</el-header>
|
||||||
|
<el-header v-if="tabsViewStore.isTabMode">
|
||||||
|
<common-tabs-view />
|
||||||
|
</el-header>
|
||||||
<el-main>
|
<el-main>
|
||||||
<router-view v-slot="{ Component, route }">
|
<router-view v-slot="{ Component, route }">
|
||||||
<transition
|
<transition
|
||||||
name="slide-fade"
|
name="slide-fade"
|
||||||
mode="out-in"
|
mode="out-in"
|
||||||
>
|
>
|
||||||
|
<KeepAlive :include="tabsViewStore.cachedTabs">
|
||||||
<component
|
<component
|
||||||
:is="Component"
|
:is="Component"
|
||||||
:key="route.fullPath"
|
:key="route.fullPath"
|
||||||
/>
|
/>
|
||||||
|
</KeepAlive>
|
||||||
</transition>
|
</transition>
|
||||||
</router-view>
|
</router-view>
|
||||||
</el-main>
|
</el-main>
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
onMounted(() => {
|
||||||
|
console.info('=========mounted', useRoute().path)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
onMounted(() => {
|
||||||
|
console.info('=========mounted', useRoute().path)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
onMounted(() => {
|
||||||
|
console.info('=========mounted', useRoute().path)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import { onMounted } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
onMounted(() => {
|
||||||
|
console.info('=========mounted', useRoute().path)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
Reference in New Issue
Block a user