后台-路由菜单、角色-cv

 

1.路由文件 第一步与第三步

import router from '@/router'
import useStore from '@/store'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { chatUtil } from '@/utils/ChatUtil'
import { getNodeTree } from '@/api/demand/demand'

import { getCurrentInstance, ref } from 'vue'

import { app as VueApp } from './main'
import { localStorage as Local } from '@/utils/storage'
NProgress.configure({ showSpinner: false }) // 进度环显示/隐藏


const map = ref<any>({})
// 全局获取nodeTree
const getAllNodeTree = async () => {
	VueApp.config.globalProperties.$treeNodeMap = (id: any) => {
		return map.value[id]
	}
	const { data } = await getNodeTree()
	Local.set('qt_nodeTree', JSON.stringify(data))
	// 设置全局id和name的字典库
	loopData(data)
}
const loopData = (arr: any) => {
	for (let i = 0; i < arr.length; i++) {
		const current = arr[i]
		map.value[current.nodeId] = current.name
		if (current.children) {
			loopData(current.children)
		}
	}
}
let firstTime = true

// 白名单路由
const whiteList = ['/login']
const vm = getCurrentInstance()
router.beforeEach(async (to, from, next) => {
	// 全局获取nodeTree
	if (Local.get('token') && firstTime) {
		firstTime = false
		getAllNodeTree()
	}
	if (to.meta.title) {
		//判断是否有标题
		document.title = `-admin-${to.meta.title}  `
	} else {
		document.title = `-admin`
	}
	NProgress.start()
	const { user, permission } = useStore()
	const hasToken = user.token
	if (hasToken) {
		// 登录成功,跳转到首页
		if (to.path == '/login') {
			next({ path: '/' })
			NProgress.done()
		} else {
			const hasGetUserInfo = user.roles.length > 0
			if (hasGetUserInfo) {
				if (to.matched.length == 0) {
					from.name ? next({ name: from.name as any }) : next('/401')
				} else {
					next()
				}
			} else {
				try {
					await user.getUserInfo()
					const roles = user.roles
					// 第一步
					const accessRoutes: any = await permission.generateRoutes(roles)
					// 第三步
					accessRoutes.get(1).forEach((route: any) => {
						router.addRoute(route)
					})
					accessRoutes.get(2).forEach((route: any) => {
						router.addRoute(route)
					})
					accessRoutes.get(3).forEach((route: any) => {
						router.addRoute(route)
					})
					// 初始化聊天窗口
					chatUtil.initChatDialog()
					next({ ...to, replace: true })
				} catch (error) {
					// 销毁聊天弹出框
					await chatUtil.destoryChatDialog()
					// 移除 token 并跳转登录页
					await user.resetToken()
					next(`/login?redirect=${to.path}`)
					NProgress.done()
				}
			}
		}
	} else {
		// 未登录可以访问白名单页面(登录页面)
		if (whiteList.indexOf(to.path) !== -1) {
			next()
		} else {
			// 销毁聊天弹出框
			await chatUtil.destoryChatDialog()
			next(`/login?redirect=${to.path}`)
			NProgress.done()
		}
	}
})

router.afterEach(() => {
	NProgress.done()
})

2.store文件 第二步

import { PermissionState } from '@/types/store/permission'
import { RouteRecordRaw } from 'vue-router'
import { defineStore } from 'pinia'
import { constantRoutes } from '@/router'
import { listRoutes, listRoutesToMap } from '@/api/system/menu'

const modules = import.meta.glob('../../views/**/**.vue')
export const Layout = () => import('@/layout/index.vue')

export const filterAsyncRoutes = (routes: RouteRecordRaw[], roles: string[]) => {
	const res: RouteRecordRaw[] = []
	routes.forEach(
		(route) => {
			const tmp = { ...route } as any
			//if (hasPermission(roles, tmp)) {
			if (tmp.component == 'Layout') {
				tmp.component = Layout
			} else {
				const component = modules[`../../views/${tmp.component}.vue`] as any
				if (component) {
					tmp.component = modules[`../../views/${tmp.component}.vue`]
				} else {
					tmp.component = modules[`../../views/error-page/404.vue`]
				}
			}
			tmp.name = tmp.path
			res.push(tmp)

			if (tmp.children) {
				tmp.children = filterAsyncRoutes(tmp.children, roles)
				// 默认选中第一个
				tmp.redirect = tmp.children[0].path
			}
		}
		//}
	)
	return res
}

const usePermissionStore = defineStore({
	id: 'permission',
	state: (): PermissionState => ({
		routes: [],
		routesDesignMake: [],
		routesPurchase: [],
		addRoutes: [],
		addRoutesDesignMake: [],
		addRoutesPurchase: [],
	}),
	actions: {
		setRoutes(routes: RouteRecordRaw[]) {
			this.addRoutes = routes
			this.routes = constantRoutes.concat(routes)
		},
		setRoutessDesignMake(routes: RouteRecordRaw[]) {
			this.addRoutesDesignMake = routes
			this.routesDesignMake = constantRoutes.concat(routes)
		},
		setRoutesPurchase(routes: RouteRecordRaw[]) {
			this.addRoutesPurchase = routes
			this.routesPurchase = constantRoutes.concat(routes)
		},
		generateRoutes(roles: string[]) {
			// 第二步
			return new Promise((resolve, reject) => {
				listRoutesToMap()
					.then((response) => {
						const mapData = new Map()
						console.log(response.data)

						// const asyncRoutes = response.data[1]
						// const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
						mapData.set(1, filterAsyncRoutes(response.data[1], roles))
						mapData.set(2, filterAsyncRoutes(response.data[2], roles))
						mapData.set(3, filterAsyncRoutes(response.data[3], roles))
						this.setRoutes(mapData.get(1))
						this.setRoutessDesignMake(mapData.get(2))
						this.setRoutesPurchase(mapData.get(3))
						resolve(mapData)
					})
					.catch((error) => {
						reject(error)
					})
			})
		},
	},
})

export default usePermissionStore

3.头部菜单栏点击事件  第四步

<template>
	<div class="navbar">
		<div class="top">
			<el-menu class="el-menu-demo" mode="horizontal" :default-active="activeIndex">
				<!-- <el-menu
				:default-active="activeIndex"
				class="el-menu-demo"
				mode="horizontal"
				@select="handleSelect"
			> -->
				<el-menu-item index="0" @click="handoffTypes(0)">首页</el-menu-item>
				<el-menu-item index="1" @click="handoffTypes(1)">产业平台</el-menu-item>
				<el-menu-item index="2" @click="handoffTypes(2)">设计制造</el-menu-item>
				<el-menu-item index="3" @click="handoffTypes(3)">供应链协同平台</el-menu-item>
			</el-menu>
			<div class="right-menu">
				<template v-if="device !== 'mobile'">
					<!--        <search id="header-search" class="right-menu-item" />
                <error-log class="errLog-container right-menu-item hover-effect" />-->
					<screenfull id="screenfull" class="right-menu-item hover-effect" />
					<!-- <el-tooltip content="布局大小" effect="dark" placement="bottom">
					<size-select id="size-select" class="right-menu-item hover-effect" />
				</el-tooltip> -->
					<!-- <lang-select class="right-menu-item hover-effect" /> -->
				</template>

				<el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click" popper-class="fs-popper">
					<div class="avatar-wrapper">
						<!-- <img :src="avatar + '?imageView2/1/w/80/h/80'" class="user-avatar" /> -->
						<el-avatar :size="40" :src="avatar">
							<img src="@/assets/images/avatar-default.png" />
						</el-avatar>
						<CaretBottom style="width: 0.6em; height: 0.6em; margin-left: 5px" />
					</div>

					<template #dropdown>
						<el-dropdown-menu>
							<el-dropdown-item divided @click="logout">
								{{ $t('navbar.logout') }}
							</el-dropdown-item>
						</el-dropdown-menu>
					</template>
				</el-dropdown>
			</div>
		</div>
		<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />

		<breadcrumb id="breadcrumb-container" class="breadcrumb-container" />
	</div>
</template>
<script setup lang="ts">
	import { computed, watch, ref } from 'vue'
	import { useRoute, useRouter } from 'vue-router'
	import { ElMessageBox } from 'element-plus'
	import useStore from '@/store'
	import { localStorage } from '@/utils/storage'

	// 组件依赖
	import Breadcrumb from '@/components/Breadcrumb/index.vue'
	import Hamburger from '@/components/Hamburger/index.vue'
	import Screenfull from '@/components/Screenfull/index.vue'
	import { chatUtil } from '@/utils/ChatUtil'

	// 图标依赖
	import { CaretBottom } from '@element-plus/icons-vue'

	const { app, user, tagsView } = useStore()

	const route = useRoute()
	const router = useRouter()

	const sidebar = computed(() => app.sidebar)
	const device = computed(() => app.device)
	const avatar = computed(() => user.avatar)

	function toggleSideBar() {
		app.toggleSidebar()
	}

	const activeIndex = ref('1')

	// 第四步
	watch(
		() => app.applicationCode,
		(value) => {
			if (localStorage.get('applicationCode') == null) {
				activeIndex.value = '1'
			} else {
				activeIndex.value = '' + localStorage.get('applicationCode')
			}
			console.log(activeIndex.value, 'activeIndex.value')
		},
		{ immediate: true }
	)

	const handoffTypes = (type: number) => {
		localStorage.set('applicationCode', type)
		app.applicationCode = type
	}

	// 去除元素
	const removeEle = (ele: any) => {
		for (let i = 0; i < ele.length; i++) {
			const element = ele[i]
			element.parentNode?.removeChild(element)
		}
	}

	function logout() {
		ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
			confirmButtonText: '确定',
			cancelButtonText: '取消',
			type: 'warning',
		}).then(async () => {
			// 销毁聊天弹出框
			await chatUtil.destoryChatDialog((flag: any) => {
				if (flag) {
					user.logout()
						.then(() => {
							tagsView.delAllViews()
						})
						.then(() => {
							router.push(`/login?redirect=${route.fullPath}`)
						})
				} else {
					alert('注销失败,请先结束聊天已接入的会话!')
				}
			})
		})
	}
</script>

<style lang="scss" scoped>
	ul {
		list-style: none;
		margin: 0;
		padding: 0;
	}
	.el-menu-demo {
		height: 50px;
	}
	.navbar {
		/* height: 50px; */
		overflow: hidden;
		position: relative;
		background: #fff;
		box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
		z-index: 999 !important;
		position: relative;

		.hamburger-container {
			line-height: 46px;
			height: 100%;
			float: left;
			cursor: pointer;
			transition: background 0.3s;
			-webkit-tap-highlight-color: transparent;

			&:hover {
				background: rgba(0, 0, 0, 0.025);
			}
		}
		.top {
			display: flex;
			justify-content: space-between;
			.el-menu {
				flex: 1;
			}
		}
		.breadcrumb-container {
			float: left;
		}

		.right-menu {
			float: right;
			height: 100%;
			line-height: 50px;
			border-bottom: 1px solid #dcdfe6;
			height: 50px;
			&:focus {
				outline: none;
			}

			.right-menu-item {
				display: inline-block;
				padding: 0 8px;
				height: 100%;
				font-size: 18px;
				color: #5a5e66;
				vertical-align: text-bottom;

				&.hover-effect {
					cursor: pointer;
					transition: background 0.3s;

					&:hover {
						background: rgba(0, 0, 0, 0.025);
					}
				}
			}

			.avatar-container {
				margin-right: 30px;

				.avatar-wrapper {
					margin-top: 5px;
					position: relative;

					.user-avatar {
						cursor: pointer;
						width: 40px;
						height: 40px;
						border-radius: 10px;
					}

					.el-icon-caret-bottom {
						cursor: pointer;
						position: absolute;
						right: -20px;
						top: 25px;
						font-size: 12px;
					}
				}
			}
		}
	}
</style>

4.侧边菜单栏  第五步

<template>
	<div :class="{ 'has-logo': showLogo }">
		<logo v-if="showLogo" :collapse="isCollapse" />
		<el-scrollbar>
			<el-menu
				:default-active="activeMenu"
				:collapse="isCollapse"
				:background-color="variables.menuBg"
				:text-color="variables.menuText"
				:active-text-color="variables.menuActiveText"
				:unique-opened="true"
				:collapse-transition="false"
				mode="vertical"
				@open="handleOpen">
				<sidebar-item
					v-for="route in currentRoutes"
					:item="route"
					:key="route.path"
					:base-path="route.path"
					:is-collapse="isCollapse" />
			</el-menu>
		</el-scrollbar>
	</div>
</template>

<script setup lang="ts">
	import { computed, watch } from 'vue'
	import { useRoute, useRouter } from 'vue-router'
	import SidebarItem from './SidebarItem.vue'
	import Logo from './Logo.vue'
	import variables from '@/styles/variables.module.scss'
	import useStore from '@/store'
	const { permission, setting, app } = useStore()

	// 第五步
	const route = useRoute()
	const router = useRouter()
	const routes = computed(() => permission.routes)
	const routesDesignMake = computed(() => permission.routesDesignMake)
	const routesPurchase = computed(() => permission.routesPurchase)
	const showLogo = computed(() => setting.sidebarLogo)
	const isCollapse = computed(() => !app.sidebar.opened)

	// 当前菜单
	const currentRoutes = computed(() => {
		if (app.applicationCode == 1) {
			return routes.value
		} else if (app.applicationCode == 2) {
			return routesDesignMake.value
		} else if (app.applicationCode == 3) {
			return routesPurchase.value
		} else {
			return routes.value
		}
	})


	const activeMenu = computed(() => {
		const { meta, path } = route
		if (meta.activeMenu) {
			return meta.activeMenu as string
		}
		return path
	})
	// 默认选中第一个
	const handleOpen = (key: string, keyPath: string[]) => {
		// 解决刷新时自动选中第一项
		if (activeMenu.value.indexOf(key) >= 0) {
			return
		}
		router.push(key)
	}

</script>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值