赞
踩
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>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。