tabs导航添加及右侧的关闭和打印printJs

tab.js

store状态

import { defineStore } from 'pinia'

import { RouteLocationNormalizedLoaded } from 'vue-router'

export const tabsStore = defineStore('tabsStore', {

    state: () => ({

        visitedViews: [] as any[],

        cachedViews: [] as any[]

    }),

    actions: {

        addView(view: RouteLocationNormalizedLoaded) {

            if (this.visitedViews.some(value => value.path === view.path)) {

                return

            }

            this.visitedViews.push(

                Object.assign({}, view, {

                    title: view.meta?.title || 'unknown'

                })

            )

        },

        addCachedView(view: RouteLocationNormalizedLoaded) {

            if (this.cachedViews.includes(view.name)) {

                return

            }

            if (view.meta.cache) {

                this.cachedViews.push(view.name)

            }

        },

        delView(view: RouteLocationNormalizedLoaded) {

            for (const [i, v] of this.visitedViews.entries()) {

                if (v.path === view.path) {

                    this.visitedViews.splice(i, 1)

                    break

                }

            }

            this.delCachedView(view).then()

        },

        delCachedView(view: RouteLocationNormalizedLoaded) {

            return new Promise(resolve => {

                const index = this.cachedViews.indexOf(view.name)

                if (index > -1) {

                    this.cachedViews.splice(index, 1)

                }

                resolve([...this.cachedViews])

            })

        },

        delOthersViews(view: RouteLocationNormalizedLoaded) {

            this.visitedViews = this.visitedViews.filter(v => {

                return v.meta.affix || v.path === view.path

            })

            const index = this.cachedViews.indexOf(view.name)

            if (index > -1) {

                this.cachedViews = this.cachedViews.slice(index, index + 1)

            } else {

                this.cachedViews = []

            }

        },

        delAllViews() {

            this.visitedViews = this.visitedViews.filter(tab => tab.meta.affix)

            this.cachedViews = []

        }

    }

})

tab.vue  使用

<template>

    <div class="tabs-container">

        <div class="tabs-item">

            <el-tabs v-model="activeTabName" :class="tabsStyleClass" @tab-click="tabClick" @tab-remove="tabRemove">

                <el-tab-pane

                    v-for="tab in store.tabsStore.visitedViews"

                    :key="tab"

                    :label="tab.title"

                    :name="tab.path"

                    :closable="!isAffix(tab)"

                ></el-tab-pane>

            </el-tabs>

        </div>

        <el-dropdown class="tabs-action" trigger="click" placement="bottom-end" @command="onClose">

            <template #dropdown>

                <el-dropdown-menu>

                    <el-dropdown-item :icon="Close" command="close">{{ $t('app.close') }}</el-dropdown-item>

                    <el-dropdown-item :icon="CircleClose" command="closeOthers">{{ $t('app.closeOthers') }}</el-dropdown-item>

                    <el-dropdown-item :icon="CircleCloseFilled" command="closeAll">{{ $t('app.closeAll') }}</el-dropdown-item>

                </el-dropdown-menu>

            </template>

            <el-icon><arrow-down /></el-icon>

        </el-dropdown>

    </div>

</template>

<script setup lang="ts">

import { watch, onMounted, ref, computed } from 'vue'

import { useRoute, useRouter } from 'vue-router'

import store from '@/store'

import { closeAllTabs, closeOthersTabs, closeTab } from '@/utils/tabs'

import { ArrowDown, Close, CircleClose, CircleCloseFilled } from '@element-plus/icons-vue'

const route = useRoute()

const router = useRouter()

const activeTabName = ref(route.path)

const tabsStyleClass = computed(() => 'tabs-item-' + store.appStore.theme.tabsStyle)

// 是否固定

const isAffix = (tab: any) => {

    return tab.meta && tab.meta.affix

}

watch(route, () => {

    // 当前路由,添加到tabs里

    if (route.name) {

        addTab()

    }

})

onMounted(() => {

    // 初始化

    initTabs()

    addTab()

})

// 初始化固定tab

const initTabs = () => {

    const affixTabs = getAffixTabs(store.routerStore.routes)

    for (const tab of affixTabs) {

        // 需要有tab名称

        if (tab.name) {

            store.tabsStore.addView(tab)

        }

    }

}

// 获取需要固定的tabs

const getAffixTabs = (routes: any) => {

    let tabs: any[] = []

    routes.forEach((route: any) => {

        if (route.meta && route.meta.affix) {

            tabs.push({

                fullPath: route.path,

                path: route.path,

                name: route.name,

                meta: { ...route.meta }

            })

        }

        if (route.children) {

            const tempTabs = getAffixTabs(route.children)

            if (tempTabs.length >= 1) {

                tabs = [...tabs, ...tempTabs]

            }

        }

    })

    return tabs

}

// 添加tab

const addTab = () => {

    store.tabsStore.addView(route)

    store.tabsStore.addCachedView(route)

    activeTabName.value = route.path

}

// tab被选中

const tabClick = (tab: any) => {

    tab.props.name && router.push(tab.props.name)

}

// 点击关闭tab

const tabRemove = (path: string) => {

    const tab = store.tabsStore.visitedViews.filter((tab: any) => tab.path === path)

    closeTab(router, tab[0])

}

// dropdown 关闭事件

const onClose = (type: string) => {

    switch (type) {

        case 'close':

            closeTab(router, route)

            break

        case 'closeOthers':

            closeOthersTabs(router, route)

            break

        case 'closeAll':

            closeAllTabs(router, route)

            break

    }

}

</script>

<style lang="scss" scoped>

.tabs-container {

    display: flex;

    position: relative;

    z-index: 6;

    height: 40px;

    .tabs-item {

        transition: left 0.3s;

        flex-grow: 1;

        overflow: hidden;

        ::v-deep(.el-tabs__nav-prev) {

            padding: 0 10px;

            border-right: var(--el-border-color-extra-light) 1px solid;

        }

        ::v-deep(.el-tabs__nav-next) {

            padding: 0 10px;

            border-left: var(--el-border-color-extra-light) 1px solid;

        }

        ::v-deep(.is-scrollable) {

            padding: 0 32px;

        }

        ::v-deep(.el-tabs__active-bar) {

            height: 0;

        }

        ::v-deep(.el-tabs__item) {

            .is-icon-close {

                transition: none !important;

                &:hover {

                    color: var(--el-color-primary-light-9);

                    background-color: var(--el-color-primary);

                    border-radius: 50%;

                }

            }

        }

    }

}

.tabs-item-style-1 {

    ::v-deep(.el-tabs__item) {

        padding: 0 15px !important;

        border-right: var(--el-border-color-extra-light) 1px solid;

        user-select: none;

        color: #8c8c8c;

        &:hover {

            color: #444;

            background: rgba(0, 0, 0, 0.02);

        }

        &.is-active {

            color: var(--el-color-primary);

            background-color: var(--el-color-primary-light-9);

            border-bottom: var(--el-border-color-light) 2px solid;

            &::before {

                background-color: var(--el-color-primary);

            }

        }

        &::before {

            content: '';

            width: 9px;

            height: 9px;

            margin-right: 8px;

            display: inline-block;

            background-color: #ddd;

            border-radius: 50%;

        }

    }

}

.tabs-item-style-2 {

    ::v-deep(.el-tabs__item) {

        padding: 0 15px !important;

        border-right: none;

        user-select: none;

        color: #8c8c8c;

        display: inline-block;

        &:hover {

            color: #444;

            background: rgba(0, 0, 0, 0.02);

            border-bottom: var(--el-color-primary) 2px solid;

        }

        &.is-active {

            color: var(--el-color-primary) !important;

            background-color: var(--el-color-primary-light-9) !important;

            border-bottom: var(--el-color-primary) 2px solid;

            &::before {

                background-color: var(--el-color-primary);

            }

        }

    }

}

.tabs-action {

    height: 40px;

    line-height: 40px;

    box-sizing: border-box;

    padding: 0 12px;

    align-items: center;

    cursor: pointer;

    color: #666;

    border-left: var(--el-border-color-extra-light) 1px solid;

    border-bottom: var(--el-border-color-light) 2px solid;

}

</style>

<template>

    <div id="printJs-vue">

        <el-card shadow="hover" header="页面打印">

            <el-alert title="页面打印演示,使用的是 `print-js`,项目地址:https://github.com/crabbly/Print.js" type="success" :closable="false"></el-alert>

            <p style="margin-top: 10px"><el-button size="default" type="primary" @click="handlePrintJs"> 点击打印 </el-button></p>

        </el-card>

    </div>

</template>

<script lang="ts" setup>

import printJs from 'print-js'

const handlePrintJs = () => {

    printJs({

        printable: 'printJs-vue',

        type: 'html',

        css: ['//unpkg.com/element-plus/dist/index.css'],

        scanStyles: false

    })

}

</script>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值