koacms(十一)后台管理Tag标签页切换

1、创建layout文件夹,在里面创建index.vue

<template>
  <div class="layout">
    <el-container style="height: 100%;">
      <el-header style="padding: 0 !important;">
        <!-- 头部 -->
        <HeadBar></HeadBar>
      </el-header>
      <el-container>
        <el-aside :width="!sidebar.opened ? '60px' : '200px'">
          <!-- 左侧区域 -->
          <SlideBar></SlideBar>
        </el-aside>
        
        <el-main style="padding: 0;">
          <!-- 内容区域 -->
          <PageTabs></PageTabs>
          <router-view v-slot="{ Component }" class="p-15 box-border" style="height: calc(100% - 41px);">
                <transition mode="fade" appear >
                    <keep-alive :max="10"> 
                        <component :is="Component" :key="openTabIndex"></component> 
                    </keep-alive>
                </transition>
            </router-view>
          
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>

<script>
import { mapState } from 'vuex' 
import HeadBar from '@/layout/components/Header.vue'
import SlideBar from '@/layout/components/SlideBar.vue' 
import PageTabs from '@/layout/components/pageTabs.vue'
export default {
  data() {
    return {
      
    }
  },
  components: {
    HeadBar,
    SlideBar, 
    PageTabs
  },
  computed: {
    ...mapState([
      'sidebar','openTabIndex'
    ]),
  },
  onLoad(){
    
  },
  methods:{

  }
};
</script>

<style>
.layout{
  height: 100%;
}
.el-aside{
    overflow: hidden !important;
}
/* 加过渡给侧边导航*/
.el-aside {
  transition: width 0.25s;
  -webkit-transition: width 0.25s;
  -moz-transition: width 0.25s;
  -webkit-transition: width 0.25s;
  -o-transition: width 0.25s;
}
.el-main{background-color: #f0f2f5;}

.fade-enter-from{
    opacity: 0;
}
.fade-enter-to{
    opacity: 1;
}
.fade-leave-from{
    opacity: 1;
}
.fade-leave-to{
    opacity: 0;
}
.fade-enter-active,
.fade-leave-active{
    transition: all 0.3s;
}
.fade-enter-active{
    transition-delay: 0.3s;
}
</style>

2、在layout文件夹里创建components,再创建Header.vue,SlideBar.vue,AppMain.vue,pageTabs.vue

// SlideBar.vue

<template>
    <div class="layout-left">
        <el-menu
            :default-active="$route.path"
            router
            :unique-opened="true"
            class="el-menu-vertical-demo"
            @open="handleOpen"
            @close="handleClose"
            background-color="#191a23"
            text-color="#fff"
            active-text-color="#fff">
            
            <template v-for="(item,index) in menuList" >
                <el-submenu v-if="item.children && item.children.length > 0" :key="index" :index='item.index'>
                    <template #title>
                        <el-icon>
                            <component :is="item.icon"></component>
                        </el-icon>
                        <span>{{ item.title }}</span>
                    </template>
                    <el-menu-item v-for="item2 in item.children" :key="item2.id" :index="item2.index">
                        <el-icon>
                            <component :is="item2.icon"></component>
                        </el-icon>
                        <span>{{ item2.title }}</span>
                    </el-menu-item>
                </el-submenu>

                <el-menu-item v-else :index="item.index" :key="index">
                    <el-icon>
                         <component :is="item.icon"></component>
                    </el-icon>
                    <span>{{ item.title }}</span>
                </el-menu-item>
            </template>

          </el-menu>
    </div>
</template>
<script>
import { mapState } from 'vuex';
  export default {
    data(){
      return {
        menuList:[
          {
            id:'1',
            title: '轮播图管理',
            icon:'el-icon-location',
            index:'/banner',
            children:[
              {
                index:'/banner/type',
                title:'轮播图分类',
                icon:''
              },
              {
                index:'/banner/page',	 
                title:'轮播图列表',
                icon:''
              },
            ]
          },
          {
            id:'2',
            title: '模型管理',
            icon:'el-icon-location',
            index:'/models',
            children:[
              {
                index:'/models/page',
                title:'模型分类',
                icon:''
              },
              {
                index:'/models/field',
                title:'模型字段',
                icon:''
              },
            ]
          },
          {
            id:'3',
            title: '栏目管理',
            icon:'el-icon-location',
            index:'/navs/page',
            children:[ ]
          },
          {
            id:'4',
            title: '内容管理',
            icon:'el-icon-location',
            index:'/content/page',
            children:[ ]
          },
          {
            id:'5',
            title: '系统管理',
            icon:'el-icon-location',
            index:'/system',
            children:[
              {
                index:'/system/user',
                title:'管理员管理',
                icon:''
              },
              {
                index:'/system/role',
                title:'角色管理',
                icon:''
              },
              {
                index:'/system/menu',
                title:'菜单管理',
                icon:''
              },
              {
                index:'/system/configs',
                title:'系统配置',
                icon:''
              },
            ]
          },
        ]
      }
    }, 
    computed: {
      ...mapState([
        'openTab'
      ]),
    },
    watch:{
      '$route'(to){
        console.log("to",to)
          //判断路由是否已经打开
          //已经打开的 ,将其置为active
          //未打开的,将其放入队列里
          let flag = false;
          for(let item of this.openTab){ 
            if(item.name === to.name){ 
              this.$store.commit('setTabsIndex',to.path)
              flag = true;
              break;
            }
          }

          if(!flag){ 
            this.$store.commit('addTabs', {route: to.path, name: to.name,title:to.meta.title,matched:to.matched});
            this.$store.commit('setTabsIndex', to.path);
          }

      }
    },
    mounted () {
      // 刷新时以当前路由做为tab加入tabs
      // 当前路由不是首页时,添加首页以及另一页到store里,并设置激活状态
      // 当当前路由是首页时,添加首页到store,并设置激活状态
    if (this.$route.path !== '/' && this.$route.path !== '/') {
        console.log('1');
        this.$store.commit('addTabs', {route: '/' , name: 'main',title:'主页',matched:[]});
        this.$store.commit('addTabs', {route: this.$route.path , name: this.$route.name,title:this.$route.meta.title,matched:this.$route.matched });
        this.$store.commit('setTabsIndex', this.$route.path);
      } else {
        console.log('2');
        this.$store.commit('addTabs', {route: '/', name: 'main',title:'主页',matched:[]});
        this.$store.commit('setTabsIndex', '/');
        this.$router.push('/');
      }
    }, 
    methods: {
      handleOpen(key, keyPath) {
        console.log(key, keyPath);
      },
      handleClose(key, keyPath) {
        console.log(key, keyPath);
      }
    }
  }
</script>
<style scoped>
    .layout-left{background-color: #191a23;height: 100%;}
    .layout-left .is-active{background-color: #1890ff;}
    .el-menu{border-right: 0px !important;}
    .el-menu-item.is-active { background-color: #1890ff !important;}
</style>
// Header.vue
<template>
    <div class="layout-header">
        <el-container style="height: 100%;">
            <el-aside :width="!sidebar.opened ? '60px' : '200px'">
            <!-- 左侧区域 -->
             <div class="logo d-flex align-center justify-center">
                网站后台
             </div>
            </el-aside>
            <el-main class="d-flex align-center justify-between " style="overflow-y:hidden !important;background-color: #ffffff;">
            <!-- 内容区域 -->
                <div class="d-flex align-center">
                    <i class="icon-btn cursor-pointer mr-20" :class="sidebar.opened ? 'el-icon-s-fold' : 'el-icon-s-unfold'" @click="changeCollapse"></i>

                    <i class="el-icon-refresh-right icon-btn cursor-pointer mr-20" @click="refresh"></i>

                    <el-breadcrumb separator="/">
                        <el-breadcrumb-item>主页</el-breadcrumb-item>
                        <el-breadcrumb-item v-for="(item,index) in breadcrumb" :key="index">{{item.meta.title}}</el-breadcrumb-item>
                    </el-breadcrumb>
                </div>
                <div class="d-flex align-center">
                    <i class="el-icon-full-screen icon-btn cursor-pointer mr-20" @click="buttoncli"></i>

                    <div class="relative mr-20">
                        <i class="el-icon-message icon-btn cursor-pointer"></i>
                        <div class="absolute right--15 top--10 f-12 bg-pink c-fff border-radius-10 d-flex align-center px-5 h-16r border-white-1">99+</div>
                    </div>

                    <el-avatar icon="el-icon-user-solid" :size="28"></el-avatar>
                    
                    <el-dropdown class="cursor-pointer ml-10 mr-20">
                        <span class="el-dropdown-link">
                            Admin<i class="el-icon-arrow-down el-icon--right"></i>
                        </span>
                        <el-dropdown-menu slot="dropdown">
                            <el-dropdown-item>
                                <i class="el-icon-user cursor-pointer"></i>个人中心
                            </el-dropdown-item>
                            <el-dropdown-item>
                                <i class="el-icon-key cursor-pointer"></i>修改密码
                            </el-dropdown-item>
                            <el-dropdown-item>
                                <i class="el-icon-switch-button cursor-pointer"></i>退出登录
                            </el-dropdown-item> 
                        </el-dropdown-menu>
                    </el-dropdown>
                </div>
            </el-main>
        </el-container>
    </div>
</template>
<script>
import { mapState } from 'vuex';  
import screenfull from 'screenfull'

export default {
  data() {
    return {
        breadcrumb:[],
        isFullscreen:false
    }
  },
  watch:{
      '$route'(to){ 
        this.breadcrumb = to.matched
      }
    },
  components: { 
  },
  computed: {
    ...mapState([
      'sidebar',
    ]),
  },
  mounted(){
     
  },
  methods:{ 
    changeCollapse(){
        this.$store.commit('setSidebar',!this.sidebar.opened)
    },
    refresh(){
        location.reload()
    },
     // 全屏
     buttoncli() { 
        screenfull.toggle() 
        this.checkFull() 
    },
    // 监控屏幕变化
    checkFull() {
        var isFull = document.fullscreenEnabled || window.fullScreen || document.webkitIsFullScreen || document.msFullscreenEnabled;
        // 是否全屏判断
        this.isFullscreen = !this.isFullscreen; 
        if (isFull === undefined) {
            isFull = false;
        }
        return isFull;
    },
  }
};
</script>
<style scoped>
    .layout-header{height: 100%;}
    .logo{ background-color: #191a23;height: 100%;color: #fff; }
    .icon-btn{ font-size: 20px;} 
</style>
// pageTabs.vue

<template>
    <div class="page-tabs bc-fff">
        <el-tabs v-model="currentTab" type="card" closable v-if="openTab.length"  @tab-click='tabClick'  @tab-remove='tabRemove'>
            <el-tab-pane :key="index" v-for="(item,index) in openTab" :label="item.title" :name="item.route" > </el-tab-pane>
        </el-tabs> 

    </div>
</template>
<script>
import { mapState } from 'vuex';
export default {
  data() {
    return {
        currentTab:''
    }
  },
  components: { 
  },
  computed: {
    ...mapState([
      'openTab','openTabIndex'
    ]),
  },
  watch:{
    openTabIndex(val){
        this.currentTab =val
    }
  },
  onLoad(){
    this.currentTab = this.openTabIndex
  },
  activated(){
    this.currentTab = this.openTabIndex
  },
  methods:{
    //tab标签点击时,切换相应的路由
    tabClick(tab){
      console.log("tab",tab);
      console.log('active',this.currentTab);

      this.$router.push({path: this.currentTab});
    },
    //移除tab标签
    tabRemove(targetName){
      console.log("tabRemove",targetName);
      //首页不删
      if(targetName == '/'){
        return
      }
      this.$store.commit('deleteTabs', targetName);
      if (this.currentTab === targetName) {
        // 设置当前激活的路由
        if (this.openTab && this.openTab.length >= 1) {
          console.log('=============',this.openTab[this.openTab.length-1].route)
          this.$store.commit('setTabsIndex', this.openTab[this.openTab.length-1].route);
          this.$router.push({path: this.currentTab});
        } else {
          this.$router.push({path: '/'});
        }
      }
    }

  }
};
</script>
<style scoped> 
    .page-tabs{height: 40px;overflow: hidden;border-top:1px solid #E4E7ED}
   .el-tabs__content,.el-tabs--border-card>.el-tabs__content,.el-tabs__header{padding: 0 !important;}
   
</style>
<style>
.el-tabs--border-card .el-tabs__header,.el-tabs__nav-wrap,.el-tabs__nav-scroll{background-color: #fff !important;}
.el-tabs--card>.el-tabs__header .el-tabs__item{border-left: 0px;position: relative;}
.el-tabs--card>.el-tabs__header .el-tabs__nav{border: 0px}
 .el-tabs__item.is-active {
    color: #1890ff;  background: #e6f7ff;
}
.el-tabs--bottom .el-tabs--left>.el-tabs__header .el-tabs__item, .el-tabs--bottom .el-tabs--right>.el-tabs__header .el-tabs__item, .el-tabs--bottom.el-tabs--border-card>.el-tabs__header .el-tabs__item, .el-tabs--bottom.el-tabs--card>.el-tabs__header .el-tabs__item, .el-tabs--top .el-tabs--left>.el-tabs__header .el-tabs__item, .el-tabs--top .el-tabs--right>.el-tabs__header .el-tabs__item, .el-tabs--top.el-tabs--border-card>.el-tabs__header .el-tabs__item, .el-tabs--top.el-tabs--card>.el-tabs__header .el-tabs__item{padding-left: 15px  !important;}
.el-tabs--top .el-tabs__item.is-top, .el-tabs--top .el-tabs__item.is-bottom, .el-tabs--bottom .el-tabs__item.is-top, .el-tabs--bottom .el-tabs__item.is-bottom{padding-right: 15px  !important;}
.ele-admin-tabs .el-tabs__item.is-closable{padding-right: 8px !important;}
.el-cascader-menu:last-child .el-cascader-node, .el-tabs--bottom .el-tabs--left>.el-tabs__header .el-tabs__item:last-child, .el-tabs--bottom .el-tabs--right>.el-tabs__header .el-tabs__item:last-child, .el-tabs--bottom.el-tabs--border-card>.el-tabs__header .el-tabs__item:last-child, .el-tabs--bottom.el-tabs--card>.el-tabs__header .el-tabs__item:last-child, .el-tabs--top .el-tabs--left>.el-tabs__header .el-tabs__item:last-child, .el-tabs--top .el-tabs--right>.el-tabs__header .el-tabs__item:last-child, .el-tabs--top.el-tabs--border-card>.el-tabs__header .el-tabs__item:last-child, .el-tabs--top.el-tabs--card>.el-tabs__header .el-tabs__item:last-child{padding-right: 8px !important;}

.el-tabs--top .el-tabs__item.is-top::after, .el-tabs--top .el-tabs__item.is-bottom::after, .el-tabs--bottom .el-tabs__item.is-top::after, .el-tabs--bottom .el-tabs__item.is-bottom::after{
    content: "";
    width: 0;
    height: 2px;
    background: #1890ff;
    position: absolute;
    bottom: 0;
    left: 0;
} 
.el-tabs--top .el-tabs__item.is-top.is-active::after, .el-tabs--top .el-tabs__item.is-bottom.is-active::after, .el-tabs--bottom .el-tabs__item.is-top.is-active::after, .el-tabs--bottom .el-tabs__item.is-bottom.is-active::after{ width: 100%; } 
</style>

3、新建store文件夹,然后新建index.js

/**
 * vuex状态管理
 */
import Vue from 'vue';
import Vuex from 'vuex';
 
Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    sliderBar: 200,
    sidebar:{
      opened: true
    },
    openTab:[], //tab标签页
    openTabIndex:'/' //激活状态
  },
  mutations: {
    setSidebar(state, data) { 
      state.sidebar.opened = data;
    },
    // 添加tabs
    addTabs (state, data) {
      state.openTab.push(data);
      console.log('state.openTab',state.openTab)
    },
    // 删除tabs
    deleteTabs (state, route) {
      let index = 0;
      for (let option of state.openTab) {
        if (option.route === route) {
          break;
        }
        index++;
      }
      state.openTab.splice(index, 1);
    },
    // 设置当前激活的tab
    setTabsIndex (state, index) {
      state.openTabIndex = index;
    }, 
  },
  actions: {
    
  },  
  getters:{

  }
});

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值