| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- <template>
- <div class="tabs">
- <el-scrollbar
- class="scroll-container tags-view-container"
- ref="scrollbarDom"
- @wheel.passive="handleWhellScroll"
- @scroll="handleScroll"
- >
- <Item
- v-for="menu in menuList"
- :key="menu.meta.title"
- :menu="menu"
- :active="activeMenu.path === menu.path"
- @close="delMenu(menu)"
- @reload="pageReload"
- />
- </el-scrollbar>
- <div class="handle">
- <div id="vueAdminBoxTabRefresh" @click="pageReload"></div>
- <div id="vueAdminBoxTabCloseSelf" @click="closeCurrentRoute"></div>
- <div id="vueAdminBoxTabCloseOther" @click="closeOtherRoute"></div>
- <div id="vueAdminBoxTabCloseAll" @click="closeAllRoute"></div>
- <el-dropdown placement="bottom">
- <div class="el-dropdown-link">
- <el-icon><ArrowDown /></el-icon>
- </div>
- <template #dropdown>
- <el-dropdown-menu>
- <el-dropdown-item class="tab-ddropdown-item" :icon="RefreshLeft" @click="pageReload">重新加载</el-dropdown-item>
- <el-dropdown-item class="tab-ddropdown-item" :icon="CircleClose" :disabled="currentDisabled" @click="closeCurrentRoute">关闭当前标签</el-dropdown-item>
- <el-dropdown-item class="tab-ddropdown-item" :icon="CircleClose" :disabled="menuList.length < 3" @click="closeOtherRoute">关闭其他标签</el-dropdown-item>
- <el-dropdown-item class="tab-ddropdown-item" :icon="CircleClose" :disabled="menuList.length <= 1" @click="closeAllRoute">关闭所有标签</el-dropdown-item>
- </el-dropdown-menu>
- </template>
- </el-dropdown>
- <el-tooltip class="item" effect="dark" :content="contentFullScreen ? '退出全屏': '全屏'" placement="bottom">
- <el-icon @click="onFullscreen"><FullScreen /></el-icon>
- </el-tooltip>
- </div>
- </div>
- </template>
- <script lang="js">
- /** 引用vue系列函数 */
- import { defineComponent, computed, unref, watch, reactive, ref, nextTick } from 'vue'
- import { useStore } from 'vuex'
- import { useRoute, useRouter } from 'vue-router'
- /** 引用图标 */
- import { ArrowDown, RefreshLeft, CircleClose, FullScreen } from '@element-plus/icons'
- import Item from './item.vue'
- import tabsHook from './tabsHook'
- export default defineComponent({
- components: {
- Item, ArrowDown, FullScreen
- },
- setup() {
- const store = useStore()
- const route = useRoute()
- const router = useRouter()
- const scrollbarDom = ref(null)
- const scrollLeft = ref(0)
- const defaultMenu = {
- path: '/dashboard',
- meta: { title: '首页', hideClose: true }
- }
- const contentFullScreen = computed(() => store.state.app.contentFullScreen)
- const currentDisabled = computed(() => route.path === defaultMenu.path)
- let activeMenu = reactive({ path: '' })
- let menuList = ref(tabsHook.getItem())
- if (menuList.value.length === 0) { // 判断之前有没有调用过
- addMenu(defaultMenu)
- }
- watch(menuList.value, (newVal) => {
- tabsHook.setItem(newVal)
- })
- watch(menuList, (newVal) => {
- tabsHook.setItem(newVal)
- })
- router.afterEach(() => {
- addMenu(route)
- initMenu(route)
- })
- // 全屏
- function onFullscreen() {
- store.commit('app/contentFullScreenChange', !contentFullScreen.value)
- }
- // 当前页面组件重新加载
- function pageReload() {
- const self = route.matched[route.matched.length-1].instances.default
- self.handleReload();
- }
- // 关闭当前标签,首页不关闭
- function closeCurrentRoute() {
- if (route.path !== defaultMenu.path) {
- const tab = document.getElementById('vueAdminBoxTabCloseSelf')
- const nextPath = tab?.getAttribute('nextPath')
- delMenu(route, nextPath)
- }
- }
- // 关闭除了当前标签之外的所有标签
- function closeOtherRoute() {
- menuList.value = [defaultMenu]
- if (route.path !== defaultMenu.path) {
- addMenu(route)
- }
- setKeepAliveData()
- }
- // 关闭所有的标签,除了首页
- function closeAllRoute() {
- menuList.value = [defaultMenu]
- setKeepAliveData()
- router.push(defaultMenu.path)
- }
- // 添加新的菜单项
- function addMenu(menu) {
- let { path, meta, name, query } = menu
- if (meta.hideTabs) {
- return
- }
- let hasMenu = menuList.value.some((obj) => {
- return obj.path === path
- })
- if (!hasMenu) {
- menuList.value.push({
- path,
- meta,
- name,
- query
- })
- }
- }
- // 删除菜单项
- function delMenu(menu, nextPath) {
- let index = 0
- if (!menu.meta.hideClose) {
- if (menu.meta.cache && menu.name) {
- store.commit('keepAlive/delKeepAliveComponentsName', menu.name)
- }
- index = menuList.value.findIndex((item) => item.path === menu.path)
- menuList.value.splice(index, 1)
- }
- if (nextPath) {
- router.push(nextPath)
- return
- }
- // 若删除的是当前页面,回到前一页,若为最后一页,则回到默认的首页
- if (menu.path === activeMenu.path) {
- const prePage = index - 1 > 0 ? menuList.value[index - 1] : { path: defaultMenu.path }
- router.push({ path: prePage.path, query: prePage.query || {} })
- }
- }
- // 初始化activeMenu
- function initMenu(menu) {
- activeMenu = menu
- nextTick(() => {
- setPosition()
- })
- }
- /** 设置当前滚动条应该在的位置 */
- function setPosition() {
- if (scrollbarDom.value) {
- const domBox = {
- scrollbar: scrollbarDom.value.wrapRef,
- activeDom: scrollbarDom.value.wrapRef.querySelector('.active'),
- activeFather: scrollbarDom.value.wrapRef.querySelector('.el-scrollbar__view')
- }
- let hasDoms = true
- Object.keys(domBox).forEach((dom) => {
- if (!dom) {
- hasDoms = false
- }
- })
- if (!hasDoms) {
- return
- }
- const domData = {
- scrollbar: domBox.scrollbar.getBoundingClientRect(),
- activeDom: domBox.activeDom.getBoundingClientRect(),
- activeFather: domBox.activeFather.getBoundingClientRect()
- }
- const num = domData.activeDom.x - domData.activeFather.x + 1/2 * domData.activeDom.width - 1/2 * domData.scrollbar.width
- domBox.scrollbar.scrollLeft = num
- }
- }
- // 配置需要缓存的数据
- function setKeepAliveData() {
- let keepAliveNames = []
- menuList.value.forEach((menu) => {
- menu.meta && menu.meta.cache && menu.name && keepAliveNames.push(menu.name)
- })
- store.commit('keepAlive/setKeepAliveComponentsName', keepAliveNames)
- }
- /** 监听鼠标滚动事件 */
- function handleWhellScroll(e) {
- let distance = 0
- let speed = 5
- if (e.wheelDelta > 30) {
- distance = -10
- } else if (e.wheelDelta < -30) {
- distance = 10
- }
- // console.log(scrollLeft.value + eventDelta / 4)
- scrollbarDom.value?.setScrollLeft(scrollLeft.value + distance * speed)
- }
- /** 监听滚动事件 */
- function handleScroll({ scrollLeft: left }) {
- scrollLeft.value = left
- }
- // 初始化时调用:1. 新增菜单 2. 初始化activeMenu
- addMenu(route)
- initMenu(route)
- return {
- RefreshLeft, CircleClose,
- contentFullScreen,
- scrollbarDom,
- // 菜单相关
- menuList,
- activeMenu,
- currentDisabled,
- onFullscreen,
- pageReload,
- delMenu,
- closeCurrentRoute,
- closeOtherRoute,
- closeAllRoute,
- handleScroll,
- handleWhellScroll
- }
- }
- })
- </script>
- <style lang="scss" scoped>
- .tabs {
- display: flex;
- justify-content: space-between;
- align-items: center;
- height: 40px;
- background: var(--system-header-background);
- border-bottom: 1px solid var(--system-header-border-color);
- border-top: 1px solid var(--system-header-border-color);
- box-shadow: 0 1px 4px 0 rgba(0, 0, 0, .1);
- .handle {
- min-width: 95px;
- height: 100%;
- display: flex;
- align-items: center;
- .el-dropdown-link {
- margin-top: 5px;
- border-left: 1px solid var(--system-header-border-color);
- height: 25px;
- width: 40px;
- display: flex;
- justify-content: center;
- align-items: center;
- }
- i {
- color: var(--system-header-text-color);
- }
- }
- }
- .scroll-container {
- white-space: nowrap;
- position: relative;
- overflow: hidden;
- width: 100%;
- :deep(.el-scrollbar__bar) {
- bottom: 0px;
- }
- :deep(.el-scrollbar__wrap) {
- height: 49px;
- }
- }
- .tags-view-container {
- height: 34px;
- flex: 1;
- width: 100%;
- display: flex;
- }
- .el-icon-full-screen {
- cursor: pointer;
- &:hover {
- background: rgba(0,0,0,.025);
- }
- &:focus {
- outline: none;
- }
- }
- .tab-ddropdown-item {
- display: flex;
- align-items: center;
- }
- </style>
|