实现思路及细节

1、利用前面博客所讲的Vuex的知识;定义几个变量

Options:存放tab页对象的容器(主要是路由路径以及tab页的名字)

activeIndex:被激活的tab页路由路径

showName:tab页的标题

Role:用来区分是否是因为左侧菜单被点击造成的路由路径发生改变;

是:pass;不是:nopass

2、左侧导航菜单绑定点击事件

将被点击的菜单名称存放到Vuex中,供路由路径变化监听时,tab页标题显示;

标记一下role为pass,到时新增tab页的时候需要作为判断依据

3、右侧对tab页进行操作

Tab页的点击(切换到被点击的tab页完成路由跳转;标记一下role为nopass,到时新增tab页的时候需要作为判断依据;);

Tab页的移除(删除指定的tab页对象;如果删除的tab页对象处于选中状态,需要将选中状态的下标移到最后一个,因为原来选中的tab页已经删除了;标记一下role为nopass,到时新增tab页的时候需要作为判断依据;))

4、监听路由路径变化

点亮已经存在的tab页(Vuex中showName与option中的哪个tab页对象的name相同,那么就点亮哪一个)

新增tab页(首先路由路径的变化是因为左侧栏的点击,其次要option中不存在的tab页对象)

效果图

State.js

  1. export default{
  2.  
  3. resturantName: '飞歌餐馆',
  4. jwt:'',
  5. options: [],//存放tab页的容器
  6. activeIndex: '',//激活的tab页路由路径
  7. showName:'show',//tab页的标题
  8. role:""//用来区分是否是因为左侧菜单被点击造成的路由路径发生改变,是:pass;不是:nopass
  9. }

Mutations.js

  1. export default {
  2. setResturantName: (state, payload) => {
  3. state.resturantName = payload.resturantName;
  4. },
  5. setJwt: (state, payload) => {
  6. state.jwt = payload.jwt;
  7. },
  8. // 添加tabs(data包含了路由路径跟tab页名字)
  9. add_tabs(state, data) {
  10. this.state.options.push(data);
  11. },
  12. // 删除tabs (route是路由路径)
  13. delete_tabs(state, route) {
  14. let index = ;
  15. for (let option of state.options) {
  16. if (option.route === route) {
  17. break;
  18. }
  19. index++;
  20. }
  21. this.state.options.splice(index, ); //删除options里面下标为Index的一个数
  22. },
  23. // 设置当前激活的tab
  24. set_active_index(state, index) {
  25. this.state.activeIndex = index;
  26. },
  27. //设置tab页显示标题
  28. set_showName(state, name) {
  29. this.state.showName = name;
  30. },
  31. set_role(state, role) {
  32. this.state.role = role;
  33. }
  34. }

Getters.js

  1. export default{
  2. getResturantName:(state)=>{
  3. return state.resturantName;
  4. },
  5. getJwt:(state)=>{
  6. return state.jwt;
  7. },
  8. getResturantName: (state) => {
  9. return state.resturantName;
  10. },
  11. getJwt: (state) => {
  12. return state.jwt;
  13. },
  14. getShowName:(state) => {
  15. return state.showName;
  16. },
  17. getOptions:(state) => {
  18. return state.options;
  19. },
  20. getRole:(state) =>{
  21. return state.role;
  22. }
  23. }

LeftNav.vue

  1. <template>
  2. <el-menu router :default-active="$route.path" default-active="" class="el-menu-vertical-demo" background-color="#334157"
  3. text-color="#fff" active-text-color="#ffd04b" :collapse="collapsed">
  4. <!-- <el-menu default-active="" :collapse="collapsed" collapse-transition router :default-active="$route.path" unique-opened class="el-menu-vertical-demo" background-color="#334157" text-color="#fff" active-text-color="#ffd04b"> -->
  5. <div class="logobox">
  6. <img class="logoimg" src="../assets/img/logo.png" alt="">
  7. </div>
  8. <el-submenu :index="'id_'+m.treeNodeId" v-for="m in menus">
  9. <template slot="title">
  10. <i :class="m.icon"></i>
  11. <span>{{m.treeNodeName}}</span>
  12. </template>
  13. <el-menu-item :key="'id_'+m2.treeNodeId" :index="m2.url" @click="showName(m2.treeNodeName)" v-for="m2 in m.children">
  14. <i :class="m2.icon"></i>
  15. <span>{{m2.treeNodeName}}</span>
  16. </el-menu-item>
  17. </el-submenu>
  18. </el-menu>
  19. </template>
  20. <script>
  21. export default {
  22. data() {
  23. return {
  24. collapsed: false,
  25. menus: []
  26. }
  27. },
  28. created() {
  29. this.$root.Bus.$on('collapsed-side', (v) => {
  30. this.collapsed = v;
  31. })
  32.  
  33. let url = this.axios.urls.SYSTEM_MENU_TREE;
  34. // let url = 'http://localhost:8080/T216_SSH/vue/userAction_login.action';
  35. this.axios.post(url, {}).then((response) => {
  36. console.log(response);
  37. this.menus = response.data.result;
  38. }).catch(function(error) {
  39. console.log(error);
  40. });
  41. },
  42. methods: {
  43. showName(name) {
  44. // 把菜单名称放进去,当成tab页的名称
  45. this.$store.commit('set_showName', name)
  46. this.$store.commit('set_role', "pass");
  47. }
  48. }
  49. }
  50. </script>
  51. <style>
  52. .el-menu-vertical-demo:not(.el-menu--collapse) {
  53. width: 240px;
  54. min-height: 400px;
  55. }
  56.  
  57. .el-menu-vertical-demo:not(.el-menu--collapse) {
  58. border: none;
  59. text-align: left;
  60. }
  61.  
  62. .el-menu-item-group__title {
  63. padding: 0px;
  64. }
  65.  
  66. .el-menu-bg {
  67. background-color: #1f2d3d !important;
  68. }
  69.  
  70. .el-menu {
  71. border: none;
  72. }
  73.  
  74. .logobox {
  75. height: 40px;
  76. line-height: 40px;
  77. color: #9d9d9d;
  78. font-size: 20px;
  79. text-align: center;
  80. padding: 20px 0px;
  81. }
  82.  
  83. .logoimg {
  84. height: 40px;
  85. }
  86. </style>

AppMain.vue

  1. <template>
  2. <el-container class="main-container">
  3. <el-aside v-bind:class="asideClass">
  4. <LeftNav></LeftNav>
  5. </el-aside>
  6. <el-container>
  7. <el-header class="main-header">
  8. <TopNav></TopNav>
  9. </el-header>
  10. <div class="template-tabs">
  11. <el-tabs v-model="activeIndex" type="border-card" closable @tab-click="tabClick" @tab-remove="tabRemove">
  12. <el-tab-pane :key="item.name" v-for="(item, index) in options" :label="item.name" :name="item.route">
  13. </el-tab-pane>
  14. </el-tabs>
  15. </div>
  16. <el-main class="main-center">
  17. <router-view></router-view>
  18. </el-main>
  19. </el-container>
  20. </el-container>
  21. </template>
  22.  
  23. <script>
  24. // 导入组件
  25. import TopNav from '@/components/TopNav.vue'
  26. import LeftNav from '@/components/LeftNav.vue'
  27.  
  28. // 导出模块
  29. export default {
  30. data(){
  31. return {
  32. asideClass : 'main-aside'
  33. }
  34. },
  35. components:{
  36. TopNav,LeftNav
  37. },
  38. created() {
  39. this.$root.Bus.$on('collapsed-side',(v)=>{
  40. this.asideClass = v ? 'main-aside-collapsed':'main-aside';
  41. })
  42. },
  43. methods: {
  44. // tab切换时,动态的切换路由
  45. tabClick(tab) {
  46. // v-model="activeIndex"是路由路径
  47. let path = this.activeIndex;
  48. this.$router.push({ path: path });
  49. this.$store.commit('set_role',"nopass");
  50. },
  51. tabRemove(targetName) {
  52. // console.log(targetName);targetName是路由路径
  53. this.$store.commit('set_role',"nopass");
  54. // let tabs = this.editableTabs;
  55. this.$store.commit('delete_tabs', targetName);
  56. // 如果激活tab页被关闭,那么需要激活别的tab页,最后一个tab页被关闭,那么跳转主界面
  57. if (this.activeIndex === targetName) {
  58. // 设置当前激活的路由
  59. if (this.options && this.options.length >= ) {
  60. this.$store.commit('set_active_index', this.options[this.options.length - ].route);
  61. this.$router.push({ path: this.activeIndex });
  62. }
  63. else {
  64. this.$router.push({ path: '/AppMain' });
  65. }
  66. }
  67. }
  68. },
  69. watch: {
  70. '$route'(to) {
  71. // 只要路由发生改变,就会触发此事件(点击左侧菜单时会触发,删除右侧tab页会触发,切换右侧已存在的tab页会触发)
  72. let role=this.$store.state.role;
  73. let showName=this.$store.getters.getShowName
  74. let flag = false;//判断是否页面中是否已经存在该路由下的tab页
  75. //options记录当前页面中已存在的tab页
  76. for (let option of this.options) {
  77. //用名称匹配,如果存在即将对应的tab页设置为active显示桌面前端
  78. if (option.name === showName) {
  79. flag = true;
  80. this.$store.commit('set_active_index', to.path);
  81. break;
  82. }
  83. }
  84. //如果不存在,则新增tab页,再将新增的tab页设置为active显示在桌面前端
  85. // if(role!='nopass'){}
  86. if(role=='pass'){
  87. if (!flag) {
  88. this.$store.commit('add_tabs', { route: to.path, name: showName});
  89. this.$store.commit('set_active_index', to.path);
  90. }
  91. }
  92. }
  93. },
  94. computed: {
  95. options() {
  96. return this.$store.state.options;
  97. },
  98. //动态设置及获取当前激活的tab页
  99. activeIndex: {
  100. get() {
  101. return this.$store.state.activeIndex;
  102. },
  103. set(val) {
  104. this.$store.commit('set_active_index', val);
  105. }
  106. }
  107. }
  108. };
  109. </script>
  110. <style type="text/css">
  111. .el-tabs--border-card>.el-tabs__content {
  112. padding: 0px;
  113. }
  114. </style>
  115. <style scoped>
  116. .main-container {
  117. height: %;
  118. width: %;
  119. box-sizing: border-box;
  120. }
  121.  
  122. .main-aside-collapsed {
  123. /* 在CSS中,通过对某一样式声明! important ,可以更改默认的CSS样式优先级规则,使该条样式属性声明具有最高优先级 */
  124. width: 64px !important;
  125. height: %;
  126. background-color: #;
  127. margin: 0px;
  128. }
  129.  
  130. .main-aside {
  131. width: 240px !important;
  132. height: %;
  133. background-color: #;
  134. margin: 0px;
  135. }
  136.  
  137. .main-header,
  138. .main-center {
  139. padding: 0px;
  140. border-left: 2px solid #;
  141. }
  142. </style>

子tab页

添加一个子tab页数据

comment.vue

  1. <template>
  2. <div>
  3. <el-tabs :tab-position="tabPosition" style="height: 200px;">
  4. <el-tab-pane label="游客评论">游客评论管理</el-tab-pane>
  5. <el-tab-pane label="普通会员评论">普通会员评论管理</el-tab-pane>
  6. <el-tab-pane label="VIP会员评论">VIP会员评论管理</el-tab-pane>
  7. <el-tab-pane label="SVIP会员评论">SVIP会员评论管理</el-tab-pane>
  8. </el-tabs>
  9. </div>
  10. </template>
  11.  
  12. <script>
  13. export default {
  14. data() {
  15. return {
  16. tabPosition: '评论管理'
  17. };
  18. }
  19. }
  20. </script>
  21.  
  22. <style>
  23.  
  24. </style>

index.js配置

  1. import Vue from 'vue'
  2. import Router from 'vue-router'
  3. import HelloWorld from '@/components/HelloWorld'
  4. import Login from '@/views/Login'
  5. import Reg from '@/views/Reg'
  6. import AppMain from '@/components/AppMain'
  7. import LeftNav from '@/components/LeftNav'
  8. import TopNav from '@/components/TopNav'
  9. import Articles from '@/views/sys/Articles'
  10. import VuexPage1 from '@/views/sys/VuexPage1'
  11. import VuexPage2 from '@/views/sys/VuexPage2'
  12. import comment from '@/views/sys/comment'
  13.  
  14. Vue.use(Router)
  15.  
  16. export default new Router({
  17. routes: [{
  18. path: '/',
  19. name: 'Login',
  20. component: Login
  21. },
  22. {
  23. path: '/Login',
  24. name: 'Login',
  25. component: Login
  26. },
  27. {
  28. path: '/Reg',
  29. name: 'Reg',
  30. component: Reg
  31. },
  32. {
  33. path: '/AppMain',
  34. name: 'AppMain',
  35. component: AppMain,
  36. children: [{
  37. path: '/LeftNav',
  38. name: 'LeftNav',
  39. component: LeftNav
  40. },
  41. {
  42. path: '/TopNav',
  43. name: 'TopNav',
  44. component: TopNav
  45. },
  46. {
  47. path: '/sys/Articles',
  48. name: 'Articles',
  49. component: Articles
  50. },
  51. {
  52. path: '/sys/VuexPage1',
  53. name: 'VuexPage1',
  54. component: VuexPage1
  55. },
  56. {
  57. path: '/sys/VuexPage2',
  58. name: 'VuexPage2',
  59. component: VuexPage2
  60. },
  61. {
  62. path: '/sys/comment',
  63. name: 'comment',
  64. component: comment
  65. }
  66. ]
  67. }
  68.  
  69. ]
  70. })

到这里就结束了

SPA项目开发之tab页实现的更多相关文章

  1. SPA项目开发之CRUD+表单验证

    表单验证 Form组件提供了表单验证的功能,只需要通过 rules 属性传入约定的验证规则, 并将Form-Item的prop属性设置为需校验的字段名即可 <el-form-item label ...

  2. vue项目中的tab页实现

    //需要自己弄雪碧图 <template> <div class="tab" id="tab"> <router-link to= ...

  3. 项目开发之package.json

    Name 必须字段. 提示: 不要在name中包含js, node字样: 这个名字不能以点号或下划线开头: 这个名字不能包含有大写字母: 这个名字可能在require()方法中被调用,所以应该尽可能短 ...

  4. vue项目开发之v-for列表渲染的坑

    不知道大家在用vue开发的过程中有没有遇到过在使用v-for的时候会出现大片的黄色警告,比如下图: 其实这是因为没有写key的原因 :key是为vue的响应式渲染提供方法,在列表中单条数据改变的情况下 ...

  5. 项目开发之git配置

    1.本地安装git配置 安装步骤,这里不详细介绍,软件下载然后安装即可. 查看git安装版本 #git --version 2.git密钥生成 ssh-keygen -t rsa -C "f ...

  6. iOS项目开发之Socket编程

    有一段时间没有认真总结和写博客了 前段时间找工作.进入工作阶段.比较少静下来认真总结,现在静下心来总结一下最近的一些心得 前言 AsyncSocket介绍 AsyncSocket详解 AsyncSoc ...

  7. 微信小程序开发之tab导航栏

    实现功能: 点击不同的tab导航,筛选数据 UI:   js: data:{ navbar: ['半月维保', '季度维保', '半年维保',"年度维保"],    //count ...

  8. Python项目开发之CMDB理解与分析

    CMDB的由来--ITIL ITIL就是IT基础架构库(Information Technology Infrastructure Library, ITIL,信息技术基础架构库),由英国政府部门CC ...

  9. 项目开发之UML之初识

随机推荐

  1. Path Manipulation 路径操作

  2. DataGridView右键菜单自定义显示及隐藏列

    WinForm程序中表单的列可自定义显示及隐藏,是一种常见的功能,对于用户体验来说是非常好的.笔者经过一段时间的摸索,终于实现了自己想要的功能及效果,现记录一下过程: 1.新建一个自定义控件,命名为: ...

  3. druid链接数据库

    所用jar包 commons-beanutils-1.8.0.jarcommons-logging-1.1.3.jardruid-1.0.9.jarmysql-connector-java-5.1.1 ...

  4. springboot传值踩坑

    由于我现在写的项目都是前后端分离的,前端用的是vue,后端springboot,于是前后端传值的问题就是一个比较重要的问题,为此我还特意去学了一下vue的传值,其实就是用一个axios组件,其实就是基 ...

  5. arcgis api 4.x for js 结合 Echarts4 实现统计图(附源码下载)

    前言 关于本篇功能实现用到的 api 涉及类看不懂的,请参照 esri 官网的 arcgis api 4.x for js:esri 官网 api,里面详细的介绍 arcgis api 4.x 各个类 ...

  6. [PHP] 接口增加recaptcha行为验证

    需要先翻墙创建一个谷歌账户和创建recaptcha验证的网站域名,获取到两个secrecthttps://www.google.com/recaptcha/admin 前端增加html和js代码,例如 ...

  7. Eclipse中如何添加相对路径的外部jar包

    在eclipse中进行java编程的时候,常常需要引用外部jar包.而采用相对路径引用jar包可以大大方便java工程的拷贝,这样使得java工程从一个路径转移到另一个路径时不用大费周章的修改外包ja ...

  8. 《Web Development with Go》JWT认证满意版

    这个比昨晚的要满意, 认证放到中间件那里了. Mux使用的是gorilla, 中间件使用的是negroni, 启动是用的negroni.classic方法. package main import ( ...

  9. MAC地址表(交换机)、ARP缓存表以及路由表(路由器)

  10. springboot hikari 连接池 在启动时未初始化数据库连接问题

    在启动Springboot 项目时 2019-11-18 21:32:38.223 INFO 1080 --- [on(4)-127.0.0.1] o.s.web.servlet.Dispatcher ...