quality_frontend/platform/base/src/layout/Tabs/index.vue

331 lines
8.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="tabs">
<el-scrollbar
class="scroll-container tags-view-container"
ref="scrollbarDom"
@wheel.native.prevent="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">
<el-dropdown placement="bottom">
<div class="el-dropdown-link">
<i class="el-icon-arrow-down el-icon--right"></i>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item icon="el-icon-refresh-left" @click="pageReload">{{
$t("message.system.tab.reload")
}}</el-dropdown-item>
<el-dropdown-item
icon="el-icon-circle-close"
:disabled="currentDisabled"
@click="closeCurrentRoute"
>{{ $t("message.system.tab.closeCurrent") }}</el-dropdown-item
>
<el-dropdown-item
icon="el-icon-circle-close"
:disabled="menuList.length < 3"
@click="closeOtherRoute"
>{{ $t("message.system.tab.closeOther") }}</el-dropdown-item
>
<el-dropdown-item
icon="el-icon-circle-close"
:disabled="menuList.length <= 1"
@click="closeAllRoute"
>{{ $t("message.system.tab.closeAll") }}</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-tooltip
class="item"
effect="dark"
:content="
contentFullScreen
? $t('message.system.fullScreenBack')
: $t('message.system.fullScreen')
"
placement="bottom"
>
<i class="el-icon-full-screen" @click="onFullscreen"></i>
</el-tooltip>
</div>
</div>
</template>
<script lang="ts">
import type { Ref } from "vue";
import Item from "./item.vue";
import {
defineComponent,
computed,
unref,
watch,
reactive,
ref,
nextTick,
} from "vue";
import { useStore } from "vuex";
import { useRoute, useRouter } from "vue-router";
import tabsHook from "./tabsHook";
interface ElScrollbar {
scrollbar: HTMLDivElement;
[propName: string]: any;
}
export default defineComponent({
components: {
Item,
},
setup() {
const store = useStore();
const route = useRoute();
const router = useRouter();
const scrollbarDom: Ref<ElScrollbar | null> = ref(null);
const allRoutes = router.options.routes;
const defaultMenu = {
path: "/workbench",
meta: { title: "message.menu.workbench.index", hideClose: true },
};
const contentFullScreen = computed(() => store.state.app.contentFullScreen);
const currentDisabled = computed(() => route.path === defaultMenu.path);
const scrollWrapper = computed(() => {
if (scrollbarDom.value) return scrollbarDom.value.$refs.wrap;
});
let activeMenu: any = 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: any =
route.matched[route.matched.length - 1].instances.default;
self.handleReload();
}
// 关闭当前标签,首页不关闭
function closeCurrentRoute() {
if (route.path !== defaultMenu.path) {
delMenu(route);
}
}
// 关闭除了当前标签之外的所有标签
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: any) {
let { path, meta, name } = menu;
if (meta.hideTabs) {
return;
}
let hasMenu = menuList.value.some((obj: any) => {
return obj.path === path;
});
if (!hasMenu) {
menuList.value.push({
path,
meta,
name,
});
}
}
// 删除菜单项
function delMenu(menu: any) {
let index = 0;
if (!menu.meta.hideClose) {
if (menu.meta.cache && menu.name) {
store.commit("keepAlive/delKeepAliveComponentsName", menu.name);
}
index = menuList.value.findIndex(
(item: any) => item.path === menu.path
);
menuList.value.splice(index, 1);
}
if (menu.path === activeMenu.path) {
index - 1 > 0
? router.push(menuList.value[index - 1].path)
: router.push(defaultMenu.path);
}
}
// 初始化activeMenu
function initMenu(menu: object) {
activeMenu = menu;
nextTick(() => {
setPosition();
});
}
// 设置当前滚动条应该在的位置
function setPosition() {
if (scrollbarDom.value) {
const domBox = {
scrollbar: scrollbarDom.value?.scrollbar?.querySelector(
".el-scrollbar__wrap "
) as HTMLDivElement,
activeDom: scrollbarDom.value?.scrollbar?.querySelector(
".active"
) as HTMLDivElement,
activeFather: scrollbarDom.value?.scrollbar?.querySelector(
".el-scrollbar__view"
) as HTMLDivElement,
};
for (let i in domBox) {
if (!domBox[i]) {
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: any[] = [];
menuList.value.forEach((menu: any) => {
menu.meta &&
menu.meta.cache &&
menu.name &&
keepAliveNames.push(menu.name);
});
store.commit("keepAlive/setKeepAliveComponentsName", keepAliveNames);
}
function handleScroll(e: WheelEvent & { wheelDelta: number }) {
const eventDelta = e.wheelDelta || -e.deltaY * 40;
const $scrollWrapper = scrollWrapper.value;
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4;
}
// 初始化时调用1. 新增菜单 2. 初始化activeMenu
addMenu(route);
initMenu(route);
return {
contentFullScreen,
onFullscreen,
pageReload,
scrollbarDom,
// 菜单相关
menuList,
activeMenu,
delMenu,
closeCurrentRoute,
closeOtherRoute,
closeAllRoute,
currentDisabled,
handleScroll,
};
},
});
</script>
<style lang="scss" scoped>
.tabs {
display: flex;
justify-content: space-between;
align-items: self-end;
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, 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;
}
.el-scrollbar__wrap {
height: 33px;
}
}
}
.tags-view-container {
height: 55px;
align-items: self-end;
flex: 1;
width: 100%;
display: flex;
}
.el-icon-full-screen {
cursor: pointer;
&:hover {
background: rgba(0, 0, 0, 0.025);
}
&:focus {
outline: none;
}
}
</style>