初始化

This commit is contained in:
2026-04-13 17:32:58 +08:00
commit c6ee0d5243
1342 changed files with 96426 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
<template>
<el-dropdown trigger="click" :teleported="false">
<div class="more-button">
<i :class="'iconfont icon-xiala'"></i>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="refresh">
<el-icon><Refresh /></el-icon>
{{ $t('tabs.refresh') }}
</el-dropdown-item>
<el-dropdown-item @click="maximize">
<el-icon><FullScreen /></el-icon>
{{ $t('tabs.maximize') }}
</el-dropdown-item>
<el-dropdown-item divided @click="closeCurrentTab">
<el-icon><Remove /></el-icon>
{{ $t('tabs.closeCurrent') }}
</el-dropdown-item>
<el-dropdown-item @click="tabStore.closeTabsOnSide(route.fullPath, 'left')">
<el-icon><DArrowLeft /></el-icon>
{{ $t('tabs.closeLeft') }}
</el-dropdown-item>
<el-dropdown-item @click="tabStore.closeTabsOnSide(route.fullPath, 'right')">
<el-icon><DArrowRight /></el-icon>
{{ $t('tabs.closeRight') }}
</el-dropdown-item>
<el-dropdown-item divided @click="tabStore.closeMultipleTab(route.fullPath)">
<el-icon><CircleClose /></el-icon>
{{ $t('tabs.closeOther') }}
</el-dropdown-item>
<el-dropdown-item @click="closeAllTab">
<el-icon><FolderDelete /></el-icon>
{{ $t('tabs.closeAll') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script setup lang="ts">
import { inject, nextTick } from 'vue'
import { HOME_URL } from '@/config'
import { useTabsStore } from '@/stores/modules/tabs'
import { useGlobalStore } from '@/stores/modules/global'
import { useKeepAliveStore } from '@/stores/modules/keepAlive'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
const tabStore = useTabsStore()
const globalStore = useGlobalStore()
const keepAliveStore = useKeepAliveStore()
// refresh current page
const refreshCurrentPage: Function = inject('refresh') as Function
const refresh = () => {
setTimeout(() => {
keepAliveStore.removeKeepAliveName(route.name as string)
refreshCurrentPage(false)
nextTick(() => {
keepAliveStore.addKeepAliveName(route.name as string)
refreshCurrentPage(true)
})
}, 0)
}
// maximize current page
const maximize = () => {
globalStore.setGlobalState('maximize', true)
}
// Close Current
const closeCurrentTab = () => {
if (route.meta.isAffix) return
tabStore.removeTabs(route.fullPath)
}
// Close All
const closeAllTab = () => {
tabStore.closeMultipleTab()
router.push(HOME_URL)
}
</script>
<style scoped lang="scss">
@use '../index.scss';
</style>

View File

@@ -0,0 +1,69 @@
.tabs-box {
background-color: var(--el-bg-color);
.tabs-menu {
position: relative;
width: 100%;
.el-dropdown {
position: absolute;
top: 0;
right: 0;
bottom: 0;
.more-button {
display: flex;
align-items: center;
justify-content: center;
width: 43px;
cursor: pointer;
border-left: 1px solid var(--el-border-color-light);
transition: all 0.3s;
&:hover {
background-color: var(--el-color-info-light-9);
}
.iconfont {
font-size: 12.5px;
}
}
}
:deep(.el-tabs) {
.el-tabs__header {
box-sizing: border-box;
height: 40px;
padding: 0 10px;
margin: 0;
.el-tabs__nav-wrap {
position: absolute;
width: calc(100% - 70px);
.el-tabs__nav {
display: flex;
border: none;
.el-tabs__item {
display: flex;
align-items: center;
justify-content: center;
color: #afafaf;
border: none;
.tabs-icon {
margin: 1.5px 4px 0 0;
font-size: 15px;
}
.is-icon-close {
margin-top: 1px;
}
&.is-active {
color: var(--el-color-primary);
&::before {
position: absolute;
bottom: 0;
width: 100%;
height: 0;
content: "";
border-bottom: 2px solid var(--el-color-primary) !important;
}
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,121 @@
<template>
<div class="tabs-box">
<div class="tabs-menu">
<el-tabs v-model="tabsMenuValue" type="card" @tab-click="tabClick" @tab-remove="tabRemove">
<el-tab-pane
v-for="item in tabsMenuList"
:key="item.path"
:label="item.title"
:name="item.path"
:closable="item.close"
>
<template #label>
<el-icon v-show="item.icon && tabsIcon" class="tabs-icon">
<component :is="item.icon"></component>
</el-icon>
{{ item.title }}
</template>
</el-tab-pane>
</el-tabs>
<MoreButton />
</div>
</div>
</template>
<script setup lang="ts">
import Sortable from 'sortablejs'
import { computed, onMounted, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { TabPaneName, TabsPaneContext } from 'element-plus'
import { HOME_URL } from '@/config'
import { useGlobalStore } from '@/stores/modules/global'
import { useTabsStore } from '@/stores/modules/tabs'
import MoreButton from './components/MoreButton.vue'
const route = useRoute()
const router = useRouter()
const tabStore = useTabsStore()
const globalStore = useGlobalStore()
const tabsMenuValue = ref(route.fullPath)
const tabsMenuList = computed(() => tabStore.tabsMenuList)
const tabsIcon = computed(() => globalStore.tabsIcon)
onMounted(() => {
tabsDrop()
initTabs()
})
watch(
() => route.fullPath,
() => {
if (route.meta.isFull) return
if (route.meta.hideTab) {
tabsMenuValue.value = route.meta.parentPath as string
} else {
tabsMenuValue.value = route.fullPath
const tabsParams = {
icon: route.meta.icon as string,
title: route.meta.title as string,
path: route.fullPath,
name: route.name as string,
close: !route.meta.isAffix,
isKeepAlive: route.meta.isKeepAlive as boolean
}
tabStore.addTabs(tabsParams)
}
},
{ immediate: true }
)
const initTabs = () => {
const affixTabs = router
.getRoutes()
.filter(item => item.meta.isAffix && !item.meta.isHide && !item.meta.isFull && item.name && item.path)
.sort((a, b) => {
if (a.path === HOME_URL) return -1
if (b.path === HOME_URL) return 1
return 0
})
for (let index = affixTabs.length - 1; index >= 0; index -= 1) {
const item = affixTabs[index]
const tabsParams = {
icon: item.meta.icon as string,
title: item.meta.title as string,
path: item.path,
name: item.name as string,
close: !item.meta.isAffix,
isKeepAlive: item.meta.isKeepAlive as boolean,
unshift: true
}
tabStore.addTabs(tabsParams)
}
}
const tabsDrop = () => {
Sortable.create(document.querySelector('.el-tabs__nav') as HTMLElement, {
draggable: '.el-tabs__item',
animation: 300,
onEnd({ newIndex, oldIndex }) {
const tabsList = [...tabStore.tabsMenuList]
const currRow = tabsList.splice(oldIndex as number, 1)[0]
tabsList.splice(newIndex as number, 0, currRow)
tabStore.setTabs(tabsList)
}
})
}
const tabClick = (tabItem: TabsPaneContext) => {
const fullPath = tabItem.props.name as string
router.push(fullPath)
}
const tabRemove = (fullPath: TabPaneName) => {
tabStore.removeTabs(fullPath as string, fullPath == route.fullPath)
}
</script>
<style scoped lang="scss">
@use './index.scss';
</style>