【原 因】:完成集群飞机控制主页面 和 控制组件 【过 程】: 【影 响】: # 类型 包含: # feat:新功能(feature) # fix:修补bug # docs:文档(documentation) # style: 格式(不影响代码运行的变动) # refactor:重构(即不是新增功能,也不是修改bug的代码变动) # test:增加测试 # chore:构建过程或辅助工具的变动
249 lines
7.4 KiB
Vue
249 lines
7.4 KiB
Vue
<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>
|