|
@@ -0,0 +1,293 @@
|
|
|
+<template>
|
|
|
+ <div id="tabs-bar-container" class="tabs-bar-container">
|
|
|
+ <el-tabs
|
|
|
+ v-model="tabActive"
|
|
|
+ type="card"
|
|
|
+ class="tabs-content"
|
|
|
+ @tab-click="handleTabClick"
|
|
|
+ @tab-remove="handleTabRemove"
|
|
|
+ >
|
|
|
+ <el-tab-pane
|
|
|
+ v-for="item in visitedRoutes"
|
|
|
+ :key="item.path"
|
|
|
+ :label="item.meta.title"
|
|
|
+ :name="item.path"
|
|
|
+ :closable="!isAffix(item)"
|
|
|
+ ></el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+
|
|
|
+ <el-dropdown @command="handleCommand">
|
|
|
+ <span style="cursor: pointer">
|
|
|
+ 更多操作
|
|
|
+ <i class="el-icon-arrow-down el-icon--right"></i>
|
|
|
+ </span>
|
|
|
+ <el-dropdown-menu slot="dropdown" class="tabs-more">
|
|
|
+ <el-dropdown-item command="closeOtherstabs">
|
|
|
+ <vab-icon :icon="['fas', 'times-circle']" />
|
|
|
+ 关闭其他
|
|
|
+ </el-dropdown-item>
|
|
|
+ <el-dropdown-item command="closeLefttabs">
|
|
|
+ <vab-icon :icon="['fas', 'arrow-alt-circle-left']"></vab-icon>
|
|
|
+ 关闭左侧
|
|
|
+ </el-dropdown-item>
|
|
|
+ <el-dropdown-item command="closeRighttabs">
|
|
|
+ <vab-icon :icon="['fas', 'arrow-alt-circle-right']"></vab-icon>
|
|
|
+ 关闭右侧
|
|
|
+ </el-dropdown-item>
|
|
|
+ <el-dropdown-item command="closeAlltabs">
|
|
|
+ <vab-icon :icon="['fas', 'ban']"></vab-icon>
|
|
|
+ 关闭全部
|
|
|
+ </el-dropdown-item>
|
|
|
+ </el-dropdown-menu>
|
|
|
+ </el-dropdown>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+ import path from 'path'
|
|
|
+ import { mapGetters } from 'vuex'
|
|
|
+
|
|
|
+ export default {
|
|
|
+ name: 'VabTabsBar',
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ affixtabs: [],
|
|
|
+ tabActive: '',
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ computed: {
|
|
|
+ ...mapGetters({
|
|
|
+ visitedRoutes: 'tabsBar/visitedRoutes',
|
|
|
+ routes: 'routes/routes',
|
|
|
+ }),
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ $route: {
|
|
|
+ handler(route) {
|
|
|
+ this.inittabs()
|
|
|
+ this.addtabs()
|
|
|
+ let tabActive = ''
|
|
|
+ this.visitedRoutes.forEach((item, index) => {
|
|
|
+ if (item.path === this.$route.path) {
|
|
|
+ tabActive = item.path
|
|
|
+ }
|
|
|
+ })
|
|
|
+ this.tabActive = tabActive
|
|
|
+ },
|
|
|
+ immediate: true,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ //console.log(this.visitedRoutes);
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ async handleTabRemove(tabActive) {
|
|
|
+ let view
|
|
|
+ this.visitedRoutes.forEach((item, index) => {
|
|
|
+ if (tabActive == item.path) {
|
|
|
+ view = item
|
|
|
+ }
|
|
|
+ })
|
|
|
+ const { visitedRoutes } = await this.$store.dispatch(
|
|
|
+ 'tabsBar/delRoute',
|
|
|
+ view
|
|
|
+ )
|
|
|
+ if (this.isActive(view)) {
|
|
|
+ this.toLastTag(visitedRoutes, view)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleTabClick(tab) {
|
|
|
+ const route = this.visitedRoutes.filter((item, index) => {
|
|
|
+ if (tab.index == index) return item
|
|
|
+ })[0]
|
|
|
+ if (this.$route.path !== route.path) {
|
|
|
+ this.$router.push({
|
|
|
+ path: route.path,
|
|
|
+ query: route.query,
|
|
|
+ fullPath: route.fullPath,
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ isActive(route) {
|
|
|
+ return route.path === this.$route.path
|
|
|
+ },
|
|
|
+ isAffix(tag) {
|
|
|
+ return tag.meta && tag.meta.affix
|
|
|
+ },
|
|
|
+ filterAffixtabs(routes, basePath = '/') {
|
|
|
+ let tabs = []
|
|
|
+ routes.forEach((route) => {
|
|
|
+ if (route.meta && route.meta.affix) {
|
|
|
+ const tagPath = path.resolve(basePath, route.path)
|
|
|
+ tabs.push({
|
|
|
+ fullPath: tagPath,
|
|
|
+ path: tagPath,
|
|
|
+ name: route.name,
|
|
|
+ meta: { ...route.meta },
|
|
|
+ })
|
|
|
+ }
|
|
|
+ if (route.children) {
|
|
|
+ const temptabs = this.filterAffixtabs(route.children, route.path)
|
|
|
+ if (temptabs.length >= 1) {
|
|
|
+ tabs = [...tabs, ...temptabs]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return tabs
|
|
|
+ },
|
|
|
+ inittabs() {
|
|
|
+ const affixtabs = (this.affixtabs = this.filterAffixtabs(this.routes))
|
|
|
+ for (const tag of affixtabs) {
|
|
|
+ if (tag.name) {
|
|
|
+ this.$store.dispatch('tabsBar/addVisitedRoute', tag)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ addtabs() {
|
|
|
+ const { name } = this.$route
|
|
|
+ if (name) {
|
|
|
+ this.$store.dispatch('tabsBar/addVisitedRoute', this.$route)
|
|
|
+ }
|
|
|
+ return false
|
|
|
+ },
|
|
|
+ handleCommand(command) {
|
|
|
+ switch (command) {
|
|
|
+ case 'refreshRoute':
|
|
|
+ this.refreshRoute()
|
|
|
+ break
|
|
|
+ case 'closeOtherstabs':
|
|
|
+ this.closeOtherstabs()
|
|
|
+ break
|
|
|
+ case 'closeLefttabs':
|
|
|
+ this.closeLefttabs()
|
|
|
+ break
|
|
|
+ case 'closeRighttabs':
|
|
|
+ this.closeRighttabs()
|
|
|
+ break
|
|
|
+ case 'closeAlltabs':
|
|
|
+ this.closeAlltabs()
|
|
|
+ break
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async refreshRoute() {
|
|
|
+ this.$baseEventBus.$emit('reloadrouter-view')
|
|
|
+ },
|
|
|
+ async closeSelectedTag(view) {
|
|
|
+ const { visitedRoutes } = await this.$store.dispatch(
|
|
|
+ 'tabsBar/delRoute',
|
|
|
+ view
|
|
|
+ )
|
|
|
+ if (this.isActive(view)) {
|
|
|
+ this.toLastTag(visitedRoutes, view)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async closeOtherstabs() {
|
|
|
+ const view = await this.toThisTag()
|
|
|
+ await this.$store.dispatch('tabsBar/delOthersRoutes', view)
|
|
|
+ },
|
|
|
+ async closeLefttabs() {
|
|
|
+ const view = await this.toThisTag()
|
|
|
+ await this.$store.dispatch('tabsBar/delLeftRoutes', view)
|
|
|
+ },
|
|
|
+ async closeRighttabs() {
|
|
|
+ const view = await this.toThisTag()
|
|
|
+ await this.$store.dispatch('tabsBar/delRightRoutes', view)
|
|
|
+ },
|
|
|
+ async closeAlltabs() {
|
|
|
+ const view = await this.toThisTag()
|
|
|
+ const { visitedRoutes } = await this.$store.dispatch(
|
|
|
+ 'tabsBar/delAllRoutes'
|
|
|
+ )
|
|
|
+ if (this.affixtabs.some((tag) => tag.path === view.path)) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.toLastTag(visitedRoutes, view)
|
|
|
+ },
|
|
|
+ toLastTag(visitedRoutes, view) {
|
|
|
+ const latestView = visitedRoutes.slice(-1)[0]
|
|
|
+ if (latestView) {
|
|
|
+ this.$router.push(latestView)
|
|
|
+ } else {
|
|
|
+ this.$router.push('/')
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async toThisTag() {
|
|
|
+ const view = this.visitedRoutes.filter((item, index) => {
|
|
|
+ if (item.path === this.$route.fullPath) {
|
|
|
+ return item
|
|
|
+ }
|
|
|
+ })[0]
|
|
|
+ if (this.$route.path !== view.path) this.$router.push(view)
|
|
|
+ return view
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+ .tabs-bar-container {
|
|
|
+ position: relative;
|
|
|
+ box-sizing: border-box;
|
|
|
+ display: flex;
|
|
|
+ align-content: center;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ height: $base-tabs-bar-height;
|
|
|
+ padding-right: $base-padding;
|
|
|
+ padding-left: $base-padding;
|
|
|
+ user-select: none;
|
|
|
+ background: $base-color-white;
|
|
|
+ border-top: 1px solid #f6f6f6;
|
|
|
+
|
|
|
+ ::v-deep {
|
|
|
+ .fold-unfold {
|
|
|
+ margin-right: $base-padding;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .tabs-content {
|
|
|
+ width: calc(100% - 90px);
|
|
|
+ height: $base-tag-item-height;
|
|
|
+
|
|
|
+ ::v-deep {
|
|
|
+ .el-tabs__nav-next,
|
|
|
+ .el-tabs__nav-prev {
|
|
|
+ height: $base-tag-item-height;
|
|
|
+ line-height: $base-tag-item-height;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-tabs__header {
|
|
|
+ border-bottom: 0;
|
|
|
+
|
|
|
+ .el-tabs__nav {
|
|
|
+ border: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-tabs__item {
|
|
|
+ box-sizing: border-box;
|
|
|
+ height: $base-tag-item-height;
|
|
|
+ margin-right: 5px;
|
|
|
+ line-height: $base-tag-item-height;
|
|
|
+ border: 1px solid $base-border-color;
|
|
|
+ border-radius: $base-border-radius;
|
|
|
+ transition: padding 0.3s cubic-bezier(0.645, 0.045, 0.355, 1) !important;
|
|
|
+
|
|
|
+ &.is-active {
|
|
|
+ border: 1px solid $base-color-blue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .more {
|
|
|
+ display: flex;
|
|
|
+ align-content: center;
|
|
|
+ align-items: center;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+</style>
|