vue3中后台管理侧边栏的实现及elementPlus的icon组件全局注册

121 阅读1分钟

1.新建global.d.ts文件

// * Menu
declare namespace Menu {
	interface MenuOptions {
		path: string;
		name: string;
		component?: string | (() => Promise<any>);
		redirect?: string;
		meta: MetaProps;
		children?: MenuOptions[];
	}
	interface MetaProps {
		icon: string;
		title: string;
		activeMenu?: string;
		isLink?: string;
		isHide: boolean;
		isFull: boolean;
		isAffix: boolean;
		isKeepAlive: boolean;
	}
}

// * Vite
declare type Recordable<T = any> = Record<string, T>;

declare interface ViteEnv {
	VITE_API_URL: string;
	VITE_PORT: number;
	VITE_OPEN: boolean;
	VITE_GLOB_APP_TITLE: string;
	VITE_DROP_CONSOLE: boolean;
	VITE_PROXY_URL: string;
	VITE_BUILD_GZIP: boolean;
	VITE_REPORT: boolean;
}

2.新建SubMenu.vue文件

<template>
  <template v-for="subItem in menuList" :key="subItem.path">
    <el-sub-menu v-if="subItem.children && subItem.children.length > 0" :index="subItem.path">
      <template #title>
        <el-icon>
          <component :is="subItem.meta.icon"></component>
        </el-icon>
        <span>{{ subItem.meta.title }}</span>
      </template>
      <SubMenu :menuList="subItem.children" />
    </el-sub-menu>
    <el-menu-item v-else :index="subItem.path" @click="handleClickMenu(subItem)">
      <el-icon>
      // 渲染图标组件,需要提前注册好
        <component :is="subItem.meta.icon"></component>
      </el-icon>
      <template #title>
        <span>{{ subItem.meta.title }}</span>
      </template>
    </el-menu-item>
  </template>
</template>

<script setup lang="ts">
import { useRouter } from 'vue-router'
// menuList 路由列表
defineProps<{ menuList: Menu.MenuOptions[] }>()

const router = useRouter()
// 点击事件
const handleClickMenu = (subItem: Menu.MenuOptions) => {
// 外部链接打开新页面
  if (subItem.meta.isLink) return window.open(subItem.meta.isLink, '_blank')
  //跳转对应页面路由
  router.push(subItem.path)
}
</script>

3.新建一个sideBar.vue页面,引入SubMenu.vue

<!-- 经典布局 -->
<template>
  <el-container class="layout">
    <el-header>
      <div class="header-lf">
        <div class="logo flx-center">
          <img src="@/assets/images/logo.svg" alt="logo" />
          <span>HB Admin</span>
        </div>
        <ToolBarLeft />
      </div>
      <ToolBarRight />
    </el-header>
    <el-container class="classic-content">
      <el-aside>
        <div class="menu" :style="{ width: isCollapse ? '65px' : '210px' }">
          <el-scrollbar>
            <el-menu
              :default-active="activeMenu"
              :router="false"
              :collapse="isCollapse"
              :collapse-transition="false"
              :unique-opened="true"
              background-color="#ffffff"
              text-color="#303133"
            >
              <SubMenu :menuList="menuList" />
            </el-menu>
          </el-scrollbar>
        </div>
      </el-aside>
      <el-container class="classic-main">
        <Main />
      </el-container>
    </el-container>
  </el-container>
</template>

<script setup lang="ts" name="layoutClassic">
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import { GlobalStore } from '@/stores'
import { AuthStore } from '@/stores/modules/auth'
import Main from '@/layouts/components/Main/index.vue'
import SubMenu from '@/layouts/components/Menu/SubMenu.vue'
import ToolBarLeft from '@/layouts/components/Header/ToolBarLeft.vue'
import ToolBarRight from '@/layouts/components/Header/ToolBarRight.vue'

const route = useRoute()
const authStore = AuthStore()
const globalStore = GlobalStore()
const activeMenu = computed(() => (route.meta.activeMenu ? route.meta.activeMenu : route.path))
const menuList = computed(() => authStore.showMenuListGet)
const isCollapse = computed(() => globalStore.themeConfig.isCollapse)
</script>

<style scoped lang="scss">
@import './index.scss';
</style>

<style lang="scss">
.classic {
  .classic-content {
    height: calc(100% - 55px); // 减去头部高度
    .classic-main {
      display: flex;
      flex-direction: column;
    }
  }
  .el-menu,
  .el-menu--popup {
    .el-menu-item {
      &.is-active {
        background: var(--el-color-primary-light-9);
        &::before {
          position: absolute;
          top: 0;
          bottom: 0;
          left: 0;
          width: 4px;
          content: '';
          // background: var(--el-color-primary);
        }
      }
    }
  }

  // guide
  #driver-highlighted-element-stage {
    background-color: #606266 !important;
  }
}
</style>

QQ截图20230908132652.png

4.menuList的数据格式

{

	"data": [
		{
			"path": "/home",
			"name": "home",
			"redirect": "/personalManage",
			"meta": {
				"title": "首页",
				"isLink": "",
				"isHide": true,
				"isFull": false,
				"isAffix": false,
				"isKeepAlive": true
			}
		},
		{
			"path": "/dataScreen",
			"name": "dataScreen",
			"component": "/dataScreen/index",
			"meta": {
				"title": "汇博资源库",
				"isLink": "",
				"isHide": true,
				"isFull": true,
				"isAffix": false,
				"isKeepAlive": true
			}
		},
		
		{
			"path": "/personalManage",
			"name": "personalManage",
			"component": "/personalManage/index",
			"meta": {
				"title": "用户管理",
				"isLink": "",
				"isHide": false,
				"isFull": false,
				"isAffix": false,
				"isKeepAlive": true
			}
		},
		{
			"path": "/watermark",
			"name": "watermark",
			"component": "/watermark/index",
			"meta": {
				"title": "水印管理",
				"isLink": "",
				"isHide": false,
				"isFull": false,
				"isAffix": false,
				"isKeepAlive": true
			}
		},
		{
			"path": "/fileTrace",
			"name": "fileTrace",
			"component": "/fileTrace/index",
			"meta": {
				"title": "文件溯源",
				"isLink": "",
				"isHide": false,
				"isFull": false,
				"isAffix": false,
				"isKeepAlive": true
			}
		},
		{
			"path": "/logHistory",
			"name": "logHistory",
			"component": "/logHistory/index",
			"meta": {
				"title": "操作日志",
				"isLink": "",
				"isHide": false,
				"isFull": false,
				"isAffix": false,
				"isKeepAlive": true
			}
		}
	
	]

}

5.elementPlus 图标组件注册,在main.ts中

import * as Icons from '@element-plus/icons-vue'

const app = createApp(App)
// 注册element Icons组件
Object.keys(Icons).forEach(key => {
  app.component(key, Icons[key as keyof typeof Icons])
})
app.use(xxx).use(xxx).mount('#app')