美文网首页
Vue3写一个后台管理系统(6)导航栏标签页的实现

Vue3写一个后台管理系统(6)导航栏标签页的实现

作者: 程序员三千_ | 来源:发表于2023-03-23 10:38 被阅读0次

先看看最终的效果吧,这样更有助于后面代码逻辑的实现。

布局实现效果分析

image.png

我们看第一张图片,可以分析得出 整个导航标签组件(我们最终会封装成一个组件,便于后期其他项目的移植使用),由标签按钮右侧弹窗层组合成。只要通过css布局 ,就可以实现右侧弹出层的效果,很简单。

image.png
image.png
从上面两张图片可以看出,整个导航标签组件,是实现了标签按钮过多后,上线可移动的效果的。

功能实现效果分析

image.png

右侧弹窗层 实现了1.刷新 2.关闭右侧 3.关闭其他 4.关闭全部四个功能

  • 刷新:只要通过 router.go(0) 刷新当前页就行
  • 关闭右侧:因为我们整个导航标签肯定是由一个数组渲染的,通过数组的splice就能实现数据的“切割”。
  • 关闭其他:通过也是通过实现的,只不过切割了当前位置的一前一后的数据,但这里需要注意一个点,就是我们这个项目一进来是自动定位到首页标签的,所以关闭其他和关闭全部是不能关闭到首页hone标签按钮的
  • 关闭全部:把数组置未空就行了,但要定位到首页,其他按钮标签是由关闭功能的,但首页是没有关闭功能的,也就是首页是没有关闭当前页的功能的,


    image.png
image.png

其他问题分析

  • 路由过渡动画的实现
    使用vue官方的过渡动画的方式
    html
<!-- 使用动态过渡名称 -->
<router-view v-slot="{ Component, route }">
  <transition :name="route.meta.transition">
    <component :is="Component" />
  </transition>
</router-view>
  • 数据的缓存
    我们使用keep-alive结合vuex的实现方式
<template>
  <div class="app-main">
    <router-view v-slot="{ Component, route }">
      <transition name="fade-transform" mode="out-in">
        <keep-alive :include="cachedViews">
          <component :is="Component" :key="route.name" />
        </keep-alive>
      </transition>
    </router-view>
  </div>
</template>

好了,总体的实现思路到讲完了,我们直接看代码吧

监听路由,往数组里里增加标签,再通过循环router-view 实现渲染

 /**
   * 监听路由变化
   */
  watch(
    route,
    (to, from) => {
      // const cachedViews = store.getters.tagsViewList
      // console.log("store.getters.tagsViewList",cachedViews)
      if (!isTags(to.path)) return
      const { fullPath, meta, name, params, path, query } = to
      store.commit('app/addTagsViewList', {
        fullPath,
        meta,
        name,
        params,
        path,
        query,
        title: getTitle(to)
      })
    },
    {
      immediate: true
    }
  )
<template>
  <div class="app-main">
    <router-view v-slot="{ Component, route }">
      <transition name="fade-transform" mode="out-in">
        <keep-alive :include="cachedViews">
          <component :is="Component" :key="route.name" />
        </keep-alive>
      </transition>
    </router-view>
  </div>
</template>

<script setup>

  const cachedViews = computed(() => {
    console.log(store.getters.tagsViewList.map((x) => x.name))
    return  store.getters.tagsViewList.map((x) => x.name)
  })

</script>


tagsViewList的数据和操作动作全都封装到vuex里

import {TAGS_VIEW} from '@/constant'
import {getItem, setItem} from '@/utils/storage'

export default {
  namespaced: true,
  state: () => ({
    sidebarOpened: true,
    tagsViewList: getItem(TAGS_VIEW) || []
  }),
  mutations: {
    triggerSidebarOpened(state) {
      state.sidebarOpened = !state.sidebarOpened
    },
    /**
     * 添加 tags
     */
    addTagsViewList(state, tag) {
      const isFind = state.tagsViewList.find(item => {
        return item.path === tag.path
      })
      // 处理重复
      if (!isFind) {
        state.tagsViewList.push(tag)
        setItem(TAGS_VIEW, state.tagsViewList)
      }
    },
    /**
     * 删除 tag
     * @param {type: 'other'||'right'||'index', index: index} payload
     */
    removeTagsView(state, payload) {
      if (payload.type === 'index') {
        state.tagsViewList.splice(payload.index, 1)
      } else if (payload.type === 'other') {
        state.tagsViewList.splice(
          payload.index + 1,
          state.tagsViewList.length - payload.index + 1
        )
        state.tagsViewList.splice(0, payload.index)
        if(payload.index !=0){
          //list第一位加入删除了的首页tag
          state.tagsViewList.unshift({
            fullPath:'/home',
            meta:{title: '首页', affix: true},
            name:'home',
            params:{},
            path:'/home',
            query:{},
            title: "首页"
          })
        }
      } else if (payload.type === 'right') {
        state.tagsViewList.splice(
          payload.index + 1,
          state.tagsViewList.length - payload.index + 1
        )
      } else if (payload.type === 'all') {
        state.tagsViewList = []
      }
      setItem(TAGS_VIEW, state.tagsViewList)
    },


  },
  actions: {}
}

右侧弹窗组件的实现

<template>
  <ul class="context-menu-container">
    <li @click="onRefreshClick">
    刷新
    </li>
    <li @click="onCloseRightClick">
    关闭右侧
    </li>
    <li @click="onCloseOtherClick">
    关闭其他
    </li>
    <li @click="onCloseAllClick">
      关闭全部
    </li>
  </ul>
</template>

<script setup>
  import { defineProps } from 'vue'
  import { useRouter } from 'vue-router'
  import { useStore } from 'vuex'

  const props = defineProps({
    index: {
      type: Number,
      required: true
    }
  })

  const router = useRouter()
  const store = useStore()
  const onRefreshClick = () => {
    router.go(0)
  }

  const onCloseRightClick = () => {
    store.commit('app/removeTagsView', {
      type: 'right',
      index: props.index
    })
  }

  const onCloseOtherClick = () => {
    store.commit('app/removeTagsView', {
      type: 'other',
      index: props.index
    })



  }

  const onCloseAllClick = () => {
    store.commit('app/removeTagsView', {
      type: 'all',
      index: props.index
    })

    router.push('/')

  }



</script>

具体的css代码很简单,就展示了,需要的私信我

整个tagView组件的实现

<template>
  <div class="tags-view-container">
    <el-scrollbar class="tags-view-wrapper">
    <router-link
      class="tags-view-item"
      :class="isActive(tag) ? 'active' : ''"
      :style="{
          backgroundColor: isActive(tag) ? $store.getters.cssVar.menuActiveText : '',
          borderColor: isActive(tag) ? $store.getters.cssVar.menuActiveText : ''
        }"
      v-for="(tag, index) in $store.getters.tagsViewList"
      :key="tag.fullPath"
      :to="{ path: tag.fullPath }"

      @contextmenu.prevent="openMenu($event, index)"
    >
      {{ tag.title }}
      <template v-if="!isAffiix(tag)">
        <i
          class="el-icon-close"
          @click.prevent.stop="onCloseClick(index,tag)"
        />
      </template>

    </router-link>
    </el-scrollbar>
    <context-menu
      v-show="visible"
      :style="menuStyle"
      :index="selectIndex"
    ></context-menu>
  </div>
</template>

<script setup>
  import ContextMenu from './ContextMenu.vue'
  import { ref, reactive, watch } from 'vue'
  import { useRoute,useRouter } from 'vue-router'
  import { useStore } from 'vuex'

  const route = useRoute()

  /**
   * 是否被选中
   */
  const isActive = tag => {
    return tag.path === route.path
  }
  const isAffiix = tag =>{
    return tag.meta && tag.meta.affix
  }
  // contextMenu 相关
  const selectIndex = ref(0)
  const visible = ref(false)
  const menuStyle = reactive({
    left: 0,
    top: 0
  })
  /**
   * 展示 menu
   */
  const openMenu = (e, index) => {
    const { x, y } = e
    menuStyle.left = x + 'px'
    menuStyle.top = y + 'px'
    selectIndex.value = index
    visible.value = true
  }

  /**
   * 关闭 tag 的点击事件
   */
  const store = useStore()
  const router = useRouter()

  const onCloseClick = (index,tag) => {

    store.commit('app/removeTagsView', {
      type: 'index',
      index: index
    })

    //如果删除的是当前页面,自动定位到上一个页面
    if (isActive(tag)) {
      let tagsViewList = store.getters.tagsViewList
      if(index ==0 && tagsViewList.length>=1){
        let pre_index = 0
        let pre_page =tagsViewList[pre_index]
        router.push(pre_page.fullPath)
      }else if(tagsViewList.length == 0){//如果是最后一个,定位到首页
        router.push('/')
      }else{
        let pre_index = index-1
        let pre_page =tagsViewList[pre_index]
        router.push(pre_page.fullPath)
      }


    }
  }


  /**
   * 关闭 menu
   */
  const closeMenu = () => {
    visible.value = false
  }

  /**
   * 监听变化
   */
  watch(visible, val => {
    if (val) {
      document.body.addEventListener('click', closeMenu)
    } else {
      document.body.removeEventListener('click', closeMenu)
    }
  })



</script>

需要注意的是,,如果删除的是当前页,自动定位到上一个页面,删除的如果是最后一个,自动定位到首页

tagsView 方案总结

那么到这里关于 tagsView 的内容我们就已经处理完成了。

整个 tagsView 就像我们之前说的,拆开来看之后,会显得明确很多。

整个 tagsView 整体来看就是三块大的内容:

  1. tagstagsView 组件
  2. contextMenucontextMenu 组件
  3. view:页面路由处理

再加上一部分的数据处理即可。

到这里,我们整个后台管理系统的框架就全部搭建完毕了,剩下就是一些细节,根据自己公司的业务需求,自行修改就行。

相关文章

网友评论

      本文标题:Vue3写一个后台管理系统(6)导航栏标签页的实现

      本文链接:https://www.haomeiwen.com/subject/ohkirdtx.html