Multi-page tags in the vue background system (2) Add right-click menu

Modify BUG points:

1 Modify the text style so that the mouse can click on the blank area next to the text.

2 After modifying the current page, right-click and select Close Others. After execution, there is no color problem when there is only one page left.

Look at the picture first

Function:

Rewritten the tag label page

Added mouse right-click menu

New functions in right-click menu: close other, close left, close right

When there is only one tag left, it cannot be deleted, and the right-click menu cannot be clicked. The code is judged (no screenshots, just understand by yourself)

======================================================

Don’t waste your wretched development (just code it)

<template>
    <div class="menu-tags">
        <template v-if="data.length != 0">
            <div class="tag-item" v-for="(item, index) in data" :key="item.name + index" @contextmenu.prevent="rightClick($event, item)">
                <!-- <span v-if="data.length==1" class="active" @click="handleSelect(item)">{
   
   { item.meta.title }}</span> -->
                <span :class="['item-title',item.isActive||data.length==1 ? 'active' : '']" @click="handleSelect(item)">{
   
   { item.meta.title }}</span>
                <span class="item-btn iconfont icon-guanbi2" @click="handleClose(item)"></span>
            </div>
        </template>

        <div class="right-menu" ref="rightMenu" v-show="rightMenu">
            <!-- <div class="menu-item" @click="handleRefresh($event)">刷新</div> -->
            <div :class="['menu-item', data.length == 1 ? 'menu-item_disabled' : '']" @click="handleRightMenuClose($event, 'other')">
                <span class="menu-item_licon iconfont icon-guanbiqita"></span>关闭其它
            </div>
            <div :class="['menu-item', data.length == 1 ? 'menu-item_disabled' : '']" @click="handleRightMenuClose($event, 'left')">
                <span class="menu-item_licon iconfont icon-guanbizuoce"></span>关闭左侧
            </div>
            <div :class="['menu-item', data.length == 1 ? 'menu-item_disabled' : '']" :disabled="data.length == 1 ? true : false" @click="handleRightMenuClose($event, 'right')">
                <span class="menu-item_licon iconfont icon-guanbiyouce"></span>关闭右侧
            </div>
        </div>
    </div>
</template>

<script>
import { mapState, mapGetters, mapActions, mapMutations } from 'vuex'

export default {
    name: 'LlMenuTags',
    props: {},
    data() {
        return {
            data: [],
            rightMenu: false,
            activeMenuItem: {},
            btnDisabled: false,
        }
    },
    computed: {
        ...mapState({
            tagData() {
                return this.$store.state.tagsRouter
            },
        }),
        ...mapGetters({
            getTagsRouterData: 'getTagsRouter', // 获取标签路由信息
        }),
    },
    // 定义引入的组件
    components: {},
    //变量监听
    watch: {},
    // 页面初始化
    created() {},
    // 页面DOM加载完成
    mounted() {
        let _this = this
        _this.init()
    },
    //离开页面时执行
    destroyed() {},
    watch: {},
    // 页面方法
    methods: {
        ...mapMutations({
            mySetRouterTagActive: 'setRouterTagActive', // clickTotal 是mutation 里的方法,totalAlise是重新定义的一个别名方法,本组件直接调用这个方法
        }),
        init() {
            let _this = this
            _this.$set(_this, 'data', [])
            _this.$nextTick(() => {
                _this.$set(_this, 'data', _this.$store.state.tagsRouter)
            })
        },
        handleRightMenuClose(event, direction) {
            let _this = this

            //只有一项时,不能关闭
            if (_this.data.length == 1) {
                return
            }

            //关闭左侧
            _this.$store.commit('delRoutes', {
                direction: direction, //方向
                item: _this.activeMenuItem,
            })

            _this.$nextTick(() => {
                _this.handleGoto(_this.activeMenuItem.path)
            })
        },
        rightClick(event, item) {
            let _this = this

            event = event || window.event
            //屏蔽样式
            event.preventDefault
                ? event.preventDefault()
                : (event.returnValue = false)
            //获取坐标
            var x = event.clientX //视口的位置
            var y = event.clientY

            _this.$set(_this, 'activeMenuItem', item)

            _this.$nextTick(() => {
                _this.rightMenu = true

                let myRightMouseMenu = _this.$refs.rightMenu

                //页面高度
                let bodyHeight = document.body.offsetHeight

                //显示菜单位置
                myRightMouseMenu.style.top = y + 'px'
                myRightMouseMenu.style.left = x + 'px'

                // 监听-点击菜单之外区域时,隐藏
                document.body.addEventListener('click', _this.hideMenu)
            })
        },
        hideMenu(event) {
            let _this = this

            event = event || window.event

            event.stopPropagation()
            event.preventDefault()

            _this.rightMenu = false

            // 清除监听-点击菜单之外区域时,隐藏
            document.body.removeEventListener('click', _this.hideMenu)
        },
        handleClose(item) {
            let _this = this
            if (_this.data.length == 1) {
                return false
            }

            //
            item.isActive = true

            let tempData = [..._this.data]

            _this.$store.commit('delRoute', {
                item,
                isSameRoute: _this.$route.path == item.path,
            })

            if (tempData.length != 0 && _this.$route.path == item.path) {
                //跳转
                let gotoPath = ''
                for (let i = 0; i < tempData.length; i++) {
                    if (tempData[i].path == item.path) {
                        if (tempData[i + 1] != undefined) {
                            gotoPath = tempData[i + 1].path
                        } else if (tempData.length != 0) {
                            gotoPath = tempData[0].path
                        }
                        break
                    }
                }

                if (gotoPath != '') {
                    _this.handleGoto(gotoPath)
                }
            }
        },
        handleSelect(item) {
            let _this = this

            //如果是当前页,则不跳
            if (item.name != _this.$route.name) {
                _this.$router.push({
                    path: item.path,
                })
                _this.mySetRouterTagActive(item)
                _this.$nextTick(() => {
                    _this.init()
                })
            }
        },
        handleGoto(path) {
            let _this = this
            if (_this.$common.isNull(path)) {
                return false
            }
            _this.$router.push({
                path: path,
            })
        },
    },
}
</script>

<style lang="less" scoped>
.right-menu {
    position: absolute;
    background: #fff;
    border: 1px solid #ccc;
    border-radius: 4px;
    .menu-item {
        font-size: 14px;
        padding: 0 20px;
        height: 40px;
        line-height: 40px;
        cursor: pointer;

        .menu-item_licon {
            width: 16px;
        }
    }
    .menu-item:hover {
        background: #c3c3c3;
    }
    .menu-item_disabled {
        cursor: not-allowed;
    }
}
.menu-tags {
    margin: 10px 16px 0 16px;

    .tag-item {
        padding: 0 25px 0 0;
        margin-right: 10px;
        margin-bottom: 10px;
        display: inline-block;
        background: #fafafa;
        color: rgba(0, 0, 0, 0.65);
        font-size: 14px;
        cursor: pointer;
        transition: 0.3s;
        position: relative;

        .item-title {
            width: 100%;
            display: inline-block;
            padding: 8px 16px;
        }

        .tag-title::selection {
            background: none;
        }
        .item-btn {
            font-size: 12px;
            position: absolute;
            top: 8px;
            right: 0;
            width: 16px;
            height: 16px;
            line-height: 16px;
        }

        .active {
            color: #63d5ce;
        }
    }
    .tag-item:hover {
        color: #63d5ce;
    }
}
</style>

vuex 

import Vue from 'vue'
import router from '@/router'
import Vuex from 'vuex'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    user: {},
    tagsRouter: [],
  },
  getters: {
    getUser(state) {
      //获取本地用户信息
      let user = localStorage.getItem('user')
      if (user != null) {
        state.user = JSON.parse(user)
        return JSON.parse(user)
      } else {
        router.push({ path: '/login' })
        return state.user
      }
    },
    getTagsRouter(state) {
      //获取标签路由信息
      debugger
      return state.tagsRouter
    },
  },
  mutations: {
    setUser(state, user) {
      //设置
      state.user = { ...user }
      //存储本地
      localStorage.setItem('user', JSON.stringify({ ...user }))
    },
    setRouterTagActive(state, routeItem) {
      //设置当前的tag页为活动页
      for (let i = 0; i < state.tagsRouter.length; i++) {
        let item = state.tagsRouter[i]
        state.tagsRouter[i].isActive = false
        if (state.tagsRouter[i].name === routeItem.name) {
          state.tagsRouter[i].isActive = true
        }
      }
    },
    addRoute(state, routeItem) {
      //选择菜单后,添加至本地路由信息中
      let flog = false
      for (let i = 0; i < state.tagsRouter.length; i++) {
        let item = state.tagsRouter[i]
        if (state.tagsRouter[i].name === routeItem.name) {
          flog = true
          break
        }
      }
      if (!flog) {
        state.tagsRouter.push({
          path: routeItem.path,
          name: routeItem.name,
          meta: routeItem.meta,
        })
      }
    },
    delRoute(state, params) {
      //删除tags路由项
      if (state.tagsRouter.length != 0) {
        let itemIndex = 0
        for (let i = 0; i < state.tagsRouter.length; i++) {
          let item = state.tagsRouter[i]
          if (state.tagsRouter[i].meta.menuKey === params.item.meta.menuKey) {
            //
            itemIndex = i
            state.tagsRouter.splice(i, 1)
            break
          }
        }

        //跳除后的跳转
      }
    },
    delRoutes(state, params) {
      //删除多项tags路由项
      if (state.tagsRouter.length != 0) {
        let flogIndex = 0
        let sum = 0
        let tempArr = state.tagsRouter
        if (params.direction == 'left') {
          //从左侧开始删除
          for (let i = 0; i < state.tagsRouter.length; i++) {
            let item = state.tagsRouter[i]
            if (state.tagsRouter[i].meta.menuKey === params.item.meta.menuKey) {
              // 记录当前位置
              flogIndex = i
              break
            }
            sum++
          }
          state.tagsRouter.splice(0, sum)
        } else if (params.direction == 'right') {
          //删除右侧所有路由
          for (let i = 0; i < state.tagsRouter.length; i++) {
            let item = state.tagsRouter[i]
            if (state.tagsRouter[i].meta.menuKey === params.item.meta.menuKey) {
              //
              sum = state.tagsRouter.length - i + 1
              flogIndex = i
              break
            }
          }
          state.tagsRouter.splice(flogIndex, sum)
        } else {
          // 关闭其它
          state.tagsRouter.splice(0, state.tagsRouter.length)
          state.tagsRouter.push({
            path: params.item.path,
            name: params.item.name,
            meta: params.item.meta,
          })
        }

        //跳除后的跳转
      }
    },
  },
  actions: {
    doSomething(context, val) {
      //应用:this.$store.dispatch("doSomething",{})
      //提交mutations内的方法:context.commit("setUser",val)
    },
  },
  modules: {},
})

route.js

import Vue from 'vue'
import VueRouter from 'vue-router'

import store from '@/store/index'
Vue.use(VueRouter)

// 页面进度条
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'

NProgress.configure({
  easing: 'ease', // 动画方式
  speed: 500, // 递增进度条的速度
  showSpinner: false, // 是否显示加载ico
  trickleSpeed: 200, // 自动递增间隔
  minimum: 0.3, // 初始化时的最小百分比
})

const routes = [
  {
    path: '/',
    name: 'index',
    meta: { title: '首页', icon: 'home', menuKey: '1', menu: true },
    redirect: '/Dashboard/DashboardIndex',
    component: () =>
      import(/* webpackChunkName: "about" */ '../views/HomeView.vue'),
  },
  {
    path: '/Dashboard',
    name: 'Dashboard',
    meta: { title: 'Dashboard', icon: 'bar-chart', menuKey: '2' },
    component: () => import('../views/HomeView.vue'),
    children: [
      {
        path: 'DashboardIndex',
        name: 'DashboardIndex',
        meta: { title: 'DashboardIndex', icon: '', menuKey: '2.1' },
        component: () => import('@/views/dashboard/Dashboard.vue'),
      },
    ],
  },
  {
    path: '/block',
    name: 'block',
    meta: { title: '区块链基础网络', icon: 'apartment', menuKey: '3' },
    component: () => import('../views/HomeView.vue'),
    children: [
      {
        path: 'BlockChainSetting',
        name: 'BlockChainSetting',
        meta: { title: '区块链配置', icon: '', menuKey: '3.1' },
        component: () => import('@/views/blockchain/BlockChainSetting.vue'),
      },
      {
        path: 'BlockChainSetUp',
        name: 'BlockChainSetUp',
        meta: { title: '区块链搭建', icon: '', menuKey: '3.2' },
        component: () => import('@/views/blockchain/BlockChainSetUp.vue'),
      },
      {
        path: 'BlockChainNetInfo',
        name: 'BlockChainNetInfo',
        meta: { title: '网络状态', icon: '', menuKey: '3.3' },
        component: () => import('@/views/blockchain/BlockChainNetInfo.vue'),
      },
    ],
  },

  {
    path: '/NewOrganization',
    name: 'NewOrganization',
    meta: { title: '新增组织', icon: 'apartment', menuKey: '4' },
    component: () => import('../views/HomeView.vue'),
    children: [
      {
        path: 'JoinChannel',
        name: 'JoinChannel',
        meta: { title: '加入通道', icon: 'apartment', menuKey: '4.1' },
        component: () => import('@/views/neworganization/JoinChannel.vue'),
      },
      {
        path: 'NewNode',
        name: 'NewNode',
        meta: { title: '新增节点', icon: 'apartment', menuKey: '4.2' },
        component: () => import('@/views/neworganization/NewNode.vue'),
      },
      {
        path: 'CreateChannel',
        name: 'CreateChannel',
        meta: { title: '创建新通道', icon: 'apartment', menuKey: '4.3' },
        component: () => import('@/views/neworganization/CreateChannel.vue'),
      },
    ],
  },
  {
    path: '/About',
    name: 'About',
    meta: { title: 'About', icon: '' },
    component: () => import('../views/AboutView.vue'),
  },
  {
    path: '/login',
    name: 'login',
    meta: { title: '登录', icon: '' },
    component: () => import('../views/login/Login.vue'),
  },
]

const router = new VueRouter({
  mode: 'hash',
  base: process.env.BASE_URL,
  routes,
})

router.beforeEach((to, from, next) => {
  // 每次切换页面时,调用进度条
  NProgress.start()

  if (to.meta.menuKey != undefined) {
    store.commit('addRoute', to)
    store.commit('setRouterTagActive', to)
  }

  next()
})

//当路由进入后:关闭进度条
router.afterEach(() => {
  // 在即将进入新的页面组件前,关闭掉进度条
  NProgress.done()
})

const VueRouterPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(to) {
  return VueRouterPush.call(this, to).catch((err) => err)
}
export default router

Finally, just introduce the component

Tip: Routing information can be used as a simple reference according to your own situation.

Guess you like

Origin blog.csdn.net/tdjqqq/article/details/124177947