【组件】自定义tagsView

前言

这到了现在,基本算是已经完成的差不多了。 就还剩下关闭的实现,以及页面的缓存。
关于关闭的实现,因为我们项目没说使用这个组件,所以我就不写了。
页面的缓存,现在是没有生效,再慢慢研究。
最后:不建议使用我这个组件,原因呢,是因为前面写左侧菜单栏的时候,封装的数据结构问题,导致耦合度太高了。所以不推荐使用。以后有机会了,再写吧。
最后,贴上代码,以后说不定会用到。每块代码的作用就不单独写了,全部删了,因为逻辑有点多。

参考文档

实现代码-第一个版本

在这里插入图片描述
在这里插入图片描述

实现代码-第二个版本

在这里插入图片描述

实现代码-最终版本

在这里插入图片描述

源码(如果使用,则需要根据代码,补充相对应的vuex里面的信息)

<template>
  <div class="tags-view">
    <el-tabs v-model="activeName" type="card" @tab-click="_clickTag">
      <el-tab-pane :name="item.url" v-for="(item, index) in publicInfo.tagsView" :key="item.url">
        <span slot="label">
          <i class="isCurrent" :class="item.isActive ? 'true' : ''"></i>
          {{ item.title }}
          <i class="fa fa-close light" @click="_delTag(index)"></i>
        </span>
        <el-card class="box-card">
          <keep-alive v-if="$route.meta.keepAlive">
            <router-view v-if="item.url===activeName"></router-view>
          </keep-alive>
          <router-view v-else-if="item.url===activeName"></router-view>
        </el-card>
      </el-tab-pane>
    </el-tabs>
    <ul class="contextmenu" v-show=" visible" :style="{ left: left + 'px', top: top + 'px' }">
      <li @click="_clickClose($event, 1)">关闭当前</li>
      <li @click="_clickClose($event, 2)">关闭其他</li>
      <li @click="_clickClose($event, 3)">关闭所有</li>
    </ul>
  </div>
</template>
<script>
import { mapState, mapActions } from 'vuex'
export default {
  data() {
    return {
      isis: false,
      top: 0,
      left: 0,
      visible: false,
      activeName: this.$store.state.StoreModuleBase.publicInfo.defaultActive
    }
  },
  props: {},
  components: {},
  computed: {
    ...mapState('StoreModuleBase', ['publicInfo']),
    overloadHandle() {
      return this.publicInfo.tagsView
    }
  },
  created() { },
  onload() {
    this._addContextmenu()
  },
  onShow() {
    this._addContextmenu()
  },
  mounted() {
    this._addContextmenu()
  },
  watch: {
    overloadHandle(enwVal, oldVal) {
      this._addContextmenu()
    }
  },
  methods: {
    ...mapActions('StoreModuleBase', ['setPublicInfo']),
    _clickClose(e, index) {
      let msg = '也不用,就不做了'
      // 关闭逻辑待补充
      if (index === 1) {
        console.log(msg)
      } else if (index === 2) {
        console.log(msg)
      } else if (index === 3) {
        console.log(msg)
      }
      // let delIndex = -1
      // this._delTag(delIndex, e)
      this.visible = false
    },
    _addOnBlur(dom) {
      // 添加失去焦点事件(如果当前元素或者其父元素包含xx类,则添加失去焦点事件)
      let that = this
      if (dom) {
        if (that.$jsUtil.hasClassFun(dom, 'el-tabs__item')) {
        } else if (that.$jsUtil.hasClassFun(dom.parent(), 'el-tabs__item')) {
          dom = dom.parent()
        }
        dom.addEventListener('blur', function (e) {
          e.preventDefault()
          that._onBlur()
        })
      }
    },
    _optTag(e, dom) {
      this.visible = true
      const offsetLeft = this.$el.getBoundingClientRect().left
      this.left = e.clientX - offsetLeft + 230
      this.top = e.clientY
      this._addOnBlur(dom)
    },
    // 获取vuex
    _getPublic() {
      let _publicInfo = JSON.parse(JSON.stringify(this.publicInfo))
      return _publicInfo
    },
    // 删除菜单标签
    _delTag(delIndex, e) {
      let _publicInfo = this._getPublic()
      if (
        _publicInfo &&
        _publicInfo.tagsView &&
        _publicInfo.tagsView.length > 1
      ) {
        // 首先,获取关闭的标签信息
        let currentIsSelected = _publicInfo.tagsView[delIndex].isActive
        let currentIsFirst = delIndex === 0
        // 然后,删除
        _publicInfo.tagsView.splice(delIndex, 1)

        let newIndex
        if (currentIsSelected) {
          if (currentIsFirst) {
            newIndex = 0
          } else {
            newIndex = delIndex - 1
          }
        } else {
          newIndex = null
        }
        // 接着,更新选中信息
        if (newIndex != null) {
          _publicInfo.activeNameTab = _publicInfo.tagsView[newIndex].url
          _publicInfo.defaultActive = _publicInfo.tagsView[newIndex].url
          _publicInfo.tagsView.forEach((item, index) => {
            if (index === newIndex) {
              item.isActive = true
            } else {
              item.isActive = false
            }
          })
        }
        // 最后,跳转到当前新的的地址,以及更新tab值
        this.activeName = _publicInfo.activeNameTab
        this.$router.push({ path: _publicInfo.defaultActive })
        // 更新vuex
        this.setPublicInfo(_publicInfo)
        window.event ? (window.event.cancelBubble = true) : e.stopPropagation()
      }
    },
    _onBlur() {
      this.visible = false
    },
    // 点击标签
    _clickTag(tab, event) {
      // 首先,获取到被点击标签的标题和地址
      let url = tab.name
      // 然后,获取public
      let _publicInfo = this._getPublic()
      // 接着,更新vuex里面菜单默认选中的信息
      _publicInfo.defaultActive = url
      _publicInfo.activeNameTab = url
      _publicInfo.tagsView.forEach(element => {
        if (element.url === url) {
          element.isActive = true
        } else {
          element.isActive = false
        }
      })
      this.setPublicInfo(_publicInfo)
      this.activeName = _publicInfo.activeNameTab
      // 最后,跳转到当前选中的地址
      this.$router.push({ path: url })
    },
    _addContextmenu() {
      let that = this
      let elTabsItems = document
        .getElementsByClassName('tags-view')[0]
        .getElementsByClassName('el-tabs__item')
      if (elTabsItems && elTabsItems.length > 0) {
        elTabsItems = [].slice.call(elTabsItems)
        elTabsItems.forEach(item => {
          // 添加右键事件
          item.addEventListener('contextmenu', function (e) {
            e.preventDefault()
            that._optTag(e, item)
          })
        })
      }
    }
  }
}
</script>
<style lang="less">
.tags-view {
  .el-tabs {
    background-color: #ffffff;
    margin: 0;
    padding: 0;
    .el-tabs__header {
      margin: 0;
      .el-tabs__nav-prev,
      .el-tabs__nav-next {
        line-height: 34px;
      }
      .el-tabs__nav {
        border-radius: 0;
        height: 34px;
        border-top-color: #ffffff;
        .el-tabs__item {
          height: 34px;
          line-height: 34px;
          color: #999;
          &.is-active {
            border-bottom-color: #eee;
            vertical-align: 1px;
          }
          &:hover {
            color: rgba(73, 80, 96, 1);
            .fa.fa-close {
              color: red;
              -webkit-transition: all 0.5s;
              transition: all 0.5s;
            }
          }
          span {
            .isCurrent {
              display: inline-block;
              width: 7px;
              height: 7px;
              border-radius: 50%;
              vertical-align: 2px;
              &.true {
                background-color: #409eff;
              }
            }
            .fa {
              &.fa-close {
                border-radius: 50%;
                font-size: 10px;
                width: 12px;
                height: 12px;
                text-align: center;
                line-height: 12px;
                // 图标变细
                -webkit-text-stroke: 1px #fff;
                -moz-text-stroke: 1px #fff;
                -o-text-stroke: 1px#fff;
                text-stroke: 1px #fff;
                z-index: 999;
                color: rgba(0, 0, 0, 0);
                &:hover {
                  background-color: #d0c8c8;
                }
              }
            }
          }
        }
      }
    }
    .el-tabs__content {
      .el-card {
        margin: 5px 8px !important;
      }
    }
  }
}

.contextmenu {
  margin: 0;
  background: #fff;
  z-index: 100;
  position: absolute;
  list-style-type: none;
  padding: 5px 0;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 400;
  color: #333;
  box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
  li {
    margin: 0;
    padding: 7px 16px;
    cursor: pointer;
    &:hover {
      background: #eee;
    }
  }
}
</style>

vuex信息

vuex的结构我是做成了模块化

store-module-base.js

export default {
  // 增加命名空间
  namespaced: 'StoreModuleBase',
  // 初始化状态值--一定要有该属性对象
  state: {
    // 系统信息
    publicInfo: {
      // 标签页
      tagsView: [],
      // 菜单开关状态
      menuSwitchState: false,
      // 当前选中的地址
      activeNameTab: ''
    }
  },
  // 自定义改变state初始值的方法--一定要有该属性对象
  mutations: {
    SETPUBLICINFO(state, param) {
      state.publicInfo = param
    },
    SETMENUSWITCHSTATE(state, param) {
      state.publicInfo.menuSwitchState = param
    }
  },
  // 状态计算属性
  getters: {},
  // 异步操作状态
  actions: {
    setPublicInfo({ commit }, param) {
      commit('SETPUBLICINFO', param)
    },
    clearPublicInfo({ commit, state }) {
      commit('SETPUBLICINFO', {})
    }
  }
}

index.js

// 引入Vue、和Vuex(固定写法)
import Vue from 'vue'
import Vuex from 'vuex'
// 引入persistedState
import persistedState from 'vuex-persistedstate'
// module
import StoreModuleBase from './modules/store-module-base'

Vue.use(Vuex)

const debug = process.env.NODE_ENV !== 'production'
export default new Vuex.Store({
  // 数据持久化,默认localStorage 参考:https://www.jianshu.com/p/c22861ec5f21
  plugins: [
    persistedState({
      storage: window.sessionStorage
    })
  ],
  modules: {
    StoreModuleBase
  },
  strict: debug
})

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值