food/src/views/layout/components/menubar.vue
szdot e7506728af 【类 型】:feat
【原  因】:完成集群飞机控制主页面  和  控制组件
【过  程】:
【影  响】:

# 类型 包含:
# feat:新功能(feature)
# fix:修补bug
# docs:文档(documentation)
# style: 格式(不影响代码运行的变动)
# refactor:重构(即不是新增功能,也不是修改bug的代码变动)
# test:增加测试
# chore:构建过程或辅助工具的变动
2025-06-22 20:39:40 +08:00

249 lines
7.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div>
<!-- logo -->
<div class="sidebar-logo-container" :class="{ 'collapse': isCollapse }">
<div v-if="isCollapse" key="isCollapse" class="sidebar-logo-link" @click="isTap = !isTap">
<img :src="isTap ? require('@/assets/logo.svg') : require('@/assets/appletLogo.svg')" class="sidebar-logo">
</div>
<div v-else key="expand" class="sidebar-logo-link" @click="isTap = !isTap">
<img :src="isTap ? require('@/assets/logo.svg') : require('@/assets/appletLogo.svg')" class="sidebar-logo">
<h1 class="sidebar-title">{{ title }}</h1>
</div>
</div>
<!-- end logo -->
<!-- menu -->
<transition name="el-zoom-in-top">
<el-menu v-show="show" class="border-n" :router="true" :default-active="activeMenu" :unique-opened="true"
background-color="#304156" text-color="rgb(191, 203, 217)" active-text-color="#409EFF"
:collapse-transition="false" :collapse="isCollapse">
<template v-for="(route, index) in routes">
<el-menu-item @click="lt480Collapse()" v-if="route.children.length < 2" :key="route.path" :index="route.children[0].path">
<i class="fc" :class="route.children[0].meta.icon"></i>
<span slot="title">{{ route.children[0].meta.title }}</span>
</el-menu-item>
<el-submenu v-else :key="index" :index="route.path">
<template slot="title">
<i class="fc" :class="route.meta.icon"></i>
<span>{{ route.meta.title }}</span>
</template>
<el-menu-item @click="lt480Collapse()" v-for="child in route.children" :key="child.path" :index="child.path">
<i class="fc" :class="child.meta.icon"></i>
<span>{{ child.meta.title }}</span>
</el-menu-item>
</el-submenu>
</template>
</el-menu>
</transition>
<!-- end menu -->
</div>
</template>
<script>
export default {
name: 'Menubar',
data () {
return {
title: '无人机控制终端',
isTap: true, // 终端 后台切换
show: true// 导航栏 动画切换
}
},
computed: {
/**
* @description: 递归过滤路由列表,同时过滤掉父级和子级中 hidden 为 true 或权限不足的项
*/
routes () {
const filterRoutes = (routes, userPower) => {
return routes.filter(item => {
const roles = item.meta.roles || [] // 设置默认空数组
const tapName = this.isTap ? 'plane' : 'admin'
// 显示
if (item.meta.hidden === true || roles.indexOf(userPower) === -1 || item.meta.tapName !== tapName) {
return false
}
if (item.children && item.children.length > 0) {
item.children = filterRoutes(item.children, userPower) // 递归处理子级
if (item.children.length === 0) {
return false // 如果子级都被过滤掉了,也过滤掉当前项
}
}
return true
})
}
const userPower = this.$store.state.user.power.trim()
return filterRoutes(this.$router.options.routes, userPower)
},
/**
* @description: 当前激活的 路由路径
*/
activeMenu () {
const route = this.$route
const { meta, path } = route
const decodedPath = decodeURIComponent(path) // 解码 URL
if (meta.activeMenu) {
return meta.activeMenu
}
return decodedPath
},
/**
* @description: 获取飞机列表
*/
airList () {
return this.$store.state.airList.filter(element => element.shop_id === this.$store.state.user.shop_id)
},
/**
* @description: 侧边导航栏状态
*/
isCollapse () {
return this.$store.state.app.isCollapse
}
},
methods: {
/**
* @description: 屏幕宽度小于480 隐藏侧边栏
*/
lt480Collapse () {
// 判断屏幕宽度是否小于 480 像素
if (this.$store.state.app.isWideScreen) {
setTimeout(() => {
this.$store.commit('app/setCollapse')
}, 100)
}
},
/**
* @description: 动态加载无人机子路由,并保留静态配置(如集群控制)
* @param {Array} planes - 飞机对象数组,每个对象包含 id 和 name
*/
loadRoute (planes) {
// 1. 将每架飞机转换为一个路由配置对象
const dynamicPlaneRoutes = planes.map((item) => ({
path: '/planes/index/' + item.id + '/' + item.name, // 动态路径携带飞机ID和名称
component: () => import('@/views/layout/components/main/planes/index.vue'), // 对应的组件路径
meta: {
title: item.name, // 用于左侧菜单或标签页显示
icon: 'iconfont icon-wurenji', // 图标(可替换)
roles: ['admin', 'editor'], // 权限控制
tapName: 'plane' // 自定义标记
// activeMenu: '/planes/swarm' // 可选:保持菜单高亮在“集群控制”
}
}))
// 2. 遍历顶层路由配置,找到 path 为 "/planes" 的主路由
this.routes.forEach((element) => {
if (element.path === '/planes') {
// 3. 过滤原本 children 中的静态路由(排除旧的动态飞机 index 路由)
const staticChildren = element.children?.filter(child => !child.path.startsWith('/planes/index/')) || []
// 4. 合并:静态子路由(如 /planes/swarm + 动态生成的飞机子路由
element.children = [
...staticChildren,
...dynamicPlaneRoutes
]
}
})
}
},
created () {
// 初始化 根据当前路由的tapName字段 判断导航是终端栏还是后台栏
this.isTap = this.$route.meta.tapName !== 'admin'
},
watch: {
/**
* @description: 监听飞机列表 有刷新导航栏
*/
airList (val) {
this.loadRoute(val)
this.$forceUpdate()// 刷新本组件
},
isTap (val) {
if (val) {
this.title = '无人机控制终端'
} else {
this.title = '小程序后台管理'
}
// 给导航栏切换加个 动画过渡
this.show = false
setTimeout(() => {
this.show = true
}, 50)
}
}
}
</script>
<style lang="scss" scoped>
@import "@/styles/theme.scss";
i {
font-size: 18px !important;
margin-right: 12px;
vertical-align: middle;
}
.el-submenu__title i,
.el-menu-item i {
color: rgb(191, 203, 217);
}
.el-submenu {
.el-menu-item {
background-color: #1f2d3d !important;
}
}
.el-submenu .el-menu-item:hover {
background-color: #001528 !important;
}
.sidebarLogoFade-enter,
.sidebarLogoFade-leave-to {
opacity: 0;
}
h1 {
color: $maintext-color !important;
}
.sidebar-logo-container {
position: relative;
width: 100%;
height: 50px;
line-height: 50px;
background: $graylight-color;
text-align: center;
overflow: hidden;
z-index: 100;
box-shadow: 0 1px 1px rgba(0, 21, 41, .08);
& .sidebar-logo-link {
height: 100%;
width: 100%;
cursor: pointer;
& .sidebar-logo {
width: 24px;
height: 24px;
vertical-align: middle;
margin-right: 12px;
}
& .sidebar-title {
display: inline-block;
margin: 0;
color: #fff;
font-weight: 600;
line-height: 50px;
font-size: 14px;
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
vertical-align: middle;
}
}
&.collapse {
.sidebar-logo {
margin-right: 0px;
}
}
}
</style>